commit eb678bb2f915bff74527b3aa01d853a94d276585 Author: Claudio Maggioni Date: Sun Mar 22 09:36:01 2020 +0100 Added pintos source and bochs support files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dab799c --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + diff --git a/Vagrantfile b/Vagrantfile new file mode 100755 index 0000000..9e8706d --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,16 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : +Vagrant.configure(2) do |config| + config.vm.box = "ubuntu/trusty64" + + # Create a forwarded port mapping which allows access to a specific port + # within the machine from a port on the host machine. In the example below, + # accessing "localhost:8080" will access port 80 on the guest machine. + # config.vm.network "forwarded_port", guest: 80, host: 8080 + + config.vm.hostname = "pintosvm" + + config.vm.provision "shell", path: "bootstrap.sh" + + config.vm.synced_folder "./pintos-env", "/pintos-env" +end diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..d9392d7 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,12 @@ +sudo apt-get update +sudo apt-get install -y gcc g++ gdb binutils \ + libxrandr2 libxrandr-dev \ + libncurses5-dev libncurses5 +cat < /home/vagrant/.bash_profile +PATH=/pintos-env/bin/:\$PATH +PATH=/pintos-env/pintos/utils:\$PATH +export PATH +export PS1='\${debian_chroot:+(\$debian_chroot)}\[\e[1;31m\]\u@\h:\[\e[0m\]\w\\$ ' + +alias ls='ls --color' +EOF diff --git a/pintos-env/bin/bochs b/pintos-env/bin/bochs new file mode 100755 index 0000000..d269547 Binary files /dev/null and b/pintos-env/bin/bochs differ diff --git a/pintos-env/bin/bxcommit b/pintos-env/bin/bxcommit new file mode 100755 index 0000000..8607069 Binary files /dev/null and b/pintos-env/bin/bxcommit differ diff --git a/pintos-env/bin/bximage b/pintos-env/bin/bximage new file mode 100755 index 0000000..92341f2 Binary files /dev/null and b/pintos-env/bin/bximage differ diff --git a/pintos-env/pintos/.cproject b/pintos-env/pintos/.cproject new file mode 100755 index 0000000..74e10d9 --- /dev/null +++ b/pintos-env/pintos/.cproject @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pintos-env/pintos/.project b/pintos-env/pintos/.project new file mode 100755 index 0000000..09e49fd --- /dev/null +++ b/pintos-env/pintos/.project @@ -0,0 +1,27 @@ + + + pintos + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/pintos-env/pintos/LICENSE b/pintos-env/pintos/LICENSE new file mode 100755 index 0000000..8702541 --- /dev/null +++ b/pintos-env/pintos/LICENSE @@ -0,0 +1,95 @@ +Pintos, including its documentation, is subject to the following +license: + + Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford + Jr. University. All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +A few individual files in Pintos were originally derived from other +projects, but they have been extensively modified for use in Pintos. +The original code falls under the original license, and modifications +for Pintos are additionally covered by the Pintos license above. + +In particular, code derived from Nachos is subject to the following +license: + +/* Copyright (c) 1992-1996 The Regents of the University of California. + All rights reserved. + + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose, without fee, and + without written agreement is hereby granted, provided that the + above copyright notice and the following two paragraphs appear + in all copies of this software. + + IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO + ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE + AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA + HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +*/ + +Also, code derived from MIT's 6.828 course code is subject to the +following license: + +/* + * 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. + */ diff --git a/pintos-env/pintos/Make.config b/pintos-env/pintos/Make.config new file mode 100755 index 0000000..6f5f8a7 --- /dev/null +++ b/pintos-env/pintos/Make.config @@ -0,0 +1,68 @@ +# -*- makefile -*- + +SHELL = /bin/sh + +VPATH = $(SRCDIR) + +# Binary utilities. +# If the host appears to be x86, use the normal tools. +# If it's x86-64, use the compiler and linker in 32-bit mode. +# Otherwise assume cross-tools are installed as i386-elf-*. +X86 = i.86\|pentium.*\|[pk][56]\|nexgen\|viac3\|6x86\|athlon.*\|i86pc +X86_64 = x86_64 +ifneq (0, $(shell expr `uname -m` : '$(X86)')) + CC = gcc + LD = ld + OBJCOPY = objcopy +else + ifneq (0, $(shell expr `uname -m` : '$(X86_64)')) + ifneq (0, $(shell expr `uname -s` : 'Darwin')) + CC = i386-elf-gcc + LD = i386-elf-ld + AR = i386-elf-ar + RANLIB = i386-elf-ranlib + OBJCOPY = i386-elf-objcopy + else + CC = gcc -m32 -fno-stack-protector + LD = ld -melf_i386 + AR = ar + RANLIB = ranlib + OBJCOPY = objcopy + endif + else + CC = i386-elf-gcc + LD = i386-elf-ld + AR = i386-elf-ar + RANLIB = i386-elf-ranlib + OBJCOPY = i386-elf-objcopy + endif +endif + +ifeq ($(strip $(shell command -v $(CC) 2> /dev/null)),) +$(warning *** Compiler ($(CC)) not found. Did you set $$PATH properly? Please refer to the Getting Started section in the documentation for details. ***) +endif + +# Compiler and assembler invocation. +DEFINES = +WARNINGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes -Wsystem-headers +CFLAGS = -g -msoft-float -O +CPPFLAGS = -nostdinc -I$(SRCDIR) -I$(SRCDIR)/lib +ASFLAGS = -Wa,--gstabs +LDFLAGS = +DEPS = -MMD -MF $(@:.o=.d) + +# Turn off -fstack-protector, which we don't support. +ifeq ($(strip $(shell echo | $(CC) -fno-stack-protector -E - > /dev/null 2>&1; echo $$?)),0) +CFLAGS += -fno-stack-protector +endif + +# Turn off --build-id in the linker, which confuses the Pintos loader. +ifeq ($(strip $(shell $(LD) --help | grep -q build-id; echo $$?)),0) +LDFLAGS += -Wl,--build-id=none +endif + +%.o: %.c + $(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(DEFINES) $(DEPS) + +%.o: %.S + $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) $(DEPS) diff --git a/pintos-env/pintos/Makefile b/pintos-env/pintos/Makefile new file mode 100755 index 0000000..229f85d --- /dev/null +++ b/pintos-env/pintos/Makefile @@ -0,0 +1,29 @@ +BUILD_SUBDIRS = threads userprog vm filesys + +all:: + @echo "Run 'make' in subdirectories: $(BUILD_SUBDIRS)." + @echo "This top-level make has only 'clean' targets." + +CLEAN_SUBDIRS = $(BUILD_SUBDIRS) examples utils + +clean:: + for d in $(CLEAN_SUBDIRS); do $(MAKE) -C $$d $@; done + rm -f TAGS tags + +distclean:: clean + find . -name '*~' -exec rm '{}' \; + +TAGS_SUBDIRS = $(BUILD_SUBDIRS) devices lib +TAGS_SOURCES = find $(TAGS_SUBDIRS) -name \*.[chS] -print + +TAGS:: + etags --members `$(TAGS_SOURCES)` + +tags:: + ctags -T --no-warn `$(TAGS_SOURCES)` + +cscope.files:: + $(TAGS_SOURCES) > cscope.files + +cscope:: cscope.files + cscope -b -q -k diff --git a/pintos-env/pintos/Makefile.build b/pintos-env/pintos/Makefile.build new file mode 100755 index 0000000..e997d27 --- /dev/null +++ b/pintos-env/pintos/Makefile.build @@ -0,0 +1,109 @@ +# -*- makefile -*- + +SRCDIR = ../.. + +all: kernel.bin loader.bin + +include ../../Make.config +include ../Make.vars +include ../../tests/Make.tests + +# Compiler and assembler options. +kernel.bin: CPPFLAGS += -I$(SRCDIR)/lib/kernel + +# Core kernel. +threads_SRC = threads/start.S # Startup code. +threads_SRC += threads/init.c # Main program. +threads_SRC += threads/thread.c # Thread management core. +threads_SRC += threads/switch.S # Thread switch routine. +threads_SRC += threads/interrupt.c # Interrupt core. +threads_SRC += threads/intr-stubs.S # Interrupt stubs. +threads_SRC += threads/synch.c # Synchronization. +threads_SRC += threads/palloc.c # Page allocator. +threads_SRC += threads/malloc.c # Subpage allocator. + +# Device driver code. +devices_SRC = devices/pit.c # Programmable interrupt timer chip. +devices_SRC += devices/timer.c # Periodic timer device. +devices_SRC += devices/kbd.c # Keyboard device. +devices_SRC += devices/vga.c # Video device. +devices_SRC += devices/serial.c # Serial port device. +devices_SRC += devices/block.c # Block device abstraction layer. +devices_SRC += devices/partition.c # Partition block device. +devices_SRC += devices/ide.c # IDE disk block device. +devices_SRC += devices/input.c # Serial and keyboard input. +devices_SRC += devices/intq.c # Interrupt queue. +devices_SRC += devices/rtc.c # Real-time clock. +devices_SRC += devices/shutdown.c # Reboot and power off. +devices_SRC += devices/speaker.c # PC speaker. + +# Library code shared between kernel and user programs. +lib_SRC = lib/debug.c # Debug helpers. +lib_SRC += lib/random.c # Pseudo-random numbers. +lib_SRC += lib/stdio.c # I/O library. +lib_SRC += lib/stdlib.c # Utility functions. +lib_SRC += lib/string.c # String functions. +lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC. +lib_SRC += lib/ustar.c # Unix standard tar format utilities. + +# Kernel-specific library code. +lib/kernel_SRC = lib/kernel/debug.c # Debug helpers. +lib/kernel_SRC += lib/kernel/list.c # Doubly-linked lists. +lib/kernel_SRC += lib/kernel/bitmap.c # Bitmaps. +lib/kernel_SRC += lib/kernel/hash.c # Hash tables. +lib/kernel_SRC += lib/kernel/console.c # printf(), putchar(). + +# User process code. +userprog_SRC = userprog/process.c # Process loading. +userprog_SRC += userprog/pagedir.c # Page directories. +userprog_SRC += userprog/exception.c # User exception handler. +userprog_SRC += userprog/syscall.c # System call handler. +userprog_SRC += userprog/gdt.c # GDT initialization. +userprog_SRC += userprog/tss.c # TSS management. + +# No virtual memory code yet. +#vm_SRC = vm/file.c # Some file. + +# Filesystem code. +filesys_SRC = filesys/filesys.c # Filesystem core. +filesys_SRC += filesys/free-map.c # Free sector bitmap. +filesys_SRC += filesys/file.c # Files. +filesys_SRC += filesys/directory.c # Directories. +filesys_SRC += filesys/inode.c # File headers. +filesys_SRC += filesys/fsutil.c # Utilities. + +SOURCES = $(foreach dir,$(KERNEL_SUBDIRS),$($(dir)_SRC)) +OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES))) +DEPENDS = $(patsubst %.o,%.d,$(OBJECTS)) + +threads/kernel.lds.s: CPPFLAGS += -P +threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h + +kernel.o: threads/kernel.lds.s $(OBJECTS) + $(LD) -T $< -o $@ $(OBJECTS) + +kernel.bin: kernel.o + $(OBJCOPY) -R .note -R .comment -S $< $@ + +threads/loader.o: threads/loader.S + $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) + +loader.bin: threads/loader.o + $(LD) -N -e 0 -Ttext 0x7c00 --oformat binary -o $@ $< + +os.dsk: kernel.bin + cat $^ > $@ + +clean:: + rm -f $(OBJECTS) $(DEPENDS) + rm -f threads/loader.o threads/kernel.lds.s threads/loader.d + rm -f kernel.bin.tmp + rm -f kernel.o kernel.lds.s + rm -f kernel.bin loader.bin + rm -f bochsout.txt bochsrc.txt + rm -f results grade + +Makefile: $(SRCDIR)/Makefile.build + cp $< $@ + +-include $(DEPENDS) diff --git a/pintos-env/pintos/Makefile.kernel b/pintos-env/pintos/Makefile.kernel new file mode 100755 index 0000000..162a411 --- /dev/null +++ b/pintos-env/pintos/Makefile.kernel @@ -0,0 +1,20 @@ +# -*- makefile -*- + +all: + +include Make.vars + +DIRS = $(sort $(addprefix build/,$(KERNEL_SUBDIRS) $(TEST_SUBDIRS) lib/user)) + +all grade check: $(DIRS) build/Makefile + cd build && $(MAKE) $@ +$(DIRS): + mkdir -p $@ +build/Makefile: ../Makefile.build + cp $< $@ + +build/%: $(DIRS) build/Makefile + cd build && $(MAKE) $* + +clean: + rm -rf build diff --git a/pintos-env/pintos/Makefile.userprog b/pintos-env/pintos/Makefile.userprog new file mode 100755 index 0000000..0b15da2 --- /dev/null +++ b/pintos-env/pintos/Makefile.userprog @@ -0,0 +1,52 @@ +# -*- makefile -*- + +$(PROGS): CPPFLAGS += -I$(SRCDIR)/lib/user -I. + +# Linker flags. +$(PROGS): LDFLAGS += -nostdlib -static -Wl,-T,$(LDSCRIPT) +$(PROGS): LDSCRIPT = $(SRCDIR)/lib/user/user.lds + +# Library code shared between kernel and user programs. +lib_SRC = lib/debug.c # Debug code. +lib_SRC += lib/random.c # Pseudo-random numbers. +lib_SRC += lib/stdio.c # I/O library. +lib_SRC += lib/stdlib.c # Utility functions. +lib_SRC += lib/string.c # String functions. +lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC. +lib_SRC += lib/ustar.c # Unix standard tar format utilities. + +# User level only library code. +lib/user_SRC = lib/user/debug.c # Debug helpers. +lib/user_SRC += lib/user/syscall.c # System calls. +lib/user_SRC += lib/user/console.c # Console code. + +LIB_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(lib_SRC) $(lib/user_SRC))) +LIB_DEP = $(patsubst %.o,%.d,$(LIB_OBJ)) +LIB = lib/user/entry.o libc.a + +PROGS_SRC = $(foreach prog,$(PROGS),$($(prog)_SRC)) +PROGS_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PROGS_SRC))) +PROGS_DEP = $(patsubst %.o,%.d,$(PROGS_OBJ)) + +all: $(PROGS) + +define TEMPLATE +$(1)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(1)_SRC))) +$(1): $$($(1)_OBJ) $$(LIB) $$(LDSCRIPT) + $$(CC) $$(LDFLAGS) $$($(1)_OBJ) $$(LIB) -o $$@ +endef + +$(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog)))) + +libc.a: $(LIB_OBJ) + rm -f $@ + $(AR) -r $@ $^ + $(RANLIB) $@ + +clean:: + rm -f $(PROGS) $(PROGS_OBJ) $(PROGS_DEP) + rm -f $(LIB_DEP) $(LIB_OBJ) lib/user/entry.[do] libc.a + +.PHONY: all clean + +-include $(LIB_DEP) $(PROGS_DEP) diff --git a/pintos-env/pintos/README b/pintos-env/pintos/README new file mode 100755 index 0000000..d1c384d --- /dev/null +++ b/pintos-env/pintos/README @@ -0,0 +1,63 @@ +### Pintos on Mac### +Pintos Mac - Pintos on Mac + +Release Version: 0.1 +Release Data: 2012/9/17 +Contact: Jeremy Mao(maojie@me.com or yujie.mao@intel.com) +Homepage: https://github.com/maojie/pintos_mac + +Content +1. Overview +2. Getting Started +3. Building Pintos +4. Running Pintos + +* 1 OVERVIEW +Welcome to Pintos. Pintos is a simple operating system framework for the 80x86 architecture. It supports kernel threads, loading and running user programs, and a file system, but it implements all of these in a very simple way. In the Pintos projects, you and your project team will strengthen its support in all three of these areas. You will also add a virtual memory implementation. + +The orignal experimental environment of Pintos is on *nix (Unix and Linux), since MacOSX comes from Unix, I am curious to make Pintos workable on MacOSX. The following instructions can help you setup your experimental environment. + +* 2 Getting Started + +2.0 Prerequisites +Before next step, you at least need + 1. Hardware: Macbook, Macbook Pro, Macbook Air, Mac Mini or iMac + 2. Software: OS X Mountaion Lion (My environment), maybe workable on Lion and Snow Leopard + +2.0.1 Setup cross build environment +To set up the pintos working environment on mac, you should + 1. Download pintos source code from github + git clone https://github.com/maojie/pintos_mac.git + 2. Download MacPorts from + http://www.macports.org + 3. Install cross build tools + sudo port install i386-elf-binutils + sudo port install i386-elf-gcc + 4. Create gcc link + cd /opt/local/bin + sudo ln -s i386-elf-gcc i386-elf-gcc-4.3.2 + 5. Install bochs + sudo port install bochs + 6. Install X on Mac from + http://xquartz.macosforge.org + +* 3 Building Pintos +As the next step, build the source code supplied for the first project. First, cd into the "threads" directory. Then, issue the "make" command. This will create a "build" directory under "theads", populate it with a "Makefile" and a few subdirectories, and then build the kernel inside. The entire build should take less than 30 second. + +Watch the commands executed during the build. On the Linux machines, the ordinary system tools are used. +Following the build, the following are the interesting files in the "build" directory: +"Makefile" + A copy of "pintos/src/Makefile.build". It describes how to build the kernel. +"kernel.o" + Object file for the entire kernel. This is the result of linking object files compiled from each individual kernel source file into a single object file. It contains debug infomation, so you can run GDB or backtrace on it. +"kernel.bin" + Memory image of the kernel, that is, the exact bytes loaded into memory to run the Pintos kernel. This is just "kernel.o" with debug information stripped out, which saves a lot of space, which in turn keeps the kernel from bumping up against a 512KB size limit imposed by the kernel loader's design. +"loader.bin" + Memory image for the kernel loader, a small chunk of code written in assembly language that reads the kernel from disk into memory and starts it up. It is exactly 512 bytes long, a size fixed by the PC BIOS. + +Subdirectories of "build" contain object files (".o") and dependency files (".d"), both produced by the compiler. The dependency file tell make which source files need to be recompiled when other source or header files are changed. + +* 4 Running Pintos +cd threads/build +pintos -- run alarm-single +(TO BE CONTINUED) diff --git a/pintos-env/pintos/devices/block.c b/pintos-env/pintos/devices/block.c new file mode 100755 index 0000000..452c433 --- /dev/null +++ b/pintos-env/pintos/devices/block.c @@ -0,0 +1,223 @@ +#include "devices/block.h" +#include +#include +#include +#include "devices/ide.h" +#include "threads/malloc.h" + +/* A block device. */ +struct block + { + struct list_elem list_elem; /* Element in all_blocks. */ + + char name[16]; /* Block device name. */ + enum block_type type; /* Type of block device. */ + block_sector_t size; /* Size in sectors. */ + + const struct block_operations *ops; /* Driver operations. */ + void *aux; /* Extra data owned by driver. */ + + unsigned long long read_cnt; /* Number of sectors read. */ + unsigned long long write_cnt; /* Number of sectors written. */ + }; + +/* List of all block devices. */ +static struct list all_blocks = LIST_INITIALIZER (all_blocks); + +/* The block block assigned to each Pintos role. */ +static struct block *block_by_role[BLOCK_ROLE_CNT]; + +static struct block *list_elem_to_block (struct list_elem *); + +/* Returns a human-readable name for the given block device + TYPE. */ +const char * +block_type_name (enum block_type type) +{ + static const char *block_type_names[BLOCK_CNT] = + { + "kernel", + "filesys", + "scratch", + "swap", + "raw", + "foreign", + }; + + ASSERT (type < BLOCK_CNT); + return block_type_names[type]; +} + +/* Returns the block device fulfilling the given ROLE, or a null + pointer if no block device has been assigned that role. */ +struct block * +block_get_role (enum block_type role) +{ + ASSERT (role < BLOCK_ROLE_CNT); + return block_by_role[role]; +} + +/* Assigns BLOCK the given ROLE. */ +void +block_set_role (enum block_type role, struct block *block) +{ + ASSERT (role < BLOCK_ROLE_CNT); + block_by_role[role] = block; +} + +/* Returns the first block device in kernel probe order, or a + null pointer if no block devices are registered. */ +struct block * +block_first (void) +{ + return list_elem_to_block (list_begin (&all_blocks)); +} + +/* Returns the block device following BLOCK in kernel probe + order, or a null pointer if BLOCK is the last block device. */ +struct block * +block_next (struct block *block) +{ + return list_elem_to_block (list_next (&block->list_elem)); +} + +/* Returns the block device with the given NAME, or a null + pointer if no block device has that name. */ +struct block * +block_get_by_name (const char *name) +{ + struct list_elem *e; + + for (e = list_begin (&all_blocks); e != list_end (&all_blocks); + e = list_next (e)) + { + struct block *block = list_entry (e, struct block, list_elem); + if (!strcmp (name, block->name)) + return block; + } + + return NULL; +} + +/* Verifies that SECTOR is a valid offset within BLOCK. + Panics if not. */ +static void +check_sector (struct block *block, block_sector_t sector) +{ + if (sector >= block->size) + { + /* We do not use ASSERT because we want to panic here + regardless of whether NDEBUG is defined. */ + PANIC ("Access past end of device %s (sector=%"PRDSNu", " + "size=%"PRDSNu")\n", block_name (block), sector, block->size); + } +} + +/* Reads sector SECTOR from BLOCK into BUFFER, which must + have room for BLOCK_SECTOR_SIZE bytes. + Internally synchronizes accesses to block devices, so external + per-block device locking is unneeded. */ +void +block_read (struct block *block, block_sector_t sector, void *buffer) +{ + check_sector (block, sector); + block->ops->read (block->aux, sector, buffer); + block->read_cnt++; +} + +/* Write sector SECTOR to BLOCK from BUFFER, which must contain + BLOCK_SECTOR_SIZE bytes. Returns after the block device has + acknowledged receiving the data. + Internally synchronizes accesses to block devices, so external + per-block device locking is unneeded. */ +void +block_write (struct block *block, block_sector_t sector, const void *buffer) +{ + check_sector (block, sector); + ASSERT (block->type != BLOCK_FOREIGN); + block->ops->write (block->aux, sector, buffer); + block->write_cnt++; +} + +/* Returns the number of sectors in BLOCK. */ +block_sector_t +block_size (struct block *block) +{ + return block->size; +} + +/* Returns BLOCK's name (e.g. "hda"). */ +const char * +block_name (struct block *block) +{ + return block->name; +} + +/* Returns BLOCK's type. */ +enum block_type +block_type (struct block *block) +{ + return block->type; +} + +/* Prints statistics for each block device used for a Pintos role. */ +void +block_print_stats (void) +{ + int i; + + for (i = 0; i < BLOCK_CNT; i++) + { + struct block *block = block_by_role[i]; + if (block != NULL) + { + printf ("%s (%s): %llu reads, %llu writes\n", + block->name, block_type_name (block->type), + block->read_cnt, block->write_cnt); + } + } +} + +/* Registers a new block device with the given NAME. If + EXTRA_INFO is non-null, it is printed as part of a user + message. The block device's SIZE in sectors and its TYPE must + be provided, as well as the it operation functions OPS, which + will be passed AUX in each function call. */ +struct block * +block_register (const char *name, enum block_type type, + const char *extra_info, block_sector_t size, + const struct block_operations *ops, void *aux) +{ + struct block *block = malloc (sizeof *block); + if (block == NULL) + PANIC ("Failed to allocate memory for block device descriptor"); + + list_push_back (&all_blocks, &block->list_elem); + strlcpy (block->name, name, sizeof block->name); + block->type = type; + block->size = size; + block->ops = ops; + block->aux = aux; + block->read_cnt = 0; + block->write_cnt = 0; + + printf ("%s: %'"PRDSNu" sectors (", block->name, block->size); + print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE); + printf (")"); + if (extra_info != NULL) + printf (", %s", extra_info); + printf ("\n"); + + return block; +} + +/* Returns the block device corresponding to LIST_ELEM, or a null + pointer if LIST_ELEM is the list end of all_blocks. */ +static struct block * +list_elem_to_block (struct list_elem *list_elem) +{ + return (list_elem != list_end (&all_blocks) + ? list_entry (list_elem, struct block, list_elem) + : NULL); +} + diff --git a/pintos-env/pintos/devices/block.h b/pintos-env/pintos/devices/block.h new file mode 100755 index 0000000..21732d6 --- /dev/null +++ b/pintos-env/pintos/devices/block.h @@ -0,0 +1,74 @@ +#ifndef DEVICES_BLOCK_H +#define DEVICES_BLOCK_H + +#include +#include + +/* Size of a block device sector in bytes. + All IDE disks use this sector size, as do most USB and SCSI + disks. It's not worth it to try to cater to other sector + sizes in Pintos (yet). */ +#define BLOCK_SECTOR_SIZE 512 + +/* Index of a block device sector. + Good enough for devices up to 2 TB. */ +typedef uint32_t block_sector_t; + +/* Format specifier for printf(), e.g.: + printf ("sector=%"PRDSNu"\n", sector); */ +#define PRDSNu PRIu32 + +/* Higher-level interface for file systems, etc. */ + +struct block; + +/* Type of a block device. */ +enum block_type + { + /* Block device types that play a role in Pintos. */ + BLOCK_KERNEL, /* Pintos OS kernel. */ + BLOCK_FILESYS, /* File system. */ + BLOCK_SCRATCH, /* Scratch. */ + BLOCK_SWAP, /* Swap. */ + BLOCK_ROLE_CNT, + + /* Other kinds of block devices that Pintos may see but does + not interact with. */ + BLOCK_RAW = BLOCK_ROLE_CNT, /* "Raw" device with unidentified contents. */ + BLOCK_FOREIGN, /* Owned by non-Pintos operating system. */ + BLOCK_CNT /* Number of Pintos block types. */ + }; + +const char *block_type_name (enum block_type); + +/* Finding block devices. */ +struct block *block_get_role (enum block_type); +void block_set_role (enum block_type, struct block *); +struct block *block_get_by_name (const char *name); + +struct block *block_first (void); +struct block *block_next (struct block *); + +/* Block device operations. */ +block_sector_t block_size (struct block *); +void block_read (struct block *, block_sector_t, void *); +void block_write (struct block *, block_sector_t, const void *); +const char *block_name (struct block *); +enum block_type block_type (struct block *); + +/* Statistics. */ +void block_print_stats (void); + +/* Lower-level interface to block device drivers. */ + +struct block_operations + { + void (*read) (void *aux, block_sector_t, void *buffer); + void (*write) (void *aux, block_sector_t, const void *buffer); + }; + +struct block *block_register (const char *name, enum block_type, + const char *extra_info, block_sector_t size, + const struct block_operations *, void *aux); + +#endif /* devices/block.h */ diff --git a/pintos-env/pintos/devices/ide.c b/pintos-env/pintos/devices/ide.c new file mode 100755 index 0000000..2cc0292 --- /dev/null +++ b/pintos-env/pintos/devices/ide.c @@ -0,0 +1,527 @@ +#include "devices/ide.h" +#include +#include +#include +#include +#include "devices/block.h" +#include "devices/partition.h" +#include "devices/timer.h" +#include "threads/io.h" +#include "threads/interrupt.h" +#include "threads/synch.h" + +/* The code in this file is an interface to an ATA (IDE) + controller. It attempts to comply to [ATA-3]. */ + +/* ATA command block port addresses. */ +#define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */ +#define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */ +#define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) /* Sector Count. */ +#define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) /* LBA 0:7. */ +#define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) /* LBA 15:8. */ +#define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) /* LBA 23:16. */ +#define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) /* Device/LBA 27:24. */ +#define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) /* Status (r/o). */ +#define reg_command(CHANNEL) reg_status (CHANNEL) /* Command (w/o). */ + +/* ATA control block port addresses. + (If we supported non-legacy ATA controllers this would not be + flexible enough, but it's fine for what we do.) */ +#define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) /* Control (w/o). */ +#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alt Status (r/o). */ + +/* Alternate Status Register bits. */ +#define STA_BSY 0x80 /* Busy. */ +#define STA_DRDY 0x40 /* Device Ready. */ +#define STA_DRQ 0x08 /* Data Request. */ + +/* Control Register bits. */ +#define CTL_SRST 0x04 /* Software Reset. */ + +/* Device Register bits. */ +#define DEV_MBS 0xa0 /* Must be set. */ +#define DEV_LBA 0x40 /* Linear based addressing. */ +#define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */ + +/* Commands. + Many more are defined but this is the small subset that we + use. */ +#define CMD_IDENTIFY_DEVICE 0xec /* IDENTIFY DEVICE. */ +#define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */ +#define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */ + +/* An ATA device. */ +struct ata_disk + { + char name[8]; /* Name, e.g. "hda". */ + struct channel *channel; /* Channel that disk is attached to. */ + int dev_no; /* Device 0 or 1 for master or slave. */ + bool is_ata; /* Is device an ATA disk? */ + }; + +/* An ATA channel (aka controller). + Each channel can control up to two disks. */ +struct channel + { + char name[8]; /* Name, e.g. "ide0". */ + uint16_t reg_base; /* Base I/O port. */ + uint8_t irq; /* Interrupt in use. */ + + struct lock lock; /* Must acquire to access the controller. */ + bool expecting_interrupt; /* True if an interrupt is expected, false if + any interrupt would be spurious. */ + struct semaphore completion_wait; /* Up'd by interrupt handler. */ + + struct ata_disk devices[2]; /* The devices on this channel. */ + }; + +/* We support the two "legacy" ATA channels found in a standard PC. */ +#define CHANNEL_CNT 2 +static struct channel channels[CHANNEL_CNT]; + +static struct block_operations ide_operations; + +static void reset_channel (struct channel *); +static bool check_device_type (struct ata_disk *); +static void identify_ata_device (struct ata_disk *); + +static void select_sector (struct ata_disk *, block_sector_t); +static void issue_pio_command (struct channel *, uint8_t command); +static void input_sector (struct channel *, void *); +static void output_sector (struct channel *, const void *); + +static void wait_until_idle (const struct ata_disk *); +static bool wait_while_busy (const struct ata_disk *); +static void select_device (const struct ata_disk *); +static void select_device_wait (const struct ata_disk *); + +static void interrupt_handler (struct intr_frame *); + +/* Initialize the disk subsystem and detect disks. */ +void +ide_init (void) +{ + size_t chan_no; + + for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++) + { + struct channel *c = &channels[chan_no]; + int dev_no; + + /* Initialize channel. */ + snprintf (c->name, sizeof c->name, "ide%zu", chan_no); + switch (chan_no) + { + case 0: + c->reg_base = 0x1f0; + c->irq = 14 + 0x20; + break; + case 1: + c->reg_base = 0x170; + c->irq = 15 + 0x20; + break; + default: + NOT_REACHED (); + } + lock_init (&c->lock); + c->expecting_interrupt = false; + sema_init (&c->completion_wait, 0); + + /* Initialize devices. */ + for (dev_no = 0; dev_no < 2; dev_no++) + { + struct ata_disk *d = &c->devices[dev_no]; + snprintf (d->name, sizeof d->name, + "hd%c", 'a' + chan_no * 2 + dev_no); + d->channel = c; + d->dev_no = dev_no; + d->is_ata = false; + } + + /* Register interrupt handler. */ + intr_register_ext (c->irq, interrupt_handler, c->name); + + /* Reset hardware. */ + reset_channel (c); + + /* Distinguish ATA hard disks from other devices. */ + if (check_device_type (&c->devices[0])) + check_device_type (&c->devices[1]); + + /* Read hard disk identity information. */ + for (dev_no = 0; dev_no < 2; dev_no++) + if (c->devices[dev_no].is_ata) + identify_ata_device (&c->devices[dev_no]); + } +} + +/* Disk detection and identification. */ + +static char *descramble_ata_string (char *, int size); + +/* Resets an ATA channel and waits for any devices present on it + to finish the reset. */ +static void +reset_channel (struct channel *c) +{ + bool present[2]; + int dev_no; + + /* The ATA reset sequence depends on which devices are present, + so we start by detecting device presence. */ + for (dev_no = 0; dev_no < 2; dev_no++) + { + struct ata_disk *d = &c->devices[dev_no]; + + select_device (d); + + outb (reg_nsect (c), 0x55); + outb (reg_lbal (c), 0xaa); + + outb (reg_nsect (c), 0xaa); + outb (reg_lbal (c), 0x55); + + outb (reg_nsect (c), 0x55); + outb (reg_lbal (c), 0xaa); + + present[dev_no] = (inb (reg_nsect (c)) == 0x55 + && inb (reg_lbal (c)) == 0xaa); + } + + /* Issue soft reset sequence, which selects device 0 as a side effect. + Also enable interrupts. */ + outb (reg_ctl (c), 0); + timer_usleep (10); + outb (reg_ctl (c), CTL_SRST); + timer_usleep (10); + outb (reg_ctl (c), 0); + + timer_msleep (150); + + /* Wait for device 0 to clear BSY. */ + if (present[0]) + { + select_device (&c->devices[0]); + wait_while_busy (&c->devices[0]); + } + + /* Wait for device 1 to clear BSY. */ + if (present[1]) + { + int i; + + select_device (&c->devices[1]); + for (i = 0; i < 3000; i++) + { + if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1) + break; + timer_msleep (10); + } + wait_while_busy (&c->devices[1]); + } +} + +/* Checks whether device D is an ATA disk and sets D's is_ata + member appropriately. If D is device 0 (master), returns true + if it's possible that a slave (device 1) exists on this + channel. If D is device 1 (slave), the return value is not + meaningful. */ +static bool +check_device_type (struct ata_disk *d) +{ + struct channel *c = d->channel; + uint8_t error, lbam, lbah, status; + + select_device (d); + + error = inb (reg_error (c)); + lbam = inb (reg_lbam (c)); + lbah = inb (reg_lbah (c)); + status = inb (reg_status (c)); + + if ((error != 1 && (error != 0x81 || d->dev_no == 1)) + || (status & STA_DRDY) == 0 + || (status & STA_BSY) != 0) + { + d->is_ata = false; + return error != 0x81; + } + else + { + d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3); + return true; + } +} + +/* Sends an IDENTIFY DEVICE command to disk D and reads the + response. Registers the disk with the block device + layer. */ +static void +identify_ata_device (struct ata_disk *d) +{ + struct channel *c = d->channel; + char id[BLOCK_SECTOR_SIZE]; + block_sector_t capacity; + char *model, *serial; + char extra_info[128]; + struct block *block; + + ASSERT (d->is_ata); + + /* Send the IDENTIFY DEVICE command, wait for an interrupt + indicating the device's response is ready, and read the data + into our buffer. */ + select_device_wait (d); + issue_pio_command (c, CMD_IDENTIFY_DEVICE); + sema_down (&c->completion_wait); + if (!wait_while_busy (d)) + { + d->is_ata = false; + return; + } + input_sector (c, id); + + /* Calculate capacity. + Read model name and serial number. */ + capacity = *(uint32_t *) &id[60 * 2]; + model = descramble_ata_string (&id[10 * 2], 20); + serial = descramble_ata_string (&id[27 * 2], 40); + snprintf (extra_info, sizeof extra_info, + "model \"%s\", serial \"%s\"", model, serial); + + /* Disable access to IDE disks over 1 GB, which are likely + physical IDE disks rather than virtual ones. If we don't + allow access to those, we're less likely to scribble on + someone's important data. You can disable this check by + hand if you really want to do so. */ + if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE) + { + printf ("%s: ignoring ", d->name); + print_human_readable_size (capacity * 512); + printf ("disk for safety\n"); + d->is_ata = false; + return; + } + + /* Register. */ + block = block_register (d->name, BLOCK_RAW, extra_info, capacity, + &ide_operations, d); + partition_scan (block); +} + +/* Translates STRING, which consists of SIZE bytes in a funky + format, into a null-terminated string in-place. Drops + trailing whitespace and null bytes. Returns STRING. */ +static char * +descramble_ata_string (char *string, int size) +{ + int i; + + /* Swap all pairs of bytes. */ + for (i = 0; i + 1 < size; i += 2) + { + char tmp = string[i]; + string[i] = string[i + 1]; + string[i + 1] = tmp; + } + + /* Find the last non-white, non-null character. */ + for (size--; size > 0; size--) + { + int c = string[size - 1]; + if (c != '\0' && !isspace (c)) + break; + } + string[size] = '\0'; + + return string; +} + +/* Reads sector SEC_NO from disk D into BUFFER, which must have + room for BLOCK_SECTOR_SIZE bytes. + Internally synchronizes accesses to disks, so external + per-disk locking is unneeded. */ +static void +ide_read (void *d_, block_sector_t sec_no, void *buffer) +{ + struct ata_disk *d = d_; + struct channel *c = d->channel; + lock_acquire (&c->lock); + select_sector (d, sec_no); + issue_pio_command (c, CMD_READ_SECTOR_RETRY); + sema_down (&c->completion_wait); + if (!wait_while_busy (d)) + PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no); + input_sector (c, buffer); + lock_release (&c->lock); +} + +/* Write sector SEC_NO to disk D from BUFFER, which must contain + BLOCK_SECTOR_SIZE bytes. Returns after the disk has + acknowledged receiving the data. + Internally synchronizes accesses to disks, so external + per-disk locking is unneeded. */ +static void +ide_write (void *d_, block_sector_t sec_no, const void *buffer) +{ + struct ata_disk *d = d_; + struct channel *c = d->channel; + lock_acquire (&c->lock); + select_sector (d, sec_no); + issue_pio_command (c, CMD_WRITE_SECTOR_RETRY); + if (!wait_while_busy (d)) + PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no); + output_sector (c, buffer); + sema_down (&c->completion_wait); + lock_release (&c->lock); +} + +static struct block_operations ide_operations = + { + ide_read, + ide_write + }; + +/* Selects device D, waiting for it to become ready, and then + writes SEC_NO to the disk's sector selection registers. (We + use LBA mode.) */ +static void +select_sector (struct ata_disk *d, block_sector_t sec_no) +{ + struct channel *c = d->channel; + + ASSERT (sec_no < (1UL << 28)); + + select_device_wait (d); + outb (reg_nsect (c), 1); + outb (reg_lbal (c), sec_no); + outb (reg_lbam (c), sec_no >> 8); + outb (reg_lbah (c), (sec_no >> 16)); + outb (reg_device (c), + DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24)); +} + +/* Writes COMMAND to channel C and prepares for receiving a + completion interrupt. */ +static void +issue_pio_command (struct channel *c, uint8_t command) +{ + /* Interrupts must be enabled or our semaphore will never be + up'd by the completion handler. */ + ASSERT (intr_get_level () == INTR_ON); + + c->expecting_interrupt = true; + outb (reg_command (c), command); +} + +/* Reads a sector from channel C's data register in PIO mode into + SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */ +static void +input_sector (struct channel *c, void *sector) +{ + insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2); +} + +/* Writes SECTOR to channel C's data register in PIO mode. + SECTOR must contain BLOCK_SECTOR_SIZE bytes. */ +static void +output_sector (struct channel *c, const void *sector) +{ + outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2); +} + +/* Low-level ATA primitives. */ + +/* Wait up to 10 seconds for the controller to become idle, that + is, for the BSY and DRQ bits to clear in the status register. + + As a side effect, reading the status register clears any + pending interrupt. */ +static void +wait_until_idle (const struct ata_disk *d) +{ + int i; + + for (i = 0; i < 1000; i++) + { + if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0) + return; + timer_usleep (10); + } + + printf ("%s: idle timeout\n", d->name); +} + +/* Wait up to 30 seconds for disk D to clear BSY, + and then return the status of the DRQ bit. + The ATA standards say that a disk may take as long as that to + complete its reset. */ +static bool +wait_while_busy (const struct ata_disk *d) +{ + struct channel *c = d->channel; + int i; + + for (i = 0; i < 3000; i++) + { + if (i == 700) + printf ("%s: busy, waiting...", d->name); + if (!(inb (reg_alt_status (c)) & STA_BSY)) + { + if (i >= 700) + printf ("ok\n"); + return (inb (reg_alt_status (c)) & STA_DRQ) != 0; + } + timer_msleep (10); + } + + printf ("failed\n"); + return false; +} + +/* Program D's channel so that D is now the selected disk. */ +static void +select_device (const struct ata_disk *d) +{ + struct channel *c = d->channel; + uint8_t dev = DEV_MBS; + if (d->dev_no == 1) + dev |= DEV_DEV; + outb (reg_device (c), dev); + inb (reg_alt_status (c)); + timer_nsleep (400); +} + +/* Select disk D in its channel, as select_device(), but wait for + the channel to become idle before and after. */ +static void +select_device_wait (const struct ata_disk *d) +{ + wait_until_idle (d); + select_device (d); + wait_until_idle (d); +} + +/* ATA interrupt handler. */ +static void +interrupt_handler (struct intr_frame *f) +{ + struct channel *c; + + for (c = channels; c < channels + CHANNEL_CNT; c++) + if (f->vec_no == c->irq) + { + if (c->expecting_interrupt) + { + inb (reg_status (c)); /* Acknowledge interrupt. */ + sema_up (&c->completion_wait); /* Wake up waiter. */ + } + else + printf ("%s: unexpected interrupt\n", c->name); + return; + } + + NOT_REACHED (); +} + + diff --git a/pintos-env/pintos/devices/ide.h b/pintos-env/pintos/devices/ide.h new file mode 100755 index 0000000..b35da5e --- /dev/null +++ b/pintos-env/pintos/devices/ide.h @@ -0,0 +1,6 @@ +#ifndef DEVICES_IDE_H +#define DEVICES_IDE_H + +void ide_init (void); + +#endif /* devices/ide.h */ diff --git a/pintos-env/pintos/devices/input.c b/pintos-env/pintos/devices/input.c new file mode 100755 index 0000000..4a12160 --- /dev/null +++ b/pintos-env/pintos/devices/input.c @@ -0,0 +1,52 @@ +#include "devices/input.h" +#include +#include "devices/intq.h" +#include "devices/serial.h" + +/* Stores keys from the keyboard and serial port. */ +static struct intq buffer; + +/* Initializes the input buffer. */ +void +input_init (void) +{ + intq_init (&buffer); +} + +/* Adds a key to the input buffer. + Interrupts must be off and the buffer must not be full. */ +void +input_putc (uint8_t key) +{ + ASSERT (intr_get_level () == INTR_OFF); + ASSERT (!intq_full (&buffer)); + + intq_putc (&buffer, key); + serial_notify (); +} + +/* Retrieves a key from the input buffer. + If the buffer is empty, waits for a key to be pressed. */ +uint8_t +input_getc (void) +{ + enum intr_level old_level; + uint8_t key; + + old_level = intr_disable (); + key = intq_getc (&buffer); + serial_notify (); + intr_set_level (old_level); + + return key; +} + +/* Returns true if the input buffer is full, + false otherwise. + Interrupts must be off. */ +bool +input_full (void) +{ + ASSERT (intr_get_level () == INTR_OFF); + return intq_full (&buffer); +} diff --git a/pintos-env/pintos/devices/input.h b/pintos-env/pintos/devices/input.h new file mode 100755 index 0000000..a2f50e9 --- /dev/null +++ b/pintos-env/pintos/devices/input.h @@ -0,0 +1,12 @@ +#ifndef DEVICES_INPUT_H +#define DEVICES_INPUT_H + +#include +#include + +void input_init (void); +void input_putc (uint8_t); +uint8_t input_getc (void); +bool input_full (void); + +#endif /* devices/input.h */ diff --git a/pintos-env/pintos/devices/intq.c b/pintos-env/pintos/devices/intq.c new file mode 100755 index 0000000..40b23ae --- /dev/null +++ b/pintos-env/pintos/devices/intq.c @@ -0,0 +1,114 @@ +#include "devices/intq.h" +#include +#include "threads/thread.h" + +static int next (int pos); +static void wait (struct intq *q, struct thread **waiter); +static void signal (struct intq *q, struct thread **waiter); + +/* Initializes interrupt queue Q. */ +void +intq_init (struct intq *q) +{ + lock_init (&q->lock); + q->not_full = q->not_empty = NULL; + q->head = q->tail = 0; +} + +/* Returns true if Q is empty, false otherwise. */ +bool +intq_empty (const struct intq *q) +{ + ASSERT (intr_get_level () == INTR_OFF); + return q->head == q->tail; +} + +/* Returns true if Q is full, false otherwise. */ +bool +intq_full (const struct intq *q) +{ + ASSERT (intr_get_level () == INTR_OFF); + return next (q->head) == q->tail; +} + +/* Removes a byte from Q and returns it. + If Q is empty, sleeps until a byte is added. + When called from an interrupt handler, Q must not be empty. */ +uint8_t +intq_getc (struct intq *q) +{ + uint8_t byte; + + ASSERT (intr_get_level () == INTR_OFF); + while (intq_empty (q)) + { + ASSERT (!intr_context ()); + lock_acquire (&q->lock); + wait (q, &q->not_empty); + lock_release (&q->lock); + } + + byte = q->buf[q->tail]; + q->tail = next (q->tail); + signal (q, &q->not_full); + return byte; +} + +/* Adds BYTE to the end of Q. + If Q is full, sleeps until a byte is removed. + When called from an interrupt handler, Q must not be full. */ +void +intq_putc (struct intq *q, uint8_t byte) +{ + ASSERT (intr_get_level () == INTR_OFF); + while (intq_full (q)) + { + ASSERT (!intr_context ()); + lock_acquire (&q->lock); + wait (q, &q->not_full); + lock_release (&q->lock); + } + + q->buf[q->head] = byte; + q->head = next (q->head); + signal (q, &q->not_empty); +} + +/* Returns the position after POS within an intq. */ +static int +next (int pos) +{ + return (pos + 1) % INTQ_BUFSIZE; +} + +/* WAITER must be the address of Q's not_empty or not_full + member. Waits until the given condition is true. */ +static void +wait (struct intq *q UNUSED, struct thread **waiter) +{ + ASSERT (!intr_context ()); + ASSERT (intr_get_level () == INTR_OFF); + ASSERT ((waiter == &q->not_empty && intq_empty (q)) + || (waiter == &q->not_full && intq_full (q))); + + *waiter = thread_current (); + thread_block (); +} + +/* WAITER must be the address of Q's not_empty or not_full + member, and the associated condition must be true. If a + thread is waiting for the condition, wakes it up and resets + the waiting thread. */ +static void +signal (struct intq *q UNUSED, struct thread **waiter) +{ + ASSERT (intr_get_level () == INTR_OFF); + ASSERT ((waiter == &q->not_empty && !intq_empty (q)) + || (waiter == &q->not_full && !intq_full (q))); + + if (*waiter != NULL) + { + thread_unblock (*waiter); + *waiter = NULL; + } +} diff --git a/pintos-env/pintos/devices/intq.h b/pintos-env/pintos/devices/intq.h new file mode 100755 index 0000000..2312b12 --- /dev/null +++ b/pintos-env/pintos/devices/intq.h @@ -0,0 +1,43 @@ +#ifndef DEVICES_INTQ_H +#define DEVICES_INTQ_H + +#include "threads/interrupt.h" +#include "threads/synch.h" + +/* An "interrupt queue", a circular buffer shared between + kernel threads and external interrupt handlers. + + Interrupt queue functions can be called from kernel threads or + from external interrupt handlers. Except for intq_init(), + interrupts must be off in either case. + + The interrupt queue has the structure of a "monitor". Locks + and condition variables from threads/synch.h cannot be used in + this case, as they normally would, because they can only + protect kernel threads from one another, not from interrupt + handlers. */ + +/* Queue buffer size, in bytes. */ +#define INTQ_BUFSIZE 64 + +/* A circular queue of bytes. */ +struct intq + { + /* Waiting threads. */ + struct lock lock; /* Only one thread may wait at once. */ + struct thread *not_full; /* Thread waiting for not-full condition. */ + struct thread *not_empty; /* Thread waiting for not-empty condition. */ + + /* Queue. */ + uint8_t buf[INTQ_BUFSIZE]; /* Buffer. */ + int head; /* New data is written here. */ + int tail; /* Old data is read here. */ + }; + +void intq_init (struct intq *); +bool intq_empty (const struct intq *); +bool intq_full (const struct intq *); +uint8_t intq_getc (struct intq *); +void intq_putc (struct intq *, uint8_t); + +#endif /* devices/intq.h */ diff --git a/pintos-env/pintos/devices/kbd.c b/pintos-env/pintos/devices/kbd.c new file mode 100755 index 0000000..fcc82be --- /dev/null +++ b/pintos-env/pintos/devices/kbd.c @@ -0,0 +1,213 @@ +#include "devices/kbd.h" +#include +#include +#include +#include +#include "devices/input.h" +#include "devices/shutdown.h" +#include "threads/interrupt.h" +#include "threads/io.h" + +/* Keyboard data register port. */ +#define DATA_REG 0x60 + +/* Current state of shift keys. + True if depressed, false otherwise. */ +static bool left_shift, right_shift; /* Left and right Shift keys. */ +static bool left_alt, right_alt; /* Left and right Alt keys. */ +static bool left_ctrl, right_ctrl; /* Left and right Ctl keys. */ + +/* Status of Caps Lock. + True when on, false when off. */ +static bool caps_lock; + +/* Number of keys pressed. */ +static int64_t key_cnt; + +static intr_handler_func keyboard_interrupt; + +/* Initializes the keyboard. */ +void +kbd_init (void) +{ + intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard"); +} + +/* Prints keyboard statistics. */ +void +kbd_print_stats (void) +{ + printf ("Keyboard: %lld keys pressed\n", key_cnt); +} + +/* Maps a set of contiguous scancodes into characters. */ +struct keymap + { + uint8_t first_scancode; /* First scancode. */ + const char *chars; /* chars[0] has scancode first_scancode, + chars[1] has scancode first_scancode + 1, + and so on to the end of the string. */ + }; + +/* Keys that produce the same characters regardless of whether + the Shift keys are down. Case of letters is an exception + that we handle elsewhere. */ +static const struct keymap invariant_keymap[] = + { + {0x01, "\033"}, /* Escape. */ + {0x0e, "\b"}, + {0x0f, "\tQWERTYUIOP"}, + {0x1c, "\r"}, + {0x1e, "ASDFGHJKL"}, + {0x2c, "ZXCVBNM"}, + {0x37, "*"}, + {0x39, " "}, + {0x53, "\177"}, /* Delete. */ + {0, NULL}, + }; + +/* Characters for keys pressed without Shift, for those keys + where it matters. */ +static const struct keymap unshifted_keymap[] = + { + {0x02, "1234567890-="}, + {0x1a, "[]"}, + {0x27, ";'`"}, + {0x2b, "\\"}, + {0x33, ",./"}, + {0, NULL}, + }; + +/* Characters for keys pressed with Shift, for those keys where + it matters. */ +static const struct keymap shifted_keymap[] = + { + {0x02, "!@#$%^&*()_+"}, + {0x1a, "{}"}, + {0x27, ":\"~"}, + {0x2b, "|"}, + {0x33, "<>?"}, + {0, NULL}, + }; + +static bool map_key (const struct keymap[], unsigned scancode, uint8_t *); + +static void +keyboard_interrupt (struct intr_frame *args UNUSED) +{ + /* Status of shift keys. */ + bool shift = left_shift || right_shift; + bool alt = left_alt || right_alt; + bool ctrl = left_ctrl || right_ctrl; + + /* Keyboard scancode. */ + unsigned code; + + /* False if key pressed, true if key released. */ + bool release; + + /* Character that corresponds to `code'. */ + uint8_t c; + + /* Read scancode, including second byte if prefix code. */ + code = inb (DATA_REG); + if (code == 0xe0) + code = (code << 8) | inb (DATA_REG); + + /* Bit 0x80 distinguishes key press from key release + (even if there's a prefix). */ + release = (code & 0x80) != 0; + code &= ~0x80u; + + /* Interpret key. */ + if (code == 0x3a) + { + /* Caps Lock. */ + if (!release) + caps_lock = !caps_lock; + } + else if (map_key (invariant_keymap, code, &c) + || (!shift && map_key (unshifted_keymap, code, &c)) + || (shift && map_key (shifted_keymap, code, &c))) + { + /* Ordinary character. */ + if (!release) + { + /* Reboot if Ctrl+Alt+Del pressed. */ + if (c == 0177 && ctrl && alt) + shutdown_reboot (); + + /* Handle Ctrl, Shift. + Note that Ctrl overrides Shift. */ + if (ctrl && c >= 0x40 && c < 0x60) + { + /* A is 0x41, Ctrl+A is 0x01, etc. */ + c -= 0x40; + } + else if (shift == caps_lock) + c = tolower (c); + + /* Handle Alt by setting the high bit. + This 0x80 is unrelated to the one used to + distinguish key press from key release. */ + if (alt) + c += 0x80; + + /* Append to keyboard buffer. */ + if (!input_full ()) + { + key_cnt++; + input_putc (c); + } + } + } + else + { + /* Maps a keycode into a shift state variable. */ + struct shift_key + { + unsigned scancode; + bool *state_var; + }; + + /* Table of shift keys. */ + static const struct shift_key shift_keys[] = + { + { 0x2a, &left_shift}, + { 0x36, &right_shift}, + { 0x38, &left_alt}, + {0xe038, &right_alt}, + { 0x1d, &left_ctrl}, + {0xe01d, &right_ctrl}, + {0, NULL}, + }; + + const struct shift_key *key; + + /* Scan the table. */ + for (key = shift_keys; key->scancode != 0; key++) + if (key->scancode == code) + { + *key->state_var = !release; + break; + } + } +} + +/* Scans the array of keymaps K for SCANCODE. + If found, sets *C to the corresponding character and returns + true. + If not found, returns false and C is ignored. */ +static bool +map_key (const struct keymap k[], unsigned scancode, uint8_t *c) +{ + for (; k->first_scancode != 0; k++) + if (scancode >= k->first_scancode + && scancode < k->first_scancode + strlen (k->chars)) + { + *c = k->chars[scancode - k->first_scancode]; + return true; + } + + return false; +} diff --git a/pintos-env/pintos/devices/kbd.h b/pintos-env/pintos/devices/kbd.h new file mode 100755 index 0000000..ed9c06b --- /dev/null +++ b/pintos-env/pintos/devices/kbd.h @@ -0,0 +1,9 @@ +#ifndef DEVICES_KBD_H +#define DEVICES_KBD_H + +#include + +void kbd_init (void); +void kbd_print_stats (void); + +#endif /* devices/kbd.h */ diff --git a/pintos-env/pintos/devices/partition.c b/pintos-env/pintos/devices/partition.c new file mode 100755 index 0000000..7e97332 --- /dev/null +++ b/pintos-env/pintos/devices/partition.c @@ -0,0 +1,324 @@ +#include "devices/partition.h" +#include +#include +#include +#include +#include "devices/block.h" +#include "threads/malloc.h" + +/* A partition of a block device. */ +struct partition + { + struct block *block; /* Underlying block device. */ + block_sector_t start; /* First sector within device. */ + }; + +static struct block_operations partition_operations; + +static void read_partition_table (struct block *, block_sector_t sector, + block_sector_t primary_extended_sector, + int *part_nr); +static void found_partition (struct block *, uint8_t type, + block_sector_t start, block_sector_t size, + int part_nr); +static const char *partition_type_name (uint8_t); + +/* Scans BLOCK for partitions of interest to Pintos. */ +void +partition_scan (struct block *block) +{ + int part_nr = 0; + read_partition_table (block, 0, 0, &part_nr); + if (part_nr == 0) + printf ("%s: Device contains no partitions\n", block_name (block)); +} + +/* Reads the partition table in the given SECTOR of BLOCK and + scans it for partitions of interest to Pintos. + + If SECTOR is 0, so that this is the top-level partition table + on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful; + otherwise, it should designate the sector of the top-level + extended partition table that was traversed to arrive at + SECTOR, for use in finding logical partitions (see the large + comment below). + + PART_NR points to the number of non-empty primary or logical + partitions already encountered on BLOCK. It is incremented as + partitions are found. */ +static void +read_partition_table (struct block *block, block_sector_t sector, + block_sector_t primary_extended_sector, + int *part_nr) +{ + /* Format of a partition table entry. See [Partitions]. */ + struct partition_table_entry + { + uint8_t bootable; /* 0x00=not bootable, 0x80=bootable. */ + uint8_t start_chs[3]; /* Encoded starting cylinder, head, sector. */ + uint8_t type; /* Partition type (see partition_type_name). */ + uint8_t end_chs[3]; /* Encoded ending cylinder, head, sector. */ + uint32_t offset; /* Start sector offset from partition table. */ + uint32_t size; /* Number of sectors. */ + } + PACKED; + + /* Partition table sector. */ + struct partition_table + { + uint8_t loader[446]; /* Loader, in top-level partition table. */ + struct partition_table_entry partitions[4]; /* Table entries. */ + uint16_t signature; /* Should be 0xaa55. */ + } + PACKED; + + struct partition_table *pt; + size_t i; + + /* Check SECTOR validity. */ + if (sector >= block_size (block)) + { + printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n", + block_name (block), sector); + return; + } + + /* Read sector. */ + ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE); + pt = malloc (sizeof *pt); + if (pt == NULL) + PANIC ("Failed to allocate memory for partition table."); + block_read (block, 0, pt); + + /* Check signature. */ + if (pt->signature != 0xaa55) + { + if (primary_extended_sector == 0) + printf ("%s: Invalid partition table signature\n", block_name (block)); + else + printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n", + block_name (block), sector); + free (pt); + return; + } + + /* Parse partitions. */ + for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++) + { + struct partition_table_entry *e = &pt->partitions[i]; + + if (e->size == 0 || e->type == 0) + { + /* Ignore empty partition. */ + } + else if (e->type == 0x05 /* Extended partition. */ + || e->type == 0x0f /* Windows 98 extended partition. */ + || e->type == 0x85 /* Linux extended partition. */ + || e->type == 0xc5) /* DR-DOS extended partition. */ + { + printf ("%s: Extended partition in sector %"PRDSNu"\n", + block_name (block), sector); + + /* The interpretation of the offset field for extended + partitions is bizarre. When the extended partition + table entry is in the master boot record, that is, + the device's primary partition table in sector 0, then + the offset is an absolute sector number. Otherwise, + no matter how deep the partition table we're reading + is nested, the offset is relative to the start of + the extended partition that the MBR points to. */ + if (sector == 0) + read_partition_table (block, e->offset, e->offset, part_nr); + else + read_partition_table (block, e->offset + primary_extended_sector, + primary_extended_sector, part_nr); + } + else + { + ++*part_nr; + + found_partition (block, e->type, e->offset + sector, + e->size, *part_nr); + } + } + + free (pt); +} + +/* We have found a primary or logical partition of the given TYPE + on BLOCK, starting at sector START and continuing for SIZE + sectors, which we are giving the partition number PART_NR. + Check whether this is a partition of interest to Pintos, and + if so then add it to the proper element of partitions[]. */ +static void +found_partition (struct block *block, uint8_t part_type, + block_sector_t start, block_sector_t size, + int part_nr) +{ + if (start >= block_size (block)) + printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n", + block_name (block), part_nr, start); + else if (start + size < start || start + size > block_size (block)) + printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n", + block_name (block), part_nr, start + size, block_size (block)); + else + { + enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL + : part_type == 0x21 ? BLOCK_FILESYS + : part_type == 0x22 ? BLOCK_SCRATCH + : part_type == 0x23 ? BLOCK_SWAP + : BLOCK_FOREIGN); + struct partition *p; + char extra_info[128]; + char name[16]; + + p = malloc (sizeof *p); + if (p == NULL) + PANIC ("Failed to allocate memory for partition descriptor"); + p->block = block; + p->start = start; + + snprintf (name, sizeof name, "%s%d", block_name (block), part_nr); + snprintf (extra_info, sizeof extra_info, "%s (%02x)", + partition_type_name (part_type), part_type); + block_register (name, type, extra_info, size, &partition_operations, p); + } +} + +/* Returns a human-readable name for the given partition TYPE. */ +static const char * +partition_type_name (uint8_t type) +{ + /* Name of each known type of partition. + From util-linux-2.12r/fdisk/i386_sys_types.c. + This initializer makes use of a C99 feature that allows + array elements to be initialized by index. */ + static const char *type_names[256] = + { + [0x00] = "Empty", + [0x01] = "FAT12", + [0x02] = "XENIX root", + [0x03] = "XENIX usr", + [0x04] = "FAT16 <32M", + [0x05] = "Extended", + [0x06] = "FAT16", + [0x07] = "HPFS/NTFS", + [0x08] = "AIX", + [0x09] = "AIX bootable", + [0x0a] = "OS/2 Boot Manager", + [0x0b] = "W95 FAT32", + [0x0c] = "W95 FAT32 (LBA)", + [0x0e] = "W95 FAT16 (LBA)", + [0x0f] = "W95 Ext'd (LBA)", + [0x10] = "OPUS", + [0x11] = "Hidden FAT12", + [0x12] = "Compaq diagnostics", + [0x14] = "Hidden FAT16 <32M", + [0x16] = "Hidden FAT16", + [0x17] = "Hidden HPFS/NTFS", + [0x18] = "AST SmartSleep", + [0x1b] = "Hidden W95 FAT32", + [0x1c] = "Hidden W95 FAT32 (LBA)", + [0x1e] = "Hidden W95 FAT16 (LBA)", + [0x20] = "Pintos OS kernel", + [0x21] = "Pintos file system", + [0x22] = "Pintos scratch", + [0x23] = "Pintos swap", + [0x24] = "NEC DOS", + [0x39] = "Plan 9", + [0x3c] = "PartitionMagic recovery", + [0x40] = "Venix 80286", + [0x41] = "PPC PReP Boot", + [0x42] = "SFS", + [0x4d] = "QNX4.x", + [0x4e] = "QNX4.x 2nd part", + [0x4f] = "QNX4.x 3rd part", + [0x50] = "OnTrack DM", + [0x51] = "OnTrack DM6 Aux1", + [0x52] = "CP/M", + [0x53] = "OnTrack DM6 Aux3", + [0x54] = "OnTrackDM6", + [0x55] = "EZ-Drive", + [0x56] = "Golden Bow", + [0x5c] = "Priam Edisk", + [0x61] = "SpeedStor", + [0x63] = "GNU HURD or SysV", + [0x64] = "Novell Netware 286", + [0x65] = "Novell Netware 386", + [0x70] = "DiskSecure Multi-Boot", + [0x75] = "PC/IX", + [0x80] = "Old Minix", + [0x81] = "Minix / old Linux", + [0x82] = "Linux swap / Solaris", + [0x83] = "Linux", + [0x84] = "OS/2 hidden C: drive", + [0x85] = "Linux extended", + [0x86] = "NTFS volume set", + [0x87] = "NTFS volume set", + [0x88] = "Linux plaintext", + [0x8e] = "Linux LVM", + [0x93] = "Amoeba", + [0x94] = "Amoeba BBT", + [0x9f] = "BSD/OS", + [0xa0] = "IBM Thinkpad hibernation", + [0xa5] = "FreeBSD", + [0xa6] = "OpenBSD", + [0xa7] = "NeXTSTEP", + [0xa8] = "Darwin UFS", + [0xa9] = "NetBSD", + [0xab] = "Darwin boot", + [0xb7] = "BSDI fs", + [0xb8] = "BSDI swap", + [0xbb] = "Boot Wizard hidden", + [0xbe] = "Solaris boot", + [0xbf] = "Solaris", + [0xc1] = "DRDOS/sec (FAT-12)", + [0xc4] = "DRDOS/sec (FAT-16 < 32M)", + [0xc6] = "DRDOS/sec (FAT-16)", + [0xc7] = "Syrinx", + [0xda] = "Non-FS data", + [0xdb] = "CP/M / CTOS / ...", + [0xde] = "Dell Utility", + [0xdf] = "BootIt", + [0xe1] = "DOS access", + [0xe3] = "DOS R/O", + [0xe4] = "SpeedStor", + [0xeb] = "BeOS fs", + [0xee] = "EFI GPT", + [0xef] = "EFI (FAT-12/16/32)", + [0xf0] = "Linux/PA-RISC boot", + [0xf1] = "SpeedStor", + [0xf4] = "SpeedStor", + [0xf2] = "DOS secondary", + [0xfd] = "Linux raid autodetect", + [0xfe] = "LANstep", + [0xff] = "BBT", + }; + + return type_names[type] != NULL ? type_names[type] : "Unknown"; +} + +/* Reads sector SECTOR from partition P into BUFFER, which must + have room for BLOCK_SECTOR_SIZE bytes. */ +static void +partition_read (void *p_, block_sector_t sector, void *buffer) +{ + struct partition *p = p_; + block_read (p->block, p->start + sector, buffer); +} + +/* Write sector SECTOR to partition P from BUFFER, which must + contain BLOCK_SECTOR_SIZE bytes. Returns after the block has + acknowledged receiving the data. */ +static void +partition_write (void *p_, block_sector_t sector, const void *buffer) +{ + struct partition *p = p_; + block_write (p->block, p->start + sector, buffer); +} + +static struct block_operations partition_operations = + { + partition_read, + partition_write + }; diff --git a/pintos-env/pintos/devices/partition.h b/pintos-env/pintos/devices/partition.h new file mode 100755 index 0000000..47fea4d --- /dev/null +++ b/pintos-env/pintos/devices/partition.h @@ -0,0 +1,8 @@ +#ifndef DEVICES_PARTITION_H +#define DEVICES_PARTITION_H + +struct block; + +void partition_scan (struct block *); + +#endif /* devices/partition.h */ diff --git a/pintos-env/pintos/devices/pit.c b/pintos-env/pintos/devices/pit.c new file mode 100755 index 0000000..bfb1889 --- /dev/null +++ b/pintos-env/pintos/devices/pit.c @@ -0,0 +1,83 @@ +#include "devices/pit.h" +#include +#include +#include "threads/interrupt.h" +#include "threads/io.h" + +/* Interface to 8254 Programmable Interrupt Timer (PIT). + Refer to [8254] for details. */ + +/* 8254 registers. */ +#define PIT_PORT_CONTROL 0x43 /* Control port. */ +#define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL)) /* Counter port. */ + +/* PIT cycles per second. */ +#define PIT_HZ 1193180 + +/* Configure the given CHANNEL in the PIT. In a PC, the PIT's + three output channels are hooked up like this: + + - Channel 0 is connected to interrupt line 0, so that it can + be used as a periodic timer interrupt, as implemented in + Pintos in devices/timer.c. + + - Channel 1 is used for dynamic RAM refresh (in older PCs). + No good can come of messing with this. + + - Channel 2 is connected to the PC speaker, so that it can + be used to play a tone, as implemented in Pintos in + devices/speaker.c. + + MODE specifies the form of output: + + - Mode 2 is a periodic pulse: the channel's output is 1 for + most of the period, but drops to 0 briefly toward the end + of the period. This is useful for hooking up to an + interrupt controller to generate a periodic interrupt. + + - Mode 3 is a square wave: for the first half of the period + it is 1, for the second half it is 0. This is useful for + generating a tone on a speaker. + + - Other modes are less useful. + + FREQUENCY is the number of periods per second, in Hz. */ +void +pit_configure_channel (int channel, int mode, int frequency) +{ + uint16_t count; + enum intr_level old_level; + + ASSERT (channel == 0 || channel == 2); + ASSERT (mode == 2 || mode == 3); + + /* Convert FREQUENCY to a PIT counter value. The PIT has a + clock that runs at PIT_HZ cycles per second. We must + translate FREQUENCY into a number of these cycles. */ + if (frequency < 19) + { + /* Frequency is too low: the quotient would overflow the + 16-bit counter. Force it to 0, which the PIT treats as + 65536, the highest possible count. This yields a 18.2 + Hz timer, approximately. */ + count = 0; + } + else if (frequency > PIT_HZ) + { + /* Frequency is too high: the quotient would underflow to + 0, which the PIT would interpret as 65536. A count of 1 + is illegal in mode 2, so we force it to 2, which yields + a 596.590 kHz timer, approximately. (This timer rate is + probably too fast to be useful anyhow.) */ + count = 2; + } + else + count = (PIT_HZ + frequency / 2) / frequency; + + /* Configure the PIT mode and load its counters. */ + old_level = intr_disable (); + outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1)); + outb (PIT_PORT_COUNTER (channel), count); + outb (PIT_PORT_COUNTER (channel), count >> 8); + intr_set_level (old_level); +} diff --git a/pintos-env/pintos/devices/pit.h b/pintos-env/pintos/devices/pit.h new file mode 100755 index 0000000..dff36ae --- /dev/null +++ b/pintos-env/pintos/devices/pit.h @@ -0,0 +1,8 @@ +#ifndef DEVICES_PIT_H +#define DEVICES_PIT_H + +#include + +void pit_configure_channel (int channel, int mode, int frequency); + +#endif /* devices/pit.h */ diff --git a/pintos-env/pintos/devices/rtc.c b/pintos-env/pintos/devices/rtc.c new file mode 100755 index 0000000..d99eb46 --- /dev/null +++ b/pintos-env/pintos/devices/rtc.c @@ -0,0 +1,112 @@ +#include "devices/rtc.h" +#include +#include "threads/io.h" + +/* This code is an interface to the MC146818A-compatible real + time clock found on PC motherboards. See [MC146818A] for + hardware details. */ + +/* I/O register addresses. */ +#define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */ +#define CMOS_REG_IO 0x71 /* Contains the selected data byte. */ + +/* Indexes of CMOS registers with real-time clock functions. + Note that all of these registers are in BCD format, + so that 0x59 means 59, not 89. */ +#define RTC_REG_SEC 0 /* Second: 0x00...0x59. */ +#define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */ +#define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */ +#define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */ +#define RTC_REG_MON 8 /* Month: 0x01...0x12. */ +#define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */ + +/* Indexes of CMOS control registers. */ +#define RTC_REG_A 0x0a /* Register A: update-in-progress. */ +#define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */ +#define RTC_REG_C 0x0c /* Register C: pending interrupts. */ +#define RTC_REG_D 0x0d /* Register D: valid time? */ + +/* Register A. */ +#define RTCSA_UIP 0x80 /* Set while time update in progress. */ + +/* Register B. */ +#define RTCSB_SET 0x80 /* Disables update to let time be set. */ +#define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */ +#define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */ + +static int bcd_to_bin (uint8_t); +static uint8_t cmos_read (uint8_t index); + +/* Returns number of seconds since Unix epoch of January 1, + 1970. */ +time_t +rtc_get_time (void) +{ + static const int days_per_month[12] = + { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + int sec, min, hour, mday, mon, year; + time_t time; + int i; + + /* Get time components. + + We repeatedly read the time until it is stable from one read + to another, in case we start our initial read in the middle + of an update. This strategy is not recommended by the + MC146818A datasheet, but it is simpler than any of their + suggestions and, furthermore, it is also used by Linux. + + The MC146818A can be configured for BCD or binary format, + but for historical reasons everyone always uses BCD format + except on obscure non-PC platforms, so we don't bother + trying to detect the format in use. */ + do + { + sec = bcd_to_bin (cmos_read (RTC_REG_SEC)); + min = bcd_to_bin (cmos_read (RTC_REG_MIN)); + hour = bcd_to_bin (cmos_read (RTC_REG_HOUR)); + mday = bcd_to_bin (cmos_read (RTC_REG_MDAY)); + mon = bcd_to_bin (cmos_read (RTC_REG_MON)); + year = bcd_to_bin (cmos_read (RTC_REG_YEAR)); + } + while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC))); + + /* Translate years-since-1900 into years-since-1970. + If it's before the epoch, assume that it has passed 2000. + This will break at 2070, but that's long after our 31-bit + time_t breaks in 2038. */ + if (year < 70) + year += 100; + year -= 70; + + /* Break down all components into seconds. */ + time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60; + for (i = 1; i <= mon; i++) + time += days_per_month[i - 1] * 24 * 60 * 60; + if (mon > 2 && year % 4 == 0) + time += 24 * 60 * 60; + time += (mday - 1) * 24 * 60 * 60; + time += hour * 60 * 60; + time += min * 60; + time += sec; + + return time; +} + +/* Returns the integer value of the given BCD byte. */ +static int +bcd_to_bin (uint8_t x) +{ + return (x & 0x0f) + ((x >> 4) * 10); +} + +/* Reads a byte from the CMOS register with the given INDEX and + returns the byte read. */ +static uint8_t +cmos_read (uint8_t index) +{ + outb (CMOS_REG_SET, index); + return inb (CMOS_REG_IO); +} diff --git a/pintos-env/pintos/devices/rtc.h b/pintos-env/pintos/devices/rtc.h new file mode 100755 index 0000000..96a822f --- /dev/null +++ b/pintos-env/pintos/devices/rtc.h @@ -0,0 +1,8 @@ +#ifndef RTC_H +#define RTC_H + +typedef unsigned long time_t; + +time_t rtc_get_time (void); + +#endif diff --git a/pintos-env/pintos/devices/serial.c b/pintos-env/pintos/devices/serial.c new file mode 100755 index 0000000..df770a7 --- /dev/null +++ b/pintos-env/pintos/devices/serial.c @@ -0,0 +1,228 @@ +#include "devices/serial.h" +#include +#include "devices/input.h" +#include "devices/intq.h" +#include "devices/timer.h" +#include "threads/io.h" +#include "threads/interrupt.h" +#include "threads/synch.h" +#include "threads/thread.h" + +/* Register definitions for the 16550A UART used in PCs. + The 16550A has a lot more going on than shown here, but this + is all we need. + + Refer to [PC16650D] for hardware information. */ + +/* I/O port base address for the first serial port. */ +#define IO_BASE 0x3f8 + +/* DLAB=0 registers. */ +#define RBR_REG (IO_BASE + 0) /* Receiver Buffer Reg. (read-only). */ +#define THR_REG (IO_BASE + 0) /* Transmitter Holding Reg. (write-only). */ +#define IER_REG (IO_BASE + 1) /* Interrupt Enable Reg.. */ + +/* DLAB=1 registers. */ +#define LS_REG (IO_BASE + 0) /* Divisor Latch (LSB). */ +#define MS_REG (IO_BASE + 1) /* Divisor Latch (MSB). */ + +/* DLAB-insensitive registers. */ +#define IIR_REG (IO_BASE + 2) /* Interrupt Identification Reg. (read-only) */ +#define FCR_REG (IO_BASE + 2) /* FIFO Control Reg. (write-only). */ +#define LCR_REG (IO_BASE + 3) /* Line Control Register. */ +#define MCR_REG (IO_BASE + 4) /* MODEM Control Register. */ +#define LSR_REG (IO_BASE + 5) /* Line Status Register (read-only). */ + +/* Interrupt Enable Register bits. */ +#define IER_RECV 0x01 /* Interrupt when data received. */ +#define IER_XMIT 0x02 /* Interrupt when transmit finishes. */ + +/* Line Control Register bits. */ +#define LCR_N81 0x03 /* No parity, 8 data bits, 1 stop bit. */ +#define LCR_DLAB 0x80 /* Divisor Latch Access Bit (DLAB). */ + +/* MODEM Control Register. */ +#define MCR_OUT2 0x08 /* Output line 2. */ + +/* Line Status Register. */ +#define LSR_DR 0x01 /* Data Ready: received data byte is in RBR. */ +#define LSR_THRE 0x20 /* THR Empty. */ + +/* Transmission mode. */ +static enum { UNINIT, POLL, QUEUE } mode; + +/* Data to be transmitted. */ +static struct intq txq; + +static void set_serial (int bps); +static void putc_poll (uint8_t); +static void write_ier (void); +static intr_handler_func serial_interrupt; + +/* Initializes the serial port device for polling mode. + Polling mode busy-waits for the serial port to become free + before writing to it. It's slow, but until interrupts have + been initialized it's all we can do. */ +static void +init_poll (void) +{ + ASSERT (mode == UNINIT); + outb (IER_REG, 0); /* Turn off all interrupts. */ + outb (FCR_REG, 0); /* Disable FIFO. */ + set_serial (9600); /* 9.6 kbps, N-8-1. */ + outb (MCR_REG, MCR_OUT2); /* Required to enable interrupts. */ + intq_init (&txq); + mode = POLL; +} + +/* Initializes the serial port device for queued interrupt-driven + I/O. With interrupt-driven I/O we don't waste CPU time + waiting for the serial device to become ready. */ +void +serial_init_queue (void) +{ + enum intr_level old_level; + + if (mode == UNINIT) + init_poll (); + ASSERT (mode == POLL); + + intr_register_ext (0x20 + 4, serial_interrupt, "serial"); + mode = QUEUE; + old_level = intr_disable (); + write_ier (); + intr_set_level (old_level); +} + +/* Sends BYTE to the serial port. */ +void +serial_putc (uint8_t byte) +{ + enum intr_level old_level = intr_disable (); + + if (mode != QUEUE) + { + /* If we're not set up for interrupt-driven I/O yet, + use dumb polling to transmit a byte. */ + if (mode == UNINIT) + init_poll (); + putc_poll (byte); + } + else + { + /* Otherwise, queue a byte and update the interrupt enable + register. */ + if (old_level == INTR_OFF && intq_full (&txq)) + { + /* Interrupts are off and the transmit queue is full. + If we wanted to wait for the queue to empty, + we'd have to reenable interrupts. + That's impolite, so we'll send a character via + polling instead. */ + putc_poll (intq_getc (&txq)); + } + + intq_putc (&txq, byte); + write_ier (); + } + + intr_set_level (old_level); +} + +/* Flushes anything in the serial buffer out the port in polling + mode. */ +void +serial_flush (void) +{ + enum intr_level old_level = intr_disable (); + while (!intq_empty (&txq)) + putc_poll (intq_getc (&txq)); + intr_set_level (old_level); +} + +/* The fullness of the input buffer may have changed. Reassess + whether we should block receive interrupts. + Called by the input buffer routines when characters are added + to or removed from the buffer. */ +void +serial_notify (void) +{ + ASSERT (intr_get_level () == INTR_OFF); + if (mode == QUEUE) + write_ier (); +} + +/* Configures the serial port for BPS bits per second. */ +static void +set_serial (int bps) +{ + int base_rate = 1843200 / 16; /* Base rate of 16550A, in Hz. */ + uint16_t divisor = base_rate / bps; /* Clock rate divisor. */ + + ASSERT (bps >= 300 && bps <= 115200); + + /* Enable DLAB. */ + outb (LCR_REG, LCR_N81 | LCR_DLAB); + + /* Set data rate. */ + outb (LS_REG, divisor & 0xff); + outb (MS_REG, divisor >> 8); + + /* Reset DLAB. */ + outb (LCR_REG, LCR_N81); +} + +/* Update interrupt enable register. */ +static void +write_ier (void) +{ + uint8_t ier = 0; + + ASSERT (intr_get_level () == INTR_OFF); + + /* Enable transmit interrupt if we have any characters to + transmit. */ + if (!intq_empty (&txq)) + ier |= IER_XMIT; + + /* Enable receive interrupt if we have room to store any + characters we receive. */ + if (!input_full ()) + ier |= IER_RECV; + + outb (IER_REG, ier); +} + +/* Polls the serial port until it's ready, + and then transmits BYTE. */ +static void +putc_poll (uint8_t byte) +{ + ASSERT (intr_get_level () == INTR_OFF); + + while ((inb (LSR_REG) & LSR_THRE) == 0) + continue; + outb (THR_REG, byte); +} + +/* Serial interrupt handler. */ +static void +serial_interrupt (struct intr_frame *f UNUSED) +{ + /* Inquire about interrupt in UART. Without this, we can + occasionally miss an interrupt running under QEMU. */ + inb (IIR_REG); + + /* As long as we have room to receive a byte, and the hardware + has a byte for us, receive a byte. */ + while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0) + input_putc (inb (RBR_REG)); + + /* As long as we have a byte to transmit, and the hardware is + ready to accept a byte for transmission, transmit a byte. */ + while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) + outb (THR_REG, intq_getc (&txq)); + + /* Update interrupt enable register based on queue status. */ + write_ier (); +} diff --git a/pintos-env/pintos/devices/serial.h b/pintos-env/pintos/devices/serial.h new file mode 100755 index 0000000..6e04778 --- /dev/null +++ b/pintos-env/pintos/devices/serial.h @@ -0,0 +1,11 @@ +#ifndef DEVICES_SERIAL_H +#define DEVICES_SERIAL_H + +#include + +void serial_init_queue (void); +void serial_putc (uint8_t); +void serial_flush (void); +void serial_notify (void); + +#endif /* devices/serial.h */ diff --git a/pintos-env/pintos/devices/shutdown.c b/pintos-env/pintos/devices/shutdown.c new file mode 100755 index 0000000..7ff9a95 --- /dev/null +++ b/pintos-env/pintos/devices/shutdown.c @@ -0,0 +1,131 @@ +#include "devices/shutdown.h" +#include +#include +#include "devices/kbd.h" +#include "devices/serial.h" +#include "devices/timer.h" +#include "threads/io.h" +#include "threads/thread.h" +#ifdef USERPROG +#include "userprog/exception.h" +#endif +#ifdef FILESYS +#include "devices/block.h" +#include "filesys/filesys.h" +#endif + +/* Keyboard control register port. */ +#define CONTROL_REG 0x64 + +/* How to shut down when shutdown() is called. */ +static enum shutdown_type how = SHUTDOWN_NONE; + +static void print_stats (void); + +/* Shuts down the machine in the way configured by + shutdown_configure(). If the shutdown type is SHUTDOWN_NONE + (which is the default), returns without doing anything. */ +void +shutdown (void) +{ + switch (how) + { + case SHUTDOWN_POWER_OFF: + shutdown_power_off (); + break; + + case SHUTDOWN_REBOOT: + shutdown_reboot (); + break; + + default: + /* Nothing to do. */ + break; + } +} + +/* Sets TYPE as the way that machine will shut down when Pintos + execution is complete. */ +void +shutdown_configure (enum shutdown_type type) +{ + how = type; +} + +/* Reboots the machine via the keyboard controller. */ +void +shutdown_reboot (void) +{ + printf ("Rebooting...\n"); + + /* See [kbd] for details on how to program the keyboard + * controller. */ + for (;;) + { + int i; + + /* Poll keyboard controller's status byte until + * 'input buffer empty' is reported. */ + for (i = 0; i < 0x10000; i++) + { + if ((inb (CONTROL_REG) & 0x02) == 0) + break; + timer_udelay (2); + } + + timer_udelay (50); + + /* Pulse bit 0 of the output port P2 of the keyboard controller. + * This will reset the CPU. */ + outb (CONTROL_REG, 0xfe); + timer_udelay (50); + } +} + +/* Powers down the machine we're running on, + as long as we're running on Bochs or QEMU. */ +void +shutdown_power_off (void) +{ + const char s[] = "Shutdown"; + const char *p; + +#ifdef FILESYS + filesys_done (); +#endif + + print_stats (); + + printf ("Powering off...\n"); + serial_flush (); + + /* This is a special power-off sequence supported by Bochs and + QEMU, but not by physical hardware. */ + for (p = s; *p != '\0'; p++) + outb (0x8900, *p); + + /* This will power off a VMware VM if "gui.exitOnCLIHLT = TRUE" + is set in its configuration file. (The "pintos" script does + that automatically.) */ + asm volatile ("cli; hlt" : : : "memory"); + + /* None of those worked. */ + printf ("still running...\n"); + for (;;); +} + +/* Print statistics about Pintos execution. */ +static void +print_stats (void) +{ + timer_print_stats (); + thread_print_stats (); +#ifdef FILESYS + block_print_stats (); +#endif + console_print_stats (); + kbd_print_stats (); +#ifdef USERPROG + exception_print_stats (); +#endif +} diff --git a/pintos-env/pintos/devices/shutdown.h b/pintos-env/pintos/devices/shutdown.h new file mode 100755 index 0000000..dc4f942 --- /dev/null +++ b/pintos-env/pintos/devices/shutdown.h @@ -0,0 +1,19 @@ +#ifndef DEVICES_SHUTDOWN_H +#define DEVICES_SHUTDOWN_H + +#include + +/* How to shut down when Pintos has nothing left to do. */ +enum shutdown_type + { + SHUTDOWN_NONE, /* Loop forever. */ + SHUTDOWN_POWER_OFF, /* Power off the machine (if possible). */ + SHUTDOWN_REBOOT, /* Reboot the machine (if possible). */ + }; + +void shutdown (void); +void shutdown_configure (enum shutdown_type); +void shutdown_reboot (void) NO_RETURN; +void shutdown_power_off (void) NO_RETURN; + +#endif /* devices/shutdown.h */ diff --git a/pintos-env/pintos/devices/speaker.c b/pintos-env/pintos/devices/speaker.c new file mode 100755 index 0000000..5052005 --- /dev/null +++ b/pintos-env/pintos/devices/speaker.c @@ -0,0 +1,68 @@ +#include "devices/speaker.h" +#include "devices/pit.h" +#include "threads/io.h" +#include "threads/interrupt.h" +#include "devices/timer.h" + +/* Speaker port enable I/O register. */ +#define SPEAKER_PORT_GATE 0x61 + +/* Speaker port enable bits. */ +#define SPEAKER_GATE_ENABLE 0x03 + +/* Sets the PC speaker to emit a tone at the given FREQUENCY, in + Hz. */ +void +speaker_on (int frequency) +{ + if (frequency >= 20 && frequency <= 20000) + { + /* Set the timer channel that's connected to the speaker to + output a square wave at the given FREQUENCY, then + connect the timer channel output to the speaker. */ + enum intr_level old_level = intr_disable (); + pit_configure_channel (2, 3, frequency); + outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE); + intr_set_level (old_level); + } + else + { + /* FREQUENCY is outside the range of normal human hearing. + Just turn off the speaker. */ + speaker_off (); + } +} + +/* Turn off the PC speaker, by disconnecting the timer channel's + output from the speaker. */ +void +speaker_off (void) +{ + enum intr_level old_level = intr_disable (); + outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE); + intr_set_level (old_level); +} + +/* Briefly beep the PC speaker. */ +void +speaker_beep (void) +{ + /* Only attempt to beep the speaker if interrupts are enabled, + because we don't want to freeze the machine during the beep. + We could add a hook to the timer interrupt to avoid that + problem, but then we'd risk failing to ever stop the beep if + Pintos crashes for some unrelated reason. There's nothing + more annoying than a machine whose beeping you can't stop + without a power cycle. + + We can't just enable interrupts while we sleep. For one + thing, we get called (indirectly) from printf, which should + always work, even during boot before we're ready to enable + interrupts. */ + if (intr_get_level () == INTR_ON) + { + speaker_on (440); + timer_msleep (250); + speaker_off (); + } +} diff --git a/pintos-env/pintos/devices/speaker.h b/pintos-env/pintos/devices/speaker.h new file mode 100755 index 0000000..98cef7b --- /dev/null +++ b/pintos-env/pintos/devices/speaker.h @@ -0,0 +1,8 @@ +#ifndef DEVICES_SPEAKER_H +#define DEVICES_SPEAKER_H + +void speaker_on (int frequency); +void speaker_off (void); +void speaker_beep (void); + +#endif /* devices/speaker.h */ diff --git a/pintos-env/pintos/devices/timer.c b/pintos-env/pintos/devices/timer.c new file mode 100755 index 0000000..befaaae --- /dev/null +++ b/pintos-env/pintos/devices/timer.c @@ -0,0 +1,246 @@ +#include "devices/timer.h" +#include +#include +#include +#include +#include "devices/pit.h" +#include "threads/interrupt.h" +#include "threads/synch.h" +#include "threads/thread.h" + +/* See [8254] for hardware details of the 8254 timer chip. */ + +#if TIMER_FREQ < 19 +#error 8254 timer requires TIMER_FREQ >= 19 +#endif +#if TIMER_FREQ > 1000 +#error TIMER_FREQ <= 1000 recommended +#endif + +/* Number of timer ticks since OS booted. */ +static int64_t ticks; + +/* Number of loops per timer tick. + Initialized by timer_calibrate(). */ +static unsigned loops_per_tick; + +static intr_handler_func timer_interrupt; +static bool too_many_loops (unsigned loops); +static void busy_wait (int64_t loops); +static void real_time_sleep (int64_t num, int32_t denom); +static void real_time_delay (int64_t num, int32_t denom); + +/* Sets up the timer to interrupt TIMER_FREQ times per second, + and registers the corresponding interrupt. */ +void +timer_init (void) +{ + pit_configure_channel (0, 2, TIMER_FREQ); + intr_register_ext (0x20, timer_interrupt, "8254 Timer"); +} + +/* Calibrates loops_per_tick, used to implement brief delays. */ +void +timer_calibrate (void) +{ + unsigned high_bit, test_bit; + + ASSERT (intr_get_level () == INTR_ON); + printf ("Calibrating timer... "); + + /* Approximate loops_per_tick as the largest power-of-two + still less than one timer tick. */ + loops_per_tick = 1u << 10; + while (!too_many_loops (loops_per_tick << 1)) + { + loops_per_tick <<= 1; + ASSERT (loops_per_tick != 0); + } + + /* Refine the next 8 bits of loops_per_tick. */ + high_bit = loops_per_tick; + for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1) + if (!too_many_loops (high_bit | test_bit)) + loops_per_tick |= test_bit; + + printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ); +} + +/* Returns the number of timer ticks since the OS booted. */ +int64_t +timer_ticks (void) +{ + enum intr_level old_level = intr_disable (); + int64_t t = ticks; + intr_set_level (old_level); + return t; +} + +/* Returns the number of timer ticks elapsed since THEN, which + should be a value once returned by timer_ticks(). */ +int64_t +timer_elapsed (int64_t then) +{ + return timer_ticks () - then; +} + +/* Sleeps for approximately TICKS timer ticks. Interrupts must + be turned on. */ +void +timer_sleep (int64_t ticks) +{ + int64_t start = timer_ticks (); + + ASSERT (intr_get_level () == INTR_ON); + while (timer_elapsed (start) < ticks) + thread_yield (); +} + +/* Sleeps for approximately MS milliseconds. Interrupts must be + turned on. */ +void +timer_msleep (int64_t ms) +{ + real_time_sleep (ms, 1000); +} + +/* Sleeps for approximately US microseconds. Interrupts must be + turned on. */ +void +timer_usleep (int64_t us) +{ + real_time_sleep (us, 1000 * 1000); +} + +/* Sleeps for approximately NS nanoseconds. Interrupts must be + turned on. */ +void +timer_nsleep (int64_t ns) +{ + real_time_sleep (ns, 1000 * 1000 * 1000); +} + +/* Busy-waits for approximately MS milliseconds. Interrupts need + not be turned on. + + Busy waiting wastes CPU cycles, and busy waiting with + interrupts off for the interval between timer ticks or longer + will cause timer ticks to be lost. Thus, use timer_msleep() + instead if interrupts are enabled. */ +void +timer_mdelay (int64_t ms) +{ + real_time_delay (ms, 1000); +} + +/* Sleeps for approximately US microseconds. Interrupts need not + be turned on. + + Busy waiting wastes CPU cycles, and busy waiting with + interrupts off for the interval between timer ticks or longer + will cause timer ticks to be lost. Thus, use timer_usleep() + instead if interrupts are enabled. */ +void +timer_udelay (int64_t us) +{ + real_time_delay (us, 1000 * 1000); +} + +/* Sleeps execution for approximately NS nanoseconds. Interrupts + need not be turned on. + + Busy waiting wastes CPU cycles, and busy waiting with + interrupts off for the interval between timer ticks or longer + will cause timer ticks to be lost. Thus, use timer_nsleep() + instead if interrupts are enabled.*/ +void +timer_ndelay (int64_t ns) +{ + real_time_delay (ns, 1000 * 1000 * 1000); +} + +/* Prints timer statistics. */ +void +timer_print_stats (void) +{ + printf ("Timer: %"PRId64" ticks\n", timer_ticks ()); +} + +/* Timer interrupt handler. */ +static void +timer_interrupt (struct intr_frame *args UNUSED) +{ + ticks++; + thread_tick (); +} + +/* Returns true if LOOPS iterations waits for more than one timer + tick, otherwise false. */ +static bool +too_many_loops (unsigned loops) +{ + /* Wait for a timer tick. */ + int64_t start = ticks; + while (ticks == start) + barrier (); + + /* Run LOOPS loops. */ + start = ticks; + busy_wait (loops); + + /* If the tick count changed, we iterated too long. */ + barrier (); + return start != ticks; +} + +/* Iterates through a simple loop LOOPS times, for implementing + brief delays. + + Marked NO_INLINE because code alignment can significantly + affect timings, so that if this function was inlined + differently in different places the results would be difficult + to predict. */ +static void NO_INLINE +busy_wait (int64_t loops) +{ + while (loops-- > 0) + barrier (); +} + +/* Sleep for approximately NUM/DENOM seconds. */ +static void +real_time_sleep (int64_t num, int32_t denom) +{ + /* Convert NUM/DENOM seconds into timer ticks, rounding down. + + (NUM / DENOM) s + ---------------------- = NUM * TIMER_FREQ / DENOM ticks. + 1 s / TIMER_FREQ ticks + */ + int64_t ticks = num * TIMER_FREQ / denom; + + ASSERT (intr_get_level () == INTR_ON); + if (ticks > 0) + { + /* We're waiting for at least one full timer tick. Use + timer_sleep() because it will yield the CPU to other + processes. */ + timer_sleep (ticks); + } + else + { + /* Otherwise, use a busy-wait loop for more accurate + sub-tick timing. */ + real_time_delay (num, denom); + } +} + +/* Busy-wait for approximately NUM/DENOM seconds. */ +static void +real_time_delay (int64_t num, int32_t denom) +{ + /* Scale the numerator and denominator down by 1000 to avoid + the possibility of overflow. */ + ASSERT (denom % 1000 == 0); + busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000)); +} diff --git a/pintos-env/pintos/devices/timer.h b/pintos-env/pintos/devices/timer.h new file mode 100755 index 0000000..cd3d6bb --- /dev/null +++ b/pintos-env/pintos/devices/timer.h @@ -0,0 +1,29 @@ +#ifndef DEVICES_TIMER_H +#define DEVICES_TIMER_H + +#include +#include + +/* Number of timer interrupts per second. */ +#define TIMER_FREQ 100 + +void timer_init (void); +void timer_calibrate (void); + +int64_t timer_ticks (void); +int64_t timer_elapsed (int64_t); + +/* Sleep and yield the CPU to other threads. */ +void timer_sleep (int64_t ticks); +void timer_msleep (int64_t milliseconds); +void timer_usleep (int64_t microseconds); +void timer_nsleep (int64_t nanoseconds); + +/* Busy waits. */ +void timer_mdelay (int64_t milliseconds); +void timer_udelay (int64_t microseconds); +void timer_ndelay (int64_t nanoseconds); + +void timer_print_stats (void); + +#endif /* devices/timer.h */ diff --git a/pintos-env/pintos/devices/vga.c b/pintos-env/pintos/devices/vga.c new file mode 100755 index 0000000..f421b61 --- /dev/null +++ b/pintos-env/pintos/devices/vga.c @@ -0,0 +1,172 @@ +#include "devices/vga.h" +#include +#include +#include +#include +#include "devices/speaker.h" +#include "threads/io.h" +#include "threads/interrupt.h" +#include "threads/vaddr.h" + +/* VGA text screen support. See [FREEVGA] for more information. */ + +/* Number of columns and rows on the text display. */ +#define COL_CNT 80 +#define ROW_CNT 25 + +/* Current cursor position. (0,0) is in the upper left corner of + the display. */ +static size_t cx, cy; + +/* Attribute value for gray text on a black background. */ +#define GRAY_ON_BLACK 0x07 + +/* Framebuffer. See [FREEVGA] under "VGA Text Mode Operation". + The character at (x,y) is fb[y][x][0]. + The attribute at (x,y) is fb[y][x][1]. */ +static uint8_t (*fb)[COL_CNT][2]; + +static void clear_row (size_t y); +static void cls (void); +static void newline (void); +static void move_cursor (void); +static void find_cursor (size_t *x, size_t *y); + +/* Initializes the VGA text display. */ +static void +init (void) +{ + /* Already initialized? */ + static bool inited; + if (!inited) + { + fb = ptov (0xb8000); + find_cursor (&cx, &cy); + inited = true; + } +} + +/* Writes C to the VGA text display, interpreting control + characters in the conventional ways. */ +void +vga_putc (int c) +{ + /* Disable interrupts to lock out interrupt handlers + that might write to the console. */ + enum intr_level old_level = intr_disable (); + + init (); + + switch (c) + { + case '\n': + newline (); + break; + + case '\f': + cls (); + break; + + case '\b': + if (cx > 0) + cx--; + break; + + case '\r': + cx = 0; + break; + + case '\t': + cx = ROUND_UP (cx + 1, 8); + if (cx >= COL_CNT) + newline (); + break; + + case '\a': + intr_set_level (old_level); + speaker_beep (); + intr_disable (); + break; + + default: + fb[cy][cx][0] = c; + fb[cy][cx][1] = GRAY_ON_BLACK; + if (++cx >= COL_CNT) + newline (); + break; + } + + /* Update cursor position. */ + move_cursor (); + + intr_set_level (old_level); +} + +/* Clears the screen and moves the cursor to the upper left. */ +static void +cls (void) +{ + size_t y; + + for (y = 0; y < ROW_CNT; y++) + clear_row (y); + + cx = cy = 0; + move_cursor (); +} + +/* Clears row Y to spaces. */ +static void +clear_row (size_t y) +{ + size_t x; + + for (x = 0; x < COL_CNT; x++) + { + fb[y][x][0] = ' '; + fb[y][x][1] = GRAY_ON_BLACK; + } +} + +/* Advances the cursor to the first column in the next line on + the screen. If the cursor is already on the last line on the + screen, scrolls the screen upward one line. */ +static void +newline (void) +{ + cx = 0; + cy++; + if (cy >= ROW_CNT) + { + cy = ROW_CNT - 1; + memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1)); + clear_row (ROW_CNT - 1); + } +} + +/* Moves the hardware cursor to (cx,cy). */ +static void +move_cursor (void) +{ + /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */ + uint16_t cp = cx + COL_CNT * cy; + outw (0x3d4, 0x0e | (cp & 0xff00)); + outw (0x3d4, 0x0f | (cp << 8)); +} + +/* Reads the current hardware cursor position into (*X,*Y). */ +static void +find_cursor (size_t *x, size_t *y) +{ + /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */ + uint16_t cp; + + outb (0x3d4, 0x0e); + cp = inb (0x3d5) << 8; + + outb (0x3d4, 0x0f); + cp |= inb (0x3d5); + + *x = cp % COL_CNT; + *y = cp / COL_CNT; +} diff --git a/pintos-env/pintos/devices/vga.h b/pintos-env/pintos/devices/vga.h new file mode 100755 index 0000000..59690fb --- /dev/null +++ b/pintos-env/pintos/devices/vga.h @@ -0,0 +1,6 @@ +#ifndef DEVICES_VGA_H +#define DEVICES_VGA_H + +void vga_putc (int); + +#endif /* devices/vga.h */ diff --git a/pintos-env/pintos/examples/Makefile b/pintos-env/pintos/examples/Makefile new file mode 100755 index 0000000..2128cc2 --- /dev/null +++ b/pintos-env/pintos/examples/Makefile @@ -0,0 +1,34 @@ +SRCDIR = .. + +# Test programs to compile, and a list of sources for each. +# To add a new test, put its name on the PROGS list +# and then add a name_SRC line that lists its source files. +PROGS = cat cmp cp echo halt hex-dump ls mcat mcp mkdir pwd rm shell \ + bubsort insult lineup matmult recursor + +# Should work from project 2 onward. +cat_SRC = cat.c +cmp_SRC = cmp.c +cp_SRC = cp.c +echo_SRC = echo.c +halt_SRC = halt.c +hex-dump_SRC = hex-dump.c +insult_SRC = insult.c +lineup_SRC = lineup.c +ls_SRC = ls.c +recursor_SRC = recursor.c +rm_SRC = rm.c + +# Should work in project 3; also in project 4 if VM is included. +bubsort_SRC = bubsort.c +matmult_SRC = matmult.c +mcat_SRC = mcat.c +mcp_SRC = mcp.c + +# Should work in project 4. +mkdir_SRC = mkdir.c +pwd_SRC = pwd.c +shell_SRC = shell.c + +include $(SRCDIR)/Make.config +include $(SRCDIR)/Makefile.userprog diff --git a/pintos-env/pintos/examples/bubsort.c b/pintos-env/pintos/examples/bubsort.c new file mode 100755 index 0000000..343219e --- /dev/null +++ b/pintos-env/pintos/examples/bubsort.c @@ -0,0 +1,38 @@ +/* sort.c + + Test program to sort a large number of integers. + + Intention is to stress virtual memory system. + + Ideally, we could read the unsorted array off of the file + system, and store the result back to the file system! */ +#include + +/* Size of array to sort. */ +#define SORT_SIZE 128 + +int +main (void) +{ + /* Array to sort. Static to reduce stack usage. */ + static int array[SORT_SIZE]; + + int i, j, tmp; + + /* First initialize the array in descending order. */ + for (i = 0; i < SORT_SIZE; i++) + array[i] = SORT_SIZE - i - 1; + + /* Then sort in ascending order. */ + for (i = 0; i < SORT_SIZE - 1; i++) + for (j = 0; j < SORT_SIZE - 1 - i; j++) + if (array[j] > array[j + 1]) + { + tmp = array[j]; + array[j] = array[j + 1]; + array[j + 1] = tmp; + } + + printf ("sort exiting with code %d\n", array[0]); + return array[0]; +} diff --git a/pintos-env/pintos/examples/cat.c b/pintos-env/pintos/examples/cat.c new file mode 100755 index 0000000..c8d229d --- /dev/null +++ b/pintos-env/pintos/examples/cat.c @@ -0,0 +1,34 @@ +/* cat.c + + Prints files specified on command line to the console. */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + bool success = true; + int i; + + for (i = 1; i < argc; i++) + { + int fd = open (argv[i]); + if (fd < 0) + { + printf ("%s: open failed\n", argv[i]); + success = false; + continue; + } + for (;;) + { + char buffer[1024]; + int bytes_read = read (fd, buffer, sizeof buffer); + if (bytes_read == 0) + break; + write (STDOUT_FILENO, buffer, bytes_read); + } + close (fd); + } + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/pintos-env/pintos/examples/cmp.c b/pintos-env/pintos/examples/cmp.c new file mode 100755 index 0000000..94b406d --- /dev/null +++ b/pintos-env/pintos/examples/cmp.c @@ -0,0 +1,68 @@ +/* cat.c + + Compares two files. */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + int fd[2]; + + if (argc != 3) + { + printf ("usage: cmp A B\n"); + return EXIT_FAILURE; + } + + /* Open files. */ + fd[0] = open (argv[1]); + if (fd[0] < 0) + { + printf ("%s: open failed\n", argv[1]); + return EXIT_FAILURE; + } + fd[1] = open (argv[2]); + if (fd[1] < 0) + { + printf ("%s: open failed\n", argv[1]); + return EXIT_FAILURE; + } + + /* Compare data. */ + for (;;) + { + int pos; + char buffer[2][1024]; + int bytes_read[2]; + int min_read; + int i; + + pos = tell (fd[0]); + bytes_read[0] = read (fd[0], buffer[0], sizeof buffer[0]); + bytes_read[1] = read (fd[1], buffer[1], sizeof buffer[1]); + min_read = bytes_read[0] < bytes_read[1] ? bytes_read[0] : bytes_read[1]; + if (min_read == 0) + break; + + for (i = 0; i < min_read; i++) + if (buffer[0][i] != buffer[1][i]) + { + printf ("Byte %d is %02hhx ('%c') in %s but %02hhx ('%c') in %s\n", + pos + i, + buffer[0][i], buffer[0][i], argv[1], + buffer[1][i], buffer[1][i], argv[2]); + return EXIT_FAILURE; + } + + if (min_read < bytes_read[1]) + printf ("%s is shorter than %s\n", argv[1], argv[2]); + else if (min_read < bytes_read[0]) + printf ("%s is shorter than %s\n", argv[2], argv[1]); + } + + printf ("%s and %s are identical\n", argv[1], argv[2]); + + return EXIT_SUCCESS; +} diff --git a/pintos-env/pintos/examples/cp.c b/pintos-env/pintos/examples/cp.c new file mode 100755 index 0000000..86a5cd7 --- /dev/null +++ b/pintos-env/pintos/examples/cp.c @@ -0,0 +1,55 @@ +/* cat.c + +Copies one file to another. */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + int in_fd, out_fd; + + if (argc != 3) + { + printf ("usage: cp OLD NEW\n"); + return EXIT_FAILURE; + } + + /* Open input file. */ + in_fd = open (argv[1]); + if (in_fd < 0) + { + printf ("%s: open failed\n", argv[1]); + return EXIT_FAILURE; + } + + /* Create and open output file. */ + if (!create (argv[2], filesize (in_fd))) + { + printf ("%s: create failed\n", argv[2]); + return EXIT_FAILURE; + } + out_fd = open (argv[2]); + if (out_fd < 0) + { + printf ("%s: open failed\n", argv[2]); + return EXIT_FAILURE; + } + + /* Copy data. */ + for (;;) + { + char buffer[1024]; + int bytes_read = read (in_fd, buffer, sizeof buffer); + if (bytes_read == 0) + break; + if (write (out_fd, buffer, bytes_read) != bytes_read) + { + printf ("%s: write failed\n", argv[2]); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/pintos-env/pintos/examples/echo.c b/pintos-env/pintos/examples/echo.c new file mode 100755 index 0000000..1b136f2 --- /dev/null +++ b/pintos-env/pintos/examples/echo.c @@ -0,0 +1,14 @@ +#include +#include + +int +main (int argc, char **argv) +{ + int i; + + for (i = 0; i < argc; i++) + printf ("%s ", argv[i]); + printf ("\n"); + + return EXIT_SUCCESS; +} diff --git a/pintos-env/pintos/examples/halt.c b/pintos-env/pintos/examples/halt.c new file mode 100755 index 0000000..bad7250 --- /dev/null +++ b/pintos-env/pintos/examples/halt.c @@ -0,0 +1,14 @@ +/* halt.c + + Simple program to test whether running a user program works. + + Just invokes a system call that shuts down the OS. */ + +#include + +int +main (void) +{ + halt (); + /* not reached */ +} diff --git a/pintos-env/pintos/examples/hex-dump.c b/pintos-env/pintos/examples/hex-dump.c new file mode 100755 index 0000000..ee313f2 --- /dev/null +++ b/pintos-env/pintos/examples/hex-dump.c @@ -0,0 +1,35 @@ +/* hex-dump.c + + Prints files specified on command line to the console in hex. */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + bool success = true; + int i; + + for (i = 1; i < argc; i++) + { + int fd = open (argv[i]); + if (fd < 0) + { + printf ("%s: open failed\n", argv[i]); + success = false; + continue; + } + for (;;) + { + char buffer[1024]; + int pos = tell (fd); + int bytes_read = read (fd, buffer, sizeof buffer); + if (bytes_read == 0) + break; + hex_dump (pos, buffer, bytes_read, true); + } + close (fd); + } + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/pintos-env/pintos/examples/insult.c b/pintos-env/pintos/examples/insult.c new file mode 100755 index 0000000..98c4e6a --- /dev/null +++ b/pintos-env/pintos/examples/insult.c @@ -0,0 +1,369 @@ +/* Insult.c + + This is a version of the famous CS 107 random sentence + generator. I wrote a program that reads a grammar definition + file and writes a C file containing that grammar as hard code + static C strings. Thus the majority of the code below in + machine generated and totally unreadable. The arrays created + are specially designed to make generating the sentences as + easy as possible. + + Originally by Greg Hutchins, March 1998. + Modified by Ben Pfaff for Pintos, Sept 2004. */ +char *start[] = + { "You", "1", "5", ".", "May", "13", ".", "With", "the", "19", "of", "18", +",", "may", "13", "." +}; +char startLoc[] = { 3, 0, 4, 7, 16 }; +char *adj[] = { "3", "4", "2", ",", "1" }; +char adjLoc[] = { 3, 0, 1, 2, 5 }; +char *adj3[] = { "3", "4" }; +char adj3Loc[] = { 2, 0, 1, 2 }; +char *adj1[] = + { "lame", "dried", "up", "par-broiled", "bloated", "half-baked", "spiteful", +"egotistical", "ungrateful", "stupid", "moronic", "fat", "ugly", "puny", "pitiful", +"insignificant", "blithering", "repulsive", "worthless", "blundering", "retarded", +"useless", "obnoxious", "low-budget", "assinine", "neurotic", "subhuman", "crochety", +"indescribable", "contemptible", "unspeakable", "sick", "lazy", "good-for-nothing", +"slutty", "mentally-deficient", "creepy", "sloppy", "dismal", "pompous", "pathetic", +"friendless", "revolting", "slovenly", "cantankerous", "uncultured", "insufferable", +"gross", "unkempt", "defective", "crumby" +}; +char adj1Loc[] = + { 50, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, +21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, +43, 44, 45, 46, 47, 48, 49, 50, 51 }; +char *adj2[] = + { "putrefied", "festering", "funky", "moldy", "leprous", "curdled", "fetid", +"slimy", "crusty", "sweaty", "damp", "deranged", "smelly", "stenchy", "malignant", +"noxious", "grimy", "reeky", "nasty", "mutilated", "sloppy", "gruesome", "grisly", +"sloshy", "wormy", "mealy", "spoiled", "contaminated", "rancid", "musty", +"fly-covered", "moth-eaten", "decaying", "decomposed", "freeze-dried", "defective", +"petrified", "rotting", "scabrous", "hirsute" +}; +char adj2Loc[] = + { 40, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, +20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 }; +char *name[] = + { "10", ",", "bad", "excuse", "for", "6", ",", "6", "for", "brains", ",", +"4", "11", "8", "for", "brains", "offspring", "of", "a", "motherless", "10", "7", "6", +"7", "4", "11", "8" +}; +char nameLoc[] = { 7, 0, 1, 6, 10, 16, 21, 23, 27 }; +char *stuff[] = + { "shit", "toe", "jam", "filth", "puss", "earwax", "leaf", "clippings", +"bat", "guano", "mucus", "fungus", "mung", "refuse", "earwax", "spittoon", "spittle", +"phlegm" +}; +char stuffLoc[] = { 14, 0, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15, 17, 18 }; +char *noun_and_prep[] = + { "bit", "of", "piece", "of", "vat", "of", "lump", "of", "crock", "of", +"ball", "of", "tub", "of", "load", "of", "bucket", "of", "mound", "of", "glob", "of", "bag", +"of", "heap", "of", "mountain", "of", "load", "of", "barrel", "of", "sack", "of", "blob", "of", +"pile", "of", "truckload", "of", "vat", "of" +}; +char noun_and_prepLoc[] = + { 21, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, +38, 40, 42 }; +char *organics[] = + { "droppings", "mung", "zits", "puckies", "tumors", "cysts", "tumors", +"livers", "froth", "parts", "scabs", "guts", "entrails", "blubber", "carcuses", "gizards", +"9" +}; +char organicsLoc[] = + { 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; +char *body_parts[] = + { "kidneys", "genitals", "buttocks", "earlobes", "innards", "feet" +}; +char body_partsLoc[] = { 6, 0, 1, 2, 3, 4, 5, 6 }; +char *noun[] = + { "pop", "tart", "warthog", "twinkie", "barnacle", "fondue", "pot", +"cretin", "fuckwad", "moron", "ass", "neanderthal", "nincompoop", "simpleton", "11" +}; +char nounLoc[] = { 13, 0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; +char *animal[] = + { "donkey", "llama", "dingo", "lizard", "gekko", "lemur", "moose", "camel", +"goat", "eel" +}; +char animalLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; +char *good_verb[] = + { "love", "cuddle", "fondle", "adore", "smooch", "hug", "caress", "worship", +"look", "at", "touch" +}; +char good_verbLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11 }; +char *curse[] = + { "14", "20", "23", "14", "17", "20", "23", "14", "find", "your", "9", +"suddenly", "delectable", "14", "and", "14", "seek", "a", "battleground", "23" +}; +char curseLoc[] = { 4, 0, 3, 7, 13, 20 }; +char *afflictors[] = + { "15", "21", "15", "21", "15", "21", "15", "21", "a", "22", "Rush", +"Limbaugh", "the", "hosts", "of", "Hades" +}; +char afflictorsLoc[] = { 6, 0, 2, 4, 6, 8, 12, 16 }; +char *quantity[] = + { "a", "4", "hoard", "of", "a", "4", "pack", "of", "a", "truckload", "of", +"a", "swarm", "of", "many", "an", "army", "of", "a", "4", "heard", "of", "a", "4", +"platoon", "of", "a", "4", "and", "4", "group", "of", "16" +}; +char quantityLoc[] = { 10, 0, 4, 8, 11, 14, 15, 18, 22, 26, 32, 33 }; +char *numbers[] = + { "a", "thousand", "three", "million", "ninty-nine", "nine-hundred,", +"ninty-nine", "forty-two", "a", "gazillion", "sixty-eight", "times", "thirty-three" +}; +char numbersLoc[] = { 7, 0, 2, 4, 5, 7, 8, 10, 13 }; +char *adv[] = + { "viciously", "manicly", "merrily", "happily", ",", "with", "the", "19", +"of", "18", ",", "gleefully", ",", "with", "much", "ritualistic", "celebration", ",", +"franticly" +}; +char advLoc[] = { 8, 0, 1, 2, 3, 4, 11, 12, 18, 19 }; +char *metaphor[] = + { "an", "irate", "manticore", "Thor's", "belch", "Alah's", "fist", "16", +"titans", "a", "particularly", "vicious", "she-bear", "in", "the", "midst", "of", "her", +"menstrual", "cycle", "a", "pissed-off", "Jabberwock" +}; +char metaphorLoc[] = { 6, 0, 3, 5, 7, 9, 20, 23 }; +char *force[] = { "force", "fury", "power", "rage" }; +char forceLoc[] = { 4, 0, 1, 2, 3, 4 }; +char *bad_action[] = + { "spit", "shimmy", "slobber", "find", "refuge", "find", "shelter", "dance", +"retch", "vomit", "defecate", "erect", "a", "strip", "mall", "build", "a", "26", "have", "a", +"religious", "experience", "discharge", "bodily", "waste", "fart", "dance", "drool", +"lambada", "spill", "16", "rusty", "tacks", "bite", "you", "sneeze", "sing", "16", +"campfire", "songs", "smite", "you", "16", "times", "construct", "a", "new", "home", "throw", +"a", "party", "procreate" +}; +char bad_actionLoc[] = + { 25, 0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 15, 18, 22, 25, 26, 27, 28, 29, 33, +35, 36, 40, 44, 48, 51, 52 }; +char *beasties[] = + { "yaks", "22", "maggots", "22", "cockroaches", "stinging", "scorpions", +"fleas", "22", "weasels", "22", "gnats", "South", "American", "killer", "bees", "spiders", +"4", "monkeys", "22", "wiener-dogs", "22", "rats", "22", "wolverines", "4", ",", "22", +"pit-fiends" +}; +char beastiesLoc[] = + { 14, 0, 1, 3, 5, 7, 8, 10, 12, 16, 17, 19, 21, 23, 25, 29 }; +char *condition[] = + { "frothing", "manic", "crazed", "plague-ridden", "disease-carrying", +"biting", "rabid", "blood-thirsty", "ravaging", "slavering" +}; +char conditionLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; +char *place[] = + { "in", "24", "25", "upon", "your", "mother's", "grave", "on", "24", "best", +"rug", "in", "the", "26", "you", "call", "home", "upon", "your", "heinie" +}; +char placeLoc[] = { 5, 0, 3, 7, 11, 17, 20 }; +char *relation[] = + { "your", "your", "your", "your", "father's", "your", "mother's", "your", +"grandma's" +}; +char relationLoc[] = { 6, 0, 1, 2, 3, 5, 7, 9 }; +char *in_something[] = + { "entrails", "anal", "cavity", "shoes", "house", "pantry", "general", +"direction", "pants", "bed" +}; +char in_somethingLoc[] = { 8, 0, 1, 3, 4, 5, 6, 8, 9, 10 }; +char *bad_place[] = + { "rat", "hole", "sewer", "toxic", "dump", "oil", "refinery", "landfill", +"porto-pottie" +}; +char bad_placeLoc[] = { 6, 0, 2, 3, 5, 7, 8, 9 }; +char **daGrammar[27]; +char *daGLoc[27]; + +static void +init_grammar (void) +{ + daGrammar[0] = start; + daGLoc[0] = startLoc; + daGrammar[1] = adj; + daGLoc[1] = adjLoc; + daGrammar[2] = adj3; + daGLoc[2] = adj3Loc; + daGrammar[3] = adj1; + daGLoc[3] = adj1Loc; + daGrammar[4] = adj2; + daGLoc[4] = adj2Loc; + daGrammar[5] = name; + daGLoc[5] = nameLoc; + daGrammar[6] = stuff; + daGLoc[6] = stuffLoc; + daGrammar[7] = noun_and_prep; + daGLoc[7] = noun_and_prepLoc; + daGrammar[8] = organics; + daGLoc[8] = organicsLoc; + daGrammar[9] = body_parts; + daGLoc[9] = body_partsLoc; + daGrammar[10] = noun; + daGLoc[10] = nounLoc; + daGrammar[11] = animal; + daGLoc[11] = animalLoc; + daGrammar[12] = good_verb; + daGLoc[12] = good_verbLoc; + daGrammar[13] = curse; + daGLoc[13] = curseLoc; + daGrammar[14] = afflictors; + daGLoc[14] = afflictorsLoc; + daGrammar[15] = quantity; + daGLoc[15] = quantityLoc; + daGrammar[16] = numbers; + daGLoc[16] = numbersLoc; + daGrammar[17] = adv; + daGLoc[17] = advLoc; + daGrammar[18] = metaphor; + daGLoc[18] = metaphorLoc; + daGrammar[19] = force; + daGLoc[19] = forceLoc; + daGrammar[20] = bad_action; + daGLoc[20] = bad_actionLoc; + daGrammar[21] = beasties; + daGLoc[21] = beastiesLoc; + daGrammar[22] = condition; + daGLoc[22] = conditionLoc; + daGrammar[23] = place; + daGLoc[23] = placeLoc; + daGrammar[24] = relation; + daGLoc[24] = relationLoc; + daGrammar[25] = in_something; + daGLoc[25] = in_somethingLoc; + daGrammar[26] = bad_place; + daGLoc[26] = bad_placeLoc; +} + +#include +#include +#include +#include +#include +#include +#include + +void expand (int num, char **grammar[], char *location[], int handle); + +static void +usage (int ret_code, const char *message, ...) PRINTF_FORMAT (2, 3); + +static void +usage (int ret_code, const char *message, ...) +{ + va_list args; + + if (message != NULL) + { + va_start (args, message); + vprintf (message, args); + va_end (args); + } + + printf ("\n" + "Usage: insult [OPTION]...\n" + "Prints random insults to screen.\n\n" + " -h: this help message\n" + " -s : set the random seed (default 4951)\n" + " -n : choose number of insults (default 4)\n" + " -f : redirect output to \n"); + + exit (ret_code); +} + +int +main (int argc, char *argv[]) +{ + int sentence_cnt, new_seed, i, file_flag, sent_flag, seed_flag; + int handle; + + new_seed = 4951; + sentence_cnt = 4; + file_flag = 0; + seed_flag = 0; + sent_flag = 0; + handle = STDOUT_FILENO; + + for (i = 1; i < argc; i++) + { + if (strcmp (argv[1], "-h") == 0) + usage (0, NULL); + else if (strcmp (argv[i], "-s") == 0) + { + if (seed_flag++) + usage (-1, "Can't have more than one seed"); + if (++i >= argc) + usage (-1, "Missing value for -s"); + new_seed = atoi (argv[i]); + } + else if (strcmp (argv[i], "-n") == 0) + { + if (sent_flag++) + usage (-1, "Can't have more than one sentence option"); + if (++i >= argc) + usage (-1, "Missing value for -n"); + sentence_cnt = atoi (argv[i]); + if (sentence_cnt < 1) + usage (-1, "Must have at least one sentence"); + } + else if (strcmp (argv[i], "-f") == 0) + { + if (file_flag++) + usage (-1, "Can't have more than one output file"); + if (++i >= argc) + usage (-1, "Missing value for -f"); + + /* Because files have fixed length in the basic Pintos + file system, the 0 argument means that this option + will not be useful until project 4 is + implemented. */ + create (argv[i], 0); + handle = open (argv[i]); + if (handle < 0) + { + printf ("%s: open failed\n", argv[i]); + return EXIT_FAILURE; + } + } + else + usage (-1, "Unrecognized flag"); + } + + init_grammar (); + + random_init (new_seed); + hprintf (handle, "\n"); + + for (i = 0; i < sentence_cnt; i++) + { + hprintf (handle, "\n"); + expand (0, daGrammar, daGLoc, handle); + hprintf (handle, "\n\n"); + } + + if (file_flag) + close (handle); + + return EXIT_SUCCESS; +} + +void +expand (int num, char **grammar[], char *location[], int handle) +{ + char *word; + int i, which, listStart, listEnd; + + which = random_ulong () % location[num][0] + 1; + listStart = location[num][which]; + listEnd = location[num][which + 1]; + for (i = listStart; i < listEnd; i++) + { + word = grammar[num][i]; + if (!isdigit (*word)) + { + if (!ispunct (*word)) + hprintf (handle, " "); + hprintf (handle, "%s", word); + } + else + expand (atoi (word), grammar, location, handle); + } + +} diff --git a/pintos-env/pintos/examples/lib/.gitignore b/pintos-env/pintos/examples/lib/.gitignore new file mode 100755 index 0000000..a438335 --- /dev/null +++ b/pintos-env/pintos/examples/lib/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/pintos-env/pintos/examples/lib/user/.dummy b/pintos-env/pintos/examples/lib/user/.dummy new file mode 100755 index 0000000..e69de29 diff --git a/pintos-env/pintos/examples/lib/user/.gitignore b/pintos-env/pintos/examples/lib/user/.gitignore new file mode 100755 index 0000000..a438335 --- /dev/null +++ b/pintos-env/pintos/examples/lib/user/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/pintos-env/pintos/examples/lineup.c b/pintos-env/pintos/examples/lineup.c new file mode 100755 index 0000000..60402d0 --- /dev/null +++ b/pintos-env/pintos/examples/lineup.c @@ -0,0 +1,46 @@ +/* lineup.c + + Converts a file to uppercase in-place. + + Incidentally, another way to do this while avoiding the seeks + would be to open the input file, then remove() it and reopen + it under another handle. Because of Unix deletion semantics + this works fine. */ + +#include +#include +#include + +int +main (int argc, char *argv[]) +{ + char buf[1024]; + int handle; + + if (argc != 2) + exit (1); + + handle = open (argv[1]); + if (handle < 0) + exit (2); + + for (;;) + { + int n, i; + + n = read (handle, buf, sizeof buf); + if (n <= 0) + break; + + for (i = 0; i < n; i++) + buf[i] = toupper ((unsigned char) buf[i]); + + seek (handle, tell (handle) - n); + if (write (handle, buf, n) != n) + printf ("write failed\n"); + } + + close (handle); + + return EXIT_SUCCESS; +} diff --git a/pintos-env/pintos/examples/ls.c b/pintos-env/pintos/examples/ls.c new file mode 100755 index 0000000..fbe27a1 --- /dev/null +++ b/pintos-env/pintos/examples/ls.c @@ -0,0 +1,90 @@ +/* ls.c + + Lists the contents of the directory or directories named on + the command line, or of the current directory if none are + named. + + By default, only the name of each file is printed. If "-l" is + given as the first argument, the type, size, and inumber of + each file is also printed. This won't work until project 4. */ + +#include +#include +#include + +static bool +list_dir (const char *dir, bool verbose) +{ + int dir_fd = open (dir); + if (dir_fd == -1) + { + printf ("%s: not found\n", dir); + return false; + } + + if (isdir (dir_fd)) + { + char name[READDIR_MAX_LEN]; + + printf ("%s", dir); + if (verbose) + printf (" (inumber %d)", inumber (dir_fd)); + printf (":\n"); + + while (readdir (dir_fd, name)) + { + printf ("%s", name); + if (verbose) + { + char full_name[128]; + int entry_fd; + + snprintf (full_name, sizeof full_name, "%s/%s", dir, name); + entry_fd = open (full_name); + + printf (": "); + if (entry_fd != -1) + { + if (isdir (entry_fd)) + printf ("directory"); + else + printf ("%d-byte file", filesize (entry_fd)); + printf (", inumber %d", inumber (entry_fd)); + } + else + printf ("open failed"); + close (entry_fd); + } + printf ("\n"); + } + } + else + printf ("%s: not a directory\n", dir); + close (dir_fd); + return true; +} + +int +main (int argc, char *argv[]) +{ + bool success = true; + bool verbose = false; + + if (argc > 1 && !strcmp (argv[1], "-l")) + { + verbose = true; + argv++; + argc--; + } + + if (argc <= 1) + success = list_dir (".", verbose); + else + { + int i; + for (i = 1; i < argc; i++) + if (!list_dir (argv[i], verbose)) + success = false; + } + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/pintos-env/pintos/examples/matmult.c b/pintos-env/pintos/examples/matmult.c new file mode 100755 index 0000000..4f0615f --- /dev/null +++ b/pintos-env/pintos/examples/matmult.c @@ -0,0 +1,57 @@ +/* matmult.c + + Test program to do matrix multiplication on large arrays. + + Intended to stress virtual memory system. + + Ideally, we could read the matrices off of the file system, + and store the result back to the file system! + */ + +#include +#include + +/* You should define DIM to be large enough that the arrays + don't fit in physical memory. + + Dim Memory + ------ -------- + 16 3 kB + 64 48 kB + 128 192 kB + 256 768 kB + 512 3,072 kB + 1,024 12,288 kB + 2,048 49,152 kB + 4,096 196,608 kB + 8,192 786,432 kB + 16,384 3,145,728 kB */ +#define DIM 128 + +int A[DIM][DIM]; +int B[DIM][DIM]; +int C[DIM][DIM]; + +int +main (void) +{ + int i, j, k; + + /* Initialize the matrices. */ + for (i = 0; i < DIM; i++) + for (j = 0; j < DIM; j++) + { + A[i][j] = i; + B[i][j] = j; + C[i][j] = 0; + } + + /* Multiply matrices. */ + for (i = 0; i < DIM; i++) + for (j = 0; j < DIM; j++) + for (k = 0; k < DIM; k++) + C[i][j] += A[i][k] * B[k][j]; + + /* Done. */ + exit (C[DIM - 1][DIM - 1]); +} diff --git a/pintos-env/pintos/examples/mcat.c b/pintos-env/pintos/examples/mcat.c new file mode 100755 index 0000000..7b39760 --- /dev/null +++ b/pintos-env/pintos/examples/mcat.c @@ -0,0 +1,45 @@ +/* mcat.c + + Prints files specified on command line to the console, using + mmap. */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) + { + int fd; + mapid_t map; + void *data = (void *) 0x10000000; + int size; + + /* Open input file. */ + fd = open (argv[i]); + if (fd < 0) + { + printf ("%s: open failed\n", argv[i]); + return EXIT_FAILURE; + } + size = filesize (fd); + + /* Map files. */ + map = mmap (fd, data); + if (map == MAP_FAILED) + { + printf ("%s: mmap failed\n", argv[i]); + return EXIT_FAILURE; + } + + /* Write file to console. */ + write (STDOUT_FILENO, data, size); + + /* Unmap files (optional). */ + munmap (map); + } + return EXIT_SUCCESS; +} diff --git a/pintos-env/pintos/examples/mcp.c b/pintos-env/pintos/examples/mcp.c new file mode 100755 index 0000000..6091dc8 --- /dev/null +++ b/pintos-env/pintos/examples/mcp.c @@ -0,0 +1,68 @@ +/* mcp.c + + Copies one file to another, using mmap. */ + +#include +#include +#include + +int +main (int argc, char *argv[]) +{ + int in_fd, out_fd; + mapid_t in_map, out_map; + void *in_data = (void *) 0x10000000; + void *out_data = (void *) 0x20000000; + int size; + + if (argc != 3) + { + printf ("usage: cp OLD NEW\n"); + return EXIT_FAILURE; + } + + /* Open input file. */ + in_fd = open (argv[1]); + if (in_fd < 0) + { + printf ("%s: open failed\n", argv[1]); + return EXIT_FAILURE; + } + size = filesize (in_fd); + + /* Create and open output file. */ + if (!create (argv[2], size)) + { + printf ("%s: create failed\n", argv[2]); + return EXIT_FAILURE; + } + out_fd = open (argv[2]); + if (out_fd < 0) + { + printf ("%s: open failed\n", argv[2]); + return EXIT_FAILURE; + } + + /* Map files. */ + in_map = mmap (in_fd, in_data); + if (in_map == MAP_FAILED) + { + printf ("%s: mmap failed\n", argv[1]); + return EXIT_FAILURE; + } + out_map = mmap (out_fd, out_data); + if (out_map == MAP_FAILED) + { + printf ("%s: mmap failed\n", argv[2]); + return EXIT_FAILURE; + } + + /* Copy files. */ + memcpy (out_data, in_data, size); + + /* Unmap files (optional). */ + munmap (in_map); + munmap (out_map); + + return EXIT_SUCCESS; +} diff --git a/pintos-env/pintos/examples/mkdir.c b/pintos-env/pintos/examples/mkdir.c new file mode 100755 index 0000000..7ddbc3f --- /dev/null +++ b/pintos-env/pintos/examples/mkdir.c @@ -0,0 +1,24 @@ +/* mkdir.c + + Creates a directory. */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + if (argc != 2) + { + printf ("usage: %s DIRECTORY\n", argv[0]); + return EXIT_FAILURE; + } + + if (!mkdir (argv[1])) + { + printf ("%s: mkdir failed\n", argv[1]); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/pintos-env/pintos/examples/pwd.c b/pintos-env/pintos/examples/pwd.c new file mode 100755 index 0000000..d2305cf --- /dev/null +++ b/pintos-env/pintos/examples/pwd.c @@ -0,0 +1,152 @@ +/* pwd.c + + Prints the absolute name of the present working directory. */ + +#include +#include +#include +#include + +static bool getcwd (char *cwd, size_t cwd_size); + +int +main (void) +{ + char cwd[128]; + if (getcwd (cwd, sizeof cwd)) + { + printf ("%s\n", cwd); + return EXIT_SUCCESS; + } + else + { + printf ("error\n"); + return EXIT_FAILURE; + } +} + +/* Stores the inode number for FILE_NAME in *INUM. + Returns true if successful, false if the file could not be + opened. */ +static bool +get_inumber (const char *file_name, int *inum) +{ + int fd = open (file_name); + if (fd >= 0) + { + *inum = inumber (fd); + close (fd); + return true; + } + else + return false; +} + +/* Prepends PREFIX to the characters stored in the final *DST_LEN + bytes of the DST_SIZE-byte buffer that starts at DST. + Returns true if successful, false if adding that many + characters, plus a null terminator, would overflow the buffer. + (No null terminator is actually added or depended upon, but + its space is accounted for.) */ +static bool +prepend (const char *prefix, + char *dst, size_t *dst_len, size_t dst_size) +{ + size_t prefix_len = strlen (prefix); + if (prefix_len + *dst_len + 1 <= dst_size) + { + *dst_len += prefix_len; + memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len); + return true; + } + else + return false; +} + +/* Stores the current working directory, as a null-terminated + string, in the CWD_SIZE bytes in CWD. + Returns true if successful, false on error. Errors include + system errors, directory trees deeper than MAX_LEVEL levels, + and insufficient space in CWD. */ +static bool +getcwd (char *cwd, size_t cwd_size) +{ + size_t cwd_len = 0; + +#define MAX_LEVEL 20 + char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1]; + char *namep; + + int child_inum; + + /* Make sure there's enough space for at least "/". */ + if (cwd_size < 2) + return false; + + /* Get inumber for current directory. */ + if (!get_inumber (".", &child_inum)) + return false; + + namep = name; + for (;;) + { + int parent_inum, parent_fd; + + /* Compose "../../../..", etc., in NAME. */ + if ((namep - name) > MAX_LEVEL * 3) + return false; + *namep++ = '.'; + *namep++ = '.'; + *namep = '\0'; + + /* Open directory. */ + parent_fd = open (name); + if (parent_fd < 0) + return false; + *namep++ = '/'; + + /* If parent and child have the same inumber, + then we've arrived at the root. */ + parent_inum = inumber (parent_fd); + if (parent_inum == child_inum) + break; + + /* Find name of file in parent directory with the child's + inumber. */ + for (;;) + { + int test_inum; + if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum)) + { + close (parent_fd); + return false; + } + if (test_inum == child_inum) + break; + } + close (parent_fd); + + /* Prepend "/name" to CWD. */ + if (!prepend (namep - 1, cwd, &cwd_len, cwd_size)) + return false; + + /* Move up. */ + child_inum = parent_inum; + } + + /* Finalize CWD. */ + if (cwd_len > 0) + { + /* Move the string to the beginning of CWD, + and null-terminate it. */ + memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len); + cwd[cwd_len] = '\0'; + } + else + { + /* Special case for the root. */ + strlcpy (cwd, "/", cwd_size); + } + + return true; +} diff --git a/pintos-env/pintos/examples/recursor.c b/pintos-env/pintos/examples/recursor.c new file mode 100755 index 0000000..79c784a --- /dev/null +++ b/pintos-env/pintos/examples/recursor.c @@ -0,0 +1,34 @@ +#include +#include +#include + +int +main (int argc, char *argv[]) +{ + char buffer[128]; + pid_t pid; + int retval = 0; + + if (argc != 4) + { + printf ("usage: recursor \n"); + exit (1); + } + + /* Print args. */ + printf ("%s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]); + + /* Execute child and wait for it to finish if requested. */ + if (atoi (argv[2]) != 0) + { + snprintf (buffer, sizeof buffer, + "recursor %s %d %s", argv[1], atoi (argv[2]) - 1, argv[3]); + pid = exec (buffer); + if (atoi (argv[3])) + retval = wait (pid); + } + + /* Done. */ + printf ("%s %s: dying, retval=%d\n", argv[1], argv[2], retval); + exit (retval); +} diff --git a/pintos-env/pintos/examples/rm.c b/pintos-env/pintos/examples/rm.c new file mode 100755 index 0000000..0db7f7b --- /dev/null +++ b/pintos-env/pintos/examples/rm.c @@ -0,0 +1,21 @@ +/* rm.c + + Removes files specified on command line. */ + +#include +#include + +int +main (int argc, char *argv[]) +{ + bool success = true; + int i; + + for (i = 1; i < argc; i++) + if (!remove (argv[i])) + { + printf ("%s: remove failed\n", argv[i]); + success = false; + } + return success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/pintos-env/pintos/examples/shell.c b/pintos-env/pintos/examples/shell.c new file mode 100755 index 0000000..93641b4 --- /dev/null +++ b/pintos-env/pintos/examples/shell.c @@ -0,0 +1,104 @@ +#include +#include +#include +#include + +static void read_line (char line[], size_t); +static bool backspace (char **pos, char line[]); + +int +main (void) +{ + printf ("Shell starting...\n"); + for (;;) + { + char command[80]; + + /* Read command. */ + printf ("--"); + read_line (command, sizeof command); + + /* Execute command. */ + if (!strcmp (command, "exit")) + break; + else if (!memcmp (command, "cd ", 3)) + { + if (!chdir (command + 3)) + printf ("\"%s\": chdir failed\n", command + 3); + } + else if (command[0] == '\0') + { + /* Empty command. */ + } + else + { + pid_t pid = exec (command); + if (pid != PID_ERROR) + printf ("\"%s\": exit code %d\n", command, wait (pid)); + else + printf ("exec failed\n"); + } + } + + printf ("Shell exiting."); + return EXIT_SUCCESS; +} + +/* Reads a line of input from the user into LINE, which has room + for SIZE bytes. Handles backspace and Ctrl+U in the ways + expected by Unix users. On return, LINE will always be + null-terminated and will not end in a new-line character. */ +static void +read_line (char line[], size_t size) +{ + char *pos = line; + for (;;) + { + char c; + read (STDIN_FILENO, &c, 1); + + switch (c) + { + case '\r': + *pos = '\0'; + putchar ('\n'); + return; + + case '\b': + backspace (&pos, line); + break; + + case ('U' - 'A') + 1: /* Ctrl+U. */ + while (backspace (&pos, line)) + continue; + break; + + default: + /* Add character to line. */ + if (pos < line + size - 1) + { + putchar (c); + *pos++ = c; + } + break; + } + } +} + +/* If *POS is past the beginning of LINE, backs up one character + position. Returns true if successful, false if nothing was + done. */ +static bool +backspace (char **pos, char line[]) +{ + if (*pos > line) + { + /* Back up cursor, overwrite character, back up + again. */ + printf ("\b \b"); + (*pos)--; + return true; + } + else + return false; +} diff --git a/pintos-env/pintos/filesys/Make.vars b/pintos-env/pintos/filesys/Make.vars new file mode 100755 index 0000000..b3aa005 --- /dev/null +++ b/pintos-env/pintos/filesys/Make.vars @@ -0,0 +1,13 @@ +# -*- makefile -*- + +kernel.bin: DEFINES = -DUSERPROG -DFILESYS +KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys +TEST_SUBDIRS = tests/userprog tests/filesys/base tests/filesys/extended +GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.no-vm +SIMULATOR = --qemu + +# Uncomment the lines below to enable VM. +#kernel.bin: DEFINES += -DVM +#KERNEL_SUBDIRS += vm +#TEST_SUBDIRS += tests/vm +#GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.with-vm diff --git a/pintos-env/pintos/filesys/Makefile b/pintos-env/pintos/filesys/Makefile new file mode 100755 index 0000000..34c10aa --- /dev/null +++ b/pintos-env/pintos/filesys/Makefile @@ -0,0 +1 @@ +include ../Makefile.kernel diff --git a/pintos-env/pintos/filesys/directory.c b/pintos-env/pintos/filesys/directory.c new file mode 100755 index 0000000..030c1c9 --- /dev/null +++ b/pintos-env/pintos/filesys/directory.c @@ -0,0 +1,236 @@ +#include "filesys/directory.h" +#include +#include +#include +#include "filesys/filesys.h" +#include "filesys/inode.h" +#include "threads/malloc.h" + +/* A directory. */ +struct dir + { + struct inode *inode; /* Backing store. */ + off_t pos; /* Current position. */ + }; + +/* A single directory entry. */ +struct dir_entry + { + block_sector_t inode_sector; /* Sector number of header. */ + char name[NAME_MAX + 1]; /* Null terminated file name. */ + bool in_use; /* In use or free? */ + }; + +/* Creates a directory with space for ENTRY_CNT entries in the + given SECTOR. Returns true if successful, false on failure. */ +bool +dir_create (block_sector_t sector, size_t entry_cnt) +{ + return inode_create (sector, entry_cnt * sizeof (struct dir_entry)); +} + +/* Opens and returns the directory for the given INODE, of which + it takes ownership. Returns a null pointer on failure. */ +struct dir * +dir_open (struct inode *inode) +{ + struct dir *dir = calloc (1, sizeof *dir); + if (inode != NULL && dir != NULL) + { + dir->inode = inode; + dir->pos = 0; + return dir; + } + else + { + inode_close (inode); + free (dir); + return NULL; + } +} + +/* Opens the root directory and returns a directory for it. + Return true if successful, false on failure. */ +struct dir * +dir_open_root (void) +{ + return dir_open (inode_open (ROOT_DIR_SECTOR)); +} + +/* Opens and returns a new directory for the same inode as DIR. + Returns a null pointer on failure. */ +struct dir * +dir_reopen (struct dir *dir) +{ + return dir_open (inode_reopen (dir->inode)); +} + +/* Destroys DIR and frees associated resources. */ +void +dir_close (struct dir *dir) +{ + if (dir != NULL) + { + inode_close (dir->inode); + free (dir); + } +} + +/* Returns the inode encapsulated by DIR. */ +struct inode * +dir_get_inode (struct dir *dir) +{ + return dir->inode; +} + +/* Searches DIR for a file with the given NAME. + If successful, returns true, sets *EP to the directory entry + if EP is non-null, and sets *OFSP to the byte offset of the + directory entry if OFSP is non-null. + otherwise, returns false and ignores EP and OFSP. */ +static bool +lookup (const struct dir *dir, const char *name, + struct dir_entry *ep, off_t *ofsp) +{ + struct dir_entry e; + size_t ofs; + + ASSERT (dir != NULL); + ASSERT (name != NULL); + + for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; + ofs += sizeof e) + if (e.in_use && !strcmp (name, e.name)) + { + if (ep != NULL) + *ep = e; + if (ofsp != NULL) + *ofsp = ofs; + return true; + } + return false; +} + +/* Searches DIR for a file with the given NAME + and returns true if one exists, false otherwise. + On success, sets *INODE to an inode for the file, otherwise to + a null pointer. The caller must close *INODE. */ +bool +dir_lookup (const struct dir *dir, const char *name, + struct inode **inode) +{ + struct dir_entry e; + + ASSERT (dir != NULL); + ASSERT (name != NULL); + + if (lookup (dir, name, &e, NULL)) + *inode = inode_open (e.inode_sector); + else + *inode = NULL; + + return *inode != NULL; +} + +/* Adds a file named NAME to DIR, which must not already contain a + file by that name. The file's inode is in sector + INODE_SECTOR. + Returns true if successful, false on failure. + Fails if NAME is invalid (i.e. too long) or a disk or memory + error occurs. */ +bool +dir_add (struct dir *dir, const char *name, block_sector_t inode_sector) +{ + struct dir_entry e; + off_t ofs; + bool success = false; + + ASSERT (dir != NULL); + ASSERT (name != NULL); + + /* Check NAME for validity. */ + if (*name == '\0' || strlen (name) > NAME_MAX) + return false; + + /* Check that NAME is not in use. */ + if (lookup (dir, name, NULL, NULL)) + goto done; + + /* Set OFS to offset of free slot. + If there are no free slots, then it will be set to the + current end-of-file. + + inode_read_at() will only return a short read at end of file. + Otherwise, we'd need to verify that we didn't get a short + read due to something intermittent such as low memory. */ + for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; + ofs += sizeof e) + if (!e.in_use) + break; + + /* Write slot. */ + e.in_use = true; + strlcpy (e.name, name, sizeof e.name); + e.inode_sector = inode_sector; + success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e; + + done: + return success; +} + +/* Removes any entry for NAME in DIR. + Returns true if successful, false on failure, + which occurs only if there is no file with the given NAME. */ +bool +dir_remove (struct dir *dir, const char *name) +{ + struct dir_entry e; + struct inode *inode = NULL; + bool success = false; + off_t ofs; + + ASSERT (dir != NULL); + ASSERT (name != NULL); + + /* Find directory entry. */ + if (!lookup (dir, name, &e, &ofs)) + goto done; + + /* Open inode. */ + inode = inode_open (e.inode_sector); + if (inode == NULL) + goto done; + + /* Erase directory entry. */ + e.in_use = false; + if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e) + goto done; + + /* Remove inode. */ + inode_remove (inode); + success = true; + + done: + inode_close (inode); + return success; +} + +/* Reads the next directory entry in DIR and stores the name in + NAME. Returns true if successful, false if the directory + contains no more entries. */ +bool +dir_readdir (struct dir *dir, char name[NAME_MAX + 1]) +{ + struct dir_entry e; + + while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e) + { + dir->pos += sizeof e; + if (e.in_use) + { + strlcpy (name, e.name, NAME_MAX + 1); + return true; + } + } + return false; +} diff --git a/pintos-env/pintos/filesys/directory.h b/pintos-env/pintos/filesys/directory.h new file mode 100755 index 0000000..930acf9 --- /dev/null +++ b/pintos-env/pintos/filesys/directory.h @@ -0,0 +1,30 @@ +#ifndef FILESYS_DIRECTORY_H +#define FILESYS_DIRECTORY_H + +#include +#include +#include "devices/block.h" + +/* Maximum length of a file name component. + This is the traditional UNIX maximum length. + After directories are implemented, this maximum length may be + retained, but much longer full path names must be allowed. */ +#define NAME_MAX 14 + +struct inode; + +/* Opening and closing directories. */ +bool dir_create (block_sector_t sector, size_t entry_cnt); +struct dir *dir_open (struct inode *); +struct dir *dir_open_root (void); +struct dir *dir_reopen (struct dir *); +void dir_close (struct dir *); +struct inode *dir_get_inode (struct dir *); + +/* Reading and writing. */ +bool dir_lookup (const struct dir *, const char *name, struct inode **); +bool dir_add (struct dir *, const char *name, block_sector_t); +bool dir_remove (struct dir *, const char *name); +bool dir_readdir (struct dir *, char name[NAME_MAX + 1]); + +#endif /* filesys/directory.h */ diff --git a/pintos-env/pintos/filesys/file.c b/pintos-env/pintos/filesys/file.c new file mode 100755 index 0000000..d5fc10d --- /dev/null +++ b/pintos-env/pintos/filesys/file.c @@ -0,0 +1,168 @@ +#include "filesys/file.h" +#include +#include "filesys/inode.h" +#include "threads/malloc.h" + +/* An open file. */ +struct file + { + struct inode *inode; /* File's inode. */ + off_t pos; /* Current position. */ + bool deny_write; /* Has file_deny_write() been called? */ + }; + +/* Opens a file for the given INODE, of which it takes ownership, + and returns the new file. Returns a null pointer if an + allocation fails or if INODE is null. */ +struct file * +file_open (struct inode *inode) +{ + struct file *file = calloc (1, sizeof *file); + if (inode != NULL && file != NULL) + { + file->inode = inode; + file->pos = 0; + file->deny_write = false; + return file; + } + else + { + inode_close (inode); + free (file); + return NULL; + } +} + +/* Opens and returns a new file for the same inode as FILE. + Returns a null pointer if unsuccessful. */ +struct file * +file_reopen (struct file *file) +{ + return file_open (inode_reopen (file->inode)); +} + +/* Closes FILE. */ +void +file_close (struct file *file) +{ + if (file != NULL) + { + file_allow_write (file); + inode_close (file->inode); + free (file); + } +} + +/* Returns the inode encapsulated by FILE. */ +struct inode * +file_get_inode (struct file *file) +{ + return file->inode; +} + +/* Reads SIZE bytes from FILE into BUFFER, + starting at the file's current position. + Returns the number of bytes actually read, + which may be less than SIZE if end of file is reached. + Advances FILE's position by the number of bytes read. */ +off_t +file_read (struct file *file, void *buffer, off_t size) +{ + off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos); + file->pos += bytes_read; + return bytes_read; +} + +/* Reads SIZE bytes from FILE into BUFFER, + starting at offset FILE_OFS in the file. + Returns the number of bytes actually read, + which may be less than SIZE if end of file is reached. + The file's current position is unaffected. */ +off_t +file_read_at (struct file *file, void *buffer, off_t size, off_t file_ofs) +{ + return inode_read_at (file->inode, buffer, size, file_ofs); +} + +/* Writes SIZE bytes from BUFFER into FILE, + starting at the file's current position. + Returns the number of bytes actually written, + which may be less than SIZE if end of file is reached. + (Normally we'd grow the file in that case, but file growth is + not yet implemented.) + Advances FILE's position by the number of bytes read. */ +off_t +file_write (struct file *file, const void *buffer, off_t size) +{ + off_t bytes_written = inode_write_at (file->inode, buffer, size, file->pos); + file->pos += bytes_written; + return bytes_written; +} + +/* Writes SIZE bytes from BUFFER into FILE, + starting at offset FILE_OFS in the file. + Returns the number of bytes actually written, + which may be less than SIZE if end of file is reached. + (Normally we'd grow the file in that case, but file growth is + not yet implemented.) + The file's current position is unaffected. */ +off_t +file_write_at (struct file *file, const void *buffer, off_t size, + off_t file_ofs) +{ + return inode_write_at (file->inode, buffer, size, file_ofs); +} + +/* Prevents write operations on FILE's underlying inode + until file_allow_write() is called or FILE is closed. */ +void +file_deny_write (struct file *file) +{ + ASSERT (file != NULL); + if (!file->deny_write) + { + file->deny_write = true; + inode_deny_write (file->inode); + } +} + +/* Re-enables write operations on FILE's underlying inode. + (Writes might still be denied by some other file that has the + same inode open.) */ +void +file_allow_write (struct file *file) +{ + ASSERT (file != NULL); + if (file->deny_write) + { + file->deny_write = false; + inode_allow_write (file->inode); + } +} + +/* Returns the size of FILE in bytes. */ +off_t +file_length (struct file *file) +{ + ASSERT (file != NULL); + return inode_length (file->inode); +} + +/* Sets the current position in FILE to NEW_POS bytes from the + start of the file. */ +void +file_seek (struct file *file, off_t new_pos) +{ + ASSERT (file != NULL); + ASSERT (new_pos >= 0); + file->pos = new_pos; +} + +/* Returns the current position in FILE as a byte offset from the + start of the file. */ +off_t +file_tell (struct file *file) +{ + ASSERT (file != NULL); + return file->pos; +} diff --git a/pintos-env/pintos/filesys/file.h b/pintos-env/pintos/filesys/file.h new file mode 100755 index 0000000..a33c5af --- /dev/null +++ b/pintos-env/pintos/filesys/file.h @@ -0,0 +1,29 @@ +#ifndef FILESYS_FILE_H +#define FILESYS_FILE_H + +#include "filesys/off_t.h" + +struct inode; + +/* Opening and closing files. */ +struct file *file_open (struct inode *); +struct file *file_reopen (struct file *); +void file_close (struct file *); +struct inode *file_get_inode (struct file *); + +/* Reading and writing. */ +off_t file_read (struct file *, void *, off_t); +off_t file_read_at (struct file *, void *, off_t size, off_t start); +off_t file_write (struct file *, const void *, off_t); +off_t file_write_at (struct file *, const void *, off_t size, off_t start); + +/* Preventing writes. */ +void file_deny_write (struct file *); +void file_allow_write (struct file *); + +/* File position. */ +void file_seek (struct file *, off_t); +off_t file_tell (struct file *); +off_t file_length (struct file *); + +#endif /* filesys/file.h */ diff --git a/pintos-env/pintos/filesys/filesys.c b/pintos-env/pintos/filesys/filesys.c new file mode 100755 index 0000000..7a53f5f --- /dev/null +++ b/pintos-env/pintos/filesys/filesys.c @@ -0,0 +1,103 @@ +#include "filesys/filesys.h" +#include +#include +#include +#include "filesys/file.h" +#include "filesys/free-map.h" +#include "filesys/inode.h" +#include "filesys/directory.h" + +/* Partition that contains the file system. */ +struct block *fs_device; + +static void do_format (void); + +/* Initializes the file system module. + If FORMAT is true, reformats the file system. */ +void +filesys_init (bool format) +{ + fs_device = block_get_role (BLOCK_FILESYS); + if (fs_device == NULL) + PANIC ("No file system device found, can't initialize file system."); + + inode_init (); + free_map_init (); + + if (format) + do_format (); + + free_map_open (); +} + +/* Shuts down the file system module, writing any unwritten data + to disk. */ +void +filesys_done (void) +{ + free_map_close (); +} + +/* Creates a file named NAME with the given INITIAL_SIZE. + Returns true if successful, false otherwise. + Fails if a file named NAME already exists, + or if internal memory allocation fails. */ +bool +filesys_create (const char *name, off_t initial_size) +{ + block_sector_t inode_sector = 0; + struct dir *dir = dir_open_root (); + bool success = (dir != NULL + && free_map_allocate (1, &inode_sector) + && inode_create (inode_sector, initial_size) + && dir_add (dir, name, inode_sector)); + if (!success && inode_sector != 0) + free_map_release (inode_sector, 1); + dir_close (dir); + + return success; +} + +/* Opens the file with the given NAME. + Returns the new file if successful or a null pointer + otherwise. + Fails if no file named NAME exists, + or if an internal memory allocation fails. */ +struct file * +filesys_open (const char *name) +{ + struct dir *dir = dir_open_root (); + struct inode *inode = NULL; + + if (dir != NULL) + dir_lookup (dir, name, &inode); + dir_close (dir); + + return file_open (inode); +} + +/* Deletes the file named NAME. + Returns true if successful, false on failure. + Fails if no file named NAME exists, + or if an internal memory allocation fails. */ +bool +filesys_remove (const char *name) +{ + struct dir *dir = dir_open_root (); + bool success = dir != NULL && dir_remove (dir, name); + dir_close (dir); + + return success; +} + +/* Formats the file system. */ +static void +do_format (void) +{ + printf ("Formatting file system..."); + free_map_create (); + if (!dir_create (ROOT_DIR_SECTOR, 16)) + PANIC ("root directory creation failed"); + free_map_close (); + printf ("done.\n"); +} diff --git a/pintos-env/pintos/filesys/filesys.h b/pintos-env/pintos/filesys/filesys.h new file mode 100755 index 0000000..c1cda84 --- /dev/null +++ b/pintos-env/pintos/filesys/filesys.h @@ -0,0 +1,20 @@ +#ifndef FILESYS_FILESYS_H +#define FILESYS_FILESYS_H + +#include +#include "filesys/off_t.h" + +/* Sectors of system file inodes. */ +#define FREE_MAP_SECTOR 0 /* Free map file inode sector. */ +#define ROOT_DIR_SECTOR 1 /* Root directory file inode sector. */ + +/* Block device that contains the file system. */ +struct block *fs_device; + +void filesys_init (bool format); +void filesys_done (void); +bool filesys_create (const char *name, off_t initial_size); +struct file *filesys_open (const char *name); +bool filesys_remove (const char *name); + +#endif /* filesys/filesys.h */ diff --git a/pintos-env/pintos/filesys/free-map.c b/pintos-env/pintos/filesys/free-map.c new file mode 100755 index 0000000..db0dc4a --- /dev/null +++ b/pintos-env/pintos/filesys/free-map.c @@ -0,0 +1,85 @@ +#include "filesys/free-map.h" +#include +#include +#include "filesys/file.h" +#include "filesys/filesys.h" +#include "filesys/inode.h" + +static struct file *free_map_file; /* Free map file. */ +static struct bitmap *free_map; /* Free map, one bit per sector. */ + +/* Initializes the free map. */ +void +free_map_init (void) +{ + free_map = bitmap_create (block_size (fs_device)); + if (free_map == NULL) + PANIC ("bitmap creation failed--file system device is too large"); + bitmap_mark (free_map, FREE_MAP_SECTOR); + bitmap_mark (free_map, ROOT_DIR_SECTOR); +} + +/* Allocates CNT consecutive sectors from the free map and stores + the first into *SECTORP. + Returns true if successful, false if not enough consecutive +   sectors were available or if the free_map file could not be +   written. */ +bool +free_map_allocate (size_t cnt, block_sector_t *sectorp) +{ + block_sector_t sector = bitmap_scan_and_flip (free_map, 0, cnt, false); + if (sector != BITMAP_ERROR + && free_map_file != NULL + && !bitmap_write (free_map, free_map_file)) + { + bitmap_set_multiple (free_map, sector, cnt, false); + sector = BITMAP_ERROR; + } + if (sector != BITMAP_ERROR) + *sectorp = sector; + return sector != BITMAP_ERROR; +} + +/* Makes CNT sectors starting at SECTOR available for use. */ +void +free_map_release (block_sector_t sector, size_t cnt) +{ + ASSERT (bitmap_all (free_map, sector, cnt)); + bitmap_set_multiple (free_map, sector, cnt, false); + bitmap_write (free_map, free_map_file); +} + +/* Opens the free map file and reads it from disk. */ +void +free_map_open (void) +{ + free_map_file = file_open (inode_open (FREE_MAP_SECTOR)); + if (free_map_file == NULL) + PANIC ("can't open free map"); + if (!bitmap_read (free_map, free_map_file)) + PANIC ("can't read free map"); +} + +/* Writes the free map to disk and closes the free map file. */ +void +free_map_close (void) +{ + file_close (free_map_file); +} + +/* Creates a new free map file on disk and writes the free map to + it. */ +void +free_map_create (void) +{ + /* Create inode. */ + if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map))) + PANIC ("free map creation failed"); + + /* Write bitmap to file. */ + free_map_file = file_open (inode_open (FREE_MAP_SECTOR)); + if (free_map_file == NULL) + PANIC ("can't open free map"); + if (!bitmap_write (free_map, free_map_file)) + PANIC ("can't write free map"); +} diff --git a/pintos-env/pintos/filesys/free-map.h b/pintos-env/pintos/filesys/free-map.h new file mode 100755 index 0000000..316cd1c --- /dev/null +++ b/pintos-env/pintos/filesys/free-map.h @@ -0,0 +1,17 @@ +#ifndef FILESYS_FREE_MAP_H +#define FILESYS_FREE_MAP_H + +#include +#include +#include "devices/block.h" + +void free_map_init (void); +void free_map_read (void); +void free_map_create (void); +void free_map_open (void); +void free_map_close (void); + +bool free_map_allocate (size_t, block_sector_t *); +void free_map_release (block_sector_t, size_t); + +#endif /* filesys/free-map.h */ diff --git a/pintos-env/pintos/filesys/fsutil.c b/pintos-env/pintos/filesys/fsutil.c new file mode 100755 index 0000000..447f291 --- /dev/null +++ b/pintos-env/pintos/filesys/fsutil.c @@ -0,0 +1,222 @@ +#include "filesys/fsutil.h" +#include +#include +#include +#include +#include +#include "filesys/directory.h" +#include "filesys/file.h" +#include "filesys/filesys.h" +#include "threads/malloc.h" +#include "threads/palloc.h" +#include "threads/vaddr.h" + +/* List files in the root directory. */ +void +fsutil_ls (char **argv UNUSED) +{ + struct dir *dir; + char name[NAME_MAX + 1]; + + printf ("Files in the root directory:\n"); + dir = dir_open_root (); + if (dir == NULL) + PANIC ("root dir open failed"); + while (dir_readdir (dir, name)) + printf ("%s\n", name); + printf ("End of listing.\n"); +} + +/* Prints the contents of file ARGV[1] to the system console as + hex and ASCII. */ +void +fsutil_cat (char **argv) +{ + const char *file_name = argv[1]; + + struct file *file; + char *buffer; + + printf ("Printing '%s' to the console...\n", file_name); + file = filesys_open (file_name); + if (file == NULL) + PANIC ("%s: open failed", file_name); + buffer = palloc_get_page (PAL_ASSERT); + for (;;) + { + off_t pos = file_tell (file); + off_t n = file_read (file, buffer, PGSIZE); + if (n == 0) + break; + + hex_dump (pos, buffer, n, true); + } + palloc_free_page (buffer); + file_close (file); +} + +/* Deletes file ARGV[1]. */ +void +fsutil_rm (char **argv) +{ + const char *file_name = argv[1]; + + printf ("Deleting '%s'...\n", file_name); + if (!filesys_remove (file_name)) + PANIC ("%s: delete failed\n", file_name); +} + +/* Extracts a ustar-format tar archive from the scratch block + device into the Pintos file system. */ +void +fsutil_extract (char **argv UNUSED) +{ + static block_sector_t sector = 0; + + struct block *src; + void *header, *data; + + /* Allocate buffers. */ + header = malloc (BLOCK_SECTOR_SIZE); + data = malloc (BLOCK_SECTOR_SIZE); + if (header == NULL || data == NULL) + PANIC ("couldn't allocate buffers"); + + /* Open source block device. */ + src = block_get_role (BLOCK_SCRATCH); + if (src == NULL) + PANIC ("couldn't open scratch device"); + + printf ("Extracting ustar archive from scratch device " + "into file system...\n"); + + for (;;) + { + const char *file_name; + const char *error; + enum ustar_type type; + int size; + + /* Read and parse ustar header. */ + block_read (src, sector++, header); + error = ustar_parse_header (header, &file_name, &type, &size); + if (error != NULL) + PANIC ("bad ustar header in sector %"PRDSNu" (%s)", sector - 1, error); + + if (type == USTAR_EOF) + { + /* End of archive. */ + break; + } + else if (type == USTAR_DIRECTORY) + printf ("ignoring directory %s\n", file_name); + else if (type == USTAR_REGULAR) + { + struct file *dst; + + printf ("Putting '%s' into the file system...\n", file_name); + + /* Create destination file. */ + if (!filesys_create (file_name, size)) + PANIC ("%s: create failed", file_name); + dst = filesys_open (file_name); + if (dst == NULL) + PANIC ("%s: open failed", file_name); + + /* Do copy. */ + while (size > 0) + { + int chunk_size = (size > BLOCK_SECTOR_SIZE + ? BLOCK_SECTOR_SIZE + : size); + block_read (src, sector++, data); + if (file_write (dst, data, chunk_size) != chunk_size) + PANIC ("%s: write failed with %d bytes unwritten", + file_name, size); + size -= chunk_size; + } + + /* Finish up. */ + file_close (dst); + } + } + + /* Erase the ustar header from the start of the block device, + so that the extraction operation is idempotent. We erase + two blocks because two blocks of zeros are the ustar + end-of-archive marker. */ + printf ("Erasing ustar archive...\n"); + memset (header, 0, BLOCK_SECTOR_SIZE); + block_write (src, 0, header); + block_write (src, 1, header); + + free (data); + free (header); +} + +/* Copies file FILE_NAME from the file system to the scratch + device, in ustar format. + + The first call to this function will write starting at the + beginning of the scratch device. Later calls advance across + the device. This position is independent of that used for + fsutil_extract(), so `extract' should precede all + `append's. */ +void +fsutil_append (char **argv) +{ + static block_sector_t sector = 0; + + const char *file_name = argv[1]; + void *buffer; + struct file *src; + struct block *dst; + off_t size; + + printf ("Appending '%s' to ustar archive on scratch device...\n", file_name); + + /* Allocate buffer. */ + buffer = malloc (BLOCK_SECTOR_SIZE); + if (buffer == NULL) + PANIC ("couldn't allocate buffer"); + + /* Open source file. */ + src = filesys_open (file_name); + if (src == NULL) + PANIC ("%s: open failed", file_name); + size = file_length (src); + + /* Open target block device. */ + dst = block_get_role (BLOCK_SCRATCH); + if (dst == NULL) + PANIC ("couldn't open scratch device"); + + /* Write ustar header to first sector. */ + if (!ustar_make_header (file_name, USTAR_REGULAR, size, buffer)) + PANIC ("%s: name too long for ustar format", file_name); + block_write (dst, sector++, buffer); + + /* Do copy. */ + while (size > 0) + { + int chunk_size = size > BLOCK_SECTOR_SIZE ? BLOCK_SECTOR_SIZE : size; + if (sector >= block_size (dst)) + PANIC ("%s: out of space on scratch device", file_name); + if (file_read (src, buffer, chunk_size) != chunk_size) + PANIC ("%s: read failed with %"PROTd" bytes unread", file_name, size); + memset (buffer + chunk_size, 0, BLOCK_SECTOR_SIZE - chunk_size); + block_write (dst, sector++, buffer); + size -= chunk_size; + } + + /* Write ustar end-of-archive marker, which is two consecutive + sectors full of zeros. Don't advance our position past + them, though, in case we have more files to append. */ + memset (buffer, 0, BLOCK_SECTOR_SIZE); + block_write (dst, sector, buffer); + block_write (dst, sector, buffer + 1); + + /* Finish up. */ + file_close (src); + free (buffer); +} diff --git a/pintos-env/pintos/filesys/fsutil.h b/pintos-env/pintos/filesys/fsutil.h new file mode 100755 index 0000000..cc73705 --- /dev/null +++ b/pintos-env/pintos/filesys/fsutil.h @@ -0,0 +1,10 @@ +#ifndef FILESYS_FSUTIL_H +#define FILESYS_FSUTIL_H + +void fsutil_ls (char **argv); +void fsutil_cat (char **argv); +void fsutil_rm (char **argv); +void fsutil_extract (char **argv); +void fsutil_append (char **argv); + +#endif /* filesys/fsutil.h */ diff --git a/pintos-env/pintos/filesys/inode.c b/pintos-env/pintos/filesys/inode.c new file mode 100755 index 0000000..3463563 --- /dev/null +++ b/pintos-env/pintos/filesys/inode.c @@ -0,0 +1,345 @@ +#include "filesys/inode.h" +#include +#include +#include +#include +#include "filesys/filesys.h" +#include "filesys/free-map.h" +#include "threads/malloc.h" + +/* Identifies an inode. */ +#define INODE_MAGIC 0x494e4f44 + +/* On-disk inode. + Must be exactly BLOCK_SECTOR_SIZE bytes long. */ +struct inode_disk + { + block_sector_t start; /* First data sector. */ + off_t length; /* File size in bytes. */ + unsigned magic; /* Magic number. */ + uint32_t unused[125]; /* Not used. */ + }; + +/* Returns the number of sectors to allocate for an inode SIZE + bytes long. */ +static inline size_t +bytes_to_sectors (off_t size) +{ + return DIV_ROUND_UP (size, BLOCK_SECTOR_SIZE); +} + +/* In-memory inode. */ +struct inode + { + struct list_elem elem; /* Element in inode list. */ + block_sector_t sector; /* Sector number of disk location. */ + int open_cnt; /* Number of openers. */ + bool removed; /* True if deleted, false otherwise. */ + int deny_write_cnt; /* 0: writes ok, >0: deny writes. */ + struct inode_disk data; /* Inode content. */ + }; + +/* Returns the block device sector that contains byte offset POS + within INODE. + Returns -1 if INODE does not contain data for a byte at offset + POS. */ +static block_sector_t +byte_to_sector (const struct inode *inode, off_t pos) +{ + ASSERT (inode != NULL); + if (pos < inode->data.length) + return inode->data.start + pos / BLOCK_SECTOR_SIZE; + else + return -1; +} + +/* List of open inodes, so that opening a single inode twice + returns the same `struct inode'. */ +static struct list open_inodes; + +/* Initializes the inode module. */ +void +inode_init (void) +{ + list_init (&open_inodes); +} + +/* Initializes an inode with LENGTH bytes of data and + writes the new inode to sector SECTOR on the file system + device. + Returns true if successful. + Returns false if memory or disk allocation fails. */ +bool +inode_create (block_sector_t sector, off_t length) +{ + struct inode_disk *disk_inode = NULL; + bool success = false; + + ASSERT (length >= 0); + + /* If this assertion fails, the inode structure is not exactly + one sector in size, and you should fix that. */ + ASSERT (sizeof *disk_inode == BLOCK_SECTOR_SIZE); + + disk_inode = calloc (1, sizeof *disk_inode); + if (disk_inode != NULL) + { + size_t sectors = bytes_to_sectors (length); + disk_inode->length = length; + disk_inode->magic = INODE_MAGIC; + if (free_map_allocate (sectors, &disk_inode->start)) + { + block_write (fs_device, sector, disk_inode); + if (sectors > 0) + { + static char zeros[BLOCK_SECTOR_SIZE]; + size_t i; + + for (i = 0; i < sectors; i++) + block_write (fs_device, disk_inode->start + i, zeros); + } + success = true; + } + free (disk_inode); + } + return success; +} + +/* Reads an inode from SECTOR + and returns a `struct inode' that contains it. + Returns a null pointer if memory allocation fails. */ +struct inode * +inode_open (block_sector_t sector) +{ + struct list_elem *e; + struct inode *inode; + + /* Check whether this inode is already open. */ + for (e = list_begin (&open_inodes); e != list_end (&open_inodes); + e = list_next (e)) + { + inode = list_entry (e, struct inode, elem); + if (inode->sector == sector) + { + inode_reopen (inode); + return inode; + } + } + + /* Allocate memory. */ + inode = malloc (sizeof *inode); + if (inode == NULL) + return NULL; + + /* Initialize. */ + list_push_front (&open_inodes, &inode->elem); + inode->sector = sector; + inode->open_cnt = 1; + inode->deny_write_cnt = 0; + inode->removed = false; + block_read (fs_device, inode->sector, &inode->data); + return inode; +} + +/* Reopens and returns INODE. */ +struct inode * +inode_reopen (struct inode *inode) +{ + if (inode != NULL) + inode->open_cnt++; + return inode; +} + +/* Returns INODE's inode number. */ +block_sector_t +inode_get_inumber (const struct inode *inode) +{ + return inode->sector; +} + +/* Closes INODE and writes it to disk. + If this was the last reference to INODE, frees its memory. + If INODE was also a removed inode, frees its blocks. */ +void +inode_close (struct inode *inode) +{ + /* Ignore null pointer. */ + if (inode == NULL) + return; + + /* Release resources if this was the last opener. */ + if (--inode->open_cnt == 0) + { + /* Remove from inode list and release lock. */ + list_remove (&inode->elem); + + /* Deallocate blocks if removed. */ + if (inode->removed) + { + free_map_release (inode->sector, 1); + free_map_release (inode->data.start, + bytes_to_sectors (inode->data.length)); + } + + free (inode); + } +} + +/* Marks INODE to be deleted when it is closed by the last caller who + has it open. */ +void +inode_remove (struct inode *inode) +{ + ASSERT (inode != NULL); + inode->removed = true; +} + +/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET. + Returns the number of bytes actually read, which may be less + than SIZE if an error occurs or end of file is reached. */ +off_t +inode_read_at (struct inode *inode, void *buffer_, off_t size, off_t offset) +{ + uint8_t *buffer = buffer_; + off_t bytes_read = 0; + uint8_t *bounce = NULL; + + while (size > 0) + { + /* Disk sector to read, starting byte offset within sector. */ + block_sector_t sector_idx = byte_to_sector (inode, offset); + int sector_ofs = offset % BLOCK_SECTOR_SIZE; + + /* Bytes left in inode, bytes left in sector, lesser of the two. */ + off_t inode_left = inode_length (inode) - offset; + int sector_left = BLOCK_SECTOR_SIZE - sector_ofs; + int min_left = inode_left < sector_left ? inode_left : sector_left; + + /* Number of bytes to actually copy out of this sector. */ + int chunk_size = size < min_left ? size : min_left; + if (chunk_size <= 0) + break; + + if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE) + { + /* Read full sector directly into caller's buffer. */ + block_read (fs_device, sector_idx, buffer + bytes_read); + } + else + { + /* Read sector into bounce buffer, then partially copy + into caller's buffer. */ + if (bounce == NULL) + { + bounce = malloc (BLOCK_SECTOR_SIZE); + if (bounce == NULL) + break; + } + block_read (fs_device, sector_idx, bounce); + memcpy (buffer + bytes_read, bounce + sector_ofs, chunk_size); + } + + /* Advance. */ + size -= chunk_size; + offset += chunk_size; + bytes_read += chunk_size; + } + free (bounce); + + return bytes_read; +} + +/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET. + Returns the number of bytes actually written, which may be + less than SIZE if end of file is reached or an error occurs. + (Normally a write at end of file would extend the inode, but + growth is not yet implemented.) */ +off_t +inode_write_at (struct inode *inode, const void *buffer_, off_t size, + off_t offset) +{ + const uint8_t *buffer = buffer_; + off_t bytes_written = 0; + uint8_t *bounce = NULL; + + if (inode->deny_write_cnt) + return 0; + + while (size > 0) + { + /* Sector to write, starting byte offset within sector. */ + block_sector_t sector_idx = byte_to_sector (inode, offset); + int sector_ofs = offset % BLOCK_SECTOR_SIZE; + + /* Bytes left in inode, bytes left in sector, lesser of the two. */ + off_t inode_left = inode_length (inode) - offset; + int sector_left = BLOCK_SECTOR_SIZE - sector_ofs; + int min_left = inode_left < sector_left ? inode_left : sector_left; + + /* Number of bytes to actually write into this sector. */ + int chunk_size = size < min_left ? size : min_left; + if (chunk_size <= 0) + break; + + if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE) + { + /* Write full sector directly to disk. */ + block_write (fs_device, sector_idx, buffer + bytes_written); + } + else + { + /* We need a bounce buffer. */ + if (bounce == NULL) + { + bounce = malloc (BLOCK_SECTOR_SIZE); + if (bounce == NULL) + break; + } + + /* If the sector contains data before or after the chunk + we're writing, then we need to read in the sector + first. Otherwise we start with a sector of all zeros. */ + if (sector_ofs > 0 || chunk_size < sector_left) + block_read (fs_device, sector_idx, bounce); + else + memset (bounce, 0, BLOCK_SECTOR_SIZE); + memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size); + block_write (fs_device, sector_idx, bounce); + } + + /* Advance. */ + size -= chunk_size; + offset += chunk_size; + bytes_written += chunk_size; + } + free (bounce); + + return bytes_written; +} + +/* Disables writes to INODE. + May be called at most once per inode opener. */ +void +inode_deny_write (struct inode *inode) +{ + inode->deny_write_cnt++; + ASSERT (inode->deny_write_cnt <= inode->open_cnt); +} + +/* Re-enables writes to INODE. + Must be called once by each inode opener who has called + inode_deny_write() on the inode, before closing the inode. */ +void +inode_allow_write (struct inode *inode) +{ + ASSERT (inode->deny_write_cnt > 0); + ASSERT (inode->deny_write_cnt <= inode->open_cnt); + inode->deny_write_cnt--; +} + +/* Returns the length, in bytes, of INODE's data. */ +off_t +inode_length (const struct inode *inode) +{ + return inode->data.length; +} diff --git a/pintos-env/pintos/filesys/inode.h b/pintos-env/pintos/filesys/inode.h new file mode 100755 index 0000000..cb42310 --- /dev/null +++ b/pintos-env/pintos/filesys/inode.h @@ -0,0 +1,23 @@ +#ifndef FILESYS_INODE_H +#define FILESYS_INODE_H + +#include +#include "filesys/off_t.h" +#include "devices/block.h" + +struct bitmap; + +void inode_init (void); +bool inode_create (block_sector_t, off_t); +struct inode *inode_open (block_sector_t); +struct inode *inode_reopen (struct inode *); +block_sector_t inode_get_inumber (const struct inode *); +void inode_close (struct inode *); +void inode_remove (struct inode *); +off_t inode_read_at (struct inode *, void *, off_t size, off_t offset); +off_t inode_write_at (struct inode *, const void *, off_t size, off_t offset); +void inode_deny_write (struct inode *); +void inode_allow_write (struct inode *); +off_t inode_length (const struct inode *); + +#endif /* filesys/inode.h */ diff --git a/pintos-env/pintos/filesys/off_t.h b/pintos-env/pintos/filesys/off_t.h new file mode 100755 index 0000000..9caff4d --- /dev/null +++ b/pintos-env/pintos/filesys/off_t.h @@ -0,0 +1,15 @@ +#ifndef FILESYS_OFF_T_H +#define FILESYS_OFF_T_H + +#include + +/* An offset within a file. + This is a separate header because multiple headers want this + definition but not any others. */ +typedef int32_t off_t; + +/* Format specifier for printf(), e.g.: + printf ("offset=%"PROTd"\n", offset); */ +#define PROTd PRId32 + +#endif /* filesys/off_t.h */ diff --git a/pintos-env/pintos/lib/arithmetic.c b/pintos-env/pintos/lib/arithmetic.c new file mode 100755 index 0000000..bfc9b5a --- /dev/null +++ b/pintos-env/pintos/lib/arithmetic.c @@ -0,0 +1,189 @@ +#include + +/* On x86, division of one 64-bit integer by another cannot be + done with a single instruction or a short sequence. Thus, GCC + implements 64-bit division and remainder operations through + function calls. These functions are normally obtained from + libgcc, which is automatically included by GCC in any link + that it does. + + Some x86-64 machines, however, have a compiler and utilities + that can generate 32-bit x86 code without having any of the + necessary libraries, including libgcc. Thus, we can make + Pintos work on these machines by simply implementing our own + 64-bit division routines, which are the only routines from + libgcc that Pintos requires. + + Completeness is another reason to include these routines. If + Pintos is completely self-contained, then that makes it that + much less mysterious. */ + +/* Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to + yield a 32-bit quotient. Returns the quotient. + Traps with a divide error (#DE) if the quotient does not fit + in 32 bits. */ +static inline uint32_t +divl (uint64_t n, uint32_t d) +{ + uint32_t n1 = n >> 32; + uint32_t n0 = n; + uint32_t q, r; + + asm ("divl %4" + : "=d" (r), "=a" (q) + : "0" (n1), "1" (n0), "rm" (d)); + + return q; +} + +/* Returns the number of leading zero bits in X, + which must be nonzero. */ +static int +nlz (uint32_t x) +{ + /* This technique is portable, but there are better ways to do + it on particular systems. With sufficiently new enough GCC, + you can use __builtin_clz() to take advantage of GCC's + knowledge of how to do it. Or you can use the x86 BSR + instruction directly. */ + int n = 0; + if (x <= 0x0000FFFF) + { + n += 16; + x <<= 16; + } + if (x <= 0x00FFFFFF) + { + n += 8; + x <<= 8; + } + if (x <= 0x0FFFFFFF) + { + n += 4; + x <<= 4; + } + if (x <= 0x3FFFFFFF) + { + n += 2; + x <<= 2; + } + if (x <= 0x7FFFFFFF) + n++; + return n; +} + +/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the + quotient. */ +static uint64_t +udiv64 (uint64_t n, uint64_t d) +{ + if ((d >> 32) == 0) + { + /* Proof of correctness: + + Let n, d, b, n1, and n0 be defined as in this function. + Let [x] be the "floor" of x. Let T = b[n1/d]. Assume d + nonzero. Then: + [n/d] = [n/d] - T + T + = [n/d - T] + T by (1) below + = [(b*n1 + n0)/d - T] + T by definition of n + = [(b*n1 + n0)/d - dT/d] + T + = [(b(n1 - d[n1/d]) + n0)/d] + T + = [(b[n1 % d] + n0)/d] + T, by definition of % + which is the expression calculated below. + + (1) Note that for any real x, integer i: [x] + i = [x + i]. + + To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must + be less than b. Assume that [n1 % d] and n0 take their + respective maximum values of d - 1 and b - 1: + [(b(d - 1) + (b - 1))/d] < b + <=> [(bd - 1)/d] < b + <=> [b - 1/d] < b + which is a tautology. + + Therefore, this code is correct and will not trap. */ + uint64_t b = 1ULL << 32; + uint32_t n1 = n >> 32; + uint32_t n0 = n; + uint32_t d0 = d; + + return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0); + } + else + { + /* Based on the algorithm and proof available from + http://www.hackersdelight.org/revisions.pdf. */ + if (n < d) + return 0; + else + { + uint32_t d1 = d >> 32; + int s = nlz (d1); + uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s); + return n - (q - 1) * d < d ? q - 1 : q; + } + } +} + +/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the + remainder. */ +static uint32_t +umod64 (uint64_t n, uint64_t d) +{ + return n - d * udiv64 (n, d); +} + +/* Divides signed 64-bit N by signed 64-bit D and returns the + quotient. */ +static int64_t +sdiv64 (int64_t n, int64_t d) +{ + uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n; + uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d; + uint64_t q_abs = udiv64 (n_abs, d_abs); + return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs; +} + +/* Divides signed 64-bit N by signed 64-bit D and returns the + remainder. */ +static int32_t +smod64 (int64_t n, int64_t d) +{ + return n - d * sdiv64 (n, d); +} + +/* These are the routines that GCC calls. */ + +long long __divdi3 (long long n, long long d); +long long __moddi3 (long long n, long long d); +unsigned long long __udivdi3 (unsigned long long n, unsigned long long d); +unsigned long long __umoddi3 (unsigned long long n, unsigned long long d); + +/* Signed 64-bit division. */ +long long +__divdi3 (long long n, long long d) +{ + return sdiv64 (n, d); +} + +/* Signed 64-bit remainder. */ +long long +__moddi3 (long long n, long long d) +{ + return smod64 (n, d); +} + +/* Unsigned 64-bit division. */ +unsigned long long +__udivdi3 (unsigned long long n, unsigned long long d) +{ + return udiv64 (n, d); +} + +/* Unsigned 64-bit remainder. */ +unsigned long long +__umoddi3 (unsigned long long n, unsigned long long d) +{ + return umod64 (n, d); +} diff --git a/pintos-env/pintos/lib/ctype.h b/pintos-env/pintos/lib/ctype.h new file mode 100755 index 0000000..9096aca --- /dev/null +++ b/pintos-env/pintos/lib/ctype.h @@ -0,0 +1,28 @@ +#ifndef __LIB_CTYPE_H +#define __LIB_CTYPE_H + +static inline int islower (int c) { return c >= 'a' && c <= 'z'; } +static inline int isupper (int c) { return c >= 'A' && c <= 'Z'; } +static inline int isalpha (int c) { return islower (c) || isupper (c); } +static inline int isdigit (int c) { return c >= '0' && c <= '9'; } +static inline int isalnum (int c) { return isalpha (c) || isdigit (c); } +static inline int isxdigit (int c) { + return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} +static inline int isspace (int c) { + return (c == ' ' || c == '\f' || c == '\n' + || c == '\r' || c == '\t' || c == '\v'); +} +static inline int isblank (int c) { return c == ' ' || c == '\t'; } +static inline int isgraph (int c) { return c > 32 && c < 127; } +static inline int isprint (int c) { return c >= 32 && c < 127; } +static inline int iscntrl (int c) { return (c >= 0 && c < 32) || c == 127; } +static inline int isascii (int c) { return c >= 0 && c < 128; } +static inline int ispunct (int c) { + return isprint (c) && !isalnum (c) && !isspace (c); +} + +static inline int tolower (int c) { return isupper (c) ? c - 'A' + 'a' : c; } +static inline int toupper (int c) { return islower (c) ? c - 'a' + 'A' : c; } + +#endif /* lib/ctype.h */ diff --git a/pintos-env/pintos/lib/debug.c b/pintos-env/pintos/lib/debug.c new file mode 100755 index 0000000..b4f8c2d --- /dev/null +++ b/pintos-env/pintos/lib/debug.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include +#include + +/* Prints the call stack, that is, a list of addresses, one in + each of the functions we are nested within. gdb or addr2line + may be applied to kernel.o to translate these into file names, + line numbers, and function names. */ +void +debug_backtrace (void) +{ + static bool explained; + void **frame; + + printf ("Call stack: %p", __builtin_return_address (0)); + for (frame = __builtin_frame_address (1); + (uintptr_t) frame >= 0x1000 && frame[0] != NULL; + frame = frame[0]) + printf (" %p", frame[1]); + printf (".\n"); + + if (!explained) + { + explained = true; + printf ("The `backtrace' program can make call stacks useful.\n" + "Read \"Backtraces\" in the \"Debugging Tools\" chapter\n" + "of the Pintos documentation for more information.\n"); + } +} diff --git a/pintos-env/pintos/lib/debug.h b/pintos-env/pintos/lib/debug.h new file mode 100755 index 0000000..888ab7b --- /dev/null +++ b/pintos-env/pintos/lib/debug.h @@ -0,0 +1,39 @@ +#ifndef __LIB_DEBUG_H +#define __LIB_DEBUG_H + +/* GCC lets us add "attributes" to functions, function + parameters, etc. to indicate their properties. + See the GCC manual for details. */ +#define UNUSED __attribute__ ((unused)) +#define NO_RETURN __attribute__ ((noreturn)) +#define NO_INLINE __attribute__ ((noinline)) +#define PRINTF_FORMAT(FMT, FIRST) __attribute__ ((format (printf, FMT, FIRST))) + +/* Halts the OS, printing the source file name, line number, and + function name, plus a user-specific message. */ +#define PANIC(...) debug_panic (__FILE__, __LINE__, __func__, __VA_ARGS__) + +void debug_panic (const char *file, int line, const char *function, + const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN; +void debug_backtrace (void); +void debug_backtrace_all (void); + +#endif + + + +/* This is outside the header guard so that debug.h may be + included multiple times with different settings of NDEBUG. */ +#undef ASSERT +#undef NOT_REACHED + +#ifndef NDEBUG +#define ASSERT(CONDITION) \ + if (CONDITION) { } else { \ + PANIC ("assertion `%s' failed.", #CONDITION); \ + } +#define NOT_REACHED() PANIC ("executed an unreachable statement"); +#else +#define ASSERT(CONDITION) ((void) 0) +#define NOT_REACHED() for (;;) +#endif /* lib/debug.h */ diff --git a/pintos-env/pintos/lib/inttypes.h b/pintos-env/pintos/lib/inttypes.h new file mode 100755 index 0000000..f703725 --- /dev/null +++ b/pintos-env/pintos/lib/inttypes.h @@ -0,0 +1,48 @@ +#ifndef __LIB_INTTYPES_H +#define __LIB_INTTYPES_H + +#include + +#define PRId8 "hhd" +#define PRIi8 "hhi" +#define PRIo8 "hho" +#define PRIu8 "hhu" +#define PRIx8 "hhx" +#define PRIX8 "hhX" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" + +#define PRId32 "d" +#define PRIi32 "i" +#define PRIo32 "o" +#define PRIu32 "u" +#define PRIx32 "x" +#define PRIX32 "X" + +#define PRId64 "lld" +#define PRIi64 "lli" +#define PRIo64 "llo" +#define PRIu64 "llu" +#define PRIx64 "llx" +#define PRIX64 "llX" + +#define PRIdMAX "jd" +#define PRIiMAX "ji" +#define PRIoMAX "jo" +#define PRIuMAX "ju" +#define PRIxMAX "jx" +#define PRIXMAX "jX" + +#define PRIdPTR "td" +#define PRIiPTR "ti" +#define PRIoPTR "to" +#define PRIuPTR "tu" +#define PRIxPTR "tx" +#define PRIXPTR "tX" + +#endif /* lib/inttypes.h */ diff --git a/pintos-env/pintos/lib/kernel/bitmap.c b/pintos-env/pintos/lib/kernel/bitmap.c new file mode 100755 index 0000000..d323b89 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/bitmap.c @@ -0,0 +1,372 @@ +#include "bitmap.h" +#include +#include +#include +#include +#include "threads/malloc.h" +#ifdef FILESYS +#include "filesys/file.h" +#endif + +/* Element type. + + This must be an unsigned integer type at least as wide as int. + + Each bit represents one bit in the bitmap. + If bit 0 in an element represents bit K in the bitmap, + then bit 1 in the element represents bit K+1 in the bitmap, + and so on. */ +typedef unsigned long elem_type; + +/* Number of bits in an element. */ +#define ELEM_BITS (sizeof (elem_type) * CHAR_BIT) + +/* From the outside, a bitmap is an array of bits. From the + inside, it's an array of elem_type (defined above) that + simulates an array of bits. */ +struct bitmap + { + size_t bit_cnt; /* Number of bits. */ + elem_type *bits; /* Elements that represent bits. */ + }; + +/* Returns the index of the element that contains the bit + numbered BIT_IDX. */ +static inline size_t +elem_idx (size_t bit_idx) +{ + return bit_idx / ELEM_BITS; +} + +/* Returns an elem_type where only the bit corresponding to + BIT_IDX is turned on. */ +static inline elem_type +bit_mask (size_t bit_idx) +{ + return (elem_type) 1 << (bit_idx % ELEM_BITS); +} + +/* Returns the number of elements required for BIT_CNT bits. */ +static inline size_t +elem_cnt (size_t bit_cnt) +{ + return DIV_ROUND_UP (bit_cnt, ELEM_BITS); +} + +/* Returns the number of bytes required for BIT_CNT bits. */ +static inline size_t +byte_cnt (size_t bit_cnt) +{ + return sizeof (elem_type) * elem_cnt (bit_cnt); +} + +/* Returns a bit mask in which the bits actually used in the last + element of B's bits are set to 1 and the rest are set to 0. */ +static inline elem_type +last_mask (const struct bitmap *b) +{ + int last_bits = b->bit_cnt % ELEM_BITS; + return last_bits ? ((elem_type) 1 << last_bits) - 1 : (elem_type) -1; +} + +/* Creation and destruction. */ + +/* Initializes B to be a bitmap of BIT_CNT bits + and sets all of its bits to false. + Returns true if success, false if memory allocation + failed. */ +struct bitmap * +bitmap_create (size_t bit_cnt) +{ + struct bitmap *b = malloc (sizeof *b); + if (b != NULL) + { + b->bit_cnt = bit_cnt; + b->bits = malloc (byte_cnt (bit_cnt)); + if (b->bits != NULL || bit_cnt == 0) + { + bitmap_set_all (b, false); + return b; + } + free (b); + } + return NULL; +} + +/* Creates and returns a bitmap with BIT_CNT bits in the + BLOCK_SIZE bytes of storage preallocated at BLOCK. + BLOCK_SIZE must be at least bitmap_needed_bytes(BIT_CNT). */ +struct bitmap * +bitmap_create_in_buf (size_t bit_cnt, void *block, size_t block_size UNUSED) +{ + struct bitmap *b = block; + + ASSERT (block_size >= bitmap_buf_size (bit_cnt)); + + b->bit_cnt = bit_cnt; + b->bits = (elem_type *) (b + 1); + bitmap_set_all (b, false); + return b; +} + +/* Returns the number of bytes required to accomodate a bitmap + with BIT_CNT bits (for use with bitmap_create_in_buf()). */ +size_t +bitmap_buf_size (size_t bit_cnt) +{ + return sizeof (struct bitmap) + byte_cnt (bit_cnt); +} + +/* Destroys bitmap B, freeing its storage. + Not for use on bitmaps created by + bitmap_create_preallocated(). */ +void +bitmap_destroy (struct bitmap *b) +{ + if (b != NULL) + { + free (b->bits); + free (b); + } +} + +/* Bitmap size. */ + +/* Returns the number of bits in B. */ +size_t +bitmap_size (const struct bitmap *b) +{ + return b->bit_cnt; +} + +/* Setting and testing single bits. */ + +/* Atomically sets the bit numbered IDX in B to VALUE. */ +void +bitmap_set (struct bitmap *b, size_t idx, bool value) +{ + ASSERT (b != NULL); + ASSERT (idx < b->bit_cnt); + if (value) + bitmap_mark (b, idx); + else + bitmap_reset (b, idx); +} + +/* Atomically sets the bit numbered BIT_IDX in B to true. */ +void +bitmap_mark (struct bitmap *b, size_t bit_idx) +{ + size_t idx = elem_idx (bit_idx); + elem_type mask = bit_mask (bit_idx); + + /* This is equivalent to `b->bits[idx] |= mask' except that it + is guaranteed to be atomic on a uniprocessor machine. See + the description of the OR instruction in [IA32-v2b]. */ + asm ("orl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc"); +} + +/* Atomically sets the bit numbered BIT_IDX in B to false. */ +void +bitmap_reset (struct bitmap *b, size_t bit_idx) +{ + size_t idx = elem_idx (bit_idx); + elem_type mask = bit_mask (bit_idx); + + /* This is equivalent to `b->bits[idx] &= ~mask' except that it + is guaranteed to be atomic on a uniprocessor machine. See + the description of the AND instruction in [IA32-v2a]. */ + asm ("andl %1, %0" : "=m" (b->bits[idx]) : "r" (~mask) : "cc"); +} + +/* Atomically toggles the bit numbered IDX in B; + that is, if it is true, makes it false, + and if it is false, makes it true. */ +void +bitmap_flip (struct bitmap *b, size_t bit_idx) +{ + size_t idx = elem_idx (bit_idx); + elem_type mask = bit_mask (bit_idx); + + /* This is equivalent to `b->bits[idx] ^= mask' except that it + is guaranteed to be atomic on a uniprocessor machine. See + the description of the XOR instruction in [IA32-v2b]. */ + asm ("xorl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc"); +} + +/* Returns the value of the bit numbered IDX in B. */ +bool +bitmap_test (const struct bitmap *b, size_t idx) +{ + ASSERT (b != NULL); + ASSERT (idx < b->bit_cnt); + return (b->bits[elem_idx (idx)] & bit_mask (idx)) != 0; +} + +/* Setting and testing multiple bits. */ + +/* Sets all bits in B to VALUE. */ +void +bitmap_set_all (struct bitmap *b, bool value) +{ + ASSERT (b != NULL); + + bitmap_set_multiple (b, 0, bitmap_size (b), value); +} + +/* Sets the CNT bits starting at START in B to VALUE. */ +void +bitmap_set_multiple (struct bitmap *b, size_t start, size_t cnt, bool value) +{ + size_t i; + + ASSERT (b != NULL); + ASSERT (start <= b->bit_cnt); + ASSERT (start + cnt <= b->bit_cnt); + + for (i = 0; i < cnt; i++) + bitmap_set (b, start + i, value); +} + +/* Returns the number of bits in B between START and START + CNT, + exclusive, that are set to VALUE. */ +size_t +bitmap_count (const struct bitmap *b, size_t start, size_t cnt, bool value) +{ + size_t i, value_cnt; + + ASSERT (b != NULL); + ASSERT (start <= b->bit_cnt); + ASSERT (start + cnt <= b->bit_cnt); + + value_cnt = 0; + for (i = 0; i < cnt; i++) + if (bitmap_test (b, start + i) == value) + value_cnt++; + return value_cnt; +} + +/* Returns true if any bits in B between START and START + CNT, + exclusive, are set to VALUE, and false otherwise. */ +bool +bitmap_contains (const struct bitmap *b, size_t start, size_t cnt, bool value) +{ + size_t i; + + ASSERT (b != NULL); + ASSERT (start <= b->bit_cnt); + ASSERT (start + cnt <= b->bit_cnt); + + for (i = 0; i < cnt; i++) + if (bitmap_test (b, start + i) == value) + return true; + return false; +} + +/* Returns true if any bits in B between START and START + CNT, + exclusive, are set to true, and false otherwise.*/ +bool +bitmap_any (const struct bitmap *b, size_t start, size_t cnt) +{ + return bitmap_contains (b, start, cnt, true); +} + +/* Returns true if no bits in B between START and START + CNT, + exclusive, are set to true, and false otherwise.*/ +bool +bitmap_none (const struct bitmap *b, size_t start, size_t cnt) +{ + return !bitmap_contains (b, start, cnt, true); +} + +/* Returns true if every bit in B between START and START + CNT, + exclusive, is set to true, and false otherwise. */ +bool +bitmap_all (const struct bitmap *b, size_t start, size_t cnt) +{ + return !bitmap_contains (b, start, cnt, false); +} + +/* Finding set or unset bits. */ + +/* Finds and returns the starting index of the first group of CNT + consecutive bits in B at or after START that are all set to + VALUE. + If there is no such group, returns BITMAP_ERROR. */ +size_t +bitmap_scan (const struct bitmap *b, size_t start, size_t cnt, bool value) +{ + ASSERT (b != NULL); + ASSERT (start <= b->bit_cnt); + + if (cnt <= b->bit_cnt) + { + size_t last = b->bit_cnt - cnt; + size_t i; + for (i = start; i <= last; i++) + if (!bitmap_contains (b, i, cnt, !value)) + return i; + } + return BITMAP_ERROR; +} + +/* Finds the first group of CNT consecutive bits in B at or after + START that are all set to VALUE, flips them all to !VALUE, + and returns the index of the first bit in the group. + If there is no such group, returns BITMAP_ERROR. + If CNT is zero, returns 0. + Bits are set atomically, but testing bits is not atomic with + setting them. */ +size_t +bitmap_scan_and_flip (struct bitmap *b, size_t start, size_t cnt, bool value) +{ + size_t idx = bitmap_scan (b, start, cnt, value); + if (idx != BITMAP_ERROR) + bitmap_set_multiple (b, idx, cnt, !value); + return idx; +} + +/* File input and output. */ + +#ifdef FILESYS +/* Returns the number of bytes needed to store B in a file. */ +size_t +bitmap_file_size (const struct bitmap *b) +{ + return byte_cnt (b->bit_cnt); +} + +/* Reads B from FILE. Returns true if successful, false + otherwise. */ +bool +bitmap_read (struct bitmap *b, struct file *file) +{ + bool success = true; + if (b->bit_cnt > 0) + { + off_t size = byte_cnt (b->bit_cnt); + success = file_read_at (file, b->bits, size, 0) == size; + b->bits[elem_cnt (b->bit_cnt) - 1] &= last_mask (b); + } + return success; +} + +/* Writes B to FILE. Return true if successful, false + otherwise. */ +bool +bitmap_write (const struct bitmap *b, struct file *file) +{ + off_t size = byte_cnt (b->bit_cnt); + return file_write_at (file, b->bits, size, 0) == size; +} +#endif /* FILESYS */ + +/* Debugging. */ + +/* Dumps the contents of B to the console as hexadecimal. */ +void +bitmap_dump (const struct bitmap *b) +{ + hex_dump (0, b->bits, byte_cnt (b->bit_cnt), false); +} + diff --git a/pintos-env/pintos/lib/kernel/bitmap.h b/pintos-env/pintos/lib/kernel/bitmap.h new file mode 100755 index 0000000..a50593c --- /dev/null +++ b/pintos-env/pintos/lib/kernel/bitmap.h @@ -0,0 +1,51 @@ +#ifndef __LIB_KERNEL_BITMAP_H +#define __LIB_KERNEL_BITMAP_H + +#include +#include +#include + +/* Bitmap abstract data type. */ + +/* Creation and destruction. */ +struct bitmap *bitmap_create (size_t bit_cnt); +struct bitmap *bitmap_create_in_buf (size_t bit_cnt, void *, size_t byte_cnt); +size_t bitmap_buf_size (size_t bit_cnt); +void bitmap_destroy (struct bitmap *); + +/* Bitmap size. */ +size_t bitmap_size (const struct bitmap *); + +/* Setting and testing single bits. */ +void bitmap_set (struct bitmap *, size_t idx, bool); +void bitmap_mark (struct bitmap *, size_t idx); +void bitmap_reset (struct bitmap *, size_t idx); +void bitmap_flip (struct bitmap *, size_t idx); +bool bitmap_test (const struct bitmap *, size_t idx); + +/* Setting and testing multiple bits. */ +void bitmap_set_all (struct bitmap *, bool); +void bitmap_set_multiple (struct bitmap *, size_t start, size_t cnt, bool); +size_t bitmap_count (const struct bitmap *, size_t start, size_t cnt, bool); +bool bitmap_contains (const struct bitmap *, size_t start, size_t cnt, bool); +bool bitmap_any (const struct bitmap *, size_t start, size_t cnt); +bool bitmap_none (const struct bitmap *, size_t start, size_t cnt); +bool bitmap_all (const struct bitmap *, size_t start, size_t cnt); + +/* Finding set or unset bits. */ +#define BITMAP_ERROR SIZE_MAX +size_t bitmap_scan (const struct bitmap *, size_t start, size_t cnt, bool); +size_t bitmap_scan_and_flip (struct bitmap *, size_t start, size_t cnt, bool); + +/* File input and output. */ +#ifdef FILESYS +struct file; +size_t bitmap_file_size (const struct bitmap *); +bool bitmap_read (struct bitmap *, struct file *); +bool bitmap_write (const struct bitmap *, struct file *); +#endif + +/* Debugging. */ +void bitmap_dump (const struct bitmap *); + +#endif /* lib/kernel/bitmap.h */ diff --git a/pintos-env/pintos/lib/kernel/console.c b/pintos-env/pintos/lib/kernel/console.c new file mode 100755 index 0000000..844b184 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/console.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include "devices/serial.h" +#include "devices/vga.h" +#include "threads/init.h" +#include "threads/interrupt.h" +#include "threads/synch.h" + +static void vprintf_helper (char, void *); +static void putchar_have_lock (uint8_t c); + +/* The console lock. + Both the vga and serial layers do their own locking, so it's + safe to call them at any time. + But this lock is useful to prevent simultaneous printf() calls + from mixing their output, which looks confusing. */ +static struct lock console_lock; + +/* True in ordinary circumstances: we want to use the console + lock to avoid mixing output between threads, as explained + above. + + False in early boot before the point that locks are functional + or the console lock has been initialized, or after a kernel + panics. In the former case, taking the lock would cause an + assertion failure, which in turn would cause a panic, turning + it into the latter case. In the latter case, if it is a buggy + lock_acquire() implementation that caused the panic, we'll + likely just recurse. */ +static bool use_console_lock; + +/* It's possible, if you add enough debug output to Pintos, to + try to recursively grab console_lock from a single thread. As + a real example, I added a printf() call to palloc_free(). + Here's a real backtrace that resulted: + + lock_console() + vprintf() + printf() - palloc() tries to grab the lock again + palloc_free() + thread_schedule_tail() - another thread dying as we switch threads + schedule() + thread_yield() + intr_handler() - timer interrupt + intr_set_level() + serial_putc() + putchar_have_lock() + putbuf() + sys_write() - one process writing to the console + syscall_handler() + intr_handler() + + This kind of thing is very difficult to debug, so we avoid the + problem by simulating a recursive lock with a depth + counter. */ +static int console_lock_depth; + +/* Number of characters written to console. */ +static int64_t write_cnt; + +/* Enable console locking. */ +void +console_init (void) +{ + lock_init (&console_lock); + use_console_lock = true; +} + +/* Notifies the console that a kernel panic is underway, + which warns it to avoid trying to take the console lock from + now on. */ +void +console_panic (void) +{ + use_console_lock = false; +} + +/* Prints console statistics. */ +void +console_print_stats (void) +{ + printf ("Console: %lld characters output\n", write_cnt); +} + +/* Acquires the console lock. */ +static void +acquire_console (void) +{ + if (!intr_context () && use_console_lock) + { + if (lock_held_by_current_thread (&console_lock)) + console_lock_depth++; + else + lock_acquire (&console_lock); + } +} + +/* Releases the console lock. */ +static void +release_console (void) +{ + if (!intr_context () && use_console_lock) + { + if (console_lock_depth > 0) + console_lock_depth--; + else + lock_release (&console_lock); + } +} + +/* Returns true if the current thread has the console lock, + false otherwise. */ +static bool +console_locked_by_current_thread (void) +{ + return (intr_context () + || !use_console_lock + || lock_held_by_current_thread (&console_lock)); +} + +/* The standard vprintf() function, + which is like printf() but uses a va_list. + Writes its output to both vga display and serial port. */ +int +vprintf (const char *format, va_list args) +{ + int char_cnt = 0; + + acquire_console (); + __vprintf (format, args, vprintf_helper, &char_cnt); + release_console (); + + return char_cnt; +} + +/* Writes string S to the console, followed by a new-line + character. */ +int +puts (const char *s) +{ + acquire_console (); + while (*s != '\0') + putchar_have_lock (*s++); + putchar_have_lock ('\n'); + release_console (); + + return 0; +} + +/* Writes the N characters in BUFFER to the console. */ +void +putbuf (const char *buffer, size_t n) +{ + acquire_console (); + while (n-- > 0) + putchar_have_lock (*buffer++); + release_console (); +} + +/* Writes C to the vga display and serial port. */ +int +putchar (int c) +{ + acquire_console (); + putchar_have_lock (c); + release_console (); + + return c; +} + +/* Helper function for vprintf(). */ +static void +vprintf_helper (char c, void *char_cnt_) +{ + int *char_cnt = char_cnt_; + (*char_cnt)++; + putchar_have_lock (c); +} + +/* Writes C to the vga display and serial port. + The caller has already acquired the console lock if + appropriate. */ +static void +putchar_have_lock (uint8_t c) +{ + ASSERT (console_locked_by_current_thread ()); + write_cnt++; + serial_putc (c); + vga_putc (c); +} diff --git a/pintos-env/pintos/lib/kernel/console.h b/pintos-env/pintos/lib/kernel/console.h new file mode 100755 index 0000000..ab99249 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/console.h @@ -0,0 +1,8 @@ +#ifndef __LIB_KERNEL_CONSOLE_H +#define __LIB_KERNEL_CONSOLE_H + +void console_init (void); +void console_panic (void); +void console_print_stats (void); + +#endif /* lib/kernel/console.h */ diff --git a/pintos-env/pintos/lib/kernel/debug.c b/pintos-env/pintos/lib/kernel/debug.c new file mode 100755 index 0000000..b12f4f9 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/debug.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include +#include +#include +#include "threads/init.h" +#include "threads/interrupt.h" +#include "threads/thread.h" +#include "threads/switch.h" +#include "threads/vaddr.h" +#include "devices/serial.h" +#include "devices/shutdown.h" + +/* Halts the OS, printing the source file name, line number, and + function name, plus a user-specific message. */ +void +debug_panic (const char *file, int line, const char *function, + const char *message, ...) +{ + static int level; + va_list args; + + intr_disable (); + console_panic (); + + level++; + if (level == 1) + { + printf ("Kernel PANIC at %s:%d in %s(): ", file, line, function); + + va_start (args, message); + vprintf (message, args); + printf ("\n"); + va_end (args); + + debug_backtrace (); + } + else if (level == 2) + printf ("Kernel PANIC recursion at %s:%d in %s().\n", + file, line, function); + else + { + /* Don't print anything: that's probably why we recursed. */ + } + + serial_flush (); + shutdown (); + for (;;); +} + +/* Print call stack of a thread. + The thread may be running, ready, or blocked. */ +static void +print_stacktrace(struct thread *t, void *aux UNUSED) +{ + void *retaddr = NULL, **frame = NULL; + const char *status = "UNKNOWN"; + + switch (t->status) { + case THREAD_RUNNING: + status = "RUNNING"; + break; + + case THREAD_READY: + status = "READY"; + break; + + case THREAD_BLOCKED: + status = "BLOCKED"; + break; + + default: + break; + } + + printf ("Call stack of thread `%s' (status %s):", t->name, status); + + if (t == thread_current()) + { + frame = __builtin_frame_address (1); + retaddr = __builtin_return_address (0); + } + else + { + /* Retrieve the values of the base and instruction pointers + as they were saved when this thread called switch_threads. */ + struct switch_threads_frame * saved_frame; + + saved_frame = (struct switch_threads_frame *)t->stack; + + /* Skip threads if they have been added to the all threads + list, but have never been scheduled. + We can identify because their `stack' member either points + at the top of their kernel stack page, or the + switch_threads_frame's 'eip' member points at switch_entry. + See also threads.c. */ + if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry) + { + printf (" thread was never scheduled.\n"); + return; + } + + frame = (void **) saved_frame->ebp; + retaddr = (void *) saved_frame->eip; + } + + printf (" %p", retaddr); + for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0]) + printf (" %p", frame[1]); + printf (".\n"); +} + +/* Prints call stack of all threads. */ +void +debug_backtrace_all (void) +{ + enum intr_level oldlevel = intr_disable (); + + thread_foreach (print_stacktrace, 0); + intr_set_level (oldlevel); +} diff --git a/pintos-env/pintos/lib/kernel/hash.c b/pintos-env/pintos/lib/kernel/hash.c new file mode 100755 index 0000000..57eed45 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/hash.c @@ -0,0 +1,430 @@ +/* Hash table. + + This data structure is thoroughly documented in the Tour of + Pintos for Project 3. + + See hash.h for basic information. */ + +#include "hash.h" +#include "../debug.h" +#include "threads/malloc.h" + +#define list_elem_to_hash_elem(LIST_ELEM) \ + list_entry(LIST_ELEM, struct hash_elem, list_elem) + +static struct list *find_bucket (struct hash *, struct hash_elem *); +static struct hash_elem *find_elem (struct hash *, struct list *, + struct hash_elem *); +static void insert_elem (struct hash *, struct list *, struct hash_elem *); +static void remove_elem (struct hash *, struct hash_elem *); +static void rehash (struct hash *); + +/* Initializes hash table H to compute hash values using HASH and + compare hash elements using LESS, given auxiliary data AUX. */ +bool +hash_init (struct hash *h, + hash_hash_func *hash, hash_less_func *less, void *aux) +{ + h->elem_cnt = 0; + h->bucket_cnt = 4; + h->buckets = malloc (sizeof *h->buckets * h->bucket_cnt); + h->hash = hash; + h->less = less; + h->aux = aux; + + if (h->buckets != NULL) + { + hash_clear (h, NULL); + return true; + } + else + return false; +} + +/* Removes all the elements from H. + + If DESTRUCTOR is non-null, then it is called for each element + in the hash. DESTRUCTOR may, if appropriate, deallocate the + memory used by the hash element. However, modifying hash + table H while hash_clear() is running, using any of the + functions hash_clear(), hash_destroy(), hash_insert(), + hash_replace(), or hash_delete(), yields undefined behavior, + whether done in DESTRUCTOR or elsewhere. */ +void +hash_clear (struct hash *h, hash_action_func *destructor) +{ + size_t i; + + for (i = 0; i < h->bucket_cnt; i++) + { + struct list *bucket = &h->buckets[i]; + + if (destructor != NULL) + while (!list_empty (bucket)) + { + struct list_elem *list_elem = list_pop_front (bucket); + struct hash_elem *hash_elem = list_elem_to_hash_elem (list_elem); + destructor (hash_elem, h->aux); + } + + list_init (bucket); + } + + h->elem_cnt = 0; +} + +/* Destroys hash table H. + + If DESTRUCTOR is non-null, then it is first called for each + element in the hash. DESTRUCTOR may, if appropriate, + deallocate the memory used by the hash element. However, + modifying hash table H while hash_clear() is running, using + any of the functions hash_clear(), hash_destroy(), + hash_insert(), hash_replace(), or hash_delete(), yields + undefined behavior, whether done in DESTRUCTOR or + elsewhere. */ +void +hash_destroy (struct hash *h, hash_action_func *destructor) +{ + if (destructor != NULL) + hash_clear (h, destructor); + free (h->buckets); +} + +/* Inserts NEW into hash table H and returns a null pointer, if + no equal element is already in the table. + If an equal element is already in the table, returns it + without inserting NEW. */ +struct hash_elem * +hash_insert (struct hash *h, struct hash_elem *new) +{ + struct list *bucket = find_bucket (h, new); + struct hash_elem *old = find_elem (h, bucket, new); + + if (old == NULL) + insert_elem (h, bucket, new); + + rehash (h); + + return old; +} + +/* Inserts NEW into hash table H, replacing any equal element + already in the table, which is returned. */ +struct hash_elem * +hash_replace (struct hash *h, struct hash_elem *new) +{ + struct list *bucket = find_bucket (h, new); + struct hash_elem *old = find_elem (h, bucket, new); + + if (old != NULL) + remove_elem (h, old); + insert_elem (h, bucket, new); + + rehash (h); + + return old; +} + +/* Finds and returns an element equal to E in hash table H, or a + null pointer if no equal element exists in the table. */ +struct hash_elem * +hash_find (struct hash *h, struct hash_elem *e) +{ + return find_elem (h, find_bucket (h, e), e); +} + +/* Finds, removes, and returns an element equal to E in hash + table H. Returns a null pointer if no equal element existed + in the table. + + If the elements of the hash table are dynamically allocated, + or own resources that are, then it is the caller's + responsibility to deallocate them. */ +struct hash_elem * +hash_delete (struct hash *h, struct hash_elem *e) +{ + struct hash_elem *found = find_elem (h, find_bucket (h, e), e); + if (found != NULL) + { + remove_elem (h, found); + rehash (h); + } + return found; +} + +/* Calls ACTION for each element in hash table H in arbitrary + order. + Modifying hash table H while hash_apply() is running, using + any of the functions hash_clear(), hash_destroy(), + hash_insert(), hash_replace(), or hash_delete(), yields + undefined behavior, whether done from ACTION or elsewhere. */ +void +hash_apply (struct hash *h, hash_action_func *action) +{ + size_t i; + + ASSERT (action != NULL); + + for (i = 0; i < h->bucket_cnt; i++) + { + struct list *bucket = &h->buckets[i]; + struct list_elem *elem, *next; + + for (elem = list_begin (bucket); elem != list_end (bucket); elem = next) + { + next = list_next (elem); + action (list_elem_to_hash_elem (elem), h->aux); + } + } +} + +/* Initializes I for iterating hash table H. + + Iteration idiom: + + struct hash_iterator i; + + hash_first (&i, h); + while (hash_next (&i)) + { + struct foo *f = hash_entry (hash_cur (&i), struct foo, elem); + ...do something with f... + } + + Modifying hash table H during iteration, using any of the + functions hash_clear(), hash_destroy(), hash_insert(), + hash_replace(), or hash_delete(), invalidates all + iterators. */ +void +hash_first (struct hash_iterator *i, struct hash *h) +{ + ASSERT (i != NULL); + ASSERT (h != NULL); + + i->hash = h; + i->bucket = i->hash->buckets; + i->elem = list_elem_to_hash_elem (list_head (i->bucket)); +} + +/* Advances I to the next element in the hash table and returns + it. Returns a null pointer if no elements are left. Elements + are returned in arbitrary order. + + Modifying a hash table H during iteration, using any of the + functions hash_clear(), hash_destroy(), hash_insert(), + hash_replace(), or hash_delete(), invalidates all + iterators. */ +struct hash_elem * +hash_next (struct hash_iterator *i) +{ + ASSERT (i != NULL); + + i->elem = list_elem_to_hash_elem (list_next (&i->elem->list_elem)); + while (i->elem == list_elem_to_hash_elem (list_end (i->bucket))) + { + if (++i->bucket >= i->hash->buckets + i->hash->bucket_cnt) + { + i->elem = NULL; + break; + } + i->elem = list_elem_to_hash_elem (list_begin (i->bucket)); + } + + return i->elem; +} + +/* Returns the current element in the hash table iteration, or a + null pointer at the end of the table. Undefined behavior + after calling hash_first() but before hash_next(). */ +struct hash_elem * +hash_cur (struct hash_iterator *i) +{ + return i->elem; +} + +/* Returns the number of elements in H. */ +size_t +hash_size (struct hash *h) +{ + return h->elem_cnt; +} + +/* Returns true if H contains no elements, false otherwise. */ +bool +hash_empty (struct hash *h) +{ + return h->elem_cnt == 0; +} + +/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */ +#define FNV_32_PRIME 16777619u +#define FNV_32_BASIS 2166136261u + +/* Returns a hash of the SIZE bytes in BUF. */ +unsigned +hash_bytes (const void *buf_, size_t size) +{ + /* Fowler-Noll-Vo 32-bit hash, for bytes. */ + const unsigned char *buf = buf_; + unsigned hash; + + ASSERT (buf != NULL); + + hash = FNV_32_BASIS; + while (size-- > 0) + hash = (hash * FNV_32_PRIME) ^ *buf++; + + return hash; +} + +/* Returns a hash of string S. */ +unsigned +hash_string (const char *s_) +{ + const unsigned char *s = (const unsigned char *) s_; + unsigned hash; + + ASSERT (s != NULL); + + hash = FNV_32_BASIS; + while (*s != '\0') + hash = (hash * FNV_32_PRIME) ^ *s++; + + return hash; +} + +/* Returns a hash of integer I. */ +unsigned +hash_int (int i) +{ + return hash_bytes (&i, sizeof i); +} + +/* Returns the bucket in H that E belongs in. */ +static struct list * +find_bucket (struct hash *h, struct hash_elem *e) +{ + size_t bucket_idx = h->hash (e, h->aux) & (h->bucket_cnt - 1); + return &h->buckets[bucket_idx]; +} + +/* Searches BUCKET in H for a hash element equal to E. Returns + it if found or a null pointer otherwise. */ +static struct hash_elem * +find_elem (struct hash *h, struct list *bucket, struct hash_elem *e) +{ + struct list_elem *i; + + for (i = list_begin (bucket); i != list_end (bucket); i = list_next (i)) + { + struct hash_elem *hi = list_elem_to_hash_elem (i); + if (!h->less (hi, e, h->aux) && !h->less (e, hi, h->aux)) + return hi; + } + return NULL; +} + +/* Returns X with its lowest-order bit set to 1 turned off. */ +static inline size_t +turn_off_least_1bit (size_t x) +{ + return x & (x - 1); +} + +/* Returns true if X is a power of 2, otherwise false. */ +static inline size_t +is_power_of_2 (size_t x) +{ + return x != 0 && turn_off_least_1bit (x) == 0; +} + +/* Element per bucket ratios. */ +#define MIN_ELEMS_PER_BUCKET 1 /* Elems/bucket < 1: reduce # of buckets. */ +#define BEST_ELEMS_PER_BUCKET 2 /* Ideal elems/bucket. */ +#define MAX_ELEMS_PER_BUCKET 4 /* Elems/bucket > 4: increase # of buckets. */ + +/* Changes the number of buckets in hash table H to match the + ideal. This function can fail because of an out-of-memory + condition, but that'll just make hash accesses less efficient; + we can still continue. */ +static void +rehash (struct hash *h) +{ + size_t old_bucket_cnt, new_bucket_cnt; + struct list *new_buckets, *old_buckets; + size_t i; + + ASSERT (h != NULL); + + /* Save old bucket info for later use. */ + old_buckets = h->buckets; + old_bucket_cnt = h->bucket_cnt; + + /* Calculate the number of buckets to use now. + We want one bucket for about every BEST_ELEMS_PER_BUCKET. + We must have at least four buckets, and the number of + buckets must be a power of 2. */ + new_bucket_cnt = h->elem_cnt / BEST_ELEMS_PER_BUCKET; + if (new_bucket_cnt < 4) + new_bucket_cnt = 4; + while (!is_power_of_2 (new_bucket_cnt)) + new_bucket_cnt = turn_off_least_1bit (new_bucket_cnt); + + /* Don't do anything if the bucket count wouldn't change. */ + if (new_bucket_cnt == old_bucket_cnt) + return; + + /* Allocate new buckets and initialize them as empty. */ + new_buckets = malloc (sizeof *new_buckets * new_bucket_cnt); + if (new_buckets == NULL) + { + /* Allocation failed. This means that use of the hash table will + be less efficient. However, it is still usable, so + there's no reason for it to be an error. */ + return; + } + for (i = 0; i < new_bucket_cnt; i++) + list_init (&new_buckets[i]); + + /* Install new bucket info. */ + h->buckets = new_buckets; + h->bucket_cnt = new_bucket_cnt; + + /* Move each old element into the appropriate new bucket. */ + for (i = 0; i < old_bucket_cnt; i++) + { + struct list *old_bucket; + struct list_elem *elem, *next; + + old_bucket = &old_buckets[i]; + for (elem = list_begin (old_bucket); + elem != list_end (old_bucket); elem = next) + { + struct list *new_bucket + = find_bucket (h, list_elem_to_hash_elem (elem)); + next = list_next (elem); + list_remove (elem); + list_push_front (new_bucket, elem); + } + } + + free (old_buckets); +} + +/* Inserts E into BUCKET (in hash table H). */ +static void +insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e) +{ + h->elem_cnt++; + list_push_front (bucket, &e->list_elem); +} + +/* Removes E from hash table H. */ +static void +remove_elem (struct hash *h, struct hash_elem *e) +{ + h->elem_cnt--; + list_remove (&e->list_elem); +} + diff --git a/pintos-env/pintos/lib/kernel/hash.h b/pintos-env/pintos/lib/kernel/hash.h new file mode 100755 index 0000000..db9f674 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/hash.h @@ -0,0 +1,103 @@ +#ifndef __LIB_KERNEL_HASH_H +#define __LIB_KERNEL_HASH_H + +/* Hash table. + + This data structure is thoroughly documented in the Tour of + Pintos for Project 3. + + This is a standard hash table with chaining. To locate an + element in the table, we compute a hash function over the + element's data and use that as an index into an array of + doubly linked lists, then linearly search the list. + + The chain lists do not use dynamic allocation. Instead, each + structure that can potentially be in a hash must embed a + struct hash_elem member. All of the hash functions operate on + these `struct hash_elem's. The hash_entry macro allows + conversion from a struct hash_elem back to a structure object + that contains it. This is the same technique used in the + linked list implementation. Refer to lib/kernel/list.h for a + detailed explanation. */ + +#include +#include +#include +#include "list.h" + +/* Hash element. */ +struct hash_elem + { + struct list_elem list_elem; + }; + +/* Converts pointer to hash element HASH_ELEM into a pointer to + the structure that HASH_ELEM is embedded inside. Supply the + name of the outer structure STRUCT and the member name MEMBER + of the hash element. See the big comment at the top of the + file for an example. */ +#define hash_entry(HASH_ELEM, STRUCT, MEMBER) \ + ((STRUCT *) ((uint8_t *) &(HASH_ELEM)->list_elem \ + - offsetof (STRUCT, MEMBER.list_elem))) + +/* Computes and returns the hash value for hash element E, given + auxiliary data AUX. */ +typedef unsigned hash_hash_func (const struct hash_elem *e, void *aux); + +/* Compares the value of two hash elements A and B, given + auxiliary data AUX. Returns true if A is less than B, or + false if A is greater than or equal to B. */ +typedef bool hash_less_func (const struct hash_elem *a, + const struct hash_elem *b, + void *aux); + +/* Performs some operation on hash element E, given auxiliary + data AUX. */ +typedef void hash_action_func (struct hash_elem *e, void *aux); + +/* Hash table. */ +struct hash + { + size_t elem_cnt; /* Number of elements in table. */ + size_t bucket_cnt; /* Number of buckets, a power of 2. */ + struct list *buckets; /* Array of `bucket_cnt' lists. */ + hash_hash_func *hash; /* Hash function. */ + hash_less_func *less; /* Comparison function. */ + void *aux; /* Auxiliary data for `hash' and `less'. */ + }; + +/* A hash table iterator. */ +struct hash_iterator + { + struct hash *hash; /* The hash table. */ + struct list *bucket; /* Current bucket. */ + struct hash_elem *elem; /* Current hash element in current bucket. */ + }; + +/* Basic life cycle. */ +bool hash_init (struct hash *, hash_hash_func *, hash_less_func *, void *aux); +void hash_clear (struct hash *, hash_action_func *); +void hash_destroy (struct hash *, hash_action_func *); + +/* Search, insertion, deletion. */ +struct hash_elem *hash_insert (struct hash *, struct hash_elem *); +struct hash_elem *hash_replace (struct hash *, struct hash_elem *); +struct hash_elem *hash_find (struct hash *, struct hash_elem *); +struct hash_elem *hash_delete (struct hash *, struct hash_elem *); + +/* Iteration. */ +void hash_apply (struct hash *, hash_action_func *); +void hash_first (struct hash_iterator *, struct hash *); +struct hash_elem *hash_next (struct hash_iterator *); +struct hash_elem *hash_cur (struct hash_iterator *); + +/* Information. */ +size_t hash_size (struct hash *); +bool hash_empty (struct hash *); + +/* Sample hash functions. */ +unsigned hash_bytes (const void *, size_t); +unsigned hash_string (const char *); +unsigned hash_int (int); + +#endif /* lib/kernel/hash.h */ diff --git a/pintos-env/pintos/lib/kernel/list.c b/pintos-env/pintos/lib/kernel/list.c new file mode 100755 index 0000000..316d9ef --- /dev/null +++ b/pintos-env/pintos/lib/kernel/list.c @@ -0,0 +1,524 @@ +#include "list.h" +#include "../debug.h" + +/* Our doubly linked lists have two header elements: the "head" + just before the first element and the "tail" just after the + last element. The `prev' link of the front header is null, as + is the `next' link of the back header. Their other two links + point toward each other via the interior elements of the list. + + An empty list looks like this: + + +------+ +------+ + <---| head |<--->| tail |---> + +------+ +------+ + + A list with two elements in it looks like this: + + +------+ +-------+ +-------+ +------+ + <---| head |<--->| 1 |<--->| 2 |<--->| tail |<---> + +------+ +-------+ +-------+ +------+ + + The symmetry of this arrangement eliminates lots of special + cases in list processing. For example, take a look at + list_remove(): it takes only two pointer assignments and no + conditionals. That's a lot simpler than the code would be + without header elements. + + (Because only one of the pointers in each header element is used, + we could in fact combine them into a single header element + without sacrificing this simplicity. But using two separate + elements allows us to do a little bit of checking on some + operations, which can be valuable.) */ + +static bool is_sorted (struct list_elem *a, struct list_elem *b, + list_less_func *less, void *aux) UNUSED; + +/* Returns true if ELEM is a head, false otherwise. */ +static inline bool +is_head (struct list_elem *elem) +{ + return elem != NULL && elem->prev == NULL && elem->next != NULL; +} + +/* Returns true if ELEM is an interior element, + false otherwise. */ +static inline bool +is_interior (struct list_elem *elem) +{ + return elem != NULL && elem->prev != NULL && elem->next != NULL; +} + +/* Returns true if ELEM is a tail, false otherwise. */ +static inline bool +is_tail (struct list_elem *elem) +{ + return elem != NULL && elem->prev != NULL && elem->next == NULL; +} + +/* Initializes LIST as an empty list. */ +void +list_init (struct list *list) +{ + ASSERT (list != NULL); + list->head.prev = NULL; + list->head.next = &list->tail; + list->tail.prev = &list->head; + list->tail.next = NULL; +} + +/* Returns the beginning of LIST. */ +struct list_elem * +list_begin (struct list *list) +{ + ASSERT (list != NULL); + return list->head.next; +} + +/* Returns the element after ELEM in its list. If ELEM is the + last element in its list, returns the list tail. Results are + undefined if ELEM is itself a list tail. */ +struct list_elem * +list_next (struct list_elem *elem) +{ + ASSERT (is_head (elem) || is_interior (elem)); + return elem->next; +} + +/* Returns LIST's tail. + + list_end() is often used in iterating through a list from + front to back. See the big comment at the top of list.h for + an example. */ +struct list_elem * +list_end (struct list *list) +{ + ASSERT (list != NULL); + return &list->tail; +} + +/* Returns the LIST's reverse beginning, for iterating through + LIST in reverse order, from back to front. */ +struct list_elem * +list_rbegin (struct list *list) +{ + ASSERT (list != NULL); + return list->tail.prev; +} + +/* Returns the element before ELEM in its list. If ELEM is the + first element in its list, returns the list head. Results are + undefined if ELEM is itself a list head. */ +struct list_elem * +list_prev (struct list_elem *elem) +{ + ASSERT (is_interior (elem) || is_tail (elem)); + return elem->prev; +} + +/* Returns LIST's head. + + list_rend() is often used in iterating through a list in + reverse order, from back to front. Here's typical usage, + following the example from the top of list.h: + + for (e = list_rbegin (&foo_list); e != list_rend (&foo_list); + e = list_prev (e)) + { + struct foo *f = list_entry (e, struct foo, elem); + ...do something with f... + } +*/ +struct list_elem * +list_rend (struct list *list) +{ + ASSERT (list != NULL); + return &list->head; +} + +/* Return's LIST's head. + + list_head() can be used for an alternate style of iterating + through a list, e.g.: + + e = list_head (&list); + while ((e = list_next (e)) != list_end (&list)) + { + ... + } +*/ +struct list_elem * +list_head (struct list *list) +{ + ASSERT (list != NULL); + return &list->head; +} + +/* Return's LIST's tail. */ +struct list_elem * +list_tail (struct list *list) +{ + ASSERT (list != NULL); + return &list->tail; +} + +/* Inserts ELEM just before BEFORE, which may be either an + interior element or a tail. The latter case is equivalent to + list_push_back(). */ +void +list_insert (struct list_elem *before, struct list_elem *elem) +{ + ASSERT (is_interior (before) || is_tail (before)); + ASSERT (elem != NULL); + + elem->prev = before->prev; + elem->next = before; + before->prev->next = elem; + before->prev = elem; +} + +/* Removes elements FIRST though LAST (exclusive) from their + current list, then inserts them just before BEFORE, which may + be either an interior element or a tail. */ +void +list_splice (struct list_elem *before, + struct list_elem *first, struct list_elem *last) +{ + ASSERT (is_interior (before) || is_tail (before)); + if (first == last) + return; + last = list_prev (last); + + ASSERT (is_interior (first)); + ASSERT (is_interior (last)); + + /* Cleanly remove FIRST...LAST from its current list. */ + first->prev->next = last->next; + last->next->prev = first->prev; + + /* Splice FIRST...LAST into new list. */ + first->prev = before->prev; + last->next = before; + before->prev->next = first; + before->prev = last; +} + +/* Inserts ELEM at the beginning of LIST, so that it becomes the + front in LIST. */ +void +list_push_front (struct list *list, struct list_elem *elem) +{ + list_insert (list_begin (list), elem); +} + +/* Inserts ELEM at the end of LIST, so that it becomes the + back in LIST. */ +void +list_push_back (struct list *list, struct list_elem *elem) +{ + list_insert (list_end (list), elem); +} + +/* Removes ELEM from its list and returns the element that + followed it. Undefined behavior if ELEM is not in a list. + + A list element must be treated very carefully after removing + it from its list. Calling list_next() or list_prev() on ELEM + will return the item that was previously before or after ELEM, + but, e.g., list_prev(list_next(ELEM)) is no longer ELEM! + + The list_remove() return value provides a convenient way to + iterate and remove elements from a list: + + for (e = list_begin (&list); e != list_end (&list); e = list_remove (e)) + { + ...do something with e... + } + + If you need to free() elements of the list then you need to be + more conservative. Here's an alternate strategy that works + even in that case: + + while (!list_empty (&list)) + { + struct list_elem *e = list_pop_front (&list); + ...do something with e... + } +*/ +struct list_elem * +list_remove (struct list_elem *elem) +{ + ASSERT (is_interior (elem)); + elem->prev->next = elem->next; + elem->next->prev = elem->prev; + return elem->next; +} + +/* Removes the front element from LIST and returns it. + Undefined behavior if LIST is empty before removal. */ +struct list_elem * +list_pop_front (struct list *list) +{ + struct list_elem *front = list_front (list); + list_remove (front); + return front; +} + +/* Removes the back element from LIST and returns it. + Undefined behavior if LIST is empty before removal. */ +struct list_elem * +list_pop_back (struct list *list) +{ + struct list_elem *back = list_back (list); + list_remove (back); + return back; +} + +/* Returns the front element in LIST. + Undefined behavior if LIST is empty. */ +struct list_elem * +list_front (struct list *list) +{ + ASSERT (!list_empty (list)); + return list->head.next; +} + +/* Returns the back element in LIST. + Undefined behavior if LIST is empty. */ +struct list_elem * +list_back (struct list *list) +{ + ASSERT (!list_empty (list)); + return list->tail.prev; +} + +/* Returns the number of elements in LIST. + Runs in O(n) in the number of elements. */ +size_t +list_size (struct list *list) +{ + struct list_elem *e; + size_t cnt = 0; + + for (e = list_begin (list); e != list_end (list); e = list_next (e)) + cnt++; + return cnt; +} + +/* Returns true if LIST is empty, false otherwise. */ +bool +list_empty (struct list *list) +{ + return list_begin (list) == list_end (list); +} + +/* Swaps the `struct list_elem *'s that A and B point to. */ +static void +swap (struct list_elem **a, struct list_elem **b) +{ + struct list_elem *t = *a; + *a = *b; + *b = t; +} + +/* Reverses the order of LIST. */ +void +list_reverse (struct list *list) +{ + if (!list_empty (list)) + { + struct list_elem *e; + + for (e = list_begin (list); e != list_end (list); e = e->prev) + swap (&e->prev, &e->next); + swap (&list->head.next, &list->tail.prev); + swap (&list->head.next->prev, &list->tail.prev->next); + } +} + +/* Returns true only if the list elements A through B (exclusive) + are in order according to LESS given auxiliary data AUX. */ +static bool +is_sorted (struct list_elem *a, struct list_elem *b, + list_less_func *less, void *aux) +{ + if (a != b) + while ((a = list_next (a)) != b) + if (less (a, list_prev (a), aux)) + return false; + return true; +} + +/* Finds a run, starting at A and ending not after B, of list + elements that are in nondecreasing order according to LESS + given auxiliary data AUX. Returns the (exclusive) end of the + run. + A through B (exclusive) must form a non-empty range. */ +static struct list_elem * +find_end_of_run (struct list_elem *a, struct list_elem *b, + list_less_func *less, void *aux) +{ + ASSERT (a != NULL); + ASSERT (b != NULL); + ASSERT (less != NULL); + ASSERT (a != b); + + do + { + a = list_next (a); + } + while (a != b && !less (a, list_prev (a), aux)); + return a; +} + +/* Merges A0 through A1B0 (exclusive) with A1B0 through B1 + (exclusive) to form a combined range also ending at B1 + (exclusive). Both input ranges must be nonempty and sorted in + nondecreasing order according to LESS given auxiliary data + AUX. The output range will be sorted the same way. */ +static void +inplace_merge (struct list_elem *a0, struct list_elem *a1b0, + struct list_elem *b1, + list_less_func *less, void *aux) +{ + ASSERT (a0 != NULL); + ASSERT (a1b0 != NULL); + ASSERT (b1 != NULL); + ASSERT (less != NULL); + ASSERT (is_sorted (a0, a1b0, less, aux)); + ASSERT (is_sorted (a1b0, b1, less, aux)); + + while (a0 != a1b0 && a1b0 != b1) + if (!less (a1b0, a0, aux)) + a0 = list_next (a0); + else + { + a1b0 = list_next (a1b0); + list_splice (a0, list_prev (a1b0), a1b0); + } +} + +/* Sorts LIST according to LESS given auxiliary data AUX, using a + natural iterative merge sort that runs in O(n lg n) time and + O(1) space in the number of elements in LIST. */ +void +list_sort (struct list *list, list_less_func *less, void *aux) +{ + size_t output_run_cnt; /* Number of runs output in current pass. */ + + ASSERT (list != NULL); + ASSERT (less != NULL); + + /* Pass over the list repeatedly, merging adjacent runs of + nondecreasing elements, until only one run is left. */ + do + { + struct list_elem *a0; /* Start of first run. */ + struct list_elem *a1b0; /* End of first run, start of second. */ + struct list_elem *b1; /* End of second run. */ + + output_run_cnt = 0; + for (a0 = list_begin (list); a0 != list_end (list); a0 = b1) + { + /* Each iteration produces one output run. */ + output_run_cnt++; + + /* Locate two adjacent runs of nondecreasing elements + A0...A1B0 and A1B0...B1. */ + a1b0 = find_end_of_run (a0, list_end (list), less, aux); + if (a1b0 == list_end (list)) + break; + b1 = find_end_of_run (a1b0, list_end (list), less, aux); + + /* Merge the runs. */ + inplace_merge (a0, a1b0, b1, less, aux); + } + } + while (output_run_cnt > 1); + + ASSERT (is_sorted (list_begin (list), list_end (list), less, aux)); +} + +/* Inserts ELEM in the proper position in LIST, which must be + sorted according to LESS given auxiliary data AUX. + Runs in O(n) average case in the number of elements in LIST. */ +void +list_insert_ordered (struct list *list, struct list_elem *elem, + list_less_func *less, void *aux) +{ + struct list_elem *e; + + ASSERT (list != NULL); + ASSERT (elem != NULL); + ASSERT (less != NULL); + + for (e = list_begin (list); e != list_end (list); e = list_next (e)) + if (less (elem, e, aux)) + break; + return list_insert (e, elem); +} + +/* Iterates through LIST and removes all but the first in each + set of adjacent elements that are equal according to LESS + given auxiliary data AUX. If DUPLICATES is non-null, then the + elements from LIST are appended to DUPLICATES. */ +void +list_unique (struct list *list, struct list *duplicates, + list_less_func *less, void *aux) +{ + struct list_elem *elem, *next; + + ASSERT (list != NULL); + ASSERT (less != NULL); + if (list_empty (list)) + return; + + elem = list_begin (list); + while ((next = list_next (elem)) != list_end (list)) + if (!less (elem, next, aux) && !less (next, elem, aux)) + { + list_remove (next); + if (duplicates != NULL) + list_push_back (duplicates, next); + } + else + elem = next; +} + +/* Returns the element in LIST with the largest value according + to LESS given auxiliary data AUX. If there is more than one + maximum, returns the one that appears earlier in the list. If + the list is empty, returns its tail. */ +struct list_elem * +list_max (struct list *list, list_less_func *less, void *aux) +{ + struct list_elem *max = list_begin (list); + if (max != list_end (list)) + { + struct list_elem *e; + + for (e = list_next (max); e != list_end (list); e = list_next (e)) + if (less (max, e, aux)) + max = e; + } + return max; +} + +/* Returns the element in LIST with the smallest value according + to LESS given auxiliary data AUX. If there is more than one + minimum, returns the one that appears earlier in the list. If + the list is empty, returns its tail. */ +struct list_elem * +list_min (struct list *list, list_less_func *less, void *aux) +{ + struct list_elem *min = list_begin (list); + if (min != list_end (list)) + { + struct list_elem *e; + + for (e = list_next (min); e != list_end (list); e = list_next (e)) + if (less (e, min, aux)) + min = e; + } + return min; +} diff --git a/pintos-env/pintos/lib/kernel/list.h b/pintos-env/pintos/lib/kernel/list.h new file mode 100755 index 0000000..82efbb5 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/list.h @@ -0,0 +1,181 @@ +#ifndef __LIB_KERNEL_LIST_H +#define __LIB_KERNEL_LIST_H + +/* Doubly linked list. + + This implementation of a doubly linked list does not require + use of dynamically allocated memory. Instead, each structure + that is a potential list element must embed a struct list_elem + member. All of the list functions operate on these `struct + list_elem's. The list_entry macro allows conversion from a + struct list_elem back to a structure object that contains it. + + For example, suppose there is a needed for a list of `struct + foo'. `struct foo' should contain a `struct list_elem' + member, like so: + + struct foo + { + struct list_elem elem; + int bar; + ...other members... + }; + + Then a list of `struct foo' can be be declared and initialized + like so: + + struct list foo_list; + + list_init (&foo_list); + + Iteration is a typical situation where it is necessary to + convert from a struct list_elem back to its enclosing + structure. Here's an example using foo_list: + + struct list_elem *e; + + for (e = list_begin (&foo_list); e != list_end (&foo_list); + e = list_next (e)) + { + struct foo *f = list_entry (e, struct foo, elem); + ...do something with f... + } + + You can find real examples of list usage throughout the + source; for example, malloc.c, palloc.c, and thread.c in the + threads directory all use lists. + + The interface for this list is inspired by the list<> template + in the C++ STL. If you're familiar with list<>, you should + find this easy to use. However, it should be emphasized that + these lists do *no* type checking and can't do much other + correctness checking. If you screw up, it will bite you. + + Glossary of list terms: + + - "front": The first element in a list. Undefined in an + empty list. Returned by list_front(). + + - "back": The last element in a list. Undefined in an empty + list. Returned by list_back(). + + - "tail": The element figuratively just after the last + element of a list. Well defined even in an empty list. + Returned by list_end(). Used as the end sentinel for an + iteration from front to back. + + - "beginning": In a non-empty list, the front. In an empty + list, the tail. Returned by list_begin(). Used as the + starting point for an iteration from front to back. + + - "head": The element figuratively just before the first + element of a list. Well defined even in an empty list. + Returned by list_rend(). Used as the end sentinel for an + iteration from back to front. + + - "reverse beginning": In a non-empty list, the back. In an + empty list, the head. Returned by list_rbegin(). Used as + the starting point for an iteration from back to front. + + - "interior element": An element that is not the head or + tail, that is, a real list element. An empty list does + not have any interior elements. +*/ + +#include +#include +#include + +/* List element. */ +struct list_elem + { + struct list_elem *prev; /* Previous list element. */ + struct list_elem *next; /* Next list element. */ + }; + +/* List. */ +struct list + { + struct list_elem head; /* List head. */ + struct list_elem tail; /* List tail. */ + }; + +/* Converts pointer to list element LIST_ELEM into a pointer to + the structure that LIST_ELEM is embedded inside. Supply the + name of the outer structure STRUCT and the member name MEMBER + of the list element. See the big comment at the top of the + file for an example. */ +#define list_entry(LIST_ELEM, STRUCT, MEMBER) \ + ((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \ + - offsetof (STRUCT, MEMBER.next))) + +/* List initialization. + + A list may be initialized by calling list_init(): + + struct list my_list; + list_init (&my_list); + + or with an initializer using LIST_INITIALIZER: + + struct list my_list = LIST_INITIALIZER (my_list); */ +#define LIST_INITIALIZER(NAME) { { NULL, &(NAME).tail }, \ + { &(NAME).head, NULL } } + +void list_init (struct list *); + +/* List traversal. */ +struct list_elem *list_begin (struct list *); +struct list_elem *list_next (struct list_elem *); +struct list_elem *list_end (struct list *); + +struct list_elem *list_rbegin (struct list *); +struct list_elem *list_prev (struct list_elem *); +struct list_elem *list_rend (struct list *); + +struct list_elem *list_head (struct list *); +struct list_elem *list_tail (struct list *); + +/* List insertion. */ +void list_insert (struct list_elem *, struct list_elem *); +void list_splice (struct list_elem *before, + struct list_elem *first, struct list_elem *last); +void list_push_front (struct list *, struct list_elem *); +void list_push_back (struct list *, struct list_elem *); + +/* List removal. */ +struct list_elem *list_remove (struct list_elem *); +struct list_elem *list_pop_front (struct list *); +struct list_elem *list_pop_back (struct list *); + +/* List elements. */ +struct list_elem *list_front (struct list *); +struct list_elem *list_back (struct list *); + +/* List properties. */ +size_t list_size (struct list *); +bool list_empty (struct list *); + +/* Miscellaneous. */ +void list_reverse (struct list *); + +/* Compares the value of two list elements A and B, given + auxiliary data AUX. Returns true if A is less than B, or + false if A is greater than or equal to B. */ +typedef bool list_less_func (const struct list_elem *a, + const struct list_elem *b, + void *aux); + +/* Operations on lists with ordered elements. */ +void list_sort (struct list *, + list_less_func *, void *aux); +void list_insert_ordered (struct list *, struct list_elem *, + list_less_func *, void *aux); +void list_unique (struct list *, struct list *duplicates, + list_less_func *, void *aux); + +/* Max and min. */ +struct list_elem *list_max (struct list *, list_less_func *, void *aux); +struct list_elem *list_min (struct list *, list_less_func *, void *aux); + +#endif /* lib/kernel/list.h */ diff --git a/pintos-env/pintos/lib/kernel/stdio.h b/pintos-env/pintos/lib/kernel/stdio.h new file mode 100755 index 0000000..3e5bae9 --- /dev/null +++ b/pintos-env/pintos/lib/kernel/stdio.h @@ -0,0 +1,6 @@ +#ifndef __LIB_KERNEL_STDIO_H +#define __LIB_KERNEL_STDIO_H + +void putbuf (const char *, size_t); + +#endif /* lib/kernel/stdio.h */ diff --git a/pintos-env/pintos/lib/limits.h b/pintos-env/pintos/lib/limits.h new file mode 100755 index 0000000..c957ec4 --- /dev/null +++ b/pintos-env/pintos/lib/limits.h @@ -0,0 +1,34 @@ +#ifndef __LIB_LIMITS_H +#define __LIB_LIMITS_H + +#define CHAR_BIT 8 + +#define SCHAR_MAX 127 +#define SCHAR_MIN (-SCHAR_MAX - 1) +#define UCHAR_MAX 255 + +#ifdef __CHAR_UNSIGNED__ +#define CHAR_MIN 0 +#define CHAR_MAX UCHAR_MAX +#else +#define CHAR_MIN SCHAR_MIN +#define CHAR_MAX SCHAR_MAX +#endif + +#define SHRT_MAX 32767 +#define SHRT_MIN (-SHRT_MAX - 1) +#define USHRT_MAX 65535 + +#define INT_MAX 2147483647 +#define INT_MIN (-INT_MAX - 1) +#define UINT_MAX 4294967295U + +#define LONG_MAX 2147483647L +#define LONG_MIN (-LONG_MAX - 1) +#define ULONG_MAX 4294967295UL + +#define LLONG_MAX 9223372036854775807LL +#define LLONG_MIN (-LLONG_MAX - 1) +#define ULLONG_MAX 18446744073709551615ULL + +#endif /* lib/limits.h */ diff --git a/pintos-env/pintos/lib/packed.h b/pintos-env/pintos/lib/packed.h new file mode 100755 index 0000000..9a9b6e2 --- /dev/null +++ b/pintos-env/pintos/lib/packed.h @@ -0,0 +1,10 @@ +#ifndef __LIB_PACKED_H +#define __LIB_PACKED_H + +/* The "packed" attribute, when applied to a structure, prevents + GCC from inserting padding bytes between or after structure + members. It must be specified at the time of the structure's + definition, normally just after the closing brace. */ +#define PACKED __attribute__ ((packed)) + +#endif /* lib/packed.h */ diff --git a/pintos-env/pintos/lib/random.c b/pintos-env/pintos/lib/random.c new file mode 100755 index 0000000..a4761b6 --- /dev/null +++ b/pintos-env/pintos/lib/random.c @@ -0,0 +1,83 @@ +#include "random.h" +#include +#include +#include "debug.h" + +/* RC4-based pseudo-random number generator (PRNG). + + RC4 is a stream cipher. We're not using it here for its + cryptographic properties, but because it is easy to implement + and its output is plenty random for non-cryptographic + purposes. + + See http://en.wikipedia.org/wiki/RC4_(cipher) for information + on RC4.*/ + +/* RC4 state. */ +static uint8_t s[256]; /* S[]. */ +static uint8_t s_i, s_j; /* i, j. */ + +/* Already initialized? */ +static bool inited; + +/* Swaps the bytes pointed to by A and B. */ +static inline void +swap_byte (uint8_t *a, uint8_t *b) +{ + uint8_t t = *a; + *a = *b; + *b = t; +} + +/* Initializes or reinitializes the PRNG with the given SEED. */ +void +random_init (unsigned seed) +{ + uint8_t *seedp = (uint8_t *) &seed; + int i; + uint8_t j; + + for (i = 0; i < 256; i++) + s[i] = i; + for (i = j = 0; i < 256; i++) + { + j += s[i] + seedp[i % sizeof seed]; + swap_byte (s + i, s + j); + } + + s_i = s_j = 0; + inited = true; +} + +/* Writes SIZE random bytes into BUF. */ +void +random_bytes (void *buf_, size_t size) +{ + uint8_t *buf; + + if (!inited) + random_init (0); + + for (buf = buf_; size-- > 0; buf++) + { + uint8_t s_k; + + s_i++; + s_j += s[s_i]; + swap_byte (s + s_i, s + s_j); + + s_k = s[s_i] + s[s_j]; + *buf = s[s_k]; + } +} + +/* Returns a pseudo-random unsigned long. + Use random_ulong() % n to obtain a random number in the range + 0...n (exclusive). */ +unsigned long +random_ulong (void) +{ + unsigned long ul; + random_bytes (&ul, sizeof ul); + return ul; +} diff --git a/pintos-env/pintos/lib/random.h b/pintos-env/pintos/lib/random.h new file mode 100755 index 0000000..0950ae2 --- /dev/null +++ b/pintos-env/pintos/lib/random.h @@ -0,0 +1,10 @@ +#ifndef __LIB_RANDOM_H +#define __LIB_RANDOM_H + +#include + +void random_init (unsigned seed); +void random_bytes (void *, size_t); +unsigned long random_ulong (void); + +#endif /* lib/random.h */ diff --git a/pintos-env/pintos/lib/round.h b/pintos-env/pintos/lib/round.h new file mode 100755 index 0000000..3aa6642 --- /dev/null +++ b/pintos-env/pintos/lib/round.h @@ -0,0 +1,18 @@ +#ifndef __LIB_ROUND_H +#define __LIB_ROUND_H + +/* Yields X rounded up to the nearest multiple of STEP. + For X >= 0, STEP >= 1 only. */ +#define ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP) * (STEP)) + +/* Yields X divided by STEP, rounded up. + For X >= 0, STEP >= 1 only. */ +#define DIV_ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP)) + +/* Yields X rounded down to the nearest multiple of STEP. + For X >= 0, STEP >= 1 only. */ +#define ROUND_DOWN(X, STEP) ((X) / (STEP) * (STEP)) + +/* There is no DIV_ROUND_DOWN. It would be simply X / STEP. */ + +#endif /* lib/round.h */ diff --git a/pintos-env/pintos/lib/stdarg.h b/pintos-env/pintos/lib/stdarg.h new file mode 100755 index 0000000..32622b5 --- /dev/null +++ b/pintos-env/pintos/lib/stdarg.h @@ -0,0 +1,14 @@ +#ifndef __LIB_STDARG_H +#define __LIB_STDARG_H + +/* GCC has functionality as built-ins, + so all we need is to use it. */ + +typedef __builtin_va_list va_list; + +#define va_start(LIST, ARG) __builtin_va_start (LIST, ARG) +#define va_end(LIST) __builtin_va_end (LIST) +#define va_arg(LIST, TYPE) __builtin_va_arg (LIST, TYPE) +#define va_copy(DST, SRC) __builtin_va_copy (DST, SRC) + +#endif /* lib/stdarg.h */ diff --git a/pintos-env/pintos/lib/stdbool.h b/pintos-env/pintos/lib/stdbool.h new file mode 100755 index 0000000..f173a91 --- /dev/null +++ b/pintos-env/pintos/lib/stdbool.h @@ -0,0 +1,9 @@ +#ifndef __LIB_STDBOOL_H +#define __LIB_STDBOOL_H + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif /* lib/stdbool.h */ diff --git a/pintos-env/pintos/lib/stddef.h b/pintos-env/pintos/lib/stddef.h new file mode 100755 index 0000000..4e74fa6 --- /dev/null +++ b/pintos-env/pintos/lib/stddef.h @@ -0,0 +1,12 @@ +#ifndef __LIB_STDDEF_H +#define __LIB_STDDEF_H + +#define NULL ((void *) 0) +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER) + +/* GCC predefines the types we need for ptrdiff_t and size_t, + so that we don't have to guess. */ +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef __SIZE_TYPE__ size_t; + +#endif /* lib/stddef.h */ diff --git a/pintos-env/pintos/lib/stdint.h b/pintos-env/pintos/lib/stdint.h new file mode 100755 index 0000000..ef5f214 --- /dev/null +++ b/pintos-env/pintos/lib/stdint.h @@ -0,0 +1,51 @@ +#ifndef __LIB_STDINT_H +#define __LIB_STDINT_H + +typedef signed char int8_t; +#define INT8_MAX 127 +#define INT8_MIN (-INT8_MAX - 1) + +typedef signed short int int16_t; +#define INT16_MAX 32767 +#define INT16_MIN (-INT16_MAX - 1) + +typedef signed int int32_t; +#define INT32_MAX 2147483647 +#define INT32_MIN (-INT32_MAX - 1) + +typedef signed long long int int64_t; +#define INT64_MAX 9223372036854775807LL +#define INT64_MIN (-INT64_MAX - 1) + +typedef unsigned char uint8_t; +#define UINT8_MAX 255 + +typedef unsigned short int uint16_t; +#define UINT16_MAX 65535 + +typedef unsigned int uint32_t; +#define UINT32_MAX 4294967295U + +typedef unsigned long long int uint64_t; +#define UINT64_MAX 18446744073709551615ULL + +typedef int32_t intptr_t; +#define INTPTR_MIN INT32_MIN +#define INTPTR_MAX INT32_MAX + +typedef uint32_t uintptr_t; +#define UINTPTR_MAX UINT32_MAX + +typedef int64_t intmax_t; +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX + +typedef uint64_t uintmax_t; +#define UINTMAX_MAX UINT64_MAX + +#define PTRDIFF_MIN INT32_MIN +#define PTRDIFF_MAX INT32_MAX + +#define SIZE_MAX UINT32_MAX + +#endif /* lib/stdint.h */ diff --git a/pintos-env/pintos/lib/stdio.c b/pintos-env/pintos/lib/stdio.c new file mode 100755 index 0000000..8927c50 --- /dev/null +++ b/pintos-env/pintos/lib/stdio.c @@ -0,0 +1,655 @@ +#include +#include +#include +#include +#include +#include + +/* Auxiliary data for vsnprintf_helper(). */ +struct vsnprintf_aux + { + char *p; /* Current output position. */ + int length; /* Length of output string. */ + int max_length; /* Max length of output string. */ + }; + +static void vsnprintf_helper (char, void *); + +/* Like vprintf(), except that output is stored into BUFFER, + which must have space for BUF_SIZE characters. Writes at most + BUF_SIZE - 1 characters to BUFFER, followed by a null + terminator. BUFFER will always be null-terminated unless + BUF_SIZE is zero. Returns the number of characters that would + have been written to BUFFER, not including a null terminator, + had there been enough room. */ +int +vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args) +{ + /* Set up aux data for vsnprintf_helper(). */ + struct vsnprintf_aux aux; + aux.p = buffer; + aux.length = 0; + aux.max_length = buf_size > 0 ? buf_size - 1 : 0; + + /* Do most of the work. */ + __vprintf (format, args, vsnprintf_helper, &aux); + + /* Add null terminator. */ + if (buf_size > 0) + *aux.p = '\0'; + + return aux.length; +} + +/* Helper function for vsnprintf(). */ +static void +vsnprintf_helper (char ch, void *aux_) +{ + struct vsnprintf_aux *aux = aux_; + + if (aux->length++ < aux->max_length) + *aux->p++ = ch; +} + +/* Like printf(), except that output is stored into BUFFER, + which must have space for BUF_SIZE characters. Writes at most + BUF_SIZE - 1 characters to BUFFER, followed by a null + terminator. BUFFER will always be null-terminated unless + BUF_SIZE is zero. Returns the number of characters that would + have been written to BUFFER, not including a null terminator, + had there been enough room. */ +int +snprintf (char *buffer, size_t buf_size, const char *format, ...) +{ + va_list args; + int retval; + + va_start (args, format); + retval = vsnprintf (buffer, buf_size, format, args); + va_end (args); + + return retval; +} + +/* Writes formatted output to the console. + In the kernel, the console is both the video display and first + serial port. + In userspace, the console is file descriptor 1. */ +int +printf (const char *format, ...) +{ + va_list args; + int retval; + + va_start (args, format); + retval = vprintf (format, args); + va_end (args); + + return retval; +} + +/* printf() formatting internals. */ + +/* A printf() conversion. */ +struct printf_conversion + { + /* Flags. */ + enum + { + MINUS = 1 << 0, /* '-' */ + PLUS = 1 << 1, /* '+' */ + SPACE = 1 << 2, /* ' ' */ + POUND = 1 << 3, /* '#' */ + ZERO = 1 << 4, /* '0' */ + GROUP = 1 << 5 /* '\'' */ + } + flags; + + /* Minimum field width. */ + int width; + + /* Numeric precision. + -1 indicates no precision was specified. */ + int precision; + + /* Type of argument to format. */ + enum + { + CHAR = 1, /* hh */ + SHORT = 2, /* h */ + INT = 3, /* (none) */ + INTMAX = 4, /* j */ + LONG = 5, /* l */ + LONGLONG = 6, /* ll */ + PTRDIFFT = 7, /* t */ + SIZET = 8 /* z */ + } + type; + }; + +struct integer_base + { + int base; /* Base. */ + const char *digits; /* Collection of digits. */ + int x; /* `x' character to use, for base 16 only. */ + int group; /* Number of digits to group with ' flag. */ + }; + +static const struct integer_base base_d = {10, "0123456789", 0, 3}; +static const struct integer_base base_o = {8, "01234567", 0, 3}; +static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4}; +static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4}; + +static const char *parse_conversion (const char *format, + struct printf_conversion *, + va_list *); +static void format_integer (uintmax_t value, bool is_signed, bool negative, + const struct integer_base *, + const struct printf_conversion *, + void (*output) (char, void *), void *aux); +static void output_dup (char ch, size_t cnt, + void (*output) (char, void *), void *aux); +static void format_string (const char *string, int length, + struct printf_conversion *, + void (*output) (char, void *), void *aux); + +void +__vprintf (const char *format, va_list args, + void (*output) (char, void *), void *aux) +{ + for (; *format != '\0'; format++) + { + struct printf_conversion c; + + /* Literally copy non-conversions to output. */ + if (*format != '%') + { + output (*format, aux); + continue; + } + format++; + + /* %% => %. */ + if (*format == '%') + { + output ('%', aux); + continue; + } + + /* Parse conversion specifiers. */ + format = parse_conversion (format, &c, &args); + + /* Do conversion. */ + switch (*format) + { + case 'd': + case 'i': + { + /* Signed integer conversions. */ + intmax_t value; + + switch (c.type) + { + case CHAR: + value = (signed char) va_arg (args, int); + break; + case SHORT: + value = (short) va_arg (args, int); + break; + case INT: + value = va_arg (args, int); + break; + case INTMAX: + value = va_arg (args, intmax_t); + break; + case LONG: + value = va_arg (args, long); + break; + case LONGLONG: + value = va_arg (args, long long); + break; + case PTRDIFFT: + value = va_arg (args, ptrdiff_t); + break; + case SIZET: + value = va_arg (args, size_t); + if (value > SIZE_MAX / 2) + value = value - SIZE_MAX - 1; + break; + default: + NOT_REACHED (); + } + + format_integer (value < 0 ? -value : value, + true, value < 0, &base_d, &c, output, aux); + } + break; + + case 'o': + case 'u': + case 'x': + case 'X': + { + /* Unsigned integer conversions. */ + uintmax_t value; + const struct integer_base *b; + + switch (c.type) + { + case CHAR: + value = (unsigned char) va_arg (args, unsigned); + break; + case SHORT: + value = (unsigned short) va_arg (args, unsigned); + break; + case INT: + value = va_arg (args, unsigned); + break; + case INTMAX: + value = va_arg (args, uintmax_t); + break; + case LONG: + value = va_arg (args, unsigned long); + break; + case LONGLONG: + value = va_arg (args, unsigned long long); + break; + case PTRDIFFT: + value = va_arg (args, ptrdiff_t); +#if UINTMAX_MAX != PTRDIFF_MAX + value &= ((uintmax_t) PTRDIFF_MAX << 1) | 1; +#endif + break; + case SIZET: + value = va_arg (args, size_t); + break; + default: + NOT_REACHED (); + } + + switch (*format) + { + case 'o': b = &base_o; break; + case 'u': b = &base_d; break; + case 'x': b = &base_x; break; + case 'X': b = &base_X; break; + default: NOT_REACHED (); + } + + format_integer (value, false, false, b, &c, output, aux); + } + break; + + case 'c': + { + /* Treat character as single-character string. */ + char ch = va_arg (args, int); + format_string (&ch, 1, &c, output, aux); + } + break; + + case 's': + { + /* String conversion. */ + const char *s = va_arg (args, char *); + if (s == NULL) + s = "(null)"; + + /* Limit string length according to precision. + Note: if c.precision == -1 then strnlen() will get + SIZE_MAX for MAXLEN, which is just what we want. */ + format_string (s, strnlen (s, c.precision), &c, output, aux); + } + break; + + case 'p': + { + /* Pointer conversion. + Format pointers as %#x. */ + void *p = va_arg (args, void *); + + c.flags = POUND; + format_integer ((uintptr_t) p, false, false, + &base_x, &c, output, aux); + } + break; + + case 'f': + case 'e': + case 'E': + case 'g': + case 'G': + case 'n': + /* We don't support floating-point arithmetic, + and %n can be part of a security hole. */ + __printf ("<>", output, aux, *format); + break; + + default: + __printf ("<>", output, aux, *format); + break; + } + } +} + +/* Parses conversion option characters starting at FORMAT and + initializes C appropriately. Returns the character in FORMAT + that indicates the conversion (e.g. the `d' in `%d'). Uses + *ARGS for `*' field widths and precisions. */ +static const char * +parse_conversion (const char *format, struct printf_conversion *c, + va_list *args) +{ + /* Parse flag characters. */ + c->flags = 0; + for (;;) + { + switch (*format++) + { + case '-': + c->flags |= MINUS; + break; + case '+': + c->flags |= PLUS; + break; + case ' ': + c->flags |= SPACE; + break; + case '#': + c->flags |= POUND; + break; + case '0': + c->flags |= ZERO; + break; + case '\'': + c->flags |= GROUP; + break; + default: + format--; + goto not_a_flag; + } + } + not_a_flag: + if (c->flags & MINUS) + c->flags &= ~ZERO; + if (c->flags & PLUS) + c->flags &= ~SPACE; + + /* Parse field width. */ + c->width = 0; + if (*format == '*') + { + format++; + c->width = va_arg (*args, int); + } + else + { + for (; isdigit (*format); format++) + c->width = c->width * 10 + *format - '0'; + } + if (c->width < 0) + { + c->width = -c->width; + c->flags |= MINUS; + } + + /* Parse precision. */ + c->precision = -1; + if (*format == '.') + { + format++; + if (*format == '*') + { + format++; + c->precision = va_arg (*args, int); + } + else + { + c->precision = 0; + for (; isdigit (*format); format++) + c->precision = c->precision * 10 + *format - '0'; + } + if (c->precision < 0) + c->precision = -1; + } + if (c->precision >= 0) + c->flags &= ~ZERO; + + /* Parse type. */ + c->type = INT; + switch (*format++) + { + case 'h': + if (*format == 'h') + { + format++; + c->type = CHAR; + } + else + c->type = SHORT; + break; + + case 'j': + c->type = INTMAX; + break; + + case 'l': + if (*format == 'l') + { + format++; + c->type = LONGLONG; + } + else + c->type = LONG; + break; + + case 't': + c->type = PTRDIFFT; + break; + + case 'z': + c->type = SIZET; + break; + + default: + format--; + break; + } + + return format; +} + +/* Performs an integer conversion, writing output to OUTPUT with + auxiliary data AUX. The integer converted has absolute value + VALUE. If IS_SIGNED is true, does a signed conversion with + NEGATIVE indicating a negative value; otherwise does an + unsigned conversion and ignores NEGATIVE. The output is done + according to the provided base B. Details of the conversion + are in C. */ +static void +format_integer (uintmax_t value, bool is_signed, bool negative, + const struct integer_base *b, + const struct printf_conversion *c, + void (*output) (char, void *), void *aux) +{ + char buf[64], *cp; /* Buffer and current position. */ + int x; /* `x' character to use or 0 if none. */ + int sign; /* Sign character or 0 if none. */ + int precision; /* Rendered precision. */ + int pad_cnt; /* # of pad characters to fill field width. */ + int digit_cnt; /* # of digits output so far. */ + + /* Determine sign character, if any. + An unsigned conversion will never have a sign character, + even if one of the flags requests one. */ + sign = 0; + if (is_signed) + { + if (c->flags & PLUS) + sign = negative ? '-' : '+'; + else if (c->flags & SPACE) + sign = negative ? '-' : ' '; + else if (negative) + sign = '-'; + } + + /* Determine whether to include `0x' or `0X'. + It will only be included with a hexadecimal conversion of a + nonzero value with the # flag. */ + x = (c->flags & POUND) && value ? b->x : 0; + + /* Accumulate digits into buffer. + This algorithm produces digits in reverse order, so later we + will output the buffer's content in reverse. */ + cp = buf; + digit_cnt = 0; + while (value > 0) + { + if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0) + *cp++ = ','; + *cp++ = b->digits[value % b->base]; + value /= b->base; + digit_cnt++; + } + + /* Append enough zeros to match precision. + If requested precision is 0, then a value of zero is + rendered as a null string, otherwise as "0". + If the # flag is used with base 8, the result must always + begin with a zero. */ + precision = c->precision < 0 ? 1 : c->precision; + while (cp - buf < precision && cp < buf + sizeof buf - 1) + *cp++ = '0'; + if ((c->flags & POUND) && b->base == 8 && (cp == buf || cp[-1] != '0')) + *cp++ = '0'; + + /* Calculate number of pad characters to fill field width. */ + pad_cnt = c->width - (cp - buf) - (x ? 2 : 0) - (sign != 0); + if (pad_cnt < 0) + pad_cnt = 0; + + /* Do output. */ + if ((c->flags & (MINUS | ZERO)) == 0) + output_dup (' ', pad_cnt, output, aux); + if (sign) + output (sign, aux); + if (x) + { + output ('0', aux); + output (x, aux); + } + if (c->flags & ZERO) + output_dup ('0', pad_cnt, output, aux); + while (cp > buf) + output (*--cp, aux); + if (c->flags & MINUS) + output_dup (' ', pad_cnt, output, aux); +} + +/* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */ +static void +output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) +{ + while (cnt-- > 0) + output (ch, aux); +} + +/* Formats the LENGTH characters starting at STRING according to + the conversion specified in C. Writes output to OUTPUT with + auxiliary data AUX. */ +static void +format_string (const char *string, int length, + struct printf_conversion *c, + void (*output) (char, void *), void *aux) +{ + int i; + if (c->width > length && (c->flags & MINUS) == 0) + output_dup (' ', c->width - length, output, aux); + for (i = 0; i < length; i++) + output (string[i], aux); + if (c->width > length && (c->flags & MINUS) != 0) + output_dup (' ', c->width - length, output, aux); +} + +/* Wrapper for __vprintf() that converts varargs into a + va_list. */ +void +__printf (const char *format, + void (*output) (char, void *), void *aux, ...) +{ + va_list args; + + va_start (args, aux); + __vprintf (format, args, output, aux); + va_end (args); +} + +/* Dumps the SIZE bytes in BUF to the console as hex bytes + arranged 16 per line. Numeric offsets are also included, + starting at OFS for the first byte in BUF. If ASCII is true + then the corresponding ASCII characters are also rendered + alongside. */ +void +hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii) +{ + const uint8_t *buf = buf_; + const size_t per_line = 16; /* Maximum bytes per line. */ + + while (size > 0) + { + size_t start, end, n; + size_t i; + + /* Number of bytes on this line. */ + start = ofs % per_line; + end = per_line; + if (end - start > size) + end = start + size; + n = end - start; + + /* Print line. */ + printf ("%08jx ", (uintmax_t) ROUND_DOWN (ofs, per_line)); + for (i = 0; i < start; i++) + printf (" "); + for (; i < end; i++) + printf ("%02hhx%c", + buf[i - start], i == per_line / 2 - 1? '-' : ' '); + if (ascii) + { + for (; i < per_line; i++) + printf (" "); + printf ("|"); + for (i = 0; i < start; i++) + printf (" "); + for (; i < end; i++) + printf ("%c", + isprint (buf[i - start]) ? buf[i - start] : '.'); + for (; i < per_line; i++) + printf (" "); + printf ("|"); + } + printf ("\n"); + + ofs += n; + buf += n; + size -= n; + } +} + +/* Prints SIZE, which represents a number of bytes, in a + human-readable format, e.g. "256 kB". */ +void +print_human_readable_size (uint64_t size) +{ + if (size == 1) + printf ("1 byte"); + else + { + static const char *factors[] = {"bytes", "kB", "MB", "GB", "TB", NULL}; + const char **fp; + + for (fp = factors; size >= 1024 && fp[1] != NULL; fp++) + size /= 1024; + printf ("%"PRIu64" %s", size, *fp); + } +} diff --git a/pintos-env/pintos/lib/stdio.h b/pintos-env/pintos/lib/stdio.h new file mode 100755 index 0000000..2739c0a --- /dev/null +++ b/pintos-env/pintos/lib/stdio.h @@ -0,0 +1,40 @@ +#ifndef __LIB_STDIO_H +#define __LIB_STDIO_H + +#include +#include +#include +#include +#include + +/* Include lib/user/stdio.h or lib/kernel/stdio.h, as + appropriate. */ +#include_next + +/* Predefined file handles. */ +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 + +/* Standard functions. */ +int printf (const char *, ...) PRINTF_FORMAT (1, 2); +int snprintf (char *, size_t, const char *, ...) PRINTF_FORMAT (3, 4); +int vprintf (const char *, va_list) PRINTF_FORMAT (1, 0); +int vsnprintf (char *, size_t, const char *, va_list) PRINTF_FORMAT (3, 0); +int putchar (int); +int puts (const char *); + +/* Nonstandard functions. */ +void hex_dump (uintptr_t ofs, const void *, size_t size, bool ascii); +void print_human_readable_size (uint64_t sz); + +/* Internal functions. */ +void __vprintf (const char *format, va_list args, + void (*output) (char, void *), void *aux); +void __printf (const char *format, + void (*output) (char, void *), void *aux, ...); + +/* Try to be helpful. */ +#define sprintf dont_use_sprintf_use_snprintf +#define vsprintf dont_use_vsprintf_use_vsnprintf + +#endif /* lib/stdio.h */ diff --git a/pintos-env/pintos/lib/stdlib.c b/pintos-env/pintos/lib/stdlib.c new file mode 100755 index 0000000..84c7f61 --- /dev/null +++ b/pintos-env/pintos/lib/stdlib.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include + +/* Converts a string representation of a signed decimal integer + in S into an `int', which is returned. */ +int +atoi (const char *s) +{ + bool negative; + int value; + + ASSERT (s != NULL); + + /* Skip white space. */ + while (isspace ((unsigned char) *s)) + s++; + + /* Parse sign. */ + negative = false; + if (*s == '+') + s++; + else if (*s == '-') + { + negative = true; + s++; + } + + /* Parse digits. We always initially parse the value as + negative, and then make it positive later, because the + negative range of an int is bigger than the positive range + on a 2's complement system. */ + for (value = 0; isdigit (*s); s++) + value = value * 10 - (*s - '0'); + if (!negative) + value = -value; + + return value; +} + +/* Compares A and B by calling the AUX function. */ +static int +compare_thunk (const void *a, const void *b, void *aux) +{ + int (**compare) (const void *, const void *) = aux; + return (*compare) (a, b); +} + +/* Sorts ARRAY, which contains CNT elements of SIZE bytes each, + using COMPARE. When COMPARE is passed a pair of elements A + and B, respectively, it must return a strcmp()-type result, + i.e. less than zero if A < B, zero if A == B, greater than + zero if A > B. Runs in O(n lg n) time and O(1) space in + CNT. */ +void +qsort (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *)) +{ + sort (array, cnt, size, compare_thunk, &compare); +} + +/* Swaps elements with 1-based indexes A_IDX and B_IDX in ARRAY + with elements of SIZE bytes each. */ +static void +do_swap (unsigned char *array, size_t a_idx, size_t b_idx, size_t size) +{ + unsigned char *a = array + (a_idx - 1) * size; + unsigned char *b = array + (b_idx - 1) * size; + size_t i; + + for (i = 0; i < size; i++) + { + unsigned char t = a[i]; + a[i] = b[i]; + b[i] = t; + } +} + +/* Compares elements with 1-based indexes A_IDX and B_IDX in + ARRAY with elements of SIZE bytes each, using COMPARE to + compare elements, passing AUX as auxiliary data, and returns a + strcmp()-type result. */ +static int +do_compare (unsigned char *array, size_t a_idx, size_t b_idx, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux) +{ + return compare (array + (a_idx - 1) * size, array + (b_idx - 1) * size, aux); +} + +/* "Float down" the element with 1-based index I in ARRAY of CNT + elements of SIZE bytes each, using COMPARE to compare + elements, passing AUX as auxiliary data. */ +static void +heapify (unsigned char *array, size_t i, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux) +{ + for (;;) + { + /* Set `max' to the index of the largest element among I + and its children (if any). */ + size_t left = 2 * i; + size_t right = 2 * i + 1; + size_t max = i; + if (left <= cnt && do_compare (array, left, max, size, compare, aux) > 0) + max = left; + if (right <= cnt + && do_compare (array, right, max, size, compare, aux) > 0) + max = right; + + /* If the maximum value is already in element I, we're + done. */ + if (max == i) + break; + + /* Swap and continue down the heap. */ + do_swap (array, i, max, size); + i = max; + } +} + +/* Sorts ARRAY, which contains CNT elements of SIZE bytes each, + using COMPARE to compare elements, passing AUX as auxiliary + data. When COMPARE is passed a pair of elements A and B, + respectively, it must return a strcmp()-type result, i.e. less + than zero if A < B, zero if A == B, greater than zero if A > + B. Runs in O(n lg n) time and O(1) space in CNT. */ +void +sort (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux) +{ + size_t i; + + ASSERT (array != NULL || cnt == 0); + ASSERT (compare != NULL); + ASSERT (size > 0); + + /* Build a heap. */ + for (i = cnt / 2; i > 0; i--) + heapify (array, i, cnt, size, compare, aux); + + /* Sort the heap. */ + for (i = cnt; i > 1; i--) + { + do_swap (array, 1, i, size); + heapify (array, 1, i - 1, size, compare, aux); + } +} + +/* Searches ARRAY, which contains CNT elements of SIZE bytes + each, for the given KEY. Returns a match is found, otherwise + a null pointer. If there are multiple matches, returns an + arbitrary one of them. + + ARRAY must be sorted in order according to COMPARE. + + Uses COMPARE to compare elements. When COMPARE is passed a + pair of elements A and B, respectively, it must return a + strcmp()-type result, i.e. less than zero if A < B, zero if A + == B, greater than zero if A > B. */ +void * +bsearch (const void *key, const void *array, size_t cnt, + size_t size, int (*compare) (const void *, const void *)) +{ + return binary_search (key, array, cnt, size, compare_thunk, &compare); +} + +/* Searches ARRAY, which contains CNT elements of SIZE bytes + each, for the given KEY. Returns a match is found, otherwise + a null pointer. If there are multiple matches, returns an + arbitrary one of them. + + ARRAY must be sorted in order according to COMPARE. + + Uses COMPARE to compare elements, passing AUX as auxiliary + data. When COMPARE is passed a pair of elements A and B, + respectively, it must return a strcmp()-type result, i.e. less + than zero if A < B, zero if A == B, greater than zero if A > + B. */ +void * +binary_search (const void *key, const void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux) +{ + const unsigned char *first = array; + const unsigned char *last = array + size * cnt; + + while (first < last) + { + size_t range = (last - first) / size; + const unsigned char *middle = first + (range / 2) * size; + int cmp = compare (key, middle, aux); + + if (cmp < 0) + last = middle; + else if (cmp > 0) + first = middle + size; + else + return (void *) middle; + } + + return NULL; +} + diff --git a/pintos-env/pintos/lib/stdlib.h b/pintos-env/pintos/lib/stdlib.h new file mode 100755 index 0000000..d14afa3 --- /dev/null +++ b/pintos-env/pintos/lib/stdlib.h @@ -0,0 +1,22 @@ +#ifndef __LIB_STDLIB_H +#define __LIB_STDLIB_H + +#include + +/* Standard functions. */ +int atoi (const char *); +void qsort (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *)); +void *bsearch (const void *key, const void *array, size_t cnt, + size_t size, int (*compare) (const void *, const void *)); + +/* Nonstandard functions. */ +void sort (void *array, size_t cnt, size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux); +void *binary_search (const void *key, const void *array, size_t cnt, + size_t size, + int (*compare) (const void *, const void *, void *aux), + void *aux); + +#endif /* lib/stdlib.h */ diff --git a/pintos-env/pintos/lib/string.c b/pintos-env/pintos/lib/string.c new file mode 100755 index 0000000..d223c89 --- /dev/null +++ b/pintos-env/pintos/lib/string.c @@ -0,0 +1,375 @@ +#include +#include + +/* Copies SIZE bytes from SRC to DST, which must not overlap. + Returns DST. */ +void * +memcpy (void *dst_, const void *src_, size_t size) +{ + unsigned char *dst = dst_; + const unsigned char *src = src_; + + ASSERT (dst != NULL || size == 0); + ASSERT (src != NULL || size == 0); + + while (size-- > 0) + *dst++ = *src++; + + return dst_; +} + +/* Copies SIZE bytes from SRC to DST, which are allowed to + overlap. Returns DST. */ +void * +memmove (void *dst_, const void *src_, size_t size) +{ + unsigned char *dst = dst_; + const unsigned char *src = src_; + + ASSERT (dst != NULL || size == 0); + ASSERT (src != NULL || size == 0); + + if (dst < src) + { + while (size-- > 0) + *dst++ = *src++; + } + else + { + dst += size; + src += size; + while (size-- > 0) + *--dst = *--src; + } + + return dst; +} + +/* Find the first differing byte in the two blocks of SIZE bytes + at A and B. Returns a positive value if the byte in A is + greater, a negative value if the byte in B is greater, or zero + if blocks A and B are equal. */ +int +memcmp (const void *a_, const void *b_, size_t size) +{ + const unsigned char *a = a_; + const unsigned char *b = b_; + + ASSERT (a != NULL || size == 0); + ASSERT (b != NULL || size == 0); + + for (; size-- > 0; a++, b++) + if (*a != *b) + return *a > *b ? +1 : -1; + return 0; +} + +/* Finds the first differing characters in strings A and B. + Returns a positive value if the character in A (as an unsigned + char) is greater, a negative value if the character in B (as + an unsigned char) is greater, or zero if strings A and B are + equal. */ +int +strcmp (const char *a_, const char *b_) +{ + const unsigned char *a = (const unsigned char *) a_; + const unsigned char *b = (const unsigned char *) b_; + + ASSERT (a != NULL); + ASSERT (b != NULL); + + while (*a != '\0' && *a == *b) + { + a++; + b++; + } + + return *a < *b ? -1 : *a > *b; +} + +/* Returns a pointer to the first occurrence of CH in the first + SIZE bytes starting at BLOCK. Returns a null pointer if CH + does not occur in BLOCK. */ +void * +memchr (const void *block_, int ch_, size_t size) +{ + const unsigned char *block = block_; + unsigned char ch = ch_; + + ASSERT (block != NULL || size == 0); + + for (; size-- > 0; block++) + if (*block == ch) + return (void *) block; + + return NULL; +} + +/* Finds and returns the first occurrence of C in STRING, or a + null pointer if C does not appear in STRING. If C == '\0' + then returns a pointer to the null terminator at the end of + STRING. */ +char * +strchr (const char *string, int c_) +{ + char c = c_; + + ASSERT (string != NULL); + + for (;;) + if (*string == c) + return (char *) string; + else if (*string == '\0') + return NULL; + else + string++; +} + +/* Returns the length of the initial substring of STRING that + consists of characters that are not in STOP. */ +size_t +strcspn (const char *string, const char *stop) +{ + size_t length; + + for (length = 0; string[length] != '\0'; length++) + if (strchr (stop, string[length]) != NULL) + break; + return length; +} + +/* Returns a pointer to the first character in STRING that is + also in STOP. If no character in STRING is in STOP, returns a + null pointer. */ +char * +strpbrk (const char *string, const char *stop) +{ + for (; *string != '\0'; string++) + if (strchr (stop, *string) != NULL) + return (char *) string; + return NULL; +} + +/* Returns a pointer to the last occurrence of C in STRING. + Returns a null pointer if C does not occur in STRING. */ +char * +strrchr (const char *string, int c_) +{ + char c = c_; + const char *p = NULL; + + for (; *string != '\0'; string++) + if (*string == c) + p = string; + return (char *) p; +} + +/* Returns the length of the initial substring of STRING that + consists of characters in SKIP. */ +size_t +strspn (const char *string, const char *skip) +{ + size_t length; + + for (length = 0; string[length] != '\0'; length++) + if (strchr (skip, string[length]) == NULL) + break; + return length; +} + +/* Returns a pointer to the first occurrence of NEEDLE within + HAYSTACK. Returns a null pointer if NEEDLE does not exist + within HAYSTACK. */ +char * +strstr (const char *haystack, const char *needle) +{ + size_t haystack_len = strlen (haystack); + size_t needle_len = strlen (needle); + + if (haystack_len >= needle_len) + { + size_t i; + + for (i = 0; i <= haystack_len - needle_len; i++) + if (!memcmp (haystack + i, needle, needle_len)) + return (char *) haystack + i; + } + + return NULL; +} + +/* Breaks a string into tokens separated by DELIMITERS. The + first time this function is called, S should be the string to + tokenize, and in subsequent calls it must be a null pointer. + SAVE_PTR is the address of a `char *' variable used to keep + track of the tokenizer's position. The return value each time + is the next token in the string, or a null pointer if no + tokens remain. + + This function treats multiple adjacent delimiters as a single + delimiter. The returned tokens will never be length 0. + DELIMITERS may change from one call to the next within a + single string. + + strtok_r() modifies the string S, changing delimiters to null + bytes. Thus, S must be a modifiable string. String literals, + in particular, are *not* modifiable in C, even though for + backward compatibility they are not `const'. + + Example usage: + + char s[] = " String to tokenize. "; + char *token, *save_ptr; + + for (token = strtok_r (s, " ", &save_ptr); token != NULL; + token = strtok_r (NULL, " ", &save_ptr)) + printf ("'%s'\n", token); + + outputs: + + 'String' + 'to' + 'tokenize.' +*/ +char * +strtok_r (char *s, const char *delimiters, char **save_ptr) +{ + char *token; + + ASSERT (delimiters != NULL); + ASSERT (save_ptr != NULL); + + /* If S is nonnull, start from it. + If S is null, start from saved position. */ + if (s == NULL) + s = *save_ptr; + ASSERT (s != NULL); + + /* Skip any DELIMITERS at our current position. */ + while (strchr (delimiters, *s) != NULL) + { + /* strchr() will always return nonnull if we're searching + for a null byte, because every string contains a null + byte (at the end). */ + if (*s == '\0') + { + *save_ptr = s; + return NULL; + } + + s++; + } + + /* Skip any non-DELIMITERS up to the end of the string. */ + token = s; + while (strchr (delimiters, *s) == NULL) + s++; + if (*s != '\0') + { + *s = '\0'; + *save_ptr = s + 1; + } + else + *save_ptr = s; + return token; +} + +/* Sets the SIZE bytes in DST to VALUE. */ +void * +memset (void *dst_, int value, size_t size) +{ + unsigned char *dst = dst_; + + ASSERT (dst != NULL || size == 0); + + while (size-- > 0) + *dst++ = value; + + return dst_; +} + +/* Returns the length of STRING. */ +size_t +strlen (const char *string) +{ + const char *p; + + ASSERT (string != NULL); + + for (p = string; *p != '\0'; p++) + continue; + return p - string; +} + +/* If STRING is less than MAXLEN characters in length, returns + its actual length. Otherwise, returns MAXLEN. */ +size_t +strnlen (const char *string, size_t maxlen) +{ + size_t length; + + for (length = 0; string[length] != '\0' && length < maxlen; length++) + continue; + return length; +} + +/* Copies string SRC to DST. If SRC is longer than SIZE - 1 + characters, only SIZE - 1 characters are copied. A null + terminator is always written to DST, unless SIZE is 0. + Returns the length of SRC, not including the null terminator. + + strlcpy() is not in the standard C library, but it is an + increasingly popular extension. See + http://www.courtesan.com/todd/papers/strlcpy.html for + information on strlcpy(). */ +size_t +strlcpy (char *dst, const char *src, size_t size) +{ + size_t src_len; + + ASSERT (dst != NULL); + ASSERT (src != NULL); + + src_len = strlen (src); + if (size > 0) + { + size_t dst_len = size - 1; + if (src_len < dst_len) + dst_len = src_len; + memcpy (dst, src, dst_len); + dst[dst_len] = '\0'; + } + return src_len; +} + +/* Concatenates string SRC to DST. The concatenated string is + limited to SIZE - 1 characters. A null terminator is always + written to DST, unless SIZE is 0. Returns the length that the + concatenated string would have assuming that there was + sufficient space, not including a null terminator. + + strlcat() is not in the standard C library, but it is an + increasingly popular extension. See + http://www.courtesan.com/todd/papers/strlcpy.html for + information on strlcpy(). */ +size_t +strlcat (char *dst, const char *src, size_t size) +{ + size_t src_len, dst_len; + + ASSERT (dst != NULL); + ASSERT (src != NULL); + + src_len = strlen (src); + dst_len = strlen (dst); + if (size > 0 && dst_len < size) + { + size_t copy_cnt = size - dst_len - 1; + if (src_len < copy_cnt) + copy_cnt = src_len; + memcpy (dst + dst_len, src, copy_cnt); + dst[dst_len + copy_cnt] = '\0'; + } + return src_len + dst_len; +} + diff --git a/pintos-env/pintos/lib/string.h b/pintos-env/pintos/lib/string.h new file mode 100755 index 0000000..1fff82a --- /dev/null +++ b/pintos-env/pintos/lib/string.h @@ -0,0 +1,35 @@ +#ifndef __LIB_STRING_H +#define __LIB_STRING_H + +#include + +/* Standard. */ +void *memcpy (void *, const void *, size_t); +void *memmove (void *, const void *, size_t); +char *strncat (char *, const char *, size_t); +int memcmp (const void *, const void *, size_t); +int strcmp (const char *, const char *); +void *memchr (const void *, int, size_t); +char *strchr (const char *, int); +size_t strcspn (const char *, const char *); +char *strpbrk (const char *, const char *); +char *strrchr (const char *, int); +size_t strspn (const char *, const char *); +char *strstr (const char *, const char *); +void *memset (void *, int, size_t); +size_t strlen (const char *); + +/* Extensions. */ +size_t strlcpy (char *, const char *, size_t); +size_t strlcat (char *, const char *, size_t); +char *strtok_r (char *, const char *, char **); +size_t strnlen (const char *, size_t); + +/* Try to be helpful. */ +#define strcpy dont_use_strcpy_use_strlcpy +#define strncpy dont_use_strncpy_use_strlcpy +#define strcat dont_use_strcat_use_strlcat +#define strncat dont_use_strncat_use_strlcat +#define strtok dont_use_strtok_use_strtok_r + +#endif /* lib/string.h */ diff --git a/pintos-env/pintos/lib/syscall-nr.h b/pintos-env/pintos/lib/syscall-nr.h new file mode 100755 index 0000000..21a7af9 --- /dev/null +++ b/pintos-env/pintos/lib/syscall-nr.h @@ -0,0 +1,34 @@ +#ifndef __LIB_SYSCALL_NR_H +#define __LIB_SYSCALL_NR_H + +/* System call numbers. */ +enum + { + /* Projects 2 and later. */ + SYS_HALT, /* Halt the operating system. */ + SYS_EXIT, /* Terminate this process. */ + SYS_EXEC, /* Start another process. */ + SYS_WAIT, /* Wait for a child process to die. */ + SYS_CREATE, /* Create a file. */ + SYS_REMOVE, /* Delete a file. */ + SYS_OPEN, /* Open a file. */ + SYS_FILESIZE, /* Obtain a file's size. */ + SYS_READ, /* Read from a file. */ + SYS_WRITE, /* Write to a file. */ + SYS_SEEK, /* Change position in a file. */ + SYS_TELL, /* Report current position in a file. */ + SYS_CLOSE, /* Close a file. */ + + /* Project 3 and optionally project 4. */ + SYS_MMAP, /* Map a file into memory. */ + SYS_MUNMAP, /* Remove a memory mapping. */ + + /* Project 4 only. */ + SYS_CHDIR, /* Change the current directory. */ + SYS_MKDIR, /* Create a directory. */ + SYS_READDIR, /* Reads a directory entry. */ + SYS_ISDIR, /* Tests if a fd represents a directory. */ + SYS_INUMBER /* Returns the inode number for a fd. */ + }; + +#endif /* lib/syscall-nr.h */ diff --git a/pintos-env/pintos/lib/user/console.c b/pintos-env/pintos/lib/user/console.c new file mode 100755 index 0000000..22bdc8c --- /dev/null +++ b/pintos-env/pintos/lib/user/console.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +/* The standard vprintf() function, + which is like printf() but uses a va_list. */ +int +vprintf (const char *format, va_list args) +{ + return vhprintf (STDOUT_FILENO, format, args); +} + +/* Like printf(), but writes output to the given HANDLE. */ +int +hprintf (int handle, const char *format, ...) +{ + va_list args; + int retval; + + va_start (args, format); + retval = vhprintf (handle, format, args); + va_end (args); + + return retval; +} + +/* Writes string S to the console, followed by a new-line + character. */ +int +puts (const char *s) +{ + write (STDOUT_FILENO, s, strlen (s)); + putchar ('\n'); + + return 0; +} + +/* Writes C to the console. */ +int +putchar (int c) +{ + char c2 = c; + write (STDOUT_FILENO, &c2, 1); + return c; +} + +/* Auxiliary data for vhprintf_helper(). */ +struct vhprintf_aux + { + char buf[64]; /* Character buffer. */ + char *p; /* Current position in buffer. */ + int char_cnt; /* Total characters written so far. */ + int handle; /* Output file handle. */ + }; + +static void add_char (char, void *); +static void flush (struct vhprintf_aux *); + +/* Formats the printf() format specification FORMAT with + arguments given in ARGS and writes the output to the given + HANDLE. */ +int +vhprintf (int handle, const char *format, va_list args) +{ + struct vhprintf_aux aux; + aux.p = aux.buf; + aux.char_cnt = 0; + aux.handle = handle; + __vprintf (format, args, add_char, &aux); + flush (&aux); + return aux.char_cnt; +} + +/* Adds C to the buffer in AUX, flushing it if the buffer fills + up. */ +static void +add_char (char c, void *aux_) +{ + struct vhprintf_aux *aux = aux_; + *aux->p++ = c; + if (aux->p >= aux->buf + sizeof aux->buf) + flush (aux); + aux->char_cnt++; +} + +/* Flushes the buffer in AUX. */ +static void +flush (struct vhprintf_aux *aux) +{ + if (aux->p > aux->buf) + write (aux->handle, aux->buf, aux->p - aux->buf); + aux->p = aux->buf; +} diff --git a/pintos-env/pintos/lib/user/debug.c b/pintos-env/pintos/lib/user/debug.c new file mode 100755 index 0000000..f49b874 --- /dev/null +++ b/pintos-env/pintos/lib/user/debug.c @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include + +/* Aborts the user program, printing the source file name, line + number, and function name, plus a user-specific message. */ +void +debug_panic (const char *file, int line, const char *function, + const char *message, ...) +{ + va_list args; + + printf ("User process ABORT at %s:%d in %s(): ", file, line, function); + + va_start (args, message); + vprintf (message, args); + printf ("\n"); + va_end (args); + + debug_backtrace (); + + exit (1); +} diff --git a/pintos-env/pintos/lib/user/entry.c b/pintos-env/pintos/lib/user/entry.c new file mode 100755 index 0000000..a707c70 --- /dev/null +++ b/pintos-env/pintos/lib/user/entry.c @@ -0,0 +1,10 @@ +#include + +int main (int, char *[]); +void _start (int argc, char *argv[]); + +void +_start (int argc, char *argv[]) +{ + exit (main (argc, argv)); +} diff --git a/pintos-env/pintos/lib/user/stdio.h b/pintos-env/pintos/lib/user/stdio.h new file mode 100755 index 0000000..b9f3cc6 --- /dev/null +++ b/pintos-env/pintos/lib/user/stdio.h @@ -0,0 +1,7 @@ +#ifndef __LIB_USER_STDIO_H +#define __LIB_USER_STDIO_H + +int hprintf (int, const char *, ...) PRINTF_FORMAT (2, 3); +int vhprintf (int, const char *, va_list) PRINTF_FORMAT (2, 0); + +#endif /* lib/user/stdio.h */ diff --git a/pintos-env/pintos/lib/user/syscall.c b/pintos-env/pintos/lib/user/syscall.c new file mode 100755 index 0000000..f0ebc60 --- /dev/null +++ b/pintos-env/pintos/lib/user/syscall.c @@ -0,0 +1,184 @@ +#include +#include "../syscall-nr.h" + +/* Invokes syscall NUMBER, passing no arguments, and returns the + return value as an `int'. */ +#define syscall0(NUMBER) \ + ({ \ + int retval; \ + asm volatile \ + ("pushl %[number]; int $0x30; addl $4, %%esp" \ + : "=a" (retval) \ + : [number] "i" (NUMBER) \ + : "memory"); \ + retval; \ + }) + +/* Invokes syscall NUMBER, passing argument ARG0, and returns the + return value as an `int'. */ +#define syscall1(NUMBER, ARG0) \ + ({ \ + int retval; \ + asm volatile \ + ("pushl %[arg0]; pushl %[number]; int $0x30; addl $8, %%esp" \ + : "=a" (retval) \ + : [number] "i" (NUMBER), \ + [arg0] "r" (ARG0) \ + : "memory"); \ + retval; \ + }) + +/* Invokes syscall NUMBER, passing arguments ARG0 and ARG1, and + returns the return value as an `int'. */ +#define syscall2(NUMBER, ARG0, ARG1) \ + ({ \ + int retval; \ + asm volatile \ + ("pushl %[arg1]; pushl %[arg0]; " \ + "pushl %[number]; int $0x30; addl $12, %%esp" \ + : "=a" (retval) \ + : [number] "i" (NUMBER), \ + [arg0] "r" (ARG0), \ + [arg1] "r" (ARG1) \ + : "memory"); \ + retval; \ + }) + +/* Invokes syscall NUMBER, passing arguments ARG0, ARG1, and + ARG2, and returns the return value as an `int'. */ +#define syscall3(NUMBER, ARG0, ARG1, ARG2) \ + ({ \ + int retval; \ + asm volatile \ + ("pushl %[arg2]; pushl %[arg1]; pushl %[arg0]; " \ + "pushl %[number]; int $0x30; addl $16, %%esp" \ + : "=a" (retval) \ + : [number] "i" (NUMBER), \ + [arg0] "r" (ARG0), \ + [arg1] "r" (ARG1), \ + [arg2] "r" (ARG2) \ + : "memory"); \ + retval; \ + }) + +void +halt (void) +{ + syscall0 (SYS_HALT); + NOT_REACHED (); +} + +void +exit (int status) +{ + syscall1 (SYS_EXIT, status); + NOT_REACHED (); +} + +pid_t +exec (const char *file) +{ + return (pid_t) syscall1 (SYS_EXEC, file); +} + +int +wait (pid_t pid) +{ + return syscall1 (SYS_WAIT, pid); +} + +bool +create (const char *file, unsigned initial_size) +{ + return syscall2 (SYS_CREATE, file, initial_size); +} + +bool +remove (const char *file) +{ + return syscall1 (SYS_REMOVE, file); +} + +int +open (const char *file) +{ + return syscall1 (SYS_OPEN, file); +} + +int +filesize (int fd) +{ + return syscall1 (SYS_FILESIZE, fd); +} + +int +read (int fd, void *buffer, unsigned size) +{ + return syscall3 (SYS_READ, fd, buffer, size); +} + +int +write (int fd, const void *buffer, unsigned size) +{ + return syscall3 (SYS_WRITE, fd, buffer, size); +} + +void +seek (int fd, unsigned position) +{ + syscall2 (SYS_SEEK, fd, position); +} + +unsigned +tell (int fd) +{ + return syscall1 (SYS_TELL, fd); +} + +void +close (int fd) +{ + syscall1 (SYS_CLOSE, fd); +} + +mapid_t +mmap (int fd, void *addr) +{ + return syscall2 (SYS_MMAP, fd, addr); +} + +void +munmap (mapid_t mapid) +{ + syscall1 (SYS_MUNMAP, mapid); +} + +bool +chdir (const char *dir) +{ + return syscall1 (SYS_CHDIR, dir); +} + +bool +mkdir (const char *dir) +{ + return syscall1 (SYS_MKDIR, dir); +} + +bool +readdir (int fd, char name[READDIR_MAX_LEN + 1]) +{ + return syscall2 (SYS_READDIR, fd, name); +} + +bool +isdir (int fd) +{ + return syscall1 (SYS_ISDIR, fd); +} + +int +inumber (int fd) +{ + return syscall1 (SYS_INUMBER, fd); +} diff --git a/pintos-env/pintos/lib/user/syscall.h b/pintos-env/pintos/lib/user/syscall.h new file mode 100755 index 0000000..8a9e0c0 --- /dev/null +++ b/pintos-env/pintos/lib/user/syscall.h @@ -0,0 +1,48 @@ +#ifndef __LIB_USER_SYSCALL_H +#define __LIB_USER_SYSCALL_H + +#include +#include + +/* Process identifier. */ +typedef int pid_t; +#define PID_ERROR ((pid_t) -1) + +/* Map region identifier. */ +typedef int mapid_t; +#define MAP_FAILED ((mapid_t) -1) + +/* Maximum characters in a filename written by readdir(). */ +#define READDIR_MAX_LEN 14 + +/* Typical return values from main() and arguments to exit(). */ +#define EXIT_SUCCESS 0 /* Successful execution. */ +#define EXIT_FAILURE 1 /* Unsuccessful execution. */ + +/* Projects 2 and later. */ +void halt (void) NO_RETURN; +void exit (int status) NO_RETURN; +pid_t exec (const char *file); +int wait (pid_t); +bool create (const char *file, unsigned initial_size); +bool remove (const char *file); +int open (const char *file); +int filesize (int fd); +int read (int fd, void *buffer, unsigned length); +int write (int fd, const void *buffer, unsigned length); +void seek (int fd, unsigned position); +unsigned tell (int fd); +void close (int fd); + +/* Project 3 and optionally project 4. */ +mapid_t mmap (int fd, void *addr); +void munmap (mapid_t); + +/* Project 4 only. */ +bool chdir (const char *dir); +bool mkdir (const char *dir); +bool readdir (int fd, char name[READDIR_MAX_LEN + 1]); +bool isdir (int fd); +int inumber (int fd); + +#endif /* lib/user/syscall.h */ diff --git a/pintos-env/pintos/lib/user/user.lds b/pintos-env/pintos/lib/user/user.lds new file mode 100755 index 0000000..cc6d6c0 --- /dev/null +++ b/pintos-env/pintos/lib/user/user.lds @@ -0,0 +1,57 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + /* Read-only sections, merged into text segment: */ + __executable_start = 0x08048000 + SIZEOF_HEADERS; + . = 0x08048000 + SIZEOF_HEADERS; + .text : { *(.text) } = 0x90 + .rodata : { *(.rodata) } + + /* Adjust the address for the data segment. We want to adjust up to + the same address within the page on the next page up. */ + . = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1)); + . = DATA_SEGMENT_ALIGN (0x1000, 0x1000); + + .data : { *(.data) } + .bss : { *(.bss) } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + + /* DWARF debug sections. + Symbols in the DWARF debugging sections are relative to the beginning + of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } + /DISCARD/ : { *(.note.GNU-stack) } + /DISCARD/ : { *(.eh_frame) } +} diff --git a/pintos-env/pintos/lib/ustar.c b/pintos-env/pintos/lib/ustar.c new file mode 100755 index 0000000..49af69a --- /dev/null +++ b/pintos-env/pintos/lib/ustar.c @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include + +/* Header for ustar-format tar archive. See the documentation of + the "pax" utility in [SUSv3] for the the "ustar" format + specification. */ +struct ustar_header + { + char name[100]; /* File name. Null-terminated if room. */ + char mode[8]; /* Permissions as octal string. */ + char uid[8]; /* User ID as octal string. */ + char gid[8]; /* Group ID as octal string. */ + char size[12]; /* File size in bytes as octal string. */ + char mtime[12]; /* Modification time in seconds + from Jan 1, 1970, as octal string. */ + char chksum[8]; /* Sum of octets in header as octal string. */ + char typeflag; /* An enum ustar_type value. */ + char linkname[100]; /* Name of link target. + Null-terminated if room. */ + char magic[6]; /* "ustar\0" */ + char version[2]; /* "00" */ + char uname[32]; /* User name, always null-terminated. */ + char gname[32]; /* Group name, always null-terminated. */ + char devmajor[8]; /* Device major number as octal string. */ + char devminor[8]; /* Device minor number as octal string. */ + char prefix[155]; /* Prefix to file name. + Null-terminated if room. */ + char padding[12]; /* Pad to 512 bytes. */ + } +PACKED; + +/* Returns the checksum for the given ustar format HEADER. */ +static unsigned int +calculate_chksum (const struct ustar_header *h) +{ + const uint8_t *header = (const uint8_t *) h; + unsigned int chksum; + size_t i; + + chksum = 0; + for (i = 0; i < USTAR_HEADER_SIZE; i++) + { + /* The ustar checksum is calculated as if the chksum field + were all spaces. */ + const size_t chksum_start = offsetof (struct ustar_header, chksum); + const size_t chksum_end = chksum_start + sizeof h->chksum; + bool in_chksum_field = i >= chksum_start && i < chksum_end; + chksum += in_chksum_field ? ' ' : header[i]; + } + return chksum; +} + +/* Drop possibly dangerous prefixes from FILE_NAME and return the + stripped name. An archive with file names that start with "/" + or "../" could cause a naive tar extractor to write to + arbitrary parts of the file system, not just the destination + directory. We don't want to create such archives or be such a + naive extractor. + + The return value can be a suffix of FILE_NAME or a string + literal. */ +static const char * +strip_antisocial_prefixes (const char *file_name) +{ + while (*file_name == '/' + || !memcmp (file_name, "./", 2) + || !memcmp (file_name, "../", 3)) + file_name = strchr (file_name, '/') + 1; + return *file_name == '\0' || !strcmp (file_name, "..") ? "." : file_name; +} + +/* Composes HEADER as a USTAR_HEADER_SIZE (512)-byte archive + header in ustar format for a SIZE-byte file named FILE_NAME of + the given TYPE. The caller is responsible for writing the + header to a file or device. + + If successful, returns true. On failure (due to an + excessively long file name), returns false. */ +bool +ustar_make_header (const char *file_name, enum ustar_type type, + int size, char header[USTAR_HEADER_SIZE]) +{ + struct ustar_header *h = (struct ustar_header *) header; + + ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE); + ASSERT (type == USTAR_REGULAR || type == USTAR_DIRECTORY); + + /* Check file name. */ + file_name = strip_antisocial_prefixes (file_name); + if (strlen (file_name) > 99) + { + printf ("%s: file name too long\n", file_name); + return false; + } + + /* Fill in header except for final checksum. */ + memset (h, 0, sizeof *h); + strlcpy (h->name, file_name, sizeof h->name); + snprintf (h->mode, sizeof h->mode, "%07o", + type == USTAR_REGULAR ? 0644 : 0755); + strlcpy (h->uid, "0000000", sizeof h->uid); + strlcpy (h->gid, "0000000", sizeof h->gid); + snprintf (h->size, sizeof h->size, "%011o", size); + snprintf (h->mtime, sizeof h->size, "%011o", 1136102400); + h->typeflag = type; + strlcpy (h->magic, "ustar", sizeof h->magic); + h->version[0] = h->version[1] = '0'; + strlcpy (h->gname, "root", sizeof h->gname); + strlcpy (h->uname, "root", sizeof h->uname); + + /* Compute and fill in final checksum. */ + snprintf (h->chksum, sizeof h->chksum, "%07o", calculate_chksum (h)); + + return true; +} + +/* Parses a SIZE-byte octal field in S in the format used by + ustar format. If successful, stores the field's value in + *VALUE and returns true; on failure, returns false. + + ustar octal fields consist of a sequence of octal digits + terminated by a space or a null byte. The ustar specification + seems ambiguous as to whether these fields must be padded on + the left with '0's, so we accept any field that fits in the + available space, regardless of whether it fills the space. */ +static bool +parse_octal_field (const char *s, size_t size, unsigned long int *value) +{ + size_t ofs; + + *value = 0; + for (ofs = 0; ofs < size; ofs++) + { + char c = s[ofs]; + if (c >= '0' && c <= '7') + { + if (*value > ULONG_MAX / 8) + { + /* Overflow. */ + return false; + } + *value = c - '0' + *value * 8; + } + else if (c == ' ' || c == '\0') + { + /* End of field, but disallow completely empty + fields. */ + return ofs > 0; + } + else + { + /* Bad character. */ + return false; + } + } + + /* Field did not end in space or null byte. */ + return false; +} + +/* Returns true if the CNT bytes starting at BLOCK are all zero, + false otherwise. */ +static bool +is_all_zeros (const char *block, size_t cnt) +{ + while (cnt-- > 0) + if (*block++ != 0) + return false; + return true; +} + +/* Parses HEADER as a ustar-format archive header for a regular + file or directory. If successful, stores the archived file's + name in *FILE_NAME (as a pointer into HEADER or a string + literal), its type in *TYPE, and its size in bytes in *SIZE, + and returns a null pointer. On failure, returns a + human-readable error message. */ +const char * +ustar_parse_header (const char header[USTAR_HEADER_SIZE], + const char **file_name, enum ustar_type *type, int *size) +{ + const struct ustar_header *h = (const struct ustar_header *) header; + unsigned long int chksum, size_ul; + + ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE); + + /* Detect end of archive. */ + if (is_all_zeros (header, USTAR_HEADER_SIZE)) + { + *file_name = NULL; + *type = USTAR_EOF; + *size = 0; + return NULL; + } + + /* Validate ustar header. */ + if (memcmp (h->magic, "ustar", 6)) + return "not a ustar archive"; + else if (h->version[0] != '0' || h->version[1] != '0') + return "invalid ustar version"; + else if (!parse_octal_field (h->chksum, sizeof h->chksum, &chksum)) + return "corrupt chksum field"; + else if (chksum != calculate_chksum (h)) + return "checksum mismatch"; + else if (h->name[sizeof h->name - 1] != '\0' || h->prefix[0] != '\0') + return "file name too long"; + else if (h->typeflag != USTAR_REGULAR && h->typeflag != USTAR_DIRECTORY) + return "unimplemented file type"; + if (h->typeflag == USTAR_REGULAR) + { + if (!parse_octal_field (h->size, sizeof h->size, &size_ul)) + return "corrupt file size field"; + else if (size_ul > INT_MAX) + return "file too large"; + } + else + size_ul = 0; + + /* Success. */ + *file_name = strip_antisocial_prefixes (h->name); + *type = h->typeflag; + *size = size_ul; + return NULL; +} + diff --git a/pintos-env/pintos/lib/ustar.h b/pintos-env/pintos/lib/ustar.h new file mode 100755 index 0000000..43a5513 --- /dev/null +++ b/pintos-env/pintos/lib/ustar.h @@ -0,0 +1,29 @@ +#ifndef __LIB_USTAR_H +#define __LIB_USTAR_H + +/* Support for the standard Posix "ustar" format. See the + documentation of the "pax" utility in [SUSv3] for the the + "ustar" format specification. */ + +#include + +/* Type of a file entry in an archive. + The values here are the bytes that appear in the file format. + Only types of interest to Pintos are listed here. */ +enum ustar_type + { + USTAR_REGULAR = '0', /* Ordinary file. */ + USTAR_DIRECTORY = '5', /* Directory. */ + USTAR_EOF = -1 /* End of archive (not an official value). */ + }; + +/* Size of a ustar archive header, in bytes. */ +#define USTAR_HEADER_SIZE 512 + +bool ustar_make_header (const char *file_name, enum ustar_type, + int size, char header[USTAR_HEADER_SIZE]); +const char *ustar_parse_header (const char header[USTAR_HEADER_SIZE], + const char **file_name, + enum ustar_type *, int *size); + +#endif /* lib/ustar.h */ diff --git a/pintos-env/pintos/misc/bochs-2.2.6-big-endian.patch b/pintos-env/pintos/misc/bochs-2.2.6-big-endian.patch new file mode 100755 index 0000000..420f69b --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-big-endian.patch @@ -0,0 +1,63 @@ +diff -urp bochs-2.2.6/gdbstub.cc bochs-2.2.6.orig/gdbstub.cc +--- bochs-2.2.6.orig/gdbstub.cc 2006-01-17 09:15:29.000000000 -0800 ++++ bochs-2.2.6/gdbstub.cc 2006-04-03 13:47:39.000000000 -0700 +@@ -672,35 +672,36 @@ + + case 'g': + #if !BX_SUPPORT_X86_64 +- registers[0] = EAX; +- registers[1] = ECX; +- registers[2] = EDX; +- registers[3] = EBX; +- registers[4] = ESP; +- registers[5] = EBP; +- registers[6] = ESI; +- registers[7] = EDI; ++ WriteHostDWordToLittleEndian(registers + 0, EAX); ++ WriteHostDWordToLittleEndian(registers + 1, ECX); ++ WriteHostDWordToLittleEndian(registers + 2, EDX); ++ WriteHostDWordToLittleEndian(registers + 3, EBX); ++ WriteHostDWordToLittleEndian(registers + 4, ESP); ++ WriteHostDWordToLittleEndian(registers + 5, EBP); ++ WriteHostDWordToLittleEndian(registers + 6, ESI); ++ WriteHostDWordToLittleEndian(registers + 7, EDI); + if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT) + { +- registers[8] = EIP + 1; ++ WriteHostDWordToLittleEndian(registers + 8, EIP + 1); + } + else + { +- registers[8] = EIP; ++ WriteHostDWordToLittleEndian(registers + 8, EIP); + } +- registers[9] = BX_CPU_THIS_PTR read_eflags(); +- registers[10] = +- BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value; +- registers[11] = +- BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value; +- registers[12] = +- BX_CPU_THIS_PTR sregs[BX_SEG_REG_DS].selector.value; +- registers[13] = +- BX_CPU_THIS_PTR sregs[BX_SEG_REG_ES].selector.value; +- registers[14] = +- BX_CPU_THIS_PTR sregs[BX_SEG_REG_FS].selector.value; +- registers[15] = +- BX_CPU_THIS_PTR sregs[BX_SEG_REG_GS].selector.value; ++ WriteHostDWordToLittleEndian(registers + 9, ++ BX_CPU_THIS_PTR read_eflags()); ++ WriteHostDWordToLittleEndian(registers + 10, ++ BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value); ++ WriteHostDWordToLittleEndian(registers + 11, ++ BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value); ++ WriteHostDWordToLittleEndian(registers + 12, ++ BX_CPU_THIS_PTR sregs[BX_SEG_REG_DS].selector.value); ++ WriteHostDWordToLittleEndian(registers + 13, ++ BX_CPU_THIS_PTR sregs[BX_SEG_REG_ES].selector.value); ++ WriteHostDWordToLittleEndian(registers + 14, ++ BX_CPU_THIS_PTR sregs[BX_SEG_REG_FS].selector.value); ++ WriteHostDWordToLittleEndian(registers + 15, ++ BX_CPU_THIS_PTR sregs[BX_SEG_REG_GS].selector.value); + mem2hex((char *)registers, obuf, NUMREGSBYTES); + #else + #define PUTREG(buf, val, len) do { \ diff --git a/pintos-env/pintos/misc/bochs-2.2.6-build.sh b/pintos-env/pintos/misc/bochs-2.2.6-build.sh new file mode 100755 index 0000000..a8a44e3 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-build.sh @@ -0,0 +1,40 @@ +#! /bin/sh -e + +if test -z "$SRCDIR" || test -z "$PINTOSDIR" || test -z "$DSTDIR"; then + echo "usage: env SRCDIR= PINTOSDIR= DSTDIR= sh $0" + echo " where contains bochs-2.2.6.tar.gz" + echo " and is the root of the pintos source tree" + echo " and is the installation prefix (e.g. /usr/local)" + exit 1 +fi + +cd /tmp +mkdir $$ +cd $$ +mkdir bochs-2.2.6 +tar xzf $SRCDIR/bochs-2.2.6.tar.gz +cd bochs-2.2.6 +cat $PINTOSDIR/src/misc/bochs-2.2.6-ms-extensions.patch | patch -p1 +cat $PINTOSDIR/src/misc/bochs-2.2.6-big-endian.patch | patch -p1 +cat $PINTOSDIR/src/misc/bochs-2.2.6-jitter.patch | patch -p1 +cat $PINTOSDIR/src/misc/bochs-2.2.6-triple-fault.patch | patch -p1 +cat $PINTOSDIR/src/misc/bochs-2.2.6-solaris-tty.patch | patch -p1 +cat $PINTOSDIR/src/misc/bochs-2.2.6-page-fault-segv.patch | patch -p1 +cat $PINTOSDIR/src/misc/bochs-2.2.6-paranoia.patch | patch -p1 +cat $PINTOSDIR/src/misc/bochs-2.2.6-gdbstub-ENN.patch | patch -p1 +if test "`uname -s`" = "SunOS"; then + cat $PINTOSDIR/src/misc/bochs-2.2.6-solaris-link.patch | patch -p1 +fi +CFGOPTS="--with-x --with-x11 --with-term --with-nogui --prefix=$DSTDIR" +mkdir plain && + cd plain && + ../configure $CFGOPTS --enable-gdb-stub && + make && + make install && + cd .. +mkdir with-dbg && + cd with-dbg && + ../configure --enable-debugger $CFGOPTS && + make && + cp bochs $DSTDIR/bin/bochs-dbg && + cd .. diff --git a/pintos-env/pintos/misc/bochs-2.2.6-gdbstub-ENN.patch b/pintos-env/pintos/misc/bochs-2.2.6-gdbstub-ENN.patch new file mode 100755 index 0000000..88a8145 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-gdbstub-ENN.patch @@ -0,0 +1,29 @@ +--- bochs-2.2.6/gdbstub.cc 2006-01-17 12:15:29.000000000 -0500 ++++ bochs-2.2.6-patched/gdbstub.cc 2007-02-06 23:04:51.095523904 -0500 +@@ -515,7 +515,7 @@ + } + else + { +- put_reply("ENN"); ++ put_reply("Eff"); + } + } + break; +@@ -761,7 +761,7 @@ + } + else + { +- put_reply("ENN"); ++ put_reply("Eff"); + } + break; + +@@ -782,7 +782,7 @@ + } + else + { +- put_reply("ENN"); ++ put_reply("Eff"); + } + break; + diff --git a/pintos-env/pintos/misc/bochs-2.2.6-jitter.patch b/pintos-env/pintos/misc/bochs-2.2.6-jitter.patch new file mode 100755 index 0000000..48917e0 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-jitter.patch @@ -0,0 +1,61 @@ +diff -urp bochs-2.2.6/iodev/pit82c54.cc bochs-2.2.6.orig/iodev/pit82c54.cc +--- bochs-2.2.6.orig/iodev/pit82c54.cc 2006-01-08 12:39:08.000000000 -0800 ++++ bochs-2.2.6/iodev/pit82c54.cc 2006-04-03 14:00:27.000000000 -0700 +@@ -28,6 +28,7 @@ + + #include "iodev.h" + #include "pit82c54.h" ++#include + #define LOG_THIS this-> + + +@@ -359,7 +360,13 @@ + case 2: + if(thisctr.count_written) { + if(thisctr.triggerGATE || thisctr.first_pass) { +- set_count(thisctr, thisctr.inlatch); ++ unsigned n = thisctr.inlatch; ++ if (jitter && n > 5) { ++ n *= (double) rand() / RAND_MAX; ++ if (n < 5) ++ n = 5; ++ } ++ set_count(thisctr, n); + thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF; + thisctr.null_count=0; + if(thisctr.inlatch==1) { +diff -urp bochs-2.2.6/main.cc bochs-2.2.6.orig/main.cc +--- bochs-2.2.6.orig/main.cc 2006-01-22 04:31:15.000000000 -0800 ++++ bochs-2.2.6/main.cc 2006-04-03 14:00:54.000000000 -0700 +@@ -105,6 +105,7 @@ + #endif + + char *bochsrc_filename = NULL; ++int jitter = 0; + + void bx_print_header () + { +@@ -459,6 +460,13 @@ + else if (!strcmp ("-q", argv[arg])) { + SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START); + } ++ else if (!strcmp ("-j", argv[arg])) { ++ if (++arg >= argc) BX_PANIC(("-j must be followed by a number")); ++ else { ++ jitter = 1; ++ srand (atoi (argv[arg])); ++ } ++ } + else if (!strcmp ("-f", argv[arg])) { + if (++arg >= argc) BX_PANIC(("-f must be followed by a filename")); + else bochsrc_filename = argv[arg]; +diff -up /home/blp/cs140/bochs-2.2.6/bochs.h\~ /home/blp/cs140/bochs-2.2.6/bochs.h +--- bochs-2.2.6/bochs.h.orig 2006-01-28 08:16:02.000000000 -0800 ++++ bochs-2.2.6/bochs.h 2006-04-03 14:03:54.000000000 -0700 +@@ -698,4 +698,6 @@ int bx_init_hardware (); + + #endif + ++extern int jitter; ++ + #endif /* BX_BOCHS_H */ diff --git a/pintos-env/pintos/misc/bochs-2.2.6-ms-extensions.patch b/pintos-env/pintos/misc/bochs-2.2.6-ms-extensions.patch new file mode 100755 index 0000000..a6e50a0 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-ms-extensions.patch @@ -0,0 +1,14 @@ +diff -urp orig/bochs-2.1.1/gui/Makefile.in bochs-2.1.1/gui/Makefile.in +--- orig/bochs-2.1.1/gui/Makefile.in 2003-11-28 07:07:28.000000000 -0800 ++++ bochs-2.1.1/gui/Makefile.in 2004-09-13 15:05:09.402039000 -0700 +@@ -44,7 +44,7 @@ SHELL = /bin/sh + @SET_MAKE@ + + CXX = @CXX@ +-CXXFLAGS = $(BX_INCDIRS) @CXXFLAGS@ @GUI_CXXFLAGS@ ++CXXFLAGS = $(BX_INCDIRS) @CXXFLAGS@ @GUI_CXXFLAGS@ -fms-extensions + LOCAL_CXXFLAGS = + LDFLAGS = @LDFLAGS@ + LIBS = @LIBS@ + + diff --git a/pintos-env/pintos/misc/bochs-2.2.6-page-fault-segv.patch b/pintos-env/pintos/misc/bochs-2.2.6-page-fault-segv.patch new file mode 100755 index 0000000..7c61a37 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-page-fault-segv.patch @@ -0,0 +1,79 @@ +Index: bochs-2.2.6/cpu/exception.cc +diff -u bochs-2.2.6/cpu/exception.cc\~ bochs-2.2.6/cpu/exception.cc +--- bochs-2.2.6/cpu/exception.cc~ 2006-09-28 15:51:39.000000000 -0700 ++++ bochs-2.2.6/cpu/exception.cc 2006-12-08 11:14:33.000000000 -0800 +@@ -1033,6 +1033,10 @@ void BX_CPU_C::exception(unsigned vector + BX_CPU_THIS_PTR curr_exception[0] = exception_type; + } + ++#if BX_GDBSTUB ++ bx_gdbstub_exception(vector); ++#endif ++ + #if BX_CPU_LEVEL >= 2 + if (!real_mode()) { + BX_CPU_THIS_PTR interrupt(vector, 0, push_error, error_code); +Index: bochs-2.2.6/gdbstub.cc +diff -u bochs-2.2.6/gdbstub.cc\~ bochs-2.2.6/gdbstub.cc +--- bochs-2.2.6/gdbstub.cc~ 2006-09-28 15:51:39.000000000 -0700 ++++ bochs-2.2.6/gdbstub.cc 2006-12-08 11:12:03.000000000 -0800 +@@ -26,6 +26,7 @@ static int last_stop_reason = GDBSTUB_ST + #define GDBSTUB_EXECUTION_BREAKPOINT (0xac1) + #define GDBSTUB_TRACE (0xac2) + #define GDBSTUB_USER_BREAK (0xac3) ++#define GDBSTUB_EXCEPTION_0E (0xac4) + + static int listen_socket_fd; + static int socket_fd; +@@ -271,6 +272,12 @@ int bx_gdbstub_check(unsigned int eip) + return(GDBSTUB_STOP_NO_REASON); + } + ++void bx_gdbstub_exception(unsigned int nr) ++{ ++ if (nr == 0x0e) ++ last_stop_reason = GDBSTUB_EXCEPTION_0E; ++} ++ + static int remove_breakpoint(unsigned int addr, int len) + { + unsigned int i; +@@ -452,6 +459,10 @@ static void debug_loop(void) + { + write_signal(&buf[1], SIGTRAP); + } ++ else if (last_stop_reason == GDBSTUB_EXCEPTION_0E) ++ { ++ write_signal(&buf[1], SIGSEGV); ++ } + else + { + write_signal(&buf[1], 0); +@@ -476,10 +487,14 @@ static void debug_loop(void) + { + write_signal(&buf[1], SIGTRAP); + } +- else ++ else if (last_stop_reason == GDBSTUB_EXCEPTION_0E) + { + write_signal(&buf[1], SIGSEGV); + } ++ else ++ { ++ write_signal(&buf[1], 0); ++ } + put_reply(buf); + break; + } +Index: bochs-2.2.6/bochs.h +diff -u bochs-2.2.6/bochs.h\~ bochs-2.2.6/bochs.h +--- bochs-2.2.6/bochs.h~ 2006-09-28 15:51:39.000000000 -0700 ++++ bochs-2.2.6/bochs.h 2006-12-08 11:14:19.000000000 -0800 +@@ -375,6 +375,7 @@ BOCHSAPI extern logfunc_t *genlog; + // defines for GDB stub + void bx_gdbstub_init(int argc, char* argv[]); + int bx_gdbstub_check(unsigned int eip); ++void bx_gdbstub_exception(unsigned int nr); + #define GDBSTUB_STOP_NO_REASON (0xac0) + + #if BX_SUPPORT_SMP diff --git a/pintos-env/pintos/misc/bochs-2.2.6-paranoia.patch b/pintos-env/pintos/misc/bochs-2.2.6-paranoia.patch new file mode 100755 index 0000000..ff8d736 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-paranoia.patch @@ -0,0 +1,19 @@ +Index: bochs-2.2.6/iodev/hdimage.h +diff -u bochs-2.2.6/iodev/hdimage.h\~ bochs-2.2.6/iodev/hdimage.h +--- bochs-2.2.6/iodev/hdimage.h~ 2005-11-06 03:07:01.000000000 -0800 ++++ bochs-2.2.6/iodev/hdimage.h 2006-09-28 15:55:50.000000000 -0700 +@@ -273,14 +273,8 @@ class sparse_image_t : public device_ima + + void panic(const char * message); + off_t +-#ifndef PARANOID +- sparse_image_t:: +-#endif + get_physical_offset(); + void +-#ifndef PARANOID +- sparse_image_t:: +-#endif + set_virtual_page(Bit32u new_virtual_page); + void read_header(); + ssize_t read_page_fragment(Bit32u read_virtual_page, Bit32u read_page_offset, size_t read_size, void * buf); diff --git a/pintos-env/pintos/misc/bochs-2.2.6-solaris-link.patch b/pintos-env/pintos/misc/bochs-2.2.6-solaris-link.patch new file mode 100755 index 0000000..5afef49 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-solaris-link.patch @@ -0,0 +1,11 @@ +--- bochs-2.2.6.orig/Makefile.in 2006-04-03 16:34:51.170387000 -0700 ++++ bochs-2.2.6/Makefile.in 2006-04-03 16:34:57.480303000 -0700 +@@ -93,7 +93,7 @@ + CFLAGS = @CFLAGS@ @GUI_CFLAGS@ $(MCH_CFLAGS) $(FLA_FLAGS) @DEFINE_PLUGIN_PATH@ -DBX_SHARE_PATH='"$(sharedir)"' + CXXFLAGS = @CXXFLAGS@ @GUI_CXXFLAGS@ $(MCH_CFLAGS) $(FLA_FLAGS) @DEFINE_PLUGIN_PATH@ -DBX_SHARE_PATH='"$(sharedir)"' + +-LDFLAGS = @LDFLAGS@ ++LDFLAGS = @LDFLAGS@ -lsocket + LIBS = @LIBS@ + # To compile with readline: + # linux needs just -lreadline diff --git a/pintos-env/pintos/misc/bochs-2.2.6-solaris-tty.patch b/pintos-env/pintos/misc/bochs-2.2.6-solaris-tty.patch new file mode 100755 index 0000000..a9725dc --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-solaris-tty.patch @@ -0,0 +1,31 @@ +--- bochs-2.2.6/iodev/serial.cc 2005-07-11 09:24:47.000000000 -0700 ++++ bochs-2.2.6.patch/iodev/serial.cc 2006-05-28 16:41:33.278938000 -0700 +@@ -245,8 +245,13 @@ + BX_SER_THIS s[i].io_mode = BX_SER_MODE_TERM; + BX_DEBUG(("com%d tty_id: %d", i+1, BX_SER_THIS s[i].tty_id)); + tcgetattr(BX_SER_THIS s[i].tty_id, &BX_SER_THIS s[i].term_orig); +- bcopy((caddr_t) &BX_SER_THIS s[i].term_orig, (caddr_t) &BX_SER_THIS s[i].term_new, sizeof(struct termios)); +- cfmakeraw(&BX_SER_THIS s[i].term_new); ++ memcpy((caddr_t) &BX_SER_THIS s[i].term_new, (caddr_t) &BX_SER_THIS s[i].term_orig, sizeof(struct termios)); ++ BX_SER_THIS s[i].term_new.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP ++ |INLCR|IGNCR|ICRNL|IXON); ++ BX_SER_THIS s[i].term_new.c_oflag &= ~OPOST; ++ BX_SER_THIS s[i].term_new.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); ++ BX_SER_THIS s[i].term_new.c_cflag &= ~(CSIZE|PARENB); ++ BX_SER_THIS s[i].term_new.c_cflag |= CS8; + BX_SER_THIS s[i].term_new.c_oflag |= OPOST | ONLCR; // Enable NL to CR-NL translation + #ifndef TRUE_CTLC + // ctl-C will exit Bochs, or trap to the debugger + + +--- bochs-2.2.6/iodev/serial.h 2005-07-10 09:51:09.000000000 -0700 ++++ bochs-2.2.6.patch/iodev/serial.h 2006-05-28 16:39:03.757839000 -0700 +@@ -40,7 +40,7 @@ + #define SERIAL_ENABLE + #endif + +-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || defined(__APPLE__) ++#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || defined(__APPLE__) || defined(__sun__) + #define SERIAL_ENABLE + extern "C" { + #include diff --git a/pintos-env/pintos/misc/bochs-2.2.6-triple-fault.patch b/pintos-env/pintos/misc/bochs-2.2.6-triple-fault.patch new file mode 100755 index 0000000..f6d0871 --- /dev/null +++ b/pintos-env/pintos/misc/bochs-2.2.6-triple-fault.patch @@ -0,0 +1,57 @@ +diff -u bochs-2.2.6.orig/cpu/exception.cc bochs-2.2.6/cpu/exception.cc +--- bochs-2.2.6.orig/cpu/exception.cc ++++ bochs-2.2.6/cpu/exception.cc +@@ -841,6 +841,13 @@ void BX_CPU_C::exception(unsigned vector + + BX_CPU_THIS_PTR errorno++; + if (BX_CPU_THIS_PTR errorno >= 3) { ++#if BX_GDBSTUB ++ if (bx_dbg.gdbstub_enabled) { ++ fprintf(stderr, "Triple fault: stopping for gdb\n"); ++ BX_CPU_THIS_PTR ispanic = 1; ++ longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1); ++ } ++#endif + #if BX_RESET_ON_TRIPLE_FAULT + BX_ERROR(("exception(): 3rd (%d) exception with no resolution, shutdown status is %02xh, resetting", vector, DEV_cmos_get_reg(0x0f))); + debug(BX_CPU_THIS_PTR prev_eip); +@@ -860,6 +867,13 @@ void BX_CPU_C::exception(unsigned vector + + /* if 1st was a double fault (software INT?), then shutdown */ + if ( (BX_CPU_THIS_PTR errorno==2) && (BX_CPU_THIS_PTR curr_exception[0]==BX_ET_DOUBLE_FAULT) ) { ++#if BX_GDBSTUB ++ if (bx_dbg.gdbstub_enabled) { ++ fprintf(stderr, "Triple fault: stopping for gdb\n"); ++ BX_CPU_THIS_PTR ispanic = 1; ++ longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1); ++ } ++#endif + #if BX_RESET_ON_TRIPLE_FAULT + BX_INFO(("exception(): triple fault encountered, shutdown status is %02xh, resetting", DEV_cmos_get_reg(0x0f))); + debug(BX_CPU_THIS_PTR prev_eip); +diff -u bochs-2.2.6.orig/gdbstub.cc bochs-2.2.6/gdbstub.cc +--- bochs-2.2.6.orig/gdbstub.cc ++++ bochs-2.2.6/gdbstub.cc +@@ -466,19 +466,19 @@ static void debug_loop(void) + + BX_INFO (("stepping")); + stub_trace_flag = 1; ++ bx_cpu.ispanic = 0; + bx_cpu.cpu_loop(-1); + DEV_vga_refresh(); + stub_trace_flag = 0; + BX_INFO (("stopped with %x", last_stop_reason)); + buf[0] = 'S'; +- if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT || +- last_stop_reason == GDBSTUB_TRACE) ++ if (last_stop_reason == GDBSTUB_TRACE && !bx_cpu.ispanic) + { + write_signal(&buf[1], SIGTRAP); + } + else + { +- write_signal(&buf[1], SIGTRAP); ++ write_signal(&buf[1], SIGSEGV); + } + put_reply(buf); + break; diff --git a/pintos-env/pintos/misc/gcc-3.3.6-cross-howto b/pintos-env/pintos/misc/gcc-3.3.6-cross-howto new file mode 100755 index 0000000..ad25173 --- /dev/null +++ b/pintos-env/pintos/misc/gcc-3.3.6-cross-howto @@ -0,0 +1,39 @@ +Here are the commands we used to build and install the SPARC +cross-compiler: + +PINTOSROOT=$HOME/private/pintos + +PREFIX=/usr/class/cs140/`uname -m` +PATH=$PATH:$PREFIX/bin +TMP=`pwd` + +wget ftp://ftp.gnu.org/pub/gnu/binutils/binutils-2.15.tar.bz2 +wget ftp://sources.redhat.com/pub/newlib/newlib-1.13.0.tar.gz +wget ftp://ftp.gnu.org/pub/gnu/gcc/gcc-3.3.6/gcc-core-3.3.6.tar.bz2 +wget ftp://ftp.gnu.org/pub/gnu/gdb/gdb-6.3.tar.bz2 + +bzcat binutils-2.15.tar.bz2 | tar x +tar xzf newlib-1.13.0.tar.gz +bzcat gcc-core-3.3.6.tar.bz2 | tar x +bzcat gdb-6.3.tar.bz2 | tar x + +cd $TMP/binutils-2.15 +mkdir i386 +cd i386 +../configure --target=i386-elf --prefix=$PREFIX +make LDFLAGS=-lintl +make install + +cd $TMP/gcc-3.3.6 +mkdir i386 +cd i386 +../configure --target=i386-elf --prefix=$PREFIX --with-gnu-as --with-as=$PREFIX/bin/i386-elf-as --with-gnu-ld --with-ld=$PREFIX/bin/i386-elf-ld --with-headers=$TMP/newlib-1.13.0/newlib/libc/include --with-newlib +make +make install + +cd $TMP/gdb-6.3 +mkdir i386 +cd i386 +../configure --target=i386-elf --prefix=$PREFIX --disable-tui +make LDFLAGS=-lintl +make install diff --git a/pintos-env/pintos/misc/gdb-macros b/pintos-env/pintos/misc/gdb-macros new file mode 100755 index 0000000..a0b68c3 --- /dev/null +++ b/pintos-env/pintos/misc/gdb-macros @@ -0,0 +1,140 @@ +# +# A set of useful macros that can help debug Pintos. +# +# Include with "source" cmd in gdb. +# Use "help user-defined" for help. +# +# Author: Godmar Back , Feb 2006 +# +# $Id: gdb-macros,v 1.1 2006-04-07 18:29:34 blp Exp $ +# + +# for internal use +define offsetof + set $rc = (char*)&((struct $arg0 *)0)->$arg1 - (char*)0 +end + +define list_entry + offsetof $arg1 $arg2 + set $rc = ((struct $arg1 *) ((uint8_t *) ($arg0) - $rc)) +end + +# dump a Pintos list +define dumplist + set $list = $arg0 + set $e = $list->head.next + set $i = 0 + while $e != &(($arg0).tail) + list_entry $e $arg1 $arg2 + set $l = $rc + printf "pintos-debug: dumplist #%d: %p ", $i++, $l + output *$l + set $e = $e->next + printf "\n" + end +end + +document dumplist + Dump the content of a Pintos list, + invoke as dumplist name_of_list name_of_struct name_of_elem_in_list_struct +end + +# print a thread's backtrace, given a pointer to the struct thread * +define btthread + if $arg0 == ($esp - ((unsigned)$esp % 4096)) + bt + else + set $saveEIP = $eip + set $saveESP = $esp + set $saveEBP = $ebp + + set $esp = ((struct thread *)$arg0)->stack + set $ebp = ((void**)$esp)[2] + set $eip = ((void**)$esp)[4] + + bt + + set $eip = $saveEIP + set $esp = $saveESP + set $ebp = $saveEBP + end +end +document btthread + Show the backtrace of a thread, + invoke as btthread pointer_to_struct_thread +end + +# print backtraces associated with all threads in a list +define btthreadlist + set $list = $arg0 + set $e = $list->head.next + while $e != &(($arg0).tail) + list_entry $e thread $arg1 + printf "pintos-debug: dumping backtrace of thread '%s' @%p\n", \ + ((struct thread*)$rc)->name, $rc + btthread $rc + set $e = $e->next + printf "\n" + end +end +document btthreadlist + Given a list of threads, print each thread's backtrace + invoke as btthreadlist name_of_list name_of_elem_in_list_struct +end + +# print backtraces of all threads (based on 'all_list' all threads list) +define btthreadall + btthreadlist all_list allelem +end +document btthreadall + Print backtraces of all threads +end + +# print a correct backtrace by adjusting $eip +# this works best right at intr0e_stub +define btpagefault + set $saveeip = $eip + set $eip = ((void**)$esp)[1] + backtrace + set $eip = $saveeip +end +document btpagefault + Print a backtrace of the current thread after a pagefault +end + +# invoked whenever the program stops +define hook-stop + # stopped at stub #0E = #14 (page fault exception handler stub) + if ($eip == intr0e_stub) + set $savedeip = ((void**)$esp)[1] + # if this was in user mode, the OS should handle it + # either handle the page fault or terminate the process + if ($savedeip < 0xC0000000) + printf "pintos-debug: a page fault exception occurred in user mode\n" + printf "pintos-debug: hit 'c' to continue, or 's' to step to intr_handler\n" + else + # if this was in kernel mode, a stack trace might be useful + printf "pintos-debug: a page fault occurred in kernel mode\n" + btpagefault + end + end +end + +# load symbols for a Pintos user program +define loadusersymbols + shell objdump -h $arg0 | awk '/.text/ { print "add-symbol-file $arg0 0x"$4 }' > .loadsymbols + source .loadsymbols + shell rm -f .loadsymbols +end +document loadusersymbols + Load the symbols contained in a user program's executable. + Example: + loadusersymbols tests/userprog/exec-multiple +end + +define debugpintos + target remote localhost:1234 +end +document debugpintos + Attach debugger to pintos process +end diff --git a/pintos-env/pintos/tests/Algorithm/Diff.pm b/pintos-env/pintos/tests/Algorithm/Diff.pm new file mode 100755 index 0000000..904c530 --- /dev/null +++ b/pintos-env/pintos/tests/Algorithm/Diff.pm @@ -0,0 +1,1713 @@ +package Algorithm::Diff; +# Skip to first "=head" line for documentation. +use strict; + +use integer; # see below in _replaceNextLargerWith() for mod to make + # if you don't use this +use vars qw( $VERSION @EXPORT_OK ); +$VERSION = 1.19_01; +# ^ ^^ ^^-- Incremented at will +# | \+----- Incremented for non-trivial changes to features +# \-------- Incremented for fundamental changes +require Exporter; +*import = \&Exporter::import; +@EXPORT_OK = qw( + prepare LCS LCDidx LCS_length + diff sdiff compact_diff + traverse_sequences traverse_balanced +); + +# McIlroy-Hunt diff algorithm +# Adapted from the Smalltalk code of Mario I. Wolczko, +# by Ned Konz, perl@bike-nomad.com +# Updates by Tye McQueen, http://perlmonks.org/?node=tye + +# Create a hash that maps each element of $aCollection to the set of +# positions it occupies in $aCollection, restricted to the elements +# within the range of indexes specified by $start and $end. +# The fourth parameter is a subroutine reference that will be called to +# generate a string to use as a key. +# Additional parameters, if any, will be passed to this subroutine. +# +# my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen ); + +sub _withPositionsOfInInterval +{ + my $aCollection = shift; # array ref + my $start = shift; + my $end = shift; + my $keyGen = shift; + my %d; + my $index; + for ( $index = $start ; $index <= $end ; $index++ ) + { + my $element = $aCollection->[$index]; + my $key = &$keyGen( $element, @_ ); + if ( exists( $d{$key} ) ) + { + unshift ( @{ $d{$key} }, $index ); + } + else + { + $d{$key} = [$index]; + } + } + return wantarray ? %d : \%d; +} + +# Find the place at which aValue would normally be inserted into the +# array. If that place is already occupied by aValue, do nothing, and +# return undef. If the place does not exist (i.e., it is off the end of +# the array), add it to the end, otherwise replace the element at that +# point with aValue. It is assumed that the array's values are numeric. +# This is where the bulk (75%) of the time is spent in this module, so +# try to make it fast! + +sub _replaceNextLargerWith +{ + my ( $array, $aValue, $high ) = @_; + $high ||= $#$array; + + # off the end? + if ( $high == -1 || $aValue > $array->[-1] ) + { + push ( @$array, $aValue ); + return $high + 1; + } + + # binary search for insertion point... + my $low = 0; + my $index; + my $found; + while ( $low <= $high ) + { + $index = ( $high + $low ) / 2; + + # $index = int(( $high + $low ) / 2); # without 'use integer' + $found = $array->[$index]; + + if ( $aValue == $found ) + { + return undef; + } + elsif ( $aValue > $found ) + { + $low = $index + 1; + } + else + { + $high = $index - 1; + } + } + + # now insertion point is in $low. + $array->[$low] = $aValue; # overwrite next larger + return $low; +} + +# This method computes the longest common subsequence in $a and $b. + +# Result is array or ref, whose contents is such that +# $a->[ $i ] == $b->[ $result[ $i ] ] +# foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined. + +# An additional argument may be passed; this is a hash or key generating +# function that should return a string that uniquely identifies the given +# element. It should be the case that if the key is the same, the elements +# will compare the same. If this parameter is undef or missing, the key +# will be the element as a string. + +# By default, comparisons will use "eq" and elements will be turned into keys +# using the default stringizing operator '""'. + +# Additional parameters, if any, will be passed to the key generation +# routine. + +sub _longestCommonSubsequence +{ + my $a = shift; # array ref or hash ref + my $b = shift; # array ref or hash ref + my $counting = shift; # scalar + my $keyGen = shift; # code ref + my $compare; # code ref + + if ( ref($a) eq 'HASH' ) + { # prepared hash must be in $b + my $tmp = $b; + $b = $a; + $a = $tmp; + } + + # Check for bogus (non-ref) argument values + if ( !ref($a) || !ref($b) ) + { + my @callerInfo = caller(1); + die 'error: must pass array or hash references to ' . $callerInfo[3]; + } + + # set up code refs + # Note that these are optimized. + if ( !defined($keyGen) ) # optimize for strings + { + $keyGen = sub { $_[0] }; + $compare = sub { my ( $a, $b ) = @_; $a eq $b }; + } + else + { + $compare = sub { + my $a = shift; + my $b = shift; + &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ ); + }; + } + + my ( $aStart, $aFinish, $matchVector ) = ( 0, $#$a, [] ); + my ( $prunedCount, $bMatches ) = ( 0, {} ); + + if ( ref($b) eq 'HASH' ) # was $bMatches prepared for us? + { + $bMatches = $b; + } + else + { + my ( $bStart, $bFinish ) = ( 0, $#$b ); + + # First we prune off any common elements at the beginning + while ( $aStart <= $aFinish + and $bStart <= $bFinish + and &$compare( $a->[$aStart], $b->[$bStart], @_ ) ) + { + $matchVector->[ $aStart++ ] = $bStart++; + $prunedCount++; + } + + # now the end + while ( $aStart <= $aFinish + and $bStart <= $bFinish + and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) ) + { + $matchVector->[ $aFinish-- ] = $bFinish--; + $prunedCount++; + } + + # Now compute the equivalence classes of positions of elements + $bMatches = + _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ ); + } + my $thresh = []; + my $links = []; + + my ( $i, $ai, $j, $k ); + for ( $i = $aStart ; $i <= $aFinish ; $i++ ) + { + $ai = &$keyGen( $a->[$i], @_ ); + if ( exists( $bMatches->{$ai} ) ) + { + $k = 0; + for $j ( @{ $bMatches->{$ai} } ) + { + + # optimization: most of the time this will be true + if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j ) + { + $thresh->[$k] = $j; + } + else + { + $k = _replaceNextLargerWith( $thresh, $j, $k ); + } + + # oddly, it's faster to always test this (CPU cache?). + if ( defined($k) ) + { + $links->[$k] = + [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ]; + } + } + } + } + + if (@$thresh) + { + return $prunedCount + @$thresh if $counting; + for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] ) + { + $matchVector->[ $link->[1] ] = $link->[2]; + } + } + elsif ($counting) + { + return $prunedCount; + } + + return wantarray ? @$matchVector : $matchVector; +} + +sub traverse_sequences +{ + my $a = shift; # array ref + my $b = shift; # array ref + my $callbacks = shift || {}; + my $keyGen = shift; + my $matchCallback = $callbacks->{'MATCH'} || sub { }; + my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; + my $finishedACallback = $callbacks->{'A_FINISHED'}; + my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; + my $finishedBCallback = $callbacks->{'B_FINISHED'}; + my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); + + # Process all the lines in @$matchVector + my $lastA = $#$a; + my $lastB = $#$b; + my $bi = 0; + my $ai; + + for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ ) + { + my $bLine = $matchVector->[$ai]; + if ( defined($bLine) ) # matched + { + &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine; + &$matchCallback( $ai, $bi++, @_ ); + } + else + { + &$discardACallback( $ai, $bi, @_ ); + } + } + + # The last entry (if any) processed was a match. + # $ai and $bi point just past the last matching lines in their sequences. + + while ( $ai <= $lastA or $bi <= $lastB ) + { + + # last A? + if ( $ai == $lastA + 1 and $bi <= $lastB ) + { + if ( defined($finishedACallback) ) + { + &$finishedACallback( $lastA, @_ ); + $finishedACallback = undef; + } + else + { + &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB; + } + } + + # last B? + if ( $bi == $lastB + 1 and $ai <= $lastA ) + { + if ( defined($finishedBCallback) ) + { + &$finishedBCallback( $lastB, @_ ); + $finishedBCallback = undef; + } + else + { + &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA; + } + } + + &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA; + &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB; + } + + return 1; +} + +sub traverse_balanced +{ + my $a = shift; # array ref + my $b = shift; # array ref + my $callbacks = shift || {}; + my $keyGen = shift; + my $matchCallback = $callbacks->{'MATCH'} || sub { }; + my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; + my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; + my $changeCallback = $callbacks->{'CHANGE'}; + my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); + + # Process all the lines in match vector + my $lastA = $#$a; + my $lastB = $#$b; + my $bi = 0; + my $ai = 0; + my $ma = -1; + my $mb; + + while (1) + { + + # Find next match indices $ma and $mb + do { + $ma++; + } while( + $ma <= $#$matchVector + && !defined $matchVector->[$ma] + ); + + last if $ma > $#$matchVector; # end of matchVector? + $mb = $matchVector->[$ma]; + + # Proceed with discard a/b or change events until + # next match + while ( $ai < $ma || $bi < $mb ) + { + + if ( $ai < $ma && $bi < $mb ) + { + + # Change + if ( defined $changeCallback ) + { + &$changeCallback( $ai++, $bi++, @_ ); + } + else + { + &$discardACallback( $ai++, $bi, @_ ); + &$discardBCallback( $ai, $bi++, @_ ); + } + } + elsif ( $ai < $ma ) + { + &$discardACallback( $ai++, $bi, @_ ); + } + else + { + + # $bi < $mb + &$discardBCallback( $ai, $bi++, @_ ); + } + } + + # Match + &$matchCallback( $ai++, $bi++, @_ ); + } + + while ( $ai <= $lastA || $bi <= $lastB ) + { + if ( $ai <= $lastA && $bi <= $lastB ) + { + + # Change + if ( defined $changeCallback ) + { + &$changeCallback( $ai++, $bi++, @_ ); + } + else + { + &$discardACallback( $ai++, $bi, @_ ); + &$discardBCallback( $ai, $bi++, @_ ); + } + } + elsif ( $ai <= $lastA ) + { + &$discardACallback( $ai++, $bi, @_ ); + } + else + { + + # $bi <= $lastB + &$discardBCallback( $ai, $bi++, @_ ); + } + } + + return 1; +} + +sub prepare +{ + my $a = shift; # array ref + my $keyGen = shift; # code ref + + # set up code ref + $keyGen = sub { $_[0] } unless defined($keyGen); + + return scalar _withPositionsOfInInterval( $a, 0, $#$a, $keyGen, @_ ); +} + +sub LCS +{ + my $a = shift; # array ref + my $b = shift; # array ref or hash ref + my $matchVector = _longestCommonSubsequence( $a, $b, 0, @_ ); + my @retval; + my $i; + for ( $i = 0 ; $i <= $#$matchVector ; $i++ ) + { + if ( defined( $matchVector->[$i] ) ) + { + push ( @retval, $a->[$i] ); + } + } + return wantarray ? @retval : \@retval; +} + +sub LCS_length +{ + my $a = shift; # array ref + my $b = shift; # array ref or hash ref + return _longestCommonSubsequence( $a, $b, 1, @_ ); +} + +sub LCSidx +{ + my $a= shift @_; + my $b= shift @_; + my $match= _longestCommonSubsequence( $a, $b, 0, @_ ); + my @am= grep defined $match->[$_], 0..$#$match; + my @bm= @{$match}[@am]; + return \@am, \@bm; +} + +sub compact_diff +{ + my $a= shift @_; + my $b= shift @_; + my( $am, $bm )= LCSidx( $a, $b, @_ ); + my @cdiff; + my( $ai, $bi )= ( 0, 0 ); + push @cdiff, $ai, $bi; + while( 1 ) { + while( @$am && $ai == $am->[0] && $bi == $bm->[0] ) { + shift @$am; + shift @$bm; + ++$ai, ++$bi; + } + push @cdiff, $ai, $bi; + last if ! @$am; + $ai = $am->[0]; + $bi = $bm->[0]; + push @cdiff, $ai, $bi; + } + push @cdiff, 0+@$a, 0+@$b + if $ai < @$a || $bi < @$b; + return wantarray ? @cdiff : \@cdiff; +} + +sub diff +{ + my $a = shift; # array ref + my $b = shift; # array ref + my $retval = []; + my $hunk = []; + my $discard = sub { + push @$hunk, [ '-', $_[0], $a->[ $_[0] ] ]; + }; + my $add = sub { + push @$hunk, [ '+', $_[1], $b->[ $_[1] ] ]; + }; + my $match = sub { + push @$retval, $hunk + if 0 < @$hunk; + $hunk = [] + }; + traverse_sequences( $a, $b, + { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ ); + &$match(); + return wantarray ? @$retval : $retval; +} + +sub sdiff +{ + my $a = shift; # array ref + my $b = shift; # array ref + my $retval = []; + my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) }; + my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) }; + my $change = sub { + push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] ); + }; + my $match = sub { + push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] ); + }; + traverse_balanced( + $a, + $b, + { + MATCH => $match, + DISCARD_A => $discard, + DISCARD_B => $add, + CHANGE => $change, + }, + @_ + ); + return wantarray ? @$retval : $retval; +} + +######################################## +my $Root= __PACKAGE__; +package Algorithm::Diff::_impl; +use strict; + +sub _Idx() { 0 } # $me->[_Idx]: Ref to array of hunk indices + # 1 # $me->[1]: Ref to first sequence + # 2 # $me->[2]: Ref to second sequence +sub _End() { 3 } # $me->[_End]: Diff between forward and reverse pos +sub _Same() { 4 } # $me->[_Same]: 1 if pos 1 contains unchanged items +sub _Base() { 5 } # $me->[_Base]: Added to range's min and max +sub _Pos() { 6 } # $me->[_Pos]: Which hunk is currently selected +sub _Off() { 7 } # $me->[_Off]: Offset into _Idx for current position +sub _Min() { -2 } # Added to _Off to get min instead of max+1 + +sub Die +{ + require Carp; + Carp::confess( @_ ); +} + +sub _ChkPos +{ + my( $me )= @_; + return if $me->[_Pos]; + my $meth= ( caller(1) )[3]; + Die( "Called $meth on 'reset' object" ); +} + +sub _ChkSeq +{ + my( $me, $seq )= @_; + return $seq + $me->[_Off] + if 1 == $seq || 2 == $seq; + my $meth= ( caller(1) )[3]; + Die( "$meth: Invalid sequence number ($seq); must be 1 or 2" ); +} + +sub getObjPkg +{ + my( $us )= @_; + return ref $us if ref $us; + return $us . "::_obj"; +} + +sub new +{ + my( $us, $seq1, $seq2, $opts ) = @_; + my @args; + for( $opts->{keyGen} ) { + push @args, $_ if $_; + } + for( $opts->{keyGenArgs} ) { + push @args, @$_ if $_; + } + my $cdif= Algorithm::Diff::compact_diff( $seq1, $seq2, @args ); + my $same= 1; + if( 0 == $cdif->[2] && 0 == $cdif->[3] ) { + $same= 0; + splice @$cdif, 0, 2; + } + my @obj= ( $cdif, $seq1, $seq2 ); + $obj[_End] = (1+@$cdif)/2; + $obj[_Same] = $same; + $obj[_Base] = 0; + my $me = bless \@obj, $us->getObjPkg(); + $me->Reset( 0 ); + return $me; +} + +sub Reset +{ + my( $me, $pos )= @_; + $pos= int( $pos || 0 ); + $pos += $me->[_End] + if $pos < 0; + $pos= 0 + if $pos < 0 || $me->[_End] <= $pos; + $me->[_Pos]= $pos || !1; + $me->[_Off]= 2*$pos - 1; + return $me; +} + +sub Base +{ + my( $me, $base )= @_; + my $oldBase= $me->[_Base]; + $me->[_Base]= 0+$base if defined $base; + return $oldBase; +} + +sub Copy +{ + my( $me, $pos, $base )= @_; + my @obj= @$me; + my $you= bless \@obj, ref($me); + $you->Reset( $pos ) if defined $pos; + $you->Base( $base ); + return $you; +} + +sub Next { + my( $me, $steps )= @_; + $steps= 1 if ! defined $steps; + if( $steps ) { + my $pos= $me->[_Pos]; + my $new= $pos + $steps; + $new= 0 if $pos && $new < 0; + $me->Reset( $new ) + } + return $me->[_Pos]; +} + +sub Prev { + my( $me, $steps )= @_; + $steps= 1 if ! defined $steps; + my $pos= $me->Next(-$steps); + $pos -= $me->[_End] if $pos; + return $pos; +} + +sub Diff { + my( $me )= @_; + $me->_ChkPos(); + return 0 if $me->[_Same] == ( 1 & $me->[_Pos] ); + my $ret= 0; + my $off= $me->[_Off]; + for my $seq ( 1, 2 ) { + $ret |= $seq + if $me->[_Idx][ $off + $seq + _Min ] + < $me->[_Idx][ $off + $seq ]; + } + return $ret; +} + +sub Min { + my( $me, $seq, $base )= @_; + $me->_ChkPos(); + my $off= $me->_ChkSeq($seq); + $base= $me->[_Base] if !defined $base; + return $base + $me->[_Idx][ $off + _Min ]; +} + +sub Max { + my( $me, $seq, $base )= @_; + $me->_ChkPos(); + my $off= $me->_ChkSeq($seq); + $base= $me->[_Base] if !defined $base; + return $base + $me->[_Idx][ $off ] -1; +} + +sub Range { + my( $me, $seq, $base )= @_; + $me->_ChkPos(); + my $off = $me->_ChkSeq($seq); + if( !wantarray ) { + return $me->[_Idx][ $off ] + - $me->[_Idx][ $off + _Min ]; + } + $base= $me->[_Base] if !defined $base; + return ( $base + $me->[_Idx][ $off + _Min ] ) + .. ( $base + $me->[_Idx][ $off ] - 1 ); +} + +sub Items { + my( $me, $seq )= @_; + $me->_ChkPos(); + my $off = $me->_ChkSeq($seq); + if( !wantarray ) { + return $me->[_Idx][ $off ] + - $me->[_Idx][ $off + _Min ]; + } + return + @{$me->[$seq]}[ + $me->[_Idx][ $off + _Min ] + .. ( $me->[_Idx][ $off ] - 1 ) + ]; +} + +sub Same { + my( $me )= @_; + $me->_ChkPos(); + return wantarray ? () : 0 + if $me->[_Same] != ( 1 & $me->[_Pos] ); + return $me->Items(1); +} + +my %getName; +BEGIN { + %getName= ( + same => \&Same, + diff => \&Diff, + base => \&Base, + min => \&Min, + max => \&Max, + range=> \&Range, + items=> \&Items, # same thing + ); +} + +sub Get +{ + my $me= shift @_; + $me->_ChkPos(); + my @value; + for my $arg ( @_ ) { + for my $word ( split ' ', $arg ) { + my $meth; + if( $word !~ /^(-?\d+)?([a-zA-Z]+)([12])?$/ + || not $meth= $getName{ lc $2 } + ) { + Die( $Root, ", Get: Invalid request ($word)" ); + } + my( $base, $name, $seq )= ( $1, $2, $3 ); + push @value, scalar( + 4 == length($name) + ? $meth->( $me ) + : $meth->( $me, $seq, $base ) + ); + } + } + if( wantarray ) { + return @value; + } elsif( 1 == @value ) { + return $value[0]; + } + Die( 0+@value, " values requested from ", + $Root, "'s Get in scalar context" ); +} + + +my $Obj= getObjPkg($Root); +no strict 'refs'; + +for my $meth ( qw( new getObjPkg ) ) { + *{$Root."::".$meth} = \&{$meth}; + *{$Obj ."::".$meth} = \&{$meth}; +} +for my $meth ( qw( + Next Prev Reset Copy Base Diff + Same Items Range Min Max Get + _ChkPos _ChkSeq +) ) { + *{$Obj."::".$meth} = \&{$meth}; +} + +1; +__END__ + +=head1 NAME + +Algorithm::Diff - Compute `intelligent' differences between two files / lists + +=head1 SYNOPSIS + + require Algorithm::Diff; + + # This example produces traditional 'diff' output: + + my $diff = Algorithm::Diff->new( \@seq1, \@seq2 ); + + $diff->Base( 1 ); # Return line numbers, not indices + while( $diff->Next() ) { + next if $diff->Same(); + my $sep = ''; + if( ! $diff->Items(2) ) { + sprintf "%d,%dd%d\n", + $diff->Get(qw( Min1 Max1 Max2 )); + } elsif( ! $diff->Items(1) ) { + sprint "%da%d,%d\n", + $diff->Get(qw( Max1 Min2 Max2 )); + } else { + $sep = "---\n"; + sprintf "%d,%dc%d,%d\n", + $diff->Get(qw( Min1 Max1 Min2 Max2 )); + } + print "< $_" for $diff->Items(1); + print $sep; + print "> $_" for $diff->Items(2); + } + + + # Alternate interfaces: + + use Algorithm::Diff qw( + LCS LCS_length LCSidx + diff sdiff compact_diff + traverse_sequences traverse_balanced ); + + @lcs = LCS( \@seq1, \@seq2 ); + $lcsref = LCS( \@seq1, \@seq2 ); + $count = LCS_length( \@seq1, \@seq2 ); + + ( $seq1idxref, $seq2idxref ) = LCSidx( \@seq1, \@seq2 ); + + + # Complicated interfaces: + + @diffs = diff( \@seq1, \@seq2 ); + + @sdiffs = sdiff( \@seq1, \@seq2 ); + + @cdiffs = compact_diff( \@seq1, \@seq2 ); + + traverse_sequences( + \@seq1, + \@seq2, + { MATCH => \&callback1, + DISCARD_A => \&callback2, + DISCARD_B => \&callback3, + }, + \&key_generator, + @extra_args, + ); + + traverse_balanced( + \@seq1, + \@seq2, + { MATCH => \&callback1, + DISCARD_A => \&callback2, + DISCARD_B => \&callback3, + CHANGE => \&callback4, + }, + \&key_generator, + @extra_args, + ); + + +=head1 INTRODUCTION + +(by Mark-Jason Dominus) + +I once read an article written by the authors of C; they said +that they worked very hard on the algorithm until they found the +right one. + +I think what they ended up using (and I hope someone will correct me, +because I am not very confident about this) was the `longest common +subsequence' method. In the LCS problem, you have two sequences of +items: + + a b c d f g h j q z + + a b c d e f g i j k r x y z + +and you want to find the longest sequence of items that is present in +both original sequences in the same order. That is, you want to find +a new sequence I which can be obtained from the first sequence by +deleting some items, and from the secend sequence by deleting other +items. You also want I to be as long as possible. In this case I +is + + a b c d f g j z + +From there it's only a small step to get diff-like output: + + e h i k q r x y + + - + + - + + + + +This module solves the LCS problem. It also includes a canned function +to generate C-like output. + +It might seem from the example above that the LCS of two sequences is +always pretty obvious, but that's not always the case, especially when +the two sequences have many repeated elements. For example, consider + + a x b y c z p d q + a b c a x b y c z + +A naive approach might start by matching up the C and C that +appear at the beginning of each sequence, like this: + + a x b y c z p d q + a b c a b y c z + +This finds the common subsequence C. But actually, the LCS +is C: + + a x b y c z p d q + a b c a x b y c z + +or + + a x b y c z p d q + a b c a x b y c z + +=head1 USAGE + +(See also the README file and several example +scripts include with this module.) + +This module now provides an object-oriented interface that uses less +memory and is easier to use than most of the previous procedural +interfaces. It also still provides several exportable functions. We'll +deal with these in ascending order of difficulty: C, +C, C, OO interface, C, C, C, +C, and C. + +=head2 C + +Given references to two lists of items, LCS returns an array containing +their longest common subsequence. In scalar context, it returns a +reference to such a list. + + @lcs = LCS( \@seq1, \@seq2 ); + $lcsref = LCS( \@seq1, \@seq2 ); + +C may be passed an optional third parameter; this is a CODE +reference to a key generation function. See L. + + @lcs = LCS( \@seq1, \@seq2, \&keyGen, @args ); + $lcsref = LCS( \@seq1, \@seq2, \&keyGen, @args ); + +Additional parameters, if any, will be passed to the key generation +routine. + +=head2 C + +This is just like C except it only returns the length of the +longest common subsequence. This provides a performance gain of about +9% compared to C. + +=head2 C + +Like C except it returns references to two arrays. The first array +contains the indices into @seq1 where the LCS items are located. The +second array contains the indices into @seq2 where the LCS items are located. + +Therefore, the following three lists will contain the same values: + + my( $idx1, $idx2 ) = LCSidx( \@seq1, \@seq2 ); + my @list1 = @seq1[ @$idx1 ]; + my @list2 = @seq2[ @$idx2 ]; + my @list3 = LCS( \@seq1, \@seq2 ); + +=head2 C + + $diff = Algorithm::Diffs->new( \@seq1, \@seq2 ); + $diff = Algorithm::Diffs->new( \@seq1, \@seq2, \%opts ); + +C computes the smallest set of additions and deletions necessary +to turn the first sequence into the second and compactly records them +in the object. + +You use the object to iterate over I, where each hunk represents +a contiguous section of items which should be added, deleted, replaced, +or left unchanged. + +=over 4 + +The following summary of all of the methods looks a lot like Perl code +but some of the symbols have different meanings: + + [ ] Encloses optional arguments + : Is followed by the default value for an optional argument + | Separates alternate return results + +Method summary: + + $obj = Algorithm::Diff->new( \@seq1, \@seq2, [ \%opts ] ); + $pos = $obj->Next( [ $count : 1 ] ); + $revPos = $obj->Prev( [ $count : 1 ] ); + $obj = $obj->Reset( [ $pos : 0 ] ); + $copy = $obj->Copy( [ $pos, [ $newBase ] ] ); + $oldBase = $obj->Base( [ $newBase ] ); + +Note that all of the following methods C if used on an object that +is "reset" (not currently pointing at any hunk). + + $bits = $obj->Diff( ); + @items|$cnt = $obj->Same( ); + @items|$cnt = $obj->Items( $seqNum ); + @idxs |$cnt = $obj->Range( $seqNum, [ $base ] ); + $minIdx = $obj->Min( $seqNum, [ $base ] ); + $maxIdx = $obj->Max( $seqNum, [ $base ] ); + @values = $obj->Get( @names ); + +Passing in C for an optional argument is always treated the same +as if no argument were passed in. + +=item C + + $pos = $diff->Next(); # Move forward 1 hunk + $pos = $diff->Next( 2 ); # Move forward 2 hunks + $pos = $diff->Next(-5); # Move backward 5 hunks + +C moves the object to point at the next hunk. The object starts +out "reset", which means it isn't pointing at any hunk. If the object +is reset, then C moves to the first hunk. + +C returns a true value iff the move didn't go past the last hunk. +So C will return true iff the object is not reset. + +Actually, C returns the object's new position, which is a number +between 1 and the number of hunks (inclusive), or returns a false value. + +=item C + +C is almost identical to C; it moves to the $Nth +previous hunk. On a 'reset' object, C [and C] move +to the last hunk. + +The position returned by C is relative to the I of the +hunks; -1 for the last hunk, -2 for the second-to-last, etc. + +=item C + + $diff->Reset(); # Reset the object's position + $diff->Reset($pos); # Move to the specified hunk + $diff->Reset(1); # Move to the first hunk + $diff->Reset(-1); # Move to the last hunk + +C returns the object, so, for example, you could use +C<< $diff->Reset()->Next(-1) >> to get the number of hunks. + +=item C + + $copy = $diff->Copy( $newPos, $newBase ); + +C returns a copy of the object. The copy and the orignal object +share most of their data, so making copies takes very little memory. +The copy maintains its own position (separate from the original), which +is the main purpose of copies. It also maintains its own base. + +By default, the copy's position starts out the same as the original +object's position. But C takes an optional first argument to set the +new position, so the following three snippets are equivalent: + + $copy = $diff->Copy($pos); + + $copy = $diff->Copy(); + $copy->Reset($pos); + + $copy = $diff->Copy()->Reset($pos); + +C takes an optional second argument to set the base for +the copy. If you wish to change the base of the copy but leave +the position the same as in the original, here are two +equivalent ways: + + $copy = $diff->Copy(); + $copy->Base( 0 ); + + $copy = $diff->Copy(undef,0); + +Here are two equivalent way to get a "reset" copy: + + $copy = $diff->Copy(0); + + $copy = $diff->Copy()->Reset(); + +=item C + + $bits = $obj->Diff(); + +C returns a true value iff the current hunk contains items that are +different between the two sequences. It actually returns one of the +follow 4 values: + +=over 4 + +=item 3 + +C<3==(1|2)>. This hunk contains items from @seq1 and the items +from @seq2 that should replace them. Both sequence 1 and 2 +contain changed items so both the 1 and 2 bits are set. + +=item 2 + +This hunk only contains items from @seq2 that should be inserted (not +items from @seq1). Only sequence 2 contains changed items so only the 2 +bit is set. + +=item 1 + +This hunk only contains items from @seq1 that should be deleted (not +items from @seq2). Only sequence 1 contains changed items so only the 1 +bit is set. + +=item 0 + +This means that the items in this hunk are the same in both sequences. +Neither sequence 1 nor 2 contain changed items so neither the 1 nor the +2 bits are set. + +=back + +=item C + +C returns a true value iff the current hunk contains items that +are the same in both sequences. It actually returns the list of items +if they are the same or an emty list if they aren't. In a scalar +context, it returns the size of the list. + +=item C + + $count = $diff->Items(2); + @items = $diff->Items($seqNum); + +C returns the (number of) items from the specified sequence that +are part of the current hunk. + +If the current hunk contains only insertions, then +C<< $diff->Items(1) >> will return an empty list (0 in a scalar conext). +If the current hunk contains only deletions, then C<< $diff->Items(2) >> +will return an empty list (0 in a scalar conext). + +If the hunk contains replacements, then both C<< $diff->Items(1) >> and +C<< $diff->Items(2) >> will return different, non-empty lists. + +Otherwise, the hunk contains identical items and all of the following +will return the same lists: + + @items = $diff->Items(1); + @items = $diff->Items(2); + @items = $diff->Same(); + +=item C + + $count = $diff->Range( $seqNum ); + @indices = $diff->Range( $seqNum ); + @indices = $diff->Range( $seqNum, $base ); + +C is like C except that it returns a list of I to +the items rather than the items themselves. By default, the index of +the first item (in each sequence) is 0 but this can be changed by +calling the C method. So, by default, the following two snippets +return the same lists: + + @list = $diff->Items(2); + @list = @seq2[ $diff->Range(2) ]; + +You can also specify the base to use as the second argument. So the +following two snippets I return the same lists: + + @list = $diff->Items(1); + @list = @seq1[ $diff->Range(1,0) ]; + +=item C + + $curBase = $diff->Base(); + $oldBase = $diff->Base($newBase); + +C sets and/or returns the current base (usually 0 or 1) that is +used when you request range information. The base defaults to 0 so +that range information is returned as array indices. You can set the +base to 1 if you want to report traditional line numbers instead. + +=item C + + $min1 = $diff->Min(1); + $min = $diff->Min( $seqNum, $base ); + +C returns the first value that C would return (given the +same arguments) or returns C if C would return an empty +list. + +=item C + +C returns the last value that C would return or C. + +=item C + + ( $n, $x, $r ) = $diff->Get(qw( min1 max1 range1 )); + @values = $diff->Get(qw( 0min2 1max2 range2 same base )); + +C returns one or more scalar values. You pass in a list of the +names of the values you want returned. Each name must match one of the +following regexes: + + /^(-?\d+)?(min|max)[12]$/i + /^(range[12]|same|diff|base)$/i + +The 1 or 2 after a name says which sequence you want the information +for (and where allowed, it is required). The optional number before +"min" or "max" is the base to use. So the following equalities hold: + + $diff->Get('min1') == $diff->Min(1) + $diff->Get('0min2') == $diff->Min(2,0) + +Using C in a scalar context when you've passed in more than one +name is a fatal error (C is called). + +=back + +=head2 C + +Given a reference to a list of items, C returns a reference +to a hash which can be used when comparing this sequence to other +sequences with C or C. + + $prep = prepare( \@seq1 ); + for $i ( 0 .. 10_000 ) + { + @lcs = LCS( $prep, $seq[$i] ); + # do something useful with @lcs + } + +C may be passed an optional third parameter; this is a CODE +reference to a key generation function. See L. + + $prep = prepare( \@seq1, \&keyGen ); + for $i ( 0 .. 10_000 ) + { + @lcs = LCS( $seq[$i], $prep, \&keyGen ); + # do something useful with @lcs + } + +Using C provides a performance gain of about 50% when calling LCS +many times compared with not preparing. + +=head2 C + + @diffs = diff( \@seq1, \@seq2 ); + $diffs_ref = diff( \@seq1, \@seq2 ); + +C computes the smallest set of additions and deletions necessary +to turn the first sequence into the second, and returns a description +of these changes. The description is a list of I; each hunk +represents a contiguous section of items which should be added, +deleted, or replaced. (Hunks containing unchanged items are not +included.) + +The return value of C is a list of hunks, or, in scalar context, a +reference to such a list. If there are no differences, the list will be +empty. + +Here is an example. Calling C for the following two sequences: + + a b c e h j l m n p + b c d e f j k l m r s t + +would produce the following list: + + ( + [ [ '-', 0, 'a' ] ], + + [ [ '+', 2, 'd' ] ], + + [ [ '-', 4, 'h' ], + [ '+', 4, 'f' ] ], + + [ [ '+', 6, 'k' ] ], + + [ [ '-', 8, 'n' ], + [ '-', 9, 'p' ], + [ '+', 9, 'r' ], + [ '+', 10, 's' ], + [ '+', 11, 't' ] ], + ) + +There are five hunks here. The first hunk says that the C at +position 0 of the first sequence should be deleted (C<->). The second +hunk says that the C at position 2 of the second sequence should +be inserted (C<+>). The third hunk says that the C at position 4 +of the first sequence should be removed and replaced with the C +from position 4 of the second sequence. And so on. + +C may be passed an optional third parameter; this is a CODE +reference to a key generation function. See L. + +Additional parameters, if any, will be passed to the key generation +routine. + +=head2 C + + @sdiffs = sdiff( \@seq1, \@seq2 ); + $sdiffs_ref = sdiff( \@seq1, \@seq2 ); + +C computes all necessary components to show two sequences +and their minimized differences side by side, just like the +Unix-utility I does: + + same same + before | after + old < - + - > new + +It returns a list of array refs, each pointing to an array of +display instructions. In scalar context it returns a reference +to such a list. If there are no differences, the list will have one +entry per item, each indicating that the item was unchanged. + +Display instructions consist of three elements: A modifier indicator +(C<+>: Element added, C<->: Element removed, C: Element unmodified, +C: Element changed) and the value of the old and new elements, to +be displayed side-by-side. + +An C of the following two sequences: + + a b c e h j l m n p + b c d e f j k l m r s t + +results in + + ( [ '-', 'a', '' ], + [ 'u', 'b', 'b' ], + [ 'u', 'c', 'c' ], + [ '+', '', 'd' ], + [ 'u', 'e', 'e' ], + [ 'c', 'h', 'f' ], + [ 'u', 'j', 'j' ], + [ '+', '', 'k' ], + [ 'u', 'l', 'l' ], + [ 'u', 'm', 'm' ], + [ 'c', 'n', 'r' ], + [ 'c', 'p', 's' ], + [ '+', '', 't' ], + ) + +C may be passed an optional third parameter; this is a CODE +reference to a key generation function. See L. + +Additional parameters, if any, will be passed to the key generation +routine. + +=head2 C + +C is much like C except it returns a much more +compact description consisting of just one flat list of indices. An +example helps explain the format: + + my @a = qw( a b c e h j l m n p ); + my @b = qw( b c d e f j k l m r s t ); + @cdiff = compact_diff( \@a, \@b ); + # Returns: + # @a @b @a @b + # start start values values + ( 0, 0, # = + 0, 0, # a ! + 1, 0, # b c = b c + 3, 2, # ! d + 3, 3, # e = e + 4, 4, # f ! h + 5, 5, # j = j + 6, 6, # ! k + 6, 7, # l m = l m + 8, 9, # n p ! r s t + 10, 12, # + ); + +The 0th, 2nd, 4th, etc. entries are all indices into @seq1 (@a in the +above example) indicating where a hunk begins. The 1st, 3rd, 5th, etc. +entries are all indices into @seq2 (@b in the above example) indicating +where the same hunk begins. + +So each pair of indices (except the last pair) describes where a hunk +begins (in each sequence). Since each hunk must end at the item just +before the item that starts the next hunk, the next pair of indices can +be used to determine where the hunk ends. + +So, the first 4 entries (0..3) describe the first hunk. Entries 0 and 1 +describe where the first hunk begins (and so are always both 0). +Entries 2 and 3 describe where the next hunk begins, so subtracting 1 +from each tells us where the first hunk ends. That is, the first hunk +contains items C<$diff[0]> through C<$diff[2] - 1> of the first sequence +and contains items C<$diff[1]> through C<$diff[3] - 1> of the second +sequence. + +In other words, the first hunk consists of the following two lists of items: + + # 1st pair 2nd pair + # of indices of indices + @list1 = @a[ $cdiff[0] .. $cdiff[2]-1 ]; + @list2 = @b[ $cdiff[1] .. $cdiff[3]-1 ]; + # Hunk start Hunk end + +Note that the hunks will always alternate between those that are part of +the LCS (those that contain unchanged items) and those that contain +changes. This means that all we need to be told is whether the first +hunk is a 'same' or 'diff' hunk and we can determine which of the other +hunks contain 'same' items or 'diff' items. + +By convention, we always make the first hunk contain unchanged items. +So the 1st, 3rd, 5th, etc. hunks (all odd-numbered hunks if you start +counting from 1) all contain unchanged items. And the 2nd, 4th, 6th, +etc. hunks (all even-numbered hunks if you start counting from 1) all +contain changed items. + +Since @a and @b don't begin with the same value, the first hunk in our +example is empty (otherwise we'd violate the above convention). Note +that the first 4 index values in our example are all zero. Plug these +values into our previous code block and we get: + + @hunk1a = @a[ 0 .. 0-1 ]; + @hunk1b = @b[ 0 .. 0-1 ]; + +And C<0..-1> returns the empty list. + +Move down one pair of indices (2..5) and we get the offset ranges for +the second hunk, which contains changed items. + +Since C<@diff[2..5]> contains (0,0,1,0) in our example, the second hunk +consists of these two lists of items: + + @hunk2a = @a[ $cdiff[2] .. $cdiff[4]-1 ]; + @hunk2b = @b[ $cdiff[3] .. $cdiff[5]-1 ]; + # or + @hunk2a = @a[ 0 .. 1-1 ]; + @hunk2b = @b[ 0 .. 0-1 ]; + # or + @hunk2a = @a[ 0 .. 0 ]; + @hunk2b = @b[ 0 .. -1 ]; + # or + @hunk2a = ( 'a' ); + @hunk2b = ( ); + +That is, we would delete item 0 ('a') from @a. + +Since C<@diff[4..7]> contains (1,0,3,2) in our example, the third hunk +consists of these two lists of items: + + @hunk3a = @a[ $cdiff[4] .. $cdiff[6]-1 ]; + @hunk3a = @b[ $cdiff[5] .. $cdiff[7]-1 ]; + # or + @hunk3a = @a[ 1 .. 3-1 ]; + @hunk3a = @b[ 0 .. 2-1 ]; + # or + @hunk3a = @a[ 1 .. 2 ]; + @hunk3a = @b[ 0 .. 1 ]; + # or + @hunk3a = qw( b c ); + @hunk3a = qw( b c ); + +Note that this third hunk contains unchanged items as our convention demands. + +You can continue this process until you reach the last two indices, +which will always be the number of items in each sequence. This is +required so that subtracting one from each will give you the indices to +the last items in each sequence. + +=head2 C + +C used to be the most general facility provided by +this module (the new OO interface is more powerful and much easier to +use). + +Imagine that there are two arrows. Arrow A points to an element of +sequence A, and arrow B points to an element of the sequence B. +Initially, the arrows point to the first elements of the respective +sequences. C will advance the arrows through the +sequences one element at a time, calling an appropriate user-specified +callback function before each advance. It willadvance the arrows in +such a way that if there are equal elements C<$A[$i]> and C<$B[$j]> +which are equal and which are part of the LCS, there will be some moment +during the execution of C when arrow A is pointing +to C<$A[$i]> and arrow B is pointing to C<$B[$j]>. When this happens, +C will call the C callback function and then +it will advance both arrows. + +Otherwise, one of the arrows is pointing to an element of its sequence +that is not part of the LCS. C will advance that +arrow and will call the C or the C callback, +depending on which arrow it advanced. If both arrows point to elements +that are not part of the LCS, then C will advance +one of them and call the appropriate callback, but it is not specified +which it will call. + +The arguments to C are the two sequences to +traverse, and a hash which specifies the callback functions, like this: + + traverse_sequences( + \@seq1, \@seq2, + { MATCH => $callback_1, + DISCARD_A => $callback_2, + DISCARD_B => $callback_3, + } + ); + +Callbacks for MATCH, DISCARD_A, and DISCARD_B are invoked with at least +the indices of the two arrows as their arguments. They are not expected +to return any values. If a callback is omitted from the table, it is +not called. + +Callbacks for A_FINISHED and B_FINISHED are invoked with at least the +corresponding index in A or B. + +If arrow A reaches the end of its sequence, before arrow B does, +C will call the C callback when it +advances arrow B, if there is such a function; if not it will call +C instead. Similarly if arrow B finishes first. +C returns when both arrows are at the ends of their +respective sequences. It returns true on success and false on failure. +At present there is no way to fail. + +C may be passed an optional fourth parameter; this +is a CODE reference to a key generation function. See L. + +Additional parameters, if any, will be passed to the key generation function. + +If you want to pass additional parameters to your callbacks, but don't +need a custom key generation function, you can get the default by +passing undef: + + traverse_sequences( + \@seq1, \@seq2, + { MATCH => $callback_1, + DISCARD_A => $callback_2, + DISCARD_B => $callback_3, + }, + undef, # default key-gen + $myArgument1, + $myArgument2, + $myArgument3, + ); + +C does not have a useful return value; you are +expected to plug in the appropriate behavior with the callback +functions. + +=head2 C + +C is an alternative to C. It +uses a different algorithm to iterate through the entries in the +computed LCS. Instead of sticking to one side and showing element changes +as insertions and deletions only, it will jump back and forth between +the two sequences and report I occurring as deletions on one +side followed immediatly by an insertion on the other side. + +In addition to the C, C, and C callbacks +supported by C, C supports +a C callback indicating that one element got C by another: + + traverse_balanced( + \@seq1, \@seq2, + { MATCH => $callback_1, + DISCARD_A => $callback_2, + DISCARD_B => $callback_3, + CHANGE => $callback_4, + } + ); + +If no C callback is specified, C +will map C events to C and C actions, +therefore resulting in a similar behaviour as C +with different order of events. + +C might be a bit slower than C, +noticable only while processing huge amounts of data. + +The C function of this module +is implemented as call to C. + +C does not have a useful return value; you are expected to +plug in the appropriate behavior with the callback functions. + +=head1 KEY GENERATION FUNCTIONS + +Most of the functions accept an optional extra parameter. This is a +CODE reference to a key generating (hashing) function that should return +a string that uniquely identifies a given element. It should be the +case that if two elements are to be considered equal, their keys should +be the same (and the other way around). If no key generation function +is provided, the key will be the element as a string. + +By default, comparisons will use "eq" and elements will be turned into keys +using the default stringizing operator '""'. + +Where this is important is when you're comparing something other than +strings. If it is the case that you have multiple different objects +that should be considered to be equal, you should supply a key +generation function. Otherwise, you have to make sure that your arrays +contain unique references. + +For instance, consider this example: + + package Person; + + sub new + { + my $package = shift; + return bless { name => '', ssn => '', @_ }, $package; + } + + sub clone + { + my $old = shift; + my $new = bless { %$old }, ref($old); + } + + sub hash + { + return shift()->{'ssn'}; + } + + my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' ); + my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' ); + my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' ); + my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' ); + my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' ); + +If you did this: + + my $array1 = [ $person1, $person2, $person4 ]; + my $array2 = [ $person1, $person3, $person4, $person5 ]; + Algorithm::Diff::diff( $array1, $array2 ); + +everything would work out OK (each of the objects would be converted +into a string like "Person=HASH(0x82425b0)" for comparison). + +But if you did this: + + my $array1 = [ $person1, $person2, $person4 ]; + my $array2 = [ $person1, $person3, $person4->clone(), $person5 ]; + Algorithm::Diff::diff( $array1, $array2 ); + +$person4 and $person4->clone() (which have the same name and SSN) +would be seen as different objects. If you wanted them to be considered +equivalent, you would have to pass in a key generation function: + + my $array1 = [ $person1, $person2, $person4 ]; + my $array2 = [ $person1, $person3, $person4->clone(), $person5 ]; + Algorithm::Diff::diff( $array1, $array2, \&Person::hash ); + +This would use the 'ssn' field in each Person as a comparison key, and +so would consider $person4 and $person4->clone() as equal. + +You may also pass additional parameters to the key generation function +if you wish. + +=head1 ERROR CHECKING + +If you pass these routines a non-reference and they expect a reference, +they will die with a message. + +=head1 AUTHOR + +This version released by Tye McQueen (http://perlmonks.org/?node=tye). + +=head1 LICENSE + +Parts Copyright (c) 2000-2004 Ned Konz. All rights reserved. +Parts by Tye McQueen. + +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl. + +=head1 MAILING LIST + +Mark-Jason still maintains a mailing list. To join a low-volume mailing +list for announcements related to diff and Algorithm::Diff, send an +empty mail message to mjd-perl-diff-request@plover.com. + +=head1 CREDITS + +Versions through 0.59 (and much of this documentation) were written by: + +Mark-Jason Dominus, mjd-perl-diff@plover.com + +This version borrows some documentation and routine names from +Mark-Jason's, but Diff.pm's code was completely replaced. + +This code was adapted from the Smalltalk code of Mario Wolczko +, which is available at +ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st + +C and C were written by Mike Schilli +. + +The algorithm is that described in +I, +CACM, vol.20, no.5, pp.350-353, May 1977, with a few +minor improvements to improve the speed. + +Much work was done by Ned Konz (perl@bike-nomad.com). + +The OO interface and some other changes are by Tye McQueen. + +=cut diff --git a/pintos-env/pintos/tests/Make.tests b/pintos-env/pintos/tests/Make.tests new file mode 100755 index 0000000..358e697 --- /dev/null +++ b/pintos-env/pintos/tests/Make.tests @@ -0,0 +1,76 @@ +# -*- makefile -*- + +include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS)) + +PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS)) +TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS)) +EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES)) + +OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES)) +ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES)) +RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES)) + +ifdef PROGS +include ../../Makefile.userprog +endif + +TIMEOUT = 60 + +clean:: + rm -f $(OUTPUTS) $(ERRORS) $(RESULTS) + +grade:: results + $(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@ + +check:: results + @cat $< + @COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \ + FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \ + if [ $$FAILURES = 0 ]; then \ + echo "All $$COUNT tests passed."; \ + else \ + echo "$$FAILURES of $$COUNT tests failed."; \ + exit 1; \ + fi + +results: $(RESULTS) + @for d in $(TESTS) $(EXTRA_GRADES); do \ + if echo PASS | cmp -s $$d.result -; then \ + echo "pass $$d"; \ + else \ + echo "FAIL $$d"; \ + fi; \ + done > $@ + +outputs:: $(OUTPUTS) + +$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog))) +$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES))) +$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test))) + +# Prevent an environment variable VERBOSE from surprising us. +VERBOSE = + +TESTCMD = pintos -v -k -T $(TIMEOUT) +TESTCMD += $(SIMULATOR) +TESTCMD += $(PINTOSOPTS) +ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog) +TESTCMD += $(FILESYSSOURCE) +TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file))) +endif +ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm) +TESTCMD += --swap-size=4 +endif +TESTCMD += -- -q +TESTCMD += $(KERNELFLAGS) +ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog) +TESTCMD += -f +endif +TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F)) +TESTCMD += < /dev/null +TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output +%.output: kernel.bin loader.bin + $(TESTCMD) + +%.result: %.ck %.output + perl -I$(SRCDIR) $< $* $@ diff --git a/pintos-env/pintos/tests/arc4.c b/pintos-env/pintos/tests/arc4.c new file mode 100755 index 0000000..b033cc6 --- /dev/null +++ b/pintos-env/pintos/tests/arc4.c @@ -0,0 +1,53 @@ +#include +#include "tests/arc4.h" + +/* Swap bytes. */ +static inline void +swap_byte (uint8_t *a, uint8_t *b) +{ + uint8_t t = *a; + *a = *b; + *b = t; +} + +void +arc4_init (struct arc4 *arc4, const void *key_, size_t size) +{ + const uint8_t *key = key_; + size_t key_idx; + uint8_t *s; + int i, j; + + s = arc4->s; + arc4->i = arc4->j = 0; + for (i = 0; i < 256; i++) + s[i] = i; + for (key_idx = 0, i = j = 0; i < 256; i++) + { + j = (j + s[i] + key[key_idx]) & 255; + swap_byte (s + i, s + j); + if (++key_idx >= size) + key_idx = 0; + } +} + +void +arc4_crypt (struct arc4 *arc4, void *buf_, size_t size) +{ + uint8_t *buf = buf_; + uint8_t *s; + uint8_t i, j; + + s = arc4->s; + i = arc4->i; + j = arc4->j; + while (size-- > 0) + { + i += 1; + j += s[i]; + swap_byte (s + i, s + j); + *buf++ ^= s[(s[i] + s[j]) & 255]; + } + arc4->i = i; + arc4->j = j; +} diff --git a/pintos-env/pintos/tests/arc4.h b/pintos-env/pintos/tests/arc4.h new file mode 100755 index 0000000..61c533a --- /dev/null +++ b/pintos-env/pintos/tests/arc4.h @@ -0,0 +1,17 @@ +#ifndef TESTS_ARC4_H +#define TESTS_ARC4_H + +#include +#include + +/* Alleged RC4 algorithm encryption state. */ +struct arc4 + { + uint8_t s[256]; + uint8_t i, j; + }; + +void arc4_init (struct arc4 *, const void *, size_t); +void arc4_crypt (struct arc4 *, void *, size_t); + +#endif /* tests/arc4.h */ diff --git a/pintos-env/pintos/tests/arc4.pm b/pintos-env/pintos/tests/arc4.pm new file mode 100755 index 0000000..df19216 --- /dev/null +++ b/pintos-env/pintos/tests/arc4.pm @@ -0,0 +1,29 @@ +use strict; +use warnings; + +sub arc4_init { + my ($key) = @_; + my (@s) = 0...255; + my ($j) = 0; + for my $i (0...255) { + $j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff; + @s[$i, $j] = @s[$j, $i]; + } + return (0, 0, @s); +} + +sub arc4_crypt { + my ($arc4, $buf) = @_; + my ($i, $j, @s) = @$arc4; + my ($out) = ""; + for my $c (split (//, $buf)) { + $i = ($i + 1) & 0xff; + $j = ($j + $s[$i]) & 0xff; + @s[$i, $j] = @s[$j, $i]; + $out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]); + } + @$arc4 = ($i, $j, @s); + return $out; +} + +1; diff --git a/pintos-env/pintos/tests/cksum.c b/pintos-env/pintos/tests/cksum.c new file mode 100755 index 0000000..92a2995 --- /dev/null +++ b/pintos-env/pintos/tests/cksum.c @@ -0,0 +1,92 @@ +/* crctab[] and cksum() are from the `cksum' entry in SUSv3. */ + +#include +#include "tests/cksum.h" + +static unsigned long crctab[] = { + 0x00000000, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +/* This is the algorithm used by the Posix `cksum' utility. */ +unsigned long +cksum (const void *b_, size_t n) +{ + const unsigned char *b = b_; + uint32_t s = 0; + size_t i; + for (i = n; i > 0; --i) + { + unsigned char c = *b++; + s = (s << 8) ^ crctab[(s >> 24) ^ c]; + } + while (n != 0) + { + unsigned char c = n; + n >>= 8; + s = (s << 8) ^ crctab[(s >> 24) ^ c]; + } + return ~s; +} + +#ifdef STANDALONE_TEST +#include +int +main (void) +{ + char buf[65536]; + int n = fread (buf, 1, sizeof buf, stdin); + printf ("%lu\n", cksum (buf, n)); + return 0; +} +#endif diff --git a/pintos-env/pintos/tests/cksum.h b/pintos-env/pintos/tests/cksum.h new file mode 100755 index 0000000..23a1fe9 --- /dev/null +++ b/pintos-env/pintos/tests/cksum.h @@ -0,0 +1,8 @@ +#ifndef TESTS_CKSUM_H +#define TESTS_CKSUM_H + +#include + +unsigned long cksum(const void *, size_t); + +#endif /* tests/cksum.h */ diff --git a/pintos-env/pintos/tests/cksum.pm b/pintos-env/pintos/tests/cksum.pm new file mode 100755 index 0000000..73be5f2 --- /dev/null +++ b/pintos-env/pintos/tests/cksum.pm @@ -0,0 +1,87 @@ +# From the `cksum' entry in SUSv3. + +use strict; +use warnings; + +my (@crctab) = + (0x00000000, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4); + +sub cksum { + my ($b) = @_; + my ($n) = length ($b); + my ($s) = 0; + for my $i (0...$n - 1) { + my ($c) = ord (substr ($b, $i, 1)); + $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c]; + $s &= 0xffff_ffff; + } + while ($n != 0) { + my ($c) = $n & 0xff; + $n >>= 8; + $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c]; + $s &= 0xffff_ffff; + } + return ~$s & 0xffff_ffff; +} + +sub cksum_file { + my ($file) = @_; + open (FILE, '<', $file) or die "$file: open: $!\n"; + my ($data); + sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n"; + close (FILE); + return cksum ($data); +} + +1; diff --git a/pintos-env/pintos/tests/filesys/Grading.no-vm b/pintos-env/pintos/tests/filesys/Grading.no-vm new file mode 100755 index 0000000..ee98fc1 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/Grading.no-vm @@ -0,0 +1,18 @@ +# Percentage of the testing point total designated for each set of +# tests. + +# This project is primarily about implementing the file system, but +# all the previous functionality should work too. It's not too easy +# to screw it up, thus the emphasis. + +# 65% for extended file system features. +30% tests/filesys/extended/Rubric.functionality +15% tests/filesys/extended/Rubric.robustness +20% tests/filesys/extended/Rubric.persistence + +# 20% to not break the provided file system features. +20% tests/filesys/base/Rubric + +# 15% for the rest. +10% tests/userprog/Rubric.functionality +5% tests/userprog/Rubric.robustness diff --git a/pintos-env/pintos/tests/filesys/Grading.with-vm b/pintos-env/pintos/tests/filesys/Grading.with-vm new file mode 100755 index 0000000..e7c041e --- /dev/null +++ b/pintos-env/pintos/tests/filesys/Grading.with-vm @@ -0,0 +1,22 @@ +# Percentage of the testing point total designated for each set of +# tests. + +# This project is primarily about implementing the file system, but +# all the previous functionality should work too. It's not too easy +# to screw it up, thus the emphasis. + +# 65% for extended file system features. +30% tests/filesys/extended/Rubric.functionality +15% tests/filesys/extended/Rubric.robustness +20% tests/filesys/extended/Rubric.persistence + +# 20% to not break the provided file system features. +20% tests/filesys/base/Rubric + +# 15% for the rest. +10% tests/userprog/Rubric.functionality +5% tests/userprog/Rubric.robustness + +# Up to 10% bonus for working VM functionality. +8% tests/vm/Rubric.functionality +2% tests/vm/Rubric.robustness diff --git a/pintos-env/pintos/tests/filesys/base/Make.tests b/pintos-env/pintos/tests/filesys/base/Make.tests new file mode 100755 index 0000000..e475222 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/Make.tests @@ -0,0 +1,18 @@ +# -*- makefile -*- + +tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \ +lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \ +sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write) + +tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \ +tests/filesys/base/,child-syn-read child-syn-wrt) + +$(foreach prog,$(tests/filesys/base_PROGS), \ + $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c)) +$(foreach prog,$(tests/filesys/base_TESTS), \ + $(eval $(prog)_SRC += tests/main.c)) + +tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read +tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt + +tests/filesys/base/syn-read.output: TIMEOUT = 300 diff --git a/pintos-env/pintos/tests/filesys/base/Rubric b/pintos-env/pintos/tests/filesys/base/Rubric new file mode 100755 index 0000000..49a9d15 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/Rubric @@ -0,0 +1,19 @@ +Functionality of base file system: +- Test basic support for small files. +1 sm-create +2 sm-full +2 sm-random +2 sm-seq-block +3 sm-seq-random + +- Test basic support for large files. +1 lg-create +2 lg-full +2 lg-random +2 lg-seq-block +3 lg-seq-random + +- Test synchronized multiprogram access to files. +4 syn-read +4 syn-write +2 syn-remove diff --git a/pintos-env/pintos/tests/filesys/base/child-syn-read.c b/pintos-env/pintos/tests/filesys/base/child-syn-read.c new file mode 100755 index 0000000..77a5e26 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/child-syn-read.c @@ -0,0 +1,44 @@ +/* Child process for syn-read test. + Reads the contents of a test file a byte at a time, in the + hope that this will take long enough that we can get a + significant amount of contention in the kernel file system + code. */ + +#include +#include +#include +#include +#include "tests/lib.h" +#include "tests/filesys/base/syn-read.h" + +const char *test_name = "child-syn-read"; + +static char buf[BUF_SIZE]; + +int +main (int argc, const char *argv[]) +{ + int child_idx; + int fd; + size_t i; + + quiet = true; + + CHECK (argc == 2, "argc must be 2, actually %d", argc); + child_idx = atoi (argv[1]); + + random_init (0); + random_bytes (buf, sizeof buf); + + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + for (i = 0; i < sizeof buf; i++) + { + char c; + CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name); + compare_bytes (&c, buf + i, 1, i, file_name); + } + close (fd); + + return child_idx; +} + diff --git a/pintos-env/pintos/tests/filesys/base/child-syn-wrt.c b/pintos-env/pintos/tests/filesys/base/child-syn-wrt.c new file mode 100755 index 0000000..1b52584 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/child-syn-wrt.c @@ -0,0 +1,35 @@ +/* Child process for syn-read test. + Writes into part of a test file. Other processes will be + writing into other parts at the same time. */ + +#include +#include +#include +#include "tests/lib.h" +#include "tests/filesys/base/syn-write.h" + +char buf[BUF_SIZE]; + +int +main (int argc, char *argv[]) +{ + int child_idx; + int fd; + + quiet = true; + + CHECK (argc == 2, "argc must be 2, actually %d", argc); + child_idx = atoi (argv[1]); + + random_init (0); + random_bytes (buf, sizeof buf); + + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + seek (fd, CHUNK_SIZE * child_idx); + CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0, + "write \"%s\"", file_name); + msg ("close \"%s\"", file_name); + close (fd); + + return child_idx; +} diff --git a/pintos-env/pintos/tests/filesys/base/full.inc b/pintos-env/pintos/tests/filesys/base/full.inc new file mode 100755 index 0000000..38a0396 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/full.inc @@ -0,0 +1,20 @@ +/* -*- c -*- */ + +#include "tests/filesys/seq-test.h" +#include "tests/main.h" + +static char buf[TEST_SIZE]; + +static size_t +return_test_size (void) +{ + return TEST_SIZE; +} + +void +test_main (void) +{ + seq_test ("quux", + buf, sizeof buf, sizeof buf, + return_test_size, NULL); +} diff --git a/pintos-env/pintos/tests/filesys/base/lg-create.c b/pintos-env/pintos/tests/filesys/base/lg-create.c new file mode 100755 index 0000000..5c45eee --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-create.c @@ -0,0 +1,5 @@ +/* Tests that create properly zeros out the contents of a fairly + large file. */ + +#define TEST_SIZE 75678 +#include "tests/filesys/create.inc" diff --git a/pintos-env/pintos/tests/filesys/base/lg-create.ck b/pintos-env/pintos/tests/filesys/base/lg-create.ck new file mode 100755 index 0000000..86b2c51 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-create.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(lg-create) begin +(lg-create) create "blargle" +(lg-create) open "blargle" for verification +(lg-create) verified contents of "blargle" +(lg-create) close "blargle" +(lg-create) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/lg-full.c b/pintos-env/pintos/tests/filesys/base/lg-full.c new file mode 100755 index 0000000..5f7234d --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-full.c @@ -0,0 +1,6 @@ +/* Writes out the contents of a fairly large file all at once, + and then reads it back to make sure that it was written + properly. */ + +#define TEST_SIZE 75678 +#include "tests/filesys/base/full.inc" diff --git a/pintos-env/pintos/tests/filesys/base/lg-full.ck b/pintos-env/pintos/tests/filesys/base/lg-full.ck new file mode 100755 index 0000000..ee6c7f9 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-full.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(lg-full) begin +(lg-full) create "quux" +(lg-full) open "quux" +(lg-full) writing "quux" +(lg-full) close "quux" +(lg-full) open "quux" for verification +(lg-full) verified contents of "quux" +(lg-full) close "quux" +(lg-full) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/lg-random.c b/pintos-env/pintos/tests/filesys/base/lg-random.c new file mode 100755 index 0000000..b6f8873 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-random.c @@ -0,0 +1,7 @@ +/* Writes out the content of a fairly large file in random order, + then reads it back in random order to verify that it was + written properly. */ + +#define BLOCK_SIZE 512 +#define TEST_SIZE (512 * 150) +#include "tests/filesys/base/random.inc" diff --git a/pintos-env/pintos/tests/filesys/base/lg-random.ck b/pintos-env/pintos/tests/filesys/base/lg-random.ck new file mode 100755 index 0000000..dd9f1dd --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-random.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(lg-random) begin +(lg-random) create "bazzle" +(lg-random) open "bazzle" +(lg-random) write "bazzle" in random order +(lg-random) read "bazzle" in random order +(lg-random) close "bazzle" +(lg-random) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/lg-seq-block.c b/pintos-env/pintos/tests/filesys/base/lg-seq-block.c new file mode 100755 index 0000000..580c30b --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-seq-block.c @@ -0,0 +1,7 @@ +/* Writes out a fairly large file sequentially, one fixed-size + block at a time, then reads it back to verify that it was + written properly. */ + +#define TEST_SIZE 75678 +#define BLOCK_SIZE 513 +#include "tests/filesys/base/seq-block.inc" diff --git a/pintos-env/pintos/tests/filesys/base/lg-seq-block.ck b/pintos-env/pintos/tests/filesys/base/lg-seq-block.ck new file mode 100755 index 0000000..b789081 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-seq-block.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(lg-seq-block) begin +(lg-seq-block) create "noodle" +(lg-seq-block) open "noodle" +(lg-seq-block) writing "noodle" +(lg-seq-block) close "noodle" +(lg-seq-block) open "noodle" for verification +(lg-seq-block) verified contents of "noodle" +(lg-seq-block) close "noodle" +(lg-seq-block) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/lg-seq-random.c b/pintos-env/pintos/tests/filesys/base/lg-seq-random.c new file mode 100755 index 0000000..fbb6bba --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-seq-random.c @@ -0,0 +1,6 @@ +/* Writes out a fairly large file sequentially, one random-sized + block at a time, then reads it back to verify that it was + written properly. */ + +#define TEST_SIZE 75678 +#include "tests/filesys/base/seq-random.inc" diff --git a/pintos-env/pintos/tests/filesys/base/lg-seq-random.ck b/pintos-env/pintos/tests/filesys/base/lg-seq-random.ck new file mode 100755 index 0000000..6b2dc82 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/lg-seq-random.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(lg-seq-random) begin +(lg-seq-random) create "nibble" +(lg-seq-random) open "nibble" +(lg-seq-random) writing "nibble" +(lg-seq-random) close "nibble" +(lg-seq-random) open "nibble" for verification +(lg-seq-random) verified contents of "nibble" +(lg-seq-random) close "nibble" +(lg-seq-random) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/random.inc b/pintos-env/pintos/tests/filesys/base/random.inc new file mode 100755 index 0000000..eeeea68 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/random.inc @@ -0,0 +1,59 @@ +/* -*- c -*- */ + +#include +#include +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +#if TEST_SIZE % BLOCK_SIZE != 0 +#error TEST_SIZE must be a multiple of BLOCK_SIZE +#endif + +#define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE) + +char buf[TEST_SIZE]; +int order[BLOCK_CNT]; + +void +test_main (void) +{ + const char *file_name = "bazzle"; + int fd; + size_t i; + + random_init (57); + random_bytes (buf, sizeof buf); + + for (i = 0; i < BLOCK_CNT; i++) + order[i] = i; + + CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name); + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + + msg ("write \"%s\" in random order", file_name); + shuffle (order, BLOCK_CNT, sizeof *order); + for (i = 0; i < BLOCK_CNT; i++) + { + size_t ofs = BLOCK_SIZE * order[i]; + seek (fd, ofs); + if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE) + fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs); + } + + msg ("read \"%s\" in random order", file_name); + shuffle (order, BLOCK_CNT, sizeof *order); + for (i = 0; i < BLOCK_CNT; i++) + { + char block[BLOCK_SIZE]; + size_t ofs = BLOCK_SIZE * order[i]; + seek (fd, ofs); + if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE) + fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs); + compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name); + } + + msg ("close \"%s\"", file_name); + close (fd); +} diff --git a/pintos-env/pintos/tests/filesys/base/seq-block.inc b/pintos-env/pintos/tests/filesys/base/seq-block.inc new file mode 100755 index 0000000..d4c1f57 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/seq-block.inc @@ -0,0 +1,20 @@ +/* -*- c -*- */ + +#include "tests/filesys/seq-test.h" +#include "tests/main.h" + +static char buf[TEST_SIZE]; + +static size_t +return_block_size (void) +{ + return BLOCK_SIZE; +} + +void +test_main (void) +{ + seq_test ("noodle", + buf, sizeof buf, sizeof buf, + return_block_size, NULL); +} diff --git a/pintos-env/pintos/tests/filesys/base/seq-random.inc b/pintos-env/pintos/tests/filesys/base/seq-random.inc new file mode 100755 index 0000000..a4da4c5 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/seq-random.inc @@ -0,0 +1,22 @@ +/* -*- c -*- */ + +#include +#include "tests/filesys/seq-test.h" +#include "tests/main.h" + +static char buf[TEST_SIZE]; + +static size_t +return_random (void) +{ + return random_ulong () % 1031 + 1; +} + +void +test_main (void) +{ + random_init (-1); + seq_test ("nibble", + buf, sizeof buf, sizeof buf, + return_random, NULL); +} diff --git a/pintos-env/pintos/tests/filesys/base/sm-create.c b/pintos-env/pintos/tests/filesys/base/sm-create.c new file mode 100755 index 0000000..6b97ac1 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-create.c @@ -0,0 +1,5 @@ +/* Tests that create properly zeros out the contents of a fairly + small file. */ + +#define TEST_SIZE 5678 +#include "tests/filesys/create.inc" diff --git a/pintos-env/pintos/tests/filesys/base/sm-create.ck b/pintos-env/pintos/tests/filesys/base/sm-create.ck new file mode 100755 index 0000000..8ca80dc --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-create.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(sm-create) begin +(sm-create) create "blargle" +(sm-create) open "blargle" for verification +(sm-create) verified contents of "blargle" +(sm-create) close "blargle" +(sm-create) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/sm-full.c b/pintos-env/pintos/tests/filesys/base/sm-full.c new file mode 100755 index 0000000..23ff3d4 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-full.c @@ -0,0 +1,6 @@ +/* Writes out the contents of a fairly small file all at once, + and then reads it back to make sure that it was written + properly. */ + +#define TEST_SIZE 5678 +#include "tests/filesys/base/full.inc" diff --git a/pintos-env/pintos/tests/filesys/base/sm-full.ck b/pintos-env/pintos/tests/filesys/base/sm-full.ck new file mode 100755 index 0000000..2e0eb36 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-full.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(sm-full) begin +(sm-full) create "quux" +(sm-full) open "quux" +(sm-full) writing "quux" +(sm-full) close "quux" +(sm-full) open "quux" for verification +(sm-full) verified contents of "quux" +(sm-full) close "quux" +(sm-full) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/sm-random.c b/pintos-env/pintos/tests/filesys/base/sm-random.c new file mode 100755 index 0000000..42d670f --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-random.c @@ -0,0 +1,7 @@ +/* Writes out the content of a fairly small file in random order, + then reads it back in random order to verify that it was + written properly. */ + +#define BLOCK_SIZE 13 +#define TEST_SIZE (13 * 123) +#include "tests/filesys/base/random.inc" diff --git a/pintos-env/pintos/tests/filesys/base/sm-random.ck b/pintos-env/pintos/tests/filesys/base/sm-random.ck new file mode 100755 index 0000000..bda049d --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-random.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(sm-random) begin +(sm-random) create "bazzle" +(sm-random) open "bazzle" +(sm-random) write "bazzle" in random order +(sm-random) read "bazzle" in random order +(sm-random) close "bazzle" +(sm-random) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/sm-seq-block.c b/pintos-env/pintos/tests/filesys/base/sm-seq-block.c new file mode 100755 index 0000000..e368327 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-seq-block.c @@ -0,0 +1,7 @@ +/* Writes out a fairly small file sequentially, one fixed-size + block at a time, then reads it back to verify that it was + written properly. */ + +#define TEST_SIZE 5678 +#define BLOCK_SIZE 513 +#include "tests/filesys/base/seq-block.inc" diff --git a/pintos-env/pintos/tests/filesys/base/sm-seq-block.ck b/pintos-env/pintos/tests/filesys/base/sm-seq-block.ck new file mode 100755 index 0000000..0e2939d --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-seq-block.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(sm-seq-block) begin +(sm-seq-block) create "noodle" +(sm-seq-block) open "noodle" +(sm-seq-block) writing "noodle" +(sm-seq-block) close "noodle" +(sm-seq-block) open "noodle" for verification +(sm-seq-block) verified contents of "noodle" +(sm-seq-block) close "noodle" +(sm-seq-block) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/sm-seq-random.c b/pintos-env/pintos/tests/filesys/base/sm-seq-random.c new file mode 100755 index 0000000..89e5b71 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-seq-random.c @@ -0,0 +1,6 @@ +/* Writes out a fairly large file sequentially, one random-sized + block at a time, then reads it back to verify that it was + written properly. */ + +#define TEST_SIZE 5678 +#include "tests/filesys/base/seq-random.inc" diff --git a/pintos-env/pintos/tests/filesys/base/sm-seq-random.ck b/pintos-env/pintos/tests/filesys/base/sm-seq-random.ck new file mode 100755 index 0000000..2fb368b --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/sm-seq-random.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(sm-seq-random) begin +(sm-seq-random) create "nibble" +(sm-seq-random) open "nibble" +(sm-seq-random) writing "nibble" +(sm-seq-random) close "nibble" +(sm-seq-random) open "nibble" for verification +(sm-seq-random) verified contents of "nibble" +(sm-seq-random) close "nibble" +(sm-seq-random) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/syn-read.c b/pintos-env/pintos/tests/filesys/base/syn-read.c new file mode 100755 index 0000000..7c36a42 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-read.c @@ -0,0 +1,31 @@ +/* Spawns 10 child processes, all of which read from the same + file and make sure that the contents are what they should + be. */ + +#include +#include +#include +#include "tests/lib.h" +#include "tests/main.h" +#include "tests/filesys/base/syn-read.h" + +static char buf[BUF_SIZE]; + +#define CHILD_CNT 10 + +void +test_main (void) +{ + pid_t children[CHILD_CNT]; + int fd; + + CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name); + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + random_bytes (buf, sizeof buf); + CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name); + msg ("close \"%s\"", file_name); + close (fd); + + exec_children ("child-syn-read", children, CHILD_CNT); + wait_children (children, CHILD_CNT); +} diff --git a/pintos-env/pintos/tests/filesys/base/syn-read.ck b/pintos-env/pintos/tests/filesys/base/syn-read.ck new file mode 100755 index 0000000..e2f68e8 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-read.ck @@ -0,0 +1,33 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(syn-read) begin +(syn-read) create "data" +(syn-read) open "data" +(syn-read) write "data" +(syn-read) close "data" +(syn-read) exec child 1 of 10: "child-syn-read 0" +(syn-read) exec child 2 of 10: "child-syn-read 1" +(syn-read) exec child 3 of 10: "child-syn-read 2" +(syn-read) exec child 4 of 10: "child-syn-read 3" +(syn-read) exec child 5 of 10: "child-syn-read 4" +(syn-read) exec child 6 of 10: "child-syn-read 5" +(syn-read) exec child 7 of 10: "child-syn-read 6" +(syn-read) exec child 8 of 10: "child-syn-read 7" +(syn-read) exec child 9 of 10: "child-syn-read 8" +(syn-read) exec child 10 of 10: "child-syn-read 9" +(syn-read) wait for child 1 of 10 returned 0 (expected 0) +(syn-read) wait for child 2 of 10 returned 1 (expected 1) +(syn-read) wait for child 3 of 10 returned 2 (expected 2) +(syn-read) wait for child 4 of 10 returned 3 (expected 3) +(syn-read) wait for child 5 of 10 returned 4 (expected 4) +(syn-read) wait for child 6 of 10 returned 5 (expected 5) +(syn-read) wait for child 7 of 10 returned 6 (expected 6) +(syn-read) wait for child 8 of 10 returned 7 (expected 7) +(syn-read) wait for child 9 of 10 returned 8 (expected 8) +(syn-read) wait for child 10 of 10 returned 9 (expected 9) +(syn-read) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/syn-read.h b/pintos-env/pintos/tests/filesys/base/syn-read.h new file mode 100755 index 0000000..bff8082 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-read.h @@ -0,0 +1,7 @@ +#ifndef TESTS_FILESYS_BASE_SYN_READ_H +#define TESTS_FILESYS_BASE_SYN_READ_H + +#define BUF_SIZE 1024 +static const char file_name[] = "data"; + +#endif /* tests/filesys/base/syn-read.h */ diff --git a/pintos-env/pintos/tests/filesys/base/syn-remove.c b/pintos-env/pintos/tests/filesys/base/syn-remove.c new file mode 100755 index 0000000..c9ba110 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-remove.c @@ -0,0 +1,30 @@ +/* Verifies that a deleted file may still be written to and read + from. */ + +#include +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +char buf1[1234]; +char buf2[1234]; + +void +test_main (void) +{ + const char *file_name = "deleteme"; + int fd; + + CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name); + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + CHECK (remove (file_name), "remove \"%s\"", file_name); + random_bytes (buf1, sizeof buf1); + CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name); + msg ("seek \"%s\" to 0", file_name); + seek (fd, 0); + CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name); + compare_bytes (buf2, buf1, sizeof buf1, 0, file_name); + msg ("close \"%s\"", file_name); + close (fd); +} diff --git a/pintos-env/pintos/tests/filesys/base/syn-remove.ck b/pintos-env/pintos/tests/filesys/base/syn-remove.ck new file mode 100755 index 0000000..16ff11e --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-remove.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(syn-remove) begin +(syn-remove) create "deleteme" +(syn-remove) open "deleteme" +(syn-remove) remove "deleteme" +(syn-remove) write "deleteme" +(syn-remove) seek "deleteme" to 0 +(syn-remove) read "deleteme" +(syn-remove) close "deleteme" +(syn-remove) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/syn-write.c b/pintos-env/pintos/tests/filesys/base/syn-write.c new file mode 100755 index 0000000..1439862 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-write.c @@ -0,0 +1,31 @@ +/* Spawns several child processes to write out different parts of + the contents of a file and waits for them to finish. Then + reads back the file and verifies its contents. */ + +#include +#include +#include +#include +#include "tests/filesys/base/syn-write.h" +#include "tests/lib.h" +#include "tests/main.h" + +char buf1[BUF_SIZE]; +char buf2[BUF_SIZE]; + +void +test_main (void) +{ + pid_t children[CHILD_CNT]; + int fd; + + CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name); + + exec_children ("child-syn-wrt", children, CHILD_CNT); + wait_children (children, CHILD_CNT); + + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name); + random_bytes (buf2, sizeof buf2); + compare_bytes (buf1, buf2, sizeof buf1, 0, file_name); +} diff --git a/pintos-env/pintos/tests/filesys/base/syn-write.ck b/pintos-env/pintos/tests/filesys/base/syn-write.ck new file mode 100755 index 0000000..629a7a2 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-write.ck @@ -0,0 +1,32 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(syn-write) begin +(syn-write) create "stuff" +(syn-write) exec child 1 of 10: "child-syn-wrt 0" +(syn-write) exec child 2 of 10: "child-syn-wrt 1" +(syn-write) exec child 3 of 10: "child-syn-wrt 2" +(syn-write) exec child 4 of 10: "child-syn-wrt 3" +(syn-write) exec child 5 of 10: "child-syn-wrt 4" +(syn-write) exec child 6 of 10: "child-syn-wrt 5" +(syn-write) exec child 7 of 10: "child-syn-wrt 6" +(syn-write) exec child 8 of 10: "child-syn-wrt 7" +(syn-write) exec child 9 of 10: "child-syn-wrt 8" +(syn-write) exec child 10 of 10: "child-syn-wrt 9" +(syn-write) wait for child 1 of 10 returned 0 (expected 0) +(syn-write) wait for child 2 of 10 returned 1 (expected 1) +(syn-write) wait for child 3 of 10 returned 2 (expected 2) +(syn-write) wait for child 4 of 10 returned 3 (expected 3) +(syn-write) wait for child 5 of 10 returned 4 (expected 4) +(syn-write) wait for child 6 of 10 returned 5 (expected 5) +(syn-write) wait for child 7 of 10 returned 6 (expected 6) +(syn-write) wait for child 8 of 10 returned 7 (expected 7) +(syn-write) wait for child 9 of 10 returned 8 (expected 8) +(syn-write) wait for child 10 of 10 returned 9 (expected 9) +(syn-write) open "stuff" +(syn-write) read "stuff" +(syn-write) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/base/syn-write.h b/pintos-env/pintos/tests/filesys/base/syn-write.h new file mode 100755 index 0000000..07a6d5a --- /dev/null +++ b/pintos-env/pintos/tests/filesys/base/syn-write.h @@ -0,0 +1,9 @@ +#ifndef TESTS_FILESYS_BASE_SYN_WRITE_H +#define TESTS_FILESYS_BASE_SYN_WRITE_H + +#define CHILD_CNT 10 +#define CHUNK_SIZE 512 +#define BUF_SIZE (CHILD_CNT * CHUNK_SIZE) +static const char file_name[] = "stuff"; + +#endif /* tests/filesys/base/syn-write.h */ diff --git a/pintos-env/pintos/tests/filesys/create.inc b/pintos-env/pintos/tests/filesys/create.inc new file mode 100755 index 0000000..4baf771 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/create.inc @@ -0,0 +1,15 @@ +/* -*- c -*- */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +static char buf[TEST_SIZE]; + +void +test_main (void) +{ + const char *file_name = "blargle"; + CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name); + check_file (file_name, buf, TEST_SIZE); +} diff --git a/pintos-env/pintos/tests/filesys/extended/Make.tests b/pintos-env/pintos/tests/filesys/extended/Make.tests new file mode 100755 index 0000000..e03b98d --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/Make.tests @@ -0,0 +1,61 @@ +# -*- makefile -*- + +raw_tests = dir-empty-name dir-mk-tree dir-mkdir dir-open \ +dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree \ +dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg \ +grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm \ +grow-sparse grow-tell grow-two-files syn-rw + +tests/filesys/extended_TESTS = $(patsubst %,tests/filesys/extended/%,$(raw_tests)) +tests/filesys/extended_EXTRA_GRADES = $(patsubst %,tests/filesys/extended/%-persistence,$(raw_tests)) + +tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \ +tests/filesys/extended/child-syn-rw tests/filesys/extended/tar + +$(foreach prog,$(tests/filesys/extended_PROGS), \ + $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c)) +$(foreach prog,$(tests/filesys/extended_TESTS), \ + $(eval $(prog)_SRC += tests/main.c)) +$(foreach prog,$(tests/filesys/extended_TESTS), \ + $(eval $(prog)_PUTFILES += tests/filesys/extended/tar)) +# The version of GNU make 3.80 on vine barfs if this is split at +# the last comma. +$(foreach test,$(tests/filesys/extended_TESTS),$(eval $(test).output: FILESYSSOURCE = --disk=tmp.dsk)) + +tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c +tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c + +tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw + +tests/filesys/extended/dir-vine.output: TIMEOUT = 150 + +GETTIMEOUT = 60 + +GETCMD = pintos -v -k -T $(GETTIMEOUT) +GETCMD += $(PINTOSOPTS) +GETCMD += $(SIMULATOR) +GETCMD += $(FILESYSSOURCE) +GETCMD += -g fs.tar -a $(TEST).tar +ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm) +GETCMD += --swap-size=4 +endif +GETCMD += -- -q +GETCMD += $(KERNELFLAGS) +GETCMD += run 'tar fs.tar /' +GETCMD += < /dev/null +GETCMD += 2> $(TEST)-persistence.errors $(if $(VERBOSE),|tee,>) $(TEST)-persistence.output + +tests/filesys/extended/%.output: kernel.bin + rm -f tmp.dsk + pintos-mkdisk tmp.dsk --filesys-size=2 + $(TESTCMD) + $(GETCMD) + rm -f tmp.dsk +$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.output: tests/filesys/extended/$(raw_test).output)) +$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.result: tests/filesys/extended/$(raw_test).result)) + +TARS = $(addsuffix .tar,$(tests/filesys/extended_TESTS)) + +clean:: + rm -f $(TARS) + rm -f tests/filesys/extended/can-rmdir-cwd diff --git a/pintos-env/pintos/tests/filesys/extended/Rubric.functionality b/pintos-env/pintos/tests/filesys/extended/Rubric.functionality new file mode 100755 index 0000000..91ed6f0 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/Rubric.functionality @@ -0,0 +1,26 @@ +Functionality of extended file system: +- Test directory support. +1 dir-mkdir +3 dir-mk-tree + +1 dir-rmdir +3 dir-rm-tree + +5 dir-vine + +- Test file growth. +1 grow-create +1 grow-seq-sm +3 grow-seq-lg +3 grow-sparse +3 grow-two-files +1 grow-tell +1 grow-file-size + +- Test directory growth. +1 grow-dir-lg +1 grow-root-sm +1 grow-root-lg + +- Test writing from multiple processes. +5 syn-rw diff --git a/pintos-env/pintos/tests/filesys/extended/Rubric.persistence b/pintos-env/pintos/tests/filesys/extended/Rubric.persistence new file mode 100755 index 0000000..405620a --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/Rubric.persistence @@ -0,0 +1,24 @@ +Persistence of file system: +1 dir-empty-name-persistence +1 dir-mk-tree-persistence +1 dir-mkdir-persistence +1 dir-open-persistence +1 dir-over-file-persistence +1 dir-rm-cwd-persistence +1 dir-rm-parent-persistence +1 dir-rm-root-persistence +1 dir-rm-tree-persistence +1 dir-rmdir-persistence +1 dir-under-file-persistence +1 dir-vine-persistence +1 grow-create-persistence +1 grow-dir-lg-persistence +1 grow-file-size-persistence +1 grow-root-lg-persistence +1 grow-root-sm-persistence +1 grow-seq-lg-persistence +1 grow-seq-sm-persistence +1 grow-sparse-persistence +1 grow-tell-persistence +1 grow-two-files-persistence +1 syn-rw-persistence diff --git a/pintos-env/pintos/tests/filesys/extended/Rubric.robustness b/pintos-env/pintos/tests/filesys/extended/Rubric.robustness new file mode 100755 index 0000000..fb9f32f --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/Rubric.robustness @@ -0,0 +1,9 @@ +Robustness of file system: +1 dir-empty-name +1 dir-open +1 dir-over-file +1 dir-under-file + +3 dir-rm-cwd +2 dir-rm-parent +1 dir-rm-root diff --git a/pintos-env/pintos/tests/filesys/extended/child-syn-rw.c b/pintos-env/pintos/tests/filesys/extended/child-syn-rw.c new file mode 100755 index 0000000..0e2217d --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/child-syn-rw.c @@ -0,0 +1,53 @@ +/* Child process for syn-rw. + Reads from a file created by our parent process, which is + growing it. We loop until we've read the whole file + successfully. Many iterations through the loop will return 0 + bytes, because the file has not grown in the meantime. That + is, we are "busy waiting" for the file to grow. + (This test could be improved by adding a "yield" system call + and calling yield whenever we receive a 0-byte read.) */ + +#include +#include +#include +#include "tests/filesys/extended/syn-rw.h" +#include "tests/lib.h" + +const char *test_name = "child-syn-rw"; + +static char buf1[BUF_SIZE]; +static char buf2[BUF_SIZE]; + +int +main (int argc, const char *argv[]) +{ + int child_idx; + int fd; + size_t ofs; + + quiet = true; + + CHECK (argc == 2, "argc must be 2, actually %d", argc); + child_idx = atoi (argv[1]); + + random_init (0); + random_bytes (buf1, sizeof buf1); + + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + ofs = 0; + while (ofs < sizeof buf2) + { + int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs); + CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs), + "%zu-byte read on \"%s\" returned invalid value of %d", + sizeof buf2 - ofs, file_name, bytes_read); + if (bytes_read > 0) + { + compare_bytes (buf2 + ofs, buf1 + ofs, bytes_read, ofs, file_name); + ofs += bytes_read; + } + } + close (fd); + + return child_idx; +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-empty-name-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-empty-name-persistence.ck new file mode 100755 index 0000000..562c451 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-empty-name-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-empty-name.c b/pintos-env/pintos/tests/filesys/extended/dir-empty-name.c new file mode 100755 index 0000000..c4859d2 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-empty-name.c @@ -0,0 +1,12 @@ +/* Tries to create a directory named as the empty string, + which must return failure. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (!mkdir (""), "mkdir \"\" (must return false)"); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-empty-name.ck b/pintos-env/pintos/tests/filesys/extended/dir-empty-name.ck new file mode 100755 index 0000000..d6c5621 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-empty-name.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-empty-name) begin +(dir-empty-name) mkdir "" (must return false) +(dir-empty-name) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-mk-tree-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-mk-tree-persistence.ck new file mode 100755 index 0000000..fb16afd --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-mk-tree-persistence.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +my ($tree); +for my $a (0...3) { + for my $b (0...2) { + for my $c (0...2) { + for my $d (0...3) { + $tree->{$a}{$b}{$c}{$d} = ['']; + } + } + } +} +check_archive ($tree); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-mk-tree.c b/pintos-env/pintos/tests/filesys/extended/dir-mk-tree.c new file mode 100755 index 0000000..a714ff3 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-mk-tree.c @@ -0,0 +1,12 @@ +/* Creates directories /0/0/0 through /3/2/2 and creates files in + the leaf directories. */ + +#include "tests/filesys/extended/mk-tree.h" +#include "tests/main.h" + +void +test_main (void) +{ + make_tree (4, 3, 3, 4); +} + diff --git a/pintos-env/pintos/tests/filesys/extended/dir-mk-tree.ck b/pintos-env/pintos/tests/filesys/extended/dir-mk-tree.ck new file mode 100755 index 0000000..a8507e2 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-mk-tree.ck @@ -0,0 +1,12 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-mk-tree) begin +(dir-mk-tree) creating /0/0/0/0 through /3/2/2/3... +(dir-mk-tree) open "/0/2/0/3" +(dir-mk-tree) close "/0/2/0/3" +(dir-mk-tree) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-mkdir-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-mkdir-persistence.ck new file mode 100755 index 0000000..7682900 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-mkdir-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({'a' => {'b' => ["\0" x 512]}}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-mkdir.c b/pintos-env/pintos/tests/filesys/extended/dir-mkdir.c new file mode 100755 index 0000000..994f41c --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-mkdir.c @@ -0,0 +1,15 @@ +/* Tests mkdir(). */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (create ("a/b", 512), "create \"a/b\""); + CHECK (chdir ("a"), "chdir \"a\""); + CHECK (open ("b") > 1, "open \"b\""); +} + diff --git a/pintos-env/pintos/tests/filesys/extended/dir-mkdir.ck b/pintos-env/pintos/tests/filesys/extended/dir-mkdir.ck new file mode 100755 index 0000000..4644f80 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-mkdir.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-mkdir) begin +(dir-mkdir) mkdir "a" +(dir-mkdir) create "a/b" +(dir-mkdir) chdir "a" +(dir-mkdir) open "b" +(dir-mkdir) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-open-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-open-persistence.ck new file mode 100755 index 0000000..26ff2f1 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-open-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({"xyzzy" => {}}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-open.c b/pintos-env/pintos/tests/filesys/extended/dir-open.c new file mode 100755 index 0000000..29d18b8 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-open.c @@ -0,0 +1,21 @@ +/* Opens a directory, then tries to write to it, which must + fail. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int fd; + int retval; + + CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\""); + CHECK ((fd = open ("xyzzy")) > 1, "open \"xyzzy\""); + + msg ("write \"xyzzy\""); + retval = write (fd, "foobar", 6); + CHECK (retval == -1, + "write \"xyzzy\" (must return -1, actually %d)", retval); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-open.ck b/pintos-env/pintos/tests/filesys/extended/dir-open.ck new file mode 100755 index 0000000..fccc563 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-open.ck @@ -0,0 +1,20 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(dir-open) begin +(dir-open) mkdir "xyzzy" +(dir-open) open "xyzzy" +(dir-open) write "xyzzy" +(dir-open) write "xyzzy" (must return -1, actually -1) +(dir-open) end +dir-open: exit(0) +EOF +(dir-open) begin +(dir-open) mkdir "xyzzy" +(dir-open) open "xyzzy" +(dir-open) write "xyzzy" +dir-open: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-over-file-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-over-file-persistence.ck new file mode 100755 index 0000000..56b4ed2 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-over-file-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({"abc" => {}}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-over-file.c b/pintos-env/pintos/tests/filesys/extended/dir-over-file.c new file mode 100755 index 0000000..cdd2c62 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-over-file.c @@ -0,0 +1,13 @@ +/* Tries to create a file with the same name as an existing + directory, which must return failure. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (mkdir ("abc"), "mkdir \"abc\""); + CHECK (!create ("abc", 0), "create \"abc\" (must return false)"); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-over-file.ck b/pintos-env/pintos/tests/filesys/extended/dir-over-file.ck new file mode 100755 index 0000000..aae1c1e --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-over-file.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-over-file) begin +(dir-over-file) mkdir "abc" +(dir-over-file) create "abc" (must return false) +(dir-over-file) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd-persistence.ck new file mode 100755 index 0000000..7533570 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd-persistence.ck @@ -0,0 +1,8 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +my ($cwd_removable) = read_text_file ("tests/filesys/extended/can-rmdir-cwd"); +$cwd_removable eq 'YES' || $cwd_removable eq 'NO' or die; +check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd.c b/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd.c new file mode 100755 index 0000000..78e13de --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd.c @@ -0,0 +1,75 @@ +/* Tries to remove the current directory, which may succeed or + fail. The requirements in each case are different; refer to + the assignment for details. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +static int +wrap_open (const char *name) +{ + static int fds[8], fd_cnt; + int fd, i; + + CHECK ((fd = open (name)) > 1, "open \"%s\"", name); + for (i = 0; i < fd_cnt; i++) + if (fds[i] == fd) + fail ("fd returned is not unique"); + fds[fd_cnt++] = fd; + return fd; +} + +void +test_main (void) +{ + int root_fd, a_fd0; + char name[READDIR_MAX_LEN + 1]; + + root_fd = wrap_open ("/"); + CHECK (mkdir ("a"), "mkdir \"a\""); + + a_fd0 = wrap_open ("/a"); + CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty"); + CHECK (inumber (root_fd) != inumber (a_fd0), + "\"/\" and \"/a\" must have different inumbers"); + + CHECK (chdir ("a"), "chdir \"a\""); + + msg ("try to remove \"/a\""); + if (remove ("/a")) + { + msg ("remove successful"); + + CHECK (open ("/a") == -1, "open \"/a\" (must fail)"); + CHECK (open (".") == -1, "open \".\" (must fail)"); + CHECK (open ("..") == -1, "open \"..\" (must fail)"); + CHECK (!create ("x", 512), "create \"x\" (must fail)"); + } + else + { + int a_fd1, a_fd2, a_fd3; + + msg ("remove failed"); + + CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)"); + CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)"); + CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)"); + + a_fd1 = wrap_open ("/a"); + a_fd2 = wrap_open ("."); + CHECK (inumber (a_fd1) == inumber (a_fd2), + "\"/a\" and \".\" must have same inumber"); + CHECK (inumber (root_fd) != inumber (a_fd1), + "\"/\" and \"/a\" must have different inumbers"); + + CHECK (chdir ("/a"), "chdir \"/a\""); + a_fd3 = wrap_open ("."); + CHECK (inumber (a_fd3) == inumber (a_fd1), + "\".\" must have same inumber as before"); + + CHECK (chdir ("/"), "chdir \"/\""); + CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)"); + } + CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty"); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd.ck new file mode 100755 index 0000000..6fa4739 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-cwd.ck @@ -0,0 +1,51 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1, + {NO => <<'EOF', YES => <<'EOF'}); +(dir-rm-cwd) begin +(dir-rm-cwd) open "/" +(dir-rm-cwd) mkdir "a" +(dir-rm-cwd) open "/a" +(dir-rm-cwd) verify "/a" is empty +(dir-rm-cwd) "/" and "/a" must have different inumbers +(dir-rm-cwd) chdir "a" +(dir-rm-cwd) try to remove "/a" +(dir-rm-cwd) remove failed +(dir-rm-cwd) try to remove "../a" (must fail) +(dir-rm-cwd) try to remove ".././a" (must fail) +(dir-rm-cwd) try to remove "/./a" (must fail) +(dir-rm-cwd) open "/a" +(dir-rm-cwd) open "." +(dir-rm-cwd) "/a" and "." must have same inumber +(dir-rm-cwd) "/" and "/a" must have different inumbers +(dir-rm-cwd) chdir "/a" +(dir-rm-cwd) open "." +(dir-rm-cwd) "." must have same inumber as before +(dir-rm-cwd) chdir "/" +(dir-rm-cwd) try to remove "a" (must fail: still open) +(dir-rm-cwd) verify "/a" is empty +(dir-rm-cwd) end +EOF +(dir-rm-cwd) begin +(dir-rm-cwd) open "/" +(dir-rm-cwd) mkdir "a" +(dir-rm-cwd) open "/a" +(dir-rm-cwd) verify "/a" is empty +(dir-rm-cwd) "/" and "/a" must have different inumbers +(dir-rm-cwd) chdir "a" +(dir-rm-cwd) try to remove "/a" +(dir-rm-cwd) remove successful +(dir-rm-cwd) open "/a" (must fail) +(dir-rm-cwd) open "." (must fail) +(dir-rm-cwd) open ".." (must fail) +(dir-rm-cwd) create "x" (must fail) +(dir-rm-cwd) verify "/a" is empty +(dir-rm-cwd) end +EOF +open (CAN_RMDIR_CWD, ">tests/filesys/extended/can-rmdir-cwd") + or die "tests/filesys/extended/can-rmdir-cwd: create: $!\n"; +print CAN_RMDIR_CWD "$cwd_removable"; +close (CAN_RMDIR_CWD); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-parent-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-parent-persistence.ck new file mode 100755 index 0000000..f30b04a --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-parent-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({"a" => {"b" => {}}}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-parent.c b/pintos-env/pintos/tests/filesys/extended/dir-rm-parent.c new file mode 100755 index 0000000..eb43f5b --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-parent.c @@ -0,0 +1,16 @@ +/* Tries to remove a parent of the current directory. This must + fail, because that directory is non-empty. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (chdir ("a"), "chdir \"a\""); + CHECK (mkdir ("b"), "mkdir \"b\""); + CHECK (chdir ("b"), "chdir \"b\""); + CHECK (!remove ("/a"), "remove \"/a\" (must fail)"); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-parent.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-parent.ck new file mode 100755 index 0000000..9fea8f2 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-parent.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-rm-parent) begin +(dir-rm-parent) mkdir "a" +(dir-rm-parent) chdir "a" +(dir-rm-parent) mkdir "b" +(dir-rm-parent) chdir "b" +(dir-rm-parent) remove "/a" (must fail) +(dir-rm-parent) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-root-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-root-persistence.ck new file mode 100755 index 0000000..6315107 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-root-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({"a" => ["\0" x 243]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-root.c b/pintos-env/pintos/tests/filesys/extended/dir-rm-root.c new file mode 100755 index 0000000..c47f1eb --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-root.c @@ -0,0 +1,13 @@ +/* Try to remove the root directory. + This must fail. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (!remove ("/"), "remove \"/\" (must fail)"); + CHECK (create ("/a", 243), "create \"/a\""); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-root.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-root.ck new file mode 100755 index 0000000..8a69ff3 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-root.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-rm-root) begin +(dir-rm-root) remove "/" (must fail) +(dir-rm-root) create "/a" +(dir-rm-root) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-tree-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-tree-persistence.ck new file mode 100755 index 0000000..562c451 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-tree-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-tree.c b/pintos-env/pintos/tests/filesys/extended/dir-rm-tree.c new file mode 100755 index 0000000..bab41a6 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-tree.c @@ -0,0 +1,62 @@ +/* Creates directories /0/0/0 through /3/2/2 and files in the + leaf directories, then removes them. */ + +#include +#include +#include +#include "tests/filesys/extended/mk-tree.h" +#include "tests/lib.h" +#include "tests/main.h" + +static void remove_tree (int at, int bt, int ct, int dt); + +void +test_main (void) +{ + make_tree (4, 3, 3, 4); + remove_tree (4, 3, 3, 4); +} + +static void do_remove (const char *format, ...) PRINTF_FORMAT (1, 2); + +static void +remove_tree (int at, int bt, int ct, int dt) +{ + char try[128]; + int a, b, c, d; + + msg ("removing /0/0/0/0 through /%d/%d/%d/%d...", + at - 1, bt - 1, ct - 1, dt - 1); + quiet = true; + for (a = 0; a < at; a++) + { + for (b = 0; b < bt; b++) + { + for (c = 0; c < ct; c++) + { + for (d = 0; d < dt; d++) + do_remove ("/%d/%d/%d/%d", a, b, c, d); + do_remove ("/%d/%d/%d", a, b, c); + } + do_remove ("/%d/%d", a, b); + } + do_remove ("/%d", a); + } + quiet = false; + + snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0); + CHECK (open (try) == -1, "open \"%s\" (must return -1)", try); +} + +static void +do_remove (const char *format, ...) +{ + char name[128]; + va_list args; + + va_start (args, format); + vsnprintf (name, sizeof name, format, args); + va_end (args); + + CHECK (remove (name), "remove \"%s\"", name); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rm-tree.ck b/pintos-env/pintos/tests/filesys/extended/dir-rm-tree.ck new file mode 100755 index 0000000..587b493 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rm-tree.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-rm-tree) begin +(dir-rm-tree) creating /0/0/0/0 through /3/2/2/3... +(dir-rm-tree) open "/0/2/0/3" +(dir-rm-tree) close "/0/2/0/3" +(dir-rm-tree) removing /0/0/0/0 through /3/2/2/3... +(dir-rm-tree) open "/3/0/2/0" (must return -1) +(dir-rm-tree) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rmdir-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-rmdir-persistence.ck new file mode 100755 index 0000000..562c451 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rmdir-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rmdir.c b/pintos-env/pintos/tests/filesys/extended/dir-rmdir.c new file mode 100755 index 0000000..b3cbc6f --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rmdir.c @@ -0,0 +1,14 @@ +/* Creates and removes a directory, then makes sure that it's + really gone. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (mkdir ("a"), "mkdir \"a\""); + CHECK (remove ("a"), "rmdir \"a\""); + CHECK (!chdir ("a"), "chdir \"a\" (must return false)"); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-rmdir.ck b/pintos-env/pintos/tests/filesys/extended/dir-rmdir.ck new file mode 100755 index 0000000..e0d8922 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-rmdir.ck @@ -0,0 +1,12 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-rmdir) begin +(dir-rmdir) mkdir "a" +(dir-rmdir) rmdir "a" +(dir-rmdir) chdir "a" (must return false) +(dir-rmdir) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-under-file-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-under-file-persistence.ck new file mode 100755 index 0000000..67ca528 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-under-file-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({"abc" => ['']}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-under-file.c b/pintos-env/pintos/tests/filesys/extended/dir-under-file.c new file mode 100755 index 0000000..973a8b1 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-under-file.c @@ -0,0 +1,13 @@ +/* Tries to create a directory with the same name as an existing + file, which must return failure. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (create ("abc", 0), "create \"abc\""); + CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)"); +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-under-file.ck b/pintos-env/pintos/tests/filesys/extended/dir-under-file.ck new file mode 100755 index 0000000..cce23b4 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-under-file.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-under-file) begin +(dir-under-file) create "abc" +(dir-under-file) mkdir "abc" (must return false) +(dir-under-file) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-vine-persistence.ck b/pintos-env/pintos/tests/filesys/extended/dir-vine-persistence.ck new file mode 100755 index 0000000..698ef01 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-vine-persistence.ck @@ -0,0 +1,37 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +# The archive should look like this: +# +# 40642 dir-vine +# 42479 tar +# 0 start +# 11 start/file0 +# 0 start/dir0 +# 11 start/dir0/file1 +# 0 start/dir0/dir1 +# 11 start/dir0/dir1/file2 +# 0 start/dir0/dir1/dir2 +# 11 start/dir0/dir1/dir2/file3 +# 0 start/dir0/dir1/dir2/dir3 +# 11 start/dir0/dir1/dir2/dir3/file4 +# 0 start/dir0/dir1/dir2/dir3/dir4 +# 11 start/dir0/dir1/dir2/dir3/dir4/file5 +# 0 start/dir0/dir1/dir2/dir3/dir4/dir5 +# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/file6 +# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6 +# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7 +# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7 +# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8 +# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8 +# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9 +# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9 +my ($dir) = {}; +my ($root) = {"start" => $dir}; +for (my ($i) = 0; $i < 10; $i++) { + $dir->{"file$i"} = ["contents $i\n"]; + $dir = $dir->{"dir$i"} = {}; +} +check_archive ($root); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/dir-vine.c b/pintos-env/pintos/tests/filesys/extended/dir-vine.c new file mode 100755 index 0000000..8a31c38 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-vine.c @@ -0,0 +1,85 @@ +/* Create a very deep "vine" of directories: /dir0/dir1/dir2/... + and an ordinary file in each of them, until we fill up the + disk. + + Then delete most of them, for two reasons. First, "tar" + limits file names to 100 characters (which could be extended + to 256 without much trouble). Second, a full disk has no room + for the tar archive. */ + +#include +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int i; + + msg ("creating many levels of files and directories..."); + quiet = true; + CHECK (mkdir ("start"), "mkdir \"start\""); + CHECK (chdir ("start"), "chdir \"start\""); + for (i = 0; ; i++) + { + char name[3][READDIR_MAX_LEN + 1]; + char file_name[16], dir_name[16]; + char contents[128]; + int fd; + + /* Create file. */ + snprintf (file_name, sizeof file_name, "file%d", i); + if (!create (file_name, 0)) + break; + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + snprintf (contents, sizeof contents, "contents %d\n", i); + if (write (fd, contents, strlen (contents)) != (int) strlen (contents)) + { + CHECK (remove (file_name), "remove \"%s\"", file_name); + close (fd); + break; + } + close (fd); + + /* Create directory. */ + snprintf (dir_name, sizeof dir_name, "dir%d", i); + if (!mkdir (dir_name)) + { + CHECK (remove (file_name), "remove \"%s\"", file_name); + break; + } + + /* Check for file and directory. */ + CHECK ((fd = open (".")) > 1, "open \".\""); + CHECK (readdir (fd, name[0]), "readdir \".\""); + CHECK (readdir (fd, name[1]), "readdir \".\""); + CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)"); + CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name)) + || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)), + "names should be \"%s\" and \"%s\", " + "actually \"%s\" and \"%s\"", + file_name, dir_name, name[0], name[1]); + close (fd); + + /* Descend into directory. */ + CHECK (chdir (dir_name), "chdir \"%s\"", dir_name); + } + CHECK (i > 200, "created files and directories only to level %d", i); + quiet = false; + + msg ("removing all but top 10 levels of files and directories..."); + quiet = true; + while (i-- > 10) + { + char file_name[16], dir_name[16]; + + snprintf (file_name, sizeof file_name, "file%d", i); + snprintf (dir_name, sizeof dir_name, "dir%d", i); + CHECK (chdir (".."), "chdir \"..\""); + CHECK (remove (dir_name), "remove \"%s\"", dir_name); + CHECK (remove (file_name), "remove \"%s\"", file_name); + } + quiet = false; +} diff --git a/pintos-env/pintos/tests/filesys/extended/dir-vine.ck b/pintos-env/pintos/tests/filesys/extended/dir-vine.ck new file mode 100755 index 0000000..db452b0 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/dir-vine.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(dir-vine) begin +(dir-vine) creating many levels of files and directories... +(dir-vine) removing all but top 10 levels of files and directories... +(dir-vine) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-create-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-create-persistence.ck new file mode 100755 index 0000000..bbcb24f --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-create-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({"blargle" => ['']}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-create.c b/pintos-env/pintos/tests/filesys/extended/grow-create.c new file mode 100755 index 0000000..9ccc4ea --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-create.c @@ -0,0 +1,4 @@ +/* Create a file of size 0. */ + +#define TEST_SIZE 0 +#include "tests/filesys/create.inc" diff --git a/pintos-env/pintos/tests/filesys/extended/grow-create.ck b/pintos-env/pintos/tests/filesys/extended/grow-create.ck new file mode 100755 index 0000000..b2e69d1 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-create.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-create) begin +(grow-create) create "blargle" +(grow-create) open "blargle" for verification +(grow-create) verified contents of "blargle" +(grow-create) close "blargle" +(grow-create) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-dir-lg-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-dir-lg-persistence.ck new file mode 100755 index 0000000..989a322 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-dir-lg-persistence.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +my ($fs); +$fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49; +check_archive ($fs); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-dir-lg.c b/pintos-env/pintos/tests/filesys/extended/grow-dir-lg.c new file mode 100755 index 0000000..20a194b --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-dir-lg.c @@ -0,0 +1,6 @@ +/* Creates a directory, + then creates 50 files in that directory. */ + +#define FILE_CNT 50 +#define DIRECTORY "/x" +#include "tests/filesys/extended/grow-dir.inc" diff --git a/pintos-env/pintos/tests/filesys/extended/grow-dir-lg.ck b/pintos-env/pintos/tests/filesys/extended/grow-dir-lg.ck new file mode 100755 index 0000000..ec58bd3 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-dir-lg.ck @@ -0,0 +1,61 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-dir-lg) begin +(grow-dir-lg) mkdir /x +(grow-dir-lg) creating and checking "/x/file0" +(grow-dir-lg) creating and checking "/x/file1" +(grow-dir-lg) creating and checking "/x/file2" +(grow-dir-lg) creating and checking "/x/file3" +(grow-dir-lg) creating and checking "/x/file4" +(grow-dir-lg) creating and checking "/x/file5" +(grow-dir-lg) creating and checking "/x/file6" +(grow-dir-lg) creating and checking "/x/file7" +(grow-dir-lg) creating and checking "/x/file8" +(grow-dir-lg) creating and checking "/x/file9" +(grow-dir-lg) creating and checking "/x/file10" +(grow-dir-lg) creating and checking "/x/file11" +(grow-dir-lg) creating and checking "/x/file12" +(grow-dir-lg) creating and checking "/x/file13" +(grow-dir-lg) creating and checking "/x/file14" +(grow-dir-lg) creating and checking "/x/file15" +(grow-dir-lg) creating and checking "/x/file16" +(grow-dir-lg) creating and checking "/x/file17" +(grow-dir-lg) creating and checking "/x/file18" +(grow-dir-lg) creating and checking "/x/file19" +(grow-dir-lg) creating and checking "/x/file20" +(grow-dir-lg) creating and checking "/x/file21" +(grow-dir-lg) creating and checking "/x/file22" +(grow-dir-lg) creating and checking "/x/file23" +(grow-dir-lg) creating and checking "/x/file24" +(grow-dir-lg) creating and checking "/x/file25" +(grow-dir-lg) creating and checking "/x/file26" +(grow-dir-lg) creating and checking "/x/file27" +(grow-dir-lg) creating and checking "/x/file28" +(grow-dir-lg) creating and checking "/x/file29" +(grow-dir-lg) creating and checking "/x/file30" +(grow-dir-lg) creating and checking "/x/file31" +(grow-dir-lg) creating and checking "/x/file32" +(grow-dir-lg) creating and checking "/x/file33" +(grow-dir-lg) creating and checking "/x/file34" +(grow-dir-lg) creating and checking "/x/file35" +(grow-dir-lg) creating and checking "/x/file36" +(grow-dir-lg) creating and checking "/x/file37" +(grow-dir-lg) creating and checking "/x/file38" +(grow-dir-lg) creating and checking "/x/file39" +(grow-dir-lg) creating and checking "/x/file40" +(grow-dir-lg) creating and checking "/x/file41" +(grow-dir-lg) creating and checking "/x/file42" +(grow-dir-lg) creating and checking "/x/file43" +(grow-dir-lg) creating and checking "/x/file44" +(grow-dir-lg) creating and checking "/x/file45" +(grow-dir-lg) creating and checking "/x/file46" +(grow-dir-lg) creating and checking "/x/file47" +(grow-dir-lg) creating and checking "/x/file48" +(grow-dir-lg) creating and checking "/x/file49" +(grow-dir-lg) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-dir.inc b/pintos-env/pintos/tests/filesys/extended/grow-dir.inc new file mode 100755 index 0000000..bee0ba0 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-dir.inc @@ -0,0 +1,41 @@ +/* -*- c -*- */ + +#include +#include +#include "tests/filesys/seq-test.h" +#include "tests/lib.h" +#include "tests/main.h" + +static char buf[512]; + +static size_t +return_block_size (void) +{ + return sizeof buf; +} + +void +test_main (void) +{ + size_t i; + +#ifdef DIRECTORY + CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY); +#define DIR_PREFIX DIRECTORY "/" +#else +#define DIR_PREFIX "" +#endif + for (i = 0; i < FILE_CNT; i++) + { + char file_name[128]; + snprintf (file_name, sizeof file_name, "%sfile%zu", DIR_PREFIX, i); + + msg ("creating and checking \"%s\"", file_name); + + quiet = true; + seq_test (file_name, + buf, sizeof buf, sizeof buf, + return_block_size, NULL); + quiet = false; + } +} diff --git a/pintos-env/pintos/tests/filesys/extended/grow-file-size-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-file-size-persistence.ck new file mode 100755 index 0000000..150f383 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-file-size-persistence.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_archive ({"testfile" => [random_bytes (2134)]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-file-size.c b/pintos-env/pintos/tests/filesys/extended/grow-file-size.c new file mode 100755 index 0000000..3ce8588 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-file-size.c @@ -0,0 +1,33 @@ +/* Grows a file from 0 bytes to 2,134 bytes, 37 bytes at a time, + and checks that the file's size is reported correctly at each + step. */ + +#include +#include "tests/filesys/seq-test.h" +#include "tests/lib.h" +#include "tests/main.h" + +static char buf[2134]; + +static size_t +return_block_size (void) +{ + return 37; +} + +static void +check_file_size (int fd, long ofs) +{ + long size = filesize (fd); + if (size != ofs) + fail ("filesize not updated properly: should be %ld, actually %ld", + ofs, size); +} + +void +test_main (void) +{ + seq_test ("testfile", + buf, sizeof buf, 0, + return_block_size, check_file_size); +} diff --git a/pintos-env/pintos/tests/filesys/extended/grow-file-size.ck b/pintos-env/pintos/tests/filesys/extended/grow-file-size.ck new file mode 100755 index 0000000..d81feff --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-file-size.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-file-size) begin +(grow-file-size) create "testfile" +(grow-file-size) open "testfile" +(grow-file-size) writing "testfile" +(grow-file-size) close "testfile" +(grow-file-size) open "testfile" for verification +(grow-file-size) verified contents of "testfile" +(grow-file-size) close "testfile" +(grow-file-size) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-root-lg-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-root-lg-persistence.ck new file mode 100755 index 0000000..1692f46 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-root-lg-persistence.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +my ($fs); +$fs->{"file$_"} = [random_bytes (512)] foreach 0...49; +check_archive ($fs); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-root-lg.c b/pintos-env/pintos/tests/filesys/extended/grow-root-lg.c new file mode 100755 index 0000000..d8d6c09 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-root-lg.c @@ -0,0 +1,4 @@ +/* Creates 50 files in the root directory. */ + +#define FILE_CNT 50 +#include "tests/filesys/extended/grow-dir.inc" diff --git a/pintos-env/pintos/tests/filesys/extended/grow-root-lg.ck b/pintos-env/pintos/tests/filesys/extended/grow-root-lg.ck new file mode 100755 index 0000000..b174bc9 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-root-lg.ck @@ -0,0 +1,60 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-root-lg) begin +(grow-root-lg) creating and checking "file0" +(grow-root-lg) creating and checking "file1" +(grow-root-lg) creating and checking "file2" +(grow-root-lg) creating and checking "file3" +(grow-root-lg) creating and checking "file4" +(grow-root-lg) creating and checking "file5" +(grow-root-lg) creating and checking "file6" +(grow-root-lg) creating and checking "file7" +(grow-root-lg) creating and checking "file8" +(grow-root-lg) creating and checking "file9" +(grow-root-lg) creating and checking "file10" +(grow-root-lg) creating and checking "file11" +(grow-root-lg) creating and checking "file12" +(grow-root-lg) creating and checking "file13" +(grow-root-lg) creating and checking "file14" +(grow-root-lg) creating and checking "file15" +(grow-root-lg) creating and checking "file16" +(grow-root-lg) creating and checking "file17" +(grow-root-lg) creating and checking "file18" +(grow-root-lg) creating and checking "file19" +(grow-root-lg) creating and checking "file20" +(grow-root-lg) creating and checking "file21" +(grow-root-lg) creating and checking "file22" +(grow-root-lg) creating and checking "file23" +(grow-root-lg) creating and checking "file24" +(grow-root-lg) creating and checking "file25" +(grow-root-lg) creating and checking "file26" +(grow-root-lg) creating and checking "file27" +(grow-root-lg) creating and checking "file28" +(grow-root-lg) creating and checking "file29" +(grow-root-lg) creating and checking "file30" +(grow-root-lg) creating and checking "file31" +(grow-root-lg) creating and checking "file32" +(grow-root-lg) creating and checking "file33" +(grow-root-lg) creating and checking "file34" +(grow-root-lg) creating and checking "file35" +(grow-root-lg) creating and checking "file36" +(grow-root-lg) creating and checking "file37" +(grow-root-lg) creating and checking "file38" +(grow-root-lg) creating and checking "file39" +(grow-root-lg) creating and checking "file40" +(grow-root-lg) creating and checking "file41" +(grow-root-lg) creating and checking "file42" +(grow-root-lg) creating and checking "file43" +(grow-root-lg) creating and checking "file44" +(grow-root-lg) creating and checking "file45" +(grow-root-lg) creating and checking "file46" +(grow-root-lg) creating and checking "file47" +(grow-root-lg) creating and checking "file48" +(grow-root-lg) creating and checking "file49" +(grow-root-lg) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-root-sm-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-root-sm-persistence.ck new file mode 100755 index 0000000..2b0b8ab --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-root-sm-persistence.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +my ($fs); +$fs->{"file$_"} = [random_bytes (512)] foreach 0...19; +check_archive ($fs); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-root-sm.c b/pintos-env/pintos/tests/filesys/extended/grow-root-sm.c new file mode 100755 index 0000000..ee375d5 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-root-sm.c @@ -0,0 +1,4 @@ +/* Creates 20 files in the root directory. */ + +#define FILE_CNT 20 +#include "tests/filesys/extended/grow-dir.inc" diff --git a/pintos-env/pintos/tests/filesys/extended/grow-root-sm.ck b/pintos-env/pintos/tests/filesys/extended/grow-root-sm.ck new file mode 100755 index 0000000..1aac7e9 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-root-sm.ck @@ -0,0 +1,30 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-root-sm) begin +(grow-root-sm) creating and checking "file0" +(grow-root-sm) creating and checking "file1" +(grow-root-sm) creating and checking "file2" +(grow-root-sm) creating and checking "file3" +(grow-root-sm) creating and checking "file4" +(grow-root-sm) creating and checking "file5" +(grow-root-sm) creating and checking "file6" +(grow-root-sm) creating and checking "file7" +(grow-root-sm) creating and checking "file8" +(grow-root-sm) creating and checking "file9" +(grow-root-sm) creating and checking "file10" +(grow-root-sm) creating and checking "file11" +(grow-root-sm) creating and checking "file12" +(grow-root-sm) creating and checking "file13" +(grow-root-sm) creating and checking "file14" +(grow-root-sm) creating and checking "file15" +(grow-root-sm) creating and checking "file16" +(grow-root-sm) creating and checking "file17" +(grow-root-sm) creating and checking "file18" +(grow-root-sm) creating and checking "file19" +(grow-root-sm) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-seq-lg-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-seq-lg-persistence.ck new file mode 100755 index 0000000..41aaae0 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-seq-lg-persistence.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_archive ({"testme" => [random_bytes (72943)]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-seq-lg.c b/pintos-env/pintos/tests/filesys/extended/grow-seq-lg.c new file mode 100755 index 0000000..3108d17 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-seq-lg.c @@ -0,0 +1,5 @@ +/* Grows a file from 0 bytes to 72,943 bytes, 1,234 bytes at a + time. */ + +#define TEST_SIZE 72943 +#include "tests/filesys/extended/grow-seq.inc" diff --git a/pintos-env/pintos/tests/filesys/extended/grow-seq-lg.ck b/pintos-env/pintos/tests/filesys/extended/grow-seq-lg.ck new file mode 100755 index 0000000..90fcd8c --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-seq-lg.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-seq-lg) begin +(grow-seq-lg) create "testme" +(grow-seq-lg) open "testme" +(grow-seq-lg) writing "testme" +(grow-seq-lg) close "testme" +(grow-seq-lg) open "testme" for verification +(grow-seq-lg) verified contents of "testme" +(grow-seq-lg) close "testme" +(grow-seq-lg) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-seq-sm-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-seq-sm-persistence.ck new file mode 100755 index 0000000..6cb0bd8 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-seq-sm-persistence.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_archive ({"testme" => [random_bytes (5678)]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-seq-sm.c b/pintos-env/pintos/tests/filesys/extended/grow-seq-sm.c new file mode 100755 index 0000000..3656e2e --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-seq-sm.c @@ -0,0 +1,5 @@ +/* Grows a file from 0 bytes to 5,678 bytes, 1,234 bytes at a + time. */ + +#define TEST_SIZE 5678 +#include "tests/filesys/extended/grow-seq.inc" diff --git a/pintos-env/pintos/tests/filesys/extended/grow-seq-sm.ck b/pintos-env/pintos/tests/filesys/extended/grow-seq-sm.ck new file mode 100755 index 0000000..5cf4518 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-seq-sm.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-seq-sm) begin +(grow-seq-sm) create "testme" +(grow-seq-sm) open "testme" +(grow-seq-sm) writing "testme" +(grow-seq-sm) close "testme" +(grow-seq-sm) open "testme" for verification +(grow-seq-sm) verified contents of "testme" +(grow-seq-sm) close "testme" +(grow-seq-sm) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-seq.inc b/pintos-env/pintos/tests/filesys/extended/grow-seq.inc new file mode 100755 index 0000000..1b7710c --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-seq.inc @@ -0,0 +1,20 @@ +/* -*- c -*- */ + +#include "tests/filesys/seq-test.h" +#include "tests/main.h" + +static char buf[TEST_SIZE]; + +static size_t +return_block_size (void) +{ + return 1234; +} + +void +test_main (void) +{ + seq_test ("testme", + buf, sizeof buf, 0, + return_block_size, NULL); +} diff --git a/pintos-env/pintos/tests/filesys/extended/grow-sparse-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-sparse-persistence.ck new file mode 100755 index 0000000..3f06a5b --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-sparse-persistence.ck @@ -0,0 +1,6 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_archive ({"testfile" => ["\0" x 76543]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-sparse.c b/pintos-env/pintos/tests/filesys/extended/grow-sparse.c new file mode 100755 index 0000000..6eab210 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-sparse.c @@ -0,0 +1,25 @@ +/* Tests that seeking past the end of a file and writing will + properly zero out the region in between. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +static char buf[76543]; + +void +test_main (void) +{ + const char *file_name = "testfile"; + char zero = 0; + int fd; + + CHECK (create (file_name, 0), "create \"%s\"", file_name); + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + msg ("seek \"%s\"", file_name); + seek (fd, sizeof buf - 1); + CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", file_name); + msg ("close \"%s\"", file_name); + close (fd); + check_file (file_name, buf, sizeof buf); +} diff --git a/pintos-env/pintos/tests/filesys/extended/grow-sparse.ck b/pintos-env/pintos/tests/filesys/extended/grow-sparse.ck new file mode 100755 index 0000000..379ba2c --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-sparse.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-sparse) begin +(grow-sparse) create "testfile" +(grow-sparse) open "testfile" +(grow-sparse) seek "testfile" +(grow-sparse) write "testfile" +(grow-sparse) close "testfile" +(grow-sparse) open "testfile" for verification +(grow-sparse) verified contents of "testfile" +(grow-sparse) close "testfile" +(grow-sparse) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-tell-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-tell-persistence.ck new file mode 100755 index 0000000..d93a422 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-tell-persistence.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_archive ({"foobar" => [random_bytes (2134)]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-tell.c b/pintos-env/pintos/tests/filesys/extended/grow-tell.c new file mode 100755 index 0000000..5f5da5b --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-tell.c @@ -0,0 +1,32 @@ +/* Checks that growing a file updates the file position + correctly. */ + +#include +#include "tests/filesys/seq-test.h" +#include "tests/lib.h" +#include "tests/main.h" + +static char buf[2134]; + +static size_t +return_block_size (void) +{ + return 37; +} + +static void +check_tell (int fd, long ofs) +{ + long pos = tell (fd); + if (pos != ofs) + fail ("file position not updated properly: should be %ld, actually %ld", + ofs, pos); +} + +void +test_main (void) +{ + seq_test ("foobar", + buf, sizeof buf, 0, + return_block_size, check_tell); +} diff --git a/pintos-env/pintos/tests/filesys/extended/grow-tell.ck b/pintos-env/pintos/tests/filesys/extended/grow-tell.ck new file mode 100755 index 0000000..fe94707 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-tell.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-tell) begin +(grow-tell) create "foobar" +(grow-tell) open "foobar" +(grow-tell) writing "foobar" +(grow-tell) close "foobar" +(grow-tell) open "foobar" for verification +(grow-tell) verified contents of "foobar" +(grow-tell) close "foobar" +(grow-tell) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-two-files-persistence.ck b/pintos-env/pintos/tests/filesys/extended/grow-two-files-persistence.ck new file mode 100755 index 0000000..1c4ced1 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-two-files-persistence.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +my ($a) = random_bytes (8143); +my ($b) = random_bytes (8143); +check_archive ({"a" => [$a], "b" => [$b]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/grow-two-files.c b/pintos-env/pintos/tests/filesys/extended/grow-two-files.c new file mode 100755 index 0000000..6a8fb1c --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-two-files.c @@ -0,0 +1,62 @@ +/* Grows two files in parallel and checks that their contents are + correct. */ + +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +#define FILE_SIZE 8143 +static char buf_a[FILE_SIZE]; +static char buf_b[FILE_SIZE]; + +static void +write_some_bytes (const char *file_name, int fd, const char *buf, size_t *ofs) +{ + if (*ofs < FILE_SIZE) + { + size_t block_size = random_ulong () % (FILE_SIZE / 8) + 1; + size_t ret_val; + if (block_size > FILE_SIZE - *ofs) + block_size = FILE_SIZE - *ofs; + + ret_val = write (fd, buf + *ofs, block_size); + if (ret_val != block_size) + fail ("write %zu bytes at offset %zu in \"%s\" returned %zu", + block_size, *ofs, file_name, ret_val); + *ofs += block_size; + } +} + +void +test_main (void) +{ + int fd_a, fd_b; + size_t ofs_a = 0, ofs_b = 0; + + random_init (0); + random_bytes (buf_a, sizeof buf_a); + random_bytes (buf_b, sizeof buf_b); + + CHECK (create ("a", 0), "create \"a\""); + CHECK (create ("b", 0), "create \"b\""); + + CHECK ((fd_a = open ("a")) > 1, "open \"a\""); + CHECK ((fd_b = open ("b")) > 1, "open \"b\""); + + msg ("write \"a\" and \"b\" alternately"); + while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE) + { + write_some_bytes ("a", fd_a, buf_a, &ofs_a); + write_some_bytes ("b", fd_b, buf_b, &ofs_b); + } + + msg ("close \"a\""); + close (fd_a); + + msg ("close \"b\""); + close (fd_b); + + check_file ("a", buf_a, FILE_SIZE); + check_file ("b", buf_b, FILE_SIZE); +} diff --git a/pintos-env/pintos/tests/filesys/extended/grow-two-files.ck b/pintos-env/pintos/tests/filesys/extended/grow-two-files.ck new file mode 100755 index 0000000..b5e754a --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/grow-two-files.ck @@ -0,0 +1,23 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(grow-two-files) begin +(grow-two-files) create "a" +(grow-two-files) create "b" +(grow-two-files) open "a" +(grow-two-files) open "b" +(grow-two-files) write "a" and "b" alternately +(grow-two-files) close "a" +(grow-two-files) close "b" +(grow-two-files) open "a" for verification +(grow-two-files) verified contents of "a" +(grow-two-files) close "a" +(grow-two-files) open "b" for verification +(grow-two-files) verified contents of "b" +(grow-two-files) close "b" +(grow-two-files) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/mk-tree.c b/pintos-env/pintos/tests/filesys/extended/mk-tree.c new file mode 100755 index 0000000..a36bb88 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/mk-tree.c @@ -0,0 +1,67 @@ +/* Library function for creating a tree of directories. */ + +#include +#include +#include "tests/filesys/extended/mk-tree.h" +#include "tests/lib.h" + +static void do_mkdir (const char *format, ...) PRINTF_FORMAT (1, 2); +static void do_touch (const char *format, ...) PRINTF_FORMAT (1, 2); + +void +make_tree (int at, int bt, int ct, int dt) +{ + char try[128]; + int a, b, c, d; + int fd; + + msg ("creating /0/0/0/0 through /%d/%d/%d/%d...", + at - 1, bt - 1, ct - 1, dt - 1); + quiet = true; + for (a = 0; a < at; a++) + { + do_mkdir ("/%d", a); + for (b = 0; b < bt; b++) + { + do_mkdir ("/%d/%d", a, b); + for (c = 0; c < ct; c++) + { + do_mkdir ("/%d/%d/%d", a, b, c); + for (d = 0; d < dt; d++) + do_touch ("/%d/%d/%d/%d", a, b, c, d); + } + } + } + quiet = false; + + snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1); + CHECK ((fd = open (try)) > 1, "open \"%s\"", try); + msg ("close \"%s\"", try); + close (fd); +} + +static void +do_mkdir (const char *format, ...) +{ + char dir[128]; + va_list args; + + va_start (args, format); + vsnprintf (dir, sizeof dir, format, args); + va_end (args); + + CHECK (mkdir (dir), "mkdir \"%s\"", dir); +} + +static void +do_touch (const char *format, ...) +{ + char file[128]; + va_list args; + + va_start (args, format); + vsnprintf (file, sizeof file, format, args); + va_end (args); + + CHECK (create (file, 0), "create \"%s\"", file); +} diff --git a/pintos-env/pintos/tests/filesys/extended/mk-tree.h b/pintos-env/pintos/tests/filesys/extended/mk-tree.h new file mode 100755 index 0000000..df0d5a6 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/mk-tree.h @@ -0,0 +1,6 @@ +#ifndef TESTS_FILESYS_EXTENDED_MK_TREE_H +#define TESTS_FILESYS_EXTENDED_MK_TREE_H + +void make_tree (int at, int bt, int ct, int dt); + +#endif /* tests/filesys/extended/mk-tree.h */ diff --git a/pintos-env/pintos/tests/filesys/extended/syn-rw-persistence.ck b/pintos-env/pintos/tests/filesys/extended/syn-rw-persistence.ck new file mode 100755 index 0000000..62d57ee --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/syn-rw-persistence.ck @@ -0,0 +1,8 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw", + "logfile" => [random_bytes (8 * 512)]}); +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/syn-rw.c b/pintos-env/pintos/tests/filesys/extended/syn-rw.c new file mode 100755 index 0000000..657dfb5 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/syn-rw.c @@ -0,0 +1,35 @@ +/* Grows a file in chunks while subprocesses read the growing + file. */ + +#include +#include +#include "tests/filesys/extended/syn-rw.h" +#include "tests/lib.h" +#include "tests/main.h" + +char buf[BUF_SIZE]; + +#define CHILD_CNT 4 + +void +test_main (void) +{ + pid_t children[CHILD_CNT]; + size_t ofs; + int fd; + + CHECK (create (file_name, 0), "create \"%s\"", file_name); + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + + exec_children ("child-syn-rw", children, CHILD_CNT); + + random_bytes (buf, sizeof buf); + quiet = true; + for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE) + CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0, + "write %d bytes at offset %zu in \"%s\"", + (int) CHUNK_SIZE, ofs, file_name); + quiet = false; + + wait_children (children, CHILD_CNT); +} diff --git a/pintos-env/pintos/tests/filesys/extended/syn-rw.ck b/pintos-env/pintos/tests/filesys/extended/syn-rw.ck new file mode 100755 index 0000000..ac82aa8 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/syn-rw.ck @@ -0,0 +1,20 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::random; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(syn-rw) begin +(syn-rw) create "logfile" +(syn-rw) open "logfile" +(syn-rw) exec child 1 of 4: "child-syn-rw 0" +(syn-rw) exec child 2 of 4: "child-syn-rw 1" +(syn-rw) exec child 3 of 4: "child-syn-rw 2" +(syn-rw) exec child 4 of 4: "child-syn-rw 3" +(syn-rw) wait for child 1 of 4 returned 0 (expected 0) +(syn-rw) wait for child 2 of 4 returned 1 (expected 1) +(syn-rw) wait for child 3 of 4 returned 2 (expected 2) +(syn-rw) wait for child 4 of 4 returned 3 (expected 3) +(syn-rw) end +EOF +pass; diff --git a/pintos-env/pintos/tests/filesys/extended/syn-rw.h b/pintos-env/pintos/tests/filesys/extended/syn-rw.h new file mode 100755 index 0000000..170aeb4 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/syn-rw.h @@ -0,0 +1,9 @@ +#ifndef TESTS_FILESYS_EXTENDED_SYN_RW_H +#define TESTS_FILESYS_EXTENDED_SYN_RW_H + +#define CHUNK_SIZE 8 +#define CHUNK_CNT 512 +#define BUF_SIZE (CHUNK_SIZE * CHUNK_CNT) +static const char file_name[] = "logfile"; + +#endif /* tests/filesys/extended/syn-rw.h */ diff --git a/pintos-env/pintos/tests/filesys/extended/tar.c b/pintos-env/pintos/tests/filesys/extended/tar.c new file mode 100755 index 0000000..9801484 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/extended/tar.c @@ -0,0 +1,208 @@ +/* tar.c + + Creates a tar archive. */ + +#include +#include +#include +#include + +static void usage (void); +static bool make_tar_archive (const char *archive_name, + char *files[], size_t file_cnt); + +int +main (int argc, char *argv[]) +{ + if (argc < 3) + usage (); + + return (make_tar_archive (argv[1], argv + 2, argc - 2) + ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static void +usage (void) +{ + printf ("tar, tar archive creator\n" + "Usage: tar ARCHIVE FILE...\n" + "where ARCHIVE is the tar archive to create\n" + " and FILE... is a list of files or directories to put into it.\n" + "(ARCHIVE itself will not be included in the archive, even if it\n" + "is in a directory to be archived.)\n"); + exit (EXIT_FAILURE); +} + +static bool archive_file (char file_name[], size_t file_name_size, + int archive_fd, bool *write_error); + +static bool archive_ordinary_file (const char *file_name, int file_fd, + int archive_fd, bool *write_error); +static bool archive_directory (char file_name[], size_t file_name_size, + int file_fd, int archive_fd, bool *write_error); +static bool write_header (const char *file_name, enum ustar_type, int size, + int archive_fd, bool *write_error); + +static bool do_write (int fd, const char *buffer, int size, bool *write_error); + +static bool +make_tar_archive (const char *archive_name, char *files[], size_t file_cnt) +{ + static const char zeros[512]; + int archive_fd; + bool success = true; + bool write_error = false; + size_t i; + + if (!create (archive_name, 0)) + { + printf ("%s: create failed\n", archive_name); + return false; + } + archive_fd = open (archive_name); + if (archive_fd < 0) + { + printf ("%s: open failed\n", archive_name); + return false; + } + + for (i = 0; i < file_cnt; i++) + { + char file_name[128]; + + strlcpy (file_name, files[i], sizeof file_name); + if (!archive_file (file_name, sizeof file_name, + archive_fd, &write_error)) + success = false; + } + + if (!do_write (archive_fd, zeros, 512, &write_error) + || !do_write (archive_fd, zeros, 512, &write_error)) + success = false; + + close (archive_fd); + + return success; +} + +static bool +archive_file (char file_name[], size_t file_name_size, + int archive_fd, bool *write_error) +{ + int file_fd = open (file_name); + if (file_fd >= 0) + { + bool success; + + if (inumber (file_fd) != inumber (archive_fd)) + { + if (!isdir (file_fd)) + success = archive_ordinary_file (file_name, file_fd, + archive_fd, write_error); + else + success = archive_directory (file_name, file_name_size, file_fd, + archive_fd, write_error); + } + else + { + /* Nothing to do: don't try to archive the archive file. */ + success = true; + } + + close (file_fd); + + return success; + } + else + { + printf ("%s: open failed\n", file_name); + return false; + } +} + +static bool +archive_ordinary_file (const char *file_name, int file_fd, + int archive_fd, bool *write_error) +{ + bool read_error = false; + bool success = true; + int file_size = filesize (file_fd); + + if (!write_header (file_name, USTAR_REGULAR, file_size, + archive_fd, write_error)) + return false; + + while (file_size > 0) + { + static char buf[512]; + int chunk_size = file_size > 512 ? 512 : file_size; + int read_retval = read (file_fd, buf, chunk_size); + int bytes_read = read_retval > 0 ? read_retval : 0; + + if (bytes_read != chunk_size && !read_error) + { + printf ("%s: read error\n", file_name); + read_error = true; + success = false; + } + + memset (buf + bytes_read, 0, 512 - bytes_read); + if (!do_write (archive_fd, buf, 512, write_error)) + success = false; + + file_size -= chunk_size; + } + + return success; +} + +static bool +archive_directory (char file_name[], size_t file_name_size, int file_fd, + int archive_fd, bool *write_error) +{ + size_t dir_len; + bool success = true; + + dir_len = strlen (file_name); + if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size) + { + printf ("%s: file name too long\n", file_name); + return false; + } + + if (!write_header (file_name, USTAR_DIRECTORY, 0, archive_fd, write_error)) + return false; + + file_name[dir_len] = '/'; + while (readdir (file_fd, &file_name[dir_len + 1])) + if (!archive_file (file_name, file_name_size, archive_fd, write_error)) + success = false; + file_name[dir_len] = '\0'; + + return success; +} + +static bool +write_header (const char *file_name, enum ustar_type type, int size, + int archive_fd, bool *write_error) +{ + static char header[512]; + return (ustar_make_header (file_name, type, size, header) + && do_write (archive_fd, header, 512, write_error)); +} + +static bool +do_write (int fd, const char *buffer, int size, bool *write_error) +{ + if (write (fd, buffer, size) == size) + return true; + else + { + if (!*write_error) + { + printf ("error writing archive\n"); + *write_error = true; + } + return false; + } +} diff --git a/pintos-env/pintos/tests/filesys/seq-test.c b/pintos-env/pintos/tests/filesys/seq-test.c new file mode 100755 index 0000000..8ce222c --- /dev/null +++ b/pintos-env/pintos/tests/filesys/seq-test.c @@ -0,0 +1,37 @@ +#include "tests/filesys/seq-test.h" +#include +#include +#include "tests/lib.h" + +void +seq_test (const char *file_name, void *buf, size_t size, size_t initial_size, + size_t (*block_size_func) (void), + void (*check_func) (int fd, long ofs)) +{ + size_t ofs; + int fd; + + random_bytes (buf, size); + CHECK (create (file_name, initial_size), "create \"%s\"", file_name); + CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); + + ofs = 0; + msg ("writing \"%s\"", file_name); + while (ofs < size) + { + size_t block_size = block_size_func (); + if (block_size > size - ofs) + block_size = size - ofs; + + if (write (fd, buf + ofs, block_size) != (int) block_size) + fail ("write %zu bytes at offset %zu in \"%s\" failed", + block_size, ofs, file_name); + + ofs += block_size; + if (check_func != NULL) + check_func (fd, ofs); + } + msg ("close \"%s\"", file_name); + close (fd); + check_file (file_name, buf, size); +} diff --git a/pintos-env/pintos/tests/filesys/seq-test.h b/pintos-env/pintos/tests/filesys/seq-test.h new file mode 100755 index 0000000..0697381 --- /dev/null +++ b/pintos-env/pintos/tests/filesys/seq-test.h @@ -0,0 +1,11 @@ +#ifndef TESTS_FILESYS_SEQ_TEST_H +#define TESTS_FILESYS_SEQ_TEST_H + +#include + +void seq_test (const char *file_name, + void *buf, size_t size, size_t initial_size, + size_t (*block_size_func) (void), + void (*check_func) (int fd, long ofs)); + +#endif /* tests/filesys/seq-test.h */ diff --git a/pintos-env/pintos/tests/internal/list.c b/pintos-env/pintos/tests/internal/list.c new file mode 100755 index 0000000..836c69e --- /dev/null +++ b/pintos-env/pintos/tests/internal/list.c @@ -0,0 +1,174 @@ +/* Test program for lib/kernel/list.c. + + Attempts to test the list functionality that is not + sufficiently tested elsewhere in Pintos. + + This is not a test we will run on your submitted projects. + It is here for completeness. +*/ + +#undef NDEBUG +#include +#include +#include +#include +#include "threads/test.h" + +/* Maximum number of elements in a linked list that we will + test. */ +#define MAX_SIZE 64 + +/* A linked list element. */ +struct value + { + struct list_elem elem; /* List element. */ + int value; /* Item value. */ + }; + +static void shuffle (struct value[], size_t); +static bool value_less (const struct list_elem *, const struct list_elem *, + void *); +static void verify_list_fwd (struct list *, int size); +static void verify_list_bkwd (struct list *, int size); + +/* Test the linked list implementation. */ +void +test (void) +{ + int size; + + printf ("testing various size lists:"); + for (size = 0; size < MAX_SIZE; size++) + { + int repeat; + + printf (" %d", size); + for (repeat = 0; repeat < 10; repeat++) + { + static struct value values[MAX_SIZE * 4]; + struct list list; + struct list_elem *e; + int i, ofs; + + /* Put values 0...SIZE in random order in VALUES. */ + for (i = 0; i < size; i++) + values[i].value = i; + shuffle (values, size); + + /* Assemble list. */ + list_init (&list); + for (i = 0; i < size; i++) + list_push_back (&list, &values[i].elem); + + /* Verify correct minimum and maximum elements. */ + e = list_min (&list, value_less, NULL); + ASSERT (size ? list_entry (e, struct value, elem)->value == 0 + : e == list_begin (&list)); + e = list_max (&list, value_less, NULL); + ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1 + : e == list_begin (&list)); + + /* Sort and verify list. */ + list_sort (&list, value_less, NULL); + verify_list_fwd (&list, size); + + /* Reverse and verify list. */ + list_reverse (&list); + verify_list_bkwd (&list, size); + + /* Shuffle, insert using list_insert_ordered(), + and verify ordering. */ + shuffle (values, size); + list_init (&list); + for (i = 0; i < size; i++) + list_insert_ordered (&list, &values[i].elem, + value_less, NULL); + verify_list_fwd (&list, size); + + /* Duplicate some items, uniquify, and verify. */ + ofs = size; + for (e = list_begin (&list); e != list_end (&list); + e = list_next (e)) + { + struct value *v = list_entry (e, struct value, elem); + int copies = random_ulong () % 4; + while (copies-- > 0) + { + values[ofs].value = v->value; + list_insert (e, &values[ofs++].elem); + } + } + ASSERT ((size_t) ofs < sizeof values / sizeof *values); + list_unique (&list, NULL, value_less, NULL); + verify_list_fwd (&list, size); + } + } + + printf (" done\n"); + printf ("list: PASS\n"); +} + +/* Shuffles the CNT elements in ARRAY into random order. */ +static void +shuffle (struct value *array, size_t cnt) +{ + size_t i; + + for (i = 0; i < cnt; i++) + { + size_t j = i + random_ulong () % (cnt - i); + struct value t = array[j]; + array[j] = array[i]; + array[i] = t; + } +} + +/* Returns true if value A is less than value B, false + otherwise. */ +static bool +value_less (const struct list_elem *a_, const struct list_elem *b_, + void *aux UNUSED) +{ + const struct value *a = list_entry (a_, struct value, elem); + const struct value *b = list_entry (b_, struct value, elem); + + return a->value < b->value; +} + +/* Verifies that LIST contains the values 0...SIZE when traversed + in forward order. */ +static void +verify_list_fwd (struct list *list, int size) +{ + struct list_elem *e; + int i; + + for (i = 0, e = list_begin (list); + i < size && e != list_end (list); + i++, e = list_next (e)) + { + struct value *v = list_entry (e, struct value, elem); + ASSERT (i == v->value); + } + ASSERT (i == size); + ASSERT (e == list_end (list)); +} + +/* Verifies that LIST contains the values 0...SIZE when traversed + in reverse order. */ +static void +verify_list_bkwd (struct list *list, int size) +{ + struct list_elem *e; + int i; + + for (i = 0, e = list_rbegin (list); + i < size && e != list_rend (list); + i++, e = list_prev (e)) + { + struct value *v = list_entry (e, struct value, elem); + ASSERT (i == v->value); + } + ASSERT (i == size); + ASSERT (e == list_rend (list)); +} diff --git a/pintos-env/pintos/tests/internal/stdio.c b/pintos-env/pintos/tests/internal/stdio.c new file mode 100755 index 0000000..fb60cda --- /dev/null +++ b/pintos-env/pintos/tests/internal/stdio.c @@ -0,0 +1,208 @@ +/* Test program for printf() in lib/stdio.c. + + Attempts to test printf() functionality that is not + sufficiently tested elsewhere in Pintos. + + This is not a test we will run on your submitted projects. + It is here for completeness. +*/ + +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include "threads/test.h" + +/* Number of failures so far. */ +static int failure_cnt; + +static void +checkf (const char *expect, const char *format, ...) +{ + char output[128]; + va_list args; + + printf ("\"%s\" -> \"%s\": ", format, expect); + + va_start (args, format); + vsnprintf (output, sizeof output, format, args); + va_end (args); + + if (strcmp (expect, output)) + { + printf ("\nFAIL: actual output \"%s\"\n", output); + failure_cnt++; + } + else + printf ("okay\n"); +} + +/* Test printf() implementation. */ +void +test (void) +{ + printf ("Testing formats:"); + + /* Check that commas show up in the right places, for positive + numbers. */ + checkf ("1", "%'d", 1); + checkf ("12", "%'d", 12); + checkf ("123", "%'d", 123); + checkf ("1,234", "%'d", 1234); + checkf ("12,345", "%'d", 12345); + checkf ("123,456", "%'ld", 123456L); + checkf ("1,234,567", "%'ld", 1234567L); + checkf ("12,345,678", "%'ld", 12345678L); + checkf ("123,456,789", "%'ld", 123456789L); + checkf ("1,234,567,890", "%'ld", 1234567890L); + checkf ("12,345,678,901", "%'lld", 12345678901LL); + checkf ("123,456,789,012", "%'lld", 123456789012LL); + checkf ("1,234,567,890,123", "%'lld", 1234567890123LL); + checkf ("12,345,678,901,234", "%'lld", 12345678901234LL); + checkf ("123,456,789,012,345", "%'lld", 123456789012345LL); + checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL); + checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL); + checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL); + checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL); + + /* Check that commas show up in the right places, for positive + numbers. */ + checkf ("-1", "%'d", -1); + checkf ("-12", "%'d", -12); + checkf ("-123", "%'d", -123); + checkf ("-1,234", "%'d", -1234); + checkf ("-12,345", "%'d", -12345); + checkf ("-123,456", "%'ld", -123456L); + checkf ("-1,234,567", "%'ld", -1234567L); + checkf ("-12,345,678", "%'ld", -12345678L); + checkf ("-123,456,789", "%'ld", -123456789L); + checkf ("-1,234,567,890", "%'ld", -1234567890L); + checkf ("-12,345,678,901", "%'lld", -12345678901LL); + checkf ("-123,456,789,012", "%'lld", -123456789012LL); + checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL); + checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL); + checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL); + checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL); + checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL); + checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL); + checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL); + + /* Check signed integer conversions. */ + checkf (" 0", "%5d", 0); + checkf ("0 ", "%-5d", 0); + checkf (" +0", "%+5d", 0); + checkf ("+0 ", "%+-5d", 0); + checkf (" 0", "% 5d", 0); + checkf ("00000", "%05d", 0); + checkf (" ", "%5.0d", 0); + checkf (" 00", "%5.2d", 0); + checkf ("0", "%d", 0); + + checkf (" 1", "%5d", 1); + checkf ("1 ", "%-5d", 1); + checkf (" +1", "%+5d", 1); + checkf ("+1 ", "%+-5d", 1); + checkf (" 1", "% 5d", 1); + checkf ("00001", "%05d", 1); + checkf (" 1", "%5.0d", 1); + checkf (" 01", "%5.2d", 1); + checkf ("1", "%d", 1); + + checkf (" -1", "%5d", -1); + checkf ("-1 ", "%-5d", -1); + checkf (" -1", "%+5d", -1); + checkf ("-1 ", "%+-5d", -1); + checkf (" -1", "% 5d", -1); + checkf ("-0001", "%05d", -1); + checkf (" -1", "%5.0d", -1); + checkf (" -01", "%5.2d", -1); + checkf ("-1", "%d", -1); + + checkf ("12345", "%5d", 12345); + checkf ("12345", "%-5d", 12345); + checkf ("+12345", "%+5d", 12345); + checkf ("+12345", "%+-5d", 12345); + checkf (" 12345", "% 5d", 12345); + checkf ("12345", "%05d", 12345); + checkf ("12345", "%5.0d", 12345); + checkf ("12345", "%5.2d", 12345); + checkf ("12345", "%d", 12345); + + checkf ("123456", "%5d", 123456); + checkf ("123456", "%-5d", 123456); + checkf ("+123456", "%+5d", 123456); + checkf ("+123456", "%+-5d", 123456); + checkf (" 123456", "% 5d", 123456); + checkf ("123456", "%05d", 123456); + checkf ("123456", "%5.0d", 123456); + checkf ("123456", "%5.2d", 123456); + checkf ("123456", "%d", 123456); + + /* Check unsigned integer conversions. */ + checkf (" 0", "%5u", 0); + checkf (" 0", "%5o", 0); + checkf (" 0", "%5x", 0); + checkf (" 0", "%5X", 0); + checkf (" 0", "%#5o", 0); + checkf (" 0", "%#5x", 0); + checkf (" 0", "%#5X", 0); + checkf (" 00000000", "%#10.8x", 0); + + checkf (" 1", "%5u", 1); + checkf (" 1", "%5o", 1); + checkf (" 1", "%5x", 1); + checkf (" 1", "%5X", 1); + checkf (" 01", "%#5o", 1); + checkf (" 0x1", "%#5x", 1); + checkf (" 0X1", "%#5X", 1); + checkf ("0x00000001", "%#10.8x", 1); + + checkf ("123456", "%5u", 123456); + checkf ("361100", "%5o", 123456); + checkf ("1e240", "%5x", 123456); + checkf ("1E240", "%5X", 123456); + checkf ("0361100", "%#5o", 123456); + checkf ("0x1e240", "%#5x", 123456); + checkf ("0X1E240", "%#5X", 123456); + checkf ("0x0001e240", "%#10.8x", 123456); + + /* Character and string conversions. */ + checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r'); + checkf (" left-right ", "%6s%s%-7s", "left", "-", "right"); + checkf ("trim", "%.4s", "trimoff"); + checkf ("%%", "%%%%"); + + /* From Cristian Cadar's automatic test case generator. */ + checkf (" abcdefgh", "%9s", "abcdefgh"); + checkf ("36657730000", "%- o", (unsigned) 036657730000); + checkf ("4139757568", "%- u", (unsigned) 4139757568UL); + checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000); + checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000); + checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL); + checkf ("-155209728", "%-zi", (size_t) -155209728); + checkf ("-155209728", "%-zd", (size_t) -155209728); + checkf ("036657730000", "%+#o", (unsigned) 036657730000); + checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000); + checkf ("-155209728", "% zi", (size_t) -155209728); + checkf ("-155209728", "% zd", (size_t) -155209728); + checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL); + checkf ("036657730000", "% #o", (unsigned) 036657730000); + checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000); + checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000); + checkf ("-155209728", "%#zd", (size_t) -155209728); + checkf ("-155209728", "%0zi", (size_t) -155209728); + checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL); + checkf ("-155,209,728", "%-'d", -155209728); + checkf ("-155209728", "%.zi", (size_t) -155209728); + checkf ("-155209728", "%zi", (size_t) -155209728); + checkf ("-155209728", "%zd", (size_t) -155209728); + checkf ("-155209728", "%+zi", (size_t) -155209728); + + if (failure_cnt == 0) + printf ("\nstdio: PASS\n"); + else + printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt); +} diff --git a/pintos-env/pintos/tests/internal/stdlib.c b/pintos-env/pintos/tests/internal/stdlib.c new file mode 100755 index 0000000..ad0f0f9 --- /dev/null +++ b/pintos-env/pintos/tests/internal/stdlib.c @@ -0,0 +1,114 @@ +/* Test program for sorting and searching in lib/stdlib.c. + + Attempts to test the sorting and searching functionality that + is not sufficiently tested elsewhere in Pintos. + + This is not a test we will run on your submitted projects. + It is here for completeness. +*/ + +#undef NDEBUG +#include +#include +#include +#include +#include +#include "threads/test.h" + +/* Maximum number of elements in an array that we will test. */ +#define MAX_CNT 4096 + +static void shuffle (int[], size_t); +static int compare_ints (const void *, const void *); +static void verify_order (const int[], size_t); +static void verify_bsearch (const int[], size_t); + +/* Test sorting and searching implementations. */ +void +test (void) +{ + int cnt; + + printf ("testing various size arrays:"); + for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1) + { + int repeat; + + printf (" %zu", cnt); + for (repeat = 0; repeat < 10; repeat++) + { + static int values[MAX_CNT]; + int i; + + /* Put values 0...CNT in random order in VALUES. */ + for (i = 0; i < cnt; i++) + values[i] = i; + shuffle (values, cnt); + + /* Sort VALUES, then verify ordering. */ + qsort (values, cnt, sizeof *values, compare_ints); + verify_order (values, cnt); + verify_bsearch (values, cnt); + } + } + + printf (" done\n"); + printf ("stdlib: PASS\n"); +} + +/* Shuffles the CNT elements in ARRAY into random order. */ +static void +shuffle (int *array, size_t cnt) +{ + size_t i; + + for (i = 0; i < cnt; i++) + { + size_t j = i + random_ulong () % (cnt - i); + int t = array[j]; + array[j] = array[i]; + array[i] = t; + } +} + +/* Returns 1 if *A is greater than *B, + 0 if *A equals *B, + -1 if *A is less than *B. */ +static int +compare_ints (const void *a_, const void *b_) +{ + const int *a = a_; + const int *b = b_; + + return *a < *b ? -1 : *a > *b; +} + +/* Verifies that ARRAY contains the CNT ints 0...CNT-1. */ +static void +verify_order (const int *array, size_t cnt) +{ + int i; + + for (i = 0; (size_t) i < cnt; i++) + ASSERT (array[i] == i); +} + +/* Checks that bsearch() works properly in ARRAY. ARRAY must + contain the values 0...CNT-1. */ +static void +verify_bsearch (const int *array, size_t cnt) +{ + int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2}; + int i; + + /* Check that all the values in the array are found properly. */ + for (i = 0; (size_t) i < cnt; i++) + ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints) + == array + i); + + /* Check that some values not in the array are not found. */ + not_in_array[0] = cnt; + for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++) + ASSERT (bsearch (¬_in_array[i], array, cnt, sizeof *array, compare_ints) + == NULL); +} diff --git a/pintos-env/pintos/tests/lib.c b/pintos-env/pintos/tests/lib.c new file mode 100755 index 0000000..ee36505 --- /dev/null +++ b/pintos-env/pintos/tests/lib.c @@ -0,0 +1,196 @@ +#include "tests/lib.h" +#include +#include +#include +#include +#include + +const char *test_name; +bool quiet = false; + +static void +vmsg (const char *format, va_list args, const char *suffix) +{ + /* We go to some trouble to stuff the entire message into a + single buffer and output it in a single system call, because + that'll (typically) ensure that it gets sent to the console + atomically. Otherwise kernel messages like "foo: exit(0)" + can end up being interleaved if we're unlucky. */ + static char buf[1024]; + + snprintf (buf, sizeof buf, "(%s) ", test_name); + vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args); + strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf)); + write (STDOUT_FILENO, buf, strlen (buf)); +} + +void +msg (const char *format, ...) +{ + va_list args; + + if (quiet) + return; + va_start (args, format); + vmsg (format, args, "\n"); + va_end (args); +} + +void +fail (const char *format, ...) +{ + va_list args; + + va_start (args, format); + vmsg (format, args, ": FAILED\n"); + va_end (args); + + exit (1); +} + +static void +swap (void *a_, void *b_, size_t size) +{ + uint8_t *a = a_; + uint8_t *b = b_; + size_t i; + + for (i = 0; i < size; i++) + { + uint8_t t = a[i]; + a[i] = b[i]; + b[i] = t; + } +} + +void +shuffle (void *buf_, size_t cnt, size_t size) +{ + char *buf = buf_; + size_t i; + + for (i = 0; i < cnt; i++) + { + size_t j = i + random_ulong () % (cnt - i); + swap (buf + i * size, buf + j * size, size); + } +} + +void +exec_children (const char *child_name, pid_t pids[], size_t child_cnt) +{ + size_t i; + + for (i = 0; i < child_cnt; i++) + { + char cmd_line[128]; + snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i); + CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR, + "exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line); + } +} + +void +wait_children (pid_t pids[], size_t child_cnt) +{ + size_t i; + + for (i = 0; i < child_cnt; i++) + { + int status = wait (pids[i]); + CHECK (status == (int) i, + "wait for child %zu of %zu returned %d (expected %zu)", + i + 1, child_cnt, status, i); + } +} + +void +check_file_handle (int fd, + const char *file_name, const void *buf_, size_t size) +{ + const char *buf = buf_; + size_t ofs = 0; + size_t file_size; + + /* Warn about file of wrong size. Don't fail yet because we + may still be able to get more information by reading the + file. */ + file_size = filesize (fd); + if (file_size != size) + msg ("size of %s (%zu) differs from expected (%zu)", + file_name, file_size, size); + + /* Read the file block-by-block, comparing data as we go. */ + while (ofs < size) + { + char block[512]; + size_t block_size, ret_val; + + block_size = size - ofs; + if (block_size > sizeof block) + block_size = sizeof block; + + ret_val = read (fd, block, block_size); + if (ret_val != block_size) + fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu", + block_size, ofs, file_name, ret_val); + + compare_bytes (block, buf + ofs, block_size, ofs, file_name); + ofs += block_size; + } + + /* Now fail due to wrong file size. */ + if (file_size != size) + fail ("size of %s (%zu) differs from expected (%zu)", + file_name, file_size, size); + + msg ("verified contents of \"%s\"", file_name); +} + +void +check_file (const char *file_name, const void *buf, size_t size) +{ + int fd; + + CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification", + file_name); + check_file_handle (fd, file_name, buf, size); + msg ("close \"%s\"", file_name); + close (fd); +} + +void +compare_bytes (const void *read_data_, const void *expected_data_, size_t size, + size_t ofs, const char *file_name) +{ + const uint8_t *read_data = read_data_; + const uint8_t *expected_data = expected_data_; + size_t i, j; + size_t show_cnt; + + if (!memcmp (read_data, expected_data, size)) + return; + + for (i = 0; i < size; i++) + if (read_data[i] != expected_data[i]) + break; + for (j = i + 1; j < size; j++) + if (read_data[j] == expected_data[j]) + break; + + quiet = false; + msg ("%zu bytes read starting at offset %zu in \"%s\" differ " + "from expected.", j - i, ofs + i, file_name); + show_cnt = j - i; + if (j - i > 64) + { + show_cnt = 64; + msg ("Showing first differing %zu bytes.", show_cnt); + } + msg ("Data actually read:"); + hex_dump (ofs + i, read_data + i, show_cnt, true); + msg ("Expected data:"); + hex_dump (ofs + i, expected_data + i, show_cnt, true); + fail ("%zu bytes read starting at offset %zu in \"%s\" differ " + "from expected", j - i, ofs + i, file_name); +} diff --git a/pintos-env/pintos/tests/lib.h b/pintos-env/pintos/tests/lib.h new file mode 100755 index 0000000..648327b --- /dev/null +++ b/pintos-env/pintos/tests/lib.h @@ -0,0 +1,50 @@ +#ifndef TESTS_LIB_H +#define TESTS_LIB_H + +#include +#include +#include +#include + +extern const char *test_name; +extern bool quiet; + +void msg (const char *, ...) PRINTF_FORMAT (1, 2); +void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN; + +/* Takes an expression to test for SUCCESS and a message, which + may include printf-style arguments. Logs the message, then + tests the expression. If it is zero, indicating failure, + emits the message as a failure. + + Somewhat tricky to use: + + - SUCCESS must not have side effects that affect the + message, because that will cause the original message and + the failure message to differ. + + - The message must not have side effects of its own, because + it will be printed twice on failure, or zero times on + success if quiet is set. */ +#define CHECK(SUCCESS, ...) \ + do \ + { \ + msg (__VA_ARGS__); \ + if (!(SUCCESS)) \ + fail (__VA_ARGS__); \ + } \ + while (0) + +void shuffle (void *, size_t cnt, size_t size); + +void exec_children (const char *child_name, pid_t pids[], size_t child_cnt); +void wait_children (pid_t pids[], size_t child_cnt); + +void check_file_handle (int fd, const char *file_name, + const void *buf_, size_t filesize); +void check_file (const char *file_name, const void *buf, size_t filesize); + +void compare_bytes (const void *read_data, const void *expected_data, + size_t size, size_t ofs, const char *file_name); + +#endif /* test/lib.h */ diff --git a/pintos-env/pintos/tests/lib.pm b/pintos-env/pintos/tests/lib.pm new file mode 100755 index 0000000..bc37ae5 --- /dev/null +++ b/pintos-env/pintos/tests/lib.pm @@ -0,0 +1,19 @@ +use strict; +use warnings; + +use tests::random; + +sub shuffle { + my ($in, $cnt, $sz) = @_; + $cnt * $sz == length $in or die; + my (@a) = 0...$cnt - 1; + for my $i (0...$cnt - 1) { + my ($j) = $i + random_ulong () % ($cnt - $i); + @a[$i, $j] = @a[$j, $i]; + } + my ($out) = ""; + $out .= substr ($in, $_ * $sz, $sz) foreach @a; + return $out; +} + +1; diff --git a/pintos-env/pintos/tests/main.c b/pintos-env/pintos/tests/main.c new file mode 100755 index 0000000..ad1b0f1 --- /dev/null +++ b/pintos-env/pintos/tests/main.c @@ -0,0 +1,15 @@ +#include +#include "tests/lib.h" +#include "tests/main.h" + +int +main (int argc UNUSED, char *argv[]) +{ + test_name = argv[0]; + + msg ("begin"); + random_init (0); + test_main (); + msg ("end"); + return 0; +} diff --git a/pintos-env/pintos/tests/main.h b/pintos-env/pintos/tests/main.h new file mode 100755 index 0000000..f0e8818 --- /dev/null +++ b/pintos-env/pintos/tests/main.h @@ -0,0 +1,6 @@ +#ifndef TESTS_MAIN_H +#define TESTS_MAIN_H + +void test_main (void); + +#endif /* tests/main.h */ diff --git a/pintos-env/pintos/tests/make-grade b/pintos-env/pintos/tests/make-grade new file mode 100755 index 0000000..a3faa0e --- /dev/null +++ b/pintos-env/pintos/tests/make-grade @@ -0,0 +1,152 @@ +#! /usr/bin/perl + +use strict; +use warnings; + +@ARGV == 3 || die; +my ($src_dir, $results_file, $grading_file) = @ARGV; + +# Read pass/file verdicts from $results_file. +open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n"; +my (%verdicts, %verdict_counts); +while () { + my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die; + $verdicts{$test} = $verdict eq 'pass'; +} +close RESULTS; + +my (@failures); +my (@overall, @rubrics, @summary); +my ($pct_actual, $pct_possible) = (0, 0); + +# Read grading file. +my (@items); +open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n"; +while () { + s/#.*//; + next if /^\s*$/; + my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die; + my ($dir) = $rubric_suffix =~ /^(.*)\//; + my ($rubric_file) = "$src_dir/$rubric_suffix"; + open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n"; + + # Rubric file must begin with title line. + my $title = ; + chomp $title; + $title =~ s/:$// or die; + $title .= " ($rubric_suffix):"; + push (@rubrics, $title); + + my ($score, $possible) = (0, 0); + my ($cnt, $passed) = (0, 0); + my ($was_score) = 0; + while () { + chomp; + push (@rubrics, "\t$_"), next if /^-/; + push (@rubrics, ""), next if /^\s*$/; + my ($poss, $name) = /^(\d+)\t(.*)$/ or die; + my ($test) = "$dir/$name"; + my ($points) = 0; + if (!defined $verdicts{$test}) { + push (@overall, "warning: $test not tested, assuming failure"); + } elsif ($verdicts{$test}) { + $points = $poss; + $passed++; + } + push (@failures, $test) if !$points; + $verdict_counts{$test}++; + push (@rubrics, sprintf ("\t%4s%2d/%2d %s", + $points ? '' : '**', $points, $poss, $test)); + $score += $points; + $possible += $poss; + $cnt++; + } + close (RUBRIC); + + push (@rubrics, ""); + push (@rubrics, "\t- Section summary."); + push (@rubrics, sprintf ("\t%4s%3d/%3d %s", + '', $passed, $cnt, 'tests passed')); + push (@rubrics, sprintf ("\t%4s%3d/%3d %s", + '', $score, $possible, 'points subtotal')); + push (@rubrics, ''); + + my ($pct) = ($score / $possible) * $max_pct; + push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%", + $rubric_suffix, + $score, $possible, + $pct, $max_pct)); + $pct_actual += $pct; + $pct_possible += $max_pct; +} +close GRADING; + +my ($sum_line) + = "--------------------------------------------- --- --- ------ ------"; +unshift (@summary, + "SUMMARY BY TEST SET", + '', + sprintf ("%-45s %3s %3s %6s %6s", + "Test Set", "Pts", "Max", "% Ttl", "% Max"), + $sum_line); +push (@summary, + $sum_line, + sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%", + 'Total', '', '', $pct_actual, $pct_possible)); + +unshift (@rubrics, + "SUMMARY OF INDIVIDUAL TESTS", + ''); + +foreach my $name (keys (%verdicts)) { + my ($count) = $verdict_counts{$name}; + if (!defined ($count) || $count != 1) { + if (!defined ($count) || !$count) { + push (@overall, "warning: test $name doesn't count for grading"); + } else { + push (@overall, + "warning: test $name counted $count times in grading"); + } + } +} +push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual)); +if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) { + push (@overall, "ALL TESTED PASSED -- PERFECT SCORE"); +} + +my (@divider) = ('', '- ' x 38, ''); + +print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics); + +for my $test (@failures) { + print map ("$_\n", @divider); + print "DETAILS OF $test FAILURE:\n\n"; + + if (open (RESULT, '<', "$test.result")) { + my $first_line = ; + my ($cnt) = 0; + while () { + print; + $cnt++; + } + close (RESULT); + } + + if (open (OUTPUT, '<', "$test.output")) { + print "\nOUTPUT FROM $test:\n\n"; + + my ($panics, $boots) = (0, 0); + while () { + if (/PANIC/ && ++$panics > 2) { + print "[...details of additional panic(s) omitted...]\n"; + last; + } + print; + if (/Pintos booting/ && ++$boots > 1) { + print "[...details of reboot(s) omitted...]\n"; + last; + } + } + close (OUTPUT); + } +} diff --git a/pintos-env/pintos/tests/random.pm b/pintos-env/pintos/tests/random.pm new file mode 100755 index 0000000..be008ff --- /dev/null +++ b/pintos-env/pintos/tests/random.pm @@ -0,0 +1,27 @@ +use strict; +use warnings; + +use tests::arc4; + +my (@arc4); + +sub random_init { + if (@arc4 == 0) { + my ($seed) = @_; + $seed = 0 if !defined $seed; + @arc4 = arc4_init (pack ("V", $seed)); + } +} + +sub random_bytes { + random_init (); + my ($n) = @_; + return arc4_crypt (\@arc4, "\0" x $n); +} + +sub random_ulong { + random_init (); + return unpack ("V", random_bytes (4)); +} + +1; diff --git a/pintos-env/pintos/tests/tests.pm b/pintos-env/pintos/tests/tests.pm new file mode 100755 index 0000000..4599cb9 --- /dev/null +++ b/pintos-env/pintos/tests/tests.pm @@ -0,0 +1,625 @@ +use strict; +use warnings; +use tests::Algorithm::Diff; +use File::Temp 'tempfile'; +use Fcntl qw(SEEK_SET SEEK_CUR); + +sub fail; +sub pass; + +die if @ARGV != 2; +our ($test, $src_dir) = @ARGV; + +my ($msg_file) = tempfile (); +select ($msg_file); + +our (@prereq_tests) = (); +if ($test =~ /^(.*)-persistence$/) { + push (@prereq_tests, $1); +} +for my $prereq_test (@prereq_tests) { + my (@result) = read_text_file ("$prereq_test.result"); + fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS'; +} + + +# Generic testing. + +sub check_expected { + my ($expected) = pop @_; + my (@options) = @_; + my (@output) = read_text_file ("$test.output"); + common_checks ("run", @output); + compare_output ("run", @options, \@output, $expected); +} + +sub common_checks { + my ($run, @output) = @_; + + fail "\u$run produced no output at all\n" if @output == 0; + + check_for_panic ($run, @output); + check_for_keyword ($run, "FAIL", @output); + check_for_triple_fault ($run, @output); + check_for_keyword ($run, "TIMEOUT", @output); + + fail "\u$run didn't start up properly: no \"Pintos booting\" message\n" + if !grep (/Pintos booting with.*kB RAM\.\.\./, @output); + fail "\u$run didn't start up properly: no \"Boot complete\" message\n" + if !grep (/Boot complete/, @output); + fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n" + if !grep (/Timer: \d+ ticks/, @output); + fail "\u$run didn't shut down properly: no \"Powering off\" message\n" + if !grep (/Powering off/, @output); +} + +sub check_for_panic { + my ($run, @output) = @_; + + my ($panic) = grep (/PANIC/, @output); + return unless defined $panic; + + print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")), + "\n"; + + my (@stack_line) = grep (/Call stack:/, @output); + if (@stack_line != 0) { + my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/; + + # Find a user program to translate user virtual addresses. + my ($userprog) = ""; + $userprog = "$test" + if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test; + + # Get and print the backtrace. + my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`); + print "Call stack:$addrs\n"; + print "Translation of call stack:\n"; + print $trace; + + # Print disclaimer. + if ($userprog ne '' && index ($trace, $userprog) >= 0) { + print <capacity/) { + print < 0; + + print < $_), @$expected)}; + } + foreach my $key (keys %$expected) { + my (@expected) = split ("\n", $expected->{$key}); + + $msg .= "Acceptable output:\n"; + $msg .= join ('', map (" $_\n", @expected)); + + # Check whether actual and expected match. + # If it's a perfect match, we're done. + if ($#output == $#expected) { + my ($eq) = 1; + for (my ($i) = 0; $i <= $#expected; $i++) { + $eq = 0 if $output[$i] ne $expected[$i]; + } + return $key if $eq; + } + + # They differ. Output a diff. + my (@diff) = ""; + my ($d) = Algorithm::Diff->new (\@expected, \@output); + while ($d->Next ()) { + my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2)); + if ($d->Same ()) { + push (@diff, map (" $_\n", $d->Items (1))); + } else { + push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1); + push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2); + } + } + + $msg .= "Differences in `diff -u' format:\n"; + $msg .= join ('', @diff); + } + + # Failed to match. Report failure. + $msg .= "\n(Process exit codes are excluded for matching purposes.)\n" + if $ignore_exit_codes; + $msg .= "\n(User fault messages are excluded for matching purposes.)\n" + if $ignore_user_faults; + fail "Test output failed to match any acceptable form.\n\n$msg"; +} + +# File system extraction. + +# check_archive (\%CONTENTS) +# +# Checks that the extracted file system's contents match \%CONTENTS. +# Each key in the hash is a file name. Each value may be: +# +# - $FILE: Name of a host file containing the expected contents. +# +# - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE +# comprising the $LENGTH bytes starting at $OFFSET. +# +# - [$CONTENTS]: The literal expected file contents, as a string. +# +# - {SUBDIR}: A subdirectory, in the same form described here, +# recursively. +sub check_archive { + my ($expected_hier) = @_; + + my (@output) = read_text_file ("$test.output"); + common_checks ("file system extraction run", @output); + + @output = get_core_output ("file system extraction run", @output); + @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output); + fail join ("\n", "Error extracting file system:", @output) if @output; + + my ($test_base_name) = $test; + $test_base_name =~ s%.*/%%; + $test_base_name =~ s%-persistence$%%; + $expected_hier->{$test_base_name} = $prereq_tests[0]; + $expected_hier->{'tar'} = 'tests/filesys/extended/tar'; + + my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, "")); + my (%actual) = read_tar ("$prereq_tests[0].tar"); + + my ($errors) = 0; + foreach my $name (sort keys %expected) { + if (exists $actual{$name}) { + if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) { + print "$name is a directory but should be an ordinary file.\n"; + $errors++; + } elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) { + print "$name is an ordinary file but should be a directory.\n"; + $errors++; + } + } else { + print "$name is missing from the file system.\n"; + $errors++; + } + } + foreach my $name (sort keys %actual) { + if (!exists $expected{$name}) { + if ($name =~ /^[[:print:]]+$/) { + print "$name exists in the file system but it should not.\n"; + } else { + my ($esc_name) = $name; + $esc_name =~ s/[^[:print:]]/./g; + print <[0]); + $file = tempfile (); + syswrite ($file, $value->[0]) == $length + or die "writing temporary file: $!\n"; + sysseek ($file, 0, SEEK_SET); + } elsif (@$value == 3) { + $length = $value->[2]; + open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n"; + die "$value->[0]: file is smaller than expected\n" + if -s $file < $value->[1] + $length; + sysseek ($file, $value->[1], SEEK_SET); + } else { + die; + } + return ($file, $length); +} + +# compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE) +# +# Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B. +# ($A and $B are handles.) +# If their contents differ, prints a brief message describing +# the differences, using $NAME to identify the file. +# The message contains more detail if $VERBOSE is nonzero. +# Returns 1 if the contents are identical, 0 otherwise. +sub compare_files { + my ($a, $a_size, $b, $b_size, $name, $verbose) = @_; + my ($ofs) = 0; + select(STDOUT); + for (;;) { + my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size; + my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size; + my ($a_data, $b_data); + if (!defined (sysread ($a, $a_data, $a_amt)) + || !defined (sysread ($b, $b_data, $b_amt))) { + die "reading $name: $!\n"; + } + + my ($a_len) = length $a_data; + my ($b_len) = length $b_data; + last if $a_len == 0 && $b_len == 0; + + if ($a_data ne $b_data) { + my ($min_len) = $a_len < $b_len ? $a_len : $b_len; + my ($diff_ofs); + for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) { + last if (substr ($a_data, $diff_ofs, 1) + ne substr ($b_data, $diff_ofs, 1)); + } + + printf "\nFile $name differs from expected " + . "starting at offset 0x%x.\n", $ofs + $diff_ofs; + if ($verbose ) { + print "Expected contents:\n"; + hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs); + print "Actual contents:\n"; + hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs); + } + return 0; + } + + $ofs += $a_len; + $a_size -= $a_len; + $b_size -= $b_len; + } + return 1; +} + +# hex_dump ($DATA, $OFS) +# +# Prints $DATA in hex and text formats. +# The first byte of $DATA corresponds to logical offset $OFS +# in whatever file the data comes from. +sub hex_dump { + my ($data, $ofs) = @_; + + if ($data eq '') { + printf " (File ends at offset %08x.)\n", $ofs; + return; + } + + my ($per_line) = 16; + while ((my $size = length ($data)) > 0) { + my ($start) = $ofs % $per_line; + my ($end) = $per_line; + $end = $start + $size if $end - $start > $size; + my ($n) = $end - $start; + + printf "0x%08x ", int ($ofs / $per_line) * $per_line; + + # Hex version. + print " " x $start; + for my $i ($start...$end - 1) { + printf "%02x", ord (substr ($data, $i - $start, 1)); + print $i == $per_line / 2 - 1 ? '-' : ' '; + } + print " " x ($per_line - $end); + + # Character version. + my ($esc_data) = substr ($data, 0, $n); + $esc_data =~ s/[^[:print:]]/./g; + print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|"; + + print "\n"; + + $data = substr ($data, $n); + $ofs += $n; + } +} + +# print_fs (%FS) +# +# Prints a list of files in %FS, which must be a file system +# as flattened by flatten_hierarchy() and normalized by +# normalize_fs(). +sub print_fs { + my (%fs) = @_; + foreach my $name (sort keys %fs) { + my ($esc_name) = $name; + $esc_name =~ s/[^[:print:]]/./g; + print "$esc_name: "; + if (!is_dir ($fs{$name})) { + print +file_size ($fs{$name}), "-byte file"; + } else { + print "directory"; + } + print "\n"; + } + print "(empty)\n" if !@_; +} + +# normalize_fs (%FS) +# +# Takes a file system as flattened by flatten_hierarchy(). +# Returns a similar file system in which values of the form $FILE +# are replaced by those of the form [$FILE, $OFFSET, $LENGTH]. +sub normalize_fs { + my (%fs) = @_; + foreach my $name (keys %fs) { + my ($value) = $fs{$name}; + next if is_dir ($value) || ref ($value) ne ''; + die "can't open $value\n" if !stat $value; + $fs{$name} = [$value, 0, -s _]; + } + return %fs; +} + +# is_dir ($VALUE) +# +# Takes a value like one in the hash returned by flatten_hierarchy() +# and returns 1 if it represents a directory, 0 otherwise. +sub is_dir { + my ($value) = @_; + return ref ($value) eq '' && $value eq 'directory'; +} + +# file_size ($VALUE) +# +# Takes a value like one in the hash returned by flatten_hierarchy() +# and returns the size of the file it represents. +sub file_size { + my ($value) = @_; + die if is_dir ($value); + die if ref ($value) ne 'ARRAY'; + return @$value > 1 ? $value->[2] : length ($value->[0]); +} + +# flatten_hierarchy ($HIER_FS, $PREFIX) +# +# Takes a file system in the format expected by check_archive() and +# returns a "flattened" version in which file names include all parent +# directory names and the value of directories is just "directory". +sub flatten_hierarchy { + my (%hier_fs) = %{$_[0]}; + my ($prefix) = $_[1]; + my (%flat_fs); + for my $name (keys %hier_fs) { + my ($value) = $hier_fs{$name}; + if (ref $value eq 'HASH') { + %flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/")); + $flat_fs{"$prefix$name"} = 'directory'; + } else { + $flat_fs{"$prefix$name"} = $value; + } + } + return %flat_fs; +} + +# read_tar ($ARCHIVE) +# +# Reads the ustar-format tar file in $ARCHIVE +# and returns a flattened file system for it. +sub read_tar { + my ($archive) = @_; + my (%content); + open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n"; + for (;;) { + my ($header); + if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) { + fail "$archive: unexpected end of file\n" if $retval >= 0; + fail "$archive: read: $!\n"; + } + + last if $header eq "\0" x 512; + + # Verify magic numbers. + if (substr ($header, 257, 6) ne "ustar\0" + || substr ($header, 263, 2) ne '00') { + fail "$archive: corrupt ustar header\n"; + } + + # Verify checksum. + my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8))); + my ($correct_chksum) = unpack ("%32a*", $header); + fail "$archive: bad header checksum\n" if $chksum != $correct_chksum; + + # Get file name. + my ($name) = unpack ("Z100", $header); + my ($prefix) = unpack ("Z*", substr ($header, 345)); + $name = "$prefix/$name" if $prefix ne ''; + fail "$archive: contains file with empty name" if $name eq ''; + + # Get type. + my ($typeflag) = substr ($header, 156, 1); + $typeflag = '0' if $typeflag eq "\0"; + fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/; + + # Get size. + my ($size) = oct (unpack ("Z*", substr ($header, 124, 12))); + fail "bad size $size\n" if $size < 0; + $size = 0 if $typeflag eq '5'; + + # Store content. + $name =~ s%^(/|\./|\.\./)*%%; # Strip leading "/", "./", "../". + $name = '' if $name eq '.' || $name eq '..'; + if (exists $content{$name}) { + fail "$archive: contains multiple entries for $name\n"; + } + if ($typeflag eq '5') { + $content{$name} = 'directory' if $name ne ''; + } else { + fail "$archive: contains file with empty name\n" if $name eq ''; + my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR); + $content{$name} = [$archive, $position, $size]; + sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR); + } + } + close (ARCHIVE); + return %content; +} + +# Utilities. + +sub fail { + finish ("FAIL", @_); +} + +sub pass { + finish ("PASS", @_); +} + +sub finish { + my ($verdict, @messages) = @_; + + seek ($msg_file, 0, 0); + push (@messages, <$msg_file>); + close ($msg_file); + chomp (@messages); + + my ($result_fn) = "$test.result"; + open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n"; + print RESULT "$verdict\n"; + print RESULT "$_\n" foreach @messages; + close (RESULT); + + if ($verdict eq 'PASS') { + print STDOUT "pass $test\n"; + } else { + print STDOUT "FAIL $test\n"; + } + print STDOUT "$_\n" foreach @messages; + + exit 0; +} + +sub read_text_file { + my ($file_name) = @_; + open (FILE, '<', $file_name) or die "$file_name: open: $!\n"; + my (@content) = ; + chomp (@content); + close (FILE); + return @content; +} + +1; diff --git a/pintos-env/pintos/tests/threads/.priority-condvar.c.swp b/pintos-env/pintos/tests/threads/.priority-condvar.c.swp new file mode 100755 index 0000000..f36f508 Binary files /dev/null and b/pintos-env/pintos/tests/threads/.priority-condvar.c.swp differ diff --git a/pintos-env/pintos/tests/threads/Grading b/pintos-env/pintos/tests/threads/Grading new file mode 100755 index 0000000..c9be35f --- /dev/null +++ b/pintos-env/pintos/tests/threads/Grading @@ -0,0 +1,6 @@ +# Percentage of the testing point total designated for each set of +# tests. + +20.0% tests/threads/Rubric.alarm +40.0% tests/threads/Rubric.priority +40.0% tests/threads/Rubric.mlfqs diff --git a/pintos-env/pintos/tests/threads/Make.tests b/pintos-env/pintos/tests/threads/Make.tests new file mode 100755 index 0000000..4569035 --- /dev/null +++ b/pintos-env/pintos/tests/threads/Make.tests @@ -0,0 +1,53 @@ +# -*- makefile -*- + +# Test names. +tests/threads_TESTS = $(addprefix tests/threads/,alarm-single \ +alarm-multiple alarm-simultaneous alarm-priority alarm-zero \ +alarm-negative priority-change priority-donate-one \ +priority-donate-multiple priority-donate-multiple2 \ +priority-donate-nest priority-donate-sema priority-donate-lower \ +priority-fifo priority-preempt priority-sema priority-condvar \ +priority-donate-chain \ +mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2 \ +mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block) + +# Sources for tests. +tests/threads_SRC = tests/threads/tests.c +tests/threads_SRC += tests/threads/alarm-wait.c +tests/threads_SRC += tests/threads/alarm-simultaneous.c +tests/threads_SRC += tests/threads/alarm-priority.c +tests/threads_SRC += tests/threads/alarm-zero.c +tests/threads_SRC += tests/threads/alarm-negative.c +tests/threads_SRC += tests/threads/priority-change.c +tests/threads_SRC += tests/threads/priority-donate-one.c +tests/threads_SRC += tests/threads/priority-donate-multiple.c +tests/threads_SRC += tests/threads/priority-donate-multiple2.c +tests/threads_SRC += tests/threads/priority-donate-nest.c +tests/threads_SRC += tests/threads/priority-donate-sema.c +tests/threads_SRC += tests/threads/priority-donate-lower.c +tests/threads_SRC += tests/threads/priority-fifo.c +tests/threads_SRC += tests/threads/priority-preempt.c +tests/threads_SRC += tests/threads/priority-sema.c +tests/threads_SRC += tests/threads/priority-condvar.c +tests/threads_SRC += tests/threads/priority-donate-chain.c +tests/threads_SRC += tests/threads/mlfqs-load-1.c +tests/threads_SRC += tests/threads/mlfqs-load-60.c +tests/threads_SRC += tests/threads/mlfqs-load-avg.c +tests/threads_SRC += tests/threads/mlfqs-recent-1.c +tests/threads_SRC += tests/threads/mlfqs-fair.c +tests/threads_SRC += tests/threads/mlfqs-block.c + +MLFQS_OUTPUTS = \ +tests/threads/mlfqs-load-1.output \ +tests/threads/mlfqs-load-60.output \ +tests/threads/mlfqs-load-avg.output \ +tests/threads/mlfqs-recent-1.output \ +tests/threads/mlfqs-fair-2.output \ +tests/threads/mlfqs-fair-20.output \ +tests/threads/mlfqs-nice-2.output \ +tests/threads/mlfqs-nice-10.output \ +tests/threads/mlfqs-block.output + +$(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs +$(MLFQS_OUTPUTS): TIMEOUT = 480 + diff --git a/pintos-env/pintos/tests/threads/Rubric.alarm b/pintos-env/pintos/tests/threads/Rubric.alarm new file mode 100755 index 0000000..61abe85 --- /dev/null +++ b/pintos-env/pintos/tests/threads/Rubric.alarm @@ -0,0 +1,8 @@ +Functionality and robustness of alarm clock: +4 alarm-single +4 alarm-multiple +4 alarm-simultaneous +4 alarm-priority + +1 alarm-zero +1 alarm-negative diff --git a/pintos-env/pintos/tests/threads/Rubric.mlfqs b/pintos-env/pintos/tests/threads/Rubric.mlfqs new file mode 100755 index 0000000..f260091 --- /dev/null +++ b/pintos-env/pintos/tests/threads/Rubric.mlfqs @@ -0,0 +1,14 @@ +Functionality of advanced scheduler: +5 mlfqs-load-1 +5 mlfqs-load-60 +3 mlfqs-load-avg + +5 mlfqs-recent-1 + +5 mlfqs-fair-2 +3 mlfqs-fair-20 + +4 mlfqs-nice-2 +2 mlfqs-nice-10 + +5 mlfqs-block diff --git a/pintos-env/pintos/tests/threads/Rubric.priority b/pintos-env/pintos/tests/threads/Rubric.priority new file mode 100755 index 0000000..652bc99 --- /dev/null +++ b/pintos-env/pintos/tests/threads/Rubric.priority @@ -0,0 +1,15 @@ +Functionality of priority scheduler: +3 priority-change +3 priority-preempt + +3 priority-fifo +3 priority-sema +3 priority-condvar + +3 priority-donate-one +3 priority-donate-multiple +3 priority-donate-multiple2 +3 priority-donate-nest +5 priority-donate-chain +3 priority-donate-sema +3 priority-donate-lower diff --git a/pintos-env/pintos/tests/threads/alarm-multiple.ck b/pintos-env/pintos/tests/threads/alarm-multiple.ck new file mode 100755 index 0000000..fd83bcd --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-multiple.ck @@ -0,0 +1,4 @@ +# -*- perl -*- +use tests::tests; +use tests::threads::alarm; +check_alarm (7); diff --git a/pintos-env/pintos/tests/threads/alarm-negative.c b/pintos-env/pintos/tests/threads/alarm-negative.c new file mode 100755 index 0000000..aec52cf --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-negative.c @@ -0,0 +1,15 @@ +/* Tests timer_sleep(-100). Only requirement is that it not crash. */ + +#include +#include "tests/threads/tests.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +void +test_alarm_negative (void) +{ + timer_sleep (-100); + pass (); +} diff --git a/pintos-env/pintos/tests/threads/alarm-negative.ck b/pintos-env/pintos/tests/threads/alarm-negative.ck new file mode 100755 index 0000000..0d2bab0 --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-negative.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(alarm-negative) begin +(alarm-negative) PASS +(alarm-negative) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/alarm-priority.c b/pintos-env/pintos/tests/threads/alarm-priority.c new file mode 100755 index 0000000..2288ff6 --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-priority.c @@ -0,0 +1,58 @@ +/* Checks that when the alarm clock wakes up threads, the + higher-priority threads run first. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static thread_func alarm_priority_thread; +static int64_t wake_time; +static struct semaphore wait_sema; + +void +test_alarm_priority (void) +{ + int i; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + wake_time = timer_ticks () + 5 * TIMER_FREQ; + sema_init (&wait_sema, 0); + + for (i = 0; i < 10; i++) + { + int priority = PRI_DEFAULT - (i + 5) % 10 - 1; + char name[16]; + snprintf (name, sizeof name, "priority %d", priority); + thread_create (name, priority, alarm_priority_thread, NULL); + } + + thread_set_priority (PRI_MIN); + + for (i = 0; i < 10; i++) + sema_down (&wait_sema); +} + +static void +alarm_priority_thread (void *aux UNUSED) +{ + /* Busy-wait until the current time changes. */ + int64_t start_time = timer_ticks (); + while (timer_elapsed (start_time) == 0) + continue; + + /* Now we know we're at the very beginning of a timer tick, so + we can call timer_sleep() without worrying about races + between checking the time and a timer interrupt. */ + timer_sleep (wake_time - timer_ticks ()); + + /* Print a message on wake-up. */ + msg ("Thread %s woke up.", thread_name ()); + + sema_up (&wait_sema); +} diff --git a/pintos-env/pintos/tests/threads/alarm-priority.ck b/pintos-env/pintos/tests/threads/alarm-priority.ck new file mode 100755 index 0000000..b57c78b --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-priority.ck @@ -0,0 +1,19 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(alarm-priority) begin +(alarm-priority) Thread priority 30 woke up. +(alarm-priority) Thread priority 29 woke up. +(alarm-priority) Thread priority 28 woke up. +(alarm-priority) Thread priority 27 woke up. +(alarm-priority) Thread priority 26 woke up. +(alarm-priority) Thread priority 25 woke up. +(alarm-priority) Thread priority 24 woke up. +(alarm-priority) Thread priority 23 woke up. +(alarm-priority) Thread priority 22 woke up. +(alarm-priority) Thread priority 21 woke up. +(alarm-priority) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/alarm-simultaneous.c b/pintos-env/pintos/tests/threads/alarm-simultaneous.c new file mode 100755 index 0000000..844eea4 --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-simultaneous.c @@ -0,0 +1,94 @@ +/* Creates N threads, each of which sleeps a different, fixed + duration, M times. Records the wake-up order and verifies + that it is valid. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static void test_sleep (int thread_cnt, int iterations); + +void +test_alarm_simultaneous (void) +{ + test_sleep (3, 5); +} + +/* Information about the test. */ +struct sleep_test + { + int64_t start; /* Current time at start of test. */ + int iterations; /* Number of iterations per thread. */ + int *output_pos; /* Current position in output buffer. */ + }; + +static void sleeper (void *); + +/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */ +static void +test_sleep (int thread_cnt, int iterations) +{ + struct sleep_test test; + int *output; + int i; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations); + msg ("Each thread sleeps 10 ticks each time."); + msg ("Within an iteration, all threads should wake up on the same tick."); + + /* Allocate memory. */ + output = malloc (sizeof *output * iterations * thread_cnt * 2); + if (output == NULL) + PANIC ("couldn't allocate memory for test"); + + /* Initialize test. */ + test.start = timer_ticks () + 100; + test.iterations = iterations; + test.output_pos = output; + + /* Start threads. */ + ASSERT (output != NULL); + for (i = 0; i < thread_cnt; i++) + { + char name[16]; + snprintf (name, sizeof name, "thread %d", i); + thread_create (name, PRI_DEFAULT, sleeper, &test); + } + + /* Wait long enough for all the threads to finish. */ + timer_sleep (100 + iterations * 10 + 100); + + /* Print completion order. */ + msg ("iteration 0, thread 0: woke up after %d ticks", output[0]); + for (i = 1; i < test.output_pos - output; i++) + msg ("iteration %d, thread %d: woke up %d ticks later", + i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]); + + free (output); +} + +/* Sleeper thread. */ +static void +sleeper (void *test_) +{ + struct sleep_test *test = test_; + int i; + + /* Make sure we're at the beginning of a timer tick. */ + timer_sleep (1); + + for (i = 1; i <= test->iterations; i++) + { + int64_t sleep_until = test->start + i * 10; + timer_sleep (sleep_until - timer_ticks ()); + *test->output_pos++ = timer_ticks () - test->start; + thread_yield (); + } +} diff --git a/pintos-env/pintos/tests/threads/alarm-simultaneous.ck b/pintos-env/pintos/tests/threads/alarm-simultaneous.ck new file mode 100755 index 0000000..406b8b0 --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-simultaneous.ck @@ -0,0 +1,27 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(alarm-simultaneous) begin +(alarm-simultaneous) Creating 3 threads to sleep 5 times each. +(alarm-simultaneous) Each thread sleeps 10 ticks each time. +(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick. +(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks +(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later +(alarm-simultaneous) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/alarm-single.ck b/pintos-env/pintos/tests/threads/alarm-single.ck new file mode 100755 index 0000000..31215df --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-single.ck @@ -0,0 +1,4 @@ +# -*- perl -*- +use tests::tests; +use tests::threads::alarm; +check_alarm (1); diff --git a/pintos-env/pintos/tests/threads/alarm-wait.c b/pintos-env/pintos/tests/threads/alarm-wait.c new file mode 100755 index 0000000..37d3afc --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-wait.c @@ -0,0 +1,152 @@ +/* Creates N threads, each of which sleeps a different, fixed + duration, M times. Records the wake-up order and verifies + that it is valid. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static void test_sleep (int thread_cnt, int iterations); + +void +test_alarm_single (void) +{ + test_sleep (5, 1); +} + +void +test_alarm_multiple (void) +{ + test_sleep (5, 7); +} + +/* Information about the test. */ +struct sleep_test + { + int64_t start; /* Current time at start of test. */ + int iterations; /* Number of iterations per thread. */ + + /* Output. */ + struct lock output_lock; /* Lock protecting output buffer. */ + int *output_pos; /* Current position in output buffer. */ + }; + +/* Information about an individual thread in the test. */ +struct sleep_thread + { + struct sleep_test *test; /* Info shared between all threads. */ + int id; /* Sleeper ID. */ + int duration; /* Number of ticks to sleep. */ + int iterations; /* Iterations counted so far. */ + }; + +static void sleeper (void *); + +/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */ +static void +test_sleep (int thread_cnt, int iterations) +{ + struct sleep_test test; + struct sleep_thread *threads; + int *output, *op; + int product; + int i; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations); + msg ("Thread 0 sleeps 10 ticks each time,"); + msg ("thread 1 sleeps 20 ticks each time, and so on."); + msg ("If successful, product of iteration count and"); + msg ("sleep duration will appear in nondescending order."); + + /* Allocate memory. */ + threads = malloc (sizeof *threads * thread_cnt); + output = malloc (sizeof *output * iterations * thread_cnt * 2); + if (threads == NULL || output == NULL) + PANIC ("couldn't allocate memory for test"); + + /* Initialize test. */ + test.start = timer_ticks () + 100; + test.iterations = iterations; + lock_init (&test.output_lock); + test.output_pos = output; + + /* Start threads. */ + ASSERT (output != NULL); + for (i = 0; i < thread_cnt; i++) + { + struct sleep_thread *t = threads + i; + char name[16]; + + t->test = &test; + t->id = i; + t->duration = (i + 1) * 10; + t->iterations = 0; + + snprintf (name, sizeof name, "thread %d", i); + thread_create (name, PRI_DEFAULT, sleeper, t); + } + + /* Wait long enough for all the threads to finish. */ + timer_sleep (100 + thread_cnt * iterations * 10 + 100); + + /* Acquire the output lock in case some rogue thread is still + running. */ + lock_acquire (&test.output_lock); + + /* Print completion order. */ + product = 0; + for (op = output; op < test.output_pos; op++) + { + struct sleep_thread *t; + int new_prod; + + ASSERT (*op >= 0 && *op < thread_cnt); + t = threads + *op; + + new_prod = ++t->iterations * t->duration; + + msg ("thread %d: duration=%d, iteration=%d, product=%d", + t->id, t->duration, t->iterations, new_prod); + + if (new_prod >= product) + product = new_prod; + else + fail ("thread %d woke up out of order (%d > %d)!", + t->id, product, new_prod); + } + + /* Verify that we had the proper number of wakeups. */ + for (i = 0; i < thread_cnt; i++) + if (threads[i].iterations != iterations) + fail ("thread %d woke up %d times instead of %d", + i, threads[i].iterations, iterations); + + lock_release (&test.output_lock); + free (output); + free (threads); +} + +/* Sleeper thread. */ +static void +sleeper (void *t_) +{ + struct sleep_thread *t = t_; + struct sleep_test *test = t->test; + int i; + + for (i = 1; i <= test->iterations; i++) + { + int64_t sleep_until = test->start + i * t->duration; + timer_sleep (sleep_until - timer_ticks ()); + lock_acquire (&test->output_lock); + *test->output_pos++ = t->id; + lock_release (&test->output_lock); + } +} diff --git a/pintos-env/pintos/tests/threads/alarm-zero.c b/pintos-env/pintos/tests/threads/alarm-zero.c new file mode 100755 index 0000000..c8a3ee2 --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-zero.c @@ -0,0 +1,15 @@ +/* Tests timer_sleep(0), which should return immediately. */ + +#include +#include "tests/threads/tests.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +void +test_alarm_zero (void) +{ + timer_sleep (0); + pass (); +} diff --git a/pintos-env/pintos/tests/threads/alarm-zero.ck b/pintos-env/pintos/tests/threads/alarm-zero.ck new file mode 100755 index 0000000..a6b1a3c --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm-zero.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(alarm-zero) begin +(alarm-zero) PASS +(alarm-zero) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/alarm.pm b/pintos-env/pintos/tests/threads/alarm.pm new file mode 100755 index 0000000..84b3b7f --- /dev/null +++ b/pintos-env/pintos/tests/threads/alarm.pm @@ -0,0 +1,32 @@ +sub check_alarm { + my ($iterations) = @_; + our ($test); + + @output = read_text_file ("$test.output"); + common_checks ("run", @output); + + my (@products); + for (my ($i) = 0; $i < $iterations; $i++) { + for (my ($t) = 0; $t < 5; $t++) { + push (@products, ($i + 1) * ($t + 1) * 10); + } + } + @products = sort {$a <=> $b} @products; + + local ($_); + foreach (@output) { + fail $_ if /out of order/i; + + my ($p) = /product=(\d+)$/; + next if !defined $p; + + my ($q) = shift (@products); + fail "Too many wakeups.\n" if !defined $q; + fail "Out of order wakeups ($p vs. $q).\n" if $p != $q; # FIXME + } + fail scalar (@products) . " fewer wakeups than expected.\n" + if @products != 0; + pass; +} + +1; diff --git a/pintos-env/pintos/tests/threads/mlfqs-block.c b/pintos-env/pintos/tests/threads/mlfqs-block.c new file mode 100755 index 0000000..6d4992d --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-block.c @@ -0,0 +1,64 @@ +/* Checks that recent_cpu and priorities are updated for blocked + threads. + + The main thread sleeps for 25 seconds, spins for 5 seconds, + then releases a lock. The "block" thread spins for 20 seconds + then attempts to acquire the lock, which will block for 10 + seconds (until the main thread releases it). If recent_cpu + decays properly while the "block" thread sleeps, then the + block thread should be immediately scheduled when the main + thread releases the lock. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static void block_thread (void *lock_); + +void +test_mlfqs_block (void) +{ + int64_t start_time; + struct lock lock; + + ASSERT (thread_mlfqs); + + msg ("Main thread acquiring lock."); + lock_init (&lock); + lock_acquire (&lock); + + msg ("Main thread creating block thread, sleeping 25 seconds..."); + thread_create ("block", PRI_DEFAULT, block_thread, &lock); + timer_sleep (25 * TIMER_FREQ); + + msg ("Main thread spinning for 5 seconds..."); + start_time = timer_ticks (); + while (timer_elapsed (start_time) < 5 * TIMER_FREQ) + continue; + + msg ("Main thread releasing lock."); + lock_release (&lock); + + msg ("Block thread should have already acquired lock."); +} + +static void +block_thread (void *lock_) +{ + struct lock *lock = lock_; + int64_t start_time; + + msg ("Block thread spinning for 20 seconds..."); + start_time = timer_ticks (); + while (timer_elapsed (start_time) < 20 * TIMER_FREQ) + continue; + + msg ("Block thread acquiring lock..."); + lock_acquire (lock); + + msg ("...got it."); +} diff --git a/pintos-env/pintos/tests/threads/mlfqs-block.ck b/pintos-env/pintos/tests/threads/mlfqs-block.ck new file mode 100755 index 0000000..8833a3a --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-block.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(mlfqs-block) begin +(mlfqs-block) Main thread acquiring lock. +(mlfqs-block) Main thread creating block thread, sleeping 25 seconds... +(mlfqs-block) Block thread spinning for 20 seconds... +(mlfqs-block) Block thread acquiring lock... +(mlfqs-block) Main thread spinning for 5 seconds... +(mlfqs-block) Main thread releasing lock. +(mlfqs-block) ...got it. +(mlfqs-block) Block thread should have already acquired lock. +(mlfqs-block) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/mlfqs-fair-2.ck b/pintos-env/pintos/tests/threads/mlfqs-fair-2.ck new file mode 100755 index 0000000..5b19ff1 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-fair-2.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::threads::mlfqs; + +check_mlfqs_fair ([0, 0], 50); diff --git a/pintos-env/pintos/tests/threads/mlfqs-fair-20.ck b/pintos-env/pintos/tests/threads/mlfqs-fair-20.ck new file mode 100755 index 0000000..bb4d051 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-fair-20.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::threads::mlfqs; + +check_mlfqs_fair ([(0) x 20], 20); diff --git a/pintos-env/pintos/tests/threads/mlfqs-fair.c b/pintos-env/pintos/tests/threads/mlfqs-fair.c new file mode 100755 index 0000000..3b1bea5 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-fair.c @@ -0,0 +1,124 @@ +/* Measures the correctness of the "nice" implementation. + + The "fair" tests run either 2 or 20 threads all niced to 0. + The threads should all receive approximately the same number + of ticks. Each test runs for 30 seconds, so the ticks should + also sum to approximately 30 * 100 == 3000 ticks. + + The mlfqs-nice-2 test runs 2 threads, one with nice 0, the + other with nice 5, which should receive 1,904 and 1,096 ticks, + respectively, over 30 seconds. + + The mlfqs-nice-10 test runs 10 threads with nice 0 through 9. + They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40, + and 8 ticks, respectively, over 30 seconds. + + (The above are computed via simulation in mlfqs.pm.) */ + +#include +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/palloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step); + +void +test_mlfqs_fair_2 (void) +{ + test_mlfqs_fair (2, 0, 0); +} + +void +test_mlfqs_fair_20 (void) +{ + test_mlfqs_fair (20, 0, 0); +} + +void +test_mlfqs_nice_2 (void) +{ + test_mlfqs_fair (2, 0, 5); +} + +void +test_mlfqs_nice_10 (void) +{ + test_mlfqs_fair (10, 0, 1); +} + +#define MAX_THREAD_CNT 20 + +struct thread_info + { + int64_t start_time; + int tick_count; + int nice; + }; + +static void load_thread (void *aux); + +static void +test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step) +{ + struct thread_info info[MAX_THREAD_CNT]; + int64_t start_time; + int nice; + int i; + + ASSERT (thread_mlfqs); + ASSERT (thread_cnt <= MAX_THREAD_CNT); + ASSERT (nice_min >= -10); + ASSERT (nice_step >= 0); + ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20); + + thread_set_nice (-20); + + start_time = timer_ticks (); + msg ("Starting %d threads...", thread_cnt); + nice = nice_min; + for (i = 0; i < thread_cnt; i++) + { + struct thread_info *ti = &info[i]; + char name[16]; + + ti->start_time = start_time; + ti->tick_count = 0; + ti->nice = nice; + + snprintf(name, sizeof name, "load %d", i); + thread_create (name, PRI_DEFAULT, load_thread, ti); + + nice += nice_step; + } + msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time)); + + msg ("Sleeping 40 seconds to let threads run, please wait..."); + timer_sleep (40 * TIMER_FREQ); + + for (i = 0; i < thread_cnt; i++) + msg ("Thread %d received %d ticks.", i, info[i].tick_count); +} + +static void +load_thread (void *ti_) +{ + struct thread_info *ti = ti_; + int64_t sleep_time = 5 * TIMER_FREQ; + int64_t spin_time = sleep_time + 30 * TIMER_FREQ; + int64_t last_time = 0; + + thread_set_nice (ti->nice); + timer_sleep (sleep_time - timer_elapsed (ti->start_time)); + while (timer_elapsed (ti->start_time) < spin_time) + { + int64_t cur_time = timer_ticks (); + if (cur_time != last_time) + ti->tick_count++; + last_time = cur_time; + } +} diff --git a/pintos-env/pintos/tests/threads/mlfqs-load-1.c b/pintos-env/pintos/tests/threads/mlfqs-load-1.c new file mode 100755 index 0000000..a39eea2 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-load-1.c @@ -0,0 +1,60 @@ +/* Verifies that a single busy thread raises the load average to + 0.5 in 38 to 45 seconds. The expected time is 42 seconds, as + you can verify: + perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"' + + Then, verifies that 10 seconds of inactivity drop the load + average back below 0.5 again. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +void +test_mlfqs_load_1 (void) +{ + int64_t start_time; + int elapsed; + int load_avg; + + ASSERT (thread_mlfqs); + + msg ("spinning for up to 45 seconds, please wait..."); + + start_time = timer_ticks (); + for (;;) + { + load_avg = thread_get_load_avg (); + ASSERT (load_avg >= 0); + elapsed = timer_elapsed (start_time) / TIMER_FREQ; + if (load_avg > 100) + fail ("load average is %d.%02d " + "but should be between 0 and 1 (after %d seconds)", + load_avg / 100, load_avg % 100, elapsed); + else if (load_avg > 50) + break; + else if (elapsed > 45) + fail ("load average stayed below 0.5 for more than 45 seconds"); + } + + if (elapsed < 38) + fail ("load average took only %d seconds to rise above 0.5", elapsed); + msg ("load average rose to 0.5 after %d seconds", elapsed); + + msg ("sleeping for another 10 seconds, please wait..."); + timer_sleep (TIMER_FREQ * 10); + + load_avg = thread_get_load_avg (); + if (load_avg < 0) + fail ("load average fell below 0"); + if (load_avg > 50) + fail ("load average stayed above 0.5 for more than 10 seconds"); + msg ("load average fell back below 0.5 (to %d.%02d)", + load_avg / 100, load_avg % 100); + + pass (); +} diff --git a/pintos-env/pintos/tests/threads/mlfqs-load-1.ck b/pintos-env/pintos/tests/threads/mlfqs-load-1.ck new file mode 100755 index 0000000..faf0ffa --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-load-1.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; + +our ($test); +my (@output) = read_text_file ("$test.output"); + +common_checks ("run", @output); + +@output = get_core_output ("run", @output); +fail "missing PASS in output" + unless grep ($_ eq '(mlfqs-load-1) PASS', @output); + +pass; diff --git a/pintos-env/pintos/tests/threads/mlfqs-load-60.c b/pintos-env/pintos/tests/threads/mlfqs-load-60.c new file mode 100755 index 0000000..b6a3eb6 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-load-60.c @@ -0,0 +1,155 @@ +/* Starts 60 threads that each sleep for 10 seconds, then spin in + a tight loop for 60 seconds, and sleep for another 60 seconds. + Every 2 seconds after the initial sleep, the main thread + prints the load average. + + The expected output is this (some margin of error is allowed): + + After 0 seconds, load average=1.00. + After 2 seconds, load average=2.95. + After 4 seconds, load average=4.84. + After 6 seconds, load average=6.66. + After 8 seconds, load average=8.42. + After 10 seconds, load average=10.13. + After 12 seconds, load average=11.78. + After 14 seconds, load average=13.37. + After 16 seconds, load average=14.91. + After 18 seconds, load average=16.40. + After 20 seconds, load average=17.84. + After 22 seconds, load average=19.24. + After 24 seconds, load average=20.58. + After 26 seconds, load average=21.89. + After 28 seconds, load average=23.15. + After 30 seconds, load average=24.37. + After 32 seconds, load average=25.54. + After 34 seconds, load average=26.68. + After 36 seconds, load average=27.78. + After 38 seconds, load average=28.85. + After 40 seconds, load average=29.88. + After 42 seconds, load average=30.87. + After 44 seconds, load average=31.84. + After 46 seconds, load average=32.77. + After 48 seconds, load average=33.67. + After 50 seconds, load average=34.54. + After 52 seconds, load average=35.38. + After 54 seconds, load average=36.19. + After 56 seconds, load average=36.98. + After 58 seconds, load average=37.74. + After 60 seconds, load average=37.48. + After 62 seconds, load average=36.24. + After 64 seconds, load average=35.04. + After 66 seconds, load average=33.88. + After 68 seconds, load average=32.76. + After 70 seconds, load average=31.68. + After 72 seconds, load average=30.63. + After 74 seconds, load average=29.62. + After 76 seconds, load average=28.64. + After 78 seconds, load average=27.69. + After 80 seconds, load average=26.78. + After 82 seconds, load average=25.89. + After 84 seconds, load average=25.04. + After 86 seconds, load average=24.21. + After 88 seconds, load average=23.41. + After 90 seconds, load average=22.64. + After 92 seconds, load average=21.89. + After 94 seconds, load average=21.16. + After 96 seconds, load average=20.46. + After 98 seconds, load average=19.79. + After 100 seconds, load average=19.13. + After 102 seconds, load average=18.50. + After 104 seconds, load average=17.89. + After 106 seconds, load average=17.30. + After 108 seconds, load average=16.73. + After 110 seconds, load average=16.17. + After 112 seconds, load average=15.64. + After 114 seconds, load average=15.12. + After 116 seconds, load average=14.62. + After 118 seconds, load average=14.14. + After 120 seconds, load average=13.67. + After 122 seconds, load average=13.22. + After 124 seconds, load average=12.78. + After 126 seconds, load average=12.36. + After 128 seconds, load average=11.95. + After 130 seconds, load average=11.56. + After 132 seconds, load average=11.17. + After 134 seconds, load average=10.80. + After 136 seconds, load average=10.45. + After 138 seconds, load average=10.10. + After 140 seconds, load average=9.77. + After 142 seconds, load average=9.45. + After 144 seconds, load average=9.13. + After 146 seconds, load average=8.83. + After 148 seconds, load average=8.54. + After 150 seconds, load average=8.26. + After 152 seconds, load average=7.98. + After 154 seconds, load average=7.72. + After 156 seconds, load average=7.47. + After 158 seconds, load average=7.22. + After 160 seconds, load average=6.98. + After 162 seconds, load average=6.75. + After 164 seconds, load average=6.53. + After 166 seconds, load average=6.31. + After 168 seconds, load average=6.10. + After 170 seconds, load average=5.90. + After 172 seconds, load average=5.70. + After 174 seconds, load average=5.52. + After 176 seconds, load average=5.33. + After 178 seconds, load average=5.16. +*/ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static int64_t start_time; + +static void load_thread (void *aux); + +#define THREAD_CNT 60 + +void +test_mlfqs_load_60 (void) +{ + int i; + + ASSERT (thread_mlfqs); + + start_time = timer_ticks (); + msg ("Starting %d niced load threads...", THREAD_CNT); + for (i = 0; i < THREAD_CNT; i++) + { + char name[16]; + snprintf(name, sizeof name, "load %d", i); + thread_create (name, PRI_DEFAULT, load_thread, NULL); + } + msg ("Starting threads took %d seconds.", + timer_elapsed (start_time) / TIMER_FREQ); + + for (i = 0; i < 90; i++) + { + int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10); + int load_avg; + timer_sleep (sleep_until - timer_ticks ()); + load_avg = thread_get_load_avg (); + msg ("After %d seconds, load average=%d.%02d.", + i * 2, load_avg / 100, load_avg % 100); + } +} + +static void +load_thread (void *aux UNUSED) +{ + int64_t sleep_time = 10 * TIMER_FREQ; + int64_t spin_time = sleep_time + 60 * TIMER_FREQ; + int64_t exit_time = spin_time + 60 * TIMER_FREQ; + + thread_set_nice (20); + timer_sleep (sleep_time - timer_elapsed (start_time)); + while (timer_elapsed (start_time) < spin_time) + continue; + timer_sleep (exit_time - timer_elapsed (start_time)); +} diff --git a/pintos-env/pintos/tests/threads/mlfqs-load-60.ck b/pintos-env/pintos/tests/threads/mlfqs-load-60.ck new file mode 100755 index 0000000..cb69220 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-load-60.ck @@ -0,0 +1,36 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::threads::mlfqs; + +our ($test); + +my (@output) = read_text_file ("$test.output"); +common_checks ("run", @output); +@output = get_core_output ("run", @output); + +# Get actual values. +local ($_); +my (@actual); +foreach (@output) { + my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./ + or next; + $actual[$t] = $load_avg; +} + +# Calculate expected values. +my ($load_avg) = 0; +my ($recent) = 0; +my (@expected); +for (my ($t) = 0; $t < 180; $t++) { + my ($ready) = $t < 60 ? 60 : 0; + $load_avg = (59/60) * $load_avg + (1/60) * $ready; + $expected[$t] = $load_avg; +} + +mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2], + "Some load average values were missing or " + . "differed from those expected " + . "by more than 3.5."); +pass; diff --git a/pintos-env/pintos/tests/threads/mlfqs-load-avg.c b/pintos-env/pintos/tests/threads/mlfqs-load-avg.c new file mode 100755 index 0000000..50e83e2 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-load-avg.c @@ -0,0 +1,167 @@ +/* Starts 60 threads numbered 0 through 59. Thread #i sleeps for + (10+i) seconds, then spins in a loop for 60 seconds, then + sleeps until a total of 120 seconds have passed. Every 2 + seconds, starting 10 seconds in, the main thread prints the + load average. + + The expected output is listed below. Some margin of error is + allowed. + + If your implementation fails this test but passes most other + tests, then consider whether you are doing too much work in + the timer interrupt. If the timer interrupt handler takes too + long, then the test's main thread will not have enough time to + do its own work (printing a message) and go back to sleep + before the next tick arrives. Then the main thread will be + ready, instead of sleeping, when the tick arrives, + artificially driving up the load average. + + After 0 seconds, load average=0.00. + After 2 seconds, load average=0.05. + After 4 seconds, load average=0.16. + After 6 seconds, load average=0.34. + After 8 seconds, load average=0.58. + After 10 seconds, load average=0.87. + After 12 seconds, load average=1.22. + After 14 seconds, load average=1.63. + After 16 seconds, load average=2.09. + After 18 seconds, load average=2.60. + After 20 seconds, load average=3.16. + After 22 seconds, load average=3.76. + After 24 seconds, load average=4.42. + After 26 seconds, load average=5.11. + After 28 seconds, load average=5.85. + After 30 seconds, load average=6.63. + After 32 seconds, load average=7.46. + After 34 seconds, load average=8.32. + After 36 seconds, load average=9.22. + After 38 seconds, load average=10.15. + After 40 seconds, load average=11.12. + After 42 seconds, load average=12.13. + After 44 seconds, load average=13.16. + After 46 seconds, load average=14.23. + After 48 seconds, load average=15.33. + After 50 seconds, load average=16.46. + After 52 seconds, load average=17.62. + After 54 seconds, load average=18.81. + After 56 seconds, load average=20.02. + After 58 seconds, load average=21.26. + After 60 seconds, load average=22.52. + After 62 seconds, load average=23.71. + After 64 seconds, load average=24.80. + After 66 seconds, load average=25.78. + After 68 seconds, load average=26.66. + After 70 seconds, load average=27.45. + After 72 seconds, load average=28.14. + After 74 seconds, load average=28.75. + After 76 seconds, load average=29.27. + After 78 seconds, load average=29.71. + After 80 seconds, load average=30.06. + After 82 seconds, load average=30.34. + After 84 seconds, load average=30.55. + After 86 seconds, load average=30.68. + After 88 seconds, load average=30.74. + After 90 seconds, load average=30.73. + After 92 seconds, load average=30.66. + After 94 seconds, load average=30.52. + After 96 seconds, load average=30.32. + After 98 seconds, load average=30.06. + After 100 seconds, load average=29.74. + After 102 seconds, load average=29.37. + After 104 seconds, load average=28.95. + After 106 seconds, load average=28.47. + After 108 seconds, load average=27.94. + After 110 seconds, load average=27.36. + After 112 seconds, load average=26.74. + After 114 seconds, load average=26.07. + After 116 seconds, load average=25.36. + After 118 seconds, load average=24.60. + After 120 seconds, load average=23.81. + After 122 seconds, load average=23.02. + After 124 seconds, load average=22.26. + After 126 seconds, load average=21.52. + After 128 seconds, load average=20.81. + After 130 seconds, load average=20.12. + After 132 seconds, load average=19.46. + After 134 seconds, load average=18.81. + After 136 seconds, load average=18.19. + After 138 seconds, load average=17.59. + After 140 seconds, load average=17.01. + After 142 seconds, load average=16.45. + After 144 seconds, load average=15.90. + After 146 seconds, load average=15.38. + After 148 seconds, load average=14.87. + After 150 seconds, load average=14.38. + After 152 seconds, load average=13.90. + After 154 seconds, load average=13.44. + After 156 seconds, load average=13.00. + After 158 seconds, load average=12.57. + After 160 seconds, load average=12.15. + After 162 seconds, load average=11.75. + After 164 seconds, load average=11.36. + After 166 seconds, load average=10.99. + After 168 seconds, load average=10.62. + After 170 seconds, load average=10.27. + After 172 seconds, load average=9.93. + After 174 seconds, load average=9.61. + After 176 seconds, load average=9.29. + After 178 seconds, load average=8.98. +*/ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static int64_t start_time; + +static void load_thread (void *seq_no); + +#define THREAD_CNT 60 + +void +test_mlfqs_load_avg (void) +{ + int i; + + ASSERT (thread_mlfqs); + + start_time = timer_ticks (); + msg ("Starting %d load threads...", THREAD_CNT); + for (i = 0; i < THREAD_CNT; i++) + { + char name[16]; + snprintf(name, sizeof name, "load %d", i); + thread_create (name, PRI_DEFAULT, load_thread, (void *) i); + } + msg ("Starting threads took %d seconds.", + timer_elapsed (start_time) / TIMER_FREQ); + thread_set_nice (-20); + + for (i = 0; i < 90; i++) + { + int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10); + int load_avg; + timer_sleep (sleep_until - timer_ticks ()); + load_avg = thread_get_load_avg (); + msg ("After %d seconds, load average=%d.%02d.", + i * 2, load_avg / 100, load_avg % 100); + } +} + +static void +load_thread (void *seq_no_) +{ + int seq_no = (int) seq_no_; + int sleep_time = TIMER_FREQ * (10 + seq_no); + int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT; + int exit_time = TIMER_FREQ * (THREAD_CNT * 2); + + timer_sleep (sleep_time - timer_elapsed (start_time)); + while (timer_elapsed (start_time) < spin_time) + continue; + timer_sleep (exit_time - timer_elapsed (start_time)); +} diff --git a/pintos-env/pintos/tests/threads/mlfqs-load-avg.ck b/pintos-env/pintos/tests/threads/mlfqs-load-avg.ck new file mode 100755 index 0000000..2254d05 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-load-avg.ck @@ -0,0 +1,36 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::threads::mlfqs; + +our ($test); +my (@output) = read_text_file ("$test.output"); + +common_checks ("run", @output); +@output = get_core_output ("run", @output); + +# Get actual values. +local ($_); +my (@actual); +foreach (@output) { + my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./ + or next; + $actual[$t] = $load_avg; +} + +# Calculate expected values. +my ($load_avg) = 0; +my ($recent) = 0; +my (@expected); +for (my ($t) = 0; $t < 180; $t++) { + my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0; + $load_avg = (59/60) * $load_avg + (1/60) * $ready; + $expected[$t] = $load_avg; +} + +mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2], + "Some load average values were missing or " + . "differed from those expected " + . "by more than 2.5."); +pass; diff --git a/pintos-env/pintos/tests/threads/mlfqs-nice-10.ck b/pintos-env/pintos/tests/threads/mlfqs-nice-10.ck new file mode 100755 index 0000000..53e0abe --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-nice-10.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::threads::mlfqs; + +check_mlfqs_fair ([0...9], 25); diff --git a/pintos-env/pintos/tests/threads/mlfqs-nice-2.ck b/pintos-env/pintos/tests/threads/mlfqs-nice-2.ck new file mode 100755 index 0000000..ada366b --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-nice-2.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::threads::mlfqs; + +check_mlfqs_fair ([0, 5], 50); diff --git a/pintos-env/pintos/tests/threads/mlfqs-recent-1.c b/pintos-env/pintos/tests/threads/mlfqs-recent-1.c new file mode 100755 index 0000000..4258671 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-recent-1.c @@ -0,0 +1,144 @@ +/* Checks that recent_cpu is calculated properly for the case of + a single ready process. + + The expected output is this (some margin of error is allowed): + + After 2 seconds, recent_cpu is 6.40, load_avg is 0.03. + After 4 seconds, recent_cpu is 12.60, load_avg is 0.07. + After 6 seconds, recent_cpu is 18.61, load_avg is 0.10. + After 8 seconds, recent_cpu is 24.44, load_avg is 0.13. + After 10 seconds, recent_cpu is 30.08, load_avg is 0.15. + After 12 seconds, recent_cpu is 35.54, load_avg is 0.18. + After 14 seconds, recent_cpu is 40.83, load_avg is 0.21. + After 16 seconds, recent_cpu is 45.96, load_avg is 0.24. + After 18 seconds, recent_cpu is 50.92, load_avg is 0.26. + After 20 seconds, recent_cpu is 55.73, load_avg is 0.29. + After 22 seconds, recent_cpu is 60.39, load_avg is 0.31. + After 24 seconds, recent_cpu is 64.90, load_avg is 0.33. + After 26 seconds, recent_cpu is 69.27, load_avg is 0.35. + After 28 seconds, recent_cpu is 73.50, load_avg is 0.38. + After 30 seconds, recent_cpu is 77.60, load_avg is 0.40. + After 32 seconds, recent_cpu is 81.56, load_avg is 0.42. + After 34 seconds, recent_cpu is 85.40, load_avg is 0.44. + After 36 seconds, recent_cpu is 89.12, load_avg is 0.45. + After 38 seconds, recent_cpu is 92.72, load_avg is 0.47. + After 40 seconds, recent_cpu is 96.20, load_avg is 0.49. + After 42 seconds, recent_cpu is 99.57, load_avg is 0.51. + After 44 seconds, recent_cpu is 102.84, load_avg is 0.52. + After 46 seconds, recent_cpu is 106.00, load_avg is 0.54. + After 48 seconds, recent_cpu is 109.06, load_avg is 0.55. + After 50 seconds, recent_cpu is 112.02, load_avg is 0.57. + After 52 seconds, recent_cpu is 114.89, load_avg is 0.58. + After 54 seconds, recent_cpu is 117.66, load_avg is 0.60. + After 56 seconds, recent_cpu is 120.34, load_avg is 0.61. + After 58 seconds, recent_cpu is 122.94, load_avg is 0.62. + After 60 seconds, recent_cpu is 125.46, load_avg is 0.64. + After 62 seconds, recent_cpu is 127.89, load_avg is 0.65. + After 64 seconds, recent_cpu is 130.25, load_avg is 0.66. + After 66 seconds, recent_cpu is 132.53, load_avg is 0.67. + After 68 seconds, recent_cpu is 134.73, load_avg is 0.68. + After 70 seconds, recent_cpu is 136.86, load_avg is 0.69. + After 72 seconds, recent_cpu is 138.93, load_avg is 0.70. + After 74 seconds, recent_cpu is 140.93, load_avg is 0.71. + After 76 seconds, recent_cpu is 142.86, load_avg is 0.72. + After 78 seconds, recent_cpu is 144.73, load_avg is 0.73. + After 80 seconds, recent_cpu is 146.54, load_avg is 0.74. + After 82 seconds, recent_cpu is 148.29, load_avg is 0.75. + After 84 seconds, recent_cpu is 149.99, load_avg is 0.76. + After 86 seconds, recent_cpu is 151.63, load_avg is 0.76. + After 88 seconds, recent_cpu is 153.21, load_avg is 0.77. + After 90 seconds, recent_cpu is 154.75, load_avg is 0.78. + After 92 seconds, recent_cpu is 156.23, load_avg is 0.79. + After 94 seconds, recent_cpu is 157.67, load_avg is 0.79. + After 96 seconds, recent_cpu is 159.06, load_avg is 0.80. + After 98 seconds, recent_cpu is 160.40, load_avg is 0.81. + After 100 seconds, recent_cpu is 161.70, load_avg is 0.81. + After 102 seconds, recent_cpu is 162.96, load_avg is 0.82. + After 104 seconds, recent_cpu is 164.18, load_avg is 0.83. + After 106 seconds, recent_cpu is 165.35, load_avg is 0.83. + After 108 seconds, recent_cpu is 166.49, load_avg is 0.84. + After 110 seconds, recent_cpu is 167.59, load_avg is 0.84. + After 112 seconds, recent_cpu is 168.66, load_avg is 0.85. + After 114 seconds, recent_cpu is 169.69, load_avg is 0.85. + After 116 seconds, recent_cpu is 170.69, load_avg is 0.86. + After 118 seconds, recent_cpu is 171.65, load_avg is 0.86. + After 120 seconds, recent_cpu is 172.58, load_avg is 0.87. + After 122 seconds, recent_cpu is 173.49, load_avg is 0.87. + After 124 seconds, recent_cpu is 174.36, load_avg is 0.88. + After 126 seconds, recent_cpu is 175.20, load_avg is 0.88. + After 128 seconds, recent_cpu is 176.02, load_avg is 0.88. + After 130 seconds, recent_cpu is 176.81, load_avg is 0.89. + After 132 seconds, recent_cpu is 177.57, load_avg is 0.89. + After 134 seconds, recent_cpu is 178.31, load_avg is 0.89. + After 136 seconds, recent_cpu is 179.02, load_avg is 0.90. + After 138 seconds, recent_cpu is 179.72, load_avg is 0.90. + After 140 seconds, recent_cpu is 180.38, load_avg is 0.90. + After 142 seconds, recent_cpu is 181.03, load_avg is 0.91. + After 144 seconds, recent_cpu is 181.65, load_avg is 0.91. + After 146 seconds, recent_cpu is 182.26, load_avg is 0.91. + After 148 seconds, recent_cpu is 182.84, load_avg is 0.92. + After 150 seconds, recent_cpu is 183.41, load_avg is 0.92. + After 152 seconds, recent_cpu is 183.96, load_avg is 0.92. + After 154 seconds, recent_cpu is 184.49, load_avg is 0.92. + After 156 seconds, recent_cpu is 185.00, load_avg is 0.93. + After 158 seconds, recent_cpu is 185.49, load_avg is 0.93. + After 160 seconds, recent_cpu is 185.97, load_avg is 0.93. + After 162 seconds, recent_cpu is 186.43, load_avg is 0.93. + After 164 seconds, recent_cpu is 186.88, load_avg is 0.94. + After 166 seconds, recent_cpu is 187.31, load_avg is 0.94. + After 168 seconds, recent_cpu is 187.73, load_avg is 0.94. + After 170 seconds, recent_cpu is 188.14, load_avg is 0.94. + After 172 seconds, recent_cpu is 188.53, load_avg is 0.94. + After 174 seconds, recent_cpu is 188.91, load_avg is 0.95. + After 176 seconds, recent_cpu is 189.27, load_avg is 0.95. + After 178 seconds, recent_cpu is 189.63, load_avg is 0.95. + After 180 seconds, recent_cpu is 189.97, load_avg is 0.95. +*/ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +/* Sensitive to assumption that recent_cpu updates happen exactly + when timer_ticks() % TIMER_FREQ == 0. */ + +void +test_mlfqs_recent_1 (void) +{ + int64_t start_time; + int last_elapsed = 0; + + ASSERT (thread_mlfqs); + + do + { + msg ("Sleeping 10 seconds to allow recent_cpu to decay, please wait..."); + start_time = timer_ticks (); + timer_sleep (DIV_ROUND_UP (start_time, TIMER_FREQ) - start_time + + 10 * TIMER_FREQ); + } + while (thread_get_recent_cpu () > 700); + + start_time = timer_ticks (); + for (;;) + { + int elapsed = timer_elapsed (start_time); + if (elapsed % (TIMER_FREQ * 2) == 0 && elapsed > last_elapsed) + { + int recent_cpu = thread_get_recent_cpu (); + int load_avg = thread_get_load_avg (); + int elapsed_seconds = elapsed / TIMER_FREQ; + msg ("After %d seconds, recent_cpu is %d.%02d, load_avg is %d.%02d.", + elapsed_seconds, + recent_cpu / 100, recent_cpu % 100, + load_avg / 100, load_avg % 100); + if (elapsed_seconds >= 180) + break; + } + last_elapsed = elapsed; + } +} diff --git a/pintos-env/pintos/tests/threads/mlfqs-recent-1.ck b/pintos-env/pintos/tests/threads/mlfqs-recent-1.ck new file mode 100755 index 0000000..a2ba44d --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs-recent-1.ck @@ -0,0 +1,31 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::threads::mlfqs; + +our ($test); +my (@output) = read_text_file ("$test.output"); +common_checks ("run", @output); +@output = get_core_output ("run", @output); + +# Get actual values. +local ($_); +my (@actual); +foreach (@output) { + my ($t, $recent_cpu) = /After (\d+) seconds, recent_cpu is (\d+\.\d+),/ + or next; + $actual[$t] = $recent_cpu; +} + +# Calculate expected values. +my ($expected_load_avg, $expected_recent_cpu) + = mlfqs_expected_load ([(1) x 180], [(100) x 180]); +my (@expected) = @$expected_recent_cpu; + +# Compare actual and expected values. +mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2], + "Some recent_cpu values were missing or " + . "differed from those expected " + . "by more than 2.5."); +pass; diff --git a/pintos-env/pintos/tests/threads/mlfqs.pm b/pintos-env/pintos/tests/threads/mlfqs.pm new file mode 100755 index 0000000..184ac16 --- /dev/null +++ b/pintos-env/pintos/tests/threads/mlfqs.pm @@ -0,0 +1,146 @@ +# -*- perl -*- +use strict; +use warnings; + +sub mlfqs_expected_load { + my ($ready, $recent_delta) = @_; + my (@load_avg) = 0; + my (@recent_cpu) = 0; + my ($load_avg) = 0; + my ($recent_cpu) = 0; + for my $i (0...$#$ready) { + $load_avg = (59/60) * $load_avg + (1/60) * $ready->[$i]; + push (@load_avg, $load_avg); + + if (defined $recent_delta->[$i]) { + my ($twice_load) = $load_avg * 2; + my ($load_factor) = $twice_load / ($twice_load + 1); + $recent_cpu = ($recent_cpu + $recent_delta->[$i]) * $load_factor; + push (@recent_cpu, $recent_cpu); + } + } + return (\@load_avg, \@recent_cpu); +} + +sub mlfqs_expected_ticks { + my (@nice) = @_; + my ($thread_cnt) = scalar (@nice); + my (@recent_cpu) = (0) x $thread_cnt; + my (@slices) = (0) x $thread_cnt; + my (@fifo) = (0) x $thread_cnt; + my ($next_fifo) = 1; + my ($load_avg) = 0; + for my $i (1...750) { + if ($i % 25 == 0) { + # Update load average. + $load_avg = (59/60) * $load_avg + (1/60) * $thread_cnt; + + # Update recent_cpu. + my ($twice_load) = $load_avg * 2; + my ($load_factor) = $twice_load / ($twice_load + 1); + $recent_cpu[$_] = $recent_cpu[$_] * $load_factor + $nice[$_] + foreach 0...($thread_cnt - 1); + } + + # Update priorities. + my (@priority); + foreach my $j (0...($thread_cnt - 1)) { + my ($priority) = int ($recent_cpu[$j] / 4 + $nice[$j] * 2); + $priority = 0 if $priority < 0; + $priority = 63 if $priority > 63; + push (@priority, $priority); + } + + # Choose thread to run. + my $max = 0; + for my $j (1...$#priority) { + if ($priority[$j] < $priority[$max] + || ($priority[$j] == $priority[$max] + && $fifo[$j] < $fifo[$max])) { + $max = $j; + } + } + $fifo[$max] = $next_fifo++; + + # Run thread. + $recent_cpu[$max] += 4; + $slices[$max] += 4; + } + return @slices; +} + +sub check_mlfqs_fair { + my ($nice, $maxdiff) = @_; + our ($test); + my (@output) = read_text_file ("$test.output"); + common_checks ("run", @output); + @output = get_core_output ("run", @output); + + my (@actual); + local ($_); + foreach (@output) { + my ($id, $count) = /Thread (\d+) received (\d+) ticks\./ or next; + $actual[$id] = $count; + } + + my (@expected) = mlfqs_expected_ticks (@$nice); + mlfqs_compare ("thread", "%d", + \@actual, \@expected, $maxdiff, [0, $#$nice, 1], + "Some tick counts were missing or differed from those " + . "expected by more than $maxdiff."); + pass; +} + +sub mlfqs_compare { + my ($indep_var, $format, + $actual_ref, $expected_ref, $maxdiff, $t_range, $message) = @_; + my ($t_min, $t_max, $t_step) = @$t_range; + + my ($ok) = 1; + for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) { + my ($actual) = $actual_ref->[$t]; + my ($expected) = $expected_ref->[$t]; + $ok = 0, last + if !defined ($actual) || abs ($actual - $expected) > $maxdiff + .01; + } + return if $ok; + + print "$message\n"; + mlfqs_row ($indep_var, "actual", "<->", "expected", "explanation"); + mlfqs_row ("------", "--------", "---", "--------", '-' x 40); + for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) { + my ($actual) = $actual_ref->[$t]; + my ($expected) = $expected_ref->[$t]; + my ($diff, $rationale); + if (!defined $actual) { + $actual = 'undef' ; + $diff = ''; + $rationale = 'Missing value.'; + } else { + my ($delta) = abs ($actual - $expected); + if ($delta > $maxdiff + .01) { + my ($excess) = $delta - $maxdiff; + if ($actual > $expected) { + $diff = '>>>'; + $rationale = sprintf "Too big, by $format.", $excess; + } else { + $diff = '<<<'; + $rationale = sprintf "Too small, by $format.", $excess; + } + } else { + $diff = ' = '; + $rationale = ''; + } + $actual = sprintf ($format, $actual); + } + $expected = sprintf ($format, $expected); + mlfqs_row ($t, $actual, $diff, $expected, $rationale); + } + fail; +} + +sub mlfqs_row { + printf "%6s %8s %3s %-8s %s\n", @_; +} + +1; diff --git a/pintos-env/pintos/tests/threads/priority-change.c b/pintos-env/pintos/tests/threads/priority-change.c new file mode 100755 index 0000000..810b05a --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-change.c @@ -0,0 +1,31 @@ +/* Verifies that lowering a thread's priority so that it is no + longer the highest-priority thread in the system causes it to + yield immediately. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/thread.h" + +static thread_func changing_thread; + +void +test_priority_change (void) +{ + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + msg ("Creating a high-priority thread 2."); + thread_create ("thread 2", PRI_DEFAULT + 1, changing_thread, NULL); + msg ("Thread 2 should have just lowered its priority."); + thread_set_priority (PRI_DEFAULT - 2); + msg ("Thread 2 should have just exited."); +} + +static void +changing_thread (void *aux UNUSED) +{ + msg ("Thread 2 now lowering priority."); + thread_set_priority (PRI_DEFAULT - 1); + msg ("Thread 2 exiting."); +} diff --git a/pintos-env/pintos/tests/threads/priority-change.ck b/pintos-env/pintos/tests/threads/priority-change.ck new file mode 100755 index 0000000..f4d9b2f --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-change.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-change) begin +(priority-change) Creating a high-priority thread 2. +(priority-change) Thread 2 now lowering priority. +(priority-change) Thread 2 should have just lowered its priority. +(priority-change) Thread 2 exiting. +(priority-change) Thread 2 should have just exited. +(priority-change) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-condvar.c b/pintos-env/pintos/tests/threads/priority-condvar.c new file mode 100755 index 0000000..c1efb1b --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-condvar.c @@ -0,0 +1,53 @@ +/* Tests that cond_signal() wakes up the highest-priority thread + waiting in cond_wait(). */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static thread_func priority_condvar_thread; +static struct lock lock; +static struct condition condition; + +void +test_priority_condvar (void) +{ + int i; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + lock_init (&lock); + cond_init (&condition); + + thread_set_priority (PRI_MIN); + for (i = 0; i < 10; i++) + { + int priority = PRI_DEFAULT - (i + 7) % 10 - 1; + char name[16]; + snprintf (name, sizeof name, "priority %d", priority); + thread_create (name, priority, priority_condvar_thread, NULL); + } + + for (i = 0; i < 10; i++) + { + lock_acquire (&lock); + msg ("Signaling..."); + cond_signal (&condition, &lock); + lock_release (&lock); + } +} + +static void +priority_condvar_thread (void *aux UNUSED) +{ + msg ("Thread %s starting.", thread_name ()); + lock_acquire (&lock); + cond_wait (&condition, &lock); + msg ("Thread %s woke up.", thread_name ()); + lock_release (&lock); +} diff --git a/pintos-env/pintos/tests/threads/priority-condvar.ck b/pintos-env/pintos/tests/threads/priority-condvar.ck new file mode 100755 index 0000000..195c1ab --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-condvar.ck @@ -0,0 +1,39 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-condvar) begin +(priority-condvar) Thread priority 23 starting. +(priority-condvar) Thread priority 22 starting. +(priority-condvar) Thread priority 21 starting. +(priority-condvar) Thread priority 30 starting. +(priority-condvar) Thread priority 29 starting. +(priority-condvar) Thread priority 28 starting. +(priority-condvar) Thread priority 27 starting. +(priority-condvar) Thread priority 26 starting. +(priority-condvar) Thread priority 25 starting. +(priority-condvar) Thread priority 24 starting. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 30 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 29 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 28 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 27 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 26 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 25 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 24 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 23 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 22 woke up. +(priority-condvar) Signaling... +(priority-condvar) Thread priority 21 woke up. +(priority-condvar) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-donate-chain.c b/pintos-env/pintos/tests/threads/priority-donate-chain.c new file mode 100755 index 0000000..3ffabca --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-chain.c @@ -0,0 +1,114 @@ +/* The main thread set its priority to PRI_MIN and creates 7 threads + (thread 1..7) with priorities PRI_MIN + 3, 6, 9, 12, ... + The main thread initializes 8 locks: lock 0..7 and acquires lock 0. + + When thread[i] starts, it first acquires lock[i] (unless i == 7.) + Subsequently, thread[i] attempts to acquire lock[i-1], which is held by + thread[i-1], except for lock[0], which is held by the main thread. + Because the lock is held, thread[i] donates its priority to thread[i-1], + which donates to thread[i-2], and so on until the main thread + receives the donation. + + After threads[1..7] have been created and are blocked on locks[0..7], + the main thread releases lock[0], unblocking thread[1], and being + preempted by it. + Thread[1] then completes acquiring lock[0], then releases lock[0], + then releases lock[1], unblocking thread[2], etc. + Thread[7] finally acquires & releases lock[7] and exits, allowing + thread[6], then thread[5] etc. to run and exit until finally the + main thread exits. + + In addition, interloper threads are created at priority levels + p = PRI_MIN + 2, 5, 8, 11, ... which should not be run until the + corresponding thread with priority p + 1 has finished. + + Written by Godmar Back */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +#define NESTING_DEPTH 8 + +struct lock_pair + { + struct lock *second; + struct lock *first; + }; + +static thread_func donor_thread_func; +static thread_func interloper_thread_func; + +void +test_priority_donate_chain (void) +{ + int i; + struct lock locks[NESTING_DEPTH - 1]; + struct lock_pair lock_pairs[NESTING_DEPTH]; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + thread_set_priority (PRI_MIN); + + for (i = 0; i < NESTING_DEPTH - 1; i++) + lock_init (&locks[i]); + + lock_acquire (&locks[0]); + msg ("%s got lock.", thread_name ()); + + for (i = 1; i < NESTING_DEPTH; i++) + { + char name[16]; + int thread_priority; + + snprintf (name, sizeof name, "thread %d", i); + thread_priority = PRI_MIN + i * 3; + lock_pairs[i].first = i < NESTING_DEPTH - 1 ? locks + i: NULL; + lock_pairs[i].second = locks + i - 1; + + thread_create (name, thread_priority, donor_thread_func, lock_pairs + i); + msg ("%s should have priority %d. Actual priority: %d.", + thread_name (), thread_priority, thread_get_priority ()); + + snprintf (name, sizeof name, "interloper %d", i); + thread_create (name, thread_priority - 1, interloper_thread_func, NULL); + } + + lock_release (&locks[0]); + msg ("%s finishing with priority %d.", thread_name (), + thread_get_priority ()); +} + +static void +donor_thread_func (void *locks_) +{ + struct lock_pair *locks = locks_; + + if (locks->first) + lock_acquire (locks->first); + + lock_acquire (locks->second); + msg ("%s got lock", thread_name ()); + + lock_release (locks->second); + msg ("%s should have priority %d. Actual priority: %d", + thread_name (), (NESTING_DEPTH - 1) * 3, + thread_get_priority ()); + + if (locks->first) + lock_release (locks->first); + + msg ("%s finishing with priority %d.", thread_name (), + thread_get_priority ()); +} + +static void +interloper_thread_func (void *arg_ UNUSED) +{ + msg ("%s finished.", thread_name ()); +} + +// vim: sw=2 diff --git a/pintos-env/pintos/tests/threads/priority-donate-chain.ck b/pintos-env/pintos/tests/threads/priority-donate-chain.ck new file mode 100755 index 0000000..213e649 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-chain.ck @@ -0,0 +1,46 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-donate-chain) begin +(priority-donate-chain) main got lock. +(priority-donate-chain) main should have priority 3. Actual priority: 3. +(priority-donate-chain) main should have priority 6. Actual priority: 6. +(priority-donate-chain) main should have priority 9. Actual priority: 9. +(priority-donate-chain) main should have priority 12. Actual priority: 12. +(priority-donate-chain) main should have priority 15. Actual priority: 15. +(priority-donate-chain) main should have priority 18. Actual priority: 18. +(priority-donate-chain) main should have priority 21. Actual priority: 21. +(priority-donate-chain) thread 1 got lock +(priority-donate-chain) thread 1 should have priority 21. Actual priority: 21 +(priority-donate-chain) thread 2 got lock +(priority-donate-chain) thread 2 should have priority 21. Actual priority: 21 +(priority-donate-chain) thread 3 got lock +(priority-donate-chain) thread 3 should have priority 21. Actual priority: 21 +(priority-donate-chain) thread 4 got lock +(priority-donate-chain) thread 4 should have priority 21. Actual priority: 21 +(priority-donate-chain) thread 5 got lock +(priority-donate-chain) thread 5 should have priority 21. Actual priority: 21 +(priority-donate-chain) thread 6 got lock +(priority-donate-chain) thread 6 should have priority 21. Actual priority: 21 +(priority-donate-chain) thread 7 got lock +(priority-donate-chain) thread 7 should have priority 21. Actual priority: 21 +(priority-donate-chain) thread 7 finishing with priority 21. +(priority-donate-chain) interloper 7 finished. +(priority-donate-chain) thread 6 finishing with priority 18. +(priority-donate-chain) interloper 6 finished. +(priority-donate-chain) thread 5 finishing with priority 15. +(priority-donate-chain) interloper 5 finished. +(priority-donate-chain) thread 4 finishing with priority 12. +(priority-donate-chain) interloper 4 finished. +(priority-donate-chain) thread 3 finishing with priority 9. +(priority-donate-chain) interloper 3 finished. +(priority-donate-chain) thread 2 finishing with priority 6. +(priority-donate-chain) interloper 2 finished. +(priority-donate-chain) thread 1 finishing with priority 3. +(priority-donate-chain) interloper 1 finished. +(priority-donate-chain) main finishing with priority 0. +(priority-donate-chain) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-donate-lower.c b/pintos-env/pintos/tests/threads/priority-donate-lower.c new file mode 100755 index 0000000..4965d75 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-lower.c @@ -0,0 +1,51 @@ +/* The main thread acquires a lock. Then it creates a + higher-priority thread that blocks acquiring the lock, causing + it to donate their priorities to the main thread. The main + thread attempts to lower its priority, which should not take + effect until the donation is released. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +static thread_func acquire_thread_func; + +void +test_priority_donate_lower (void) +{ + struct lock lock; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + lock_init (&lock); + lock_acquire (&lock); + thread_create ("acquire", PRI_DEFAULT + 10, acquire_thread_func, &lock); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 10, thread_get_priority ()); + + msg ("Lowering base priority..."); + thread_set_priority (PRI_DEFAULT - 10); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 10, thread_get_priority ()); + lock_release (&lock); + msg ("acquire must already have finished."); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT - 10, thread_get_priority ()); +} + +static void +acquire_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("acquire: got the lock"); + lock_release (lock); + msg ("acquire: done"); +} diff --git a/pintos-env/pintos/tests/threads/priority-donate-lower.ck b/pintos-env/pintos/tests/threads/priority-donate-lower.ck new file mode 100755 index 0000000..c9bb61b --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-lower.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-donate-lower) begin +(priority-donate-lower) Main thread should have priority 41. Actual priority: 41. +(priority-donate-lower) Lowering base priority... +(priority-donate-lower) Main thread should have priority 41. Actual priority: 41. +(priority-donate-lower) acquire: got the lock +(priority-donate-lower) acquire: done +(priority-donate-lower) acquire must already have finished. +(priority-donate-lower) Main thread should have priority 21. Actual priority: 21. +(priority-donate-lower) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-donate-multiple.c b/pintos-env/pintos/tests/threads/priority-donate-multiple.c new file mode 100755 index 0000000..df4689c --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-multiple.c @@ -0,0 +1,77 @@ +/* The main thread acquires locks A and B, then it creates two + higher-priority threads. Each of these threads blocks + acquiring one of the locks and thus donate their priority to + the main thread. The main thread releases the locks in turn + and relinquishes its donated priorities. + + Based on a test originally submitted for Stanford's CS 140 in + winter 1999 by Matt Franklin , + Greg Hutchins , Yu Ping Hu + . Modified by arens. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +static thread_func a_thread_func; +static thread_func b_thread_func; + +void +test_priority_donate_multiple (void) +{ + struct lock a, b; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + lock_init (&a); + lock_init (&b); + + lock_acquire (&a); + lock_acquire (&b); + + thread_create ("a", PRI_DEFAULT + 1, a_thread_func, &a); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 1, thread_get_priority ()); + + thread_create ("b", PRI_DEFAULT + 2, b_thread_func, &b); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 2, thread_get_priority ()); + + lock_release (&b); + msg ("Thread b should have just finished."); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 1, thread_get_priority ()); + + lock_release (&a); + msg ("Thread a should have just finished."); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT, thread_get_priority ()); +} + +static void +a_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("Thread a acquired lock a."); + lock_release (lock); + msg ("Thread a finished."); +} + +static void +b_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("Thread b acquired lock b."); + lock_release (lock); + msg ("Thread b finished."); +} diff --git a/pintos-env/pintos/tests/threads/priority-donate-multiple.ck b/pintos-env/pintos/tests/threads/priority-donate-multiple.ck new file mode 100755 index 0000000..0afd20b --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-multiple.ck @@ -0,0 +1,19 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-donate-multiple) begin +(priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. +(priority-donate-multiple) Main thread should have priority 33. Actual priority: 33. +(priority-donate-multiple) Thread b acquired lock b. +(priority-donate-multiple) Thread b finished. +(priority-donate-multiple) Thread b should have just finished. +(priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. +(priority-donate-multiple) Thread a acquired lock a. +(priority-donate-multiple) Thread a finished. +(priority-donate-multiple) Thread a should have just finished. +(priority-donate-multiple) Main thread should have priority 31. Actual priority: 31. +(priority-donate-multiple) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-donate-multiple2.c b/pintos-env/pintos/tests/threads/priority-donate-multiple2.c new file mode 100755 index 0000000..7f65fef --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-multiple2.c @@ -0,0 +1,90 @@ +/* The main thread acquires locks A and B, then it creates three + higher-priority threads. The first two of these threads block + acquiring one of the locks and thus donate their priority to + the main thread. The main thread releases the locks in turn + and relinquishes its donated priorities, allowing the third thread + to run. + + In this test, the main thread releases the locks in a different + order compared to priority-donate-multiple.c. + + Written by Godmar Back . + Based on a test originally submitted for Stanford's CS 140 in + winter 1999 by Matt Franklin , + Greg Hutchins , Yu Ping Hu + . Modified by arens. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +static thread_func a_thread_func; +static thread_func b_thread_func; +static thread_func c_thread_func; + +void +test_priority_donate_multiple2 (void) +{ + struct lock a, b; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + lock_init (&a); + lock_init (&b); + + lock_acquire (&a); + lock_acquire (&b); + + thread_create ("a", PRI_DEFAULT + 3, a_thread_func, &a); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 3, thread_get_priority ()); + + thread_create ("c", PRI_DEFAULT + 1, c_thread_func, NULL); + + thread_create ("b", PRI_DEFAULT + 5, b_thread_func, &b); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 5, thread_get_priority ()); + + lock_release (&a); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 5, thread_get_priority ()); + + lock_release (&b); + msg ("Threads b, a, c should have just finished, in that order."); + msg ("Main thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT, thread_get_priority ()); +} + +static void +a_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("Thread a acquired lock a."); + lock_release (lock); + msg ("Thread a finished."); +} + +static void +b_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("Thread b acquired lock b."); + lock_release (lock); + msg ("Thread b finished."); +} + +static void +c_thread_func (void *a_ UNUSED) +{ + msg ("Thread c finished."); +} diff --git a/pintos-env/pintos/tests/threads/priority-donate-multiple2.ck b/pintos-env/pintos/tests/threads/priority-donate-multiple2.ck new file mode 100755 index 0000000..b23533a --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-multiple2.ck @@ -0,0 +1,19 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-donate-multiple2) begin +(priority-donate-multiple2) Main thread should have priority 34. Actual priority: 34. +(priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. +(priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. +(priority-donate-multiple2) Thread b acquired lock b. +(priority-donate-multiple2) Thread b finished. +(priority-donate-multiple2) Thread a acquired lock a. +(priority-donate-multiple2) Thread a finished. +(priority-donate-multiple2) Thread c finished. +(priority-donate-multiple2) Threads b, a, c should have just finished, in that order. +(priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31. +(priority-donate-multiple2) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-donate-nest.c b/pintos-env/pintos/tests/threads/priority-donate-nest.c new file mode 100755 index 0000000..3a3a9a5 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-nest.c @@ -0,0 +1,94 @@ +/* Low-priority main thread L acquires lock A. Medium-priority + thread M then acquires lock B then blocks on acquiring lock A. + High-priority thread H then blocks on acquiring lock B. Thus, + thread H donates its priority to M, which in turn donates it + to thread L. + + Based on a test originally submitted for Stanford's CS 140 in + winter 1999 by Matt Franklin , + Greg Hutchins , Yu Ping Hu + . Modified by arens. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +struct locks + { + struct lock *a; + struct lock *b; + }; + +static thread_func medium_thread_func; +static thread_func high_thread_func; + +void +test_priority_donate_nest (void) +{ + struct lock a, b; + struct locks locks; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + lock_init (&a); + lock_init (&b); + + lock_acquire (&a); + + locks.a = &a; + locks.b = &b; + thread_create ("medium", PRI_DEFAULT + 1, medium_thread_func, &locks); + thread_yield (); + msg ("Low thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 1, thread_get_priority ()); + + thread_create ("high", PRI_DEFAULT + 2, high_thread_func, &b); + thread_yield (); + msg ("Low thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 2, thread_get_priority ()); + + lock_release (&a); + thread_yield (); + msg ("Medium thread should just have finished."); + msg ("Low thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT, thread_get_priority ()); +} + +static void +medium_thread_func (void *locks_) +{ + struct locks *locks = locks_; + + lock_acquire (locks->b); + lock_acquire (locks->a); + + msg ("Medium thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 2, thread_get_priority ()); + msg ("Medium thread got the lock."); + + lock_release (locks->a); + thread_yield (); + + lock_release (locks->b); + thread_yield (); + + msg ("High thread should have just finished."); + msg ("Middle thread finished."); +} + +static void +high_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("High thread got the lock."); + lock_release (lock); + msg ("High thread finished."); +} diff --git a/pintos-env/pintos/tests/threads/priority-donate-nest.ck b/pintos-env/pintos/tests/threads/priority-donate-nest.ck new file mode 100755 index 0000000..923460e --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-nest.ck @@ -0,0 +1,19 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-donate-nest) begin +(priority-donate-nest) Low thread should have priority 32. Actual priority: 32. +(priority-donate-nest) Low thread should have priority 33. Actual priority: 33. +(priority-donate-nest) Medium thread should have priority 33. Actual priority: 33. +(priority-donate-nest) Medium thread got the lock. +(priority-donate-nest) High thread got the lock. +(priority-donate-nest) High thread finished. +(priority-donate-nest) High thread should have just finished. +(priority-donate-nest) Middle thread finished. +(priority-donate-nest) Medium thread should just have finished. +(priority-donate-nest) Low thread should have priority 31. Actual priority: 31. +(priority-donate-nest) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-donate-one.c b/pintos-env/pintos/tests/threads/priority-donate-one.c new file mode 100755 index 0000000..3189f3a --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-one.c @@ -0,0 +1,65 @@ +/* The main thread acquires a lock. Then it creates two + higher-priority threads that block acquiring the lock, causing + them to donate their priorities to the main thread. When the + main thread releases the lock, the other threads should + acquire it in priority order. + + Based on a test originally submitted for Stanford's CS 140 in + winter 1999 by Matt Franklin , + Greg Hutchins , Yu Ping Hu + . Modified by arens. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +static thread_func acquire1_thread_func; +static thread_func acquire2_thread_func; + +void +test_priority_donate_one (void) +{ + struct lock lock; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + lock_init (&lock); + lock_acquire (&lock); + thread_create ("acquire1", PRI_DEFAULT + 1, acquire1_thread_func, &lock); + msg ("This thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 1, thread_get_priority ()); + thread_create ("acquire2", PRI_DEFAULT + 2, acquire2_thread_func, &lock); + msg ("This thread should have priority %d. Actual priority: %d.", + PRI_DEFAULT + 2, thread_get_priority ()); + lock_release (&lock); + msg ("acquire2, acquire1 must already have finished, in that order."); + msg ("This should be the last line before finishing this test."); +} + +static void +acquire1_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("acquire1: got the lock"); + lock_release (lock); + msg ("acquire1: done"); +} + +static void +acquire2_thread_func (void *lock_) +{ + struct lock *lock = lock_; + + lock_acquire (lock); + msg ("acquire2: got the lock"); + lock_release (lock); + msg ("acquire2: done"); +} diff --git a/pintos-env/pintos/tests/threads/priority-donate-one.ck b/pintos-env/pintos/tests/threads/priority-donate-one.ck new file mode 100755 index 0000000..b7c8e6f --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-one.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-donate-one) begin +(priority-donate-one) This thread should have priority 32. Actual priority: 32. +(priority-donate-one) This thread should have priority 33. Actual priority: 33. +(priority-donate-one) acquire2: got the lock +(priority-donate-one) acquire2: done +(priority-donate-one) acquire1: got the lock +(priority-donate-one) acquire1: done +(priority-donate-one) acquire2, acquire1 must already have finished, in that order. +(priority-donate-one) This should be the last line before finishing this test. +(priority-donate-one) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-donate-sema.c b/pintos-env/pintos/tests/threads/priority-donate-sema.c new file mode 100755 index 0000000..b33cb72 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-sema.c @@ -0,0 +1,82 @@ +/* Low priority thread L acquires a lock, then blocks downing a + semaphore. Medium priority thread M then blocks waiting on + the same semaphore. Next, high priority thread H attempts to + acquire the lock, donating its priority to L. + + Next, the main thread ups the semaphore, waking up L. L + releases the lock, which wakes up H. H "up"s the semaphore, + waking up M. H terminates, then M, then L, and finally the + main thread. + + Written by Godmar Back . */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +struct lock_and_sema + { + struct lock lock; + struct semaphore sema; + }; + +static thread_func l_thread_func; +static thread_func m_thread_func; +static thread_func h_thread_func; + +void +test_priority_donate_sema (void) +{ + struct lock_and_sema ls; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + lock_init (&ls.lock); + sema_init (&ls.sema, 0); + thread_create ("low", PRI_DEFAULT + 1, l_thread_func, &ls); + thread_create ("med", PRI_DEFAULT + 3, m_thread_func, &ls); + thread_create ("high", PRI_DEFAULT + 5, h_thread_func, &ls); + sema_up (&ls.sema); + msg ("Main thread finished."); +} + +static void +l_thread_func (void *ls_) +{ + struct lock_and_sema *ls = ls_; + + lock_acquire (&ls->lock); + msg ("Thread L acquired lock."); + sema_down (&ls->sema); + msg ("Thread L downed semaphore."); + lock_release (&ls->lock); + msg ("Thread L finished."); +} + +static void +m_thread_func (void *ls_) +{ + struct lock_and_sema *ls = ls_; + + sema_down (&ls->sema); + msg ("Thread M finished."); +} + +static void +h_thread_func (void *ls_) +{ + struct lock_and_sema *ls = ls_; + + lock_acquire (&ls->lock); + msg ("Thread H acquired lock."); + + sema_up (&ls->sema); + lock_release (&ls->lock); + msg ("Thread H finished."); +} diff --git a/pintos-env/pintos/tests/threads/priority-donate-sema.ck b/pintos-env/pintos/tests/threads/priority-donate-sema.ck new file mode 100755 index 0000000..92b8d07 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-donate-sema.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-donate-sema) begin +(priority-donate-sema) Thread L acquired lock. +(priority-donate-sema) Thread L downed semaphore. +(priority-donate-sema) Thread H acquired lock. +(priority-donate-sema) Thread H finished. +(priority-donate-sema) Thread M finished. +(priority-donate-sema) Thread L finished. +(priority-donate-sema) Main thread finished. +(priority-donate-sema) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-fifo.c b/pintos-env/pintos/tests/threads/priority-fifo.c new file mode 100755 index 0000000..3af98a3 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-fifo.c @@ -0,0 +1,99 @@ +/* Creates several threads all at the same priority and ensures + that they consistently run in the same round-robin order. + + Based on a test originally submitted for Stanford's CS 140 in + winter 1999 by by Matt Franklin + , Greg Hutchins + , Yu Ping Hu . + Modified by arens. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "devices/timer.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" + +struct simple_thread_data + { + int id; /* Sleeper ID. */ + int iterations; /* Iterations so far. */ + struct lock *lock; /* Lock on output. */ + int **op; /* Output buffer position. */ + }; + +#define THREAD_CNT 16 +#define ITER_CNT 16 + +static thread_func simple_thread_func; + +void +test_priority_fifo (void) +{ + struct simple_thread_data data[THREAD_CNT]; + struct lock lock; + int *output, *op; + int i, cnt; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + msg ("%d threads will iterate %d times in the same order each time.", + THREAD_CNT, ITER_CNT); + msg ("If the order varies then there is a bug."); + + output = op = malloc (sizeof *output * THREAD_CNT * ITER_CNT * 2); + ASSERT (output != NULL); + lock_init (&lock); + + thread_set_priority (PRI_DEFAULT + 2); + for (i = 0; i < THREAD_CNT; i++) + { + char name[16]; + struct simple_thread_data *d = data + i; + snprintf (name, sizeof name, "%d", i); + d->id = i; + d->iterations = 0; + d->lock = &lock; + d->op = &op; + thread_create (name, PRI_DEFAULT + 1, simple_thread_func, d); + } + + thread_set_priority (PRI_DEFAULT); + /* All the other threads now run to termination here. */ + ASSERT (lock.holder == NULL); + + cnt = 0; + for (; output < op; output++) + { + struct simple_thread_data *d; + + ASSERT (*output >= 0 && *output < THREAD_CNT); + d = data + *output; + if (cnt % THREAD_CNT == 0) + printf ("(priority-fifo) iteration:"); + printf (" %d", d->id); + if (++cnt % THREAD_CNT == 0) + printf ("\n"); + d->iterations++; + } +} + +static void +simple_thread_func (void *data_) +{ + struct simple_thread_data *data = data_; + int i; + + for (i = 0; i < ITER_CNT; i++) + { + lock_acquire (data->lock); + *(*data->op)++ = data->id; + lock_release (data->lock); + thread_yield (); + } +} diff --git a/pintos-env/pintos/tests/threads/priority-fifo.ck b/pintos-env/pintos/tests/threads/priority-fifo.ck new file mode 100755 index 0000000..11f1dd3 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-fifo.ck @@ -0,0 +1,63 @@ +# -*- perl -*- + +# The expected output looks like this: +# +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +# +# A different permutation of 0...15 is acceptable, but every line must +# be in the same order. + +use strict; +use warnings; +use tests::tests; + +our ($test); +my (@output) = read_text_file ("$test.output"); + +common_checks ("run", @output); + +my ($thread_cnt) = 16; +my ($iter_cnt) = 16; +my (@order); +my (@t) = (-1) x $thread_cnt; + +my (@iterations) = grep (/iteration:/, @output); +fail "No iterations found in output.\n" if !@iterations; + +my (@numbering) = $iterations[0] =~ /(\d+)/g; +fail "First iteration does not list exactly $thread_cnt threads.\n" + if @numbering != $thread_cnt; + +my (@sorted_numbering) = sort { $a <=> $b } @numbering; +for my $i (0...$#sorted_numbering) { + if ($sorted_numbering[$i] != $i) { + fail "First iteration does not list all threads " + . "0...$#sorted_numbering\n"; + } +} + +for my $i (1...$#iterations) { + if ($iterations[$i] ne $iterations[0]) { + fail "Iteration $i differs from iteration 0\n"; + } +} + +fail "$iter_cnt iterations expected but " . scalar (@iterations) . " found\n" + if $iter_cnt != @iterations; + +pass; diff --git a/pintos-env/pintos/tests/threads/priority-preempt.c b/pintos-env/pintos/tests/threads/priority-preempt.c new file mode 100755 index 0000000..3c3aacb --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-preempt.c @@ -0,0 +1,41 @@ +/* Ensures that a high-priority thread really preempts. + + Based on a test originally submitted for Stanford's CS 140 in + winter 1999 by by Matt Franklin + , Greg Hutchins + , Yu Ping Hu . + Modified by arens. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/synch.h" +#include "threads/thread.h" + +static thread_func simple_thread_func; + +void +test_priority_preempt (void) +{ + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + /* Make sure our priority is the default. */ + ASSERT (thread_get_priority () == PRI_DEFAULT); + + thread_create ("high-priority", PRI_DEFAULT + 1, simple_thread_func, NULL); + msg ("The high-priority thread should have already completed."); +} + +static void +simple_thread_func (void *aux UNUSED) +{ + int i; + + for (i = 0; i < 5; i++) + { + msg ("Thread %s iteration %d", thread_name (), i); + thread_yield (); + } + msg ("Thread %s done!", thread_name ()); +} diff --git a/pintos-env/pintos/tests/threads/priority-preempt.ck b/pintos-env/pintos/tests/threads/priority-preempt.ck new file mode 100755 index 0000000..43a26ee --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-preempt.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-preempt) begin +(priority-preempt) Thread high-priority iteration 0 +(priority-preempt) Thread high-priority iteration 1 +(priority-preempt) Thread high-priority iteration 2 +(priority-preempt) Thread high-priority iteration 3 +(priority-preempt) Thread high-priority iteration 4 +(priority-preempt) Thread high-priority done! +(priority-preempt) The high-priority thread should have already completed. +(priority-preempt) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/priority-sema.c b/pintos-env/pintos/tests/threads/priority-sema.c new file mode 100755 index 0000000..2834a88 --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-sema.c @@ -0,0 +1,45 @@ +/* Tests that the highest-priority thread waiting on a semaphore + is the first to wake up. */ + +#include +#include "tests/threads/tests.h" +#include "threads/init.h" +#include "threads/malloc.h" +#include "threads/synch.h" +#include "threads/thread.h" +#include "devices/timer.h" + +static thread_func priority_sema_thread; +static struct semaphore sema; + +void +test_priority_sema (void) +{ + int i; + + /* This test does not work with the MLFQS. */ + ASSERT (!thread_mlfqs); + + sema_init (&sema, 0); + thread_set_priority (PRI_MIN); + for (i = 0; i < 10; i++) + { + int priority = PRI_DEFAULT - (i + 3) % 10 - 1; + char name[16]; + snprintf (name, sizeof name, "priority %d", priority); + thread_create (name, priority, priority_sema_thread, NULL); + } + + for (i = 0; i < 10; i++) + { + sema_up (&sema); + msg ("Back in main thread."); + } +} + +static void +priority_sema_thread (void *aux UNUSED) +{ + sema_down (&sema); + msg ("Thread %s woke up.", thread_name ()); +} diff --git a/pintos-env/pintos/tests/threads/priority-sema.ck b/pintos-env/pintos/tests/threads/priority-sema.ck new file mode 100755 index 0000000..559988d --- /dev/null +++ b/pintos-env/pintos/tests/threads/priority-sema.ck @@ -0,0 +1,29 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(priority-sema) begin +(priority-sema) Thread priority 30 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 29 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 28 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 27 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 26 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 25 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 24 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 23 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 22 woke up. +(priority-sema) Back in main thread. +(priority-sema) Thread priority 21 woke up. +(priority-sema) Back in main thread. +(priority-sema) end +EOF +pass; diff --git a/pintos-env/pintos/tests/threads/tests.c b/pintos-env/pintos/tests/threads/tests.c new file mode 100755 index 0000000..af15aee --- /dev/null +++ b/pintos-env/pintos/tests/threads/tests.c @@ -0,0 +1,102 @@ +#include "tests/threads/tests.h" +#include +#include +#include + +struct test + { + const char *name; + test_func *function; + }; + +static const struct test tests[] = + { + {"alarm-single", test_alarm_single}, + {"alarm-multiple", test_alarm_multiple}, + {"alarm-simultaneous", test_alarm_simultaneous}, + {"alarm-priority", test_alarm_priority}, + {"alarm-zero", test_alarm_zero}, + {"alarm-negative", test_alarm_negative}, + {"priority-change", test_priority_change}, + {"priority-donate-one", test_priority_donate_one}, + {"priority-donate-multiple", test_priority_donate_multiple}, + {"priority-donate-multiple2", test_priority_donate_multiple2}, + {"priority-donate-nest", test_priority_donate_nest}, + {"priority-donate-sema", test_priority_donate_sema}, + {"priority-donate-lower", test_priority_donate_lower}, + {"priority-donate-chain", test_priority_donate_chain}, + {"priority-fifo", test_priority_fifo}, + {"priority-preempt", test_priority_preempt}, + {"priority-sema", test_priority_sema}, + {"priority-condvar", test_priority_condvar}, + {"mlfqs-load-1", test_mlfqs_load_1}, + {"mlfqs-load-60", test_mlfqs_load_60}, + {"mlfqs-load-avg", test_mlfqs_load_avg}, + {"mlfqs-recent-1", test_mlfqs_recent_1}, + {"mlfqs-fair-2", test_mlfqs_fair_2}, + {"mlfqs-fair-20", test_mlfqs_fair_20}, + {"mlfqs-nice-2", test_mlfqs_nice_2}, + {"mlfqs-nice-10", test_mlfqs_nice_10}, + {"mlfqs-block", test_mlfqs_block}, + }; + +static const char *test_name; + +/* Runs the test named NAME. */ +void +run_test (const char *name) +{ + const struct test *t; + + for (t = tests; t < tests + sizeof tests / sizeof *tests; t++) + if (!strcmp (name, t->name)) + { + test_name = name; + msg ("begin"); + t->function (); + msg ("end"); + return; + } + PANIC ("no test named \"%s\"", name); +} + +/* Prints FORMAT as if with printf(), + prefixing the output by the name of the test + and following it with a new-line character. */ +void +msg (const char *format, ...) +{ + va_list args; + + printf ("(%s) ", test_name); + va_start (args, format); + vprintf (format, args); + va_end (args); + putchar ('\n'); +} + +/* Prints failure message FORMAT as if with printf(), + prefixing the output by the name of the test and FAIL: + and following it with a new-line character, + and then panics the kernel. */ +void +fail (const char *format, ...) +{ + va_list args; + + printf ("(%s) FAIL: ", test_name); + va_start (args, format); + vprintf (format, args); + va_end (args); + putchar ('\n'); + + PANIC ("test failed"); +} + +/* Prints a message indicating the current test passed. */ +void +pass (void) +{ + printf ("(%s) PASS\n", test_name); +} + diff --git a/pintos-env/pintos/tests/threads/tests.h b/pintos-env/pintos/tests/threads/tests.h new file mode 100755 index 0000000..cd9d489 --- /dev/null +++ b/pintos-env/pintos/tests/threads/tests.h @@ -0,0 +1,41 @@ +#ifndef TESTS_THREADS_TESTS_H +#define TESTS_THREADS_TESTS_H + +void run_test (const char *); + +typedef void test_func (void); + +extern test_func test_alarm_single; +extern test_func test_alarm_multiple; +extern test_func test_alarm_simultaneous; +extern test_func test_alarm_priority; +extern test_func test_alarm_zero; +extern test_func test_alarm_negative; +extern test_func test_priority_change; +extern test_func test_priority_donate_one; +extern test_func test_priority_donate_multiple; +extern test_func test_priority_donate_multiple2; +extern test_func test_priority_donate_sema; +extern test_func test_priority_donate_nest; +extern test_func test_priority_donate_lower; +extern test_func test_priority_donate_chain; +extern test_func test_priority_fifo; +extern test_func test_priority_preempt; +extern test_func test_priority_sema; +extern test_func test_priority_condvar; +extern test_func test_mlfqs_load_1; +extern test_func test_mlfqs_load_60; +extern test_func test_mlfqs_load_avg; +extern test_func test_mlfqs_recent_1; +extern test_func test_mlfqs_fair_2; +extern test_func test_mlfqs_fair_20; +extern test_func test_mlfqs_nice_2; +extern test_func test_mlfqs_nice_10; +extern test_func test_mlfqs_block; + +void msg (const char *, ...); +void fail (const char *, ...); +void pass (void); + +#endif /* tests/threads/tests.h */ + diff --git a/pintos-env/pintos/tests/userprog/Grading b/pintos-env/pintos/tests/userprog/Grading new file mode 100755 index 0000000..f70dc99 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/Grading @@ -0,0 +1,11 @@ +# Percentage of the testing point total designated for each set of +# tests. + +# This project is primarily about implementing system calls. +# If you do so properly, the base file system functionality +# should come "for free". Thus, the points emphasis below. + +35% tests/userprog/Rubric.functionality +25% tests/userprog/Rubric.robustness +10% tests/userprog/no-vm/Rubric +30% tests/filesys/base/Rubric diff --git a/pintos-env/pintos/tests/userprog/Make.tests b/pintos-env/pintos/tests/userprog/Make.tests new file mode 100755 index 0000000..caadd90 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/Make.tests @@ -0,0 +1,133 @@ +# -*- makefile -*- + +tests/%.output: FILESYSSOURCE = --filesys-size=2 +tests/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^) + +tests/userprog_TESTS = $(addprefix tests/userprog/,args-none \ +args-single args-multiple args-many args-dbl-space sc-bad-sp \ +sc-bad-arg sc-boundary sc-boundary-2 halt exit create-normal \ +create-empty create-null create-bad-ptr create-long create-exists \ +create-bound open-normal open-missing open-boundary open-empty \ +open-null open-bad-ptr open-twice close-normal close-twice close-stdin \ +close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \ +read-zero read-stdout read-bad-fd write-normal write-bad-ptr \ +write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \ +exec-multiple exec-missing exec-bad-ptr wait-simple wait-twice \ +wait-killed wait-bad-pid multi-recurse multi-child-fd rox-simple \ +rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \ +bad-jump bad-jump2) + +tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \ +tests/userprog/,child-simple child-args child-bad child-close child-rox) + +tests/userprog/args-none_SRC = tests/userprog/args.c +tests/userprog/args-single_SRC = tests/userprog/args.c +tests/userprog/args-multiple_SRC = tests/userprog/args.c +tests/userprog/args-many_SRC = tests/userprog/args.c +tests/userprog/args-dbl-space_SRC = tests/userprog/args.c +tests/userprog/sc-bad-sp_SRC = tests/userprog/sc-bad-sp.c tests/main.c +tests/userprog/sc-bad-arg_SRC = tests/userprog/sc-bad-arg.c tests/main.c +tests/userprog/bad-read_SRC = tests/userprog/bad-read.c tests/main.c +tests/userprog/bad-write_SRC = tests/userprog/bad-write.c tests/main.c +tests/userprog/bad-jump_SRC = tests/userprog/bad-jump.c tests/main.c +tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c +tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c +tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c +tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \ +tests/userprog/boundary.c tests/main.c +tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \ +tests/userprog/boundary.c tests/main.c +tests/userprog/halt_SRC = tests/userprog/halt.c tests/main.c +tests/userprog/exit_SRC = tests/userprog/exit.c tests/main.c +tests/userprog/create-normal_SRC = tests/userprog/create-normal.c tests/main.c +tests/userprog/create-empty_SRC = tests/userprog/create-empty.c tests/main.c +tests/userprog/create-null_SRC = tests/userprog/create-null.c tests/main.c +tests/userprog/create-bad-ptr_SRC = tests/userprog/create-bad-ptr.c \ +tests/main.c +tests/userprog/create-long_SRC = tests/userprog/create-long.c tests/main.c +tests/userprog/create-exists_SRC = tests/userprog/create-exists.c tests/main.c +tests/userprog/create-bound_SRC = tests/userprog/create-bound.c \ +tests/userprog/boundary.c tests/main.c +tests/userprog/open-normal_SRC = tests/userprog/open-normal.c tests/main.c +tests/userprog/open-missing_SRC = tests/userprog/open-missing.c tests/main.c +tests/userprog/open-boundary_SRC = tests/userprog/open-boundary.c \ +tests/userprog/boundary.c tests/main.c +tests/userprog/open-empty_SRC = tests/userprog/open-empty.c tests/main.c +tests/userprog/open-null_SRC = tests/userprog/open-null.c tests/main.c +tests/userprog/open-bad-ptr_SRC = tests/userprog/open-bad-ptr.c tests/main.c +tests/userprog/open-twice_SRC = tests/userprog/open-twice.c tests/main.c +tests/userprog/close-normal_SRC = tests/userprog/close-normal.c tests/main.c +tests/userprog/close-twice_SRC = tests/userprog/close-twice.c tests/main.c +tests/userprog/close-stdin_SRC = tests/userprog/close-stdin.c tests/main.c +tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c +tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c +tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c +tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c +tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \ +tests/userprog/boundary.c tests/main.c +tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c +tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c +tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c +tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c +tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c +tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \ +tests/userprog/boundary.c tests/main.c +tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c +tests/userprog/write-stdin_SRC = tests/userprog/write-stdin.c tests/main.c +tests/userprog/write-bad-fd_SRC = tests/userprog/write-bad-fd.c tests/main.c +tests/userprog/exec-once_SRC = tests/userprog/exec-once.c tests/main.c +tests/userprog/exec-arg_SRC = tests/userprog/exec-arg.c tests/main.c +tests/userprog/exec-multiple_SRC = tests/userprog/exec-multiple.c tests/main.c +tests/userprog/exec-missing_SRC = tests/userprog/exec-missing.c tests/main.c +tests/userprog/exec-bad-ptr_SRC = tests/userprog/exec-bad-ptr.c tests/main.c +tests/userprog/wait-simple_SRC = tests/userprog/wait-simple.c tests/main.c +tests/userprog/wait-twice_SRC = tests/userprog/wait-twice.c tests/main.c +tests/userprog/wait-killed_SRC = tests/userprog/wait-killed.c tests/main.c +tests/userprog/wait-bad-pid_SRC = tests/userprog/wait-bad-pid.c tests/main.c +tests/userprog/multi-recurse_SRC = tests/userprog/multi-recurse.c +tests/userprog/multi-child-fd_SRC = tests/userprog/multi-child-fd.c \ +tests/main.c +tests/userprog/rox-simple_SRC = tests/userprog/rox-simple.c tests/main.c +tests/userprog/rox-child_SRC = tests/userprog/rox-child.c tests/main.c +tests/userprog/rox-multichild_SRC = tests/userprog/rox-multichild.c \ +tests/main.c + +tests/userprog/child-simple_SRC = tests/userprog/child-simple.c +tests/userprog/child-args_SRC = tests/userprog/args.c +tests/userprog/child-bad_SRC = tests/userprog/child-bad.c tests/main.c +tests/userprog/child-close_SRC = tests/userprog/child-close.c +tests/userprog/child-rox_SRC = tests/userprog/child-rox.c + +$(foreach prog,$(tests/userprog_PROGS),$(eval $(prog)_SRC += tests/lib.c)) + +tests/userprog/args-single_ARGS = onearg +tests/userprog/args-multiple_ARGS = some arguments for you! +tests/userprog/args-many_ARGS = a b c d e f g h i j k l m n o p q r s t u v +tests/userprog/args-dbl-space_ARGS = two spaces! +tests/userprog/multi-recurse_ARGS = 15 + +tests/userprog/open-normal_PUTFILES += tests/userprog/sample.txt +tests/userprog/open-boundary_PUTFILES += tests/userprog/sample.txt +tests/userprog/open-twice_PUTFILES += tests/userprog/sample.txt +tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt +tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt +tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt +tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt +tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt +tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt +tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt +tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt +tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt +tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt +tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt + +tests/userprog/exec-once_PUTFILES += tests/userprog/child-simple +tests/userprog/exec-multiple_PUTFILES += tests/userprog/child-simple +tests/userprog/wait-simple_PUTFILES += tests/userprog/child-simple +tests/userprog/wait-twice_PUTFILES += tests/userprog/child-simple + +tests/userprog/exec-arg_PUTFILES += tests/userprog/child-args +tests/userprog/multi-child-fd_PUTFILES += tests/userprog/child-close +tests/userprog/wait-killed_PUTFILES += tests/userprog/child-bad +tests/userprog/rox-child_PUTFILES += tests/userprog/child-rox +tests/userprog/rox-multichild_PUTFILES += tests/userprog/child-rox diff --git a/pintos-env/pintos/tests/userprog/Rubric.functionality b/pintos-env/pintos/tests/userprog/Rubric.functionality new file mode 100755 index 0000000..ea76c44 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/Rubric.functionality @@ -0,0 +1,52 @@ +Functionality of system calls: +- Test argument passing on Pintos command line. +3 args-none +3 args-single +3 args-multiple +3 args-many +3 args-dbl-space + +- Test "create" system call. +3 create-empty +3 create-long +3 create-normal +3 create-exists + +- Test "open" system call. +3 open-missing +3 open-normal +3 open-twice + +- Test "read" system call. +3 read-normal +3 read-zero + +- Test "write" system call. +3 write-normal +3 write-zero + +- Test "close" system call. +3 close-normal + +- Test "exec" system call. +5 exec-once +5 exec-multiple +5 exec-arg + +- Test "wait" system call. +5 wait-simple +5 wait-twice + +- Test "exit" system call. +5 exit + +- Test "halt" system call. +3 halt + +- Test recursive execution of user programs. +15 multi-recurse + +- Test read-only executable feature. +3 rox-simple +3 rox-child +3 rox-multichild diff --git a/pintos-env/pintos/tests/userprog/Rubric.robustness b/pintos-env/pintos/tests/userprog/Rubric.robustness new file mode 100755 index 0000000..b7d1035 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/Rubric.robustness @@ -0,0 +1,48 @@ +Robustness of system calls: +- Test robustness of file descriptor handling. +2 close-stdin +2 close-stdout +2 close-bad-fd +2 close-twice +2 read-bad-fd +2 read-stdout +2 write-bad-fd +2 write-stdin +2 multi-child-fd + +- Test robustness of pointer handling. +3 create-bad-ptr +3 exec-bad-ptr +3 open-bad-ptr +3 read-bad-ptr +3 write-bad-ptr + +- Test robustness of buffer copying across page boundaries. +3 create-bound +3 open-boundary +3 read-boundary +3 write-boundary + +- Test handling of null pointer and empty strings. +2 create-null +2 open-null +2 open-empty + +- Test robustness of system call implementation. +3 sc-bad-arg +3 sc-bad-sp +5 sc-boundary +5 sc-boundary-2 + +- Test robustness of "exec" and "wait" system calls. +5 exec-missing +5 wait-bad-pid +5 wait-killed + +- Test robustness of exception handling. +1 bad-read +1 bad-write +1 bad-jump +1 bad-read2 +1 bad-write2 +1 bad-jump2 diff --git a/pintos-env/pintos/tests/userprog/args-dbl-space.ck b/pintos-env/pintos/tests/userprog/args-dbl-space.ck new file mode 100755 index 0000000..dfbcf4b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/args-dbl-space.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(args) begin +(args) argc = 3 +(args) argv[0] = 'args-dbl-space' +(args) argv[1] = 'two' +(args) argv[2] = 'spaces!' +(args) argv[3] = null +(args) end +args-dbl-space: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/args-many.ck b/pintos-env/pintos/tests/userprog/args-many.ck new file mode 100755 index 0000000..214574a --- /dev/null +++ b/pintos-env/pintos/tests/userprog/args-many.ck @@ -0,0 +1,35 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(args) begin +(args) argc = 23 +(args) argv[0] = 'args-many' +(args) argv[1] = 'a' +(args) argv[2] = 'b' +(args) argv[3] = 'c' +(args) argv[4] = 'd' +(args) argv[5] = 'e' +(args) argv[6] = 'f' +(args) argv[7] = 'g' +(args) argv[8] = 'h' +(args) argv[9] = 'i' +(args) argv[10] = 'j' +(args) argv[11] = 'k' +(args) argv[12] = 'l' +(args) argv[13] = 'm' +(args) argv[14] = 'n' +(args) argv[15] = 'o' +(args) argv[16] = 'p' +(args) argv[17] = 'q' +(args) argv[18] = 'r' +(args) argv[19] = 's' +(args) argv[20] = 't' +(args) argv[21] = 'u' +(args) argv[22] = 'v' +(args) argv[23] = null +(args) end +args-many: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/args-multiple.ck b/pintos-env/pintos/tests/userprog/args-multiple.ck new file mode 100755 index 0000000..227e6cc --- /dev/null +++ b/pintos-env/pintos/tests/userprog/args-multiple.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(args) begin +(args) argc = 5 +(args) argv[0] = 'args-multiple' +(args) argv[1] = 'some' +(args) argv[2] = 'arguments' +(args) argv[3] = 'for' +(args) argv[4] = 'you!' +(args) argv[5] = null +(args) end +args-multiple: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/args-none.ck b/pintos-env/pintos/tests/userprog/args-none.ck new file mode 100755 index 0000000..146318e --- /dev/null +++ b/pintos-env/pintos/tests/userprog/args-none.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(args) begin +(args) argc = 1 +(args) argv[0] = 'args-none' +(args) argv[1] = null +(args) end +args-none: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/args-single.ck b/pintos-env/pintos/tests/userprog/args-single.ck new file mode 100755 index 0000000..24582b4 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/args-single.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(args) begin +(args) argc = 2 +(args) argv[0] = 'args-single' +(args) argv[1] = 'onearg' +(args) argv[2] = null +(args) end +args-single: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/args.c b/pintos-env/pintos/tests/userprog/args.c new file mode 100755 index 0000000..20eda44 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/args.c @@ -0,0 +1,25 @@ +/* Prints the command-line arguments. + This program is used for all of the args-* tests. Grading is + done differently for each of the args-* tests based on the + output. */ + +#include "tests/lib.h" + +int +main (int argc, char *argv[]) +{ + int i; + + test_name = "args"; + + msg ("begin"); + msg ("argc = %d", argc); + for (i = 0; i <= argc; i++) + if (argv[i] != NULL) + msg ("argv[%d] = '%s'", i, argv[i]); + else + msg ("argv[%d] = null", i); + msg ("end"); + + return 0; +} diff --git a/pintos-env/pintos/tests/userprog/bad-jump.c b/pintos-env/pintos/tests/userprog/bad-jump.c new file mode 100755 index 0000000..51b7c9f --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-jump.c @@ -0,0 +1,13 @@ +/* This program attempts to execute code at address 0, which is not mapped. + This should terminate the process with a -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("Congratulations - you have successfully called NULL: %d", + ((int (*)(void))NULL)()); + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/bad-jump.ck b/pintos-env/pintos/tests/userprog/bad-jump.ck new file mode 100755 index 0000000..e1c178b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-jump.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(bad-jump) begin +bad-jump: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/bad-jump2.c b/pintos-env/pintos/tests/userprog/bad-jump2.c new file mode 100755 index 0000000..dc7c2a7 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-jump2.c @@ -0,0 +1,13 @@ +/* This program attempts to execute code at a kernel virtual address. + This should terminate the process with a -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("Congratulations - you have successfully called kernel code: %d", + ((int (*)(void))0xC0000000)()); + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/bad-jump2.ck b/pintos-env/pintos/tests/userprog/bad-jump2.ck new file mode 100755 index 0000000..35f0f97 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-jump2.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(bad-jump2) begin +bad-jump2: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/bad-read.c b/pintos-env/pintos/tests/userprog/bad-read.c new file mode 100755 index 0000000..904c278 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-read.c @@ -0,0 +1,13 @@ +/* This program attempts to read memory at an address that is not mapped. + This should terminate the process with a -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("Congratulations - you have successfully dereferenced NULL: %d", + *(int *)NULL); + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/bad-read.ck b/pintos-env/pintos/tests/userprog/bad-read.ck new file mode 100755 index 0000000..4d4d926 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-read.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(bad-read) begin +bad-read: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/bad-read2.c b/pintos-env/pintos/tests/userprog/bad-read2.c new file mode 100755 index 0000000..a2fc237 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-read2.c @@ -0,0 +1,13 @@ +/* This program attempts to read kernel memory. + This should terminate the process with a -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("Congratulations - you have successfully read kernel memory: %d", + *(int *)0xC0000000); + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/bad-read2.ck b/pintos-env/pintos/tests/userprog/bad-read2.ck new file mode 100755 index 0000000..fa27c7d --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-read2.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(bad-read2) begin +bad-read2: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/bad-write.c b/pintos-env/pintos/tests/userprog/bad-write.c new file mode 100755 index 0000000..000c26b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-write.c @@ -0,0 +1,12 @@ +/* This program attempts to write to memory at an address that is not mapped. + This should terminate the process with a -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + *(int *)NULL = 42; + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/bad-write.ck b/pintos-env/pintos/tests/userprog/bad-write.ck new file mode 100755 index 0000000..d213b49 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-write.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(bad-write) begin +bad-write: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/bad-write2.c b/pintos-env/pintos/tests/userprog/bad-write2.c new file mode 100755 index 0000000..753da1e --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-write2.c @@ -0,0 +1,12 @@ +/* This program attempts to write to kernel memory. + This should terminate the process with a -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + *(int *)0xC0000000 = 42; + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/bad-write2.ck b/pintos-env/pintos/tests/userprog/bad-write2.ck new file mode 100755 index 0000000..c6a3420 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/bad-write2.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(bad-write2) begin +bad-write2: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/boundary.c b/pintos-env/pintos/tests/userprog/boundary.c new file mode 100755 index 0000000..59907ec --- /dev/null +++ b/pintos-env/pintos/tests/userprog/boundary.c @@ -0,0 +1,33 @@ +/* Utility function for tests that try to break system calls by + passing them data that crosses from one virtual page to + another. */ + +#include +#include +#include +#include "tests/userprog/boundary.h" + +static char dst[8192]; + +/* Returns the beginning of a page. There are at least 2048 + modifiable bytes on either side of the pointer returned. */ +void * +get_boundary_area (void) +{ + char *p = (char *) ROUND_UP ((uintptr_t) dst, 4096); + if (p - dst < 2048) + p += 4096; + return p; +} + +/* Returns a copy of SRC split across the boundary between two + pages. */ +char * +copy_string_across_boundary (const char *src) +{ + char *p = get_boundary_area (); + p -= strlen (src) < 4096 ? strlen (src) / 2 : 4096; + strlcpy (p, src, 4096); + return p; +} + diff --git a/pintos-env/pintos/tests/userprog/boundary.h b/pintos-env/pintos/tests/userprog/boundary.h new file mode 100755 index 0000000..c8e4b3b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/boundary.h @@ -0,0 +1,7 @@ +#ifndef TESTS_USERPROG_BOUNDARY_H +#define TESTS_USERPROG_BOUNDARY_H + +void *get_boundary_area (void); +char *copy_string_across_boundary (const char *); + +#endif /* tests/userprog/boundary.h */ diff --git a/pintos-env/pintos/tests/userprog/child-bad.c b/pintos-env/pintos/tests/userprog/child-bad.c new file mode 100755 index 0000000..77d7a69 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/child-bad.c @@ -0,0 +1,14 @@ +/* Child process run by wait-killed test. + Sets the stack pointer (%esp) to an invalid value and invokes + a system call, which should then terminate the process with a + -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + asm volatile ("movl $0x20101234, %esp; int $0x30"); + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/child-close.c b/pintos-env/pintos/tests/userprog/child-close.c new file mode 100755 index 0000000..ac948c8 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/child-close.c @@ -0,0 +1,28 @@ +/* Child process run by multi-child-fd test. + + Attempts to close the file descriptor passed as the first + command-line argument. This is invalid, because file + descriptors are not inherited in Pintos. Two results are + allowed: either the system call should return without taking + any action, or the kernel should terminate the process with a + -1 exit code. */ + +#include +#include +#include +#include +#include "tests/lib.h" + +const char *test_name = "child-close"; + +int +main (int argc UNUSED, char *argv[]) +{ + msg ("begin"); + if (!isdigit (*argv[1])) + fail ("bad command-line arguments"); + close (atoi (argv[1])); + msg ("end"); + + return 0; +} diff --git a/pintos-env/pintos/tests/userprog/child-rox.c b/pintos-env/pintos/tests/userprog/child-rox.c new file mode 100755 index 0000000..aba808b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/child-rox.c @@ -0,0 +1,55 @@ +/* Child process run by rox-child and rox-multichild tests. + Opens and tries to write to its own executable, verifying that + that is disallowed. + Then recursively executes itself to the depth indicated by the + first command-line argument. */ + +#include +#include +#include +#include +#include "tests/lib.h" + +const char *test_name = "child-rox"; + +static void +try_write (void) +{ + int handle; + char buffer[19]; + + quiet = true; + CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\""); + quiet = false; + + CHECK (write (handle, buffer, sizeof buffer) == 0, + "try to write \"child-rox\""); + + close (handle); +} + +int +main (int argc UNUSED, char *argv[]) +{ + msg ("begin"); + try_write (); + + if (!isdigit (*argv[1])) + fail ("bad command-line arguments"); + if (atoi (argv[1]) > 1) + { + char cmd[128]; + int child; + + snprintf (cmd, sizeof cmd, "child-rox %d", atoi (argv[1]) - 1); + CHECK ((child = exec (cmd)) != -1, "exec \"%s\"", cmd); + quiet = true; + CHECK (wait (child) == 12, "wait for \"child-rox\""); + quiet = false; + } + + try_write (); + msg ("end"); + + return 12; +} diff --git a/pintos-env/pintos/tests/userprog/child-simple.c b/pintos-env/pintos/tests/userprog/child-simple.c new file mode 100755 index 0000000..0d2dacf --- /dev/null +++ b/pintos-env/pintos/tests/userprog/child-simple.c @@ -0,0 +1,15 @@ +/* Child process run by exec-multiple, exec-one, wait-simple, and + wait-twice tests. + Just prints a single message and terminates. */ + +#include +#include "tests/lib.h" + +const char *test_name = "child-simple"; + +int +main (void) +{ + msg ("run"); + return 81; +} diff --git a/pintos-env/pintos/tests/userprog/close-bad-fd.c b/pintos-env/pintos/tests/userprog/close-bad-fd.c new file mode 100755 index 0000000..f63bb9a --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-bad-fd.c @@ -0,0 +1,11 @@ +/* Tries to close an invalid fd, which must either fail silently + or terminate with exit code -1. */ + +#include +#include "tests/main.h" + +void +test_main (void) +{ + close (0x20101234); +} diff --git a/pintos-env/pintos/tests/userprog/close-bad-fd.ck b/pintos-env/pintos/tests/userprog/close-bad-fd.ck new file mode 100755 index 0000000..497b17c --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-bad-fd.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(close-bad-fd) begin +(close-bad-fd) end +close-bad-fd: exit(0) +EOF +(close-bad-fd) begin +close-bad-fd: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/close-normal.c b/pintos-env/pintos/tests/userprog/close-normal.c new file mode 100755 index 0000000..8ce04e3 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-normal.c @@ -0,0 +1,14 @@ +/* Opens a file and then closes it. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + msg ("close \"sample.txt\""); + close (handle); +} diff --git a/pintos-env/pintos/tests/userprog/close-normal.ck b/pintos-env/pintos/tests/userprog/close-normal.ck new file mode 100755 index 0000000..fe41342 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-normal.ck @@ -0,0 +1,12 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(close-normal) begin +(close-normal) open "sample.txt" +(close-normal) close "sample.txt" +(close-normal) end +close-normal: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/close-stdin.c b/pintos-env/pintos/tests/userprog/close-stdin.c new file mode 100755 index 0000000..9bbf9f2 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-stdin.c @@ -0,0 +1,11 @@ +/* Tries to close the keyboard input stream, which must either + fail silently or terminate with exit code -1. */ + +#include +#include "tests/main.h" + +void +test_main (void) +{ + close (0); +} diff --git a/pintos-env/pintos/tests/userprog/close-stdin.ck b/pintos-env/pintos/tests/userprog/close-stdin.ck new file mode 100755 index 0000000..3d28507 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-stdin.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(close-stdin) begin +(close-stdin) end +close-stdin: exit(0) +EOF +(close-stdin) begin +close-stdin: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/close-stdout.c b/pintos-env/pintos/tests/userprog/close-stdout.c new file mode 100755 index 0000000..886523f --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-stdout.c @@ -0,0 +1,11 @@ +/* Tries to close the console output stream, which must either + fail silently or terminate with exit code -1. */ + +#include +#include "tests/main.h" + +void +test_main (void) +{ + close (1); +} diff --git a/pintos-env/pintos/tests/userprog/close-stdout.ck b/pintos-env/pintos/tests/userprog/close-stdout.ck new file mode 100755 index 0000000..3cbbcff --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-stdout.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(close-stdout) begin +(close-stdout) end +close-stdout: exit(0) +EOF +(close-stdout) begin +close-stdout: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/close-twice.c b/pintos-env/pintos/tests/userprog/close-twice.c new file mode 100755 index 0000000..830bccf --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-twice.c @@ -0,0 +1,18 @@ +/* Opens a file and then tries to close it twice. The second + close must either fail silently or terminate with exit code + -1. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + msg ("close \"sample.txt\""); + close (handle); + msg ("close \"sample.txt\" again"); + close (handle); +} diff --git a/pintos-env/pintos/tests/userprog/close-twice.ck b/pintos-env/pintos/tests/userprog/close-twice.ck new file mode 100755 index 0000000..deb55a6 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/close-twice.ck @@ -0,0 +1,19 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(close-twice) begin +(close-twice) open "sample.txt" +(close-twice) close "sample.txt" +(close-twice) close "sample.txt" again +(close-twice) end +close-twice: exit(0) +EOF +(close-twice) begin +(close-twice) open "sample.txt" +(close-twice) close "sample.txt" +(close-twice) close "sample.txt" again +close-twice: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/create-bad-ptr.c b/pintos-env/pintos/tests/userprog/create-bad-ptr.c new file mode 100755 index 0000000..4a07bb3 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-bad-ptr.c @@ -0,0 +1,12 @@ +/* Passes a bad pointer to the create system call, + which must cause the process to be terminated with exit code + -1. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("create(0x20101234): %d", create ((char *) 0x20101234, 0)); +} diff --git a/pintos-env/pintos/tests/userprog/create-bad-ptr.ck b/pintos-env/pintos/tests/userprog/create-bad-ptr.ck new file mode 100755 index 0000000..ac13405 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-bad-ptr.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(create-bad-ptr) begin +create-bad-ptr: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/create-bound.c b/pintos-env/pintos/tests/userprog/create-bound.c new file mode 100755 index 0000000..0a829f3 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-bound.c @@ -0,0 +1,14 @@ +/* Opens a file whose name spans the boundary between two pages. + This is valid, so it must succeed. */ + +#include +#include "tests/userprog/boundary.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("create(\"quux.dat\"): %d", + create (copy_string_across_boundary ("quux.dat"), 0)); +} diff --git a/pintos-env/pintos/tests/userprog/create-bound.ck b/pintos-env/pintos/tests/userprog/create-bound.ck new file mode 100755 index 0000000..7656b7f --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-bound.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(create-bound) begin +(create-bound) create("quux.dat"): 1 +(create-bound) end +create-bound: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/create-empty.c b/pintos-env/pintos/tests/userprog/create-empty.c new file mode 100755 index 0000000..fa26b43 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-empty.c @@ -0,0 +1,10 @@ +/* Tries to create a file with the empty string as its name. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("create(\"\"): %d", create ("", 0)); +} diff --git a/pintos-env/pintos/tests/userprog/create-empty.ck b/pintos-env/pintos/tests/userprog/create-empty.ck new file mode 100755 index 0000000..93a1058 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-empty.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(create-empty) begin +(create-empty) create(""): 0 +(create-empty) end +create-empty: exit(0) +EOF +(create-empty) begin +create-empty: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/create-exists.c b/pintos-env/pintos/tests/userprog/create-exists.c new file mode 100755 index 0000000..d395008 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-exists.c @@ -0,0 +1,16 @@ +/* Verifies that trying to create a file under a name that + already exists will fail. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (create ("quux.dat", 0), "create quux.dat"); + CHECK (create ("warble.dat", 0), "create warble.dat"); + CHECK (!create ("quux.dat", 0), "try to re-create quux.dat"); + CHECK (create ("baffle.dat", 0), "create baffle.dat"); + CHECK (!create ("warble.dat", 0), "try to re-create quux.dat"); +} diff --git a/pintos-env/pintos/tests/userprog/create-exists.ck b/pintos-env/pintos/tests/userprog/create-exists.ck new file mode 100755 index 0000000..006885e --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-exists.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(create-exists) begin +(create-exists) create quux.dat +(create-exists) create warble.dat +(create-exists) try to re-create quux.dat +(create-exists) create baffle.dat +(create-exists) try to re-create quux.dat +(create-exists) end +create-exists: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/create-long.c b/pintos-env/pintos/tests/userprog/create-long.c new file mode 100755 index 0000000..16b31bd --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-long.c @@ -0,0 +1,17 @@ +/* Tries to create a file with a name that is much too long, + which must fail. */ + +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + static char name[512]; + memset (name, 'x', sizeof name); + name[sizeof name - 1] = '\0'; + + msg ("create(\"x...\"): %d", create (name, 0)); +} diff --git a/pintos-env/pintos/tests/userprog/create-long.ck b/pintos-env/pintos/tests/userprog/create-long.ck new file mode 100755 index 0000000..628411c --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-long.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(create-long) begin +(create-long) create("x..."): 0 +(create-long) end +create-long: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/create-normal.c b/pintos-env/pintos/tests/userprog/create-normal.c new file mode 100755 index 0000000..3cbc463 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-normal.c @@ -0,0 +1,10 @@ +/* Creates an ordinary empty file. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (create ("quux.dat", 0), "create quux.dat"); +} diff --git a/pintos-env/pintos/tests/userprog/create-normal.ck b/pintos-env/pintos/tests/userprog/create-normal.ck new file mode 100755 index 0000000..ca74a6e --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-normal.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(create-normal) begin +(create-normal) create quux.dat +(create-normal) end +create-normal: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/create-null.c b/pintos-env/pintos/tests/userprog/create-null.c new file mode 100755 index 0000000..287cb23 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-null.c @@ -0,0 +1,11 @@ +/* Tries to create a file with the null pointer as its name. + The process must be terminated with exit code -1. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("create(NULL): %d", create (NULL, 0)); +} diff --git a/pintos-env/pintos/tests/userprog/create-null.ck b/pintos-env/pintos/tests/userprog/create-null.ck new file mode 100755 index 0000000..09b7872 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/create-null.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(create-null) begin +create-null: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/exec-arg.c b/pintos-env/pintos/tests/userprog/exec-arg.c new file mode 100755 index 0000000..82d0744 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-arg.c @@ -0,0 +1,10 @@ +/* Tests argument passing to child processes. */ + +#include +#include "tests/main.h" + +void +test_main (void) +{ + wait (exec ("child-args childarg")); +} diff --git a/pintos-env/pintos/tests/userprog/exec-arg.ck b/pintos-env/pintos/tests/userprog/exec-arg.ck new file mode 100755 index 0000000..b7533ed --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-arg.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(exec-arg) begin +(args) begin +(args) argc = 2 +(args) argv[0] = 'child-args' +(args) argv[1] = 'childarg' +(args) argv[2] = null +(args) end +child-args: exit(0) +(exec-arg) end +exec-arg: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/exec-bad-ptr.c b/pintos-env/pintos/tests/userprog/exec-bad-ptr.c new file mode 100755 index 0000000..0abadd3 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-bad-ptr.c @@ -0,0 +1,11 @@ +/* Passes an invalid pointer to the exec system call. + The process must be terminated with -1 exit code. */ + +#include +#include "tests/main.h" + +void +test_main (void) +{ + exec ((char *) 0x20101234); +} diff --git a/pintos-env/pintos/tests/userprog/exec-bad-ptr.ck b/pintos-env/pintos/tests/userprog/exec-bad-ptr.ck new file mode 100755 index 0000000..63f5f78 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-bad-ptr.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(exec-bad-ptr) begin +(exec-bad-ptr) end +exec-bad-ptr: exit(0) +EOF +(exec-bad-ptr) begin +exec-bad-ptr: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/exec-missing.c b/pintos-env/pintos/tests/userprog/exec-missing.c new file mode 100755 index 0000000..bf08cad --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-missing.c @@ -0,0 +1,12 @@ +/* Tries to execute a nonexistent process. + The exec system call must return -1. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("exec(\"no-such-file\"): %d", exec ("no-such-file")); +} diff --git a/pintos-env/pintos/tests/userprog/exec-missing.ck b/pintos-env/pintos/tests/userprog/exec-missing.ck new file mode 100755 index 0000000..0ef7aaa --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-missing.ck @@ -0,0 +1,31 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF', <<'EOF', <<'EOF']); +(exec-missing) begin +load: no-such-file: open failed +(exec-missing) exec("no-such-file"): -1 +(exec-missing) end +exec-missing: exit(0) +EOF +(exec-missing) begin +(exec-missing) exec("no-such-file"): -1 +(exec-missing) end +exec-missing: exit(0) +EOF +(exec-missing) begin +load: no-such-file: open failed +no-such-file: exit(-1) +(exec-missing) exec("no-such-file"): -1 +(exec-missing) end +exec-missing: exit(0) +EOF +(exec-missing) begin +load: no-such-file: open failed +(exec-missing) exec("no-such-file"): -1 +no-such-file: exit(-1) +(exec-missing) end +exec-missing: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/exec-multiple.c b/pintos-env/pintos/tests/userprog/exec-multiple.c new file mode 100755 index 0000000..ba4c26e --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-multiple.c @@ -0,0 +1,14 @@ +/* Executes and waits for multiple child processes. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + wait (exec ("child-simple")); + wait (exec ("child-simple")); + wait (exec ("child-simple")); + wait (exec ("child-simple")); +} diff --git a/pintos-env/pintos/tests/userprog/exec-multiple.ck b/pintos-env/pintos/tests/userprog/exec-multiple.ck new file mode 100755 index 0000000..99624cd --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-multiple.ck @@ -0,0 +1,18 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(exec-multiple) begin +(child-simple) run +child-simple: exit(81) +(child-simple) run +child-simple: exit(81) +(child-simple) run +child-simple: exit(81) +(child-simple) run +child-simple: exit(81) +(exec-multiple) end +exec-multiple: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/exec-once.c b/pintos-env/pintos/tests/userprog/exec-once.c new file mode 100755 index 0000000..7bae5a1 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-once.c @@ -0,0 +1,11 @@ +/* Executes and waits for a single child process. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + wait (exec ("child-simple")); +} diff --git a/pintos-env/pintos/tests/userprog/exec-once.ck b/pintos-env/pintos/tests/userprog/exec-once.ck new file mode 100755 index 0000000..00b59ed --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exec-once.ck @@ -0,0 +1,12 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(exec-once) begin +(child-simple) run +child-simple: exit(81) +(exec-once) end +exec-once: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/exit.c b/pintos-env/pintos/tests/userprog/exit.c new file mode 100755 index 0000000..cb4eb8f --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exit.c @@ -0,0 +1,11 @@ +/* Tests the exit system call. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + exit (57); + fail ("should have called exit(57)"); +} diff --git a/pintos-env/pintos/tests/userprog/exit.ck b/pintos-env/pintos/tests/userprog/exit.ck new file mode 100755 index 0000000..a552702 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/exit.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(exit) begin +exit: exit(57) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/halt.c b/pintos-env/pintos/tests/userprog/halt.c new file mode 100755 index 0000000..4a99bce --- /dev/null +++ b/pintos-env/pintos/tests/userprog/halt.c @@ -0,0 +1,11 @@ +/* Tests the halt system call. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + halt (); + fail ("should have halted"); +} diff --git a/pintos-env/pintos/tests/userprog/halt.ck b/pintos-env/pintos/tests/userprog/halt.ck new file mode 100755 index 0000000..1b701ed --- /dev/null +++ b/pintos-env/pintos/tests/userprog/halt.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; + +our ($test); +my (@output) = read_text_file ("$test.output"); + +common_checks ("run", @output); + +fail "missing 'begin' message\n" + if !grep ($_ eq '(halt) begin', @output); +fail "found 'fail' message--halt didn't really halt\n" + if grep ($_ eq '(halt) fail', @output); +pass; diff --git a/pintos-env/pintos/tests/userprog/lib/.gitignore b/pintos-env/pintos/tests/userprog/lib/.gitignore new file mode 100755 index 0000000..a438335 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/lib/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/pintos-env/pintos/tests/userprog/lib/user/.dummy b/pintos-env/pintos/tests/userprog/lib/user/.dummy new file mode 100755 index 0000000..e69de29 diff --git a/pintos-env/pintos/tests/userprog/lib/user/.gitignore b/pintos-env/pintos/tests/userprog/lib/user/.gitignore new file mode 100755 index 0000000..a438335 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/lib/user/.gitignore @@ -0,0 +1 @@ +*.d diff --git a/pintos-env/pintos/tests/userprog/multi-child-fd.c b/pintos-env/pintos/tests/userprog/multi-child-fd.c new file mode 100755 index 0000000..48de4b4 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/multi-child-fd.c @@ -0,0 +1,25 @@ +/* Opens a file and then runs a subprocess that tries to close + the file. (Pintos does not have inheritance of file handles, + so this must fail.) The parent process then attempts to use + the file handle, which must succeed. */ + +#include +#include +#include "tests/userprog/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char child_cmd[128]; + int handle; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + snprintf (child_cmd, sizeof child_cmd, "child-close %d", handle); + + msg ("wait(exec()) = %d", wait (exec (child_cmd))); + + check_file_handle (handle, "sample.txt", sample, sizeof sample - 1); +} diff --git a/pintos-env/pintos/tests/userprog/multi-child-fd.ck b/pintos-env/pintos/tests/userprog/multi-child-fd.ck new file mode 100755 index 0000000..d0b3a33 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/multi-child-fd.ck @@ -0,0 +1,25 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(multi-child-fd) begin +(multi-child-fd) open "sample.txt" +(child-close) begin +(child-close) end +child-close: exit(0) +(multi-child-fd) wait(exec()) = 0 +(multi-child-fd) verified contents of "sample.txt" +(multi-child-fd) end +multi-child-fd: exit(0) +EOF +(multi-child-fd) begin +(multi-child-fd) open "sample.txt" +(child-close) begin +child-close: exit(-1) +(multi-child-fd) wait(exec()) = -1 +(multi-child-fd) verified contents of "sample.txt" +(multi-child-fd) end +multi-child-fd: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/multi-recurse.c b/pintos-env/pintos/tests/userprog/multi-recurse.c new file mode 100755 index 0000000..7172ec3 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/multi-recurse.c @@ -0,0 +1,34 @@ +/* Executes itself recursively to the depth indicated by the + first command-line argument. */ + +#include +#include +#include +#include +#include "tests/lib.h" + +const char *test_name = "multi-recurse"; + +int +main (int argc UNUSED, char *argv[]) +{ + int n = atoi (argv[1]); + + msg ("begin %d", n); + if (n != 0) + { + char child_cmd[128]; + pid_t child_pid; + int code; + + snprintf (child_cmd, sizeof child_cmd, "multi-recurse %d", n - 1); + CHECK ((child_pid = exec (child_cmd)) != -1, "exec(\"%s\")", child_cmd); + + code = wait (child_pid); + if (code != n - 1) + fail ("wait(exec(\"%s\")) returned %d", child_cmd, code); + } + + msg ("end %d", n); + return n; +} diff --git a/pintos-env/pintos/tests/userprog/multi-recurse.ck b/pintos-env/pintos/tests/userprog/multi-recurse.ck new file mode 100755 index 0000000..41eb4a6 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/multi-recurse.ck @@ -0,0 +1,70 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(multi-recurse) begin 15 +(multi-recurse) exec("multi-recurse 14") +(multi-recurse) begin 14 +(multi-recurse) exec("multi-recurse 13") +(multi-recurse) begin 13 +(multi-recurse) exec("multi-recurse 12") +(multi-recurse) begin 12 +(multi-recurse) exec("multi-recurse 11") +(multi-recurse) begin 11 +(multi-recurse) exec("multi-recurse 10") +(multi-recurse) begin 10 +(multi-recurse) exec("multi-recurse 9") +(multi-recurse) begin 9 +(multi-recurse) exec("multi-recurse 8") +(multi-recurse) begin 8 +(multi-recurse) exec("multi-recurse 7") +(multi-recurse) begin 7 +(multi-recurse) exec("multi-recurse 6") +(multi-recurse) begin 6 +(multi-recurse) exec("multi-recurse 5") +(multi-recurse) begin 5 +(multi-recurse) exec("multi-recurse 4") +(multi-recurse) begin 4 +(multi-recurse) exec("multi-recurse 3") +(multi-recurse) begin 3 +(multi-recurse) exec("multi-recurse 2") +(multi-recurse) begin 2 +(multi-recurse) exec("multi-recurse 1") +(multi-recurse) begin 1 +(multi-recurse) exec("multi-recurse 0") +(multi-recurse) begin 0 +(multi-recurse) end 0 +multi-recurse: exit(0) +(multi-recurse) end 1 +multi-recurse: exit(1) +(multi-recurse) end 2 +multi-recurse: exit(2) +(multi-recurse) end 3 +multi-recurse: exit(3) +(multi-recurse) end 4 +multi-recurse: exit(4) +(multi-recurse) end 5 +multi-recurse: exit(5) +(multi-recurse) end 6 +multi-recurse: exit(6) +(multi-recurse) end 7 +multi-recurse: exit(7) +(multi-recurse) end 8 +multi-recurse: exit(8) +(multi-recurse) end 9 +multi-recurse: exit(9) +(multi-recurse) end 10 +multi-recurse: exit(10) +(multi-recurse) end 11 +multi-recurse: exit(11) +(multi-recurse) end 12 +multi-recurse: exit(12) +(multi-recurse) end 13 +multi-recurse: exit(13) +(multi-recurse) end 14 +multi-recurse: exit(14) +(multi-recurse) end 15 +multi-recurse: exit(15) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/no-vm/Make.tests b/pintos-env/pintos/tests/userprog/no-vm/Make.tests new file mode 100755 index 0000000..a545e18 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/no-vm/Make.tests @@ -0,0 +1,8 @@ +# -*- makefile -*- + +tests/userprog/no-vm_TESTS = tests/userprog/no-vm/multi-oom +tests/userprog/no-vm_PROGS = $(tests/userprog/no-vm_TESTS) +tests/userprog/no-vm/multi-oom_SRC = tests/userprog/no-vm/multi-oom.c \ +tests/lib.c + +tests/userprog/no-vm/multi-oom.output: TIMEOUT = 360 diff --git a/pintos-env/pintos/tests/userprog/no-vm/Rubric b/pintos-env/pintos/tests/userprog/no-vm/Rubric new file mode 100755 index 0000000..c3816c6 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/no-vm/Rubric @@ -0,0 +1,3 @@ +Functionality of features that VM might break: + +1 multi-oom diff --git a/pintos-env/pintos/tests/userprog/no-vm/multi-oom.c b/pintos-env/pintos/tests/userprog/no-vm/multi-oom.c new file mode 100755 index 0000000..6a4472d --- /dev/null +++ b/pintos-env/pintos/tests/userprog/no-vm/multi-oom.c @@ -0,0 +1,179 @@ +/* Recursively executes itself until the child fails to execute. + We expect that at least 30 copies can run. + + We count how many children your kernel was able to execute + before it fails to start a new process. We require that, + if a process doesn't actually get to start, exec() must + return -1, not a valid PID. + + We repeat this process 10 times, checking that your kernel + allows for the same level of depth every time. + + In addition, some processes will spawn children that terminate + abnormally after allocating some resources. + + Written by Godmar Back + */ + +#include +#include +#include +#include +#include +#include +#include +#include "tests/lib.h" + +static const int EXPECTED_DEPTH_TO_PASS = 30; +static const int EXPECTED_REPETITIONS = 10; + +const char *test_name = "multi-oom"; + +enum child_termination_mode { RECURSE, CRASH }; + +/* Spawn a recursive copy of ourselves, passing along instructions + for the child. */ +static pid_t +spawn_child (int c, enum child_termination_mode mode) +{ + char child_cmd[128]; + snprintf (child_cmd, sizeof child_cmd, + "%s %d %s", test_name, c, mode == CRASH ? "-k" : ""); + return exec (child_cmd); +} + +/* Open a number of files (and fail to close them). + The kernel must free any kernel resources associated + with these file descriptors. */ +static void +consume_some_resources (void) +{ + int fd, fdmax = 126; + + /* Open as many files as we can, up to fdmax. + Depending on how file descriptors are allocated inside + the kernel, open() may fail if the kernel is low on memory. + A low-memory condition in open() should not lead to the + termination of the process. */ + for (fd = 0; fd < fdmax; fd++) + if (open (test_name) == -1) + break; +} + +/* Consume some resources, then terminate this process + in some abnormal way. */ +static int NO_INLINE +consume_some_resources_and_die (int seed) +{ + consume_some_resources (); + random_init (seed); + int *PHYS_BASE = (int *)0xC0000000; + + switch (random_ulong () % 5) + { + case 0: + *(int *) NULL = 42; + + case 1: + return *(int *) NULL; + + case 2: + return *PHYS_BASE; + + case 3: + *PHYS_BASE = 42; + + case 4: + open ((char *)PHYS_BASE); + exit (-1); + + default: + NOT_REACHED (); + } + return 0; +} + +/* The first copy is invoked without command line arguments. + Subsequent copies are invoked with a parameter 'depth' + that describes how many parent processes preceded them. + Each process spawns one or multiple recursive copies of + itself, passing 'depth+1' as depth. + + Some children are started with the '-k' flag, which will + result in abnormal termination. + */ +int +main (int argc, char *argv[]) +{ + int n; + + n = argc > 1 ? atoi (argv[1]) : 0; + bool is_at_root = (n == 0); + if (is_at_root) + msg ("begin"); + + /* If -k is passed, crash this process. */ + if (argc > 2 && !strcmp(argv[2], "-k")) + { + consume_some_resources_and_die (n); + NOT_REACHED (); + } + + int howmany = is_at_root ? EXPECTED_REPETITIONS : 1; + int i, expected_depth = -1; + + for (i = 0; i < howmany; i++) + { + pid_t child_pid; + + /* Spawn a child that will be abnormally terminated. + To speed the test up, do this only for processes + spawned at a certain depth. */ + if (n > EXPECTED_DEPTH_TO_PASS/2) + { + child_pid = spawn_child (n + 1, CRASH); + if (child_pid != -1) + { + if (wait (child_pid) != -1) + fail ("crashed child should return -1."); + } + /* If spawning this child failed, so should + the next spawn_child below. */ + } + + /* Now spawn the child that will recurse. */ + child_pid = spawn_child (n + 1, RECURSE); + + /* If maximum depth is reached, return result. */ + if (child_pid == -1) + return n; + + /* Else wait for child to report how deeply it was able to recurse. */ + int reached_depth = wait (child_pid); + if (reached_depth == -1) + fail ("wait returned -1."); + + /* Record the depth reached during the first run; on subsequent + runs, fail if those runs do not match the depth achieved on the + first run. */ + if (i == 0) + expected_depth = reached_depth; + else if (expected_depth != reached_depth) + fail ("after run %d/%d, expected depth %d, actual depth %d.", + i, howmany, expected_depth, reached_depth); + ASSERT (expected_depth == reached_depth); + } + + consume_some_resources (); + + if (n == 0) + { + if (expected_depth < EXPECTED_DEPTH_TO_PASS) + fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS); + msg ("success. program forked %d times.", howmany); + msg ("end"); + } + + return expected_depth; +} +// vim: sw=2 diff --git a/pintos-env/pintos/tests/userprog/no-vm/multi-oom.ck b/pintos-env/pintos/tests/userprog/no-vm/multi-oom.ck new file mode 100755 index 0000000..59a0bcd --- /dev/null +++ b/pintos-env/pintos/tests/userprog/no-vm/multi-oom.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, IGNORE_EXIT_CODES => 1, [<<'EOF']); +(multi-oom) begin +(multi-oom) success. program forked 10 times. +(multi-oom) end +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/null.ck b/pintos-env/pintos/tests/userprog/null.ck new file mode 100755 index 0000000..980de35 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/null.ck @@ -0,0 +1,8 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +system call! +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/open-bad-ptr.c b/pintos-env/pintos/tests/userprog/open-bad-ptr.c new file mode 100755 index 0000000..9cd4edf --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-bad-ptr.c @@ -0,0 +1,13 @@ +/* Passes an invalid pointer to the open system call. + The process must be terminated with -1 exit code. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("open(0x20101234): %d", open ((char *) 0x20101234)); + fail ("should have called exit(-1)"); +} diff --git a/pintos-env/pintos/tests/userprog/open-bad-ptr.ck b/pintos-env/pintos/tests/userprog/open-bad-ptr.ck new file mode 100755 index 0000000..45349e2 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-bad-ptr.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(open-bad-ptr) begin +(open-bad-ptr) end +open-bad-ptr: exit(0) +EOF +(open-bad-ptr) begin +open-bad-ptr: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/open-boundary.c b/pintos-env/pintos/tests/userprog/open-boundary.c new file mode 100755 index 0000000..cc8ff8b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-boundary.c @@ -0,0 +1,14 @@ +/* Creates a file whose name spans the boundary between two pages. + This is valid, so it must succeed. */ + +#include +#include "tests/userprog/boundary.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (open (copy_string_across_boundary ("sample.txt")) > 1, + "open \"sample.txt\""); +} diff --git a/pintos-env/pintos/tests/userprog/open-boundary.ck b/pintos-env/pintos/tests/userprog/open-boundary.ck new file mode 100755 index 0000000..8060d22 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-boundary.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(open-boundary) begin +(open-boundary) open "sample.txt" +(open-boundary) end +open-boundary: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/open-empty.c b/pintos-env/pintos/tests/userprog/open-empty.c new file mode 100755 index 0000000..3ea9907 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-empty.c @@ -0,0 +1,13 @@ +/* Tries to open a file with the empty string as its name. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle = open (""); + if (handle != -1) + fail ("open() returned %d instead of -1", handle); +} diff --git a/pintos-env/pintos/tests/userprog/open-empty.ck b/pintos-env/pintos/tests/userprog/open-empty.ck new file mode 100755 index 0000000..885fb41 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-empty.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(open-empty) begin +(open-empty) end +open-empty: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/open-missing.c b/pintos-env/pintos/tests/userprog/open-missing.c new file mode 100755 index 0000000..13ecbda --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-missing.c @@ -0,0 +1,13 @@ +/* Tries to open a nonexistent file. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle = open ("no-such-file"); + if (handle != -1) + fail ("open() returned %d", handle); +} diff --git a/pintos-env/pintos/tests/userprog/open-missing.ck b/pintos-env/pintos/tests/userprog/open-missing.ck new file mode 100755 index 0000000..d72d878 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-missing.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(open-missing) begin +(open-missing) end +open-missing: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/open-normal.c b/pintos-env/pintos/tests/userprog/open-normal.c new file mode 100755 index 0000000..5132465 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-normal.c @@ -0,0 +1,13 @@ +/* Open a file. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle = open ("sample.txt"); + if (handle < 2) + fail ("open() returned %d", handle); +} diff --git a/pintos-env/pintos/tests/userprog/open-normal.ck b/pintos-env/pintos/tests/userprog/open-normal.ck new file mode 100755 index 0000000..4f6c342 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-normal.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(open-normal) begin +(open-normal) end +open-normal: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/open-null.c b/pintos-env/pintos/tests/userprog/open-null.c new file mode 100755 index 0000000..bb418b8 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-null.c @@ -0,0 +1,12 @@ +/* Tries to open a file with the null pointer as its name. + The process must be terminated with exit code -1. */ + +#include +#include +#include "tests/main.h" + +void +test_main (void) +{ + open (NULL); +} diff --git a/pintos-env/pintos/tests/userprog/open-null.ck b/pintos-env/pintos/tests/userprog/open-null.ck new file mode 100755 index 0000000..b4a3bcb --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-null.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(open-null) begin +(open-null) end +open-null: exit(0) +EOF +(open-null) begin +open-null: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/open-twice.c b/pintos-env/pintos/tests/userprog/open-twice.c new file mode 100755 index 0000000..dd333af --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-twice.c @@ -0,0 +1,19 @@ +/* Tries to open the same file twice, + which must succeed and must return a different file descriptor + in each case. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int h1 = open ("sample.txt"); + int h2 = open ("sample.txt"); + + CHECK ((h1 = open ("sample.txt")) > 1, "open \"sample.txt\" once"); + CHECK ((h2 = open ("sample.txt")) > 1, "open \"sample.txt\" again"); + if (h1 == h2) + fail ("open() returned %d both times", h1); +} diff --git a/pintos-env/pintos/tests/userprog/open-twice.ck b/pintos-env/pintos/tests/userprog/open-twice.ck new file mode 100755 index 0000000..64fa805 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/open-twice.ck @@ -0,0 +1,12 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(open-twice) begin +(open-twice) open "sample.txt" once +(open-twice) open "sample.txt" again +(open-twice) end +open-twice: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/read-bad-fd.c b/pintos-env/pintos/tests/userprog/read-bad-fd.c new file mode 100755 index 0000000..a8b190d --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-bad-fd.c @@ -0,0 +1,21 @@ +/* Tries to read from an invalid fd, + which must either fail silently or terminate the process with + exit code -1. */ + +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char buf; + read (0x20101234, &buf, 1); + read (5, &buf, 1); + read (1234, &buf, 1); + read (-1, &buf, 1); + read (-1024, &buf, 1); + read (INT_MIN, &buf, 1); + read (INT_MAX, &buf, 1); +} diff --git a/pintos-env/pintos/tests/userprog/read-bad-fd.ck b/pintos-env/pintos/tests/userprog/read-bad-fd.ck new file mode 100755 index 0000000..5fedcc7 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-bad-fd.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(read-bad-fd) begin +(read-bad-fd) end +read-bad-fd: exit(0) +EOF +(read-bad-fd) begin +read-bad-fd: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/read-bad-ptr.c b/pintos-env/pintos/tests/userprog/read-bad-ptr.c new file mode 100755 index 0000000..8fe756e --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-bad-ptr.c @@ -0,0 +1,16 @@ +/* Passes an invalid pointer to the read system call. + The process must be terminated with -1 exit code. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + read (handle, (char *) 0xc0100000, 123); + fail ("should not have survived read()"); +} diff --git a/pintos-env/pintos/tests/userprog/read-bad-ptr.ck b/pintos-env/pintos/tests/userprog/read-bad-ptr.ck new file mode 100755 index 0000000..d10accf --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-bad-ptr.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(read-bad-ptr) begin +(read-bad-ptr) open "sample.txt" +(read-bad-ptr) end +read-bad-ptr: exit(0) +EOF +(read-bad-ptr) begin +(read-bad-ptr) open "sample.txt" +read-bad-ptr: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/read-boundary.c b/pintos-env/pintos/tests/userprog/read-boundary.c new file mode 100755 index 0000000..9c19966 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-boundary.c @@ -0,0 +1,30 @@ +/* Reads data spanning two pages in virtual address space, + which must succeed. */ + +#include +#include +#include "tests/userprog/boundary.h" +#include "tests/userprog/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + int byte_cnt; + char *buffer; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + buffer = get_boundary_area () - sizeof sample / 2; + byte_cnt = read (handle, buffer, sizeof sample - 1); + if (byte_cnt != sizeof sample - 1) + fail ("read() returned %d instead of %zu", byte_cnt, sizeof sample - 1); + else if (strcmp (sample, buffer)) + { + msg ("expected text:\n%s", sample); + msg ("text actually read:\n%s", buffer); + fail ("expected text differs from actual"); + } +} diff --git a/pintos-env/pintos/tests/userprog/read-boundary.ck b/pintos-env/pintos/tests/userprog/read-boundary.ck new file mode 100755 index 0000000..08dc161 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-boundary.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(read-boundary) begin +(read-boundary) open "sample.txt" +(read-boundary) end +read-boundary: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/read-normal.c b/pintos-env/pintos/tests/userprog/read-normal.c new file mode 100755 index 0000000..16d15cc --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-normal.c @@ -0,0 +1,11 @@ +/* Try reading a file in the most normal way. */ + +#include "tests/userprog/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + check_file ("sample.txt", sample, sizeof sample - 1); +} diff --git a/pintos-env/pintos/tests/userprog/read-normal.ck b/pintos-env/pintos/tests/userprog/read-normal.ck new file mode 100755 index 0000000..0ed2998 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-normal.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(read-normal) begin +(read-normal) open "sample.txt" for verification +(read-normal) verified contents of "sample.txt" +(read-normal) close "sample.txt" +(read-normal) end +read-normal: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/read-stdout.c b/pintos-env/pintos/tests/userprog/read-stdout.c new file mode 100755 index 0000000..d0630b9 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-stdout.c @@ -0,0 +1,14 @@ +/* Try reading from fd 1 (stdout), + which may just fail or terminate the process with -1 exit + code. */ + +#include +#include +#include "tests/main.h" + +void +test_main (void) +{ + char buf; + read (STDOUT_FILENO, &buf, 1); +} diff --git a/pintos-env/pintos/tests/userprog/read-stdout.ck b/pintos-env/pintos/tests/userprog/read-stdout.ck new file mode 100755 index 0000000..7d87b52 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-stdout.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(read-stdout) begin +(read-stdout) end +read-stdout: exit(0) +EOF +(read-stdout) begin +read-stdout: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/read-zero.c b/pintos-env/pintos/tests/userprog/read-zero.c new file mode 100755 index 0000000..e441817 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-zero.c @@ -0,0 +1,22 @@ +/* Try a 0-byte read, which should return 0 without reading + anything. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle, byte_cnt; + char buf; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + buf = 123; + byte_cnt = read (handle, &buf, 0); + if (byte_cnt != 0) + fail ("read() returned %d instead of 0", byte_cnt); + else if (buf != 123) + fail ("0-byte read() modified buffer"); +} diff --git a/pintos-env/pintos/tests/userprog/read-zero.ck b/pintos-env/pintos/tests/userprog/read-zero.ck new file mode 100755 index 0000000..8346dbc --- /dev/null +++ b/pintos-env/pintos/tests/userprog/read-zero.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(read-zero) begin +(read-zero) open "sample.txt" +(read-zero) end +read-zero: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/rox-child.c b/pintos-env/pintos/tests/userprog/rox-child.c new file mode 100755 index 0000000..30afba2 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/rox-child.c @@ -0,0 +1,5 @@ +/* Ensure that the executable of a running process cannot be + modified, even by a child process. */ + +#define CHILD_CNT "1" +#include "tests/userprog/rox-child.inc" diff --git a/pintos-env/pintos/tests/userprog/rox-child.ck b/pintos-env/pintos/tests/userprog/rox-child.ck new file mode 100755 index 0000000..e6363fb --- /dev/null +++ b/pintos-env/pintos/tests/userprog/rox-child.ck @@ -0,0 +1,20 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(rox-child) begin +(rox-child) open "child-rox" +(rox-child) read "child-rox" +(rox-child) write "child-rox" +(rox-child) exec "child-rox 1" +(child-rox) begin +(child-rox) try to write "child-rox" +(child-rox) try to write "child-rox" +(child-rox) end +child-rox: exit(12) +(rox-child) write "child-rox" +(rox-child) end +rox-child: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/rox-child.inc b/pintos-env/pintos/tests/userprog/rox-child.inc new file mode 100755 index 0000000..1e2ade9 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/rox-child.inc @@ -0,0 +1,33 @@ +/* -*- c -*- */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + const char *child_cmd = "child-rox " CHILD_CNT; + int handle; + pid_t child; + char buffer[16]; + + /* Open child-rox, read from it, write back same data. */ + CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\""); + CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer, + "read \"child-rox\""); + seek (handle, 0); + CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer, + "write \"child-rox\""); + + /* Execute child-rox and wait for it. */ + CHECK ((child = exec (child_cmd)) != -1, "exec \"%s\"", child_cmd); + quiet = true; + CHECK (wait (child) == 12, "wait for child"); + quiet = false; + + /* Write to child-rox again. */ + seek (handle, 0); + CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer, + "write \"child-rox\""); +} diff --git a/pintos-env/pintos/tests/userprog/rox-multichild.c b/pintos-env/pintos/tests/userprog/rox-multichild.c new file mode 100755 index 0000000..8e74dab --- /dev/null +++ b/pintos-env/pintos/tests/userprog/rox-multichild.c @@ -0,0 +1,5 @@ +/* Ensure that the executable of a running process cannot be + modified, even in the presence of multiple children. */ + +#define CHILD_CNT "5" +#include "tests/userprog/rox-child.inc" diff --git a/pintos-env/pintos/tests/userprog/rox-multichild.ck b/pintos-env/pintos/tests/userprog/rox-multichild.ck new file mode 100755 index 0000000..14b27db --- /dev/null +++ b/pintos-env/pintos/tests/userprog/rox-multichild.ck @@ -0,0 +1,44 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(rox-multichild) begin +(rox-multichild) open "child-rox" +(rox-multichild) read "child-rox" +(rox-multichild) write "child-rox" +(rox-multichild) exec "child-rox 5" +(child-rox) begin +(child-rox) try to write "child-rox" +(child-rox) exec "child-rox 4" +(child-rox) begin +(child-rox) try to write "child-rox" +(child-rox) exec "child-rox 3" +(child-rox) begin +(child-rox) try to write "child-rox" +(child-rox) exec "child-rox 2" +(child-rox) begin +(child-rox) try to write "child-rox" +(child-rox) exec "child-rox 1" +(child-rox) begin +(child-rox) try to write "child-rox" +(child-rox) try to write "child-rox" +(child-rox) end +child-rox: exit(12) +(child-rox) try to write "child-rox" +(child-rox) end +child-rox: exit(12) +(child-rox) try to write "child-rox" +(child-rox) end +child-rox: exit(12) +(child-rox) try to write "child-rox" +(child-rox) end +child-rox: exit(12) +(child-rox) try to write "child-rox" +(child-rox) end +child-rox: exit(12) +(rox-multichild) write "child-rox" +(rox-multichild) end +rox-multichild: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/rox-simple.c b/pintos-env/pintos/tests/userprog/rox-simple.c new file mode 100755 index 0000000..e84a064 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/rox-simple.c @@ -0,0 +1,19 @@ +/* Ensure that the executable of a running process cannot be + modified. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + char buffer[16]; + + CHECK ((handle = open ("rox-simple")) > 1, "open \"rox-simple\""); + CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer, + "read \"rox-simple\""); + CHECK (write (handle, buffer, sizeof buffer) == 0, + "try to write \"rox-simple\""); +} diff --git a/pintos-env/pintos/tests/userprog/rox-simple.ck b/pintos-env/pintos/tests/userprog/rox-simple.ck new file mode 100755 index 0000000..c9dcc66 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/rox-simple.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(rox-simple) begin +(rox-simple) open "rox-simple" +(rox-simple) read "rox-simple" +(rox-simple) try to write "rox-simple" +(rox-simple) end +rox-simple: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/sample.inc b/pintos-env/pintos/tests/userprog/sample.inc new file mode 100755 index 0000000..59f2bcb --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sample.inc @@ -0,0 +1,6 @@ +char sample[] = { + "\"Amazing Electronic Fact: If you scuffed your feet long enough without\n" + " touching anything, you would build up so many electrons that your\n" + " finger would explode! But this is nothing to worry about unless you\n" + " have carpeting.\" --Dave Barry\n" +}; diff --git a/pintos-env/pintos/tests/userprog/sample.txt b/pintos-env/pintos/tests/userprog/sample.txt new file mode 100755 index 0000000..5050fec --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sample.txt @@ -0,0 +1,4 @@ +"Amazing Electronic Fact: If you scuffed your feet long enough without + touching anything, you would build up so many electrons that your + finger would explode! But this is nothing to worry about unless you + have carpeting." --Dave Barry diff --git a/pintos-env/pintos/tests/userprog/sc-bad-arg.c b/pintos-env/pintos/tests/userprog/sc-bad-arg.c new file mode 100755 index 0000000..0b512a0 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-bad-arg.c @@ -0,0 +1,17 @@ +/* Sticks a system call number (SYS_EXIT) at the very top of the + stack, then invokes a system call with the stack pointer + (%esp) set to its address. The process must be terminated + with -1 exit code because the argument to the system call + would be above the top of the user address space. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + asm volatile ("movl $0xbffffffc, %%esp; movl %0, (%%esp); int $0x30" + : : "i" (SYS_EXIT)); + fail ("should have called exit(-1)"); +} diff --git a/pintos-env/pintos/tests/userprog/sc-bad-arg.ck b/pintos-env/pintos/tests/userprog/sc-bad-arg.ck new file mode 100755 index 0000000..8981105 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-bad-arg.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(sc-bad-arg) begin +sc-bad-arg: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/sc-bad-sp.c b/pintos-env/pintos/tests/userprog/sc-bad-sp.c new file mode 100755 index 0000000..39cce84 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-bad-sp.c @@ -0,0 +1,20 @@ +/* Invokes a system call with the stack pointer (%esp) set to a + bad address. The process must be terminated with -1 exit + code. + + For Project 3: The bad address lies approximately 64MB below + the code segment, so there is no ambiguity that this attempt + must be rejected even after stack growth is implemented. + Moreover, a good stack growth heuristics should probably not + grow the stack for the purpose of reading the system call + number and arguments. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + asm volatile ("movl $.-(64*1024*1024), %esp; int $0x30"); + fail ("should have called exit(-1)"); +} diff --git a/pintos-env/pintos/tests/userprog/sc-bad-sp.ck b/pintos-env/pintos/tests/userprog/sc-bad-sp.ck new file mode 100755 index 0000000..498cec1 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-bad-sp.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(sc-bad-sp) begin +sc-bad-sp: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/sc-boundary-2.c b/pintos-env/pintos/tests/userprog/sc-boundary-2.c new file mode 100755 index 0000000..8acf036 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-boundary-2.c @@ -0,0 +1,22 @@ +/* Invokes a system call with one byte of the system call's + argument on a separate page from the rest of the bytes. This + must work. */ + +#include +#include "tests/userprog/boundary.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + /* Make one byte of a syscall argument hang over into a second + page. */ + int *p = (int *) ((char *) get_boundary_area () - 7); + p[0] = SYS_EXIT; + p[1] = 67; + + /* Invoke the system call. */ + asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p)); + fail ("should have called exit(67)"); +} diff --git a/pintos-env/pintos/tests/userprog/sc-boundary-2.ck b/pintos-env/pintos/tests/userprog/sc-boundary-2.ck new file mode 100755 index 0000000..43766bf --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-boundary-2.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(sc-boundary-2) begin +sc-boundary-2: exit(67) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/sc-boundary.c b/pintos-env/pintos/tests/userprog/sc-boundary.c new file mode 100755 index 0000000..d889535 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-boundary.c @@ -0,0 +1,22 @@ +/* Invokes a system call with the system call number and its + argument on separate pages. This must work. */ + +#include +#include "tests/userprog/boundary.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + /* Put a syscall number at the end of one page + and its argument at the beginning of another. */ + int *p = get_boundary_area (); + p--; + p[0] = SYS_EXIT; + p[1] = 42; + + /* Invoke the system call. */ + asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p)); + fail ("should have called exit(42)"); +} diff --git a/pintos-env/pintos/tests/userprog/sc-boundary.ck b/pintos-env/pintos/tests/userprog/sc-boundary.ck new file mode 100755 index 0000000..3f7cbaf --- /dev/null +++ b/pintos-env/pintos/tests/userprog/sc-boundary.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(sc-boundary) begin +sc-boundary: exit(42) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/wait-bad-pid.c b/pintos-env/pintos/tests/userprog/wait-bad-pid.c new file mode 100755 index 0000000..3fe8ee4 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-bad-pid.c @@ -0,0 +1,11 @@ +/* Waits for an invalid pid. This may fail or terminate the + process with -1 exit code. */ + +#include +#include "tests/main.h" + +void +test_main (void) +{ + wait ((pid_t) 0x0c020301); +} diff --git a/pintos-env/pintos/tests/userprog/wait-bad-pid.ck b/pintos-env/pintos/tests/userprog/wait-bad-pid.ck new file mode 100755 index 0000000..db63fb9 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-bad-pid.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(wait-bad-pid) begin +(wait-bad-pid) end +wait-bad-pid: exit(0) +EOF +(wait-bad-pid) begin +wait-bad-pid: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/wait-killed.c b/pintos-env/pintos/tests/userprog/wait-killed.c new file mode 100755 index 0000000..6a2a6b5 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-killed.c @@ -0,0 +1,11 @@ +/* Wait for a process that will be killed for bad behavior. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("wait(exec()) = %d", wait (exec ("child-bad"))); +} diff --git a/pintos-env/pintos/tests/userprog/wait-killed.ck b/pintos-env/pintos/tests/userprog/wait-killed.ck new file mode 100755 index 0000000..5df0e9c --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-killed.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(wait-killed) begin +(child-bad) begin +child-bad: exit(-1) +(wait-killed) wait(exec()) = -1 +(wait-killed) end +wait-killed: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/wait-simple.c b/pintos-env/pintos/tests/userprog/wait-simple.c new file mode 100755 index 0000000..d3afcf3 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-simple.c @@ -0,0 +1,11 @@ +/* Wait for a subprocess to finish. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + msg ("wait(exec()) = %d", wait (exec ("child-simple"))); +} diff --git a/pintos-env/pintos/tests/userprog/wait-simple.ck b/pintos-env/pintos/tests/userprog/wait-simple.ck new file mode 100755 index 0000000..93dd577 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-simple.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(wait-simple) begin +(child-simple) run +child-simple: exit(81) +(wait-simple) wait(exec()) = 81 +(wait-simple) end +wait-simple: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/wait-twice.c b/pintos-env/pintos/tests/userprog/wait-twice.c new file mode 100755 index 0000000..785e684 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-twice.c @@ -0,0 +1,15 @@ +/* Wait for a subprocess to finish, twice. + The first call must wait in the usual way and return the exit code. + The second wait call must return -1 immediately. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + pid_t child = exec ("child-simple"); + msg ("wait(exec()) = %d", wait (child)); + msg ("wait(exec()) = %d", wait (child)); +} diff --git a/pintos-env/pintos/tests/userprog/wait-twice.ck b/pintos-env/pintos/tests/userprog/wait-twice.ck new file mode 100755 index 0000000..6d53843 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/wait-twice.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(wait-twice) begin +(child-simple) run +child-simple: exit(81) +(wait-twice) wait(exec()) = 81 +(wait-twice) wait(exec()) = -1 +(wait-twice) end +wait-twice: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/write-bad-fd.c b/pintos-env/pintos/tests/userprog/write-bad-fd.c new file mode 100755 index 0000000..f3b1151 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-bad-fd.c @@ -0,0 +1,20 @@ +/* Tries to write to an invalid fd, + which must either fail silently or terminate the process with + exit code -1. */ + +#include +#include +#include "tests/main.h" + +void +test_main (void) +{ + char buf = 123; + write (0x01012342, &buf, 1); + write (7, &buf, 1); + write (2546, &buf, 1); + write (-5, &buf, 1); + write (-8192, &buf, 1); + write (INT_MIN + 1, &buf, 1); + write (INT_MAX - 1, &buf, 1); +} diff --git a/pintos-env/pintos/tests/userprog/write-bad-fd.ck b/pintos-env/pintos/tests/userprog/write-bad-fd.ck new file mode 100755 index 0000000..8da7a8b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-bad-fd.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(write-bad-fd) begin +(write-bad-fd) end +write-bad-fd: exit(0) +EOF +(write-bad-fd) begin +write-bad-fd: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/write-bad-ptr.c b/pintos-env/pintos/tests/userprog/write-bad-ptr.c new file mode 100755 index 0000000..5336479 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-bad-ptr.c @@ -0,0 +1,16 @@ +/* Passes an invalid pointer to the write system call. + The process must be terminated with -1 exit code. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + write (handle, (char *) 0x10123420, 123); + fail ("should have exited with -1"); +} diff --git a/pintos-env/pintos/tests/userprog/write-bad-ptr.ck b/pintos-env/pintos/tests/userprog/write-bad-ptr.ck new file mode 100755 index 0000000..ad9f399 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-bad-ptr.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(write-bad-ptr) begin +(write-bad-ptr) open "sample.txt" +(write-bad-ptr) end +write-bad-ptr: exit(0) +EOF +(write-bad-ptr) begin +(write-bad-ptr) open "sample.txt" +write-bad-ptr: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/write-boundary.c b/pintos-env/pintos/tests/userprog/write-boundary.c new file mode 100755 index 0000000..d2de1d4 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-boundary.c @@ -0,0 +1,25 @@ +/* Writes data spanning two pages in virtual address space, + which must succeed. */ + +#include +#include +#include "tests/userprog/boundary.h" +#include "tests/userprog/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + int byte_cnt; + char *sample_p; + + sample_p = copy_string_across_boundary (sample); + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + byte_cnt = write (handle, sample_p, sizeof sample - 1); + if (byte_cnt != sizeof sample - 1) + fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1); +} diff --git a/pintos-env/pintos/tests/userprog/write-boundary.ck b/pintos-env/pintos/tests/userprog/write-boundary.ck new file mode 100755 index 0000000..7883781 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-boundary.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(write-boundary) begin +(write-boundary) open "sample.txt" +(write-boundary) end +write-boundary: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/write-normal.c b/pintos-env/pintos/tests/userprog/write-normal.c new file mode 100755 index 0000000..e0297aa --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-normal.c @@ -0,0 +1,20 @@ +/* Try writing a file in the most normal way. */ + +#include +#include "tests/userprog/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle, byte_cnt; + + CHECK (create ("test.txt", sizeof sample - 1), "create \"test.txt\""); + CHECK ((handle = open ("test.txt")) > 1, "open \"test.txt\""); + + byte_cnt = write (handle, sample, sizeof sample - 1); + if (byte_cnt != sizeof sample - 1) + fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1); +} + diff --git a/pintos-env/pintos/tests/userprog/write-normal.ck b/pintos-env/pintos/tests/userprog/write-normal.ck new file mode 100755 index 0000000..9fa6024 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-normal.ck @@ -0,0 +1,12 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(write-normal) begin +(write-normal) create "test.txt" +(write-normal) open "test.txt" +(write-normal) end +write-normal: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/write-stdin.c b/pintos-env/pintos/tests/userprog/write-stdin.c new file mode 100755 index 0000000..491ea53 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-stdin.c @@ -0,0 +1,14 @@ +/* Try writing to fd 0 (stdin), + which may just fail or terminate the process with -1 exit + code. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char buf = 123; + write (0, &buf, 1); +} diff --git a/pintos-env/pintos/tests/userprog/write-stdin.ck b/pintos-env/pintos/tests/userprog/write-stdin.ck new file mode 100755 index 0000000..a6caf81 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-stdin.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(write-stdin) begin +(write-stdin) end +write-stdin: exit(0) +EOF +(write-stdin) begin +write-stdin: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/userprog/write-zero.c b/pintos-env/pintos/tests/userprog/write-zero.c new file mode 100755 index 0000000..d8dac9b --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-zero.c @@ -0,0 +1,20 @@ +/* Try a 0-byte write, which should return 0 without writing + anything. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle, byte_cnt; + char buf; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + + buf = 123; + byte_cnt = write (handle, &buf, 0); + if (byte_cnt != 0) + fail("write() returned %d instead of 0", byte_cnt); +} diff --git a/pintos-env/pintos/tests/userprog/write-zero.ck b/pintos-env/pintos/tests/userprog/write-zero.ck new file mode 100755 index 0000000..cc4cd60 --- /dev/null +++ b/pintos-env/pintos/tests/userprog/write-zero.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(write-zero) begin +(write-zero) open "sample.txt" +(write-zero) end +write-zero: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/Grading b/pintos-env/pintos/tests/vm/Grading new file mode 100755 index 0000000..f0c2c13 --- /dev/null +++ b/pintos-env/pintos/tests/vm/Grading @@ -0,0 +1,12 @@ +# Percentage of the testing point total designated for each set of +# tests. + +# This project is primarily about virtual memory, but all the previous +# functionality should work too, and it's easy to screw it up, thus +# the equal weight placed on each. + +50% tests/vm/Rubric.functionality +15% tests/vm/Rubric.robustness +10% tests/userprog/Rubric.functionality +5% tests/userprog/Rubric.robustness +20% tests/filesys/base/Rubric diff --git a/pintos-env/pintos/tests/vm/Make.tests b/pintos-env/pintos/tests/vm/Make.tests new file mode 100755 index 0000000..04b1b81 --- /dev/null +++ b/pintos-env/pintos/tests/vm/Make.tests @@ -0,0 +1,103 @@ +# -*- makefile -*- + +tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha \ +pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code \ +pt-write-code2 pt-grow-stk-sc page-linear page-parallel page-merge-seq \ +page-merge-par page-merge-stk page-merge-mm page-shuffle mmap-read \ +mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit \ +mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign \ +mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove \ +mmap-zero) + +tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear \ +child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit) + +tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c \ +tests/cksum.c tests/lib.c tests/main.c +tests/vm/pt-grow-pusha_SRC = tests/vm/pt-grow-pusha.c tests/lib.c \ +tests/main.c +tests/vm/pt-grow-bad_SRC = tests/vm/pt-grow-bad.c tests/lib.c tests/main.c +tests/vm/pt-big-stk-obj_SRC = tests/vm/pt-big-stk-obj.c tests/arc4.c \ +tests/cksum.c tests/lib.c tests/main.c +tests/vm/pt-bad-addr_SRC = tests/vm/pt-bad-addr.c tests/lib.c tests/main.c +tests/vm/pt-bad-read_SRC = tests/vm/pt-bad-read.c tests/lib.c tests/main.c +tests/vm/pt-write-code_SRC = tests/vm/pt-write-code.c tests/lib.c tests/main.c +tests/vm/pt-write-code2_SRC = tests/vm/pt-write-code-2.c tests/lib.c tests/main.c +tests/vm/pt-grow-stk-sc_SRC = tests/vm/pt-grow-stk-sc.c tests/lib.c tests/main.c +tests/vm/page-linear_SRC = tests/vm/page-linear.c tests/arc4.c \ +tests/lib.c tests/main.c +tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c +tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c \ +tests/lib.c tests/main.c +tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \ +tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c +tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \ +tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c +tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \ +tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c +tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c \ +tests/cksum.c tests/lib.c tests/main.c +tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c +tests/vm/mmap-close_SRC = tests/vm/mmap-close.c tests/lib.c tests/main.c +tests/vm/mmap-unmap_SRC = tests/vm/mmap-unmap.c tests/lib.c tests/main.c +tests/vm/mmap-overlap_SRC = tests/vm/mmap-overlap.c tests/lib.c tests/main.c +tests/vm/mmap-twice_SRC = tests/vm/mmap-twice.c tests/lib.c tests/main.c +tests/vm/mmap-write_SRC = tests/vm/mmap-write.c tests/lib.c tests/main.c +tests/vm/mmap-exit_SRC = tests/vm/mmap-exit.c tests/lib.c tests/main.c +tests/vm/mmap-shuffle_SRC = tests/vm/mmap-shuffle.c tests/arc4.c \ +tests/cksum.c tests/lib.c tests/main.c +tests/vm/mmap-bad-fd_SRC = tests/vm/mmap-bad-fd.c tests/lib.c tests/main.c +tests/vm/mmap-clean_SRC = tests/vm/mmap-clean.c tests/lib.c tests/main.c +tests/vm/mmap-inherit_SRC = tests/vm/mmap-inherit.c tests/lib.c tests/main.c +tests/vm/mmap-misalign_SRC = tests/vm/mmap-misalign.c tests/lib.c \ +tests/main.c +tests/vm/mmap-null_SRC = tests/vm/mmap-null.c tests/lib.c tests/main.c +tests/vm/mmap-over-code_SRC = tests/vm/mmap-over-code.c tests/lib.c \ +tests/main.c +tests/vm/mmap-over-data_SRC = tests/vm/mmap-over-data.c tests/lib.c \ +tests/main.c +tests/vm/mmap-over-stk_SRC = tests/vm/mmap-over-stk.c tests/lib.c tests/main.c +tests/vm/mmap-remove_SRC = tests/vm/mmap-remove.c tests/lib.c tests/main.c +tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c + +tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c +tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c +tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \ +tests/lib.c +tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c +tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c +tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c + +tests/vm/pt-bad-read_PUTFILES = tests/vm/sample.txt +tests/vm/pt-write-code2_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-close_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-read_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-unmap_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-twice_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-overlap_PUTFILES = tests/vm/zeros +tests/vm/mmap-exit_PUTFILES = tests/vm/child-mm-wrt +tests/vm/page-parallel_PUTFILES = tests/vm/child-linear +tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort +tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort +tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort +tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm +tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit +tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-null_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-over-code_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-over-data_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-over-stk_PUTFILES = tests/vm/sample.txt +tests/vm/mmap-remove_PUTFILES = tests/vm/sample.txt + +tests/vm/page-linear.output: TIMEOUT = 300 +tests/vm/page-shuffle.output: TIMEOUT = 600 +tests/vm/mmap-shuffle.output: TIMEOUT = 600 +tests/vm/page-merge-seq.output: TIMEOUT = 600 +tests/vm/page-merge-par.output: TIMEOUT = 600 + +tests/vm/zeros: + dd if=/dev/zero of=$@ bs=1024 count=6 + +clean:: + rm -f tests/vm/zeros diff --git a/pintos-env/pintos/tests/vm/Rubric.functionality b/pintos-env/pintos/tests/vm/Rubric.functionality new file mode 100755 index 0000000..8a86612 --- /dev/null +++ b/pintos-env/pintos/tests/vm/Rubric.functionality @@ -0,0 +1,30 @@ +Functionality of virtual memory subsystem: +- Test stack growth. +3 pt-grow-stack +3 pt-grow-stk-sc +3 pt-big-stk-obj +3 pt-grow-pusha + +- Test paging behavior. +3 page-linear +3 page-parallel +3 page-shuffle +4 page-merge-seq +4 page-merge-par +4 page-merge-mm +4 page-merge-stk + +- Test "mmap" system call. +2 mmap-read +2 mmap-write +2 mmap-shuffle + +2 mmap-twice + +2 mmap-unmap +1 mmap-exit + +3 mmap-clean + +2 mmap-close +2 mmap-remove diff --git a/pintos-env/pintos/tests/vm/Rubric.robustness b/pintos-env/pintos/tests/vm/Rubric.robustness new file mode 100755 index 0000000..0b2552f --- /dev/null +++ b/pintos-env/pintos/tests/vm/Rubric.robustness @@ -0,0 +1,21 @@ +Robustness of virtual memory subsystem: +- Test robustness of page table support. +2 pt-bad-addr +3 pt-bad-read +2 pt-write-code +3 pt-write-code2 +4 pt-grow-bad + +- Test robustness of "mmap" system call. +1 mmap-bad-fd +1 mmap-inherit +1 mmap-null +1 mmap-zero + +2 mmap-misalign + +2 mmap-over-code +2 mmap-over-data +2 mmap-over-stk +2 mmap-overlap + diff --git a/pintos-env/pintos/tests/vm/child-inherit.c b/pintos-env/pintos/tests/vm/child-inherit.c new file mode 100755 index 0000000..d3186a1 --- /dev/null +++ b/pintos-env/pintos/tests/vm/child-inherit.c @@ -0,0 +1,16 @@ +/* Child process for mmap-inherit test. + Tries to write to a mapping present in the parent. + The process must be terminated with -1 exit code. */ + +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + memset ((char *) 0x54321000, 0, 4096); + fail ("child can modify parent's memory mappings"); +} + diff --git a/pintos-env/pintos/tests/vm/child-linear.c b/pintos-env/pintos/tests/vm/child-linear.c new file mode 100755 index 0000000..eca3e3f --- /dev/null +++ b/pintos-env/pintos/tests/vm/child-linear.c @@ -0,0 +1,36 @@ +/* Child process of page-parallel. + Encrypts 1 MB of zeros, then decrypts it, and ensures that + the zeros are back. */ + +#include +#include "tests/arc4.h" +#include "tests/lib.h" +#include "tests/main.h" + +const char *test_name = "child-linear"; + +#define SIZE (1024 * 1024) +static char buf[SIZE]; + +int +main (int argc, char *argv[]) +{ + const char *key = argv[argc - 1]; + struct arc4 arc4; + size_t i; + + /* Encrypt zeros. */ + arc4_init (&arc4, key, strlen (key)); + arc4_crypt (&arc4, buf, SIZE); + + /* Decrypt back to zeros. */ + arc4_init (&arc4, key, strlen (key)); + arc4_crypt (&arc4, buf, SIZE); + + /* Check that it's all zeros. */ + for (i = 0; i < SIZE; i++) + if (buf[i] != '\0') + fail ("byte %zu != 0", i); + + return 0x42; +} diff --git a/pintos-env/pintos/tests/vm/child-mm-wrt.c b/pintos-env/pintos/tests/vm/child-mm-wrt.c new file mode 100755 index 0000000..8419788 --- /dev/null +++ b/pintos-env/pintos/tests/vm/child-mm-wrt.c @@ -0,0 +1,24 @@ +/* Child process of mmap-exit. + Mmaps a file and writes to it via the mmap'ing, then exits + without calling munmap. The data in the mapped region must be + written out at program termination. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +#define ACTUAL ((void *) 0x10000000) + +void +test_main (void) +{ + int handle; + + CHECK (create ("sample.txt", sizeof sample), "create \"sample.txt\""); + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (mmap (handle, ACTUAL) != MAP_FAILED, "mmap \"sample.txt\""); + memcpy (ACTUAL, sample, sizeof sample); +} + diff --git a/pintos-env/pintos/tests/vm/child-qsort-mm.c b/pintos-env/pintos/tests/vm/child-qsort-mm.c new file mode 100755 index 0000000..db45499 --- /dev/null +++ b/pintos-env/pintos/tests/vm/child-qsort-mm.c @@ -0,0 +1,25 @@ +/* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort, + a multi-pass divide and conquer algorithm. */ + +#include +#include +#include "tests/lib.h" +#include "tests/main.h" +#include "tests/vm/qsort.h" + +const char *test_name = "child-qsort-mm"; + +int +main (int argc UNUSED, char *argv[]) +{ + int handle; + unsigned char *p = (unsigned char *) 0x10000000; + + quiet = true; + + CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); + CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]); + qsort_bytes (p, 1024 * 128); + + return 80; +} diff --git a/pintos-env/pintos/tests/vm/child-qsort.c b/pintos-env/pintos/tests/vm/child-qsort.c new file mode 100755 index 0000000..355f4eb --- /dev/null +++ b/pintos-env/pintos/tests/vm/child-qsort.c @@ -0,0 +1,32 @@ +/* Reads a 128 kB file onto the stack and "sorts" the bytes in + it, using quick sort, a multi-pass divide and conquer + algorithm. The sorted data is written back to the same file + in-place. */ + +#include +#include +#include "tests/lib.h" +#include "tests/main.h" +#include "tests/vm/qsort.h" + +const char *test_name = "child-qsort"; + +int +main (int argc UNUSED, char *argv[]) +{ + int handle; + unsigned char buf[128 * 1024]; + size_t size; + + quiet = true; + + CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); + + size = read (handle, buf, sizeof buf); + qsort_bytes (buf, sizeof buf); + seek (handle, 0); + write (handle, buf, size); + close (handle); + + return 72; +} diff --git a/pintos-env/pintos/tests/vm/child-sort.c b/pintos-env/pintos/tests/vm/child-sort.c new file mode 100755 index 0000000..dff2c77 --- /dev/null +++ b/pintos-env/pintos/tests/vm/child-sort.c @@ -0,0 +1,42 @@ +/* Reads a 128 kB file into static data and "sorts" the bytes in + it, using counting sort, a single-pass algorithm. The sorted + data is written back to the same file in-place. */ + +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +const char *test_name = "child-sort"; + +unsigned char buf[128 * 1024]; +size_t histogram[256]; + +int +main (int argc UNUSED, char *argv[]) +{ + int handle; + unsigned char *p; + size_t size; + size_t i; + + quiet = true; + + CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); + + size = read (handle, buf, sizeof buf); + for (i = 0; i < size; i++) + histogram[buf[i]]++; + p = buf; + for (i = 0; i < sizeof histogram / sizeof *histogram; i++) + { + size_t j = histogram[i]; + while (j-- > 0) + *p++ = i; + } + seek (handle, 0); + write (handle, buf, size); + close (handle); + + return 123; +} diff --git a/pintos-env/pintos/tests/vm/mmap-bad-fd.c b/pintos-env/pintos/tests/vm/mmap-bad-fd.c new file mode 100755 index 0000000..76a7b50 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-bad-fd.c @@ -0,0 +1,15 @@ +/* Tries to mmap an invalid fd, + which must either fail silently or terminate the process with + exit code -1. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + CHECK (mmap (0x5678, (void *) 0x10000000) == MAP_FAILED, + "try to mmap invalid fd"); +} + diff --git a/pintos-env/pintos/tests/vm/mmap-bad-fd.ck b/pintos-env/pintos/tests/vm/mmap-bad-fd.ck new file mode 100755 index 0000000..f3f58d5 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-bad-fd.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF', <<'EOF']); +(mmap-bad-fd) begin +(mmap-bad-fd) try to mmap invalid fd +(mmap-bad-fd) end +mmap-bad-fd: exit(0) +EOF +(mmap-bad-fd) begin +(mmap-bad-fd) try to mmap invalid fd +mmap-bad-fd: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-clean.c b/pintos-env/pintos/tests/vm/mmap-clean.c new file mode 100755 index 0000000..ea1dc9c --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-clean.c @@ -0,0 +1,53 @@ +/* Verifies that mmap'd regions are only written back on munmap + if the data was actually modified in memory. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + static const char overwrite[] = "Now is the time for all good..."; + static char buffer[sizeof sample - 1]; + char *actual = (char *) 0x54321000; + int handle; + mapid_t map; + + /* Open file, map, verify data. */ + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); + if (memcmp (actual, sample, strlen (sample))) + fail ("read of mmap'd file reported bad data"); + + /* Modify file. */ + CHECK (write (handle, overwrite, strlen (overwrite)) + == (int) strlen (overwrite), + "write \"sample.txt\""); + + /* Close mapping. Data should not be written back, because we + didn't modify it via the mapping. */ + msg ("munmap \"sample.txt\""); + munmap (map); + + /* Read file back. */ + msg ("seek \"sample.txt\""); + seek (handle, 0); + CHECK (read (handle, buffer, sizeof buffer) == sizeof buffer, + "read \"sample.txt\""); + + /* Verify that file overwrite worked. */ + if (memcmp (buffer, overwrite, strlen (overwrite)) + || memcmp (buffer + strlen (overwrite), sample + strlen (overwrite), + strlen (sample) - strlen (overwrite))) + { + if (!memcmp (buffer, sample, strlen (sample))) + fail ("munmap wrote back clean page"); + else + fail ("read surprising data from file"); + } + else + msg ("file change was retained after munmap"); +} diff --git a/pintos-env/pintos/tests/vm/mmap-clean.ck b/pintos-env/pintos/tests/vm/mmap-clean.ck new file mode 100755 index 0000000..1666d6c --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-clean.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-clean) begin +(mmap-clean) open "sample.txt" +(mmap-clean) mmap "sample.txt" +(mmap-clean) write "sample.txt" +(mmap-clean) munmap "sample.txt" +(mmap-clean) seek "sample.txt" +(mmap-clean) read "sample.txt" +(mmap-clean) file change was retained after munmap +(mmap-clean) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-close.c b/pintos-env/pintos/tests/vm/mmap-close.c new file mode 100755 index 0000000..d016ee3 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-close.c @@ -0,0 +1,27 @@ +/* Verifies that memory mappings persist after file close. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/arc4.h" +#include "tests/lib.h" +#include "tests/main.h" + +#define ACTUAL ((void *) 0x10000000) + +void +test_main (void) +{ + int handle; + mapid_t map; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); + + close (handle); + + if (memcmp (ACTUAL, sample, strlen (sample))) + fail ("read of mmap'd file reported bad data"); + + munmap (map); +} diff --git a/pintos-env/pintos/tests/vm/mmap-close.ck b/pintos-env/pintos/tests/vm/mmap-close.ck new file mode 100755 index 0000000..d15e41a --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-close.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-close) begin +(mmap-close) open "sample.txt" +(mmap-close) mmap "sample.txt" +(mmap-close) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-exit.c b/pintos-env/pintos/tests/vm/mmap-exit.c new file mode 100755 index 0000000..7a2278a --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-exit.c @@ -0,0 +1,22 @@ +/* Executes child-mm-wrt and verifies that the writes that should + have occurred really did. */ + +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + pid_t child; + + /* Make child write file. */ + quiet = true; + CHECK ((child = exec ("child-mm-wrt")) != -1, "exec \"child-mm-wrt\""); + CHECK (wait (child) == 0, "wait for child (should return 0)"); + quiet = false; + + /* Check file contents. */ + check_file ("sample.txt", sample, sizeof sample); +} diff --git a/pintos-env/pintos/tests/vm/mmap-exit.ck b/pintos-env/pintos/tests/vm/mmap-exit.ck new file mode 100755 index 0000000..457d34a --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-exit.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-exit) begin +(child-mm-wrt) begin +(child-mm-wrt) create "sample.txt" +(child-mm-wrt) open "sample.txt" +(child-mm-wrt) mmap "sample.txt" +(child-mm-wrt) end +(mmap-exit) open "sample.txt" for verification +(mmap-exit) verified contents of "sample.txt" +(mmap-exit) close "sample.txt" +(mmap-exit) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-inherit.c b/pintos-env/pintos/tests/vm/mmap-inherit.c new file mode 100755 index 0000000..7fa9607 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-inherit.c @@ -0,0 +1,32 @@ +/* Maps a file into memory and runs child-inherit to verify that + mappings are not inherited. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char *actual = (char *) 0x54321000; + int handle; + pid_t child; + + /* Open file, map, verify data. */ + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (mmap (handle, actual) != MAP_FAILED, "mmap \"sample.txt\""); + if (memcmp (actual, sample, strlen (sample))) + fail ("read of mmap'd file reported bad data"); + + /* Spawn child and wait. */ + CHECK ((child = exec ("child-inherit")) != -1, "exec \"child-inherit\""); + quiet = true; + CHECK (wait (child) == -1, "wait for child (should return -1)"); + quiet = false; + + /* Verify data again. */ + CHECK (!memcmp (actual, sample, strlen (sample)), + "checking that mmap'd file still has same data"); +} diff --git a/pintos-env/pintos/tests/vm/mmap-inherit.ck b/pintos-env/pintos/tests/vm/mmap-inherit.ck new file mode 100755 index 0000000..c54638a --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-inherit.ck @@ -0,0 +1,16 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(mmap-inherit) begin +(mmap-inherit) open "sample.txt" +(mmap-inherit) mmap "sample.txt" +(mmap-inherit) exec "child-inherit" +(child-inherit) begin +child-inherit: exit(-1) +(mmap-inherit) checking that mmap'd file still has same data +(mmap-inherit) end +mmap-inherit: exit(0) +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-misalign.c b/pintos-env/pintos/tests/vm/mmap-misalign.c new file mode 100755 index 0000000..34141a9 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-misalign.c @@ -0,0 +1,16 @@ +/* Verifies that misaligned memory mappings are disallowed. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (mmap (handle, (void *) 0x10001234) == MAP_FAILED, + "try to mmap at misaligned address"); +} + diff --git a/pintos-env/pintos/tests/vm/mmap-misalign.ck b/pintos-env/pintos/tests/vm/mmap-misalign.ck new file mode 100755 index 0000000..145a2e8 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-misalign.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-misalign) begin +(mmap-misalign) open "sample.txt" +(mmap-misalign) try to mmap at misaligned address +(mmap-misalign) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-null.c b/pintos-env/pintos/tests/vm/mmap-null.c new file mode 100755 index 0000000..f8ef075 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-null.c @@ -0,0 +1,15 @@ +/* Verifies that memory mappings at address 0 are disallowed. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (mmap (handle, NULL) == MAP_FAILED, "try to mmap at address 0"); +} + diff --git a/pintos-env/pintos/tests/vm/mmap-null.ck b/pintos-env/pintos/tests/vm/mmap-null.ck new file mode 100755 index 0000000..aacdd65 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-null.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-null) begin +(mmap-null) open "sample.txt" +(mmap-null) try to mmap at address 0 +(mmap-null) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-over-code.c b/pintos-env/pintos/tests/vm/mmap-over-code.c new file mode 100755 index 0000000..d3619a3 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-over-code.c @@ -0,0 +1,19 @@ +/* Verifies that mapping over the code segment is disallowed. */ + +#include +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + uintptr_t test_main_page = ROUND_DOWN ((uintptr_t) test_main, 4096); + int handle; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (mmap (handle, (void *) test_main_page) == MAP_FAILED, + "try to mmap over code segment"); +} + diff --git a/pintos-env/pintos/tests/vm/mmap-over-code.ck b/pintos-env/pintos/tests/vm/mmap-over-code.ck new file mode 100755 index 0000000..b5b23c7 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-over-code.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-over-code) begin +(mmap-over-code) open "sample.txt" +(mmap-over-code) try to mmap over code segment +(mmap-over-code) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-over-data.c b/pintos-env/pintos/tests/vm/mmap-over-data.c new file mode 100755 index 0000000..9ea5d49 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-over-data.c @@ -0,0 +1,21 @@ +/* Verifies that mapping over the data segment is disallowed. */ + +#include +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +static char x; + +void +test_main (void) +{ + uintptr_t x_page = ROUND_DOWN ((uintptr_t) &x, 4096); + int handle; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (mmap (handle, (void *) x_page) == MAP_FAILED, + "try to mmap over data segment"); +} + diff --git a/pintos-env/pintos/tests/vm/mmap-over-data.ck b/pintos-env/pintos/tests/vm/mmap-over-data.ck new file mode 100755 index 0000000..98770cc --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-over-data.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-over-data) begin +(mmap-over-data) open "sample.txt" +(mmap-over-data) try to mmap over data segment +(mmap-over-data) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-over-stk.c b/pintos-env/pintos/tests/vm/mmap-over-stk.c new file mode 100755 index 0000000..4e241e8 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-over-stk.c @@ -0,0 +1,19 @@ +/* Verifies that mapping over the stack segment is disallowed. */ + +#include +#include +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + uintptr_t handle_page = ROUND_DOWN ((uintptr_t) &handle, 4096); + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (mmap (handle, (void *) handle_page) == MAP_FAILED, + "try to mmap over stack segment"); +} + diff --git a/pintos-env/pintos/tests/vm/mmap-over-stk.ck b/pintos-env/pintos/tests/vm/mmap-over-stk.ck new file mode 100755 index 0000000..e6880cf --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-over-stk.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-over-stk) begin +(mmap-over-stk) open "sample.txt" +(mmap-over-stk) try to mmap over stack segment +(mmap-over-stk) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-overlap.c b/pintos-env/pintos/tests/vm/mmap-overlap.c new file mode 100755 index 0000000..668ae5f --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-overlap.c @@ -0,0 +1,20 @@ +/* Verifies that overlapping memory mappings are disallowed. */ + +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char *start = (char *) 0x10000000; + int fd[2]; + + CHECK ((fd[0] = open ("zeros")) > 1, "open \"zeros\" once"); + CHECK (mmap (fd[0], start) != MAP_FAILED, "mmap \"zeros\""); + CHECK ((fd[1] = open ("zeros")) > 1 && fd[0] != fd[1], + "open \"zeros\" again"); + CHECK (mmap (fd[1], start + 4096) == MAP_FAILED, + "try to mmap \"zeros\" again"); +} diff --git a/pintos-env/pintos/tests/vm/mmap-overlap.ck b/pintos-env/pintos/tests/vm/mmap-overlap.ck new file mode 100755 index 0000000..f13801e --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-overlap.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-overlap) begin +(mmap-overlap) open "zeros" once +(mmap-overlap) mmap "zeros" +(mmap-overlap) open "zeros" again +(mmap-overlap) try to mmap "zeros" again +(mmap-overlap) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-read.c b/pintos-env/pintos/tests/vm/mmap-read.c new file mode 100755 index 0000000..c0f23a1 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-read.c @@ -0,0 +1,32 @@ +/* Uses a memory mapping to read a file. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char *actual = (char *) 0x10000000; + int handle; + mapid_t map; + size_t i; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); + + /* Check that data is correct. */ + if (memcmp (actual, sample, strlen (sample))) + fail ("read of mmap'd file reported bad data"); + + /* Verify that data is followed by zeros. */ + for (i = strlen (sample); i < 4096; i++) + if (actual[i] != 0) + fail ("byte %zu of mmap'd region has value %02hhx (should be 0)", + i, actual[i]); + + munmap (map); + close (handle); +} diff --git a/pintos-env/pintos/tests/vm/mmap-read.ck b/pintos-env/pintos/tests/vm/mmap-read.ck new file mode 100755 index 0000000..95ab790 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-read.ck @@ -0,0 +1,11 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-read) begin +(mmap-read) open "sample.txt" +(mmap-read) mmap "sample.txt" +(mmap-read) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-remove.c b/pintos-env/pintos/tests/vm/mmap-remove.c new file mode 100755 index 0000000..5f7444d --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-remove.c @@ -0,0 +1,43 @@ +/* Deletes and closes file that is mapped into memory + and verifies that it can still be read through the mapping. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char *actual = (char *) 0x10000000; + int handle; + mapid_t map; + size_t i; + + /* Map file. */ + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); + + /* Close file and delete it. */ + close (handle); + CHECK (remove ("sample.txt"), "remove \"sample.txt\""); + CHECK (open ("sample.txt") == -1, "try to open \"sample.txt\""); + + /* Create a new file in hopes of overwriting data from the old + one, in case the file system has incorrectly freed the + file's data. */ + CHECK (create ("another", 4096 * 10), "create \"another\""); + + /* Check that mapped data is correct. */ + if (memcmp (actual, sample, strlen (sample))) + fail ("read of mmap'd file reported bad data"); + + /* Verify that data is followed by zeros. */ + for (i = strlen (sample); i < 4096; i++) + if (actual[i] != 0) + fail ("byte %zu of mmap'd region has value %02hhx (should be 0)", + i, actual[i]); + + munmap (map); +} diff --git a/pintos-env/pintos/tests/vm/mmap-remove.ck b/pintos-env/pintos/tests/vm/mmap-remove.ck new file mode 100755 index 0000000..d3cc938 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-remove.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-remove) begin +(mmap-remove) open "sample.txt" +(mmap-remove) mmap "sample.txt" +(mmap-remove) remove "sample.txt" +(mmap-remove) try to open "sample.txt" +(mmap-remove) create "another" +(mmap-remove) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-shuffle.c b/pintos-env/pintos/tests/vm/mmap-shuffle.c new file mode 100755 index 0000000..29921ad --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-shuffle.c @@ -0,0 +1,38 @@ +/* Creates a 128 kB file and repeatedly shuffles data in it + through a memory mapping. */ + +#include +#include +#include +#include "tests/arc4.h" +#include "tests/cksum.h" +#include "tests/lib.h" +#include "tests/main.h" + +#define SIZE (128 * 1024) + +static char *buf = (char *) 0x10000000; + +void +test_main (void) +{ + size_t i; + int handle; + + /* Create file, mmap. */ + CHECK (create ("buffer", SIZE), "create \"buffer\""); + CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); + CHECK (mmap (handle, buf) != MAP_FAILED, "mmap \"buffer\""); + + /* Initialize. */ + for (i = 0; i < SIZE; i++) + buf[i] = i * 257; + msg ("init: cksum=%lu", cksum (buf, SIZE)); + + /* Shuffle repeatedly. */ + for (i = 0; i < 10; i++) + { + shuffle (buf, SIZE, 1); + msg ("shuffle %zu: cksum=%lu", i, cksum (buf, SIZE)); + } +} diff --git a/pintos-env/pintos/tests/vm/mmap-shuffle.ck b/pintos-env/pintos/tests/vm/mmap-shuffle.ck new file mode 100755 index 0000000..c158301 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-shuffle.ck @@ -0,0 +1,47 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::cksum; +use tests::lib; + +my ($init, @shuffle); +if (1) { + # Use precalculated values. + $init = 3115322833; + @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467, + 3022003332, 3614934266, 2704001777, 735775156, 1864109763); +} else { + # Recalculate values. + my ($buf) = ""; + for my $i (0...128 * 1024 - 1) { + $buf .= chr (($i * 257) & 0xff); + } + $init = cksum ($buf); + + random_init (0); + for my $i (1...10) { + $buf = shuffle ($buf, length ($buf), 1); + push (@shuffle, cksum ($buf)); + } +} + +check_expected (IGNORE_EXIT_CODES => 1, [< +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char *actual[2] = {(char *) 0x10000000, (char *) 0x20000000}; + size_t i; + int handle[2]; + + for (i = 0; i < 2; i++) + { + CHECK ((handle[i] = open ("sample.txt")) > 1, + "open \"sample.txt\" #%zu", i); + CHECK (mmap (handle[i], actual[i]) != MAP_FAILED, + "mmap \"sample.txt\" #%zu at %p", i, (void *) actual[i]); + } + + for (i = 0; i < 2; i++) + CHECK (!memcmp (actual[i], sample, strlen (sample)), + "compare mmap'd file %zu against data", i); +} diff --git a/pintos-env/pintos/tests/vm/mmap-twice.ck b/pintos-env/pintos/tests/vm/mmap-twice.ck new file mode 100755 index 0000000..05e9724 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-twice.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-twice) begin +(mmap-twice) open "sample.txt" #0 +(mmap-twice) mmap "sample.txt" #0 at 0x10000000 +(mmap-twice) open "sample.txt" #1 +(mmap-twice) mmap "sample.txt" #1 at 0x20000000 +(mmap-twice) compare mmap'd file 0 against data +(mmap-twice) compare mmap'd file 1 against data +(mmap-twice) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-unmap.c b/pintos-env/pintos/tests/vm/mmap-unmap.c new file mode 100755 index 0000000..d35a79e --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-unmap.c @@ -0,0 +1,23 @@ +/* Maps and unmaps a file and verifies that the mapped region is + inaccessible afterward. */ + +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +#define ACTUAL ((void *) 0x10000000) + +void +test_main (void) +{ + int handle; + mapid_t map; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); + + munmap (map); + + fail ("unmapped memory is readable (%d)", *(int *) ACTUAL); +} diff --git a/pintos-env/pintos/tests/vm/mmap-unmap.ck b/pintos-env/pintos/tests/vm/mmap-unmap.ck new file mode 100755 index 0000000..119658c --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-unmap.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::vm::process_death; + +check_process_death ('mmap-unmap'); diff --git a/pintos-env/pintos/tests/vm/mmap-write.c b/pintos-env/pintos/tests/vm/mmap-write.c new file mode 100755 index 0000000..46e8043 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-write.c @@ -0,0 +1,32 @@ +/* Writes to a file through a mapping, and unmaps the file, + then reads the data in the file back using the read system + call to verify. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +#define ACTUAL ((void *) 0x10000000) + +void +test_main (void) +{ + int handle; + mapid_t map; + char buf[1024]; + + /* Write file via mmap. */ + CHECK (create ("sample.txt", strlen (sample)), "create \"sample.txt\""); + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); + memcpy (ACTUAL, sample, strlen (sample)); + munmap (map); + + /* Read back via read(). */ + read (handle, buf, strlen (sample)); + CHECK (!memcmp (buf, sample, strlen (sample)), + "compare read data against written data"); + close (handle); +} diff --git a/pintos-env/pintos/tests/vm/mmap-write.ck b/pintos-env/pintos/tests/vm/mmap-write.ck new file mode 100755 index 0000000..d2c9cc5 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-write.ck @@ -0,0 +1,13 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(mmap-write) begin +(mmap-write) create "sample.txt" +(mmap-write) open "sample.txt" +(mmap-write) mmap "sample.txt" +(mmap-write) compare read data against written data +(mmap-write) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/mmap-zero.c b/pintos-env/pintos/tests/vm/mmap-zero.c new file mode 100755 index 0000000..368b759 --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-zero.c @@ -0,0 +1,27 @@ +/* Tries to map a zero-length file, which may or may not work but + should not terminate the process or crash. + Then dereferences the address that we tried to map, + and the process must be terminated with -1 exit code. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char *data = (char *) 0x7f000000; + int handle; + + CHECK (create ("empty", 0), "create empty file \"empty\""); + CHECK ((handle = open ("empty")) > 1, "open \"empty\""); + + /* Calling mmap() might succeed or fail. We don't care. */ + msg ("mmap \"empty\""); + mmap (handle, data); + + /* Regardless of whether the call worked, *data should cause + the process to be terminated. */ + fail ("unmapped memory is readable (%d)", *data); +} + diff --git a/pintos-env/pintos/tests/vm/mmap-zero.ck b/pintos-env/pintos/tests/vm/mmap-zero.ck new file mode 100755 index 0000000..6033d5d --- /dev/null +++ b/pintos-env/pintos/tests/vm/mmap-zero.ck @@ -0,0 +1,12 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(mmap-zero) begin +(mmap-zero) create empty file "empty" +(mmap-zero) open "empty" +(mmap-zero) mmap "empty" +mmap-zero: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/page-linear.c b/pintos-env/pintos/tests/vm/page-linear.c new file mode 100755 index 0000000..652a47b --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-linear.c @@ -0,0 +1,44 @@ +/* Encrypts, then decrypts, 2 MB of memory and verifies that the + values are as they should be. */ + +#include +#include "tests/arc4.h" +#include "tests/lib.h" +#include "tests/main.h" + +#define SIZE (2 * 1024 * 1024) + +static char buf[SIZE]; + +void +test_main (void) +{ + struct arc4 arc4; + size_t i; + + /* Initialize to 0x5a. */ + msg ("initialize"); + memset (buf, 0x5a, sizeof buf); + + /* Check that it's all 0x5a. */ + msg ("read pass"); + for (i = 0; i < SIZE; i++) + if (buf[i] != 0x5a) + fail ("byte %zu != 0x5a", i); + + /* Encrypt zeros. */ + msg ("read/modify/write pass one"); + arc4_init (&arc4, "foobar", 6); + arc4_crypt (&arc4, buf, SIZE); + + /* Decrypt back to zeros. */ + msg ("read/modify/write pass two"); + arc4_init (&arc4, "foobar", 6); + arc4_crypt (&arc4, buf, SIZE); + + /* Check that it's all 0x5a. */ + msg ("read pass"); + for (i = 0; i < SIZE; i++) + if (buf[i] != 0x5a) + fail ("byte %zu != 0x5a", i); +} diff --git a/pintos-env/pintos/tests/vm/page-linear.ck b/pintos-env/pintos/tests/vm/page-linear.ck new file mode 100755 index 0000000..dcbc884 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-linear.ck @@ -0,0 +1,14 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(page-linear) begin +(page-linear) initialize +(page-linear) read pass +(page-linear) read/modify/write pass one +(page-linear) read/modify/write pass two +(page-linear) read pass +(page-linear) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/page-merge-mm.c b/pintos-env/pintos/tests/vm/page-merge-mm.c new file mode 100755 index 0000000..908c71c --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-mm.c @@ -0,0 +1,8 @@ +#include "tests/main.h" +#include "tests/vm/parallel-merge.h" + +void +test_main (void) +{ + parallel_merge ("child-qsort-mm", 80); +} diff --git a/pintos-env/pintos/tests/vm/page-merge-mm.ck b/pintos-env/pintos/tests/vm/page-merge-mm.ck new file mode 100755 index 0000000..74fa980 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-mm.ck @@ -0,0 +1,29 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(page-merge-mm) begin +(page-merge-mm) init +(page-merge-mm) sort chunk 0 +(page-merge-mm) sort chunk 1 +(page-merge-mm) sort chunk 2 +(page-merge-mm) sort chunk 3 +(page-merge-mm) sort chunk 4 +(page-merge-mm) sort chunk 5 +(page-merge-mm) sort chunk 6 +(page-merge-mm) sort chunk 7 +(page-merge-mm) wait for child 0 +(page-merge-mm) wait for child 1 +(page-merge-mm) wait for child 2 +(page-merge-mm) wait for child 3 +(page-merge-mm) wait for child 4 +(page-merge-mm) wait for child 5 +(page-merge-mm) wait for child 6 +(page-merge-mm) wait for child 7 +(page-merge-mm) merge +(page-merge-mm) verify +(page-merge-mm) success, buf_idx=1,048,576 +(page-merge-mm) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/page-merge-par.c b/pintos-env/pintos/tests/vm/page-merge-par.c new file mode 100755 index 0000000..e7e1609 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-par.c @@ -0,0 +1,8 @@ +#include "tests/main.h" +#include "tests/vm/parallel-merge.h" + +void +test_main (void) +{ + parallel_merge ("child-sort", 123); +} diff --git a/pintos-env/pintos/tests/vm/page-merge-par.ck b/pintos-env/pintos/tests/vm/page-merge-par.ck new file mode 100755 index 0000000..31f8aa7 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-par.ck @@ -0,0 +1,29 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(page-merge-par) begin +(page-merge-par) init +(page-merge-par) sort chunk 0 +(page-merge-par) sort chunk 1 +(page-merge-par) sort chunk 2 +(page-merge-par) sort chunk 3 +(page-merge-par) sort chunk 4 +(page-merge-par) sort chunk 5 +(page-merge-par) sort chunk 6 +(page-merge-par) sort chunk 7 +(page-merge-par) wait for child 0 +(page-merge-par) wait for child 1 +(page-merge-par) wait for child 2 +(page-merge-par) wait for child 3 +(page-merge-par) wait for child 4 +(page-merge-par) wait for child 5 +(page-merge-par) wait for child 6 +(page-merge-par) wait for child 7 +(page-merge-par) merge +(page-merge-par) verify +(page-merge-par) success, buf_idx=1,048,576 +(page-merge-par) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/page-merge-seq.c b/pintos-env/pintos/tests/vm/page-merge-seq.c new file mode 100755 index 0000000..12e3880 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-seq.c @@ -0,0 +1,137 @@ +/* Generates about 1 MB of random data that is then divided into + 16 chunks. A separate subprocess sorts each chunk in + sequence. Then we merge the chunks and verify that the result + is what it should be. */ + +#include +#include "tests/arc4.h" +#include "tests/lib.h" +#include "tests/main.h" + +/* This is the max file size for an older version of the Pintos + file system that had 126 direct blocks each pointing to a + single disk sector. We could raise it now. */ +#define CHUNK_SIZE (126 * 512) +#define CHUNK_CNT 16 /* Number of chunks. */ +#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */ + +unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE]; +size_t histogram[256]; + +/* Initialize buf1 with random data, + then count the number of instances of each value within it. */ +static void +init (void) +{ + struct arc4 arc4; + size_t i; + + msg ("init"); + + arc4_init (&arc4, "foobar", 6); + arc4_crypt (&arc4, buf1, sizeof buf1); + for (i = 0; i < sizeof buf1; i++) + histogram[buf1[i]]++; +} + +/* Sort each chunk of buf1 using a subprocess. */ +static void +sort_chunks (void) +{ + size_t i; + + create ("buffer", CHUNK_SIZE); + for (i = 0; i < CHUNK_CNT; i++) + { + pid_t child; + int handle; + + msg ("sort chunk %zu", i); + + /* Write this chunk to a file. */ + quiet = true; + CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); + write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); + close (handle); + + /* Sort with subprocess. */ + CHECK ((child = exec ("child-sort buffer")) != -1, + "exec \"child-sort buffer\""); + CHECK (wait (child) == 123, "wait for child-sort"); + + /* Read chunk back from file. */ + CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); + read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); + close (handle); + + quiet = false; + } +} + +/* Merge the sorted chunks in buf1 into a fully sorted buf2. */ +static void +merge (void) +{ + unsigned char *mp[CHUNK_CNT]; + size_t mp_left; + unsigned char *op; + size_t i; + + msg ("merge"); + + /* Initialize merge pointers. */ + mp_left = CHUNK_CNT; + for (i = 0; i < CHUNK_CNT; i++) + mp[i] = buf1 + CHUNK_SIZE * i; + + /* Merge. */ + op = buf2; + while (mp_left > 0) + { + /* Find smallest value. */ + size_t min = 0; + for (i = 1; i < mp_left; i++) + if (*mp[i] < *mp[min]) + min = i; + + /* Append value to buf2. */ + *op++ = *mp[min]; + + /* Advance merge pointer. + Delete this chunk from the set if it's emptied. */ + if ((++mp[min] - buf1) % CHUNK_SIZE == 0) + mp[min] = mp[--mp_left]; + } +} + +static void +verify (void) +{ + size_t buf_idx; + size_t hist_idx; + + msg ("verify"); + + buf_idx = 0; + for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram; + hist_idx++) + { + while (histogram[hist_idx]-- > 0) + { + if (buf2[buf_idx] != hist_idx) + fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx); + buf_idx++; + } + } + + msg ("success, buf_idx=%'zu", buf_idx); +} + +void +test_main (void) +{ + init (); + sort_chunks (); + merge (); + verify (); +} diff --git a/pintos-env/pintos/tests/vm/page-merge-seq.ck b/pintos-env/pintos/tests/vm/page-merge-seq.ck new file mode 100755 index 0000000..d78f69d --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-seq.ck @@ -0,0 +1,29 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(page-merge-seq) begin +(page-merge-seq) init +(page-merge-seq) sort chunk 0 +(page-merge-seq) sort chunk 1 +(page-merge-seq) sort chunk 2 +(page-merge-seq) sort chunk 3 +(page-merge-seq) sort chunk 4 +(page-merge-seq) sort chunk 5 +(page-merge-seq) sort chunk 6 +(page-merge-seq) sort chunk 7 +(page-merge-seq) sort chunk 8 +(page-merge-seq) sort chunk 9 +(page-merge-seq) sort chunk 10 +(page-merge-seq) sort chunk 11 +(page-merge-seq) sort chunk 12 +(page-merge-seq) sort chunk 13 +(page-merge-seq) sort chunk 14 +(page-merge-seq) sort chunk 15 +(page-merge-seq) merge +(page-merge-seq) verify +(page-merge-seq) success, buf_idx=1,032,192 +(page-merge-seq) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/page-merge-stk.c b/pintos-env/pintos/tests/vm/page-merge-stk.c new file mode 100755 index 0000000..5eb1069 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-stk.c @@ -0,0 +1,8 @@ +#include "tests/main.h" +#include "tests/vm/parallel-merge.h" + +void +test_main (void) +{ + parallel_merge ("child-qsort", 72); +} diff --git a/pintos-env/pintos/tests/vm/page-merge-stk.ck b/pintos-env/pintos/tests/vm/page-merge-stk.ck new file mode 100755 index 0000000..c5bc1ae --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-merge-stk.ck @@ -0,0 +1,29 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(page-merge-stk) begin +(page-merge-stk) init +(page-merge-stk) sort chunk 0 +(page-merge-stk) sort chunk 1 +(page-merge-stk) sort chunk 2 +(page-merge-stk) sort chunk 3 +(page-merge-stk) sort chunk 4 +(page-merge-stk) sort chunk 5 +(page-merge-stk) sort chunk 6 +(page-merge-stk) sort chunk 7 +(page-merge-stk) wait for child 0 +(page-merge-stk) wait for child 1 +(page-merge-stk) wait for child 2 +(page-merge-stk) wait for child 3 +(page-merge-stk) wait for child 4 +(page-merge-stk) wait for child 5 +(page-merge-stk) wait for child 6 +(page-merge-stk) wait for child 7 +(page-merge-stk) merge +(page-merge-stk) verify +(page-merge-stk) success, buf_idx=1,048,576 +(page-merge-stk) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/page-parallel.c b/pintos-env/pintos/tests/vm/page-parallel.c new file mode 100755 index 0000000..9d619e0 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-parallel.c @@ -0,0 +1,21 @@ +/* Runs 4 child-linear processes at once. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +#define CHILD_CNT 4 + +void +test_main (void) +{ + pid_t children[CHILD_CNT]; + int i; + + for (i = 0; i < CHILD_CNT; i++) + CHECK ((children[i] = exec ("child-linear")) != -1, + "exec \"child-linear\""); + + for (i = 0; i < CHILD_CNT; i++) + CHECK (wait (children[i]) == 0x42, "wait for child %d", i); +} diff --git a/pintos-env/pintos/tests/vm/page-parallel.ck b/pintos-env/pintos/tests/vm/page-parallel.ck new file mode 100755 index 0000000..90c14ef --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-parallel.ck @@ -0,0 +1,17 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(page-parallel) begin +(page-parallel) exec "child-linear" +(page-parallel) exec "child-linear" +(page-parallel) exec "child-linear" +(page-parallel) exec "child-linear" +(page-parallel) wait for child 0 +(page-parallel) wait for child 1 +(page-parallel) wait for child 2 +(page-parallel) wait for child 3 +(page-parallel) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/page-shuffle.c b/pintos-env/pintos/tests/vm/page-shuffle.c new file mode 100755 index 0000000..095a9da --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-shuffle.c @@ -0,0 +1,30 @@ +/* Shuffles a 128 kB data buffer 10 times, printing the checksum + after each time. */ + +#include +#include "tests/arc4.h" +#include "tests/cksum.h" +#include "tests/lib.h" +#include "tests/main.h" + +#define SIZE (128 * 1024) + +static char buf[SIZE]; + +void +test_main (void) +{ + size_t i; + + /* Initialize. */ + for (i = 0; i < sizeof buf; i++) + buf[i] = i * 257; + msg ("init: cksum=%lu", cksum (buf, sizeof buf)); + + /* Shuffle repeatedly. */ + for (i = 0; i < 10; i++) + { + shuffle (buf, sizeof buf, 1); + msg ("shuffle %zu: cksum=%lu", i, cksum (buf, sizeof buf)); + } +} diff --git a/pintos-env/pintos/tests/vm/page-shuffle.ck b/pintos-env/pintos/tests/vm/page-shuffle.ck new file mode 100755 index 0000000..6447d38 --- /dev/null +++ b/pintos-env/pintos/tests/vm/page-shuffle.ck @@ -0,0 +1,44 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::cksum; +use tests::lib; + +my ($init, @shuffle); +if (1) { + # Use precalculated values. + $init = 3115322833; + @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467, + 3022003332, 3614934266, 2704001777, 735775156, 1864109763); +} else { + # Recalculate values. + my ($buf) = ""; + for my $i (0...128 * 1024 - 1) { + $buf .= chr (($i * 257) & 0xff); + } + $init = cksum ($buf); + + random_init (0); + for my $i (1...10) { + $buf = shuffle ($buf, length ($buf), 1); + push (@shuffle, cksum ($buf)); + } +} + +check_expected (IGNORE_EXIT_CODES => 1, [< +#include +#include "tests/arc4.h" +#include "tests/lib.h" +#include "tests/main.h" + +#define CHUNK_SIZE (128 * 1024) +#define CHUNK_CNT 8 /* Number of chunks. */ +#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */ + +unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE]; +size_t histogram[256]; + +/* Initialize buf1 with random data, + then count the number of instances of each value within it. */ +static void +init (void) +{ + struct arc4 arc4; + size_t i; + + msg ("init"); + + arc4_init (&arc4, "foobar", 6); + arc4_crypt (&arc4, buf1, sizeof buf1); + for (i = 0; i < sizeof buf1; i++) + histogram[buf1[i]]++; +} + +/* Sort each chunk of buf1 using SUBPROCESS, + which is expected to return EXIT_STATUS. */ +static void +sort_chunks (const char *subprocess, int exit_status) +{ + pid_t children[CHUNK_CNT]; + size_t i; + + for (i = 0; i < CHUNK_CNT; i++) + { + char fn[128]; + char cmd[128]; + int handle; + + msg ("sort chunk %zu", i); + + /* Write this chunk to a file. */ + snprintf (fn, sizeof fn, "buf%zu", i); + create (fn, CHUNK_SIZE); + quiet = true; + CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); + write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); + close (handle); + + /* Sort with subprocess. */ + snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn); + CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd); + quiet = false; + } + + for (i = 0; i < CHUNK_CNT; i++) + { + char fn[128]; + int handle; + + CHECK (wait (children[i]) == exit_status, "wait for child %zu", i); + + /* Read chunk back from file. */ + quiet = true; + snprintf (fn, sizeof fn, "buf%zu", i); + CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); + read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); + close (handle); + quiet = false; + } +} + +/* Merge the sorted chunks in buf1 into a fully sorted buf2. */ +static void +merge (void) +{ + unsigned char *mp[CHUNK_CNT]; + size_t mp_left; + unsigned char *op; + size_t i; + + msg ("merge"); + + /* Initialize merge pointers. */ + mp_left = CHUNK_CNT; + for (i = 0; i < CHUNK_CNT; i++) + mp[i] = buf1 + CHUNK_SIZE * i; + + /* Merge. */ + op = buf2; + while (mp_left > 0) + { + /* Find smallest value. */ + size_t min = 0; + for (i = 1; i < mp_left; i++) + if (*mp[i] < *mp[min]) + min = i; + + /* Append value to buf2. */ + *op++ = *mp[min]; + + /* Advance merge pointer. + Delete this chunk from the set if it's emptied. */ + if ((++mp[min] - buf1) % CHUNK_SIZE == 0) + mp[min] = mp[--mp_left]; + } +} + +static void +verify (void) +{ + size_t buf_idx; + size_t hist_idx; + + msg ("verify"); + + buf_idx = 0; + for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram; + hist_idx++) + { + while (histogram[hist_idx]-- > 0) + { + if (buf2[buf_idx] != hist_idx) + fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx); + buf_idx++; + } + } + + msg ("success, buf_idx=%'zu", buf_idx); +} + +void +parallel_merge (const char *child_name, int exit_status) +{ + init (); + sort_chunks (child_name, exit_status); + merge (); + verify (); +} diff --git a/pintos-env/pintos/tests/vm/parallel-merge.h b/pintos-env/pintos/tests/vm/parallel-merge.h new file mode 100755 index 0000000..a6b6431 --- /dev/null +++ b/pintos-env/pintos/tests/vm/parallel-merge.h @@ -0,0 +1,6 @@ +#ifndef TESTS_VM_PARALLEL_MERGE +#define TESTS_VM_PARALLEL_MERGE 1 + +void parallel_merge (const char *child_name, int exit_status); + +#endif /* tests/vm/parallel-merge.h */ diff --git a/pintos-env/pintos/tests/vm/process_death.pm b/pintos-env/pintos/tests/vm/process_death.pm new file mode 100755 index 0000000..52039a1 --- /dev/null +++ b/pintos-env/pintos/tests/vm/process_death.pm @@ -0,0 +1,22 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; + +sub check_process_death { + my ($proc_name) = @_; + our ($test); + my (@output) = read_text_file ("$test.output"); + + common_checks ("run", @output); + @output = get_core_output ("run", @output); + fail "First line of output is not `($proc_name) begin' message.\n" + if $output[0] ne "($proc_name) begin"; + fail "Output missing '$proc_name: exit(-1)' message.\n" + if !grep ("$proc_name: exit(-1)" eq $_, @output); + fail "Output contains '($proc_name) end' message.\n" + if grep (/\($proc_name\) end/, @output); + pass; +} + +1; diff --git a/pintos-env/pintos/tests/vm/pt-bad-addr.c b/pintos-env/pintos/tests/vm/pt-bad-addr.c new file mode 100755 index 0000000..3ca4084 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-bad-addr.c @@ -0,0 +1,11 @@ +/* Accesses a bad address. + The process must be terminated with -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + fail ("bad addr read as %d", *(int *) 0x04000000); +} diff --git a/pintos-env/pintos/tests/vm/pt-bad-addr.ck b/pintos-env/pintos/tests/vm/pt-bad-addr.ck new file mode 100755 index 0000000..09ea039 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-bad-addr.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::vm::process_death; + +check_process_death ('pt-bad-addr'); diff --git a/pintos-env/pintos/tests/vm/pt-bad-read.c b/pintos-env/pintos/tests/vm/pt-bad-read.c new file mode 100755 index 0000000..ee791ff --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-bad-read.c @@ -0,0 +1,16 @@ +/* Reads from a file into a bad address. + The process must be terminated with -1 exit code. */ + +#include +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + read (handle, (char *) &handle - 4096, 1); + fail ("survived reading data into bad address"); +} diff --git a/pintos-env/pintos/tests/vm/pt-bad-read.ck b/pintos-env/pintos/tests/vm/pt-bad-read.ck new file mode 100755 index 0000000..1f96bb4 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-bad-read.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(pt-bad-read) begin +(pt-bad-read) open "sample.txt" +pt-bad-read: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/pt-big-stk-obj.c b/pintos-env/pintos/tests/vm/pt-big-stk-obj.c new file mode 100755 index 0000000..6b630ec --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-big-stk-obj.c @@ -0,0 +1,20 @@ +/* Allocates and writes to a 64 kB object on the stack. + This must succeed. */ + +#include +#include "tests/arc4.h" +#include "tests/cksum.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char stk_obj[65536]; + struct arc4 arc4; + + arc4_init (&arc4, "foobar", 6); + memset (stk_obj, 0, sizeof stk_obj); + arc4_crypt (&arc4, stk_obj, sizeof stk_obj); + msg ("cksum: %lu", cksum (stk_obj, sizeof stk_obj)); +} diff --git a/pintos-env/pintos/tests/vm/pt-big-stk-obj.ck b/pintos-env/pintos/tests/vm/pt-big-stk-obj.ck new file mode 100755 index 0000000..eb5853a --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-big-stk-obj.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(pt-big-stk-obj) begin +(pt-big-stk-obj) cksum: 3256410166 +(pt-big-stk-obj) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/pt-grow-bad.c b/pintos-env/pintos/tests/vm/pt-grow-bad.c new file mode 100755 index 0000000..d4beba2 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-bad.c @@ -0,0 +1,14 @@ +/* Read from an address 4,096 bytes below the stack pointer. + The process must be terminated with -1 exit code. */ + +#include +#include "tests/arc4.h" +#include "tests/cksum.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + asm volatile ("movl -4096(%esp), %eax"); +} diff --git a/pintos-env/pintos/tests/vm/pt-grow-bad.ck b/pintos-env/pintos/tests/vm/pt-grow-bad.ck new file mode 100755 index 0000000..4c0ab8a --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-bad.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); +(pt-grow-bad) begin +pt-grow-bad: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/pt-grow-pusha.c b/pintos-env/pintos/tests/vm/pt-grow-pusha.c new file mode 100755 index 0000000..f9762a5 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-pusha.c @@ -0,0 +1,20 @@ +/* Expand the stack by 32 bytes all at once using the PUSHA + instruction. + This must succeed. */ + +#include +#include "tests/arc4.h" +#include "tests/cksum.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + asm volatile + ("movl %%esp, %%eax;" /* Save a copy of the stack pointer. */ + "andl $0xfffff000, %%esp;" /* Move stack pointer to bottom of page. */ + "pushal;" /* Push 32 bytes on stack at once. */ + "movl %%eax, %%esp" /* Restore copied stack pointer. */ + : : : "eax"); /* Tell GCC we destroyed eax. */ +} diff --git a/pintos-env/pintos/tests/vm/pt-grow-pusha.ck b/pintos-env/pintos/tests/vm/pt-grow-pusha.ck new file mode 100755 index 0000000..5000966 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-pusha.ck @@ -0,0 +1,9 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(pt-grow-pusha) begin +(pt-grow-pusha) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/pt-grow-stack.c b/pintos-env/pintos/tests/vm/pt-grow-stack.c new file mode 100755 index 0000000..0997a00 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-stack.c @@ -0,0 +1,20 @@ +/* Demonstrate that the stack can grow. + This must succeed. */ + +#include +#include "tests/arc4.h" +#include "tests/cksum.h" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + char stack_obj[4096]; + struct arc4 arc4; + + arc4_init (&arc4, "foobar", 6); + memset (stack_obj, 0, sizeof stack_obj); + arc4_crypt (&arc4, stack_obj, sizeof stack_obj); + msg ("cksum: %lu", cksum (stack_obj, sizeof stack_obj)); +} diff --git a/pintos-env/pintos/tests/vm/pt-grow-stack.ck b/pintos-env/pintos/tests/vm/pt-grow-stack.ck new file mode 100755 index 0000000..1e669db --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-stack.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(pt-grow-stack) begin +(pt-grow-stack) cksum: 3424492700 +(pt-grow-stack) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/pt-grow-stk-sc.c b/pintos-env/pintos/tests/vm/pt-grow-stk-sc.c new file mode 100755 index 0000000..3efbb5f --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-stk-sc.c @@ -0,0 +1,32 @@ +/* This test checks that the stack is properly extended even if + the first access to a stack location occurs inside a system + call. + + From Godmar Back. */ + +#include +#include +#include "tests/vm/sample.inc" +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + int slen = strlen (sample); + char buf2[65536]; + + /* Write file via write(). */ + CHECK (create ("sample.txt", slen), "create \"sample.txt\""); + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + CHECK (write (handle, sample, slen) == slen, "write \"sample.txt\""); + close (handle); + + /* Read back via read(). */ + CHECK ((handle = open ("sample.txt")) > 1, "2nd open \"sample.txt\""); + CHECK (read (handle, buf2 + 32768, slen) == slen, "read \"sample.txt\""); + + CHECK (!memcmp (sample, buf2 + 32768, slen), "compare written data against read data"); + close (handle); +} diff --git a/pintos-env/pintos/tests/vm/pt-grow-stk-sc.ck b/pintos-env/pintos/tests/vm/pt-grow-stk-sc.ck new file mode 100755 index 0000000..23d3b02 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-grow-stk-sc.ck @@ -0,0 +1,15 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); +(pt-grow-stk-sc) begin +(pt-grow-stk-sc) create "sample.txt" +(pt-grow-stk-sc) open "sample.txt" +(pt-grow-stk-sc) write "sample.txt" +(pt-grow-stk-sc) 2nd open "sample.txt" +(pt-grow-stk-sc) read "sample.txt" +(pt-grow-stk-sc) compare written data against read data +(pt-grow-stk-sc) end +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/pt-write-code-2.c b/pintos-env/pintos/tests/vm/pt-write-code-2.c new file mode 100755 index 0000000..83bcc2c --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-write-code-2.c @@ -0,0 +1,15 @@ +/* Try to write to the code segment using a system call. + The process must be terminated with -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + int handle; + + CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); + read (handle, (void *) test_main, 1); + fail ("survived reading data into code segment"); +} diff --git a/pintos-env/pintos/tests/vm/pt-write-code.c b/pintos-env/pintos/tests/vm/pt-write-code.c new file mode 100755 index 0000000..5072cec --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-write-code.c @@ -0,0 +1,12 @@ +/* Try to write to the code segment. + The process must be terminated with -1 exit code. */ + +#include "tests/lib.h" +#include "tests/main.h" + +void +test_main (void) +{ + *(int *) test_main = 0; + fail ("writing the code segment succeeded"); +} diff --git a/pintos-env/pintos/tests/vm/pt-write-code.ck b/pintos-env/pintos/tests/vm/pt-write-code.ck new file mode 100755 index 0000000..65610fb --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-write-code.ck @@ -0,0 +1,7 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +use tests::vm::process_death; + +check_process_death ('pt-write-code'); diff --git a/pintos-env/pintos/tests/vm/pt-write-code2.ck b/pintos-env/pintos/tests/vm/pt-write-code2.ck new file mode 100755 index 0000000..69ffc77 --- /dev/null +++ b/pintos-env/pintos/tests/vm/pt-write-code2.ck @@ -0,0 +1,10 @@ +# -*- perl -*- +use strict; +use warnings; +use tests::tests; +check_expected ([<<'EOF']); +(pt-write-code2) begin +(pt-write-code2) open "sample.txt" +pt-write-code2: exit(-1) +EOF +pass; diff --git a/pintos-env/pintos/tests/vm/qsort.c b/pintos-env/pintos/tests/vm/qsort.c new file mode 100755 index 0000000..922572c --- /dev/null +++ b/pintos-env/pintos/tests/vm/qsort.c @@ -0,0 +1,136 @@ +#include "tests/vm/qsort.h" +#include +#include +#include + +/* Picks a pivot for the quicksort from the SIZE bytes in BUF. */ +static unsigned char +pick_pivot (unsigned char *buf, size_t size) +{ + ASSERT (size >= 1); + return buf[random_ulong () % size]; +} + +/* Checks whether the SIZE bytes in ARRAY are divided into an + initial LEFT_SIZE elements all less than PIVOT followed by + SIZE - LEFT_SIZE elements all greater than or equal to + PIVOT. */ +static bool +is_partitioned (const unsigned char *array, size_t size, + unsigned char pivot, size_t left_size) +{ + size_t i; + + for (i = 0; i < left_size; i++) + if (array[i] >= pivot) + return false; + + for (; i < size; i++) + if (array[i] < pivot) + return false; + + return true; +} + +/* Swaps the bytes at *A and *B. */ +static void +swap (unsigned char *a, unsigned char *b) +{ + unsigned char t = *a; + *a = *b; + *b = t; +} + +/* Partitions ARRAY in-place in an initial run of bytes all less + than PIVOT, followed by a run of bytes all greater than or + equal to PIVOT. Returns the length of the initial run. */ +static size_t +partition (unsigned char *array, size_t size, int pivot) +{ + size_t left_size = size; + unsigned char *first = array; + unsigned char *last = first + left_size; + + for (;;) + { + /* Move FIRST forward to point to first element greater than + PIVOT. */ + for (;;) + { + if (first == last) + { + ASSERT (is_partitioned (array, size, pivot, left_size)); + return left_size; + } + else if (*first >= pivot) + break; + + first++; + } + left_size--; + + /* Move LAST backward to point to last element no bigger + than PIVOT. */ + for (;;) + { + last--; + + if (first == last) + { + ASSERT (is_partitioned (array, size, pivot, left_size)); + return left_size; + } + else if (*last < pivot) + break; + else + left_size--; + } + + /* By swapping FIRST and LAST we extend the starting and + ending sequences that pass and fail, respectively, + PREDICATE. */ + swap (first, last); + first++; + } +} + +/* Returns true if the SIZE bytes in BUF are in nondecreasing + order, false otherwise. */ +static bool +is_sorted (const unsigned char *buf, size_t size) +{ + size_t i; + + for (i = 1; i < size; i++) + if (buf[i - 1] > buf[i]) + return false; + + return true; +} + +/* Sorts the SIZE bytes in BUF into nondecreasing order, using + the quick-sort algorithm. */ +void +qsort_bytes (unsigned char *buf, size_t size) +{ + if (!is_sorted (buf, size)) + { + int pivot = pick_pivot (buf, size); + + unsigned char *left_half = buf; + size_t left_size = partition (buf, size, pivot); + unsigned char *right_half = left_half + left_size; + size_t right_size = size - left_size; + + if (left_size <= right_size) + { + qsort_bytes (left_half, left_size); + qsort_bytes (right_half, right_size); + } + else + { + qsort_bytes (right_half, right_size); + qsort_bytes (left_half, left_size); + } + } +} diff --git a/pintos-env/pintos/tests/vm/qsort.h b/pintos-env/pintos/tests/vm/qsort.h new file mode 100755 index 0000000..61b65f3 --- /dev/null +++ b/pintos-env/pintos/tests/vm/qsort.h @@ -0,0 +1,8 @@ +#ifndef TESTS_VM_QSORT_H +#define TESTS_VM_QSORT_H 1 + +#include + +void qsort_bytes (unsigned char *buf, size_t size); + +#endif /* tests/vm/qsort.h */ diff --git a/pintos-env/pintos/tests/vm/sample.inc b/pintos-env/pintos/tests/vm/sample.inc new file mode 100755 index 0000000..a60a139 --- /dev/null +++ b/pintos-env/pintos/tests/vm/sample.inc @@ -0,0 +1,19 @@ +char sample[] = { + "=== ALL USERS PLEASE NOTE ========================\n" + "\n" + "CAR and CDR now return extra values.\n" + "\n" + "The function CAR now returns two values. Since it has to go to the\n" + "trouble to figure out if the object is carcdr-able anyway, we figured\n" + "you might as well get both halves at once. For example, the following\n" + "code shows how to destructure a cons (SOME-CONS) into its two slots\n" + "(THE-CAR and THE-CDR):\n" + "\n" + " (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)\n" + "\n" + "For symmetry with CAR, CDR returns a second value which is the CAR of\n" + "the object. In a related change, the functions MAKE-ARRAY and CONS\n" + "have been fixed so they don't allocate any storage except on the\n" + "stack. This should hopefully help people who don't like using the\n" + "garbage collector because it cold boots the machine so often.\n" +}; diff --git a/pintos-env/pintos/tests/vm/sample.txt b/pintos-env/pintos/tests/vm/sample.txt new file mode 100755 index 0000000..c446830 --- /dev/null +++ b/pintos-env/pintos/tests/vm/sample.txt @@ -0,0 +1,17 @@ +=== ALL USERS PLEASE NOTE ======================== + +CAR and CDR now return extra values. + +The function CAR now returns two values. Since it has to go to the +trouble to figure out if the object is carcdr-able anyway, we figured +you might as well get both halves at once. For example, the following +code shows how to destructure a cons (SOME-CONS) into its two slots +(THE-CAR and THE-CDR): + + (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...) + +For symmetry with CAR, CDR returns a second value which is the CAR of +the object. In a related change, the functions MAKE-ARRAY and CONS +have been fixed so they don't allocate any storage except on the +stack. This should hopefully help people who don't like using the +garbage collector because it cold boots the machine so often. diff --git a/pintos-env/pintos/threads/Make.vars b/pintos-env/pintos/threads/Make.vars new file mode 100755 index 0000000..310c240 --- /dev/null +++ b/pintos-env/pintos/threads/Make.vars @@ -0,0 +1,7 @@ +# -*- makefile -*- + +kernel.bin: DEFINES = +KERNEL_SUBDIRS = threads devices lib lib/kernel $(TEST_SUBDIRS) +TEST_SUBDIRS = tests/threads +GRADING_FILE = $(SRCDIR)/tests/threads/Grading +SIMULATOR = --bochs diff --git a/pintos-env/pintos/threads/Makefile b/pintos-env/pintos/threads/Makefile new file mode 100755 index 0000000..34c10aa --- /dev/null +++ b/pintos-env/pintos/threads/Makefile @@ -0,0 +1 @@ +include ../Makefile.kernel diff --git a/pintos-env/pintos/threads/bochsout.txt b/pintos-env/pintos/threads/bochsout.txt new file mode 100755 index 0000000..e6b4085 --- /dev/null +++ b/pintos-env/pintos/threads/bochsout.txt @@ -0,0 +1,173 @@ +00000000000i[ ] Bochs x86 Emulator 2.6 +00000000000i[ ] Built from SVN snapshot on September 2nd, 2012 +00000000000i[ ] Compiled on Feb 17 2015 at 16:28:11 +00000000000i[ ] System configuration +00000000000i[ ] processors: 1 (cores=1, HT threads=1) +00000000000i[ ] A20 line support: yes +00000000000i[ ] IPS is set to 1000000 +00000000000i[ ] CPU configuration +00000000000i[ ] level: 6 +00000000000i[ ] SMP support: no +00000000000i[ ] APIC support: xapic +00000000000i[ ] FPU support: yes +00000000000i[ ] MMX support: yes +00000000000i[ ] 3dnow! support: no +00000000000i[ ] SEP support: yes +00000000000i[ ] SSE support: sse2 +00000000000i[ ] XSAVE support: no +00000000000i[ ] AES support: no +00000000000i[ ] MOVBE support: no +00000000000i[ ] ADX support: no +00000000000i[ ] x86-64 support: no +00000000000i[ ] MWAIT support: yes +00000000000i[ ] Optimization configuration +00000000000i[ ] RepeatSpeedups support: no +00000000000i[ ] Fast function calls: no +00000000000i[ ] Handlers Chaining speedups: no +00000000000i[ ] Devices configuration +00000000000i[ ] NE2000 support: no +00000000000i[ ] PCI support: no, enabled=no +00000000000i[ ] SB16 support: no +00000000000i[ ] USB support: no +00000000000i[ ] VGA extension support: vbe +00000000000i[MEM0 ] allocated memory at 0x7f3a2075c010. after alignment, vector=0x7f3a2075d000 +00000000000i[MEM0 ] 4.00MB +00000000000i[MEM0 ] mem block size = 0x00100000, blocks=4 +00000000000i[MEM0 ] rom at 0xfffe0000/131072 ('/pintos-env/share/bochs/BIOS-bochs-latest') +00000000000i[ ] init_dev of 'cmos' plugin device by virtual method +00000000000i[CMOS ] Using specified time for initial clock +00000000000i[CMOS ] Setting initial clock to: Thu Jan 1 00:00:00 1970 (time0=0) +00000000000i[ ] init_dev of 'dma' plugin device by virtual method +00000000000i[DMA ] channel 4 used by cascade +00000000000i[ ] init_dev of 'pic' plugin device by virtual method +00000000000i[ ] init_dev of 'pit' plugin device by virtual method +00000000000i[ ] init_dev of 'floppy' plugin device by virtual method +00000000000i[DMA ] channel 2 used by Floppy Drive +00000000000i[ ] init_dev of 'vga' plugin device by virtual method +00000000000i[MEM0 ] Register memory access handlers: 0x00000000000a0000 - 0x00000000000bffff +00000000000i[VGA ] interval=200000 +00000000000i[MEM0 ] Register memory access handlers: 0x00000000e0000000 - 0x00000000e0ffffff +00000000000i[BXVGA] VBE Bochs Display Extension Enabled +00000000000i[MEM0 ] rom at 0xc0000/41472 ('/pintos-env/share/bochs/VGABIOS-lgpl-latest') +00000000000i[ ] init_dev of 'ioapic' plugin device by virtual method +00000000000i[IOAP ] initializing I/O APIC +00000000000i[MEM0 ] Register memory access handlers: 0x00000000fec00000 - 0x00000000fec00fff +00000000000i[ ] init_dev of 'keyboard' plugin device by virtual method +00000000000i[KBD ] will paste characters every 1000 keyboard ticks +00000000000i[ ] init_dev of 'harddrv' plugin device by virtual method +00000000000i[HD ] HD on ata0-0: '/tmp/vgIiYcPakx.dsk', 'flat' mode +00000000000i[IMG ] hd_size: 516096 +00000000000i[HD ] ata0-0: using specified geometry: CHS=1/16/63 +00000000000i[HD ] Using boot sequence disk, none, none +00000000000i[HD ] Floppy boot signature check is enabled +00000000000i[ ] init_dev of 'unmapped' plugin device by virtual method +00000000000i[ ] init_dev of 'biosdev' plugin device by virtual method +00000000000i[ ] init_dev of 'speaker' plugin device by virtual method +00000000000i[ ] init_dev of 'extfpuirq' plugin device by virtual method +00000000000i[ ] init_dev of 'parallel' plugin device by virtual method +00000000000i[PAR ] parallel port 1 at 0x0378 irq 7 +00000000000i[ ] init_dev of 'serial' plugin device by virtual method +00000000000i[SER ] com1 at 0x03f8 irq 4 +00000000000i[ ] register state of 'cmos' plugin device by virtual method +00000000000i[ ] register state of 'dma' plugin device by virtual method +00000000000i[ ] register state of 'pic' plugin device by virtual method +00000000000i[ ] register state of 'pit' plugin device by virtual method +00000000000i[ ] register state of 'floppy' plugin device by virtual method +00000000000i[ ] register state of 'vga' plugin device by virtual method +00000000000i[ ] register state of 'unmapped' plugin device by virtual method +00000000000i[ ] register state of 'biosdev' plugin device by virtual method +00000000000i[ ] register state of 'speaker' plugin device by virtual method +00000000000i[ ] register state of 'extfpuirq' plugin device by virtual method +00000000000i[ ] register state of 'parallel' plugin device by virtual method +00000000000i[ ] register state of 'serial' plugin device by virtual method +00000000000i[ ] register state of 'ioapic' plugin device by virtual method +00000000000i[ ] register state of 'keyboard' plugin device by virtual method +00000000000i[ ] register state of 'harddrv' plugin device by virtual method +00000000000i[SYS ] bx_pc_system_c::Reset(HARDWARE) called +00000000000i[CPU0 ] cpu hardware reset +00000000000i[APIC0] allocate APIC id=0 (MMIO enabled) to 0x00000000fee00000 +00000000000i[CPU0 ] CPUID[0x00000000]: 00000002 756e6547 6c65746e 49656e69 +00000000000i[CPU0 ] CPUID[0x00000001]: 00000633 00010800 00000008 1fcbfbff +00000000000i[CPU0 ] CPUID[0x00000002]: 00410601 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000000]: 80000008 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000001]: 00000000 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000002]: 20202020 20202020 20202020 6e492020 +00000000000i[CPU0 ] CPUID[0x80000003]: 286c6574 50202952 69746e65 52286d75 +00000000000i[CPU0 ] CPUID[0x80000004]: 20342029 20555043 20202020 00202020 +00000000000i[CPU0 ] CPUID[0x80000005]: 01ff01ff 01ff01ff 40020140 40020140 +00000000000i[CPU0 ] CPUID[0x80000006]: 00000000 42004200 02008140 00000000 +00000000000i[CPU0 ] CPUID[0x80000007]: 00000000 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000008]: 00002028 00000000 00000000 00000000 +00000000000i[ ] reset of 'cmos' plugin device by virtual method +00000000000i[ ] reset of 'dma' plugin device by virtual method +00000000000i[ ] reset of 'pic' plugin device by virtual method +00000000000i[ ] reset of 'pit' plugin device by virtual method +00000000000i[ ] reset of 'floppy' plugin device by virtual method +00000000000i[ ] reset of 'vga' plugin device by virtual method +00000000000i[ ] reset of 'ioapic' plugin device by virtual method +00000000000i[ ] reset of 'keyboard' plugin device by virtual method +00000000000i[ ] reset of 'harddrv' plugin device by virtual method +00000000000i[ ] reset of 'unmapped' plugin device by virtual method +00000000000i[ ] reset of 'biosdev' plugin device by virtual method +00000000000i[ ] reset of 'speaker' plugin device by virtual method +00000000000e[SPEAK] Failed to open /dev/console: Permission denied +00000000000e[SPEAK] Deactivating beep on console +00000000000i[ ] reset of 'extfpuirq' plugin device by virtual method +00000000000i[ ] reset of 'parallel' plugin device by virtual method +00000000000i[ ] reset of 'serial' plugin device by virtual method +00000000025i[MEM0 ] allocate_block: block=0x0 used 0x1 of 0x4 +00000004661i[BIOS ] $Revision: 11318 $ $Date: 2012-08-06 19:59:54 +0200 (Mo, 06. Aug 2012) $ +00000317820i[KBD ] reset-disable command received +00000319072i[BIOS ] Starting rombios32 +00000319506i[BIOS ] Shutdown flag 0 +00000320089i[BIOS ] ram_size=0x00400000 +00000320487i[BIOS ] ram_end=4MB +00000331328i[BIOS ] Found 1 cpu(s) +00000345510i[BIOS ] bios_table_addr: 0x000fa438 end=0x000fcc00 +00000363970i[BIOS ] bios_table_cur_addr: 0x000fa438 +00000491587i[VBIOS] VGABios $Id: vgabios.c,v 1.75 2011/10/15 14:07:21 vruppert Exp $ +00000491658i[BXVGA] VBE known Display Interface b0c0 +00000491690i[BXVGA] VBE known Display Interface b0c5 +00000494615i[VBIOS] VBE Bios $Id: vbe.c,v 1.64 2011/07/19 18:25:05 vruppert Exp $ +00000833946i[BIOS ] ata0-0: PCHS=1/16/63 translation=none LCHS=1/16/63 +00004712316i[BIOS ] IDE time out +00007773617i[BIOS ] Booting from 0000:7c00 +00008173052i[MEM0 ] allocate_block: block=0x1 used 0x2 of 0x4 +00008213953i[MEM0 ] allocate_block: block=0x2 used 0x3 of 0x4 +00932406100i[NGUI ] ips = 932.406M +02044894390i[NGUI ] ips = 1112.488M +03169115889i[NGUI ] ips = 1124.221M +04292997614i[NGUI ] ips = 1123.882M +05409617500i[NGUI ] ips = 1116.620M +06520342850i[NGUI ] ips = 1110.725M +07644454593i[NGUI ] ips = 1124.112M +08764788638i[NGUI ] ips = 1120.334M +09863746294i[NGUI ] ips = 1098.958M +10984577800i[NGUI ] ips = 1120.832M +12103658109i[NGUI ] ips = 1119.080M +13226495300i[NGUI ] ips = 1122.837M +14347017519i[NGUI ] ips = 1120.522M +15463585695i[NGUI ] ips = 1116.568M +16391355100p[ ] >>PANIC<< SIGNAL 2 caught +16391355100i[CPU0 ] CPU is in protected mode (halted) +16391355100i[CPU0 ] CS.mode = 32 bit +16391355100i[CPU0 ] SS.mode = 32 bit +16391355100i[CPU0 ] EFER = 0x00000000 +16391355100i[CPU0 ] | EAX=00000000 EBX=c0020fd7 ECX=c0103000 EDX=00000018 +16391355100i[CPU0 ] | ESP=c0103fa8 EBP=00000000 ESI=00000000 EDI=00000000 +16391355100i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df IF tf SF zf AF pf cf +16391355100i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D +16391355100i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1 +16391355100i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +16391355100i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +16391355100i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +16391355100i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +16391355100i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +16391355100i[CPU0 ] | EIP=c0020ffc (c0020ffc) +16391355100i[CPU0 ] | CR0=0xe0010015 CR2=0x00000000 +16391355100i[CPU0 ] | CR3=0x00101000 CR4=0x00000000 +16391355100i[CPU0 ] 0xc0020ffc>> lea esi, dword ptr ds:[esi] : 8D742600 +16391355100i[CMOS ] Last time is 16391 (Thu Jan 1 04:33:11 1970) +16391355100i[NGUI ] bx_nogui_gui_c::exit() not implemented yet. +16391355100i[ ] restoring default signal behavior +16391355100i[CTRL ] quit_sim called with exit code 1 diff --git a/pintos-env/pintos/threads/bochsrc.txt b/pintos-env/pintos/threads/bochsrc.txt new file mode 100755 index 0000000..5c9ac21 --- /dev/null +++ b/pintos-env/pintos/threads/bochsrc.txt @@ -0,0 +1,12 @@ +romimage: file=$BXSHARE/BIOS-bochs-latest +vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest +boot: disk +cpu: ips=1000000 +megs: 4 +log: bochsout.txt +panic: action=fatal +user_shortcut: keys=ctrlaltdel +clock: sync=none, time0=0 +ata0-master: type=disk, path=/tmp/vgIiYcPakx.dsk, mode=flat, cylinders=1, heads=16, spt=63, translation=none +com1: enabled=1, mode=file, dev=/dev/stdout +display_library: nogui diff --git a/pintos-env/pintos/threads/build/Makefile b/pintos-env/pintos/threads/build/Makefile new file mode 100755 index 0000000..e997d27 --- /dev/null +++ b/pintos-env/pintos/threads/build/Makefile @@ -0,0 +1,109 @@ +# -*- makefile -*- + +SRCDIR = ../.. + +all: kernel.bin loader.bin + +include ../../Make.config +include ../Make.vars +include ../../tests/Make.tests + +# Compiler and assembler options. +kernel.bin: CPPFLAGS += -I$(SRCDIR)/lib/kernel + +# Core kernel. +threads_SRC = threads/start.S # Startup code. +threads_SRC += threads/init.c # Main program. +threads_SRC += threads/thread.c # Thread management core. +threads_SRC += threads/switch.S # Thread switch routine. +threads_SRC += threads/interrupt.c # Interrupt core. +threads_SRC += threads/intr-stubs.S # Interrupt stubs. +threads_SRC += threads/synch.c # Synchronization. +threads_SRC += threads/palloc.c # Page allocator. +threads_SRC += threads/malloc.c # Subpage allocator. + +# Device driver code. +devices_SRC = devices/pit.c # Programmable interrupt timer chip. +devices_SRC += devices/timer.c # Periodic timer device. +devices_SRC += devices/kbd.c # Keyboard device. +devices_SRC += devices/vga.c # Video device. +devices_SRC += devices/serial.c # Serial port device. +devices_SRC += devices/block.c # Block device abstraction layer. +devices_SRC += devices/partition.c # Partition block device. +devices_SRC += devices/ide.c # IDE disk block device. +devices_SRC += devices/input.c # Serial and keyboard input. +devices_SRC += devices/intq.c # Interrupt queue. +devices_SRC += devices/rtc.c # Real-time clock. +devices_SRC += devices/shutdown.c # Reboot and power off. +devices_SRC += devices/speaker.c # PC speaker. + +# Library code shared between kernel and user programs. +lib_SRC = lib/debug.c # Debug helpers. +lib_SRC += lib/random.c # Pseudo-random numbers. +lib_SRC += lib/stdio.c # I/O library. +lib_SRC += lib/stdlib.c # Utility functions. +lib_SRC += lib/string.c # String functions. +lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC. +lib_SRC += lib/ustar.c # Unix standard tar format utilities. + +# Kernel-specific library code. +lib/kernel_SRC = lib/kernel/debug.c # Debug helpers. +lib/kernel_SRC += lib/kernel/list.c # Doubly-linked lists. +lib/kernel_SRC += lib/kernel/bitmap.c # Bitmaps. +lib/kernel_SRC += lib/kernel/hash.c # Hash tables. +lib/kernel_SRC += lib/kernel/console.c # printf(), putchar(). + +# User process code. +userprog_SRC = userprog/process.c # Process loading. +userprog_SRC += userprog/pagedir.c # Page directories. +userprog_SRC += userprog/exception.c # User exception handler. +userprog_SRC += userprog/syscall.c # System call handler. +userprog_SRC += userprog/gdt.c # GDT initialization. +userprog_SRC += userprog/tss.c # TSS management. + +# No virtual memory code yet. +#vm_SRC = vm/file.c # Some file. + +# Filesystem code. +filesys_SRC = filesys/filesys.c # Filesystem core. +filesys_SRC += filesys/free-map.c # Free sector bitmap. +filesys_SRC += filesys/file.c # Files. +filesys_SRC += filesys/directory.c # Directories. +filesys_SRC += filesys/inode.c # File headers. +filesys_SRC += filesys/fsutil.c # Utilities. + +SOURCES = $(foreach dir,$(KERNEL_SUBDIRS),$($(dir)_SRC)) +OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES))) +DEPENDS = $(patsubst %.o,%.d,$(OBJECTS)) + +threads/kernel.lds.s: CPPFLAGS += -P +threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h + +kernel.o: threads/kernel.lds.s $(OBJECTS) + $(LD) -T $< -o $@ $(OBJECTS) + +kernel.bin: kernel.o + $(OBJCOPY) -R .note -R .comment -S $< $@ + +threads/loader.o: threads/loader.S + $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) + +loader.bin: threads/loader.o + $(LD) -N -e 0 -Ttext 0x7c00 --oformat binary -o $@ $< + +os.dsk: kernel.bin + cat $^ > $@ + +clean:: + rm -f $(OBJECTS) $(DEPENDS) + rm -f threads/loader.o threads/kernel.lds.s threads/loader.d + rm -f kernel.bin.tmp + rm -f kernel.o kernel.lds.s + rm -f kernel.bin loader.bin + rm -f bochsout.txt bochsrc.txt + rm -f results grade + +Makefile: $(SRCDIR)/Makefile.build + cp $< $@ + +-include $(DEPENDS) diff --git a/pintos-env/pintos/threads/build/bochsout.txt b/pintos-env/pintos/threads/build/bochsout.txt new file mode 100644 index 0000000..c88f660 --- /dev/null +++ b/pintos-env/pintos/threads/build/bochsout.txt @@ -0,0 +1,159 @@ +00000000000i[ ] Bochs x86 Emulator 2.6 +00000000000i[ ] Built from SVN snapshot on September 2nd, 2012 +00000000000i[ ] Compiled on Feb 17 2015 at 16:28:11 +00000000000i[ ] System configuration +00000000000i[ ] processors: 1 (cores=1, HT threads=1) +00000000000i[ ] A20 line support: yes +00000000000i[ ] IPS is set to 1000000 +00000000000i[ ] CPU configuration +00000000000i[ ] level: 6 +00000000000i[ ] SMP support: no +00000000000i[ ] APIC support: xapic +00000000000i[ ] FPU support: yes +00000000000i[ ] MMX support: yes +00000000000i[ ] 3dnow! support: no +00000000000i[ ] SEP support: yes +00000000000i[ ] SSE support: sse2 +00000000000i[ ] XSAVE support: no +00000000000i[ ] AES support: no +00000000000i[ ] MOVBE support: no +00000000000i[ ] ADX support: no +00000000000i[ ] x86-64 support: no +00000000000i[ ] MWAIT support: yes +00000000000i[ ] Optimization configuration +00000000000i[ ] RepeatSpeedups support: no +00000000000i[ ] Fast function calls: no +00000000000i[ ] Handlers Chaining speedups: no +00000000000i[ ] Devices configuration +00000000000i[ ] NE2000 support: no +00000000000i[ ] PCI support: no, enabled=no +00000000000i[ ] SB16 support: no +00000000000i[ ] USB support: no +00000000000i[ ] VGA extension support: vbe +00000000000i[MEM0 ] allocated memory at 0x2b1e011b7010. after alignment, vector=0x2b1e011b8000 +00000000000i[MEM0 ] 4.00MB +00000000000i[MEM0 ] mem block size = 0x00100000, blocks=4 +00000000000i[MEM0 ] rom at 0xfffe0000/131072 ('/pintos-env/share/bochs/BIOS-bochs-latest') +00000000000i[ ] init_dev of 'cmos' plugin device by virtual method +00000000000i[CMOS ] Using specified time for initial clock +00000000000i[CMOS ] Setting initial clock to: Thu Jan 1 00:00:00 1970 (time0=0) +00000000000i[ ] init_dev of 'dma' plugin device by virtual method +00000000000i[DMA ] channel 4 used by cascade +00000000000i[ ] init_dev of 'pic' plugin device by virtual method +00000000000i[ ] init_dev of 'pit' plugin device by virtual method +00000000000i[ ] init_dev of 'floppy' plugin device by virtual method +00000000000i[DMA ] channel 2 used by Floppy Drive +00000000000i[ ] init_dev of 'vga' plugin device by virtual method +00000000000i[MEM0 ] Register memory access handlers: 0x00000000000a0000 - 0x00000000000bffff +00000000000i[VGA ] interval=200000 +00000000000i[MEM0 ] Register memory access handlers: 0x00000000e0000000 - 0x00000000e0ffffff +00000000000i[BXVGA] VBE Bochs Display Extension Enabled +00000000000i[MEM0 ] rom at 0xc0000/41472 ('/pintos-env/share/bochs/VGABIOS-lgpl-latest') +00000000000i[ ] init_dev of 'ioapic' plugin device by virtual method +00000000000i[IOAP ] initializing I/O APIC +00000000000i[MEM0 ] Register memory access handlers: 0x00000000fec00000 - 0x00000000fec00fff +00000000000i[ ] init_dev of 'keyboard' plugin device by virtual method +00000000000i[KBD ] will paste characters every 1000 keyboard ticks +00000000000i[ ] init_dev of 'harddrv' plugin device by virtual method +00000000000i[HD ] HD on ata0-0: '/tmp/d1Z0LnzFEn.dsk', 'flat' mode +00000000000i[IMG ] hd_size: 516096 +00000000000i[HD ] ata0-0: using specified geometry: CHS=1/16/63 +00000000000i[HD ] Using boot sequence disk, none, none +00000000000i[HD ] Floppy boot signature check is enabled +00000000000i[ ] init_dev of 'unmapped' plugin device by virtual method +00000000000i[ ] init_dev of 'biosdev' plugin device by virtual method +00000000000i[ ] init_dev of 'speaker' plugin device by virtual method +00000000000i[ ] init_dev of 'extfpuirq' plugin device by virtual method +00000000000i[ ] init_dev of 'parallel' plugin device by virtual method +00000000000i[PAR ] parallel port 1 at 0x0378 irq 7 +00000000000i[ ] init_dev of 'serial' plugin device by virtual method +00000000000i[SER ] com1 at 0x03f8 irq 4 +00000000000i[ ] register state of 'cmos' plugin device by virtual method +00000000000i[ ] register state of 'dma' plugin device by virtual method +00000000000i[ ] register state of 'pic' plugin device by virtual method +00000000000i[ ] register state of 'pit' plugin device by virtual method +00000000000i[ ] register state of 'floppy' plugin device by virtual method +00000000000i[ ] register state of 'vga' plugin device by virtual method +00000000000i[ ] register state of 'unmapped' plugin device by virtual method +00000000000i[ ] register state of 'biosdev' plugin device by virtual method +00000000000i[ ] register state of 'speaker' plugin device by virtual method +00000000000i[ ] register state of 'extfpuirq' plugin device by virtual method +00000000000i[ ] register state of 'parallel' plugin device by virtual method +00000000000i[ ] register state of 'serial' plugin device by virtual method +00000000000i[ ] register state of 'ioapic' plugin device by virtual method +00000000000i[ ] register state of 'keyboard' plugin device by virtual method +00000000000i[ ] register state of 'harddrv' plugin device by virtual method +00000000000i[SYS ] bx_pc_system_c::Reset(HARDWARE) called +00000000000i[CPU0 ] cpu hardware reset +00000000000i[APIC0] allocate APIC id=0 (MMIO enabled) to 0x00000000fee00000 +00000000000i[CPU0 ] CPUID[0x00000000]: 00000002 756e6547 6c65746e 49656e69 +00000000000i[CPU0 ] CPUID[0x00000001]: 00000633 00010800 00000008 1fcbfbff +00000000000i[CPU0 ] CPUID[0x00000002]: 00410601 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000000]: 80000008 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000001]: 00000000 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000002]: 20202020 20202020 20202020 6e492020 +00000000000i[CPU0 ] CPUID[0x80000003]: 286c6574 50202952 69746e65 52286d75 +00000000000i[CPU0 ] CPUID[0x80000004]: 20342029 20555043 20202020 00202020 +00000000000i[CPU0 ] CPUID[0x80000005]: 01ff01ff 01ff01ff 40020140 40020140 +00000000000i[CPU0 ] CPUID[0x80000006]: 00000000 42004200 02008140 00000000 +00000000000i[CPU0 ] CPUID[0x80000007]: 00000000 00000000 00000000 00000000 +00000000000i[CPU0 ] CPUID[0x80000008]: 00002028 00000000 00000000 00000000 +00000000000i[ ] reset of 'cmos' plugin device by virtual method +00000000000i[ ] reset of 'dma' plugin device by virtual method +00000000000i[ ] reset of 'pic' plugin device by virtual method +00000000000i[ ] reset of 'pit' plugin device by virtual method +00000000000i[ ] reset of 'floppy' plugin device by virtual method +00000000000i[ ] reset of 'vga' plugin device by virtual method +00000000000i[ ] reset of 'ioapic' plugin device by virtual method +00000000000i[ ] reset of 'keyboard' plugin device by virtual method +00000000000i[ ] reset of 'harddrv' plugin device by virtual method +00000000000i[ ] reset of 'unmapped' plugin device by virtual method +00000000000i[ ] reset of 'biosdev' plugin device by virtual method +00000000000i[ ] reset of 'speaker' plugin device by virtual method +00000000000e[SPEAK] Failed to open /dev/console: Permission denied +00000000000e[SPEAK] Deactivating beep on console +00000000000i[ ] reset of 'extfpuirq' plugin device by virtual method +00000000000i[ ] reset of 'parallel' plugin device by virtual method +00000000000i[ ] reset of 'serial' plugin device by virtual method +00000000025i[MEM0 ] allocate_block: block=0x0 used 0x1 of 0x4 +00000004661i[BIOS ] $Revision: 11318 $ $Date: 2012-08-06 19:59:54 +0200 (Mo, 06. Aug 2012) $ +00000317820i[KBD ] reset-disable command received +00000319072i[BIOS ] Starting rombios32 +00000319506i[BIOS ] Shutdown flag 0 +00000320089i[BIOS ] ram_size=0x00400000 +00000320487i[BIOS ] ram_end=4MB +00000331328i[BIOS ] Found 1 cpu(s) +00000345510i[BIOS ] bios_table_addr: 0x000fa438 end=0x000fcc00 +00000363970i[BIOS ] bios_table_cur_addr: 0x000fa438 +00000491587i[VBIOS] VGABios $Id: vgabios.c,v 1.75 2011/10/15 14:07:21 vruppert Exp $ +00000491658i[BXVGA] VBE known Display Interface b0c0 +00000491690i[BXVGA] VBE known Display Interface b0c5 +00000494615i[VBIOS] VBE Bios $Id: vbe.c,v 1.64 2011/07/19 18:25:05 vruppert Exp $ +00000833946i[BIOS ] ata0-0: PCHS=1/16/63 translation=none LCHS=1/16/63 +00004712316i[BIOS ] IDE time out +00007773617i[BIOS ] Booting from 0000:7c00 +00008181201i[MEM0 ] allocate_block: block=0x1 used 0x2 of 0x4 +00008222102i[MEM0 ] allocate_block: block=0x2 used 0x3 of 0x4 +00039252091p[UNMP ] >>PANIC<< Shutdown port: shutdown requested +00039252091i[CPU0 ] CPU is in protected mode (active) +00039252091i[CPU0 ] CS.mode = 32 bit +00039252091i[CPU0 ] SS.mode = 32 bit +00039252091i[CPU0 ] EFER = 0x00000000 +00039252091i[CPU0 ] | EAX=0000006e EBX=c002c62c ECX=c000ef7e EDX=ffff8900 +00039252091i[CPU0 ] | ESP=c000ef60 EBP=c000eff8 ESI=00000000 EDI=c00347b0 +00039252091i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df IF tf sf zf af pf cf +00039252091i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D +00039252091i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1 +00039252091i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +00039252091i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +00039252091i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +00039252091i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +00039252091i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 ffffffff 1 1 +00039252091i[CPU0 ] | EIP=c0025a7f (c0025a7e) +00039252091i[CPU0 ] | CR0=0xe0010015 CR2=0x00000000 +00039252091i[CPU0 ] | CR3=0x00101000 CR4=0x00000000 +00039252091i[CPU0 ] 0xc0025a7e>> out dx, al : EE +00039252091i[CMOS ] Last time is 39 (Thu Jan 1 00:00:39 1970) +00039252091i[NGUI ] bx_nogui_gui_c::exit() not implemented yet. +00039252091i[ ] restoring default signal behavior +00039252091i[CTRL ] quit_sim called with exit code 1 diff --git a/pintos-env/pintos/threads/build/bochsrc.txt b/pintos-env/pintos/threads/build/bochsrc.txt new file mode 100644 index 0000000..bdc39ee --- /dev/null +++ b/pintos-env/pintos/threads/build/bochsrc.txt @@ -0,0 +1,12 @@ +romimage: file=$BXSHARE/BIOS-bochs-latest +vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest +boot: disk +cpu: ips=1000000 +megs: 4 +log: bochsout.txt +panic: action=fatal +user_shortcut: keys=ctrlaltdel +clock: sync=none, time0=0 +ata0-master: type=disk, path=/tmp/d1Z0LnzFEn.dsk, mode=flat, cylinders=1, heads=16, spt=63, translation=none +com1: enabled=1, mode=file, dev=/dev/stdout +display_library: nogui diff --git a/pintos-env/pintos/threads/build/kernel.bin b/pintos-env/pintos/threads/build/kernel.bin new file mode 100755 index 0000000..33d7ef2 Binary files /dev/null and b/pintos-env/pintos/threads/build/kernel.bin differ diff --git a/pintos-env/pintos/threads/build/loader.bin b/pintos-env/pintos/threads/build/loader.bin new file mode 100755 index 0000000..8b3ebf6 Binary files /dev/null and b/pintos-env/pintos/threads/build/loader.bin differ diff --git a/pintos-env/pintos/threads/build/results b/pintos-env/pintos/threads/build/results new file mode 100644 index 0000000..8088ba1 --- /dev/null +++ b/pintos-env/pintos/threads/build/results @@ -0,0 +1,27 @@ +pass tests/threads/alarm-single +pass tests/threads/alarm-multiple +pass tests/threads/alarm-simultaneous +FAIL tests/threads/alarm-priority +pass tests/threads/alarm-zero +pass tests/threads/alarm-negative +FAIL tests/threads/priority-change +FAIL tests/threads/priority-donate-one +FAIL tests/threads/priority-donate-multiple +FAIL tests/threads/priority-donate-multiple2 +FAIL tests/threads/priority-donate-nest +FAIL tests/threads/priority-donate-sema +FAIL tests/threads/priority-donate-lower +FAIL tests/threads/priority-fifo +FAIL tests/threads/priority-preempt +FAIL tests/threads/priority-sema +FAIL tests/threads/priority-condvar +FAIL tests/threads/priority-donate-chain +FAIL tests/threads/mlfqs-load-1 +FAIL tests/threads/mlfqs-load-60 +FAIL tests/threads/mlfqs-load-avg +FAIL tests/threads/mlfqs-recent-1 +pass tests/threads/mlfqs-fair-2 +pass tests/threads/mlfqs-fair-20 +FAIL tests/threads/mlfqs-nice-2 +FAIL tests/threads/mlfqs-nice-10 +FAIL tests/threads/mlfqs-block diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.errors b/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.output b/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.output new file mode 100644 index 0000000..15ce6f9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.output @@ -0,0 +1,63 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run alarm-multiple +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'alarm-multiple': +(alarm-multiple) begin +(alarm-multiple) Creating 5 threads to sleep 7 times each. +(alarm-multiple) Thread 0 sleeps 10 ticks each time, +(alarm-multiple) thread 1 sleeps 20 ticks each time, and so on. +(alarm-multiple) If successful, product of iteration count and +(alarm-multiple) sleep duration will appear in nondescending order. +(alarm-multiple) thread 0: duration=10, iteration=1, product=10 +(alarm-multiple) thread 0: duration=10, iteration=2, product=20 +(alarm-multiple) thread 1: duration=20, iteration=1, product=20 +(alarm-multiple) thread 2: duration=30, iteration=1, product=30 +(alarm-multiple) thread 0: duration=10, iteration=3, product=30 +(alarm-multiple) thread 0: duration=10, iteration=4, product=40 +(alarm-multiple) thread 1: duration=20, iteration=2, product=40 +(alarm-multiple) thread 3: duration=40, iteration=1, product=40 +(alarm-multiple) thread 0: duration=10, iteration=5, product=50 +(alarm-multiple) thread 4: duration=50, iteration=1, product=50 +(alarm-multiple) thread 0: duration=10, iteration=6, product=60 +(alarm-multiple) thread 1: duration=20, iteration=3, product=60 +(alarm-multiple) thread 2: duration=30, iteration=2, product=60 +(alarm-multiple) thread 0: duration=10, iteration=7, product=70 +(alarm-multiple) thread 3: duration=40, iteration=2, product=80 +(alarm-multiple) thread 1: duration=20, iteration=4, product=80 +(alarm-multiple) thread 2: duration=30, iteration=3, product=90 +(alarm-multiple) thread 1: duration=20, iteration=5, product=100 +(alarm-multiple) thread 4: duration=50, iteration=2, product=100 +(alarm-multiple) thread 1: duration=20, iteration=6, product=120 +(alarm-multiple) thread 2: duration=30, iteration=4, product=120 +(alarm-multiple) thread 3: duration=40, iteration=3, product=120 +(alarm-multiple) thread 1: duration=20, iteration=7, product=140 +(alarm-multiple) thread 2: duration=30, iteration=5, product=150 +(alarm-multiple) thread 4: duration=50, iteration=3, product=150 +(alarm-multiple) thread 3: duration=40, iteration=4, product=160 +(alarm-multiple) thread 2: duration=30, iteration=6, product=180 +(alarm-multiple) thread 4: duration=50, iteration=4, product=200 +(alarm-multiple) thread 3: duration=40, iteration=5, product=200 +(alarm-multiple) thread 2: duration=30, iteration=7, product=210 +(alarm-multiple) thread 3: duration=40, iteration=6, product=240 +(alarm-multiple) thread 4: duration=50, iteration=5, product=250 +(alarm-multiple) thread 3: duration=40, iteration=7, product=280 +(alarm-multiple) thread 4: duration=50, iteration=6, product=300 +(alarm-multiple) thread 4: duration=50, iteration=7, product=350 +(alarm-multiple) end +Execution of 'alarm-multiple' complete. +Timer: 842 ticks +Thread: 0 idle ticks, 844 kernel ticks, 0 user ticks +Console: 2950 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.result b/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.result new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-multiple.result @@ -0,0 +1 @@ +PASS diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-negative.errors b/pintos-env/pintos/threads/build/tests/threads/alarm-negative.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-negative.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-negative.output b/pintos-env/pintos/threads/build/tests/threads/alarm-negative.output new file mode 100644 index 0000000..4177959 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-negative.output @@ -0,0 +1,24 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run alarm-negative +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'alarm-negative': +(alarm-negative) begin +(alarm-negative) PASS +(alarm-negative) end +Execution of 'alarm-negative' complete. +Timer: 34 ticks +Thread: 0 idle ticks, 37 kernel ticks, 0 user ticks +Console: 405 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-negative.result b/pintos-env/pintos/threads/build/tests/threads/alarm-negative.result new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-negative.result @@ -0,0 +1 @@ +PASS diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-priority.errors b/pintos-env/pintos/threads/build/tests/threads/alarm-priority.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-priority.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-priority.output b/pintos-env/pintos/threads/build/tests/threads/alarm-priority.output new file mode 100644 index 0000000..e6712b2 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-priority.output @@ -0,0 +1,33 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run alarm-priority +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'alarm-priority': +(alarm-priority) begin +(alarm-priority) Thread priority 25 woke up. +(alarm-priority) Thread priority 24 woke up. +(alarm-priority) Thread priority 21 woke up. +(alarm-priority) Thread priority 28 woke up. +(alarm-priority) Thread priority 23 woke up. +(alarm-priority) Thread priority 29 woke up. +(alarm-priority) Thread priority 22 woke up. +(alarm-priority) Thread priority 26 woke up. +(alarm-priority) Thread priority 30 woke up. +(alarm-priority) Thread priority 27 woke up. +(alarm-priority) end +Execution of 'alarm-priority' complete. +Timer: 589 ticks +Thread: 0 idle ticks, 591 kernel ticks, 0 user ticks +Console: 835 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-priority.result b/pintos-env/pintos/threads/build/tests/threads/alarm-priority.result new file mode 100644 index 0000000..49817e0 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-priority.result @@ -0,0 +1,35 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (alarm-priority) begin + (alarm-priority) Thread priority 30 woke up. + (alarm-priority) Thread priority 29 woke up. + (alarm-priority) Thread priority 28 woke up. + (alarm-priority) Thread priority 27 woke up. + (alarm-priority) Thread priority 26 woke up. + (alarm-priority) Thread priority 25 woke up. + (alarm-priority) Thread priority 24 woke up. + (alarm-priority) Thread priority 23 woke up. + (alarm-priority) Thread priority 22 woke up. + (alarm-priority) Thread priority 21 woke up. + (alarm-priority) end +Differences in `diff -u' format: + (alarm-priority) begin +- (alarm-priority) Thread priority 30 woke up. +- (alarm-priority) Thread priority 29 woke up. +- (alarm-priority) Thread priority 28 woke up. +- (alarm-priority) Thread priority 27 woke up. +- (alarm-priority) Thread priority 26 woke up. + (alarm-priority) Thread priority 25 woke up. + (alarm-priority) Thread priority 24 woke up. ++ (alarm-priority) Thread priority 21 woke up. ++ (alarm-priority) Thread priority 28 woke up. + (alarm-priority) Thread priority 23 woke up. ++ (alarm-priority) Thread priority 29 woke up. + (alarm-priority) Thread priority 22 woke up. +- (alarm-priority) Thread priority 21 woke up. ++ (alarm-priority) Thread priority 26 woke up. ++ (alarm-priority) Thread priority 30 woke up. ++ (alarm-priority) Thread priority 27 woke up. + (alarm-priority) end diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.errors b/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.output b/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.output new file mode 100644 index 0000000..9f28524 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.output @@ -0,0 +1,41 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run alarm-simultaneous +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'alarm-simultaneous': +(alarm-simultaneous) begin +(alarm-simultaneous) Creating 3 threads to sleep 5 times each. +(alarm-simultaneous) Each thread sleeps 10 ticks each time. +(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick. +(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks +(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later +(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later +(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later +(alarm-simultaneous) end +Execution of 'alarm-simultaneous' complete. +Timer: 406 ticks +Thread: 0 idle ticks, 408 kernel ticks, 0 user ticks +Console: 1610 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.result b/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.result new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-simultaneous.result @@ -0,0 +1 @@ +PASS diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-single.errors b/pintos-env/pintos/threads/build/tests/threads/alarm-single.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-single.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-single.output b/pintos-env/pintos/threads/build/tests/threads/alarm-single.output new file mode 100644 index 0000000..5d7d5c2 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-single.output @@ -0,0 +1,33 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run alarm-single +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'alarm-single': +(alarm-single) begin +(alarm-single) Creating 5 threads to sleep 1 times each. +(alarm-single) Thread 0 sleeps 10 ticks each time, +(alarm-single) thread 1 sleeps 20 ticks each time, and so on. +(alarm-single) If successful, product of iteration count and +(alarm-single) sleep duration will appear in nondescending order. +(alarm-single) thread 0: duration=10, iteration=1, product=10 +(alarm-single) thread 1: duration=20, iteration=1, product=20 +(alarm-single) thread 2: duration=30, iteration=1, product=30 +(alarm-single) thread 3: duration=40, iteration=1, product=40 +(alarm-single) thread 4: duration=50, iteration=1, product=50 +(alarm-single) end +Execution of 'alarm-single' complete. +Timer: 354 ticks +Thread: 0 idle ticks, 357 kernel ticks, 0 user ticks +Console: 982 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-single.result b/pintos-env/pintos/threads/build/tests/threads/alarm-single.result new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-single.result @@ -0,0 +1 @@ +PASS diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-zero.errors b/pintos-env/pintos/threads/build/tests/threads/alarm-zero.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-zero.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-zero.output b/pintos-env/pintos/threads/build/tests/threads/alarm-zero.output new file mode 100644 index 0000000..4504436 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-zero.output @@ -0,0 +1,24 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run alarm-zero +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'alarm-zero': +(alarm-zero) begin +(alarm-zero) PASS +(alarm-zero) end +Execution of 'alarm-zero' complete. +Timer: 33 ticks +Thread: 0 idle ticks, 35 kernel ticks, 0 user ticks +Console: 381 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/alarm-zero.result b/pintos-env/pintos/threads/build/tests/threads/alarm-zero.result new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/alarm-zero.result @@ -0,0 +1 @@ +PASS diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.output new file mode 100644 index 0000000..3c14cd0 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.output @@ -0,0 +1,30 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-block +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-block': +(mlfqs-block) begin +(mlfqs-block) Main thread acquiring lock. +(mlfqs-block) Main thread creating block thread, sleeping 25 seconds... +(mlfqs-block) Block thread spinning for 20 seconds... +(mlfqs-block) Block thread acquiring lock... +(mlfqs-block) Main thread spinning for 5 seconds... +(mlfqs-block) Main thread releasing lock. +(mlfqs-block) Block thread should have already acquired lock. +(mlfqs-block) end +Execution of 'mlfqs-block' complete. +Timer: 3058 ticks +Thread: 0 idle ticks, 3061 kernel ticks, 0 user ticks +Console: 748 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.result new file mode 100644 index 0000000..b80c566 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-block.result @@ -0,0 +1,25 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (mlfqs-block) begin + (mlfqs-block) Main thread acquiring lock. + (mlfqs-block) Main thread creating block thread, sleeping 25 seconds... + (mlfqs-block) Block thread spinning for 20 seconds... + (mlfqs-block) Block thread acquiring lock... + (mlfqs-block) Main thread spinning for 5 seconds... + (mlfqs-block) Main thread releasing lock. + (mlfqs-block) ...got it. + (mlfqs-block) Block thread should have already acquired lock. + (mlfqs-block) end +Differences in `diff -u' format: + (mlfqs-block) begin + (mlfqs-block) Main thread acquiring lock. + (mlfqs-block) Main thread creating block thread, sleeping 25 seconds... + (mlfqs-block) Block thread spinning for 20 seconds... + (mlfqs-block) Block thread acquiring lock... + (mlfqs-block) Main thread spinning for 5 seconds... + (mlfqs-block) Main thread releasing lock. +- (mlfqs-block) ...got it. + (mlfqs-block) Block thread should have already acquired lock. + (mlfqs-block) end diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.output new file mode 100644 index 0000000..534463b --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.output @@ -0,0 +1,28 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-fair-2 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-fair-2': +(mlfqs-fair-2) begin +(mlfqs-fair-2) Starting 2 threads... +(mlfqs-fair-2) Starting threads took 7 ticks. +(mlfqs-fair-2) Sleeping 40 seconds to let threads run, please wait... +(mlfqs-fair-2) Thread 0 received 1501 ticks. +(mlfqs-fair-2) Thread 1 received 1501 ticks. +(mlfqs-fair-2) end +Execution of 'mlfqs-fair-2' complete. +Timer: 4059 ticks +Thread: 0 idle ticks, 4062 kernel ticks, 0 user ticks +Console: 627 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.result new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-2.result @@ -0,0 +1 @@ +PASS diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.output new file mode 100644 index 0000000..2e64b68 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.output @@ -0,0 +1,46 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-fair-20 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-fair-20': +(mlfqs-fair-20) begin +(mlfqs-fair-20) Starting 20 threads... +(mlfqs-fair-20) Starting threads took 46 ticks. +(mlfqs-fair-20) Sleeping 40 seconds to let threads run, please wait... +(mlfqs-fair-20) Thread 0 received 152 ticks. +(mlfqs-fair-20) Thread 1 received 149 ticks. +(mlfqs-fair-20) Thread 2 received 148 ticks. +(mlfqs-fair-20) Thread 3 received 149 ticks. +(mlfqs-fair-20) Thread 4 received 148 ticks. +(mlfqs-fair-20) Thread 5 received 148 ticks. +(mlfqs-fair-20) Thread 6 received 148 ticks. +(mlfqs-fair-20) Thread 7 received 149 ticks. +(mlfqs-fair-20) Thread 8 received 148 ticks. +(mlfqs-fair-20) Thread 9 received 148 ticks. +(mlfqs-fair-20) Thread 10 received 149 ticks. +(mlfqs-fair-20) Thread 11 received 152 ticks. +(mlfqs-fair-20) Thread 12 received 153 ticks. +(mlfqs-fair-20) Thread 13 received 152 ticks. +(mlfqs-fair-20) Thread 14 received 153 ticks. +(mlfqs-fair-20) Thread 15 received 153 ticks. +(mlfqs-fair-20) Thread 16 received 152 ticks. +(mlfqs-fair-20) Thread 17 received 153 ticks. +(mlfqs-fair-20) Thread 18 received 153 ticks. +(mlfqs-fair-20) Thread 19 received 153 ticks. +(mlfqs-fair-20) end +Execution of 'mlfqs-fair-20' complete. +Timer: 4186 ticks +Thread: 0 idle ticks, 4188 kernel ticks, 0 user ticks +Console: 1457 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.result new file mode 100644 index 0000000..7ef22e9 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-fair-20.result @@ -0,0 +1 @@ +PASS diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.output new file mode 100644 index 0000000..8147114 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.output @@ -0,0 +1,28 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-load-1 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-load-1': +(mlfqs-load-1) begin +(mlfqs-load-1) spinning for up to 45 seconds, please wait... +(mlfqs-load-1) FAIL: load average stayed below 0.5 for more than 45 seconds +Kernel PANIC at ../../tests/threads/tests.c:93 in fail(): test failed +Call stack: 0xc0027fee. +The `backtrace' program can make call stacks useful. +Read "Backtraces" in the "Debugging Tools" chapter +of the Pintos documentation for more information. +Timer: 4636 ticks +Thread: 0 idle ticks, 4636 kernel ticks, 0 user ticks +Console: 712 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.result new file mode 100644 index 0000000..53d0d2d --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-1.result @@ -0,0 +1,5 @@ +FAIL +Kernel panic in run: PANIC at ../../tests/threads/tests.c:93 in fail(): test failed +Call stack: 0xc0027fee +Translation of call stack: +0xc0027fee: debug_panic (.../../lib/kernel/debug.c:38) diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.output new file mode 100644 index 0000000..424bb97 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.output @@ -0,0 +1,115 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-load-60 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-load-60': +(mlfqs-load-60) begin +(mlfqs-load-60) Starting 60 niced load threads... +(mlfqs-load-60) Starting threads took 1 seconds. +(mlfqs-load-60) After 0 seconds, load average=0.00. +(mlfqs-load-60) After 2 seconds, load average=0.00. +(mlfqs-load-60) After 4 seconds, load average=0.00. +(mlfqs-load-60) After 6 seconds, load average=0.00. +(mlfqs-load-60) After 8 seconds, load average=0.00. +(mlfqs-load-60) After 10 seconds, load average=0.00. +(mlfqs-load-60) After 12 seconds, load average=0.00. +(mlfqs-load-60) After 14 seconds, load average=0.00. +(mlfqs-load-60) After 16 seconds, load average=0.00. +(mlfqs-load-60) After 18 seconds, load average=0.00. +(mlfqs-load-60) After 20 seconds, load average=0.00. +(mlfqs-load-60) After 22 seconds, load average=0.00. +(mlfqs-load-60) After 24 seconds, load average=0.00. +(mlfqs-load-60) After 26 seconds, load average=0.00. +(mlfqs-load-60) After 28 seconds, load average=0.00. +(mlfqs-load-60) After 30 seconds, load average=0.00. +(mlfqs-load-60) After 32 seconds, load average=0.00. +(mlfqs-load-60) After 34 seconds, load average=0.00. +(mlfqs-load-60) After 36 seconds, load average=0.00. +(mlfqs-load-60) After 38 seconds, load average=0.00. +(mlfqs-load-60) After 40 seconds, load average=0.00. +(mlfqs-load-60) After 42 seconds, load average=0.00. +(mlfqs-load-60) After 44 seconds, load average=0.00. +(mlfqs-load-60) After 46 seconds, load average=0.00. +(mlfqs-load-60) After 48 seconds, load average=0.00. +(mlfqs-load-60) After 50 seconds, load average=0.00. +(mlfqs-load-60) After 52 seconds, load average=0.00. +(mlfqs-load-60) After 54 seconds, load average=0.00. +(mlfqs-load-60) After 56 seconds, load average=0.00. +(mlfqs-load-60) After 58 seconds, load average=0.00. +(mlfqs-load-60) After 60 seconds, load average=0.00. +(mlfqs-load-60) After 62 seconds, load average=0.00. +(mlfqs-load-60) After 64 seconds, load average=0.00. +(mlfqs-load-60) After 66 seconds, load average=0.00. +(mlfqs-load-60) After 68 seconds, load average=0.00. +(mlfqs-load-60) After 70 seconds, load average=0.00. +(mlfqs-load-60) After 72 seconds, load average=0.00. +(mlfqs-load-60) After 74 seconds, load average=0.00. +(mlfqs-load-60) After 76 seconds, load average=0.00. +(mlfqs-load-60) After 78 seconds, load average=0.00. +(mlfqs-load-60) After 80 seconds, load average=0.00. +(mlfqs-load-60) After 82 seconds, load average=0.00. +(mlfqs-load-60) After 84 seconds, load average=0.00. +(mlfqs-load-60) After 86 seconds, load average=0.00. +(mlfqs-load-60) After 88 seconds, load average=0.00. +(mlfqs-load-60) After 90 seconds, load average=0.00. +(mlfqs-load-60) After 92 seconds, load average=0.00. +(mlfqs-load-60) After 94 seconds, load average=0.00. +(mlfqs-load-60) After 96 seconds, load average=0.00. +(mlfqs-load-60) After 98 seconds, load average=0.00. +(mlfqs-load-60) After 100 seconds, load average=0.00. +(mlfqs-load-60) After 102 seconds, load average=0.00. +(mlfqs-load-60) After 104 seconds, load average=0.00. +(mlfqs-load-60) After 106 seconds, load average=0.00. +(mlfqs-load-60) After 108 seconds, load average=0.00. +(mlfqs-load-60) After 110 seconds, load average=0.00. +(mlfqs-load-60) After 112 seconds, load average=0.00. +(mlfqs-load-60) After 114 seconds, load average=0.00. +(mlfqs-load-60) After 116 seconds, load average=0.00. +(mlfqs-load-60) After 118 seconds, load average=0.00. +(mlfqs-load-60) After 120 seconds, load average=0.00. +(mlfqs-load-60) After 122 seconds, load average=0.00. +(mlfqs-load-60) After 124 seconds, load average=0.00. +(mlfqs-load-60) After 126 seconds, load average=0.00. +(mlfqs-load-60) After 128 seconds, load average=0.00. +(mlfqs-load-60) After 130 seconds, load average=0.00. +(mlfqs-load-60) After 132 seconds, load average=0.00. +(mlfqs-load-60) After 134 seconds, load average=0.00. +(mlfqs-load-60) After 136 seconds, load average=0.00. +(mlfqs-load-60) After 138 seconds, load average=0.00. +(mlfqs-load-60) After 140 seconds, load average=0.00. +(mlfqs-load-60) After 142 seconds, load average=0.00. +(mlfqs-load-60) After 144 seconds, load average=0.00. +(mlfqs-load-60) After 146 seconds, load average=0.00. +(mlfqs-load-60) After 148 seconds, load average=0.00. +(mlfqs-load-60) After 150 seconds, load average=0.00. +(mlfqs-load-60) After 152 seconds, load average=0.00. +(mlfqs-load-60) After 154 seconds, load average=0.00. +(mlfqs-load-60) After 156 seconds, load average=0.00. +(mlfqs-load-60) After 158 seconds, load average=0.00. +(mlfqs-load-60) After 160 seconds, load average=0.00. +(mlfqs-load-60) After 162 seconds, load average=0.00. +(mlfqs-load-60) After 164 seconds, load average=0.00. +(mlfqs-load-60) After 166 seconds, load average=0.00. +(mlfqs-load-60) After 168 seconds, load average=0.00. +(mlfqs-load-60) After 170 seconds, load average=0.00. +(mlfqs-load-60) After 172 seconds, load average=0.00. +(mlfqs-load-60) After 174 seconds, load average=0.00. +(mlfqs-load-60) After 176 seconds, load average=0.00. +(mlfqs-load-60) After 178 seconds, load average=0.00. +(mlfqs-load-60) end +Execution of 'mlfqs-load-60' complete. +Timer: 18837 ticks +Thread: 0 idle ticks, 18839 kernel ticks, 0 user ticks +Console: 5295 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.result new file mode 100644 index 0000000..2a04b01 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-60.result @@ -0,0 +1,93 @@ +FAIL +Some load average values were missing or differed from those expected by more than 3.5. + time actual <-> expected explanation +------ -------- --- -------- ---------------------------------------- + 2 0.00 = 2.95 + 4 0.00 <<< 4.84 Too small, by 1.34. + 6 0.00 <<< 6.66 Too small, by 3.16. + 8 0.00 <<< 8.42 Too small, by 4.92. + 10 0.00 <<< 10.13 Too small, by 6.63. + 12 0.00 <<< 11.78 Too small, by 8.28. + 14 0.00 <<< 13.37 Too small, by 9.87. + 16 0.00 <<< 14.91 Too small, by 11.41. + 18 0.00 <<< 16.40 Too small, by 12.90. + 20 0.00 <<< 17.84 Too small, by 14.34. + 22 0.00 <<< 19.24 Too small, by 15.74. + 24 0.00 <<< 20.58 Too small, by 17.08. + 26 0.00 <<< 21.89 Too small, by 18.39. + 28 0.00 <<< 23.15 Too small, by 19.65. + 30 0.00 <<< 24.37 Too small, by 20.87. + 32 0.00 <<< 25.54 Too small, by 22.04. + 34 0.00 <<< 26.68 Too small, by 23.18. + 36 0.00 <<< 27.78 Too small, by 24.28. + 38 0.00 <<< 28.85 Too small, by 25.35. + 40 0.00 <<< 29.88 Too small, by 26.38. + 42 0.00 <<< 30.87 Too small, by 27.37. + 44 0.00 <<< 31.84 Too small, by 28.34. + 46 0.00 <<< 32.77 Too small, by 29.27. + 48 0.00 <<< 33.67 Too small, by 30.17. + 50 0.00 <<< 34.54 Too small, by 31.04. + 52 0.00 <<< 35.38 Too small, by 31.88. + 54 0.00 <<< 36.19 Too small, by 32.69. + 56 0.00 <<< 36.98 Too small, by 33.48. + 58 0.00 <<< 37.74 Too small, by 34.24. + 60 0.00 <<< 37.48 Too small, by 33.98. + 62 0.00 <<< 36.24 Too small, by 32.74. + 64 0.00 <<< 35.04 Too small, by 31.54. + 66 0.00 <<< 33.88 Too small, by 30.38. + 68 0.00 <<< 32.76 Too small, by 29.26. + 70 0.00 <<< 31.68 Too small, by 28.18. + 72 0.00 <<< 30.63 Too small, by 27.13. + 74 0.00 <<< 29.62 Too small, by 26.12. + 76 0.00 <<< 28.64 Too small, by 25.14. + 78 0.00 <<< 27.69 Too small, by 24.19. + 80 0.00 <<< 26.78 Too small, by 23.28. + 82 0.00 <<< 25.89 Too small, by 22.39. + 84 0.00 <<< 25.04 Too small, by 21.54. + 86 0.00 <<< 24.21 Too small, by 20.71. + 88 0.00 <<< 23.41 Too small, by 19.91. + 90 0.00 <<< 22.64 Too small, by 19.14. + 92 0.00 <<< 21.89 Too small, by 18.39. + 94 0.00 <<< 21.16 Too small, by 17.66. + 96 0.00 <<< 20.46 Too small, by 16.96. + 98 0.00 <<< 19.79 Too small, by 16.29. + 100 0.00 <<< 19.13 Too small, by 15.63. + 102 0.00 <<< 18.50 Too small, by 15.00. + 104 0.00 <<< 17.89 Too small, by 14.39. + 106 0.00 <<< 17.30 Too small, by 13.80. + 108 0.00 <<< 16.73 Too small, by 13.23. + 110 0.00 <<< 16.17 Too small, by 12.67. + 112 0.00 <<< 15.64 Too small, by 12.14. + 114 0.00 <<< 15.12 Too small, by 11.62. + 116 0.00 <<< 14.62 Too small, by 11.12. + 118 0.00 <<< 14.14 Too small, by 10.64. + 120 0.00 <<< 13.67 Too small, by 10.17. + 122 0.00 <<< 13.22 Too small, by 9.72. + 124 0.00 <<< 12.78 Too small, by 9.28. + 126 0.00 <<< 12.36 Too small, by 8.86. + 128 0.00 <<< 11.95 Too small, by 8.45. + 130 0.00 <<< 11.56 Too small, by 8.06. + 132 0.00 <<< 11.17 Too small, by 7.67. + 134 0.00 <<< 10.80 Too small, by 7.30. + 136 0.00 <<< 10.45 Too small, by 6.95. + 138 0.00 <<< 10.10 Too small, by 6.60. + 140 0.00 <<< 9.77 Too small, by 6.27. + 142 0.00 <<< 9.45 Too small, by 5.95. + 144 0.00 <<< 9.13 Too small, by 5.63. + 146 0.00 <<< 8.83 Too small, by 5.33. + 148 0.00 <<< 8.54 Too small, by 5.04. + 150 0.00 <<< 8.26 Too small, by 4.76. + 152 0.00 <<< 7.98 Too small, by 4.48. + 154 0.00 <<< 7.72 Too small, by 4.22. + 156 0.00 <<< 7.47 Too small, by 3.97. + 158 0.00 <<< 7.22 Too small, by 3.72. + 160 0.00 <<< 6.98 Too small, by 3.48. + 162 0.00 <<< 6.75 Too small, by 3.25. + 164 0.00 <<< 6.53 Too small, by 3.03. + 166 0.00 <<< 6.31 Too small, by 2.81. + 168 0.00 <<< 6.10 Too small, by 2.60. + 170 0.00 <<< 5.90 Too small, by 2.40. + 172 0.00 <<< 5.70 Too small, by 2.20. + 174 0.00 <<< 5.52 Too small, by 2.02. + 176 0.00 <<< 5.33 Too small, by 1.83. + 178 0.00 <<< 5.16 Too small, by 1.66. diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.output new file mode 100644 index 0000000..0cbee1c --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.output @@ -0,0 +1,115 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-load-avg +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-load-avg': +(mlfqs-load-avg) begin +(mlfqs-load-avg) Starting 60 load threads... +(mlfqs-load-avg) Starting threads took 1 seconds. +(mlfqs-load-avg) After 0 seconds, load average=0.00. +(mlfqs-load-avg) After 2 seconds, load average=0.00. +(mlfqs-load-avg) After 4 seconds, load average=0.00. +(mlfqs-load-avg) After 6 seconds, load average=0.00. +(mlfqs-load-avg) After 8 seconds, load average=0.00. +(mlfqs-load-avg) After 10 seconds, load average=0.00. +(mlfqs-load-avg) After 12 seconds, load average=0.00. +(mlfqs-load-avg) After 14 seconds, load average=0.00. +(mlfqs-load-avg) After 16 seconds, load average=0.00. +(mlfqs-load-avg) After 18 seconds, load average=0.00. +(mlfqs-load-avg) After 20 seconds, load average=0.00. +(mlfqs-load-avg) After 22 seconds, load average=0.00. +(mlfqs-load-avg) After 24 seconds, load average=0.00. +(mlfqs-load-avg) After 26 seconds, load average=0.00. +(mlfqs-load-avg) After 28 seconds, load average=0.00. +(mlfqs-load-avg) After 30 seconds, load average=0.00. +(mlfqs-load-avg) After 32 seconds, load average=0.00. +(mlfqs-load-avg) After 34 seconds, load average=0.00. +(mlfqs-load-avg) After 36 seconds, load average=0.00. +(mlfqs-load-avg) After 38 seconds, load average=0.00. +(mlfqs-load-avg) After 40 seconds, load average=0.00. +(mlfqs-load-avg) After 42 seconds, load average=0.00. +(mlfqs-load-avg) After 44 seconds, load average=0.00. +(mlfqs-load-avg) After 46 seconds, load average=0.00. +(mlfqs-load-avg) After 48 seconds, load average=0.00. +(mlfqs-load-avg) After 50 seconds, load average=0.00. +(mlfqs-load-avg) After 52 seconds, load average=0.00. +(mlfqs-load-avg) After 54 seconds, load average=0.00. +(mlfqs-load-avg) After 56 seconds, load average=0.00. +(mlfqs-load-avg) After 58 seconds, load average=0.00. +(mlfqs-load-avg) After 60 seconds, load average=0.00. +(mlfqs-load-avg) After 62 seconds, load average=0.00. +(mlfqs-load-avg) After 64 seconds, load average=0.00. +(mlfqs-load-avg) After 66 seconds, load average=0.00. +(mlfqs-load-avg) After 68 seconds, load average=0.00. +(mlfqs-load-avg) After 70 seconds, load average=0.00. +(mlfqs-load-avg) After 72 seconds, load average=0.00. +(mlfqs-load-avg) After 74 seconds, load average=0.00. +(mlfqs-load-avg) After 76 seconds, load average=0.00. +(mlfqs-load-avg) After 78 seconds, load average=0.00. +(mlfqs-load-avg) After 80 seconds, load average=0.00. +(mlfqs-load-avg) After 82 seconds, load average=0.00. +(mlfqs-load-avg) After 84 seconds, load average=0.00. +(mlfqs-load-avg) After 86 seconds, load average=0.00. +(mlfqs-load-avg) After 88 seconds, load average=0.00. +(mlfqs-load-avg) After 90 seconds, load average=0.00. +(mlfqs-load-avg) After 92 seconds, load average=0.00. +(mlfqs-load-avg) After 94 seconds, load average=0.00. +(mlfqs-load-avg) After 96 seconds, load average=0.00. +(mlfqs-load-avg) After 98 seconds, load average=0.00. +(mlfqs-load-avg) After 100 seconds, load average=0.00. +(mlfqs-load-avg) After 102 seconds, load average=0.00. +(mlfqs-load-avg) After 104 seconds, load average=0.00. +(mlfqs-load-avg) After 106 seconds, load average=0.00. +(mlfqs-load-avg) After 108 seconds, load average=0.00. +(mlfqs-load-avg) After 110 seconds, load average=0.00. +(mlfqs-load-avg) After 112 seconds, load average=0.00. +(mlfqs-load-avg) After 114 seconds, load average=0.00. +(mlfqs-load-avg) After 116 seconds, load average=0.00. +(mlfqs-load-avg) After 118 seconds, load average=0.00. +(mlfqs-load-avg) After 120 seconds, load average=0.00. +(mlfqs-load-avg) After 122 seconds, load average=0.00. +(mlfqs-load-avg) After 124 seconds, load average=0.00. +(mlfqs-load-avg) After 126 seconds, load average=0.00. +(mlfqs-load-avg) After 128 seconds, load average=0.00. +(mlfqs-load-avg) After 130 seconds, load average=0.00. +(mlfqs-load-avg) After 132 seconds, load average=0.00. +(mlfqs-load-avg) After 134 seconds, load average=0.00. +(mlfqs-load-avg) After 136 seconds, load average=0.00. +(mlfqs-load-avg) After 138 seconds, load average=0.00. +(mlfqs-load-avg) After 140 seconds, load average=0.00. +(mlfqs-load-avg) After 142 seconds, load average=0.00. +(mlfqs-load-avg) After 144 seconds, load average=0.00. +(mlfqs-load-avg) After 146 seconds, load average=0.00. +(mlfqs-load-avg) After 148 seconds, load average=0.00. +(mlfqs-load-avg) After 150 seconds, load average=0.00. +(mlfqs-load-avg) After 152 seconds, load average=0.00. +(mlfqs-load-avg) After 154 seconds, load average=0.00. +(mlfqs-load-avg) After 156 seconds, load average=0.00. +(mlfqs-load-avg) After 158 seconds, load average=0.00. +(mlfqs-load-avg) After 160 seconds, load average=0.00. +(mlfqs-load-avg) After 162 seconds, load average=0.00. +(mlfqs-load-avg) After 164 seconds, load average=0.00. +(mlfqs-load-avg) After 166 seconds, load average=0.00. +(mlfqs-load-avg) After 168 seconds, load average=0.00. +(mlfqs-load-avg) After 170 seconds, load average=0.00. +(mlfqs-load-avg) After 172 seconds, load average=0.00. +(mlfqs-load-avg) After 174 seconds, load average=0.00. +(mlfqs-load-avg) After 176 seconds, load average=0.00. +(mlfqs-load-avg) After 178 seconds, load average=0.00. +(mlfqs-load-avg) end +Execution of 'mlfqs-load-avg' complete. +Timer: 18837 ticks +Thread: 0 idle ticks, 18839 kernel ticks, 0 user ticks +Console: 5386 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.result new file mode 100644 index 0000000..74730bc --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-load-avg.result @@ -0,0 +1,93 @@ +FAIL +Some load average values were missing or differed from those expected by more than 2.5. + time actual <-> expected explanation +------ -------- --- -------- ---------------------------------------- + 2 0.00 = 0.05 + 4 0.00 = 0.16 + 6 0.00 = 0.34 + 8 0.00 = 0.58 + 10 0.00 = 0.87 + 12 0.00 = 1.22 + 14 0.00 = 1.63 + 16 0.00 = 2.09 + 18 0.00 <<< 2.60 Too small, by 0.10. + 20 0.00 <<< 3.16 Too small, by 0.66. + 22 0.00 <<< 3.76 Too small, by 1.26. + 24 0.00 <<< 4.42 Too small, by 1.92. + 26 0.00 <<< 5.11 Too small, by 2.61. + 28 0.00 <<< 5.85 Too small, by 3.35. + 30 0.00 <<< 6.63 Too small, by 4.13. + 32 0.00 <<< 7.46 Too small, by 4.96. + 34 0.00 <<< 8.32 Too small, by 5.82. + 36 0.00 <<< 9.22 Too small, by 6.72. + 38 0.00 <<< 10.15 Too small, by 7.65. + 40 0.00 <<< 11.12 Too small, by 8.62. + 42 0.00 <<< 12.13 Too small, by 9.63. + 44 0.00 <<< 13.16 Too small, by 10.66. + 46 0.00 <<< 14.23 Too small, by 11.73. + 48 0.00 <<< 15.33 Too small, by 12.83. + 50 0.00 <<< 16.46 Too small, by 13.96. + 52 0.00 <<< 17.62 Too small, by 15.12. + 54 0.00 <<< 18.81 Too small, by 16.31. + 56 0.00 <<< 20.02 Too small, by 17.52. + 58 0.00 <<< 21.26 Too small, by 18.76. + 60 0.00 <<< 22.52 Too small, by 20.02. + 62 0.00 <<< 23.71 Too small, by 21.21. + 64 0.00 <<< 24.80 Too small, by 22.30. + 66 0.00 <<< 25.78 Too small, by 23.28. + 68 0.00 <<< 26.66 Too small, by 24.16. + 70 0.00 <<< 27.45 Too small, by 24.95. + 72 0.00 <<< 28.14 Too small, by 25.64. + 74 0.00 <<< 28.75 Too small, by 26.25. + 76 0.00 <<< 29.27 Too small, by 26.77. + 78 0.00 <<< 29.71 Too small, by 27.21. + 80 0.00 <<< 30.06 Too small, by 27.56. + 82 0.00 <<< 30.34 Too small, by 27.84. + 84 0.00 <<< 30.55 Too small, by 28.05. + 86 0.00 <<< 30.68 Too small, by 28.18. + 88 0.00 <<< 30.74 Too small, by 28.24. + 90 0.00 <<< 30.73 Too small, by 28.23. + 92 0.00 <<< 30.66 Too small, by 28.16. + 94 0.00 <<< 30.52 Too small, by 28.02. + 96 0.00 <<< 30.32 Too small, by 27.82. + 98 0.00 <<< 30.06 Too small, by 27.56. + 100 0.00 <<< 29.74 Too small, by 27.24. + 102 0.00 <<< 29.37 Too small, by 26.87. + 104 0.00 <<< 28.95 Too small, by 26.45. + 106 0.00 <<< 28.47 Too small, by 25.97. + 108 0.00 <<< 27.94 Too small, by 25.44. + 110 0.00 <<< 27.36 Too small, by 24.86. + 112 0.00 <<< 26.74 Too small, by 24.24. + 114 0.00 <<< 26.07 Too small, by 23.57. + 116 0.00 <<< 25.36 Too small, by 22.86. + 118 0.00 <<< 24.60 Too small, by 22.10. + 120 0.00 <<< 23.81 Too small, by 21.31. + 122 0.00 <<< 23.02 Too small, by 20.52. + 124 0.00 <<< 22.26 Too small, by 19.76. + 126 0.00 <<< 21.52 Too small, by 19.02. + 128 0.00 <<< 20.81 Too small, by 18.31. + 130 0.00 <<< 20.12 Too small, by 17.62. + 132 0.00 <<< 19.46 Too small, by 16.96. + 134 0.00 <<< 18.81 Too small, by 16.31. + 136 0.00 <<< 18.19 Too small, by 15.69. + 138 0.00 <<< 17.59 Too small, by 15.09. + 140 0.00 <<< 17.01 Too small, by 14.51. + 142 0.00 <<< 16.45 Too small, by 13.95. + 144 0.00 <<< 15.90 Too small, by 13.40. + 146 0.00 <<< 15.38 Too small, by 12.88. + 148 0.00 <<< 14.87 Too small, by 12.37. + 150 0.00 <<< 14.38 Too small, by 11.88. + 152 0.00 <<< 13.90 Too small, by 11.40. + 154 0.00 <<< 13.44 Too small, by 10.94. + 156 0.00 <<< 13.00 Too small, by 10.50. + 158 0.00 <<< 12.57 Too small, by 10.07. + 160 0.00 <<< 12.15 Too small, by 9.65. + 162 0.00 <<< 11.75 Too small, by 9.25. + 164 0.00 <<< 11.36 Too small, by 8.86. + 166 0.00 <<< 10.99 Too small, by 8.49. + 168 0.00 <<< 10.62 Too small, by 8.12. + 170 0.00 <<< 10.27 Too small, by 7.77. + 172 0.00 <<< 9.93 Too small, by 7.43. + 174 0.00 <<< 9.61 Too small, by 7.11. + 176 0.00 <<< 9.29 Too small, by 6.79. + 178 0.00 <<< 8.98 Too small, by 6.48. diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.output new file mode 100644 index 0000000..d6718bd --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.output @@ -0,0 +1,36 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-nice-10 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-nice-10': +(mlfqs-nice-10) begin +(mlfqs-nice-10) Starting 10 threads... +(mlfqs-nice-10) Starting threads took 24 ticks. +(mlfqs-nice-10) Sleeping 40 seconds to let threads run, please wait... +(mlfqs-nice-10) Thread 0 received 300 ticks. +(mlfqs-nice-10) Thread 1 received 300 ticks. +(mlfqs-nice-10) Thread 2 received 301 ticks. +(mlfqs-nice-10) Thread 3 received 301 ticks. +(mlfqs-nice-10) Thread 4 received 301 ticks. +(mlfqs-nice-10) Thread 5 received 300 ticks. +(mlfqs-nice-10) Thread 6 received 300 ticks. +(mlfqs-nice-10) Thread 7 received 300 ticks. +(mlfqs-nice-10) Thread 8 received 301 ticks. +(mlfqs-nice-10) Thread 9 received 300 ticks. +(mlfqs-nice-10) end +Execution of 'mlfqs-nice-10' complete. +Timer: 4114 ticks +Thread: 0 idle ticks, 4116 kernel ticks, 0 user ticks +Console: 997 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.result new file mode 100644 index 0000000..9f389ca --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-10.result @@ -0,0 +1,14 @@ +FAIL +Some tick counts were missing or differed from those expected by more than 25. +thread actual <-> expected explanation +------ -------- --- -------- ---------------------------------------- + 0 300 <<< 672 Too small, by 347. + 1 300 <<< 588 Too small, by 263. + 2 301 <<< 492 Too small, by 166. + 3 301 <<< 408 Too small, by 82. + 4 301 = 316 + 5 300 >>> 232 Too big, by 43. + 6 300 >>> 152 Too big, by 123. + 7 300 >>> 92 Too big, by 183. + 8 301 >>> 40 Too big, by 236. + 9 300 >>> 8 Too big, by 267. diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.output new file mode 100644 index 0000000..c05e416 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.output @@ -0,0 +1,28 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-nice-2 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-nice-2': +(mlfqs-nice-2) begin +(mlfqs-nice-2) Starting 2 threads... +(mlfqs-nice-2) Starting threads took 7 ticks. +(mlfqs-nice-2) Sleeping 40 seconds to let threads run, please wait... +(mlfqs-nice-2) Thread 0 received 1501 ticks. +(mlfqs-nice-2) Thread 1 received 1501 ticks. +(mlfqs-nice-2) end +Execution of 'mlfqs-nice-2' complete. +Timer: 4059 ticks +Thread: 0 idle ticks, 4062 kernel ticks, 0 user ticks +Console: 627 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.result new file mode 100644 index 0000000..a5be8f0 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-nice-2.result @@ -0,0 +1,6 @@ +FAIL +Some tick counts were missing or differed from those expected by more than 50. +thread actual <-> expected explanation +------ -------- --- -------- ---------------------------------------- + 0 1501 <<< 1904 Too small, by 353. + 1 1501 >>> 1096 Too big, by 355. diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.errors b/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.output b/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.output new file mode 100644 index 0000000..3f0fd82 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.output @@ -0,0 +1,114 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q -mlfqs run mlfqs-recent-1 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'mlfqs-recent-1': +(mlfqs-recent-1) begin +(mlfqs-recent-1) Sleeping 10 seconds to allow recent_cpu to decay, please wait... +(mlfqs-recent-1) After 2 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 4 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 6 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 8 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 10 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 12 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 14 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 16 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 18 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 20 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 22 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 24 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 26 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 28 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 30 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 32 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 34 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 36 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 38 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 40 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 42 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 44 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 46 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 48 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 50 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 52 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 54 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 56 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 58 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 60 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 62 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 64 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 66 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 68 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 70 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 72 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 74 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 76 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 78 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 80 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 82 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 84 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 86 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 88 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 90 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 92 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 94 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 96 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 98 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 100 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 102 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 104 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 106 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 108 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 110 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 112 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 114 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 116 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 118 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 120 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 122 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 124 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 126 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 128 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 130 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 132 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 134 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 136 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 138 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 140 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 142 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 144 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 146 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 148 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 150 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 152 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 154 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 156 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 158 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 160 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 162 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 164 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 166 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 168 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 170 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 172 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 174 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 176 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 178 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) After 180 seconds, recent_cpu is 0.00, load_avg is 0.00. +(mlfqs-recent-1) end +Execution of 'mlfqs-recent-1' complete. +Timer: 19014 ticks +Thread: 0 idle ticks, 19017 kernel ticks, 0 user ticks +Console: 7085 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.result b/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.result new file mode 100644 index 0000000..ba8374e --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/mlfqs-recent-1.result @@ -0,0 +1,93 @@ +FAIL +Some recent_cpu values were missing or differed from those expected by more than 2.5. + time actual <-> expected explanation +------ -------- --- -------- ---------------------------------------- + 2 0.00 <<< 6.40 Too small, by 3.90. + 4 0.00 <<< 12.60 Too small, by 10.10. + 6 0.00 <<< 18.61 Too small, by 16.11. + 8 0.00 <<< 24.44 Too small, by 21.94. + 10 0.00 <<< 30.08 Too small, by 27.58. + 12 0.00 <<< 35.54 Too small, by 33.04. + 14 0.00 <<< 40.83 Too small, by 38.33. + 16 0.00 <<< 45.96 Too small, by 43.46. + 18 0.00 <<< 50.92 Too small, by 48.42. + 20 0.00 <<< 55.73 Too small, by 53.23. + 22 0.00 <<< 60.39 Too small, by 57.89. + 24 0.00 <<< 64.90 Too small, by 62.40. + 26 0.00 <<< 69.27 Too small, by 66.77. + 28 0.00 <<< 73.50 Too small, by 71.00. + 30 0.00 <<< 77.60 Too small, by 75.10. + 32 0.00 <<< 81.56 Too small, by 79.06. + 34 0.00 <<< 85.40 Too small, by 82.90. + 36 0.00 <<< 89.12 Too small, by 86.62. + 38 0.00 <<< 92.72 Too small, by 90.22. + 40 0.00 <<< 96.20 Too small, by 93.70. + 42 0.00 <<< 99.57 Too small, by 97.07. + 44 0.00 <<< 102.84 Too small, by 100.34. + 46 0.00 <<< 106.00 Too small, by 103.50. + 48 0.00 <<< 109.06 Too small, by 106.56. + 50 0.00 <<< 112.02 Too small, by 109.52. + 52 0.00 <<< 114.89 Too small, by 112.39. + 54 0.00 <<< 117.66 Too small, by 115.16. + 56 0.00 <<< 120.34 Too small, by 117.84. + 58 0.00 <<< 122.94 Too small, by 120.44. + 60 0.00 <<< 125.46 Too small, by 122.96. + 62 0.00 <<< 127.89 Too small, by 125.39. + 64 0.00 <<< 130.25 Too small, by 127.75. + 66 0.00 <<< 132.53 Too small, by 130.03. + 68 0.00 <<< 134.73 Too small, by 132.23. + 70 0.00 <<< 136.86 Too small, by 134.36. + 72 0.00 <<< 138.93 Too small, by 136.43. + 74 0.00 <<< 140.93 Too small, by 138.43. + 76 0.00 <<< 142.86 Too small, by 140.36. + 78 0.00 <<< 144.73 Too small, by 142.23. + 80 0.00 <<< 146.54 Too small, by 144.04. + 82 0.00 <<< 148.29 Too small, by 145.79. + 84 0.00 <<< 149.99 Too small, by 147.49. + 86 0.00 <<< 151.63 Too small, by 149.13. + 88 0.00 <<< 153.21 Too small, by 150.71. + 90 0.00 <<< 154.75 Too small, by 152.25. + 92 0.00 <<< 156.23 Too small, by 153.73. + 94 0.00 <<< 157.67 Too small, by 155.17. + 96 0.00 <<< 159.06 Too small, by 156.56. + 98 0.00 <<< 160.40 Too small, by 157.90. + 100 0.00 <<< 161.70 Too small, by 159.20. + 102 0.00 <<< 162.96 Too small, by 160.46. + 104 0.00 <<< 164.18 Too small, by 161.68. + 106 0.00 <<< 165.35 Too small, by 162.85. + 108 0.00 <<< 166.49 Too small, by 163.99. + 110 0.00 <<< 167.59 Too small, by 165.09. + 112 0.00 <<< 168.66 Too small, by 166.16. + 114 0.00 <<< 169.69 Too small, by 167.19. + 116 0.00 <<< 170.69 Too small, by 168.19. + 118 0.00 <<< 171.65 Too small, by 169.15. + 120 0.00 <<< 172.58 Too small, by 170.08. + 122 0.00 <<< 173.49 Too small, by 170.99. + 124 0.00 <<< 174.36 Too small, by 171.86. + 126 0.00 <<< 175.20 Too small, by 172.70. + 128 0.00 <<< 176.02 Too small, by 173.52. + 130 0.00 <<< 176.81 Too small, by 174.31. + 132 0.00 <<< 177.57 Too small, by 175.07. + 134 0.00 <<< 178.31 Too small, by 175.81. + 136 0.00 <<< 179.02 Too small, by 176.52. + 138 0.00 <<< 179.72 Too small, by 177.22. + 140 0.00 <<< 180.38 Too small, by 177.88. + 142 0.00 <<< 181.03 Too small, by 178.53. + 144 0.00 <<< 181.65 Too small, by 179.15. + 146 0.00 <<< 182.26 Too small, by 179.76. + 148 0.00 <<< 182.84 Too small, by 180.34. + 150 0.00 <<< 183.41 Too small, by 180.91. + 152 0.00 <<< 183.96 Too small, by 181.46. + 154 0.00 <<< 184.49 Too small, by 181.99. + 156 0.00 <<< 185.00 Too small, by 182.50. + 158 0.00 <<< 185.49 Too small, by 182.99. + 160 0.00 <<< 185.97 Too small, by 183.47. + 162 0.00 <<< 186.43 Too small, by 183.93. + 164 0.00 <<< 186.88 Too small, by 184.38. + 166 0.00 <<< 187.31 Too small, by 184.81. + 168 0.00 <<< 187.73 Too small, by 185.23. + 170 0.00 <<< 188.14 Too small, by 185.64. + 172 0.00 <<< 188.53 Too small, by 186.03. + 174 0.00 <<< 188.91 Too small, by 186.41. + 176 0.00 <<< 189.27 Too small, by 186.77. + 178 0.00 <<< 189.63 Too small, by 187.13. diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-change.errors b/pintos-env/pintos/threads/build/tests/threads/priority-change.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-change.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-change.output b/pintos-env/pintos/threads/build/tests/threads/priority-change.output new file mode 100644 index 0000000..afbe50b --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-change.output @@ -0,0 +1,26 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-change +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-change': +(priority-change) begin +(priority-change) Creating a high-priority thread 2. +(priority-change) Thread 2 should have just lowered its priority. +(priority-change) Thread 2 should have just exited. +(priority-change) end +Execution of 'priority-change' complete. +Timer: 51 ticks +Thread: 0 idle ticks, 53 kernel ticks, 0 user ticks +Console: 559 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-change.result b/pintos-env/pintos/threads/build/tests/threads/priority-change.result new file mode 100644 index 0000000..91f3f2e --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-change.result @@ -0,0 +1,19 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-change) begin + (priority-change) Creating a high-priority thread 2. + (priority-change) Thread 2 now lowering priority. + (priority-change) Thread 2 should have just lowered its priority. + (priority-change) Thread 2 exiting. + (priority-change) Thread 2 should have just exited. + (priority-change) end +Differences in `diff -u' format: + (priority-change) begin + (priority-change) Creating a high-priority thread 2. +- (priority-change) Thread 2 now lowering priority. + (priority-change) Thread 2 should have just lowered its priority. +- (priority-change) Thread 2 exiting. + (priority-change) Thread 2 should have just exited. + (priority-change) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-condvar.errors b/pintos-env/pintos/threads/build/tests/threads/priority-condvar.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-condvar.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-condvar.output b/pintos-env/pintos/threads/build/tests/threads/priority-condvar.output new file mode 100644 index 0000000..aac68f3 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-condvar.output @@ -0,0 +1,43 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-condvar +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-condvar': +(priority-condvar) begin +(priority-condvar) Thread priority 23 starting. +(priority-condvar) Thread priority 22 starting. +(priority-condvar) Thread priority 30 starting. +(priority-condvar) Thread priority 28 starting. +(priority-condvar) Thread priority 27 starting. +(priority-condvar) Thread priority 25 starting. +(priority-condvar) Thread priority 24 starting. +(priority-condvar) Thread priority 29 starting. +(priority-condvar) Thread priority 26 starting. +(priority-condvar) Thread priority 21 starting. +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) Signaling... +(priority-condvar) end +Execution of 'priority-condvar' complete. +Timer: 138 ticks +Thread: 0 idle ticks, 140 kernel ticks, 0 user ticks +Console: 1195 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-condvar.result b/pintos-env/pintos/threads/build/tests/threads/priority-condvar.result new file mode 100644 index 0000000..a96628d --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-condvar.result @@ -0,0 +1,72 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-condvar) begin + (priority-condvar) Thread priority 23 starting. + (priority-condvar) Thread priority 22 starting. + (priority-condvar) Thread priority 21 starting. + (priority-condvar) Thread priority 30 starting. + (priority-condvar) Thread priority 29 starting. + (priority-condvar) Thread priority 28 starting. + (priority-condvar) Thread priority 27 starting. + (priority-condvar) Thread priority 26 starting. + (priority-condvar) Thread priority 25 starting. + (priority-condvar) Thread priority 24 starting. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 30 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 29 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 28 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 27 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 26 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 25 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 24 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 23 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 22 woke up. + (priority-condvar) Signaling... + (priority-condvar) Thread priority 21 woke up. + (priority-condvar) end +Differences in `diff -u' format: + (priority-condvar) begin + (priority-condvar) Thread priority 23 starting. + (priority-condvar) Thread priority 22 starting. +- (priority-condvar) Thread priority 21 starting. + (priority-condvar) Thread priority 30 starting. +- (priority-condvar) Thread priority 29 starting. + (priority-condvar) Thread priority 28 starting. + (priority-condvar) Thread priority 27 starting. +- (priority-condvar) Thread priority 26 starting. + (priority-condvar) Thread priority 25 starting. + (priority-condvar) Thread priority 24 starting. ++ (priority-condvar) Thread priority 29 starting. ++ (priority-condvar) Thread priority 26 starting. ++ (priority-condvar) Thread priority 21 starting. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 30 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 29 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 28 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 27 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 26 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 25 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 24 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 23 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 22 woke up. + (priority-condvar) Signaling... +- (priority-condvar) Thread priority 21 woke up. + (priority-condvar) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.errors b/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.output b/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.output new file mode 100644 index 0000000..93e9750 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.output @@ -0,0 +1,37 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-donate-chain +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-donate-chain': +(priority-donate-chain) begin +(priority-donate-chain) main got lock. +(priority-donate-chain) main should have priority 3. Actual priority: 0. +(priority-donate-chain) main should have priority 6. Actual priority: 0. +(priority-donate-chain) interloper 1 finished. +(priority-donate-chain) interloper 2 finished. +(priority-donate-chain) main should have priority 9. Actual priority: 0. +(priority-donate-chain) main should have priority 12. Actual priority: 0. +(priority-donate-chain) interloper 3 finished. +(priority-donate-chain) interloper 4 finished. +(priority-donate-chain) main should have priority 15. Actual priority: 0. +(priority-donate-chain) interloper 5 finished. +(priority-donate-chain) main should have priority 18. Actual priority: 0. +(priority-donate-chain) main should have priority 21. Actual priority: 0. +(priority-donate-chain) main finishing with priority 0. +(priority-donate-chain) end +Execution of 'priority-donate-chain' complete. +Timer: 148 ticks +Thread: 0 idle ticks, 151 kernel ticks, 0 user ticks +Console: 1272 characters output +Keyboard: 0 keys pressed +Powering off...======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.result b/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.result new file mode 100644 index 0000000..5b9a568 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-chain.result @@ -0,0 +1,94 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-donate-chain) begin + (priority-donate-chain) main got lock. + (priority-donate-chain) main should have priority 3. Actual priority: 3. + (priority-donate-chain) main should have priority 6. Actual priority: 6. + (priority-donate-chain) main should have priority 9. Actual priority: 9. + (priority-donate-chain) main should have priority 12. Actual priority: 12. + (priority-donate-chain) main should have priority 15. Actual priority: 15. + (priority-donate-chain) main should have priority 18. Actual priority: 18. + (priority-donate-chain) main should have priority 21. Actual priority: 21. + (priority-donate-chain) thread 1 got lock + (priority-donate-chain) thread 1 should have priority 21. Actual priority: 21 + (priority-donate-chain) thread 2 got lock + (priority-donate-chain) thread 2 should have priority 21. Actual priority: 21 + (priority-donate-chain) thread 3 got lock + (priority-donate-chain) thread 3 should have priority 21. Actual priority: 21 + (priority-donate-chain) thread 4 got lock + (priority-donate-chain) thread 4 should have priority 21. Actual priority: 21 + (priority-donate-chain) thread 5 got lock + (priority-donate-chain) thread 5 should have priority 21. Actual priority: 21 + (priority-donate-chain) thread 6 got lock + (priority-donate-chain) thread 6 should have priority 21. Actual priority: 21 + (priority-donate-chain) thread 7 got lock + (priority-donate-chain) thread 7 should have priority 21. Actual priority: 21 + (priority-donate-chain) thread 7 finishing with priority 21. + (priority-donate-chain) interloper 7 finished. + (priority-donate-chain) thread 6 finishing with priority 18. + (priority-donate-chain) interloper 6 finished. + (priority-donate-chain) thread 5 finishing with priority 15. + (priority-donate-chain) interloper 5 finished. + (priority-donate-chain) thread 4 finishing with priority 12. + (priority-donate-chain) interloper 4 finished. + (priority-donate-chain) thread 3 finishing with priority 9. + (priority-donate-chain) interloper 3 finished. + (priority-donate-chain) thread 2 finishing with priority 6. + (priority-donate-chain) interloper 2 finished. + (priority-donate-chain) thread 1 finishing with priority 3. + (priority-donate-chain) interloper 1 finished. + (priority-donate-chain) main finishing with priority 0. + (priority-donate-chain) end +Differences in `diff -u' format: + (priority-donate-chain) begin + (priority-donate-chain) main got lock. +- (priority-donate-chain) main should have priority 3. Actual priority: 3. +- (priority-donate-chain) main should have priority 6. Actual priority: 6. +- (priority-donate-chain) main should have priority 9. Actual priority: 9. +- (priority-donate-chain) main should have priority 12. Actual priority: 12. +- (priority-donate-chain) main should have priority 15. Actual priority: 15. +- (priority-donate-chain) main should have priority 18. Actual priority: 18. +- (priority-donate-chain) main should have priority 21. Actual priority: 21. +- (priority-donate-chain) thread 1 got lock +- (priority-donate-chain) thread 1 should have priority 21. Actual priority: 21 +- (priority-donate-chain) thread 2 got lock +- (priority-donate-chain) thread 2 should have priority 21. Actual priority: 21 +- (priority-donate-chain) thread 3 got lock +- (priority-donate-chain) thread 3 should have priority 21. Actual priority: 21 +- (priority-donate-chain) thread 4 got lock +- (priority-donate-chain) thread 4 should have priority 21. Actual priority: 21 +- (priority-donate-chain) thread 5 got lock +- (priority-donate-chain) thread 5 should have priority 21. Actual priority: 21 +- (priority-donate-chain) thread 6 got lock +- (priority-donate-chain) thread 6 should have priority 21. Actual priority: 21 +- (priority-donate-chain) thread 7 got lock +- (priority-donate-chain) thread 7 should have priority 21. Actual priority: 21 +- (priority-donate-chain) thread 7 finishing with priority 21. +- (priority-donate-chain) interloper 7 finished. +- (priority-donate-chain) thread 6 finishing with priority 18. +- (priority-donate-chain) interloper 6 finished. +- (priority-donate-chain) thread 5 finishing with priority 15. +- (priority-donate-chain) interloper 5 finished. +- (priority-donate-chain) thread 4 finishing with priority 12. +- (priority-donate-chain) interloper 4 finished. +- (priority-donate-chain) thread 3 finishing with priority 9. +- (priority-donate-chain) interloper 3 finished. +- (priority-donate-chain) thread 2 finishing with priority 6. +- (priority-donate-chain) interloper 2 finished. +- (priority-donate-chain) thread 1 finishing with priority 3. ++ (priority-donate-chain) main should have priority 3. Actual priority: 0. ++ (priority-donate-chain) main should have priority 6. Actual priority: 0. + (priority-donate-chain) interloper 1 finished. ++ (priority-donate-chain) interloper 2 finished. ++ (priority-donate-chain) main should have priority 9. Actual priority: 0. ++ (priority-donate-chain) main should have priority 12. Actual priority: 0. ++ (priority-donate-chain) interloper 3 finished. ++ (priority-donate-chain) interloper 4 finished. ++ (priority-donate-chain) main should have priority 15. Actual priority: 0. ++ (priority-donate-chain) interloper 5 finished. ++ (priority-donate-chain) main should have priority 18. Actual priority: 0. ++ (priority-donate-chain) main should have priority 21. Actual priority: 0. + (priority-donate-chain) main finishing with priority 0. + (priority-donate-chain) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.errors b/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.output b/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.output new file mode 100644 index 0000000..5db3b8d --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.output @@ -0,0 +1,28 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-donate-lower +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-donate-lower': +(priority-donate-lower) begin +(priority-donate-lower) Main thread should have priority 41. Actual priority: 31. +(priority-donate-lower) Lowering base priority... +(priority-donate-lower) Main thread should have priority 41. Actual priority: 21. +(priority-donate-lower) acquire must already have finished. +(priority-donate-lower) Main thread should have priority 21. Actual priority: 21. +(priority-donate-lower) end +Execution of 'priority-donate-lower' complete. +Timer: 72 ticks +Thread: 0 idle ticks, 75 kernel ticks, 0 user ticks +Console: 777 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.result b/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.result new file mode 100644 index 0000000..174eb8f --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-lower.result @@ -0,0 +1,25 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-donate-lower) begin + (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. + (priority-donate-lower) Lowering base priority... + (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. + (priority-donate-lower) acquire: got the lock + (priority-donate-lower) acquire: done + (priority-donate-lower) acquire must already have finished. + (priority-donate-lower) Main thread should have priority 21. Actual priority: 21. + (priority-donate-lower) end +Differences in `diff -u' format: + (priority-donate-lower) begin +- (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. ++ (priority-donate-lower) Main thread should have priority 41. Actual priority: 31. + (priority-donate-lower) Lowering base priority... +- (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. +- (priority-donate-lower) acquire: got the lock +- (priority-donate-lower) acquire: done ++ (priority-donate-lower) Main thread should have priority 41. Actual priority: 21. + (priority-donate-lower) acquire must already have finished. + (priority-donate-lower) Main thread should have priority 21. Actual priority: 21. + (priority-donate-lower) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.errors b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.output b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.output new file mode 100644 index 0000000..6517df1 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.output @@ -0,0 +1,29 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-donate-multiple +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-donate-multiple': +(priority-donate-multiple) begin +(priority-donate-multiple) Main thread should have priority 32. Actual priority: 31. +(priority-donate-multiple) Main thread should have priority 33. Actual priority: 31. +(priority-donate-multiple) Thread b should have just finished. +(priority-donate-multiple) Main thread should have priority 32. Actual priority: 31. +(priority-donate-multiple) Thread a should have just finished. +(priority-donate-multiple) Main thread should have priority 31. Actual priority: 31. +(priority-donate-multiple) end +Execution of 'priority-donate-multiple' complete. +Timer: 86 ticks +Thread: 0 idle ticks, 89 kernel ticks, 0 user ticks +Console: 903 characters output +Keyboard: 0 keys pressed +Powering off...======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.result b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.result new file mode 100644 index 0000000..3c71275 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple.result @@ -0,0 +1,32 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-donate-multiple) begin + (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. + (priority-donate-multiple) Main thread should have priority 33. Actual priority: 33. + (priority-donate-multiple) Thread b acquired lock b. + (priority-donate-multiple) Thread b finished. + (priority-donate-multiple) Thread b should have just finished. + (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. + (priority-donate-multiple) Thread a acquired lock a. + (priority-donate-multiple) Thread a finished. + (priority-donate-multiple) Thread a should have just finished. + (priority-donate-multiple) Main thread should have priority 31. Actual priority: 31. + (priority-donate-multiple) end +Differences in `diff -u' format: + (priority-donate-multiple) begin +- (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. +- (priority-donate-multiple) Main thread should have priority 33. Actual priority: 33. +- (priority-donate-multiple) Thread b acquired lock b. +- (priority-donate-multiple) Thread b finished. ++ (priority-donate-multiple) Main thread should have priority 32. Actual priority: 31. ++ (priority-donate-multiple) Main thread should have priority 33. Actual priority: 31. + (priority-donate-multiple) Thread b should have just finished. +- (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. +- (priority-donate-multiple) Thread a acquired lock a. +- (priority-donate-multiple) Thread a finished. ++ (priority-donate-multiple) Main thread should have priority 32. Actual priority: 31. + (priority-donate-multiple) Thread a should have just finished. + (priority-donate-multiple) Main thread should have priority 31. Actual priority: 31. + (priority-donate-multiple) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.errors b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.output b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.output new file mode 100644 index 0000000..281c6ce --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.output @@ -0,0 +1,31 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-donate-multiple2 +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-donate-multiple2': +(priority-donate-multiple2) begin +(priority-donate-multiple2) Main thread should have priority 34. Actual priority: 31. +(priority-donate-multiple2) Thread c finished. +(priority-donate-multiple2) Main thread should have priority 36. Actual priority: 31. +(priority-donate-multiple2) (priority-donate-multiple2) Thread a acquired lock a. +(priority-donate-multiple2) Thread a finished. +Main thread should have priority 36. Actual priority: 31. +(priority-donate-multiple2) Threads b, a, c should have just finished, in that order. +(priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31. +(priority-donate-multiple2) end +Execution of 'priority-donate-multiple2' complete. +Timer: 103 ticks +Thread: 0 idle ticks, 106 kernel ticks, 0 user ticks +Console: 1022 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.result b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.result new file mode 100644 index 0000000..b1328ac --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-multiple2.result @@ -0,0 +1,34 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-donate-multiple2) begin + (priority-donate-multiple2) Main thread should have priority 34. Actual priority: 34. + (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. + (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. + (priority-donate-multiple2) Thread b acquired lock b. + (priority-donate-multiple2) Thread b finished. + (priority-donate-multiple2) Thread a acquired lock a. + (priority-donate-multiple2) Thread a finished. + (priority-donate-multiple2) Thread c finished. + (priority-donate-multiple2) Threads b, a, c should have just finished, in that order. + (priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31. + (priority-donate-multiple2) end +Differences in `diff -u' format: + (priority-donate-multiple2) begin +- (priority-donate-multiple2) Main thread should have priority 34. Actual priority: 34. +- (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. +- (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. +- (priority-donate-multiple2) Thread b acquired lock b. +- (priority-donate-multiple2) Thread b finished. +- (priority-donate-multiple2) Thread a acquired lock a. +- (priority-donate-multiple2) Thread a finished. ++ (priority-donate-multiple2) Main thread should have priority 34. Actual priority: 31. + (priority-donate-multiple2) Thread c finished. ++ (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 31. ++ (priority-donate-multiple2) (priority-donate-multiple2) Thread a acquired lock a. ++ (priority-donate-multiple2) Thread a finished. ++ Main thread should have priority 36. Actual priority: 31. + (priority-donate-multiple2) Threads b, a, c should have just finished, in that order. + (priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31. + (priority-donate-multiple2) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.errors b/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.output b/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.output new file mode 100644 index 0000000..81b42a2 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.output @@ -0,0 +1,29 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-donate-nest +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-donate-nest': +(priority-donate-nest) begin +(priority-donate-nest) Low thread should have priority 32. Actual priority: 31. +(priority-donate-nest) Low thread should have priority 33. Actual priority: 31. +(priority-donate-nest) Medium thread should have priority 33. Actual priority: 32. +(priority-donate-nest) Medium thread got the lock. +(priority-donate-nest) Medium thread should just have finished. +(priority-donate-nest) Low thread should have priority 31. Actual priority: 31. +(priority-donate-nest) end +Execution of 'priority-donate-nest' complete. +Timer: 83 ticks +Thread: 0 idle ticks, 85 kernel ticks, 0 user ticks +Console: 855 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.result b/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.result new file mode 100644 index 0000000..1aee713 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-nest.result @@ -0,0 +1,32 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-donate-nest) begin + (priority-donate-nest) Low thread should have priority 32. Actual priority: 32. + (priority-donate-nest) Low thread should have priority 33. Actual priority: 33. + (priority-donate-nest) Medium thread should have priority 33. Actual priority: 33. + (priority-donate-nest) Medium thread got the lock. + (priority-donate-nest) High thread got the lock. + (priority-donate-nest) High thread finished. + (priority-donate-nest) High thread should have just finished. + (priority-donate-nest) Middle thread finished. + (priority-donate-nest) Medium thread should just have finished. + (priority-donate-nest) Low thread should have priority 31. Actual priority: 31. + (priority-donate-nest) end +Differences in `diff -u' format: + (priority-donate-nest) begin +- (priority-donate-nest) Low thread should have priority 32. Actual priority: 32. +- (priority-donate-nest) Low thread should have priority 33. Actual priority: 33. +- (priority-donate-nest) Medium thread should have priority 33. Actual priority: 33. ++ (priority-donate-nest) Low thread should have priority 32. Actual priority: 31. ++ (priority-donate-nest) Low thread should have priority 33. Actual priority: 31. ++ (priority-donate-nest) Medium thread should have priority 33. Actual priority: 32. + (priority-donate-nest) Medium thread got the lock. +- (priority-donate-nest) High thread got the lock. +- (priority-donate-nest) High thread finished. +- (priority-donate-nest) High thread should have just finished. +- (priority-donate-nest) Middle thread finished. + (priority-donate-nest) Medium thread should just have finished. + (priority-donate-nest) Low thread should have priority 31. Actual priority: 31. + (priority-donate-nest) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.errors b/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.output b/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.output new file mode 100644 index 0000000..e1124c1 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.output @@ -0,0 +1,27 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-donate-one +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-donate-one': +(priority-donate-one) begin +(priority-donate-one) This thread should have priority 32. Actual priority: 31. +(priority-donate-one) This thread should have priority 33. Actual priority: 31. +(priority-donate-one) acquire2, acquire1 must already have finished, in that order. +(priority-donate-one) This should be the last line before finishing this test. +(priority-donate-one) end +Execution of 'priority-donate-one' complete. +Timer: 70 ticks +Thread: 0 idle ticks, 72 kernel ticks, 0 user ticks +Console: 733 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.result b/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.result new file mode 100644 index 0000000..0102de3 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-one.result @@ -0,0 +1,27 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-donate-one) begin + (priority-donate-one) This thread should have priority 32. Actual priority: 32. + (priority-donate-one) This thread should have priority 33. Actual priority: 33. + (priority-donate-one) acquire2: got the lock + (priority-donate-one) acquire2: done + (priority-donate-one) acquire1: got the lock + (priority-donate-one) acquire1: done + (priority-donate-one) acquire2, acquire1 must already have finished, in that order. + (priority-donate-one) This should be the last line before finishing this test. + (priority-donate-one) end +Differences in `diff -u' format: + (priority-donate-one) begin +- (priority-donate-one) This thread should have priority 32. Actual priority: 32. +- (priority-donate-one) This thread should have priority 33. Actual priority: 33. +- (priority-donate-one) acquire2: got the lock +- (priority-donate-one) acquire2: done +- (priority-donate-one) acquire1: got the lock +- (priority-donate-one) acquire1: done ++ (priority-donate-one) This thread should have priority 32. Actual priority: 31. ++ (priority-donate-one) This thread should have priority 33. Actual priority: 31. + (priority-donate-one) acquire2, acquire1 must already have finished, in that order. + (priority-donate-one) This should be the last line before finishing this test. + (priority-donate-one) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.errors b/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.output b/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.output new file mode 100644 index 0000000..39ca211 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.output @@ -0,0 +1,27 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-donate-sema +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-donate-sema': +(priority-donate-sema) begin +(priority-donate-sema) Thread L acquired lock. +(priority-donate-sema) Thread L downed semaphore. +(priority-donate-sema) Thread L finished. +(priority-donate-sema) Main thread finished. +(priority-donate-sema) end +Execution of 'priority-donate-sema' complete. +Timer: 60 ticks +Thread: 0 idle ticks, 62 kernel ticks, 0 user ticks +Console: 597 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.result b/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.result new file mode 100644 index 0000000..826082a --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-donate-sema.result @@ -0,0 +1,23 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-donate-sema) begin + (priority-donate-sema) Thread L acquired lock. + (priority-donate-sema) Thread L downed semaphore. + (priority-donate-sema) Thread H acquired lock. + (priority-donate-sema) Thread H finished. + (priority-donate-sema) Thread M finished. + (priority-donate-sema) Thread L finished. + (priority-donate-sema) Main thread finished. + (priority-donate-sema) end +Differences in `diff -u' format: + (priority-donate-sema) begin + (priority-donate-sema) Thread L acquired lock. + (priority-donate-sema) Thread L downed semaphore. +- (priority-donate-sema) Thread H acquired lock. +- (priority-donate-sema) Thread H finished. +- (priority-donate-sema) Thread M finished. + (priority-donate-sema) Thread L finished. + (priority-donate-sema) Main thread finished. + (priority-donate-sema) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-fifo.errors b/pintos-env/pintos/threads/build/tests/threads/priority-fifo.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-fifo.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-fifo.output b/pintos-env/pintos/threads/build/tests/threads/priority-fifo.output new file mode 100644 index 0000000..a520a2e --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-fifo.output @@ -0,0 +1,41 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-fifo +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-fifo': +(priority-fifo) begin +(priority-fifo) 16 threads will iterate 16 times in the same order each time. +(priority-fifo) If the order varies then there is a bug. +(priority-fifo) iteration: 0 0 1 2 0 1 2 3 4 0 1 2 3 4 5 6 +(priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 +(priority-fifo) iteration: 7 8 9 10 0 1 2 3 4 5 6 7 8 9 10 11 +(priority-fifo) iteration: 12 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 +(priority-fifo) iteration: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 +(priority-fifo) iteration: 2 3 4 5 6 7 8 9 10 11 12 13 14 15 3 4 +(priority-fifo) iteration: 5 6 7 8 9 10 11 12 13 14 15 5 6 7 8 9 +(priority-fifo) iteration: 10 11 12 13 14 15 7 8 9 10 11 12 13 14 15 9 +(priority-fifo) iteration: 10 11 12 13 14 15 11 12 13 14 15 13 14 15 14 15 +(priority-fifo) end +Execution of 'priority-fifo' complete. +Timer: 225 ticks +Thread: 0 idle ticks, 227 kernel ticks, 0 user ticks +Console: 1555 characters output +Keyboard: 0 keys pressed +Powering off..======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-fifo.result b/pintos-env/pintos/threads/build/tests/threads/priority-fifo.result new file mode 100644 index 0000000..54f9a56 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-fifo.result @@ -0,0 +1,2 @@ +FAIL +First iteration does not list all threads 0...15 diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-preempt.errors b/pintos-env/pintos/threads/build/tests/threads/priority-preempt.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-preempt.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-preempt.output b/pintos-env/pintos/threads/build/tests/threads/priority-preempt.output new file mode 100644 index 0000000..211ca0e --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-preempt.output @@ -0,0 +1,25 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-preempt +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-preempt': +(priority-preempt) begin +(priority-preempt) (priority-preempt) Thread high-priority iteration 0 +The high-priority thread should have already completed. +(priority-preempt) end +Execution of 'priority-preempt' complete. +Timer: 46 ticks +Thread: 0 idle ticks, 49 kernel ticks, 0 user ticks +Console: 520 characters output +Keyboard: 0 keys pressed +Powering off...======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-preempt.result b/pintos-env/pintos/threads/build/tests/threads/priority-preempt.result new file mode 100644 index 0000000..d26f4cd --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-preempt.result @@ -0,0 +1,25 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-preempt) begin + (priority-preempt) Thread high-priority iteration 0 + (priority-preempt) Thread high-priority iteration 1 + (priority-preempt) Thread high-priority iteration 2 + (priority-preempt) Thread high-priority iteration 3 + (priority-preempt) Thread high-priority iteration 4 + (priority-preempt) Thread high-priority done! + (priority-preempt) The high-priority thread should have already completed. + (priority-preempt) end +Differences in `diff -u' format: + (priority-preempt) begin +- (priority-preempt) Thread high-priority iteration 0 +- (priority-preempt) Thread high-priority iteration 1 +- (priority-preempt) Thread high-priority iteration 2 +- (priority-preempt) Thread high-priority iteration 3 +- (priority-preempt) Thread high-priority iteration 4 +- (priority-preempt) Thread high-priority done! +- (priority-preempt) The high-priority thread should have already completed. ++ (priority-preempt) (priority-preempt) Thread high-priority iteration 0 ++ The high-priority thread should have already completed. + (priority-preempt) end diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-sema.errors b/pintos-env/pintos/threads/build/tests/threads/priority-sema.errors new file mode 100644 index 0000000..07f5cf4 --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-sema.errors @@ -0,0 +1,10 @@ +Prototype mismatch: sub main::SIGVTALRM () vs none at /pintos-env/pintos/utils/pintos line 934. +Constant subroutine SIGVTALRM redefined at /pintos-env/pintos/utils/pintos line 926. +00000000000i[ ] reading configuration from bochsrc.txt +00000000000e[ ] user_shortcut: old-style syntax detected +00000000000i[ ] installing nogui module as the Bochs GUI +00000000000i[ ] using log file bochsout.txt +======================================================================== +Bochs is exiting with the following message: +[UNMP ] Shutdown port: shutdown requested +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-sema.output b/pintos-env/pintos/threads/build/tests/threads/priority-sema.output new file mode 100644 index 0000000..47b020e --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-sema.output @@ -0,0 +1,34 @@ +warning: can't find squish-pty, so terminal input will fail +bochs -q +PiLo hda1 +Loading.......... +Kernel command line: -q run priority-sema +Pintos booting with 4,096 kB RAM... +383 pages available in kernel pool. +383 pages available in user pool. +Calibrating timer... 204,600 loops/s. +Boot complete. +Executing 'priority-sema': +(priority-sema) begin +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) Back in main thread. +(priority-sema) end +Execution of 'priority-sema' complete. +Timer: 92 ticks +Thread: 0 idle ticks, 95 kernel ticks, 0 user ticks +Console: 748 characters output +Keyboard: 0 keys pressed +Powering off... +======================================================================== + Bochs x86 Emulator 2.6 + Built from SVN snapshot on September 2nd, 2012 + Compiled on Feb 17 2015 at 16:28:11 +======================================================================== diff --git a/pintos-env/pintos/threads/build/tests/threads/priority-sema.result b/pintos-env/pintos/threads/build/tests/threads/priority-sema.result new file mode 100644 index 0000000..251607c --- /dev/null +++ b/pintos-env/pintos/threads/build/tests/threads/priority-sema.result @@ -0,0 +1,49 @@ +FAIL +Test output failed to match any acceptable form. + +Acceptable output: + (priority-sema) begin + (priority-sema) Thread priority 30 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 29 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 28 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 27 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 26 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 25 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 24 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 23 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 22 woke up. + (priority-sema) Back in main thread. + (priority-sema) Thread priority 21 woke up. + (priority-sema) Back in main thread. + (priority-sema) end +Differences in `diff -u' format: + (priority-sema) begin +- (priority-sema) Thread priority 30 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 29 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 28 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 27 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 26 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 25 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 24 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 23 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 22 woke up. + (priority-sema) Back in main thread. +- (priority-sema) Thread priority 21 woke up. + (priority-sema) Back in main thread. + (priority-sema) end diff --git a/pintos-env/pintos/threads/build/threads/kernel.lds.s b/pintos-env/pintos/threads/build/threads/kernel.lds.s new file mode 100644 index 0000000..778681d --- /dev/null +++ b/pintos-env/pintos/threads/build/threads/kernel.lds.s @@ -0,0 +1,19 @@ +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386") +ENTRY(start) +SECTIONS +{ + _start = 0xc0000000 + 0x20000; + . = _start + SIZEOF_HEADERS; + .text : { *(.start) *(.text) } = 0x90 + .rodata : { *(.rodata) *(.rodata.*) + . = ALIGN(0x1000); + _end_kernel_text = .; } + .data : { *(.data) + _signature = .; LONG(0xaa55aa55) } + _start_bss = .; + .bss : { *(.bss) } + _end_bss = .; + _end = .; + ASSERT (_end - _start <= 512K, "Kernel image is too big.") +} diff --git a/pintos-env/pintos/threads/flags.h b/pintos-env/pintos/threads/flags.h new file mode 100755 index 0000000..5654ac7 --- /dev/null +++ b/pintos-env/pintos/threads/flags.h @@ -0,0 +1,8 @@ +#ifndef THREADS_FLAGS_H +#define THREADS_FLAGS_H + +/* EFLAGS Register. */ +#define FLAG_MBS 0x00000002 /* Must be set. */ +#define FLAG_IF 0x00000200 /* Interrupt Flag. */ + +#endif /* threads/flags.h */ diff --git a/pintos-env/pintos/threads/init.c b/pintos-env/pintos/threads/init.c new file mode 100755 index 0000000..cebec2c --- /dev/null +++ b/pintos-env/pintos/threads/init.c @@ -0,0 +1,429 @@ +#include "threads/init.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "devices/kbd.h" +#include "devices/input.h" +#include "devices/serial.h" +#include "devices/shutdown.h" +#include "devices/timer.h" +#include "devices/vga.h" +#include "devices/rtc.h" +#include "threads/interrupt.h" +#include "threads/io.h" +#include "threads/loader.h" +#include "threads/malloc.h" +#include "threads/palloc.h" +#include "threads/pte.h" +#include "threads/thread.h" +#ifdef USERPROG +#include "userprog/process.h" +#include "userprog/exception.h" +#include "userprog/gdt.h" +#include "userprog/syscall.h" +#include "userprog/tss.h" +#else +#include "tests/threads/tests.h" +#endif +#ifdef FILESYS +#include "devices/block.h" +#include "devices/ide.h" +#include "filesys/filesys.h" +#include "filesys/fsutil.h" +#endif + +/* Page directory with kernel mappings only. */ +uint32_t *init_page_dir; + +#ifdef FILESYS +/* -f: Format the file system? */ +static bool format_filesys; + +/* -filesys, -scratch, -swap: Names of block devices to use, + overriding the defaults. */ +static const char *filesys_bdev_name; +static const char *scratch_bdev_name; +#ifdef VM +static const char *swap_bdev_name; +#endif +#endif /* FILESYS */ + +/* -ul: Maximum number of pages to put into palloc's user pool. */ +static size_t user_page_limit = SIZE_MAX; + +static void bss_init (void); +static void paging_init (void); + +static char **read_command_line (void); +static char **parse_options (char **argv); +static void run_actions (char **argv); +static void usage (void); + +#ifdef FILESYS +static void locate_block_devices (void); +static void locate_block_device (enum block_type, const char *name); +#endif + +int main (void) NO_RETURN; + +/* Pintos main program. */ +int +main (void) +{ + char **argv; + + /* Clear BSS. */ + bss_init (); + + /* Break command line into arguments and parse options. */ + argv = read_command_line (); + argv = parse_options (argv); + + /* Initialize ourselves as a thread so we can use locks, + then enable console locking. */ + thread_init (); + console_init (); + + /* Greet user. */ + printf ("Pintos booting with %'"PRIu32" kB RAM...\n", + init_ram_pages * PGSIZE / 1024); + + /* Initialize memory system. */ + palloc_init (user_page_limit); + malloc_init (); + paging_init (); + + /* Segmentation. */ +#ifdef USERPROG + tss_init (); + gdt_init (); +#endif + + /* Initialize interrupt handlers. */ + intr_init (); + timer_init (); + kbd_init (); + input_init (); +#ifdef USERPROG + exception_init (); + syscall_init (); +#endif + + /* Start thread scheduler and enable interrupts. */ + thread_start (); + serial_init_queue (); + timer_calibrate (); + +#ifdef FILESYS + /* Initialize file system. */ + ide_init (); + locate_block_devices (); + filesys_init (format_filesys); +#endif + + printf ("Boot complete.\n"); + + /* Run actions specified on kernel command line. */ + run_actions (argv); + + /* Finish up. */ + shutdown (); + thread_exit (); +} + +/* Clear the "BSS", a segment that should be initialized to + zeros. It isn't actually stored on disk or zeroed by the + kernel loader, so we have to zero it ourselves. + + The start and end of the BSS segment is recorded by the + linker as _start_bss and _end_bss. See kernel.lds. */ +static void +bss_init (void) +{ + extern char _start_bss, _end_bss; + memset (&_start_bss, 0, &_end_bss - &_start_bss); +} + +/* Populates the base page directory and page table with the + kernel virtual mapping, and then sets up the CPU to use the + new page directory. Points init_page_dir to the page + directory it creates. */ +static void +paging_init (void) +{ + uint32_t *pd, *pt; + size_t page; + extern char _start, _end_kernel_text; + + pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO); + pt = NULL; + for (page = 0; page < init_ram_pages; page++) + { + uintptr_t paddr = page * PGSIZE; + char *vaddr = ptov (paddr); + size_t pde_idx = pd_no (vaddr); + size_t pte_idx = pt_no (vaddr); + bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text; + + if (pd[pde_idx] == 0) + { + pt = palloc_get_page (PAL_ASSERT | PAL_ZERO); + pd[pde_idx] = pde_create (pt); + } + + pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text); + } + + /* Store the physical address of the page directory into CR3 + aka PDBR (page directory base register). This activates our + new page tables immediately. See [IA32-v2a] "MOV--Move + to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address + of the Page Directory". */ + asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir))); +} + +/* Breaks the kernel command line into words and returns them as + an argv-like array. */ +static char ** +read_command_line (void) +{ + static char *argv[LOADER_ARGS_LEN / 2 + 1]; + char *p, *end; + int argc; + int i; + + argc = *(uint32_t *) ptov (LOADER_ARG_CNT); + p = ptov (LOADER_ARGS); + end = p + LOADER_ARGS_LEN; + for (i = 0; i < argc; i++) + { + if (p >= end) + PANIC ("command line arguments overflow"); + + argv[i] = p; + p += strnlen (p, end - p) + 1; + } + argv[argc] = NULL; + + /* Print kernel command line. */ + printf ("Kernel command line:"); + for (i = 0; i < argc; i++) + if (strchr (argv[i], ' ') == NULL) + printf (" %s", argv[i]); + else + printf (" '%s'", argv[i]); + printf ("\n"); + + return argv; +} + +/* Parses options in ARGV[] + and returns the first non-option argument. */ +static char ** +parse_options (char **argv) +{ + for (; *argv != NULL && **argv == '-'; argv++) + { + char *save_ptr; + char *name = strtok_r (*argv, "=", &save_ptr); + char *value = strtok_r (NULL, "", &save_ptr); + + if (!strcmp (name, "-h")) + usage (); + else if (!strcmp (name, "-q")) + shutdown_configure (SHUTDOWN_POWER_OFF); + else if (!strcmp (name, "-r")) + shutdown_configure (SHUTDOWN_REBOOT); +#ifdef FILESYS + else if (!strcmp (name, "-f")) + format_filesys = true; + else if (!strcmp (name, "-filesys")) + filesys_bdev_name = value; + else if (!strcmp (name, "-scratch")) + scratch_bdev_name = value; +#ifdef VM + else if (!strcmp (name, "-swap")) + swap_bdev_name = value; +#endif +#endif + else if (!strcmp (name, "-rs")) + random_init (atoi (value)); + else if (!strcmp (name, "-mlfqs")) + thread_mlfqs = true; +#ifdef USERPROG + else if (!strcmp (name, "-ul")) + user_page_limit = atoi (value); +#endif + else + PANIC ("unknown option `%s' (use -h for help)", name); + } + + /* Initialize the random number generator based on the system + time. This has no effect if an "-rs" option was specified. + + When running under Bochs, this is not enough by itself to + get a good seed value, because the pintos script sets the + initial time to a predictable value, not to the local time, + for reproducibility. To fix this, give the "-r" option to + the pintos script to request real-time execution. */ + random_init (rtc_get_time ()); + + return argv; +} + +/* Runs the task specified in ARGV[1]. */ +static void +run_task (char **argv) +{ + const char *task = argv[1]; + + printf ("Executing '%s':\n", task); +#ifdef USERPROG + process_wait (process_execute (task)); +#else + run_test (task); +#endif + printf ("Execution of '%s' complete.\n", task); +} + +/* Executes all of the actions specified in ARGV[] + up to the null pointer sentinel. */ +static void +run_actions (char **argv) +{ + /* An action. */ + struct action + { + char *name; /* Action name. */ + int argc; /* # of args, including action name. */ + void (*function) (char **argv); /* Function to execute action. */ + }; + + /* Table of supported actions. */ + static const struct action actions[] = + { + {"run", 2, run_task}, +#ifdef FILESYS + {"ls", 1, fsutil_ls}, + {"cat", 2, fsutil_cat}, + {"rm", 2, fsutil_rm}, + {"extract", 1, fsutil_extract}, + {"append", 2, fsutil_append}, +#endif + {NULL, 0, NULL}, + }; + + while (*argv != NULL) + { + const struct action *a; + int i; + + /* Find action name. */ + for (a = actions; ; a++) + if (a->name == NULL) + PANIC ("unknown action `%s' (use -h for help)", *argv); + else if (!strcmp (*argv, a->name)) + break; + + /* Check for required arguments. */ + for (i = 1; i < a->argc; i++) + if (argv[i] == NULL) + PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1); + + /* Invoke action and advance. */ + a->function (argv); + argv += a->argc; + } + +} + +/* Prints a kernel command line help message and powers off the + machine. */ +static void +usage (void) +{ + printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n" + "Options must precede actions.\n" + "Actions are executed in the order specified.\n" + "\nAvailable actions:\n" +#ifdef USERPROG + " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n" +#else + " run TEST Run TEST.\n" +#endif +#ifdef FILESYS + " ls List files in the root directory.\n" + " cat FILE Print FILE to the console.\n" + " rm FILE Delete FILE.\n" + "Use these actions indirectly via `pintos' -g and -p options:\n" + " extract Untar from scratch device into file system.\n" + " append FILE Append FILE to tar file on scratch device.\n" +#endif + "\nOptions:\n" + " -h Print this help message and power off.\n" + " -q Power off VM after actions or on panic.\n" + " -r Reboot after actions.\n" +#ifdef FILESYS + " -f Format file system device during startup.\n" + " -filesys=BDEV Use BDEV for file system instead of default.\n" + " -scratch=BDEV Use BDEV for scratch instead of default.\n" +#ifdef VM + " -swap=BDEV Use BDEV for swap instead of default.\n" +#endif +#endif + " -rs=SEED Set random number seed to SEED.\n" + " -mlfqs Use multi-level feedback queue scheduler.\n" +#ifdef USERPROG + " -ul=COUNT Limit user memory to COUNT pages.\n" +#endif + ); + shutdown_power_off (); +} + +#ifdef FILESYS +/* Figure out what block devices to cast in the various Pintos roles. */ +static void +locate_block_devices (void) +{ + locate_block_device (BLOCK_FILESYS, filesys_bdev_name); + locate_block_device (BLOCK_SCRATCH, scratch_bdev_name); +#ifdef VM + locate_block_device (BLOCK_SWAP, swap_bdev_name); +#endif +} + +/* Figures out what block device to use for the given ROLE: the + block device with the given NAME, if NAME is non-null, + otherwise the first block device in probe order of type + ROLE. */ +static void +locate_block_device (enum block_type role, const char *name) +{ + struct block *block = NULL; + + if (name != NULL) + { + block = block_get_by_name (name); + if (block == NULL) + PANIC ("No such block device \"%s\"", name); + } + else + { + for (block = block_first (); block != NULL; block = block_next (block)) + if (block_type (block) == role) + break; + } + + if (block != NULL) + { + printf ("%s: using %s\n", block_type_name (role), block_name (block)); + block_set_role (role, block); + } +} +#endif diff --git a/pintos-env/pintos/threads/init.h b/pintos-env/pintos/threads/init.h new file mode 100755 index 0000000..8a3df90 --- /dev/null +++ b/pintos-env/pintos/threads/init.h @@ -0,0 +1,12 @@ +#ifndef THREADS_INIT_H +#define THREADS_INIT_H + +#include +#include +#include +#include + +/* Page directory with kernel mappings only. */ +extern uint32_t *init_page_dir; + +#endif /* threads/init.h */ diff --git a/pintos-env/pintos/threads/interrupt.c b/pintos-env/pintos/threads/interrupt.c new file mode 100755 index 0000000..e3b90dc --- /dev/null +++ b/pintos-env/pintos/threads/interrupt.c @@ -0,0 +1,438 @@ +#include "threads/interrupt.h" +#include +#include +#include +#include +#include "threads/flags.h" +#include "threads/intr-stubs.h" +#include "threads/io.h" +#include "threads/thread.h" +#include "threads/vaddr.h" +#include "devices/timer.h" + +/* Programmable Interrupt Controller (PIC) registers. + A PC has two PICs, called the master and slave PICs, with the + slave attached ("cascaded") to the master IRQ line 2. */ +#define PIC0_CTRL 0x20 /* Master PIC control register address. */ +#define PIC0_DATA 0x21 /* Master PIC data register address. */ +#define PIC1_CTRL 0xa0 /* Slave PIC control register address. */ +#define PIC1_DATA 0xa1 /* Slave PIC data register address. */ + +/* Number of x86 interrupts. */ +#define INTR_CNT 256 + +/* The Interrupt Descriptor Table (IDT). The format is fixed by + the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor + Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By + Exception- or Interrupt-Handler Procedure". */ +static uint64_t idt[INTR_CNT]; + +/* Interrupt handler functions for each interrupt. */ +static intr_handler_func *intr_handlers[INTR_CNT]; + +/* Names for each interrupt, for debugging purposes. */ +static const char *intr_names[INTR_CNT]; + +/* Number of unexpected interrupts for each vector. An + unexpected interrupt is one that has no registered handler. */ +static unsigned int unexpected_cnt[INTR_CNT]; + +/* External interrupts are those generated by devices outside the + CPU, such as the timer. External interrupts run with + interrupts turned off, so they never nest, nor are they ever + pre-empted. Handlers for external interrupts also may not + sleep, although they may invoke intr_yield_on_return() to + request that a new process be scheduled just before the + interrupt returns. */ +static bool in_external_intr; /* Are we processing an external interrupt? */ +static bool yield_on_return; /* Should we yield on interrupt return? */ + +/* Programmable Interrupt Controller helpers. */ +static void pic_init (void); +static void pic_end_of_interrupt (int irq); + +/* Interrupt Descriptor Table helpers. */ +static uint64_t make_intr_gate (void (*) (void), int dpl); +static uint64_t make_trap_gate (void (*) (void), int dpl); +static inline uint64_t make_idtr_operand (uint16_t limit, void *base); + +/* Interrupt handlers. */ +void intr_handler (struct intr_frame *args); +static void unexpected_interrupt (const struct intr_frame *); + +/* Returns the current interrupt status. */ +enum intr_level +intr_get_level (void) +{ + uint32_t flags; + + /* Push the flags register on the processor stack, then pop the + value off the stack into `flags'. See [IA32-v2b] "PUSHF" + and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware + Interrupts". */ + asm volatile ("pushfl; popl %0" : "=g" (flags)); + + return flags & FLAG_IF ? INTR_ON : INTR_OFF; +} + +/* Enables or disables interrupts as specified by LEVEL and + returns the previous interrupt status. */ +enum intr_level +intr_set_level (enum intr_level level) +{ + return level == INTR_ON ? intr_enable () : intr_disable (); +} + +/* Enables interrupts and returns the previous interrupt status. */ +enum intr_level +intr_enable (void) +{ + enum intr_level old_level = intr_get_level (); + ASSERT (!intr_context ()); + + /* Enable interrupts by setting the interrupt flag. + + See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable + Hardware Interrupts". */ + asm volatile ("sti"); + + return old_level; +} + +/* Disables interrupts and returns the previous interrupt status. */ +enum intr_level +intr_disable (void) +{ + enum intr_level old_level = intr_get_level (); + + /* Disable interrupts by clearing the interrupt flag. + See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable + Hardware Interrupts". */ + asm volatile ("cli" : : : "memory"); + + return old_level; +} + +/* Initializes the interrupt system. */ +void +intr_init (void) +{ + uint64_t idtr_operand; + int i; + + /* Initialize interrupt controller. */ + pic_init (); + + /* Initialize IDT. */ + for (i = 0; i < INTR_CNT; i++) + idt[i] = make_intr_gate (intr_stubs[i], 0); + + /* Load IDT register. + See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt + Descriptor Table (IDT)". */ + idtr_operand = make_idtr_operand (sizeof idt - 1, idt); + asm volatile ("lidt %0" : : "m" (idtr_operand)); + + /* Initialize intr_names. */ + for (i = 0; i < INTR_CNT; i++) + intr_names[i] = "unknown"; + intr_names[0] = "#DE Divide Error"; + intr_names[1] = "#DB Debug Exception"; + intr_names[2] = "NMI Interrupt"; + intr_names[3] = "#BP Breakpoint Exception"; + intr_names[4] = "#OF Overflow Exception"; + intr_names[5] = "#BR BOUND Range Exceeded Exception"; + intr_names[6] = "#UD Invalid Opcode Exception"; + intr_names[7] = "#NM Device Not Available Exception"; + intr_names[8] = "#DF Double Fault Exception"; + intr_names[9] = "Coprocessor Segment Overrun"; + intr_names[10] = "#TS Invalid TSS Exception"; + intr_names[11] = "#NP Segment Not Present"; + intr_names[12] = "#SS Stack Fault Exception"; + intr_names[13] = "#GP General Protection Exception"; + intr_names[14] = "#PF Page-Fault Exception"; + intr_names[16] = "#MF x87 FPU Floating-Point Error"; + intr_names[17] = "#AC Alignment Check Exception"; + intr_names[18] = "#MC Machine-Check Exception"; + intr_names[19] = "#XF SIMD Floating-Point Exception"; +} + +/* Registers interrupt VEC_NO to invoke HANDLER with descriptor + privilege level DPL. Names the interrupt NAME for debugging + purposes. The interrupt handler will be invoked with + interrupt status set to LEVEL. */ +static void +register_handler (uint8_t vec_no, int dpl, enum intr_level level, + intr_handler_func *handler, const char *name) +{ + ASSERT (intr_handlers[vec_no] == NULL); + if (level == INTR_ON) + idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl); + else + idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl); + intr_handlers[vec_no] = handler; + intr_names[vec_no] = name; +} + +/* Registers external interrupt VEC_NO to invoke HANDLER, which + is named NAME for debugging purposes. The handler will + execute with interrupts disabled. */ +void +intr_register_ext (uint8_t vec_no, intr_handler_func *handler, + const char *name) +{ + ASSERT (vec_no >= 0x20 && vec_no <= 0x2f); + register_handler (vec_no, 0, INTR_OFF, handler, name); +} + +/* Registers internal interrupt VEC_NO to invoke HANDLER, which + is named NAME for debugging purposes. The interrupt handler + will be invoked with interrupt status LEVEL. + + The handler will have descriptor privilege level DPL, meaning + that it can be invoked intentionally when the processor is in + the DPL or lower-numbered ring. In practice, DPL==3 allows + user mode to invoke the interrupts and DPL==0 prevents such + invocation. Faults and exceptions that occur in user mode + still cause interrupts with DPL==0 to be invoked. See + [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1 + "Accessing Nonconforming Code Segments" for further + discussion. */ +void +intr_register_int (uint8_t vec_no, int dpl, enum intr_level level, + intr_handler_func *handler, const char *name) +{ + ASSERT (vec_no < 0x20 || vec_no > 0x2f); + register_handler (vec_no, dpl, level, handler, name); +} + +/* Returns true during processing of an external interrupt + and false at all other times. */ +bool +intr_context (void) +{ + return in_external_intr; +} + +/* During processing of an external interrupt, directs the + interrupt handler to yield to a new process just before + returning from the interrupt. May not be called at any other + time. */ +void +intr_yield_on_return (void) +{ + ASSERT (intr_context ()); + yield_on_return = true; +} + +/* 8259A Programmable Interrupt Controller. */ + +/* Initializes the PICs. Refer to [8259A] for details. + + By default, interrupts 0...15 delivered by the PICs will go to + interrupt vectors 0...15. Those vectors are also used for CPU + traps and exceptions, so we reprogram the PICs so that + interrupts 0...15 are delivered to interrupt vectors 32...47 + (0x20...0x2f) instead. */ +static void +pic_init (void) +{ + /* Mask all interrupts on both PICs. */ + outb (PIC0_DATA, 0xff); + outb (PIC1_DATA, 0xff); + + /* Initialize master. */ + outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */ + outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */ + outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */ + outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */ + + /* Initialize slave. */ + outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */ + outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */ + outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */ + outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */ + + /* Unmask all interrupts. */ + outb (PIC0_DATA, 0x00); + outb (PIC1_DATA, 0x00); +} + +/* Sends an end-of-interrupt signal to the PIC for the given IRQ. + If we don't acknowledge the IRQ, it will never be delivered to + us again, so this is important. */ +static void +pic_end_of_interrupt (int irq) +{ + ASSERT (irq >= 0x20 && irq < 0x30); + + /* Acknowledge master PIC. */ + outb (0x20, 0x20); + + /* Acknowledge slave PIC if this is a slave interrupt. */ + if (irq >= 0x28) + outb (0xa0, 0x20); +} + +/* Creates an gate that invokes FUNCTION. + + The gate has descriptor privilege level DPL, meaning that it + can be invoked intentionally when the processor is in the DPL + or lower-numbered ring. In practice, DPL==3 allows user mode + to call into the gate and DPL==0 prevents such calls. Faults + and exceptions that occur in user mode still cause gates with + DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege + Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments" + for further discussion. + + TYPE must be either 14 (for an interrupt gate) or 15 (for a + trap gate). The difference is that entering an interrupt gate + disables interrupts, but entering a trap gate does not. See + [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or + Interrupt-Handler Procedure" for discussion. */ +static uint64_t +make_gate (void (*function) (void), int dpl, int type) +{ + uint32_t e0, e1; + + ASSERT (function != NULL); + ASSERT (dpl >= 0 && dpl <= 3); + ASSERT (type >= 0 && type <= 15); + + e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */ + | (SEL_KCSEG << 16)); /* Target code segment. */ + + e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */ + | (1 << 15) /* Present. */ + | ((uint32_t) dpl << 13) /* Descriptor privilege level. */ + | (0 << 12) /* System. */ + | ((uint32_t) type << 8)); /* Gate type. */ + + return e0 | ((uint64_t) e1 << 32); +} + +/* Creates an interrupt gate that invokes FUNCTION with the given + DPL. */ +static uint64_t +make_intr_gate (void (*function) (void), int dpl) +{ + return make_gate (function, dpl, 14); +} + +/* Creates a trap gate that invokes FUNCTION with the given + DPL. */ +static uint64_t +make_trap_gate (void (*function) (void), int dpl) +{ + return make_gate (function, dpl, 15); +} + +/* Returns a descriptor that yields the given LIMIT and BASE when + used as an operand for the LIDT instruction. */ +static inline uint64_t +make_idtr_operand (uint16_t limit, void *base) +{ + return limit | ((uint64_t) (uint32_t) base << 16); +} + +/* Interrupt handlers. */ + +/* Handler for all interrupts, faults, and exceptions. This + function is called by the assembly language interrupt stubs in + intr-stubs.S. FRAME describes the interrupt and the + interrupted thread's registers. */ +void +intr_handler (struct intr_frame *frame) +{ + bool external; + intr_handler_func *handler; + + /* External interrupts are special. + We only handle one at a time (so interrupts must be off) + and they need to be acknowledged on the PIC (see below). + An external interrupt handler cannot sleep. */ + external = frame->vec_no >= 0x20 && frame->vec_no < 0x30; + if (external) + { + ASSERT (intr_get_level () == INTR_OFF); + ASSERT (!intr_context ()); + + in_external_intr = true; + yield_on_return = false; + } + + /* Invoke the interrupt's handler. */ + handler = intr_handlers[frame->vec_no]; + if (handler != NULL) + handler (frame); + else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f) + { + /* There is no handler, but this interrupt can trigger + spuriously due to a hardware fault or hardware race + condition. Ignore it. */ + } + else + unexpected_interrupt (frame); + + /* Complete the processing of an external interrupt. */ + if (external) + { + ASSERT (intr_get_level () == INTR_OFF); + ASSERT (intr_context ()); + + in_external_intr = false; + pic_end_of_interrupt (frame->vec_no); + + if (yield_on_return) + thread_yield (); + } +} + +/* Handles an unexpected interrupt with interrupt frame F. An + unexpected interrupt is one that has no registered handler. */ +static void +unexpected_interrupt (const struct intr_frame *f) +{ + /* Count the number so far. */ + unsigned int n = ++unexpected_cnt[f->vec_no]; + + /* If the number is a power of 2, print a message. This rate + limiting means that we get information about an uncommon + unexpected interrupt the first time and fairly often after + that, but one that occurs many times will not overwhelm the + console. */ + if ((n & (n - 1)) == 0) + printf ("Unexpected interrupt %#04x (%s)\n", + f->vec_no, intr_names[f->vec_no]); +} + +/* Dumps interrupt frame F to the console, for debugging. */ +void +intr_dump_frame (const struct intr_frame *f) +{ + uint32_t cr2; + + /* Store current value of CR2 into `cr2'. + CR2 is the linear address of the last page fault. + See [IA32-v2a] "MOV--Move to/from Control Registers" and + [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception + (#PF)". */ + asm ("movl %%cr2, %0" : "=r" (cr2)); + + printf ("Interrupt %#04x (%s) at eip=%p\n", + f->vec_no, intr_names[f->vec_no], f->eip); + printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code); + printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n", + f->eax, f->ebx, f->ecx, f->edx); + printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n", + f->esi, f->edi, (uint32_t) f->esp, f->ebp); + printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n", + f->cs, f->ds, f->es, f->ss); +} + +/* Returns the name of interrupt VEC. */ +const char * +intr_name (uint8_t vec) +{ + return intr_names[vec]; +} diff --git a/pintos-env/pintos/threads/interrupt.h b/pintos-env/pintos/threads/interrupt.h new file mode 100755 index 0000000..d43e06d --- /dev/null +++ b/pintos-env/pintos/threads/interrupt.h @@ -0,0 +1,70 @@ +#ifndef THREADS_INTERRUPT_H +#define THREADS_INTERRUPT_H + +#include +#include + +/* Interrupts on or off? */ +enum intr_level + { + INTR_OFF, /* Interrupts disabled. */ + INTR_ON /* Interrupts enabled. */ + }; + +enum intr_level intr_get_level (void); +enum intr_level intr_set_level (enum intr_level); +enum intr_level intr_enable (void); +enum intr_level intr_disable (void); + +/* Interrupt stack frame. */ +struct intr_frame + { + /* Pushed by intr_entry in intr-stubs.S. + These are the interrupted task's saved registers. */ + uint32_t edi; /* Saved EDI. */ + uint32_t esi; /* Saved ESI. */ + uint32_t ebp; /* Saved EBP. */ + uint32_t esp_dummy; /* Not used. */ + uint32_t ebx; /* Saved EBX. */ + uint32_t edx; /* Saved EDX. */ + uint32_t ecx; /* Saved ECX. */ + uint32_t eax; /* Saved EAX. */ + uint16_t gs, :16; /* Saved GS segment register. */ + uint16_t fs, :16; /* Saved FS segment register. */ + uint16_t es, :16; /* Saved ES segment register. */ + uint16_t ds, :16; /* Saved DS segment register. */ + + /* Pushed by intrNN_stub in intr-stubs.S. */ + uint32_t vec_no; /* Interrupt vector number. */ + + /* Sometimes pushed by the CPU, + otherwise for consistency pushed as 0 by intrNN_stub. + The CPU puts it just under `eip', but we move it here. */ + uint32_t error_code; /* Error code. */ + + /* Pushed by intrNN_stub in intr-stubs.S. + This frame pointer eases interpretation of backtraces. */ + void *frame_pointer; /* Saved EBP (frame pointer). */ + + /* Pushed by the CPU. + These are the interrupted task's saved registers. */ + void (*eip) (void); /* Next instruction to execute. */ + uint16_t cs, :16; /* Code segment for eip. */ + uint32_t eflags; /* Saved CPU flags. */ + void *esp; /* Saved stack pointer. */ + uint16_t ss, :16; /* Data segment for esp. */ + }; + +typedef void intr_handler_func (struct intr_frame *); + +void intr_init (void); +void intr_register_ext (uint8_t vec, intr_handler_func *, const char *name); +void intr_register_int (uint8_t vec, int dpl, enum intr_level, + intr_handler_func *, const char *name); +bool intr_context (void); +void intr_yield_on_return (void); + +void intr_dump_frame (const struct intr_frame *); +const char *intr_name (uint8_t vec); + +#endif /* threads/interrupt.h */ diff --git a/pintos-env/pintos/threads/intr-stubs.S b/pintos-env/pintos/threads/intr-stubs.S new file mode 100755 index 0000000..adb674e --- /dev/null +++ b/pintos-env/pintos/threads/intr-stubs.S @@ -0,0 +1,203 @@ +#include "threads/loader.h" + + .text + +/* Main interrupt entry point. + + An internal or external interrupt starts in one of the + intrNN_stub routines, which push the `struct intr_frame' + frame_pointer, error_code, and vec_no members on the stack, + then jump here. + + We save the rest of the `struct intr_frame' members to the + stack, set up some registers as needed by the kernel, and then + call intr_handler(), which actually handles the interrupt. + + We "fall through" to intr_exit to return from the interrupt. +*/ +.func intr_entry +intr_entry: + /* Save caller's registers. */ + pushl %ds + pushl %es + pushl %fs + pushl %gs + pushal + + /* Set up kernel environment. */ + cld /* String instructions go upward. */ + mov $SEL_KDSEG, %eax /* Initialize segment registers. */ + mov %eax, %ds + mov %eax, %es + leal 56(%esp), %ebp /* Set up frame pointer. */ + + /* Call interrupt handler. */ + pushl %esp +.globl intr_handler + call intr_handler + addl $4, %esp +.endfunc + +/* Interrupt exit. + + Restores the caller's registers, discards extra data on the + stack, and returns to the caller. + + This is a separate function because it is called directly when + we launch a new user process (see start_process() in + userprog/process.c). */ +.globl intr_exit +.func intr_exit +intr_exit: + /* Restore caller's registers. */ + popal + popl %gs + popl %fs + popl %es + popl %ds + + /* Discard `struct intr_frame' vec_no, error_code, + frame_pointer members. */ + addl $12, %esp + + /* Return to caller. */ + iret +.endfunc + +/* Interrupt stubs. + + This defines 256 fragments of code, named `intr00_stub' + through `intrff_stub', each of which is used as the entry + point for the corresponding interrupt vector. It also puts + the address of each of these functions in the correct spot in + `intr_stubs', an array of function pointers. + + Most of the stubs do this: + + 1. Push %ebp on the stack (frame_pointer in `struct intr_frame'). + + 2. Push 0 on the stack (error_code). + + 3. Push the interrupt number on the stack (vec_no). + + The CPU pushes an extra "error code" on the stack for a few + interrupts. Because we want %ebp to be where the error code + is, we follow a different path: + + 1. Push a duplicate copy of the error code on the stack. + + 2. Replace the original copy of the error code by %ebp. + + 3. Push the interrupt number on the stack. */ + + .data +.globl intr_stubs +intr_stubs: + +/* This implements steps 1 and 2, described above, in the common + case where we just push a 0 error code. */ +#define zero \ + pushl %ebp; \ + pushl $0 + +/* This implements steps 1 and 2, described above, in the case + where the CPU already pushed an error code. */ +#define REAL \ + pushl (%esp); \ + movl %ebp, 4(%esp) + +/* Emits a stub for interrupt vector NUMBER. + TYPE is `zero', for the case where we push a 0 error code, + or `REAL', if the CPU pushes an error code for us. */ +#define STUB(NUMBER, TYPE) \ + .text; \ +.func intr##NUMBER##_stub; \ +intr##NUMBER##_stub: \ + TYPE; \ + push $0x##NUMBER; \ + jmp intr_entry; \ +.endfunc; \ + \ + .data; \ + .long intr##NUMBER##_stub; + +/* All the stubs. */ +STUB(00, zero) STUB(01, zero) STUB(02, zero) STUB(03, zero) +STUB(04, zero) STUB(05, zero) STUB(06, zero) STUB(07, zero) +STUB(08, REAL) STUB(09, zero) STUB(0a, REAL) STUB(0b, REAL) +STUB(0c, zero) STUB(0d, REAL) STUB(0e, REAL) STUB(0f, zero) + +STUB(10, zero) STUB(11, REAL) STUB(12, zero) STUB(13, zero) +STUB(14, zero) STUB(15, zero) STUB(16, zero) STUB(17, zero) +STUB(18, REAL) STUB(19, zero) STUB(1a, REAL) STUB(1b, REAL) +STUB(1c, zero) STUB(1d, REAL) STUB(1e, REAL) STUB(1f, zero) + +STUB(20, zero) STUB(21, zero) STUB(22, zero) STUB(23, zero) +STUB(24, zero) STUB(25, zero) STUB(26, zero) STUB(27, zero) +STUB(28, zero) STUB(29, zero) STUB(2a, zero) STUB(2b, zero) +STUB(2c, zero) STUB(2d, zero) STUB(2e, zero) STUB(2f, zero) + +STUB(30, zero) STUB(31, zero) STUB(32, zero) STUB(33, zero) +STUB(34, zero) STUB(35, zero) STUB(36, zero) STUB(37, zero) +STUB(38, zero) STUB(39, zero) STUB(3a, zero) STUB(3b, zero) +STUB(3c, zero) STUB(3d, zero) STUB(3e, zero) STUB(3f, zero) + +STUB(40, zero) STUB(41, zero) STUB(42, zero) STUB(43, zero) +STUB(44, zero) STUB(45, zero) STUB(46, zero) STUB(47, zero) +STUB(48, zero) STUB(49, zero) STUB(4a, zero) STUB(4b, zero) +STUB(4c, zero) STUB(4d, zero) STUB(4e, zero) STUB(4f, zero) + +STUB(50, zero) STUB(51, zero) STUB(52, zero) STUB(53, zero) +STUB(54, zero) STUB(55, zero) STUB(56, zero) STUB(57, zero) +STUB(58, zero) STUB(59, zero) STUB(5a, zero) STUB(5b, zero) +STUB(5c, zero) STUB(5d, zero) STUB(5e, zero) STUB(5f, zero) + +STUB(60, zero) STUB(61, zero) STUB(62, zero) STUB(63, zero) +STUB(64, zero) STUB(65, zero) STUB(66, zero) STUB(67, zero) +STUB(68, zero) STUB(69, zero) STUB(6a, zero) STUB(6b, zero) +STUB(6c, zero) STUB(6d, zero) STUB(6e, zero) STUB(6f, zero) + +STUB(70, zero) STUB(71, zero) STUB(72, zero) STUB(73, zero) +STUB(74, zero) STUB(75, zero) STUB(76, zero) STUB(77, zero) +STUB(78, zero) STUB(79, zero) STUB(7a, zero) STUB(7b, zero) +STUB(7c, zero) STUB(7d, zero) STUB(7e, zero) STUB(7f, zero) + +STUB(80, zero) STUB(81, zero) STUB(82, zero) STUB(83, zero) +STUB(84, zero) STUB(85, zero) STUB(86, zero) STUB(87, zero) +STUB(88, zero) STUB(89, zero) STUB(8a, zero) STUB(8b, zero) +STUB(8c, zero) STUB(8d, zero) STUB(8e, zero) STUB(8f, zero) + +STUB(90, zero) STUB(91, zero) STUB(92, zero) STUB(93, zero) +STUB(94, zero) STUB(95, zero) STUB(96, zero) STUB(97, zero) +STUB(98, zero) STUB(99, zero) STUB(9a, zero) STUB(9b, zero) +STUB(9c, zero) STUB(9d, zero) STUB(9e, zero) STUB(9f, zero) + +STUB(a0, zero) STUB(a1, zero) STUB(a2, zero) STUB(a3, zero) +STUB(a4, zero) STUB(a5, zero) STUB(a6, zero) STUB(a7, zero) +STUB(a8, zero) STUB(a9, zero) STUB(aa, zero) STUB(ab, zero) +STUB(ac, zero) STUB(ad, zero) STUB(ae, zero) STUB(af, zero) + +STUB(b0, zero) STUB(b1, zero) STUB(b2, zero) STUB(b3, zero) +STUB(b4, zero) STUB(b5, zero) STUB(b6, zero) STUB(b7, zero) +STUB(b8, zero) STUB(b9, zero) STUB(ba, zero) STUB(bb, zero) +STUB(bc, zero) STUB(bd, zero) STUB(be, zero) STUB(bf, zero) + +STUB(c0, zero) STUB(c1, zero) STUB(c2, zero) STUB(c3, zero) +STUB(c4, zero) STUB(c5, zero) STUB(c6, zero) STUB(c7, zero) +STUB(c8, zero) STUB(c9, zero) STUB(ca, zero) STUB(cb, zero) +STUB(cc, zero) STUB(cd, zero) STUB(ce, zero) STUB(cf, zero) + +STUB(d0, zero) STUB(d1, zero) STUB(d2, zero) STUB(d3, zero) +STUB(d4, zero) STUB(d5, zero) STUB(d6, zero) STUB(d7, zero) +STUB(d8, zero) STUB(d9, zero) STUB(da, zero) STUB(db, zero) +STUB(dc, zero) STUB(dd, zero) STUB(de, zero) STUB(df, zero) + +STUB(e0, zero) STUB(e1, zero) STUB(e2, zero) STUB(e3, zero) +STUB(e4, zero) STUB(e5, zero) STUB(e6, zero) STUB(e7, zero) +STUB(e8, zero) STUB(e9, zero) STUB(ea, zero) STUB(eb, zero) +STUB(ec, zero) STUB(ed, zero) STUB(ee, zero) STUB(ef, zero) + +STUB(f0, zero) STUB(f1, zero) STUB(f2, zero) STUB(f3, zero) +STUB(f4, zero) STUB(f5, zero) STUB(f6, zero) STUB(f7, zero) +STUB(f8, zero) STUB(f9, zero) STUB(fa, zero) STUB(fb, zero) +STUB(fc, zero) STUB(fd, zero) STUB(fe, zero) STUB(ff, zero) diff --git a/pintos-env/pintos/threads/intr-stubs.h b/pintos-env/pintos/threads/intr-stubs.h new file mode 100755 index 0000000..9ceba15 --- /dev/null +++ b/pintos-env/pintos/threads/intr-stubs.h @@ -0,0 +1,19 @@ +#ifndef THREADS_INTR_STUBS_H +#define THREADS_INTR_STUBS_H + +/* Interrupt stubs. + + These are little snippets of code in intr-stubs.S, one for + each of the 256 possible x86 interrupts. Each one does a + little bit of stack manipulation, then jumps to intr_entry(). + See intr-stubs.S for more information. + + This array points to each of the interrupt stub entry points + so that intr_init() can easily find them. */ +typedef void intr_stub_func (void); +extern intr_stub_func *intr_stubs[256]; + +/* Interrupt return path. */ +void intr_exit (void); + +#endif /* threads/intr-stubs.h */ diff --git a/pintos-env/pintos/threads/io.h b/pintos-env/pintos/threads/io.h new file mode 100755 index 0000000..30d52da --- /dev/null +++ b/pintos-env/pintos/threads/io.h @@ -0,0 +1,115 @@ +#ifndef THREADS_IO_H +#define THREADS_IO_H + +#include +#include + +/* Reads and returns a byte from PORT. */ +static inline uint8_t +inb (uint16_t port) +{ + /* See [IA32-v2a] "IN". */ + uint8_t data; + asm volatile ("inb %w1, %b0" : "=a" (data) : "Nd" (port)); + return data; +} + +/* Reads CNT bytes from PORT, one after another, and stores them + into the buffer starting at ADDR. */ +static inline void +insb (uint16_t port, void *addr, size_t cnt) +{ + /* See [IA32-v2a] "INS". */ + asm volatile ("rep insb" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); +} + +/* Reads and returns 16 bits from PORT. */ +static inline uint16_t +inw (uint16_t port) +{ + uint16_t data; + /* See [IA32-v2a] "IN". */ + asm volatile ("inw %w1, %w0" : "=a" (data) : "Nd" (port)); + return data; +} + +/* Reads CNT 16-bit (halfword) units from PORT, one after + another, and stores them into the buffer starting at ADDR. */ +static inline void +insw (uint16_t port, void *addr, size_t cnt) +{ + /* See [IA32-v2a] "INS". */ + asm volatile ("rep insw" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); +} + +/* Reads and returns 32 bits from PORT. */ +static inline uint32_t +inl (uint16_t port) +{ + /* See [IA32-v2a] "IN". */ + uint32_t data; + asm volatile ("inl %w1, %0" : "=a" (data) : "Nd" (port)); + return data; +} + +/* Reads CNT 32-bit (word) units from PORT, one after another, + and stores them into the buffer starting at ADDR. */ +static inline void +insl (uint16_t port, void *addr, size_t cnt) +{ + /* See [IA32-v2a] "INS". */ + asm volatile ("rep insl" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); +} + +/* Writes byte DATA to PORT. */ +static inline void +outb (uint16_t port, uint8_t data) +{ + /* See [IA32-v2b] "OUT". */ + asm volatile ("outb %b0, %w1" : : "a" (data), "Nd" (port)); +} + +/* Writes to PORT each byte of data in the CNT-byte buffer + starting at ADDR. */ +static inline void +outsb (uint16_t port, const void *addr, size_t cnt) +{ + /* See [IA32-v2b] "OUTS". */ + asm volatile ("rep outsb" : "+S" (addr), "+c" (cnt) : "d" (port)); +} + +/* Writes the 16-bit DATA to PORT. */ +static inline void +outw (uint16_t port, uint16_t data) +{ + /* See [IA32-v2b] "OUT". */ + asm volatile ("outw %w0, %w1" : : "a" (data), "Nd" (port)); +} + +/* Writes to PORT each 16-bit unit (halfword) of data in the + CNT-halfword buffer starting at ADDR. */ +static inline void +outsw (uint16_t port, const void *addr, size_t cnt) +{ + /* See [IA32-v2b] "OUTS". */ + asm volatile ("rep outsw" : "+S" (addr), "+c" (cnt) : "d" (port)); +} + +/* Writes the 32-bit DATA to PORT. */ +static inline void +outl (uint16_t port, uint32_t data) +{ + /* See [IA32-v2b] "OUT". */ + asm volatile ("outl %0, %w1" : : "a" (data), "Nd" (port)); +} + +/* Writes to PORT each 32-bit unit (word) of data in the CNT-word + buffer starting at ADDR. */ +static inline void +outsl (uint16_t port, const void *addr, size_t cnt) +{ + /* See [IA32-v2b] "OUTS". */ + asm volatile ("rep outsl" : "+S" (addr), "+c" (cnt) : "d" (port)); +} + +#endif /* threads/io.h */ diff --git a/pintos-env/pintos/threads/kernel.lds.S b/pintos-env/pintos/threads/kernel.lds.S new file mode 100755 index 0000000..19082d5 --- /dev/null +++ b/pintos-env/pintos/threads/kernel.lds.S @@ -0,0 +1,30 @@ +#include "threads/loader.h" + +OUTPUT_FORMAT("elf32-i386") +OUTPUT_ARCH("i386") +ENTRY(start) /* Kernel starts at "start" symbol. */ +SECTIONS +{ + /* Specify the kernel base address. */ + _start = LOADER_PHYS_BASE + LOADER_KERN_BASE; + + /* Make room for the ELF headers. */ + . = _start + SIZEOF_HEADERS; + + /* Kernel starts with code, followed by read-only data and writable data. */ + .text : { *(.start) *(.text) } = 0x90 + .rodata : { *(.rodata) *(.rodata.*) + . = ALIGN(0x1000); + _end_kernel_text = .; } + .data : { *(.data) + _signature = .; LONG(0xaa55aa55) } + + /* BSS (zero-initialized data) is after everything else. */ + _start_bss = .; + .bss : { *(.bss) } + _end_bss = .; + + _end = .; + + ASSERT (_end - _start <= 512K, "Kernel image is too big.") +} diff --git a/pintos-env/pintos/threads/loader.S b/pintos-env/pintos/threads/loader.S new file mode 100755 index 0000000..dd87ea1 --- /dev/null +++ b/pintos-env/pintos/threads/loader.S @@ -0,0 +1,263 @@ +#include "threads/loader.h" + +#### Kernel loader. + +#### This code should be stored in the first sector of a hard disk. +#### When the BIOS runs, it loads this code at physical address +#### 0x7c00-0x7e00 (512 bytes) and jumps to the beginning of it, +#### in real mode. The loader loads the kernel into memory and jumps +#### to its entry point, which is the start function in start.S. +#### +#### The BIOS passes in the drive that the loader was read from as +#### DL, with floppy drives numbered 0x00, 0x01, ... and hard drives +#### numbered 0x80, 0x81, ... We want to support booting a kernel on +#### a different drive from the loader, so we don't take advantage of +#### this. + +# Runs in real mode, which is a 16-bit segment. + .code16 + +# Set up segment registers. +# Set stack to grow downward from 60 kB (after boot, the kernel +# continues to use this stack for its initial thread). + + sub %ax, %ax + mov %ax, %ds + mov %ax, %ss + mov $0xf000, %esp + +# Configure serial port so we can report progress without connected VGA. +# See [IntrList] for details. + sub %dx, %dx # Serial port 0. + mov $0xe3, %al # 9600 bps, N-8-1. + # AH is already 0 (Initialize Port). + int $0x14 # Destroys AX. + + call puts + .string "PiLo" + +#### Read the partition table on each system hard disk and scan for a +#### partition of type 0x20, which is the type that we use for a +#### Pintos kernel. +#### +#### Read [Partitions] for a description of the partition table format +#### that we parse. +#### +#### We print out status messages to show the disk and partition being +#### scanned, e.g. hda1234 as we scan four partitions on the first +#### hard disk. + + mov $0x80, %dl # Hard disk 0. +read_mbr: + sub %ebx, %ebx # Sector 0. + mov $0x2000, %ax # Use 0x20000 for buffer. + mov %ax, %es + call read_sector + jc no_such_drive + + # Print hd[a-z]. + call puts + .string " hd" + mov %dl, %al + add $'a' - 0x80, %al + call putc + + # Check for MBR signature--if not present, it's not a + # partitioned hard disk. + cmpw $0xaa55, %es:510 + jne next_drive + + mov $446, %si # Offset of partition table entry 1. + mov $'1', %al +check_partition: + # Is it an unused partition? + cmpl $0, %es:(%si) + je next_partition + + # Print [1-4]. + call putc + + # Is it a Pintos kernel partition? + cmpb $0x20, %es:4(%si) + jne next_partition + + # Is it a bootable partition? + cmpb $0x80, %es:(%si) + je load_kernel + +next_partition: + # No match for this partition, go on to the next one. + add $16, %si # Offset to next partition table entry. + inc %al + cmp $510, %si + jb check_partition + +next_drive: + # No match on this drive, go on to the next one. + inc %dl + jnc read_mbr + +no_such_drive: +no_boot_partition: + # Didn't find a Pintos kernel partition anywhere, give up. + call puts + .string "\rNot found\r" + + # Notify BIOS that boot failed. See [IntrList]. + int $0x18 + +#### We found a kernel. The kernel's drive is in DL. The partition +#### table entry for the kernel's partition is at ES:SI. Our job now +#### is to read the kernel from disk and jump to its start address. + +load_kernel: + call puts + .string "\rLoading" + + # Figure out number of sectors to read. A Pintos kernel is + # just an ELF format object, which doesn't have an + # easy-to-read field to identify its own size (see [ELF1]). + # But we limit Pintos kernels to 512 kB for other reasons, so + # it's easy enough to just read the entire contents of the + # partition or 512 kB from disk, whichever is smaller. + mov %es:12(%si), %ecx # EBP = number of sectors + cmp $1024, %ecx # Cap size at 512 kB + jbe 1f + mov $1024, %cx +1: + + mov %es:8(%si), %ebx # EBX = first sector + mov $0x2000, %ax # Start load address: 0x20000 + +next_sector: + # Read one sector into memory. + mov %ax, %es # ES:0000 -> load address + call read_sector + jc read_failed + + # Print '.' as progress indicator once every 16 sectors == 8 kB. + test $15, %bl + jnz 1f + call puts + .string "." +1: + + # Advance memory pointer and disk sector. + add $0x20, %ax + inc %bx + loop next_sector + + call puts + .string "\r" + +#### Transfer control to the kernel that we loaded. We read the start +#### address out of the ELF header (see [ELF1]) and convert it from a +#### 32-bit linear address into a 16:16 segment:offset address for +#### real mode, then jump to the converted address. The 80x86 doesn't +#### have an instruction to jump to an absolute segment:offset kept in +#### registers, so in fact we store the address in a temporary memory +#### location, then jump indirectly through that location. To save 4 +#### bytes in the loader, we reuse 4 bytes of the loader's code for +#### this temporary pointer. + + mov $0x2000, %ax + mov %ax, %es + mov %es:0x18, %dx + mov %dx, start + movw $0x2000, start + 2 + ljmp *start + +read_failed: +start: + # Disk sector read failed. + call puts +1: .string "\rBad read\r" + + # Notify BIOS that boot failed. See [IntrList]. + int $0x18 + +#### Print string subroutine. To save space in the loader, this +#### subroutine takes its null-terminated string argument from the +#### code stream just after the call, and then returns to the byte +#### just after the terminating null. This subroutine preserves all +#### general-purpose registers. + +puts: xchg %si, %ss:(%esp) + push %ax +next_char: + mov %cs:(%si), %al + inc %si + test %al, %al + jz 1f + call putc + jmp next_char +1: pop %ax + xchg %si, %ss:(%esp) + ret + +#### Character output subroutine. Prints the character in AL to the +#### VGA display and serial port 0, using BIOS services (see +#### [IntrList]). Preserves all general-purpose registers. +#### +#### If called upon to output a carriage return, this subroutine +#### automatically supplies the following line feed. + +putc: pusha + +1: sub %bh, %bh # Page 0. + mov $0x0e, %ah # Teletype output service. + int $0x10 + + mov $0x01, %ah # Serial port output service. + sub %dx, %dx # Serial port 0. +2: int $0x14 # Destroys AH. + test $0x80, %ah # Output timed out? + jz 3f + movw $0x9090, 2b # Turn "int $0x14" above into NOPs. + +3: + cmp $'\r', %al + jne popa_ret + mov $'\n', %al + jmp 1b + +#### Sector read subroutine. Takes a drive number in DL (0x80 = hard +#### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and +#### reads the specified sector into memory at ES:0000. Returns with +#### carry set on error, clear otherwise. Preserves all +#### general-purpose registers. + +read_sector: + pusha + sub %ax, %ax + push %ax # LBA sector number [48:63] + push %ax # LBA sector number [32:47] + push %ebx # LBA sector number [0:31] + push %es # Buffer segment + push %ax # Buffer offset (always 0) + push $1 # Number of sectors to read + push $16 # Packet size + mov $0x42, %ah # Extended read + mov %sp, %si # DS:SI -> packet + int $0x13 # Error code in CF + popa # Pop 16 bytes, preserve flags +popa_ret: + popa + ret # Error code still in CF + +#### Command-line arguments and their count. +#### This is written by the `pintos' utility and read by the kernel. +#### The loader itself does not do anything with the command line. + .org LOADER_ARG_CNT - LOADER_BASE + .fill LOADER_ARG_CNT_LEN, 1, 0 + + .org LOADER_ARGS - LOADER_BASE + .fill LOADER_ARGS_LEN, 1, 0 + +#### Partition table. + .org LOADER_PARTS - LOADER_BASE + .fill LOADER_PARTS_LEN, 1, 0 + +#### Boot-sector signature for BIOS inspection. + .org LOADER_SIG - LOADER_BASE + .word 0xaa55 diff --git a/pintos-env/pintos/threads/loader.h b/pintos-env/pintos/threads/loader.h new file mode 100755 index 0000000..1bfe111 --- /dev/null +++ b/pintos-env/pintos/threads/loader.h @@ -0,0 +1,40 @@ +#ifndef THREADS_LOADER_H +#define THREADS_LOADER_H + +/* Constants fixed by the PC BIOS. */ +#define LOADER_BASE 0x7c00 /* Physical address of loader's base. */ +#define LOADER_END 0x7e00 /* Physical address of end of loader. */ + +/* Physical address of kernel base. */ +#define LOADER_KERN_BASE 0x20000 /* 128 kB. */ + +/* Kernel virtual address at which all physical memory is mapped. + Must be aligned on a 4 MB boundary. */ +#define LOADER_PHYS_BASE 0xc0000000 /* 3 GB. */ + +/* Important loader physical addresses. */ +#define LOADER_SIG (LOADER_END - LOADER_SIG_LEN) /* 0xaa55 BIOS signature. */ +#define LOADER_PARTS (LOADER_SIG - LOADER_PARTS_LEN) /* Partition table. */ +#define LOADER_ARGS (LOADER_PARTS - LOADER_ARGS_LEN) /* Command-line args. */ +#define LOADER_ARG_CNT (LOADER_ARGS - LOADER_ARG_CNT_LEN) /* Number of args. */ + +/* Sizes of loader data structures. */ +#define LOADER_SIG_LEN 2 +#define LOADER_PARTS_LEN 64 +#define LOADER_ARGS_LEN 128 +#define LOADER_ARG_CNT_LEN 4 + +/* GDT selectors defined by loader. + More selectors are defined by userprog/gdt.h. */ +#define SEL_NULL 0x00 /* Null selector. */ +#define SEL_KCSEG 0x08 /* Kernel code selector. */ +#define SEL_KDSEG 0x10 /* Kernel data selector. */ + +#ifndef __ASSEMBLER__ +#include + +/* Amount of physical memory, in 4 kB pages. */ +extern uint32_t init_ram_pages; +#endif + +#endif /* threads/loader.h */ diff --git a/pintos-env/pintos/threads/malloc.c b/pintos-env/pintos/threads/malloc.c new file mode 100755 index 0000000..f6f803b --- /dev/null +++ b/pintos-env/pintos/threads/malloc.c @@ -0,0 +1,294 @@ +#include "threads/malloc.h" +#include +#include +#include +#include +#include +#include +#include "threads/palloc.h" +#include "threads/synch.h" +#include "threads/vaddr.h" + +/* A simple implementation of malloc(). + + The size of each request, in bytes, is rounded up to a power + of 2 and assigned to the "descriptor" that manages blocks of + that size. The descriptor keeps a list of free blocks. If + the free list is nonempty, one of its blocks is used to + satisfy the request. + + Otherwise, a new page of memory, called an "arena", is + obtained from the page allocator (if none is available, + malloc() returns a null pointer). The new arena is divided + into blocks, all of which are added to the descriptor's free + list. Then we return one of the new blocks. + + When we free a block, we add it to its descriptor's free list. + But if the arena that the block was in now has no in-use + blocks, we remove all of the arena's blocks from the free list + and give the arena back to the page allocator. + + We can't handle blocks bigger than 2 kB using this scheme, + because they're too big to fit in a single page with a + descriptor. We handle those by allocating contiguous pages + with the page allocator and sticking the allocation size at + the beginning of the allocated block's arena header. */ + +/* Descriptor. */ +struct desc + { + size_t block_size; /* Size of each element in bytes. */ + size_t blocks_per_arena; /* Number of blocks in an arena. */ + struct list free_list; /* List of free blocks. */ + struct lock lock; /* Lock. */ + }; + +/* Magic number for detecting arena corruption. */ +#define ARENA_MAGIC 0x9a548eed + +/* Arena. */ +struct arena + { + unsigned magic; /* Always set to ARENA_MAGIC. */ + struct desc *desc; /* Owning descriptor, null for big block. */ + size_t free_cnt; /* Free blocks; pages in big block. */ + }; + +/* Free block. */ +struct block + { + struct list_elem free_elem; /* Free list element. */ + }; + +/* Our set of descriptors. */ +static struct desc descs[10]; /* Descriptors. */ +static size_t desc_cnt; /* Number of descriptors. */ + +static struct arena *block_to_arena (struct block *); +static struct block *arena_to_block (struct arena *, size_t idx); + +/* Initializes the malloc() descriptors. */ +void +malloc_init (void) +{ + size_t block_size; + + for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2) + { + struct desc *d = &descs[desc_cnt++]; + ASSERT (desc_cnt <= sizeof descs / sizeof *descs); + d->block_size = block_size; + d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size; + list_init (&d->free_list); + lock_init (&d->lock); + } +} + +/* Obtains and returns a new block of at least SIZE bytes. + Returns a null pointer if memory is not available. */ +void * +malloc (size_t size) +{ + struct desc *d; + struct block *b; + struct arena *a; + + /* A null pointer satisfies a request for 0 bytes. */ + if (size == 0) + return NULL; + + /* Find the smallest descriptor that satisfies a SIZE-byte + request. */ + for (d = descs; d < descs + desc_cnt; d++) + if (d->block_size >= size) + break; + if (d == descs + desc_cnt) + { + /* SIZE is too big for any descriptor. + Allocate enough pages to hold SIZE plus an arena. */ + size_t page_cnt = DIV_ROUND_UP (size + sizeof *a, PGSIZE); + a = palloc_get_multiple (0, page_cnt); + if (a == NULL) + return NULL; + + /* Initialize the arena to indicate a big block of PAGE_CNT + pages, and return it. */ + a->magic = ARENA_MAGIC; + a->desc = NULL; + a->free_cnt = page_cnt; + return a + 1; + } + + lock_acquire (&d->lock); + + /* If the free list is empty, create a new arena. */ + if (list_empty (&d->free_list)) + { + size_t i; + + /* Allocate a page. */ + a = palloc_get_page (0); + if (a == NULL) + { + lock_release (&d->lock); + return NULL; + } + + /* Initialize arena and add its blocks to the free list. */ + a->magic = ARENA_MAGIC; + a->desc = d; + a->free_cnt = d->blocks_per_arena; + for (i = 0; i < d->blocks_per_arena; i++) + { + struct block *b = arena_to_block (a, i); + list_push_back (&d->free_list, &b->free_elem); + } + } + + /* Get a block from free list and return it. */ + b = list_entry (list_pop_front (&d->free_list), struct block, free_elem); + a = block_to_arena (b); + a->free_cnt--; + lock_release (&d->lock); + return b; +} + +/* Allocates and return A times B bytes initialized to zeroes. + Returns a null pointer if memory is not available. */ +void * +calloc (size_t a, size_t b) +{ + void *p; + size_t size; + + /* Calculate block size and make sure it fits in size_t. */ + size = a * b; + if (size < a || size < b) + return NULL; + + /* Allocate and zero memory. */ + p = malloc (size); + if (p != NULL) + memset (p, 0, size); + + return p; +} + +/* Returns the number of bytes allocated for BLOCK. */ +static size_t +block_size (void *block) +{ + struct block *b = block; + struct arena *a = block_to_arena (b); + struct desc *d = a->desc; + + return d != NULL ? d->block_size : PGSIZE * a->free_cnt - pg_ofs (block); +} + +/* Attempts to resize OLD_BLOCK to NEW_SIZE bytes, possibly + moving it in the process. + If successful, returns the new block; on failure, returns a + null pointer. + A call with null OLD_BLOCK is equivalent to malloc(NEW_SIZE). + A call with zero NEW_SIZE is equivalent to free(OLD_BLOCK). */ +void * +realloc (void *old_block, size_t new_size) +{ + if (new_size == 0) + { + free (old_block); + return NULL; + } + else + { + void *new_block = malloc (new_size); + if (old_block != NULL && new_block != NULL) + { + size_t old_size = block_size (old_block); + size_t min_size = new_size < old_size ? new_size : old_size; + memcpy (new_block, old_block, min_size); + free (old_block); + } + return new_block; + } +} + +/* Frees block P, which must have been previously allocated with + malloc(), calloc(), or realloc(). */ +void +free (void *p) +{ + if (p != NULL) + { + struct block *b = p; + struct arena *a = block_to_arena (b); + struct desc *d = a->desc; + + if (d != NULL) + { + /* It's a normal block. We handle it here. */ + +#ifndef NDEBUG + /* Clear the block to help detect use-after-free bugs. */ + memset (b, 0xcc, d->block_size); +#endif + + lock_acquire (&d->lock); + + /* Add block to free list. */ + list_push_front (&d->free_list, &b->free_elem); + + /* If the arena is now entirely unused, free it. */ + if (++a->free_cnt >= d->blocks_per_arena) + { + size_t i; + + ASSERT (a->free_cnt == d->blocks_per_arena); + for (i = 0; i < d->blocks_per_arena; i++) + { + struct block *b = arena_to_block (a, i); + list_remove (&b->free_elem); + } + palloc_free_page (a); + } + + lock_release (&d->lock); + } + else + { + /* It's a big block. Free its pages. */ + palloc_free_multiple (a, a->free_cnt); + return; + } + } +} + +/* Returns the arena that block B is inside. */ +static struct arena * +block_to_arena (struct block *b) +{ + struct arena *a = pg_round_down (b); + + /* Check that the arena is valid. */ + ASSERT (a != NULL); + ASSERT (a->magic == ARENA_MAGIC); + + /* Check that the block is properly aligned for the arena. */ + ASSERT (a->desc == NULL + || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0); + ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a); + + return a; +} + +/* Returns the (IDX - 1)'th block within arena A. */ +static struct block * +arena_to_block (struct arena *a, size_t idx) +{ + ASSERT (a != NULL); + ASSERT (a->magic == ARENA_MAGIC); + ASSERT (idx < a->desc->blocks_per_arena); + return (struct block *) ((uint8_t *) a + + sizeof *a + + idx * a->desc->block_size); +} diff --git a/pintos-env/pintos/threads/malloc.h b/pintos-env/pintos/threads/malloc.h new file mode 100755 index 0000000..bc55d36 --- /dev/null +++ b/pintos-env/pintos/threads/malloc.h @@ -0,0 +1,13 @@ +#ifndef THREADS_MALLOC_H +#define THREADS_MALLOC_H + +#include +#include + +void malloc_init (void); +void *malloc (size_t) __attribute__ ((malloc)); +void *calloc (size_t, size_t) __attribute__ ((malloc)); +void *realloc (void *, size_t); +void free (void *); + +#endif /* threads/malloc.h */ diff --git a/pintos-env/pintos/threads/palloc.c b/pintos-env/pintos/threads/palloc.c new file mode 100755 index 0000000..4fc8394 --- /dev/null +++ b/pintos-env/pintos/threads/palloc.c @@ -0,0 +1,182 @@ +#include "threads/palloc.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "threads/loader.h" +#include "threads/synch.h" +#include "threads/vaddr.h" + +/* Page allocator. Hands out memory in page-size (or + page-multiple) chunks. See malloc.h for an allocator that + hands out smaller chunks. + + System memory is divided into two "pools" called the kernel + and user pools. The user pool is for user (virtual) memory + pages, the kernel pool for everything else. The idea here is + that the kernel needs to have memory for its own operations + even if user processes are swapping like mad. + + By default, half of system RAM is given to the kernel pool and + half to the user pool. That should be huge overkill for the + kernel pool, but that's just fine for demonstration purposes. */ + +/* A memory pool. */ +struct pool + { + struct lock lock; /* Mutual exclusion. */ + struct bitmap *used_map; /* Bitmap of free pages. */ + uint8_t *base; /* Base of pool. */ + }; + +/* Two pools: one for kernel data, one for user pages. */ +static struct pool kernel_pool, user_pool; + +static void init_pool (struct pool *, void *base, size_t page_cnt, + const char *name); +static bool page_from_pool (const struct pool *, void *page); + +/* Initializes the page allocator. At most USER_PAGE_LIMIT + pages are put into the user pool. */ +void +palloc_init (size_t user_page_limit) +{ + /* Free memory starts at 1 MB and runs to the end of RAM. */ + uint8_t *free_start = ptov (1024 * 1024); + uint8_t *free_end = ptov (init_ram_pages * PGSIZE); + size_t free_pages = (free_end - free_start) / PGSIZE; + size_t user_pages = free_pages / 2; + size_t kernel_pages; + if (user_pages > user_page_limit) + user_pages = user_page_limit; + kernel_pages = free_pages - user_pages; + + /* Give half of memory to kernel, half to user. */ + init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool"); + init_pool (&user_pool, free_start + kernel_pages * PGSIZE, + user_pages, "user pool"); +} + +/* Obtains and returns a group of PAGE_CNT contiguous free pages. + If PAL_USER is set, the pages are obtained from the user pool, + otherwise from the kernel pool. If PAL_ZERO is set in FLAGS, + then the pages are filled with zeros. If too few pages are + available, returns a null pointer, unless PAL_ASSERT is set in + FLAGS, in which case the kernel panics. */ +void * +palloc_get_multiple (enum palloc_flags flags, size_t page_cnt) +{ + struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool; + void *pages; + size_t page_idx; + + if (page_cnt == 0) + return NULL; + + lock_acquire (&pool->lock); + page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false); + lock_release (&pool->lock); + + if (page_idx != BITMAP_ERROR) + pages = pool->base + PGSIZE * page_idx; + else + pages = NULL; + + if (pages != NULL) + { + if (flags & PAL_ZERO) + memset (pages, 0, PGSIZE * page_cnt); + } + else + { + if (flags & PAL_ASSERT) + PANIC ("palloc_get: out of pages"); + } + + return pages; +} + +/* Obtains a single free page and returns its kernel virtual + address. + If PAL_USER is set, the page is obtained from the user pool, + otherwise from the kernel pool. If PAL_ZERO is set in FLAGS, + then the page is filled with zeros. If no pages are + available, returns a null pointer, unless PAL_ASSERT is set in + FLAGS, in which case the kernel panics. */ +void * +palloc_get_page (enum palloc_flags flags) +{ + return palloc_get_multiple (flags, 1); +} + +/* Frees the PAGE_CNT pages starting at PAGES. */ +void +palloc_free_multiple (void *pages, size_t page_cnt) +{ + struct pool *pool; + size_t page_idx; + + ASSERT (pg_ofs (pages) == 0); + if (pages == NULL || page_cnt == 0) + return; + + if (page_from_pool (&kernel_pool, pages)) + pool = &kernel_pool; + else if (page_from_pool (&user_pool, pages)) + pool = &user_pool; + else + NOT_REACHED (); + + page_idx = pg_no (pages) - pg_no (pool->base); + +#ifndef NDEBUG + memset (pages, 0xcc, PGSIZE * page_cnt); +#endif + + ASSERT (bitmap_all (pool->used_map, page_idx, page_cnt)); + bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false); +} + +/* Frees the page at PAGE. */ +void +palloc_free_page (void *page) +{ + palloc_free_multiple (page, 1); +} + +/* Initializes pool P as starting at START and ending at END, + naming it NAME for debugging purposes. */ +static void +init_pool (struct pool *p, void *base, size_t page_cnt, const char *name) +{ + /* We'll put the pool's used_map at its base. + Calculate the space needed for the bitmap + and subtract it from the pool's size. */ + size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE); + if (bm_pages > page_cnt) + PANIC ("Not enough memory in %s for bitmap.", name); + page_cnt -= bm_pages; + + printf ("%zu pages available in %s.\n", page_cnt, name); + + /* Initialize the pool. */ + lock_init (&p->lock); + p->used_map = bitmap_create_in_buf (page_cnt, base, bm_pages * PGSIZE); + p->base = base + bm_pages * PGSIZE; +} + +/* Returns true if PAGE was allocated from POOL, + false otherwise. */ +static bool +page_from_pool (const struct pool *pool, void *page) +{ + size_t page_no = pg_no (page); + size_t start_page = pg_no (pool->base); + size_t end_page = start_page + bitmap_size (pool->used_map); + + return page_no >= start_page && page_no < end_page; +} diff --git a/pintos-env/pintos/threads/palloc.h b/pintos-env/pintos/threads/palloc.h new file mode 100755 index 0000000..cb3b70e --- /dev/null +++ b/pintos-env/pintos/threads/palloc.h @@ -0,0 +1,20 @@ +#ifndef THREADS_PALLOC_H +#define THREADS_PALLOC_H + +#include + +/* How to allocate pages. */ +enum palloc_flags + { + PAL_ASSERT = 001, /* Panic on failure. */ + PAL_ZERO = 002, /* Zero page contents. */ + PAL_USER = 004 /* User page. */ + }; + +void palloc_init (size_t user_page_limit); +void *palloc_get_page (enum palloc_flags); +void *palloc_get_multiple (enum palloc_flags, size_t page_cnt); +void palloc_free_page (void *); +void palloc_free_multiple (void *, size_t page_cnt); + +#endif /* threads/palloc.h */ diff --git a/pintos-env/pintos/threads/pte.h b/pintos-env/pintos/threads/pte.h new file mode 100755 index 0000000..1660727 --- /dev/null +++ b/pintos-env/pintos/threads/pte.h @@ -0,0 +1,107 @@ +#ifndef THREADS_PTE_H +#define THREADS_PTE_H + +#include "threads/vaddr.h" + +/* Functions and macros for working with x86 hardware page + tables. + + See vaddr.h for more generic functions and macros for virtual + addresses. + + Virtual addresses are structured as follows: + + 31 22 21 12 11 0 + +----------------------+----------------------+----------------------+ + | Page Directory Index | Page Table Index | Page Offset | + +----------------------+----------------------+----------------------+ +*/ + +/* Page table index (bits 12:21). */ +#define PTSHIFT PGBITS /* First page table bit. */ +#define PTBITS 10 /* Number of page table bits. */ +#define PTSPAN (1 << PTBITS << PGBITS) /* Bytes covered by a page table. */ +#define PTMASK BITMASK(PTSHIFT, PTBITS) /* Page table bits (12:21). */ + +/* Page directory index (bits 22:31). */ +#define PDSHIFT (PTSHIFT + PTBITS) /* First page directory bit. */ +#define PDBITS 10 /* Number of page dir bits. */ +#define PDMASK BITMASK(PDSHIFT, PDBITS) /* Page directory bits (22:31). */ + +/* Obtains page table index from a virtual address. */ +static inline unsigned pt_no (const void *va) { + return ((uintptr_t) va & PTMASK) >> PTSHIFT; +} + +/* Obtains page directory index from a virtual address. */ +static inline uintptr_t pd_no (const void *va) { + return (uintptr_t) va >> PDSHIFT; +} + +/* Page directory and page table entries. + + For more information see the section on page tables in the + Pintos reference guide chapter, or [IA32-v3a] 3.7.6 + "Page-Directory and Page-Table Entries". + + PDEs and PTEs share a common format: + + 31 12 11 0 + +------------------------------------+------------------------+ + | Physical Address | Flags | + +------------------------------------+------------------------+ + + In a PDE, the physical address points to a page table. + In a PTE, the physical address points to a data or code page. + The important flags are listed below. + When a PDE or PTE is not "present", the other flags are + ignored. + A PDE or PTE that is initialized to 0 will be interpreted as + "not present", which is just fine. */ +#define PTE_FLAGS 0x00000fff /* Flag bits. */ +#define PTE_ADDR 0xfffff000 /* Address bits. */ +#define PTE_AVL 0x00000e00 /* Bits available for OS use. */ +#define PTE_P 0x1 /* 1=present, 0=not present. */ +#define PTE_W 0x2 /* 1=read/write, 0=read-only. */ +#define PTE_U 0x4 /* 1=user/kernel, 0=kernel only. */ +#define PTE_A 0x20 /* 1=accessed, 0=not acccessed. */ +#define PTE_D 0x40 /* 1=dirty, 0=not dirty (PTEs only). */ + +/* Returns a PDE that points to page table PT. */ +static inline uint32_t pde_create (uint32_t *pt) { + ASSERT (pg_ofs (pt) == 0); + return vtop (pt) | PTE_U | PTE_P | PTE_W; +} + +/* Returns a pointer to the page table that page directory entry + PDE, which must "present", points to. */ +static inline uint32_t *pde_get_pt (uint32_t pde) { + ASSERT (pde & PTE_P); + return ptov (pde & PTE_ADDR); +} + +/* Returns a PTE that points to PAGE. + The PTE's page is readable. + If WRITABLE is true then it will be writable as well. + The page will be usable only by ring 0 code (the kernel). */ +static inline uint32_t pte_create_kernel (void *page, bool writable) { + ASSERT (pg_ofs (page) == 0); + return vtop (page) | PTE_P | (writable ? PTE_W : 0); +} + +/* Returns a PTE that points to PAGE. + The PTE's page is readable. + If WRITABLE is true then it will be writable as well. + The page will be usable by both user and kernel code. */ +static inline uint32_t pte_create_user (void *page, bool writable) { + return pte_create_kernel (page, writable) | PTE_U; +} + +/* Returns a pointer to the page that page table entry PTE points + to. */ +static inline void *pte_get_page (uint32_t pte) { + return ptov (pte & PTE_ADDR); +} + +#endif /* threads/pte.h */ + diff --git a/pintos-env/pintos/threads/start.S b/pintos-env/pintos/threads/start.S new file mode 100755 index 0000000..29ffa7a --- /dev/null +++ b/pintos-env/pintos/threads/start.S @@ -0,0 +1,204 @@ + #include "threads/loader.h" + +#### Kernel startup code. + +#### The loader (in loader.S) loads the kernel at physical address +#### 0x20000 (128 kB) and jumps to "start", defined here. This code +#### switches from real mode to 32-bit protected mode and calls +#### main(). + +/* Flags in control register 0. */ +#define CR0_PE 0x00000001 /* Protection Enable. */ +#define CR0_EM 0x00000004 /* (Floating-point) Emulation. */ +#define CR0_PG 0x80000000 /* Paging. */ +#define CR0_WP 0x00010000 /* Write-Protect enable in kernel mode. */ + + .section .start + +# The following code runs in real mode, which is a 16-bit code segment. + .code16 + +.func start +.globl start +start: + +# The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000, +# but we should initialize the other segment registers. + + mov $0x2000, %ax + mov %ax, %ds + mov %ax, %es + +# Set string instructions to go upward. + cld + +#### Get memory size, via interrupt 15h function 88h (see [IntrList]), +#### which returns AX = (kB of physical memory) - 1024. This only +#### works for memory sizes <= 65 MB, which should be fine for our +#### purposes. We cap memory at 64 MB because that's all we prepare +#### page tables for, below. + + movb $0x88, %ah + int $0x15 + addl $1024, %eax # Total kB memory + cmp $0x10000, %eax # Cap at 64 MB + jbe 1f + mov $0x10000, %eax +1: shrl $2, %eax # Total 4 kB pages + addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000 + +#### Enable A20. Address line 20 is tied low when the machine boots, +#### which prevents addressing memory about 1 MB. This code fixes it. + +# Poll status register while busy. + +1: inb $0x64, %al + testb $0x2, %al + jnz 1b + +# Send command for writing output port. + + movb $0xd1, %al + outb %al, $0x64 + +# Poll status register while busy. + +1: inb $0x64, %al + testb $0x2, %al + jnz 1b + +# Enable A20 line. + + movb $0xdf, %al + outb %al, $0x60 + +# Poll status register while busy. + +1: inb $0x64, %al + testb $0x2, %al + jnz 1b + +#### Create temporary page directory and page table and set page +#### directory base register. + +# Create page directory at 0xf000 (60 kB) and fill with zeroes. + mov $0xf00, %ax + mov %ax, %es + subl %eax, %eax + subl %edi, %edi + movl $0x400, %ecx + rep stosl + +# Add PDEs to point to page tables for the first 64 MB of RAM. +# Also add identical PDEs starting at LOADER_PHYS_BASE. +# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" +# for a description of the bits in %eax. + + movl $0x10007, %eax + movl $0x11, %ecx + subl %edi, %edi +1: movl %eax, %es:(%di) + movl %eax, %es:LOADER_PHYS_BASE >> 20(%di) + addw $4, %di + addl $0x1000, %eax + loop 1b + +# Set up page tables for one-to-map linear to physical map for the +# first 64 MB of RAM. +# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" +# for a description of the bits in %eax. + + movw $0x1000, %ax + movw %ax, %es + movl $0x7, %eax + movl $0x4000, %ecx + subl %edi, %edi +1: movl %eax, %es:(%di) + addw $4, %di + addl $0x1000, %eax + loop 1b + +# Set page directory base register. + + movl $0xf000, %eax + movl %eax, %cr3 + +#### Switch to protected mode. + +# First, disable interrupts. We won't set up the IDT until we get +# into C code, so any interrupt would blow us away. + + cli + +# Protected mode requires a GDT, so point the GDTR to our GDT. +# We need a data32 prefix to ensure that all 32 bits of the GDT +# descriptor are loaded (default is to load only 24 bits). +# The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit +# relocations. + + data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000 + +# Then we turn on the following bits in CR0: +# PE (Protect Enable): this turns on protected mode. +# PG (Paging): turns on paging. +# WP (Write Protect): if unset, ring 0 code ignores +# write-protect bits in page tables (!). +# EM (Emulation): forces floating-point instructions to trap. +# We don't support floating point. + + movl %cr0, %eax + orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax + movl %eax, %cr0 + +# We're now in protected mode in a 16-bit segment. The CPU still has +# the real-mode code segment cached in %cs's segment descriptor. We +# need to reload %cs, and the easiest way is to use a far jump. +# Because we're not running in a 32-bit segment the data32 prefix is +# needed to jump to a 32-bit offset in the target segment. + + data32 ljmp $SEL_KCSEG, $1f + +# We're now in protected mode in a 32-bit segment. +# Let the assembler know. + + .code32 + +# Reload all the other segment registers and the stack pointer to +# point into our new GDT. + +1: mov $SEL_KDSEG, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + addl $LOADER_PHYS_BASE, %esp + movl $0, %ebp # Null-terminate main()'s backtrace + +#### Call main(). + + call main + +# main() shouldn't ever return. If it does, spin. + +1: jmp 1b +.endfunc + +#### GDT + + .align 8 +gdt: + .quad 0x0000000000000000 # Null segment. Not used by CPU. + .quad 0x00cf9a000000ffff # System code, base 0, limit 4 GB. + .quad 0x00cf92000000ffff # System data, base 0, limit 4 GB. + +gdtdesc: + .word gdtdesc - gdt - 1 # Size of the GDT, minus 1 byte. + .long gdt # Address of the GDT. + +#### Physical memory size in 4 kB pages. This is exported to the rest +#### of the kernel. +.globl init_ram_pages +init_ram_pages: + .long 0 + diff --git a/pintos-env/pintos/threads/switch.S b/pintos-env/pintos/threads/switch.S new file mode 100755 index 0000000..feca86c --- /dev/null +++ b/pintos-env/pintos/threads/switch.S @@ -0,0 +1,65 @@ +#include "threads/switch.h" + +#### struct thread *switch_threads (struct thread *cur, struct thread *next); +#### +#### Switches from CUR, which must be the running thread, to NEXT, +#### which must also be running switch_threads(), returning CUR in +#### NEXT's context. +#### +#### This function works by assuming that the thread we're switching +#### into is also running switch_threads(). Thus, all it has to do is +#### preserve a few registers on the stack, then switch stacks and +#### restore the registers. As part of switching stacks we record the +#### current stack pointer in CUR's thread structure. + +.globl switch_threads +.func switch_threads +switch_threads: + # Save caller's register state. + # + # Note that the SVR4 ABI allows us to destroy %eax, %ecx, %edx, + # but requires us to preserve %ebx, %ebp, %esi, %edi. See + # [SysV-ABI-386] pages 3-11 and 3-12 for details. + # + # This stack frame must match the one set up by thread_create() + # in size. + pushl %ebx + pushl %ebp + pushl %esi + pushl %edi + + # Get offsetof (struct thread, stack). +.globl thread_stack_ofs + mov thread_stack_ofs, %edx + + # Save current stack pointer to old thread's stack, if any. + movl SWITCH_CUR(%esp), %eax + movl %esp, (%eax,%edx,1) + + # Restore stack pointer from new thread's stack. + movl SWITCH_NEXT(%esp), %ecx + movl (%ecx,%edx,1), %esp + + # Restore caller's register state. + popl %edi + popl %esi + popl %ebp + popl %ebx + ret +.endfunc + +.globl switch_entry +.func switch_entry +switch_entry: + # Discard switch_threads() arguments. + addl $8, %esp + + # Call thread_schedule_tail(prev). + pushl %eax +.globl thread_schedule_tail + call thread_schedule_tail + addl $4, %esp + + # Start thread proper. + ret +.endfunc diff --git a/pintos-env/pintos/threads/switch.h b/pintos-env/pintos/threads/switch.h new file mode 100755 index 0000000..cc156b6 --- /dev/null +++ b/pintos-env/pintos/threads/switch.h @@ -0,0 +1,39 @@ +#ifndef THREADS_SWITCH_H +#define THREADS_SWITCH_H + +#ifndef __ASSEMBLER__ +/* switch_thread()'s stack frame. */ +struct switch_threads_frame + { + uint32_t edi; /* 0: Saved %edi. */ + uint32_t esi; /* 4: Saved %esi. */ + uint32_t ebp; /* 8: Saved %ebp. */ + uint32_t ebx; /* 12: Saved %ebx. */ + void (*eip) (void); /* 16: Return address. */ + struct thread *cur; /* 20: switch_threads()'s CUR argument. */ + struct thread *next; /* 24: switch_threads()'s NEXT argument. */ + }; + +/* Switches from CUR, which must be the running thread, to NEXT, + which must also be running switch_threads(), returning CUR in + NEXT's context. */ +struct thread *switch_threads (struct thread *cur, struct thread *next); + +/* Stack frame for switch_entry(). */ +struct switch_entry_frame + { + void (*eip) (void); + }; + +void switch_entry (void); + +/* Pops the CUR and NEXT arguments off the stack, for use in + initializing threads. */ +void switch_thunk (void); +#endif + +/* Offsets used by switch.S. */ +#define SWITCH_CUR 20 +#define SWITCH_NEXT 24 + +#endif /* threads/switch.h */ diff --git a/pintos-env/pintos/threads/synch.c b/pintos-env/pintos/threads/synch.c new file mode 100755 index 0000000..317c68a --- /dev/null +++ b/pintos-env/pintos/threads/synch.c @@ -0,0 +1,338 @@ +/* This file is derived from source code for the Nachos + instructional operating system. The Nachos copyright notice + is reproduced in full below. */ + +/* Copyright (c) 1992-1996 The Regents of the University of California. + All rights reserved. + + Permission to use, copy, modify, and distribute this software + and its documentation for any purpose, without fee, and + without written agreement is hereby granted, provided that the + above copyright notice and the following two paragraphs appear + in all copies of this software. + + IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO + ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE + AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA + HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" + BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +*/ + +#include "threads/synch.h" +#include +#include +#include "threads/interrupt.h" +#include "threads/thread.h" + +/* Initializes semaphore SEMA to VALUE. A semaphore is a + nonnegative integer along with two atomic operators for + manipulating it: + + - down or "P": wait for the value to become positive, then + decrement it. + + - up or "V": increment the value (and wake up one waiting + thread, if any). */ +void +sema_init (struct semaphore *sema, unsigned value) +{ + ASSERT (sema != NULL); + + sema->value = value; + list_init (&sema->waiters); +} + +/* Down or "P" operation on a semaphore. Waits for SEMA's value + to become positive and then atomically decrements it. + + This function may sleep, so it must not be called within an + interrupt handler. This function may be called with + interrupts disabled, but if it sleeps then the next scheduled + thread will probably turn interrupts back on. */ +void +sema_down (struct semaphore *sema) +{ + enum intr_level old_level; + + ASSERT (sema != NULL); + ASSERT (!intr_context ()); + + old_level = intr_disable (); + while (sema->value == 0) + { + list_push_back (&sema->waiters, &thread_current ()->elem); + thread_block (); + } + sema->value--; + intr_set_level (old_level); +} + +/* Down or "P" operation on a semaphore, but only if the + semaphore is not already 0. Returns true if the semaphore is + decremented, false otherwise. + + This function may be called from an interrupt handler. */ +bool +sema_try_down (struct semaphore *sema) +{ + enum intr_level old_level; + bool success; + + ASSERT (sema != NULL); + + old_level = intr_disable (); + if (sema->value > 0) + { + sema->value--; + success = true; + } + else + success = false; + intr_set_level (old_level); + + return success; +} + +/* Up or "V" operation on a semaphore. Increments SEMA's value + and wakes up one thread of those waiting for SEMA, if any. + + This function may be called from an interrupt handler. */ +void +sema_up (struct semaphore *sema) +{ + enum intr_level old_level; + + ASSERT (sema != NULL); + + old_level = intr_disable (); + if (!list_empty (&sema->waiters)) + thread_unblock (list_entry (list_pop_front (&sema->waiters), + struct thread, elem)); + sema->value++; + intr_set_level (old_level); +} + +static void sema_test_helper (void *sema_); + +/* Self-test for semaphores that makes control "ping-pong" + between a pair of threads. Insert calls to printf() to see + what's going on. */ +void +sema_self_test (void) +{ + struct semaphore sema[2]; + int i; + + printf ("Testing semaphores..."); + sema_init (&sema[0], 0); + sema_init (&sema[1], 0); + thread_create ("sema-test", PRI_DEFAULT, sema_test_helper, &sema); + for (i = 0; i < 10; i++) + { + sema_up (&sema[0]); + sema_down (&sema[1]); + } + printf ("done.\n"); +} + +/* Thread function used by sema_self_test(). */ +static void +sema_test_helper (void *sema_) +{ + struct semaphore *sema = sema_; + int i; + + for (i = 0; i < 10; i++) + { + sema_down (&sema[0]); + sema_up (&sema[1]); + } +} + +/* Initializes LOCK. A lock can be held by at most a single + thread at any given time. Our locks are not "recursive", that + is, it is an error for the thread currently holding a lock to + try to acquire that lock. + + A lock is a specialization of a semaphore with an initial + value of 1. The difference between a lock and such a + semaphore is twofold. First, a semaphore can have a value + greater than 1, but a lock can only be owned by a single + thread at a time. Second, a semaphore does not have an owner, + meaning that one thread can "down" the semaphore and then + another one "up" it, but with a lock the same thread must both + acquire and release it. When these restrictions prove + onerous, it's a good sign that a semaphore should be used, + instead of a lock. */ +void +lock_init (struct lock *lock) +{ + ASSERT (lock != NULL); + + lock->holder = NULL; + sema_init (&lock->semaphore, 1); +} + +/* Acquires LOCK, sleeping until it becomes available if + necessary. The lock must not already be held by the current + thread. + + This function may sleep, so it must not be called within an + interrupt handler. This function may be called with + interrupts disabled, but interrupts will be turned back on if + we need to sleep. */ +void +lock_acquire (struct lock *lock) +{ + ASSERT (lock != NULL); + ASSERT (!intr_context ()); + ASSERT (!lock_held_by_current_thread (lock)); + + sema_down (&lock->semaphore); + lock->holder = thread_current (); +} + +/* Tries to acquires LOCK and returns true if successful or false + on failure. The lock must not already be held by the current + thread. + + This function will not sleep, so it may be called within an + interrupt handler. */ +bool +lock_try_acquire (struct lock *lock) +{ + bool success; + + ASSERT (lock != NULL); + ASSERT (!lock_held_by_current_thread (lock)); + + success = sema_try_down (&lock->semaphore); + if (success) + lock->holder = thread_current (); + return success; +} + +/* Releases LOCK, which must be owned by the current thread. + + An interrupt handler cannot acquire a lock, so it does not + make sense to try to release a lock within an interrupt + handler. */ +void +lock_release (struct lock *lock) +{ + ASSERT (lock != NULL); + ASSERT (lock_held_by_current_thread (lock)); + + lock->holder = NULL; + sema_up (&lock->semaphore); +} + +/* Returns true if the current thread holds LOCK, false + otherwise. (Note that testing whether some other thread holds + a lock would be racy.) */ +bool +lock_held_by_current_thread (const struct lock *lock) +{ + ASSERT (lock != NULL); + + return lock->holder == thread_current (); +} + +/* One semaphore in a list. */ +struct semaphore_elem + { + struct list_elem elem; /* List element. */ + struct semaphore semaphore; /* This semaphore. */ + }; + +/* Initializes condition variable COND. A condition variable + allows one piece of code to signal a condition and cooperating + code to receive the signal and act upon it. */ +void +cond_init (struct condition *cond) +{ + ASSERT (cond != NULL); + + list_init (&cond->waiters); +} + +/* Atomically releases LOCK and waits for COND to be signaled by + some other piece of code. After COND is signaled, LOCK is + reacquired before returning. LOCK must be held before calling + this function. + + The monitor implemented by this function is "Mesa" style, not + "Hoare" style, that is, sending and receiving a signal are not + an atomic operation. Thus, typically the caller must recheck + the condition after the wait completes and, if necessary, wait + again. + + A given condition variable is associated with only a single + lock, but one lock may be associated with any number of + condition variables. That is, there is a one-to-many mapping + from locks to condition variables. + + This function may sleep, so it must not be called within an + interrupt handler. This function may be called with + interrupts disabled, but interrupts will be turned back on if + we need to sleep. */ +void +cond_wait (struct condition *cond, struct lock *lock) +{ + struct semaphore_elem waiter; + + ASSERT (cond != NULL); + ASSERT (lock != NULL); + ASSERT (!intr_context ()); + ASSERT (lock_held_by_current_thread (lock)); + + sema_init (&waiter.semaphore, 0); + list_push_back (&cond->waiters, &waiter.elem); + lock_release (lock); + sema_down (&waiter.semaphore); + lock_acquire (lock); +} + +/* If any threads are waiting on COND (protected by LOCK), then + this function signals one of them to wake up from its wait. + LOCK must be held before calling this function. + + An interrupt handler cannot acquire a lock, so it does not + make sense to try to signal a condition variable within an + interrupt handler. */ +void +cond_signal (struct condition *cond, struct lock *lock UNUSED) +{ + ASSERT (cond != NULL); + ASSERT (lock != NULL); + ASSERT (!intr_context ()); + ASSERT (lock_held_by_current_thread (lock)); + + if (!list_empty (&cond->waiters)) + sema_up (&list_entry (list_pop_front (&cond->waiters), + struct semaphore_elem, elem)->semaphore); +} + +/* Wakes up all threads, if any, waiting on COND (protected by + LOCK). LOCK must be held before calling this function. + + An interrupt handler cannot acquire a lock, so it does not + make sense to try to signal a condition variable within an + interrupt handler. */ +void +cond_broadcast (struct condition *cond, struct lock *lock) +{ + ASSERT (cond != NULL); + ASSERT (lock != NULL); + + while (!list_empty (&cond->waiters)) + cond_signal (cond, lock); +} diff --git a/pintos-env/pintos/threads/synch.h b/pintos-env/pintos/threads/synch.h new file mode 100755 index 0000000..a19e88b --- /dev/null +++ b/pintos-env/pintos/threads/synch.h @@ -0,0 +1,51 @@ +#ifndef THREADS_SYNCH_H +#define THREADS_SYNCH_H + +#include +#include + +/* A counting semaphore. */ +struct semaphore + { + unsigned value; /* Current value. */ + struct list waiters; /* List of waiting threads. */ + }; + +void sema_init (struct semaphore *, unsigned value); +void sema_down (struct semaphore *); +bool sema_try_down (struct semaphore *); +void sema_up (struct semaphore *); +void sema_self_test (void); + +/* Lock. */ +struct lock + { + struct thread *holder; /* Thread holding lock (for debugging). */ + struct semaphore semaphore; /* Binary semaphore controlling access. */ + }; + +void lock_init (struct lock *); +void lock_acquire (struct lock *); +bool lock_try_acquire (struct lock *); +void lock_release (struct lock *); +bool lock_held_by_current_thread (const struct lock *); + +/* Condition variable. */ +struct condition + { + struct list waiters; /* List of waiting threads. */ + }; + +void cond_init (struct condition *); +void cond_wait (struct condition *, struct lock *); +void cond_signal (struct condition *, struct lock *); +void cond_broadcast (struct condition *, struct lock *); + +/* Optimization barrier. + + The compiler will not reorder operations across an + optimization barrier. See "Optimization Barriers" in the + reference guide for more information.*/ +#define barrier() asm volatile ("" : : : "memory") + +#endif /* threads/synch.h */ diff --git a/pintos-env/pintos/threads/thread.c b/pintos-env/pintos/threads/thread.c new file mode 100755 index 0000000..955ccdc --- /dev/null +++ b/pintos-env/pintos/threads/thread.c @@ -0,0 +1,587 @@ +#include "threads/thread.h" +#include +#include +#include +#include +#include +#include "threads/flags.h" +#include "threads/interrupt.h" +#include "threads/intr-stubs.h" +#include "threads/palloc.h" +#include "threads/switch.h" +#include "threads/synch.h" +#include "threads/vaddr.h" +#ifdef USERPROG +#include "userprog/process.h" +#endif + +/* Random value for struct thread's `magic' member. + Used to detect stack overflow. See the big comment at the top + of thread.h for details. */ +#define THREAD_MAGIC 0xcd6abf4b + +/* List of processes in THREAD_READY state, that is, processes + that are ready to run but not actually running. */ +static struct list ready_list; + +/* List of all processes. Processes are added to this list + when they are first scheduled and removed when they exit. */ +static struct list all_list; + +/* Idle thread. */ +static struct thread *idle_thread; + +/* Initial thread, the thread running init.c:main(). */ +static struct thread *initial_thread; + +/* Lock used by allocate_tid(). */ +static struct lock tid_lock; + +/* Stack frame for kernel_thread(). */ +struct kernel_thread_frame + { + void *eip; /* Return address. */ + thread_func *function; /* Function to call. */ + void *aux; /* Auxiliary data for function. */ + }; + +/* Statistics. */ +static long long idle_ticks; /* # of timer ticks spent idle. */ +static long long kernel_ticks; /* # of timer ticks in kernel threads. */ +static long long user_ticks; /* # of timer ticks in user programs. */ + +/* Scheduling. */ +#define TIME_SLICE 4 /* # of timer ticks to give each thread. */ +static unsigned thread_ticks; /* # of timer ticks since last yield. */ + +/* If false (default), use round-robin scheduler. + If true, use multi-level feedback queue scheduler. + Controlled by kernel command-line option "-o mlfqs". */ +bool thread_mlfqs; + +static void kernel_thread (thread_func *, void *aux); + +static void idle (void *aux UNUSED); +static struct thread *running_thread (void); +static struct thread *next_thread_to_run (void); +static void init_thread (struct thread *, const char *name, int priority); +static bool is_thread (struct thread *) UNUSED; +static void *alloc_frame (struct thread *, size_t size); +static void schedule (void); +void thread_schedule_tail (struct thread *prev); +static tid_t allocate_tid (void); + +/* Initializes the threading system by transforming the code + that's currently running into a thread. This can't work in + general and it is possible in this case only because loader.S + was careful to put the bottom of the stack at a page boundary. + + Also initializes the run queue and the tid lock. + + After calling this function, be sure to initialize the page + allocator before trying to create any threads with + thread_create(). + + It is not safe to call thread_current() until this function + finishes. */ +void +thread_init (void) +{ + ASSERT (intr_get_level () == INTR_OFF); + + lock_init (&tid_lock); + list_init (&ready_list); + list_init (&all_list); + + /* Set up a thread structure for the running thread. */ + initial_thread = running_thread (); + init_thread (initial_thread, "main", PRI_DEFAULT); + initial_thread->status = THREAD_RUNNING; + initial_thread->tid = allocate_tid (); +} + +/* Starts preemptive thread scheduling by enabling interrupts. + Also creates the idle thread. */ +void +thread_start (void) +{ + /* Create the idle thread. */ + struct semaphore idle_started; + sema_init (&idle_started, 0); + thread_create ("idle", PRI_MIN, idle, &idle_started); + + /* Start preemptive thread scheduling. */ + intr_enable (); + + /* Wait for the idle thread to initialize idle_thread. */ + sema_down (&idle_started); +} + +/* Called by the timer interrupt handler at each timer tick. + Thus, this function runs in an external interrupt context. */ +void +thread_tick (void) +{ + struct thread *t = thread_current (); + + /* Update statistics. */ + if (t == idle_thread) + idle_ticks++; +#ifdef USERPROG + else if (t->pagedir != NULL) + user_ticks++; +#endif + else + kernel_ticks++; + + /* Enforce preemption. */ + if (++thread_ticks >= TIME_SLICE) + intr_yield_on_return (); +} + +/* Prints thread statistics. */ +void +thread_print_stats (void) +{ + printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n", + idle_ticks, kernel_ticks, user_ticks); +} + +/* Creates a new kernel thread named NAME with the given initial + PRIORITY, which executes FUNCTION passing AUX as the argument, + and adds it to the ready queue. Returns the thread identifier + for the new thread, or TID_ERROR if creation fails. + + If thread_start() has been called, then the new thread may be + scheduled before thread_create() returns. It could even exit + before thread_create() returns. Contrariwise, the original + thread may run for any amount of time before the new thread is + scheduled. Use a semaphore or some other form of + synchronization if you need to ensure ordering. + + The code provided sets the new thread's `priority' member to + PRIORITY, but no actual priority scheduling is implemented. + Priority scheduling is the goal of Problem 1-3. */ +tid_t +thread_create (const char *name, int priority, + thread_func *function, void *aux) +{ + struct thread *t; + struct kernel_thread_frame *kf; + struct switch_entry_frame *ef; + struct switch_threads_frame *sf; + tid_t tid; + enum intr_level old_level; + + ASSERT (function != NULL); + + /* Allocate thread. */ + t = palloc_get_page (PAL_ZERO); + if (t == NULL) + return TID_ERROR; + + /* Initialize thread. */ + init_thread (t, name, priority); + tid = t->tid = allocate_tid (); + + /* Prepare thread for first run by initializing its stack. + Do this atomically so intermediate values for the 'stack' + member cannot be observed. */ + old_level = intr_disable (); + + /* Stack frame for kernel_thread(). */ + kf = alloc_frame (t, sizeof *kf); + kf->eip = NULL; + kf->function = function; + kf->aux = aux; + + /* Stack frame for switch_entry(). */ + ef = alloc_frame (t, sizeof *ef); + ef->eip = (void (*) (void)) kernel_thread; + + /* Stack frame for switch_threads(). */ + sf = alloc_frame (t, sizeof *sf); + sf->eip = switch_entry; + sf->ebp = 0; + + intr_set_level (old_level); + + /* Add to run queue. */ + thread_unblock (t); + + return tid; +} + +/* Puts the current thread to sleep. It will not be scheduled + again until awoken by thread_unblock(). + + This function must be called with interrupts turned off. It + is usually a better idea to use one of the synchronization + primitives in synch.h. */ +void +thread_block (void) +{ + ASSERT (!intr_context ()); + ASSERT (intr_get_level () == INTR_OFF); + + thread_current ()->status = THREAD_BLOCKED; + schedule (); +} + +/* Transitions a blocked thread T to the ready-to-run state. + This is an error if T is not blocked. (Use thread_yield() to + make the running thread ready.) + + This function does not preempt the running thread. This can + be important: if the caller had disabled interrupts itself, + it may expect that it can atomically unblock a thread and + update other data. */ +void +thread_unblock (struct thread *t) +{ + enum intr_level old_level; + + ASSERT (is_thread (t)); + + old_level = intr_disable (); + ASSERT (t->status == THREAD_BLOCKED); + list_push_back (&ready_list, &t->elem); + t->status = THREAD_READY; + intr_set_level (old_level); +} + +/* Returns the name of the running thread. */ +const char * +thread_name (void) +{ + return thread_current ()->name; +} + +/* Returns the running thread. + This is running_thread() plus a couple of sanity checks. + See the big comment at the top of thread.h for details. */ +struct thread * +thread_current (void) +{ + struct thread *t = running_thread (); + + /* Make sure T is really a thread. + If either of these assertions fire, then your thread may + have overflowed its stack. Each thread has less than 4 kB + of stack, so a few big automatic arrays or moderate + recursion can cause stack overflow. */ + ASSERT (is_thread (t)); + ASSERT (t->status == THREAD_RUNNING); + + return t; +} + +/* Returns the running thread's tid. */ +tid_t +thread_tid (void) +{ + return thread_current ()->tid; +} + +/* Deschedules the current thread and destroys it. Never + returns to the caller. */ +void +thread_exit (void) +{ + ASSERT (!intr_context ()); + +#ifdef USERPROG + process_exit (); +#endif + + /* Remove thread from all threads list, set our status to dying, + and schedule another process. That process will destroy us + when it calls thread_schedule_tail(). */ + intr_disable (); + list_remove (&thread_current()->allelem); + thread_current ()->status = THREAD_DYING; + schedule (); + NOT_REACHED (); +} + +/* Yields the CPU. The current thread is not put to sleep and + may be scheduled again immediately at the scheduler's whim. */ +void +thread_yield (void) +{ + struct thread *cur = thread_current (); + enum intr_level old_level; + + ASSERT (!intr_context ()); + + old_level = intr_disable (); + if (cur != idle_thread) + list_push_back (&ready_list, &cur->elem); + cur->status = THREAD_READY; + schedule (); + intr_set_level (old_level); +} + +/* Invoke function 'func' on all threads, passing along 'aux'. + This function must be called with interrupts off. */ +void +thread_foreach (thread_action_func *func, void *aux) +{ + struct list_elem *e; + + ASSERT (intr_get_level () == INTR_OFF); + + for (e = list_begin (&all_list); e != list_end (&all_list); + e = list_next (e)) + { + struct thread *t = list_entry (e, struct thread, allelem); + func (t, aux); + } +} + +/* Sets the current thread's priority to NEW_PRIORITY. */ +void +thread_set_priority (int new_priority) +{ + thread_current ()->priority = new_priority; +} + +/* Returns the current thread's priority. */ +int +thread_get_priority (void) +{ + return thread_current ()->priority; +} + +/* Sets the current thread's nice value to NICE. */ +void +thread_set_nice (int nice UNUSED) +{ + /* Not yet implemented. */ +} + +/* Returns the current thread's nice value. */ +int +thread_get_nice (void) +{ + /* Not yet implemented. */ + return 0; +} + +/* Returns 100 times the system load average. */ +int +thread_get_load_avg (void) +{ + /* Not yet implemented. */ + return 0; +} + +/* Returns 100 times the current thread's recent_cpu value. */ +int +thread_get_recent_cpu (void) +{ + /* Not yet implemented. */ + return 0; +} + +/* Idle thread. Executes when no other thread is ready to run. + + The idle thread is initially put on the ready list by + thread_start(). It will be scheduled once initially, at which + point it initializes idle_thread, "up"s the semaphore passed + to it to enable thread_start() to continue, and immediately + blocks. After that, the idle thread never appears in the + ready list. It is returned by next_thread_to_run() as a + special case when the ready list is empty. */ +static void +idle (void *idle_started_ UNUSED) +{ + struct semaphore *idle_started = idle_started_; + idle_thread = thread_current (); + sema_up (idle_started); + + for (;;) + { + /* Let someone else run. */ + intr_disable (); + thread_block (); + + /* Re-enable interrupts and wait for the next one. + + The `sti' instruction disables interrupts until the + completion of the next instruction, so these two + instructions are executed atomically. This atomicity is + important; otherwise, an interrupt could be handled + between re-enabling interrupts and waiting for the next + one to occur, wasting as much as one clock tick worth of + time. + + See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a] + 7.11.1 "HLT Instruction". */ + asm volatile ("sti; hlt" : : : "memory"); + } +} + +/* Function used as the basis for a kernel thread. */ +static void +kernel_thread (thread_func *function, void *aux) +{ + ASSERT (function != NULL); + + intr_enable (); /* The scheduler runs with interrupts off. */ + function (aux); /* Execute the thread function. */ + thread_exit (); /* If function() returns, kill the thread. */ +} + +/* Returns the running thread. */ +struct thread * +running_thread (void) +{ + uint32_t *esp; + + /* Copy the CPU's stack pointer into `esp', and then round that + down to the start of a page. Because `struct thread' is + always at the beginning of a page and the stack pointer is + somewhere in the middle, this locates the curent thread. */ + asm ("mov %%esp, %0" : "=g" (esp)); + return pg_round_down (esp); +} + +/* Returns true if T appears to point to a valid thread. */ +static bool +is_thread (struct thread *t) +{ + return t != NULL && t->magic == THREAD_MAGIC; +} + +/* Does basic initialization of T as a blocked thread named + NAME. */ +static void +init_thread (struct thread *t, const char *name, int priority) +{ + ASSERT (t != NULL); + ASSERT (PRI_MIN <= priority && priority <= PRI_MAX); + ASSERT (name != NULL); + + memset (t, 0, sizeof *t); + t->status = THREAD_BLOCKED; + strlcpy (t->name, name, sizeof t->name); + t->stack = (uint8_t *) t + PGSIZE; + t->priority = priority; + t->magic = THREAD_MAGIC; + list_push_back (&all_list, &t->allelem); +} + +/* Allocates a SIZE-byte frame at the top of thread T's stack and + returns a pointer to the frame's base. */ +static void * +alloc_frame (struct thread *t, size_t size) +{ + /* Stack data is always allocated in word-size units. */ + ASSERT (is_thread (t)); + ASSERT (size % sizeof (uint32_t) == 0); + + t->stack -= size; + return t->stack; +} + +/* Chooses and returns the next thread to be scheduled. Should + return a thread from the run queue, unless the run queue is + empty. (If the running thread can continue running, then it + will be in the run queue.) If the run queue is empty, return + idle_thread. */ +static struct thread * +next_thread_to_run (void) +{ + if (list_empty (&ready_list)) + return idle_thread; + else + return list_entry (list_pop_front (&ready_list), struct thread, elem); +} + +/* Completes a thread switch by activating the new thread's page + tables, and, if the previous thread is dying, destroying it. + + At this function's invocation, we just switched from thread + PREV, the new thread is already running, and interrupts are + still disabled. This function is normally invoked by + thread_schedule() as its final action before returning, but + the first time a thread is scheduled it is called by + switch_entry() (see switch.S). + + It's not safe to call printf() until the thread switch is + complete. In practice that means that printf()s should be + added at the end of the function. + + After this function and its caller returns, the thread switch + is complete. */ +void +thread_schedule_tail (struct thread *prev) +{ + struct thread *cur = running_thread (); + + ASSERT (intr_get_level () == INTR_OFF); + + /* Mark us as running. */ + cur->status = THREAD_RUNNING; + + /* Start new time slice. */ + thread_ticks = 0; + +#ifdef USERPROG + /* Activate the new address space. */ + process_activate (); +#endif + + /* If the thread we switched from is dying, destroy its struct + thread. This must happen late so that thread_exit() doesn't + pull out the rug under itself. (We don't free + initial_thread because its memory was not obtained via + palloc().) */ + if (prev != NULL && prev->status == THREAD_DYING && prev != initial_thread) + { + ASSERT (prev != cur); + palloc_free_page (prev); + } +} + +/* Schedules a new process. At entry, interrupts must be off and + the running process's state must have been changed from + running to some other state. This function finds another + thread to run and switches to it. + + It's not safe to call printf() until thread_schedule_tail() + has completed. */ +static void +schedule (void) +{ + struct thread *cur = running_thread (); + struct thread *next = next_thread_to_run (); + struct thread *prev = NULL; + + ASSERT (intr_get_level () == INTR_OFF); + ASSERT (cur->status != THREAD_RUNNING); + ASSERT (is_thread (next)); + + if (cur != next) + prev = switch_threads (cur, next); + thread_schedule_tail (prev); +} + +/* Returns a tid to use for a new thread. */ +static tid_t +allocate_tid (void) +{ + static tid_t next_tid = 1; + tid_t tid; + + lock_acquire (&tid_lock); + tid = next_tid++; + lock_release (&tid_lock); + + return tid; +} + +/* Offset of `stack' member within `struct thread'. + Used by switch.S, which can't figure it out on its own. */ +uint32_t thread_stack_ofs = offsetof (struct thread, stack); diff --git a/pintos-env/pintos/threads/thread.h b/pintos-env/pintos/threads/thread.h new file mode 100755 index 0000000..7965c06 --- /dev/null +++ b/pintos-env/pintos/threads/thread.h @@ -0,0 +1,141 @@ +#ifndef THREADS_THREAD_H +#define THREADS_THREAD_H + +#include +#include +#include + +/* States in a thread's life cycle. */ +enum thread_status + { + THREAD_RUNNING, /* Running thread. */ + THREAD_READY, /* Not running but ready to run. */ + THREAD_BLOCKED, /* Waiting for an event to trigger. */ + THREAD_DYING /* About to be destroyed. */ + }; + +/* Thread identifier type. + You can redefine this to whatever type you like. */ +typedef int tid_t; +#define TID_ERROR ((tid_t) -1) /* Error value for tid_t. */ + +/* Thread priorities. */ +#define PRI_MIN 0 /* Lowest priority. */ +#define PRI_DEFAULT 31 /* Default priority. */ +#define PRI_MAX 63 /* Highest priority. */ + +/* A kernel thread or user process. + + Each thread structure is stored in its own 4 kB page. The + thread structure itself sits at the very bottom of the page + (at offset 0). The rest of the page is reserved for the + thread's kernel stack, which grows downward from the top of + the page (at offset 4 kB). Here's an illustration: + + 4 kB +---------------------------------+ + | kernel stack | + | | | + | | | + | V | + | grows downward | + | | + | | + | | + | | + | | + | | + | | + | | + +---------------------------------+ + | magic | + | : | + | : | + | name | + | status | + 0 kB +---------------------------------+ + + The upshot of this is twofold: + + 1. First, `struct thread' must not be allowed to grow too + big. If it does, then there will not be enough room for + the kernel stack. Our base `struct thread' is only a + few bytes in size. It probably should stay well under 1 + kB. + + 2. Second, kernel stacks must not be allowed to grow too + large. If a stack overflows, it will corrupt the thread + state. Thus, kernel functions should not allocate large + structures or arrays as non-static local variables. Use + dynamic allocation with malloc() or palloc_get_page() + instead. + + The first symptom of either of these problems will probably be + an assertion failure in thread_current(), which checks that + the `magic' member of the running thread's `struct thread' is + set to THREAD_MAGIC. Stack overflow will normally change this + value, triggering the assertion. */ +/* The `elem' member has a dual purpose. It can be an element in + the run queue (thread.c), or it can be an element in a + semaphore wait list (synch.c). It can be used these two ways + only because they are mutually exclusive: only a thread in the + ready state is on the run queue, whereas only a thread in the + blocked state is on a semaphore wait list. */ +struct thread + { + /* Owned by thread.c. */ + tid_t tid; /* Thread identifier. */ + enum thread_status status; /* Thread state. */ + char name[16]; /* Name (for debugging purposes). */ + uint8_t *stack; /* Saved stack pointer. */ + int priority; /* Priority. */ + struct list_elem allelem; /* List element for all threads list. */ + + /* Shared between thread.c and synch.c. */ + struct list_elem elem; /* List element. */ + +#ifdef USERPROG + /* Owned by userprog/process.c. */ + uint32_t *pagedir; /* Page directory. */ +#endif + + /* Owned by thread.c. */ + unsigned magic; /* Detects stack overflow. */ + }; + +/* If false (default), use round-robin scheduler. + If true, use multi-level feedback queue scheduler. + Controlled by kernel command-line option "-o mlfqs". */ +extern bool thread_mlfqs; + +void thread_init (void); +void thread_start (void); + +void thread_tick (void); +void thread_print_stats (void); + +typedef void thread_func (void *aux); +tid_t thread_create (const char *name, int priority, thread_func *, void *); + +void thread_block (void); +void thread_unblock (struct thread *); + +struct thread *thread_current (void); +tid_t thread_tid (void); +const char *thread_name (void); + +void thread_exit (void) NO_RETURN; +void thread_yield (void); + +/* Performs some operation on thread t, given auxiliary data AUX. */ +typedef void thread_action_func (struct thread *t, void *aux); +void thread_foreach (thread_action_func *, void *); + +int thread_get_priority (void); +void thread_set_priority (int); + +int thread_get_nice (void); +void thread_set_nice (int); +int thread_get_recent_cpu (void); +int thread_get_load_avg (void); + +#endif /* threads/thread.h */ diff --git a/pintos-env/pintos/threads/vaddr.h b/pintos-env/pintos/threads/vaddr.h new file mode 100755 index 0000000..184c824 --- /dev/null +++ b/pintos-env/pintos/threads/vaddr.h @@ -0,0 +1,89 @@ +#ifndef THREADS_VADDR_H +#define THREADS_VADDR_H + +#include +#include +#include + +#include "threads/loader.h" + +/* Functions and macros for working with virtual addresses. + + See pte.h for functions and macros specifically for x86 + hardware page tables. */ + +#define BITMASK(SHIFT, CNT) (((1ul << (CNT)) - 1) << (SHIFT)) + +/* Page offset (bits 0:12). */ +#define PGSHIFT 0 /* Index of first offset bit. */ +#define PGBITS 12 /* Number of offset bits. */ +#define PGSIZE (1 << PGBITS) /* Bytes in a page. */ +#define PGMASK BITMASK(PGSHIFT, PGBITS) /* Page offset bits (0:12). */ + +/* Offset within a page. */ +static inline unsigned pg_ofs (const void *va) { + return (uintptr_t) va & PGMASK; +} + +/* Virtual page number. */ +static inline uintptr_t pg_no (const void *va) { + return (uintptr_t) va >> PGBITS; +} + +/* Round up to nearest page boundary. */ +static inline void *pg_round_up (const void *va) { + return (void *) (((uintptr_t) va + PGSIZE - 1) & ~PGMASK); +} + +/* Round down to nearest page boundary. */ +static inline void *pg_round_down (const void *va) { + return (void *) ((uintptr_t) va & ~PGMASK); +} + +/* Base address of the 1:1 physical-to-virtual mapping. Physical + memory is mapped starting at this virtual address. Thus, + physical address 0 is accessible at PHYS_BASE, physical + address address 0x1234 at (uint8_t *) PHYS_BASE + 0x1234, and + so on. + + This address also marks the end of user programs' address + space. Up to this point in memory, user programs are allowed + to map whatever they like. At this point and above, the + virtual address space belongs to the kernel. */ +#define PHYS_BASE ((void *) LOADER_PHYS_BASE) + +/* Returns true if VADDR is a user virtual address. */ +static inline bool +is_user_vaddr (const void *vaddr) +{ + return vaddr < PHYS_BASE; +} + +/* Returns true if VADDR is a kernel virtual address. */ +static inline bool +is_kernel_vaddr (const void *vaddr) +{ + return vaddr >= PHYS_BASE; +} + +/* Returns kernel virtual address at which physical address PADDR + is mapped. */ +static inline void * +ptov (uintptr_t paddr) +{ + ASSERT ((void *) paddr < PHYS_BASE); + + return (void *) (paddr + PHYS_BASE); +} + +/* Returns physical address at which kernel virtual address VADDR + is mapped. */ +static inline uintptr_t +vtop (const void *vaddr) +{ + ASSERT (is_kernel_vaddr (vaddr)); + + return (uintptr_t) vaddr - (uintptr_t) PHYS_BASE; +} + +#endif /* threads/vaddr.h */ diff --git a/pintos-env/pintos/userprog/Make.vars b/pintos-env/pintos/userprog/Make.vars new file mode 100755 index 0000000..e470292 --- /dev/null +++ b/pintos-env/pintos/userprog/Make.vars @@ -0,0 +1,7 @@ +# -*- makefile -*- + +kernel.bin: DEFINES = -DUSERPROG -DFILESYS +KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys +TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base +GRADING_FILE = $(SRCDIR)/tests/userprog/Grading +SIMULATOR = --bochs diff --git a/pintos-env/pintos/userprog/Makefile b/pintos-env/pintos/userprog/Makefile new file mode 100755 index 0000000..34c10aa --- /dev/null +++ b/pintos-env/pintos/userprog/Makefile @@ -0,0 +1 @@ +include ../Makefile.kernel diff --git a/pintos-env/pintos/userprog/exception.c b/pintos-env/pintos/userprog/exception.c new file mode 100755 index 0000000..19aca12 --- /dev/null +++ b/pintos-env/pintos/userprog/exception.c @@ -0,0 +1,161 @@ +#include "userprog/exception.h" +#include +#include +#include "userprog/gdt.h" +#include "threads/interrupt.h" +#include "threads/thread.h" + +/* Number of page faults processed. */ +static long long page_fault_cnt; + +static void kill (struct intr_frame *); +static void page_fault (struct intr_frame *); + +/* Registers handlers for interrupts that can be caused by user + programs. + + In a real Unix-like OS, most of these interrupts would be + passed along to the user process in the form of signals, as + described in [SV-386] 3-24 and 3-25, but we don't implement + signals. Instead, we'll make them simply kill the user + process. + + Page faults are an exception. Here they are treated the same + way as other exceptions, but this will need to change to + implement virtual memory. + + Refer to [IA32-v3a] section 5.15 "Exception and Interrupt + Reference" for a description of each of these exceptions. */ +void +exception_init (void) +{ + /* These exceptions can be raised explicitly by a user program, + e.g. via the INT, INT3, INTO, and BOUND instructions. Thus, + we set DPL==3, meaning that user programs are allowed to + invoke them via these instructions. */ + intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception"); + intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception"); + intr_register_int (5, 3, INTR_ON, kill, + "#BR BOUND Range Exceeded Exception"); + + /* These exceptions have DPL==0, preventing user processes from + invoking them via the INT instruction. They can still be + caused indirectly, e.g. #DE can be caused by dividing by + 0. */ + intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error"); + intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception"); + intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception"); + intr_register_int (7, 0, INTR_ON, kill, + "#NM Device Not Available Exception"); + intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present"); + intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception"); + intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception"); + intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error"); + intr_register_int (19, 0, INTR_ON, kill, + "#XF SIMD Floating-Point Exception"); + + /* Most exceptions can be handled with interrupts turned on. + We need to disable interrupts for page faults because the + fault address is stored in CR2 and needs to be preserved. */ + intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception"); +} + +/* Prints exception statistics. */ +void +exception_print_stats (void) +{ + printf ("Exception: %lld page faults\n", page_fault_cnt); +} + +/* Handler for an exception (probably) caused by a user process. */ +static void +kill (struct intr_frame *f) +{ + /* This interrupt is one (probably) caused by a user process. + For example, the process might have tried to access unmapped + virtual memory (a page fault). For now, we simply kill the + user process. Later, we'll want to handle page faults in + the kernel. Real Unix-like operating systems pass most + exceptions back to the process via signals, but we don't + implement them. */ + + /* The interrupt frame's code segment value tells us where the + exception originated. */ + switch (f->cs) + { + case SEL_UCSEG: + /* User's code segment, so it's a user exception, as we + expected. Kill the user process. */ + printf ("%s: dying due to interrupt %#04x (%s).\n", + thread_name (), f->vec_no, intr_name (f->vec_no)); + intr_dump_frame (f); + thread_exit (); + + case SEL_KCSEG: + /* Kernel's code segment, which indicates a kernel bug. + Kernel code shouldn't throw exceptions. (Page faults + may cause kernel exceptions--but they shouldn't arrive + here.) Panic the kernel to make the point. */ + intr_dump_frame (f); + PANIC ("Kernel bug - unexpected interrupt in kernel"); + + default: + /* Some other code segment? Shouldn't happen. Panic the + kernel. */ + printf ("Interrupt %#04x (%s) in unknown segment %04x\n", + f->vec_no, intr_name (f->vec_no), f->cs); + thread_exit (); + } +} + +/* Page fault handler. This is a skeleton that must be filled in + to implement virtual memory. Some solutions to project 2 may + also require modifying this code. + + At entry, the address that faulted is in CR2 (Control Register + 2) and information about the fault, formatted as described in + the PF_* macros in exception.h, is in F's error_code member. The + example code here shows how to parse that information. You + can find more information about both of these in the + description of "Interrupt 14--Page Fault Exception (#PF)" in + [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */ +static void +page_fault (struct intr_frame *f) +{ + bool not_present; /* True: not-present page, false: writing r/o page. */ + bool write; /* True: access was write, false: access was read. */ + bool user; /* True: access by user, false: access by kernel. */ + void *fault_addr; /* Fault address. */ + + /* Obtain faulting address, the virtual address that was + accessed to cause the fault. It may point to code or to + data. It is not necessarily the address of the instruction + that caused the fault (that's f->eip). + See [IA32-v2a] "MOV--Move to/from Control Registers" and + [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception + (#PF)". */ + asm ("movl %%cr2, %0" : "=r" (fault_addr)); + + /* Turn interrupts back on (they were only off so that we could + be assured of reading CR2 before it changed). */ + intr_enable (); + + /* Count page faults. */ + page_fault_cnt++; + + /* Determine cause. */ + not_present = (f->error_code & PF_P) == 0; + write = (f->error_code & PF_W) != 0; + user = (f->error_code & PF_U) != 0; + + /* To implement virtual memory, delete the rest of the function + body, and replace it with code that brings in the page to + which fault_addr refers. */ + printf ("Page fault at %p: %s error %s page in %s context.\n", + fault_addr, + not_present ? "not present" : "rights violation", + write ? "writing" : "reading", + user ? "user" : "kernel"); + kill (f); +} + diff --git a/pintos-env/pintos/userprog/exception.h b/pintos-env/pintos/userprog/exception.h new file mode 100755 index 0000000..f83e615 --- /dev/null +++ b/pintos-env/pintos/userprog/exception.h @@ -0,0 +1,12 @@ +#ifndef USERPROG_EXCEPTION_H +#define USERPROG_EXCEPTION_H + +/* Page fault error code bits that describe the cause of the exception. */ +#define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */ +#define PF_W 0x2 /* 0: read, 1: write. */ +#define PF_U 0x4 /* 0: kernel, 1: user process. */ + +void exception_init (void); +void exception_print_stats (void); + +#endif /* userprog/exception.h */ diff --git a/pintos-env/pintos/userprog/gdt.c b/pintos-env/pintos/userprog/gdt.c new file mode 100755 index 0000000..e866037 --- /dev/null +++ b/pintos-env/pintos/userprog/gdt.c @@ -0,0 +1,146 @@ +#include "userprog/gdt.h" +#include +#include "userprog/tss.h" +#include "threads/palloc.h" +#include "threads/vaddr.h" + +/* The Global Descriptor Table (GDT). + + The GDT, an x86-specific structure, defines segments that can + potentially be used by all processes in a system, subject to + their permissions. There is also a per-process Local + Descriptor Table (LDT) but that is not used by modern + operating systems. + + Each entry in the GDT, which is known by its byte offset in + the table, identifies a segment. For our purposes only three + types of segments are of interest: code, data, and TSS or + Task-State Segment descriptors. The former two types are + exactly what they sound like. The TSS is used primarily for + stack switching on interrupts. + + For more information on the GDT as used here, refer to + [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor + Types". */ +static uint64_t gdt[SEL_CNT]; + +/* GDT helpers. */ +static uint64_t make_code_desc (int dpl); +static uint64_t make_data_desc (int dpl); +static uint64_t make_tss_desc (void *laddr); +static uint64_t make_gdtr_operand (uint16_t limit, void *base); + +/* Sets up a proper GDT. The bootstrap loader's GDT didn't + include user-mode selectors or a TSS, but we need both now. */ +void +gdt_init (void) +{ + uint64_t gdtr_operand; + + /* Initialize GDT. */ + gdt[SEL_NULL / sizeof *gdt] = 0; + gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0); + gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0); + gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3); + gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3); + gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ()); + + /* Load GDTR, TR. See [IA32-v3a] 2.4.1 "Global Descriptor + Table Register (GDTR)", 2.4.4 "Task Register (TR)", and + 6.2.4 "Task Register". */ + gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt); + asm volatile ("lgdt %0" : : "m" (gdtr_operand)); + asm volatile ("ltr %w0" : : "q" (SEL_TSS)); +} + +/* System segment or code/data segment? */ +enum seg_class + { + CLS_SYSTEM = 0, /* System segment. */ + CLS_CODE_DATA = 1 /* Code or data segment. */ + }; + +/* Limit has byte or 4 kB page granularity? */ +enum seg_granularity + { + GRAN_BYTE = 0, /* Limit has 1-byte granularity. */ + GRAN_PAGE = 1 /* Limit has 4 kB granularity. */ + }; + +/* Returns a segment descriptor with the given 32-bit BASE and + 20-bit LIMIT (whose interpretation depends on GRANULARITY). + The descriptor represents a system or code/data segment + according to CLASS, and TYPE is its type (whose interpretation + depends on the class). + + The segment has descriptor privilege level DPL, meaning that + it can be used in rings numbered DPL or lower. In practice, + DPL==3 means that user processes can use the segment and + DPL==0 means that only the kernel can use the segment. See + [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */ +static uint64_t +make_seg_desc (uint32_t base, + uint32_t limit, + enum seg_class class, + int type, + int dpl, + enum seg_granularity granularity) +{ + uint32_t e0, e1; + + ASSERT (limit <= 0xfffff); + ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA); + ASSERT (type >= 0 && type <= 15); + ASSERT (dpl >= 0 && dpl <= 3); + ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE); + + e0 = ((limit & 0xffff) /* Limit 15:0. */ + | (base << 16)); /* Base 15:0. */ + + e1 = (((base >> 16) & 0xff) /* Base 23:16. */ + | (type << 8) /* Segment type. */ + | (class << 12) /* 0=system, 1=code/data. */ + | (dpl << 13) /* Descriptor privilege. */ + | (1 << 15) /* Present. */ + | (limit & 0xf0000) /* Limit 16:19. */ + | (1 << 22) /* 32-bit segment. */ + | (granularity << 23) /* Byte/page granularity. */ + | (base & 0xff000000)); /* Base 31:24. */ + + return e0 | ((uint64_t) e1 << 32); +} + +/* Returns a descriptor for a readable code segment with base at + 0, a limit of 4 GB, and the given DPL. */ +static uint64_t +make_code_desc (int dpl) +{ + return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE); +} + +/* Returns a descriptor for a writable data segment with base at + 0, a limit of 4 GB, and the given DPL. */ +static uint64_t +make_data_desc (int dpl) +{ + return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE); +} + +/* Returns a descriptor for an "available" 32-bit Task-State + Segment with its base at the given linear address, a limit of + 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. + See [IA32-v3a] 6.2.2 "TSS Descriptor". */ +static uint64_t +make_tss_desc (void *laddr) +{ + return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE); +} + + +/* Returns a descriptor that yields the given LIMIT and BASE when + used as an operand for the LGDT instruction. */ +static uint64_t +make_gdtr_operand (uint16_t limit, void *base) +{ + return limit | ((uint64_t) (uint32_t) base << 16); +} diff --git a/pintos-env/pintos/userprog/gdt.h b/pintos-env/pintos/userprog/gdt.h new file mode 100755 index 0000000..81fe50c --- /dev/null +++ b/pintos-env/pintos/userprog/gdt.h @@ -0,0 +1,15 @@ +#ifndef USERPROG_GDT_H +#define USERPROG_GDT_H + +#include "threads/loader.h" + +/* Segment selectors. + More selectors are defined by the loader in loader.h. */ +#define SEL_UCSEG 0x1B /* User code selector. */ +#define SEL_UDSEG 0x23 /* User data selector. */ +#define SEL_TSS 0x28 /* Task-state segment. */ +#define SEL_CNT 6 /* Number of segments. */ + +void gdt_init (void); + +#endif /* userprog/gdt.h */ diff --git a/pintos-env/pintos/userprog/pagedir.c b/pintos-env/pintos/userprog/pagedir.c new file mode 100755 index 0000000..a6a87b8 --- /dev/null +++ b/pintos-env/pintos/userprog/pagedir.c @@ -0,0 +1,263 @@ +#include "userprog/pagedir.h" +#include +#include +#include +#include "threads/init.h" +#include "threads/pte.h" +#include "threads/palloc.h" + +static uint32_t *active_pd (void); +static void invalidate_pagedir (uint32_t *); + +/* Creates a new page directory that has mappings for kernel + virtual addresses, but none for user virtual addresses. + Returns the new page directory, or a null pointer if memory + allocation fails. */ +uint32_t * +pagedir_create (void) +{ + uint32_t *pd = palloc_get_page (0); + if (pd != NULL) + memcpy (pd, init_page_dir, PGSIZE); + return pd; +} + +/* Destroys page directory PD, freeing all the pages it + references. */ +void +pagedir_destroy (uint32_t *pd) +{ + uint32_t *pde; + + if (pd == NULL) + return; + + ASSERT (pd != init_page_dir); + for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++) + if (*pde & PTE_P) + { + uint32_t *pt = pde_get_pt (*pde); + uint32_t *pte; + + for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++) + if (*pte & PTE_P) + palloc_free_page (pte_get_page (*pte)); + palloc_free_page (pt); + } + palloc_free_page (pd); +} + +/* Returns the address of the page table entry for virtual + address VADDR in page directory PD. + If PD does not have a page table for VADDR, behavior depends + on CREATE. If CREATE is true, then a new page table is + created and a pointer into it is returned. Otherwise, a null + pointer is returned. */ +static uint32_t * +lookup_page (uint32_t *pd, const void *vaddr, bool create) +{ + uint32_t *pt, *pde; + + ASSERT (pd != NULL); + + /* Shouldn't create new kernel virtual mappings. */ + ASSERT (!create || is_user_vaddr (vaddr)); + + /* Check for a page table for VADDR. + If one is missing, create one if requested. */ + pde = pd + pd_no (vaddr); + if (*pde == 0) + { + if (create) + { + pt = palloc_get_page (PAL_ZERO); + if (pt == NULL) + return NULL; + + *pde = pde_create (pt); + } + else + return NULL; + } + + /* Return the page table entry. */ + pt = pde_get_pt (*pde); + return &pt[pt_no (vaddr)]; +} + +/* Adds a mapping in page directory PD from user virtual page + UPAGE to the physical frame identified by kernel virtual + address KPAGE. + UPAGE must not already be mapped. + KPAGE should probably be a page obtained from the user pool + with palloc_get_page(). + If WRITABLE is true, the new page is read/write; + otherwise it is read-only. + Returns true if successful, false if memory allocation + failed. */ +bool +pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable) +{ + uint32_t *pte; + + ASSERT (pg_ofs (upage) == 0); + ASSERT (pg_ofs (kpage) == 0); + ASSERT (is_user_vaddr (upage)); + ASSERT (vtop (kpage) >> PTSHIFT < init_ram_pages); + ASSERT (pd != init_page_dir); + + pte = lookup_page (pd, upage, true); + + if (pte != NULL) + { + ASSERT ((*pte & PTE_P) == 0); + *pte = pte_create_user (kpage, writable); + return true; + } + else + return false; +} + +/* Looks up the physical address that corresponds to user virtual + address UADDR in PD. Returns the kernel virtual address + corresponding to that physical address, or a null pointer if + UADDR is unmapped. */ +void * +pagedir_get_page (uint32_t *pd, const void *uaddr) +{ + uint32_t *pte; + + ASSERT (is_user_vaddr (uaddr)); + + pte = lookup_page (pd, uaddr, false); + if (pte != NULL && (*pte & PTE_P) != 0) + return pte_get_page (*pte) + pg_ofs (uaddr); + else + return NULL; +} + +/* Marks user virtual page UPAGE "not present" in page + directory PD. Later accesses to the page will fault. Other + bits in the page table entry are preserved. + UPAGE need not be mapped. */ +void +pagedir_clear_page (uint32_t *pd, void *upage) +{ + uint32_t *pte; + + ASSERT (pg_ofs (upage) == 0); + ASSERT (is_user_vaddr (upage)); + + pte = lookup_page (pd, upage, false); + if (pte != NULL && (*pte & PTE_P) != 0) + { + *pte &= ~PTE_P; + invalidate_pagedir (pd); + } +} + +/* Returns true if the PTE for virtual page VPAGE in PD is dirty, + that is, if the page has been modified since the PTE was + installed. + Returns false if PD contains no PTE for VPAGE. */ +bool +pagedir_is_dirty (uint32_t *pd, const void *vpage) +{ + uint32_t *pte = lookup_page (pd, vpage, false); + return pte != NULL && (*pte & PTE_D) != 0; +} + +/* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE + in PD. */ +void +pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) +{ + uint32_t *pte = lookup_page (pd, vpage, false); + if (pte != NULL) + { + if (dirty) + *pte |= PTE_D; + else + { + *pte &= ~(uint32_t) PTE_D; + invalidate_pagedir (pd); + } + } +} + +/* Returns true if the PTE for virtual page VPAGE in PD has been + accessed recently, that is, between the time the PTE was + installed and the last time it was cleared. Returns false if + PD contains no PTE for VPAGE. */ +bool +pagedir_is_accessed (uint32_t *pd, const void *vpage) +{ + uint32_t *pte = lookup_page (pd, vpage, false); + return pte != NULL && (*pte & PTE_A) != 0; +} + +/* Sets the accessed bit to ACCESSED in the PTE for virtual page + VPAGE in PD. */ +void +pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) +{ + uint32_t *pte = lookup_page (pd, vpage, false); + if (pte != NULL) + { + if (accessed) + *pte |= PTE_A; + else + { + *pte &= ~(uint32_t) PTE_A; + invalidate_pagedir (pd); + } + } +} + +/* Loads page directory PD into the CPU's page directory base + register. */ +void +pagedir_activate (uint32_t *pd) +{ + if (pd == NULL) + pd = init_page_dir; + + /* Store the physical address of the page directory into CR3 + aka PDBR (page directory base register). This activates our + new page tables immediately. See [IA32-v2a] "MOV--Move + to/from Control Registers" and [IA32-v3a] 3.7.5 "Base + Address of the Page Directory". */ + asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory"); +} + +/* Returns the currently active page directory. */ +static uint32_t * +active_pd (void) +{ + /* Copy CR3, the page directory base register (PDBR), into + `pd'. + See [IA32-v2a] "MOV--Move to/from Control Registers" and + [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */ + uintptr_t pd; + asm volatile ("movl %%cr3, %0" : "=r" (pd)); + return ptov (pd); +} + +/* Seom page table changes can cause the CPU's translation + lookaside buffer (TLB) to become out-of-sync with the page + table. When this happens, we have to "invalidate" the TLB by + re-activating it. + + This function invalidates the TLB if PD is the active page + directory. (If PD is not active then its entries are not in + the TLB, so there is no need to invalidate anything.) */ +static void +invalidate_pagedir (uint32_t *pd) +{ + if (active_pd () == pd) + { + /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12 + "Translation Lookaside Buffers (TLBs)". */ + pagedir_activate (pd); + } +} diff --git a/pintos-env/pintos/userprog/pagedir.h b/pintos-env/pintos/userprog/pagedir.h new file mode 100755 index 0000000..cd92447 --- /dev/null +++ b/pintos-env/pintos/userprog/pagedir.h @@ -0,0 +1,18 @@ +#ifndef USERPROG_PAGEDIR_H +#define USERPROG_PAGEDIR_H + +#include +#include + +uint32_t *pagedir_create (void); +void pagedir_destroy (uint32_t *pd); +bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw); +void *pagedir_get_page (uint32_t *pd, const void *upage); +void pagedir_clear_page (uint32_t *pd, void *upage); +bool pagedir_is_dirty (uint32_t *pd, const void *upage); +void pagedir_set_dirty (uint32_t *pd, const void *upage, bool dirty); +bool pagedir_is_accessed (uint32_t *pd, const void *upage); +void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed); +void pagedir_activate (uint32_t *pd); + +#endif /* userprog/pagedir.h */ diff --git a/pintos-env/pintos/userprog/process.c b/pintos-env/pintos/userprog/process.c new file mode 100755 index 0000000..c0e5215 --- /dev/null +++ b/pintos-env/pintos/userprog/process.c @@ -0,0 +1,465 @@ +#include "userprog/process.h" +#include +#include +#include +#include +#include +#include +#include "userprog/gdt.h" +#include "userprog/pagedir.h" +#include "userprog/tss.h" +#include "filesys/directory.h" +#include "filesys/file.h" +#include "filesys/filesys.h" +#include "threads/flags.h" +#include "threads/init.h" +#include "threads/interrupt.h" +#include "threads/palloc.h" +#include "threads/thread.h" +#include "threads/vaddr.h" + +static thread_func start_process NO_RETURN; +static bool load (const char *cmdline, void (**eip) (void), void **esp); + +/* Starts a new thread running a user program loaded from + FILENAME. The new thread may be scheduled (and may even exit) + before process_execute() returns. Returns the new process's + thread id, or TID_ERROR if the thread cannot be created. */ +tid_t +process_execute (const char *file_name) +{ + char *fn_copy; + tid_t tid; + + /* Make a copy of FILE_NAME. + Otherwise there's a race between the caller and load(). */ + fn_copy = palloc_get_page (0); + if (fn_copy == NULL) + return TID_ERROR; + strlcpy (fn_copy, file_name, PGSIZE); + + /* Create a new thread to execute FILE_NAME. */ + tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy); + if (tid == TID_ERROR) + palloc_free_page (fn_copy); + return tid; +} + +/* A thread function that loads a user process and starts it + running. */ +static void +start_process (void *file_name_) +{ + char *file_name = file_name_; + struct intr_frame if_; + bool success; + + /* Initialize interrupt frame and load executable. */ + memset (&if_, 0, sizeof if_); + if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; + if_.cs = SEL_UCSEG; + if_.eflags = FLAG_IF | FLAG_MBS; + success = load (file_name, &if_.eip, &if_.esp); + + /* If load failed, quit. */ + palloc_free_page (file_name); + if (!success) + thread_exit (); + + /* Start the user process by simulating a return from an + interrupt, implemented by intr_exit (in + threads/intr-stubs.S). Because intr_exit takes all of its + arguments on the stack in the form of a `struct intr_frame', + we just point the stack pointer (%esp) to our stack frame + and jump to it. */ + asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory"); + NOT_REACHED (); +} + +/* Waits for thread TID to die and returns its exit status. If + it was terminated by the kernel (i.e. killed due to an + exception), returns -1. If TID is invalid or if it was not a + child of the calling process, or if process_wait() has already + been successfully called for the given TID, returns -1 + immediately, without waiting. + + This function will be implemented in problem 2-2. For now, it + does nothing. */ +int +process_wait (tid_t child_tid UNUSED) +{ + return -1; +} + +/* Free the current process's resources. */ +void +process_exit (void) +{ + struct thread *cur = thread_current (); + uint32_t *pd; + + /* Destroy the current process's page directory and switch back + to the kernel-only page directory. */ + pd = cur->pagedir; + if (pd != NULL) + { + /* Correct ordering here is crucial. We must set + cur->pagedir to NULL before switching page directories, + so that a timer interrupt can't switch back to the + process page directory. We must activate the base page + directory before destroying the process's page + directory, or our active page directory will be one + that's been freed (and cleared). */ + cur->pagedir = NULL; + pagedir_activate (NULL); + pagedir_destroy (pd); + } +} + +/* Sets up the CPU for running user code in the current + thread. + This function is called on every context switch. */ +void +process_activate (void) +{ + struct thread *t = thread_current (); + + /* Activate thread's page tables. */ + pagedir_activate (t->pagedir); + + /* Set thread's kernel stack for use in processing + interrupts. */ + tss_update (); +} + +/* We load ELF binaries. The following definitions are taken + from the ELF specification, [ELF1], more-or-less verbatim. */ + +/* ELF types. See [ELF1] 1-2. */ +typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off; +typedef uint16_t Elf32_Half; + +/* For use with ELF types in printf(). */ +#define PE32Wx PRIx32 /* Print Elf32_Word in hexadecimal. */ +#define PE32Ax PRIx32 /* Print Elf32_Addr in hexadecimal. */ +#define PE32Ox PRIx32 /* Print Elf32_Off in hexadecimal. */ +#define PE32Hx PRIx16 /* Print Elf32_Half in hexadecimal. */ + +/* Executable header. See [ELF1] 1-4 to 1-8. + This appears at the very beginning of an ELF binary. */ +struct Elf32_Ehdr + { + unsigned char e_ident[16]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; + }; + +/* Program header. See [ELF1] 2-2 to 2-4. + There are e_phnum of these, starting at file offset e_phoff + (see [ELF1] 1-6). */ +struct Elf32_Phdr + { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; + }; + +/* Values for p_type. See [ELF1] 2-3. */ +#define PT_NULL 0 /* Ignore. */ +#define PT_LOAD 1 /* Loadable segment. */ +#define PT_DYNAMIC 2 /* Dynamic linking info. */ +#define PT_INTERP 3 /* Name of dynamic loader. */ +#define PT_NOTE 4 /* Auxiliary info. */ +#define PT_SHLIB 5 /* Reserved. */ +#define PT_PHDR 6 /* Program header table. */ +#define PT_STACK 0x6474e551 /* Stack segment. */ + +/* Flags for p_flags. See [ELF3] 2-3 and 2-4. */ +#define PF_X 1 /* Executable. */ +#define PF_W 2 /* Writable. */ +#define PF_R 4 /* Readable. */ + +static bool setup_stack (void **esp); +static bool validate_segment (const struct Elf32_Phdr *, struct file *); +static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, + uint32_t read_bytes, uint32_t zero_bytes, + bool writable); + +/* Loads an ELF executable from FILE_NAME into the current thread. + Stores the executable's entry point into *EIP + and its initial stack pointer into *ESP. + Returns true if successful, false otherwise. */ +bool +load (const char *file_name, void (**eip) (void), void **esp) +{ + struct thread *t = thread_current (); + struct Elf32_Ehdr ehdr; + struct file *file = NULL; + off_t file_ofs; + bool success = false; + int i; + + /* Allocate and activate page directory. */ + t->pagedir = pagedir_create (); + if (t->pagedir == NULL) + goto done; + process_activate (); + + /* Open executable file. */ + file = filesys_open (file_name); + if (file == NULL) + { + printf ("load: %s: open failed\n", file_name); + goto done; + } + + /* Read and verify executable header. */ + if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr + || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7) + || ehdr.e_type != 2 + || ehdr.e_machine != 3 + || ehdr.e_version != 1 + || ehdr.e_phentsize != sizeof (struct Elf32_Phdr) + || ehdr.e_phnum > 1024) + { + printf ("load: %s: error loading executable\n", file_name); + goto done; + } + + /* Read program headers. */ + file_ofs = ehdr.e_phoff; + for (i = 0; i < ehdr.e_phnum; i++) + { + struct Elf32_Phdr phdr; + + if (file_ofs < 0 || file_ofs > file_length (file)) + goto done; + file_seek (file, file_ofs); + + if (file_read (file, &phdr, sizeof phdr) != sizeof phdr) + goto done; + file_ofs += sizeof phdr; + switch (phdr.p_type) + { + case PT_NULL: + case PT_NOTE: + case PT_PHDR: + case PT_STACK: + default: + /* Ignore this segment. */ + break; + case PT_DYNAMIC: + case PT_INTERP: + case PT_SHLIB: + goto done; + case PT_LOAD: + if (validate_segment (&phdr, file)) + { + bool writable = (phdr.p_flags & PF_W) != 0; + uint32_t file_page = phdr.p_offset & ~PGMASK; + uint32_t mem_page = phdr.p_vaddr & ~PGMASK; + uint32_t page_offset = phdr.p_vaddr & PGMASK; + uint32_t read_bytes, zero_bytes; + if (phdr.p_filesz > 0) + { + /* Normal segment. + Read initial part from disk and zero the rest. */ + read_bytes = page_offset + phdr.p_filesz; + zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE) + - read_bytes); + } + else + { + /* Entirely zero. + Don't read anything from disk. */ + read_bytes = 0; + zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE); + } + if (!load_segment (file, file_page, (void *) mem_page, + read_bytes, zero_bytes, writable)) + goto done; + } + else + goto done; + break; + } + } + + /* Set up stack. */ + if (!setup_stack (esp)) + goto done; + + /* Start address. */ + *eip = (void (*) (void)) ehdr.e_entry; + + success = true; + + done: + /* We arrive here whether the load is successful or not. */ + file_close (file); + return success; +} + +/* load() helpers. */ + +static bool install_page (void *upage, void *kpage, bool writable); + +/* Checks whether PHDR describes a valid, loadable segment in + FILE and returns true if so, false otherwise. */ +static bool +validate_segment (const struct Elf32_Phdr *phdr, struct file *file) +{ + /* p_offset and p_vaddr must have the same page offset. */ + if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK)) + return false; + + /* p_offset must point within FILE. */ + if (phdr->p_offset > (Elf32_Off) file_length (file)) + return false; + + /* p_memsz must be at least as big as p_filesz. */ + if (phdr->p_memsz < phdr->p_filesz) + return false; + + /* The segment must not be empty. */ + if (phdr->p_memsz == 0) + return false; + + /* The virtual memory region must both start and end within the + user address space range. */ + if (!is_user_vaddr ((void *) phdr->p_vaddr)) + return false; + if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz))) + return false; + + /* The region cannot "wrap around" across the kernel virtual + address space. */ + if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr) + return false; + + /* Disallow mapping page 0. + Not only is it a bad idea to map page 0, but if we allowed + it then user code that passed a null pointer to system calls + could quite likely panic the kernel by way of null pointer + assertions in memcpy(), etc. */ + if (phdr->p_vaddr < PGSIZE) + return false; + + /* It's okay. */ + return true; +} + +/* Loads a segment starting at offset OFS in FILE at address + UPAGE. In total, READ_BYTES + ZERO_BYTES bytes of virtual + memory are initialized, as follows: + + - READ_BYTES bytes at UPAGE must be read from FILE + starting at offset OFS. + + - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed. + + The pages initialized by this function must be writable by the + user process if WRITABLE is true, read-only otherwise. + + Return true if successful, false if a memory allocation error + or disk read error occurs. */ +static bool +load_segment (struct file *file, off_t ofs, uint8_t *upage, + uint32_t read_bytes, uint32_t zero_bytes, bool writable) +{ + ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); + ASSERT (pg_ofs (upage) == 0); + ASSERT (ofs % PGSIZE == 0); + + file_seek (file, ofs); + while (read_bytes > 0 || zero_bytes > 0) + { + /* Calculate how to fill this page. + We will read PAGE_READ_BYTES bytes from FILE + and zero the final PAGE_ZERO_BYTES bytes. */ + size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; + size_t page_zero_bytes = PGSIZE - page_read_bytes; + + /* Get a page of memory. */ + uint8_t *kpage = palloc_get_page (PAL_USER); + if (kpage == NULL) + return false; + + /* Load this page. */ + if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes) + { + palloc_free_page (kpage); + return false; + } + memset (kpage + page_read_bytes, 0, page_zero_bytes); + + /* Add the page to the process's address space. */ + if (!install_page (upage, kpage, writable)) + { + palloc_free_page (kpage); + return false; + } + + /* Advance. */ + read_bytes -= page_read_bytes; + zero_bytes -= page_zero_bytes; + upage += PGSIZE; + } + return true; +} + +/* Create a minimal stack by mapping a zeroed page at the top of + user virtual memory. */ +static bool +setup_stack (void **esp) +{ + uint8_t *kpage; + bool success = false; + + kpage = palloc_get_page (PAL_USER | PAL_ZERO); + if (kpage != NULL) + { + success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true); + if (success) + *esp = PHYS_BASE; + else + palloc_free_page (kpage); + } + return success; +} + +/* Adds a mapping from user virtual address UPAGE to kernel + virtual address KPAGE to the page table. + If WRITABLE is true, the user process may modify the page; + otherwise, it is read-only. + UPAGE must not already be mapped. + KPAGE should probably be a page obtained from the user pool + with palloc_get_page(). + Returns true on success, false if UPAGE is already mapped or + if memory allocation fails. */ +static bool +install_page (void *upage, void *kpage, bool writable) +{ + struct thread *t = thread_current (); + + /* Verify that there's not already a page at that virtual + address, then map our page there. */ + return (pagedir_get_page (t->pagedir, upage) == NULL + && pagedir_set_page (t->pagedir, upage, kpage, writable)); +} diff --git a/pintos-env/pintos/userprog/process.h b/pintos-env/pintos/userprog/process.h new file mode 100755 index 0000000..688cd2a --- /dev/null +++ b/pintos-env/pintos/userprog/process.h @@ -0,0 +1,11 @@ +#ifndef USERPROG_PROCESS_H +#define USERPROG_PROCESS_H + +#include "threads/thread.h" + +tid_t process_execute (const char *file_name); +int process_wait (tid_t); +void process_exit (void); +void process_activate (void); + +#endif /* userprog/process.h */ diff --git a/pintos-env/pintos/userprog/syscall.c b/pintos-env/pintos/userprog/syscall.c new file mode 100755 index 0000000..370c89b --- /dev/null +++ b/pintos-env/pintos/userprog/syscall.c @@ -0,0 +1,20 @@ +#include "userprog/syscall.h" +#include +#include +#include "threads/interrupt.h" +#include "threads/thread.h" + +static void syscall_handler (struct intr_frame *); + +void +syscall_init (void) +{ + intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); +} + +static void +syscall_handler (struct intr_frame *f UNUSED) +{ + printf ("system call!\n"); + thread_exit (); +} diff --git a/pintos-env/pintos/userprog/syscall.h b/pintos-env/pintos/userprog/syscall.h new file mode 100755 index 0000000..9059096 --- /dev/null +++ b/pintos-env/pintos/userprog/syscall.h @@ -0,0 +1,6 @@ +#ifndef USERPROG_SYSCALL_H +#define USERPROG_SYSCALL_H + +void syscall_init (void); + +#endif /* userprog/syscall.h */ diff --git a/pintos-env/pintos/userprog/tss.c b/pintos-env/pintos/userprog/tss.c new file mode 100755 index 0000000..f8ed9a9 --- /dev/null +++ b/pintos-env/pintos/userprog/tss.c @@ -0,0 +1,106 @@ +#include "userprog/tss.h" +#include +#include +#include "userprog/gdt.h" +#include "threads/thread.h" +#include "threads/palloc.h" +#include "threads/vaddr.h" + +/* The Task-State Segment (TSS). + + Instances of the TSS, an x86-specific structure, are used to + define "tasks", a form of support for multitasking built right + into the processor. However, for various reasons including + portability, speed, and flexibility, most x86 OSes almost + completely ignore the TSS. We are no exception. + + Unfortunately, there is one thing that can only be done using + a TSS: stack switching for interrupts that occur in user mode. + When an interrupt occurs in user mode (ring 3), the processor + consults the ss0 and esp0 members of the current TSS to + determine the stack to use for handling the interrupt. Thus, + we must create a TSS and initialize at least these fields, and + this is precisely what this file does. + + When an interrupt is handled by an interrupt or trap gate + (which applies to all interrupts we handle), an x86 processor + works like this: + + - If the code interrupted by the interrupt is in the same + ring as the interrupt handler, then no stack switch takes + place. This is the case for interrupts that happen when + we're running in the kernel. The contents of the TSS are + irrelevant for this case. + + - If the interrupted code is in a different ring from the + handler, then the processor switches to the stack + specified in the TSS for the new ring. This is the case + for interrupts that happen when we're in user space. It's + important that we switch to a stack that's not already in + use, to avoid corruption. Because we're running in user + space, we know that the current process's kernel stack is + not in use, so we can always use that. Thus, when the + scheduler switches threads, it also changes the TSS's + stack pointer to point to the new thread's kernel stack. + (The call is in thread_schedule_tail() in thread.c.) + + See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a + description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or + Interrupt-Handler Procedures" for a description of when and + how stack switching occurs during an interrupt. */ +struct tss + { + uint16_t back_link, :16; + void *esp0; /* Ring 0 stack virtual address. */ + uint16_t ss0, :16; /* Ring 0 stack segment selector. */ + void *esp1; + uint16_t ss1, :16; + void *esp2; + uint16_t ss2, :16; + uint32_t cr3; + void (*eip) (void); + uint32_t eflags; + uint32_t eax, ecx, edx, ebx; + uint32_t esp, ebp, esi, edi; + uint16_t es, :16; + uint16_t cs, :16; + uint16_t ss, :16; + uint16_t ds, :16; + uint16_t fs, :16; + uint16_t gs, :16; + uint16_t ldt, :16; + uint16_t trace, bitmap; + }; + +/* Kernel TSS. */ +static struct tss *tss; + +/* Initializes the kernel TSS. */ +void +tss_init (void) +{ + /* Our TSS is never used in a call gate or task gate, so only a + few fields of it are ever referenced, and those are the only + ones we initialize. */ + tss = palloc_get_page (PAL_ASSERT | PAL_ZERO); + tss->ss0 = SEL_KDSEG; + tss->bitmap = 0xdfff; + tss_update (); +} + +/* Returns the kernel TSS. */ +struct tss * +tss_get (void) +{ + ASSERT (tss != NULL); + return tss; +} + +/* Sets the ring 0 stack pointer in the TSS to point to the end + of the thread stack. */ +void +tss_update (void) +{ + ASSERT (tss != NULL); + tss->esp0 = (uint8_t *) thread_current () + PGSIZE; +} diff --git a/pintos-env/pintos/userprog/tss.h b/pintos-env/pintos/userprog/tss.h new file mode 100755 index 0000000..467bd19 --- /dev/null +++ b/pintos-env/pintos/userprog/tss.h @@ -0,0 +1,11 @@ +#ifndef USERPROG_TSS_H +#define USERPROG_TSS_H + +#include + +struct tss; +void tss_init (void); +struct tss *tss_get (void); +void tss_update (void); + +#endif /* userprog/tss.h */ diff --git a/pintos-env/pintos/utils/Makefile b/pintos-env/pintos/utils/Makefile new file mode 100755 index 0000000..d775756 --- /dev/null +++ b/pintos-env/pintos/utils/Makefile @@ -0,0 +1,11 @@ +all: setitimer-helper squish-pty squish-unix + +CC = clang +CFLAGS = -Wall -W +LDFLAGS = -lm +setitimer-helper: setitimer-helper.o +squish-pty: squish-pty.o +squish-unix: squish-unix.o + +clean: + rm -f *.o setitimer-helper squish-pty squish-unix diff --git a/pintos-env/pintos/utils/Pintos.pm b/pintos-env/pintos/utils/Pintos.pm new file mode 100755 index 0000000..70df40d --- /dev/null +++ b/pintos-env/pintos/utils/Pintos.pm @@ -0,0 +1,491 @@ +# Pintos helper subroutines. + +# Number of bytes available for the loader at the beginning of the MBR. +# Kernel command-line arguments follow the loader. +our $LOADER_SIZE = 314; + +# Partition types. +my (%role2type) = (KERNEL => 0x20, + FILESYS => 0x21, + SCRATCH => 0x22, + SWAP => 0x23); +my (%type2role) = reverse %role2type; + +# Order of roles within a given disk. +our (@role_order) = qw (KERNEL FILESYS SCRATCH SWAP); + +# Partitions. +# +# Valid keys are KERNEL, FILESYS, SCRATCH, SWAP. Only those +# partitions which are in use are included. +# +# Each value is a reference to a hash. If the partition's contents +# are to be obtained from a file (that will be copied into a new +# virtual disk), then the hash contains: +# +# FILE => name of file from which the partition's contents are copied +# (perhaps "/dev/zero"), +# OFFSET => offset in bytes in FILE, +# BYTES => size in bytes of contents from FILE, +# +# If the partition is taken from a virtual disk directly, then it +# contains the following. The same keys are also filled in once a +# file-based partition has been copied into a new virtual disk: +# +# DISK => name of virtual disk file, +# START => sector offset of start of partition within DISK, +# SECTORS => number of sectors of partition within DISK, which is usually +# greater than round_up (BYTES, 512) due to padding. +our (%parts); + +# set_part($opt, $arg) +# +# For use as a helper function for Getopt::Long::GetOptions to set +# disk sources. +sub set_part { + my ($opt, $arg) = @_; + my ($role, $source) = $opt =~ /^([a-z]+)(?:-([a-z]+))?/ or die; + + $role = uc $role; + $source = 'FILE' if $source eq ''; + + die "can't have two sources for \L$role\E partition" + if exists $parts{$role}; + + do_set_part ($role, $source, $arg); +} + +# do_set_part($role, $source, $arg) +# +# Sets partition $role as coming from $source (one of 'file', 'from', +# or 'size'). $arg is a file name for 'file' or 'from', a size in +# megabytes for 'size'. +sub do_set_part { + my ($role, $source, $arg) = @_; + + my ($p) = $parts{$role} = {}; + if ($source eq 'file') { + if (read_mbr ($arg)) { + print STDERR "warning: $arg looks like a partitioned disk "; + print STDERR "(did you want --$role-from=$arg or --disk=$arg?)\n" + } + + $p->{FILE} = $arg; + $p->{OFFSET} = 0; + $p->{BYTES} = -s $arg; + } elsif ($source eq 'from') { + my (%pt) = read_partition_table ($arg); + my ($sp) = $pt{$role}; + die "$arg: does not contain \L$role\E partition\n" if !defined $sp; + + $p->{FILE} = $arg; + $p->{OFFSET} = $sp->{START} * 512; + $p->{BYTES} = $sp->{SECTORS} * 512; + } elsif ($source eq 'size') { + $arg =~ /^\d+(\.\d+)?|\.\d+$/ or die "$arg: not a valid size in MB\n"; + + $p->{FILE} = "/dev/zero"; + $p->{OFFSET} = 0; + $p->{BYTES} = ceil ($arg * 1024 * 1024); + } else { + die; + } +} + +# set_geometry('HEADS,SPT') +# set_geometry('zip') +# +# For use as a helper function for Getopt::Long::GetOptions to set +# disk geometry. +sub set_geometry { + local ($_) = $_[1]; + if ($_ eq 'zip') { + @geometry{'H', 'S'} = (64, 32); + } else { + @geometry{'H', 'S'} = /^(\d+)[,\s]+(\d+)$/ + or die "bad syntax for geometry\n"; + $geometry{H} <= 255 or die "heads limited to 255\n"; + $geometry{S} <= 63 or die "sectors per track limited to 63\n"; + } +} + +# set_align('bochs|full|none') +# +# For use as a helper function for Getopt::Long::GetOptions to set +# partition alignment. +sub set_align { + $align = $_[1]; + die "unknown alignment type \"$align\"\n" + if $align ne 'bochs' && $align ne 'full' && $align ne 'none'; +} + +# assemble_disk(%args) +# +# Creates a virtual disk $args{DISK} containing the partitions +# described by @args{KERNEL, FILESYS, SCRATCH, SWAP}. +# +# Required arguments: +# DISK => output disk file name +# HANDLE => output file handle (will be closed) +# +# Normally at least one of the following is included: +# KERNEL, FILESYS, SCRATCH, SWAP => {input: +# FILE => file to read, +# OFFSET => byte offset in file, +# BYTES => byte count from file, +# +# output: +# DISK => output disk file name, +# START => sector offset in DISK, +# SECTORS => sector count in DISK}, +# +# Optional arguments: +# ALIGN => 'bochs' (default), 'full', or 'none' +# GEOMETRY => {H => heads, S => sectors per track} (default 16, 63) +# FORMAT => 'partitioned' (default) or 'raw' +# LOADER => $LOADER_SIZE-byte string containing the loader binary +# ARGS => ['arg 1', 'arg 2', ...] +sub assemble_disk { + my (%args) = @_; + + my (%geometry) = $args{GEOMETRY} || (H => 16, S => 63); + + my ($align); # Align partition start, end to cylinder boundary? + my ($pad); # Pad end of disk out to cylinder boundary? + if (!defined ($args{ALIGN}) || $args{ALIGN} eq 'bochs') { + $align = 0; + $pad = 1; + } elsif ($args{ALIGN} eq 'full') { + $align = 1; + $pad = 0; + } elsif ($args{ALIGN} eq 'none') { + $align = $pad = 0; + } else { + die; + } + + my ($format) = $args{FORMAT} || 'partitioned'; + die if $format ne 'partitioned' && $format ne 'raw'; + + # Check that we have apartitions to copy in. + my $part_cnt = grep (defined ($args{$_}), keys %role2type); + die "must have exactly one partition for raw output\n" + if $format eq 'raw' && $part_cnt != 1; + + # Calculate the disk size. + my ($total_sectors) = 0; + if ($format eq 'partitioned') { + $total_sectors += $align ? $geometry{S} : 1; + } + for my $role (@role_order) { + my ($p) = $args{$role}; + next if !defined $p; + + die if $p->{DISK}; + + my ($bytes) = $p->{BYTES}; + my ($start) = $total_sectors; + my ($end) = $start + div_round_up ($bytes, 512); + $end = round_up ($end, cyl_sectors (%geometry)) if $align; + + $p->{DISK} = $args{DISK}; + $p->{START} = $start; + $p->{SECTORS} = $end - $start; + $total_sectors = $end; + } + + # Write the disk. + my ($disk_fn) = $args{DISK}; + my ($disk) = $args{HANDLE}; + if ($format eq 'partitioned') { + # Pack loader into MBR. + my ($loader) = $args{LOADER} || "\xcd\x18"; + my ($mbr) = pack ("a$LOADER_SIZE", $loader); + + $mbr .= make_kernel_command_line (@{$args{ARGS}}); + + # Pack partition table into MBR. + $mbr .= make_partition_table (\%geometry, \%args); + + # Add signature to MBR. + $mbr .= pack ("v", 0xaa55); + + die if length ($mbr) != 512; + write_fully ($disk, $disk_fn, $mbr); + write_zeros ($disk, $disk_fn, 512 * ($geometry{S} - 1)) if $align; + } + for my $role (@role_order) { + my ($p) = $args{$role}; + next if !defined $p; + + my ($source); + my ($fn) = $p->{FILE}; + open ($source, '<', $fn) or die "$fn: open: $!\n"; + if ($p->{OFFSET}) { + sysseek ($source, $p->{OFFSET}, 0) == $p->{OFFSET} + or die "$fn: seek: $!\n"; + } + copy_file ($source, $fn, $disk, $disk_fn, $p->{BYTES}); + close ($source) or die "$fn: close: $!\n"; + + write_zeros ($disk, $disk_fn, $p->{SECTORS} * 512 - $p->{BYTES}); + } + if ($pad) { + my ($pad_sectors) = round_up ($total_sectors, cyl_sectors (%geometry)); + write_zeros ($disk, $disk_fn, ($pad_sectors - $total_sectors) * 512); + } + close ($disk) or die "$disk: close: $!\n"; +} + +# make_partition_table({H => heads, S => sectors}, {KERNEL => ..., ...}) +# +# Creates and returns a partition table for the given partitions and +# disk geometry. +sub make_partition_table { + my ($geometry, $partitions) = @_; + my ($table) = ''; + for my $role (@role_order) { + defined (my $p = $partitions->{$role}) or next; + + my $end = $p->{START} + $p->{SECTORS} - 1; + my $bootable = $role eq 'KERNEL'; + + $table .= pack ("C", $bootable ? 0x80 : 0); # Bootable? + $table .= pack_chs ($p->{START}, $geometry); # CHS of partition start + $table .= pack ("C", $role2type{$role}); # Partition type + $table .= pack_chs($end, $geometry); # CHS of partition end + $table .= pack ("V", $p->{START}); # LBA of partition start + $table .= pack ("V", $p->{SECTORS}); # Length in sectors + die if length ($table) % 16; + } + return pack ("a64", $table); +} + +# make_kernel_command_line(@args) +# +# Returns the raw bytes to write to an MBR at offset $LOADER_SIZE to +# set a Pintos kernel command line. +sub make_kernel_command_line { + my (@args) = @_; + my ($args) = join ('', map ("$_\0", @args)); + die "command line exceeds 128 bytes" if length ($args) > 128; + return pack ("V a128", scalar (@args), $args); +} + +# copy_file($from_handle, $from_file_name, $to_handle, $to_file_name, $size) +# +# Copies $size bytes from $from_handle to $to_handle. +# $from_file_name and $to_file_name are used in error messages. +sub copy_file { + my ($from_handle, $from_file_name, $to_handle, $to_file_name, $size) = @_; + + while ($size > 0) { + my ($chunk_size) = 4096; + $chunk_size = $size if $chunk_size > $size; + $size -= $chunk_size; + + my ($data) = read_fully ($from_handle, $from_file_name, $chunk_size); + write_fully ($to_handle, $to_file_name, $data); + } +} + +# read_fully($handle, $file_name, $bytes) +# +# Reads exactly $bytes bytes from $handle and returns the data read. +# $file_name is used in error messages. +sub read_fully { + my ($handle, $file_name, $bytes) = @_; + my ($data); + my ($read_bytes) = sysread ($handle, $data, $bytes); + die "$file_name: read: $!\n" if !defined $read_bytes; + die "$file_name: unexpected end of file\n" if $read_bytes != $bytes; + return $data; +} + +# write_fully($handle, $file_name, $data) +# +# Write $data to $handle. +# $file_name is used in error messages. +sub write_fully { + my ($handle, $file_name, $data) = @_; + my ($written_bytes) = syswrite ($handle, $data); + die "$file_name: write: $!\n" if !defined $written_bytes; + die "$file_name: short write\n" if $written_bytes != length $data; +} + +sub write_zeros { + my ($handle, $file_name, $size) = @_; + + while ($size > 0) { + my ($chunk_size) = 4096; + $chunk_size = $size if $chunk_size > $size; + $size -= $chunk_size; + + write_fully ($handle, $file_name, "\0" x $chunk_size); + } +} + +# div_round_up($x,$y) +# +# Returns $x / $y, rounded up to the nearest integer. +# $y must be an integer. +sub div_round_up { + my ($x, $y) = @_; + return int ((ceil ($x) + $y - 1) / $y); +} + +# round_up($x, $y) +# +# Returns $x rounded up to the nearest multiple of $y. +# $y must be an integer. +sub round_up { + my ($x, $y) = @_; + return div_round_up ($x, $y) * $y; +} + +# cyl_sectors(H => heads, S => sectors) +# +# Returns the number of sectors in a cylinder of a disk with the given +# geometry. +sub cyl_sectors { + my (%geometry) = @_; + return $geometry{H} * $geometry{S}; +} + +# read_loader($file_name) +# +# Reads and returns the first $LOADER_SIZE bytes in $file_name. +# If $file_name is undefined, tries to find the default loader. +# Makes sure that the loader is a reasonable size. +sub read_loader { + my ($name) = @_; + $name = find_file ("loader.bin") if !defined $name; + die "Cannot find loader\n" if !defined $name; + + my ($handle); + open ($handle, '<', $name) or die "$name: open: $!\n"; + -s $handle == $LOADER_SIZE || -s $handle == 512 + or die "$name: must be exactly $LOADER_SIZE or 512 bytes long\n"; + $loader = read_fully ($handle, $name, $LOADER_SIZE); + close ($handle) or die "$name: close: $!\n"; + return $loader; +} + +# pack_chs($lba, {H => heads, S => sectors}) +# +# Converts logical sector $lba to a 3-byte packed geometrical sector +# in the format used in PC partition tables (see [Partitions]) and +# returns the geometrical sector as a 3-byte string. +sub pack_chs { + my ($lba, $geometry) = @_; + my ($cyl, $head, $sect) = lba_to_chs ($lba, $geometry); + return pack ("CCC", $head, $sect | (($cyl >> 2) & 0xc0), $cyl & 0xff); +} + +# lba_to_chs($lba, {H => heads, S => sectors}) +# +# Returns the geometrical sector corresponding to logical sector $lba +# given the specified geometry. +sub lba_to_chs { + my ($lba, $geometry) = @_; + my ($hpc) = $geometry->{H}; + my ($spt) = $geometry->{S}; + + # Source: + # http://en.wikipedia.org/wiki/CHS_conversion + use integer; + my $cyl = $lba / ($hpc * $spt); + my $temp = $lba % ($hpc * $spt); + my $head = $temp / $spt; + my $sect = $temp % $spt + 1; + + # Source: + # http://www.cgsecurity.org/wiki/Intel_Partition_Table + if ($cyl <= 1023) { + return ($cyl, $head, $sect); + } else { + return (1023, 254, 63); ## or should this be (1023, $hpc, $spt)? + } +} + +# read_mbr($file) +# +# Tries to read an MBR from $file. Returns the 512-byte MBR if +# successful, otherwise numeric 0. +sub read_mbr { + my ($file) = @_; + my ($retval) = 0; + open (FILE, '<', $file) or die "$file: open: $!\n"; + if (-s FILE == 0) { + die "$file: file has zero size\n"; + } elsif (-s FILE >= 512) { + my ($mbr); + sysread (FILE, $mbr, 512) == 512 or die "$file: read: $!\n"; + $retval = $mbr if unpack ("v", substr ($mbr, 510)) == 0xaa55; + } + close (FILE); + return $retval; +} + +# interpret_partition_table($mbr, $disk) +# +# Parses the partition-table in the specified 512-byte $mbr and +# returns the partitions. $disk is used for error messages. +sub interpret_partition_table { + my ($mbr, $disk) = @_; + my (%parts); + for my $i (0...3) { + my ($bootable, $valid, $type, $lba_start, $lba_length) + = unpack ("C X V C x3 V V", substr ($mbr, 446 + 16 * $i, 16)); + next if !$valid; + + (print STDERR "warning: invalid partition entry $i in $disk\n"), + next if $bootable != 0 && $bootable != 0x80; + + my ($role) = $type2role{$type}; + (printf STDERR "warning: non-Pintos partition type 0x%02x in %s\n", + $type, $disk), + next if !defined $role; + + (print STDERR "warning: duplicate \L$role\E partition in $disk\n"), + next if exists $parts{$role}; + + $parts{$role} = {START => $lba_start, + SECTORS => $lba_length}; + } + return %parts; +} + +# find_file($base_name) +# +# Looks for a file named $base_name in a couple of likely spots. If +# found, returns the name; otherwise, returns undef. +sub find_file { + my ($base_name) = @_; + -e && return $_ foreach $base_name, "build/$base_name"; + return undef; +} + +# read_partition_table($file) +# +# Reads a partition table from $file and returns the parsed +# partitions. Dies if partitions can't be read. +sub read_partition_table { + my ($file) = @_; + my ($mbr) = read_mbr ($file); + die "$file: not a partitioned disk\n" if !$mbr; + return interpret_partition_table ($mbr, $file); +} + +# max(@args) +# +# Returns the numerically largest value in @args. +sub max { + my ($max) = $_[0]; + foreach (@_[1..$#_]) { + $max = $_ if $_ > $max; + } + return $max; +} + +1; diff --git a/pintos-env/pintos/utils/backtrace b/pintos-env/pintos/utils/backtrace new file mode 100755 index 0000000..95e422f --- /dev/null +++ b/pintos-env/pintos/utils/backtrace @@ -0,0 +1,106 @@ +#! /usr/bin/perl -w + +use strict; + +# Check command line. +if (grep ($_ eq '-h' || $_ eq '--help', @ARGV)) { + print <<'EOF'; +backtrace, for converting raw addresses into symbolic backtraces +usage: backtrace [BINARY]... ADDRESS... +where BINARY is the binary file or files from which to obtain symbols + and ADDRESS is a raw address to convert to a symbol name. + +If no BINARY is unspecified, the default is the first of kernel.o or +build/kernel.o that exists. If multiple binaries are specified, each +symbol printed is from the first binary that contains a match. + +The ADDRESS list should be taken from the "Call stack:" printed by the +kernel. Read "Backtraces" in the "Debugging Tools" chapter of the +Pintos documentation for more information. +EOF + exit 0; +} +die "backtrace: at least one argument required (use --help for help)\n" + if @ARGV == 0; + +# Drop garbage inserted by kernel. +@ARGV = grep (!/^(call|stack:?|[-+])$/i, @ARGV); +s/\.$// foreach @ARGV; + +# Find binaries. +my (@binaries); +while ($ARGV[0] !~ /^0x/) { + my ($bin) = shift @ARGV; + die "backtrace: $bin: not found (use --help for help)\n" if ! -e $bin; + push (@binaries, $bin); +} +if (!@binaries) { + my ($bin); + if (-e 'kernel.o') { + $bin = 'kernel.o'; + } elsif (-e 'build/kernel.o') { + $bin = 'build/kernel.o'; + } else { + die "backtrace: no binary specified and neither \"kernel.o\" nor \"build/kernel.o\" exists (use --help for help)\n"; + } + push (@binaries, $bin); +} + +# Find addr2line. +my ($a2l) = search_path ("i386-elf-addr2line") || search_path ("addr2line"); +if (!$a2l) { + die "backtrace: neither `i386-elf-addr2line' nor `addr2line' in PATH\n"; +} +sub search_path { + my ($target) = @_; + for my $dir (split (':', $ENV{PATH})) { + my ($file) = "$dir/$target"; + return $file if -e $file; + } + return undef; +} + +# Figure out backtrace. +my (@locs) = map ({ADDR => $_}, @ARGV); +for my $bin (@binaries) { + open (A2L, "$a2l -fe $bin " . join (' ', map ($_->{ADDR}, @locs)) . "|"); + for (my ($i) = 0; ; $i++) { + my ($function, $line); + chomp ($function = $_); + chomp ($line = ); + next if defined $locs[$i]{BINARY}; + + if ($function ne '??' || $line ne '??:0') { + $locs[$i]{FUNCTION} = $function; + $locs[$i]{LINE} = $line; + $locs[$i]{BINARY} = $bin; + } + } + close (A2L); +} + +# Print backtrace. +my ($cur_binary); +for my $loc (@locs) { + if (defined ($loc->{BINARY}) + && @binaries > 1 + && (!defined ($cur_binary) || $loc->{BINARY} ne $cur_binary)) { + $cur_binary = $loc->{BINARY}; + print "In $cur_binary:\n"; + } + + my ($addr) = $loc->{ADDR}; + $addr = sprintf ("0x%08x", hex ($addr)) if $addr =~ /^0x[0-9a-f]+$/i; + + print $addr, ": "; + if (defined ($loc->{BINARY})) { + my ($function) = $loc->{FUNCTION}; + my ($line) = $loc->{LINE}; + $line =~ s/^(\.\.\/)*//; + $line = "..." . substr ($line, -25) if length ($line) > 28; + print "$function ($line)"; + } else { + print "(unknown)"; + } + print "\n"; +} diff --git a/pintos-env/pintos/utils/pintos b/pintos-env/pintos/utils/pintos new file mode 100755 index 0000000..4b385cd --- /dev/null +++ b/pintos-env/pintos/utils/pintos @@ -0,0 +1,944 @@ +#! /usr/bin/perl -w + +use strict; +use POSIX; +use Fcntl; +use File::Temp 'tempfile'; +use Getopt::Long qw(:config bundling); +use Fcntl qw(SEEK_SET SEEK_CUR); + +# Read Pintos.pm from the same directory as this program. +BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; } + +# Command-line options. +our ($start_time) = time (); +our ($sim); # Simulator: bochs, qemu, or player. +our ($debug) = "none"; # Debugger: none, monitor, or gdb. +our ($mem) = 4; # Physical RAM in MB. +our ($serial) = 1; # Use serial port for input and output? +our ($vga); # VGA output: window, terminal, or none. +our ($jitter); # Seed for random timer interrupts, if set. +our ($realtime); # Synchronize timer interrupts with real time? +our ($timeout); # Maximum runtime in seconds, if set. +our ($kill_on_failure); # Abort quickly on test failure? +our (@puts); # Files to copy into the VM. +our (@gets); # Files to copy out of the VM. +our ($as_ref); # Reference to last addition to @gets or @puts. +our (@kernel_args); # Arguments to pass to kernel. +our (%parts); # Partitions. +our ($make_disk); # Name of disk to create. +our ($tmp_disk) = 1; # Delete $make_disk after run? +our (@disks); # Extra disk images to pass to simulator. +our ($loader_fn); # Bootstrap loader. +our (%geometry); # IDE disk geometry. +our ($align); # Partition alignment. + +parse_command_line (); +prepare_scratch_disk (); +find_disks (); +run_vm (); +finish_scratch_disk (); + +exit 0; + +# Parses the command line. +sub parse_command_line { + usage (0) if @ARGV == 0 || (@ARGV == 1 && $ARGV[0] eq '--help'); + + @kernel_args = @ARGV; + if (grep ($_ eq '--', @kernel_args)) { + @ARGV = (); + while ((my $arg = shift (@kernel_args)) ne '--') { + push (@ARGV, $arg); + } + GetOptions ("sim=s" => sub { set_sim ($_[1]) }, + "bochs" => sub { set_sim ("bochs") }, + "qemu" => sub { set_sim ("qemu") }, + "player" => sub { set_sim ("player") }, + + "debug=s" => sub { set_debug ($_[1]) }, + "no-debug" => sub { set_debug ("none") }, + "monitor" => sub { set_debug ("monitor") }, + "gdb" => sub { set_debug ("gdb") }, + + "m|memory=i" => \$mem, + "j|jitter=i" => sub { set_jitter ($_[1]) }, + "r|realtime" => sub { set_realtime () }, + + "T|timeout=i" => \$timeout, + "k|kill-on-failure" => \$kill_on_failure, + + "v|no-vga" => sub { set_vga ('none'); }, + "s|no-serial" => sub { $serial = 0; }, + "t|terminal" => sub { set_vga ('terminal'); }, + + "p|put-file=s" => sub { add_file (\@puts, $_[1]); }, + "g|get-file=s" => sub { add_file (\@gets, $_[1]); }, + "a|as=s" => sub { set_as ($_[1]); }, + + "h|help" => sub { usage (0); }, + + "kernel=s" => \&set_part, + "filesys=s" => \&set_part, + "swap=s" => \&set_part, + + "filesys-size=s" => \&set_part, + "scratch-size=s" => \&set_part, + "swap-size=s" => \&set_part, + + "kernel-from=s" => \&set_part, + "filesys-from=s" => \&set_part, + "swap-from=s" => \&set_part, + + "make-disk=s" => sub { $make_disk = $_[1]; + $tmp_disk = 0; }, + "disk=s" => sub { set_disk ($_[1]); }, + "loader=s" => \$loader_fn, + + "geometry=s" => \&set_geometry, + "align=s" => \&set_align) + or exit 1; + } + + $sim = "bochs" if !defined $sim; + $debug = "none" if !defined $debug; + $vga = exists ($ENV{DISPLAY}) ? "window" : "none" if !defined $vga; + + undef $timeout, print "warning: disabling timeout with --$debug\n" + if defined ($timeout) && $debug ne 'none'; + + print "warning: enabling serial port for -k or --kill-on-failure\n" + if $kill_on_failure && !$serial; + + $align = "bochs", + print STDERR "warning: setting --align=bochs for Bochs support\n" + if $sim eq 'bochs' && defined ($align) && $align eq 'none'; +} + +# usage($exitcode). +# Prints a usage message and exits with $exitcode. +sub usage { + my ($exitcode) = @_; + $exitcode = 1 unless defined $exitcode; + print <<'EOF'; +pintos, a utility for running Pintos in a simulator +Usage: pintos [OPTION...] -- [ARGUMENT...] +where each OPTION is one of the following options + and each ARGUMENT is passed to Pintos kernel verbatim. +Simulator selection: + --bochs (default) Use Bochs as simulator + --qemu Use QEMU as simulator + --player Use VMware Player as simulator +Debugger selection: + --no-debug (default) No debugger + --monitor Debug with simulator's monitor + --gdb Debug with gdb +Display options: (default is both VGA and serial) + -v, --no-vga No VGA display or keyboard + -s, --no-serial No serial input or output + -t, --terminal Display VGA in terminal (Bochs only) +Timing options: (Bochs only) + -j SEED Randomize timer interrupts + -r, --realtime Use realistic, not reproducible, timings +Testing options: + -T, --timeout=N Kill Pintos after N seconds CPU time or N*load_avg + seconds wall-clock time (whichever comes first) + -k, --kill-on-failure Kill Pintos a few seconds after a kernel or user + panic, test failure, or triple fault +Configuration options: + -m, --mem=N Give Pintos N MB physical RAM (default: 4) +File system commands: + -p, --put-file=HOSTFN Copy HOSTFN into VM, by default under same name + -g, --get-file=GUESTFN Copy GUESTFN out of VM, by default under same name + -a, --as=FILENAME Specifies guest (for -p) or host (for -g) file name +Partition options: (where PARTITION is one of: kernel filesys scratch swap) + --PARTITION=FILE Use a copy of FILE for the given PARTITION + --PARTITION-size=SIZE Create an empty PARTITION of the given SIZE in MB + --PARTITION-from=DISK Use of a copy of the given PARTITION in DISK + (There is no --kernel-size, --scratch, or --scratch-from option.) +Disk configuration options: + --make-disk=DISK Name the new DISK and don't delete it after the run + --disk=DISK Also use existing DISK (may be used multiple times) +Advanced disk configuration options: + --loader=FILE Use FILE as bootstrap loader (default: loader.bin) + --geometry=H,S Use H head, S sector geometry (default: 16,63) + --geometry=zip Use 64 head, 32 sector geometry for USB-ZIP boot + (see http://syslinux.zytor.com/usbkey.php) + --align=bochs Pad out disk to cylinder to support Bochs (default) + --align=full Align partition boundaries to cylinder boundary to + let fdisk guess correct geometry and quiet warnings + --align=none Don't align partitions at all, to save space +Other options: + -h, --help Display this help message. +EOF + exit $exitcode; +} + +# Sets the simulator. +sub set_sim { + my ($new_sim) = @_; + die "--$new_sim conflicts with --$sim\n" + if defined ($sim) && $sim ne $new_sim; + $sim = $new_sim; +} + +# Sets the debugger. +sub set_debug { + my ($new_debug) = @_; + die "--$new_debug conflicts with --$debug\n" + if $debug ne 'none' && $new_debug ne 'none' && $debug ne $new_debug; + $debug = $new_debug; +} + +# Sets VGA output destination. +sub set_vga { + my ($new_vga) = @_; + if (defined ($vga) && $vga ne $new_vga) { + print "warning: conflicting vga display options\n"; + } + $vga = $new_vga; +} + +# Sets randomized timer interrupts. +sub set_jitter { + my ($new_jitter) = @_; + die "--realtime conflicts with --jitter\n" if defined $realtime; + die "different --jitter already defined\n" + if defined $jitter && $jitter != $new_jitter; + $jitter = $new_jitter; +} + +# Sets real-time timer interrupts. +sub set_realtime { + die "--realtime conflicts with --jitter\n" if defined $jitter; + $realtime = 1; +} + +# add_file(\@list, $file) +# +# Adds [$file] to @list, which should be @puts or @gets. +# Sets $as_ref to point to the added element. +sub add_file { + my ($list, $file) = @_; + $as_ref = [$file]; + push (@$list, $as_ref); +} + +# Sets the guest/host name for the previous put/get. +sub set_as { + my ($as) = @_; + die "-a (or --as) is only allowed after -p or -g\n" if !defined $as_ref; + die "Only one -a (or --as) is allowed after -p or -g\n" + if defined $as_ref->[1]; + $as_ref->[1] = $as; +} + +# Sets $disk as a disk to be included in the VM to run. +sub set_disk { + my ($disk) = @_; + + push (@disks, $disk); + + my (%pt) = read_partition_table ($disk); + for my $role (keys %pt) { + die "can't have two sources for \L$role\E partition" + if exists $parts{$role}; + $parts{$role}{DISK} = $disk; + $parts{$role}{START} = $pt{$role}{START}; + $parts{$role}{SECTORS} = $pt{$role}{SECTORS}; + } +} + +# Locates the files used to back each of the virtual disks, +# and creates temporary disks. +sub find_disks { + # Find kernel, if we don't already have one. + if (!exists $parts{KERNEL}) { + my $name = find_file ('kernel.bin'); + die "Cannot find kernel\n" if !defined $name; + do_set_part ('KERNEL', 'file', $name); + } + + # Try to find file system and swap disks, if we don't already have + # partitions. + if (!exists $parts{FILESYS}) { + my $name = find_file ('filesys.dsk'); + set_disk ($name) if defined $name; + } + if (!exists $parts{SWAP}) { + my $name = find_file ('swap.dsk'); + set_disk ($name) if defined $name; + } + + # Warn about (potentially) missing partitions. + if (my ($project) = `pwd` =~ /\b(threads|userprog|vm|filesys)\b/) { + if ((grep ($project eq $_, qw (userprog vm filesys))) + && !defined $parts{FILESYS}) { + print STDERR "warning: it looks like you're running the $project "; + print STDERR "project, but no file system partition is present\n"; + } + if ($project eq 'vm' && !defined $parts{SWAP}) { + print STDERR "warning: it looks like you're running the $project "; + print STDERR "project, but no swap partition is present\n"; + } + } + + # Open disk handle. + my ($handle); + if (!defined $make_disk) { + ($handle, $make_disk) = tempfile (UNLINK => $tmp_disk, + SUFFIX => '.dsk'); + } else { + die "$make_disk: already exists\n" if -e $make_disk; + open ($handle, '>', $make_disk) or die "$make_disk: create: $!\n"; + } + + # Prepare the arguments to pass to the Pintos kernel. + my (@args); + push (@args, shift (@kernel_args)) + while @kernel_args && $kernel_args[0] =~ /^-/; + push (@args, 'extract') if @puts; + push (@args, @kernel_args); + push (@args, 'append', $_->[0]) foreach @gets; + + # Make disk. + my (%disk); + our (@role_order); + for my $role (@role_order) { + my $p = $parts{$role}; + next if !defined $p; + next if exists $p->{DISK}; + $disk{$role} = $p; + } + $disk{DISK} = $make_disk; + $disk{HANDLE} = $handle; + $disk{ALIGN} = $align; + $disk{GEOMETRY} = %geometry; + $disk{FORMAT} = 'partitioned'; + $disk{LOADER} = read_loader ($loader_fn); + $disk{ARGS} = \@args; + assemble_disk (%disk); + + # Put the disk at the front of the list of disks. + unshift (@disks, $make_disk); + die "can't use more than " . scalar (@disks) . "disks\n" if @disks > 4; +} + +# Prepare the scratch disk for gets and puts. +sub prepare_scratch_disk { + return if !@gets && !@puts; + + my ($p) = $parts{SCRATCH}; + # Create temporary partition and write the files to put to it, + # then write an end-of-archive marker. + my ($part_handle, $part_fn) = tempfile (UNLINK => 1, SUFFIX => '.part'); + put_scratch_file ($_->[0], defined $_->[1] ? $_->[1] : $_->[0], + $part_handle, $part_fn) + foreach @puts; + write_fully ($part_handle, $part_fn, "\0" x 1024); + + # Make sure the scratch disk is big enough to get big files + # and at least as big as any requested size. + my ($size) = round_up (max (@gets * 1024 * 1024, $p->{BYTES} || 0), 512); + extend_file ($part_handle, $part_fn, $size); + close ($part_handle); + + if (exists $p->{DISK}) { + # Copy the scratch partition to the disk. + die "$p->{DISK}: scratch partition too small\n" + if $p->{SECTORS} * 512 < $size; + + my ($disk_handle); + open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n"; + open ($disk_handle, '+<', $p->{DISK}) or die "$p->{DISK}: open: $!\n"; + my ($start) = $p->{START} * 512; + sysseek ($disk_handle, $start, SEEK_SET) == $start + or die "$p->{DISK}: seek: $!\n"; + copy_file ($part_handle, $part_fn, $disk_handle, $p->{DISK}, $size); + close ($disk_handle) or die "$p->{DISK}: close: $!\n"; + close ($part_handle) or die "$part_fn: close: $!\n"; + } else { + # Set $part_fn as the source for the scratch partition. + do_set_part ('SCRATCH', 'file', $part_fn); + } +} + +# Read "get" files from the scratch disk. +sub finish_scratch_disk { + return if !@gets; + + # Open scratch partition. + my ($p) = $parts{SCRATCH}; + my ($part_handle); + my ($part_fn) = $p->{DISK}; + open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n"; + sysseek ($part_handle, $p->{START} * 512, SEEK_SET) == $p->{START} * 512 + or die "$part_fn: seek: $!\n"; + + # Read each file. + # If reading fails, delete that file and all subsequent files, but + # don't die with an error, because that's a guest error not a host + # error. (If we do exit with an error code, it fouls up the + # grading process.) Instead, just make sure that the host file(s) + # we were supposed to retrieve is unlinked. + my ($ok) = 1; + my ($part_end) = ($p->{START} + $p->{SECTORS}) * 512; + foreach my $get (@gets) { + my ($name) = defined ($get->[1]) ? $get->[1] : $get->[0]; + if ($ok) { + my ($error) = get_scratch_file ($name, $part_handle, $part_fn); + if (!$error && sysseek ($part_handle, 0, SEEK_CUR) > $part_end) { + $error = "$part_fn: scratch data overflows partition"; + } + if ($error) { + print STDERR "getting $name failed ($error)\n"; + $ok = 0; + } + } + die "$name: unlink: $!\n" if !$ok && !unlink ($name) && !$!{ENOENT}; + } +} + +# mk_ustar_field($number, $size) +# +# Returns $number in a $size-byte numeric field in the format used by +# the standard ustar archive header. +sub mk_ustar_field { + my ($number, $size) = @_; + my ($len) = $size - 1; + my ($out) = sprintf ("%0${len}o", $number) . "\0"; + die "$number: too large for $size-byte octal ustar field\n" + if length ($out) != $size; + return $out; +} + +# calc_ustar_chksum($s) +# +# Calculates and returns the ustar checksum of 512-byte ustar archive +# header $s. +sub calc_ustar_chksum { + my ($s) = @_; + die if length ($s) != 512; + substr ($s, 148, 8, ' ' x 8); + return unpack ("%32a*", $s); +} + +# put_scratch_file($src_file_name, $dst_file_name, +# $disk_handle, $disk_file_name). +# +# Copies $src_file_name into $disk_handle for extraction as +# $dst_file_name. $disk_file_name is used for error messages. +sub put_scratch_file { + my ($src_file_name, $dst_file_name, $disk_handle, $disk_file_name) = @_; + + print "Copying $src_file_name to scratch partition...\n"; + + # ustar format supports up to 100 characters for a file name, and + # even longer names given some common properties, but our code in + # the Pintos kernel only supports at most 99 characters. + die "$dst_file_name: name too long (max 99 characters)\n" + if length ($dst_file_name) > 99; + + # Compose and write ustar header. + stat $src_file_name or die "$src_file_name: stat: $!\n"; + my ($size) = -s _; + my ($header) = (pack ("a100", $dst_file_name) # name + . mk_ustar_field (0644, 8) # mode + . mk_ustar_field (0, 8) # uid + . mk_ustar_field (0, 8) # gid + . mk_ustar_field ($size, 12) # size + . mk_ustar_field (1136102400, 12) # mtime + . (' ' x 8) # chksum + . '0' # typeflag + . ("\0" x 100) # linkname + . "ustar\0" # magic + . "00" # version + . "root" . ("\0" x 28) # uname + . "root" . ("\0" x 28) # gname + . "\0" x 8 # devmajor + . "\0" x 8 # devminor + . ("\0" x 155)) # prefix + . "\0" x 12; # pad to 512 bytes + substr ($header, 148, 8) = mk_ustar_field (calc_ustar_chksum ($header), 8); + write_fully ($disk_handle, $disk_file_name, $header); + + # Copy file data. + my ($put_handle); + sysopen ($put_handle, $src_file_name, O_RDONLY) + or die "$src_file_name: open: $!\n"; + copy_file ($put_handle, $src_file_name, $disk_handle, $disk_file_name, + $size); + die "$src_file_name: changed size while being read\n" + if $size != -s $put_handle; + close ($put_handle); + + # Round up disk data to beginning of next sector. + write_fully ($disk_handle, $disk_file_name, "\0" x (512 - $size % 512)) + if $size % 512; +} + +# get_scratch_file($get_file_name, $disk_handle, $disk_file_name) +# +# Copies from $disk_handle to $get_file_name (which is created). +# $disk_file_name is used for error messages. +# Returns 1 if successful, 0 on failure. +sub get_scratch_file { + my ($get_file_name, $disk_handle, $disk_file_name) = @_; + + print "Copying $get_file_name out of $disk_file_name...\n"; + + # Read ustar header sector. + my ($header) = read_fully ($disk_handle, $disk_file_name, 512); + return "scratch disk tar archive ends unexpectedly" + if $header eq ("\0" x 512); + + # Verify magic numbers. + return "corrupt ustar signature" if substr ($header, 257, 6) ne "ustar\0"; + return "invalid ustar version" if substr ($header, 263, 2) ne '00'; + + # Verify checksum. + my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8))); + my ($correct_chksum) = calc_ustar_chksum ($header); + return "checksum mismatch" if $chksum != $correct_chksum; + + # Get type. + my ($typeflag) = substr ($header, 156, 1); + return "not a regular file" if $typeflag ne '0' && $typeflag ne "\0"; + + # Get size. + my ($size) = oct (unpack ("Z*", substr ($header, 124, 12))); + return "bad size $size\n" if $size < 0; + + # Copy file data. + my ($get_handle); + sysopen ($get_handle, $get_file_name, O_WRONLY | O_CREAT, 0666) + or die "$get_file_name: create: $!\n"; + copy_file ($disk_handle, $disk_file_name, $get_handle, $get_file_name, + $size); + close ($get_handle); + + # Skip forward in disk up to beginning of next sector. + read_fully ($disk_handle, $disk_file_name, 512 - $size % 512) + if $size % 512; + + return 0; +} + +# Running simulators. + +# Runs the selected simulator. +sub run_vm { + if ($sim eq 'bochs') { + run_bochs (); + } elsif ($sim eq 'qemu') { + run_qemu (); + } elsif ($sim eq 'player') { + run_player (); + } else { + die "unknown simulator `$sim'\n"; + } +} + +# Runs Bochs. +sub run_bochs { + # Select Bochs binary based on the chosen debugger. + my ($bin) = $debug eq 'monitor' ? 'bochs-dbg' : 'bochs'; + + my ($squish_pty); + if ($serial) { + $squish_pty = find_in_path ("squish-pty"); + print "warning: can't find squish-pty, so terminal input will fail\n" + if !defined $squish_pty; + } + + # Write bochsrc.txt configuration file. + open (BOCHSRC, ">", "bochsrc.txt") or die "bochsrc.txt: create: $!\n"; + print BOCHSRC < 2; + print_bochs_disk_line ("ata0-master", $disks[0]); + print_bochs_disk_line ("ata0-slave", $disks[1]); + print_bochs_disk_line ("ata1-master", $disks[2]); + print_bochs_disk_line ("ata1-slave", $disks[3]); + if ($vga ne 'terminal') { + if ($serial) { + my $mode = defined ($squish_pty) ? "term" : "file"; + print BOCHSRC "com1: enabled=1, mode=$mode, dev=/dev/stdout\n"; + } + print BOCHSRC "display_library: nogui\n" if $vga eq 'none'; + } else { + print BOCHSRC "display_library: term\n"; + } + close (BOCHSRC); + + # Compose Bochs command line. + my (@cmd) = ($bin, '-q'); + unshift (@cmd, $squish_pty) if defined $squish_pty; + push (@cmd, '-j', $jitter) if defined $jitter; + + # Run Bochs. + print join (' ', @cmd), "\n"; + my ($exit) = xsystem (@cmd); + if (WIFEXITED ($exit)) { + # Bochs exited normally. + # Ignore the exit code; Bochs normally exits with status 1, + # which is weird. + } elsif (WIFSIGNALED ($exit)) { + die "Bochs died with signal ", WTERMSIG ($exit), "\n"; + } else { + die "Bochs died: code $exit\n"; + } +} + +sub print_bochs_disk_line { + my ($device, $disk) = @_; + if (defined $disk) { + my (%geom) = disk_geometry ($disk); + print BOCHSRC "$device: type=disk, path=$disk, mode=flat, "; + print BOCHSRC "cylinders=$geom{C}, heads=$geom{H}, spt=$geom{S}, "; + print BOCHSRC "translation=none\n"; + } +} + +# Runs QEMU. +sub run_qemu { + print "warning: qemu doesn't support --terminal\n" + if $vga eq 'terminal'; + print "warning: qemu doesn't support jitter\n" + if defined $jitter; + my (@cmd) = ('qemu'); + push (@cmd, '-no-kqemu'); + push (@cmd, '-hda', $disks[0]) if defined $disks[0]; + push (@cmd, '-hdb', $disks[1]) if defined $disks[1]; + push (@cmd, '-hdc', $disks[2]) if defined $disks[2]; + push (@cmd, '-hdd', $disks[3]) if defined $disks[3]; + push (@cmd, '-m', $mem); + push (@cmd, '-net', 'none'); + push (@cmd, '-nographic') if $vga eq 'none'; + push (@cmd, '-serial', 'stdio') if $serial && $vga ne 'none'; + push (@cmd, '-S') if $debug eq 'monitor'; + push (@cmd, '-s', '-S') if $debug eq 'gdb'; + push (@cmd, '-monitor', 'null') if $vga eq 'none' && $debug eq 'none'; + run_command (@cmd); +} + +# player_unsup($flag) +# +# Prints a message that $flag is unsupported by VMware Player. +sub player_unsup { + my ($flag) = @_; + print "warning: no support for $flag with VMware Player\n"; +} + +# Runs VMware Player. +sub run_player { + player_unsup ("--$debug") if $debug ne 'none'; + player_unsup ("--no-vga") if $vga eq 'none'; + player_unsup ("--terminal") if $vga eq 'terminal'; + player_unsup ("--jitter") if defined $jitter; + player_unsup ("--timeout"), undef $timeout if defined $timeout; + player_unsup ("--kill-on-failure"), undef $kill_on_failure + if defined $kill_on_failure; + + $mem = round_up ($mem, 4); # Memory must be multiple of 4 MB. + + open (VMX, ">", "pintos.vmx") or die "pintos.vmx: create: $!\n"; + chmod 0777 & ~umask, "pintos.vmx"; + print VMX <", $pln) or die "$pln: create: $!\n"; + print PLN < $size / 512, + C => $cylinders, + H => 16, + S => 63); +} + +# Subprocess utilities. + +# run_command(@args) +# +# Runs xsystem(@args). +# Also prints the command it's running and checks that it succeeded. +sub run_command { + print join (' ', @_), "\n"; + die "command failed\n" if xsystem (@_); +} + +# xsystem(@args) +# +# Creates a subprocess via exec(@args) and waits for it to complete. +# Relays common signals to the subprocess. +# If $timeout is set then the subprocess will be killed after that long. +sub xsystem { + # QEMU turns off local echo and does not restore it if killed by a signal. + # We compensate by restoring it ourselves. + my $cleanup = sub {}; + if (isatty (0)) { + my $termios = POSIX::Termios->new; + $termios->getattr (0); + $cleanup = sub { $termios->setattr (0, &POSIX::TCSANOW); } + } + + # Create pipe for filtering output. + pipe (my $in, my $out) or die "pipe: $!\n" if $kill_on_failure; + + my ($pid) = fork; + if (!defined ($pid)) { + # Fork failed. + die "fork: $!\n"; + } elsif (!$pid) { + # Running in child process. + dup2 (fileno ($out), STDOUT_FILENO) or die "dup2: $!\n" + if $kill_on_failure; + exec_setitimer (@_); + } else { + # Running in parent process. + close $out if $kill_on_failure; + + my ($cause); + local $SIG{ALRM} = sub { timeout ($pid, $cause, $cleanup); }; + local $SIG{INT} = sub { relay_signal ($pid, "INT", $cleanup); }; + local $SIG{TERM} = sub { relay_signal ($pid, "TERM", $cleanup); }; + alarm ($timeout * get_load_average () + 1) if defined ($timeout); + + if ($kill_on_failure) { + # Filter output. + my ($buf) = ""; + my ($boots) = 0; + local ($|) = 1; + for (;;) { + if (waitpid ($pid, WNOHANG) != 0) { + # Subprocess died. Pass through any remaining data. + print $buf while sysread ($in, $buf, 4096) > 0; + last; + } + + # Read and print out pipe data. + my ($len) = length ($buf); + waitpid ($pid, 0), last + if sysread ($in, $buf, 4096, $len) <= 0; + print substr ($buf, $len); + + # Remove full lines from $buf and scan them for keywords. + while ((my $idx = index ($buf, "\n")) >= 0) { + local $_ = substr ($buf, 0, $idx + 1, ''); + next if defined ($cause); + if (/(Kernel PANIC|User process ABORT)/ ) { + $cause = "\L$1\E"; + alarm (5); + } elsif (/Pintos booting/ && ++$boots > 1) { + $cause = "triple fault"; + alarm (5); + } elsif (/FAILED/) { + $cause = "test failure"; + alarm (5); + } + } + } + } else { + waitpid ($pid, 0); + } + alarm (0); + &$cleanup (); + + if (WIFSIGNALED ($?) && WTERMSIG ($?) == SIGVTALRM ()) { + seek (STDOUT, 0, 2); + print "\nTIMEOUT after $timeout seconds of host CPU time\n"; + exit 0; + } + + return $?; + } +} + +# relay_signal($pid, $signal, &$cleanup) +# +# Relays $signal to $pid and then reinvokes it for us with the default +# handler. Also cleans up temporary files and invokes $cleanup. +sub relay_signal { + my ($pid, $signal, $cleanup) = @_; + kill $signal, $pid; + eval { File::Temp::cleanup() }; # Not defined in old File::Temp. + &$cleanup (); + $SIG{$signal} = 'DEFAULT'; + kill $signal, getpid (); +} + +# timeout($pid, $cause, &$cleanup) +# +# Interrupts $pid and dies with a timeout error message, +# after invoking $cleanup. +sub timeout { + my ($pid, $cause, $cleanup) = @_; + kill "INT", $pid; + waitpid ($pid, 0); + &$cleanup (); + seek (STDOUT, 0, 2); + if (!defined ($cause)) { + my ($load_avg) = `uptime` =~ /(load average:.*)$/i; + print "\nTIMEOUT after ", time () - $start_time, + " seconds of wall-clock time"; + print " - $load_avg" if defined $load_avg; + print "\n"; + } else { + print "Simulation terminated due to $cause.\n"; + } + exit 0; +} + +# Returns the system load average over the last minute. +# If the load average is less than 1.0 or cannot be determined, returns 1.0. +sub get_load_average { + my ($avg) = `uptime` =~ /load average:\s*([^,]+),/; + return $avg >= 1.0 ? $avg : 1.0; +} + +# Calls setitimer to set a timeout, then execs what was passed to us. +sub exec_setitimer { + if (defined $timeout) { + if ($ ge 5.8.0) { + eval " + use Time::HiRes qw(setitimer ITIMER_VIRTUAL); + setitimer (ITIMER_VIRTUAL, $timeout, 0); + "; + } else { + { exec ("setitimer-helper", $timeout, @_); }; + exit 1 if !$!{ENOENT}; + print STDERR "warning: setitimer-helper is not installed, so ", + "CPU time limit will not be enforced\n"; + } + } + exec (@_); + exit (1); +} + +sub SIGVTALRM { + use Config; + my $i = 0; + foreach my $name (split(' ', $Config{sig_name})) { + return $i if $name eq 'VTALRM'; + $i++; + } + return 0; +} + +# find_in_path ($program) +# +# Searches for $program in $ENV{PATH}. +# Returns $program if found, otherwise undef. +sub find_in_path { + my ($program) = @_; + -x "$_/$program" and return $program foreach split (':', $ENV{PATH}); + return; +} diff --git a/pintos-env/pintos/utils/pintos-gdb b/pintos-env/pintos/utils/pintos-gdb new file mode 100755 index 0000000..a43284b --- /dev/null +++ b/pintos-env/pintos/utils/pintos-gdb @@ -0,0 +1,20 @@ +#! /bin/sh + +# Path to GDB macros file. Customize for your site. +GDBMACROS=/pintos-env/pintos/misc/gdb-macros + +# Choose correct GDB. +if command -v i386-elf-gdb >/dev/null 2>&1; then + GDB=i386-elf-gdb +else + GDB=gdb +fi + +# Run GDB. +if test -f "$GDBMACROS"; then + exec $GDB -x "$GDBMACROS" "$@" +else + echo "*** $GDBMACROS does not exist ***" + echo "*** Pintos GDB macros will not be available ***" + exec $GDB "$@" +fi diff --git a/pintos-env/pintos/utils/pintos-mkdisk b/pintos-env/pintos/utils/pintos-mkdisk new file mode 100755 index 0000000..87b1563 --- /dev/null +++ b/pintos-env/pintos/utils/pintos-mkdisk @@ -0,0 +1,134 @@ +#! /usr/bin/perl + +use strict; +use warnings; +use POSIX; +use Getopt::Long qw(:config bundling); +use Fcntl 'SEEK_SET'; + +# Read Pintos.pm from the same directory as this program. +BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; } + +our ($disk_fn); # Output disk file name. +our (%parts); # Partitions. +our ($format); # "partitioned" (default) or "raw" +our (%geometry); # IDE disk geometry. +our ($align); # Align partitions on cylinders? +our ($loader_fn); # File name of loader. +our ($include_loader); # Include loader? +our (@kernel_args); # Kernel arguments. + +if (grep ($_ eq '--', @ARGV)) { + @kernel_args = @ARGV; + @ARGV = (); + while ((my $arg = shift (@kernel_args)) ne '--') { + push (@ARGV, $arg); + } +} + +GetOptions ("h|help" => sub { usage (0); }, + + "kernel=s" => \&set_part, + "filesys=s" => \&set_part, + "scratch=s" => \&set_part, + "swap=s" => \&set_part, + + "filesys-size=s" => \&set_part, + "scratch-size=s" => \&set_part, + "swap-size=s" => \&set_part, + + "kernel-from=s" => \&set_part, + "filesys-from=s" => \&set_part, + "scratch-from=s" => \&set_part, + "swap-from=s" => \&set_part, + + "format=s" => \$format, + "loader:s" => \&set_loader, + "no-loader" => \&set_no_loader, + "geometry=s" => \&set_geometry, + "align=s" => \&set_align) + or exit 1; +usage (1) if @ARGV != 1; + +$disk_fn = $ARGV[0]; +die "$disk_fn: already exists\n" if -e $disk_fn; + +# Sets the loader to copy to the MBR. +sub set_loader { + die "can't specify both --loader and --no-loader\n" + if defined ($include_loader) && !$include_loader; + $include_loader = 1; + $loader_fn = $_[1] if $_[1] ne ''; +} + +# Disables copying a loader to the MBR. +sub set_no_loader { + die "can't specify both --loader and --no-loader\n" + if defined ($include_loader) && $include_loader; + $include_loader = 0; +} + +# Figure out whether to include a loader. +$include_loader = exists ($parts{KERNEL}) && $format eq 'partitioned' + if !defined ($include_loader); +die "can't write loader to raw disk\n" if $include_loader && $format eq 'raw'; +die "can't write command-line arguments without --loader or --kernel\n" + if @kernel_args && !$include_loader; +print STDERR "warning: --loader only makes sense without --kernel " + . "if this disk will be used to load a kernel from another disk\n" + if $include_loader && !exists ($parts{KERNEL}); + +# Open disk. +my ($disk_handle); +open ($disk_handle, '>', $disk_fn) or die "$disk_fn: create: $!\n"; + +# Read loader. +my ($loader); +$loader = read_loader ($loader_fn) if $include_loader; + +# Write disk. +my (%disk) = %parts; +$disk{DISK} = $disk_fn; +$disk{HANDLE} = $disk_handle; +$disk{ALIGN} = $align; +$disk{GEOMETRY} = %geometry; +$disk{FORMAT} = $format; +$disk{LOADER} = $loader; +$disk{ARGS} = \@kernel_args; +assemble_disk (%disk); + +# Done. +exit 0; + +sub usage { + print <<'EOF'; +pintos-mkdisk, a utility for creating Pintos virtual disks +Usage: pintos-mkdisk [OPTIONS] DISK [-- ARGUMENT...] +where DISK is the virtual disk to create, + each ARGUMENT is inserted into the command line written to DISK, + and each OPTION is one of the following options. +Partition options: (where PARTITION is one of: kernel filesys scratch swap) + --PARTITION=FILE Use a copy of FILE for the given PARTITION + --PARTITION-size=SIZE Create an empty PARTITION of the given SIZE in MB + --PARTITION-from=DISK Use of a copy of the given PARTITION in DISK + (There is no --kernel-size option.) +Output disk options: + --format=partitioned Write partition table to output (default) + --format=raw Do not write partition table to output + (Pintos can only use partitioned disks.) +Partitioned format output options: + --loader[=FILE] Get bootstrap loader from FILE (default: loader.bin + if --kernel option is specified, empty otherwise) + --no-loader Do not include a bootstrap loader + --geometry=H,S Use H head, S sector geometry (default: 16, 63) + --geometry=zip Use 64 head, 32 sector geometry for USB-ZIP boot + per http://syslinux.zytor.com/usbkey.php + --align=bochs Round size to cylinder for Bochs support (default) + --align=full Align partition boundaries to cylinder boundary to + let fdisk guess correct geometry and quiet warnings + --align=none Don't align partitions at all, to save space +Other options: + -h, --help Display this help message. +EOF + exit ($_[0]); +} diff --git a/pintos-env/pintos/utils/pintos-set-cmdline b/pintos-env/pintos/utils/pintos-set-cmdline new file mode 100755 index 0000000..8c8f702 --- /dev/null +++ b/pintos-env/pintos/utils/pintos-set-cmdline @@ -0,0 +1,42 @@ +#! /usr/bin/perl -w + +use strict; +use Fcntl 'SEEK_SET'; + +# Read Pintos.pm from the same directory as this program. +BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; } + +# Get command-line arguments. +usage (0) if @ARGV == 1 && $ARGV[0] eq '--help'; +usage (1) if @ARGV < 2 || $ARGV[1] ne '--'; +my ($disk, undef, @kernel_args) = @ARGV; + +# Open disk. +my ($handle); +open ($handle, '+<', $disk) or die "$disk: open: $!\n"; + +# Check that it's a partitioned disk with a Pintos loader. +my ($buffer) = read_fully ($handle, $disk, 512); +unpack ("x510 v", $buffer) == 0xaa55 or die "$disk: not a partitioned disk\n"; +$buffer =~ /Pintos/ or die "$disk: does not contain Pintos loader\n"; + +# Write the command line. +our ($LOADER_SIZE); +sysseek ($handle, $LOADER_SIZE, SEEK_SET) == $LOADER_SIZE + or die "$disk: seek: $!\n"; +write_fully ($handle, $disk, make_kernel_command_line (@kernel_args)); + +# Close disk. +close ($handle) or die "$disk: close: $!\n"; + +exit 0; + +sub usage { + print <<'EOF'; +pintos-set-cmdline, a utility for changing the command line in Pintos disks +Usage: pintos-set-cmdline DISK -- [ARGUMENT...] +where DISK is a bootable disk containing a Pintos loader + and each ARGUMENT is inserted into the command line written to DISK. +EOF + exit ($_[0]); +} diff --git a/pintos-env/pintos/utils/setitimer-helper.c b/pintos-env/pintos/utils/setitimer-helper.c new file mode 100755 index 0000000..772d736 --- /dev/null +++ b/pintos-env/pintos/utils/setitimer-helper.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +int +main (int argc, char *argv[]) +{ + const char *program_name = argv[0]; + double timeout; + + if (argc < 3) + { + fprintf (stderr, + "setitimer-helper: runs a program with a virtual CPU limit\n" + "usage: %s TIMEOUT PROGRAM [ARG...]\n" + " where TIMEOUT is the virtual CPU limit, in seconds,\n" + " and remaining arguments specify the program to run\n" + " and its argument.\n", + program_name); + return EXIT_FAILURE; + } + + timeout = strtod (argv[1], NULL); + if (timeout >= 0.0 && timeout < LONG_MAX) + { + struct itimerval it; + + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = timeout; + it.it_value.tv_usec = (timeout - floor (timeout)) * 1000000; + if (setitimer (ITIMER_VIRTUAL, &it, NULL) < 0) + fprintf (stderr, "%s: setitimer: %s\n", + program_name, strerror (errno)); + } + else + fprintf (stderr, "%s: invalid timeout value \"%s\"\n", + program_name, argv[1]); + + execvp (argv[2], &argv[2]); + fprintf (stderr, "%s: couldn't exec \"%s\": %s\n", + program_name, argv[2], strerror (errno)); + return EXIT_FAILURE; +} diff --git a/pintos-env/pintos/utils/squish-pty.c b/pintos-env/pintos/utils/squish-pty.c new file mode 100755 index 0000000..c8375a5 --- /dev/null +++ b/pintos-env/pintos/utils/squish-pty.c @@ -0,0 +1,355 @@ +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +fail_io (const char *msg, ...) + __attribute__ ((noreturn)) + __attribute__ ((format (printf, 1, 2))); + +/* Prints MSG, formatting as with printf(), + plus an error message based on errno, + and exits. */ +static void +fail_io (const char *msg, ...) +{ + va_list args; + + va_start (args, msg); + vfprintf (stderr, msg, args); + va_end (args); + + if (errno != 0) + fprintf (stderr, ": %s", strerror (errno)); + putc ('\n', stderr); + exit (EXIT_FAILURE); +} + +/* If FD is a terminal, configures it for noncanonical input mode + with VMIN and VTIME set as indicated. + If FD is not a terminal, has no effect. */ +static void +make_noncanon (int fd, int vmin, int vtime) +{ + if (isatty (fd)) + { + struct termios termios; + if (tcgetattr (fd, &termios) < 0) + fail_io ("tcgetattr"); + termios.c_lflag &= ~(ICANON | ECHO); + termios.c_cc[VMIN] = vmin; + termios.c_cc[VTIME] = vtime; + if (tcsetattr (fd, TCSANOW, &termios) < 0) + fail_io ("tcsetattr"); + } +} + +/* Make FD non-blocking if NONBLOCKING is true, + or blocking if NONBLOCKING is false. */ +static void +make_nonblocking (int fd, bool nonblocking) +{ + int flags = fcntl (fd, F_GETFL); + if (flags < 0) + fail_io ("fcntl"); + if (nonblocking) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + if (fcntl (fd, F_SETFL, flags) < 0) + fail_io ("fcntl"); +} + +/* Handle a read or write on *FD, which is the pty if FD_IS_PTY + is true, that returned end-of-file or error indication RETVAL. + The system call is named CALL, for use in error messages. + Returns true if processing may continue, false if we're all + done. */ +static bool +handle_error (ssize_t retval, int *fd, bool fd_is_pty, const char *call) +{ + if (fd_is_pty) + { + if (retval < 0) + { + if (errno == EIO) + { + /* Slave side of pty has been closed. */ + return false; + } + else + fail_io (call); + } + else + return true; + } + else + { + if (retval == 0) + { + close (*fd); + *fd = -1; + return true; + } + else + fail_io (call); + } +} + +/* Copies data from stdin to PTY and from PTY to stdout until no + more data can be read or written. */ +static void +relay (int pty, int dead_child_fd) +{ + struct pipe + { + int in, out; + char buf[BUFSIZ]; + size_t size, ofs; + bool active; + }; + struct pipe pipes[2]; + + /* Make PTY, stdin, and stdout non-blocking. */ + make_nonblocking (pty, true); + make_nonblocking (STDIN_FILENO, true); + make_nonblocking (STDOUT_FILENO, true); + + /* Configure noncanonical mode on PTY and stdin to avoid + waiting for end-of-line. We want to minimize context + switching on PTY (for efficiency) and minimize latency on + stdin to avoid a laggy user experience. */ + make_noncanon (pty, 16, 1); + make_noncanon (STDIN_FILENO, 1, 0); + + memset (pipes, 0, sizeof pipes); + pipes[0].in = STDIN_FILENO; + pipes[0].out = pty; + pipes[1].in = pty; + pipes[1].out = STDOUT_FILENO; + + while (pipes[0].in != -1 || pipes[1].in != -1) + { + fd_set read_fds, write_fds; + int retval; + int i; + + FD_ZERO (&read_fds); + FD_ZERO (&write_fds); + for (i = 0; i < 2; i++) + { + struct pipe *p = &pipes[i]; + + /* Don't do anything with the stdin->pty pipe until we + have some data for the pty->stdout pipe. If we get + too eager, Bochs will throw away our input. */ + if (i == 0 && !pipes[1].active) + continue; + + if (p->in != -1 && p->size + p->ofs < sizeof p->buf) + FD_SET (p->in, &read_fds); + if (p->out != -1 && p->size > 0) + FD_SET (p->out, &write_fds); + } + FD_SET (dead_child_fd, &read_fds); + + do + { + retval = select (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL); + } + while (retval < 0 && errno == EINTR); + if (retval < 0) + fail_io ("select"); + + if (FD_ISSET (dead_child_fd, &read_fds)) + { + /* Child died. Do final relaying. */ + struct pipe *p = &pipes[1]; + if (p->out == -1) + return; + make_nonblocking (STDOUT_FILENO, false); + for (;;) + { + ssize_t n; + + /* Write buffer. */ + while (p->size > 0) + { + n = write (p->out, p->buf + p->ofs, p->size); + if (n < 0) + fail_io ("write"); + else if (n == 0) + fail_io ("zero-length write"); + p->ofs += n; + p->size -= n; + } + p->ofs = 0; + + p->size = n = read (p->in, p->buf, sizeof p->buf); + if (n <= 0) + return; + } + } + + for (i = 0; i < 2; i++) + { + struct pipe *p = &pipes[i]; + if (p->in != -1 && FD_ISSET (p->in, &read_fds)) + { + ssize_t n = read (p->in, p->buf + p->ofs + p->size, + sizeof p->buf - p->ofs - p->size); + if (n > 0) + { + p->active = true; + p->size += n; + if (p->size == BUFSIZ && p->ofs != 0) + { + memmove (p->buf, p->buf + p->ofs, p->size); + p->ofs = 0; + } + } + else if (!handle_error (n, &p->in, p->in == pty, "read")) + return; + } + if (p->out != -1 && FD_ISSET (p->out, &write_fds)) + { + ssize_t n = write (p->out, p->buf + p->ofs, p->size); + if (n > 0) + { + p->ofs += n; + p->size -= n; + if (p->size == 0) + p->ofs = 0; + } + else if (!handle_error (n, &p->out, p->out == pty, "write")) + return; + } + } + } +} + +static int dead_child_fd; + +static void +sigchld_handler (int signo __attribute__ ((unused))) +{ + if (write (dead_child_fd, "", 1) < 0) + _exit (1); +} + +int +main (int argc __attribute__ ((unused)), char *argv[]) +{ + int master, slave; + char *name; + pid_t pid; + struct sigaction sa; + int pipe_fds[2]; + struct itimerval zero_itimerval, old_itimerval; + + if (argc < 2) + { + fprintf (stderr, + "usage: squish-pty COMMAND [ARG]...\n" + "Squishes both stdin and stdout into a single pseudoterminal,\n" + "which is passed as stdout to run the specified COMMAND.\n"); + return EXIT_FAILURE; + } + + /* Open master side of pty and get ready to open slave. */ + master = open ("/dev/ptmx", O_RDWR | O_NOCTTY); + if (master < 0) + fail_io ("open \"/dev/ptmx\""); + if (grantpt (master) < 0) + fail_io ("grantpt"); + if (unlockpt (master) < 0) + fail_io ("unlockpt"); + + /* Open slave side of pty. */ + name = ptsname (master); + if (name == NULL) + fail_io ("ptsname"); + slave = open (name, O_RDWR); + if (slave < 0) + fail_io ("open \"%s\"", name); + + /* System V implementations need STREAMS configuration for the + slave. */ + if (isastream (slave)) + { + if (ioctl (slave, I_PUSH, "ptem") < 0 + || ioctl (slave, I_PUSH, "ldterm") < 0) + fail_io ("ioctl"); + } + + /* Arrange to get notified when a child dies, by writing a byte + to a pipe fd. We really want to use pselect() and + sigprocmask(), but Solaris 2.7 doesn't have it. */ + if (pipe (pipe_fds) < 0) + fail_io ("pipe"); + dead_child_fd = pipe_fds[1]; + + memset (&sa, 0, sizeof sa); + sa.sa_handler = sigchld_handler; + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + if (sigaction (SIGCHLD, &sa, NULL) < 0) + fail_io ("sigaction"); + + /* Save the virtual interval timer, which might have been set + by the process that ran us. It really should be applied to + our child process. */ + memset (&zero_itimerval, 0, sizeof zero_itimerval); + if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, &old_itimerval) < 0) + fail_io ("setitimer"); + + pid = fork (); + if (pid < 0) + fail_io ("fork"); + else if (pid != 0) + { + /* Running in parent process. */ + int status; + close (slave); + relay (master, pipe_fds[0]); + + /* If the subprocess has died, die in the same fashion. + In particular, dying from SIGVTALRM tells the pintos + script that we ran out of CPU time. */ + if (waitpid (pid, &status, WNOHANG) > 0) + { + if (WIFEXITED (status)) + return WEXITSTATUS (status); + else if (WIFSIGNALED (status)) + raise (WTERMSIG (status)); + } + return 0; + } + else + { + /* Running in child process. */ + if (setitimer (ITIMER_VIRTUAL, &old_itimerval, NULL) < 0) + fail_io ("setitimer"); + if (dup2 (slave, STDOUT_FILENO) < 0) + fail_io ("dup2"); + if (close (pipe_fds[0]) < 0 || close (pipe_fds[1]) < 0 + || close (slave) < 0 || close (master) < 0) + fail_io ("close"); + execvp (argv[1], argv + 1); + fail_io ("exec"); + } +} diff --git a/pintos-env/pintos/utils/squish-unix.c b/pintos-env/pintos/utils/squish-unix.c new file mode 100755 index 0000000..805205b --- /dev/null +++ b/pintos-env/pintos/utils/squish-unix.c @@ -0,0 +1,338 @@ +#define _GNU_SOURCE 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void +fail_io (const char *msg, ...) + __attribute__ ((noreturn)) + __attribute__ ((format (printf, 1, 2))); + +/* Prints MSG, formatting as with printf(), + plus an error message based on errno, + and exits. */ +static void +fail_io (const char *msg, ...) +{ + va_list args; + + va_start (args, msg); + vfprintf (stderr, msg, args); + va_end (args); + + if (errno != 0) + fprintf (stderr, ": %s", strerror (errno)); + putc ('\n', stderr); + exit (EXIT_FAILURE); +} + +/* If FD is a terminal, configures it for noncanonical input mode + with VMIN and VTIME set as indicated. + If FD is not a terminal, has no effect. */ +static void +make_noncanon (int fd, int vmin, int vtime) +{ + if (isatty (fd)) + { + struct termios termios; + if (tcgetattr (fd, &termios) < 0) + fail_io ("tcgetattr"); + termios.c_lflag &= ~(ICANON | ECHO); + termios.c_cc[VMIN] = vmin; + termios.c_cc[VTIME] = vtime; + if (tcsetattr (fd, TCSANOW, &termios) < 0) + fail_io ("tcsetattr"); + } +} + +/* Make FD non-blocking if NONBLOCKING is true, + or blocking if NONBLOCKING is false. */ +static void +make_nonblocking (int fd, bool nonblocking) +{ + int flags = fcntl (fd, F_GETFL); + if (flags < 0) + fail_io ("fcntl"); + if (nonblocking) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + if (fcntl (fd, F_SETFL, flags) < 0) + fail_io ("fcntl"); +} + +/* Handle a read or write on *FD, which is the socket if + FD_IS_SOCK is true, that returned end-of-file or error + indication RETVAL. The system call is named CALL, for use in + error messages. Returns true if processing may continue, + false if we're all done. */ +static bool +handle_error (ssize_t retval, int *fd, bool fd_is_sock, const char *call) +{ + if (retval == 0) + { + if (fd_is_sock) + return false; + else + { + *fd = -1; + return true; + } + } + else + fail_io (call); +} + +/* Copies data from stdin to SOCK and from SOCK to stdout until no + more data can be read or written. */ +static void +relay (int sock) +{ + struct pipe + { + int in, out; + char buf[BUFSIZ]; + size_t size, ofs; + bool active; + }; + struct pipe pipes[2]; + + /* In case stdin is a file, go back to the beginning. + This allows replaying the input on reset. */ + lseek (STDIN_FILENO, 0, SEEK_SET); + + /* Make SOCK, stdin, and stdout non-blocking. */ + make_nonblocking (sock, true); + make_nonblocking (STDIN_FILENO, true); + make_nonblocking (STDOUT_FILENO, true); + + /* Configure noncanonical mode on stdin to avoid waiting for + end-of-line. */ + make_noncanon (STDIN_FILENO, 1, 0); + + memset (pipes, 0, sizeof pipes); + pipes[0].in = STDIN_FILENO; + pipes[0].out = sock; + pipes[1].in = sock; + pipes[1].out = STDOUT_FILENO; + + while (pipes[0].in != -1 || pipes[1].in != -1 + || (pipes[1].size && pipes[1].out != -1)) + { + fd_set read_fds, write_fds; + sigset_t empty_set; + int retval; + int i; + + FD_ZERO (&read_fds); + FD_ZERO (&write_fds); + for (i = 0; i < 2; i++) + { + struct pipe *p = &pipes[i]; + + /* Don't do anything with the stdin->sock pipe until we + have some data for the sock->stdout pipe. If we get + too eager, vmplayer will throw away our input. */ + if (i == 0 && !pipes[1].active) + continue; + + if (p->in != -1 && p->size + p->ofs < sizeof p->buf) + FD_SET (p->in, &read_fds); + if (p->out != -1 && p->size > 0) + FD_SET (p->out, &write_fds); + } + sigemptyset (&empty_set); + retval = pselect (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL, + &empty_set); + if (retval < 0) + { + if (errno == EINTR) + { + /* Child died. Do final relaying. */ + struct pipe *p = &pipes[1]; + if (p->out == -1) + exit (0); + make_nonblocking (STDOUT_FILENO, false); + for (;;) + { + ssize_t n; + + /* Write buffer. */ + while (p->size > 0) + { + n = write (p->out, p->buf + p->ofs, p->size); + if (n < 0) + fail_io ("write"); + else if (n == 0) + fail_io ("zero-length write"); + p->ofs += n; + p->size -= n; + } + p->ofs = 0; + + p->size = n = read (p->in, p->buf, sizeof p->buf); + if (n <= 0) + exit (0); + } + } + fail_io ("select"); + } + + for (i = 0; i < 2; i++) + { + struct pipe *p = &pipes[i]; + if (p->in != -1 && FD_ISSET (p->in, &read_fds)) + { + ssize_t n = read (p->in, p->buf + p->ofs + p->size, + sizeof p->buf - p->ofs - p->size); + if (n > 0) + { + p->active = true; + p->size += n; + if (p->size == BUFSIZ && p->ofs != 0) + { + memmove (p->buf, p->buf + p->ofs, p->size); + p->ofs = 0; + } + } + else if (!handle_error (n, &p->in, p->in == sock, "read")) + return; + } + if (p->out != -1 && FD_ISSET (p->out, &write_fds)) + { + ssize_t n = write (p->out, p->buf + p->ofs, p->size); + if (n > 0) + { + p->ofs += n; + p->size -= n; + if (p->size == 0) + p->ofs = 0; + } + else if (!handle_error (n, &p->out, p->out == sock, "write")) + return; + } + } + } +} + +static void +sigchld_handler (int signo __attribute__ ((unused))) +{ + /* Nothing to do. */ +} + +int +main (int argc __attribute__ ((unused)), char *argv[]) +{ + pid_t pid; + struct itimerval zero_itimerval; + struct sockaddr_un sun; + sigset_t sigchld_set; + int sock; + + if (argc < 3) + { + fprintf (stderr, + "usage: squish-unix SOCKET COMMAND [ARG]...\n" + "Squishes both stdin and stdout into a single Unix domain\n" + "socket named SOCKET, and runs COMMAND as a subprocess.\n"); + return EXIT_FAILURE; + } + + /* Create socket. */ + sock = socket (PF_LOCAL, SOCK_STREAM, 0); + if (sock < 0) + fail_io ("socket"); + + /* Configure socket. */ + sun.sun_family = AF_LOCAL; + strncpy (sun.sun_path, argv[1], sizeof sun.sun_path); + sun.sun_path[sizeof sun.sun_path - 1] = '\0'; + if (unlink (sun.sun_path) < 0 && errno != ENOENT) + fail_io ("unlink"); + if (bind (sock, (struct sockaddr *) &sun, + (offsetof (struct sockaddr_un, sun_path) + + strlen (sun.sun_path) + 1)) < 0) + fail_io ("bind"); + + /* Listen on socket. */ + if (listen (sock, 1) < 0) + fail_io ("listen"); + + /* Block SIGCHLD and set up a handler for it. */ + sigemptyset (&sigchld_set); + sigaddset (&sigchld_set, SIGCHLD); + if (sigprocmask (SIG_BLOCK, &sigchld_set, NULL) < 0) + fail_io ("sigprocmask"); + if (signal (SIGCHLD, sigchld_handler) == SIG_ERR) + fail_io ("signal"); + + /* Save the virtual interval timer, which might have been set + by the process that ran us. It really should be applied to + our child process. */ + memset (&zero_itimerval, 0, sizeof zero_itimerval); + if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, NULL) < 0) + fail_io ("setitimer"); + + pid = fork (); + if (pid < 0) + fail_io ("fork"); + else if (pid != 0) + { + /* Running in parent process. */ + make_nonblocking (sock, true); + for (;;) + { + fd_set read_fds; + sigset_t empty_set; + int retval; + int conn; + + /* Wait for connection. */ + FD_ZERO (&read_fds); + FD_SET (sock, &read_fds); + sigemptyset (&empty_set); + retval = pselect (sock + 1, &read_fds, NULL, NULL, NULL, &empty_set); + if (retval < 0) + { + if (errno == EINTR) + break; + fail_io ("select"); + } + + /* Accept connection. */ + conn = accept (sock, NULL, NULL); + if (conn < 0) + fail_io ("accept"); + + /* Relay connection. */ + relay (conn); + close (conn); + } + return 0; + } + else + { + /* Running in child process. */ + if (close (sock) < 0) + fail_io ("close"); + execvp (argv[2], argv + 2); + fail_io ("exec"); + } +} diff --git a/pintos-env/pintos/vm/Make.vars b/pintos-env/pintos/vm/Make.vars new file mode 100755 index 0000000..e3c33a7 --- /dev/null +++ b/pintos-env/pintos/vm/Make.vars @@ -0,0 +1,7 @@ +# -*- makefile -*- + +kernel.bin: DEFINES = -DUSERPROG -DFILESYS -DVM +KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys vm +TEST_SUBDIRS = tests/userprog tests/vm tests/filesys/base +GRADING_FILE = $(SRCDIR)/tests/vm/Grading +SIMULATOR = --qemu diff --git a/pintos-env/pintos/vm/Makefile b/pintos-env/pintos/vm/Makefile new file mode 100755 index 0000000..34c10aa --- /dev/null +++ b/pintos-env/pintos/vm/Makefile @@ -0,0 +1 @@ +include ../Makefile.kernel diff --git a/pintos-env/share/bochs/BIOS-bochs-latest b/pintos-env/share/bochs/BIOS-bochs-latest new file mode 100755 index 0000000..f990410 Binary files /dev/null and b/pintos-env/share/bochs/BIOS-bochs-latest differ diff --git a/pintos-env/share/bochs/BIOS-bochs-legacy b/pintos-env/share/bochs/BIOS-bochs-legacy new file mode 100755 index 0000000..7497fac Binary files /dev/null and b/pintos-env/share/bochs/BIOS-bochs-legacy differ diff --git a/pintos-env/share/bochs/VGABIOS-elpin-2.40 b/pintos-env/share/bochs/VGABIOS-elpin-2.40 new file mode 100755 index 0000000..fc3d99d Binary files /dev/null and b/pintos-env/share/bochs/VGABIOS-elpin-2.40 differ diff --git a/pintos-env/share/bochs/VGABIOS-elpin-LICENSE b/pintos-env/share/bochs/VGABIOS-elpin-LICENSE new file mode 100755 index 0000000..0ba5717 --- /dev/null +++ b/pintos-env/share/bochs/VGABIOS-elpin-LICENSE @@ -0,0 +1,9 @@ +The VGA BIOS from Elpin Systems, Inc. (http://www.elpin.com/) +is now permanently licensed for use with bochs, courtesy +of MandrakeSoft, creators of the leading "Linux-Mandrake" +distribution (http://www.linux-mandrake.com/). You may +freely use/distribute it with bochs, as long as it is used +in bochs for the intended use as a VGA BIOS. + +Please check out Elpin Systems. They make cool software games, +educational software, and VGA development products. diff --git a/pintos-env/share/bochs/VGABIOS-lgpl-README b/pintos-env/share/bochs/VGABIOS-lgpl-README new file mode 100755 index 0000000..3462670 --- /dev/null +++ b/pintos-env/share/bochs/VGABIOS-lgpl-README @@ -0,0 +1,235 @@ +Plex86/Bochs VGABios +-------------------- + +The goal of this project is to have a LGPL'd Video Bios in plex86, +Bochs and qemu. +This VGA Bios is very specific to the emulated VGA card. +It is NOT meant to drive a physical vga card. + + +Cirrus SVGA extension +--------------------- + +The Cirrus SVGA extension is designed for the Cirrus emulation in Bochs and +qemu. The initial patch for the Cirrus extension has been written by Makoto +Suzuki (suzu). + + +Install +------- +To compile the VGA Bios you will need : +- gcc +- bcc +- as86 +- ld86 + +Untar the archive, and type make. You should get a "VGABIOS-lgpl-latest.bin" +file. Alternatively, you can use the binary file "VGABIOS-lgpl-latest.bin", +i have compiled for you. + +Edit your plex86/bochs conf file, and modify the load-rom command in the +VGA BIOS section, to point to the new vgabios image file. + + +Debugging +--------- +You can get a very basic debugging system: messages printed by the vgabios. +You have to register the "unmapped" device driver in plex86 or bochs, and make +sure it grabs port 0xfff0. + +Comment the #undef DEBUG at the beginning of vgabios.c. +You can then use the "printf" function in the bios. + + +Testing +------- +Look at the "testvga.c" file in the archive. This is a minimal Turbo C 2.0 +source file that calls a few int10 functions. Feel free to modify it to suit +your needs. + + +Copyright and License +--------------------- +This program has been written by Christophe Bothamy +It is protected by the GNU Lesser Public License, which you should +have received a copy of along with this package. + + +Reverse Engineering +------------------- +The VGA Bios has been written without reverse-engineering any existing Bios. + + +Acknowledgment +-------------- +The source code contains code ripped from rombios.c of plex86, written +by Kevin Lawton + +The source code contains fonts from fntcol16.zip (c) by Joseph Gil avalable at : +ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip +These fonts are public domain + +The source code is based on information taken from : +- Kevin Lawton's vga card emulation for bochs/plex86 +- Ralf Brown's interrupts list avalaible at + http://www.cs.cmu.edu/afs/cs/user/ralf/pub/WWW/files.html +- Finn Thogersons' VGADOC4b available at http://home.worldonline.dk/~finth/ +- Michael Abrash's Graphics Programming Black Book +- Francois Gervais' book "programmation des cartes graphiques cga-ega-vga" + edited by sybex +- DOSEMU 1.0.1 source code for several tables values and formulas + + +Feedback +-------- +Please report any bugs, comments, patches for this VGA Bios to info@vruppert.de +You can find the latest release at : http://www.nongnu.org/vgabios/ +For any information on bochs, visit the website http://bochs.sourceforge.net/ +For any information on qemu, visit the website http://fabrice.bellard.free.fr/qemu/ + + +History +------- +vgabios-0.7a : Oct 30 2011 + - Volker + . added HDTV resolutions (patch by Tristan Schmelcher) + . added PCI ROM support to the VBE-specific images + . implemented vgabios functions with AX=0x112x (patch by Hugo Mercier) + . fixed DAC palette in 8 bpp VBE and Cirrus modes (using the same palette + as VGA mode 0x13) + . Makefile cleanup (patch by Gerd Hoffmann) + +vgabios-0.6c : Apr 08 2009 + - Volker + . added DPMS support to cirrus vgabios (patch from Gleb Natapov) + . use VBE LFB address from PCI base address if present + . added support for a lot more non-standard VBE modes (e.g. widescreen modes) + . minor bugfixes + +vgabios-0.6b : May 30 2008 + - Volker + . added PCI data structure for the Cirrus VGABIOS images + . minor bugfixes in biossums utility, VBE support and makefile + +vgabios-0.6a : Aug 19 2006 + - Volker + . added minimal support for the video parameter table (VPT) + . Cirrus SVGA now supports the "no clear" bit in Cirrus and VESA mode + . Bochs VBE protected mode interface improved + . save/restore video state support for Bochs VBE and standard VGA added + . generate vbetables.h dynamicly + . VBE video memory increased to 8 MB (VBE dispi ID changed to B0C4) + . lots of 4bpp VBE fixes (all 4bpp VBE modes now enabled) + . VGA compatible setup for VBE modes added + +vgabios-0.5d : Dec 29 2005 + - Volker + . Bochs VBE protected mode interface added (based on a patch by malc@pulsesoft.com) + . biossums utility now supports VGABIOS sizes up to 64 kBytes + . VGA mode 0x11: all color planes must be enabled in this 2-color VGA mode + +vgabios-0.5c : Jul 07 2005 + - Volker + . BIOS configuration word usually reports initial mode 80x25 color text + . vgabios function 0x0e (write teletype): linefeed (0x0a) only increments the + cursor row value + +vgabios-0.5b : May 24 2005 + - Volker + . fixed return value for the default case in the VBE section (non-debug mode) + . removed unused stuff + +vgabios-0.5a : Mar 07 2005 + - Volker + . Cirrus SVGA extension (initial patches from Makoto Suzuki, improvements + from Fabrice Bellard) + . vgabios image size is now exactly 32k with a checksum + . a lot of vgabios and vbe functions rewritten in assembler + . dynamicly generated VBE mode info list + . write character function for CGA and LINEAR8 modes + . read/write graphics pixel for some graphics modes + . text scroll feature for some graphics modes + . VBE 8-bit DAC support + +vgabios-0.4c : Nov 06 2003 + - Christophe + . fix font problem on initial screen of NT4 Loader + +vgabios-0.4b : Nov 04 2003 + - Volker + . fix offset of character tables + . optimizations of CRT controller accesses + . VBE i/o registers changed to 0x01CE/CF + (suggestion from Daniel Gimpelevich) + . "noclear" flag stored in BIOS area + . fix character height returned by get_font_info function + +vgabios-0.4a : Aug 17 2003 + - Volker + . VBE mode search rewritten (VBE modes with LFB bit removed) + . many bugfixes and optimizations + . write character function implemented for graphics modes + . support for 15bpp, 16bpp, 24bpp and 32bpp VBE modes added + . SVGA mode 0x6A added + . VBE modes 0x102, 0x117, 0x118 and 0x142 (Bochs specific) + +vgabios-0.3b : Nov 23 2002 + - Christophe + . added lfb-mode numbers (patch from mathis) + . updated the Makefile + . removed display of copyrights. + . changed the Copyright string to "LGPL VGABios developers" + - Volker + . set the cursor shape depending on the current font height + . clear BL before calling int 0x10 function 0x1103 in vgabios_init_func + . added some text font functions + - Jeroen + . Forced to new DISPI (0xb0c1) interface (requires latest bochs vbe code) + . Added multibuffering support + . Added new DISPI interface for: virt width, height, x offset, y offset + . Added LFB modes (to be used with the vbe-lfb patch in bochs) + see VBE_HAVE_LFB in vbe.c (currently default enabled) + . updated TODO & docs for changes after bochs 1.4 + +vgabios-0.3a : Mar 10 2002 + - Christophe + . Fixed bug in function ah=13 + - Jeroen + . updated vbebios implementation to new api + . added vbe_display_api documentation + . added 640x400x8, 640x480x8, 800x600x8, 1024x768 + (>640x480 needs a special bochs patch atm) + . added 320x200x8 vbe support (uses the standard 320x200x8 vga mode to + display, this allows for testing & having something on screen as well, + at least until bochs host side display is up & running) + . adding lfbprof (vbe) testprogram (+some small fixes to it) + . merging with vbebios 0.2 + +vgabios-0.2b : Nov 19 2001 + - Christophe + . Fixed bug in function ah=13 + +vgabios-0.2a : Nov 09 2001 + - Christophe + . Included bugfix from techt@pikeonline.net about grayscale summing + . Added the "IBM" string at org 0x1e as Bart Oldeman suggested + . Fixed DS and ES that where inverted in the int10 parameters list! + . The following have been implemented : + - function ax=1a00, ax=1a01, ah=1b + - function ax=1130 + . Added debug messages for unimplemented/unknown functions + Must be compiled with DEBUG defined. The output is trapped + by the unknown-ioport driver of plex/bochs (port 0xfff0 is used) + +vgabios-0.1a : May 8 2001 + - Christophe + . First release. The work has been focused only on text mode. + . The following have been implemented : + - inits + - int 10 handler + - functions ah=00, ah=01, ah=02, ah=03, ah=05, ah=06, ah=07, ah=08 + ah=09, ah=0a, ah=0e, ah=0f, ax=1000, ax=1001, ax=1002, ax=1003 + ax=1007, ax=1008, ax=1009, ax=1010, ax=1012, ax=1013, ax=1015 + ax=1017, ax=1018, ax=1019, ax=101a, ax=101b, ah=12 bl=10, + ah=12 bl=30, ah=12 bl=31, ah=12 bl=32, ah=12 bl=33, ah=12 bl=34 + ah=13 diff --git a/pintos-env/share/bochs/VGABIOS-lgpl-latest b/pintos-env/share/bochs/VGABIOS-lgpl-latest new file mode 100755 index 0000000..990f32a Binary files /dev/null and b/pintos-env/share/bochs/VGABIOS-lgpl-latest differ diff --git a/pintos-env/share/bochs/VGABIOS-lgpl-latest-cirrus b/pintos-env/share/bochs/VGABIOS-lgpl-latest-cirrus new file mode 100755 index 0000000..3737153 Binary files /dev/null and b/pintos-env/share/bochs/VGABIOS-lgpl-latest-cirrus differ diff --git a/pintos-env/share/bochs/VGABIOS-lgpl-latest-cirrus-debug b/pintos-env/share/bochs/VGABIOS-lgpl-latest-cirrus-debug new file mode 100755 index 0000000..8331da5 Binary files /dev/null and b/pintos-env/share/bochs/VGABIOS-lgpl-latest-cirrus-debug differ diff --git a/pintos-env/share/bochs/VGABIOS-lgpl-latest-debug b/pintos-env/share/bochs/VGABIOS-lgpl-latest-debug new file mode 100755 index 0000000..8e80c97 Binary files /dev/null and b/pintos-env/share/bochs/VGABIOS-lgpl-latest-debug differ diff --git a/pintos-env/share/doc/bochs/CHANGES b/pintos-env/share/doc/bochs/CHANGES new file mode 100755 index 0000000..ebf633c --- /dev/null +++ b/pintos-env/share/doc/bochs/CHANGES @@ -0,0 +1,3823 @@ +Changes in 2.6 (September 2, 2012): + +Brief summary : +- More than 10% CPU emulation speedup ! +- Support for AMD's SVM hardware emulation (including extended XAPIC support). +- Implemented support for new x86 ISA extensions. + Bochs is fully aligned with rev043 of Intel(R) Architecture Manual. +- Improved emulation accuracy (critical fixes for APIC, VMX and AVX/XOP emulation). +- Bochs internal debugger: new command to show state of a device from the debugger. +- ROM BIOS: improved PCI boot ROM support (for VGA and other devices) +- Networking: + - Ported Intel(R) 82540EM Gigabit Ethernet adapter emulation from Qemu. + - Added PCI network boot ROM support to all network adapters. + - Added TFTP support to the 'slirp' networking module. +- Harddrive: added support for VirtualPC disk images, fixed sparse disk images. +- Sound: + - implemented PC speaker beep using the lowlevel sound interface. + - SDL audio output support. +- Added ability to set log action per device from .bochsrc. +- Moved disk imaging, networking, sound and USB devices to subdirectories + in the iodev folder. + +Detailed change log : + +- CPU + - Implemented EPT A/D extensions support. + Bochs is fully aligned with rev043 of the of Intel(R) Architecture Manual. + - Implemented ADX (ADCX/ADOX) instructions support, the feature can + be enabled using .bochsrc CPUID option. + - More than 10% CPU emulation speedup with even more optimal lazy flags + handling, stack access optimizations and cross branch trace linking. + - Support for AMD's SVM hardware emulation in Bochs CPU, to enable + configure with --enable-svm option + - Implemented AMD Extended XAPIC support, to enable set .bochsrc CPU + APIC option to 'xapic_ext' + ! Added Corei5 750 (Lynnfield) configuration to the CPUDB + ! Added Turion64 (Tyler) configuration to the CPUDB + ! Added AMD Phenom X3 8650 (Toliman) configuration to the CPUDB + ! Added Corei7 3770K (Ivy Bridge) configuration to the CPUDB + - Bugfixes for CPU emulation correctness and stability + (critical fixes for APIC/X2APIC, VMX and AVX/XOP emulation) + +- Bochs Debugger and Instrumentation + - Implemented new debugger command 'info device [string]' that shows the + state of the device specified in 'string' + - Improved debug dump for ne2k, pci, pic and vga/cirrus devices. Added + debug dump for pci2isa, i/o apic, cmos, pit, floppy and dma devices. + - Added TLB to CPU param tree - now it can be browsed from Bochs internal + debugger and Bochs debugger GUI through param tree interfaces + - Implemented 'writemem' debugger command to dump virtual memory block + starting from selected linear address into a file + - Updated definition of instrumentation callbacks, see description in + instrumentation.txt / Fixed instrumentation examples + +- Configure and compile + - Moved disk imaging, networking, sound and USB devices to subdirectories + in the iodev folder. + - pcidev: enable support for Linux kernel 3.x (Debian patch by Guillem Jover) + - debugger: generate parser / lexer files only if all required tools are present + +- Config interface + - Added support for direct device plugin loading with bochsrc directive for + devices which have the same name for the plugin and config option. + - The bochsrc option 'plugin_ctrl' can now be used to load/unload optional + plugins directly when parsing the config file or command line. See the bochsrc + sample for supported devices. + - Moved bochsrc parsing / writing and config parameter handling for networking, + sound and USB devices to the plugin device code. The options are only + available when the corresponding plugin device is loaded. + - Added ability to set log action per device from .bochsrc. + - Added new command line option '-noconsole' to disable the console + window on Windows host. + - Renamed PCI Pseudo NIC option to "pcipnic" (for direct plugin loading) + - Moved several related options to the new "keyboard" bochsrc option. + - Added new parameter 'rtc_sync' for the 'clock' option. If this option + is enabled together with the realtime synchronization, the RTC runs + at realtime speed. + - Moved MWAIT_IS_NOP bochsrc option from CPUID to CPU so it can be set + even if cpu was configured using pre-defined CPUDB profile. + - Allow larger CPU 'quantum' values when emulating SMP systems for speed + (quantum values up to 32 are allowed now). + +- I/O Devices + - Networking + - Ported Intel(R) 82540EM Gigabit Ethernet adapter emulation from Qemu, + to enable configure with option --enable-e1000 + - Added PCI network boot ROM support to all network adapters + - Added TFTP support to the 'slirp' networking module + - PCI + - added "auto-assign to PCI slot" feature for PCI-only devices + - DMA + - Added the capability to transfer more then one byte / word per ISA DMA cycle + - VGA + - Major rewrite of the vga / cirrus code: + - vgacore (shared standard VGA emulation code) + - vga (Bochs VBE and PCI VGA) + - cirrus (CL-GD 5430 ISA / CL-GD 5446 PCI) + - Added VGA graphics blinking support + - More accurate vertical and horizontal retrace emulation + (based on the DOSBox implementation) + - hard drive / hdimage + - added new disk image mode 'vpc' for VirtualPC images + - undoable mode: added coherency check (flat image size and timestamp) + - sparse mode: fixed read support + - Sound + - implemented PC speaker beep using the lowlevel sound interface + - added SDL audio output support + +- ROM BIOS + - improved PCI boot ROM support (for VGA and other devices) + - added MADT entry for Interrupt Source Override to ACPI tables + +- GUI and display libraries + - implemented "auto-off" timer for status LEDs indicating a data transfer + - Added support for sending absolute mouse position values from the gui if an + absolute pointing device (USB tablet) is active (rfb, sdl, win32, wx, x). + - Gui debugger now supported when using sdl gui on Windows + - Implemented "hideIPS" option in rfb, sdl, win32 and wx libraries + - wx: fixed random freezes with wxGTK and "show ips" feature enabled + - rfb: the "show ips" feature now works on Windows host + +- Tools + - bxcommit: added support for converting growing to flat mode images + - bxcommit: support command line options and non-interactive (quiet) mode + - bximage: increased maximum disk size to 8 TB + +- SF patches applied + [3540389] Patch 5 : Change memory reference functions argument order by Yeong-uk Jo + [3539254] Patch 4 : Memory reference optimization 2 by Yeong-uk Jo + [3539251] Patch 3 : Memory reference optimization by Yeong-uk Jo + [3539237] Patch 2 : Some optimization by Yeong-uk Jo + [3539228] Patch 1 : ROM BIOS Compatibility patch by Yeong-uk Jo + [3505209] Fixed combo box size by Konrad Grochowski + [2864391] Gui debugger default regs by Thomas Nilsen + [3486555] Fix critical stack leak in Win32 GUI by Carlo Bramini + +- these S.F. bugs were closed/fixed + [625877] wx: power button -> thread deadlock + [3534063] Configure does not check for flex presence + [3555237] NE2000 doesn't compile on OS/X + [3553168] X doesn't build in latest code on OS/X + [3550175] Crash when saving snapshot to directory instead of file + [3548109] VMX State Not Restored After Entering SMM on 32-bit Systems + [3548108] VMEXIT Instruction Length Not Always Getting Updated + [3545941] Typo in preprocessor symbol + [3538731] Missing CR8 register + [3538574] Missing XD flag ( Execute Disable ) when showing PTEs + [3537556] Missing initializations + [3537309] Unable to use the debug GUI with SDL and plugins + [3537473] GUI debugger only shows 32-bit CRx registers in x86-64 mode + [3533426] UHCI PCI interrupts + [3459359] svga cirrus initialization + [3535228] info gdt does not show long mode segments + [3531806] Bochs crashes (SIGSEGV) when starting via ssh console + [3531807] Various missing initialization values reported by Valgrind + [635789] mapping mode 0 not supported + [3307201] BOCHS panics when execute HBIOS.COM + [3528809] IO APIC index register width + [3528516] Missing #if in soundmod.h + [3526069] MADT:Interrupt Source Override missed + [3518753] update dump after manual chages to memory contents + [3516859] bug in svn e1000 module + [3516029] stepping not working in debugger GUI in case of smp vm + [3510403] closing config dialog box closes entire simulator + [3459998] Bochs cannot be compiled outside the source tree + [2905969] can't use --enable-gdb-stub on vs2008 + [3294001] Bochs GUI doesn't appear properly for 2nd Bochs copy + [3493315] Changing VGA 9/8 dot mode causes screen corruption + +- these S.F. feature requests were closed/implemented + [3540441] automatically enable devices in plugin control + [1251456] command line method to query bochs for features + [3409396] sdl sound + [3519794] debugger's ability to save physical/linear memory dumps + [1429612] Idea how to speedup simulation - stack direct access + [1703452] Other Network Devices? + [2919376] Disable show the console window + [534271] can't set log action by device (bochsrc) + +------------------------------------------------------------------------- +Changes in 2.5.1 (January 6, 2012): + +- CPU / CPUDB + ! Added Athlon64 (Venice) configuration to the CPUDB + - BMI: fixed EFLAGS after BMI instructions + - MSR: access to AMD extended MSR space was impossible due to a bug in RDMSR/WRMSR + - VMX: fixed VMFUNC instruction behavior to align with Intel SDM revision 041 + - VMX: fixed Bochs PANIC when doing I/O access crossing VMX I/O permission bitmaps + - VMX: fixed VirtualBox VMX guest Guru Meditation - FS.BASE got corrupted after + saving/restoring unusable FS selector + - VMX: fixed VirtualBox failures with VMX+EPT enabled + - Better report of supported CPUID features when not using pre-defined CPUID profile + +- Debugger / Instrumentation + - fixed typo - closing SF bug [3461405] step all command fails in SMP mode + - instrumentation: added special indication for indirect call/jump + +- Configure and compile + - fixed compilation err in instrumentation call (tasking.cc) + - fixed compilation err with x86 hw breakpoint enabled and CPU_LEVEL < 6 + - fixed compilation issue under win32 --with-nogui + - added missing dependencies for cdrom_osx.o + - removed very old deprecated configure options from configure script + +- I/O Devices + - fixed possible failures of PCI DMA transfers + - VVFAT: several fixes in the optional write support (passes test with XP) + - USB UHCI: some fixes to make it work in XP guest + - removed devices 'acpi', 'ioapic' and 'pci_ide' from optional plugin control + to avoid trouble + +- Config interface / GUI and display libraries + - added VMX .bochsrc option to control VMX support on runtime + - fix for x86-64 .bochsrc option parsing (patch from @SF) + - fix for @SF bug: Crash on WIN2K - ID: 3454214 + - added 'nokeyrepeat' option for the SDL and win32 gui + +------------------------------------------------------------------------- +Changes in 2.5 (November 27, 2011): + + Bochs repository moved to the SVN version control ! + +Brief summary : + +! Fully configurable CPU to emulate with a single .bochsrc option ! +- 10% (ST) to 50% (SMP) CPU emulation speedup ! +- Implemented support for new x86 ISA extensions, Bochs is aligned with + latest published Intel Architecture Manual (rev 040, AVX rev 011): + - XSAVEOPT, AVX/AVX2/FMA/F16C, BMI1/BMI2, SMEP, INVPCID, TSC-Deadline + - VMX: VMX Preemption Timer, Pause Loop Exiting and VM Functions +- Implemented support for AMD SSE4A/XOP/FMA4/TBM instruction sets +- Networking: introduced new networking module 'slirp' +- Harddrive: fixed buffer overflow causing Bochs crash in LBA48 mode +- VGA: Added PCI ROM support to cirrus and pcivga and moved ROM loading + for the ISA case to the vga code (SeaBIOS now usable by Bochs) +- Sound: ported ES1370 soundcard emulation from Qemu +- Continuing configure rework, check for more removed configure and .bochsrc + options and their replacements ! +- LGPL'd VGABIOS updated to version 0.7a + +Detailed change log : + +- CPU + - Now you can configure CPU to emulate using a single .bochsrc option ! + The option selects CPU configuration to emulate from pre-defined list + of supported configurations. When this option is used, Bochs CPU emulation + engine is automatically configured to emulate a specific real hardware CPU, + including exact CPUID matching reference hardware. Check .bochsrc example + or check user manual for list of supported configurations and more details. + * It is also possible to choose the CPU to emulate from Bochs command line + using command line interface to .bochsrc: "cpu::model " + * Query for supported CPU models using command line option: -help cpu. + + - 10% emulation speedup with handlers chaining optimization implemented. The + feature is enabled by default when configure with --enable-all-optimizations + option, to disable handlers chaining speedups configure with + --disable-handlers-chaining + - New way of CPUs scheduling in SMP mode brings up to 50% speedup to the + SMP emulation. New implementation uses dynamic CPU quantum value and takes + full advantage of the trace cache. Each emulated processor will execute + the whole trace before switching to the next processor. + * It is also safe to use large (up to 16 instructions) quantum values for + the SMP emulation now and improve performance even further. + + - Implemented Supervisor Mode Execution Protection (SMEP), the feature can + be enabled using .bochsrc CPUID option. + - Added support for XSAVEOPT instruction, the instruction can be enabled + using .bochsrc CPUID option. + - Added support for AVX and AVX2 instructions emulation, to enable configure + with --enable-avx option. When compiled in, AVX still has to be enabled + using .bochsrc CPUID option. + - Added emulation of AVX float16 convert instructions, the feature can be + enabled using .bochsrc CPUID option. + - Added support for AVX2 FMA instructions emulation. The implementation + was ported (with few bugfixes) from QEMU patch by Peter Maydell. + The FMA instructions support can be enabled using .bochsrc CPUID option. + - Added support for Bit Manipulation Instructions (BMI1/BMI2) emulation. + The BMI instructions support can be enabled using .bochsrc CPUID option. + - Added support for AMD SSE4A/XOP/FMA4/TBM extensions emulation, the + instructions can be enabled using .bochsrc CPUID option. + - Implemented VMX preemption timer VMEXIT control (patch by Jianan Hao) + - Implemented Pause-Loop Exiting Secondary VMEXIT control. + - Implemented VM Functions support and EPTP-Switching VM Function. + - Added INVPCID instruction emulation support. + - Added APIC timer TSC-Deadline mode emulation support. + - Now you could disable x86-64 from .bochsrc so it become possible to + emulate 32-bit CPUs using Bochs binary compiled with x86-64 support. + - Updated/fixed instrumentation callbacks. + - Bugfixes for CPU emulation correctness and stability. + +- Bochs Internal Debugger and Debugger GUI + - Bochs disassembler fixes / new instructions support. + - Fixed timer breakpoint handling in Bochs internal debugger. + - Fixed bug in Bochs internal debugger 'show off' command. + - Added Bochs internal debugger command 'vmexitbp' to set breakpoint on + VMX guest VMEXIT (patch by Jianan Hao). Type 'vmexitbp' in debugger + command window to switch it on/off (similar to modebp). + - Fixed linear to physical address translation by Bochs internal debugger + for EPT unrestricted guest (VMX guest with paging disabled under EPT) + - Fixed bug in GUI debugger SSE registers display. + - Correctly display current CPU mode in GUI debugger status bar. + - Turn off the mouse capture when the internal debugger or gdbstub enter + the input loop. + +- Memory + - Added new configure option which enables RAM file backing for large guest + memory with a smaller amount host memory, without causing a panic when + host memory is exhausted (patch by Gary Cameron). To enable configure with + --enable-large-ramfile option. + +- Configure and compile + - Fixed Bochs manifest for Win64 compilation using Microsoft Visual Studio + command line compiler. + - Added ability to configure CPUID family through .bochsrc. + The default family value determined by configure option --enable-cpu-level. + - Added ability to configure CPUID model through .bochsrc. + The default model value is 3. + - Added ability to configure x2apic support through .bochsrc. + The APIC configuration could be selected using new CPUID .bochsrc APIC option. + Possible configurations are: "legacy", "xapic" and "x2apic". + Configure option --enable-x2apic and Bochs 2.4.6 .bochsrc XAPIC option are + deprecated and should not be used anymore. + - Configure option --enable-vbe is deprecated and should not be used anymore. + The VBE support is always automatically compiled in, in order to enable + VBE support the .bochsrc option "vga: extension=" has to be set to "vbe". + If PCI is present, the "pcivga" device can be assigned to PCI slot. + - Configure option --enable-acpi is deprecated and should not be used anymore. + The ACPI support is always automatically compiled in if PCI is compiled in. + The ACPI still could be disabled using .bochsrc 'plugin_ctrl' option. + - Removed --enable-trace-cache configure option. The option will be always ON + for any Bochs configuration. + - Compile in MONITOR/MWAIT support by default for all cpu-level=6 configurations. + - added support for MSVC DLL plugins with a separate workspace package. + VS2008Ex can now create a BOCHS.EXE with a set of plugin DLLs. + TODO: nmake still cannot create plugin DLLs. + - removed some outdated / unmaintained parts from the Bochs code: BeOS host + support, plex86 support, networking module 'arpback', text snapshot check + feature. + +- I/O Devices + - Networking + - new networking module 'slirp' (user mode networking using Slirp and a + builtin DHCP server) + - Hard drive / cdrom + - fixed buffer overflow causing Bochs crash in LBA48 mode + - implemented ATA commands "READ NATIVE MAX ADDRESS" and + "READ NATIVE MAX ADDRESS EXT" + - Sound + - ported ES1370 soundcard emulation from Qemu, to enable configure with + the option --enable-es1370 + - sound input implemented in the sound lowlevel modules for Windows and + Linux (ALSA / OSS) + - PCI + - added framework for PCI ROM support + - new bochsrc option 'pci' replaces the 'i440fxsupport' option. The 'chipset' + parameter for now only accepts the value 'i440fx'. + - VGA + - added PCI ROM support to cirrus and pcivga and moved ROM loading for the ISA + case to the vga code (SeaBIOS now usable by Bochs) + - log prefix now depends on the selected extension (new prefix BXVGA for + Bochs VBE support) + - USB + - experimental USB xHCI support (written by Ben Lunt) + + - LGPL'd VGABIOS updated to version 0.7a + - implemented vgabios functions with AX=0x112x (patch by Hugo Mercier) + - fixed DAC palette in 8 bpp VBE and Cirrus modes (using the same palette + as VGA mode 0x13) + - VBE: added HDTV resolutions (patch by Tristan Schmelcher) + - VBE: added PCI ROM signature and data structure + + - ROM BIOS + - Report memory above 4GB to BIOS (patch by Sebastian Herbszt) + - added PCI ROM init code for BIOS-bochs-latest + (WARNING: legacy BIOS no longer works with a PCI display adapter) + +- GUI and display libraries + - new parameter 'update_freq' for the 'vga' bochsrc option replaces the + 'vga_update_interval' option + - vga update frequency now uses host timing if the realtime synchronization + is enabled with the "clock" option (FIXME: it should always be used - + independent from the "clock" setting) + - Implemented graphics mode snapshot for VBE, Cirrus and standard VGA modes. + CGA modes are not supported yet. + - added 'x' display library option 'nokeyrepeat' to turn off keyboard repeat + +- Config interface + - win32paramdlg: dialog size now adjusted to support larger label text + - win32paramdlg: added tooltip support using the parameter description + +- SF patches applied + [3412431] Enabling raw devices as hdimage by affiss + [3435049] rombios: fix package size in pointing device flags 2 by Sebastian Herbszt + [3426460] [PATCH] PIC: remove never-executed code by Christian Inci + [3370604] Ctrl-Break support for the bochs BIOS by Nikolay Nikolov + [3302668] VMX preemption timer by Jianan Hao + [3327510] Fix wrong address translation in debugger by Jianan Hao + [3323758] Ctrl-Break support for the Win32 gui by Nikolay Nikolov + [3316785] Ctrl-Break support for the X11 gui by Nikolay Nikolov + [3298173] Breakpoint on VMEXIT event by Jianan Hao + [3295737] Fix CopyHost*WordLittleEndian macros by Heikki Lindholm + [3289448] optimized powerpc byte swapping by Heikki Lindholm + [3292581] Core Audio first aid by Heikki Lindholm + [3205979] Compilation fixes for OpenBSD by Brad Smith + [3290979] acpi/muldiv64 endian bug by Heikki Lindholm + [3289459] Mac OS X audio missing framework dependency by Heikki Lindholm + [3267459] fix xrandr related crash by Heikki Lindholm + [3190995] add eth backend based on Slirp by Heikki Lindholm + +- these S.F. bugs were closed/fixed + [3365456] block device dimensions problem + [3441564] interrupts vectors 0x67 should also be NULL ! cf: 2902118 + [2829847] Mouse locked during magic-break + [3418621] release mouse when debugger breakpoint was hit + [1947077] sb command bug + [2802677] Unable to install Cirrus SVGA driver in guest Windows ME + [3422638] large ramfile support broken on anything but Linux + [3312237] stepN command might be not working properly + [3392760] Bochs does not compile with linux3.0 + [3403746] segfault crash with sparse disk images + [3062054] Problems with BIOS pointing device services (int 15h) + [3277639] incompatible colours in palette + [1788739] Abort on large memory setting + [3012207] Int 13h FN 48h incorrect return values + [3363289] holding shift key causes capslock hang + [1819639] Two incompatible crc32 modules + [3324111] configure for VCPP.NET issues + [3190970] Installing linux causes a crash in pci_ide + [3077616] Fedora 13 installation fails on Bochs 2.4.5 + [3294671] ./configure --enable-pci --disable-cdrom + [3303818] wrong memory size is reported to GRUB (e820 problem?) + [3297475] trace cache disabled mode will miss SMC + [3170157] BIOS32 PCI service wrong length + [3025030] PIT mistakenly connected to IRQ0 IOAPIC instead of IRQ2 + [3266738] GUI debugger does not update CPU mode correctly + [3292571] SB16 doesn't reinit correctly + [3175168] Cirrus CL-GD5446 emulation incorrect + [3260134] Failed to compile when trace cache disabled + [3197425] Error compile with vmx in vs2008/2010 and for correct x64 + +- these S.F. feature requests were closed/implemented + [3424738] Legacy BIOS int13 AL=17/18h diskette issue + [1197067] Screenshot for graphical + [2800839] VMX-preemption timer + [1507519] configurable CPUID + [579002] Allow user to specify architecture + [1228567] CPU option + +------------------------------------------------------------------------- +Changes in 2.4.6 (February 22, 2011): + +Brief summary : +- Support more host OS to run on: + - Include win64 native binary in the release. + - Fixed failures on big endian hosts. +- BIOS: Support for up to 2M ROM BIOS images. +- GUI: select mouse capture toggle method in .bochsrc. +- Ported most of Qemu's 'virtual VFAT' block driver + (except runtime write support, but plus FAT32 support) +- Added write protect option for floppy drives. +- Bugfixes / improved internal debugger + instrumentation. + +Detailed change log : + +- CPU and internal debugger + - Implemented Process Context ID (PCID) feature + - Implemented FS/GS BASE access instructions support + (according to document from http://software.intel.com/en-us/avx/) + - Rewritten from scratch SMC detection algorithm + - Implemented fine-grained SMC detection (on 128 byte granularity) + - Bugfixes for CPU emulation correctness and stability + - Fixed failures on Big Endian hosts ! + - Print detailed page walk information and attributes in + internal debugger 'page' command + - Updated/Fixed instrumentation callbacks + +- Configure and compile + - Bochs now can be compiled as native Windows x86-64 application + (tested with Mingw gcc 4.5.1 and Microsoft Visual Studio Express 2010) + - Added ability to configure CPUID stepping through .bochsrc. + The default stepping value is 3. + - Added ability to disable MONITOR/MWAIT support through .bochsrc + CPUID option. The option is available only if compiled with + --enable-monitor-mwait configure option. + - Determine and select max physical address size automatically at + configure time: + - 32-bit physical address for 386/486 guests + - 36-bit physical address for PSE-36 enabled Pentium guest + - 40-bit physical address for PAE enabled P6 or later guests + - Update config.guess/config.sub scripts to May 2010 revisions. + - Update Visual Studio 2008 project files in build/win32/vs2008ex-workspace.zip + - Added Bochs compilation timestamp after Bochs version string. + +- GUI and display libraries (Volker) + - Added new .bochsrc option to select mouse capture toggle method. + In addition to the default Bochs method using the CTRL key and the + middle mouse button there are now the choices: + - CTRL+F10 (like DOSBox) + - CTRL+ALT (like QEMU) + - F12 (replaces win32 'legacyF12' option) + - display library 'x' now uses the desktop size for the maximum guest resolution + +- ROM BIOS + - Support for up to 2M ROM BIOS images + +- I/O Devices + - 3 new 'pseudo device' plugins created by plugin separation (see below) + - Fixes for emulated DHCP in eth_vnet (patch from @SF tracker) + - Added support for VGA graphics mode with 400 lines (partial fix for SF bug #2948724) + - NE2K: Fixed "send buffer" command issue on big endian hosts + - USB + - converted common USB code plus devices to the new 'usb_common' plugin + Now the USB device classes no longer exist twice if both HC plugins are loaded. + - added 'pseudo device' in common USB code for the device creation. This makes + the HCs independent from the device specific code. + - USB MSD: added support for disk image modes (like ATA disks) + - USB printer: output file creation failure now causes a disconnect + - re-implemented "options" parameter for additional options of connected + devices (currently only used to set the speed reported by device and to + specify an alternative redolog file of USB MSD disk image modes) + - hard drive + - new disk image mode 'vvfat' + - ported the read-only part of Qemu's 'virtual VFAT' block driver + - additions: configurable disk geometry, FAT32 support, read MBR and/or + boot sector from file, volatile write support using hdimage redolog_t + class, optional commit support on Bochs exit, save/restore file + attributes, 1.44 MB floppy support, set file modification date/time + - converted the complete hdimage stuff to the new 'hdimage' plugin + - new hdimage method get_capabilities() that can return special flags + - vmware3, vmware4 and vvfat classes now return HDIMAGE_HAS_GEOMETRY flag + - other disk image modes by default return HDIMAGE_AUTO_GEOMETRY if + cylinder value is set to 0 + - multiple sector read/write support for some image modes + - new log prefix "IMG" for hdimage messages + - floppy + - added write protect option for floppy drives (based on @SF patch by Ben Lunt) + - vvfat support + - bugfix: close images on exit + - SB16 + - converted the sound output module stuff to the new 'soundmod' plugin + +- SF patches applied + [3164945] hack to compile under WIN64 by Darek Mihocka and Stanislav + [3164073] Fine grain SMC invalidation by Stanislav + [1539417] write protect for floppy drives by Ben Lunt + [2862322] fixes for emulated DHCP in eth_vnet + +- these S.F. bugs were closed/fixed + [2588085] Mouse capture + [3140332] typo in mf3/ps2 mapping of BX_KEY_CTRL_R + [3111577] No "back" option in log settings + [3108422] Timing window in NE2K emulation + [3084390] Bochs won't load floppy plugin right on startup + [3043174] Docbook use of '_' build failure + [3085140] Ia_arpl_Ew_Rw definition of error + [3078995] ROL/ROR/SHL/SHR modeling wrong when dest reg is 32 bit + [2864794] BX_INSTR_OPCODE in "cpu_loop" causes crash in x86_64 host + [2884071] [AIX host] prefetch: EIP [00010000] > CS.limit [0000ffff] + [3053542] 64 bit mode: far-jmp instruction is error + [3011112] error compile vs2008/2010 with X2APIC + [3002017] compile error with vs 2010 + [3009767] guest RFLAGS.IF blocks externel interrupt in VMX guest mode + [2964655] VMX not enabled in MSR IA32_FEATURE_CONTROL + [3005865] IDT show bug + [3001637] CMOS MAP register meaning error + [2994370] Cannot build with 3DNow support + +- these S.F. feature requests were closed/implemented + [1510142] Native Windows XP x64 Edition binary + [1062553] select mouse (de)activation in bochsrc + [2930633] legacy mouse capture key : not specific enough + [2930679] Let user change mouse capture control key + [2803538] Show flags for pages when using "info tab" + +------------------------------------------------------------------------- +Changes in 2.4.5 (April 25, 2010): + +Brief summary : +- Major configure/cpu rework allowing to enable/disable CPU options at runtime + through .bochsrc (Stanislav) +- Bugfixes for CPU emulation correctness and stability +- Implemented X2APIC extensions (Stanislav) +- Implemented Intel VMXx2 extensions (Stanislav) + - Extended VMX capability MSRs, APIC Virtualization, + X2APIC Virtualization, Extended Page Tables (EPT), + VPID, Unrestricted Guests, new VMX controls. +- Implemented PCLMULQDQ AES instruction +- Extended Bochs internal debugger functionality +- USB HP DeskJet 920C printer device emulation (Ben Lunt) + +Detailed change log : + +- Configure rework + - Deprecate --enable-popcnt configure option. POPCNT instruction will be + enabled automatically iff SSE4_2 is supported (like in hardware). + + - Make --ignore-bad-msrs runtime option in .bochsrc. Old --ignore-bad-msrs + configure option is deprecated and should not be used anymore. + + - Enable changing part of CPU functionality at runtime through .bochsrc. + - Now you could enable/disable any of SSEx/AES/MOVBE/SYSENTER_SYSEXIT/XSAVE + instruction sets using new CPUID option in .bochsrc. + - When x86-64 support is compiled in, you could enable/disable long mode + 1G pages support without recompile using new CPUID option in .bochsrc. + Configure options: + --enable-mmx, --enable-sse, --enable-movbe, --enable-xsave, + --enable-sep, --enable-aes, --enable-1g-pages + are deprecated and should not be used anymore. + + - Local APIC configure option --enable-apic is deprecated and should not + be used anymore. The LAPIC option now automatically determined from + other configure options. XAPIC functionality could be enabled using + new CPUID .bochsrc option. + + - Changed default CPU configuration (generated by configure script with + default options) to BX_CPU_LEVEL=6 with SSE2 enabled. + +- CPU + - Implemented PCLMULQDQ AES instruction + - Implemented X2APIC extensions / enable extended topology CPUID leaf (0xb), + in order to enable X2APIC configure with --enable-x2apic + - Implemented Intel VMXx2 extensions: + - Enabled extended VMX capability MSRs + - Implemented VMX controls for loading/storing of MSR_PAT and MSR_EFER + - Enabled/Implemented secondary proc-based vmexec controls: + - Implemented APIC virtualization + - Implemented Extended Page Tables (EPT) mode + - Implemented Descriptor Table Access VMEXIT control + - Implemented RDTSCP VMEXIT control + - Implemented Virtualize X2APIC mode control + - Implemented Virtual Process ID (VPID) + - Implemented WBINVD VMEXIT control + - Implemented Unrestricted Guest mode + In order to enable emulation of VMXx2 extensions configure with + --enable-vmx=2 option (x86-64 must be enabled) + - Bugfixes for CPU emulation correctness + - Fixed Bochs crash when accessing the first byte above emulated memory size + +- Internal Debugger + - Introduced range read/write physical watchpoints + - Allow reloading of segment registers from internal debugger + - Improved verbose physical memory access tracing + +- BIOS + - Fix MTRR configuration (prevented boot of modern Linux kernels) + - Fix interrupt vectors for INT 60h-66h (reserved for user interrupt) by + setting them to zero + - Fix BIOS INT13 function 08 when the number of cylinders on the disk = 1 + +- I/O Devices + - USB HP DeskJet 920C printer device emulation (Ben Lunt) + +- Misc + - Updated Bochs TESTFORM to version 0.5 + +- SF patches applied + [2864402] outstanding x2apic patches by Stanislav + [2960379] Fix build with -Wformat -Werror=format-security by Per Oyvind Karlsen + [2938273] allow instrumentation to change execute by Konrad Grochowski + [2926072] Indirection operators in expressions by Derek Peschel + [2914433] makesym.perl misses symbols by John R. Jackson + [2908481] USB Printer by Ben Lunt + +- these S.F. bugs were closed/fixed + [2861662] dbg_xlate_linear2phy needs to be updated + [2956217] INT13 AH=8 returns wrong values when cylinders=1 + [2981161] Allow DMA transfers to continue when CPU is in HALT state + [2795115] NX fault could be missed + [2964824] bad newline sequence in aspi-win32.h + [913419] configure options and build process needs some work + [2938398] gdbstub compile error with x86_64 enabled + [2734455] shutdown/reset type 05 should reinit the PICs + [1921294] extended memory less than 1M wrong size + [1947249] BX_USE_EBDA_TABLES and MP table placement + [1933859] BX_USE_EBDA_TABLES and memory overlapping + [2923680] "help dregs" is a syntax error + [2919661] CPU may fail to do 16bit near call + [2790768] Memory corruption with SMP > 32, Panic BIOS Keyboard Error + [2902118] interrupts vectors 0x60 to 67 should be NULL ! + [2912502] Instruction Pointer behaving erratically + [2901047] Bochs crashed, closed by guest os + [2905385] Bochs crash + [2901481] Instruction SYSRET and SS(PL) + [2900632] Broken long mode RETF to outer priviledge with null SS + [1429011] Use bx_phyaddr_t for physaddr vars and bx_adress for lin adr + +- these S.F. feature requests were closed/implemented + [2955911] RPM preuninstall scriptlet removes /core + [2947863] don't abort on unrecognised options + [2878861] numerics in the disassembler output + [2900619] make more CPU state changeable + +------------------------------------------------------------------------- +Changes in 2.4.2 (November 12, 2009): + +- CPU and internal debugger + - VMX: Implemented TPR shadow VMEXIT + - Bugfixes for CPU emulation correctness (mostly for VMX support). + - Bugfixes and updates for Bochs internal debugger + - On SMP system stepN command now affects only current processor + +- Memory + - Bugfixes for > 32-bit physical address space. + - Allow to emulate more physical memory than host actually could or would + like to allocate. For more details look for new .bochsrc 'memory' option. + +- Cleanup configure options + - All paging related options now will be automatically determined according + to --enable-cpu-level option. Related configure options + --enable-global-pages, --enable-large-pages, + --enable-pae, --enable-mtrr + are deprecated now. Only 1G paging option still remaining unchanged. + - Deprecate --enable-daz configure option. Denormals-are-zeros MXCSR control + will be enabled automatically iff SSE2 is supported (like in hardware). + - Deprecate --enable-vme configure option, now it will be supported iff + CPU_LEVEL >= 5 (like in hardware). + +- I/O Devices + - Bugfixes for 8254 PIT, VGA, Cirrus-Logic SVGA, USB UCHI + +- SF patches applied + [2817840] Make old_callback static by Mark Marshall + [2874004] fix for VMWRITE instruction by Roberto Paleari + [2873999] fix CS segment type during fast syscall invocation by Roberto Paleari + [2864389] Debugger gui maximize on startup by Thomas Nilsen + [2817868] Rework loops in the memory code by Mark Marshall + [2812948] PIT bug by Derek + +- these S.F. bugs were closed/fixed + [2833504] GUI debugger bug-about GDT display + [2872244] BIOS writes not allowed value to MTRR MSR causing #GP + [2885383] SDL GUI memory leak + [2872290] compilation in AIX5.3 ML10 failes + [2867904] crash with cirrus bx_vga_c::mem_write + [2851495] BIOS PCI returns with INT flag = 0 + [2860333] vista 64 guest STOP 109 (GDT modification) + [2849745] disassembler bug for 3DNow and SSE opcodes + [1066748] Wrong registers values after #RESET, #INIT + [2836893] Regression: Windows XP installer unable to format harddrive + [2812239] VMX: VM-Exit: Incorrect instruction length on software int + [2814130] bx_debug lex/yacc files incorrectly generated + [2813199] MP Tables Missing From BIOS + [2824093] VMX exception bug + [2811909] VMX : CS Access-rights Type.Accessed stays 0 + [2810571] Compile Errors on OSX + [2823749] GCC regression or VM_EXIT RDMSR/WRMSR bug + [2815929] Vista/XP64 unnecessary panic + [2803519] Wrong example in man page bochsrc + +- these S.F. feature requests were closed/implemented + [422766] Large Memory configurations + [1311287] Idea for a better GUI + [455971] USB support + [615363] debugger shortcut for repeat last cmd + +------------------------------------------------------------------------- +Changes in 2.4.1 (June 7, 2009): + +- Fixed bunch of CPUID issues + - Bochs is now able to install and boot 64-bit Windows images! + (special thanks to Mark Ebersole for his patch) +- Several bugfixes in CPU emulation (mostly for x87 instructions) +- Fixed two critical deadlock bugs in the Win32 gui (patches from @SF tracker) +- Fixes related to the 'show ips' feature + - removed conflicting win32-specific alarm() functions ('win32' and 'sdl' gui) + - feature now works in wx on win32 +- Added support for gdb stub on big endian machine (patch by Godmar Back) +- Rewritten obsolete hash_map code in dbg symbols module (patch from @SF) +- BIOS: implemented missing INT 15h/89h (patch by Sebastian Herbszt) + +------------------------------------------------------------------------- +Changes in 2.4 (May 3, 2009): + +Brief summary : + +- Added graphical Bochs debugger frontend for most of the supported platforms. + - Thanks for Chourdakis Michael and Bruce Ewing. +- Many new CPU features in emulation + - Support for > 32 bit physical address space and configurable MSRs + - VMX, 1G pages in long mode, MOVBE instruction +- Bugfixes for CPU emulation correctness, debugger and CPU instrumentation. +- New config interface 'win32config' with start and runtime menu +- USB: added OHCI support, external hub and cdrom +- Added user plugin interface support. + +Detailed change log : + +- CPU and internal debugger + - Support for VMX hardware emulation in Bochs CPU, to enable configure with + --enable-vmx option + Nearly complete VMX implementation, with few exceptions: + - Dual-monitor treatment of SMIs and SMM not implemented yet + - NMI virtualization, APIC virtualization not implemented yet + - VMENTER to not-active state not supported yet + - No advanced features like Extended Page Tables or VPID + - Support for configurable MSR registers emulation, to enable configure with + --enable-configurable-msrs option + Look for configuration example in .bochsrc and msrs.def + - Support new Intel Atom(R) MOVBE instruction, to enable configure with + --enable-movbe option + - Support for 1G pages in long mode, to enable configure with + --enable-1g-pages option + - Support for > 32 bit physical address space in CPU. Up to 36 bit could be + seen in legacy mode (PAE) and up to 40 bit in x86-64 mode. + Still support the same amount of the physical memory in the memory object, + so system with > 4Gb of RAM yet cannot be emulated. + To enable configure with --enable-long-phy-address option. + - Implemented modern BIOSes mode limiting max reported CPUID function + to 3 using .bochsrc CPU option. The mode is required in order to + correctly install and boot WinNT. + - Added ability to configure CPUID vendor/brand strings through .bochsrc + (patch from @SF by Doug Reed). + - Many bugfixes for CPU emulation correctness (both x86 and x86-64). + - Updated CPU instrumentation callbacks. + - Fixed Bochs internal debugger breakpoints/watchpoints handling. + +- Configure and compile + - Added ability to choose Bochs log file name and Bochs debugger log file + name from Bochs command line (using new -log and -dbglog options) + - Removed Peter Tattam's closed source external debugger interface from + the code. + - Removed --enable-guest2host-tlb configure option. The option is always + enabled for any Bochs configuration. + - Removed --enable-icache configure option. The option is always enabled + for any Bochs configuration. Trace cache support still remains optional + and could be configured off. + - Added configure option to compile in GUI frontend for Bochs debugger, + to enable configure with --enable-debugger-gui option. + The GUI debugger frontend is enabled by default with Bochs debugger. + - Removed --enable-port-e9-hack configure option. The feature now could be + configured at runtime through .bochsrc. + - Added configure option to enable/disable A20 pin support. Disabling the + A20 pin support slightly speeds up the emulation. + - reduced dependencies between source files for faster code generation + +- BIOS + - Added S3 (suspend to RAM) ACPI state to BIOS (patch by Gleb Natapov) + - Implemented MTRR support in the bios (patches by Avi Kivity and Alex + Williamsion with additions by Sebastian Herbszt) + - Bug fixes + +- I/O Devices + - Added user plugin support + - remaining devices converted to plugins: pit, ioapic, iodebug + - added 'plugin_ctrl' bochsrc option to control the presence of optional + device plugins without a separate option. By default all plugins are enabled. + - added register mechanism for removable mouse and keyboard devices + - Hard drive / cdrom + - PACKET-DMA feature now supported by all ATAPI commands + - ATAPI command 0x1A added (based on the Qemu implementation) + - sb16 + - Added ALSA sound support on Linux (PCM/MIDI output) + - FM synthesizer now usable with MIDI output (simple piano only) + - Fixed OPL frequency to MIDI note translation + - Fixed MIDI output command + - keyboard + - added keyboard controller commands 0xCA and 0xCB + - USB + - USB code reorganized to support more HC types and devices + - added USB OHCI support written by Ben Lunt + - added external USB hub support (initial code ported from Qemu) + - added USB cdrom support (SCSI layer ported from Qemu) + - added status bar indicators to show data transfer + - VGA + - VBE video memory increased to 16 MB + - implemented changeable VBE LFB base address (PCI only, requires latest + BIOS and VGABIOS images) + - I/O APIC + - implemented I/O APIC device hardware reset + +- Config interface + - new config interface 'win32config' with start and runtime menu is now + the default on Windows ('textconfig' is still available) + - win32 device config dialogs are now created dynamicly from a parameter list + (works like the wx ParamDialog) + - changes in textcofig and the wx ParamDialog for compatibility with the new + win32 dialog behaviour + - Bochs param tree index keys are case independent now + - some other additions / bugfixes in the simulator interface code + +- Misc + - updated LGPL'd VGABIOS to version 0.6c + - Updated Bochs TESTFORM to version 0.4 + +- SF patches applied + [2784858] IO Handler names are not compared properly + [2712569] Legacy bios serial data buffer timeout bug by grybranix + [2655090] 64 bit BSWAP with REX.W broken by M. Eby + [2645919] CR8 bug when reading by M. Eby + [1895665] kvm: bios: add support to memory above the pci hole by Izik Eidus + [2403372] rombios: check for valid cdrom before using it by Sebastian + [2307269] acpi: handle S3 by Sebastian + [2354134] TAP networking on Solaris/Sparc repaired + [2144692] The scsi device can not complete its writing data command by naiyue + [1827082] [PATCH] Configurable CPU vendor by Marcel Sondaar + [2217229] Panic on EBDA overflow in rombios32 by Sebastian + [2210194] Log pci class code by Sebastian + [1984662] red led for disk write and titlebar mod by ggbsf + [2142955] Fix for monitor/mwait by Doug Gibson + [2137774] Patch to fix bug: cdrom: read_block: lseek returned error by Gabor Olah + [2134642] Fix scan_to_scanascii table for F11 and F12 by Ben Guthro & Steve Ofsthun + [2123036] sdl fullscreen fix by ggbsf + [2073039] Remove CMOS accsess from AML code by Gleb Natapov + [2072168] smbios: add L1-L3 cache handle to processor information by Sebastian + [2055416] bochsrc cpu options for cpuid vendor and brand string by Doug Reed + [2035278] rombios: Fix return from BEV via retf by Sebastian + [2035260] rombios: El Torito load segment fix by Sebastian + [2031978] Fix VMware backdoor command 0Ah by Jamie Lokier + [2015277] Remove obsolete comment about DATA_SEG_DEFS_HERE hack by Sebastian + [2011268] Set new default format and unit only if both are supported by Sebastian + [2001919] gdbstub: fix qSupported reply by Sebastian + [2001912] gdbstub: enclose packet data by apostrophes by Sebastian + [1998071] fix missing SIGHUP and SIGQUIT with term ui on mingw by Sebastian + [1998063] fix wrong colors with term ui by Sebastian + [1995064] Compile fix needed for --enable-debugger and gcc 4.3 by Hans de Goede + [1994564] Fix typo in RDMSR BX_MSR_MTRRFIX16K_A0000 by Sebastian + [1994396] Change hard_drive_post #if by Sebastian + [1993235] TESTFORM email address update by Sebastian + [1992322] PATCH: fix compilation of bochs 2.3.7 on bigendian machines by Hans de Goede + [1991280] Shutdown status code 0Ch handler by Sebastian + [1990108] Shutdown status code 0Bh handler by Sebastian + [1988907] Shutdown status code 0Ah handler by Sebastian + [1984467] two typos in a release! (2.3.7) + [1981505] Init PIIX4 PCI to ISA bridge and IDE by Sebastian + +- these S.F. bugs were closed/fixed + [2784148] an integer overflow BUG of Bochs-2.3.7 source code + [2695273] MSVC cpu.dsp failure in 2.3.7.zip + [616114] Snapshot/Copy crash on Win2K + [2628318] 'VGABIOS-latest' bug + [1945055] can't 'make install' lastest bochs on loepard + [2031993] Mac OS X Makefile bug + [1843199] install error on mac osx + [2710931] Problem compiling both instrumentation and debugger + [2617003] ExceptionInfo conflicts with OS X api + [2609432] stepping causes segfault (CVS) + [2605861] compile error with --enable-smp + [1757068] current cvs(Jul19, 07) failed to boot smp + [2426271] cannot get correct symbol entry + [2471982] VGA character height glitches + [1659659] wrong behaviour a20 at boot + [1998027] minwg + --with-term + --with-out-win32 = link failure + [1871936] bochs-2.3.6 make fails on wx.cc + [1684666] info idt for long mode + [2105989] could not read() hard drive image file at byte 269824 + [1173093] Debugger totally not supports x86-64 + [1803018] new win32debug dialog problems + [2141679] windows vcc build broken + [2162824] latest cvs fails to compile + [2164506] latest bochs fails to start + [2129223] MOV reg16, SS not working in real mode due to dead code + [2106514] RIS / startrom.com install ALMOST works + [2123358] SMP (HTT): wbinvd executed by CPU1 crashes CPU0 + [2002758] Arch Linux: >>PANIC<< ATAPI command with zero byte count + [2026501] El Torito incorrect boot segment:offset + [2029758] BEV can return via retf instead of int 18h + [2010173] x command breaks after one error about x/s or x/i + [1830665] harddrv PANIC: ATAPI command with zero byte count + [1985387] fail to make using gcc4 with --enable-debugger + [1990187] testform feedback + [1992138] Misspell in cpu/ia_opcodes.h + +- these S.F. feature requests were closed/implemented + [2175153] Update MSVC project files + [658800] front end program and bios + [1883370] Make cd and floppy images more usable + [422783] change floppy size without restarting + [2552685] param tree names should be case insensitive + [1214659] PC Speaker emu turnoff. Plugin Controll. + [1977045] support 40 bit physical address + [1506385] Intel Core Duo VT features + [1429015] Support for user plugins + [1488136] debugger access to floppy controller + [1363136] Full debugger SMP and 64 bit support + [2068304] Support for ACPI + [431032] debugger "x" command + [423420] profiling ideas (SMF) + [445342] Add FM support? + [928439] alsa + +------------------------------------------------------------------------- +Changes in 2.3.7 (June 3, 2008): + +Brief summary : + ++ More optimizations in CPU code - Bochs 2.3.7 is more than 2x faster + than Bochs 2.3.5 build ! +- Implemented LBA48 support in BIOS +- Added memory access tracing for Bochs internal debugger +- Implemented Intel(R) XSAVE/XRSTOR and AES instruction set extensions +- Many fixes in CPU emulation and internal debugger + - MenuetOS64 floppy images booting perfect again ! +- updated LGPL'd VGABIOS to version 0.6b + +Detailed change log : + +- CPU + - Support of XSAVE/XRSTOR CPU extensions, to enable configure with + --enable-xsave option + - Support of AES CPU extensions, to enable configure with + --enable-aes option + - Fixed Bochs failure on RISC host machines with BxRepeatSpeedups + optimization enabled + - Implemented SYSENTER/SYSEXIT instructions in long mode + - More than 100 bugfixes for CPU emulation correctness (both x86 and x86-64) + - MenuetOS64 floppy images booting perfect again ! + - Updated CPU instrumentation callbacks + +- Bochs Internal Debugger and Disassembler + - Added memory access tracing for Bochs internal debugger, enable + by typing 'trace-mem on' in debugger command line + - Many bug fixes in Bochs internal debugger and disassembler + +- System BIOS (Volker) + - Implemented LBA48 support + - Added generation of SSDT ACPI table that contains definitions + for available processors + - Added RTC device to ACPI DSDT table + - Added implementation of SMBIOS + +- I/O devices (Volker) + - VGA + - Implemented screen disable bit in sequencer register #1 + - Implemented text mode cursor blinking + - Serial + - new serial modes 'pipe-server' and 'pipe-client' for win32 + - new serial mode 'socket-server' + +- Configure and compile + - Fixed configure bug with enabling of POPCNT instruction, POPCNT + instruction should be enabled by default when SSE4.2 is enabled. + - Removed --enable-magic-breakpoint configure option. The option is + automatically enabled if Bochs internal debugger is compiled in. + It is still possible to turn on/off the feature through .bochsrc. + - Allow boot from network option in .bochsrc + - Added Bochs version info for Win32 + +- Display libraries + - implemented text mode character blinking in some guis + - improved 'X' gui runtime dialogs + +- SF patches applied + [1980833] Fix shutdown status code 5h handler by Kevin O'Connor + [1928848] "pipe" mode for serial port (win32 only) by Eugene Toder + [1956843] Set the compatible pci interrupt router back to PIIX by Sebastian + [1956366] Do not announce C2 & C3 cpu power state support by Igor Lvovsky + [1921733] support for LBA48 by Robert Millan + [1938185] Fix link problem with --enable-debugger by Sebastian + [1938182] Makefile.in - use @IODEV_LIB_VAR@ by Sebastian + [1928945] fix for legacy rombios - e820 map and ACPI_DATA_SIZE by Sebastian + [1925578] rombios32.c - fix ram_size in ram_probe for low memory setup by Sebastian + [1908921] rombios32.c - move uuid_probe() call by Sebastian + [1928902] improvements to load-symbols by Eugene Toder + [1925568] PATCH: msvc compilation by Eugene Toder + [1913150] rombios.c - e820 cover full size if memory <= 16 mb by Alexander van Heukelum + [1919804] rombios.c - fix and add #ifdef comments by Sebastian + [1909782] rombios.c - remove segment values from comment by Sebastian + [1908918] SMBIOS - BIOS characteristics fix by Sebastian + [1901027] BIOS boot menu support (take 3) + [1902579] rombios32.c - define pci ids by Sebastian + [1859447] Pass segment:offset to put_str and introduce %S by Sebastian + [1889057] rombios.c - boot failure message by Sebastian + [1891469] rombios.c - print BEV product string by Sebastian + [1889851] Win32 version information FILEVERSION for bochs.exe by Sebastian + [1889042] rombios.c - fix comment by Sebastian + [1881500] bochsrc, allow boot: network by Sebastian + [1880755] Win32 version information for bochs.exe by Sebastian + [1880471] SMBIOS fix type 0 by Sebastian + [1878558] SMBIOS fixes by Sebastian + [1864692] SMBIOS support by Filip Navara + [1865105] Move bios_table_area_end to 0xcc00 by Sebastian + [1875414] Makefile.in - change make use by Sebastian + [1874276] Added instrumentation for sysenter/sysexit by Lluis + [1873221] TLB page flush: add logical address to instrumentation by Lluis + [1830626] lba32 support by Samuel Thibault + [1861839] Move option rom scan after floppy and hard drive post by Sebastian + [1838283] Early vga bios init by Sebastian + [1838272] rom_scan range parameter by Sebastian + [1864680] Save CPUID signature by Filip Navara + +- these S.F. bugs were closed + [1976171] Keyboard missing break code for enter (0x9C) + [666433] physical read/write breakpoint sometimes fails + [1744820] info gdt and info idt shows the entire tables + [1755652] graphics: MenuetOS64 shows black screen + [1782207] Windows Installer malfunction, Host=Linux, Guest=Win98SE + [1697762] OS/2 Warp Install Failed + [1952548] String to char * warnings + [1940714] SYSENTER/SYSEXIT doesn't work in long mode + [1422342] SYSRET errors + [1923803] legacy rombios - e820 map and ACPI_DATA_SIZE + [1936132] Link problem with --enable-debugger & --enable-disasm + [1934477] Linear address wrap is not working + [1424984] virtual machine freezes in Bochs 2.2.6 + [1902928] with debugger cpu_loop leaves CPU with unstable state + [1898929] Bochs VESA BIOS violates specs (banks == 1) + [1569256] bug in datasegment change in long mode + [1830662] ACPI: no DMI BIOS year, acpi=force is required + [1868806] VGA blink enable & screen disable + [1875721] Bit "Accessed" in LDT/GDT descriptors & #PF + [1874124] bx_Instruction_c::ilen() const + [1873488] bochs-2.3.6 make fails on dbg_main.cc + +- these S.F. feature requests were implemented + [1422769] SYSENTER/SYSEXIT support in x86-64 mode + [1847955] Version information for bochs(dbg).exe + [939797] SMBIOS support + +------------------------------------------------------------------------- +Changes in 2.3.6 (December 24, 2007): + +Brief summary : + ++ More than 25% emulation speedup vs Bochs 2.3.5 release! + + - Thanks to Darek Mihocka (http://www.emulators.com) + for providing patches and ideas that made the + speedup possible! + ++ Up to 40% speedup vs Bochs 2.3.5 release with trace cache optimization! + +- Lots of bugfixes in CPU emulation +- Bochs benchmarking support +- Added emulation of Intel SSE4.2 instruction set + +Detailed change log : + +- CPU + - Added emulation of SSE4.2 instruction set, to enable use + --enable-sse=4 --enable-sse-extension configure options + to enable POPCNT instruction only use configure option + --enable-popcnt + - Implemented MTRR emulation, to enable use --enable-mtrr configure + option. MTRRs is enabled by default when cpu-level >= 6. + - Implemented experimental MONITOR/MWAIT support including optimized + MWAIT CPU state and hardware monitoring of physical address range, + to enable use --enable-monitor-mwait configure option. + - Removed hostasm optimizations, after Bochs rebenchmarking it was found + that the feature bringing no speedup or even sometimes slows down + emulation! + - Merged trace cache optimization patch, the trace cache optimization + is enabled by default when configure with --enable-all-optimizations + option, to disable trace cache optimization configure with + --disable-trace-cache + - Many minor bugfixes in CPU emulation (both ia32 and x86-64) + - Updated CPU instrumentation callbacks + +- Bochs Internal Debugger and Disassembler + - Many fixes in Bochs internal debugger and disassembler, some debugger + interfaces significantly changed due transition to the param tree + architecture + - Added support for restoring of the CPU state from external file + directly from Bochs debugger + +- Configure and compile + - Renamed configure option --enable-4meg-pages to --enable-large-pages. + The option enables page size extensions (PSE) which refers to 2M pages + as well. + - Removed --enable-save-restore configure option, save/restore feature + changed to be one of the basic Bochs features and compiled by default + for all configurations. + - Added new Bochs benchmark mode. To run Bochs in benchmark mode execute + it with new command line option 'bochs -benchmark time'. The emulation + will be automatically stopped after 'time' millions of emulation + cycles executed. + - Another very useful option for benchmarking of Bochs could be enabled + using new 'print_timestamps' directive from .bochsrc: + print_timestamps: enable=1 + - Added --enable-show-ips option to all configuration scripts used to + build release binaries, so all future releases will enjoy IPS display. + - Enable alignment check in the CPU and #AC exception by default for + --cpu-level >= 4 (like in real hardware) + +- SF patches applied + [1491207] Trace Cache Speedup patch by Stanislav + [1857149] Define some IPL values by Sebastian + [1850183] Get memory access mode in BX_INSTR_LIN_READ by Lluis Vilanova + [1841421] pic: keep slave_pic.INT and master_pic.IRQ_in bit 2 in sync by Russ Cox + [1841420] give segment numbers in exception logs by Russ Cox + [1801696] Allow Intel builds on Mac OS X + [1830658] Fix >32GB disk banner by Samuel Thibault + [1813314] Move #define IPL_* and typedef ipl_entry by Sebastian + [1809001] Save PnP Option ROM Product Name string in IPL Boot Table by Sebastian + [1821242] Fix for #1801285, Niclist.exe broken by Sebastian + [1819567] Code warning cleanup + [1816162] Update comment on bios_printf() by Sebastian + [1811139] Trivial Fix when BX_PCIBIOS and BX_ROMBIOS32 not defined by Myles Watson + [1811190] Improve HD recognition and CD boot by Myles Watson + [1811860] Implement %X in bios_printf by Sebastian + [1809649] printf %lx %ld %lu by Myles Watson + [1809651] move BX_SUPPORT_FLOPPY by Myles Watson + [1809652] dpte and Int13DPT fixes by Myles Watson + [1809669] clip cylinders to 16383 in hard drive by Myles Watson + [1799903] Build BIOS on amd64 by Robert Millan + [1799877] Fix for parallel build (make -j2) by Robert Millan + +- these S.F. bugs were closed + [1837354] website bug: View the Source link broken + [1801268] Reset from real mode no longer working + [1843250] Using forward slashes gives invalid filename + [1823446] BIOS bug, local APIC #0 not detected + [1801285] Niclist.exe broken + [1364472] breakpoints sometimes don't work + [994451] breakpoint bug + [1801295] NSIS installer vs Windows Notepad + [1715328] Unreal mode quirk + [1503972] debugger doesn't debug first instruction on exception + [1069071] div al, byte ptr [ds:0x7c18] fails to execute + [1800080] Wrong "BX_MAX_SMP_THREADS_SUPPORTED" assertion + +- these S.F. feature requests were implemented + [1662687] Download for Win32-exe with x64 Mode and debugging + [604221] Debugger command: query lin->phys mapping + +------------------------------------------------------------------------- +Changes in 2.3.5 (September 16, 2007): + +Brief summary : +- Critical problems fixed for x86-64 support in CPU and Bochs internal debugger +- ACPI support +- The release compiled with x86-64 and ACPI +- Hard disk emulation supports ATA-6 (LBA48 addressing, UDMA modes) +- Added emulation of Intel SSE4.1 instruction set + +Detailed change log : + +- CPU + - Fixed critical bug with 0x90 opcode (NOP) handling in x86-64 mode + - implied stack references where the stack address is not in canonical form + should causes a stack exception (#SS) + - Added emulation of SSE4.1 instruction set (Stanislav) + - Do not save and restore XMM8-XMM15 registers when not in x86-64 mode + - Fixed zero upper 32-bit part of GPR in x86-64 mode + - CMOV_GdEd should zero upper 32-bit part of GPR register even if the + 'cmov' condition was false ! + - Implemented CLFLUSH instruction, report non-zero cache size in CPUID + - Fixed PUSHA/POPA instructions behavior in real mode + - Fixed detection of inexact result by FPU + - Fixed denormals-are-zero (DAZ) handling by SSE convert instructions + - Implemented Misaligned Exception Mask support for SSE (MXCSR[17]) + - Implemented Alignment Check in the CPU and #AC exception, to enable + use --enable-alignment-check configure option + +- General + - 2nd simulation support in wxBochs now almost usable (simulation cleanup + code added and memory leaks fixed) + +- Configure and compile + - several fixes for MacOSX, OpenBSD and Solaris 10 + - enable save/restore feature by default for all configurations + - reorganized SSE configure options to match Intel(R) Programming + Reference Manual, new option introduced for SSE extensions enabling. + To enable Intel Core Duo 2 new instructions use + --enable-sse=3 --enable-sse-extension + enabling of SSE4.1 (--enable-sse=4) will enable SSE3 extensions as well + - removed old PIT, always use new PIT written by Greg Alexander, + removed configure option --enable-new-pit + +- I/O devices (Volker) + - Floppy + - partial non-DMA mode support (patch by John Comeau) + - Hard drive / cdrom + - hard disk emulation now supports ATA-6 (LBA48 addressing, UDMA modes) + - VMWare version 4 disk image support added (patch by Sharvil Nanavati) + - PCI + - initial support for the PIIX4 ACPI controller + - Serial + - added support for 3-button mouse with Mousesystems protocol + - USB + - experimental USB device change support added + - rewrite of the existing USB devices code + - new USB devices 'disk' and 'tablet' (ported from the Qemu project) + +- Bochs internal debugger + - fixed broken debugger "rc file" option (execute debugger command from file) + - implementation of a gui frontend ("windebug") for win32 started + - gdbstub now accepts connection from any host + - several documentation updates + - a lot of disasm and internal debugger x86_64 support fixes + +- Configuration interface + - fixes and improvements to the save state dialog handling + +- Display libraries + - text mode color handling improved in some guis + - win32 fullscreen mode (patch by John Comeau) + +- System BIOS (Volker) + - 32-bit PM BIOS init code for ACPI, PCI, SMP and SMM (initial patches by + Fabrice Bellard) + - PCI BIOS function "find class code" implemented + +- SF patches applied + [1791000] 15h 8600h is reading the wrong stack frame by Sebastian + [1791016] rombios32.c, ram_probe(), BX_INFO missing value by Sebastian + [1786429] typo in bochsrc.5 by Sebastian + [1785204] Extend acpi_build_table_header to accept a revision number by Sebastian + [1766536] Partial Patch for Bug Report 1549873 by Ben Lunt + [1763578] ACPI Table Revision 0 -> 1 + [1642490] implement alignment check and #AC exception by Stanislav Shwartsman + [1695652] [PATCH] .pcap pktlog and vnet PXE boot by Duane Voth + [1741153] Add expansion-ROM boot support to the ROMBIOS + [1734159] Implemented INT15h, fn 0xC2 (mouse), subfn 3, set resolution + [1712970] bios_printf %s fix + [1573297] PUSHA/POPA real mode fix by Stanislav Shwartsman + [1641816] partial support for non-DMA access to floppy by John Comeau + [1624032] shows where write outside of memory occurred by John Comeau + [1607793] allow fullscreen when app requests it by John Comeau + [1603013] Bugfix for major NOP problem on x64 by mvysin + [1600178] Make tap and tuntap compile on OpenBSD by Jonathan Gray + [1149659] improve gdbstub network efficiency by Avi Kivity + [1554502] Trivial FPU exception handling fix + +- these S.F. bugs were closed + [1316008] Double faults when it shouldn't - gcc 4.0.2 + [1787289] broken ABI for redolog class when enable-compressed-hd + [1787500] tftp_send_optack not 64bit clean + [1264540] Security issue with Bochs website + [1767217] Debugger Faults including ud2 + [1729822] Various security issues in io device emulation + [1675202] mptable hosed (bad entry count in header) + [1197141] 'make install' installs to bad location + [1157623] x86Solaris10 cannot recoginize ACPI RSD PTR + [1768254] large HDD in Bochs/bximage + [1496157] Windows Vista Beta2 dosn't boot + [1755915] Illegal Hard Disk Signature Output + [1717790] info gdt and info idt scrolls away, too long result + [1726640] Debugger displays incorrect segment for mov instruction + [1719156] Typo in misc_mem.cpp + [1715270] Debugger broken in/beyond 2.3 + [1689107] v8086 mode priviledge check failed + [1704484] A few checks when CPU_LEVEL < 4 + [1678395] Problem with zero sector... + [876990] SA-RTL OS fails on PIC configuration + [1673582] save/restore didn't restore simulation correctly + [1586662] EDD int 13h bug, modify eax + [666618] POP_A Panic in DOS EMU + [1001485] panic: not enough bytes on stack + [1667336] delay times an order of magnitude slow + [1665601] crash disassembling bootcode + [1657065] CVS sources won't compile + [1653805] bochs's gdbstub uses incorrect protocol + [1640737] ASM sti command frezzes guest OS + [1636439] latest CVS sources don't compile under Cygwin + [1634357] disasm incorrect (no sign ext) displacement in 64-bit mode + [1376453] pcidev segfaults bochs + [1180890] IOAPIC in BOCHS - WinXP 64 in MP version + [1597528] 2.3 fails to compile on amd64 + [1526255] FLD1 broken when compaling with gcc 4.0.x + [1597451] eth_fbsd is broken under FreeBSD + [1571949] Bochs will not compile under Solaris + [1500216] Bochs fails to boot BeOs CD + [1458339] bochs-2.2.6 WinXP Binary ACPI error installing FreeBSD 6.0 + [1440011] patches needed for FreeBSD 6.0 to compile Bochs + [431674] some devices don't have a prefix + [458150] QNX demo disk crashes with new pit + [818322] Bochs 2.1 cvs: OS/2 - read verify on non disk + [906840] KBD: bogus scan codes generated in set 3 + [1005053] No keyboard codes translation + [1109374] Problem with Scancodeset 2 + [1572345] Bochs won't continue + [1568153] Bochs looks for (and loads?) unspecified display libraries + [1563462] Errors in /iodev/harddrv.h + [1562172] TLB_init() fails to initialize priv_check array if USE_TLB 0 + [1385303] debugger crashes after panic + [1438227] crc.cpp missing in bx_debug version 2.2.6 + [1501825] debugger crashes on to high input + [1420959] Memory leak + buffer overflow in Bochs debugger + [1553289] Error in Dis-assembler + [542464] I cannot use FLAT + [1548270] Bochs won't die with its pseudo terminal + [1545588] roundAndPackFloatx80 does not detect round up correctly + +------------------------------------------------------------------------- +Changes in 2.3 (August 27, 2006): + +Brief summary : +- limited save/restore support added (config + log options, hardware state) +- configuration parameter handling rewritten to a parameter tree +- lots of cpu and internal debugger fixes +- hard disk geometry autodetection now supported by most of the image types +- hard disk emulation now supports ATA-3 (multiple sector transfers) +- VBE memory size increased to 8MB and several VGA/VBE fixes +- updated LGPL'd VGABIOS to version 0.6a + +Detailed change log : + +- CPU and internal debugger fixes + - Fixed bug in FSTENV instruction (Stanislav Shwartsman) + - Recognize #XF exception (19) when SSE is enabled + - Fixed bug in PSRAW/PSRAD MMX and SSE instructions + - Save and restore RIP/RSP only for FAULT-type exceptions, not for traps + - Correctly decode, disassemble and execute multi-byte NOP '0F F1' opcode + - Raise A20 line after system reset (Stanislav Shwartsman) + - Implemented SMI and NMI delivery (APIC) and handling in CPU (Stanislav) + - Experimental implementation of System Management Mode (Stanislav) + - Added emulation of SSE3E instructions (Stanislav Shwarstman) + - Save and restore FPU opcode, FIP and FDP in FXSAVE/FRSTOR instructions + - Fixed bug in MOVD_EdVd opcode (always generated #UD exception) + - Fixed critical issue, Bochs was not supporting > 16 bit LDT.LIMIT values + - Many fixes in Bochs internal debugger and disassembler + +- CPU x86-64 fixes + - Fixed SYSRET instruction implementation + - Fixed bug in CALL/JMP far through 64-bit callgate in x86-64 mode + - Correctly decode, disassemble and execute 'XCHG R8, rAX' instruction + - Correctly decode and execute 'BSWAP R8-R15' instructions + - Fixed ENTER and LEAVE instructions in x86-64 mode (Stanislav) + - Fixed CR4 exception condition (No Name) + - Fixed x86 debugger to support x86-64 mode (Stanislav) + +- APIC and SMP + - Support for Dual Core and Intel(R) HyperThreading Technology. Now you + could choose amount of cores per processor and amount of HT threads per + core from .bochsrc for SMP simulation (Stanislav Shwartsman) + - Allow to control SMP quantum value through .bochsrc CPU + option parameter. Previous Bochs versions used hardcoded quantum=5 + value. + - Fixed interrupt priority bug in service_local_apic() + - Fixed again reading of APIC IRR/ISR/TMR registers. Finally it becomes + fully correct :-) + +- Configure and compile + - Moved configure time --enable-reset-on-triple-fault option to runtime, + the 'cpu' option in .bochsrc is extended and the old configure option + is deprecated (Stanislav Shwartsman) + - Removed --enable-pni configure option, to compile with PNI use + --enable-sse=3 instead (Stanislav Shwartsman) + - enable SEP (SYSENTER/SYSEXIT) support by default for Penitum II+ + processor emulation (i.e. if cpu-level >= 6 and MMX is enabled) + +- general + - Limited save/restore support added. The state of CPU, memory and all + devices can be saved now (state of harddisk images not handled yet). + - Fixed several memory leaks + +- configuration interface + - Configuration parameter handling rewritten to a parameter tree. This is + required for dynamic menus/dialogs, user-defined options and save/restore. + - Support for user-defined bochsrc options added + - help support at the parameter prompt in textconfig added + +- I/O devices (Volker) + - Floppy + - partial sector transfers fixed + - Hard drive / cdrom + - several fixes to the IDE register behaviour (e.g. in case of a channel + with only one drive connected) + - fixed data alignment of 'growing' hard drive images (sharing images + between Windows and Linux now possible) + - disk geometry autodetection now supported by most of the image types + (unsupported: external, dll and compressed modes) + - multi sector read/write commands implemented + - hard disk now reporting ATA-3 supported + - ATAPI 'inquiry' now returns a unique device name + - Keyboard + - reset sent to keyboard has no effect on the 8042 (scancode translation) + - PCI + - forward PIRQ register changes to the I/O APIC (if present) + - attempt to fix and update the emulation part of 'pcidev' (untested) + - VGA + - VBE memory size increased to 8MB and several VBE fixes + - VGA memory read access fixed (bit plane access and read mode) + - VGA memory is now a part of the common video memory + +- System BIOS (Volker) + - enable interrupts before executing INT 19h + - fixed ATA device detection in case of one drive only connected to controller + - improved INT 15h function AX=E820h + - real mode PCI BIOS now returns IRQ routing information (function 0Eh) + - keyboard LED flags handling fixed and improved + - fixed handling of extended keys in INT 09h + - Updated LGPL'd VGABIOS to version 0.6a + +- SF patches applied + [1340111] fixes and updates to usb support by Ben Lunt + [1539420] minor addition to pci_usb code by Ben Lunt + [1455958] call/jmp through call gate in 64-bit mode + [1433107] PATCH: fix compile with wxwindows 2.6 (unicode / utf8) by jwrdegoede + [1386671] Combined dual core and hyper-threading patch + +- these S.F. bugs were closed + [833927] TTD: System Error TNT.40025: Unexpected processor exception + [789230] Sending code that shows lock up when setting idt + [909670] Problems with Symantec Ghost + [1540241] include missing in osdep.cc + [1539373] Incorrect disasm for "mov moffset,bla" in 64bit + [1538419] incorrect disassembly of [rip+disp] with rex.b + [1535432] shift+cursor key maps to a digit + [1504891] Knoopix 5.0.1 error + [1424355] bochs-2.2.6 ata failure in windoze 98se + [1533979] wrong disassembly of IN instruction + [620059] paste won't stop + [1164904] status bar doesn't show num/caps/scroll lock status + [1061720] ATA Support level for HD + [1522196] Broken CHANGES link in main page + [1438415] crash if screen scrolled downwards + [778441] Shouldn't interrupts be enable after BIOS? + [1514949] I got a problem with the 8253 timer + [1513544] disasm of 0xec (in AL,DX) returns ilen of 2 instead of 1 + [1508947] APIC interrupt priority checking and interrupt delivery + [766286] Debugger halts after any GPF exception + [639143] va_list is not a pointer on linuxppc + [1501815] debugger examines memory over page-boundary wrong + [1503978] movsb/w/d doesn't work when direction is stored + [1499405] WinPCap has changed URL hosting + [1498519] APIC IRR bits not set while interrupts disabled + [1498193] Bochs segfaults on LTR instruction + [787140] Guest2HostTLB optimization bug + [1492070] instrument stop + [1487772] No SEP on P4 + [1488335] Growing hard disk images severe interoperability errors! + [1076312] Shadow RAM and TLB + [1282249] The real i440FX chipset Award bios hangs + [1479763] mistake "mov ax,[es:di]" for "mov ax,[ds:di]" + [1453575] Misconfigured floppy DMA transfers do not terminate. + [1460068] Incorrect handling for the Options Menu Item + [910203] bochs-2.1.1 wx.lo failed + [1438654] PANIC when trying to run install-amd64-minimal-2005.0.iso + [1458320] compile hdimage.h fails + [1455880] bochs-2.2.6,2: make error on FreeBSD + [696890] Network wouldn't run under W2k hosting MSDOS + [673391] SMP timer problems + [1291059] wxWindows GUI on non-windows/configure issue + [1356450] bochs 2.2.1 errors-omittions + [1178017] Win98 guest cannot receive network packets from host + [1076315] a20_mask after restarting + [1436323] real hw does not panic when bad Ib in CMPSS_VssWssIb + [1435269] cdrom_amigaos is not compilable + [1433314] disasm issues + [1170614] relative jumps/calls wrong in debugger + [758121] user might get confused when interrupt handler invoked + [1170622] You cannot toggle OFF "show" flags + [1406387] JMP instruction should display absolute address + [1428813] PANIC: ROM address space out of range + [1426288] DR-DOSs EMM386 problem + [1412036] Bochs cannot recognize PCI NIC correctly + [435115] dbg: modebp broken and no docs + [1419366] disasm cs:eip does not work anymore + [1419393] SSE's #XF exception -> "exception(19): bad vector" + [1419429] disassembly of "260f6f00" show DS: instead of ES: prefix + [1417583] Interrupt behaviour changed from 2.2.1 to 2.2.5 + [1418281] 'push' (6A) incorrectly disassembled + [1417791] FLDENV generating exception when real hw does not. + [1264583] OS/2 1.1 doesn't run + +------------------------------------------------------------------------- +Changes in 2.2.6 (January 29, 2006): + +- First major SMP release ! + - several APIC and I/O APIC fixes make SMP Bochs booting Windows NT4.0 + or Knoppix 4.0.2 without noapic kernel option in SMP configuration. + - critical APIC timer bug fixed + - obsolete SMP BIOS images removed (MP tables created dynamicaly) + - determine number of processors in SMP configuration through .bochsrc + new .bochsrc option 'CPU' allows to choose number of processors to emulate + - new configure option --enable-smp to configure Bochs for SMP support, + the old --enable-processors=N option is deprecated +- CPU and internal debugger fixes + - enabled #PCE bit in CR4 register, previosly setting of this bit + generated #GP(0) fault + - enabled LAHF/SAHF instructions in x86-64 mode + - fixed bug in PMULUDQ SSE2 instruction + - fixes in Bochs debugger +- Configure and compile + - enable VME (virtual 8086 mode extensions) by default if cpu-level >= 5 + - enable Bochs disassembler by default for all configurations + - win32 installer script improvements + - ips parameter moved to new 'CPU' option + - show IPS value in status bar if BX_SHOW_IPS is enabled +- Other + - several fixes in the hard drive, keyboard, timer, usb and vga code + - new user button shortcut "bksl" (backslash) + - updated Bochs instrumentation examples + - user and development documentation improved + +------------------------------------------------------------------------- +Changes in 2.2.5 (December 30, 2005): + +Brief summary : +- added virtual 8086 mode extensions (VME) implementation +- several fixes/improvements in x86-64 emulation, debugger and disassembler +- new serial mode 'socket' connects a network socket +- IDE busmaster DMA feature for harddisks and cdroms completed and enabled +- many improvements in Bochs emulated I/O devices (e.g. floppy, cdrom) +- Updated LGPL'd VGABIOS to version 0.5d + +Detailed change log : + +- CPU + - fixed XMM registers restore in FXRSTOR instruction (Andrej Palkovsky) + - print registers dump to the log if tripple fault occured + - fixed PANIC in LTR instruction (Stanislav) + - added virtual 8086 mode extensions (VME) implementation, to enable + configure with --enable-vme (Stanislav) + - flush caches and TLBs when executing WBINVD and INVD instructions + - do not modify segment limit and AR bytes when modifying segment + register in real mode (support for unreal mode) + - fixed init/reset values for LDTR and TR registers + - reimplemented hardware task switching mechanism (Stanislav) + - generate #GP(0) when fetching instruction cross segment boundary + +- CPU (x86-64) (Stanislav Shwartsman) + - implemented call_far/ret_far/jmp_far instructions in long mode + - fixed IRET operation in long mode + - fixed bug prevented setting of NXE/FFXSR bits in MSR.EFER register + - implemented RDTSCP instruction + - do not check CS.limit when prefetching instructions in long mode + - fixed masked write instructions (MASKMOVQ/MASKMOVDQU) in long mode + - fetchdecode fixes for x86-64 + +- APIC + - Fixed bug in changing local APIC id (Stanislav) + - Fixed reading of IRR/ISR/TMR registers (patch by wmrieker) + - Implemented spurious interrupt register (Stanislav, patch by wmrieker) + - Fixed interrupt delivery bug (anonymous #SF patch) + - Correctly implemented ESR APIC register (Stanislav) + +- Bochs debugger + - Fixed bug in bochs debugger caused breakpoints doesn't fire sometimes + (Alexander Krisak) + - watchpoints in device memory fixed (Nickolai Zeldovich) + - new debug interface to access Bochs CPU general purpose registers + with support for x86-64 + +- Disassembler (Stanislav Shwartsman) + - Fixed disassembly for FCOMI/FUCOMI instructions + - Full x86-64 support in disassembler. The disassembler module extended + to support x86-64 extensions. Still limited by Bochs debugger which + is not supporting x86-64 at all ;( + +- I/O devices (Volker) + - general + - memory management prepared for large BIOS images (up to 512k) + - slowdown timer sleep rate fixed (now using 1 msec on all platforms) + - some device specific parameter handlers moved into the device code + - serial + - new serial mode 'socket' connects a network socket (#SF patch by Andrew Backer) + - hard drive / cdrom + - assign a unique serial number to each drive (fixes harddrive detection + problems with Linux kernels 2.6.x: "ignoring undecoded slave") + - geometry autodetection for 'flat' hard disk images added. Works with + images created with bximage (heads = 16, sectors per track = 63) + - ATAPI command 'read cd' implemented, some other commands improved + - cdrom read block function now tries up to 3 times before giving up + - emulation of raw cdrom reads added, some other lowlevel cdrom fixes + - IDE busmaster DMA feature for harddisks and cdroms completed and enabled + - disk image size limit changed from 32 to 127 GB + - split ATA/ATAPI emulation code and image handling code + - floppy + - fixes for OS/2 (patch by Robin Kay) + - disk change line behaviour fixed (initial patch by Ben Lunt) + - end-of-track (EOT) condition handling implemented + - more accurate timing for read/write data and format track commands using + a motor speed of 300 RPM + - timing of recalibrate and seek commands now depends on the step rate, + date rate and the steps to do + - floppy controller type changed to 82077AA + - cmos + - RTC 12-hour and binary mode implemented + - number of CMOS registers changed from 64 to 128 + - bochsrc option 'cmosimage' improved + - save cmos image on exit if enabled + - speaker + - simple speaker support for OS X added (patch by brianonn@telus.net) + - pci + - BeOS boot failure fix in the PCI IDE code + - don't register i/o and memory regions during PCI probe + - vga + - memory allocation for vga extensions fixed + - usb + - some bugfixes by Ben Lunt (mouse and keypad are usable now) + - networking modules + - VDE networking module now enabled on Linux + +- display libraries + - general + - new syntax for the userbutton shortcut string and more keys supported + - win32 + - fixed keycode generation for right alt/ctrl/shift keys + - runtime dialog is now a property sheet + - x11 + - simple dialog boxes for the "ask" and "user shortcut" feature implemented + - Slovenian keymap added (contributed by Mitja Ursic) + +- configuration interface + - ask dialog is now enabled by default for win32, wx and x display libraries + - bochsrc option floppy_command_delay is obsolete now (floppy timing now based + on hardware specs) + - floppy image size detection now available in the whole config interface + - some device specific parameter handlers moved into the device code + - calculate BIOS ROM start address from image if not specified + +- System BIOS (Volker) + - PCI i/o and memory base address initialization added + - several keyboard interrupt handler fixes (e.g. patch by japheth) + - several floppy fixes (e.g. OS/2 works with patch by Robin Kay) + - some more APM functions added + - Updated LGPL'd VGABIOS to version 0.5d + - generate SMP specific tables dynamicly by the Bochs memory init code + +- SF patches applied + [1389776] Disk sizes over 64 Gbytes by Andrzej Zaborowski + [1359162] disasm support for x86-64 by Stanislav Shwartsman + [857235] task priority and other APIC bugs, etc by wmrieker + [1359011] build breaks for 386 + debugger + disasm by shirokuma + [1352761] Infinite loop when trying to debug a triple exception + [1311170] small APIC bug fix (interrupt sent to the wrong CPU) + [1309763] Watchpoints don't work in device memory by Nickolai Zeldovich + [1294930] change line status on floppy by Ben Lunt + [1282033] SSE FXRESTORE not working correctly by Ondrej Palkovsky + [816979] wget generalizations by Lyndon Nerenberg + [1214886] No more pageWriteStamp / unified icache by H. Johansson + [1107945] com->socket redirection support by Andrew Backer + +- these S.F. bugs were closed + [669180] win95 install : unknown SET FEATURES subcommand 0x03 + [1346692] bochs 2.2.1 VGA BIOS error + [1354963] floppy in KolibriOS + [1378204] error: bochs-2.2.1, --enable-sb16, --disable-gameport + [1368412] VDE problems in BOCHS + [533446] CPU and APIC devices appear twice + [1000796] bximage fails to create image of specified size + [1170793] Quarterdeck QEMM doesn't work + [923704] Multiple opcode prefixes don't reflect Trap 13 + [1166392] DocBook/documentation issues + [1368239] broken grater than 4GB size of sparse type hd image + [1365830] i386 compile breaks on paging + [427550] Incomplete IRETD implementation + [1215081] MSVC workspace STILL not fixed + [736279] Jump to Task + [1356488] FD change fail & occur error + [957615] [CPU ] prefetch: RIP > CS.limit + [1353866] not booting linux-2.6.14 + [1351667] load32bitOSImage does not work with --enable-x86-debugger + [1217476] Incorrect (?) handling of segment registers in real mode + [1184711] OS2 DOS crash [2.2.pre2] + [624330] support for disks > 32GiB + [1348368] bochs 2.2.1 bximage error + [1342081] Configuration Menu option failed + [1138616] OS/2 Warp 4 hangs when booting + [1049840] mouse and video conflict + [1164570] Unable to perform Fedora Core 4 test 1 installation + [1183201] Windows 2000 (MSDN build 2150?) does not completely install + [1194284] Can't boot from CD-ROM (Windows NT) + [962969] Windows NT crashes while trying to intall them. + [1054594] WinXP install halts (redo) + [1153107] Windows XP fails with BSOD on 'vga' + [938518] Win XP installation fails + [645420] getHostMemAddr vetoed direct read + [1179985] MS XENIX: >>PANIC<< VGABIOS panic at vgabios.c, line 0 + [1329600] WBINVD and INVD should flush caches and TLB + [638924] eliminate BX_USE_CONFIG_INTERFACE + [1048711] Funny behaviour with CTRL + [1288450] keyboard BIOS error + [1310706] Keyboard - about key SHIFT + [1295981] Ubuntu 5.04 Live-CD won't boot in Bochs + [879047] APIC timer behavior different before reset and after + [1188506] I still can't install the german Windows XP! + [1301847] Windows XP dosn't boot - FXRSTOR problem ? + [661259] does not boot QNX under WinX + [924412] Keyboard lock states all whacked + [681127] MIPSpro compiler (IRIX) is allergic to ^M + [1285923] BIOS keyboard handler + [516639] ATA controller revisited... + [657918] does not boot BeOS under WinX + [649245] BeOS CD locks halfway on boot + [1094385] Attachment for bug 1090339 (beos failure) + [1183196] BeOS 4.5 developer CD does not install + [1090339] BeOS fails to boot + [639484] panics when int 13 is called + [711701] divide by zero + [704295] ATAPI/BIOS call missing + [682856] hard drive problems + [627691] Cursor keys problem + [588011] keyboard not working + [542260] os/2 warp crashes with floppy handling + [1273878] SB16 doesn't work in pure DOS + [542254] OS/2 FDC driver dies + [1099610] Windows 98 SE Does not install + [875479] cr3 problem on task switch + [731423] NE2000 causing PANIC on Win2K detection + [1156155] bochs fails to boot plan9 iso + [1251979] --enable-cpu-level=3 should assume --without-fpu + [1257538] Interupt 15h 83h - set wait event interval + [658396] Panic for DR DOS emm386 + [679339] /? doesn't divulge Bochs command-line syntax + [1167016] call/jump/return_protected doesn't support x86-64 + [1252432] Mac OS X compile bug + [881442] Bochs 2.1 PANIC when loading DOS Turbo Pascal protected mode + [1249324] Boch2.2.1 Buffer Overfollow in void bx_local_apic_c::init () + [1197144] 'make install' has dependency on wget + [1079595] LTR:386TSS: loading tr.limit < 103 + [1244070] Compilation Error in gui/rfb.cc + [761707] CPU error when trying to start Privateer + [517281] Crash running Privateer in DOS... + +------------------------------------------------------------------------- +Changes in 2.2.1 (July 8, 2005): + +- Fixed several compilation warnings and errors for different platforms (Volker) +- Fixed FPU tag word restore in FXRSTOR instruction (Stanislav) +- Added missing scancodes for F11 and F12 to BIOS translation table (Volker) +- Bochs disassembler bugfixes (h.johansson) +- About 5% emulation speed improvement (h.johansson) +- Handle writing of zero to APIC timer initial count register (Stanislav) +- Enable Idle-Hack for 'TERM' GUI (h.johansson) +- Reduced overhead of BX_SHOW_IPS option to minimum. Now every simulation + could run with --enable-show-ips without significant performance + penalty. (Stanislav) +- Fixed pcipnic register access (Volker) +- Limited write support for TFTP server in 'vnet' networking module added (Volker) +- Changed some timing defaults to more useful values (Volker) +- WinXP/2003 style common controls now supported (Vitaly Vorobyov) +- Updated LGPL'd VGABIOS to version 0.5c (Volker) +- Added new BX_INSTR_HLT callback to instrumentation (Stanislav) + +------------------------------------------------------------------------- +Changes in 2.2 (May 28, 2005): + +Brief summary : +- New floating point emulator based on SoftFloat floating point + emulation library. +- improved x86-64 emulation +- Cirrus SVGA card emulation added +- status bar with indicators for keyboard, floppy, cdrom and disk (gui dependant) +- many improvements in Bochs emulated I/O devices (e.g. PCI subsystem) + +Detailed change log : + +- CPU + - fixes for booting OS/2 by Dmitri Froloff + - fixed v8086 priveleged instruction processing bug (was also reported + by LightCone Aug 7 2003) + - exception process bug (was reported by Diego Henriquez Sat Nov 15 + 01:16:51 CET 2003) + - segment validation with IRET instruction + - CS segment not present exception processing with IRET + - several fixes by Kevin Lawton + - add MSVC host asm instructions (patch by suzu) + - fixed bug in HADDPD/HSUBPD (SSE3) instructions + - fixed bug in float to integer SSE/SSE2 convert instructions + - fixed BCD instructions implementation + - execution speed improvements (sshwarts and psychosmur) + - fix MSR_APICBASE base address (Kangmo Kim, Christian Neubert) + - change BX_PANIC messages to BX_INFO when behaviour exactly + matches Intel docs + - EIP > CS.limit case should always cause #GP(0), even in real mode. + Fixed all jump, call and ret instructions for 16/32 modes + - fixed using invalid segment register for MOV instruction (h.johansson) + - fixed ET bit mismatch between CR0 and SMSW instruction + - fixed possible simulator #DIVZERO fault when executing IDIV instruction + - fixed undocumented flags handling for BTS, BTR, SHR, SHLD, MUL and IMUL + instructions (Stanislav Shwartsman) + - added missed #GP(0) exception when loading incorrect flags combination + to CR0 (Stanislav Shwartsman) + - in case of --enable-ignore-bad-msr enabled read ignored MSRs as zero + - enabled #DE, #TSD and #MCE bits in CR4 register, previosly setting + of one of these bits generated #GP(0) (Stanislav, Volker Ruppert) + - exceeding the instruction length limit of 15 bytes (this only can + occur when redundant prefixes are placed before an instruction) + generate #GP(0) (Stanislav Shwartsman) + - corrected PAE functionality + +- CPU (x86-64) + - fetchdecode fixes for x86-64 and 3DNow! (Stanislav) + - fixed CF flag handling for SHL instruction in x86-64 mode (Stanislav) + - implemented CR8 register (aliased to APIC.TPR[7:4]) (Stanislav) + - implemented NXE bit (No-Execute page protection) support (Stanislav) + - STOSQ instruction emulation fixed (Avi Kivity) + - allow null SS selector for MOV SS, POP SS, and LSS instructions + in long mode (Avi Kivity) + - ignore segment bases for all segments (except FS and GS) in long + mode (Avi Kivity) + - allow SYSENTER/SYSEXIT instructions together with x86-64 (Stanislav) + - canonical address checking for RIP (Stanislav) + +- FPU (Stanislav Shwartsman) + - totally rewritten all FPU code based on softfloat library + - significantly improved accuracy of all floating point + instructions. + - implemented all missed P6 and PNI floating point instructions. + - hundreds of bug fixes in FPU code. + + TODO: + ! Unmasked underflow/overflow should correct the result + by magic number for all operations, including float32 + and float64. + +- APIC (Zwane Mwaikambo) + - APIC arbitration + - Processor priority + - Various interrupt delivery fixes + - Focus processor checking + - ExtINT delivery + +- Disassembler + - fixed MOV opcode 0x88, had exchanged the operands (h.johansson) + - fixed MOV opcode 0xA3, had wrong operand size (h.johansson) + - fixed BOUND opcode 0x62 (Stanislav) + - fixed CALLW opcode 0xFF /3 and JMPW opcode 0xFF /5 (Stanislav) + - fixed INS opcode 0x6D, had wrong operand size (Stanislav) + - fixed disassembly for repeatable instructions (Stanislav) + - fixed sign-extended immediate opcodes (Stanislav) + - fixed MOVSS/MOVSD instructions opcode names (Stanislav) + - fixed NEG instruction opcode name (Stanislav) + - fixed CMPXCHG8B instruction, had wrong operand size (Stanislav) + - fixed floating point instructions operands (Stanislav) + - experimental support of AT&T syntax in disassembler (Stanislav) + +- I/O devices + - general + - handle cpu reset through port 0x92 + - new memory handler API for PCI i/o and memory handling (Frank Cornelis) + - speaker emulation for Linux (David N. Welton) and Win32 (Volker Ruppert) added + - pci + - PCI slot configuration added for 5 slots (Volker) + - PCI irq routing, irq sharing and level sensitive irq mode implemented + - ne2k device appears as a Realtec 8029 NIC if connected to a PCI slot + - PCI IDE controller dummy device added + - PCI host device mapping for Linux (Frank Cornelis) + - PCI Pseudo-NIC emulation (Michael Brown) + - serial + - multiple serial port support added (4 ports now available) + - partial raw serial support on win32 (transmit data) + - serial port i/o mode option added (modes: null, file, term, raw, mouse) + - parallel + - multiple parallel port support added (2 ports now available) + - mouse + - serial mouse support (Volker) + - PS/2 and serial wheel mouse support (Ben Lunt) + - usb + - USB mouse and keypad support (Ben Lunt) + - config option to specify devices connected to USB ports + - vga + - VBE 8 bit DAC support added + - VBE memory now registered using DEV_register_memory_handlers() + - CL-GD 54xx SVGA emulation added (Makoto Suzuki) + - vga extension option added (choices: vbe, cirrus, none) (Volker) + - floppy + - raw floppy access now works on Win9x host (Ben Lunt) + - sb16 + - MacOSX sound support (Brian Huffman) + - networking modules + - new: 'eth_vnet' simulates ARP, DHCP, ICMP-echo and read-only TFTP + (m_suzu, easeway) + - new: 'eth_vde' for Virtual Distributed Ethernet (Renzo Davoli) + +- System BIOS + - turn floppy motor off 2 seconds after last read/write/recalibrate command + (Ben Lunt) + - int13_cdrom / 32 bit register update fixes for FreeBSD cdrom boot + (Fabrice Bellard) + - APM and system shutdown support (Fabrice Bellard) + - checksum calculation for expansion ROMs + - extended floppy parameter table (Mike Nordell, Derek Favcus) + - PCI IRQ initialisation added + - boot sequence with up to 3 boot devices added + +- display libraries + - status bar with indicators for cdrom, floppy, harddisk and keyboard added + (done in rfb, sdl, win32, wx and x) + - 3rd (middle) mouse button now supported (rfb, sdl, win32, wx, x) + - mouse wheel support (sdl, win32, x) + - CTRL key + middle mouse button now used to toggle the mouse capture mode + (sdl, win32, wx, x) + - text mode split screen feature added (sdl, win32, wx, x) + - new gui function returns the display library capabilities (xres, yres, bpp) + for the Bochs VBE support + - display library specific options added - currently supported: + rfb: timeout (time to wait for client connection) + sdl: fullscreen (startup in fullscreen mode) + win32: legacyF12 (use F12 to toggle mouse) + - new graphics update API added (used by svga_cirrus) (Robin Kay) + +- configuration interface + - win32: gui runtime dialogs replace textconfig runtime dialogs + - set default IPS to 10000000 in .bochsrc sample + - SB16 options dmatimer and loglevel now available at runtime + +- configure script / compile + - --enable-ignore-bad-msr (ignore bad MSR references) option is enabled + by default + - --enable-mmx enabled by default only if cpu-level >= 5 + - --enable-4meg-pages will be enabled by default if cpu-level >= 5 + - fixes for Solaris SunPro (Robin Kay) + - --enable-pni option added + - --enable-show-ips option added, enable Instruction Per Second counter + in log file + - autodetection for lowlevel sound support added + +- documentation + - a bunch of updates in user and documentation docs (Alexander Schuch) + +- SF patches applied + [894595] MSR_APICBASE always returns APIC ADDRESS 0 by Kangmo Kim + [907163] ctrl_xfer8 clean/speed up + [907161] clean/speed up of io.cc + [899972] data xfer performance patch V 2.0.4 + [904549] imul gives incorrect result in long mode + [877510] amd64 fixes... + [903465] SEGV in iodev/ne2k.cc line 1211 on Alpha architecture by Christian Lestrade + [903332] copy the bximage result to clipboard, etc by Lukewarm + [950905] Do not PANIC on rare, bad input from user-mode by h.johansson + [924428] ET bit mismatch between CR0 and MSW + [869822] a real SVGA implementation by m_suzu + [867045] fix for compiler errors on VC++ by m_suzu + [838601] support for the over 2GB disk size with MSVC++ + [874816] local ARP/ping/DHCP simulator by m_suzu + [976066] Keyboard: Get controller version by Ben Lunt + [832330] ROMBIOS improvement (reduce stack consumption, etc.) + [977900] READ_CDROM_TOC and base address by Ben Lunt + [961665] WinXP patch to read physical CDROM's TOC by Ben Lunt + [978793] CDROM_SENSE_MODE medium_type by Ben Lunt + [615457] gif to png migration + [1021767] Portability in sb16ctrl.c by Robert Millan + [690400] gzip is confused by GZIP variable in Makefile + [567595] guess floppy image size from image file length by Tal Benavidor + [888426] bochsrc to make vnet useful by m_suzu + [1021758] GNU/k*BSD host support by Robert Millan + [969967] int 15/ah=87h clearing cr0 by Ben Lunt + [1048327] Russian Keymap by Dmitry Soshnikov + [851332] DESTDIR support for install_dlx by Ville Skyttä + [970929] gdbstub support for MinGW tool chains by Muranaka Masaki + [1021740] Turn gdb stub into a runtime option by Charles Duffy + [1063329] RFB key press/release bug fix by Remko van der Vossen + [1079240] Wheel Mouse by Ben Lunt + [1087537] Fix for Win9x CD boot by lukewarm + [1083218] Start of wheel for USB #2 by Ben Lunt + [1098480] bochsrc: fixed floppya example by Alexander Schuch (ci-dev) + [1094407] configure.in: changed wxWindows to wxWidgets by Alexander Schuch + [1092058] serial.cc debug output cleanup by Ben Lunt + [1101165] APIC base address by Christian Neubert (flashburn) + [1093796] Fix for bug #1093786 (Nigel Horne) + [1082584] The start of Bus mice and USB mice by Ben Lunt + [1104695] msvc6 compatibility update (Royce Mitchell III) + [1059199] VGA text font bug fix (Anonymous) + [1108001] Null pointer on bx_atexit() (Ben Lunt) + [1112093] Fixed mouse cursor remain area drawing (Anonymous) + [1114826] Fix PCIBIOS (Destruction prevention of esi and edi) + [1095473] Reading from a CDRW (Fixed) by Ben Lunt + [1123895] x86-64 gdb/debugger fixes by Avi Kivity + [1145423] stosq simulation bugfix by Avi Kivity + [1151012] allow null ss on x86-64 by Avi Kivity + [1153327] ignore segment bases in x86-64 by Avi Kivity + [1153511] Fixed broken screen update (VBE) + [1152808] use 'install' instead of 'cp' during installation by Avi Kivity + [1159626] bugfix [1156776] keyboard scanmode fault by Rene Kootstra + [843328] PATCH: support for Flat-style ToolBar with Win32GUI + [1198308] PATCH: fix incorrect moving mouse cursor when wheel used + [1200515] add TFTP server to vnet & ipv4 bug fix by easeway + [1203305] tuntap incompatibility by Jan Kratochvil + +- SF patches partially applied + [896733] Lazy flags, for more instructions, only 1 src op + [1005422] Improve mouse cursol Grub when 2 buttons-mouse use (WIN32) + (TODO: improved japanese keyboard support) + +- patches applied + - patch.rombios.markevich (Start/Stop Wait Timer) (Kory Markevich) + - patch.apic-zwane (APIC fixes) (Zwane Mwaikambo) + - patch.v8086-exception.lightcone (LightCone) + +- these S.F. bugs were closed + #957660 >>PANIC<< APIC: R(curr timer count): delta < initial + #1192654 60 x 90 text not quite right... + #1189097 "configure --with-sdl --with-rfb" doesn't compile + #1188980 Crash on XP when break into debugger + #1186693 Improving quality of ./configure --help + #1185245 Errors Making Bochs with Mingw32 + #1185289 PSE is not enabled by default on Pentium + #1170620 info cpu scrolls away, is too long + #1157998 ips shown even when waiting for input + #663108 APIC Timer Bug + #831750 bochs unlike real PC in paging + #1182698 PAE support doesn't work + #954400 debugger causes segfaults when gcc 3.4.0 is used + #1171312 Possible SMP problem with ICACHE pageWriteStamp + #1179964 PANIC: RIP>CS.limit when jumping to longmode + #1171067 configure fails to add -lpthread + #1171065 Term UI needs -lncurses + #1171061 SDL GUI startup failure + #1022056 win32 error build debug version..c2146 + #957190 error while attempting to compile sb16.cc + #804797 Debugger: visualization problem (jmp) + #675523 2.0.1 doesn't run on W2K? + #1167358 When using 5430PCI in DR-DOS Bochs would panic with a PUSHAD + #1164654 Bochs VBE bios causing exception 0B in Windows 95 (SVGA bios) + #1162983 conflicts which configure could detect + #1164536 Windows 95 B crashes during install + #526978 cygwin: in an rxvt, stdout is flaky + #542303 >>PANIC<< call_protected: CS selector null + #859457 BRICKS game doesn't work + #1159639 text modes on address A0000-BFFFF are not handled properly + #1164225 define BX_SUPPORT_X86_64 0 + #1163720 ROL bug + #1156776 keyboard scanmode fault + #1162042 Duke Nukem 3D: >>PANIC<< iret: VM set on stack, CPL!=0 + #1161945 ctrl_xfer32 compile/make error + #1157124 Bochs doesn't run with large amounts of memory in bochsrc + #1154266 weird INT handling in V86 mode + #1157051 default Bochs CVS doesn't work-out-of-the-box + #923954 enter() with level > 0, >>PANIC<< iret: return CS selector + #1098476 Privilege Problem after SYSEXIT + #1121734 Bochs crashes when shutting down Win95 + #1099294 VESA for Win98 bogus + #1112836 PGE-Bit crashes Bochs + #947586 The specification difference in x86-64 emulation + #1117145 Push reset Button, APIC0 Error occur + #1123043 fpu stack pointer changed *despite* fault occured (e.g. #pf) + #1122066 PANIC: iret: IP > descriptor limit when installing os/2 warp + #809574 rm -f segfaults + #593952 SuSE rm segfaults + #929783 floppy not recogniced since 2.1 + #1099298 SB16 for Win98 Bogus + #1079483 Reading from a CDRW + #549793 flaw in interrupt gate handling(exception.cc) + #692055 SMP Error + #805479 Booting from disk causes illegal instruction warnings + #909677 pc-speaker doesn't work + #831751 behaviour unrealistic + #661213 CR4.TSD is broken + #685508 PANIC: prefetch: RIP > CS.limit + #1037923 Non-executable page support missed (NX bit, x86-64) + #1106530 wrong disassemble result + #1105208 drive order for boot gets saved wrong + #661060 Problem with Win98SE + #837377 Norton Ghost don't boot + #876689 Unknown register 0x17 [CPU] / WIN98SE + #947282 Bochs segfault + #963317 Persistent Win98SE + #1101168 APIC base address change + #680737 panic when installing winme + #1097187 Install FC3 on bochs-win failed + #875461 vgabios-lgpl should be default + #594797 Bochs segfaults + #602994 bochs breaks boot kernel + #571539 FreeBSD Install from CD Fails + #774257 Device doesn't work under W98 + #759228 Installing Suse 8.2 in Bochs on WinME + #792561 cant boot freebsd 5.1 from cdrom + #804004 Live cd will halt on boot.. + #956173 FreeBSD won't boot iso install images in Bochs v2.1 or 2.2. + #864401 >>PANIC<< jmp_ev: IP out of CS limits! + #853831 Error on int 15h + #1094150 DR-DOS 7.03: panics when using multitasker + #655592 win98 hardwaredetection + #1093786 Solaris 9 installation CD fails + #959585 USB UHCI IO-Device + #963314 Redhat Fedora Installer Kernel Panic and Crash + #1086920 Bochs (cvs) doesn't compile on FreeBSD 5 + #879050 Bochs reports enabled APIC without support + #1071199 dBaseII cause prefetch: RIP > CS limit + #1070812 typecast error while compiling wx.cc + #1068786 FSINCOS Cos value wrong at 90 degrees + #675248 Panic: EIP > limit on win98 install + #829793 [CPU ] prefetch: RIP > CS.limit + #1034059 >>PANIC<< prefetch: running in bogus memory + #1067813 pbm fpu_proto.h:144 ebuild gentoo bochs 2.1.1 + #922034 bios not aliased at 0xFFFFF000, registers wrong after reset + #912666 Configure fails on Yellow Dog Linux 3.0.1 + #922038 Unexisting memory should read back as 0xFFFFFFFF + #1019723 HD image + #1057814 Shadow RAM not aligned + #1057240 Invalid IRET32 implementation + #809682 >> PANIC << : prefetch: RIP > CS.limit + #618680 memory referencing problems + #724262 A few things (Windows 2.03, Wolfenstein) + #985375 Crash Mandrake 7.1 + #913418 compiler errors with --enable-external-debugger option + #708847 CR8 access should not panic X86-64 + #1039499 Compile error pcipnic.cc (cygwin) + #978024 compile against wxGTK-2.5.2 fails + #639073 MacOSX: Networking not implemented + #639074 MacOSX: Soundblaster not implemented + #963264 Latest CVS --enable-pcidev fails to configue on YDL Linux + #586282 Mac OS X, will not "make" + #699532 CVS (as of 2003/03/07) cannot read disk images + #639275 wrong more than 2GB size DVD-ROM + #766020 info registers / dump_cpu get old eflags + #655920 QuickBasic (qbx.exe) panics + #676188 Error BX_MAX_DIRTY_PAGE_TABLE_MEGS + #923821 LOCK not generating exceptions properly + #1007747 Wrong configure? + #1022577 show "call" command crashes bochs every time + #681849 SuSE 8.1 Compile problem + #660322 Install bochs 2.0 on SuSe 8,0 + #1022587 "Unrecognized args" message wrong after invalid show command + #833118 TUN/TAP interface bug + #1022178 tuntap module mangles incoming broadcast packets + #1028682 Report incorrect disk parameters of floppy + #1026234 make fails on Cygwin because of missing .exe extension + #1026241 --enable-cpp needed for .conf.win32-cygwin + #855323 BIOS Panic at rombios.c, line 1563 + #762773 ROM checksum is not checked in rom_scan_loop + #657604 concat_image_t.lseek to byte -1378816 fa + #800140 No AH=83h INT15h implemented + #831965 Win32.zip BIOSes in wrong directory + #873280 bximage crashes after createing "growing" + #892223 bochsrc-sample.txt/vgaromimage option error + #1014361 Bximage on WinXP won't create image + #651510 bximage won't create larger than 2GB + #759206 bximage fails on Win32 for hd images over 2Gb + #759210 Bochs fails on image files over 2Gb on Win32 + #799785 bximage doesn't work + #903345 Problem compiling harddrv.cc + #933303 Bochs cannot lseek() HD images > 2GB on W32 platforms + #888438 bximage crashes...(hacked fix included) + #871720 bximage 2GB file size limit + #930368 Can't create big hard drives (>2 GB) + #912496 IDIV can cause simulator divide error + #522111 Host os SIGILL, booting grub from hd + #1005052 DMA Controller Model Problem + #552939 Bochs window doesn't resize when win311 + #989478 I-Cache and undefined Instructions + #661008 make install fails + #845691 Workaround: Ne2k and Windows 2000 not working + #923662 BIOS diskette motor countdown byte broken + #848141 VGA problems running Scitech Display Driver on Win95 + #799370 Problem booting ReactOS 0.1.3 + #670143 No rule to make config.h + #653444 with vbe/lgpl bios, scrn updates broken + #655696 quickstart broken? + #659350 FDC + #620853 Ne2000 ethernet card *NOT* supported + #607611 Numlock + #543476 Sound card does not work in Windows 95 + #529554 unsupported VBE features DISPI update + #487316 Access violation on Win32 + #576253 RTC too fast + #489748 io read from address 000003c0, len=2 + #656861 Gentoo Linux panics in VGA code + #787184 Video BIOS's don't checksum correctly + #988529 textconfig [Save options to] function output obsolete option + #987293 Cannot accesss header/toolbar + #988246 floppy read error + #933199 speedup Bochs compilation 4x -> suggestion + #979106 Incorrect disassembly table entry + #658374 FPU incorrect emulation + #706933 Problem with the F12-Key + #477043 math_abort panic in RH 7.1 + #634371 Floating point problems + #681138 // is not valid in C + #643300 cpuid feature flag 15, cmov and fcmov + #913697 missing division by 0 exeption in fpu emuation + #923682 FSTENV/FINIT problems + #923855 FPTAN doesn't work right with full NPX stack + #924379 ET bit mismatch between CR0 and MSW + #716116 Direct floppy access + #962919 Mac: iodev/cdrom.cc disordered + #954751 Two FPU.CPP in project + #954359 Compile faile is 3dnow support is selected without SSE support + #906412 FreeSCO error + #942060 FDC Controller not conforming to specifications + #938522 Win XP installation fails + #923613 BOUND instruction exception handling is broken + #923223 memtest86 errors + #593342 autoconf script doesn't regenerate clean + #616116 Crash on exit... + #922042 shutdown through port 92 does not work + #891633 02839990390p[CPU0 ] >>PANIC<< RDMSR: Unknown register 0x17 + #923653 DAA instruction is broken + #911225 obscure AAA / AAS bugs + #837206 Problems with numerical keys + #658765 BOCHS halts in runtime config + #890734 Bochsrc Parser Bug with commas included in strings + #877285 MSR_APICBASE zero upon startup + #526984 SDL compiled in cygwin just quits + #886406 I/O permissions bug + #883239 undefined symbols in gui/siminterface.h:1215 + #419647 on OSF1, cxx hates C++ inlines + #809790 "No rule to make target `devices.cc?..." + #873654 How compile without plug-in support ? + #837161 Test case for BX_CPU_C::IRET32 + #888116 mmx.cc compile error + +------------------------------------------------------------------------- +Changes in 2.1.1 (February 8, 2004): + +- fix bug in int15h function 0xe820 (Christian Neubert) +- fix vmware3 disk support on big-endian platforms (Christophe Bothamy) +- fix conditions for NM exception on FWAIT instruction (Christophe) +- fix symbol conflict in rfb and x display libraries (Volker Ruppert) +- allow 16 bit writes to ne2k page 0 (Kenneth Stailey) +- notify display libraries on change of bpp (Volker) +- fix bug in int13h function 0x10 (Volker) +- fix floppy dialog error on win2k (Volker) +- fix adress check in TSS IO permission bitmap (Christophe) +- fix buffer overflow vulnerability pointed out by SeSoX (Christophe) +- updates for MacOS compile (Daniel Gimpelevich) + +------------------------------------------------------------------------- +Changes in 2.1 (January 9, 2004): + +Brief summary : +- New disassembler +- 3DNow!/SSE/SSE2/PNI instruction support +- Vmware3/Sparse/Undoable/Growing harddisk images support +- many VGA emulation improvements (e.g. high/true color VBE modes added) +- No more X11 vga font required + +Detailed change log : + +- CPU + - added emulation of AMD 3DNow! instructions set. (Stanislav Shwartsman) + Bochs now could decode all AMD 3DNow! instructions. + Most of instructions still not implemented, but the basis already presents. + Configure --enable-3dnow to enable 3DNow! support. + Notes : + - These instructions are not implemented yet: + PFPNACC_PqQq, PF2IW_PqQq, PFNACC_PqQq, PFCMPGE_PqQq, PFMIN_PqQq, + PFRCP_PqQq, PFRSQRT_PqQq, PFSUB_PqQq, PFADD_PqQq, PFCMPGT_PqQq, + PFMAX_PqQq, PFRCPIT1_PqQq, PFRSQIT1_PqQq, PFSUBR_PqQq, PFACC_PqQq, + PFCMPEQ_PqQq, PFMUL_PqQq, PFRCPIT2_PqQq + - CPUID does not report 3DNow! instruction set. + - added emulation of SSE/SSE2 floating point instructions. (Stanislav) + All SSE/SSE2 floating point instructions are fully implemented using + free softfloat library (including DAZ support and floating point + exceptions). Correctness of the emulation checked with heavily random + testing. + - added emulation of SSE3 (PNI) instructions (Stanislav) + Currently only 3 PNI opcodes still not implemented: + FISTTP m16int, FISTTP m32int, FISTTP m64int + - added P4 CPU support to CPUID instruction. (Stanislav) + - fixed implementation of FXSAVE/FXRSTOR instructions. (Stanislav) + - bugfix: unallowed lock prefix cases must cause #UD exception. (Stanislav) + - fixed fetchdecode bug caused #UD in SYSENTER/SYSEXIT instructions + in 32bit mode. (Stanislav) + - fixed fetchdecode64 bug caused wrong decoding of opcodes containing + BxImmediate_IvIw or BxImmediate_IwIb in x86-64. (Stanislav) + - fixed bug in int01 (opcode 0xF1) emulation. (Vitaly Vorobyov) + - fixed bug in x86 debugger with dr0-dr3 registers (Vitaly) + - fixed bug with mov to/from dr register in v86mode. + (now exception is generated (according to Intel documentation) + instead of panic) (Vitaly) + - fixed stack limit checking, now message is generated as BX_DEBUG, + rather then BX_PANIC, and exception code is executed. (Vitaly) + - instrumentation code updated. (Stanislav) + - fix flaw in IO bitmap permission of TSS (Christophe Bothamy) + - cpu resets on triple fault (Christophe) + - remove calculation on cr3 in dtranslate_linear to increase + emulation speed (Conn Clark) + - numerous x86-64 fixes (Peter Tattam) + +- FPU + - hundreds of bugfixes in FPU emulation after checking of the emulation + with testfloat (Scott Duplichan). + - Fixed cases: + - floatx80_to_int32, floatx80_to_float32 + - floatx80_to_float64, floatx80_round_to_int + - floatx80_add, floatx80_sub, + - floatx80_mul, floatx80_div + - implemented FCMOVcc instructions (Stanislav) + - 64-bit addressing support for x86-64 mode (Peter) + +- Disassembler + - replaced Bochs disassember. New table-based disassembler fully supports + all IA-32 instruction sets including all FPU/MMX/SSE/SSE2/SSE3 opcodes. + (Stanislav) + +- I/O devices + - general + - i/o access mask implemented, unallowed cases are now handled in the devices + code and cause a BX_ERROR (Volker Ruppert) + - include slowdown timer as a runtime option (Christophe) + - netBSD : fix serial, ethernet, cdrom (fredb, uebayasi and David Laight) + - VGA + - color depth 15, 16, 24 and 32 bpp supported by VBE (Volker and + Christopher Nelson for 32 bpp on win32). Supported by sdl, x, win32 and wx. + - SVGA mode 0x6A (800x600x4bpp) implemented (Volker) + - new CGA graphics modes 640x200x1bpp and 160x100x4bpp (text mode 80x100) (Volker) + - raster operations AND, OR and XOR in write mode 2 (based on SF patch #707931) (Volker) + - 'split screen' in standard VGA graphics mode implemented (Volker) + - 'double scan' and 'double width' now handled in the VGA code (Volker) + - more accurate emulation of the horizontal and vertical retrace (Volker) + - changeable start address and variable line length supported by all + graphics modes (Volker) + - VBE: preserve video memory feature implemented (Volker) + - additional text mode features prepared (handled in the display library + code) (Volker) + - PCI + - add experimental PCI VGA card (Mike Nordell) + - add experimental PCI USB card (Ben Lunt) + - Harddisks + - per device selectable harddisk modes : + - undoable, volatile, growing disks support (Christophe) + - sparse disks support (justinSB) + - vmware3 disks support (Sharvil Nanavati) + - fix non detection of hard drives by minix2 (Christophe) + - implement atapi command 0xA8 read (12) (Christophe) + - mode sense command updated (Hartmut Birr) + - sb16 + - opl2 support enhanced (James E. Flemer) + - ne2k + - tap support for FreeBSD (Ronald Klop and Gen Otsuji) + - fix when booting with grub (Keir Fraser) + - cmos + - date/time change support added (Volker) + - UIP bit and divider chain reset implemented (Volker) + - initial time can now be set to local time or utc (Christophe, Daniel Gimpelevich) + - keyboard + - keyboard reset function (0xff) now resets the keyboard (Volker) + - gameport + - new standard PC gameport device (real joystick connected on Linux and + win32 only). Enable it with --enable-gameport or the SB16 emulation (Volker) + - serial + - FIFO emulation (UART type 16550A) implemented (Volker) + - floppies + - 160k,180k,320k floppies support (Ben Lunt) + +- display libraries + - X11 + - onboard vgacard charmap usage (no need for external X11 vga font any more) (Christophe) + - vgacard charmap change support (Christophe) + - fix black stripes on partial exposes (Dirk Thierbach) + - headerbar redraw optimizations (Dirk Thierbach) + - external font files and their installation mechanism removed (Volker) + - belgian keymap support (Wouter Verhelst, Eric Brasseur) + - win32 + wx + x: new application/window icon (bochs.ico / icon_bochs.xpm) (Volker) + - sdl + win32 + wx + x: new textmode features: variable line length, + char width switch, horizontal and vertical pel panning (Volker) + - win32 + wxMSW: key event handling rewritten (Volker) + - win32: status bar at the bottom of the simulation window added (Volker) + - wxMSW: resource problems fixed - wx dll plugin works now without errors (Volker) + - term: variable line length and cursor enable/disable feature implemented (Volker) + - rfb + - textmode: charmap change, better cursor emulation, variable line length (Volker) + - headerbar works now (power, reset and user button are okay) (Volker) + - key event handling rewritten (Volker) + - Bochs-RFB waits up to 30 seconds for a client connection. The emulation + starts after connecting the client. (Volker) + - carbon: Alt/Ctrl/Shift key handling rewritten & SysRq/Ctrl-Break key support added + (Daniel) + +- configuration interface + - gui dialogs as an extension of the textconfig interface on win32 added (Volker) + * ask dialog + * save text snapshot + * user button shortcut + * floppy image change + - wxwindows configuration dialogs improved (Volker, Christophe) + +- support tools + - bximage : added support for + - growing disks (Christophe) + - sparse disks (justinSB) + - created bxcommit tool for undoable disk images (Christophe) + +- System BIOS : + - fixed int15 function e801 (get memory size) (Christophe) + - added int75_handler for FPU Dos Exceptions (Christophe) + - added int16 function 0a (Get Keyboard ID) (Volker) + - added support for ElTorito Harddisk-on-CD emulation (Christophe) + - fixed ATA/Serial ioport conflict (Daniel) + +- VGA BIOS : updated to version 0.4c (Christophe) + +- configure script/compile/porting to other OSes/installation + - fixes for compilation with MSVC (Andrew Zabolotny) + - fixes for cross-compilation (Jeroen Janssen) + - win32 nsis installer script updates (Volker) + - small configure fixes for MacOS (Christophe) + - optimizations & compile fixes for MacOS/X (Daniel) + +- configuration files. The following options have been deprecated : + diskc, diskd, cdromd, time0, pit, newharddrivesupport. + +- documentation + - already ported and obsolete parts of the old documentation removed (Volker) + - user documentation updated and extended : + - improved section "What does Bochs need" (Volker) + - command line arguments (Volker) + - search order for the configuration file (Volker) + - the configuration interface 'textconfig' (Volker) + - FreeDOS Beta 8 installation instructions (Volker) + - disk modes (Christophe) + - LBA translation (Christophe) + - cdboot error codes (Christophe) + - SCO OpenServer install section (Carl Sopchak) + - MacOS-X DMG install guide (Aard Vark) + - update Win98 install guide (Dirk Thierbach) + +- SF patches applied + #658950 Bug in FPU (Anonymous) + #678117 build fail due to bad SGML punctuation (Anonymous) + #671873 minimal USB support (UHCI) (Ben Lunt) + #682539 Fix CapsLock and NumLock behavior (rock at gimp.org) + #720776 REX MOVB immediate broken for x86_64 (Arnd Bergmann) + #729450 new keymap x11-pc-be.map (Wouter Verhelst) + #735990 Limited patches for VC++ (Anonymous) + #742670 fix library dependencies in GUI plugins (Robert Millan) + #742782 LFB bugfix (Jan L. Hauffa) + #748414 load32bitOShack bug (kyriazis at nvidia.com) + #830079 Fix bochs's application error if unsupported key pressed (Anonymous) + #724466 enable building with CC=gcc-3.2 CXX=g++-3.2; dist-clean adds(Leonard Norrgard) + #834962 Fixed drawing graphics is broken (Anonymous) + #838401 Fixed redrawing of ToolBar on Win32GUI (Anonymous) + #850236 Fixed accessing DVD-ROM with direct device access on Win32 (Anonymous) + #847822 Bochs crash when exmining memory that crosses page boundary (ortal at jungo.com) + +- SF patches partially applied + #707931 Support EGA/VGA write mode 2 and others (Anonymous) + already applied: disable IME, split screen, write mode 2, + BIOS INT16h/AH=05h + #856506/#856510 Patch to fix compile-time iodev/cd-rom.cc error (alden.dima at nist.gov) + Correct patch provided in SF bug report #843433 (birkhofer at users.sourceforge.net) + +- patches applied + - patch.highmem (memory allocation) (Zwane Mwaikambo) + - patch.floppy-160k-180k-320k-benlunt (exotic floppies) (Ben Lunt) + - patch.perf-regparm-cclark (performance) (Conn Clark) + +- new patches present in the patches directory : + patch.pipelined-asm-cclark + patch.mingw-resources + patch.v8086-exception.lightcone + patch.pit-vitaly-vorobyov + patch.rombios-vitaly-vorobyov + patch.win32-vitaly-vorobyov + patch.win32-new-files-vitaly-vorobyov.tgz + patch.rombios.markevich + patch.rombios.dirk.thierbach + +- these S.F. bugs were closed + #865354 ">>PANIC<< CRA: divider chain control 0x07" in Linux 1.1 + #725796 configure script bug + #859768 cpuid + #863964 panic in duron 2000 + #843433 cdrom.cc on MacOSX: wrong const names + #818493 EMU][ (DJGPP app running on FreeDOS) broken + #787005 Some MOV instructions are not implemented!!! + #840664 2200136693936p[CPU ] >>PANIC<< prefetch: RIP > CS.limit + #837416 V2 OS not compatible !? + #650917 Serial port broken under win95 + #829863 Make bochs 2.0.2 build with gcc3 + #816971 main.cc: getcwd() missing argument + #813556 Compile error under gcc 3.3.1 + #809758 RIGHT ALT does not function properly + #809695 CVS complains about unknown files after compilation + #628762 Error in Floppy Booting + #474526 Crash under win32 (access violation) + #687619 test case for BX_CPU_C::IRET32 + #664544 Panic in IRET32 - Reporting test case + #637822 test case for BX_CPU_C::IRET32 + #603410 BX_CP U_C::IRET32 + #537047 IRET32 incomplete emulation, panic + #805541 Compile fails on i686, gcc 3.3 + #798829 Problem booting from ISO image + #688163 Panic at rombios.c + #688161 rombios.c crashes when boot from a CD. + #796339 int 15h, e801h broken? + #666946 Slowdown Timer should be a module + #783826 the clock is extremely fast + #645609 Real Time Clock is too *FAST* + #663320 flaw in IO bitmap permission handling + #764929 Timing is off. + #659510 Bochs timing off by x10 + #787138 No ROM BIOS character map + #787134 Config options not saved + #689201 Disassembler bug + #666202 Windows 2000 - random screen blanking with linux DLX demo + #629242 reset during Doom -> BIOS panic + #695434 minix floppies won't boot. + #764473 Freesco Linux crashes on boot + #656026 error when trying to run some stuff + #614202 HD: non-byte IO read to 01f4 + #777357 Strange FPU compiler error + #583758 gag bootloader doesn't run + #658639 ne2k panics with MS lanman Client/DOS62 + #536711 problem running smart bootmanager + #741433 Disabling all ata# results in HD error. + #753200 lock instruction doesn't do an illegal instruction trap + #679389 libbx_wx.so.0: undefined symbol + #758936 Problem Installing Bochs + #742580 I configured fants but bochs still give me the same error + #772242 iodev/vga.cc wrong memory access. + #739222 Cannot change resultion + #693344 libwx_gtk2.3.so.2 RedHat linux 8.0 + #639320 sparc: needs -lm to compile + #587422 Windows 95j doesn't boot + #547817 sparc: rfb needs -lsocket + #480963 RFB: option to wait for client + #763893 i've got problems with a "libvga.so.1" and another file + #766490 Documentation mistake + #766481 Bochs 2.0.2 Fails to compile on YDL3.0 + #626144 %lld is not portable + #752241 lock prefix erronously allowed for some instructions + #743305 fetchdecode.c probs + #658707 Automatic exit? + #696758 BeOS can't mount image disk, won't complete boot. + #737048 Enabling keyboard resets controllers translation mode + #717713 Bochs panics on startup on RH 9 + #741108 VGA PANIC + #730922 seg fault on "bochs boot: cdrom" + #658905 VGA read write error + #564218 Panic on vga_mem_write + #614231 X11 doesn't support charmap change + #708311 Missing CGA low-res emulation + #720776 REX MOVB immediate broken for x86_64 + #643296 lock prefix, unallowed cases + #716964 [sb16] OPL.timer_running not initialized + #662074 little mistake in the default config example + #470701 CD-ROM on Win2K needs FILE_SHARE_READ + #706454 bug?? + #653861 Win32 build bug + #421155 panic on vga read 0x3c7,0x3cb + #666434 VGA BIOS: Incompatible mode reporting + #681819 Incorrent return value from cdrom reads + #648222 Lotus Agenda futuristic dates off + #657455 doesn't boot plan9 + #658938 SGDT in VM8086 + +------------------------------------------------------------------------- +Changes in 2.0.2 (January 21, 2003): + +- fix possible segfault in wxWindows (Volker Ruppert) +- fix instrumentation (Stanislav Shwartsman) +- fix cdrom read_toc() function for *BSD (Keith Matthew Jones) +- fix NetBSD boot from cdrom (Christophe Bothamy) +- fix cmos checksum (Volker) +- fix "refresh bit" behaviour in pit (Volker) +- fix .bochsrc parsing (Volker) +- fix vga resize/redraw problems (Volker) +- fix compilation issues on Irix and Tru64 (Christophe) +- fix MMX/SSE bugs (Stanislav, Peter Tattam) + +------------------------------------------------------------------------- +Changes in 2.0.1 (January 4, 2003): + +- fix corrupt saved configuration files (Christophe Bothamy) +- fix missing break statements in apic (Shai Fultheim) +- fix compiling sb16 under FreeBSD (Volker Ruppert) +- updates to the documentation (Volker) +- fix text mode colors 8 to 15 (Volker) +- fix FPU integer load bug (Volker) +- stop pasting on hardware reset (Volker) + +------------------------------------------------------------------------- +Changes in 2.0 (December 21, 2002): + +Since the change log is hundreds of lines long, here is a very brief summary. +- 2x emulation speedup!!! +- added plugin devices and guis. Now you can compile with many more + options, and choose between them at runtime. +- added emulation of AMD x86-64, MMX, SSE, SSE2 instructions +- add wxWindows port (a graphical configuration interface and display lib) + and SVGAlib port (full screen display for Linux without X11) +- improvements in many I/O devices: for example up to 8 hard disks/cdroms, + TUN/TAP network interface, 360k floppies, +- improved MacOSX/Carbon interface and updated MacOS9 port +- GDB remote stub, allows symbolic debugging with Bochs simulation. +- support for up to 32gig hard disk images + +Detailed change log follows. + +- documentation + - manpages updated (Volker, Christophe) + - install HTML rendering of docbook documentation instead of + docs-html (Bryce) + - doc/docbook/Makefile is now generated by configure script. + if configure detects docbook2html on your system, it will turn on + --enable-docbook and run make in the doc/docbook directory. Also + make install will install documentation into $(docdir). You can use + --disable-docbook to turn this off, if necessary. (Bryce) + - add "make bochsdoc.tar.gz" target to create a documentation tarball. + If you do "make webinst" and you have write access on SF shell server, + it updates doc/docbook/* on the website (Bryce) + - user documentation additions: + - new options (Bryce, Volker, Christophe) + - Bios tips section (Christophe) + - Tuntap section (Christophe) + - Serial Port section (Christophe) + - "Will it Work for Me" / "Is Bochs Right for Me" sections + (N. David Guarneri) + - VESA section (Jeroen Janssen) + - several documents, previously existing as separate html files, have + been included : + - internal debugger section (Christophe) + - gdb stub debugger section (Christophe) + - WinME, WinNT, WinXP, The Hurd, Japanese Win95 install tips (Christophe) + - Win95, Win98 install tips (N. David) + - SB16 section (N. David) + +- configure script/compile/porting to other OSes/installation + - added plugin architecture + - plugin code written by Bryce, Christophe, Volker based on + plex86's plugin code by Kevin. Testing help from Psyon and Br'fin. + - Plugins are shared libraries that can be loaded on demand. Example: + the serial device is implemented as a plugin. In UNIX, the + serial plugin is called libbx_serial.so. When Bochs reads its + configuration file, if the serial device is enabled it loads + libbx_serial.so. + - all display libraries, most I/O devices are converted to plugins now + - plugins supported on Linux, Solaris, and MacOS X using libtool, + Cygwin using dlltool. On MacOSX, you must have dlcompat installed + and in your include/library paths at configure time. (See .conf.macosx + for an example.) + - we use libtool's LTDL library from libtool 1.4.2, with a number of + critical bug fixes (Bryce Denney) + - the Linux binary RPMs are built with plugin support + - to compile with plugins, configure with --enable-plugins + - the LTDL_LIBRARY_PATH variable tells Bochs where its plugins can be + found. Bochs has a compile-time default for this variable which is + correct if you do a make install. You would only need to set the + variable if the default is wrong. + - for win32 plugins we added "BOCHSAPI" in front of many classes + and methods, to aid in building DLLs. This turns into __declspecs + which are used when making an export library for Bochs. + - allow many display libraries to be configured and compiled at + a time. For example --with-win32 --with-sdl --with-rfb. + Also, we added an experimental option --with-all-libs which + tries to detect which --with-* options will work. If the + autodetection fails, just type the --with-* options explicitly. (Bryce) + - add #if's around all files which are conditionally compiled such + as cdrom.cc and sb16.cc. This makes it possible to compile every + source file all the time, which has the potential to simplify the + configure script and makefiles. At present we only take advantage + of this capability in the win32 VC++ workspace. (Bryce) + - the MacOS9 port has been updated so that it works again. It had + not been updated in at least 2 years, maybe more. (Christophe Bothamy) + - improve support for FHS standard (Robert Millan, Volker Ruppert) + See patches 551811 and 650066. + - keep separate CFLAGS and CXXFLAGS for Bochs (usually a graphical + program) and console programs such as bximage and niclist. Some + sdl and wx compile flags were making bximage and niclist unusable.(Bryce) + - add concept of cross-configuring in the configure script. If you + use the --target option to generate makefiles to be used on another + machine, some detection of compilers and libraries is disabled. (Bryce) + - fix term compile on Cygwin, but it has to be done without -mno-cygwin, + which means that several win32 features such as networking do not work. + - add "-Wno-multichar" on beos + - test for largefile support, and add required CFLAGS (Bryce) + - add -lm when it's needed, and not when it's not (Bryce) + - add configure support for 8 processors. Bochs can support up to 15 + with some work on the BIOS. + - fix nmake makefile generation (Psyon) + - improved pthread detection function from ac-archive project on SF + - add installer package for Windows, using Nullsoft (Michael Rich, Bryce) + - on MacOSX, add startup script that creates a text console and then + runs Bochs. Also add make target to create a DMG disk image (Br'fin) + - do not restart the font server on Unix/X11, if vga.pcf was already + installed. On several modern machines, if you restart the font + server the user has to restart X windows. (Bryce) + - update most .conf.* files with modern options such as + --enable-all-optimizations. (Bryce) + - The MacosX .conf script adds /sw/include and /sw/lib to the compile/link + path list because it is a common place to put dlcompat. Dlcompat is + required when building with plugins. (Bryce) + - rpms can now be built without root privileges (Bryce) + +- command line + - fixed up our command line options (Volker, Bryce, Christophe) + Usage: bochs [flags] [bochsrc options] + -n no configuration file + -f configfile specify configuration file + -q quick start (skip configuration interface) + --help display this help and exit + +- configuration file (bochsrc) + - There are several new options. See the documentation for more details. + - config_interface: select text mode menus or wxWindows for configuration + - display_library: select which display lib to use + - optromimage: load optional rom images + - ataN (N=0,1,2,3): up to 4 ATA controllers for hard disks, cdroms + - ataN-master, ataN-slave, N=0,1,2,3: defines a hard disk or cdrom. + The "ata*" options replace diskc, diskd, and cdromd, which are + now deprecated. + - floppy_bootsig_check: control the 0xaa55 signature check on boot floppies + - logprefix: lets you change the format of log messages + (patch by Carl Sopchak, help from Christophe) + - debugger_log: log all output from bochs debugger + - user_shortcut: allow you to type key combinations like Ctrl-Alt-Del + - pit: control the PIT model, including realtime option to try to + keep in sync with real time. + - Credits: Christophe added optromimage, everything about ATA, + floppy_bootsig_check, debugger_log. Bryce added config_interface + and display_library. Volker did the user_shortcut button. + Greg Alexander wrote the PIT model and added the realtime option. + - since v1.3 we've been able to use environment variables in pathnames + in the bochsrc file. Now, a few variables have default values, set at + compile time, that are used if the user does not set a value. If Bochs + is installed correctly, the defaults will be correct and the user will + not need to override them. + - $LTDL_LIBRARY_PATH is the path name where the plugins can be found. + The default value comes from $(plugdir) in the makefile. This is only + important if plugins are enabled. (Bryce) + - $BXSHARE is the path where the BIOSes and keymaps are installed. + The default value comes from $(sharedir) in the makefile. Disk + images on the Bochs website will begin to use BIOS pathnames like + $BXSHARE/BIOS-bios-latest. On win32, the $BXSHARE default is + set by the NSIS installer and read from the registry. On MacoSX, + the $BXSHARE default is set to the path containing bochs.app. + (Bryce, Volker, Br'fin) + - new option in the configuration interface to reset all bochsrc + settings to initial defaults. A reset occurs just before reading + a new configuration file, so that leftover parameters from a + previous configuration do not affect the new configuration. Also, + you can request a reset using the configuration interface. (Volker, Bryce) + - ne2k line can now specify a script to set up the interface (Christophe) + - on Unix, also search /etc/bochsrc (Bernhard Bablok) + - you can use #include in the bochsrc to read configuration from other + files (Volker) + +- CPU + - speed optimizations from Kevin Lawton, yielding around 2x speedup + - guest2host_tlb : for entries in the paging TLB which point to normal + physical memory pages, a pointer to the host address of the emulated + physical memory (from malloc()) page is stored in the TLB entry. In + many cases, this pointer can be used in memory accesses to directly + read/write the guest memory address. In exceptional cases, the physical + memory access routines are used. Turn on with --enable-guest2host-tlb. + - repeat IO/string : for some variants of repeatable IO and string + instructions, the segmentation and paging checks are done in batch along + with the data transfers, constrained within page boundaries and the + segment limits. Turn on with --enable-repeat-speedups. + - icache : The structure holding instruction decode information was + reduced to 32 bytes. 24 bytes for the actual decode data, and 4 each + for pointers to the address resolution routine (not always needed) and + the instruction emulation routine. With a reasonably small + per-instruction decode size, an instruction cache (iCache) was created, + which is simply a hash table. The main cpu loop looks in the table + first; if the instruction has already been decoded, execution can begin + immediately without decoding. Turn on with --enable-icache. + - host specific asm : when compiling on an x86 platform, use of + x86-specific asms can be enabled to accelerate several facets of + emulating instructions. For example, the EFLAGS values are much more + efficient to calculate when the actual x86 instructions are used to + generate the EFLAGS values. Turn on with --enable-host-specific-asms. + (Kevin, with help from Jas Sandys-Lumsdaine) + - if you want to enable all the speed optimizations that we believe + to be stable, use --enable-all-optimizations. The release binaries + are built with this option. + - add support for AMD's x86-64 instruction set. To enable, configure with + --enable-x86-64. The AMD x86-64 support is about 90% complete and is + still experimental. We've implemented the core x86-64 instruction set and + the changes to the rest of Bochs necessary to operate in long mode, but + we've still to implement checking for canonical 64 bit addresses. The code + has been tested on a limited number of test programs. It has been able to + successfully boot a x86-64 Linux kernel and run a 64 bit userland + application. It has also successfully run a DOS based 64 bit protected + mode test application. (Peter Tattam, with merge/bugfix help from Kevin + Lawton and Bryce Denney) + - add MMX support. To enable, configure with --enable-mmx. + (Stanislav Shwartsman) + - add SSE and SSE2 support. To enable, configure with --enable-sse=1 + or --enable-sse=2. (Stanislav) + - fixed the behaviour of the bcd instructions AAM, AAD and DAA based on + SF patch #537146 (Volker) + - stop printing an error for VERR/VERW. According to the i386 opcode + description there is no error present. (Volker) + - fix bug [ 625878 ] reset doesn't reset something(?). Fix cpu reset + when executing a rep instruction (Christophe) + - use accessors methods for CFLAGS and several other registers, so that + the implementation can be changed transparently later (Bryce, Stanislav) + - add support for page size extensions, also known as 4meg pages. + Turn on with --enable-4meg-pages. (Kevin Lawton) + - add support for page global extensions. Turn on with + --enable-global-pages. (Kevin) + - add support for physical address extensions. Turn on with --enable-pae. + (Peter Tattam) + - implement RDMSR and WRMSR. not all MSRs are supported (Zwane Mwaikambo) + - new configure option --enable-ignore-bad-msr, which makes unrecognized + MSR reads and writes into just a warning + - fix PIC/APIC interrupt problem that caused Linux 2.4.19 to hang + during boot (Peter) + - CMPXCHG8B patch (Michael Hohmuth) + - EFLAGS are now stored in the same form as the native EFLAGS on an x86, + so that we can use native machine instructions in some cases (Kevin) + - instrumentation code updated (Stanislav) + +- FPU + - fixed bug [ 452275 ] fprem emulation bug (Volker) + - fixed bug [ 648579 ] Mac OSX >>PANIC<< FPU_printall. There was an + endianness issue with the fpu (Christophe) + +- I/O devices + - rewrote pc_system timers (Kevin) + - biosdev + - this new device handles the panic/error/info/debug messages sent + by the Bios and VGABios. It was previously done in the unmapped device. + - cdrom + - implementation of the function READ TOC for cdrom image files. (Volker) + - function capacity() for win32 fixed. Now it returns the number of blocks + instead of bytes. (Volker) + - added multiple cdrom support for win32 (NT/2000 version untested). The + ASPI version uses the cdrom drives in the system's order. Drive letters + are not used by ASPI. (Volker) + - fix configure script's cdrom detection on BeOS (Bryce) + - fix physical CD change at runtime (Bryce) + - cmos + - fix panic when WinXP read port 70h (Christophe) + - add ps/2 style century at index 37 to allow WinXP to boot. (Bryce) + - dma + - DMA register and unregister functions for DMA channels added and macros + for DMA functions defined. The changes are based on the Plex86 functions. + (Volker) + - implementation of the DMA controller reset (Volker) + - the value of the command register must be always 0x00 (BX_ERROR fixed) + - floppy (Volker) + - implemented Tape Drive Register (Dave Poirier) + - added support for 360k floppy images + - the skip flag (SK) in command 'read sector' is ignored now + - floppy read and write function do not set the 'seek end' bit in status + register 0 (fixes SF bug #553377) + - the status of the 'disk changed' line depends on the selected drive. + The digital input register is now an array (DIR[4]). + - apply patch [ 635021 ] floppy cleanup by Alex Thiel + - distinguish between floppy drive type and media type + - hard drive + - add largefiles support, to allow disk images larger than 2gig. + (Stu Grossman) + - missing conditions for lower_irq() added (Volker) + - several noncritical panics replaced with BX_ERRORS and the controller + returns an error code until we implement the features (Volker) + - applied patch from Carl Sopchak for booting sco openserver + - allow disk block access only if concatenated images are not used + (Christophe) + - fix bug [ 419415 ] netbsd 1.5 rescue disk won't boot (Volker) + - multiple drq atapi data transfers corruption fixed (Christophe) + - added some commands to the unsupported "Set Feature" commands (Christophe) + - speedups in repeated IO transfers (Kevin) + - support for Peter Tattam's external disk simulator (Bryce) + - 4 channels / 8 devices support (Christophe) + - "inquiry" atapi command results corrected (Volker) + - check for incomplete devices configuration before starting the + simulation (Bryce) + - implemented the different bios disk translation schemes (Christophe) + - keyboard and mouse + - add commands 0xd2, 0xdd and 0xdf (Dave) + - fix bug [ 613975 ] wxWindows: params redefined on restart (Bryce) + - in function mouse_motion(): added parentheses to fix compilation problems + with MSVC. See SF bug #575301. (Volker) + - added missing register_irq() for the PS/2 mouse IRQ12 (Volker) + - fix "AltGr" key on European keyboards wxWindows/win32, SDL (Volker) + - NE2000 + - function reset() clears the IRQ line (Volker) + - added TUN/TAP interface (Renzo Davoli, Christophe) + - fix DOS based packet drivers that use an odd count for the NE2000 DMA (Peter) + - changed "TCR write, reserved bits set" panic into an error, fixes + networking with debian image (Bryce) + - parallel + - parport1 enable/disable support added (Volker) + - PCI (Volker) + - implementation of the PCI device register mechanism + - PCI memory handling moved to the memory code + - replaced memcpy() in pci_read() by a more portable code. Problems with + PCI on big-endian machines are fixed now (SF bug #638481). + - implementation of the PCI-to-ISA bridge started (still incomplete) + - PIC + - fixed detection of single mode and level senistive mode in ICW1 (Volker) + - fixed handling of rotate_on_autoeoi for master PIC (Volker) + - irq mask is now cleared on initialization (Dave) + - fixed lockup during mouse movements during win98 install. (patch from + Wilfried Weissmann) + - PIT + - Added realtime PIT support (Greg) + - Sound Blaster 16 + - it used to enable itself all the time; now only when you ask + - fix memory leaks (Bryce) + - serial + - don't cause problems when serial device is disabled (Volker) + - unmapped + - add programmatic shutdown feature at port 0x8900 (Christophe) + - vga + - VBE fixes (Jeroen, Volker) + - CRTC fixes (Volker) + - sequencer reset with bits 'reset1' and 'reset2' implemented (Volker) + - add charmap change support (used by SDL, win32 and wxWindows gui) (Volker) + - screen dimensions / updates for some graphics and text modes fixed (Volker) + - use the start address when calculating the byte offset for standard + EGA/VGA modes (Volker) + - byte offset for modeX fixed (use value of CRT register 0x13) (Volker) + - text mode memory mappings 0 and 1 support (Christophe) + - fix bug [ 612741 ] VBE mem conflicts w/ local APIC address (Jeroen) + - fix bug #635223: VGA tiles array access out of bounds (Bryce) + +- ROM BIOS + - improve compile process. Now bioses for 1, 2, 4 and 8 processors + are built at the same time (Bryce) + - fixes to be able to compile the bios with gcc2 or gcc3 (Jeroen and + Christophe) + - changes on boot signature check (Christophe): + - never done for cdroms + - always done for hard-disks + - conditional for floppies + - add keyboard int16 functions 0x09 (get keyboard functionality) and + 0x0a (get keyboard id) (Christophe) + - fix bug [ 629810 ] int 16/ah=01 broken? Enable interrupt on entering + int16 handler (Christophe) + - new keyboard init in POST (patch from Adam Sulmicki) + - flush input and output keyboard buffer before keyboard self test + (Volker and Christophe) + - fix bug [ 547603 ] kbd up/down arrows in dos install (Christophe) + - fix bug [ 549815 ] bios wrongly loads CS,ES. CS and ES are set to 0 + before the bootloader code is called. (Christophe) + - PCI functions support (Volker) : + - BIOS32 service directory + - real mode PCI int1a functions + - protected mode PCI int1a functions + - fix reset for MS-DOS and Win95 (Volker) + - 360K floppy support (Volker) + - enhanced ata/atapi support (Christophe) : + - 4 channels / 8 devices + - device auto detection (with help from Adam Sulmicki) + - EDD3.0 + - 32bits device access + - optional disk translation "large", "r-echs" or "lba" (up to 8.4GiB) + - re-enable harddisk controller interrupt after reads/writes. + Win95 can now use native access to harddisks and cdroms. (Volker) + - shutdown status handling (cmos index 0x0f) (Christophe) : + - fix bug [ 601166 ] CMOS Problem @ "0x0F Index 0x05 data". After reset + execution will resume by a jump to [0x40:0x67] if the shutdown status + is 5 + - the bios don't panic any more if the shutdown status is 9 + - two parallel ports detection in POST (Volker) + - two serial ports detection in POST (Volker) + - add int15 extended memory function 0xe820 (patch from osmaker) and + 0xe801 (patch from Hartmut Birr) + - fix return values on some int15 functions (Bryce) + - fix int70 handler overlapping int08 handler (Christophe) + - simplify 8 processors BIOS for operating systems which don't do + paranoia/sanity checks (Zwane) + +- configuration interface + - wxWindows config interface now allows you to change every bochsrc + option using menus and dialog boxes. There is also the beginning of + a wxWindows graphical debugger, but it needs a lot of work before it + will be useful. + - renamed control.cc to textconfig.cc. Now we're calling it a + text configuration interface, instead of a control panel. + +- display libraries + - Even though we've had them for years, the term "display library" is new in + release 2.0. In the gui directory, Bochs has a number of different C++ + files which you can select to display the text and graphics on the + simulated monitor. Each of these is a display library. The display + libraries are: + x use X windows interface, cross platform + win32 use native win32 libraries + carbon use Carbon library (for MacOS X) + beos use native BeOS libraries + macintosh use MacOS pre-10 + amigaos use native AmigaOS libraries + sdl use SDL library, cross platform + svga use SVGALIB library for Linux, allows graphics without X + term text only, uses curses/ncurses library, cross platform + rfb provides an interface to AT&T's VNC viewer, cross platform + wx use wxWindows library, cross platform + nogui no display at all + - it is now possible to compile Bochs with support for many different + display libraries and select the one to use at runtime (even without + plugins). See the display_library directive in .bochsrc. + - add new svgalib display library by Igor Popik + - fix bug [ 614724 ] SDL can get stuck in full screen mode + display libraries such as SDL which have a full screen mode can be + dangerous, if Bochs does not switch back to normal display mode at + the right time. This is fixed for SDL and the new svga. + - keymap support added in SDL interface (Bryce, Volker) + - new keymap files: SDL keymaps for US and DE keyboards, X11 keymap + for Danish keyboard. + - use keyboard mapping for keyup messages too + - renamed almost all references to data type "Boolean" to "bx_bool". + The Boolean data type was defined in Carbon.h headers, and conflicted + with Bochs's definition. See bug [ 618388 ] Unable to boot under MacOS X + Exceptions: When talking to the Carbon library, you must use Boolean. + Also, siminterface.h uses standard "bool" instead of bx_bool. + - "User" button added in toolbar. It can send keyboard shortcuts to + the guest OS. (Volker) + - snapshot improvement and memory leak fixed (Volker) + - testing framework, based on comparing screen content, added (Greg) + - term display library: + - support for color terminal, function keys, clear screen (Volker) + - solaris compilation problem (bug #613393) fixed (Bryce) + - win32 display library: + - use native win32 toolbar for headerbar, use system palette (Volker) + - many Carbon interface improvements: + - patch [ 549248 ] Fix Carbon key & menu handling (Chris Thomas) + - partial keymap support, copy&paste, menu items fixed, new toolbar + behavior, dialog box display for panics (Br'fin) + - sdl display library: + - keyboard fixes and key mapping added (Bryce) + - when captured, the mouse is forced to stay in the window. fix bug + [ 619283 ] SDL: os mouse pointer leaves the window (Bryce) + - x display library: + - bug [ #537593 ] vga font not being found fixed. If vga font not + found, search for a font called "-*-vga-*" (Bryce) + - keyboard problems fixed (Bryce, Christophe) + - beos platform, any display library: add a nice icon to the executable + (Bernd Korz) + +- wxWindows + - wxWindows is a cross-platform C++ user interface library which you can + download for free at http://wxwindows.org. wxWindows provides C++ + classes for all sorts of GUI controls (buttons, menubars, etc.) and + implements all of them using the native controls on the platform. + - The new wxWindows port of Bochs provides both a graphical configuration + interface (for editing bochsrc options) and a display. It was + written by Bryce Denney, Don Becker, Dave Poirier, and Volker Ruppert. + - In release 2.0, we concentrated on making the wxWindows port as stable + and functional as the other interfaces. wxWindows provides a great + toolbox that we can use to make Bochs easier to learn and use. + - wxWindows supports charmap changes, keyboard mapping, cut and paste, + text and graphics modes, text mode cursor size, and mouse (Volker, Bryce) + - To compile Bochs with wxWindows, you should install wxWindows 2.3.3 + or later. Then configure Bochs with --with-wx. + - if you have multiple versions of wxWindows installed (e.g. a debug + and a release version), you can set $WX_CONFIG before configuring + to select between them. (Bryce) + +- Bochs debugger + - [ 609616 ] remote GDB stub + add GDB Stub support from Johan Rydberg, with bug fixes by Stu Grossman + - add hooks for external debugger for win32. The external debugger + that connects to Bochs is distributed in + build/win32/tattam-external-debugger.zip in binary form. Turn on + with --enable-external-debugger. (Peter) + - add "debugger_log" option to bochsrc, which logs all debug output + into a file. feature [ 629068 ] (Christophe) + - debugger is now usable in fullscreen SDL and SVGA guis. It will + switch back to text mode for each debug prompt (Bryce) + - disassembly output cleaned up and improved + (Kernel Panic, Peter Tattam, Jonathan Shapiro, Luiz Henrique Shigunov) + - fix [ 628806 ] debug: x/c prints unprintable chars (Bryce) + - add the beginnings of a wxWindows debugger. Not ready for mainstream use + yet. CPU register display is implemented, and you can type any debugger + command you want into the Debug Console window. (Bryce) + - add help command (Alexander Krisak) + - symbol table lookups cleaned up a bit (Bryce) + - displays the address of the caught watchpoint, feature #435271 (Dave) + - remove obsolete "loader" + +- utilities + - fixed bug [ 487758 ] bximage fails on file creation >2048meg + Bximage should now work up to 32gig. (Bryce) + - on win32, both bximage and niclist now ask the user to press + return before exiting, so that you have time to read the results + before the window disappears. (Bryce) + +------------------------------------------------------------------------- +Changes in 1.4.1 (June 22, 2002): + +- now cdrom is enabled in configure, unless you specifically disable + it with --disable-cdrom. (Christophe) +- fix compile error in main.cc when SMP or APIC is enabled (Dave) +- the runtime menu now displays 11 (continue) by default (Bryce) +- initialize DMA controller before floppy and SB16 +- fix DMA panic when installing win95 (Volker) +- first character of the vga bitmap is blank on win32 (Volker) + Before, it was incorrectly coded as a '@'. +- AltGr key on European keyboards works now on win32 (Volker) +- fix problem with console/serial port on Bochs exit (Volker) +- enable serial port for GNU and GNU/Linux (Volker) +- small documentation fixes (Volker) +- remove unnecessary include statements for X11 (Volker) +- italian keymap added (Emanuele Goldoni) +- fix win32 ethernet frames error. It will no longer reject packets + that are less than 60 bytes long. (Peter Tattam) +- BIOS fixes : + - win2k cd-boot (Christophe) + - emm386 crash (Dave) + - cs=0 at boot time (Christophe) + - keyboard failure in scandisk (Dave) +- fix bug in forming the 64-bit APIC base address from two 32-bit registers. + A compiler warning in cpu/proc_ctrl.cc pointed this out. +- fix default choice in the runtime options menu + +------------------------------------------------------------------------- +Changes in 1.4 (March 27, 2002): +- ROM BIOS + - Boot from CDROM! Christophe Bothamy added partial El Torito support in + rombios.c, which allows Bochs to boot cdroms. Booting from win2k or winXP + cdrom is not supported yet. The default BIOS includes El Torito functions. + the boot line must say "cdrom". + Example: + boot: cdrom + - implementation of int13 diskette function 5 (format track) (Volker) + - initialisation of PIC and DMA-2 added to POST code (Volker) +- configure script (Bryce Denney) + - the configure script now detects your platform and provides a default + GUI and the required compiler flags. All supported platforms should + compile with simply "configure" and "make". + - default guis by platform: + - win32/windows/cygwin: win32 gui + - MacOS X: carbon gui + - MacOS 9 or earlier: macos gui + - BeOS: beos gui + - AmigaOS: amigaos gui + - all other platforms: X windows gui + - compile arguments supplied by configure script + - win32: too many to list here; see documentation + - cygwin: -mno-cygwin -DWIN32 + - MacOS X: -fpascal-strings -fno-common -arch ppc -Wno-four-char-constants + -Wno-unknown-pragmas -Dmacintosh + - the --with-GUINAME configure option is only needed if you want to override + the default GUI. This is the only way to use the Term, RFB, and SDL + GUIs. +- VGA + - added VESA BIOS Extensions code by Jeroen Janssen (banked mode only, LFB + support in patches) + - vga memory read/write functions in text mode fixed + - implementation of CGA mode 320*200*4 (patch from Sebastien Bechet) +- VGA BIOS + - updated Christophe Bothamy's LGPL VGA BIOS to version 0.3a. This consists + of a bug fix for function ah=13 and VBE support by Jeroen Janssen. +- networking + - chipmem read/write limit fixed (Mike Lerwill) + - writing a byte in 16-bit mode now possible (Mike Lerwill) + - new ethertap interface for Linux, which allows Bochs to talk to + the local machine and the internet (Bryce Denney) + - NE2000 is now enabled by default on Win32, Cygwin, and Linux compiles + in the .conf.* scripts and release binaries. + - fix check for auto transmit disable, which was checking the wrong bit + (Peter Tattam) + - Win32 only + - niclist.exe has been revised to work on more Windows versions, and it + suggests a usable ne2k line (Dean Payne) + - fix timeout setting so that ne2000 does not slow down the whole + simulation (Don Becker) + - bug fix: be able to handle multiple packets that arrive at once + (Mike Lerwill) +- GUI changes + - cdrom button: click this to notify Bochs when you changed the CDROM (Volker) + - snapshot button: saves the text on the Bochs screen into a file called + snapshot.txt (Volker) + - copy button: on Win32 and X windows, copy the text on the Bochs screen + to the clipboard (Volker) + - paste button: on Win32 and X windows, paste the characters on the + clipboard into the Bochs window. This requires keyboard_mapping to + be enabled. (Bryce Denney) + - improved text mode cursor for Win32, X11, and SDL (Volker) + - new SDL interface (Dave Poirier, debugging by Christophe, Volker, Bryce) + SDL is a graphics library that has works on many platforms. This interface + is experimental, and is missing a few features of the standard Bochs + interfaces: extended keys (arrows, keypad). + - MacOS X: add MacOS X carbonized event handlers by Jeremy Parsons + - X windows: when not enough colors can be allocated, force use of + private colormap (Bryce Denney) + - bug #490570 fixed: OUTB set and command 0xaa encountered (Dave Poirier) +- keyboard + - completed keyboard emulation with the implementation of the three scancodes + sets (mf1, mf2, mf3) with or without translation. This is based on Ludovic + Lange's plex86 keyboard patch. (Christophe Bothamy) + - added a "keyboard_type" option, that defines the answer to an "identify + keybord" request to the keyboard controller. The available values are + "xt","at","mf". (Christophe Bothamy) + - added an optional keyboard_mapping option that enables to use your + country specific keyboard with Bochs. If enabled, the keymap file must be + specified in bochsrc. The available keymaps are US, German, Spanish and + French for PCs running X11. Contributions are welcomed. (Christophe + Bothamy) + - added Windows(tm) key definitions (Volker Ruppert) + - added paste button, which causes the emulated keyboard to type characters + from the system clipboard. This only works when keyboard_mapping is + enabled. (Bryce Denney) +- cdrom + - bug fix: win32 could not read a cdrom image file + - eject cd support for linux (patch from Petr Stehlik) + - BeOS fixes + - changing cdrom media is possible now with CDROM button +- sound blaster(tm) emulation (Volker) + - you can use --enable-sb16=freebsd now + - 16-bit DMA controller added + - 16-bit mode of the SB16 implemented (output to file works) +- floppy drive (Volker Ruppert) + - implementation of the floppy command 'format track' + - implementation of read / write operations with MT=0 + - behaviour of a few floppy commands fixed + - floppy reset behaviour fixed + - lots of other fixes +- fixed bug [ #468340 ] pic:slave: OCW3 not implemented. Now the slave PIC + supports all the modes that the master PIC does, and nobody will see this + message again. +- serial port (by Volker Ruppert unless noted) + - improved IRQ handling + - now Windows 95 serial driver works correctly + - fixed the return value of the MCR (loopback bit) + - interrupt reasons LSR change and MSR change implemented + - the number of data bits is considered when sending data + - all serial port changes are tested in loopback mode only + - serial port emulation fixed for FreeBSD and OpenBSD (Stu Grossman) + - fix receiver poll frequency so that it doesn't slow emulation (Stu Grossman) +- Bochs debugger + - when tracing, print the instruction just before it is executed, instead + of just after (Greg Alexander) + - after a triple-fault panic, you can now return to the debugger +- symmetric multiprocessor (SMP) simulation + - no more panic if you read the EOI register + - fixed default destination format in local APIC + - fix SMP instruction tracing in bochs debugger + - fix deadlock when debugger enabled and all processors HLT + - MSR support added by Zwane Mwaikambo +- simulation of interrupts is more accurate (Volker) + - implemented edge triggered interrupt mode + - added functions raise_irq() and lower_irq() +- programmable interrupt timer (Greg Alexander) + - fixed the PIT gate and improved the PIT printing options + - experimental real-time PIT +- parallel port improvements (Volker Ruppert) +- bug fix: hard disk errors caused by overflowing imul in the BIOS code. + Sebastian Bechet and Peter Tattam tracked it down and fixed it. +- fix some memory leaks (patch from Darko Tominac) +- Double-Word IO is supported for ATA devices +- fix bash-specific syntax in install-x11-fonts script +- print stack_return_from_v86 error only the first 100 times + +------------------------------------------------------------------------- +Changes in 1.3 (December 10, 2001): +- networking works on Windows and Linux platforms +- emulated cdrom can now read from ISO image files, on any platform. +- new PIT model by Greg Alexander which is much more complete than the + old one. The new PIT is used by default, but you can switch back to + the old one if you configure with --disable-new-pit. + (PIT = 8254 programmable interrupt timer) +- new configuration menus by Bryce Denney, which allow you to change any + bochsrc option using text menus, then save the configuration into + a new bochsrc file for later use. You can disable the new code using + configure --disable-control-panel. Also you can use the command + line arguments -nocp or -nocontrolpanel. Also, there is a new + "Config" button on the GUI that allows limited changes to the + configuration at runtime, such as changing the floppy disk. +- add docbook documentation directory in the sources under doc/docbook. + The transition from HTML to docbook documentation is still in progress. +- Add new log action "ask", as shown in these example bochsrc lines: + panic: action=ask + error: action=ask + When an event occurs which is set to "ask", you get a beep and message + on the text terminal that asks what you want to do. Choices are: continue, + continue and disable future messages from this device, quit immediately, + or segfault (where abort() function is available). If compiled with + --enable-debugger, you can also choose to enter the debugger. +- Parallel port emulation cleaned up by Volker Ruppert. See .bochsrc for + syntax of new parport1 line in bochsrc. +- PCI support improved by Volker Ruppert, including BIOS changes. Still + not complete. +- floppy controller returns a proper error response if you try to write + a read-only disk image. For systems such as DOS that actually use the BIOS + services, it was also necessary to add code in int13_diskette_function to + recognize a write-protected error and return the correct error status code + (AH=3, Carry Set). +- the ROM BIOS now prints panic messages to the console. Thanks to Cliff + Hones for his console display code. +- the ROM BIOS detects nonbootable disks (Barry Allard), and prints a message + on the console. Barry Allard's patch who helped with checking the boot + signature. +- LBA support added for hard disks. (Not tested very much.) +- add dependencies to makefiles +- logging code moved into a separate file, logio.cc +- new option --enable-slowdown-timer, by Greg Alexander, which kicks in if + Bochs simulation time starts to run faster than real time. This helps to + keep the Bochs clock in sync with the real clock when the CPU is mostly + idle. +- new option --enable-iodebug, by Dave Poirier, which creates an I/O + interface to the debugger. This lets you write software to be emulated + in Bochs which can turn on instruction, register, or memory tracing + using I/O accesses. +- improved detection of readline in configure script +- configure substitutes the version number into many files, instead of + using sed in the makefile. There are still a few uses of sed remaining. +- you can now use environment variables in bochsrc values. For example, + diskd: file="$BOCHS_IMG/diskd.img", cyl=615, heads=6, spt=17 +- configure with --prefix=PATH works now +- running configure from a different directory works now, thanks to + a patch from Edouard G. Parmelan +- fix [ #433759 ] virtual address checks can overflow. + > Bochs has been crashing in some cases when you try to access data which + > overlaps the segment limit, when the segment limit is near the 32-bit + > boundary. The example that came up a few times is reading/writing 4 bytes + > starting at 0xffffffff when the segment limit was 0xffffffff. The + > condition used to compare offset+length-1 with the limit, but + > offset+length-1 was overflowing so the comparison went wrong. +- cmpxchg8b patch from Michael Hohmuth +- apply patch from Thomas Fitzsimmons to fix compile + problems when BX_SUPPORT_PAGING and BX_USE_TLB are turned off +- fix bug introduced in 1.2.1 which caused spurious exceptions. + See patch #439314, Exception 1 (debug) on HALT, from + thomas.petazzoni@meridon.com. +- add panic in ctrl_xfer32.cc where the IRET32 implementation is broken. + This only happens if you are NOT in vm8086 mode or protected mode. + The intent is to warn people when they are getting bad emulation, and + encourage people to report how they got to that point. +- apply patch from Santiago Bazerque. See this bug report: + [ #463018 ] retf not removing parameters sometimes +- fix bug [ #461730 ] IRETD causes problems if NT-flag is set + reported by Peter Lammich. +- apply patch [ #455014 ] CR0 bug in 80486, described as: + > In the register CR0, when the bit PM is enabled, the bit 4 is 0 + > when should be 1. +- apply patch from Mike Rieker associated with this bug + report: [ #480422 ] gdt 'accessed' bit +- in task_switch when it tried to ensure that the old TSS was paged in, + it actually used the new TSS address, fixed. +- updated the instrumentation code, and added a working example. To try + it, configure --enable-instrumentation=instrument/example1. Then when + you run bochs, you will get one line for each instruction PC and for + each I/O access in a new file called bxevent.txt. +- set a bit in the CMOS that says the processor has an FPU. This is + from patch [ #455006 ] Device byte is not initialized aptly. + Author did not leave their name. +- add logging code to the "null ethernet" which does not require host OS + support. All this does is print the outgoing packets from the guest OS. +- cleanup of log functions (Todd Fries) +- add BX_ERROR for every command in ATAPI-6 that bochs does not support. + I still need to do add some commands from older specs that are obsolete + (and not listed) in ATAPI-6. Commands that aren't in the spec will still + panic. +- only put 0xf into the 2nd hard disk field when the cdrom is not present. + This is a patch from Volker Ruppert , who + comments: "The fdisk command reports an unusable second harddisk if the cdrom + is enabled. This patch helps, but I don't know if it is the right way." +- make hard disk code return error codes when data is not available instead + of just panicing. In particular, if the logical sector is out of bounds + or the disk image cannot be read/written at the desired offset, we now + abort the ATA command and return an error code. Many of the old BX_PANIC + messages are turned to BX_ERROR, so they will still appear in the + log, but now the device model will try to communicate this fact to + the OS instead of simply giving up. +- don't blindly reject odd length atapi commands. There are cases when + it's really ok according to ATA-4. +- for big endian machines, reversed the bit fields in interrupt_reason. + This was pointed out by Nicholai Benalal. +- extended keyboard improvements by Dave Poirier +- major mouse patch from Dave Spring, that implements several missing + mouse modes. +- commit keyboard patch from David Haslam + posted to mailing list, that addresses the problem of each key press + printing ^@. See cvs log for details. +- mouse performance fixes by Greg Alexander and Robb Main +- NE2000 fixes by Frode Vatvedt Fjeld, ecelca@yahoo.com, Greg Alexander, + and angelos@openbsd.org. +- fix bug [ #468340 ] pic:slave: OCW3 not implemented. Some event handling + code appeared in the master pic but not the slave pic. +- fix compile problems in SB16 code, related to fpos_t being treated as + an integer. +- patch from Volker Ruppert to fix + midi output file so that winamp can play it. +- some cleanup of serial code by Todd Fries and Volker Ruppert, but it + doesn't work yet. + +X Windows specific: +- commit patch from David Haslam + [ #455763 ] Cursor trail with DOS Edit/Minix vi +- error for missing fonts now points to the documentation +- new option --enable-idle-hack, by Roland Mainz, which makes Bochs more + friendly toward other processes when its CPU is idle. Presently, + this option is specific to X windows. + +Win32 specific: +- now Windows 95/98/ME can read the physical cdrom (Don Becker) +- The default configuration for Win32 VC++, given in .conf.win32-vcpp, + now enables the NE2000 and renames all .cc files to .cpp. This keeps VC++ + happy but may make it hard to use CVS. +- The default configuration for Cygwin, given in .conf.win32-cygwin, now + enables cdrom and SB16. +- See "new docs" on the web site for compile instructions for VC++ and Cygwin. +- The sources include a VC++ workspace, in addition to the old "nmake" + makefile. +- ethernet support (emulated NE2000 card), coded by Don Becker. This + implementation requires a library called WinPCap, which you can + download from http://netgroup-serv.polito.it/winpcap. +- new utility called niclist.exe which lists the ID number of all your network + cards (well probably you just have one). The ID be used when setting up your + .bochsrc. +- patch [ #466403 ] make text colors more accurate. The author did not leave + his/her name. +- fix GUI bug [ #452159 ] win32: mouse stuck if bochs win partly off screen + Now we center the mouse periodically, whether or not the mouse has + wandered outside of the window or not. +- event handler recognizes the extended keycode flag +- fixes for raw floppy and floppy disk images (Don Becker) + +Linux specific: +- Ethernet (emulated NE2000 card) now works in Linux! Contributed by + splite@purdue.edu. This has been tested using host OS kernel 2.2.14, and + works with telnet, ftp, irc, lynx, etc. Because it is a packet filter + solution, you aren't able to talk to the host machine, only to other + machines on the network. +- The default configuration for Linux, given in .conf.linux, now enables + the NE2000 model. +- RPM build process configures with --prefix=/usr so that everything is + installed in /usr/bochs instead of /usr/local/bochs. +- DLX Linux disk image is now installed so that only root can write it, to + avoid security problems. When you run the bochs-dlx script, it creates a + local copy in your home directory and then runs it. +- code that determines the capacity of a cdrom now works for both ATAPI + and SCSI drives (splite@purdue.edu) +- applied patch from bochs@sigint.cs.purdue.edu. The comments are: + > The Linux 2.4.5 CD-ROM driver sends a READ_DISC_INFO command which caused + > an "unrecognized ATAPI command" panic. Looks like READ_DISC_INFO is only + > recognized by CD-R and CD-RW drives, so I ignore it for now. + +Amiga MorphOS specific: +- Bochs now compiles and works on Amiga MorphOS. Configure with + --with-amigaos. For AmigaOS only, see .bochsrc for use of fullscreeen and + screenmode options. The Amiga MorphsOS is written and maintained by + Nicholai Benalal . +- raw cdrom supported if you configure with --enable-cdrom + +BeOS specific: +- Bochs compiles and works on BeOS. Configure with --with-beos. + Bernd Thorsten Korz maintains the BeOS port. +- raw cdrom supported if you configure with --enable-cdrom + +MacOS X specific: +- Bochs now compiles and works on MacOS X. Configure with --with-carbon. + Emmanuel Mailliard ported the Macintosh code to the + Carbon API. +- The MacOS X application is built using (gasp) mkdir, copy, and rez. + Surely this is not the right way, but it works. +- raw cdrom supported if you configure with --enable-cdrom + +RFB mode: +- apply patch.rfb-mouse by MURANAKA Masaki (monaka@users.sf.net) + see this source forge bug [ #457968 ] Strange mouse motion on RFB +- add a retry loop in RFB code, so that if port 5900 is not available + it can try 5901, etc. + +Bochs Debugger: +- do a vga update whenever you print a debugger prompt. +- added debugger command "info fpu" that prints the FPU registers. If you + do "info all" you get cpu and fpu registers. +- added debugger command "info ne2k" which prints all the registers + of the NE2000 model +- add ability to do register tracing and flag tracing (Dave Poirier). + Try the trace-reg-on and trace-reg-off commands. +- instruction trace now includes time ticks +- fixed problems in which bochs compiled with debugger measured time + differently from bochs compiled without debugger. Also when instruction + trace was enabled, breakpoints and control-C did not work. Also, + breakpoints at the beginning of an interrupt handler did not work. + +------------------------------------------------------------------------- +Changes in 1.2.1 (June 12, 2001): +- more work on makefile for building RPMs +- [ #432382 ] build debian packages patch + add build/debian directory from Rob Lemley + which allows us to make Debian packages! +- optimize for speed when simulating one processor. Now 1-processor + performance should be equivalent to 1.1.2. +- [ #425640 ] sb16 assumes fpos_t is long int + This fixes compiles of iodev/sb16.cc on linux systems with newer libraries + in which fpos_t is not an integer. +- [ #432488 ] SMP:assert "n_logfn < MAX_LOGFNS" fails + increase MAX_LOGFNS since we ran out of them on an SMP simulation with + 4 processors +- changes to compile clean on cygwin: + - don't use the WIN32 snprintf define for cygwin + - add ssize_t definition for cygwin + - only compile "struct timeval tval" if select is available + on that platform. +- [ #432491 ] SMP: CPUID says no APIC feature + clean up inconsistent use of BX_SUPPORT_APIC and BX_APIC_SUPPORT, which + caused the CPUID to report no APIC was present +- [ #431025 ] --enable-external-device-models broken + removed configure options for external-device-models and + external-cpu-memory. These don't work and aren't going to be fixed. +- [ #429448 ] configure: -lreadline when not there + Now configure allows you to choose not to use readline, even if it's found + on your system. +- [ #428915 ] apply extended keyboard patch + extended keyboard patch by Dave Poirier +- [ #428626 ] if no X11 found, configure&make fails + Now configure halts if X windows is selected but no X libraries are found. +- updated rombios to version 1.13. This fixes several problems: + - [ #430472 ] DOS HIMEM "A20 line" error + This problem was apparantly caused when Bryce added a function that prints + the BIOS version, and he called it too early in the boot process. Now the + same function is called later, and it doesn't break the A20. + - [ #431010 ] SMP structure overwritten in v1.2 + SMP structures were getting overwritten by BCC-generated data, + preventing SMP operating systems from detecting that other processors + were available. + - [ #431016 ] bios: SMP struct has wrong entry count + SMP structure had the wrong entry counts +- very minor doc updates (typos, replace broken link to mtools info) +- quit when the user clicks the power button, even if they have disabled + panics. +- win32 now defaults to having mouse capture mode turned off. For new users, + it would be distressing for their mouse cursor to disappear until they + pressed F12. +- [ #428222 ] vga font not installed + added script called "install-x11-fonts" which should help people install + the VGA font on X windows systems, if it isn't already there. + +------------------------------------------------------------------------- +Changes in 1.2 (June 3, 2001): +- [ #427259 ] rombios HALT calls don't print + Fixed bios/rombios.c HALT macro so that it writes the line number of the + panic to the PANIC_PORT (port 0x400) and then does NOT do a halt + instruction. Also changed iodev/unmapped.cc so that the line number written + to PANIC_PORT is displayed as a BX_PANIC message. Because the HALT + macro now triggers the normal panic behavior, it can be controlled by + the bochsrc. +- [ #429016 ] crash if no hard drive + rombios used to call HALT macro if no hard drive was found. Now it only + calls HALT if a hard drive has an illegal geometry. +- [ #425388 ] include source for simple disk img tool + [ #428478 ] mkimg tool creates image 1 byte too big + Added bximage tool, which makes empty floppy and hard disk images. + It is now included in the top level Makefile, so it will get built + by default on all platforms. +- [ #426036 ] eth_fbsd.cc compile problem on solaris26 + added configure test so that "configure --enable-ne2000" only + includes the Berkeley Packet Filter code (eth_fbsd) if the header + file can be found. If you don't have BPF the ne2000 + will not actually move packets, but at least it will compile clean now. +- [ #428214 ] 1.2.pre1 need documentation for binaries + Write windows and linux specific documentation to be installed in + binary releases. +- [ #429258 ] disable RESET for version 1.2 + Since soft reset was not completely working, I reverted the reset patch. + Now it does panics on reset instead of trying to reboot, as the old + bochs versions did. +- [ #428222 ] Should the linux RPM install vga font? + now font/vga.pcf will be installed in the RPM package +- [ #429020 ] stop renaming the BIOS!!! + new BIOS changes are now in BIOS-bochs-latest, instead of a BIOS + whose name changes every time we change anything! To help distinguish + different BIOS versions, the BIOS now prints its RCS Id into the + log file. +- [ #428625 ] compile problem if SHOW_IPS is on + removed extra paren that broke SHOW_IPS +- [ #428219 ] PCI doesn't compile with SMF=1 +- [ #429375 ] pthreads detection broken +- [ #429073 ] configure: if no X11, makes bad config +- [ #429229 ] install current .bochsrc in binary rels +- install Tim's man pages on linux RPM +- BIOS prints messages in log in case of boot failure +- rewrote instructions for compiling in win32 (win32.txt) +- fixed link in HTML changelog.html to point to the real sources on SF. +- added missing LOG_THIS definition to gui/nogui.cc and gui/rfb.cc +- added additional check for null pointer in debugger exit routine +- added diskd to .bochsrc + +------------------------------------------------------------------------- +Changes in version 1.2-pre1 (May 25, 2001): +- major cleanup of .bochsrc +- major cleanup of stderr output: prints bochs version information when + starting, and at the end it tries to print the message that caused + bochs to quit. +- two hard disk support (diskd). At present, you cannot have two + hard drives and a cdrom at the same time, because there is only + one IDE controller with two channels. +- split hard disk support allows different partitions to be stored in + different image files +- two new GUI choices: term mode and RFB mode. Term is a text-only + interface, and RFB creates a server that can be accessed using + the AT&T VNC viewer. +- now Bochs can simulate an SMP machine, if you configure with + --enable-processors=N. Configuring more than one processor has + a major performance impact, so the default is 1 processor. + See SMP documentation for more details. +- to make SMP work, bx_mem and bx_cpu have been replaced with + bx_mem_array[] and bx_cpu_array[]. The cpus are referenced through + the BX_CPU(n) macro and memories through the BX_MEM(n). Normal + mode has one cpu and one memory, SMP mode has multiple cpu's and + one memory, cosimulation mode has multiple cpus and multiple memories. +- use --enable-cpu-level=6 to make Bochs claim to be a Pentium Pro. + The only feature that requires CPU level 6 is apic support. +- new logging system by Todd Fries, which has 4 levels of event + severity (panic, error, info, debug). There are new .bochsrc + options that control what action to take when a + panic/error/info/debug event occurs. +- now searches for .bochsrc, bochsrc, bochsrc.txt, and (on unix only) + $HOME/.bochsrc. +- use GNU readline library if --enable-debugger is on, as long as readline + can be found on the machine +- configure checks for existence strtoull and strtouq. if neither exists, + Bochs uses its own implementation +- applied patches from Cliff Hones to fix up the + rombios. This includes many improvements, which you can list by + doing "cvs log -r 1.6 bios/rombios.c" or looking at cvsweb. +- added suggested geometries of larger disks to the documentation +- this is the first release to have official binary packages for win32 + and Linux. There is a new "make rpm" in the top-level Makefile which + will create an RPM of the current bochs directory. To use this, + become root and type "configure; make rpm". +- applied some FreeBSD patches from Maxim Sobolev (cdrom and serial). + +------------------------------------------------------------------------- +Changes in version 1.1.2 (bugfix3, May 16, 2001): +- updated Elpin VGA BIOS to version 2.40, and changed pointer in .bochsrc +- fixed .conf.x86 script so that it uses c++ instead of egcs for C++ files +- now Makefile targets that recurse into subdirectories use double colons, + so that it will always recurse into subdirectories. Now a single make + command should notice a modified source file in a subdir. +- fixed bug in bx_panic. If BX_PANIC_IS_FATAL==0 and a (non-fatal) panic + occurs, it used to call bx_atexit() and then return. It should never + call bx_atexit, which starts to shut down the simulator, unless it's + really going to quit! +- support 2.88 MB floppy disks +- since dataseghack is checked in as non-executable, invoke it with + "csh dataseghack" +- double fault patch from Thomas Petazzoni , + sourceforge patch #423726. +- removed -fno-builtin from fpu makefiles +- redefine u_char, u_short, etc. in order to not conflict with system + definitions of these same types. +- in cdrom.cc, remove the extern "C" { } structure around some of the + header files. This is no longer necessary. +- do not panic on hard disk command 0x1f2 (read sector count) +- in keyboard.cc: + - apply Todd Fries' reset patch + - recognize most of the "Grey" insert/delete/home/end/etc. keys the + same as keypad keys. + - removed panic on "kbd_ctrl_to_kbd(): got value of 0x??" + - implement mouse command 0xf6 (set defaults) +- apply Suboner@aol.com's Xwindows timing patch from + http://sourceforge.net/tracker/index.php?func=detail&aid=418730&group_id=12580&atid=312580 +- remove all patches from patches subdir which have already been applied. + The remaining ones are under consideration but not applied. + +------------------------------------------------------------------------- +Changes in version 1.1.1 (bugfix2, April 9, 2001): +- in soundwin.cc, arg 3 should be typecast to LPWAVEFORMATEX +- in fpu_entry.c, Bryce mistyped his own initials! +- in configure.in and configure, define good defaults for VC++ + #define BX_64BIT_CONSTANTS_USE_LL 0 + #define inline __inline + #define BX_NO_EMPTY_STRUCTS 1 + #define BX_NO_ATTRIBUTES 1 + #define BX_HAVE_HASH_MAP 0 +- in config.h.in, fixed typo in #error message + +------------------------------------------------------------------------- +Changes in version 1.1 (bugfix1, April 6, 2001): + +(FIXED, patch #414360: update copyrights) +update headers. Change copyright to 2001, replace config.h.in header with +the standard mandrake header that every other file uses. + +(FIXED, patch #414356: inlines) +make macro to replace inline and static/extern keywords. Then make +define the macro appropriately based on configure. + +(FIXED: patch #414234: macos-no-strdup) +--with-macos should force HAVE_STRDUP=0. + +(FIXED, patch #403027: Fix mouse bugs in Linux and BSD) +Linux and BSD (maybe others) cause panic in mouse code. + +(FIXED, patch #413851: const64bit patch) +VC++ does not allow "LL" after 64-bit constant. + +(FIXED, patch #413859: fabs symbol conflict) +fpu code contains fabs, which conflicts with math library fabs. + +(FIXED, patch #403004: Implement aborts on a few SET FEATURE commands...) +Implement aborts on a few SET FEATURE commands for ATA. + +(FIXED, patch #402991: Update to iodev/vga.cc to add 3c3h read support) +Implement VGA enable register, at 0x3c3. + +(FIXED, patch #403027: Fix mouse bugs in Linux and BSD) +Mouse panic in linux/BSD: +KBD: io write 0x64: command = 0xD3(write mouse outb) + +(FIXED, patch #414229: panic-is-fatal) +Allow user to decide if panic is fatal, or just a warning + +(FIXED, patch #414230: sun-cdrom) +Support Sun CDROM + +(FIXED, patch #413574: portable1) +there are cases where a pointer is cast to a 32-bit int, +then later cast to a pointer and dereferenced, which crashes any 64-bit +machine. + +(FIXED, patch #413574: portable1) +some machines have no snprintf or strtoull. include a replacement function +when needed. + +(FIXED, patch #413574: portable1) +Some compilers don't allow "typedef struct { } foo;" + +(FIXED, patch #413574: portable1) +Some people don't have hash_map.h, used in dbg_main.cc. Disable this code +if hash_map.h not found. + +(FIXED, patch #413574: portable1) +Some compilers can't handle labels at the end of a block, as in + void main () { /*code*/ label: } + +(FIXED, patch #413574: portable1) +Most compilers can't handle __attribute__. Use macro to define it away. + +(FIXED, patch #413574: portable1) +if --enable-debugger, turn on --enable-disasm too. + +(FIXED, patch #413574: portable1) +ome compilers can't handle any chars after an #endif + +(FIXED, patch #413574: portable1) +wrong type arg1 of bx_dbg_watch and bx_dbg_unwatch. The code in +lexer.l was calling it with integers (not booleans) + +(FIXED, patch #413574: portable1) +in fpu code, "setcc" macro was implemented with braces inside parens, +which some compilers don't understand. + +(FIXED, patch #413574: portable1) +in fpu_entry.c, FPU_load_int32 was consistently called with arg1 of +type (s32 *), but should be (u32 *) + +(FIXED, patch #413574: portable1) +comment out sigcontext structure in fpu/stubs/asm/sigcontext.h because +it conflicted with sigcontext of other machines. This struct was never +used by bochs anyway. + +(FIXED, patch #414046: portable2) +move definition of missing library functions into osdep.h and osdep.cc, +include contents of macutils*. + +(FIXED, patch #414061: win32-rawcd) +CDROM drive letter for WIN32 should not be hardcoded. + +(FIXED, patch #414060: win32-rawfloppy) +Bypass fstat when opening WIN32 raw floppy disk. + +(FIXED, patch #414226: pit-panic) +WinME install dies with panic: +bochs: panic, pit: outp(43h): comm Bh, mode 00, bcd 00 unhandled +I think I had a similar problem. All three timers should support modes +0, 2, and 3. Other modes really aren't implemented. diff --git a/pintos-env/share/doc/bochs/COPYING b/pintos-env/share/doc/bochs/COPYING new file mode 100755 index 0000000..654ead3 --- /dev/null +++ b/pintos-env/share/doc/bochs/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/pintos-env/share/doc/bochs/LICENSE b/pintos-env/share/doc/bochs/LICENSE new file mode 100755 index 0000000..008d147 --- /dev/null +++ b/pintos-env/share/doc/bochs/LICENSE @@ -0,0 +1,7 @@ +The following points clarify the Bochs license: + +1) Bochs as a whole is released under the GNU Lesser General Public License + +2) Parts of Bochs have specific licenses which are compatible with the + GNU Lesser General Public License. Hence each source file contains its + own licensing information. diff --git a/pintos-env/share/doc/bochs/README b/pintos-env/share/doc/bochs/README new file mode 100755 index 0000000..c1c53fa --- /dev/null +++ b/pintos-env/share/doc/bochs/README @@ -0,0 +1,83 @@ +Bochs - The cross platform IA-32 (x86) emulator +Updated: Sun Sep 2 13:10:00 CEST 2012 +Version: 2.6 + +WHAT IS BOCHS? + +Bochs is a highly portable open source IA-32 (x86) PC emulator +written in C++, that runs on most popular platforms. It includes +emulation of the Intel x86 CPU, common I/O devices, and a custom +BIOS. Bochs can be compiled to emulate many different x86 CPUs, +from early 386 to the most recent x86-64 Intel and AMD processors +which may even not reached the market yet. Bochs is capable of running +most Operating Systems inside the emulation, for example DOS, +Linux or Windows. Bochs was written by Kevin Lawton and is currently +maintained by the Bochs project at "http://bochs.sourceforge.net". + +Bochs can be compiled and used in a variety of modes, some which are +still in development. The 'typical' use of bochs is to provide +complete x86 PC emulation, including the x86 processor, hardware +devices, and memory. This allows you to run OS's and software within +the emulator on your workstation, much like you have a machine +inside of a machine. Bochs will allow you to run Windows +applications on a Solaris machine with X11, for example. + +Bochs is distributed under the GNU LGPL. See LICENSE and COPYING for details. + +GETTING CURRENT SOURCE CODE + +Source code for Bochs is available from the Bochs home page at +http://bochs.sourceforge.net. You can download the most recent +release, use SVN to get the latest sources, or grab a SVN +snapshot which is updated frequently. The releases contain the most +stable code, but if you want the very newest features try the +SVN version instead. + +WHERE ARE THE DOCS? + +The Bochs documentation is written in Docbook. Docbook is a text +format that can be rendered to many popular browser formats such +as HTML, PDF, and Postscript. Each binary release contains the +HTML rendering of the documentation. Also, you can view the +latest documentation on the web at + http://bochs.sf.net/doc/docbook/index.html + +Some information has not yet been transferred from the older +HTML docs. These can be found at http://bochs.sf.net/docs-html + +WHERE CAN I GET MORE INFORMATION? HOW DO I REPORT PROBLEMS? + +Both the documentation and the Bochs website have instructions on how +to join the bochs-developers mailing list, which is the primary +forum for discussion of Bochs. The main page of the website also +has links to bug reports and feature requests. You can browse and +add to the content in these areas even if you do not have a (free) +SourceForge account. We need your feedback so that we know what +parts of Bochs to improve. + +There is a patches section on the web site too, if you have made +some changes to Bochs that you want to share. + +HOW CAN I HELP? + +If you would like contribute to the Bochs project, a good first step +is to join the bochs-developers mailing list, and read the archive +of recent messages to see what's going on. + +If you are a technical person (can follow hardware specs, can write +C/C++) take a look at the list of open bug reports and feature +requests to see if you are interested in working on any of the +problems that are mentioned in them. If you check out the SVN +sources, make some changes, and create a patch, one of the +developers will be very happy to apply it for you. Developers who +frequently submit patches, or who embark on major changes in the +source can get write access to SVN. Be sure to communicate with the +bochs-developers list to avoid several people working on the same +thing without realizing it. + +If you are a Bochs user, not a hardware/C++ guru, there are still +many ways you could help out. For example: + - write instructions on how to install a particular operating system + - writing/cleaning up documentation + - testing out Bochs on every imaginable operating system and + reporting how it goes. diff --git a/pintos-env/share/doc/bochs/TODO b/pintos-env/share/doc/bochs/TODO new file mode 100755 index 0000000..e61f199 --- /dev/null +++ b/pintos-env/share/doc/bochs/TODO @@ -0,0 +1,245 @@ +This is the "roadmap" posted in the mailing list, augmented by +comments from the mailing list and the irc chat. +Anybody is welcome to work on any of these issues. Some of +these items are rather simple and can be implemented by single +individuals. Other items are quite complex and development needs +to be coordinated. So, if you want to contribute, please drop +us a note in the mailing list, so you can get help or exchange +ideas. +Christophe Bothamy. + +0. Donations +Source Forge recently set up a donation system for hosted projects. +Should we accept donations ? What could we do with the money ? +- give to EFF, FSF or other +- fund Kevin to continue the work on plex86 so we can use it +- bounties for somebody write optimized win9x/NT/XFree/linux/*BSD + drivers for our vga/net/ide cards +- other ? +Status in Bochs 2.5: +No decisions about this yet. + +1. Speed +Speed (well lack of) is one of the biggest criticism made by users +who'd like to see Bochs run as fast as Virtual PC. +Paths we can explore to get more speed : +1.1 virtualization : plex86 +1.2 dynamic translation : qemu +Status: +Some work has been done for Bochs 2.5 but still long way is ahead. + +2 multithreading. Conn Clark wrote : +Threading might be nice too, for those of us who have SMP/SMT machines. +I have a patch from Mathis (who hangs out on the IRC channel all the +time) that puts the video card interface in its own thread. It has +troubles though that I have not resolved. It may also be easier to debug +a threaded peripheral. +I also think that it might be possible to thread a chunk of the CPU +emulation to improve performance on a SMP/SMT machine. Specifically +write_virtual_dword, write_virtual_word, write_virtual_byte, etc... +might just be able to be threaded. I think the threading overhead might +be less than the protection and address translation code. We would have +to try it to find out. I'm also sure there can be some nasty hurdles to +overcome. +Status: +Third party group started a para-Bochs project exactly to reach above goals, +some beta version is already released. +The home page of the project: http://grid.hust.edu.cn/cluster/VirtualMachine/main.html + +3. Plugin architecture +3.1 The plugin architecture can be reworked if we want to support +multiple similar devices like serial, net or vga cards. +We currently have two "types" of plugins: "core" and "optional". +Maybe we could add "classes" of plugins. The current version of +Bochs supports the classes "display_library" and "io_device". +New classes can be "config_interface", "net_lowlevel" and +"sound_lowlevel" +3.2 Stanislav wrote : +Plugin architecture should be rewritten like real plugin architecture s.t. +Bochs VGA plugin for example will be real plugin. I mean that replacement +of plugin dll in already compiled Bochs will replace Bochs VGA card and +the new card will be detected automatically. +This will allow for example developing of plugins separately from Bochs. +3.3 Michael Brown wrote : +If the configuration interface is to be reworked, could we also make it so +that plugins are self-contained, rather than needing to pollute config.cc +with code for defining and parsing plugin-specific options +Status: +Some of the basic work is done now: The config parameter handling has +been rewritten to a parameter tree and user-defined bochsrc options are now +supported. For most of the optional plugins the config parameter creation and +bochsrc parsing has been moved to the plugin code. Unknown bochsrc options are +now treated as plugin names and Bochs tries to load them. + +4. PCI host<->guest proxy +Being able to use a real pci device from inside Bochs would be a +great feature of Bochs. It would ease reverse engineering of non +documented cards, or one could even use a real spare vga card. +Frank Cornellis has done a great job on this subject, and we began +integrating his changes. +Status: +The pcidev device is present in SVN and it has been updated for the new PCI +infrastructure, but the new code is untested yet. + +5. Subdirectories in iodev +The iodev directory contains the various implemented iodevice. +With the new pci devices, new harddrives and new net access methods, +it could be interesting to add new subdirectories like : +iodev/video/... --> for standard vga and new card emulation +iodev/disks/... --> for the ata/atapi classes, hd/cd classes and host accesses +iodev/net/... --> for ne2k and host net access +isa and pci devices would be mixed in the directories, but this should +be manageable. +Status: +Subfolders for disk imaging, sound, network and usb devices were created +under the iodev folder. + +6. VGA +For SVGA emulation we have Bochs VBE and the Cirrus adapter. We should have +a look at the voodoo3 (specs http://v3tv.sourceforge.net/docs.php). +Status: +Not done yet. + +7. Random thoughts on disk emulation improvements : +7.1 autodetection of disk size / geometry +7.2 uml cow disk image support +7.3 compressed disk image support +7.4 extend redolog-disk specification to add coherency check of the flat +image file, by storing its fstat-mtime field in the redolog. +Status: +Autodetection now works for all image types created with bximage and vmware3 +images. Vmware4 disk images support was added in Bochs 2.3.5 release. +Coherency check for 'undoable' mode images added for Bochs 2.6. + +8. net +8.1 bootable ethernet rom ? +8.2 user mode networking ? +see etherboot, Micheal Brown wrote : +This already works; you can build an Etherboot rom image with the pnic +driver, specify it as an option ROM in bochsrc and it will boot. I'm +using this extensively at the moment in Etherboot development. +In the Etherboot project's CVS, in the contrib/bochs directory, you can +find a working bochsrc file and an up-to-date README with step-by-step +instructions on getting this working. +Status: +The pnic device is present in SVN, but the status is unknown. PCI boot ROM support +has been added for Bochs 2.6. User mode networking currently requires the 'slirp' +program and it is not possible on all platforms. The slirp core should be added +to the Bochs sources. + +9. Bios +9.1 add "jump table placeholder" and log missing function calls in the bios. +Check completness with Ralf Brown interrupt list. +Status: +Not done yet. +9.2 use Coreboot or SeaBios as possible alternatives/extensions to +Bochs Bios ROM we have. +Status: +Starting from Bochs 2.5 SeaBIOS is usable. + +10. LGPL VGABios +11.1 Video parameters table +There is a very nice parameter table in 3dfx banshee document +http://www2.lm-sensors.nu/~lm78/pdfs/Banshee_2d_spec.PDF +see also http://www.xyzzy.claranet.de/dos/vgacrt.c +Status: +Version 0.7a of the LGPL'd VGABIOS has minimal support for the video +parameter table. + +11. Optimized Guest drivers still needed : VGA, IDE, NET +We have a specific VGA driver for winNT/2K, but still +lack drivers for other OSes. +Status: +Not done yet. + +12. USB support +Ben Lunt has been working on USB support. The USB mouse and keypad code +is present in Bochs and almost stable. USB flash disk support has been +started and the runtime device change support should be completed. +Status: +OHCI and UHCI host controller and 7 devices are known to work in Bochs. +USB xHCI support is present, but needs more testing. + +13. Config file and dynamic menu +13.1 Benjamen R. Meyer wrote : +I think we should rework the .bochsrc file to be more standard across all +devices. I like how the USB configuration is done in it, and think we should +put something similar together for everything else. In other words, create +something that can be easily used for everything, and make it easier to +configure in the process. +From what I can tell right now, most of the configuration lines are randomly +thrown together as each gets implemented or added, instead of having +something that is based on a standard approach to the configuration. +The result should be something that would be able to easily auto-configured +by another program (a configuration editor?) with minimal changes necessary +when new devices/features are added. +13.2 Franck Cornelis wrote : the config system needs some work... +e.g. the main menu is static while it could be generated at run-time... +the main menu text lives somewhere in a file... while it should be generated +at run-time by iterating the main menu objects +Status: +The config options handling has been rewritten to a parameter tree. Dynamic +menus are now implemented on win32. + +14. lowlevel serial support for Windows. +Volker has been working on this. +Status: +Not yet complete (transmit works, receive is losing data). + +15. Parallel port +Conn Clark wrote : +I would like to see better parallel port support so I can use a dongle. +This is something I would find very useful as it would mean I wouldn't +have to boot back into windows ever again. I also recognize that this +may require a kernel module be written, which is beyond my current +skills. I know others will find this useful as I have had to tell a +few people that their parallel port driven peripherals that require a +bidirectional parallel port won't work. +Status: +Not done yet. + +16. Guest-To-Host Communication +Try to adapt VirtualBox guest-to-host communication methods into Bochs. +Having VirtualBox Shared Folders or VNAT support in Bochs could very +simplify its usage. + +17. Patches / Bug reports +There are dozens of patches floating around. Some are outdated, +don't apply cleanly, are obsolete/unneeded. We could try to do +some clean-up, and keep only relevant ones. +We should also clean up the SF bug tracker. Some bugreports are +very old and we asked for more information with no response. +Status: +There is some progress, but still a lot of work to do. + +18. Positions +If you want to help without coding, here are available positions : +19.1 Webmaster : update website (Jan Bruun Andersen offered to help) +19.2 patch coordinator : look at incoming patches (sourceforge and +mailing list) and upload / update in the SVN patches directory. +19.3 platform maintainers for macos / win32 +19.4 disk image maintainer : create and maintain our collection +of disk images. Usually, only the configuration file needs to be +updated, and old bios files have to be removed. Some packages +still contain very old bios files, they should definitely have +to be removed. +Status: +More active developers are needed to do the things described above. + +19. Bochs demo cd/dvd +With version 2.1, it is now technically possible to use disk images +on a read-only media, with a journal files on a read/write media. +It would be great to create a demo cd/dvd with executables for +supported platforms, configuration files and read-only disk +images, the journal files would be written in a temporary +directory on the harddisk. +Status: +Not done yet. + +20. Other CPU architectures : arm, ppc +This has been asked in the mailing list. I'm not really +interested, but other people might be. Should we propose to +host the new CPUs code in our source tree, or should we let +people fork ? +Status: +Not done yet. diff --git a/pintos-env/share/doc/bochs/bochsrc-sample.txt b/pintos-env/share/doc/bochs/bochsrc-sample.txt new file mode 100755 index 0000000..cc714e5 --- /dev/null +++ b/pintos-env/share/doc/bochs/bochsrc-sample.txt @@ -0,0 +1,1116 @@ +# You may now use double quotes around pathnames, in case +# your pathname includes spaces. + +#======================================================================= +# PLUGIN_CTRL: +# Controls the presence of optional device plugins. These plugins are loaded +# directly with this option and some of them install a config option that is +# only available when the plugin device is loaded. The value "1" means to load +# the plugin and "0" will unload it (if loaded before). +# +# These plugins will be loaded by default (if present): 'biosdev', 'extfpuirq', +# 'gameport', 'iodebug','parallel', 'serial', 'speaker' and 'unmapped'. +# +# These plugins are also supported, but they are usually loaded directly with +# their bochsrc option: 'e1000', 'es1370', 'ne2k', 'pcidev', 'pcipnic', 'sb16', +# 'usb_ohci', 'usb_uhci' and 'usb_xhci'. +#======================================================================= +#plugin_ctrl: unmapped=0, e1000=1 # unload 'unmapped' and load 'e1000' + +#======================================================================= +# CONFIG_INTERFACE +# +# The configuration interface is a series of menus or dialog boxes that +# allows you to change all the settings that control Bochs's behavior. +# Depending on the platform there are up to 3 choices of configuration +# interface: a text mode version called "textconfig" and two graphical versions +# called "win32config" and "wx". The text mode version uses stdin/stdout and +# is always compiled in, unless Bochs is compiled for wx only. The choice +# "win32config" is only available on win32 and it is the default there. +# The choice "wx" is only available when you use "--with-wx" on the configure +# command. If you do not write a config_interface line, Bochs will +# choose a default for you. +# +# NOTE: if you use the "wx" configuration interface, you must also use +# the "wx" display library. +#======================================================================= +#config_interface: textconfig +#config_interface: win32config +#config_interface: wx + +#======================================================================= +# DISPLAY_LIBRARY +# +# The display library is the code that displays the Bochs VGA screen. Bochs +# has a selection of about 10 different display library implementations for +# different platforms. If you run configure with multiple --with-* options, +# the display_library command lets you choose which one you want to run with. +# If you do not write a display_library line, Bochs will choose a default for +# you. +# +# The choices are: +# x use X windows interface, cross platform +# win32 use native win32 libraries +# carbon use Carbon library (for MacOS X) +# macintosh use MacOS pre-10 +# amigaos use native AmigaOS libraries +# sdl use SDL library, cross platform +# svga use SVGALIB library for Linux, allows graphics without X11 +# term text only, uses curses/ncurses library, cross platform +# rfb provides an interface to AT&T's VNC viewer, cross platform +# wx use wxWidgets library, cross platform +# nogui no display at all +# +# NOTE: if you use the "wx" configuration interface, you must also use +# the "wx" display library. +# +# Specific options: +# Some display libraries now support specific options to control their +# behaviour. These options are supported by more than one display library: +# +# "gui_debug" - use GTK debugger gui (sdl, x) / Win32 debugger gui (sdl, win32) +# "hideIPS" - disable IPS output in status bar (rfb, sdl, win32, wx, x) +# "nokeyrepeat" - turn off host keyboard repeat (sdl, win32, x) +# +# See the examples below for other currently supported options. +#======================================================================= +#display_library: amigaos +#display_library: carbon +#display_library: macintosh +#display_library: nogui +#display_library: rfb, options="timeout=60" # time to wait for client +#display_library: sdl, options="fullscreen" # startup in fullscreen mode +#display_library: term +#display_library: win32 +#display_library: wx +#display_library: x + +#======================================================================= +# ROMIMAGE: +# The ROM BIOS controls what the PC does when it first powers on. +# Normally, you can use a precompiled BIOS in the source or binary +# distribution called BIOS-bochs-latest. The ROM BIOS is usually loaded +# starting at address 0xf0000, and it is exactly 64k long. Another option +# is 128k BIOS which is loaded at address 0xe0000. +# You can also use the environment variable $BXSHARE to specify the +# location of the BIOS. +# The usage of external large BIOS images (up to 512k) at memory top is +# now supported, but we still recommend to use the BIOS distributed with +# Bochs. The start address optional, since it can be calculated from image size. +#======================================================================= +romimage: file=$BXSHARE/BIOS-bochs-latest +#romimage: file=bios/seabios-1.6.3.bin +#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top + +#======================================================================= +# CPU: +# This defines cpu-related parameters inside Bochs: +# +# MODEL: +# Selects CPU configuration to emulate from pre-defined list of all +# supported configurations. When this option is used, the CPUID option +# has no effect anymore. +# +# CPU configurations that can be selected: +# ----------------------------------------------------------------- +# pentium_mmx Intel Pentium MMX +# amd_k6_2_chomper AMD-K6(tm) 3D processor (Chomper) +# p2_klamath Intel Pentium II (Klamath) +# p3_katmai Intel Pentium III (Katmai) +# p4_willamette Intel(R) Pentium(R) 4 (Willamette) +# core_duo_t2400_yonah Intel(R) Core(TM) Duo CPU T2400 (Yonah) +# atom_n270 Intel(R) Atom(TM) CPU N270 +# athlon64_clawhammer AMD Athlon(tm) 64 Processor 2800+ (Clawhammer) +# athlon64_venice AMD Athlon(tm) 64 Processor 3000+ (Venice) +# turion64_tyler AMD Turion(tm) 64 X2 Mobile TL-60 (Tyler) +# phenom_8650_toliman AMD Phenom X3 8650 (Toliman) +# p4_prescott_celeron_336 Intel(R) Celeron(R) 336 (Prescott) +# core2_penryn_t9600 Intel Mobile Core 2 Duo T9600 (Penryn) +# corei5_lynnfield_750 Intel(R) Core(TM) i5 750 (Lynnfield) +# corei5_arrandale_m520 Intel(R) Core(TM) i5 M 520 (Arrandale) +# corei7_sandy_bridge_2600k Intel(R) Core(TM) i7-2600K (Sandy Bridge) +# corei7_ivy_bridge_3770k Intel(R) Core(TM) i7-3770K CPU (Ivy Bridge) +# +# COUNT: +# Set the number of processors:cores per processor:threads per core +# when Bochs is compiled for SMP emulation. +# Bochs currently supports up to 8 threads running simultaniosly. +# If Bochs is compiled without SMP support, it won't accept values +# different from 1. +# +# QUANTUM: +# Maximum amount of instructions allowed to execute by processor before +# returning control to another cpu. This option exists only in Bochs +# binary compiled with SMP support. +# +# RESET_ON_TRIPLE_FAULT: +# Reset the CPU when triple fault occur (highly recommended) rather than +# PANIC. Remember that if you trying to continue after triple fault the +# simulation will be completely bogus ! +# +# CPUID_LIMIT_WINNT: +# Determine whether to limit maximum CPUID function to 2. This mode is +# required to workaround WinNT installation and boot issues. +# +# MSRS: +# Define path to user CPU Model Specific Registers (MSRs) specification. +# See example in msrs.def. +# +# IGNORE_BAD_MSRS: +# Ignore MSR references that Bochs does not understand; print a warning +# message instead of generating #GP exception. This option is enabled +# by default but will not be avaiable if configurable MSRs are enabled. +# +# MWAIT_IS_NOP: +# When this option is enabled MWAIT will not put the CPU into a sleep state. +# This option exists only if Bochs compiled with --enable-monitor-mwait. +# +# IPS: +# Emulated Instructions Per Second. This is the number of IPS that bochs +# is capable of running on your machine. You can recompile Bochs with +# --enable-show-ips option enabled, to find your host's capability. +# Measured IPS value will then be logged into your log file or shown +# in the status bar (if supported by the gui). +# +# IPS is used to calibrate many time-dependent events within the bochs +# simulation. For example, changing IPS affects the frequency of VGA +# updates, the duration of time before a key starts to autorepeat, and +# the measurement of BogoMips and other benchmarks. +# +# Examples: +# +# Bochs Machine/Compiler Mips +# ______________________________________________________________________ +# 2.4.6 3.4Ghz Intel Core i7 2600 with Win7x64/g++ 4.5.2 85 to 95 Mips +# 2.3.7 3.2Ghz Intel Core 2 Q9770 with WinXP/g++ 3.4 50 to 55 Mips +# 2.3.7 2.6Ghz Intel Core 2 Duo with WinXP/g++ 3.4 38 to 43 Mips +# 2.2.6 2.6Ghz Intel Core 2 Duo with WinXP/g++ 3.4 21 to 25 Mips +# 2.2.6 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4 12 to 15 Mips +#======================================================================= +cpu: model=core2_penryn_t9600, count=1, ips=50000000, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def" +cpu: cpuid_limit_winnt=0 + +#======================================================================= +# CPUID: +# +# This defines features and functionality supported by Bochs emulated CPU. +# The option has no offect if CPU model was selected in CPU option. +# +# MMX: +# Select MMX instruction set support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. +# +# APIC: +# Select APIC configuration (LEGACY/XAPIC/XAPIC_EXT/X2APIC). +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 5. +# +# SEP: +# Select SYSENTER/SYSEXIT instruction set support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# SSE: +# Select SSE instruction set support. +# Any of NONE/SSE/SSE2/SSE3/SSSE3/SSE4_1/SSE4_2 could be selected. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# SSE4A: +# Select AMD SSE4A instructions support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# AES: +# Select AES instruction set support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# MOVBE: +# Select MOVBE Intel(R) Atom instruction support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# ADX: +# Select ADCX/ADOX instructions support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# XSAVE: +# Select XSAVE extensions support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# XSAVEOPT: +# Select XSAVEOPT instruction support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# AVX: +# Select AVX/AVX2 instruction set support. +# This option exists only if Bochs compiled with --enable-avx option. +# +# AVX_F16C: +# Select AVX float16 convert instructions support. +# This option exists only if Bochs compiled with --enable-avx option. +# +# AVX_FMA: +# Select AVX fused multiply add (FMA) instructions support. +# This option exists only if Bochs compiled with --enable-avx option. +# +# BMI: +# Select BMI1/BMI2 instructions support. +# This option exists only if Bochs compiled with --enable-avx option. +# +# XOP: +# Select AMD XOP instructions support. +# This option exists only if Bochs compiled with --enable-avx option. +# +# FMA4: +# Select AMD four operand FMA instructions support. +# This option exists only if Bochs compiled with --enable-avx option. +# +# TBM: +# Select AMD Trailing Bit Manipulation (TBM) instructions support. +# This option exists only if Bochs compiled with --enable-avx option. +# +# X86-64: +# Enable x86-64 and long mode support. +# This option exists only if Bochs compiled with x86-64 support. +# +# 1G_PAGES: +# Enable 1G page size support in long mode. +# This option exists only if Bochs compiled with x86-64 support. +# +# PCID: +# Enable Process-Context Identifiers (PCID) support in long mode. +# This option exists only if Bochs compiled with x86-64 support. +# +# FSGSBASE: +# Enable GS/GS BASE access instructions support in long mode. +# This option exists only if Bochs compiled with x86-64 support. +# +# SMEP: +# Enable Supervisor Mode Execution Protection (SMEP) support. +# This option exists only if Bochs compiled with BX_CPU_LEVEL >= 6. +# +# MWAIT: +# Select MONITOR/MWAIT instructions support. +# This option exists only if Bochs compiled with --enable-monitor-mwait. +# +# VMX: +# Select VMX extensions emulation support. +# This option exists only if Bochs compiled with --enable-vmx option. +# +# VENDOR_STRING: +# Set the CPUID vendor string returned by CPUID(0x0). This should be a +# twelve-character ASCII string. +# +# BRAND_STRING: +# Set the CPUID vendor string returned by CPUID(0x80000002 .. 0x80000004). +# This should be at most a forty-eight-character ASCII string. +# +# FAMILY: +# Set model information returned by CPUID. Default family value determined +# by configure option --enable-cpu-level. +# +# MODEL: +# Set model information returned by CPUID. Default model value is 3. +# +# STEPPING: +# Set stepping information returned by CPUID. Default stepping value is 3. +#======================================================================= +#cpuid: x86_64=1, mmx=1, sep=1, sse=sse4_2, apic=xapic, aes=1, movbe=1, xsave=1 +#cpuid: family=6, model=0x1a, stepping=5 + +#======================================================================= +# MEMORY +# Set the amount of physical memory you want to emulate. +# +# GUEST: +# Set amount of guest physical memory to emulate. The default is 32MB, +# the maximum amount limited only by physical address space limitations. +# +# HOST: +# Set amount of host memory you want to allocate for guest RAM emulation. +# It is possible to allocate less memory than you want to emulate in guest +# system. This will fake guest to see the non-existing memory. Once guest +# system touches new memory block it will be dynamically taken from the +# memory pool. You will be warned (by FATAL PANIC) in case guest already +# used all allocated host memory and wants more. +# +#======================================================================= +memory: guest=512, host=256 + +#======================================================================= +# OPTROMIMAGE[1-4]: +# You may now load up to 4 optional ROM images. Be sure to use a +# read-only area, typically between C8000 and EFFFF. These optional +# ROM images should not overwrite the rombios (located at +# F0000-FFFFF) and the videobios (located at C0000-C7FFF). +# Those ROM images will be initialized by the bios if they contain +# the right signature (0x55AA) and a valid checksum. +# It can also be a convenient way to upload some arbitrary code/data +# in the simulation, that can be retrieved by the boot loader +#======================================================================= +#optromimage1: file=optionalrom.bin, address=0xd0000 +#optromimage2: file=optionalrom.bin, address=0xd1000 +#optromimage3: file=optionalrom.bin, address=0xd2000 +#optromimage4: file=optionalrom.bin, address=0xd3000 + +#optramimage1: file=/path/file1.img, address=0x0010000 +#optramimage2: file=/path/file2.img, address=0x0020000 +#optramimage3: file=/path/file3.img, address=0x0030000 +#optramimage4: file=/path/file4.img, address=0x0040000 + +#======================================================================= +# VGAROMIMAGE +# You now need to load a VGA ROM BIOS into C0000. +#======================================================================= +#vgaromimage: file=bios/VGABIOS-elpin-2.40 +vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest +#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus + +#======================================================================= +# VGA: +# This defines parameters related to the VGA display +# +# EXTENSION +# Here you can specify the display extension to be used. With the value +# 'none' you can use standard VGA with no extension. Other supported +# values are 'vbe' for Bochs VBE and 'cirrus' for Cirrus SVGA support. +# +# UPDATE_FREQ +# The VGA update frequency is based on the emulated clock and the default +# value is 5. Keep in mind that you must tweak the 'cpu: ips=N' directive +# to be as close to the number of emulated instructions-per-second your +# workstation can do, for this to be accurate. If the realtime sync is +# enabled with the 'clock' option, the value is based on the real time. +# This parameter can be changed at runtime. +# +# Examples: +# vga: extension=cirrus, update_freq=10 +#======================================================================= +#vga: extension=vbe, update_freq=5 + +#======================================================================= +# FLOPPYA: +# Point this to pathname of floppy image file or device +# This should be of a bootable floppy(image/device) if you're +# booting from 'a' (or 'floppy'). +# +# You can set the initial status of the media to 'ejected' or 'inserted'. +# floppya: 2_88=path, status=ejected (2.88M 3.5" media) +# floppya: 1_44=path, status=inserted (1.44M 3.5" media) +# floppya: 1_2=path, status=ejected (1.2M 5.25" media) +# floppya: 720k=path, status=inserted (720K 3.5" media) +# floppya: 360k=path, status=inserted (360K 5.25" media) +# floppya: 320k=path, status=inserted (320K 5.25" media) +# floppya: 180k=path, status=inserted (180K 5.25" media) +# floppya: 160k=path, status=inserted (160K 5.25" media) +# floppya: image=path, status=inserted (guess media type from image size) +# floppya: 1_44=vvfat:path, status=inserted (use directory as VFAT media) +# floppya: type=1_44 (1.44M 3.5" floppy drive, no media) +# +# The path should be the name of a disk image file. On Unix, you can use a raw +# device name such as /dev/fd0 on Linux. On win32 platforms, use drive letters +# such as a: or b: as the path. The parameter 'image' works with image files +# only. In that case the size must match one of the supported types. +# The parameter 'type' can be used to enable the floppy drive without media +# and status specified. Usually the drive type is set up based on the media type. +# The optional parameter 'write_protected' can be used to control the media +# write protect switch. By default it is turned off. +#======================================================================= +floppya: 1_44=/dev/fd0, status=inserted +#floppya: image=../1.44, status=inserted +#floppya: 1_44=/dev/fd0H1440, status=inserted +#floppya: 1_2=../1_2, status=inserted +#floppya: 1_44=a:, status=inserted +#floppya: 1_44=a.img, status=inserted, write_protected=1 +#floppya: 1_44=/dev/rfd0a, status=inserted + +#======================================================================= +# FLOPPYB: +# See FLOPPYA above for syntax +#======================================================================= +#floppyb: 1_44=b:, status=inserted +#floppyb: 1_44=b.img, status=inserted + +#======================================================================= +# ATA0, ATA1, ATA2, ATA3 +# ATA controller for hard disks and cdroms +# +# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number +# +# These options enables up to 4 ata channels. For each channel +# the two base io addresses and the irq must be specified. +# +# ata0 and ata1 are enabled by default with the values shown below +# +# Examples: +# ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +# ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 +# ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 +# ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9 +#======================================================================= +ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15 +ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 +ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 + +#======================================================================= +# ATA[0-3]-MASTER, ATA[0-3]-SLAVE +# +# This defines the type and characteristics of all attached ata devices: +# type= type of attached device [disk|cdrom] +# mode= only valid for disks [flat|concat|external|dll|sparse|vmware3] +# [vmware4|undoable|growing|volatile|vpc|vvfat] +# path= path of the image / directory +# cylinders= only valid for disks +# heads= only valid for disks +# spt= only valid for disks +# status= only valid for cdroms [inserted|ejected] +# biosdetect= type of biosdetection [none|auto], only for disks on ata0 [cmos] +# translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto] +# model= string returned by identify device command +# journal= optional filename of the redolog for undoable, volatile and vvfat disks +# +# Point this at a hard disk image file, cdrom iso file, or physical cdrom +# device. To create a hard disk image, try running bximage. It will help you +# choose the size and then suggest a line that works with it. +# +# In UNIX it may be possible to use a raw device as a Bochs hard disk, +# but WE DON'T RECOMMEND IT. In Windows there is no easy way. +# +# In windows, the drive letter + colon notation should be used for cdroms. +# Depending on versions of windows and drivers, you may only be able to +# access the "first" cdrom in the system. On MacOSX, use path="drive" +# to access the physical drive. +# +# The path is mandatory for hard disks. Disk geometry autodetection works with +# images created by bximage if CHS is set to 0/0/0 (cylinders are calculated +# using heads=16 and spt=63). For other hard disk images and modes the +# cylinders, heads, and spt are mandatory. In all cases the disk size reported +# from the image must be exactly C*H*S*512. +# +# Default values are: +# mode=flat, biosdetect=auto, translation=auto, model="Generic 1234" +# +# The biosdetect option has currently no effect on the bios +# +# Examples: +# ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17 +# ata0-slave: type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17 +# ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17 +# ata1-slave: type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17 +# ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17 +# ata2-slave: type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17 +# ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63 +# ata3-slave: type=cdrom, path=iso.sample, status=inserted +#======================================================================= +ata0-master: type=disk, mode=flat, path="30M.sample" +#ata0-master: type=disk, mode=flat, path="30M.sample", cylinders=615, heads=6, spt=17 +#ata0-master: type=disk, mode=flat, path="c.img", cylinders=0 # autodetect +#ata0-slave: type=disk, mode=vvfat, path=/bochs/images/vvfat, journal=vvfat.redolog +#ata0-slave: type=cdrom, path=D:, status=inserted +#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted +#ata0-slave: type=cdrom, path="drive", status=inserted +#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted + +#======================================================================= +# BOOT: +# This defines the boot sequence. Now you can specify up to 3 boot drives, +# which can be 'floppy', 'disk', 'cdrom' or 'network' (boot ROM). +# Legacy 'a' and 'c' are also supported. +# Examples: +# boot: floppy +# boot: cdrom, disk +# boot: network, disk +# boot: cdrom, floppy, disk +#======================================================================= +#boot: floppy +boot: disk + +#======================================================================= +# CLOCK: +# This defines the parameters of the clock inside Bochs: +# +# SYNC: +# This defines the method how to synchronize the Bochs internal time +# with realtime. With the value 'none' the Bochs time relies on the IPS +# value and no host time synchronization is used. The 'slowdown' method +# sacrifices performance to preserve reproducibility while allowing host +# time correlation. The 'realtime' method sacrifices reproducibility to +# preserve performance and host-time correlation. +# It is possible to enable both synchronization methods. +# +# RTC_SYNC: +# If this option is enabled together with the realtime synchronization, +# the RTC runs at realtime speed. This feature is disabled by default. +# +# TIME0: +# Specifies the start (boot) time of the virtual machine. Use a time +# value as returned by the time(2) system call. If no time0 value is +# set or if time0 equal to 1 (special case) or if time0 equal 'local', +# the simulation will be started at the current local host time. +# If time0 equal to 2 (special case) or if time0 equal 'utc', +# the simulation will be started at the current utc time. +# +# Syntax: +# clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc] +# +# Example: +# clock: sync=none, time0=local # Now (localtime) +# clock: sync=slowdown, time0=315529200 # Tue Jan 1 00:00:00 1980 +# clock: sync=none, time0=631148400 # Mon Jan 1 00:00:00 1990 +# clock: sync=realtime, time0=938581955 # Wed Sep 29 07:12:35 1999 +# clock: sync=realtime, time0=946681200 # Sat Jan 1 00:00:00 2000 +# clock: sync=none, time0=1 # Now (localtime) +# clock: sync=none, time0=utc # Now (utc/gmt) +# +# Default value are sync=none, time0=local +#======================================================================= +#clock: sync=none, time0=local + + +#======================================================================= +# FLOPPY_BOOTSIG_CHECK: disabled=[0|1] +# Enables or disables the 0xaa55 signature check on boot floppies +# Defaults to disabled=0 +# Examples: +# floppy_bootsig_check: disabled=0 +# floppy_bootsig_check: disabled=1 +#======================================================================= +floppy_bootsig_check: disabled=0 + +#======================================================================= +# LOG: +# Give the path of the log file you'd like Bochs debug and misc. verbiage +# to be written to. If you don't use this option or set the filename to +# '-' the output is written to the console. If you really don't want it, +# make it "/dev/null" (Unix) or "nul" (win32). :^( +# +# Examples: +# log: ./bochs.out +# log: /dev/tty +#======================================================================= +#log: /dev/null +log: bochsout.txt + +#======================================================================= +# LOGPREFIX: +# This handles the format of the string prepended to each log line. +# You may use those special tokens : +# %t : 11 decimal digits timer tick +# %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration) +# %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror) +# %d : 5 characters string of the device, between brackets +# +# Default : %t%e%d +# Examples: +# logprefix: %t-%e-@%i-%d +# logprefix: %i%e%d +#======================================================================= +#logprefix: %t%e%d + +#======================================================================= +# LOG CONTROLS +# +# Bochs has four severity levels for event logging. +# panic: cannot proceed. If you choose to continue after a panic, +# don't be surprised if you get strange behavior or crashes. +# error: something went wrong, but it is probably safe to continue the +# simulation. +# info: interesting or useful messages. +# debug: messages useful only when debugging the code. This may +# spit out thousands per second. +# +# For events of each level, you can choose to exit Bochs ('fatal'), 'report' +# or 'ignore'. On some guis you have the additional choice 'ask'. A gui dialog +# appears asks how to proceed. +# +# It is also possible to specify the 'action' to do for each Bochs facility +# separately (e.g. crash on panics from everything except the cdrom, and only +# report those). See the 'log function' module list in the user documentation. +# +# If you are experiencing many panics, it can be helpful to change +# the panic action to report instead of fatal. However, be aware +# that anything executed after a panic is uncharted territory and can +# cause bochs to become unstable. The panic is a "graceful exit," so +# if you disable it you may get a spectacular disaster instead. +#======================================================================= +panic: action=ask +error: action=report +info: action=report +debug: action=ignore, pci=report # report BX_DEBUG from module 'pci' + +#======================================================================= +# DEBUGGER_LOG: +# Give the path of the log file you'd like Bochs to log debugger output. +# If you really don't want it, make it /dev/null or '-'. :^( +# +# Examples: +# debugger_log: ./debugger.out +#======================================================================= +#debugger_log: /dev/null +#debugger_log: debugger.out +debugger_log: - + +#======================================================================= +# COM1, COM2, COM3, COM4: +# This defines a serial port (UART type 16550A). In the 'term' you can specify +# a device to use as com1. This can be a real serial line, or a pty. To use +# a pty (under X/Unix), create two windows (xterms, usually). One of them will +# run bochs, and the other will act as com1. Find out the tty the com1 +# window using the `tty' command, and use that as the `dev' parameter. +# Then do `sleep 1000000' in the com1 window to keep the shell from +# messing with things, and run bochs in the other window. Serial I/O to +# com1 (port 0x3f8) will all go to the other window. +# In socket* and pipe* (win32 only) modes Bochs becomes either socket/named pipe +# client or server. In client mode it connects to an already running server (if +# connection fails Bochs treats com port as not connected). In server mode it +# opens socket/named pipe and waits until a client application connects to it +# before starting simulation. This mode is useful for remote debugging (e.g. +# with gdb's "target remote host:port" command or windbg's command line option +# -k com:pipe,port=\\.\pipe\pipename). Note: 'socket' is a shorthand for +# 'socket-client' and 'pipe' for 'pipe-client'. Socket modes use simple TCP +# communication, pipe modes use duplex byte mode pipes. +# Other serial modes are 'null' (no input/output), 'file' (output to a file +# specified as the 'dev' parameter), 'raw' (use the real serial port - under +# construction for win32), 'mouse' (standard serial mouse - requires +# mouse option setting 'type=serial', 'type=serial_wheel' or 'type=serial_msys'). +# +# Examples: +# com1: enabled=1, mode=null +# com1: enabled=1, mode=mouse +# com2: enabled=1, mode=file, dev=serial.out +# com3: enabled=1, mode=raw, dev=com1 +# com3: enabled=1, mode=socket-client, dev=localhost:8888 +# com3: enabled=1, mode=socket-server, dev=localhost:8888 +# com4: enabled=1, mode=pipe-client, dev=\\.\pipe\mypipe +# com4: enabled=1, mode=pipe-server, dev=\\.\pipe\mypipe +#======================================================================= +#com1: enabled=1, mode=term, dev=/dev/ttyp9 + + +#======================================================================= +# PARPORT1, PARPORT2: +# This defines a parallel (printer) port. When turned on and an output file is +# defined the emulated printer port sends characters printed by the guest OS +# into the output file. On some platforms a device filename can be used to +# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on +# win32 platforms). +# +# Examples: +# parport1: enabled=1, file="parport.out" +# parport2: enabled=1, file="/dev/lp0" +# parport1: enabled=0 +#======================================================================= +parport1: enabled=1, file="parport.out" + +#======================================================================= +# SB16: +# This defines the SB16 sound emulation. It can have several of the +# following properties. +# All properties are in the format sb16: property=value +# enabled: +# This optional property controls the presence of the SB16 emulation. +# The emulation is turned on unless this property is used and set to 0. +# midi: The filename is where the midi data is sent. This can be a +# device or just a file if you want to record the midi data. +# midimode: +# 0=no data +# 1=output to device (system dependent. midi denotes the device driver) +# 2=SMF file output, including headers +# 3=output the midi data stream to the file (no midi headers and no +# delta times, just command and data bytes) +# wave: This is the device/file where wave output is stored +# wavemode: +# 0=no data +# 1=output to device (system dependent. wave denotes the device driver) +# 2=VOC file output, incl. headers +# 3=output the raw wave stream to the file +# log: The file to write the sb16 emulator messages to. +# loglevel: +# 0=no log +# 1=resource changes, midi program and bank changes +# 2=severe errors +# 3=all errors +# 4=all errors plus all port accesses +# 5=all errors and port accesses plus a lot of extra info +# dmatimer: +# microseconds per second for a DMA cycle. Make it smaller to fix +# non-continuous sound. 750000 is usually a good value. This needs a +# reasonably correct setting for the IPS parameter of the CPU option. +# +# Examples for output devices: +# sb16: midimode=1, midi="", wavemode=1, wave="" # win32 +# sb16: midimode=1, midi=alsa:128:0, wavemode=1, wave=alsa # Linux with ALSA +# sb16: wavemode=1, wave=sdl # use SDL audio (if present) for output +#======================================================================= +#sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=sb16.log, dmatimer=600000 + +#======================================================================= +# ES1370: +# This defines the ES1370 sound emulation. The parameter 'enabled' controls the +# presence of the device. The 'wavedev' parameter is similar to the 'wave' +# parameter of the SB16 soundcard. The emulation supports recording and playback +# (except DAC1+DAC2 output at the same time). +# +# Examples: +# es1370: enabled=1, wavedev="" # win32 +# es1370: enabled=1, wavedev=alsa # Linux with ALSA +# es1370: enabled=1, wavedev=sdl # use SDL audio (if present) for output +#======================================================================= +#es1370: enabled=1, wavedev=alsa + +#======================================================================= +# KEYBOARD: +# This defines parameters related to the emulated keyboard +# +# TYPE: +# Type of keyboard return by a "identify keyboard" command to the +# keyboard controller. It must be one of "xt", "at" or "mf". +# Defaults to "mf". It should be ok for almost everybody. A known +# exception is french macs, that do have a "at"-like keyboard. +# +# SERIAL_DELAY: +# Approximate time in microseconds that it takes one character to +# be transferred from the keyboard to controller over the serial path. +# +# PASTE_DELAY: +# Approximate time in microseconds between attempts to paste +# characters to the keyboard controller. This leaves time for the +# guest os to deal with the flow of characters. The ideal setting +# depends on how your operating system processes characters. The +# default of 100000 usec (.1 seconds) was chosen because it works +# consistently in Windows. +# If your OS is losing characters during a paste, increase the paste +# delay until it stops losing characters. +# +# KEYMAP: +# This enables a remap of a physical localized keyboard to a +# virtualized us keyboard, as the PC architecture expects. +# +# Examples: +# keyboard: type=mf, serial_delay=200, paste_delay=100000 +# keyboard: keymap=gui/keymaps/x11-pc-de.map +#======================================================================= +#keyboard: type=mf, serial_delay=250 + +#======================================================================= +# MOUSE: +# This defines parameters for the emulated mouse type, the initial status +# of the mouse capture and the runtime method to toggle it. +# +# TYPE: +# With the mouse type option you can select the type of mouse to emulate. +# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse +# on PS/2), 'serial', 'serial_wheel' and 'serial_msys' (one com port requires +# setting 'mode=mouse'). To connect a mouse to an USB port, see the 'usb_uhci', +# 'usb_ohci' or 'usb_xhci' options (requires PCI and USB support). +# +# ENABLED: +# The Bochs gui creates mouse "events" unless the 'enabled' option is +# set to 0. The hardware emulation itself is not disabled by this. +# Unless you have a particular reason for enabling the mouse by default, +# it is recommended that you leave it off. You can also toggle the mouse +# usage at runtime (RFB, SDL, Win32, wxWidgets and X11 - see below). +# +# TOGGLE: +# The default method to toggle the mouse capture at runtime is to press the +# CTRL key and the middle mouse button ('ctrl+mbutton'). This option allows +# to change the method to 'ctrl+f10' (like DOSBox), 'ctrl+alt' (like QEMU) +# or 'f12' (replaces win32 'legacyF12' option). +# +# Examples: +# mouse: enabled=1 +# mouse: type=imps2, enabled=1 +# mouse: type=serial, enabled=1 +# mouse: enabled=0, toggle=ctrl+f10 +#======================================================================= +mouse: enabled=0 + +#======================================================================= +# private_colormap: Request that the GUI create and use it's own +# non-shared colormap. This colormap will be used +# when in the bochs window. If not enabled, a +# shared colormap scheme may be used. Not implemented +# on all GUI's. +# +# Examples: +# private_colormap: enabled=1 +# private_colormap: enabled=0 +#======================================================================= +private_colormap: enabled=0 + +#======================================================================= +# fullscreen: ONLY IMPLEMENTED ON AMIGA +# Request that Bochs occupy the entire screen instead of a +# window. +# +# Examples: +# fullscreen: enabled=0 +# fullscreen: enabled=1 +#======================================================================= +#fullscreen: enabled=0 +#screenmode: name="sample" + +#======================================================================= +# ne2k: NE2000 compatible ethernet adapter +# +# Format: +# ne2k: enabled=1, ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, +# ethdev=DEVICE, script=SCRIPT, bootrom=BOOTROM +# +# IOADDR, IRQ: You probably won't need to change ioaddr and irq, unless there +# are IRQ conflicts. These arguments are ignored when assign the ne2k to a +# PCI slot. +# +# MAC: The MAC address MUST NOT match the address of any machine on the net. +# Also, the first byte must be an even number (bit 0 set means a multicast +# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast +# address. For the ethertap module, you must use fe:fd:00:00:00:01. There may +# be other restrictions too. To be safe, just use the b0:c4... address. +# +# ETHDEV: The ethdev value is the name of the network interface on your host +# platform. On UNIX machines, you can get the name by running ifconfig. On +# Windows machines, you must run niclist to get the name of the ethdev. +# Niclist source code is in misc/niclist.c and it is included in Windows +# binary releases. +# +# SCRIPT: The script value is optional, and is the name of a script that +# is executed after bochs initialize the network interface. You can use +# this script to configure this network interface, or enable masquerading. +# This is mainly useful for the tun/tap devices that only exist during +# Bochs execution. The network interface name is supplied to the script +# as first parameter. +# +# BOOTROM: The bootrom value is optional, and is the name of the ROM image +# to load. Note that this feature is only implemented for the PCI version of +# the NE2000. +# +# If you don't want to make connections to any physical networks, +# you can use the following 'ethmod's to simulate a virtual network. +# null: All packets are discarded, but logged to a few files. +# vde: Virtual Distributed Ethernet +# vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated. +# The virtual host uses 192.168.10.1. +# DHCP assigns 192.168.10.2 to the guest. +# TFTP uses the 'ethdev' value for the root directory and doesn't +# overwrite files. +# +#======================================================================= +# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx +# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0 +# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0 +# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD +# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0 +# ne2k: ioaddr=0x300, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig +# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0 +# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl" +# ne2k: ioaddr=0x300, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp" +# ne2k: mac=b0:c4:20:00:00:01, ethmod=slirp, script=/usr/local/bin/slirp, bootrom=ne2k_pci.rom + +#======================================================================= +# pcipnic: Bochs/Etherboot pseudo-NIC +# +# Format: +# pcipnic: enabled=1, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT, +# bootrom=BOOTROM +# +# The pseudo-NIC accepts the same syntax (for mac, ethmod, ethdev, script, +# bootrom) and supports the same networking modules as the NE2000 adapter. +#======================================================================= +#pcipnic: enabled=1, mac=b0:c4:20:00:00:00, ethmod=vnet + +#======================================================================= +# e1000: Intel(R) 82540EM Gigabit Ethernet adapter +# +# Format: +# e1000: enabled=1, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT +# bootrom=BOOTROM +# +# The E1000 accepts the same syntax (for mac, ethmod, ethdev, script, bootrom) +# and supports the same networking modules as the NE2000 adapter. +#======================================================================= +#e1000: enabled=1, mac=52:54:00:12:34:56, ethmod=slirp, script=/usr/local/bin/slirp + +#======================================================================= +# USER_SHORTCUT: +# This defines the keyboard shortcut to be sent when you press the "user" +# button in the headerbar. The shortcut string is a combination of maximum +# 3 key names (listed below) separated with a '-' character. +# Valid key names: +# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc", +# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup", +# "plus", "right", "shift", "space", "tab", "up", "win", "print" and "power". +# +# Example: +# user_shortcut: keys=ctrl-alt-del +#======================================================================= +#user_shortcut: keys=ctrl-alt-del + +#======================================================================= +# PCI: +# This option controls the presence of a PCI chipset in Bochs. Currently it only +# supports the i440FX chipset. You can also specify the devices connected to +# PCI slots. Up to 5 slots are available. For these combined PCI/ISA devices +# assigning to slot is mandatory if you want to emulate the PCI model: cirrus, +# ne2k and pcivga. These PCI-only devices are also supported, but they are +# auto-assigned if you don't use the slot configuration: e1000, es1370, pcidev, +# pcipnic, usb_ohci and usb_xhci. +# +# Example: +# pci: enabled=1, chipset=i440fx, slot1=pcivga, slot2=ne2k +#======================================================================= +pci: enabled=1, chipset=i440fx + +#======================================================================= +# USB_UHCI: +# This option controls the presence of the USB root hub which is a part +# of the i440FX PCI chipset. With the portX parameter you can connect devices +# to the hub (currently supported: 'mouse', 'tablet', 'keypad', 'disk', 'cdrom' +# 'hub' and 'printer'). +# +# The optionsX parameter can be used to assign specific options to the device +# connected to the corresponding USB port. Currently this feature is only used +# to set the speed reported by device and by the 'disk' device to specify +# an alternative redolog file of some image modes. +# +# If you connect the mouse or tablet to one of the ports, Bochs forwards the +# mouse movement data to the USB device instead of the selected mouse type. +# When connecting the keypad to one of the ports, Bochs forwards the input of +# the numeric keypad to the USB device instead of the PS/2 keyboard. +# +# To connect a 'flat' mode image as an USB hardisk you can use the 'disk' device +# with the path to the image separated with a colon. To use other disk image modes +# similar to ATA disks the syntax 'disk:mode:filename' must be used (see below). +# +# To emulate an USB cdrom you can use the 'cdrom' device name and the path to +# an ISO image or raw device name also separated with a colon. An option to +# insert/eject media is available in the runtime configuration. +# +# The device name 'hub' connects an external hub with max. 8 ports (default: 4) +# to the root hub. To specify the number of ports you have to add the value +# separated with a colon. Connecting devices to the external hub ports is only +# available in the runtime configuration. +# +# The device 'printer' emulates the HP Deskjet 920C printer. The PCL data is +# sent to a file specified in bochsrc.txt. The current code appends the PCL +# code to the file if the file already existed. It would probably be nice to +# overwrite the file instead, asking user first. +#======================================================================= +#usb_uhci: enabled=1 +#usb_uhci: enabled=1, port1=mouse, port2=disk:usbstick.img +#usb_uhci: enabled=1, port1=hub:7, port2=disk:growing:usbdisk.img +#usb_uhci: enabled=1, port2=disk:undoable:usbdisk.img, options1=journal:redo.log +#usb_uhci: enabled=1, port1=printer:printdata.bin, port2=cdrom:image.iso + +#======================================================================= +# USB_OHCI: +# This option controls the presence of the USB OHCI host controller with a +# 2-port hub. The portX option accepts the same device types with the same +# syntax as the UHCI controller (see above). +#======================================================================= +#usb_ohci: enabled=1 +#usb_ohci: enabled=1, port1=printer:usbprinter.bin + +#======================================================================= +# USB_XHCI: +# This option controls the presence of the experimental USB xHCI host controller +# with a 4-port hub. The portX option accepts the same device types with the +# same syntax as the UHCI controller (see above). +#======================================================================= +#usb_xhci: enabled=1 + +#======================================================================= +# CMOSIMAGE: +# This defines image file that can be loaded into the CMOS RAM at startup. +# The rtc_init parameter controls whether initialize the RTC with values stored +# in the image. By default the time0 argument given to the clock option is used. +# With 'rtc_init=image' the image is the source for the initial time. +# +# Example: +# cmosimage: file=cmos.img, rtc_init=image +#======================================================================= +#cmosimage: file=cmos.img, rtc_init=time0 + +#======================================================================= +# MAGIC_BREAK: +# This enables the "magic breakpoint" feature when using the debugger. +# The useless cpu instruction XCHG BX, BX causes Bochs to enter the +# debugger mode. This might be useful for software development. +# +# Example: +# magic_break: enabled=1 +#======================================================================= +#magic_break: enabled=1 + +#======================================================================= +# PORT_E9_HACK: +# The 0xE9 port doesn't exists in normal ISA architecture. However, we +# define a convention here, to display on the console of the system running +# Bochs anything that is written to it. The idea is to provide debug output +# very early when writing BIOS or OS code for example, without having to +# bother with setting up a serial port or etc. Reading from port 0xE9 will +# will return 0xe9 to let you know if the feature is available. +# Leave this 0 unless you have a reason to use it. +# +# Example: +# port_e9_hack: enabled=1 +#======================================================================= +#port_e9_hack: enabled=1 + +#======================================================================= +# DEBUG_SYMBOLS: +# This loads symbols from the specified file for use in Bochs' internal +# debugger. Symbols are loaded into global context. This is equivalent to +# issuing ldsym debugger command at start up. +# +# Example: +# debug_symbols: file="kernel.sym" +# debug_symbols: file="kernel.sym", offset=0x80000000 +#======================================================================= +#debug_symbols: file="kernel.sym" + +#======================================================================= +# other stuff +#======================================================================= +#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log +#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img +#print_timestamps: enabled=1 + +#------------------------- +# PCI host device mapping +#------------------------- +#pcidev: vendor=0x1234, device=0x5678 + +#======================================================================= +# GDBSTUB: +# Enable GDB stub. See user documentation for details. +# Default value is enabled=0. +#======================================================================= +#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0 + +#======================================================================= +# USER_PLUGIN: +# Load user-defined plugin. This option is available only if Bochs is +# compiled with plugin support. Maximum 8 different plugins are supported. +# See the example in the Bochs sources how to write a plugin device. +#======================================================================= +#user_plugin: name=testdev + +#======================================================================= +# for Macintosh, use the style of pathnames in the following +# examples. +# +# vgaromimage: :bios:VGABIOS-elpin-2.40 +# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000 +# floppya: 1_44=[fd:], status=inserted +#======================================================================= + +#======================================================================= +# MEGS +# Set the number of Megabytes of physical memory you want to emulate. +# The default is 32MB, most OS's won't need more than that. +# The maximum amount of memory supported is 2048Mb. +# The 'MEGS' option is deprecated. Use 'MEMORY' option instead. +#======================================================================= +#megs: 256 +#megs: 128 +#megs: 64 +#megs: 32 +#megs: 16 +#megs: 8 diff --git a/pintos-env/share/man/man1/bochs-dlx.1.gz b/pintos-env/share/man/man1/bochs-dlx.1.gz new file mode 100755 index 0000000..d7a51af Binary files /dev/null and b/pintos-env/share/man/man1/bochs-dlx.1.gz differ diff --git a/pintos-env/share/man/man1/bochs.1.gz b/pintos-env/share/man/man1/bochs.1.gz new file mode 100755 index 0000000..62e5019 Binary files /dev/null and b/pintos-env/share/man/man1/bochs.1.gz differ diff --git a/pintos-env/share/man/man1/bxcommit.1.gz b/pintos-env/share/man/man1/bxcommit.1.gz new file mode 100755 index 0000000..b9b27b0 Binary files /dev/null and b/pintos-env/share/man/man1/bxcommit.1.gz differ diff --git a/pintos-env/share/man/man1/bximage.1.gz b/pintos-env/share/man/man1/bximage.1.gz new file mode 100755 index 0000000..50f6d69 Binary files /dev/null and b/pintos-env/share/man/man1/bximage.1.gz differ diff --git a/pintos-env/share/man/man5/bochsrc.5.gz b/pintos-env/share/man/man5/bochsrc.5.gz new file mode 100755 index 0000000..76a9b87 Binary files /dev/null and b/pintos-env/share/man/man5/bochsrc.5.gz differ