diff options
Diffstat (limited to 'pintos-progos')
634 files changed, 33824 insertions, 0 deletions
diff --git a/pintos-progos/LICENSE b/pintos-progos/LICENSE new file mode 100644 index 0000000..8702541 --- /dev/null +++ b/pintos-progos/LICENSE | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | Pintos, including its documentation, is subject to the following | ||
| 2 | license: | ||
| 3 | |||
| 4 | Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford | ||
| 5 | Jr. University. All rights reserved. | ||
| 6 | |||
| 7 | Permission is hereby granted, free of charge, to any person obtaining | ||
| 8 | a copy of this software and associated documentation files (the | ||
| 9 | "Software"), to deal in the Software without restriction, including | ||
| 10 | without limitation the rights to use, copy, modify, merge, publish, | ||
| 11 | distribute, sublicense, and/or sell copies of the Software, and to | ||
| 12 | permit persons to whom the Software is furnished to do so, subject to | ||
| 13 | the following conditions: | ||
| 14 | |||
| 15 | The above copyright notice and this permission notice shall be | ||
| 16 | included in all copies or substantial portions of the Software. | ||
| 17 | |||
| 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| 21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
| 22 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| 24 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 25 | |||
| 26 | A few individual files in Pintos were originally derived from other | ||
| 27 | projects, but they have been extensively modified for use in Pintos. | ||
| 28 | The original code falls under the original license, and modifications | ||
| 29 | for Pintos are additionally covered by the Pintos license above. | ||
| 30 | |||
| 31 | In particular, code derived from Nachos is subject to the following | ||
| 32 | license: | ||
| 33 | |||
| 34 | /* Copyright (c) 1992-1996 The Regents of the University of California. | ||
| 35 | All rights reserved. | ||
| 36 | |||
| 37 | Permission to use, copy, modify, and distribute this software | ||
| 38 | and its documentation for any purpose, without fee, and | ||
| 39 | without written agreement is hereby granted, provided that the | ||
| 40 | above copyright notice and the following two paragraphs appear | ||
| 41 | in all copies of this software. | ||
| 42 | |||
| 43 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO | ||
| 44 | ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR | ||
| 45 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE | ||
| 46 | AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA | ||
| 47 | HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 48 | |||
| 49 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | ||
| 50 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
| 51 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| 52 | PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" | ||
| 53 | BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO | ||
| 54 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR | ||
| 55 | MODIFICATIONS. | ||
| 56 | */ | ||
| 57 | |||
| 58 | Also, code derived from MIT's 6.828 course code is subject to the | ||
| 59 | following license: | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Copyright (C) 1997 Massachusetts Institute of Technology | ||
| 63 | * | ||
| 64 | * This software is being provided by the copyright holders under the | ||
| 65 | * following license. By obtaining, using and/or copying this software, | ||
| 66 | * you agree that you have read, understood, and will comply with the | ||
| 67 | * following terms and conditions: | ||
| 68 | * | ||
| 69 | * Permission to use, copy, modify, distribute, and sell this software | ||
| 70 | * and its documentation for any purpose and without fee or royalty is | ||
| 71 | * hereby granted, provided that the full text of this NOTICE appears on | ||
| 72 | * ALL copies of the software and documentation or portions thereof, | ||
| 73 | * including modifications, that you make. | ||
| 74 | * | ||
| 75 | * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO | ||
| 76 | * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, | ||
| 77 | * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR | ||
| 78 | * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR | ||
| 79 | * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY | ||
| 80 | * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT | ||
| 81 | * HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR | ||
| 82 | * DOCUMENTATION. | ||
| 83 | * | ||
| 84 | * The name and trademarks of copyright holders may NOT be used in | ||
| 85 | * advertising or publicity pertaining to the software without specific, | ||
| 86 | * written prior permission. Title to copyright in this software and any | ||
| 87 | * associated documentation will at all times remain with copyright | ||
| 88 | * holders. See the file AUTHORS which should have accompanied this software | ||
| 89 | * for a list of all copyright holders. | ||
| 90 | * | ||
| 91 | * This file may be derived from previously copyrighted software. This | ||
| 92 | * copyright applies only to those changes made by the copyright | ||
| 93 | * holders listed in the AUTHORS file. The rest of this file is covered by | ||
| 94 | * the copyright notices, if any, listed below. | ||
| 95 | */ | ||
diff --git a/pintos-progos/Make.config b/pintos-progos/Make.config new file mode 100644 index 0000000..f00e8fc --- /dev/null +++ b/pintos-progos/Make.config | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | SHELL = /bin/sh | ||
| 4 | |||
| 5 | VPATH = $(SRCDIR) | ||
| 6 | |||
| 7 | # Binary utilities. | ||
| 8 | # If the host appears to be x86, use the normal tools. | ||
| 9 | # If it's x86-64, use the compiler and linker in 32-bit mode. | ||
| 10 | # Otherwise assume cross-tools are installed as i386-elf-*. | ||
| 11 | X86 = i.86\|pentium.*\|[pk][56]\|nexgen\|viac3\|6x86\|athlon.*\|i86pc | ||
| 12 | X86_64 = x86_64 | ||
| 13 | ifneq (0, $(shell expr `uname -m` : '$(X86)')) | ||
| 14 | CC = gcc | ||
| 15 | LD = ld | ||
| 16 | OBJCOPY = objcopy | ||
| 17 | else | ||
| 18 | ifneq (0, $(shell expr `uname -m` : '$(X86_64)')) | ||
| 19 | CC = gcc -m32 | ||
| 20 | LD = ld -melf_i386 | ||
| 21 | OBJCOPY = objcopy | ||
| 22 | else | ||
| 23 | CC = i386-elf-gcc | ||
| 24 | LD = i386-elf-ld | ||
| 25 | OBJCOPY = i386-elf-objcopy | ||
| 26 | endif | ||
| 27 | endif | ||
| 28 | |||
| 29 | ifeq ($(strip $(shell command -v $(CC) 2> /dev/null)),) | ||
| 30 | $(warning *** Compiler ($(CC)) not found. Did you set $$PATH properly? Please refer to the Getting Started section in the documentation for details. ***) | ||
| 31 | endif | ||
| 32 | |||
| 33 | # Compiler and assembler invocation. | ||
| 34 | DEFINES = | ||
| 35 | WARNINGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes -Wsystem-headers | ||
| 36 | CFLAGS = -g -msoft-float -O | ||
| 37 | CPPFLAGS = -nostdinc -I$(SRCDIR) -I$(SRCDIR)/lib | ||
| 38 | ASFLAGS = -Wa,--gstabs | ||
| 39 | LDFLAGS = | ||
| 40 | DEPS = -MMD -MF $(@:.o=.d) | ||
| 41 | |||
| 42 | # Turn off -fstack-protector, which we don't support. | ||
| 43 | CFLAGS += -fno-stack-protector | ||
| 44 | |||
| 45 | # Turn off --build-id in the linker, which confuses the Pintos loader. | ||
| 46 | LDFLAGS += -Wl,--build-id=none | ||
| 47 | |||
| 48 | %.o: %.c | ||
| 49 | $(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(DEFINES) $(DEPS) | ||
| 50 | |||
| 51 | %.o: %.S | ||
| 52 | $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) $(DEPS) | ||
diff --git a/pintos-progos/Makefile b/pintos-progos/Makefile new file mode 100644 index 0000000..3ba9194 --- /dev/null +++ b/pintos-progos/Makefile | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | BUILD_SUBDIRS = threads userprog vm filesys intro | ||
| 2 | |||
| 3 | all:: | ||
| 4 | @echo "Run 'make' in subdirectories: $(BUILD_SUBDIRS)." | ||
| 5 | @echo "This top-level make has only 'clean' targets." | ||
| 6 | |||
| 7 | CLEAN_SUBDIRS = $(BUILD_SUBDIRS) examples utils | ||
| 8 | |||
| 9 | clean:: | ||
| 10 | for d in $(CLEAN_SUBDIRS); do $(MAKE) -C $$d $@; done | ||
| 11 | rm -f TAGS tags | ||
| 12 | |||
| 13 | distclean:: clean | ||
| 14 | find . -name '*~' -exec rm '{}' \; | ||
| 15 | |||
| 16 | TAGS_SUBDIRS = $(BUILD_SUBDIRS) devices lib | ||
| 17 | TAGS_SOURCES = find $(TAGS_SUBDIRS) -name \*.[chS] -print | ||
| 18 | |||
| 19 | TAGS:: | ||
| 20 | etags --members `$(TAGS_SOURCES)` | ||
| 21 | |||
| 22 | tags:: | ||
| 23 | ctags -T --no-warn `$(TAGS_SOURCES)` | ||
| 24 | |||
| 25 | cscope.files:: | ||
| 26 | $(TAGS_SOURCES) > cscope.files | ||
| 27 | |||
| 28 | cscope:: cscope.files | ||
| 29 | cscope -b -q -k | ||
diff --git a/pintos-progos/Makefile.build b/pintos-progos/Makefile.build new file mode 100644 index 0000000..e997d27 --- /dev/null +++ b/pintos-progos/Makefile.build | |||
| @@ -0,0 +1,109 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | SRCDIR = ../.. | ||
| 4 | |||
| 5 | all: kernel.bin loader.bin | ||
| 6 | |||
| 7 | include ../../Make.config | ||
| 8 | include ../Make.vars | ||
| 9 | include ../../tests/Make.tests | ||
| 10 | |||
| 11 | # Compiler and assembler options. | ||
| 12 | kernel.bin: CPPFLAGS += -I$(SRCDIR)/lib/kernel | ||
| 13 | |||
| 14 | # Core kernel. | ||
| 15 | threads_SRC = threads/start.S # Startup code. | ||
| 16 | threads_SRC += threads/init.c # Main program. | ||
| 17 | threads_SRC += threads/thread.c # Thread management core. | ||
| 18 | threads_SRC += threads/switch.S # Thread switch routine. | ||
| 19 | threads_SRC += threads/interrupt.c # Interrupt core. | ||
| 20 | threads_SRC += threads/intr-stubs.S # Interrupt stubs. | ||
| 21 | threads_SRC += threads/synch.c # Synchronization. | ||
| 22 | threads_SRC += threads/palloc.c # Page allocator. | ||
| 23 | threads_SRC += threads/malloc.c # Subpage allocator. | ||
| 24 | |||
| 25 | # Device driver code. | ||
| 26 | devices_SRC = devices/pit.c # Programmable interrupt timer chip. | ||
| 27 | devices_SRC += devices/timer.c # Periodic timer device. | ||
| 28 | devices_SRC += devices/kbd.c # Keyboard device. | ||
| 29 | devices_SRC += devices/vga.c # Video device. | ||
| 30 | devices_SRC += devices/serial.c # Serial port device. | ||
| 31 | devices_SRC += devices/block.c # Block device abstraction layer. | ||
| 32 | devices_SRC += devices/partition.c # Partition block device. | ||
| 33 | devices_SRC += devices/ide.c # IDE disk block device. | ||
| 34 | devices_SRC += devices/input.c # Serial and keyboard input. | ||
| 35 | devices_SRC += devices/intq.c # Interrupt queue. | ||
| 36 | devices_SRC += devices/rtc.c # Real-time clock. | ||
| 37 | devices_SRC += devices/shutdown.c # Reboot and power off. | ||
| 38 | devices_SRC += devices/speaker.c # PC speaker. | ||
| 39 | |||
| 40 | # Library code shared between kernel and user programs. | ||
| 41 | lib_SRC = lib/debug.c # Debug helpers. | ||
| 42 | lib_SRC += lib/random.c # Pseudo-random numbers. | ||
| 43 | lib_SRC += lib/stdio.c # I/O library. | ||
| 44 | lib_SRC += lib/stdlib.c # Utility functions. | ||
| 45 | lib_SRC += lib/string.c # String functions. | ||
| 46 | lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC. | ||
| 47 | lib_SRC += lib/ustar.c # Unix standard tar format utilities. | ||
| 48 | |||
| 49 | # Kernel-specific library code. | ||
| 50 | lib/kernel_SRC = lib/kernel/debug.c # Debug helpers. | ||
| 51 | lib/kernel_SRC += lib/kernel/list.c # Doubly-linked lists. | ||
| 52 | lib/kernel_SRC += lib/kernel/bitmap.c # Bitmaps. | ||
| 53 | lib/kernel_SRC += lib/kernel/hash.c # Hash tables. | ||
| 54 | lib/kernel_SRC += lib/kernel/console.c # printf(), putchar(). | ||
| 55 | |||
| 56 | # User process code. | ||
| 57 | userprog_SRC = userprog/process.c # Process loading. | ||
| 58 | userprog_SRC += userprog/pagedir.c # Page directories. | ||
| 59 | userprog_SRC += userprog/exception.c # User exception handler. | ||
| 60 | userprog_SRC += userprog/syscall.c # System call handler. | ||
| 61 | userprog_SRC += userprog/gdt.c # GDT initialization. | ||
| 62 | userprog_SRC += userprog/tss.c # TSS management. | ||
| 63 | |||
| 64 | # No virtual memory code yet. | ||
| 65 | #vm_SRC = vm/file.c # Some file. | ||
| 66 | |||
| 67 | # Filesystem code. | ||
| 68 | filesys_SRC = filesys/filesys.c # Filesystem core. | ||
| 69 | filesys_SRC += filesys/free-map.c # Free sector bitmap. | ||
| 70 | filesys_SRC += filesys/file.c # Files. | ||
| 71 | filesys_SRC += filesys/directory.c # Directories. | ||
| 72 | filesys_SRC += filesys/inode.c # File headers. | ||
| 73 | filesys_SRC += filesys/fsutil.c # Utilities. | ||
| 74 | |||
| 75 | SOURCES = $(foreach dir,$(KERNEL_SUBDIRS),$($(dir)_SRC)) | ||
| 76 | OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES))) | ||
| 77 | DEPENDS = $(patsubst %.o,%.d,$(OBJECTS)) | ||
| 78 | |||
| 79 | threads/kernel.lds.s: CPPFLAGS += -P | ||
| 80 | threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h | ||
| 81 | |||
| 82 | kernel.o: threads/kernel.lds.s $(OBJECTS) | ||
| 83 | $(LD) -T $< -o $@ $(OBJECTS) | ||
| 84 | |||
| 85 | kernel.bin: kernel.o | ||
| 86 | $(OBJCOPY) -R .note -R .comment -S $< $@ | ||
| 87 | |||
| 88 | threads/loader.o: threads/loader.S | ||
| 89 | $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) | ||
| 90 | |||
| 91 | loader.bin: threads/loader.o | ||
| 92 | $(LD) -N -e 0 -Ttext 0x7c00 --oformat binary -o $@ $< | ||
| 93 | |||
| 94 | os.dsk: kernel.bin | ||
| 95 | cat $^ > $@ | ||
| 96 | |||
| 97 | clean:: | ||
| 98 | rm -f $(OBJECTS) $(DEPENDS) | ||
| 99 | rm -f threads/loader.o threads/kernel.lds.s threads/loader.d | ||
| 100 | rm -f kernel.bin.tmp | ||
| 101 | rm -f kernel.o kernel.lds.s | ||
| 102 | rm -f kernel.bin loader.bin | ||
| 103 | rm -f bochsout.txt bochsrc.txt | ||
| 104 | rm -f results grade | ||
| 105 | |||
| 106 | Makefile: $(SRCDIR)/Makefile.build | ||
| 107 | cp $< $@ | ||
| 108 | |||
| 109 | -include $(DEPENDS) | ||
diff --git a/pintos-progos/Makefile.kernel b/pintos-progos/Makefile.kernel new file mode 100644 index 0000000..162a411 --- /dev/null +++ b/pintos-progos/Makefile.kernel | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | all: | ||
| 4 | |||
| 5 | include Make.vars | ||
| 6 | |||
| 7 | DIRS = $(sort $(addprefix build/,$(KERNEL_SUBDIRS) $(TEST_SUBDIRS) lib/user)) | ||
| 8 | |||
| 9 | all grade check: $(DIRS) build/Makefile | ||
| 10 | cd build && $(MAKE) $@ | ||
| 11 | $(DIRS): | ||
| 12 | mkdir -p $@ | ||
| 13 | build/Makefile: ../Makefile.build | ||
| 14 | cp $< $@ | ||
| 15 | |||
| 16 | build/%: $(DIRS) build/Makefile | ||
| 17 | cd build && $(MAKE) $* | ||
| 18 | |||
| 19 | clean: | ||
| 20 | rm -rf build | ||
diff --git a/pintos-progos/Makefile.userprog b/pintos-progos/Makefile.userprog new file mode 100644 index 0000000..0df391a --- /dev/null +++ b/pintos-progos/Makefile.userprog | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | $(PROGS): CPPFLAGS += -I$(SRCDIR)/lib/user -I. | ||
| 4 | |||
| 5 | # Linker flags. | ||
| 6 | $(PROGS): LDFLAGS += -nostdlib -static -Wl,-T,$(LDSCRIPT) | ||
| 7 | $(PROGS): LDSCRIPT = $(SRCDIR)/lib/user/user.lds | ||
| 8 | |||
| 9 | # Library code shared between kernel and user programs. | ||
| 10 | lib_SRC = lib/debug.c # Debug code. | ||
| 11 | lib_SRC += lib/random.c # Pseudo-random numbers. | ||
| 12 | lib_SRC += lib/stdio.c # I/O library. | ||
| 13 | lib_SRC += lib/stdlib.c # Utility functions. | ||
| 14 | lib_SRC += lib/string.c # String functions. | ||
| 15 | lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC. | ||
| 16 | lib_SRC += lib/ustar.c # Unix standard tar format utilities. | ||
| 17 | |||
| 18 | # User level only library code. | ||
| 19 | lib/user_SRC = lib/user/debug.c # Debug helpers. | ||
| 20 | lib/user_SRC += lib/user/syscall.c # System calls. | ||
| 21 | lib/user_SRC += lib/user/console.c # Console code. | ||
| 22 | |||
| 23 | LIB_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(lib_SRC) $(lib/user_SRC))) | ||
| 24 | LIB_DEP = $(patsubst %.o,%.d,$(LIB_OBJ)) | ||
| 25 | LIB = lib/user/entry.o libc.a | ||
| 26 | |||
| 27 | PROGS_SRC = $(foreach prog,$(PROGS),$($(prog)_SRC)) | ||
| 28 | PROGS_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PROGS_SRC))) | ||
| 29 | PROGS_DEP = $(patsubst %.o,%.d,$(PROGS_OBJ)) | ||
| 30 | |||
| 31 | all: $(PROGS) | ||
| 32 | |||
| 33 | define TEMPLATE | ||
| 34 | $(1)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(1)_SRC))) | ||
| 35 | $(1): $$($(1)_OBJ) $$(LIB) $$(LDSCRIPT) | ||
| 36 | $$(CC) $$(LDFLAGS) $$($(1)_OBJ) $$(LIB) -o $$@ | ||
| 37 | endef | ||
| 38 | |||
| 39 | $(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog)))) | ||
| 40 | |||
| 41 | libc.a: $(LIB_OBJ) | ||
| 42 | rm -f $@ | ||
| 43 | ar r $@ $^ | ||
| 44 | ranlib $@ | ||
| 45 | |||
| 46 | clean:: | ||
| 47 | rm -f $(PROGS) $(PROGS_OBJ) $(PROGS_DEP) | ||
| 48 | rm -f $(LIB_DEP) $(LIB_OBJ) lib/user/entry.[do] libc.a | ||
| 49 | |||
| 50 | .PHONY: all clean | ||
| 51 | |||
| 52 | -include $(LIB_DEP) $(PROGS_DEP) | ||
diff --git a/pintos-progos/devices/block.c b/pintos-progos/devices/block.c new file mode 100644 index 0000000..a3acec1 --- /dev/null +++ b/pintos-progos/devices/block.c | |||
| @@ -0,0 +1,223 @@ | |||
| 1 | #include "devices/block.h" | ||
| 2 | #include <list.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include "devices/ide.h" | ||
| 6 | #include "threads/malloc.h" | ||
| 7 | |||
| 8 | /* A block device. */ | ||
| 9 | struct block | ||
| 10 | { | ||
| 11 | struct list_elem list_elem; /* Element in all_blocks. */ | ||
| 12 | |||
| 13 | char name[16]; /* Block device name. */ | ||
| 14 | enum block_type type; /* Type of block device. */ | ||
| 15 | block_sector_t size; /* Size in sectors. */ | ||
| 16 | |||
| 17 | const struct block_operations *ops; /* Driver operations. */ | ||
| 18 | void *aux; /* Extra data owned by driver. */ | ||
| 19 | |||
| 20 | unsigned long long read_cnt; /* Number of sectors read. */ | ||
| 21 | unsigned long long write_cnt; /* Number of sectors written. */ | ||
| 22 | }; | ||
| 23 | |||
| 24 | /* List of all block devices. */ | ||
| 25 | static struct list all_blocks = LIST_INITIALIZER (all_blocks); | ||
| 26 | |||
| 27 | /* The block block assigned to each Pintos role. */ | ||
| 28 | static struct block *block_by_role[BLOCK_ROLE_CNT]; | ||
| 29 | |||
| 30 | static struct block *list_elem_to_block (struct list_elem *); | ||
| 31 | |||
| 32 | /* Returns a human-readable name for the given block device | ||
| 33 | TYPE. */ | ||
| 34 | const char * | ||
| 35 | block_type_name (enum block_type type) | ||
| 36 | { | ||
| 37 | static const char *block_type_names[BLOCK_CNT] = | ||
| 38 | { | ||
| 39 | "kernel", | ||
| 40 | "filesys", | ||
| 41 | "scratch", | ||
| 42 | "swap", | ||
| 43 | "raw", | ||
| 44 | "foreign", | ||
| 45 | }; | ||
| 46 | |||
| 47 | ASSERT (type < BLOCK_CNT); | ||
| 48 | return block_type_names[type]; | ||
| 49 | } | ||
| 50 | |||
| 51 | /* Returns the block device fulfilling the given ROLE, or a null | ||
| 52 | pointer if no block device has been assigned that role. */ | ||
| 53 | struct block * | ||
| 54 | block_get_role (enum block_type role) | ||
| 55 | { | ||
| 56 | ASSERT (role < BLOCK_ROLE_CNT); | ||
| 57 | return block_by_role[role]; | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Assigns BLOCK the given ROLE. */ | ||
| 61 | void | ||
| 62 | block_set_role (enum block_type role, struct block *block) | ||
| 63 | { | ||
| 64 | ASSERT (role < BLOCK_ROLE_CNT); | ||
| 65 | block_by_role[role] = block; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Returns the first block device in kernel probe order, or a | ||
| 69 | null pointer if no block devices are registered. */ | ||
| 70 | struct block * | ||
| 71 | block_first (void) | ||
| 72 | { | ||
| 73 | return list_elem_to_block (list_begin (&all_blocks)); | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Returns the block device following BLOCK in kernel probe | ||
| 77 | order, or a null pointer if BLOCK is the last block device. */ | ||
| 78 | struct block * | ||
| 79 | block_next (struct block *block) | ||
| 80 | { | ||
| 81 | return list_elem_to_block (list_next (&block->list_elem)); | ||
| 82 | } | ||
| 83 | |||
| 84 | /* Returns the block device with the given NAME, or a null | ||
| 85 | pointer if no block device has that name. */ | ||
| 86 | struct block * | ||
| 87 | block_get_by_name (const char *name) | ||
| 88 | { | ||
| 89 | struct list_elem *e; | ||
| 90 | |||
| 91 | for (e = list_begin (&all_blocks); e != list_end (&all_blocks); | ||
| 92 | e = list_next (e)) | ||
| 93 | { | ||
| 94 | struct block *block = list_entry (e, struct block, list_elem); | ||
| 95 | if (!strcmp (name, block->name)) | ||
| 96 | return block; | ||
| 97 | } | ||
| 98 | |||
| 99 | return NULL; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Verifies that SECTOR is a valid offset within BLOCK. | ||
| 103 | Panics if not. */ | ||
| 104 | static void | ||
| 105 | check_sector (struct block *block, block_sector_t sector) | ||
| 106 | { | ||
| 107 | if (sector >= block->size) | ||
| 108 | { | ||
| 109 | /* We do not use ASSERT because we want to panic here | ||
| 110 | regardless of whether NDEBUG is defined. */ | ||
| 111 | PANIC ("Access past end of device %s (sector=%"PRDSNu", " | ||
| 112 | "size=%"PRDSNu")\n", block_name (block), sector, block->size); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Reads sector SECTOR from BLOCK into BUFFER, which must | ||
| 117 | have room for BLOCK_SECTOR_SIZE bytes. | ||
| 118 | Internally synchronizes accesses to block devices, so external | ||
| 119 | per-block device locking is unneeded. */ | ||
| 120 | void | ||
| 121 | block_read (struct block *block, block_sector_t sector, void *buffer) | ||
| 122 | { | ||
| 123 | check_sector (block, sector); | ||
| 124 | block->ops->read (block->aux, sector, buffer); | ||
| 125 | block->read_cnt++; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* Write sector SECTOR to BLOCK from BUFFER, which must contain | ||
| 129 | BLOCK_SECTOR_SIZE bytes. Returns after the block device has | ||
| 130 | acknowledged receiving the data. | ||
| 131 | Internally synchronizes accesses to block devices, so external | ||
| 132 | per-block device locking is unneeded. */ | ||
| 133 | void | ||
| 134 | block_write (struct block *block, block_sector_t sector, const void *buffer) | ||
| 135 | { | ||
| 136 | check_sector (block, sector); | ||
| 137 | ASSERT (block->type != BLOCK_FOREIGN); | ||
| 138 | block->ops->write (block->aux, sector, buffer); | ||
| 139 | block->write_cnt++; | ||
| 140 | } | ||
| 141 | |||
| 142 | /* Returns the number of sectors in BLOCK. */ | ||
| 143 | block_sector_t | ||
| 144 | block_size (struct block *block) | ||
| 145 | { | ||
| 146 | return block->size; | ||
| 147 | } | ||
| 148 | |||
| 149 | /* Returns BLOCK's name (e.g. "hda"). */ | ||
| 150 | const char * | ||
| 151 | block_name (struct block *block) | ||
| 152 | { | ||
| 153 | return block->name; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Returns BLOCK's type. */ | ||
| 157 | enum block_type | ||
| 158 | block_type (struct block *block) | ||
| 159 | { | ||
| 160 | return block->type; | ||
| 161 | } | ||
| 162 | |||
| 163 | /* Prints statistics for each block device used for a Pintos role. */ | ||
| 164 | void | ||
| 165 | block_print_stats (void) | ||
| 166 | { | ||
| 167 | int i; | ||
| 168 | |||
| 169 | for (i = 0; i < BLOCK_ROLE_CNT; i++) | ||
| 170 | { | ||
| 171 | struct block *block = block_by_role[i]; | ||
| 172 | if (block != NULL) | ||
| 173 | { | ||
| 174 | printf ("%s (%s): %llu reads, %llu writes\n", | ||
| 175 | block->name, block_type_name (block->type), | ||
| 176 | block->read_cnt, block->write_cnt); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | } | ||
| 180 | |||
| 181 | /* Registers a new block device with the given NAME. If | ||
| 182 | EXTRA_INFO is non-null, it is printed as part of a user | ||
| 183 | message. The block device's SIZE in sectors and its TYPE must | ||
| 184 | be provided, as well as the it operation functions OPS, which | ||
| 185 | will be passed AUX in each function call. */ | ||
| 186 | struct block * | ||
| 187 | block_register (const char *name, enum block_type type, | ||
| 188 | const char *extra_info, block_sector_t size, | ||
| 189 | const struct block_operations *ops, void *aux) | ||
| 190 | { | ||
| 191 | struct block *block = malloc (sizeof *block); | ||
| 192 | if (block == NULL) | ||
| 193 | PANIC ("Failed to allocate memory for block device descriptor"); | ||
| 194 | |||
| 195 | list_push_back (&all_blocks, &block->list_elem); | ||
| 196 | strlcpy (block->name, name, sizeof block->name); | ||
| 197 | block->type = type; | ||
| 198 | block->size = size; | ||
| 199 | block->ops = ops; | ||
| 200 | block->aux = aux; | ||
| 201 | block->read_cnt = 0; | ||
| 202 | block->write_cnt = 0; | ||
| 203 | |||
| 204 | printf ("%s: %'"PRDSNu" sectors (", block->name, block->size); | ||
| 205 | print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE); | ||
| 206 | printf (")"); | ||
| 207 | if (extra_info != NULL) | ||
| 208 | printf (", %s", extra_info); | ||
| 209 | printf ("\n"); | ||
| 210 | |||
| 211 | return block; | ||
| 212 | } | ||
| 213 | |||
| 214 | /* Returns the block device corresponding to LIST_ELEM, or a null | ||
| 215 | pointer if LIST_ELEM is the list end of all_blocks. */ | ||
| 216 | static struct block * | ||
| 217 | list_elem_to_block (struct list_elem *list_elem) | ||
| 218 | { | ||
| 219 | return (list_elem != list_end (&all_blocks) | ||
| 220 | ? list_entry (list_elem, struct block, list_elem) | ||
| 221 | : NULL); | ||
| 222 | } | ||
| 223 | |||
diff --git a/pintos-progos/devices/block.h b/pintos-progos/devices/block.h new file mode 100644 index 0000000..21732d6 --- /dev/null +++ b/pintos-progos/devices/block.h | |||
| @@ -0,0 +1,74 @@ | |||
| 1 | #ifndef DEVICES_BLOCK_H | ||
| 2 | #define DEVICES_BLOCK_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <inttypes.h> | ||
| 6 | |||
| 7 | /* Size of a block device sector in bytes. | ||
| 8 | All IDE disks use this sector size, as do most USB and SCSI | ||
| 9 | disks. It's not worth it to try to cater to other sector | ||
| 10 | sizes in Pintos (yet). */ | ||
| 11 | #define BLOCK_SECTOR_SIZE 512 | ||
| 12 | |||
| 13 | /* Index of a block device sector. | ||
| 14 | Good enough for devices up to 2 TB. */ | ||
| 15 | typedef uint32_t block_sector_t; | ||
| 16 | |||
| 17 | /* Format specifier for printf(), e.g.: | ||
| 18 | printf ("sector=%"PRDSNu"\n", sector); */ | ||
| 19 | #define PRDSNu PRIu32 | ||
| 20 | |||
| 21 | /* Higher-level interface for file systems, etc. */ | ||
| 22 | |||
| 23 | struct block; | ||
| 24 | |||
| 25 | /* Type of a block device. */ | ||
| 26 | enum block_type | ||
| 27 | { | ||
| 28 | /* Block device types that play a role in Pintos. */ | ||
| 29 | BLOCK_KERNEL, /* Pintos OS kernel. */ | ||
| 30 | BLOCK_FILESYS, /* File system. */ | ||
| 31 | BLOCK_SCRATCH, /* Scratch. */ | ||
| 32 | BLOCK_SWAP, /* Swap. */ | ||
| 33 | BLOCK_ROLE_CNT, | ||
| 34 | |||
| 35 | /* Other kinds of block devices that Pintos may see but does | ||
| 36 | not interact with. */ | ||
| 37 | BLOCK_RAW = BLOCK_ROLE_CNT, /* "Raw" device with unidentified contents. */ | ||
| 38 | BLOCK_FOREIGN, /* Owned by non-Pintos operating system. */ | ||
| 39 | BLOCK_CNT /* Number of Pintos block types. */ | ||
| 40 | }; | ||
| 41 | |||
| 42 | const char *block_type_name (enum block_type); | ||
| 43 | |||
| 44 | /* Finding block devices. */ | ||
| 45 | struct block *block_get_role (enum block_type); | ||
| 46 | void block_set_role (enum block_type, struct block *); | ||
| 47 | struct block *block_get_by_name (const char *name); | ||
| 48 | |||
| 49 | struct block *block_first (void); | ||
| 50 | struct block *block_next (struct block *); | ||
| 51 | |||
| 52 | /* Block device operations. */ | ||
| 53 | block_sector_t block_size (struct block *); | ||
| 54 | void block_read (struct block *, block_sector_t, void *); | ||
| 55 | void block_write (struct block *, block_sector_t, const void *); | ||
| 56 | const char *block_name (struct block *); | ||
| 57 | enum block_type block_type (struct block *); | ||
| 58 | |||
| 59 | /* Statistics. */ | ||
| 60 | void block_print_stats (void); | ||
| 61 | |||
| 62 | /* Lower-level interface to block device drivers. */ | ||
| 63 | |||
| 64 | struct block_operations | ||
| 65 | { | ||
| 66 | void (*read) (void *aux, block_sector_t, void *buffer); | ||
| 67 | void (*write) (void *aux, block_sector_t, const void *buffer); | ||
| 68 | }; | ||
| 69 | |||
| 70 | struct block *block_register (const char *name, enum block_type, | ||
| 71 | const char *extra_info, block_sector_t size, | ||
| 72 | const struct block_operations *, void *aux); | ||
| 73 | |||
| 74 | #endif /* devices/block.h */ | ||
diff --git a/pintos-progos/devices/ide.c b/pintos-progos/devices/ide.c new file mode 100644 index 0000000..2cc0292 --- /dev/null +++ b/pintos-progos/devices/ide.c | |||
| @@ -0,0 +1,527 @@ | |||
| 1 | #include "devices/ide.h" | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "devices/block.h" | ||
| 7 | #include "devices/partition.h" | ||
| 8 | #include "devices/timer.h" | ||
| 9 | #include "threads/io.h" | ||
| 10 | #include "threads/interrupt.h" | ||
| 11 | #include "threads/synch.h" | ||
| 12 | |||
| 13 | /* The code in this file is an interface to an ATA (IDE) | ||
| 14 | controller. It attempts to comply to [ATA-3]. */ | ||
| 15 | |||
| 16 | /* ATA command block port addresses. */ | ||
| 17 | #define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */ | ||
| 18 | #define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */ | ||
| 19 | #define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) /* Sector Count. */ | ||
| 20 | #define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) /* LBA 0:7. */ | ||
| 21 | #define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) /* LBA 15:8. */ | ||
| 22 | #define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) /* LBA 23:16. */ | ||
| 23 | #define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) /* Device/LBA 27:24. */ | ||
| 24 | #define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) /* Status (r/o). */ | ||
| 25 | #define reg_command(CHANNEL) reg_status (CHANNEL) /* Command (w/o). */ | ||
| 26 | |||
| 27 | /* ATA control block port addresses. | ||
| 28 | (If we supported non-legacy ATA controllers this would not be | ||
| 29 | flexible enough, but it's fine for what we do.) */ | ||
| 30 | #define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) /* Control (w/o). */ | ||
| 31 | #define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alt Status (r/o). */ | ||
| 32 | |||
| 33 | /* Alternate Status Register bits. */ | ||
| 34 | #define STA_BSY 0x80 /* Busy. */ | ||
| 35 | #define STA_DRDY 0x40 /* Device Ready. */ | ||
| 36 | #define STA_DRQ 0x08 /* Data Request. */ | ||
| 37 | |||
| 38 | /* Control Register bits. */ | ||
| 39 | #define CTL_SRST 0x04 /* Software Reset. */ | ||
| 40 | |||
| 41 | /* Device Register bits. */ | ||
| 42 | #define DEV_MBS 0xa0 /* Must be set. */ | ||
| 43 | #define DEV_LBA 0x40 /* Linear based addressing. */ | ||
| 44 | #define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */ | ||
| 45 | |||
| 46 | /* Commands. | ||
| 47 | Many more are defined but this is the small subset that we | ||
| 48 | use. */ | ||
| 49 | #define CMD_IDENTIFY_DEVICE 0xec /* IDENTIFY DEVICE. */ | ||
| 50 | #define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */ | ||
| 51 | #define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */ | ||
| 52 | |||
| 53 | /* An ATA device. */ | ||
| 54 | struct ata_disk | ||
| 55 | { | ||
| 56 | char name[8]; /* Name, e.g. "hda". */ | ||
| 57 | struct channel *channel; /* Channel that disk is attached to. */ | ||
| 58 | int dev_no; /* Device 0 or 1 for master or slave. */ | ||
| 59 | bool is_ata; /* Is device an ATA disk? */ | ||
| 60 | }; | ||
| 61 | |||
| 62 | /* An ATA channel (aka controller). | ||
| 63 | Each channel can control up to two disks. */ | ||
| 64 | struct channel | ||
| 65 | { | ||
| 66 | char name[8]; /* Name, e.g. "ide0". */ | ||
| 67 | uint16_t reg_base; /* Base I/O port. */ | ||
| 68 | uint8_t irq; /* Interrupt in use. */ | ||
| 69 | |||
| 70 | struct lock lock; /* Must acquire to access the controller. */ | ||
| 71 | bool expecting_interrupt; /* True if an interrupt is expected, false if | ||
| 72 | any interrupt would be spurious. */ | ||
| 73 | struct semaphore completion_wait; /* Up'd by interrupt handler. */ | ||
| 74 | |||
| 75 | struct ata_disk devices[2]; /* The devices on this channel. */ | ||
| 76 | }; | ||
| 77 | |||
| 78 | /* We support the two "legacy" ATA channels found in a standard PC. */ | ||
| 79 | #define CHANNEL_CNT 2 | ||
| 80 | static struct channel channels[CHANNEL_CNT]; | ||
| 81 | |||
| 82 | static struct block_operations ide_operations; | ||
| 83 | |||
| 84 | static void reset_channel (struct channel *); | ||
| 85 | static bool check_device_type (struct ata_disk *); | ||
| 86 | static void identify_ata_device (struct ata_disk *); | ||
| 87 | |||
| 88 | static void select_sector (struct ata_disk *, block_sector_t); | ||
| 89 | static void issue_pio_command (struct channel *, uint8_t command); | ||
| 90 | static void input_sector (struct channel *, void *); | ||
| 91 | static void output_sector (struct channel *, const void *); | ||
| 92 | |||
| 93 | static void wait_until_idle (const struct ata_disk *); | ||
| 94 | static bool wait_while_busy (const struct ata_disk *); | ||
| 95 | static void select_device (const struct ata_disk *); | ||
| 96 | static void select_device_wait (const struct ata_disk *); | ||
| 97 | |||
| 98 | static void interrupt_handler (struct intr_frame *); | ||
| 99 | |||
| 100 | /* Initialize the disk subsystem and detect disks. */ | ||
| 101 | void | ||
| 102 | ide_init (void) | ||
| 103 | { | ||
| 104 | size_t chan_no; | ||
| 105 | |||
| 106 | for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++) | ||
| 107 | { | ||
| 108 | struct channel *c = &channels[chan_no]; | ||
| 109 | int dev_no; | ||
| 110 | |||
| 111 | /* Initialize channel. */ | ||
| 112 | snprintf (c->name, sizeof c->name, "ide%zu", chan_no); | ||
| 113 | switch (chan_no) | ||
| 114 | { | ||
| 115 | case 0: | ||
| 116 | c->reg_base = 0x1f0; | ||
| 117 | c->irq = 14 + 0x20; | ||
| 118 | break; | ||
| 119 | case 1: | ||
| 120 | c->reg_base = 0x170; | ||
| 121 | c->irq = 15 + 0x20; | ||
| 122 | break; | ||
| 123 | default: | ||
| 124 | NOT_REACHED (); | ||
| 125 | } | ||
| 126 | lock_init (&c->lock); | ||
| 127 | c->expecting_interrupt = false; | ||
| 128 | sema_init (&c->completion_wait, 0); | ||
| 129 | |||
| 130 | /* Initialize devices. */ | ||
| 131 | for (dev_no = 0; dev_no < 2; dev_no++) | ||
| 132 | { | ||
| 133 | struct ata_disk *d = &c->devices[dev_no]; | ||
| 134 | snprintf (d->name, sizeof d->name, | ||
| 135 | "hd%c", 'a' + chan_no * 2 + dev_no); | ||
| 136 | d->channel = c; | ||
| 137 | d->dev_no = dev_no; | ||
| 138 | d->is_ata = false; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Register interrupt handler. */ | ||
| 142 | intr_register_ext (c->irq, interrupt_handler, c->name); | ||
| 143 | |||
| 144 | /* Reset hardware. */ | ||
| 145 | reset_channel (c); | ||
| 146 | |||
| 147 | /* Distinguish ATA hard disks from other devices. */ | ||
| 148 | if (check_device_type (&c->devices[0])) | ||
| 149 | check_device_type (&c->devices[1]); | ||
| 150 | |||
| 151 | /* Read hard disk identity information. */ | ||
| 152 | for (dev_no = 0; dev_no < 2; dev_no++) | ||
| 153 | if (c->devices[dev_no].is_ata) | ||
| 154 | identify_ata_device (&c->devices[dev_no]); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /* Disk detection and identification. */ | ||
| 159 | |||
| 160 | static char *descramble_ata_string (char *, int size); | ||
| 161 | |||
| 162 | /* Resets an ATA channel and waits for any devices present on it | ||
| 163 | to finish the reset. */ | ||
| 164 | static void | ||
| 165 | reset_channel (struct channel *c) | ||
| 166 | { | ||
| 167 | bool present[2]; | ||
| 168 | int dev_no; | ||
| 169 | |||
| 170 | /* The ATA reset sequence depends on which devices are present, | ||
| 171 | so we start by detecting device presence. */ | ||
| 172 | for (dev_no = 0; dev_no < 2; dev_no++) | ||
| 173 | { | ||
| 174 | struct ata_disk *d = &c->devices[dev_no]; | ||
| 175 | |||
| 176 | select_device (d); | ||
| 177 | |||
| 178 | outb (reg_nsect (c), 0x55); | ||
| 179 | outb (reg_lbal (c), 0xaa); | ||
| 180 | |||
| 181 | outb (reg_nsect (c), 0xaa); | ||
| 182 | outb (reg_lbal (c), 0x55); | ||
| 183 | |||
| 184 | outb (reg_nsect (c), 0x55); | ||
| 185 | outb (reg_lbal (c), 0xaa); | ||
| 186 | |||
| 187 | present[dev_no] = (inb (reg_nsect (c)) == 0x55 | ||
| 188 | && inb (reg_lbal (c)) == 0xaa); | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Issue soft reset sequence, which selects device 0 as a side effect. | ||
| 192 | Also enable interrupts. */ | ||
| 193 | outb (reg_ctl (c), 0); | ||
| 194 | timer_usleep (10); | ||
| 195 | outb (reg_ctl (c), CTL_SRST); | ||
| 196 | timer_usleep (10); | ||
| 197 | outb (reg_ctl (c), 0); | ||
| 198 | |||
| 199 | timer_msleep (150); | ||
| 200 | |||
| 201 | /* Wait for device 0 to clear BSY. */ | ||
| 202 | if (present[0]) | ||
| 203 | { | ||
| 204 | select_device (&c->devices[0]); | ||
| 205 | wait_while_busy (&c->devices[0]); | ||
| 206 | } | ||
| 207 | |||
| 208 | /* Wait for device 1 to clear BSY. */ | ||
| 209 | if (present[1]) | ||
| 210 | { | ||
| 211 | int i; | ||
| 212 | |||
| 213 | select_device (&c->devices[1]); | ||
| 214 | for (i = 0; i < 3000; i++) | ||
| 215 | { | ||
| 216 | if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1) | ||
| 217 | break; | ||
| 218 | timer_msleep (10); | ||
| 219 | } | ||
| 220 | wait_while_busy (&c->devices[1]); | ||
| 221 | } | ||
| 222 | } | ||
| 223 | |||
| 224 | /* Checks whether device D is an ATA disk and sets D's is_ata | ||
| 225 | member appropriately. If D is device 0 (master), returns true | ||
| 226 | if it's possible that a slave (device 1) exists on this | ||
| 227 | channel. If D is device 1 (slave), the return value is not | ||
| 228 | meaningful. */ | ||
| 229 | static bool | ||
| 230 | check_device_type (struct ata_disk *d) | ||
| 231 | { | ||
| 232 | struct channel *c = d->channel; | ||
| 233 | uint8_t error, lbam, lbah, status; | ||
| 234 | |||
| 235 | select_device (d); | ||
| 236 | |||
| 237 | error = inb (reg_error (c)); | ||
| 238 | lbam = inb (reg_lbam (c)); | ||
| 239 | lbah = inb (reg_lbah (c)); | ||
| 240 | status = inb (reg_status (c)); | ||
| 241 | |||
| 242 | if ((error != 1 && (error != 0x81 || d->dev_no == 1)) | ||
| 243 | || (status & STA_DRDY) == 0 | ||
| 244 | || (status & STA_BSY) != 0) | ||
| 245 | { | ||
| 246 | d->is_ata = false; | ||
| 247 | return error != 0x81; | ||
| 248 | } | ||
| 249 | else | ||
| 250 | { | ||
| 251 | d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3); | ||
| 252 | return true; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | /* Sends an IDENTIFY DEVICE command to disk D and reads the | ||
| 257 | response. Registers the disk with the block device | ||
| 258 | layer. */ | ||
| 259 | static void | ||
| 260 | identify_ata_device (struct ata_disk *d) | ||
| 261 | { | ||
| 262 | struct channel *c = d->channel; | ||
| 263 | char id[BLOCK_SECTOR_SIZE]; | ||
| 264 | block_sector_t capacity; | ||
| 265 | char *model, *serial; | ||
| 266 | char extra_info[128]; | ||
| 267 | struct block *block; | ||
| 268 | |||
| 269 | ASSERT (d->is_ata); | ||
| 270 | |||
| 271 | /* Send the IDENTIFY DEVICE command, wait for an interrupt | ||
| 272 | indicating the device's response is ready, and read the data | ||
| 273 | into our buffer. */ | ||
| 274 | select_device_wait (d); | ||
| 275 | issue_pio_command (c, CMD_IDENTIFY_DEVICE); | ||
| 276 | sema_down (&c->completion_wait); | ||
| 277 | if (!wait_while_busy (d)) | ||
| 278 | { | ||
| 279 | d->is_ata = false; | ||
| 280 | return; | ||
| 281 | } | ||
| 282 | input_sector (c, id); | ||
| 283 | |||
| 284 | /* Calculate capacity. | ||
| 285 | Read model name and serial number. */ | ||
| 286 | capacity = *(uint32_t *) &id[60 * 2]; | ||
| 287 | model = descramble_ata_string (&id[10 * 2], 20); | ||
| 288 | serial = descramble_ata_string (&id[27 * 2], 40); | ||
| 289 | snprintf (extra_info, sizeof extra_info, | ||
| 290 | "model \"%s\", serial \"%s\"", model, serial); | ||
| 291 | |||
| 292 | /* Disable access to IDE disks over 1 GB, which are likely | ||
| 293 | physical IDE disks rather than virtual ones. If we don't | ||
| 294 | allow access to those, we're less likely to scribble on | ||
| 295 | someone's important data. You can disable this check by | ||
| 296 | hand if you really want to do so. */ | ||
| 297 | if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE) | ||
| 298 | { | ||
| 299 | printf ("%s: ignoring ", d->name); | ||
| 300 | print_human_readable_size (capacity * 512); | ||
| 301 | printf ("disk for safety\n"); | ||
| 302 | d->is_ata = false; | ||
| 303 | return; | ||
| 304 | } | ||
| 305 | |||
| 306 | /* Register. */ | ||
| 307 | block = block_register (d->name, BLOCK_RAW, extra_info, capacity, | ||
| 308 | &ide_operations, d); | ||
| 309 | partition_scan (block); | ||
| 310 | } | ||
| 311 | |||
| 312 | /* Translates STRING, which consists of SIZE bytes in a funky | ||
| 313 | format, into a null-terminated string in-place. Drops | ||
| 314 | trailing whitespace and null bytes. Returns STRING. */ | ||
| 315 | static char * | ||
| 316 | descramble_ata_string (char *string, int size) | ||
| 317 | { | ||
| 318 | int i; | ||
| 319 | |||
| 320 | /* Swap all pairs of bytes. */ | ||
| 321 | for (i = 0; i + 1 < size; i += 2) | ||
| 322 | { | ||
| 323 | char tmp = string[i]; | ||
| 324 | string[i] = string[i + 1]; | ||
| 325 | string[i + 1] = tmp; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* Find the last non-white, non-null character. */ | ||
| 329 | for (size--; size > 0; size--) | ||
| 330 | { | ||
| 331 | int c = string[size - 1]; | ||
| 332 | if (c != '\0' && !isspace (c)) | ||
| 333 | break; | ||
| 334 | } | ||
| 335 | string[size] = '\0'; | ||
| 336 | |||
| 337 | return string; | ||
| 338 | } | ||
| 339 | |||
| 340 | /* Reads sector SEC_NO from disk D into BUFFER, which must have | ||
| 341 | room for BLOCK_SECTOR_SIZE bytes. | ||
| 342 | Internally synchronizes accesses to disks, so external | ||
| 343 | per-disk locking is unneeded. */ | ||
| 344 | static void | ||
| 345 | ide_read (void *d_, block_sector_t sec_no, void *buffer) | ||
| 346 | { | ||
| 347 | struct ata_disk *d = d_; | ||
| 348 | struct channel *c = d->channel; | ||
| 349 | lock_acquire (&c->lock); | ||
| 350 | select_sector (d, sec_no); | ||
| 351 | issue_pio_command (c, CMD_READ_SECTOR_RETRY); | ||
| 352 | sema_down (&c->completion_wait); | ||
| 353 | if (!wait_while_busy (d)) | ||
| 354 | PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no); | ||
| 355 | input_sector (c, buffer); | ||
| 356 | lock_release (&c->lock); | ||
| 357 | } | ||
| 358 | |||
| 359 | /* Write sector SEC_NO to disk D from BUFFER, which must contain | ||
| 360 | BLOCK_SECTOR_SIZE bytes. Returns after the disk has | ||
| 361 | acknowledged receiving the data. | ||
| 362 | Internally synchronizes accesses to disks, so external | ||
| 363 | per-disk locking is unneeded. */ | ||
| 364 | static void | ||
| 365 | ide_write (void *d_, block_sector_t sec_no, const void *buffer) | ||
| 366 | { | ||
| 367 | struct ata_disk *d = d_; | ||
| 368 | struct channel *c = d->channel; | ||
| 369 | lock_acquire (&c->lock); | ||
| 370 | select_sector (d, sec_no); | ||
| 371 | issue_pio_command (c, CMD_WRITE_SECTOR_RETRY); | ||
| 372 | if (!wait_while_busy (d)) | ||
| 373 | PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no); | ||
| 374 | output_sector (c, buffer); | ||
| 375 | sema_down (&c->completion_wait); | ||
| 376 | lock_release (&c->lock); | ||
| 377 | } | ||
| 378 | |||
| 379 | static struct block_operations ide_operations = | ||
| 380 | { | ||
| 381 | ide_read, | ||
| 382 | ide_write | ||
| 383 | }; | ||
| 384 | |||
| 385 | /* Selects device D, waiting for it to become ready, and then | ||
| 386 | writes SEC_NO to the disk's sector selection registers. (We | ||
| 387 | use LBA mode.) */ | ||
| 388 | static void | ||
| 389 | select_sector (struct ata_disk *d, block_sector_t sec_no) | ||
| 390 | { | ||
| 391 | struct channel *c = d->channel; | ||
| 392 | |||
| 393 | ASSERT (sec_no < (1UL << 28)); | ||
| 394 | |||
| 395 | select_device_wait (d); | ||
| 396 | outb (reg_nsect (c), 1); | ||
| 397 | outb (reg_lbal (c), sec_no); | ||
| 398 | outb (reg_lbam (c), sec_no >> 8); | ||
| 399 | outb (reg_lbah (c), (sec_no >> 16)); | ||
| 400 | outb (reg_device (c), | ||
| 401 | DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24)); | ||
| 402 | } | ||
| 403 | |||
| 404 | /* Writes COMMAND to channel C and prepares for receiving a | ||
| 405 | completion interrupt. */ | ||
| 406 | static void | ||
| 407 | issue_pio_command (struct channel *c, uint8_t command) | ||
| 408 | { | ||
| 409 | /* Interrupts must be enabled or our semaphore will never be | ||
| 410 | up'd by the completion handler. */ | ||
| 411 | ASSERT (intr_get_level () == INTR_ON); | ||
| 412 | |||
| 413 | c->expecting_interrupt = true; | ||
| 414 | outb (reg_command (c), command); | ||
| 415 | } | ||
| 416 | |||
| 417 | /* Reads a sector from channel C's data register in PIO mode into | ||
| 418 | SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */ | ||
| 419 | static void | ||
| 420 | input_sector (struct channel *c, void *sector) | ||
| 421 | { | ||
| 422 | insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2); | ||
| 423 | } | ||
| 424 | |||
| 425 | /* Writes SECTOR to channel C's data register in PIO mode. | ||
| 426 | SECTOR must contain BLOCK_SECTOR_SIZE bytes. */ | ||
| 427 | static void | ||
| 428 | output_sector (struct channel *c, const void *sector) | ||
| 429 | { | ||
| 430 | outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2); | ||
| 431 | } | ||
| 432 | |||
| 433 | /* Low-level ATA primitives. */ | ||
| 434 | |||
| 435 | /* Wait up to 10 seconds for the controller to become idle, that | ||
| 436 | is, for the BSY and DRQ bits to clear in the status register. | ||
| 437 | |||
| 438 | As a side effect, reading the status register clears any | ||
| 439 | pending interrupt. */ | ||
| 440 | static void | ||
| 441 | wait_until_idle (const struct ata_disk *d) | ||
| 442 | { | ||
| 443 | int i; | ||
| 444 | |||
| 445 | for (i = 0; i < 1000; i++) | ||
| 446 | { | ||
| 447 | if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0) | ||
| 448 | return; | ||
| 449 | timer_usleep (10); | ||
| 450 | } | ||
| 451 | |||
| 452 | printf ("%s: idle timeout\n", d->name); | ||
| 453 | } | ||
| 454 | |||
| 455 | /* Wait up to 30 seconds for disk D to clear BSY, | ||
| 456 | and then return the status of the DRQ bit. | ||
| 457 | The ATA standards say that a disk may take as long as that to | ||
| 458 | complete its reset. */ | ||
| 459 | static bool | ||
| 460 | wait_while_busy (const struct ata_disk *d) | ||
| 461 | { | ||
| 462 | struct channel *c = d->channel; | ||
| 463 | int i; | ||
| 464 | |||
| 465 | for (i = 0; i < 3000; i++) | ||
| 466 | { | ||
| 467 | if (i == 700) | ||
| 468 | printf ("%s: busy, waiting...", d->name); | ||
| 469 | if (!(inb (reg_alt_status (c)) & STA_BSY)) | ||
| 470 | { | ||
| 471 | if (i >= 700) | ||
| 472 | printf ("ok\n"); | ||
| 473 | return (inb (reg_alt_status (c)) & STA_DRQ) != 0; | ||
| 474 | } | ||
| 475 | timer_msleep (10); | ||
| 476 | } | ||
| 477 | |||
| 478 | printf ("failed\n"); | ||
| 479 | return false; | ||
| 480 | } | ||
| 481 | |||
| 482 | /* Program D's channel so that D is now the selected disk. */ | ||
| 483 | static void | ||
| 484 | select_device (const struct ata_disk *d) | ||
| 485 | { | ||
| 486 | struct channel *c = d->channel; | ||
| 487 | uint8_t dev = DEV_MBS; | ||
| 488 | if (d->dev_no == 1) | ||
| 489 | dev |= DEV_DEV; | ||
| 490 | outb (reg_device (c), dev); | ||
| 491 | inb (reg_alt_status (c)); | ||
| 492 | timer_nsleep (400); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* Select disk D in its channel, as select_device(), but wait for | ||
| 496 | the channel to become idle before and after. */ | ||
| 497 | static void | ||
| 498 | select_device_wait (const struct ata_disk *d) | ||
| 499 | { | ||
| 500 | wait_until_idle (d); | ||
| 501 | select_device (d); | ||
| 502 | wait_until_idle (d); | ||
| 503 | } | ||
| 504 | |||
| 505 | /* ATA interrupt handler. */ | ||
| 506 | static void | ||
| 507 | interrupt_handler (struct intr_frame *f) | ||
| 508 | { | ||
| 509 | struct channel *c; | ||
| 510 | |||
| 511 | for (c = channels; c < channels + CHANNEL_CNT; c++) | ||
| 512 | if (f->vec_no == c->irq) | ||
| 513 | { | ||
| 514 | if (c->expecting_interrupt) | ||
| 515 | { | ||
| 516 | inb (reg_status (c)); /* Acknowledge interrupt. */ | ||
| 517 | sema_up (&c->completion_wait); /* Wake up waiter. */ | ||
| 518 | } | ||
| 519 | else | ||
| 520 | printf ("%s: unexpected interrupt\n", c->name); | ||
| 521 | return; | ||
| 522 | } | ||
| 523 | |||
| 524 | NOT_REACHED (); | ||
| 525 | } | ||
| 526 | |||
| 527 | |||
diff --git a/pintos-progos/devices/ide.h b/pintos-progos/devices/ide.h new file mode 100644 index 0000000..b35da5e --- /dev/null +++ b/pintos-progos/devices/ide.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef DEVICES_IDE_H | ||
| 2 | #define DEVICES_IDE_H | ||
| 3 | |||
| 4 | void ide_init (void); | ||
| 5 | |||
| 6 | #endif /* devices/ide.h */ | ||
diff --git a/pintos-progos/devices/input.c b/pintos-progos/devices/input.c new file mode 100644 index 0000000..4a12160 --- /dev/null +++ b/pintos-progos/devices/input.c | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #include "devices/input.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "devices/intq.h" | ||
| 4 | #include "devices/serial.h" | ||
| 5 | |||
| 6 | /* Stores keys from the keyboard and serial port. */ | ||
| 7 | static struct intq buffer; | ||
| 8 | |||
| 9 | /* Initializes the input buffer. */ | ||
| 10 | void | ||
| 11 | input_init (void) | ||
| 12 | { | ||
| 13 | intq_init (&buffer); | ||
| 14 | } | ||
| 15 | |||
| 16 | /* Adds a key to the input buffer. | ||
| 17 | Interrupts must be off and the buffer must not be full. */ | ||
| 18 | void | ||
| 19 | input_putc (uint8_t key) | ||
| 20 | { | ||
| 21 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 22 | ASSERT (!intq_full (&buffer)); | ||
| 23 | |||
| 24 | intq_putc (&buffer, key); | ||
| 25 | serial_notify (); | ||
| 26 | } | ||
| 27 | |||
| 28 | /* Retrieves a key from the input buffer. | ||
| 29 | If the buffer is empty, waits for a key to be pressed. */ | ||
| 30 | uint8_t | ||
| 31 | input_getc (void) | ||
| 32 | { | ||
| 33 | enum intr_level old_level; | ||
| 34 | uint8_t key; | ||
| 35 | |||
| 36 | old_level = intr_disable (); | ||
| 37 | key = intq_getc (&buffer); | ||
| 38 | serial_notify (); | ||
| 39 | intr_set_level (old_level); | ||
| 40 | |||
| 41 | return key; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Returns true if the input buffer is full, | ||
| 45 | false otherwise. | ||
| 46 | Interrupts must be off. */ | ||
| 47 | bool | ||
| 48 | input_full (void) | ||
| 49 | { | ||
| 50 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 51 | return intq_full (&buffer); | ||
| 52 | } | ||
diff --git a/pintos-progos/devices/input.h b/pintos-progos/devices/input.h new file mode 100644 index 0000000..a2f50e9 --- /dev/null +++ b/pintos-progos/devices/input.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef DEVICES_INPUT_H | ||
| 2 | #define DEVICES_INPUT_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | void input_init (void); | ||
| 8 | void input_putc (uint8_t); | ||
| 9 | uint8_t input_getc (void); | ||
| 10 | bool input_full (void); | ||
| 11 | |||
| 12 | #endif /* devices/input.h */ | ||
diff --git a/pintos-progos/devices/intq.c b/pintos-progos/devices/intq.c new file mode 100644 index 0000000..40b23ae --- /dev/null +++ b/pintos-progos/devices/intq.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | #include "devices/intq.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "threads/thread.h" | ||
| 4 | |||
| 5 | static int next (int pos); | ||
| 6 | static void wait (struct intq *q, struct thread **waiter); | ||
| 7 | static void signal (struct intq *q, struct thread **waiter); | ||
| 8 | |||
| 9 | /* Initializes interrupt queue Q. */ | ||
| 10 | void | ||
| 11 | intq_init (struct intq *q) | ||
| 12 | { | ||
| 13 | lock_init (&q->lock); | ||
| 14 | q->not_full = q->not_empty = NULL; | ||
| 15 | q->head = q->tail = 0; | ||
| 16 | } | ||
| 17 | |||
| 18 | /* Returns true if Q is empty, false otherwise. */ | ||
| 19 | bool | ||
| 20 | intq_empty (const struct intq *q) | ||
| 21 | { | ||
| 22 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 23 | return q->head == q->tail; | ||
| 24 | } | ||
| 25 | |||
| 26 | /* Returns true if Q is full, false otherwise. */ | ||
| 27 | bool | ||
| 28 | intq_full (const struct intq *q) | ||
| 29 | { | ||
| 30 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 31 | return next (q->head) == q->tail; | ||
| 32 | } | ||
| 33 | |||
| 34 | /* Removes a byte from Q and returns it. | ||
| 35 | If Q is empty, sleeps until a byte is added. | ||
| 36 | When called from an interrupt handler, Q must not be empty. */ | ||
| 37 | uint8_t | ||
| 38 | intq_getc (struct intq *q) | ||
| 39 | { | ||
| 40 | uint8_t byte; | ||
| 41 | |||
| 42 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 43 | while (intq_empty (q)) | ||
| 44 | { | ||
| 45 | ASSERT (!intr_context ()); | ||
| 46 | lock_acquire (&q->lock); | ||
| 47 | wait (q, &q->not_empty); | ||
| 48 | lock_release (&q->lock); | ||
| 49 | } | ||
| 50 | |||
| 51 | byte = q->buf[q->tail]; | ||
| 52 | q->tail = next (q->tail); | ||
| 53 | signal (q, &q->not_full); | ||
| 54 | return byte; | ||
| 55 | } | ||
| 56 | |||
| 57 | /* Adds BYTE to the end of Q. | ||
| 58 | If Q is full, sleeps until a byte is removed. | ||
| 59 | When called from an interrupt handler, Q must not be full. */ | ||
| 60 | void | ||
| 61 | intq_putc (struct intq *q, uint8_t byte) | ||
| 62 | { | ||
| 63 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 64 | while (intq_full (q)) | ||
| 65 | { | ||
| 66 | ASSERT (!intr_context ()); | ||
| 67 | lock_acquire (&q->lock); | ||
| 68 | wait (q, &q->not_full); | ||
| 69 | lock_release (&q->lock); | ||
| 70 | } | ||
| 71 | |||
| 72 | q->buf[q->head] = byte; | ||
| 73 | q->head = next (q->head); | ||
| 74 | signal (q, &q->not_empty); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Returns the position after POS within an intq. */ | ||
| 78 | static int | ||
| 79 | next (int pos) | ||
| 80 | { | ||
| 81 | return (pos + 1) % INTQ_BUFSIZE; | ||
| 82 | } | ||
| 83 | |||
| 84 | /* WAITER must be the address of Q's not_empty or not_full | ||
| 85 | member. Waits until the given condition is true. */ | ||
| 86 | static void | ||
| 87 | wait (struct intq *q UNUSED, struct thread **waiter) | ||
| 88 | { | ||
| 89 | ASSERT (!intr_context ()); | ||
| 90 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 91 | ASSERT ((waiter == &q->not_empty && intq_empty (q)) | ||
| 92 | || (waiter == &q->not_full && intq_full (q))); | ||
| 93 | |||
| 94 | *waiter = thread_current (); | ||
| 95 | thread_block (); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* WAITER must be the address of Q's not_empty or not_full | ||
| 99 | member, and the associated condition must be true. If a | ||
| 100 | thread is waiting for the condition, wakes it up and resets | ||
| 101 | the waiting thread. */ | ||
| 102 | static void | ||
| 103 | signal (struct intq *q UNUSED, struct thread **waiter) | ||
| 104 | { | ||
| 105 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 106 | ASSERT ((waiter == &q->not_empty && !intq_empty (q)) | ||
| 107 | || (waiter == &q->not_full && !intq_full (q))); | ||
| 108 | |||
| 109 | if (*waiter != NULL) | ||
| 110 | { | ||
| 111 | thread_unblock (*waiter); | ||
| 112 | *waiter = NULL; | ||
| 113 | } | ||
| 114 | } | ||
diff --git a/pintos-progos/devices/intq.h b/pintos-progos/devices/intq.h new file mode 100644 index 0000000..2312b12 --- /dev/null +++ b/pintos-progos/devices/intq.h | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #ifndef DEVICES_INTQ_H | ||
| 2 | #define DEVICES_INTQ_H | ||
| 3 | |||
| 4 | #include "threads/interrupt.h" | ||
| 5 | #include "threads/synch.h" | ||
| 6 | |||
| 7 | /* An "interrupt queue", a circular buffer shared between | ||
| 8 | kernel threads and external interrupt handlers. | ||
| 9 | |||
| 10 | Interrupt queue functions can be called from kernel threads or | ||
| 11 | from external interrupt handlers. Except for intq_init(), | ||
| 12 | interrupts must be off in either case. | ||
| 13 | |||
| 14 | The interrupt queue has the structure of a "monitor". Locks | ||
| 15 | and condition variables from threads/synch.h cannot be used in | ||
| 16 | this case, as they normally would, because they can only | ||
| 17 | protect kernel threads from one another, not from interrupt | ||
| 18 | handlers. */ | ||
| 19 | |||
| 20 | /* Queue buffer size, in bytes. */ | ||
| 21 | #define INTQ_BUFSIZE 64 | ||
| 22 | |||
| 23 | /* A circular queue of bytes. */ | ||
| 24 | struct intq | ||
| 25 | { | ||
| 26 | /* Waiting threads. */ | ||
| 27 | struct lock lock; /* Only one thread may wait at once. */ | ||
| 28 | struct thread *not_full; /* Thread waiting for not-full condition. */ | ||
| 29 | struct thread *not_empty; /* Thread waiting for not-empty condition. */ | ||
| 30 | |||
| 31 | /* Queue. */ | ||
| 32 | uint8_t buf[INTQ_BUFSIZE]; /* Buffer. */ | ||
| 33 | int head; /* New data is written here. */ | ||
| 34 | int tail; /* Old data is read here. */ | ||
| 35 | }; | ||
| 36 | |||
| 37 | void intq_init (struct intq *); | ||
| 38 | bool intq_empty (const struct intq *); | ||
| 39 | bool intq_full (const struct intq *); | ||
| 40 | uint8_t intq_getc (struct intq *); | ||
| 41 | void intq_putc (struct intq *, uint8_t); | ||
| 42 | |||
| 43 | #endif /* devices/intq.h */ | ||
diff --git a/pintos-progos/devices/kbd.c b/pintos-progos/devices/kbd.c new file mode 100644 index 0000000..fcc82be --- /dev/null +++ b/pintos-progos/devices/kbd.c | |||
| @@ -0,0 +1,213 @@ | |||
| 1 | #include "devices/kbd.h" | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include "devices/input.h" | ||
| 7 | #include "devices/shutdown.h" | ||
| 8 | #include "threads/interrupt.h" | ||
| 9 | #include "threads/io.h" | ||
| 10 | |||
| 11 | /* Keyboard data register port. */ | ||
| 12 | #define DATA_REG 0x60 | ||
| 13 | |||
| 14 | /* Current state of shift keys. | ||
| 15 | True if depressed, false otherwise. */ | ||
| 16 | static bool left_shift, right_shift; /* Left and right Shift keys. */ | ||
| 17 | static bool left_alt, right_alt; /* Left and right Alt keys. */ | ||
| 18 | static bool left_ctrl, right_ctrl; /* Left and right Ctl keys. */ | ||
| 19 | |||
| 20 | /* Status of Caps Lock. | ||
| 21 | True when on, false when off. */ | ||
| 22 | static bool caps_lock; | ||
| 23 | |||
| 24 | /* Number of keys pressed. */ | ||
| 25 | static int64_t key_cnt; | ||
| 26 | |||
| 27 | static intr_handler_func keyboard_interrupt; | ||
| 28 | |||
| 29 | /* Initializes the keyboard. */ | ||
| 30 | void | ||
| 31 | kbd_init (void) | ||
| 32 | { | ||
| 33 | intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard"); | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Prints keyboard statistics. */ | ||
| 37 | void | ||
| 38 | kbd_print_stats (void) | ||
| 39 | { | ||
| 40 | printf ("Keyboard: %lld keys pressed\n", key_cnt); | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Maps a set of contiguous scancodes into characters. */ | ||
| 44 | struct keymap | ||
| 45 | { | ||
| 46 | uint8_t first_scancode; /* First scancode. */ | ||
| 47 | const char *chars; /* chars[0] has scancode first_scancode, | ||
| 48 | chars[1] has scancode first_scancode + 1, | ||
| 49 | and so on to the end of the string. */ | ||
| 50 | }; | ||
| 51 | |||
| 52 | /* Keys that produce the same characters regardless of whether | ||
| 53 | the Shift keys are down. Case of letters is an exception | ||
| 54 | that we handle elsewhere. */ | ||
| 55 | static const struct keymap invariant_keymap[] = | ||
| 56 | { | ||
| 57 | {0x01, "\033"}, /* Escape. */ | ||
| 58 | {0x0e, "\b"}, | ||
| 59 | {0x0f, "\tQWERTYUIOP"}, | ||
| 60 | {0x1c, "\r"}, | ||
| 61 | {0x1e, "ASDFGHJKL"}, | ||
| 62 | {0x2c, "ZXCVBNM"}, | ||
| 63 | {0x37, "*"}, | ||
| 64 | {0x39, " "}, | ||
| 65 | {0x53, "\177"}, /* Delete. */ | ||
| 66 | {0, NULL}, | ||
| 67 | }; | ||
| 68 | |||
| 69 | /* Characters for keys pressed without Shift, for those keys | ||
| 70 | where it matters. */ | ||
| 71 | static const struct keymap unshifted_keymap[] = | ||
| 72 | { | ||
| 73 | {0x02, "1234567890-="}, | ||
| 74 | {0x1a, "[]"}, | ||
| 75 | {0x27, ";'`"}, | ||
| 76 | {0x2b, "\\"}, | ||
| 77 | {0x33, ",./"}, | ||
| 78 | {0, NULL}, | ||
| 79 | }; | ||
| 80 | |||
| 81 | /* Characters for keys pressed with Shift, for those keys where | ||
| 82 | it matters. */ | ||
| 83 | static const struct keymap shifted_keymap[] = | ||
| 84 | { | ||
| 85 | {0x02, "!@#$%^&*()_+"}, | ||
| 86 | {0x1a, "{}"}, | ||
| 87 | {0x27, ":\"~"}, | ||
| 88 | {0x2b, "|"}, | ||
| 89 | {0x33, "<>?"}, | ||
| 90 | {0, NULL}, | ||
| 91 | }; | ||
| 92 | |||
| 93 | static bool map_key (const struct keymap[], unsigned scancode, uint8_t *); | ||
| 94 | |||
| 95 | static void | ||
| 96 | keyboard_interrupt (struct intr_frame *args UNUSED) | ||
| 97 | { | ||
| 98 | /* Status of shift keys. */ | ||
| 99 | bool shift = left_shift || right_shift; | ||
| 100 | bool alt = left_alt || right_alt; | ||
| 101 | bool ctrl = left_ctrl || right_ctrl; | ||
| 102 | |||
| 103 | /* Keyboard scancode. */ | ||
| 104 | unsigned code; | ||
| 105 | |||
| 106 | /* False if key pressed, true if key released. */ | ||
| 107 | bool release; | ||
| 108 | |||
| 109 | /* Character that corresponds to `code'. */ | ||
| 110 | uint8_t c; | ||
| 111 | |||
| 112 | /* Read scancode, including second byte if prefix code. */ | ||
| 113 | code = inb (DATA_REG); | ||
| 114 | if (code == 0xe0) | ||
| 115 | code = (code << 8) | inb (DATA_REG); | ||
| 116 | |||
| 117 | /* Bit 0x80 distinguishes key press from key release | ||
| 118 | (even if there's a prefix). */ | ||
| 119 | release = (code & 0x80) != 0; | ||
| 120 | code &= ~0x80u; | ||
| 121 | |||
| 122 | /* Interpret key. */ | ||
| 123 | if (code == 0x3a) | ||
| 124 | { | ||
| 125 | /* Caps Lock. */ | ||
| 126 | if (!release) | ||
| 127 | caps_lock = !caps_lock; | ||
| 128 | } | ||
| 129 | else if (map_key (invariant_keymap, code, &c) | ||
| 130 | || (!shift && map_key (unshifted_keymap, code, &c)) | ||
| 131 | || (shift && map_key (shifted_keymap, code, &c))) | ||
| 132 | { | ||
| 133 | /* Ordinary character. */ | ||
| 134 | if (!release) | ||
| 135 | { | ||
| 136 | /* Reboot if Ctrl+Alt+Del pressed. */ | ||
| 137 | if (c == 0177 && ctrl && alt) | ||
| 138 | shutdown_reboot (); | ||
| 139 | |||
| 140 | /* Handle Ctrl, Shift. | ||
| 141 | Note that Ctrl overrides Shift. */ | ||
| 142 | if (ctrl && c >= 0x40 && c < 0x60) | ||
| 143 | { | ||
| 144 | /* A is 0x41, Ctrl+A is 0x01, etc. */ | ||
| 145 | c -= 0x40; | ||
| 146 | } | ||
| 147 | else if (shift == caps_lock) | ||
| 148 | c = tolower (c); | ||
| 149 | |||
| 150 | /* Handle Alt by setting the high bit. | ||
| 151 | This 0x80 is unrelated to the one used to | ||
| 152 | distinguish key press from key release. */ | ||
| 153 | if (alt) | ||
| 154 | c += 0x80; | ||
| 155 | |||
| 156 | /* Append to keyboard buffer. */ | ||
| 157 | if (!input_full ()) | ||
| 158 | { | ||
| 159 | key_cnt++; | ||
| 160 | input_putc (c); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
| 164 | else | ||
| 165 | { | ||
| 166 | /* Maps a keycode into a shift state variable. */ | ||
| 167 | struct shift_key | ||
| 168 | { | ||
| 169 | unsigned scancode; | ||
| 170 | bool *state_var; | ||
| 171 | }; | ||
| 172 | |||
| 173 | /* Table of shift keys. */ | ||
| 174 | static const struct shift_key shift_keys[] = | ||
| 175 | { | ||
| 176 | { 0x2a, &left_shift}, | ||
| 177 | { 0x36, &right_shift}, | ||
| 178 | { 0x38, &left_alt}, | ||
| 179 | {0xe038, &right_alt}, | ||
| 180 | { 0x1d, &left_ctrl}, | ||
| 181 | {0xe01d, &right_ctrl}, | ||
| 182 | {0, NULL}, | ||
| 183 | }; | ||
| 184 | |||
| 185 | const struct shift_key *key; | ||
| 186 | |||
| 187 | /* Scan the table. */ | ||
| 188 | for (key = shift_keys; key->scancode != 0; key++) | ||
| 189 | if (key->scancode == code) | ||
| 190 | { | ||
| 191 | *key->state_var = !release; | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Scans the array of keymaps K for SCANCODE. | ||
| 198 | If found, sets *C to the corresponding character and returns | ||
| 199 | true. | ||
| 200 | If not found, returns false and C is ignored. */ | ||
| 201 | static bool | ||
| 202 | map_key (const struct keymap k[], unsigned scancode, uint8_t *c) | ||
| 203 | { | ||
| 204 | for (; k->first_scancode != 0; k++) | ||
| 205 | if (scancode >= k->first_scancode | ||
| 206 | && scancode < k->first_scancode + strlen (k->chars)) | ||
| 207 | { | ||
| 208 | *c = k->chars[scancode - k->first_scancode]; | ||
| 209 | return true; | ||
| 210 | } | ||
| 211 | |||
| 212 | return false; | ||
| 213 | } | ||
diff --git a/pintos-progos/devices/kbd.h b/pintos-progos/devices/kbd.h new file mode 100644 index 0000000..ed9c06b --- /dev/null +++ b/pintos-progos/devices/kbd.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef DEVICES_KBD_H | ||
| 2 | #define DEVICES_KBD_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | void kbd_init (void); | ||
| 7 | void kbd_print_stats (void); | ||
| 8 | |||
| 9 | #endif /* devices/kbd.h */ | ||
diff --git a/pintos-progos/devices/partition.c b/pintos-progos/devices/partition.c new file mode 100644 index 0000000..7e97332 --- /dev/null +++ b/pintos-progos/devices/partition.c | |||
| @@ -0,0 +1,324 @@ | |||
| 1 | #include "devices/partition.h" | ||
| 2 | #include <packed.h> | ||
| 3 | #include <stdlib.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "devices/block.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | |||
| 9 | /* A partition of a block device. */ | ||
| 10 | struct partition | ||
| 11 | { | ||
| 12 | struct block *block; /* Underlying block device. */ | ||
| 13 | block_sector_t start; /* First sector within device. */ | ||
| 14 | }; | ||
| 15 | |||
| 16 | static struct block_operations partition_operations; | ||
| 17 | |||
| 18 | static void read_partition_table (struct block *, block_sector_t sector, | ||
| 19 | block_sector_t primary_extended_sector, | ||
| 20 | int *part_nr); | ||
| 21 | static void found_partition (struct block *, uint8_t type, | ||
| 22 | block_sector_t start, block_sector_t size, | ||
| 23 | int part_nr); | ||
| 24 | static const char *partition_type_name (uint8_t); | ||
| 25 | |||
| 26 | /* Scans BLOCK for partitions of interest to Pintos. */ | ||
| 27 | void | ||
| 28 | partition_scan (struct block *block) | ||
| 29 | { | ||
| 30 | int part_nr = 0; | ||
| 31 | read_partition_table (block, 0, 0, &part_nr); | ||
| 32 | if (part_nr == 0) | ||
| 33 | printf ("%s: Device contains no partitions\n", block_name (block)); | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Reads the partition table in the given SECTOR of BLOCK and | ||
| 37 | scans it for partitions of interest to Pintos. | ||
| 38 | |||
| 39 | If SECTOR is 0, so that this is the top-level partition table | ||
| 40 | on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful; | ||
| 41 | otherwise, it should designate the sector of the top-level | ||
| 42 | extended partition table that was traversed to arrive at | ||
| 43 | SECTOR, for use in finding logical partitions (see the large | ||
| 44 | comment below). | ||
| 45 | |||
| 46 | PART_NR points to the number of non-empty primary or logical | ||
| 47 | partitions already encountered on BLOCK. It is incremented as | ||
| 48 | partitions are found. */ | ||
| 49 | static void | ||
| 50 | read_partition_table (struct block *block, block_sector_t sector, | ||
| 51 | block_sector_t primary_extended_sector, | ||
| 52 | int *part_nr) | ||
| 53 | { | ||
| 54 | /* Format of a partition table entry. See [Partitions]. */ | ||
| 55 | struct partition_table_entry | ||
| 56 | { | ||
| 57 | uint8_t bootable; /* 0x00=not bootable, 0x80=bootable. */ | ||
| 58 | uint8_t start_chs[3]; /* Encoded starting cylinder, head, sector. */ | ||
| 59 | uint8_t type; /* Partition type (see partition_type_name). */ | ||
| 60 | uint8_t end_chs[3]; /* Encoded ending cylinder, head, sector. */ | ||
| 61 | uint32_t offset; /* Start sector offset from partition table. */ | ||
| 62 | uint32_t size; /* Number of sectors. */ | ||
| 63 | } | ||
| 64 | PACKED; | ||
| 65 | |||
| 66 | /* Partition table sector. */ | ||
| 67 | struct partition_table | ||
| 68 | { | ||
| 69 | uint8_t loader[446]; /* Loader, in top-level partition table. */ | ||
| 70 | struct partition_table_entry partitions[4]; /* Table entries. */ | ||
| 71 | uint16_t signature; /* Should be 0xaa55. */ | ||
| 72 | } | ||
| 73 | PACKED; | ||
| 74 | |||
| 75 | struct partition_table *pt; | ||
| 76 | size_t i; | ||
| 77 | |||
| 78 | /* Check SECTOR validity. */ | ||
| 79 | if (sector >= block_size (block)) | ||
| 80 | { | ||
| 81 | printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n", | ||
| 82 | block_name (block), sector); | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Read sector. */ | ||
| 87 | ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE); | ||
| 88 | pt = malloc (sizeof *pt); | ||
| 89 | if (pt == NULL) | ||
| 90 | PANIC ("Failed to allocate memory for partition table."); | ||
| 91 | block_read (block, 0, pt); | ||
| 92 | |||
| 93 | /* Check signature. */ | ||
| 94 | if (pt->signature != 0xaa55) | ||
| 95 | { | ||
| 96 | if (primary_extended_sector == 0) | ||
| 97 | printf ("%s: Invalid partition table signature\n", block_name (block)); | ||
| 98 | else | ||
| 99 | printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n", | ||
| 100 | block_name (block), sector); | ||
| 101 | free (pt); | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* Parse partitions. */ | ||
| 106 | for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++) | ||
| 107 | { | ||
| 108 | struct partition_table_entry *e = &pt->partitions[i]; | ||
| 109 | |||
| 110 | if (e->size == 0 || e->type == 0) | ||
| 111 | { | ||
| 112 | /* Ignore empty partition. */ | ||
| 113 | } | ||
| 114 | else if (e->type == 0x05 /* Extended partition. */ | ||
| 115 | || e->type == 0x0f /* Windows 98 extended partition. */ | ||
| 116 | || e->type == 0x85 /* Linux extended partition. */ | ||
| 117 | || e->type == 0xc5) /* DR-DOS extended partition. */ | ||
| 118 | { | ||
| 119 | printf ("%s: Extended partition in sector %"PRDSNu"\n", | ||
| 120 | block_name (block), sector); | ||
| 121 | |||
| 122 | /* The interpretation of the offset field for extended | ||
| 123 | partitions is bizarre. When the extended partition | ||
| 124 | table entry is in the master boot record, that is, | ||
| 125 | the device's primary partition table in sector 0, then | ||
| 126 | the offset is an absolute sector number. Otherwise, | ||
| 127 | no matter how deep the partition table we're reading | ||
| 128 | is nested, the offset is relative to the start of | ||
| 129 | the extended partition that the MBR points to. */ | ||
| 130 | if (sector == 0) | ||
| 131 | read_partition_table (block, e->offset, e->offset, part_nr); | ||
| 132 | else | ||
| 133 | read_partition_table (block, e->offset + primary_extended_sector, | ||
| 134 | primary_extended_sector, part_nr); | ||
| 135 | } | ||
| 136 | else | ||
| 137 | { | ||
| 138 | ++*part_nr; | ||
| 139 | |||
| 140 | found_partition (block, e->type, e->offset + sector, | ||
| 141 | e->size, *part_nr); | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | free (pt); | ||
| 146 | } | ||
| 147 | |||
| 148 | /* We have found a primary or logical partition of the given TYPE | ||
| 149 | on BLOCK, starting at sector START and continuing for SIZE | ||
| 150 | sectors, which we are giving the partition number PART_NR. | ||
| 151 | Check whether this is a partition of interest to Pintos, and | ||
| 152 | if so then add it to the proper element of partitions[]. */ | ||
| 153 | static void | ||
| 154 | found_partition (struct block *block, uint8_t part_type, | ||
| 155 | block_sector_t start, block_sector_t size, | ||
| 156 | int part_nr) | ||
| 157 | { | ||
| 158 | if (start >= block_size (block)) | ||
| 159 | printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n", | ||
| 160 | block_name (block), part_nr, start); | ||
| 161 | else if (start + size < start || start + size > block_size (block)) | ||
| 162 | printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n", | ||
| 163 | block_name (block), part_nr, start + size, block_size (block)); | ||
| 164 | else | ||
| 165 | { | ||
| 166 | enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL | ||
| 167 | : part_type == 0x21 ? BLOCK_FILESYS | ||
| 168 | : part_type == 0x22 ? BLOCK_SCRATCH | ||
| 169 | : part_type == 0x23 ? BLOCK_SWAP | ||
| 170 | : BLOCK_FOREIGN); | ||
| 171 | struct partition *p; | ||
| 172 | char extra_info[128]; | ||
| 173 | char name[16]; | ||
| 174 | |||
| 175 | p = malloc (sizeof *p); | ||
| 176 | if (p == NULL) | ||
| 177 | PANIC ("Failed to allocate memory for partition descriptor"); | ||
| 178 | p->block = block; | ||
| 179 | p->start = start; | ||
| 180 | |||
| 181 | snprintf (name, sizeof name, "%s%d", block_name (block), part_nr); | ||
| 182 | snprintf (extra_info, sizeof extra_info, "%s (%02x)", | ||
| 183 | partition_type_name (part_type), part_type); | ||
| 184 | block_register (name, type, extra_info, size, &partition_operations, p); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Returns a human-readable name for the given partition TYPE. */ | ||
| 189 | static const char * | ||
| 190 | partition_type_name (uint8_t type) | ||
| 191 | { | ||
| 192 | /* Name of each known type of partition. | ||
| 193 | From util-linux-2.12r/fdisk/i386_sys_types.c. | ||
| 194 | This initializer makes use of a C99 feature that allows | ||
| 195 | array elements to be initialized by index. */ | ||
| 196 | static const char *type_names[256] = | ||
| 197 | { | ||
| 198 | [0x00] = "Empty", | ||
| 199 | [0x01] = "FAT12", | ||
| 200 | [0x02] = "XENIX root", | ||
| 201 | [0x03] = "XENIX usr", | ||
| 202 | [0x04] = "FAT16 <32M", | ||
| 203 | [0x05] = "Extended", | ||
| 204 | [0x06] = "FAT16", | ||
| 205 | [0x07] = "HPFS/NTFS", | ||
| 206 | [0x08] = "AIX", | ||
| 207 | [0x09] = "AIX bootable", | ||
| 208 | [0x0a] = "OS/2 Boot Manager", | ||
| 209 | [0x0b] = "W95 FAT32", | ||
| 210 | [0x0c] = "W95 FAT32 (LBA)", | ||
| 211 | [0x0e] = "W95 FAT16 (LBA)", | ||
| 212 | [0x0f] = "W95 Ext'd (LBA)", | ||
| 213 | [0x10] = "OPUS", | ||
| 214 | [0x11] = "Hidden FAT12", | ||
| 215 | [0x12] = "Compaq diagnostics", | ||
| 216 | [0x14] = "Hidden FAT16 <32M", | ||
| 217 | [0x16] = "Hidden FAT16", | ||
| 218 | [0x17] = "Hidden HPFS/NTFS", | ||
| 219 | [0x18] = "AST SmartSleep", | ||
| 220 | [0x1b] = "Hidden W95 FAT32", | ||
| 221 | [0x1c] = "Hidden W95 FAT32 (LBA)", | ||
| 222 | [0x1e] = "Hidden W95 FAT16 (LBA)", | ||
| 223 | [0x20] = "Pintos OS kernel", | ||
| 224 | [0x21] = "Pintos file system", | ||
| 225 | [0x22] = "Pintos scratch", | ||
| 226 | [0x23] = "Pintos swap", | ||
| 227 | [0x24] = "NEC DOS", | ||
| 228 | [0x39] = "Plan 9", | ||
| 229 | [0x3c] = "PartitionMagic recovery", | ||
| 230 | [0x40] = "Venix 80286", | ||
| 231 | [0x41] = "PPC PReP Boot", | ||
| 232 | [0x42] = "SFS", | ||
| 233 | [0x4d] = "QNX4.x", | ||
| 234 | [0x4e] = "QNX4.x 2nd part", | ||
| 235 | [0x4f] = "QNX4.x 3rd part", | ||
| 236 | [0x50] = "OnTrack DM", | ||
| 237 | [0x51] = "OnTrack DM6 Aux1", | ||
| 238 | [0x52] = "CP/M", | ||
| 239 | [0x53] = "OnTrack DM6 Aux3", | ||
| 240 | [0x54] = "OnTrackDM6", | ||
| 241 | [0x55] = "EZ-Drive", | ||
| 242 | [0x56] = "Golden Bow", | ||
| 243 | [0x5c] = "Priam Edisk", | ||
| 244 | [0x61] = "SpeedStor", | ||
| 245 | [0x63] = "GNU HURD or SysV", | ||
| 246 | [0x64] = "Novell Netware 286", | ||
| 247 | [0x65] = "Novell Netware 386", | ||
| 248 | [0x70] = "DiskSecure Multi-Boot", | ||
| 249 | [0x75] = "PC/IX", | ||
| 250 | [0x80] = "Old Minix", | ||
| 251 | [0x81] = "Minix / old Linux", | ||
| 252 | [0x82] = "Linux swap / Solaris", | ||
| 253 | [0x83] = "Linux", | ||
| 254 | [0x84] = "OS/2 hidden C: drive", | ||
| 255 | [0x85] = "Linux extended", | ||
| 256 | [0x86] = "NTFS volume set", | ||
| 257 | [0x87] = "NTFS volume set", | ||
| 258 | [0x88] = "Linux plaintext", | ||
| 259 | [0x8e] = "Linux LVM", | ||
| 260 | [0x93] = "Amoeba", | ||
| 261 | [0x94] = "Amoeba BBT", | ||
| 262 | [0x9f] = "BSD/OS", | ||
| 263 | [0xa0] = "IBM Thinkpad hibernation", | ||
| 264 | [0xa5] = "FreeBSD", | ||
| 265 | [0xa6] = "OpenBSD", | ||
| 266 | [0xa7] = "NeXTSTEP", | ||
| 267 | [0xa8] = "Darwin UFS", | ||
| 268 | [0xa9] = "NetBSD", | ||
| 269 | [0xab] = "Darwin boot", | ||
| 270 | [0xb7] = "BSDI fs", | ||
| 271 | [0xb8] = "BSDI swap", | ||
| 272 | [0xbb] = "Boot Wizard hidden", | ||
| 273 | [0xbe] = "Solaris boot", | ||
| 274 | [0xbf] = "Solaris", | ||
| 275 | [0xc1] = "DRDOS/sec (FAT-12)", | ||
| 276 | [0xc4] = "DRDOS/sec (FAT-16 < 32M)", | ||
| 277 | [0xc6] = "DRDOS/sec (FAT-16)", | ||
| 278 | [0xc7] = "Syrinx", | ||
| 279 | [0xda] = "Non-FS data", | ||
| 280 | [0xdb] = "CP/M / CTOS / ...", | ||
| 281 | [0xde] = "Dell Utility", | ||
| 282 | [0xdf] = "BootIt", | ||
| 283 | [0xe1] = "DOS access", | ||
| 284 | [0xe3] = "DOS R/O", | ||
| 285 | [0xe4] = "SpeedStor", | ||
| 286 | [0xeb] = "BeOS fs", | ||
| 287 | [0xee] = "EFI GPT", | ||
| 288 | [0xef] = "EFI (FAT-12/16/32)", | ||
| 289 | [0xf0] = "Linux/PA-RISC boot", | ||
| 290 | [0xf1] = "SpeedStor", | ||
| 291 | [0xf4] = "SpeedStor", | ||
| 292 | [0xf2] = "DOS secondary", | ||
| 293 | [0xfd] = "Linux raid autodetect", | ||
| 294 | [0xfe] = "LANstep", | ||
| 295 | [0xff] = "BBT", | ||
| 296 | }; | ||
| 297 | |||
| 298 | return type_names[type] != NULL ? type_names[type] : "Unknown"; | ||
| 299 | } | ||
| 300 | |||
| 301 | /* Reads sector SECTOR from partition P into BUFFER, which must | ||
| 302 | have room for BLOCK_SECTOR_SIZE bytes. */ | ||
| 303 | static void | ||
| 304 | partition_read (void *p_, block_sector_t sector, void *buffer) | ||
| 305 | { | ||
| 306 | struct partition *p = p_; | ||
| 307 | block_read (p->block, p->start + sector, buffer); | ||
| 308 | } | ||
| 309 | |||
| 310 | /* Write sector SECTOR to partition P from BUFFER, which must | ||
| 311 | contain BLOCK_SECTOR_SIZE bytes. Returns after the block has | ||
| 312 | acknowledged receiving the data. */ | ||
| 313 | static void | ||
| 314 | partition_write (void *p_, block_sector_t sector, const void *buffer) | ||
| 315 | { | ||
| 316 | struct partition *p = p_; | ||
| 317 | block_write (p->block, p->start + sector, buffer); | ||
| 318 | } | ||
| 319 | |||
| 320 | static struct block_operations partition_operations = | ||
| 321 | { | ||
| 322 | partition_read, | ||
| 323 | partition_write | ||
| 324 | }; | ||
diff --git a/pintos-progos/devices/partition.h b/pintos-progos/devices/partition.h new file mode 100644 index 0000000..47fea4d --- /dev/null +++ b/pintos-progos/devices/partition.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef DEVICES_PARTITION_H | ||
| 2 | #define DEVICES_PARTITION_H | ||
| 3 | |||
| 4 | struct block; | ||
| 5 | |||
| 6 | void partition_scan (struct block *); | ||
| 7 | |||
| 8 | #endif /* devices/partition.h */ | ||
diff --git a/pintos-progos/devices/pit.c b/pintos-progos/devices/pit.c new file mode 100644 index 0000000..bfb1889 --- /dev/null +++ b/pintos-progos/devices/pit.c | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | #include "devices/pit.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stdint.h> | ||
| 4 | #include "threads/interrupt.h" | ||
| 5 | #include "threads/io.h" | ||
| 6 | |||
| 7 | /* Interface to 8254 Programmable Interrupt Timer (PIT). | ||
| 8 | Refer to [8254] for details. */ | ||
| 9 | |||
| 10 | /* 8254 registers. */ | ||
| 11 | #define PIT_PORT_CONTROL 0x43 /* Control port. */ | ||
| 12 | #define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL)) /* Counter port. */ | ||
| 13 | |||
| 14 | /* PIT cycles per second. */ | ||
| 15 | #define PIT_HZ 1193180 | ||
| 16 | |||
| 17 | /* Configure the given CHANNEL in the PIT. In a PC, the PIT's | ||
| 18 | three output channels are hooked up like this: | ||
| 19 | |||
| 20 | - Channel 0 is connected to interrupt line 0, so that it can | ||
| 21 | be used as a periodic timer interrupt, as implemented in | ||
| 22 | Pintos in devices/timer.c. | ||
| 23 | |||
| 24 | - Channel 1 is used for dynamic RAM refresh (in older PCs). | ||
| 25 | No good can come of messing with this. | ||
| 26 | |||
| 27 | - Channel 2 is connected to the PC speaker, so that it can | ||
| 28 | be used to play a tone, as implemented in Pintos in | ||
| 29 | devices/speaker.c. | ||
| 30 | |||
| 31 | MODE specifies the form of output: | ||
| 32 | |||
| 33 | - Mode 2 is a periodic pulse: the channel's output is 1 for | ||
| 34 | most of the period, but drops to 0 briefly toward the end | ||
| 35 | of the period. This is useful for hooking up to an | ||
| 36 | interrupt controller to generate a periodic interrupt. | ||
| 37 | |||
| 38 | - Mode 3 is a square wave: for the first half of the period | ||
| 39 | it is 1, for the second half it is 0. This is useful for | ||
| 40 | generating a tone on a speaker. | ||
| 41 | |||
| 42 | - Other modes are less useful. | ||
| 43 | |||
| 44 | FREQUENCY is the number of periods per second, in Hz. */ | ||
| 45 | void | ||
| 46 | pit_configure_channel (int channel, int mode, int frequency) | ||
| 47 | { | ||
| 48 | uint16_t count; | ||
| 49 | enum intr_level old_level; | ||
| 50 | |||
| 51 | ASSERT (channel == 0 || channel == 2); | ||
| 52 | ASSERT (mode == 2 || mode == 3); | ||
| 53 | |||
| 54 | /* Convert FREQUENCY to a PIT counter value. The PIT has a | ||
| 55 | clock that runs at PIT_HZ cycles per second. We must | ||
| 56 | translate FREQUENCY into a number of these cycles. */ | ||
| 57 | if (frequency < 19) | ||
| 58 | { | ||
| 59 | /* Frequency is too low: the quotient would overflow the | ||
| 60 | 16-bit counter. Force it to 0, which the PIT treats as | ||
| 61 | 65536, the highest possible count. This yields a 18.2 | ||
| 62 | Hz timer, approximately. */ | ||
| 63 | count = 0; | ||
| 64 | } | ||
| 65 | else if (frequency > PIT_HZ) | ||
| 66 | { | ||
| 67 | /* Frequency is too high: the quotient would underflow to | ||
| 68 | 0, which the PIT would interpret as 65536. A count of 1 | ||
| 69 | is illegal in mode 2, so we force it to 2, which yields | ||
| 70 | a 596.590 kHz timer, approximately. (This timer rate is | ||
| 71 | probably too fast to be useful anyhow.) */ | ||
| 72 | count = 2; | ||
| 73 | } | ||
| 74 | else | ||
| 75 | count = (PIT_HZ + frequency / 2) / frequency; | ||
| 76 | |||
| 77 | /* Configure the PIT mode and load its counters. */ | ||
| 78 | old_level = intr_disable (); | ||
| 79 | outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1)); | ||
| 80 | outb (PIT_PORT_COUNTER (channel), count); | ||
| 81 | outb (PIT_PORT_COUNTER (channel), count >> 8); | ||
| 82 | intr_set_level (old_level); | ||
| 83 | } | ||
diff --git a/pintos-progos/devices/pit.h b/pintos-progos/devices/pit.h new file mode 100644 index 0000000..dff36ae --- /dev/null +++ b/pintos-progos/devices/pit.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef DEVICES_PIT_H | ||
| 2 | #define DEVICES_PIT_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | void pit_configure_channel (int channel, int mode, int frequency); | ||
| 7 | |||
| 8 | #endif /* devices/pit.h */ | ||
diff --git a/pintos-progos/devices/rtc.c b/pintos-progos/devices/rtc.c new file mode 100644 index 0000000..d99eb46 --- /dev/null +++ b/pintos-progos/devices/rtc.c | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | #include "devices/rtc.h" | ||
| 2 | #include <stdio.h> | ||
| 3 | #include "threads/io.h" | ||
| 4 | |||
| 5 | /* This code is an interface to the MC146818A-compatible real | ||
| 6 | time clock found on PC motherboards. See [MC146818A] for | ||
| 7 | hardware details. */ | ||
| 8 | |||
| 9 | /* I/O register addresses. */ | ||
| 10 | #define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */ | ||
| 11 | #define CMOS_REG_IO 0x71 /* Contains the selected data byte. */ | ||
| 12 | |||
| 13 | /* Indexes of CMOS registers with real-time clock functions. | ||
| 14 | Note that all of these registers are in BCD format, | ||
| 15 | so that 0x59 means 59, not 89. */ | ||
| 16 | #define RTC_REG_SEC 0 /* Second: 0x00...0x59. */ | ||
| 17 | #define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */ | ||
| 18 | #define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */ | ||
| 19 | #define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */ | ||
| 20 | #define RTC_REG_MON 8 /* Month: 0x01...0x12. */ | ||
| 21 | #define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */ | ||
| 22 | |||
| 23 | /* Indexes of CMOS control registers. */ | ||
| 24 | #define RTC_REG_A 0x0a /* Register A: update-in-progress. */ | ||
| 25 | #define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */ | ||
| 26 | #define RTC_REG_C 0x0c /* Register C: pending interrupts. */ | ||
| 27 | #define RTC_REG_D 0x0d /* Register D: valid time? */ | ||
| 28 | |||
| 29 | /* Register A. */ | ||
| 30 | #define RTCSA_UIP 0x80 /* Set while time update in progress. */ | ||
| 31 | |||
| 32 | /* Register B. */ | ||
| 33 | #define RTCSB_SET 0x80 /* Disables update to let time be set. */ | ||
| 34 | #define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */ | ||
| 35 | #define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */ | ||
| 36 | |||
| 37 | static int bcd_to_bin (uint8_t); | ||
| 38 | static uint8_t cmos_read (uint8_t index); | ||
| 39 | |||
| 40 | /* Returns number of seconds since Unix epoch of January 1, | ||
| 41 | 1970. */ | ||
| 42 | time_t | ||
| 43 | rtc_get_time (void) | ||
| 44 | { | ||
| 45 | static const int days_per_month[12] = | ||
| 46 | { | ||
| 47 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
| 48 | }; | ||
| 49 | int sec, min, hour, mday, mon, year; | ||
| 50 | time_t time; | ||
| 51 | int i; | ||
| 52 | |||
| 53 | /* Get time components. | ||
| 54 | |||
| 55 | We repeatedly read the time until it is stable from one read | ||
| 56 | to another, in case we start our initial read in the middle | ||
| 57 | of an update. This strategy is not recommended by the | ||
| 58 | MC146818A datasheet, but it is simpler than any of their | ||
| 59 | suggestions and, furthermore, it is also used by Linux. | ||
| 60 | |||
| 61 | The MC146818A can be configured for BCD or binary format, | ||
| 62 | but for historical reasons everyone always uses BCD format | ||
| 63 | except on obscure non-PC platforms, so we don't bother | ||
| 64 | trying to detect the format in use. */ | ||
| 65 | do | ||
| 66 | { | ||
| 67 | sec = bcd_to_bin (cmos_read (RTC_REG_SEC)); | ||
| 68 | min = bcd_to_bin (cmos_read (RTC_REG_MIN)); | ||
| 69 | hour = bcd_to_bin (cmos_read (RTC_REG_HOUR)); | ||
| 70 | mday = bcd_to_bin (cmos_read (RTC_REG_MDAY)); | ||
| 71 | mon = bcd_to_bin (cmos_read (RTC_REG_MON)); | ||
| 72 | year = bcd_to_bin (cmos_read (RTC_REG_YEAR)); | ||
| 73 | } | ||
| 74 | while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC))); | ||
| 75 | |||
| 76 | /* Translate years-since-1900 into years-since-1970. | ||
| 77 | If it's before the epoch, assume that it has passed 2000. | ||
| 78 | This will break at 2070, but that's long after our 31-bit | ||
| 79 | time_t breaks in 2038. */ | ||
| 80 | if (year < 70) | ||
| 81 | year += 100; | ||
| 82 | year -= 70; | ||
| 83 | |||
| 84 | /* Break down all components into seconds. */ | ||
| 85 | time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60; | ||
| 86 | for (i = 1; i <= mon; i++) | ||
| 87 | time += days_per_month[i - 1] * 24 * 60 * 60; | ||
| 88 | if (mon > 2 && year % 4 == 0) | ||
| 89 | time += 24 * 60 * 60; | ||
| 90 | time += (mday - 1) * 24 * 60 * 60; | ||
| 91 | time += hour * 60 * 60; | ||
| 92 | time += min * 60; | ||
| 93 | time += sec; | ||
| 94 | |||
| 95 | return time; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Returns the integer value of the given BCD byte. */ | ||
| 99 | static int | ||
| 100 | bcd_to_bin (uint8_t x) | ||
| 101 | { | ||
| 102 | return (x & 0x0f) + ((x >> 4) * 10); | ||
| 103 | } | ||
| 104 | |||
| 105 | /* Reads a byte from the CMOS register with the given INDEX and | ||
| 106 | returns the byte read. */ | ||
| 107 | static uint8_t | ||
| 108 | cmos_read (uint8_t index) | ||
| 109 | { | ||
| 110 | outb (CMOS_REG_SET, index); | ||
| 111 | return inb (CMOS_REG_IO); | ||
| 112 | } | ||
diff --git a/pintos-progos/devices/rtc.h b/pintos-progos/devices/rtc.h new file mode 100644 index 0000000..96a822f --- /dev/null +++ b/pintos-progos/devices/rtc.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef RTC_H | ||
| 2 | #define RTC_H | ||
| 3 | |||
| 4 | typedef unsigned long time_t; | ||
| 5 | |||
| 6 | time_t rtc_get_time (void); | ||
| 7 | |||
| 8 | #endif | ||
diff --git a/pintos-progos/devices/serial.c b/pintos-progos/devices/serial.c new file mode 100644 index 0000000..df770a7 --- /dev/null +++ b/pintos-progos/devices/serial.c | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | #include "devices/serial.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "devices/input.h" | ||
| 4 | #include "devices/intq.h" | ||
| 5 | #include "devices/timer.h" | ||
| 6 | #include "threads/io.h" | ||
| 7 | #include "threads/interrupt.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | |||
| 11 | /* Register definitions for the 16550A UART used in PCs. | ||
| 12 | The 16550A has a lot more going on than shown here, but this | ||
| 13 | is all we need. | ||
| 14 | |||
| 15 | Refer to [PC16650D] for hardware information. */ | ||
| 16 | |||
| 17 | /* I/O port base address for the first serial port. */ | ||
| 18 | #define IO_BASE 0x3f8 | ||
| 19 | |||
| 20 | /* DLAB=0 registers. */ | ||
| 21 | #define RBR_REG (IO_BASE + 0) /* Receiver Buffer Reg. (read-only). */ | ||
| 22 | #define THR_REG (IO_BASE + 0) /* Transmitter Holding Reg. (write-only). */ | ||
| 23 | #define IER_REG (IO_BASE + 1) /* Interrupt Enable Reg.. */ | ||
| 24 | |||
| 25 | /* DLAB=1 registers. */ | ||
| 26 | #define LS_REG (IO_BASE + 0) /* Divisor Latch (LSB). */ | ||
| 27 | #define MS_REG (IO_BASE + 1) /* Divisor Latch (MSB). */ | ||
| 28 | |||
| 29 | /* DLAB-insensitive registers. */ | ||
| 30 | #define IIR_REG (IO_BASE + 2) /* Interrupt Identification Reg. (read-only) */ | ||
| 31 | #define FCR_REG (IO_BASE + 2) /* FIFO Control Reg. (write-only). */ | ||
| 32 | #define LCR_REG (IO_BASE + 3) /* Line Control Register. */ | ||
| 33 | #define MCR_REG (IO_BASE + 4) /* MODEM Control Register. */ | ||
| 34 | #define LSR_REG (IO_BASE + 5) /* Line Status Register (read-only). */ | ||
| 35 | |||
| 36 | /* Interrupt Enable Register bits. */ | ||
| 37 | #define IER_RECV 0x01 /* Interrupt when data received. */ | ||
| 38 | #define IER_XMIT 0x02 /* Interrupt when transmit finishes. */ | ||
| 39 | |||
| 40 | /* Line Control Register bits. */ | ||
| 41 | #define LCR_N81 0x03 /* No parity, 8 data bits, 1 stop bit. */ | ||
| 42 | #define LCR_DLAB 0x80 /* Divisor Latch Access Bit (DLAB). */ | ||
| 43 | |||
| 44 | /* MODEM Control Register. */ | ||
| 45 | #define MCR_OUT2 0x08 /* Output line 2. */ | ||
| 46 | |||
| 47 | /* Line Status Register. */ | ||
| 48 | #define LSR_DR 0x01 /* Data Ready: received data byte is in RBR. */ | ||
| 49 | #define LSR_THRE 0x20 /* THR Empty. */ | ||
| 50 | |||
| 51 | /* Transmission mode. */ | ||
| 52 | static enum { UNINIT, POLL, QUEUE } mode; | ||
| 53 | |||
| 54 | /* Data to be transmitted. */ | ||
| 55 | static struct intq txq; | ||
| 56 | |||
| 57 | static void set_serial (int bps); | ||
| 58 | static void putc_poll (uint8_t); | ||
| 59 | static void write_ier (void); | ||
| 60 | static intr_handler_func serial_interrupt; | ||
| 61 | |||
| 62 | /* Initializes the serial port device for polling mode. | ||
| 63 | Polling mode busy-waits for the serial port to become free | ||
| 64 | before writing to it. It's slow, but until interrupts have | ||
| 65 | been initialized it's all we can do. */ | ||
| 66 | static void | ||
| 67 | init_poll (void) | ||
| 68 | { | ||
| 69 | ASSERT (mode == UNINIT); | ||
| 70 | outb (IER_REG, 0); /* Turn off all interrupts. */ | ||
| 71 | outb (FCR_REG, 0); /* Disable FIFO. */ | ||
| 72 | set_serial (9600); /* 9.6 kbps, N-8-1. */ | ||
| 73 | outb (MCR_REG, MCR_OUT2); /* Required to enable interrupts. */ | ||
| 74 | intq_init (&txq); | ||
| 75 | mode = POLL; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Initializes the serial port device for queued interrupt-driven | ||
| 79 | I/O. With interrupt-driven I/O we don't waste CPU time | ||
| 80 | waiting for the serial device to become ready. */ | ||
| 81 | void | ||
| 82 | serial_init_queue (void) | ||
| 83 | { | ||
| 84 | enum intr_level old_level; | ||
| 85 | |||
| 86 | if (mode == UNINIT) | ||
| 87 | init_poll (); | ||
| 88 | ASSERT (mode == POLL); | ||
| 89 | |||
| 90 | intr_register_ext (0x20 + 4, serial_interrupt, "serial"); | ||
| 91 | mode = QUEUE; | ||
| 92 | old_level = intr_disable (); | ||
| 93 | write_ier (); | ||
| 94 | intr_set_level (old_level); | ||
| 95 | } | ||
| 96 | |||
| 97 | /* Sends BYTE to the serial port. */ | ||
| 98 | void | ||
| 99 | serial_putc (uint8_t byte) | ||
| 100 | { | ||
| 101 | enum intr_level old_level = intr_disable (); | ||
| 102 | |||
| 103 | if (mode != QUEUE) | ||
| 104 | { | ||
| 105 | /* If we're not set up for interrupt-driven I/O yet, | ||
| 106 | use dumb polling to transmit a byte. */ | ||
| 107 | if (mode == UNINIT) | ||
| 108 | init_poll (); | ||
| 109 | putc_poll (byte); | ||
| 110 | } | ||
| 111 | else | ||
| 112 | { | ||
| 113 | /* Otherwise, queue a byte and update the interrupt enable | ||
| 114 | register. */ | ||
| 115 | if (old_level == INTR_OFF && intq_full (&txq)) | ||
| 116 | { | ||
| 117 | /* Interrupts are off and the transmit queue is full. | ||
| 118 | If we wanted to wait for the queue to empty, | ||
| 119 | we'd have to reenable interrupts. | ||
| 120 | That's impolite, so we'll send a character via | ||
| 121 | polling instead. */ | ||
| 122 | putc_poll (intq_getc (&txq)); | ||
| 123 | } | ||
| 124 | |||
| 125 | intq_putc (&txq, byte); | ||
| 126 | write_ier (); | ||
| 127 | } | ||
| 128 | |||
| 129 | intr_set_level (old_level); | ||
| 130 | } | ||
| 131 | |||
| 132 | /* Flushes anything in the serial buffer out the port in polling | ||
| 133 | mode. */ | ||
| 134 | void | ||
| 135 | serial_flush (void) | ||
| 136 | { | ||
| 137 | enum intr_level old_level = intr_disable (); | ||
| 138 | while (!intq_empty (&txq)) | ||
| 139 | putc_poll (intq_getc (&txq)); | ||
| 140 | intr_set_level (old_level); | ||
| 141 | } | ||
| 142 | |||
| 143 | /* The fullness of the input buffer may have changed. Reassess | ||
| 144 | whether we should block receive interrupts. | ||
| 145 | Called by the input buffer routines when characters are added | ||
| 146 | to or removed from the buffer. */ | ||
| 147 | void | ||
| 148 | serial_notify (void) | ||
| 149 | { | ||
| 150 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 151 | if (mode == QUEUE) | ||
| 152 | write_ier (); | ||
| 153 | } | ||
| 154 | |||
| 155 | /* Configures the serial port for BPS bits per second. */ | ||
| 156 | static void | ||
| 157 | set_serial (int bps) | ||
| 158 | { | ||
| 159 | int base_rate = 1843200 / 16; /* Base rate of 16550A, in Hz. */ | ||
| 160 | uint16_t divisor = base_rate / bps; /* Clock rate divisor. */ | ||
| 161 | |||
| 162 | ASSERT (bps >= 300 && bps <= 115200); | ||
| 163 | |||
| 164 | /* Enable DLAB. */ | ||
| 165 | outb (LCR_REG, LCR_N81 | LCR_DLAB); | ||
| 166 | |||
| 167 | /* Set data rate. */ | ||
| 168 | outb (LS_REG, divisor & 0xff); | ||
| 169 | outb (MS_REG, divisor >> 8); | ||
| 170 | |||
| 171 | /* Reset DLAB. */ | ||
| 172 | outb (LCR_REG, LCR_N81); | ||
| 173 | } | ||
| 174 | |||
| 175 | /* Update interrupt enable register. */ | ||
| 176 | static void | ||
| 177 | write_ier (void) | ||
| 178 | { | ||
| 179 | uint8_t ier = 0; | ||
| 180 | |||
| 181 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 182 | |||
| 183 | /* Enable transmit interrupt if we have any characters to | ||
| 184 | transmit. */ | ||
| 185 | if (!intq_empty (&txq)) | ||
| 186 | ier |= IER_XMIT; | ||
| 187 | |||
| 188 | /* Enable receive interrupt if we have room to store any | ||
| 189 | characters we receive. */ | ||
| 190 | if (!input_full ()) | ||
| 191 | ier |= IER_RECV; | ||
| 192 | |||
| 193 | outb (IER_REG, ier); | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Polls the serial port until it's ready, | ||
| 197 | and then transmits BYTE. */ | ||
| 198 | static void | ||
| 199 | putc_poll (uint8_t byte) | ||
| 200 | { | ||
| 201 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 202 | |||
| 203 | while ((inb (LSR_REG) & LSR_THRE) == 0) | ||
| 204 | continue; | ||
| 205 | outb (THR_REG, byte); | ||
| 206 | } | ||
| 207 | |||
| 208 | /* Serial interrupt handler. */ | ||
| 209 | static void | ||
| 210 | serial_interrupt (struct intr_frame *f UNUSED) | ||
| 211 | { | ||
| 212 | /* Inquire about interrupt in UART. Without this, we can | ||
| 213 | occasionally miss an interrupt running under QEMU. */ | ||
| 214 | inb (IIR_REG); | ||
| 215 | |||
| 216 | /* As long as we have room to receive a byte, and the hardware | ||
| 217 | has a byte for us, receive a byte. */ | ||
| 218 | while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0) | ||
| 219 | input_putc (inb (RBR_REG)); | ||
| 220 | |||
| 221 | /* As long as we have a byte to transmit, and the hardware is | ||
| 222 | ready to accept a byte for transmission, transmit a byte. */ | ||
| 223 | while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) | ||
| 224 | outb (THR_REG, intq_getc (&txq)); | ||
| 225 | |||
| 226 | /* Update interrupt enable register based on queue status. */ | ||
| 227 | write_ier (); | ||
| 228 | } | ||
diff --git a/pintos-progos/devices/serial.h b/pintos-progos/devices/serial.h new file mode 100644 index 0000000..6e04778 --- /dev/null +++ b/pintos-progos/devices/serial.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef DEVICES_SERIAL_H | ||
| 2 | #define DEVICES_SERIAL_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | void serial_init_queue (void); | ||
| 7 | void serial_putc (uint8_t); | ||
| 8 | void serial_flush (void); | ||
| 9 | void serial_notify (void); | ||
| 10 | |||
| 11 | #endif /* devices/serial.h */ | ||
diff --git a/pintos-progos/devices/shutdown.c b/pintos-progos/devices/shutdown.c new file mode 100644 index 0000000..7ff9a95 --- /dev/null +++ b/pintos-progos/devices/shutdown.c | |||
| @@ -0,0 +1,131 @@ | |||
| 1 | #include "devices/shutdown.h" | ||
| 2 | #include <console.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include "devices/kbd.h" | ||
| 5 | #include "devices/serial.h" | ||
| 6 | #include "devices/timer.h" | ||
| 7 | #include "threads/io.h" | ||
| 8 | #include "threads/thread.h" | ||
| 9 | #ifdef USERPROG | ||
| 10 | #include "userprog/exception.h" | ||
| 11 | #endif | ||
| 12 | #ifdef FILESYS | ||
| 13 | #include "devices/block.h" | ||
| 14 | #include "filesys/filesys.h" | ||
| 15 | #endif | ||
| 16 | |||
| 17 | /* Keyboard control register port. */ | ||
| 18 | #define CONTROL_REG 0x64 | ||
| 19 | |||
| 20 | /* How to shut down when shutdown() is called. */ | ||
| 21 | static enum shutdown_type how = SHUTDOWN_NONE; | ||
| 22 | |||
| 23 | static void print_stats (void); | ||
| 24 | |||
| 25 | /* Shuts down the machine in the way configured by | ||
| 26 | shutdown_configure(). If the shutdown type is SHUTDOWN_NONE | ||
| 27 | (which is the default), returns without doing anything. */ | ||
| 28 | void | ||
| 29 | shutdown (void) | ||
| 30 | { | ||
| 31 | switch (how) | ||
| 32 | { | ||
| 33 | case SHUTDOWN_POWER_OFF: | ||
| 34 | shutdown_power_off (); | ||
| 35 | break; | ||
| 36 | |||
| 37 | case SHUTDOWN_REBOOT: | ||
| 38 | shutdown_reboot (); | ||
| 39 | break; | ||
| 40 | |||
| 41 | default: | ||
| 42 | /* Nothing to do. */ | ||
| 43 | break; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | /* Sets TYPE as the way that machine will shut down when Pintos | ||
| 48 | execution is complete. */ | ||
| 49 | void | ||
| 50 | shutdown_configure (enum shutdown_type type) | ||
| 51 | { | ||
| 52 | how = type; | ||
| 53 | } | ||
| 54 | |||
| 55 | /* Reboots the machine via the keyboard controller. */ | ||
| 56 | void | ||
| 57 | shutdown_reboot (void) | ||
| 58 | { | ||
| 59 | printf ("Rebooting...\n"); | ||
| 60 | |||
| 61 | /* See [kbd] for details on how to program the keyboard | ||
| 62 | * controller. */ | ||
| 63 | for (;;) | ||
| 64 | { | ||
| 65 | int i; | ||
| 66 | |||
| 67 | /* Poll keyboard controller's status byte until | ||
| 68 | * 'input buffer empty' is reported. */ | ||
| 69 | for (i = 0; i < 0x10000; i++) | ||
| 70 | { | ||
| 71 | if ((inb (CONTROL_REG) & 0x02) == 0) | ||
| 72 | break; | ||
| 73 | timer_udelay (2); | ||
| 74 | } | ||
| 75 | |||
| 76 | timer_udelay (50); | ||
| 77 | |||
| 78 | /* Pulse bit 0 of the output port P2 of the keyboard controller. | ||
| 79 | * This will reset the CPU. */ | ||
| 80 | outb (CONTROL_REG, 0xfe); | ||
| 81 | timer_udelay (50); | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | /* Powers down the machine we're running on, | ||
| 86 | as long as we're running on Bochs or QEMU. */ | ||
| 87 | void | ||
| 88 | shutdown_power_off (void) | ||
| 89 | { | ||
| 90 | const char s[] = "Shutdown"; | ||
| 91 | const char *p; | ||
| 92 | |||
| 93 | #ifdef FILESYS | ||
| 94 | filesys_done (); | ||
| 95 | #endif | ||
| 96 | |||
| 97 | print_stats (); | ||
| 98 | |||
| 99 | printf ("Powering off...\n"); | ||
| 100 | serial_flush (); | ||
| 101 | |||
| 102 | /* This is a special power-off sequence supported by Bochs and | ||
| 103 | QEMU, but not by physical hardware. */ | ||
| 104 | for (p = s; *p != '\0'; p++) | ||
| 105 | outb (0x8900, *p); | ||
| 106 | |||
| 107 | /* This will power off a VMware VM if "gui.exitOnCLIHLT = TRUE" | ||
| 108 | is set in its configuration file. (The "pintos" script does | ||
| 109 | that automatically.) */ | ||
| 110 | asm volatile ("cli; hlt" : : : "memory"); | ||
| 111 | |||
| 112 | /* None of those worked. */ | ||
| 113 | printf ("still running...\n"); | ||
| 114 | for (;;); | ||
| 115 | } | ||
| 116 | |||
| 117 | /* Print statistics about Pintos execution. */ | ||
| 118 | static void | ||
| 119 | print_stats (void) | ||
| 120 | { | ||
| 121 | timer_print_stats (); | ||
| 122 | thread_print_stats (); | ||
| 123 | #ifdef FILESYS | ||
| 124 | block_print_stats (); | ||
| 125 | #endif | ||
| 126 | console_print_stats (); | ||
| 127 | kbd_print_stats (); | ||
| 128 | #ifdef USERPROG | ||
| 129 | exception_print_stats (); | ||
| 130 | #endif | ||
| 131 | } | ||
diff --git a/pintos-progos/devices/shutdown.h b/pintos-progos/devices/shutdown.h new file mode 100644 index 0000000..dc4f942 --- /dev/null +++ b/pintos-progos/devices/shutdown.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #ifndef DEVICES_SHUTDOWN_H | ||
| 2 | #define DEVICES_SHUTDOWN_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | |||
| 6 | /* How to shut down when Pintos has nothing left to do. */ | ||
| 7 | enum shutdown_type | ||
| 8 | { | ||
| 9 | SHUTDOWN_NONE, /* Loop forever. */ | ||
| 10 | SHUTDOWN_POWER_OFF, /* Power off the machine (if possible). */ | ||
| 11 | SHUTDOWN_REBOOT, /* Reboot the machine (if possible). */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | void shutdown (void); | ||
| 15 | void shutdown_configure (enum shutdown_type); | ||
| 16 | void shutdown_reboot (void) NO_RETURN; | ||
| 17 | void shutdown_power_off (void) NO_RETURN; | ||
| 18 | |||
| 19 | #endif /* devices/shutdown.h */ | ||
diff --git a/pintos-progos/devices/speaker.c b/pintos-progos/devices/speaker.c new file mode 100644 index 0000000..5052005 --- /dev/null +++ b/pintos-progos/devices/speaker.c | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | #include "devices/speaker.h" | ||
| 2 | #include "devices/pit.h" | ||
| 3 | #include "threads/io.h" | ||
| 4 | #include "threads/interrupt.h" | ||
| 5 | #include "devices/timer.h" | ||
| 6 | |||
| 7 | /* Speaker port enable I/O register. */ | ||
| 8 | #define SPEAKER_PORT_GATE 0x61 | ||
| 9 | |||
| 10 | /* Speaker port enable bits. */ | ||
| 11 | #define SPEAKER_GATE_ENABLE 0x03 | ||
| 12 | |||
| 13 | /* Sets the PC speaker to emit a tone at the given FREQUENCY, in | ||
| 14 | Hz. */ | ||
| 15 | void | ||
| 16 | speaker_on (int frequency) | ||
| 17 | { | ||
| 18 | if (frequency >= 20 && frequency <= 20000) | ||
| 19 | { | ||
| 20 | /* Set the timer channel that's connected to the speaker to | ||
| 21 | output a square wave at the given FREQUENCY, then | ||
| 22 | connect the timer channel output to the speaker. */ | ||
| 23 | enum intr_level old_level = intr_disable (); | ||
| 24 | pit_configure_channel (2, 3, frequency); | ||
| 25 | outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE); | ||
| 26 | intr_set_level (old_level); | ||
| 27 | } | ||
| 28 | else | ||
| 29 | { | ||
| 30 | /* FREQUENCY is outside the range of normal human hearing. | ||
| 31 | Just turn off the speaker. */ | ||
| 32 | speaker_off (); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Turn off the PC speaker, by disconnecting the timer channel's | ||
| 37 | output from the speaker. */ | ||
| 38 | void | ||
| 39 | speaker_off (void) | ||
| 40 | { | ||
| 41 | enum intr_level old_level = intr_disable (); | ||
| 42 | outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE); | ||
| 43 | intr_set_level (old_level); | ||
| 44 | } | ||
| 45 | |||
| 46 | /* Briefly beep the PC speaker. */ | ||
| 47 | void | ||
| 48 | speaker_beep (void) | ||
| 49 | { | ||
| 50 | /* Only attempt to beep the speaker if interrupts are enabled, | ||
| 51 | because we don't want to freeze the machine during the beep. | ||
| 52 | We could add a hook to the timer interrupt to avoid that | ||
| 53 | problem, but then we'd risk failing to ever stop the beep if | ||
| 54 | Pintos crashes for some unrelated reason. There's nothing | ||
| 55 | more annoying than a machine whose beeping you can't stop | ||
| 56 | without a power cycle. | ||
| 57 | |||
| 58 | We can't just enable interrupts while we sleep. For one | ||
| 59 | thing, we get called (indirectly) from printf, which should | ||
| 60 | always work, even during boot before we're ready to enable | ||
| 61 | interrupts. */ | ||
| 62 | if (intr_get_level () == INTR_ON) | ||
| 63 | { | ||
| 64 | speaker_on (440); | ||
| 65 | timer_msleep (250); | ||
| 66 | speaker_off (); | ||
| 67 | } | ||
| 68 | } | ||
diff --git a/pintos-progos/devices/speaker.h b/pintos-progos/devices/speaker.h new file mode 100644 index 0000000..98cef7b --- /dev/null +++ b/pintos-progos/devices/speaker.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef DEVICES_SPEAKER_H | ||
| 2 | #define DEVICES_SPEAKER_H | ||
| 3 | |||
| 4 | void speaker_on (int frequency); | ||
| 5 | void speaker_off (void); | ||
| 6 | void speaker_beep (void); | ||
| 7 | |||
| 8 | #endif /* devices/speaker.h */ | ||
diff --git a/pintos-progos/devices/timer.c b/pintos-progos/devices/timer.c new file mode 100644 index 0000000..befaaae --- /dev/null +++ b/pintos-progos/devices/timer.c | |||
| @@ -0,0 +1,246 @@ | |||
| 1 | #include "devices/timer.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <inttypes.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "devices/pit.h" | ||
| 7 | #include "threads/interrupt.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | |||
| 11 | /* See [8254] for hardware details of the 8254 timer chip. */ | ||
| 12 | |||
| 13 | #if TIMER_FREQ < 19 | ||
| 14 | #error 8254 timer requires TIMER_FREQ >= 19 | ||
| 15 | #endif | ||
| 16 | #if TIMER_FREQ > 1000 | ||
| 17 | #error TIMER_FREQ <= 1000 recommended | ||
| 18 | #endif | ||
| 19 | |||
| 20 | /* Number of timer ticks since OS booted. */ | ||
| 21 | static int64_t ticks; | ||
| 22 | |||
| 23 | /* Number of loops per timer tick. | ||
| 24 | Initialized by timer_calibrate(). */ | ||
| 25 | static unsigned loops_per_tick; | ||
| 26 | |||
| 27 | static intr_handler_func timer_interrupt; | ||
| 28 | static bool too_many_loops (unsigned loops); | ||
| 29 | static void busy_wait (int64_t loops); | ||
| 30 | static void real_time_sleep (int64_t num, int32_t denom); | ||
| 31 | static void real_time_delay (int64_t num, int32_t denom); | ||
| 32 | |||
| 33 | /* Sets up the timer to interrupt TIMER_FREQ times per second, | ||
| 34 | and registers the corresponding interrupt. */ | ||
| 35 | void | ||
| 36 | timer_init (void) | ||
| 37 | { | ||
| 38 | pit_configure_channel (0, 2, TIMER_FREQ); | ||
| 39 | intr_register_ext (0x20, timer_interrupt, "8254 Timer"); | ||
| 40 | } | ||
| 41 | |||
| 42 | /* Calibrates loops_per_tick, used to implement brief delays. */ | ||
| 43 | void | ||
| 44 | timer_calibrate (void) | ||
| 45 | { | ||
| 46 | unsigned high_bit, test_bit; | ||
| 47 | |||
| 48 | ASSERT (intr_get_level () == INTR_ON); | ||
| 49 | printf ("Calibrating timer... "); | ||
| 50 | |||
| 51 | /* Approximate loops_per_tick as the largest power-of-two | ||
| 52 | still less than one timer tick. */ | ||
| 53 | loops_per_tick = 1u << 10; | ||
| 54 | while (!too_many_loops (loops_per_tick << 1)) | ||
| 55 | { | ||
| 56 | loops_per_tick <<= 1; | ||
| 57 | ASSERT (loops_per_tick != 0); | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Refine the next 8 bits of loops_per_tick. */ | ||
| 61 | high_bit = loops_per_tick; | ||
| 62 | for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1) | ||
| 63 | if (!too_many_loops (high_bit | test_bit)) | ||
| 64 | loops_per_tick |= test_bit; | ||
| 65 | |||
| 66 | printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ); | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Returns the number of timer ticks since the OS booted. */ | ||
| 70 | int64_t | ||
| 71 | timer_ticks (void) | ||
| 72 | { | ||
| 73 | enum intr_level old_level = intr_disable (); | ||
| 74 | int64_t t = ticks; | ||
| 75 | intr_set_level (old_level); | ||
| 76 | return t; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Returns the number of timer ticks elapsed since THEN, which | ||
| 80 | should be a value once returned by timer_ticks(). */ | ||
| 81 | int64_t | ||
| 82 | timer_elapsed (int64_t then) | ||
| 83 | { | ||
| 84 | return timer_ticks () - then; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Sleeps for approximately TICKS timer ticks. Interrupts must | ||
| 88 | be turned on. */ | ||
| 89 | void | ||
| 90 | timer_sleep (int64_t ticks) | ||
| 91 | { | ||
| 92 | int64_t start = timer_ticks (); | ||
| 93 | |||
| 94 | ASSERT (intr_get_level () == INTR_ON); | ||
| 95 | while (timer_elapsed (start) < ticks) | ||
| 96 | thread_yield (); | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Sleeps for approximately MS milliseconds. Interrupts must be | ||
| 100 | turned on. */ | ||
| 101 | void | ||
| 102 | timer_msleep (int64_t ms) | ||
| 103 | { | ||
| 104 | real_time_sleep (ms, 1000); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* Sleeps for approximately US microseconds. Interrupts must be | ||
| 108 | turned on. */ | ||
| 109 | void | ||
| 110 | timer_usleep (int64_t us) | ||
| 111 | { | ||
| 112 | real_time_sleep (us, 1000 * 1000); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Sleeps for approximately NS nanoseconds. Interrupts must be | ||
| 116 | turned on. */ | ||
| 117 | void | ||
| 118 | timer_nsleep (int64_t ns) | ||
| 119 | { | ||
| 120 | real_time_sleep (ns, 1000 * 1000 * 1000); | ||
| 121 | } | ||
| 122 | |||
| 123 | /* Busy-waits for approximately MS milliseconds. Interrupts need | ||
| 124 | not be turned on. | ||
| 125 | |||
| 126 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 127 | interrupts off for the interval between timer ticks or longer | ||
| 128 | will cause timer ticks to be lost. Thus, use timer_msleep() | ||
| 129 | instead if interrupts are enabled. */ | ||
| 130 | void | ||
| 131 | timer_mdelay (int64_t ms) | ||
| 132 | { | ||
| 133 | real_time_delay (ms, 1000); | ||
| 134 | } | ||
| 135 | |||
| 136 | /* Sleeps for approximately US microseconds. Interrupts need not | ||
| 137 | be turned on. | ||
| 138 | |||
| 139 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 140 | interrupts off for the interval between timer ticks or longer | ||
| 141 | will cause timer ticks to be lost. Thus, use timer_usleep() | ||
| 142 | instead if interrupts are enabled. */ | ||
| 143 | void | ||
| 144 | timer_udelay (int64_t us) | ||
| 145 | { | ||
| 146 | real_time_delay (us, 1000 * 1000); | ||
| 147 | } | ||
| 148 | |||
| 149 | /* Sleeps execution for approximately NS nanoseconds. Interrupts | ||
| 150 | need not be turned on. | ||
| 151 | |||
| 152 | Busy waiting wastes CPU cycles, and busy waiting with | ||
| 153 | interrupts off for the interval between timer ticks or longer | ||
| 154 | will cause timer ticks to be lost. Thus, use timer_nsleep() | ||
| 155 | instead if interrupts are enabled.*/ | ||
| 156 | void | ||
| 157 | timer_ndelay (int64_t ns) | ||
| 158 | { | ||
| 159 | real_time_delay (ns, 1000 * 1000 * 1000); | ||
| 160 | } | ||
| 161 | |||
| 162 | /* Prints timer statistics. */ | ||
| 163 | void | ||
| 164 | timer_print_stats (void) | ||
| 165 | { | ||
| 166 | printf ("Timer: %"PRId64" ticks\n", timer_ticks ()); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* Timer interrupt handler. */ | ||
| 170 | static void | ||
| 171 | timer_interrupt (struct intr_frame *args UNUSED) | ||
| 172 | { | ||
| 173 | ticks++; | ||
| 174 | thread_tick (); | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Returns true if LOOPS iterations waits for more than one timer | ||
| 178 | tick, otherwise false. */ | ||
| 179 | static bool | ||
| 180 | too_many_loops (unsigned loops) | ||
| 181 | { | ||
| 182 | /* Wait for a timer tick. */ | ||
| 183 | int64_t start = ticks; | ||
| 184 | while (ticks == start) | ||
| 185 | barrier (); | ||
| 186 | |||
| 187 | /* Run LOOPS loops. */ | ||
| 188 | start = ticks; | ||
| 189 | busy_wait (loops); | ||
| 190 | |||
| 191 | /* If the tick count changed, we iterated too long. */ | ||
| 192 | barrier (); | ||
| 193 | return start != ticks; | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Iterates through a simple loop LOOPS times, for implementing | ||
| 197 | brief delays. | ||
| 198 | |||
| 199 | Marked NO_INLINE because code alignment can significantly | ||
| 200 | affect timings, so that if this function was inlined | ||
| 201 | differently in different places the results would be difficult | ||
| 202 | to predict. */ | ||
| 203 | static void NO_INLINE | ||
| 204 | busy_wait (int64_t loops) | ||
| 205 | { | ||
| 206 | while (loops-- > 0) | ||
| 207 | barrier (); | ||
| 208 | } | ||
| 209 | |||
| 210 | /* Sleep for approximately NUM/DENOM seconds. */ | ||
| 211 | static void | ||
| 212 | real_time_sleep (int64_t num, int32_t denom) | ||
| 213 | { | ||
| 214 | /* Convert NUM/DENOM seconds into timer ticks, rounding down. | ||
| 215 | |||
| 216 | (NUM / DENOM) s | ||
| 217 | ---------------------- = NUM * TIMER_FREQ / DENOM ticks. | ||
| 218 | 1 s / TIMER_FREQ ticks | ||
| 219 | */ | ||
| 220 | int64_t ticks = num * TIMER_FREQ / denom; | ||
| 221 | |||
| 222 | ASSERT (intr_get_level () == INTR_ON); | ||
| 223 | if (ticks > 0) | ||
| 224 | { | ||
| 225 | /* We're waiting for at least one full timer tick. Use | ||
| 226 | timer_sleep() because it will yield the CPU to other | ||
| 227 | processes. */ | ||
| 228 | timer_sleep (ticks); | ||
| 229 | } | ||
| 230 | else | ||
| 231 | { | ||
| 232 | /* Otherwise, use a busy-wait loop for more accurate | ||
| 233 | sub-tick timing. */ | ||
| 234 | real_time_delay (num, denom); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | /* Busy-wait for approximately NUM/DENOM seconds. */ | ||
| 239 | static void | ||
| 240 | real_time_delay (int64_t num, int32_t denom) | ||
| 241 | { | ||
| 242 | /* Scale the numerator and denominator down by 1000 to avoid | ||
| 243 | the possibility of overflow. */ | ||
| 244 | ASSERT (denom % 1000 == 0); | ||
| 245 | busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000)); | ||
| 246 | } | ||
diff --git a/pintos-progos/devices/timer.h b/pintos-progos/devices/timer.h new file mode 100644 index 0000000..cd3d6bb --- /dev/null +++ b/pintos-progos/devices/timer.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #ifndef DEVICES_TIMER_H | ||
| 2 | #define DEVICES_TIMER_H | ||
| 3 | |||
| 4 | #include <round.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | /* Number of timer interrupts per second. */ | ||
| 8 | #define TIMER_FREQ 100 | ||
| 9 | |||
| 10 | void timer_init (void); | ||
| 11 | void timer_calibrate (void); | ||
| 12 | |||
| 13 | int64_t timer_ticks (void); | ||
| 14 | int64_t timer_elapsed (int64_t); | ||
| 15 | |||
| 16 | /* Sleep and yield the CPU to other threads. */ | ||
| 17 | void timer_sleep (int64_t ticks); | ||
| 18 | void timer_msleep (int64_t milliseconds); | ||
| 19 | void timer_usleep (int64_t microseconds); | ||
| 20 | void timer_nsleep (int64_t nanoseconds); | ||
| 21 | |||
| 22 | /* Busy waits. */ | ||
| 23 | void timer_mdelay (int64_t milliseconds); | ||
| 24 | void timer_udelay (int64_t microseconds); | ||
| 25 | void timer_ndelay (int64_t nanoseconds); | ||
| 26 | |||
| 27 | void timer_print_stats (void); | ||
| 28 | |||
| 29 | #endif /* devices/timer.h */ | ||
diff --git a/pintos-progos/devices/vga.c b/pintos-progos/devices/vga.c new file mode 100644 index 0000000..f421b61 --- /dev/null +++ b/pintos-progos/devices/vga.c | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | #include "devices/vga.h" | ||
| 2 | #include <round.h> | ||
| 3 | #include <stdint.h> | ||
| 4 | #include <stddef.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include "devices/speaker.h" | ||
| 7 | #include "threads/io.h" | ||
| 8 | #include "threads/interrupt.h" | ||
| 9 | #include "threads/vaddr.h" | ||
| 10 | |||
| 11 | /* VGA text screen support. See [FREEVGA] for more information. */ | ||
| 12 | |||
| 13 | /* Number of columns and rows on the text display. */ | ||
| 14 | #define COL_CNT 80 | ||
| 15 | #define ROW_CNT 25 | ||
| 16 | |||
| 17 | /* Current cursor position. (0,0) is in the upper left corner of | ||
| 18 | the display. */ | ||
| 19 | static size_t cx, cy; | ||
| 20 | |||
| 21 | /* Attribute value for gray text on a black background. */ | ||
| 22 | #define GRAY_ON_BLACK 0x07 | ||
| 23 | |||
| 24 | /* Framebuffer. See [FREEVGA] under "VGA Text Mode Operation". | ||
| 25 | The character at (x,y) is fb[y][x][0]. | ||
| 26 | The attribute at (x,y) is fb[y][x][1]. */ | ||
| 27 | static uint8_t (*fb)[COL_CNT][2]; | ||
| 28 | |||
| 29 | static void clear_row (size_t y); | ||
| 30 | static void cls (void); | ||
| 31 | static void newline (void); | ||
| 32 | static void move_cursor (void); | ||
| 33 | static void find_cursor (size_t *x, size_t *y); | ||
| 34 | |||
| 35 | /* Initializes the VGA text display. */ | ||
| 36 | static void | ||
| 37 | init (void) | ||
| 38 | { | ||
| 39 | /* Already initialized? */ | ||
| 40 | static bool inited; | ||
| 41 | if (!inited) | ||
| 42 | { | ||
| 43 | fb = ptov (0xb8000); | ||
| 44 | find_cursor (&cx, &cy); | ||
| 45 | inited = true; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | /* Writes C to the VGA text display, interpreting control | ||
| 50 | characters in the conventional ways. */ | ||
| 51 | void | ||
| 52 | vga_putc (int c) | ||
| 53 | { | ||
| 54 | /* Disable interrupts to lock out interrupt handlers | ||
| 55 | that might write to the console. */ | ||
| 56 | enum intr_level old_level = intr_disable (); | ||
| 57 | |||
| 58 | init (); | ||
| 59 | |||
| 60 | switch (c) | ||
| 61 | { | ||
| 62 | case '\n': | ||
| 63 | newline (); | ||
| 64 | break; | ||
| 65 | |||
| 66 | case '\f': | ||
| 67 | cls (); | ||
| 68 | break; | ||
| 69 | |||
| 70 | case '\b': | ||
| 71 | if (cx > 0) | ||
| 72 | cx--; | ||
| 73 | break; | ||
| 74 | |||
| 75 | case '\r': | ||
| 76 | cx = 0; | ||
| 77 | break; | ||
| 78 | |||
| 79 | case '\t': | ||
| 80 | cx = ROUND_UP (cx + 1, 8); | ||
| 81 | if (cx >= COL_CNT) | ||
| 82 | newline (); | ||
| 83 | break; | ||
| 84 | |||
| 85 | case '\a': | ||
| 86 | intr_set_level (old_level); | ||
| 87 | speaker_beep (); | ||
| 88 | intr_disable (); | ||
| 89 | break; | ||
| 90 | |||
| 91 | default: | ||
| 92 | fb[cy][cx][0] = c; | ||
| 93 | fb[cy][cx][1] = GRAY_ON_BLACK; | ||
| 94 | if (++cx >= COL_CNT) | ||
| 95 | newline (); | ||
| 96 | break; | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Update cursor position. */ | ||
| 100 | move_cursor (); | ||
| 101 | |||
| 102 | intr_set_level (old_level); | ||
| 103 | } | ||
| 104 | |||
| 105 | /* Clears the screen and moves the cursor to the upper left. */ | ||
| 106 | static void | ||
| 107 | cls (void) | ||
| 108 | { | ||
| 109 | size_t y; | ||
| 110 | |||
| 111 | for (y = 0; y < ROW_CNT; y++) | ||
| 112 | clear_row (y); | ||
| 113 | |||
| 114 | cx = cy = 0; | ||
| 115 | move_cursor (); | ||
| 116 | } | ||
| 117 | |||
| 118 | /* Clears row Y to spaces. */ | ||
| 119 | static void | ||
| 120 | clear_row (size_t y) | ||
| 121 | { | ||
| 122 | size_t x; | ||
| 123 | |||
| 124 | for (x = 0; x < COL_CNT; x++) | ||
| 125 | { | ||
| 126 | fb[y][x][0] = ' '; | ||
| 127 | fb[y][x][1] = GRAY_ON_BLACK; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /* Advances the cursor to the first column in the next line on | ||
| 132 | the screen. If the cursor is already on the last line on the | ||
| 133 | screen, scrolls the screen upward one line. */ | ||
| 134 | static void | ||
| 135 | newline (void) | ||
| 136 | { | ||
| 137 | cx = 0; | ||
| 138 | cy++; | ||
| 139 | if (cy >= ROW_CNT) | ||
| 140 | { | ||
| 141 | cy = ROW_CNT - 1; | ||
| 142 | memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1)); | ||
| 143 | clear_row (ROW_CNT - 1); | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | /* Moves the hardware cursor to (cx,cy). */ | ||
| 148 | static void | ||
| 149 | move_cursor (void) | ||
| 150 | { | ||
| 151 | /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */ | ||
| 152 | uint16_t cp = cx + COL_CNT * cy; | ||
| 153 | outw (0x3d4, 0x0e | (cp & 0xff00)); | ||
| 154 | outw (0x3d4, 0x0f | (cp << 8)); | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Reads the current hardware cursor position into (*X,*Y). */ | ||
| 158 | static void | ||
| 159 | find_cursor (size_t *x, size_t *y) | ||
| 160 | { | ||
| 161 | /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */ | ||
| 162 | uint16_t cp; | ||
| 163 | |||
| 164 | outb (0x3d4, 0x0e); | ||
| 165 | cp = inb (0x3d5) << 8; | ||
| 166 | |||
| 167 | outb (0x3d4, 0x0f); | ||
| 168 | cp |= inb (0x3d5); | ||
| 169 | |||
| 170 | *x = cp % COL_CNT; | ||
| 171 | *y = cp / COL_CNT; | ||
| 172 | } | ||
diff --git a/pintos-progos/devices/vga.h b/pintos-progos/devices/vga.h new file mode 100644 index 0000000..59690fb --- /dev/null +++ b/pintos-progos/devices/vga.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef DEVICES_VGA_H | ||
| 2 | #define DEVICES_VGA_H | ||
| 3 | |||
| 4 | void vga_putc (int); | ||
| 5 | |||
| 6 | #endif /* devices/vga.h */ | ||
diff --git a/pintos-progos/examples/.gitignore b/pintos-progos/examples/.gitignore new file mode 100644 index 0000000..a9e09d7 --- /dev/null +++ b/pintos-progos/examples/.gitignore | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | cat | ||
| 2 | cmp | ||
| 3 | cp | ||
| 4 | echo | ||
| 5 | halt | ||
| 6 | hex-dump | ||
| 7 | ls | ||
| 8 | mcat | ||
| 9 | mcp | ||
| 10 | mkdir | ||
| 11 | pwd | ||
| 12 | rm | ||
| 13 | shell | ||
| 14 | bubsort | ||
| 15 | insult | ||
| 16 | lineup | ||
| 17 | matmult | ||
| 18 | recursor | ||
| 19 | *.d | ||
diff --git a/pintos-progos/examples/Makefile b/pintos-progos/examples/Makefile new file mode 100644 index 0000000..f773950 --- /dev/null +++ b/pintos-progos/examples/Makefile | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | SRCDIR = .. | ||
| 2 | |||
| 3 | # Test programs to compile, and a list of sources for each. | ||
| 4 | # To add a new test, put its name on the PROGS list | ||
| 5 | # and then add a name_SRC line that lists its source files. | ||
| 6 | PROGS = cat cmp cp echo halt hello hex-dump ls mcat mcp mkdir pwd rm shell \ | ||
| 7 | bubsort insult lineup matmult recursor test | ||
| 8 | |||
| 9 | # Should work from project 2 onward. | ||
| 10 | cat_SRC = cat.c | ||
| 11 | cmp_SRC = cmp.c | ||
| 12 | cp_SRC = cp.c | ||
| 13 | echo_SRC = echo.c | ||
| 14 | halt_SRC = halt.c | ||
| 15 | hello_SRC = hello.c | ||
| 16 | hex-dump_SRC = hex-dump.c | ||
| 17 | insult_SRC = insult.c | ||
| 18 | lineup_SRC = lineup.c | ||
| 19 | ls_SRC = ls.c | ||
| 20 | recursor_SRC = recursor.c | ||
| 21 | rm_SRC = rm.c | ||
| 22 | test_SRC = test.c | ||
| 23 | |||
| 24 | # Should work in project 3; also in project 4 if VM is included. | ||
| 25 | bubsort_SRC = bubsort.c | ||
| 26 | matmult_SRC = matmult.c | ||
| 27 | mcat_SRC = mcat.c | ||
| 28 | mcp_SRC = mcp.c | ||
| 29 | |||
| 30 | # Should work in project 4. | ||
| 31 | mkdir_SRC = mkdir.c | ||
| 32 | pwd_SRC = pwd.c | ||
| 33 | shell_SRC = shell.c | ||
| 34 | |||
| 35 | include $(SRCDIR)/Make.config | ||
| 36 | include $(SRCDIR)/Makefile.userprog | ||
diff --git a/pintos-progos/examples/bubsort.c b/pintos-progos/examples/bubsort.c new file mode 100644 index 0000000..343219e --- /dev/null +++ b/pintos-progos/examples/bubsort.c | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* sort.c | ||
| 2 | |||
| 3 | Test program to sort a large number of integers. | ||
| 4 | |||
| 5 | Intention is to stress virtual memory system. | ||
| 6 | |||
| 7 | Ideally, we could read the unsorted array off of the file | ||
| 8 | system, and store the result back to the file system! */ | ||
| 9 | #include <stdio.h> | ||
| 10 | |||
| 11 | /* Size of array to sort. */ | ||
| 12 | #define SORT_SIZE 128 | ||
| 13 | |||
| 14 | int | ||
| 15 | main (void) | ||
| 16 | { | ||
| 17 | /* Array to sort. Static to reduce stack usage. */ | ||
| 18 | static int array[SORT_SIZE]; | ||
| 19 | |||
| 20 | int i, j, tmp; | ||
| 21 | |||
| 22 | /* First initialize the array in descending order. */ | ||
| 23 | for (i = 0; i < SORT_SIZE; i++) | ||
| 24 | array[i] = SORT_SIZE - i - 1; | ||
| 25 | |||
| 26 | /* Then sort in ascending order. */ | ||
| 27 | for (i = 0; i < SORT_SIZE - 1; i++) | ||
| 28 | for (j = 0; j < SORT_SIZE - 1 - i; j++) | ||
| 29 | if (array[j] > array[j + 1]) | ||
| 30 | { | ||
| 31 | tmp = array[j]; | ||
| 32 | array[j] = array[j + 1]; | ||
| 33 | array[j + 1] = tmp; | ||
| 34 | } | ||
| 35 | |||
| 36 | printf ("sort exiting with code %d\n", array[0]); | ||
| 37 | return array[0]; | ||
| 38 | } | ||
diff --git a/pintos-progos/examples/cat.c b/pintos-progos/examples/cat.c new file mode 100644 index 0000000..c8d229d --- /dev/null +++ b/pintos-progos/examples/cat.c | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* cat.c | ||
| 2 | |||
| 3 | Prints files specified on command line to the console. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | bool success = true; | ||
| 12 | int i; | ||
| 13 | |||
| 14 | for (i = 1; i < argc; i++) | ||
| 15 | { | ||
| 16 | int fd = open (argv[i]); | ||
| 17 | if (fd < 0) | ||
| 18 | { | ||
| 19 | printf ("%s: open failed\n", argv[i]); | ||
| 20 | success = false; | ||
| 21 | continue; | ||
| 22 | } | ||
| 23 | for (;;) | ||
| 24 | { | ||
| 25 | char buffer[1024]; | ||
| 26 | int bytes_read = read (fd, buffer, sizeof buffer); | ||
| 27 | if (bytes_read == 0) | ||
| 28 | break; | ||
| 29 | write (STDOUT_FILENO, buffer, bytes_read); | ||
| 30 | } | ||
| 31 | close (fd); | ||
| 32 | } | ||
| 33 | return success ? EXIT_SUCCESS : EXIT_FAILURE; | ||
| 34 | } | ||
diff --git a/pintos-progos/examples/cmp.c b/pintos-progos/examples/cmp.c new file mode 100644 index 0000000..94b406d --- /dev/null +++ b/pintos-progos/examples/cmp.c | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* cat.c | ||
| 2 | |||
| 3 | Compares two files. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | int fd[2]; | ||
| 12 | |||
| 13 | if (argc != 3) | ||
| 14 | { | ||
| 15 | printf ("usage: cmp A B\n"); | ||
| 16 | return EXIT_FAILURE; | ||
| 17 | } | ||
| 18 | |||
| 19 | /* Open files. */ | ||
| 20 | fd[0] = open (argv[1]); | ||
| 21 | if (fd[0] < 0) | ||
| 22 | { | ||
| 23 | printf ("%s: open failed\n", argv[1]); | ||
| 24 | return EXIT_FAILURE; | ||
| 25 | } | ||
| 26 | fd[1] = open (argv[2]); | ||
| 27 | if (fd[1] < 0) | ||
| 28 | { | ||
| 29 | printf ("%s: open failed\n", argv[1]); | ||
| 30 | return EXIT_FAILURE; | ||
| 31 | } | ||
| 32 | |||
| 33 | /* Compare data. */ | ||
| 34 | for (;;) | ||
| 35 | { | ||
| 36 | int pos; | ||
| 37 | char buffer[2][1024]; | ||
| 38 | int bytes_read[2]; | ||
| 39 | int min_read; | ||
| 40 | int i; | ||
| 41 | |||
| 42 | pos = tell (fd[0]); | ||
| 43 | bytes_read[0] = read (fd[0], buffer[0], sizeof buffer[0]); | ||
| 44 | bytes_read[1] = read (fd[1], buffer[1], sizeof buffer[1]); | ||
| 45 | min_read = bytes_read[0] < bytes_read[1] ? bytes_read[0] : bytes_read[1]; | ||
| 46 | if (min_read == 0) | ||
| 47 | break; | ||
| 48 | |||
| 49 | for (i = 0; i < min_read; i++) | ||
| 50 | if (buffer[0][i] != buffer[1][i]) | ||
| 51 | { | ||
| 52 | printf ("Byte %d is %02hhx ('%c') in %s but %02hhx ('%c') in %s\n", | ||
| 53 | pos + i, | ||
| 54 | buffer[0][i], buffer[0][i], argv[1], | ||
| 55 | buffer[1][i], buffer[1][i], argv[2]); | ||
| 56 | return EXIT_FAILURE; | ||
| 57 | } | ||
| 58 | |||
| 59 | if (min_read < bytes_read[1]) | ||
| 60 | printf ("%s is shorter than %s\n", argv[1], argv[2]); | ||
| 61 | else if (min_read < bytes_read[0]) | ||
| 62 | printf ("%s is shorter than %s\n", argv[2], argv[1]); | ||
| 63 | } | ||
| 64 | |||
| 65 | printf ("%s and %s are identical\n", argv[1], argv[2]); | ||
| 66 | |||
| 67 | return EXIT_SUCCESS; | ||
| 68 | } | ||
diff --git a/pintos-progos/examples/cp.c b/pintos-progos/examples/cp.c new file mode 100644 index 0000000..86a5cd7 --- /dev/null +++ b/pintos-progos/examples/cp.c | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* cat.c | ||
| 2 | |||
| 3 | Copies one file to another. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | int in_fd, out_fd; | ||
| 12 | |||
| 13 | if (argc != 3) | ||
| 14 | { | ||
| 15 | printf ("usage: cp OLD NEW\n"); | ||
| 16 | return EXIT_FAILURE; | ||
| 17 | } | ||
| 18 | |||
| 19 | /* Open input file. */ | ||
| 20 | in_fd = open (argv[1]); | ||
| 21 | if (in_fd < 0) | ||
| 22 | { | ||
| 23 | printf ("%s: open failed\n", argv[1]); | ||
| 24 | return EXIT_FAILURE; | ||
| 25 | } | ||
| 26 | |||
| 27 | /* Create and open output file. */ | ||
| 28 | if (!create (argv[2], filesize (in_fd))) | ||
| 29 | { | ||
| 30 | printf ("%s: create failed\n", argv[2]); | ||
| 31 | return EXIT_FAILURE; | ||
| 32 | } | ||
| 33 | out_fd = open (argv[2]); | ||
| 34 | if (out_fd < 0) | ||
| 35 | { | ||
| 36 | printf ("%s: open failed\n", argv[2]); | ||
| 37 | return EXIT_FAILURE; | ||
| 38 | } | ||
| 39 | |||
| 40 | /* Copy data. */ | ||
| 41 | for (;;) | ||
| 42 | { | ||
| 43 | char buffer[1024]; | ||
| 44 | int bytes_read = read (in_fd, buffer, sizeof buffer); | ||
| 45 | if (bytes_read == 0) | ||
| 46 | break; | ||
| 47 | if (write (out_fd, buffer, bytes_read) != bytes_read) | ||
| 48 | { | ||
| 49 | printf ("%s: write failed\n", argv[2]); | ||
| 50 | return EXIT_FAILURE; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | return EXIT_SUCCESS; | ||
| 55 | } | ||
diff --git a/pintos-progos/examples/echo.c b/pintos-progos/examples/echo.c new file mode 100644 index 0000000..1b136f2 --- /dev/null +++ b/pintos-progos/examples/echo.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <syscall.h> | ||
| 3 | |||
| 4 | int | ||
| 5 | main (int argc, char **argv) | ||
| 6 | { | ||
| 7 | int i; | ||
| 8 | |||
| 9 | for (i = 0; i < argc; i++) | ||
| 10 | printf ("%s ", argv[i]); | ||
| 11 | printf ("\n"); | ||
| 12 | |||
| 13 | return EXIT_SUCCESS; | ||
| 14 | } | ||
diff --git a/pintos-progos/examples/halt.c b/pintos-progos/examples/halt.c new file mode 100644 index 0000000..bad7250 --- /dev/null +++ b/pintos-progos/examples/halt.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* halt.c | ||
| 2 | |||
| 3 | Simple program to test whether running a user program works. | ||
| 4 | |||
| 5 | Just invokes a system call that shuts down the OS. */ | ||
| 6 | |||
| 7 | #include <syscall.h> | ||
| 8 | |||
| 9 | int | ||
| 10 | main (void) | ||
| 11 | { | ||
| 12 | halt (); | ||
| 13 | /* not reached */ | ||
| 14 | } | ||
diff --git a/pintos-progos/examples/hello.c b/pintos-progos/examples/hello.c new file mode 100644 index 0000000..a12fd94 --- /dev/null +++ b/pintos-progos/examples/hello.c | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <syscall.h> | ||
| 3 | |||
| 4 | int | ||
| 5 | main (int argc, char **argv) | ||
| 6 | { | ||
| 7 | printf ("Hello World :)\n"); | ||
| 8 | return EXIT_SUCCESS; | ||
| 9 | } | ||
diff --git a/pintos-progos/examples/hex-dump.c b/pintos-progos/examples/hex-dump.c new file mode 100644 index 0000000..ee313f2 --- /dev/null +++ b/pintos-progos/examples/hex-dump.c | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* hex-dump.c | ||
| 2 | |||
| 3 | Prints files specified on command line to the console in hex. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | bool success = true; | ||
| 12 | int i; | ||
| 13 | |||
| 14 | for (i = 1; i < argc; i++) | ||
| 15 | { | ||
| 16 | int fd = open (argv[i]); | ||
| 17 | if (fd < 0) | ||
| 18 | { | ||
| 19 | printf ("%s: open failed\n", argv[i]); | ||
| 20 | success = false; | ||
| 21 | continue; | ||
| 22 | } | ||
| 23 | for (;;) | ||
| 24 | { | ||
| 25 | char buffer[1024]; | ||
| 26 | int pos = tell (fd); | ||
| 27 | int bytes_read = read (fd, buffer, sizeof buffer); | ||
| 28 | if (bytes_read == 0) | ||
| 29 | break; | ||
| 30 | hex_dump (pos, buffer, bytes_read, true); | ||
| 31 | } | ||
| 32 | close (fd); | ||
| 33 | } | ||
| 34 | return success ? EXIT_SUCCESS : EXIT_FAILURE; | ||
| 35 | } | ||
diff --git a/pintos-progos/examples/insult.c b/pintos-progos/examples/insult.c new file mode 100644 index 0000000..98c4e6a --- /dev/null +++ b/pintos-progos/examples/insult.c | |||
| @@ -0,0 +1,369 @@ | |||
| 1 | /* Insult.c | ||
| 2 | |||
| 3 | This is a version of the famous CS 107 random sentence | ||
| 4 | generator. I wrote a program that reads a grammar definition | ||
| 5 | file and writes a C file containing that grammar as hard code | ||
| 6 | static C strings. Thus the majority of the code below in | ||
| 7 | machine generated and totally unreadable. The arrays created | ||
| 8 | are specially designed to make generating the sentences as | ||
| 9 | easy as possible. | ||
| 10 | |||
| 11 | Originally by Greg Hutchins, March 1998. | ||
| 12 | Modified by Ben Pfaff for Pintos, Sept 2004. */ | ||
| 13 | char *start[] = | ||
| 14 | { "You", "1", "5", ".", "May", "13", ".", "With", "the", "19", "of", "18", | ||
| 15 | ",", "may", "13", "." | ||
| 16 | }; | ||
| 17 | char startLoc[] = { 3, 0, 4, 7, 16 }; | ||
| 18 | char *adj[] = { "3", "4", "2", ",", "1" }; | ||
| 19 | char adjLoc[] = { 3, 0, 1, 2, 5 }; | ||
| 20 | char *adj3[] = { "3", "4" }; | ||
| 21 | char adj3Loc[] = { 2, 0, 1, 2 }; | ||
| 22 | char *adj1[] = | ||
| 23 | { "lame", "dried", "up", "par-broiled", "bloated", "half-baked", "spiteful", | ||
| 24 | "egotistical", "ungrateful", "stupid", "moronic", "fat", "ugly", "puny", "pitiful", | ||
| 25 | "insignificant", "blithering", "repulsive", "worthless", "blundering", "retarded", | ||
| 26 | "useless", "obnoxious", "low-budget", "assinine", "neurotic", "subhuman", "crochety", | ||
| 27 | "indescribable", "contemptible", "unspeakable", "sick", "lazy", "good-for-nothing", | ||
| 28 | "slutty", "mentally-deficient", "creepy", "sloppy", "dismal", "pompous", "pathetic", | ||
| 29 | "friendless", "revolting", "slovenly", "cantankerous", "uncultured", "insufferable", | ||
| 30 | "gross", "unkempt", "defective", "crumby" | ||
| 31 | }; | ||
| 32 | char adj1Loc[] = | ||
| 33 | { 50, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, | ||
| 34 | 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, | ||
| 35 | 43, 44, 45, 46, 47, 48, 49, 50, 51 }; | ||
| 36 | char *adj2[] = | ||
| 37 | { "putrefied", "festering", "funky", "moldy", "leprous", "curdled", "fetid", | ||
| 38 | "slimy", "crusty", "sweaty", "damp", "deranged", "smelly", "stenchy", "malignant", | ||
| 39 | "noxious", "grimy", "reeky", "nasty", "mutilated", "sloppy", "gruesome", "grisly", | ||
| 40 | "sloshy", "wormy", "mealy", "spoiled", "contaminated", "rancid", "musty", | ||
| 41 | "fly-covered", "moth-eaten", "decaying", "decomposed", "freeze-dried", "defective", | ||
| 42 | "petrified", "rotting", "scabrous", "hirsute" | ||
| 43 | }; | ||
| 44 | char adj2Loc[] = | ||
| 45 | { 40, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, | ||
| 46 | 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 }; | ||
| 47 | char *name[] = | ||
| 48 | { "10", ",", "bad", "excuse", "for", "6", ",", "6", "for", "brains", ",", | ||
| 49 | "4", "11", "8", "for", "brains", "offspring", "of", "a", "motherless", "10", "7", "6", | ||
| 50 | "7", "4", "11", "8" | ||
| 51 | }; | ||
| 52 | char nameLoc[] = { 7, 0, 1, 6, 10, 16, 21, 23, 27 }; | ||
| 53 | char *stuff[] = | ||
| 54 | { "shit", "toe", "jam", "filth", "puss", "earwax", "leaf", "clippings", | ||
| 55 | "bat", "guano", "mucus", "fungus", "mung", "refuse", "earwax", "spittoon", "spittle", | ||
| 56 | "phlegm" | ||
| 57 | }; | ||
| 58 | char stuffLoc[] = { 14, 0, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15, 17, 18 }; | ||
| 59 | char *noun_and_prep[] = | ||
| 60 | { "bit", "of", "piece", "of", "vat", "of", "lump", "of", "crock", "of", | ||
| 61 | "ball", "of", "tub", "of", "load", "of", "bucket", "of", "mound", "of", "glob", "of", "bag", | ||
| 62 | "of", "heap", "of", "mountain", "of", "load", "of", "barrel", "of", "sack", "of", "blob", "of", | ||
| 63 | "pile", "of", "truckload", "of", "vat", "of" | ||
| 64 | }; | ||
| 65 | char noun_and_prepLoc[] = | ||
| 66 | { 21, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, | ||
| 67 | 38, 40, 42 }; | ||
| 68 | char *organics[] = | ||
| 69 | { "droppings", "mung", "zits", "puckies", "tumors", "cysts", "tumors", | ||
| 70 | "livers", "froth", "parts", "scabs", "guts", "entrails", "blubber", "carcuses", "gizards", | ||
| 71 | "9" | ||
| 72 | }; | ||
| 73 | char organicsLoc[] = | ||
| 74 | { 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; | ||
| 75 | char *body_parts[] = | ||
| 76 | { "kidneys", "genitals", "buttocks", "earlobes", "innards", "feet" | ||
| 77 | }; | ||
| 78 | char body_partsLoc[] = { 6, 0, 1, 2, 3, 4, 5, 6 }; | ||
| 79 | char *noun[] = | ||
| 80 | { "pop", "tart", "warthog", "twinkie", "barnacle", "fondue", "pot", | ||
| 81 | "cretin", "fuckwad", "moron", "ass", "neanderthal", "nincompoop", "simpleton", "11" | ||
| 82 | }; | ||
| 83 | char nounLoc[] = { 13, 0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; | ||
| 84 | char *animal[] = | ||
| 85 | { "donkey", "llama", "dingo", "lizard", "gekko", "lemur", "moose", "camel", | ||
| 86 | "goat", "eel" | ||
| 87 | }; | ||
| 88 | char animalLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | ||
| 89 | char *good_verb[] = | ||
| 90 | { "love", "cuddle", "fondle", "adore", "smooch", "hug", "caress", "worship", | ||
| 91 | "look", "at", "touch" | ||
| 92 | }; | ||
| 93 | char good_verbLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11 }; | ||
| 94 | char *curse[] = | ||
| 95 | { "14", "20", "23", "14", "17", "20", "23", "14", "find", "your", "9", | ||
| 96 | "suddenly", "delectable", "14", "and", "14", "seek", "a", "battleground", "23" | ||
| 97 | }; | ||
| 98 | char curseLoc[] = { 4, 0, 3, 7, 13, 20 }; | ||
| 99 | char *afflictors[] = | ||
| 100 | { "15", "21", "15", "21", "15", "21", "15", "21", "a", "22", "Rush", | ||
| 101 | "Limbaugh", "the", "hosts", "of", "Hades" | ||
| 102 | }; | ||
| 103 | char afflictorsLoc[] = { 6, 0, 2, 4, 6, 8, 12, 16 }; | ||
| 104 | char *quantity[] = | ||
| 105 | { "a", "4", "hoard", "of", "a", "4", "pack", "of", "a", "truckload", "of", | ||
| 106 | "a", "swarm", "of", "many", "an", "army", "of", "a", "4", "heard", "of", "a", "4", | ||
| 107 | "platoon", "of", "a", "4", "and", "4", "group", "of", "16" | ||
| 108 | }; | ||
| 109 | char quantityLoc[] = { 10, 0, 4, 8, 11, 14, 15, 18, 22, 26, 32, 33 }; | ||
| 110 | char *numbers[] = | ||
| 111 | { "a", "thousand", "three", "million", "ninty-nine", "nine-hundred,", | ||
| 112 | "ninty-nine", "forty-two", "a", "gazillion", "sixty-eight", "times", "thirty-three" | ||
| 113 | }; | ||
| 114 | char numbersLoc[] = { 7, 0, 2, 4, 5, 7, 8, 10, 13 }; | ||
| 115 | char *adv[] = | ||
| 116 | { "viciously", "manicly", "merrily", "happily", ",", "with", "the", "19", | ||
| 117 | "of", "18", ",", "gleefully", ",", "with", "much", "ritualistic", "celebration", ",", | ||
| 118 | "franticly" | ||
| 119 | }; | ||
| 120 | char advLoc[] = { 8, 0, 1, 2, 3, 4, 11, 12, 18, 19 }; | ||
| 121 | char *metaphor[] = | ||
| 122 | { "an", "irate", "manticore", "Thor's", "belch", "Alah's", "fist", "16", | ||
| 123 | "titans", "a", "particularly", "vicious", "she-bear", "in", "the", "midst", "of", "her", | ||
| 124 | "menstrual", "cycle", "a", "pissed-off", "Jabberwock" | ||
| 125 | }; | ||
| 126 | char metaphorLoc[] = { 6, 0, 3, 5, 7, 9, 20, 23 }; | ||
| 127 | char *force[] = { "force", "fury", "power", "rage" }; | ||
| 128 | char forceLoc[] = { 4, 0, 1, 2, 3, 4 }; | ||
| 129 | char *bad_action[] = | ||
| 130 | { "spit", "shimmy", "slobber", "find", "refuge", "find", "shelter", "dance", | ||
| 131 | "retch", "vomit", "defecate", "erect", "a", "strip", "mall", "build", "a", "26", "have", "a", | ||
| 132 | "religious", "experience", "discharge", "bodily", "waste", "fart", "dance", "drool", | ||
| 133 | "lambada", "spill", "16", "rusty", "tacks", "bite", "you", "sneeze", "sing", "16", | ||
| 134 | "campfire", "songs", "smite", "you", "16", "times", "construct", "a", "new", "home", "throw", | ||
| 135 | "a", "party", "procreate" | ||
| 136 | }; | ||
| 137 | char bad_actionLoc[] = | ||
| 138 | { 25, 0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 15, 18, 22, 25, 26, 27, 28, 29, 33, | ||
| 139 | 35, 36, 40, 44, 48, 51, 52 }; | ||
| 140 | char *beasties[] = | ||
| 141 | { "yaks", "22", "maggots", "22", "cockroaches", "stinging", "scorpions", | ||
| 142 | "fleas", "22", "weasels", "22", "gnats", "South", "American", "killer", "bees", "spiders", | ||
| 143 | "4", "monkeys", "22", "wiener-dogs", "22", "rats", "22", "wolverines", "4", ",", "22", | ||
| 144 | "pit-fiends" | ||
| 145 | }; | ||
| 146 | char beastiesLoc[] = | ||
| 147 | { 14, 0, 1, 3, 5, 7, 8, 10, 12, 16, 17, 19, 21, 23, 25, 29 }; | ||
| 148 | char *condition[] = | ||
| 149 | { "frothing", "manic", "crazed", "plague-ridden", "disease-carrying", | ||
| 150 | "biting", "rabid", "blood-thirsty", "ravaging", "slavering" | ||
| 151 | }; | ||
| 152 | char conditionLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | ||
| 153 | char *place[] = | ||
| 154 | { "in", "24", "25", "upon", "your", "mother's", "grave", "on", "24", "best", | ||
| 155 | "rug", "in", "the", "26", "you", "call", "home", "upon", "your", "heinie" | ||
| 156 | }; | ||
| 157 | char placeLoc[] = { 5, 0, 3, 7, 11, 17, 20 }; | ||
| 158 | char *relation[] = | ||
| 159 | { "your", "your", "your", "your", "father's", "your", "mother's", "your", | ||
| 160 | "grandma's" | ||
| 161 | }; | ||
| 162 | char relationLoc[] = { 6, 0, 1, 2, 3, 5, 7, 9 }; | ||
| 163 | char *in_something[] = | ||
| 164 | { "entrails", "anal", "cavity", "shoes", "house", "pantry", "general", | ||
| 165 | "direction", "pants", "bed" | ||
| 166 | }; | ||
| 167 | char in_somethingLoc[] = { 8, 0, 1, 3, 4, 5, 6, 8, 9, 10 }; | ||
| 168 | char *bad_place[] = | ||
| 169 | { "rat", "hole", "sewer", "toxic", "dump", "oil", "refinery", "landfill", | ||
| 170 | "porto-pottie" | ||
| 171 | }; | ||
| 172 | char bad_placeLoc[] = { 6, 0, 2, 3, 5, 7, 8, 9 }; | ||
| 173 | char **daGrammar[27]; | ||
| 174 | char *daGLoc[27]; | ||
| 175 | |||
| 176 | static void | ||
| 177 | init_grammar (void) | ||
| 178 | { | ||
| 179 | daGrammar[0] = start; | ||
| 180 | daGLoc[0] = startLoc; | ||
| 181 | daGrammar[1] = adj; | ||
| 182 | daGLoc[1] = adjLoc; | ||
| 183 | daGrammar[2] = adj3; | ||
| 184 | daGLoc[2] = adj3Loc; | ||
| 185 | daGrammar[3] = adj1; | ||
| 186 | daGLoc[3] = adj1Loc; | ||
| 187 | daGrammar[4] = adj2; | ||
| 188 | daGLoc[4] = adj2Loc; | ||
| 189 | daGrammar[5] = name; | ||
| 190 | daGLoc[5] = nameLoc; | ||
| 191 | daGrammar[6] = stuff; | ||
| 192 | daGLoc[6] = stuffLoc; | ||
| 193 | daGrammar[7] = noun_and_prep; | ||
| 194 | daGLoc[7] = noun_and_prepLoc; | ||
| 195 | daGrammar[8] = organics; | ||
| 196 | daGLoc[8] = organicsLoc; | ||
| 197 | daGrammar[9] = body_parts; | ||
| 198 | daGLoc[9] = body_partsLoc; | ||
| 199 | daGrammar[10] = noun; | ||
| 200 | daGLoc[10] = nounLoc; | ||
| 201 | daGrammar[11] = animal; | ||
| 202 | daGLoc[11] = animalLoc; | ||
| 203 | daGrammar[12] = good_verb; | ||
| 204 | daGLoc[12] = good_verbLoc; | ||
| 205 | daGrammar[13] = curse; | ||
| 206 | daGLoc[13] = curseLoc; | ||
| 207 | daGrammar[14] = afflictors; | ||
| 208 | daGLoc[14] = afflictorsLoc; | ||
| 209 | daGrammar[15] = quantity; | ||
| 210 | daGLoc[15] = quantityLoc; | ||
| 211 | daGrammar[16] = numbers; | ||
| 212 | daGLoc[16] = numbersLoc; | ||
| 213 | daGrammar[17] = adv; | ||
| 214 | daGLoc[17] = advLoc; | ||
| 215 | daGrammar[18] = metaphor; | ||
| 216 | daGLoc[18] = metaphorLoc; | ||
| 217 | daGrammar[19] = force; | ||
| 218 | daGLoc[19] = forceLoc; | ||
| 219 | daGrammar[20] = bad_action; | ||
| 220 | daGLoc[20] = bad_actionLoc; | ||
| 221 | daGrammar[21] = beasties; | ||
| 222 | daGLoc[21] = beastiesLoc; | ||
| 223 | daGrammar[22] = condition; | ||
| 224 | daGLoc[22] = conditionLoc; | ||
| 225 | daGrammar[23] = place; | ||
| 226 | daGLoc[23] = placeLoc; | ||
| 227 | daGrammar[24] = relation; | ||
| 228 | daGLoc[24] = relationLoc; | ||
| 229 | daGrammar[25] = in_something; | ||
| 230 | daGLoc[25] = in_somethingLoc; | ||
| 231 | daGrammar[26] = bad_place; | ||
| 232 | daGLoc[26] = bad_placeLoc; | ||
| 233 | } | ||
| 234 | |||
| 235 | #include <ctype.h> | ||
| 236 | #include <debug.h> | ||
| 237 | #include <random.h> | ||
| 238 | #include <stdio.h> | ||
| 239 | #include <stdlib.h> | ||
| 240 | #include <string.h> | ||
| 241 | #include <syscall.h> | ||
| 242 | |||
| 243 | void expand (int num, char **grammar[], char *location[], int handle); | ||
| 244 | |||
| 245 | static void | ||
| 246 | usage (int ret_code, const char *message, ...) PRINTF_FORMAT (2, 3); | ||
| 247 | |||
| 248 | static void | ||
| 249 | usage (int ret_code, const char *message, ...) | ||
| 250 | { | ||
| 251 | va_list args; | ||
| 252 | |||
| 253 | if (message != NULL) | ||
| 254 | { | ||
| 255 | va_start (args, message); | ||
| 256 | vprintf (message, args); | ||
| 257 | va_end (args); | ||
| 258 | } | ||
| 259 | |||
| 260 | printf ("\n" | ||
| 261 | "Usage: insult [OPTION]...\n" | ||
| 262 | "Prints random insults to screen.\n\n" | ||
| 263 | " -h: this help message\n" | ||
| 264 | " -s <integer>: set the random seed (default 4951)\n" | ||
| 265 | " -n <integer>: choose number of insults (default 4)\n" | ||
| 266 | " -f <file>: redirect output to <file>\n"); | ||
| 267 | |||
| 268 | exit (ret_code); | ||
| 269 | } | ||
| 270 | |||
| 271 | int | ||
| 272 | main (int argc, char *argv[]) | ||
| 273 | { | ||
| 274 | int sentence_cnt, new_seed, i, file_flag, sent_flag, seed_flag; | ||
| 275 | int handle; | ||
| 276 | |||
| 277 | new_seed = 4951; | ||
| 278 | sentence_cnt = 4; | ||
| 279 | file_flag = 0; | ||
| 280 | seed_flag = 0; | ||
| 281 | sent_flag = 0; | ||
| 282 | handle = STDOUT_FILENO; | ||
| 283 | |||
| 284 | for (i = 1; i < argc; i++) | ||
| 285 | { | ||
| 286 | if (strcmp (argv[1], "-h") == 0) | ||
| 287 | usage (0, NULL); | ||
| 288 | else if (strcmp (argv[i], "-s") == 0) | ||
| 289 | { | ||
| 290 | if (seed_flag++) | ||
| 291 | usage (-1, "Can't have more than one seed"); | ||
| 292 | if (++i >= argc) | ||
| 293 | usage (-1, "Missing value for -s"); | ||
| 294 | new_seed = atoi (argv[i]); | ||
| 295 | } | ||
| 296 | else if (strcmp (argv[i], "-n") == 0) | ||
| 297 | { | ||
| 298 | if (sent_flag++) | ||
| 299 | usage (-1, "Can't have more than one sentence option"); | ||
| 300 | if (++i >= argc) | ||
| 301 | usage (-1, "Missing value for -n"); | ||
| 302 | sentence_cnt = atoi (argv[i]); | ||
| 303 | if (sentence_cnt < 1) | ||
| 304 | usage (-1, "Must have at least one sentence"); | ||
| 305 | } | ||
| 306 | else if (strcmp (argv[i], "-f") == 0) | ||
| 307 | { | ||
| 308 | if (file_flag++) | ||
| 309 | usage (-1, "Can't have more than one output file"); | ||
| 310 | if (++i >= argc) | ||
| 311 | usage (-1, "Missing value for -f"); | ||
| 312 | |||
| 313 | /* Because files have fixed length in the basic Pintos | ||
| 314 | file system, the 0 argument means that this option | ||
| 315 | will not be useful until project 4 is | ||
| 316 | implemented. */ | ||
| 317 | create (argv[i], 0); | ||
| 318 | handle = open (argv[i]); | ||
| 319 | if (handle < 0) | ||
| 320 | { | ||
| 321 | printf ("%s: open failed\n", argv[i]); | ||
| 322 | return EXIT_FAILURE; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | else | ||
| 326 | usage (-1, "Unrecognized flag"); | ||
| 327 | } | ||
| 328 | |||
| 329 | init_grammar (); | ||
| 330 | |||
| 331 | random_init (new_seed); | ||
| 332 | hprintf (handle, "\n"); | ||
| 333 | |||
| 334 | for (i = 0; i < sentence_cnt; i++) | ||
| 335 | { | ||
| 336 | hprintf (handle, "\n"); | ||
| 337 | expand (0, daGrammar, daGLoc, handle); | ||
| 338 | hprintf (handle, "\n\n"); | ||
| 339 | } | ||
| 340 | |||
| 341 | if (file_flag) | ||
| 342 | close (handle); | ||
| 343 | |||
| 344 | return EXIT_SUCCESS; | ||
| 345 | } | ||
| 346 | |||
| 347 | void | ||
| 348 | expand (int num, char **grammar[], char *location[], int handle) | ||
| 349 | { | ||
| 350 | char *word; | ||
| 351 | int i, which, listStart, listEnd; | ||
| 352 | |||
| 353 | which = random_ulong () % location[num][0] + 1; | ||
| 354 | listStart = location[num][which]; | ||
| 355 | listEnd = location[num][which + 1]; | ||
| 356 | for (i = listStart; i < listEnd; i++) | ||
| 357 | { | ||
| 358 | word = grammar[num][i]; | ||
| 359 | if (!isdigit (*word)) | ||
| 360 | { | ||
| 361 | if (!ispunct (*word)) | ||
| 362 | hprintf (handle, " "); | ||
| 363 | hprintf (handle, "%s", word); | ||
| 364 | } | ||
| 365 | else | ||
| 366 | expand (atoi (word), grammar, location, handle); | ||
| 367 | } | ||
| 368 | |||
| 369 | } | ||
diff --git a/pintos-progos/examples/lib/.gitignore b/pintos-progos/examples/lib/.gitignore new file mode 100644 index 0000000..a438335 --- /dev/null +++ b/pintos-progos/examples/lib/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| *.d | |||
diff --git a/pintos-progos/examples/lib/user/.dummy b/pintos-progos/examples/lib/user/.dummy new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pintos-progos/examples/lib/user/.dummy | |||
diff --git a/pintos-progos/examples/lib/user/.gitignore b/pintos-progos/examples/lib/user/.gitignore new file mode 100644 index 0000000..a438335 --- /dev/null +++ b/pintos-progos/examples/lib/user/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| *.d | |||
diff --git a/pintos-progos/examples/lineup.c b/pintos-progos/examples/lineup.c new file mode 100644 index 0000000..60402d0 --- /dev/null +++ b/pintos-progos/examples/lineup.c | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | /* lineup.c | ||
| 2 | |||
| 3 | Converts a file to uppercase in-place. | ||
| 4 | |||
| 5 | Incidentally, another way to do this while avoiding the seeks | ||
| 6 | would be to open the input file, then remove() it and reopen | ||
| 7 | it under another handle. Because of Unix deletion semantics | ||
| 8 | this works fine. */ | ||
| 9 | |||
| 10 | #include <ctype.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <syscall.h> | ||
| 13 | |||
| 14 | int | ||
| 15 | main (int argc, char *argv[]) | ||
| 16 | { | ||
| 17 | char buf[1024]; | ||
| 18 | int handle; | ||
| 19 | |||
| 20 | if (argc != 2) | ||
| 21 | exit (1); | ||
| 22 | |||
| 23 | handle = open (argv[1]); | ||
| 24 | if (handle < 0) | ||
| 25 | exit (2); | ||
| 26 | |||
| 27 | for (;;) | ||
| 28 | { | ||
| 29 | int n, i; | ||
| 30 | |||
| 31 | n = read (handle, buf, sizeof buf); | ||
| 32 | if (n <= 0) | ||
| 33 | break; | ||
| 34 | |||
| 35 | for (i = 0; i < n; i++) | ||
| 36 | buf[i] = toupper ((unsigned char) buf[i]); | ||
| 37 | |||
| 38 | seek (handle, tell (handle) - n); | ||
| 39 | if (write (handle, buf, n) != n) | ||
| 40 | printf ("write failed\n"); | ||
| 41 | } | ||
| 42 | |||
| 43 | close (handle); | ||
| 44 | |||
| 45 | return EXIT_SUCCESS; | ||
| 46 | } | ||
diff --git a/pintos-progos/examples/ls.c b/pintos-progos/examples/ls.c new file mode 100644 index 0000000..fbe27a1 --- /dev/null +++ b/pintos-progos/examples/ls.c | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | /* ls.c | ||
| 2 | |||
| 3 | Lists the contents of the directory or directories named on | ||
| 4 | the command line, or of the current directory if none are | ||
| 5 | named. | ||
| 6 | |||
| 7 | By default, only the name of each file is printed. If "-l" is | ||
| 8 | given as the first argument, the type, size, and inumber of | ||
| 9 | each file is also printed. This won't work until project 4. */ | ||
| 10 | |||
| 11 | #include <syscall.h> | ||
| 12 | #include <stdio.h> | ||
| 13 | #include <string.h> | ||
| 14 | |||
| 15 | static bool | ||
| 16 | list_dir (const char *dir, bool verbose) | ||
| 17 | { | ||
| 18 | int dir_fd = open (dir); | ||
| 19 | if (dir_fd == -1) | ||
| 20 | { | ||
| 21 | printf ("%s: not found\n", dir); | ||
| 22 | return false; | ||
| 23 | } | ||
| 24 | |||
| 25 | if (isdir (dir_fd)) | ||
| 26 | { | ||
| 27 | char name[READDIR_MAX_LEN]; | ||
| 28 | |||
| 29 | printf ("%s", dir); | ||
| 30 | if (verbose) | ||
| 31 | printf (" (inumber %d)", inumber (dir_fd)); | ||
| 32 | printf (":\n"); | ||
| 33 | |||
| 34 | while (readdir (dir_fd, name)) | ||
| 35 | { | ||
| 36 | printf ("%s", name); | ||
| 37 | if (verbose) | ||
| 38 | { | ||
| 39 | char full_name[128]; | ||
| 40 | int entry_fd; | ||
| 41 | |||
| 42 | snprintf (full_name, sizeof full_name, "%s/%s", dir, name); | ||
| 43 | entry_fd = open (full_name); | ||
| 44 | |||
| 45 | printf (": "); | ||
| 46 | if (entry_fd != -1) | ||
| 47 | { | ||
| 48 | if (isdir (entry_fd)) | ||
| 49 | printf ("directory"); | ||
| 50 | else | ||
| 51 | printf ("%d-byte file", filesize (entry_fd)); | ||
| 52 | printf (", inumber %d", inumber (entry_fd)); | ||
| 53 | } | ||
| 54 | else | ||
| 55 | printf ("open failed"); | ||
| 56 | close (entry_fd); | ||
| 57 | } | ||
| 58 | printf ("\n"); | ||
| 59 | } | ||
| 60 | } | ||
| 61 | else | ||
| 62 | printf ("%s: not a directory\n", dir); | ||
| 63 | close (dir_fd); | ||
| 64 | return true; | ||
| 65 | } | ||
| 66 | |||
| 67 | int | ||
| 68 | main (int argc, char *argv[]) | ||
| 69 | { | ||
| 70 | bool success = true; | ||
| 71 | bool verbose = false; | ||
| 72 | |||
| 73 | if (argc > 1 && !strcmp (argv[1], "-l")) | ||
| 74 | { | ||
| 75 | verbose = true; | ||
| 76 | argv++; | ||
| 77 | argc--; | ||
| 78 | } | ||
| 79 | |||
| 80 | if (argc <= 1) | ||
| 81 | success = list_dir (".", verbose); | ||
| 82 | else | ||
| 83 | { | ||
| 84 | int i; | ||
| 85 | for (i = 1; i < argc; i++) | ||
| 86 | if (!list_dir (argv[i], verbose)) | ||
| 87 | success = false; | ||
| 88 | } | ||
| 89 | return success ? EXIT_SUCCESS : EXIT_FAILURE; | ||
| 90 | } | ||
diff --git a/pintos-progos/examples/matmult.c b/pintos-progos/examples/matmult.c new file mode 100644 index 0000000..4f0615f --- /dev/null +++ b/pintos-progos/examples/matmult.c | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* matmult.c | ||
| 2 | |||
| 3 | Test program to do matrix multiplication on large arrays. | ||
| 4 | |||
| 5 | Intended to stress virtual memory system. | ||
| 6 | |||
| 7 | Ideally, we could read the matrices off of the file system, | ||
| 8 | and store the result back to the file system! | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <stdio.h> | ||
| 12 | #include <syscall.h> | ||
| 13 | |||
| 14 | /* You should define DIM to be large enough that the arrays | ||
| 15 | don't fit in physical memory. | ||
| 16 | |||
| 17 | Dim Memory | ||
| 18 | ------ -------- | ||
| 19 | 16 3 kB | ||
| 20 | 64 48 kB | ||
| 21 | 128 192 kB | ||
| 22 | 256 768 kB | ||
| 23 | 512 3,072 kB | ||
| 24 | 1,024 12,288 kB | ||
| 25 | 2,048 49,152 kB | ||
| 26 | 4,096 196,608 kB | ||
| 27 | 8,192 786,432 kB | ||
| 28 | 16,384 3,145,728 kB */ | ||
| 29 | #define DIM 128 | ||
| 30 | |||
| 31 | int A[DIM][DIM]; | ||
| 32 | int B[DIM][DIM]; | ||
| 33 | int C[DIM][DIM]; | ||
| 34 | |||
| 35 | int | ||
| 36 | main (void) | ||
| 37 | { | ||
| 38 | int i, j, k; | ||
| 39 | |||
| 40 | /* Initialize the matrices. */ | ||
| 41 | for (i = 0; i < DIM; i++) | ||
| 42 | for (j = 0; j < DIM; j++) | ||
| 43 | { | ||
| 44 | A[i][j] = i; | ||
| 45 | B[i][j] = j; | ||
| 46 | C[i][j] = 0; | ||
| 47 | } | ||
| 48 | |||
| 49 | /* Multiply matrices. */ | ||
| 50 | for (i = 0; i < DIM; i++) | ||
| 51 | for (j = 0; j < DIM; j++) | ||
| 52 | for (k = 0; k < DIM; k++) | ||
| 53 | C[i][j] += A[i][k] * B[k][j]; | ||
| 54 | |||
| 55 | /* Done. */ | ||
| 56 | exit (C[DIM - 1][DIM - 1]); | ||
| 57 | } | ||
diff --git a/pintos-progos/examples/mcat.c b/pintos-progos/examples/mcat.c new file mode 100644 index 0000000..7b39760 --- /dev/null +++ b/pintos-progos/examples/mcat.c | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* mcat.c | ||
| 2 | |||
| 3 | Prints files specified on command line to the console, using | ||
| 4 | mmap. */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | |||
| 9 | int | ||
| 10 | main (int argc, char *argv[]) | ||
| 11 | { | ||
| 12 | int i; | ||
| 13 | |||
| 14 | for (i = 1; i < argc; i++) | ||
| 15 | { | ||
| 16 | int fd; | ||
| 17 | mapid_t map; | ||
| 18 | void *data = (void *) 0x10000000; | ||
| 19 | int size; | ||
| 20 | |||
| 21 | /* Open input file. */ | ||
| 22 | fd = open (argv[i]); | ||
| 23 | if (fd < 0) | ||
| 24 | { | ||
| 25 | printf ("%s: open failed\n", argv[i]); | ||
| 26 | return EXIT_FAILURE; | ||
| 27 | } | ||
| 28 | size = filesize (fd); | ||
| 29 | |||
| 30 | /* Map files. */ | ||
| 31 | map = mmap (fd, data); | ||
| 32 | if (map == MAP_FAILED) | ||
| 33 | { | ||
| 34 | printf ("%s: mmap failed\n", argv[i]); | ||
| 35 | return EXIT_FAILURE; | ||
| 36 | } | ||
| 37 | |||
| 38 | /* Write file to console. */ | ||
| 39 | write (STDOUT_FILENO, data, size); | ||
| 40 | |||
| 41 | /* Unmap files (optional). */ | ||
| 42 | munmap (map); | ||
| 43 | } | ||
| 44 | return EXIT_SUCCESS; | ||
| 45 | } | ||
diff --git a/pintos-progos/examples/mcp.c b/pintos-progos/examples/mcp.c new file mode 100644 index 0000000..6091dc8 --- /dev/null +++ b/pintos-progos/examples/mcp.c | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | /* mcp.c | ||
| 2 | |||
| 3 | Copies one file to another, using mmap. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <string.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | |||
| 9 | int | ||
| 10 | main (int argc, char *argv[]) | ||
| 11 | { | ||
| 12 | int in_fd, out_fd; | ||
| 13 | mapid_t in_map, out_map; | ||
| 14 | void *in_data = (void *) 0x10000000; | ||
| 15 | void *out_data = (void *) 0x20000000; | ||
| 16 | int size; | ||
| 17 | |||
| 18 | if (argc != 3) | ||
| 19 | { | ||
| 20 | printf ("usage: cp OLD NEW\n"); | ||
| 21 | return EXIT_FAILURE; | ||
| 22 | } | ||
| 23 | |||
| 24 | /* Open input file. */ | ||
| 25 | in_fd = open (argv[1]); | ||
| 26 | if (in_fd < 0) | ||
| 27 | { | ||
| 28 | printf ("%s: open failed\n", argv[1]); | ||
| 29 | return EXIT_FAILURE; | ||
| 30 | } | ||
| 31 | size = filesize (in_fd); | ||
| 32 | |||
| 33 | /* Create and open output file. */ | ||
| 34 | if (!create (argv[2], size)) | ||
| 35 | { | ||
| 36 | printf ("%s: create failed\n", argv[2]); | ||
| 37 | return EXIT_FAILURE; | ||
| 38 | } | ||
| 39 | out_fd = open (argv[2]); | ||
| 40 | if (out_fd < 0) | ||
| 41 | { | ||
| 42 | printf ("%s: open failed\n", argv[2]); | ||
| 43 | return EXIT_FAILURE; | ||
| 44 | } | ||
| 45 | |||
| 46 | /* Map files. */ | ||
| 47 | in_map = mmap (in_fd, in_data); | ||
| 48 | if (in_map == MAP_FAILED) | ||
| 49 | { | ||
| 50 | printf ("%s: mmap failed\n", argv[1]); | ||
| 51 | return EXIT_FAILURE; | ||
| 52 | } | ||
| 53 | out_map = mmap (out_fd, out_data); | ||
| 54 | if (out_map == MAP_FAILED) | ||
| 55 | { | ||
| 56 | printf ("%s: mmap failed\n", argv[2]); | ||
| 57 | return EXIT_FAILURE; | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Copy files. */ | ||
| 61 | memcpy (out_data, in_data, size); | ||
| 62 | |||
| 63 | /* Unmap files (optional). */ | ||
| 64 | munmap (in_map); | ||
| 65 | munmap (out_map); | ||
| 66 | |||
| 67 | return EXIT_SUCCESS; | ||
| 68 | } | ||
diff --git a/pintos-progos/examples/mkdir.c b/pintos-progos/examples/mkdir.c new file mode 100644 index 0000000..7ddbc3f --- /dev/null +++ b/pintos-progos/examples/mkdir.c | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* mkdir.c | ||
| 2 | |||
| 3 | Creates a directory. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | if (argc != 2) | ||
| 12 | { | ||
| 13 | printf ("usage: %s DIRECTORY\n", argv[0]); | ||
| 14 | return EXIT_FAILURE; | ||
| 15 | } | ||
| 16 | |||
| 17 | if (!mkdir (argv[1])) | ||
| 18 | { | ||
| 19 | printf ("%s: mkdir failed\n", argv[1]); | ||
| 20 | return EXIT_FAILURE; | ||
| 21 | } | ||
| 22 | |||
| 23 | return EXIT_SUCCESS; | ||
| 24 | } | ||
diff --git a/pintos-progos/examples/pwd.c b/pintos-progos/examples/pwd.c new file mode 100644 index 0000000..d2305cf --- /dev/null +++ b/pintos-progos/examples/pwd.c | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | /* pwd.c | ||
| 2 | |||
| 3 | Prints the absolute name of the present working directory. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include <stdbool.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <string.h> | ||
| 9 | |||
| 10 | static bool getcwd (char *cwd, size_t cwd_size); | ||
| 11 | |||
| 12 | int | ||
| 13 | main (void) | ||
| 14 | { | ||
| 15 | char cwd[128]; | ||
| 16 | if (getcwd (cwd, sizeof cwd)) | ||
| 17 | { | ||
| 18 | printf ("%s\n", cwd); | ||
| 19 | return EXIT_SUCCESS; | ||
| 20 | } | ||
| 21 | else | ||
| 22 | { | ||
| 23 | printf ("error\n"); | ||
| 24 | return EXIT_FAILURE; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | /* Stores the inode number for FILE_NAME in *INUM. | ||
| 29 | Returns true if successful, false if the file could not be | ||
| 30 | opened. */ | ||
| 31 | static bool | ||
| 32 | get_inumber (const char *file_name, int *inum) | ||
| 33 | { | ||
| 34 | int fd = open (file_name); | ||
| 35 | if (fd >= 0) | ||
| 36 | { | ||
| 37 | *inum = inumber (fd); | ||
| 38 | close (fd); | ||
| 39 | return true; | ||
| 40 | } | ||
| 41 | else | ||
| 42 | return false; | ||
| 43 | } | ||
| 44 | |||
| 45 | /* Prepends PREFIX to the characters stored in the final *DST_LEN | ||
| 46 | bytes of the DST_SIZE-byte buffer that starts at DST. | ||
| 47 | Returns true if successful, false if adding that many | ||
| 48 | characters, plus a null terminator, would overflow the buffer. | ||
| 49 | (No null terminator is actually added or depended upon, but | ||
| 50 | its space is accounted for.) */ | ||
| 51 | static bool | ||
| 52 | prepend (const char *prefix, | ||
| 53 | char *dst, size_t *dst_len, size_t dst_size) | ||
| 54 | { | ||
| 55 | size_t prefix_len = strlen (prefix); | ||
| 56 | if (prefix_len + *dst_len + 1 <= dst_size) | ||
| 57 | { | ||
| 58 | *dst_len += prefix_len; | ||
| 59 | memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len); | ||
| 60 | return true; | ||
| 61 | } | ||
| 62 | else | ||
| 63 | return false; | ||
| 64 | } | ||
| 65 | |||
| 66 | /* Stores the current working directory, as a null-terminated | ||
| 67 | string, in the CWD_SIZE bytes in CWD. | ||
| 68 | Returns true if successful, false on error. Errors include | ||
| 69 | system errors, directory trees deeper than MAX_LEVEL levels, | ||
| 70 | and insufficient space in CWD. */ | ||
| 71 | static bool | ||
| 72 | getcwd (char *cwd, size_t cwd_size) | ||
| 73 | { | ||
| 74 | size_t cwd_len = 0; | ||
| 75 | |||
| 76 | #define MAX_LEVEL 20 | ||
| 77 | char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1]; | ||
| 78 | char *namep; | ||
| 79 | |||
| 80 | int child_inum; | ||
| 81 | |||
| 82 | /* Make sure there's enough space for at least "/". */ | ||
| 83 | if (cwd_size < 2) | ||
| 84 | return false; | ||
| 85 | |||
| 86 | /* Get inumber for current directory. */ | ||
| 87 | if (!get_inumber (".", &child_inum)) | ||
| 88 | return false; | ||
| 89 | |||
| 90 | namep = name; | ||
| 91 | for (;;) | ||
| 92 | { | ||
| 93 | int parent_inum, parent_fd; | ||
| 94 | |||
| 95 | /* Compose "../../../..", etc., in NAME. */ | ||
| 96 | if ((namep - name) > MAX_LEVEL * 3) | ||
| 97 | return false; | ||
| 98 | *namep++ = '.'; | ||
| 99 | *namep++ = '.'; | ||
| 100 | *namep = '\0'; | ||
| 101 | |||
| 102 | /* Open directory. */ | ||
| 103 | parent_fd = open (name); | ||
| 104 | if (parent_fd < 0) | ||
| 105 | return false; | ||
| 106 | *namep++ = '/'; | ||
| 107 | |||
| 108 | /* If parent and child have the same inumber, | ||
| 109 | then we've arrived at the root. */ | ||
| 110 | parent_inum = inumber (parent_fd); | ||
| 111 | if (parent_inum == child_inum) | ||
| 112 | break; | ||
| 113 | |||
| 114 | /* Find name of file in parent directory with the child's | ||
| 115 | inumber. */ | ||
| 116 | for (;;) | ||
| 117 | { | ||
| 118 | int test_inum; | ||
| 119 | if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum)) | ||
| 120 | { | ||
| 121 | close (parent_fd); | ||
| 122 | return false; | ||
| 123 | } | ||
| 124 | if (test_inum == child_inum) | ||
| 125 | break; | ||
| 126 | } | ||
| 127 | close (parent_fd); | ||
| 128 | |||
| 129 | /* Prepend "/name" to CWD. */ | ||
| 130 | if (!prepend (namep - 1, cwd, &cwd_len, cwd_size)) | ||
| 131 | return false; | ||
| 132 | |||
| 133 | /* Move up. */ | ||
| 134 | child_inum = parent_inum; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Finalize CWD. */ | ||
| 138 | if (cwd_len > 0) | ||
| 139 | { | ||
| 140 | /* Move the string to the beginning of CWD, | ||
| 141 | and null-terminate it. */ | ||
| 142 | memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len); | ||
| 143 | cwd[cwd_len] = '\0'; | ||
| 144 | } | ||
| 145 | else | ||
| 146 | { | ||
| 147 | /* Special case for the root. */ | ||
| 148 | strlcpy (cwd, "/", cwd_size); | ||
| 149 | } | ||
| 150 | |||
| 151 | return true; | ||
| 152 | } | ||
diff --git a/pintos-progos/examples/recursor.c b/pintos-progos/examples/recursor.c new file mode 100644 index 0000000..79c784a --- /dev/null +++ b/pintos-progos/examples/recursor.c | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <stdlib.h> | ||
| 3 | #include <syscall.h> | ||
| 4 | |||
| 5 | int | ||
| 6 | main (int argc, char *argv[]) | ||
| 7 | { | ||
| 8 | char buffer[128]; | ||
| 9 | pid_t pid; | ||
| 10 | int retval = 0; | ||
| 11 | |||
| 12 | if (argc != 4) | ||
| 13 | { | ||
| 14 | printf ("usage: recursor <string> <depth> <waitp>\n"); | ||
| 15 | exit (1); | ||
| 16 | } | ||
| 17 | |||
| 18 | /* Print args. */ | ||
| 19 | printf ("%s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]); | ||
| 20 | |||
| 21 | /* Execute child and wait for it to finish if requested. */ | ||
| 22 | if (atoi (argv[2]) != 0) | ||
| 23 | { | ||
| 24 | snprintf (buffer, sizeof buffer, | ||
| 25 | "recursor %s %d %s", argv[1], atoi (argv[2]) - 1, argv[3]); | ||
| 26 | pid = exec (buffer); | ||
| 27 | if (atoi (argv[3])) | ||
| 28 | retval = wait (pid); | ||
| 29 | } | ||
| 30 | |||
| 31 | /* Done. */ | ||
| 32 | printf ("%s %s: dying, retval=%d\n", argv[1], argv[2], retval); | ||
| 33 | exit (retval); | ||
| 34 | } | ||
diff --git a/pintos-progos/examples/rm.c b/pintos-progos/examples/rm.c new file mode 100644 index 0000000..0db7f7b --- /dev/null +++ b/pintos-progos/examples/rm.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* rm.c | ||
| 2 | |||
| 3 | Removes files specified on command line. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | bool success = true; | ||
| 12 | int i; | ||
| 13 | |||
| 14 | for (i = 1; i < argc; i++) | ||
| 15 | if (!remove (argv[i])) | ||
| 16 | { | ||
| 17 | printf ("%s: remove failed\n", argv[i]); | ||
| 18 | success = false; | ||
| 19 | } | ||
| 20 | return success ? EXIT_SUCCESS : EXIT_FAILURE; | ||
| 21 | } | ||
diff --git a/pintos-progos/examples/shell.c b/pintos-progos/examples/shell.c new file mode 100644 index 0000000..93641b4 --- /dev/null +++ b/pintos-progos/examples/shell.c | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | #include <stdbool.h> | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <syscall.h> | ||
| 5 | |||
| 6 | static void read_line (char line[], size_t); | ||
| 7 | static bool backspace (char **pos, char line[]); | ||
| 8 | |||
| 9 | int | ||
| 10 | main (void) | ||
| 11 | { | ||
| 12 | printf ("Shell starting...\n"); | ||
| 13 | for (;;) | ||
| 14 | { | ||
| 15 | char command[80]; | ||
| 16 | |||
| 17 | /* Read command. */ | ||
| 18 | printf ("--"); | ||
| 19 | read_line (command, sizeof command); | ||
| 20 | |||
| 21 | /* Execute command. */ | ||
| 22 | if (!strcmp (command, "exit")) | ||
| 23 | break; | ||
| 24 | else if (!memcmp (command, "cd ", 3)) | ||
| 25 | { | ||
| 26 | if (!chdir (command + 3)) | ||
| 27 | printf ("\"%s\": chdir failed\n", command + 3); | ||
| 28 | } | ||
| 29 | else if (command[0] == '\0') | ||
| 30 | { | ||
| 31 | /* Empty command. */ | ||
| 32 | } | ||
| 33 | else | ||
| 34 | { | ||
| 35 | pid_t pid = exec (command); | ||
| 36 | if (pid != PID_ERROR) | ||
| 37 | printf ("\"%s\": exit code %d\n", command, wait (pid)); | ||
| 38 | else | ||
| 39 | printf ("exec failed\n"); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | printf ("Shell exiting."); | ||
| 44 | return EXIT_SUCCESS; | ||
| 45 | } | ||
| 46 | |||
| 47 | /* Reads a line of input from the user into LINE, which has room | ||
| 48 | for SIZE bytes. Handles backspace and Ctrl+U in the ways | ||
| 49 | expected by Unix users. On return, LINE will always be | ||
| 50 | null-terminated and will not end in a new-line character. */ | ||
| 51 | static void | ||
| 52 | read_line (char line[], size_t size) | ||
| 53 | { | ||
| 54 | char *pos = line; | ||
| 55 | for (;;) | ||
| 56 | { | ||
| 57 | char c; | ||
| 58 | read (STDIN_FILENO, &c, 1); | ||
| 59 | |||
| 60 | switch (c) | ||
| 61 | { | ||
| 62 | case '\r': | ||
| 63 | *pos = '\0'; | ||
| 64 | putchar ('\n'); | ||
| 65 | return; | ||
| 66 | |||
| 67 | case '\b': | ||
| 68 | backspace (&pos, line); | ||
| 69 | break; | ||
| 70 | |||
| 71 | case ('U' - 'A') + 1: /* Ctrl+U. */ | ||
| 72 | while (backspace (&pos, line)) | ||
| 73 | continue; | ||
| 74 | break; | ||
| 75 | |||
| 76 | default: | ||
| 77 | /* Add character to line. */ | ||
| 78 | if (pos < line + size - 1) | ||
| 79 | { | ||
| 80 | putchar (c); | ||
| 81 | *pos++ = c; | ||
| 82 | } | ||
| 83 | break; | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /* If *POS is past the beginning of LINE, backs up one character | ||
| 89 | position. Returns true if successful, false if nothing was | ||
| 90 | done. */ | ||
| 91 | static bool | ||
| 92 | backspace (char **pos, char line[]) | ||
| 93 | { | ||
| 94 | if (*pos > line) | ||
| 95 | { | ||
| 96 | /* Back up cursor, overwrite character, back up | ||
| 97 | again. */ | ||
| 98 | printf ("\b \b"); | ||
| 99 | (*pos)--; | ||
| 100 | return true; | ||
| 101 | } | ||
| 102 | else | ||
| 103 | return false; | ||
| 104 | } | ||
diff --git a/pintos-progos/examples/test.c b/pintos-progos/examples/test.c new file mode 100644 index 0000000..44dc307 --- /dev/null +++ b/pintos-progos/examples/test.c | |||
| @@ -0,0 +1,101 @@ | |||
| 1 | /* test.c | ||
| 2 | |||
| 3 | Experiments with syscalls | ||
| 4 | argc < 2 Print Hello World | ||
| 5 | argv[1][0] == 'p' print argv[2] | ||
| 6 | == 'e' Exec Test | ||
| 7 | == 'f' File test | ||
| 8 | == 'F' File descriptor stress test | ||
| 9 | == 'h' Halt | ||
| 10 | == '0' Null-Pointer Access | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <stdio.h> | ||
| 14 | #include <syscall.h> | ||
| 15 | |||
| 16 | #define LARGE_BUF_SIZE 4150 | ||
| 17 | char large_buf[LARGE_BUF_SIZE]; | ||
| 18 | |||
| 19 | #define NUM_EXEC_CHILDS 7 | ||
| 20 | char *execs[NUM_EXEC_CHILDS] = { "test", "test p FOO", "test p BAR", "test f", "test 0", &large_buf[0], "test^" }; | ||
| 21 | |||
| 22 | #define MAX_FD 4097 | ||
| 23 | |||
| 24 | static void init_args(void); | ||
| 25 | static void init_args() | ||
| 26 | { | ||
| 27 | int i = 0; | ||
| 28 | char *t = ""; | ||
| 29 | while(i < LARGE_BUF_SIZE-1) { | ||
| 30 | if(!*t) t = "test "; | ||
| 31 | large_buf[i++] = *t++; | ||
| 32 | } | ||
| 33 | large_buf[LARGE_BUF_SIZE-1]='\0'; | ||
| 34 | } | ||
| 35 | |||
| 36 | int | ||
| 37 | main (int argc, char** argv) | ||
| 38 | { | ||
| 39 | if(argc < 2) { | ||
| 40 | printf("Hello World!\n"); | ||
| 41 | exit(0); | ||
| 42 | } | ||
| 43 | init_args(); | ||
| 44 | if(argv[1][0] == 'e') { | ||
| 45 | int r = 0; | ||
| 46 | int i; | ||
| 47 | int tid[NUM_EXEC_CHILDS]; | ||
| 48 | |||
| 49 | for(i = 0; i < NUM_EXEC_CHILDS; i++) { | ||
| 50 | tid[i] = exec(execs[i]); | ||
| 51 | } | ||
| 52 | for(i = 0; i < NUM_EXEC_CHILDS; i++) { | ||
| 53 | if (tid[i] >= 0) { | ||
| 54 | r = wait(tid[i]); | ||
| 55 | printf("P child %d exited with exit code %d\n",i, r); | ||
| 56 | } else { | ||
| 57 | printf("P child %d failed to start\n", i); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | } else if(argv[1][0] == 'f') { | ||
| 61 | char buf[10]; | ||
| 62 | int r; | ||
| 63 | create ("test.txt", 10); | ||
| 64 | int handle = open ("test.txt"); | ||
| 65 | if (handle < 2) | ||
| 66 | printf ("open(test.txt) returned %d", handle); | ||
| 67 | if ((r=write(handle,"987654321",10)) != 10) { | ||
| 68 | printf("write failed: %d not %d\n",r,10); | ||
| 69 | exit(1); | ||
| 70 | } | ||
| 71 | seek(handle,0); | ||
| 72 | if ((r=read(handle, buf, 10)) != 10) { | ||
| 73 | printf("read failed: %d not %d\n",r,10); | ||
| 74 | exit(1); | ||
| 75 | } | ||
| 76 | printf("test.txt: %s\n", buf); | ||
| 77 | } else if(argv[1][0] == 'F') { | ||
| 78 | int j,i; | ||
| 79 | create ("foo.txt", 10); | ||
| 80 | for (j = 0; j < 5; j++) { | ||
| 81 | for (i = 2; i <= MAX_FD; i++) { | ||
| 82 | if (open ("foo.txt") < 0) { | ||
| 83 | printf("Opening the %d's file failed\n",i-2); | ||
| 84 | break; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | while(--i >= 2) { | ||
| 88 | close (i); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | } else if(argv[1][0] == '0') { | ||
| 92 | printf("Null pointer value is: %d\n",*((int*)NULL)); | ||
| 93 | } else if(argv[1][0] == 'h') { | ||
| 94 | halt(); | ||
| 95 | } else if(argv[1][0] == 'p' && argc >= 3) { | ||
| 96 | printf("%s\n", argv[2]); | ||
| 97 | } else { | ||
| 98 | printf("ARGV[1] is %s\n", argv[1]); | ||
| 99 | } | ||
| 100 | return 0; | ||
| 101 | } | ||
diff --git a/pintos-progos/filesys/.gitignore b/pintos-progos/filesys/.gitignore new file mode 100644 index 0000000..6d5357c --- /dev/null +++ b/pintos-progos/filesys/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | build | ||
| 2 | bochsrc.txt | ||
| 3 | bochsout.txt | ||
diff --git a/pintos-progos/filesys/Make.vars b/pintos-progos/filesys/Make.vars new file mode 100644 index 0000000..b3aa005 --- /dev/null +++ b/pintos-progos/filesys/Make.vars | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = -DUSERPROG -DFILESYS | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys | ||
| 5 | TEST_SUBDIRS = tests/userprog tests/filesys/base tests/filesys/extended | ||
| 6 | GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.no-vm | ||
| 7 | SIMULATOR = --qemu | ||
| 8 | |||
| 9 | # Uncomment the lines below to enable VM. | ||
| 10 | #kernel.bin: DEFINES += -DVM | ||
| 11 | #KERNEL_SUBDIRS += vm | ||
| 12 | #TEST_SUBDIRS += tests/vm | ||
| 13 | #GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.with-vm | ||
diff --git a/pintos-progos/filesys/Makefile b/pintos-progos/filesys/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/pintos-progos/filesys/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
diff --git a/pintos-progos/filesys/directory.c b/pintos-progos/filesys/directory.c new file mode 100644 index 0000000..030c1c9 --- /dev/null +++ b/pintos-progos/filesys/directory.c | |||
| @@ -0,0 +1,236 @@ | |||
| 1 | #include "filesys/directory.h" | ||
| 2 | #include <stdio.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <list.h> | ||
| 5 | #include "filesys/filesys.h" | ||
| 6 | #include "filesys/inode.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | |||
| 9 | /* A directory. */ | ||
| 10 | struct dir | ||
| 11 | { | ||
| 12 | struct inode *inode; /* Backing store. */ | ||
| 13 | off_t pos; /* Current position. */ | ||
| 14 | }; | ||
| 15 | |||
| 16 | /* A single directory entry. */ | ||
| 17 | struct dir_entry | ||
| 18 | { | ||
| 19 | block_sector_t inode_sector; /* Sector number of header. */ | ||
| 20 | char name[NAME_MAX + 1]; /* Null terminated file name. */ | ||
| 21 | bool in_use; /* In use or free? */ | ||
| 22 | }; | ||
| 23 | |||
| 24 | /* Creates a directory with space for ENTRY_CNT entries in the | ||
| 25 | given SECTOR. Returns true if successful, false on failure. */ | ||
| 26 | bool | ||
| 27 | dir_create (block_sector_t sector, size_t entry_cnt) | ||
| 28 | { | ||
| 29 | return inode_create (sector, entry_cnt * sizeof (struct dir_entry)); | ||
| 30 | } | ||
| 31 | |||
| 32 | /* Opens and returns the directory for the given INODE, of which | ||
| 33 | it takes ownership. Returns a null pointer on failure. */ | ||
| 34 | struct dir * | ||
| 35 | dir_open (struct inode *inode) | ||
| 36 | { | ||
| 37 | struct dir *dir = calloc (1, sizeof *dir); | ||
| 38 | if (inode != NULL && dir != NULL) | ||
| 39 | { | ||
| 40 | dir->inode = inode; | ||
| 41 | dir->pos = 0; | ||
| 42 | return dir; | ||
| 43 | } | ||
| 44 | else | ||
| 45 | { | ||
| 46 | inode_close (inode); | ||
| 47 | free (dir); | ||
| 48 | return NULL; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | /* Opens the root directory and returns a directory for it. | ||
| 53 | Return true if successful, false on failure. */ | ||
| 54 | struct dir * | ||
| 55 | dir_open_root (void) | ||
| 56 | { | ||
| 57 | return dir_open (inode_open (ROOT_DIR_SECTOR)); | ||
| 58 | } | ||
| 59 | |||
| 60 | /* Opens and returns a new directory for the same inode as DIR. | ||
| 61 | Returns a null pointer on failure. */ | ||
| 62 | struct dir * | ||
| 63 | dir_reopen (struct dir *dir) | ||
| 64 | { | ||
| 65 | return dir_open (inode_reopen (dir->inode)); | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Destroys DIR and frees associated resources. */ | ||
| 69 | void | ||
| 70 | dir_close (struct dir *dir) | ||
| 71 | { | ||
| 72 | if (dir != NULL) | ||
| 73 | { | ||
| 74 | inode_close (dir->inode); | ||
| 75 | free (dir); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Returns the inode encapsulated by DIR. */ | ||
| 80 | struct inode * | ||
| 81 | dir_get_inode (struct dir *dir) | ||
| 82 | { | ||
| 83 | return dir->inode; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Searches DIR for a file with the given NAME. | ||
| 87 | If successful, returns true, sets *EP to the directory entry | ||
| 88 | if EP is non-null, and sets *OFSP to the byte offset of the | ||
| 89 | directory entry if OFSP is non-null. | ||
| 90 | otherwise, returns false and ignores EP and OFSP. */ | ||
| 91 | static bool | ||
| 92 | lookup (const struct dir *dir, const char *name, | ||
| 93 | struct dir_entry *ep, off_t *ofsp) | ||
| 94 | { | ||
| 95 | struct dir_entry e; | ||
| 96 | size_t ofs; | ||
| 97 | |||
| 98 | ASSERT (dir != NULL); | ||
| 99 | ASSERT (name != NULL); | ||
| 100 | |||
| 101 | for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; | ||
| 102 | ofs += sizeof e) | ||
| 103 | if (e.in_use && !strcmp (name, e.name)) | ||
| 104 | { | ||
| 105 | if (ep != NULL) | ||
| 106 | *ep = e; | ||
| 107 | if (ofsp != NULL) | ||
| 108 | *ofsp = ofs; | ||
| 109 | return true; | ||
| 110 | } | ||
| 111 | return false; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* Searches DIR for a file with the given NAME | ||
| 115 | and returns true if one exists, false otherwise. | ||
| 116 | On success, sets *INODE to an inode for the file, otherwise to | ||
| 117 | a null pointer. The caller must close *INODE. */ | ||
| 118 | bool | ||
| 119 | dir_lookup (const struct dir *dir, const char *name, | ||
| 120 | struct inode **inode) | ||
| 121 | { | ||
| 122 | struct dir_entry e; | ||
| 123 | |||
| 124 | ASSERT (dir != NULL); | ||
| 125 | ASSERT (name != NULL); | ||
| 126 | |||
| 127 | if (lookup (dir, name, &e, NULL)) | ||
| 128 | *inode = inode_open (e.inode_sector); | ||
| 129 | else | ||
| 130 | *inode = NULL; | ||
| 131 | |||
| 132 | return *inode != NULL; | ||
| 133 | } | ||
| 134 | |||
| 135 | /* Adds a file named NAME to DIR, which must not already contain a | ||
| 136 | file by that name. The file's inode is in sector | ||
| 137 | INODE_SECTOR. | ||
| 138 | Returns true if successful, false on failure. | ||
| 139 | Fails if NAME is invalid (i.e. too long) or a disk or memory | ||
| 140 | error occurs. */ | ||
| 141 | bool | ||
| 142 | dir_add (struct dir *dir, const char *name, block_sector_t inode_sector) | ||
| 143 | { | ||
| 144 | struct dir_entry e; | ||
| 145 | off_t ofs; | ||
| 146 | bool success = false; | ||
| 147 | |||
| 148 | ASSERT (dir != NULL); | ||
| 149 | ASSERT (name != NULL); | ||
| 150 | |||
| 151 | /* Check NAME for validity. */ | ||
| 152 | if (*name == '\0' || strlen (name) > NAME_MAX) | ||
| 153 | return false; | ||
| 154 | |||
| 155 | /* Check that NAME is not in use. */ | ||
| 156 | if (lookup (dir, name, NULL, NULL)) | ||
| 157 | goto done; | ||
| 158 | |||
| 159 | /* Set OFS to offset of free slot. | ||
| 160 | If there are no free slots, then it will be set to the | ||
| 161 | current end-of-file. | ||
| 162 | |||
| 163 | inode_read_at() will only return a short read at end of file. | ||
| 164 | Otherwise, we'd need to verify that we didn't get a short | ||
| 165 | read due to something intermittent such as low memory. */ | ||
| 166 | for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e; | ||
| 167 | ofs += sizeof e) | ||
| 168 | if (!e.in_use) | ||
| 169 | break; | ||
| 170 | |||
| 171 | /* Write slot. */ | ||
| 172 | e.in_use = true; | ||
| 173 | strlcpy (e.name, name, sizeof e.name); | ||
| 174 | e.inode_sector = inode_sector; | ||
| 175 | success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e; | ||
| 176 | |||
| 177 | done: | ||
| 178 | return success; | ||
| 179 | } | ||
| 180 | |||
| 181 | /* Removes any entry for NAME in DIR. | ||
| 182 | Returns true if successful, false on failure, | ||
| 183 | which occurs only if there is no file with the given NAME. */ | ||
| 184 | bool | ||
| 185 | dir_remove (struct dir *dir, const char *name) | ||
| 186 | { | ||
| 187 | struct dir_entry e; | ||
| 188 | struct inode *inode = NULL; | ||
| 189 | bool success = false; | ||
| 190 | off_t ofs; | ||
| 191 | |||
| 192 | ASSERT (dir != NULL); | ||
| 193 | ASSERT (name != NULL); | ||
| 194 | |||
| 195 | /* Find directory entry. */ | ||
| 196 | if (!lookup (dir, name, &e, &ofs)) | ||
| 197 | goto done; | ||
| 198 | |||
| 199 | /* Open inode. */ | ||
| 200 | inode = inode_open (e.inode_sector); | ||
| 201 | if (inode == NULL) | ||
| 202 | goto done; | ||
| 203 | |||
| 204 | /* Erase directory entry. */ | ||
| 205 | e.in_use = false; | ||
| 206 | if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e) | ||
| 207 | goto done; | ||
| 208 | |||
| 209 | /* Remove inode. */ | ||
| 210 | inode_remove (inode); | ||
| 211 | success = true; | ||
| 212 | |||
| 213 | done: | ||
| 214 | inode_close (inode); | ||
| 215 | return success; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* Reads the next directory entry in DIR and stores the name in | ||
| 219 | NAME. Returns true if successful, false if the directory | ||
| 220 | contains no more entries. */ | ||
| 221 | bool | ||
| 222 | dir_readdir (struct dir *dir, char name[NAME_MAX + 1]) | ||
| 223 | { | ||
| 224 | struct dir_entry e; | ||
| 225 | |||
| 226 | while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e) | ||
| 227 | { | ||
| 228 | dir->pos += sizeof e; | ||
| 229 | if (e.in_use) | ||
| 230 | { | ||
| 231 | strlcpy (name, e.name, NAME_MAX + 1); | ||
| 232 | return true; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | return false; | ||
| 236 | } | ||
diff --git a/pintos-progos/filesys/directory.h b/pintos-progos/filesys/directory.h new file mode 100644 index 0000000..930acf9 --- /dev/null +++ b/pintos-progos/filesys/directory.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #ifndef FILESYS_DIRECTORY_H | ||
| 2 | #define FILESYS_DIRECTORY_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | #include "devices/block.h" | ||
| 7 | |||
| 8 | /* Maximum length of a file name component. | ||
| 9 | This is the traditional UNIX maximum length. | ||
| 10 | After directories are implemented, this maximum length may be | ||
| 11 | retained, but much longer full path names must be allowed. */ | ||
| 12 | #define NAME_MAX 14 | ||
| 13 | |||
| 14 | struct inode; | ||
| 15 | |||
| 16 | /* Opening and closing directories. */ | ||
| 17 | bool dir_create (block_sector_t sector, size_t entry_cnt); | ||
| 18 | struct dir *dir_open (struct inode *); | ||
| 19 | struct dir *dir_open_root (void); | ||
| 20 | struct dir *dir_reopen (struct dir *); | ||
| 21 | void dir_close (struct dir *); | ||
| 22 | struct inode *dir_get_inode (struct dir *); | ||
| 23 | |||
| 24 | /* Reading and writing. */ | ||
| 25 | bool dir_lookup (const struct dir *, const char *name, struct inode **); | ||
| 26 | bool dir_add (struct dir *, const char *name, block_sector_t); | ||
| 27 | bool dir_remove (struct dir *, const char *name); | ||
| 28 | bool dir_readdir (struct dir *, char name[NAME_MAX + 1]); | ||
| 29 | |||
| 30 | #endif /* filesys/directory.h */ | ||
diff --git a/pintos-progos/filesys/file.c b/pintos-progos/filesys/file.c new file mode 100644 index 0000000..d5fc10d --- /dev/null +++ b/pintos-progos/filesys/file.c | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | #include "filesys/file.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "filesys/inode.h" | ||
| 4 | #include "threads/malloc.h" | ||
| 5 | |||
| 6 | /* An open file. */ | ||
| 7 | struct file | ||
| 8 | { | ||
| 9 | struct inode *inode; /* File's inode. */ | ||
| 10 | off_t pos; /* Current position. */ | ||
| 11 | bool deny_write; /* Has file_deny_write() been called? */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | /* Opens a file for the given INODE, of which it takes ownership, | ||
| 15 | and returns the new file. Returns a null pointer if an | ||
| 16 | allocation fails or if INODE is null. */ | ||
| 17 | struct file * | ||
| 18 | file_open (struct inode *inode) | ||
| 19 | { | ||
| 20 | struct file *file = calloc (1, sizeof *file); | ||
| 21 | if (inode != NULL && file != NULL) | ||
| 22 | { | ||
| 23 | file->inode = inode; | ||
| 24 | file->pos = 0; | ||
| 25 | file->deny_write = false; | ||
| 26 | return file; | ||
| 27 | } | ||
| 28 | else | ||
| 29 | { | ||
| 30 | inode_close (inode); | ||
| 31 | free (file); | ||
| 32 | return NULL; | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Opens and returns a new file for the same inode as FILE. | ||
| 37 | Returns a null pointer if unsuccessful. */ | ||
| 38 | struct file * | ||
| 39 | file_reopen (struct file *file) | ||
| 40 | { | ||
| 41 | return file_open (inode_reopen (file->inode)); | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Closes FILE. */ | ||
| 45 | void | ||
| 46 | file_close (struct file *file) | ||
| 47 | { | ||
| 48 | if (file != NULL) | ||
| 49 | { | ||
| 50 | file_allow_write (file); | ||
| 51 | inode_close (file->inode); | ||
| 52 | free (file); | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | /* Returns the inode encapsulated by FILE. */ | ||
| 57 | struct inode * | ||
| 58 | file_get_inode (struct file *file) | ||
| 59 | { | ||
| 60 | return file->inode; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Reads SIZE bytes from FILE into BUFFER, | ||
| 64 | starting at the file's current position. | ||
| 65 | Returns the number of bytes actually read, | ||
| 66 | which may be less than SIZE if end of file is reached. | ||
| 67 | Advances FILE's position by the number of bytes read. */ | ||
| 68 | off_t | ||
| 69 | file_read (struct file *file, void *buffer, off_t size) | ||
| 70 | { | ||
| 71 | off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos); | ||
| 72 | file->pos += bytes_read; | ||
| 73 | return bytes_read; | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Reads SIZE bytes from FILE into BUFFER, | ||
| 77 | starting at offset FILE_OFS in the file. | ||
| 78 | Returns the number of bytes actually read, | ||
| 79 | which may be less than SIZE if end of file is reached. | ||
| 80 | The file's current position is unaffected. */ | ||
| 81 | off_t | ||
| 82 | file_read_at (struct file *file, void *buffer, off_t size, off_t file_ofs) | ||
| 83 | { | ||
| 84 | return inode_read_at (file->inode, buffer, size, file_ofs); | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Writes SIZE bytes from BUFFER into FILE, | ||
| 88 | starting at the file's current position. | ||
| 89 | Returns the number of bytes actually written, | ||
| 90 | which may be less than SIZE if end of file is reached. | ||
| 91 | (Normally we'd grow the file in that case, but file growth is | ||
| 92 | not yet implemented.) | ||
| 93 | Advances FILE's position by the number of bytes read. */ | ||
| 94 | off_t | ||
| 95 | file_write (struct file *file, const void *buffer, off_t size) | ||
| 96 | { | ||
| 97 | off_t bytes_written = inode_write_at (file->inode, buffer, size, file->pos); | ||
| 98 | file->pos += bytes_written; | ||
| 99 | return bytes_written; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Writes SIZE bytes from BUFFER into FILE, | ||
| 103 | starting at offset FILE_OFS in the file. | ||
| 104 | Returns the number of bytes actually written, | ||
| 105 | which may be less than SIZE if end of file is reached. | ||
| 106 | (Normally we'd grow the file in that case, but file growth is | ||
| 107 | not yet implemented.) | ||
| 108 | The file's current position is unaffected. */ | ||
| 109 | off_t | ||
| 110 | file_write_at (struct file *file, const void *buffer, off_t size, | ||
| 111 | off_t file_ofs) | ||
| 112 | { | ||
| 113 | return inode_write_at (file->inode, buffer, size, file_ofs); | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Prevents write operations on FILE's underlying inode | ||
| 117 | until file_allow_write() is called or FILE is closed. */ | ||
| 118 | void | ||
| 119 | file_deny_write (struct file *file) | ||
| 120 | { | ||
| 121 | ASSERT (file != NULL); | ||
| 122 | if (!file->deny_write) | ||
| 123 | { | ||
| 124 | file->deny_write = true; | ||
| 125 | inode_deny_write (file->inode); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Re-enables write operations on FILE's underlying inode. | ||
| 130 | (Writes might still be denied by some other file that has the | ||
| 131 | same inode open.) */ | ||
| 132 | void | ||
| 133 | file_allow_write (struct file *file) | ||
| 134 | { | ||
| 135 | ASSERT (file != NULL); | ||
| 136 | if (file->deny_write) | ||
| 137 | { | ||
| 138 | file->deny_write = false; | ||
| 139 | inode_allow_write (file->inode); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | /* Returns the size of FILE in bytes. */ | ||
| 144 | off_t | ||
| 145 | file_length (struct file *file) | ||
| 146 | { | ||
| 147 | ASSERT (file != NULL); | ||
| 148 | return inode_length (file->inode); | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Sets the current position in FILE to NEW_POS bytes from the | ||
| 152 | start of the file. */ | ||
| 153 | void | ||
| 154 | file_seek (struct file *file, off_t new_pos) | ||
| 155 | { | ||
| 156 | ASSERT (file != NULL); | ||
| 157 | ASSERT (new_pos >= 0); | ||
| 158 | file->pos = new_pos; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* Returns the current position in FILE as a byte offset from the | ||
| 162 | start of the file. */ | ||
| 163 | off_t | ||
| 164 | file_tell (struct file *file) | ||
| 165 | { | ||
| 166 | ASSERT (file != NULL); | ||
| 167 | return file->pos; | ||
| 168 | } | ||
diff --git a/pintos-progos/filesys/file.h b/pintos-progos/filesys/file.h new file mode 100644 index 0000000..a33c5af --- /dev/null +++ b/pintos-progos/filesys/file.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #ifndef FILESYS_FILE_H | ||
| 2 | #define FILESYS_FILE_H | ||
| 3 | |||
| 4 | #include "filesys/off_t.h" | ||
| 5 | |||
| 6 | struct inode; | ||
| 7 | |||
| 8 | /* Opening and closing files. */ | ||
| 9 | struct file *file_open (struct inode *); | ||
| 10 | struct file *file_reopen (struct file *); | ||
| 11 | void file_close (struct file *); | ||
| 12 | struct inode *file_get_inode (struct file *); | ||
| 13 | |||
| 14 | /* Reading and writing. */ | ||
| 15 | off_t file_read (struct file *, void *, off_t); | ||
| 16 | off_t file_read_at (struct file *, void *, off_t size, off_t start); | ||
| 17 | off_t file_write (struct file *, const void *, off_t); | ||
| 18 | off_t file_write_at (struct file *, const void *, off_t size, off_t start); | ||
| 19 | |||
| 20 | /* Preventing writes. */ | ||
| 21 | void file_deny_write (struct file *); | ||
| 22 | void file_allow_write (struct file *); | ||
| 23 | |||
| 24 | /* File position. */ | ||
| 25 | void file_seek (struct file *, off_t); | ||
| 26 | off_t file_tell (struct file *); | ||
| 27 | off_t file_length (struct file *); | ||
| 28 | |||
| 29 | #endif /* filesys/file.h */ | ||
diff --git a/pintos-progos/filesys/filesys.c b/pintos-progos/filesys/filesys.c new file mode 100644 index 0000000..7a53f5f --- /dev/null +++ b/pintos-progos/filesys/filesys.c | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | #include "filesys/filesys.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include "filesys/file.h" | ||
| 6 | #include "filesys/free-map.h" | ||
| 7 | #include "filesys/inode.h" | ||
| 8 | #include "filesys/directory.h" | ||
| 9 | |||
| 10 | /* Partition that contains the file system. */ | ||
| 11 | struct block *fs_device; | ||
| 12 | |||
| 13 | static void do_format (void); | ||
| 14 | |||
| 15 | /* Initializes the file system module. | ||
| 16 | If FORMAT is true, reformats the file system. */ | ||
| 17 | void | ||
| 18 | filesys_init (bool format) | ||
| 19 | { | ||
| 20 | fs_device = block_get_role (BLOCK_FILESYS); | ||
| 21 | if (fs_device == NULL) | ||
| 22 | PANIC ("No file system device found, can't initialize file system."); | ||
| 23 | |||
| 24 | inode_init (); | ||
| 25 | free_map_init (); | ||
| 26 | |||
| 27 | if (format) | ||
| 28 | do_format (); | ||
| 29 | |||
| 30 | free_map_open (); | ||
| 31 | } | ||
| 32 | |||
| 33 | /* Shuts down the file system module, writing any unwritten data | ||
| 34 | to disk. */ | ||
| 35 | void | ||
| 36 | filesys_done (void) | ||
| 37 | { | ||
| 38 | free_map_close (); | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Creates a file named NAME with the given INITIAL_SIZE. | ||
| 42 | Returns true if successful, false otherwise. | ||
| 43 | Fails if a file named NAME already exists, | ||
| 44 | or if internal memory allocation fails. */ | ||
| 45 | bool | ||
| 46 | filesys_create (const char *name, off_t initial_size) | ||
| 47 | { | ||
| 48 | block_sector_t inode_sector = 0; | ||
| 49 | struct dir *dir = dir_open_root (); | ||
| 50 | bool success = (dir != NULL | ||
| 51 | && free_map_allocate (1, &inode_sector) | ||
| 52 | && inode_create (inode_sector, initial_size) | ||
| 53 | && dir_add (dir, name, inode_sector)); | ||
| 54 | if (!success && inode_sector != 0) | ||
| 55 | free_map_release (inode_sector, 1); | ||
| 56 | dir_close (dir); | ||
| 57 | |||
| 58 | return success; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* Opens the file with the given NAME. | ||
| 62 | Returns the new file if successful or a null pointer | ||
| 63 | otherwise. | ||
| 64 | Fails if no file named NAME exists, | ||
| 65 | or if an internal memory allocation fails. */ | ||
| 66 | struct file * | ||
| 67 | filesys_open (const char *name) | ||
| 68 | { | ||
| 69 | struct dir *dir = dir_open_root (); | ||
| 70 | struct inode *inode = NULL; | ||
| 71 | |||
| 72 | if (dir != NULL) | ||
| 73 | dir_lookup (dir, name, &inode); | ||
| 74 | dir_close (dir); | ||
| 75 | |||
| 76 | return file_open (inode); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Deletes the file named NAME. | ||
| 80 | Returns true if successful, false on failure. | ||
| 81 | Fails if no file named NAME exists, | ||
| 82 | or if an internal memory allocation fails. */ | ||
| 83 | bool | ||
| 84 | filesys_remove (const char *name) | ||
| 85 | { | ||
| 86 | struct dir *dir = dir_open_root (); | ||
| 87 | bool success = dir != NULL && dir_remove (dir, name); | ||
| 88 | dir_close (dir); | ||
| 89 | |||
| 90 | return success; | ||
| 91 | } | ||
| 92 | |||
| 93 | /* Formats the file system. */ | ||
| 94 | static void | ||
| 95 | do_format (void) | ||
| 96 | { | ||
| 97 | printf ("Formatting file system..."); | ||
| 98 | free_map_create (); | ||
| 99 | if (!dir_create (ROOT_DIR_SECTOR, 16)) | ||
| 100 | PANIC ("root directory creation failed"); | ||
| 101 | free_map_close (); | ||
| 102 | printf ("done.\n"); | ||
| 103 | } | ||
diff --git a/pintos-progos/filesys/filesys.h b/pintos-progos/filesys/filesys.h new file mode 100644 index 0000000..c1cda84 --- /dev/null +++ b/pintos-progos/filesys/filesys.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #ifndef FILESYS_FILESYS_H | ||
| 2 | #define FILESYS_FILESYS_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include "filesys/off_t.h" | ||
| 6 | |||
| 7 | /* Sectors of system file inodes. */ | ||
| 8 | #define FREE_MAP_SECTOR 0 /* Free map file inode sector. */ | ||
| 9 | #define ROOT_DIR_SECTOR 1 /* Root directory file inode sector. */ | ||
| 10 | |||
| 11 | /* Block device that contains the file system. */ | ||
| 12 | struct block *fs_device; | ||
| 13 | |||
| 14 | void filesys_init (bool format); | ||
| 15 | void filesys_done (void); | ||
| 16 | bool filesys_create (const char *name, off_t initial_size); | ||
| 17 | struct file *filesys_open (const char *name); | ||
| 18 | bool filesys_remove (const char *name); | ||
| 19 | |||
| 20 | #endif /* filesys/filesys.h */ | ||
diff --git a/pintos-progos/filesys/free-map.c b/pintos-progos/filesys/free-map.c new file mode 100644 index 0000000..29ea4df --- /dev/null +++ b/pintos-progos/filesys/free-map.c | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | #include "filesys/free-map.h" | ||
| 2 | #include <bitmap.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include "filesys/file.h" | ||
| 5 | #include "filesys/filesys.h" | ||
| 6 | #include "filesys/inode.h" | ||
| 7 | |||
| 8 | static struct file *free_map_file; /* Free map file. */ | ||
| 9 | static struct bitmap *free_map; /* Free map, one bit per sector. */ | ||
| 10 | |||
| 11 | /* Initializes the free map. */ | ||
| 12 | void | ||
| 13 | free_map_init (void) | ||
| 14 | { | ||
| 15 | free_map = bitmap_create (block_size (fs_device)); | ||
| 16 | if (free_map == NULL) | ||
| 17 | PANIC ("bitmap creation failed--file system device is too large"); | ||
| 18 | bitmap_mark (free_map, FREE_MAP_SECTOR); | ||
| 19 | bitmap_mark (free_map, ROOT_DIR_SECTOR); | ||
| 20 | } | ||
| 21 | |||
| 22 | /* Allocates CNT consecutive sectors from the free map and stores | ||
| 23 | the first into *SECTORP. | ||
| 24 | Returns true if successful, false if not enough consecutive | ||
| 25 | sectors were available or if the free_map file could not be | ||
| 26 | written. */ | ||
| 27 | bool | ||
| 28 | free_map_allocate (size_t cnt, block_sector_t *sectorp) | ||
| 29 | { | ||
| 30 | block_sector_t sector = bitmap_scan_and_flip (free_map, 0, cnt, false); | ||
| 31 | if (sector != BITMAP_ERROR | ||
| 32 | && free_map_file != NULL | ||
| 33 | && !bitmap_write (free_map, free_map_file)) | ||
| 34 | { | ||
| 35 | bitmap_set_multiple (free_map, sector, cnt, false); | ||
| 36 | sector = BITMAP_ERROR; | ||
| 37 | } | ||
| 38 | if (sector != BITMAP_ERROR) | ||
| 39 | *sectorp = sector; | ||
| 40 | return sector != BITMAP_ERROR; | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Makes CNT sectors starting at SECTOR available for use. */ | ||
| 44 | void | ||
| 45 | free_map_release (block_sector_t sector, size_t cnt) | ||
| 46 | { | ||
| 47 | ASSERT (bitmap_all (free_map, sector, cnt)); | ||
| 48 | bitmap_set_multiple (free_map, sector, cnt, false); | ||
| 49 | bitmap_write (free_map, free_map_file); | ||
| 50 | } | ||
| 51 | |||
| 52 | /* Opens the free map file and reads it from disk. */ | ||
| 53 | void | ||
| 54 | free_map_open (void) | ||
| 55 | { | ||
| 56 | free_map_file = file_open (inode_open (FREE_MAP_SECTOR)); | ||
| 57 | if (free_map_file == NULL) | ||
| 58 | PANIC ("can't open free map"); | ||
| 59 | if (!bitmap_read (free_map, free_map_file)) | ||
| 60 | PANIC ("can't read free map"); | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Writes the free map to disk and closes the free map file. */ | ||
| 64 | void | ||
| 65 | free_map_close (void) | ||
| 66 | { | ||
| 67 | file_close (free_map_file); | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Creates a new free map file on disk and writes the free map to | ||
| 71 | it. */ | ||
| 72 | void | ||
| 73 | free_map_create (void) | ||
| 74 | { | ||
| 75 | /* Create inode. */ | ||
| 76 | if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map))) | ||
| 77 | PANIC ("free map creation failed"); | ||
| 78 | |||
| 79 | /* Write bitmap to file. */ | ||
| 80 | free_map_file = file_open (inode_open (FREE_MAP_SECTOR)); | ||
| 81 | if (free_map_file == NULL) | ||
| 82 | PANIC ("can't open free map"); | ||
| 83 | if (!bitmap_write (free_map, free_map_file)) | ||
| 84 | PANIC ("can't write free map"); | ||
| 85 | } | ||
diff --git a/pintos-progos/filesys/free-map.h b/pintos-progos/filesys/free-map.h new file mode 100644 index 0000000..316cd1c --- /dev/null +++ b/pintos-progos/filesys/free-map.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #ifndef FILESYS_FREE_MAP_H | ||
| 2 | #define FILESYS_FREE_MAP_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | #include "devices/block.h" | ||
| 7 | |||
| 8 | void free_map_init (void); | ||
| 9 | void free_map_read (void); | ||
| 10 | void free_map_create (void); | ||
| 11 | void free_map_open (void); | ||
| 12 | void free_map_close (void); | ||
| 13 | |||
| 14 | bool free_map_allocate (size_t, block_sector_t *); | ||
| 15 | void free_map_release (block_sector_t, size_t); | ||
| 16 | |||
| 17 | #endif /* filesys/free-map.h */ | ||
diff --git a/pintos-progos/filesys/fsutil.c b/pintos-progos/filesys/fsutil.c new file mode 100644 index 0000000..447f291 --- /dev/null +++ b/pintos-progos/filesys/fsutil.c | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | #include "filesys/fsutil.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <ustar.h> | ||
| 7 | #include "filesys/directory.h" | ||
| 8 | #include "filesys/file.h" | ||
| 9 | #include "filesys/filesys.h" | ||
| 10 | #include "threads/malloc.h" | ||
| 11 | #include "threads/palloc.h" | ||
| 12 | #include "threads/vaddr.h" | ||
| 13 | |||
| 14 | /* List files in the root directory. */ | ||
| 15 | void | ||
| 16 | fsutil_ls (char **argv UNUSED) | ||
| 17 | { | ||
| 18 | struct dir *dir; | ||
| 19 | char name[NAME_MAX + 1]; | ||
| 20 | |||
| 21 | printf ("Files in the root directory:\n"); | ||
| 22 | dir = dir_open_root (); | ||
| 23 | if (dir == NULL) | ||
| 24 | PANIC ("root dir open failed"); | ||
| 25 | while (dir_readdir (dir, name)) | ||
| 26 | printf ("%s\n", name); | ||
| 27 | printf ("End of listing.\n"); | ||
| 28 | } | ||
| 29 | |||
| 30 | /* Prints the contents of file ARGV[1] to the system console as | ||
| 31 | hex and ASCII. */ | ||
| 32 | void | ||
| 33 | fsutil_cat (char **argv) | ||
| 34 | { | ||
| 35 | const char *file_name = argv[1]; | ||
| 36 | |||
| 37 | struct file *file; | ||
| 38 | char *buffer; | ||
| 39 | |||
| 40 | printf ("Printing '%s' to the console...\n", file_name); | ||
| 41 | file = filesys_open (file_name); | ||
| 42 | if (file == NULL) | ||
| 43 | PANIC ("%s: open failed", file_name); | ||
| 44 | buffer = palloc_get_page (PAL_ASSERT); | ||
| 45 | for (;;) | ||
| 46 | { | ||
| 47 | off_t pos = file_tell (file); | ||
| 48 | off_t n = file_read (file, buffer, PGSIZE); | ||
| 49 | if (n == 0) | ||
| 50 | break; | ||
| 51 | |||
| 52 | hex_dump (pos, buffer, n, true); | ||
| 53 | } | ||
| 54 | palloc_free_page (buffer); | ||
| 55 | file_close (file); | ||
| 56 | } | ||
| 57 | |||
| 58 | /* Deletes file ARGV[1]. */ | ||
| 59 | void | ||
| 60 | fsutil_rm (char **argv) | ||
| 61 | { | ||
| 62 | const char *file_name = argv[1]; | ||
| 63 | |||
| 64 | printf ("Deleting '%s'...\n", file_name); | ||
| 65 | if (!filesys_remove (file_name)) | ||
| 66 | PANIC ("%s: delete failed\n", file_name); | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Extracts a ustar-format tar archive from the scratch block | ||
| 70 | device into the Pintos file system. */ | ||
| 71 | void | ||
| 72 | fsutil_extract (char **argv UNUSED) | ||
| 73 | { | ||
| 74 | static block_sector_t sector = 0; | ||
| 75 | |||
| 76 | struct block *src; | ||
| 77 | void *header, *data; | ||
| 78 | |||
| 79 | /* Allocate buffers. */ | ||
| 80 | header = malloc (BLOCK_SECTOR_SIZE); | ||
| 81 | data = malloc (BLOCK_SECTOR_SIZE); | ||
| 82 | if (header == NULL || data == NULL) | ||
| 83 | PANIC ("couldn't allocate buffers"); | ||
| 84 | |||
| 85 | /* Open source block device. */ | ||
| 86 | src = block_get_role (BLOCK_SCRATCH); | ||
| 87 | if (src == NULL) | ||
| 88 | PANIC ("couldn't open scratch device"); | ||
| 89 | |||
| 90 | printf ("Extracting ustar archive from scratch device " | ||
| 91 | "into file system...\n"); | ||
| 92 | |||
| 93 | for (;;) | ||
| 94 | { | ||
| 95 | const char *file_name; | ||
| 96 | const char *error; | ||
| 97 | enum ustar_type type; | ||
| 98 | int size; | ||
| 99 | |||
| 100 | /* Read and parse ustar header. */ | ||
| 101 | block_read (src, sector++, header); | ||
| 102 | error = ustar_parse_header (header, &file_name, &type, &size); | ||
| 103 | if (error != NULL) | ||
| 104 | PANIC ("bad ustar header in sector %"PRDSNu" (%s)", sector - 1, error); | ||
| 105 | |||
| 106 | if (type == USTAR_EOF) | ||
| 107 | { | ||
| 108 | /* End of archive. */ | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | else if (type == USTAR_DIRECTORY) | ||
| 112 | printf ("ignoring directory %s\n", file_name); | ||
| 113 | else if (type == USTAR_REGULAR) | ||
| 114 | { | ||
| 115 | struct file *dst; | ||
| 116 | |||
| 117 | printf ("Putting '%s' into the file system...\n", file_name); | ||
| 118 | |||
| 119 | /* Create destination file. */ | ||
| 120 | if (!filesys_create (file_name, size)) | ||
| 121 | PANIC ("%s: create failed", file_name); | ||
| 122 | dst = filesys_open (file_name); | ||
| 123 | if (dst == NULL) | ||
| 124 | PANIC ("%s: open failed", file_name); | ||
| 125 | |||
| 126 | /* Do copy. */ | ||
| 127 | while (size > 0) | ||
| 128 | { | ||
| 129 | int chunk_size = (size > BLOCK_SECTOR_SIZE | ||
| 130 | ? BLOCK_SECTOR_SIZE | ||
| 131 | : size); | ||
| 132 | block_read (src, sector++, data); | ||
| 133 | if (file_write (dst, data, chunk_size) != chunk_size) | ||
| 134 | PANIC ("%s: write failed with %d bytes unwritten", | ||
| 135 | file_name, size); | ||
| 136 | size -= chunk_size; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Finish up. */ | ||
| 140 | file_close (dst); | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Erase the ustar header from the start of the block device, | ||
| 145 | so that the extraction operation is idempotent. We erase | ||
| 146 | two blocks because two blocks of zeros are the ustar | ||
| 147 | end-of-archive marker. */ | ||
| 148 | printf ("Erasing ustar archive...\n"); | ||
| 149 | memset (header, 0, BLOCK_SECTOR_SIZE); | ||
| 150 | block_write (src, 0, header); | ||
| 151 | block_write (src, 1, header); | ||
| 152 | |||
| 153 | free (data); | ||
| 154 | free (header); | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Copies file FILE_NAME from the file system to the scratch | ||
| 158 | device, in ustar format. | ||
| 159 | |||
| 160 | The first call to this function will write starting at the | ||
| 161 | beginning of the scratch device. Later calls advance across | ||
| 162 | the device. This position is independent of that used for | ||
| 163 | fsutil_extract(), so `extract' should precede all | ||
| 164 | `append's. */ | ||
| 165 | void | ||
| 166 | fsutil_append (char **argv) | ||
| 167 | { | ||
| 168 | static block_sector_t sector = 0; | ||
| 169 | |||
| 170 | const char *file_name = argv[1]; | ||
| 171 | void *buffer; | ||
| 172 | struct file *src; | ||
| 173 | struct block *dst; | ||
| 174 | off_t size; | ||
| 175 | |||
| 176 | printf ("Appending '%s' to ustar archive on scratch device...\n", file_name); | ||
| 177 | |||
| 178 | /* Allocate buffer. */ | ||
| 179 | buffer = malloc (BLOCK_SECTOR_SIZE); | ||
| 180 | if (buffer == NULL) | ||
| 181 | PANIC ("couldn't allocate buffer"); | ||
| 182 | |||
| 183 | /* Open source file. */ | ||
| 184 | src = filesys_open (file_name); | ||
| 185 | if (src == NULL) | ||
| 186 | PANIC ("%s: open failed", file_name); | ||
| 187 | size = file_length (src); | ||
| 188 | |||
| 189 | /* Open target block device. */ | ||
| 190 | dst = block_get_role (BLOCK_SCRATCH); | ||
| 191 | if (dst == NULL) | ||
| 192 | PANIC ("couldn't open scratch device"); | ||
| 193 | |||
| 194 | /* Write ustar header to first sector. */ | ||
| 195 | if (!ustar_make_header (file_name, USTAR_REGULAR, size, buffer)) | ||
| 196 | PANIC ("%s: name too long for ustar format", file_name); | ||
| 197 | block_write (dst, sector++, buffer); | ||
| 198 | |||
| 199 | /* Do copy. */ | ||
| 200 | while (size > 0) | ||
| 201 | { | ||
| 202 | int chunk_size = size > BLOCK_SECTOR_SIZE ? BLOCK_SECTOR_SIZE : size; | ||
| 203 | if (sector >= block_size (dst)) | ||
| 204 | PANIC ("%s: out of space on scratch device", file_name); | ||
| 205 | if (file_read (src, buffer, chunk_size) != chunk_size) | ||
| 206 | PANIC ("%s: read failed with %"PROTd" bytes unread", file_name, size); | ||
| 207 | memset (buffer + chunk_size, 0, BLOCK_SECTOR_SIZE - chunk_size); | ||
| 208 | block_write (dst, sector++, buffer); | ||
| 209 | size -= chunk_size; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* Write ustar end-of-archive marker, which is two consecutive | ||
| 213 | sectors full of zeros. Don't advance our position past | ||
| 214 | them, though, in case we have more files to append. */ | ||
| 215 | memset (buffer, 0, BLOCK_SECTOR_SIZE); | ||
| 216 | block_write (dst, sector, buffer); | ||
| 217 | block_write (dst, sector, buffer + 1); | ||
| 218 | |||
| 219 | /* Finish up. */ | ||
| 220 | file_close (src); | ||
| 221 | free (buffer); | ||
| 222 | } | ||
diff --git a/pintos-progos/filesys/fsutil.h b/pintos-progos/filesys/fsutil.h new file mode 100644 index 0000000..cc73705 --- /dev/null +++ b/pintos-progos/filesys/fsutil.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | #ifndef FILESYS_FSUTIL_H | ||
| 2 | #define FILESYS_FSUTIL_H | ||
| 3 | |||
| 4 | void fsutil_ls (char **argv); | ||
| 5 | void fsutil_cat (char **argv); | ||
| 6 | void fsutil_rm (char **argv); | ||
| 7 | void fsutil_extract (char **argv); | ||
| 8 | void fsutil_append (char **argv); | ||
| 9 | |||
| 10 | #endif /* filesys/fsutil.h */ | ||
diff --git a/pintos-progos/filesys/inode.c b/pintos-progos/filesys/inode.c new file mode 100644 index 0000000..3463563 --- /dev/null +++ b/pintos-progos/filesys/inode.c | |||
| @@ -0,0 +1,345 @@ | |||
| 1 | #include "filesys/inode.h" | ||
| 2 | #include <list.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include "filesys/filesys.h" | ||
| 7 | #include "filesys/free-map.h" | ||
| 8 | #include "threads/malloc.h" | ||
| 9 | |||
| 10 | /* Identifies an inode. */ | ||
| 11 | #define INODE_MAGIC 0x494e4f44 | ||
| 12 | |||
| 13 | /* On-disk inode. | ||
| 14 | Must be exactly BLOCK_SECTOR_SIZE bytes long. */ | ||
| 15 | struct inode_disk | ||
| 16 | { | ||
| 17 | block_sector_t start; /* First data sector. */ | ||
| 18 | off_t length; /* File size in bytes. */ | ||
| 19 | unsigned magic; /* Magic number. */ | ||
| 20 | uint32_t unused[125]; /* Not used. */ | ||
| 21 | }; | ||
| 22 | |||
| 23 | /* Returns the number of sectors to allocate for an inode SIZE | ||
| 24 | bytes long. */ | ||
| 25 | static inline size_t | ||
| 26 | bytes_to_sectors (off_t size) | ||
| 27 | { | ||
| 28 | return DIV_ROUND_UP (size, BLOCK_SECTOR_SIZE); | ||
| 29 | } | ||
| 30 | |||
| 31 | /* In-memory inode. */ | ||
| 32 | struct inode | ||
| 33 | { | ||
| 34 | struct list_elem elem; /* Element in inode list. */ | ||
| 35 | block_sector_t sector; /* Sector number of disk location. */ | ||
| 36 | int open_cnt; /* Number of openers. */ | ||
| 37 | bool removed; /* True if deleted, false otherwise. */ | ||
| 38 | int deny_write_cnt; /* 0: writes ok, >0: deny writes. */ | ||
| 39 | struct inode_disk data; /* Inode content. */ | ||
| 40 | }; | ||
| 41 | |||
| 42 | /* Returns the block device sector that contains byte offset POS | ||
| 43 | within INODE. | ||
| 44 | Returns -1 if INODE does not contain data for a byte at offset | ||
| 45 | POS. */ | ||
| 46 | static block_sector_t | ||
| 47 | byte_to_sector (const struct inode *inode, off_t pos) | ||
| 48 | { | ||
| 49 | ASSERT (inode != NULL); | ||
| 50 | if (pos < inode->data.length) | ||
| 51 | return inode->data.start + pos / BLOCK_SECTOR_SIZE; | ||
| 52 | else | ||
| 53 | return -1; | ||
| 54 | } | ||
| 55 | |||
| 56 | /* List of open inodes, so that opening a single inode twice | ||
| 57 | returns the same `struct inode'. */ | ||
| 58 | static struct list open_inodes; | ||
| 59 | |||
| 60 | /* Initializes the inode module. */ | ||
| 61 | void | ||
| 62 | inode_init (void) | ||
| 63 | { | ||
| 64 | list_init (&open_inodes); | ||
| 65 | } | ||
| 66 | |||
| 67 | /* Initializes an inode with LENGTH bytes of data and | ||
| 68 | writes the new inode to sector SECTOR on the file system | ||
| 69 | device. | ||
| 70 | Returns true if successful. | ||
| 71 | Returns false if memory or disk allocation fails. */ | ||
| 72 | bool | ||
| 73 | inode_create (block_sector_t sector, off_t length) | ||
| 74 | { | ||
| 75 | struct inode_disk *disk_inode = NULL; | ||
| 76 | bool success = false; | ||
| 77 | |||
| 78 | ASSERT (length >= 0); | ||
| 79 | |||
| 80 | /* If this assertion fails, the inode structure is not exactly | ||
| 81 | one sector in size, and you should fix that. */ | ||
| 82 | ASSERT (sizeof *disk_inode == BLOCK_SECTOR_SIZE); | ||
| 83 | |||
| 84 | disk_inode = calloc (1, sizeof *disk_inode); | ||
| 85 | if (disk_inode != NULL) | ||
| 86 | { | ||
| 87 | size_t sectors = bytes_to_sectors (length); | ||
| 88 | disk_inode->length = length; | ||
| 89 | disk_inode->magic = INODE_MAGIC; | ||
| 90 | if (free_map_allocate (sectors, &disk_inode->start)) | ||
| 91 | { | ||
| 92 | block_write (fs_device, sector, disk_inode); | ||
| 93 | if (sectors > 0) | ||
| 94 | { | ||
| 95 | static char zeros[BLOCK_SECTOR_SIZE]; | ||
| 96 | size_t i; | ||
| 97 | |||
| 98 | for (i = 0; i < sectors; i++) | ||
| 99 | block_write (fs_device, disk_inode->start + i, zeros); | ||
| 100 | } | ||
| 101 | success = true; | ||
| 102 | } | ||
| 103 | free (disk_inode); | ||
| 104 | } | ||
| 105 | return success; | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Reads an inode from SECTOR | ||
| 109 | and returns a `struct inode' that contains it. | ||
| 110 | Returns a null pointer if memory allocation fails. */ | ||
| 111 | struct inode * | ||
| 112 | inode_open (block_sector_t sector) | ||
| 113 | { | ||
| 114 | struct list_elem *e; | ||
| 115 | struct inode *inode; | ||
| 116 | |||
| 117 | /* Check whether this inode is already open. */ | ||
| 118 | for (e = list_begin (&open_inodes); e != list_end (&open_inodes); | ||
| 119 | e = list_next (e)) | ||
| 120 | { | ||
| 121 | inode = list_entry (e, struct inode, elem); | ||
| 122 | if (inode->sector == sector) | ||
| 123 | { | ||
| 124 | inode_reopen (inode); | ||
| 125 | return inode; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Allocate memory. */ | ||
| 130 | inode = malloc (sizeof *inode); | ||
| 131 | if (inode == NULL) | ||
| 132 | return NULL; | ||
| 133 | |||
| 134 | /* Initialize. */ | ||
| 135 | list_push_front (&open_inodes, &inode->elem); | ||
| 136 | inode->sector = sector; | ||
| 137 | inode->open_cnt = 1; | ||
| 138 | inode->deny_write_cnt = 0; | ||
| 139 | inode->removed = false; | ||
| 140 | block_read (fs_device, inode->sector, &inode->data); | ||
| 141 | return inode; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Reopens and returns INODE. */ | ||
| 145 | struct inode * | ||
| 146 | inode_reopen (struct inode *inode) | ||
| 147 | { | ||
| 148 | if (inode != NULL) | ||
| 149 | inode->open_cnt++; | ||
| 150 | return inode; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* Returns INODE's inode number. */ | ||
| 154 | block_sector_t | ||
| 155 | inode_get_inumber (const struct inode *inode) | ||
| 156 | { | ||
| 157 | return inode->sector; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Closes INODE and writes it to disk. | ||
| 161 | If this was the last reference to INODE, frees its memory. | ||
| 162 | If INODE was also a removed inode, frees its blocks. */ | ||
| 163 | void | ||
| 164 | inode_close (struct inode *inode) | ||
| 165 | { | ||
| 166 | /* Ignore null pointer. */ | ||
| 167 | if (inode == NULL) | ||
| 168 | return; | ||
| 169 | |||
| 170 | /* Release resources if this was the last opener. */ | ||
| 171 | if (--inode->open_cnt == 0) | ||
| 172 | { | ||
| 173 | /* Remove from inode list and release lock. */ | ||
| 174 | list_remove (&inode->elem); | ||
| 175 | |||
| 176 | /* Deallocate blocks if removed. */ | ||
| 177 | if (inode->removed) | ||
| 178 | { | ||
| 179 | free_map_release (inode->sector, 1); | ||
| 180 | free_map_release (inode->data.start, | ||
| 181 | bytes_to_sectors (inode->data.length)); | ||
| 182 | } | ||
| 183 | |||
| 184 | free (inode); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Marks INODE to be deleted when it is closed by the last caller who | ||
| 189 | has it open. */ | ||
| 190 | void | ||
| 191 | inode_remove (struct inode *inode) | ||
| 192 | { | ||
| 193 | ASSERT (inode != NULL); | ||
| 194 | inode->removed = true; | ||
| 195 | } | ||
| 196 | |||
| 197 | /* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET. | ||
| 198 | Returns the number of bytes actually read, which may be less | ||
| 199 | than SIZE if an error occurs or end of file is reached. */ | ||
| 200 | off_t | ||
| 201 | inode_read_at (struct inode *inode, void *buffer_, off_t size, off_t offset) | ||
| 202 | { | ||
| 203 | uint8_t *buffer = buffer_; | ||
| 204 | off_t bytes_read = 0; | ||
| 205 | uint8_t *bounce = NULL; | ||
| 206 | |||
| 207 | while (size > 0) | ||
| 208 | { | ||
| 209 | /* Disk sector to read, starting byte offset within sector. */ | ||
| 210 | block_sector_t sector_idx = byte_to_sector (inode, offset); | ||
| 211 | int sector_ofs = offset % BLOCK_SECTOR_SIZE; | ||
| 212 | |||
| 213 | /* Bytes left in inode, bytes left in sector, lesser of the two. */ | ||
| 214 | off_t inode_left = inode_length (inode) - offset; | ||
| 215 | int sector_left = BLOCK_SECTOR_SIZE - sector_ofs; | ||
| 216 | int min_left = inode_left < sector_left ? inode_left : sector_left; | ||
| 217 | |||
| 218 | /* Number of bytes to actually copy out of this sector. */ | ||
| 219 | int chunk_size = size < min_left ? size : min_left; | ||
| 220 | if (chunk_size <= 0) | ||
| 221 | break; | ||
| 222 | |||
| 223 | if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE) | ||
| 224 | { | ||
| 225 | /* Read full sector directly into caller's buffer. */ | ||
| 226 | block_read (fs_device, sector_idx, buffer + bytes_read); | ||
| 227 | } | ||
| 228 | else | ||
| 229 | { | ||
| 230 | /* Read sector into bounce buffer, then partially copy | ||
| 231 | into caller's buffer. */ | ||
| 232 | if (bounce == NULL) | ||
| 233 | { | ||
| 234 | bounce = malloc (BLOCK_SECTOR_SIZE); | ||
| 235 | if (bounce == NULL) | ||
| 236 | break; | ||
| 237 | } | ||
| 238 | block_read (fs_device, sector_idx, bounce); | ||
| 239 | memcpy (buffer + bytes_read, bounce + sector_ofs, chunk_size); | ||
| 240 | } | ||
| 241 | |||
| 242 | /* Advance. */ | ||
| 243 | size -= chunk_size; | ||
| 244 | offset += chunk_size; | ||
| 245 | bytes_read += chunk_size; | ||
| 246 | } | ||
| 247 | free (bounce); | ||
| 248 | |||
| 249 | return bytes_read; | ||
| 250 | } | ||
| 251 | |||
| 252 | /* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET. | ||
| 253 | Returns the number of bytes actually written, which may be | ||
| 254 | less than SIZE if end of file is reached or an error occurs. | ||
| 255 | (Normally a write at end of file would extend the inode, but | ||
| 256 | growth is not yet implemented.) */ | ||
| 257 | off_t | ||
| 258 | inode_write_at (struct inode *inode, const void *buffer_, off_t size, | ||
| 259 | off_t offset) | ||
| 260 | { | ||
| 261 | const uint8_t *buffer = buffer_; | ||
| 262 | off_t bytes_written = 0; | ||
| 263 | uint8_t *bounce = NULL; | ||
| 264 | |||
| 265 | if (inode->deny_write_cnt) | ||
| 266 | return 0; | ||
| 267 | |||
| 268 | while (size > 0) | ||
| 269 | { | ||
| 270 | /* Sector to write, starting byte offset within sector. */ | ||
| 271 | block_sector_t sector_idx = byte_to_sector (inode, offset); | ||
| 272 | int sector_ofs = offset % BLOCK_SECTOR_SIZE; | ||
| 273 | |||
| 274 | /* Bytes left in inode, bytes left in sector, lesser of the two. */ | ||
| 275 | off_t inode_left = inode_length (inode) - offset; | ||
| 276 | int sector_left = BLOCK_SECTOR_SIZE - sector_ofs; | ||
| 277 | int min_left = inode_left < sector_left ? inode_left : sector_left; | ||
| 278 | |||
| 279 | /* Number of bytes to actually write into this sector. */ | ||
| 280 | int chunk_size = size < min_left ? size : min_left; | ||
| 281 | if (chunk_size <= 0) | ||
| 282 | break; | ||
| 283 | |||
| 284 | if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE) | ||
| 285 | { | ||
| 286 | /* Write full sector directly to disk. */ | ||
| 287 | block_write (fs_device, sector_idx, buffer + bytes_written); | ||
| 288 | } | ||
| 289 | else | ||
| 290 | { | ||
| 291 | /* We need a bounce buffer. */ | ||
| 292 | if (bounce == NULL) | ||
| 293 | { | ||
| 294 | bounce = malloc (BLOCK_SECTOR_SIZE); | ||
| 295 | if (bounce == NULL) | ||
| 296 | break; | ||
| 297 | } | ||
| 298 | |||
| 299 | /* If the sector contains data before or after the chunk | ||
| 300 | we're writing, then we need to read in the sector | ||
| 301 | first. Otherwise we start with a sector of all zeros. */ | ||
| 302 | if (sector_ofs > 0 || chunk_size < sector_left) | ||
| 303 | block_read (fs_device, sector_idx, bounce); | ||
| 304 | else | ||
| 305 | memset (bounce, 0, BLOCK_SECTOR_SIZE); | ||
| 306 | memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size); | ||
| 307 | block_write (fs_device, sector_idx, bounce); | ||
| 308 | } | ||
| 309 | |||
| 310 | /* Advance. */ | ||
| 311 | size -= chunk_size; | ||
| 312 | offset += chunk_size; | ||
| 313 | bytes_written += chunk_size; | ||
| 314 | } | ||
| 315 | free (bounce); | ||
| 316 | |||
| 317 | return bytes_written; | ||
| 318 | } | ||
| 319 | |||
| 320 | /* Disables writes to INODE. | ||
| 321 | May be called at most once per inode opener. */ | ||
| 322 | void | ||
| 323 | inode_deny_write (struct inode *inode) | ||
| 324 | { | ||
| 325 | inode->deny_write_cnt++; | ||
| 326 | ASSERT (inode->deny_write_cnt <= inode->open_cnt); | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Re-enables writes to INODE. | ||
| 330 | Must be called once by each inode opener who has called | ||
| 331 | inode_deny_write() on the inode, before closing the inode. */ | ||
| 332 | void | ||
| 333 | inode_allow_write (struct inode *inode) | ||
| 334 | { | ||
| 335 | ASSERT (inode->deny_write_cnt > 0); | ||
| 336 | ASSERT (inode->deny_write_cnt <= inode->open_cnt); | ||
| 337 | inode->deny_write_cnt--; | ||
| 338 | } | ||
| 339 | |||
| 340 | /* Returns the length, in bytes, of INODE's data. */ | ||
| 341 | off_t | ||
| 342 | inode_length (const struct inode *inode) | ||
| 343 | { | ||
| 344 | return inode->data.length; | ||
| 345 | } | ||
diff --git a/pintos-progos/filesys/inode.h b/pintos-progos/filesys/inode.h new file mode 100644 index 0000000..cb42310 --- /dev/null +++ b/pintos-progos/filesys/inode.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #ifndef FILESYS_INODE_H | ||
| 2 | #define FILESYS_INODE_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include "filesys/off_t.h" | ||
| 6 | #include "devices/block.h" | ||
| 7 | |||
| 8 | struct bitmap; | ||
| 9 | |||
| 10 | void inode_init (void); | ||
| 11 | bool inode_create (block_sector_t, off_t); | ||
| 12 | struct inode *inode_open (block_sector_t); | ||
| 13 | struct inode *inode_reopen (struct inode *); | ||
| 14 | block_sector_t inode_get_inumber (const struct inode *); | ||
| 15 | void inode_close (struct inode *); | ||
| 16 | void inode_remove (struct inode *); | ||
| 17 | off_t inode_read_at (struct inode *, void *, off_t size, off_t offset); | ||
| 18 | off_t inode_write_at (struct inode *, const void *, off_t size, off_t offset); | ||
| 19 | void inode_deny_write (struct inode *); | ||
| 20 | void inode_allow_write (struct inode *); | ||
| 21 | off_t inode_length (const struct inode *); | ||
| 22 | |||
| 23 | #endif /* filesys/inode.h */ | ||
diff --git a/pintos-progos/filesys/off_t.h b/pintos-progos/filesys/off_t.h new file mode 100644 index 0000000..9caff4d --- /dev/null +++ b/pintos-progos/filesys/off_t.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef FILESYS_OFF_T_H | ||
| 2 | #define FILESYS_OFF_T_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | /* An offset within a file. | ||
| 7 | This is a separate header because multiple headers want this | ||
| 8 | definition but not any others. */ | ||
| 9 | typedef int32_t off_t; | ||
| 10 | |||
| 11 | /* Format specifier for printf(), e.g.: | ||
| 12 | printf ("offset=%"PROTd"\n", offset); */ | ||
| 13 | #define PROTd PRId32 | ||
| 14 | |||
| 15 | #endif /* filesys/off_t.h */ | ||
diff --git a/pintos-progos/intro/Make.vars b/pintos-progos/intro/Make.vars new file mode 100644 index 0000000..c612275 --- /dev/null +++ b/pintos-progos/intro/Make.vars | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = -DUSERPROG -DFILESYS | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys $(KERNEL_TESTS) | ||
| 5 | KERNEL_TESTS = tests/intro/alarm-clock | ||
| 6 | TEST_SUBDIRS = tests/intro/alarm-clock tests/intro/userprog-args | ||
| 7 | GRADING_FILE = $(SRCDIR)/tests/intro/Grading | ||
diff --git a/pintos-progos/intro/Makefile b/pintos-progos/intro/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/pintos-progos/intro/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
diff --git a/pintos-progos/lib/arithmetic.c b/pintos-progos/lib/arithmetic.c new file mode 100644 index 0000000..bfc9b5a --- /dev/null +++ b/pintos-progos/lib/arithmetic.c | |||
| @@ -0,0 +1,189 @@ | |||
| 1 | #include <stdint.h> | ||
| 2 | |||
| 3 | /* On x86, division of one 64-bit integer by another cannot be | ||
| 4 | done with a single instruction or a short sequence. Thus, GCC | ||
| 5 | implements 64-bit division and remainder operations through | ||
| 6 | function calls. These functions are normally obtained from | ||
| 7 | libgcc, which is automatically included by GCC in any link | ||
| 8 | that it does. | ||
| 9 | |||
| 10 | Some x86-64 machines, however, have a compiler and utilities | ||
| 11 | that can generate 32-bit x86 code without having any of the | ||
| 12 | necessary libraries, including libgcc. Thus, we can make | ||
| 13 | Pintos work on these machines by simply implementing our own | ||
| 14 | 64-bit division routines, which are the only routines from | ||
| 15 | libgcc that Pintos requires. | ||
| 16 | |||
| 17 | Completeness is another reason to include these routines. If | ||
| 18 | Pintos is completely self-contained, then that makes it that | ||
| 19 | much less mysterious. */ | ||
| 20 | |||
| 21 | /* Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to | ||
| 22 | yield a 32-bit quotient. Returns the quotient. | ||
| 23 | Traps with a divide error (#DE) if the quotient does not fit | ||
| 24 | in 32 bits. */ | ||
| 25 | static inline uint32_t | ||
| 26 | divl (uint64_t n, uint32_t d) | ||
| 27 | { | ||
| 28 | uint32_t n1 = n >> 32; | ||
| 29 | uint32_t n0 = n; | ||
| 30 | uint32_t q, r; | ||
| 31 | |||
| 32 | asm ("divl %4" | ||
| 33 | : "=d" (r), "=a" (q) | ||
| 34 | : "0" (n1), "1" (n0), "rm" (d)); | ||
| 35 | |||
| 36 | return q; | ||
| 37 | } | ||
| 38 | |||
| 39 | /* Returns the number of leading zero bits in X, | ||
| 40 | which must be nonzero. */ | ||
| 41 | static int | ||
| 42 | nlz (uint32_t x) | ||
| 43 | { | ||
| 44 | /* This technique is portable, but there are better ways to do | ||
| 45 | it on particular systems. With sufficiently new enough GCC, | ||
| 46 | you can use __builtin_clz() to take advantage of GCC's | ||
| 47 | knowledge of how to do it. Or you can use the x86 BSR | ||
| 48 | instruction directly. */ | ||
| 49 | int n = 0; | ||
| 50 | if (x <= 0x0000FFFF) | ||
| 51 | { | ||
| 52 | n += 16; | ||
| 53 | x <<= 16; | ||
| 54 | } | ||
| 55 | if (x <= 0x00FFFFFF) | ||
| 56 | { | ||
| 57 | n += 8; | ||
| 58 | x <<= 8; | ||
| 59 | } | ||
| 60 | if (x <= 0x0FFFFFFF) | ||
| 61 | { | ||
| 62 | n += 4; | ||
| 63 | x <<= 4; | ||
| 64 | } | ||
| 65 | if (x <= 0x3FFFFFFF) | ||
| 66 | { | ||
| 67 | n += 2; | ||
| 68 | x <<= 2; | ||
| 69 | } | ||
| 70 | if (x <= 0x7FFFFFFF) | ||
| 71 | n++; | ||
| 72 | return n; | ||
| 73 | } | ||
| 74 | |||
| 75 | /* Divides unsigned 64-bit N by unsigned 64-bit D and returns the | ||
| 76 | quotient. */ | ||
| 77 | static uint64_t | ||
| 78 | udiv64 (uint64_t n, uint64_t d) | ||
| 79 | { | ||
| 80 | if ((d >> 32) == 0) | ||
| 81 | { | ||
| 82 | /* Proof of correctness: | ||
| 83 | |||
| 84 | Let n, d, b, n1, and n0 be defined as in this function. | ||
| 85 | Let [x] be the "floor" of x. Let T = b[n1/d]. Assume d | ||
| 86 | nonzero. Then: | ||
| 87 | [n/d] = [n/d] - T + T | ||
| 88 | = [n/d - T] + T by (1) below | ||
| 89 | = [(b*n1 + n0)/d - T] + T by definition of n | ||
| 90 | = [(b*n1 + n0)/d - dT/d] + T | ||
| 91 | = [(b(n1 - d[n1/d]) + n0)/d] + T | ||
| 92 | = [(b[n1 % d] + n0)/d] + T, by definition of % | ||
| 93 | which is the expression calculated below. | ||
| 94 | |||
| 95 | (1) Note that for any real x, integer i: [x] + i = [x + i]. | ||
| 96 | |||
| 97 | To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must | ||
| 98 | be less than b. Assume that [n1 % d] and n0 take their | ||
| 99 | respective maximum values of d - 1 and b - 1: | ||
| 100 | [(b(d - 1) + (b - 1))/d] < b | ||
| 101 | <=> [(bd - 1)/d] < b | ||
| 102 | <=> [b - 1/d] < b | ||
| 103 | which is a tautology. | ||
| 104 | |||
| 105 | Therefore, this code is correct and will not trap. */ | ||
| 106 | uint64_t b = 1ULL << 32; | ||
| 107 | uint32_t n1 = n >> 32; | ||
| 108 | uint32_t n0 = n; | ||
| 109 | uint32_t d0 = d; | ||
| 110 | |||
| 111 | return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0); | ||
| 112 | } | ||
| 113 | else | ||
| 114 | { | ||
| 115 | /* Based on the algorithm and proof available from | ||
| 116 | http://www.hackersdelight.org/revisions.pdf. */ | ||
| 117 | if (n < d) | ||
| 118 | return 0; | ||
| 119 | else | ||
| 120 | { | ||
| 121 | uint32_t d1 = d >> 32; | ||
| 122 | int s = nlz (d1); | ||
| 123 | uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s); | ||
| 124 | return n - (q - 1) * d < d ? q - 1 : q; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Divides unsigned 64-bit N by unsigned 64-bit D and returns the | ||
| 130 | remainder. */ | ||
| 131 | static uint32_t | ||
| 132 | umod64 (uint64_t n, uint64_t d) | ||
| 133 | { | ||
| 134 | return n - d * udiv64 (n, d); | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Divides signed 64-bit N by signed 64-bit D and returns the | ||
| 138 | quotient. */ | ||
| 139 | static int64_t | ||
| 140 | sdiv64 (int64_t n, int64_t d) | ||
| 141 | { | ||
| 142 | uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n; | ||
| 143 | uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d; | ||
| 144 | uint64_t q_abs = udiv64 (n_abs, d_abs); | ||
| 145 | return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs; | ||
| 146 | } | ||
| 147 | |||
| 148 | /* Divides signed 64-bit N by signed 64-bit D and returns the | ||
| 149 | remainder. */ | ||
| 150 | static int32_t | ||
| 151 | smod64 (int64_t n, int64_t d) | ||
| 152 | { | ||
| 153 | return n - d * sdiv64 (n, d); | ||
| 154 | } | ||
| 155 | |||
| 156 | /* These are the routines that GCC calls. */ | ||
| 157 | |||
| 158 | long long __divdi3 (long long n, long long d); | ||
| 159 | long long __moddi3 (long long n, long long d); | ||
| 160 | unsigned long long __udivdi3 (unsigned long long n, unsigned long long d); | ||
| 161 | unsigned long long __umoddi3 (unsigned long long n, unsigned long long d); | ||
| 162 | |||
| 163 | /* Signed 64-bit division. */ | ||
| 164 | long long | ||
| 165 | __divdi3 (long long n, long long d) | ||
| 166 | { | ||
| 167 | return sdiv64 (n, d); | ||
| 168 | } | ||
| 169 | |||
| 170 | /* Signed 64-bit remainder. */ | ||
| 171 | long long | ||
| 172 | __moddi3 (long long n, long long d) | ||
| 173 | { | ||
| 174 | return smod64 (n, d); | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Unsigned 64-bit division. */ | ||
| 178 | unsigned long long | ||
| 179 | __udivdi3 (unsigned long long n, unsigned long long d) | ||
| 180 | { | ||
| 181 | return udiv64 (n, d); | ||
| 182 | } | ||
| 183 | |||
| 184 | /* Unsigned 64-bit remainder. */ | ||
| 185 | unsigned long long | ||
| 186 | __umoddi3 (unsigned long long n, unsigned long long d) | ||
| 187 | { | ||
| 188 | return umod64 (n, d); | ||
| 189 | } | ||
diff --git a/pintos-progos/lib/ctype.h b/pintos-progos/lib/ctype.h new file mode 100644 index 0000000..9096aca --- /dev/null +++ b/pintos-progos/lib/ctype.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | #ifndef __LIB_CTYPE_H | ||
| 2 | #define __LIB_CTYPE_H | ||
| 3 | |||
| 4 | static inline int islower (int c) { return c >= 'a' && c <= 'z'; } | ||
| 5 | static inline int isupper (int c) { return c >= 'A' && c <= 'Z'; } | ||
| 6 | static inline int isalpha (int c) { return islower (c) || isupper (c); } | ||
| 7 | static inline int isdigit (int c) { return c >= '0' && c <= '9'; } | ||
| 8 | static inline int isalnum (int c) { return isalpha (c) || isdigit (c); } | ||
| 9 | static inline int isxdigit (int c) { | ||
| 10 | return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); | ||
| 11 | } | ||
| 12 | static inline int isspace (int c) { | ||
| 13 | return (c == ' ' || c == '\f' || c == '\n' | ||
| 14 | || c == '\r' || c == '\t' || c == '\v'); | ||
| 15 | } | ||
| 16 | static inline int isblank (int c) { return c == ' ' || c == '\t'; } | ||
| 17 | static inline int isgraph (int c) { return c > 32 && c < 127; } | ||
| 18 | static inline int isprint (int c) { return c >= 32 && c < 127; } | ||
| 19 | static inline int iscntrl (int c) { return (c >= 0 && c < 32) || c == 127; } | ||
| 20 | static inline int isascii (int c) { return c >= 0 && c < 128; } | ||
| 21 | static inline int ispunct (int c) { | ||
| 22 | return isprint (c) && !isalnum (c) && !isspace (c); | ||
| 23 | } | ||
| 24 | |||
| 25 | static inline int tolower (int c) { return isupper (c) ? c - 'A' + 'a' : c; } | ||
| 26 | static inline int toupper (int c) { return islower (c) ? c - 'a' + 'A' : c; } | ||
| 27 | |||
| 28 | #endif /* lib/ctype.h */ | ||
diff --git a/pintos-progos/lib/debug.c b/pintos-progos/lib/debug.c new file mode 100644 index 0000000..b4f8c2d --- /dev/null +++ b/pintos-progos/lib/debug.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | #include <debug.h> | ||
| 2 | #include <stdarg.h> | ||
| 3 | #include <stdbool.h> | ||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <string.h> | ||
| 7 | |||
| 8 | /* Prints the call stack, that is, a list of addresses, one in | ||
| 9 | each of the functions we are nested within. gdb or addr2line | ||
| 10 | may be applied to kernel.o to translate these into file names, | ||
| 11 | line numbers, and function names. */ | ||
| 12 | void | ||
| 13 | debug_backtrace (void) | ||
| 14 | { | ||
| 15 | static bool explained; | ||
| 16 | void **frame; | ||
| 17 | |||
| 18 | printf ("Call stack: %p", __builtin_return_address (0)); | ||
| 19 | for (frame = __builtin_frame_address (1); | ||
| 20 | (uintptr_t) frame >= 0x1000 && frame[0] != NULL; | ||
| 21 | frame = frame[0]) | ||
| 22 | printf (" %p", frame[1]); | ||
| 23 | printf (".\n"); | ||
| 24 | |||
| 25 | if (!explained) | ||
| 26 | { | ||
| 27 | explained = true; | ||
| 28 | printf ("The `backtrace' program can make call stacks useful.\n" | ||
| 29 | "Read \"Backtraces\" in the \"Debugging Tools\" chapter\n" | ||
| 30 | "of the Pintos documentation for more information.\n"); | ||
| 31 | } | ||
| 32 | } | ||
diff --git a/pintos-progos/lib/debug.h b/pintos-progos/lib/debug.h new file mode 100644 index 0000000..888ab7b --- /dev/null +++ b/pintos-progos/lib/debug.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #ifndef __LIB_DEBUG_H | ||
| 2 | #define __LIB_DEBUG_H | ||
| 3 | |||
| 4 | /* GCC lets us add "attributes" to functions, function | ||
| 5 | parameters, etc. to indicate their properties. | ||
| 6 | See the GCC manual for details. */ | ||
| 7 | #define UNUSED __attribute__ ((unused)) | ||
| 8 | #define NO_RETURN __attribute__ ((noreturn)) | ||
| 9 | #define NO_INLINE __attribute__ ((noinline)) | ||
| 10 | #define PRINTF_FORMAT(FMT, FIRST) __attribute__ ((format (printf, FMT, FIRST))) | ||
| 11 | |||
| 12 | /* Halts the OS, printing the source file name, line number, and | ||
| 13 | function name, plus a user-specific message. */ | ||
| 14 | #define PANIC(...) debug_panic (__FILE__, __LINE__, __func__, __VA_ARGS__) | ||
| 15 | |||
| 16 | void debug_panic (const char *file, int line, const char *function, | ||
| 17 | const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN; | ||
| 18 | void debug_backtrace (void); | ||
| 19 | void debug_backtrace_all (void); | ||
| 20 | |||
| 21 | #endif | ||
| 22 | |||
| 23 | |||
| 24 | |||
| 25 | /* This is outside the header guard so that debug.h may be | ||
| 26 | included multiple times with different settings of NDEBUG. */ | ||
| 27 | #undef ASSERT | ||
| 28 | #undef NOT_REACHED | ||
| 29 | |||
| 30 | #ifndef NDEBUG | ||
| 31 | #define ASSERT(CONDITION) \ | ||
| 32 | if (CONDITION) { } else { \ | ||
| 33 | PANIC ("assertion `%s' failed.", #CONDITION); \ | ||
| 34 | } | ||
| 35 | #define NOT_REACHED() PANIC ("executed an unreachable statement"); | ||
| 36 | #else | ||
| 37 | #define ASSERT(CONDITION) ((void) 0) | ||
| 38 | #define NOT_REACHED() for (;;) | ||
| 39 | #endif /* lib/debug.h */ | ||
diff --git a/pintos-progos/lib/inttypes.h b/pintos-progos/lib/inttypes.h new file mode 100644 index 0000000..f703725 --- /dev/null +++ b/pintos-progos/lib/inttypes.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | #ifndef __LIB_INTTYPES_H | ||
| 2 | #define __LIB_INTTYPES_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | #define PRId8 "hhd" | ||
| 7 | #define PRIi8 "hhi" | ||
| 8 | #define PRIo8 "hho" | ||
| 9 | #define PRIu8 "hhu" | ||
| 10 | #define PRIx8 "hhx" | ||
| 11 | #define PRIX8 "hhX" | ||
| 12 | |||
| 13 | #define PRId16 "hd" | ||
| 14 | #define PRIi16 "hi" | ||
| 15 | #define PRIo16 "ho" | ||
| 16 | #define PRIu16 "hu" | ||
| 17 | #define PRIx16 "hx" | ||
| 18 | #define PRIX16 "hX" | ||
| 19 | |||
| 20 | #define PRId32 "d" | ||
| 21 | #define PRIi32 "i" | ||
| 22 | #define PRIo32 "o" | ||
| 23 | #define PRIu32 "u" | ||
| 24 | #define PRIx32 "x" | ||
| 25 | #define PRIX32 "X" | ||
| 26 | |||
| 27 | #define PRId64 "lld" | ||
| 28 | #define PRIi64 "lli" | ||
| 29 | #define PRIo64 "llo" | ||
| 30 | #define PRIu64 "llu" | ||
| 31 | #define PRIx64 "llx" | ||
| 32 | #define PRIX64 "llX" | ||
| 33 | |||
| 34 | #define PRIdMAX "jd" | ||
| 35 | #define PRIiMAX "ji" | ||
| 36 | #define PRIoMAX "jo" | ||
| 37 | #define PRIuMAX "ju" | ||
| 38 | #define PRIxMAX "jx" | ||
| 39 | #define PRIXMAX "jX" | ||
| 40 | |||
| 41 | #define PRIdPTR "td" | ||
| 42 | #define PRIiPTR "ti" | ||
| 43 | #define PRIoPTR "to" | ||
| 44 | #define PRIuPTR "tu" | ||
| 45 | #define PRIxPTR "tx" | ||
| 46 | #define PRIXPTR "tX" | ||
| 47 | |||
| 48 | #endif /* lib/inttypes.h */ | ||
diff --git a/pintos-progos/lib/kernel/bitmap.c b/pintos-progos/lib/kernel/bitmap.c new file mode 100644 index 0000000..d14a98c --- /dev/null +++ b/pintos-progos/lib/kernel/bitmap.c | |||
| @@ -0,0 +1,371 @@ | |||
| 1 | #include "bitmap.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <limits.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "threads/malloc.h" | ||
| 7 | #ifdef FILESYS | ||
| 8 | #include "filesys/file.h" | ||
| 9 | #endif | ||
| 10 | |||
| 11 | /* Element type. | ||
| 12 | |||
| 13 | This must be an unsigned integer type at least as wide as int. | ||
| 14 | |||
| 15 | Each bit represents one bit in the bitmap. | ||
| 16 | If bit 0 in an element represents bit K in the bitmap, | ||
| 17 | then bit 1 in the element represents bit K+1 in the bitmap, | ||
| 18 | and so on. */ | ||
| 19 | typedef unsigned long elem_type; | ||
| 20 | |||
| 21 | /* Number of bits in an element. */ | ||
| 22 | #define ELEM_BITS (sizeof (elem_type) * CHAR_BIT) | ||
| 23 | |||
| 24 | /* From the outside, a bitmap is an array of bits. From the | ||
| 25 | inside, it's an array of elem_type (defined above) that | ||
| 26 | simulates an array of bits. */ | ||
| 27 | struct bitmap | ||
| 28 | { | ||
| 29 | size_t bit_cnt; /* Number of bits. */ | ||
| 30 | elem_type *bits; /* Elements that represent bits. */ | ||
| 31 | }; | ||
| 32 | |||
| 33 | /* Returns the index of the element that contains the bit | ||
| 34 | numbered BIT_IDX. */ | ||
| 35 | static inline size_t | ||
| 36 | elem_idx (size_t bit_idx) | ||
| 37 | { | ||
| 38 | return bit_idx / ELEM_BITS; | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Returns an elem_type where only the bit corresponding to | ||
| 42 | BIT_IDX is turned on. */ | ||
| 43 | static inline elem_type | ||
| 44 | bit_mask (size_t bit_idx) | ||
| 45 | { | ||
| 46 | return (elem_type) 1 << (bit_idx % ELEM_BITS); | ||
| 47 | } | ||
| 48 | |||
| 49 | /* Returns the number of elements required for BIT_CNT bits. */ | ||
| 50 | static inline size_t | ||
| 51 | elem_cnt (size_t bit_cnt) | ||
| 52 | { | ||
| 53 | return DIV_ROUND_UP (bit_cnt, ELEM_BITS); | ||
| 54 | } | ||
| 55 | |||
| 56 | /* Returns the number of bytes required for BIT_CNT bits. */ | ||
| 57 | static inline size_t | ||
| 58 | byte_cnt (size_t bit_cnt) | ||
| 59 | { | ||
| 60 | return sizeof (elem_type) * elem_cnt (bit_cnt); | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Returns a bit mask in which the bits actually used in the last | ||
| 64 | element of B's bits are set to 1 and the rest are set to 0. */ | ||
| 65 | static inline elem_type | ||
| 66 | last_mask (const struct bitmap *b) | ||
| 67 | { | ||
| 68 | int last_bits = b->bit_cnt % ELEM_BITS; | ||
| 69 | return last_bits ? ((elem_type) 1 << last_bits) - 1 : (elem_type) -1; | ||
| 70 | } | ||
| 71 | |||
| 72 | /* Creation and destruction. */ | ||
| 73 | |||
| 74 | /* Creates and returns a pointer to a newly allocated bitmap with room for | ||
| 75 | BIT_CNT (or more) bits. Returns a null pointer if memory allocation fails. | ||
| 76 | The caller is responsible for freeing the bitmap, with bitmap_destroy(), | ||
| 77 | when it is no longer needed. */ | ||
| 78 | struct bitmap * | ||
| 79 | bitmap_create (size_t bit_cnt) | ||
| 80 | { | ||
| 81 | struct bitmap *b = malloc (sizeof *b); | ||
| 82 | if (b != NULL) | ||
| 83 | { | ||
| 84 | b->bit_cnt = bit_cnt; | ||
| 85 | b->bits = malloc (byte_cnt (bit_cnt)); | ||
| 86 | if (b->bits != NULL || bit_cnt == 0) | ||
| 87 | { | ||
| 88 | bitmap_set_all (b, false); | ||
| 89 | return b; | ||
| 90 | } | ||
| 91 | free (b); | ||
| 92 | } | ||
| 93 | return NULL; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Creates and returns a bitmap with BIT_CNT bits in the | ||
| 97 | BLOCK_SIZE bytes of storage preallocated at BLOCK. | ||
| 98 | BLOCK_SIZE must be at least bitmap_needed_bytes(BIT_CNT). */ | ||
| 99 | struct bitmap * | ||
| 100 | bitmap_create_in_buf (size_t bit_cnt, void *block, size_t block_size UNUSED) | ||
| 101 | { | ||
| 102 | struct bitmap *b = block; | ||
| 103 | |||
| 104 | ASSERT (block_size >= bitmap_buf_size (bit_cnt)); | ||
| 105 | |||
| 106 | b->bit_cnt = bit_cnt; | ||
| 107 | b->bits = (elem_type *) (b + 1); | ||
| 108 | bitmap_set_all (b, false); | ||
| 109 | return b; | ||
| 110 | } | ||
| 111 | |||
| 112 | /* Returns the number of bytes required to accomodate a bitmap | ||
| 113 | with BIT_CNT bits (for use with bitmap_create_in_buf()). */ | ||
| 114 | size_t | ||
| 115 | bitmap_buf_size (size_t bit_cnt) | ||
| 116 | { | ||
| 117 | return sizeof (struct bitmap) + byte_cnt (bit_cnt); | ||
| 118 | } | ||
| 119 | |||
| 120 | /* Destroys bitmap B, freeing its storage. | ||
| 121 | Not for use on bitmaps created by bitmap_create_in_buf(). */ | ||
| 122 | void | ||
| 123 | bitmap_destroy (struct bitmap *b) | ||
| 124 | { | ||
| 125 | if (b != NULL) | ||
| 126 | { | ||
| 127 | free (b->bits); | ||
| 128 | free (b); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /* Bitmap size. */ | ||
| 133 | |||
| 134 | /* Returns the number of bits in B. */ | ||
| 135 | size_t | ||
| 136 | bitmap_size (const struct bitmap *b) | ||
| 137 | { | ||
| 138 | return b->bit_cnt; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Setting and testing single bits. */ | ||
| 142 | |||
| 143 | /* Atomically sets the bit numbered IDX in B to VALUE. */ | ||
| 144 | void | ||
| 145 | bitmap_set (struct bitmap *b, size_t idx, bool value) | ||
| 146 | { | ||
| 147 | ASSERT (b != NULL); | ||
| 148 | ASSERT (idx < b->bit_cnt); | ||
| 149 | if (value) | ||
| 150 | bitmap_mark (b, idx); | ||
| 151 | else | ||
| 152 | bitmap_reset (b, idx); | ||
| 153 | } | ||
| 154 | |||
| 155 | /* Atomically sets the bit numbered BIT_IDX in B to true. */ | ||
| 156 | void | ||
| 157 | bitmap_mark (struct bitmap *b, size_t bit_idx) | ||
| 158 | { | ||
| 159 | size_t idx = elem_idx (bit_idx); | ||
| 160 | elem_type mask = bit_mask (bit_idx); | ||
| 161 | |||
| 162 | /* This is equivalent to `b->bits[idx] |= mask' except that it | ||
| 163 | is guaranteed to be atomic on a uniprocessor machine. See | ||
| 164 | the description of the OR instruction in [IA32-v2b]. */ | ||
| 165 | asm ("orl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc"); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Atomically sets the bit numbered BIT_IDX in B to false. */ | ||
| 169 | void | ||
| 170 | bitmap_reset (struct bitmap *b, size_t bit_idx) | ||
| 171 | { | ||
| 172 | size_t idx = elem_idx (bit_idx); | ||
| 173 | elem_type mask = bit_mask (bit_idx); | ||
| 174 | |||
| 175 | /* This is equivalent to `b->bits[idx] &= ~mask' except that it | ||
| 176 | is guaranteed to be atomic on a uniprocessor machine. See | ||
| 177 | the description of the AND instruction in [IA32-v2a]. */ | ||
| 178 | asm ("andl %1, %0" : "=m" (b->bits[idx]) : "r" (~mask) : "cc"); | ||
| 179 | } | ||
| 180 | |||
| 181 | /* Atomically toggles the bit numbered IDX in B; | ||
| 182 | that is, if it is true, makes it false, | ||
| 183 | and if it is false, makes it true. */ | ||
| 184 | void | ||
| 185 | bitmap_flip (struct bitmap *b, size_t bit_idx) | ||
| 186 | { | ||
| 187 | size_t idx = elem_idx (bit_idx); | ||
| 188 | elem_type mask = bit_mask (bit_idx); | ||
| 189 | |||
| 190 | /* This is equivalent to `b->bits[idx] ^= mask' except that it | ||
| 191 | is guaranteed to be atomic on a uniprocessor machine. See | ||
| 192 | the description of the XOR instruction in [IA32-v2b]. */ | ||
| 193 | asm ("xorl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc"); | ||
| 194 | } | ||
| 195 | |||
| 196 | /* Returns the value of the bit numbered IDX in B. */ | ||
| 197 | bool | ||
| 198 | bitmap_test (const struct bitmap *b, size_t idx) | ||
| 199 | { | ||
| 200 | ASSERT (b != NULL); | ||
| 201 | ASSERT (idx < b->bit_cnt); | ||
| 202 | return (b->bits[elem_idx (idx)] & bit_mask (idx)) != 0; | ||
| 203 | } | ||
| 204 | |||
| 205 | /* Setting and testing multiple bits. */ | ||
| 206 | |||
| 207 | /* Sets all bits in B to VALUE. */ | ||
| 208 | void | ||
| 209 | bitmap_set_all (struct bitmap *b, bool value) | ||
| 210 | { | ||
| 211 | ASSERT (b != NULL); | ||
| 212 | |||
| 213 | bitmap_set_multiple (b, 0, bitmap_size (b), value); | ||
| 214 | } | ||
| 215 | |||
| 216 | /* Sets the CNT bits starting at START in B to VALUE. */ | ||
| 217 | void | ||
| 218 | bitmap_set_multiple (struct bitmap *b, size_t start, size_t cnt, bool value) | ||
| 219 | { | ||
| 220 | size_t i; | ||
| 221 | |||
| 222 | ASSERT (b != NULL); | ||
| 223 | ASSERT (start <= b->bit_cnt); | ||
| 224 | ASSERT (start + cnt <= b->bit_cnt); | ||
| 225 | |||
| 226 | for (i = 0; i < cnt; i++) | ||
| 227 | bitmap_set (b, start + i, value); | ||
| 228 | } | ||
| 229 | |||
| 230 | /* Returns the number of bits in B between START and START + CNT, | ||
| 231 | exclusive, that are set to VALUE. */ | ||
| 232 | size_t | ||
| 233 | bitmap_count (const struct bitmap *b, size_t start, size_t cnt, bool value) | ||
| 234 | { | ||
| 235 | size_t i, value_cnt; | ||
| 236 | |||
| 237 | ASSERT (b != NULL); | ||
| 238 | ASSERT (start <= b->bit_cnt); | ||
| 239 | ASSERT (start + cnt <= b->bit_cnt); | ||
| 240 | |||
| 241 | value_cnt = 0; | ||
| 242 | for (i = 0; i < cnt; i++) | ||
| 243 | if (bitmap_test (b, start + i) == value) | ||
| 244 | value_cnt++; | ||
| 245 | return value_cnt; | ||
| 246 | } | ||
| 247 | |||
| 248 | /* Returns true if any bits in B between START and START + CNT, | ||
| 249 | exclusive, are set to VALUE, and false otherwise. */ | ||
| 250 | bool | ||
| 251 | bitmap_contains (const struct bitmap *b, size_t start, size_t cnt, bool value) | ||
| 252 | { | ||
| 253 | size_t i; | ||
| 254 | |||
| 255 | ASSERT (b != NULL); | ||
| 256 | ASSERT (start <= b->bit_cnt); | ||
| 257 | ASSERT (start + cnt <= b->bit_cnt); | ||
| 258 | |||
| 259 | for (i = 0; i < cnt; i++) | ||
| 260 | if (bitmap_test (b, start + i) == value) | ||
| 261 | return true; | ||
| 262 | return false; | ||
| 263 | } | ||
| 264 | |||
| 265 | /* Returns true if any bits in B between START and START + CNT, | ||
| 266 | exclusive, are set to true, and false otherwise.*/ | ||
| 267 | bool | ||
| 268 | bitmap_any (const struct bitmap *b, size_t start, size_t cnt) | ||
| 269 | { | ||
| 270 | return bitmap_contains (b, start, cnt, true); | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Returns true if no bits in B between START and START + CNT, | ||
| 274 | exclusive, are set to true, and false otherwise.*/ | ||
| 275 | bool | ||
| 276 | bitmap_none (const struct bitmap *b, size_t start, size_t cnt) | ||
| 277 | { | ||
| 278 | return !bitmap_contains (b, start, cnt, true); | ||
| 279 | } | ||
| 280 | |||
| 281 | /* Returns true if every bit in B between START and START + CNT, | ||
| 282 | exclusive, is set to true, and false otherwise. */ | ||
| 283 | bool | ||
| 284 | bitmap_all (const struct bitmap *b, size_t start, size_t cnt) | ||
| 285 | { | ||
| 286 | return !bitmap_contains (b, start, cnt, false); | ||
| 287 | } | ||
| 288 | |||
| 289 | /* Finding set or unset bits. */ | ||
| 290 | |||
| 291 | /* Finds and returns the starting index of the first group of CNT | ||
| 292 | consecutive bits in B at or after START that are all set to | ||
| 293 | VALUE. | ||
| 294 | If there is no such group, returns BITMAP_ERROR. */ | ||
| 295 | size_t | ||
| 296 | bitmap_scan (const struct bitmap *b, size_t start, size_t cnt, bool value) | ||
| 297 | { | ||
| 298 | ASSERT (b != NULL); | ||
| 299 | ASSERT (start <= b->bit_cnt); | ||
| 300 | |||
| 301 | if (cnt <= b->bit_cnt) | ||
| 302 | { | ||
| 303 | size_t last = b->bit_cnt - cnt; | ||
| 304 | size_t i; | ||
| 305 | for (i = start; i <= last; i++) | ||
| 306 | if (!bitmap_contains (b, i, cnt, !value)) | ||
| 307 | return i; | ||
| 308 | } | ||
| 309 | return BITMAP_ERROR; | ||
| 310 | } | ||
| 311 | |||
| 312 | /* Finds the first group of CNT consecutive bits in B at or after | ||
| 313 | START that are all set to VALUE, flips them all to !VALUE, | ||
| 314 | and returns the index of the first bit in the group. | ||
| 315 | If there is no such group, returns BITMAP_ERROR. | ||
| 316 | If CNT is zero, returns 0. | ||
| 317 | Bits are set atomically, but testing bits is not atomic with | ||
| 318 | setting them. */ | ||
| 319 | size_t | ||
| 320 | bitmap_scan_and_flip (struct bitmap *b, size_t start, size_t cnt, bool value) | ||
| 321 | { | ||
| 322 | size_t idx = bitmap_scan (b, start, cnt, value); | ||
| 323 | if (idx != BITMAP_ERROR) | ||
| 324 | bitmap_set_multiple (b, idx, cnt, !value); | ||
| 325 | return idx; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* File input and output. */ | ||
| 329 | |||
| 330 | #ifdef FILESYS | ||
| 331 | /* Returns the number of bytes needed to store B in a file. */ | ||
| 332 | size_t | ||
| 333 | bitmap_file_size (const struct bitmap *b) | ||
| 334 | { | ||
| 335 | return byte_cnt (b->bit_cnt); | ||
| 336 | } | ||
| 337 | |||
| 338 | /* Reads B from FILE. Returns true if successful, false | ||
| 339 | otherwise. */ | ||
| 340 | bool | ||
| 341 | bitmap_read (struct bitmap *b, struct file *file) | ||
| 342 | { | ||
| 343 | bool success = true; | ||
| 344 | if (b->bit_cnt > 0) | ||
| 345 | { | ||
| 346 | off_t size = byte_cnt (b->bit_cnt); | ||
| 347 | success = file_read_at (file, b->bits, size, 0) == size; | ||
| 348 | b->bits[elem_cnt (b->bit_cnt) - 1] &= last_mask (b); | ||
| 349 | } | ||
| 350 | return success; | ||
| 351 | } | ||
| 352 | |||
| 353 | /* Writes B to FILE. Return true if successful, false | ||
| 354 | otherwise. */ | ||
| 355 | bool | ||
| 356 | bitmap_write (const struct bitmap *b, struct file *file) | ||
| 357 | { | ||
| 358 | off_t size = byte_cnt (b->bit_cnt); | ||
| 359 | return file_write_at (file, b->bits, size, 0) == size; | ||
| 360 | } | ||
| 361 | #endif /* FILESYS */ | ||
| 362 | |||
| 363 | /* Debugging. */ | ||
| 364 | |||
| 365 | /* Dumps the contents of B to the console as hexadecimal. */ | ||
| 366 | void | ||
| 367 | bitmap_dump (const struct bitmap *b) | ||
| 368 | { | ||
| 369 | hex_dump (0, b->bits, byte_cnt (b->bit_cnt), false); | ||
| 370 | } | ||
| 371 | |||
diff --git a/pintos-progos/lib/kernel/bitmap.h b/pintos-progos/lib/kernel/bitmap.h new file mode 100644 index 0000000..a50593c --- /dev/null +++ b/pintos-progos/lib/kernel/bitmap.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | #ifndef __LIB_KERNEL_BITMAP_H | ||
| 2 | #define __LIB_KERNEL_BITMAP_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | #include <inttypes.h> | ||
| 7 | |||
| 8 | /* Bitmap abstract data type. */ | ||
| 9 | |||
| 10 | /* Creation and destruction. */ | ||
| 11 | struct bitmap *bitmap_create (size_t bit_cnt); | ||
| 12 | struct bitmap *bitmap_create_in_buf (size_t bit_cnt, void *, size_t byte_cnt); | ||
| 13 | size_t bitmap_buf_size (size_t bit_cnt); | ||
| 14 | void bitmap_destroy (struct bitmap *); | ||
| 15 | |||
| 16 | /* Bitmap size. */ | ||
| 17 | size_t bitmap_size (const struct bitmap *); | ||
| 18 | |||
| 19 | /* Setting and testing single bits. */ | ||
| 20 | void bitmap_set (struct bitmap *, size_t idx, bool); | ||
| 21 | void bitmap_mark (struct bitmap *, size_t idx); | ||
| 22 | void bitmap_reset (struct bitmap *, size_t idx); | ||
| 23 | void bitmap_flip (struct bitmap *, size_t idx); | ||
| 24 | bool bitmap_test (const struct bitmap *, size_t idx); | ||
| 25 | |||
| 26 | /* Setting and testing multiple bits. */ | ||
| 27 | void bitmap_set_all (struct bitmap *, bool); | ||
| 28 | void bitmap_set_multiple (struct bitmap *, size_t start, size_t cnt, bool); | ||
| 29 | size_t bitmap_count (const struct bitmap *, size_t start, size_t cnt, bool); | ||
| 30 | bool bitmap_contains (const struct bitmap *, size_t start, size_t cnt, bool); | ||
| 31 | bool bitmap_any (const struct bitmap *, size_t start, size_t cnt); | ||
| 32 | bool bitmap_none (const struct bitmap *, size_t start, size_t cnt); | ||
| 33 | bool bitmap_all (const struct bitmap *, size_t start, size_t cnt); | ||
| 34 | |||
| 35 | /* Finding set or unset bits. */ | ||
| 36 | #define BITMAP_ERROR SIZE_MAX | ||
| 37 | size_t bitmap_scan (const struct bitmap *, size_t start, size_t cnt, bool); | ||
| 38 | size_t bitmap_scan_and_flip (struct bitmap *, size_t start, size_t cnt, bool); | ||
| 39 | |||
| 40 | /* File input and output. */ | ||
| 41 | #ifdef FILESYS | ||
| 42 | struct file; | ||
| 43 | size_t bitmap_file_size (const struct bitmap *); | ||
| 44 | bool bitmap_read (struct bitmap *, struct file *); | ||
| 45 | bool bitmap_write (const struct bitmap *, struct file *); | ||
| 46 | #endif | ||
| 47 | |||
| 48 | /* Debugging. */ | ||
| 49 | void bitmap_dump (const struct bitmap *); | ||
| 50 | |||
| 51 | #endif /* lib/kernel/bitmap.h */ | ||
diff --git a/pintos-progos/lib/kernel/console.c b/pintos-progos/lib/kernel/console.c new file mode 100644 index 0000000..844b184 --- /dev/null +++ b/pintos-progos/lib/kernel/console.c | |||
| @@ -0,0 +1,191 @@ | |||
| 1 | #include <console.h> | ||
| 2 | #include <stdarg.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include "devices/serial.h" | ||
| 5 | #include "devices/vga.h" | ||
| 6 | #include "threads/init.h" | ||
| 7 | #include "threads/interrupt.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | |||
| 10 | static void vprintf_helper (char, void *); | ||
| 11 | static void putchar_have_lock (uint8_t c); | ||
| 12 | |||
| 13 | /* The console lock. | ||
| 14 | Both the vga and serial layers do their own locking, so it's | ||
| 15 | safe to call them at any time. | ||
| 16 | But this lock is useful to prevent simultaneous printf() calls | ||
| 17 | from mixing their output, which looks confusing. */ | ||
| 18 | static struct lock console_lock; | ||
| 19 | |||
| 20 | /* True in ordinary circumstances: we want to use the console | ||
| 21 | lock to avoid mixing output between threads, as explained | ||
| 22 | above. | ||
| 23 | |||
| 24 | False in early boot before the point that locks are functional | ||
| 25 | or the console lock has been initialized, or after a kernel | ||
| 26 | panics. In the former case, taking the lock would cause an | ||
| 27 | assertion failure, which in turn would cause a panic, turning | ||
| 28 | it into the latter case. In the latter case, if it is a buggy | ||
| 29 | lock_acquire() implementation that caused the panic, we'll | ||
| 30 | likely just recurse. */ | ||
| 31 | static bool use_console_lock; | ||
| 32 | |||
| 33 | /* It's possible, if you add enough debug output to Pintos, to | ||
| 34 | try to recursively grab console_lock from a single thread. As | ||
| 35 | a real example, I added a printf() call to palloc_free(). | ||
| 36 | Here's a real backtrace that resulted: | ||
| 37 | |||
| 38 | lock_console() | ||
| 39 | vprintf() | ||
| 40 | printf() - palloc() tries to grab the lock again | ||
| 41 | palloc_free() | ||
| 42 | thread_schedule_tail() - another thread dying as we switch threads | ||
| 43 | schedule() | ||
| 44 | thread_yield() | ||
| 45 | intr_handler() - timer interrupt | ||
| 46 | intr_set_level() | ||
| 47 | serial_putc() | ||
| 48 | putchar_have_lock() | ||
| 49 | putbuf() | ||
| 50 | sys_write() - one process writing to the console | ||
| 51 | syscall_handler() | ||
| 52 | intr_handler() | ||
| 53 | |||
| 54 | This kind of thing is very difficult to debug, so we avoid the | ||
| 55 | problem by simulating a recursive lock with a depth | ||
| 56 | counter. */ | ||
| 57 | static int console_lock_depth; | ||
| 58 | |||
| 59 | /* Number of characters written to console. */ | ||
| 60 | static int64_t write_cnt; | ||
| 61 | |||
| 62 | /* Enable console locking. */ | ||
| 63 | void | ||
| 64 | console_init (void) | ||
| 65 | { | ||
| 66 | lock_init (&console_lock); | ||
| 67 | use_console_lock = true; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Notifies the console that a kernel panic is underway, | ||
| 71 | which warns it to avoid trying to take the console lock from | ||
| 72 | now on. */ | ||
| 73 | void | ||
| 74 | console_panic (void) | ||
| 75 | { | ||
| 76 | use_console_lock = false; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Prints console statistics. */ | ||
| 80 | void | ||
| 81 | console_print_stats (void) | ||
| 82 | { | ||
| 83 | printf ("Console: %lld characters output\n", write_cnt); | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Acquires the console lock. */ | ||
| 87 | static void | ||
| 88 | acquire_console (void) | ||
| 89 | { | ||
| 90 | if (!intr_context () && use_console_lock) | ||
| 91 | { | ||
| 92 | if (lock_held_by_current_thread (&console_lock)) | ||
| 93 | console_lock_depth++; | ||
| 94 | else | ||
| 95 | lock_acquire (&console_lock); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Releases the console lock. */ | ||
| 100 | static void | ||
| 101 | release_console (void) | ||
| 102 | { | ||
| 103 | if (!intr_context () && use_console_lock) | ||
| 104 | { | ||
| 105 | if (console_lock_depth > 0) | ||
| 106 | console_lock_depth--; | ||
| 107 | else | ||
| 108 | lock_release (&console_lock); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /* Returns true if the current thread has the console lock, | ||
| 113 | false otherwise. */ | ||
| 114 | static bool | ||
| 115 | console_locked_by_current_thread (void) | ||
| 116 | { | ||
| 117 | return (intr_context () | ||
| 118 | || !use_console_lock | ||
| 119 | || lock_held_by_current_thread (&console_lock)); | ||
| 120 | } | ||
| 121 | |||
| 122 | /* The standard vprintf() function, | ||
| 123 | which is like printf() but uses a va_list. | ||
| 124 | Writes its output to both vga display and serial port. */ | ||
| 125 | int | ||
| 126 | vprintf (const char *format, va_list args) | ||
| 127 | { | ||
| 128 | int char_cnt = 0; | ||
| 129 | |||
| 130 | acquire_console (); | ||
| 131 | __vprintf (format, args, vprintf_helper, &char_cnt); | ||
| 132 | release_console (); | ||
| 133 | |||
| 134 | return char_cnt; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Writes string S to the console, followed by a new-line | ||
| 138 | character. */ | ||
| 139 | int | ||
| 140 | puts (const char *s) | ||
| 141 | { | ||
| 142 | acquire_console (); | ||
| 143 | while (*s != '\0') | ||
| 144 | putchar_have_lock (*s++); | ||
| 145 | putchar_have_lock ('\n'); | ||
| 146 | release_console (); | ||
| 147 | |||
| 148 | return 0; | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Writes the N characters in BUFFER to the console. */ | ||
| 152 | void | ||
| 153 | putbuf (const char *buffer, size_t n) | ||
| 154 | { | ||
| 155 | acquire_console (); | ||
| 156 | while (n-- > 0) | ||
| 157 | putchar_have_lock (*buffer++); | ||
| 158 | release_console (); | ||
| 159 | } | ||
| 160 | |||
| 161 | /* Writes C to the vga display and serial port. */ | ||
| 162 | int | ||
| 163 | putchar (int c) | ||
| 164 | { | ||
| 165 | acquire_console (); | ||
| 166 | putchar_have_lock (c); | ||
| 167 | release_console (); | ||
| 168 | |||
| 169 | return c; | ||
| 170 | } | ||
| 171 | |||
| 172 | /* Helper function for vprintf(). */ | ||
| 173 | static void | ||
| 174 | vprintf_helper (char c, void *char_cnt_) | ||
| 175 | { | ||
| 176 | int *char_cnt = char_cnt_; | ||
| 177 | (*char_cnt)++; | ||
| 178 | putchar_have_lock (c); | ||
| 179 | } | ||
| 180 | |||
| 181 | /* Writes C to the vga display and serial port. | ||
| 182 | The caller has already acquired the console lock if | ||
| 183 | appropriate. */ | ||
| 184 | static void | ||
| 185 | putchar_have_lock (uint8_t c) | ||
| 186 | { | ||
| 187 | ASSERT (console_locked_by_current_thread ()); | ||
| 188 | write_cnt++; | ||
| 189 | serial_putc (c); | ||
| 190 | vga_putc (c); | ||
| 191 | } | ||
diff --git a/pintos-progos/lib/kernel/console.h b/pintos-progos/lib/kernel/console.h new file mode 100644 index 0000000..ab99249 --- /dev/null +++ b/pintos-progos/lib/kernel/console.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef __LIB_KERNEL_CONSOLE_H | ||
| 2 | #define __LIB_KERNEL_CONSOLE_H | ||
| 3 | |||
| 4 | void console_init (void); | ||
| 5 | void console_panic (void); | ||
| 6 | void console_print_stats (void); | ||
| 7 | |||
| 8 | #endif /* lib/kernel/console.h */ | ||
diff --git a/pintos-progos/lib/kernel/debug.c b/pintos-progos/lib/kernel/debug.c new file mode 100644 index 0000000..b12f4f9 --- /dev/null +++ b/pintos-progos/lib/kernel/debug.c | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | #include <debug.h> | ||
| 2 | #include <console.h> | ||
| 3 | #include <stdarg.h> | ||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include "threads/init.h" | ||
| 9 | #include "threads/interrupt.h" | ||
| 10 | #include "threads/thread.h" | ||
| 11 | #include "threads/switch.h" | ||
| 12 | #include "threads/vaddr.h" | ||
| 13 | #include "devices/serial.h" | ||
| 14 | #include "devices/shutdown.h" | ||
| 15 | |||
| 16 | /* Halts the OS, printing the source file name, line number, and | ||
| 17 | function name, plus a user-specific message. */ | ||
| 18 | void | ||
| 19 | debug_panic (const char *file, int line, const char *function, | ||
| 20 | const char *message, ...) | ||
| 21 | { | ||
| 22 | static int level; | ||
| 23 | va_list args; | ||
| 24 | |||
| 25 | intr_disable (); | ||
| 26 | console_panic (); | ||
| 27 | |||
| 28 | level++; | ||
| 29 | if (level == 1) | ||
| 30 | { | ||
| 31 | printf ("Kernel PANIC at %s:%d in %s(): ", file, line, function); | ||
| 32 | |||
| 33 | va_start (args, message); | ||
| 34 | vprintf (message, args); | ||
| 35 | printf ("\n"); | ||
| 36 | va_end (args); | ||
| 37 | |||
| 38 | debug_backtrace (); | ||
| 39 | } | ||
| 40 | else if (level == 2) | ||
| 41 | printf ("Kernel PANIC recursion at %s:%d in %s().\n", | ||
| 42 | file, line, function); | ||
| 43 | else | ||
| 44 | { | ||
| 45 | /* Don't print anything: that's probably why we recursed. */ | ||
| 46 | } | ||
| 47 | |||
| 48 | serial_flush (); | ||
| 49 | shutdown (); | ||
| 50 | for (;;); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Print call stack of a thread. | ||
| 54 | The thread may be running, ready, or blocked. */ | ||
| 55 | static void | ||
| 56 | print_stacktrace(struct thread *t, void *aux UNUSED) | ||
| 57 | { | ||
| 58 | void *retaddr = NULL, **frame = NULL; | ||
| 59 | const char *status = "UNKNOWN"; | ||
| 60 | |||
| 61 | switch (t->status) { | ||
| 62 | case THREAD_RUNNING: | ||
| 63 | status = "RUNNING"; | ||
| 64 | break; | ||
| 65 | |||
| 66 | case THREAD_READY: | ||
| 67 | status = "READY"; | ||
| 68 | break; | ||
| 69 | |||
| 70 | case THREAD_BLOCKED: | ||
| 71 | status = "BLOCKED"; | ||
| 72 | break; | ||
| 73 | |||
| 74 | default: | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | |||
| 78 | printf ("Call stack of thread `%s' (status %s):", t->name, status); | ||
| 79 | |||
| 80 | if (t == thread_current()) | ||
| 81 | { | ||
| 82 | frame = __builtin_frame_address (1); | ||
| 83 | retaddr = __builtin_return_address (0); | ||
| 84 | } | ||
| 85 | else | ||
| 86 | { | ||
| 87 | /* Retrieve the values of the base and instruction pointers | ||
| 88 | as they were saved when this thread called switch_threads. */ | ||
| 89 | struct switch_threads_frame * saved_frame; | ||
| 90 | |||
| 91 | saved_frame = (struct switch_threads_frame *)t->stack; | ||
| 92 | |||
| 93 | /* Skip threads if they have been added to the all threads | ||
| 94 | list, but have never been scheduled. | ||
| 95 | We can identify because their `stack' member either points | ||
| 96 | at the top of their kernel stack page, or the | ||
| 97 | switch_threads_frame's 'eip' member points at switch_entry. | ||
| 98 | See also threads.c. */ | ||
| 99 | if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry) | ||
| 100 | { | ||
| 101 | printf (" thread was never scheduled.\n"); | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | frame = (void **) saved_frame->ebp; | ||
| 106 | retaddr = (void *) saved_frame->eip; | ||
| 107 | } | ||
| 108 | |||
| 109 | printf (" %p", retaddr); | ||
| 110 | for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0]) | ||
| 111 | printf (" %p", frame[1]); | ||
| 112 | printf (".\n"); | ||
| 113 | } | ||
| 114 | |||
| 115 | /* Prints call stack of all threads. */ | ||
| 116 | void | ||
| 117 | debug_backtrace_all (void) | ||
| 118 | { | ||
| 119 | enum intr_level oldlevel = intr_disable (); | ||
| 120 | |||
| 121 | thread_foreach (print_stacktrace, 0); | ||
| 122 | intr_set_level (oldlevel); | ||
| 123 | } | ||
diff --git a/pintos-progos/lib/kernel/hash.c b/pintos-progos/lib/kernel/hash.c new file mode 100644 index 0000000..57eed45 --- /dev/null +++ b/pintos-progos/lib/kernel/hash.c | |||
| @@ -0,0 +1,430 @@ | |||
| 1 | /* Hash table. | ||
| 2 | |||
| 3 | This data structure is thoroughly documented in the Tour of | ||
| 4 | Pintos for Project 3. | ||
| 5 | |||
| 6 | See hash.h for basic information. */ | ||
| 7 | |||
| 8 | #include "hash.h" | ||
| 9 | #include "../debug.h" | ||
| 10 | #include "threads/malloc.h" | ||
| 11 | |||
| 12 | #define list_elem_to_hash_elem(LIST_ELEM) \ | ||
| 13 | list_entry(LIST_ELEM, struct hash_elem, list_elem) | ||
| 14 | |||
| 15 | static struct list *find_bucket (struct hash *, struct hash_elem *); | ||
| 16 | static struct hash_elem *find_elem (struct hash *, struct list *, | ||
| 17 | struct hash_elem *); | ||
| 18 | static void insert_elem (struct hash *, struct list *, struct hash_elem *); | ||
| 19 | static void remove_elem (struct hash *, struct hash_elem *); | ||
| 20 | static void rehash (struct hash *); | ||
| 21 | |||
| 22 | /* Initializes hash table H to compute hash values using HASH and | ||
| 23 | compare hash elements using LESS, given auxiliary data AUX. */ | ||
| 24 | bool | ||
| 25 | hash_init (struct hash *h, | ||
| 26 | hash_hash_func *hash, hash_less_func *less, void *aux) | ||
| 27 | { | ||
| 28 | h->elem_cnt = 0; | ||
| 29 | h->bucket_cnt = 4; | ||
| 30 | h->buckets = malloc (sizeof *h->buckets * h->bucket_cnt); | ||
| 31 | h->hash = hash; | ||
| 32 | h->less = less; | ||
| 33 | h->aux = aux; | ||
| 34 | |||
| 35 | if (h->buckets != NULL) | ||
| 36 | { | ||
| 37 | hash_clear (h, NULL); | ||
| 38 | return true; | ||
| 39 | } | ||
| 40 | else | ||
| 41 | return false; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Removes all the elements from H. | ||
| 45 | |||
| 46 | If DESTRUCTOR is non-null, then it is called for each element | ||
| 47 | in the hash. DESTRUCTOR may, if appropriate, deallocate the | ||
| 48 | memory used by the hash element. However, modifying hash | ||
| 49 | table H while hash_clear() is running, using any of the | ||
| 50 | functions hash_clear(), hash_destroy(), hash_insert(), | ||
| 51 | hash_replace(), or hash_delete(), yields undefined behavior, | ||
| 52 | whether done in DESTRUCTOR or elsewhere. */ | ||
| 53 | void | ||
| 54 | hash_clear (struct hash *h, hash_action_func *destructor) | ||
| 55 | { | ||
| 56 | size_t i; | ||
| 57 | |||
| 58 | for (i = 0; i < h->bucket_cnt; i++) | ||
| 59 | { | ||
| 60 | struct list *bucket = &h->buckets[i]; | ||
| 61 | |||
| 62 | if (destructor != NULL) | ||
| 63 | while (!list_empty (bucket)) | ||
| 64 | { | ||
| 65 | struct list_elem *list_elem = list_pop_front (bucket); | ||
| 66 | struct hash_elem *hash_elem = list_elem_to_hash_elem (list_elem); | ||
| 67 | destructor (hash_elem, h->aux); | ||
| 68 | } | ||
| 69 | |||
| 70 | list_init (bucket); | ||
| 71 | } | ||
| 72 | |||
| 73 | h->elem_cnt = 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Destroys hash table H. | ||
| 77 | |||
| 78 | If DESTRUCTOR is non-null, then it is first called for each | ||
| 79 | element in the hash. DESTRUCTOR may, if appropriate, | ||
| 80 | deallocate the memory used by the hash element. However, | ||
| 81 | modifying hash table H while hash_clear() is running, using | ||
| 82 | any of the functions hash_clear(), hash_destroy(), | ||
| 83 | hash_insert(), hash_replace(), or hash_delete(), yields | ||
| 84 | undefined behavior, whether done in DESTRUCTOR or | ||
| 85 | elsewhere. */ | ||
| 86 | void | ||
| 87 | hash_destroy (struct hash *h, hash_action_func *destructor) | ||
| 88 | { | ||
| 89 | if (destructor != NULL) | ||
| 90 | hash_clear (h, destructor); | ||
| 91 | free (h->buckets); | ||
| 92 | } | ||
| 93 | |||
| 94 | /* Inserts NEW into hash table H and returns a null pointer, if | ||
| 95 | no equal element is already in the table. | ||
| 96 | If an equal element is already in the table, returns it | ||
| 97 | without inserting NEW. */ | ||
| 98 | struct hash_elem * | ||
| 99 | hash_insert (struct hash *h, struct hash_elem *new) | ||
| 100 | { | ||
| 101 | struct list *bucket = find_bucket (h, new); | ||
| 102 | struct hash_elem *old = find_elem (h, bucket, new); | ||
| 103 | |||
| 104 | if (old == NULL) | ||
| 105 | insert_elem (h, bucket, new); | ||
| 106 | |||
| 107 | rehash (h); | ||
| 108 | |||
| 109 | return old; | ||
| 110 | } | ||
| 111 | |||
| 112 | /* Inserts NEW into hash table H, replacing any equal element | ||
| 113 | already in the table, which is returned. */ | ||
| 114 | struct hash_elem * | ||
| 115 | hash_replace (struct hash *h, struct hash_elem *new) | ||
| 116 | { | ||
| 117 | struct list *bucket = find_bucket (h, new); | ||
| 118 | struct hash_elem *old = find_elem (h, bucket, new); | ||
| 119 | |||
| 120 | if (old != NULL) | ||
| 121 | remove_elem (h, old); | ||
| 122 | insert_elem (h, bucket, new); | ||
| 123 | |||
| 124 | rehash (h); | ||
| 125 | |||
| 126 | return old; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Finds and returns an element equal to E in hash table H, or a | ||
| 130 | null pointer if no equal element exists in the table. */ | ||
| 131 | struct hash_elem * | ||
| 132 | hash_find (struct hash *h, struct hash_elem *e) | ||
| 133 | { | ||
| 134 | return find_elem (h, find_bucket (h, e), e); | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Finds, removes, and returns an element equal to E in hash | ||
| 138 | table H. Returns a null pointer if no equal element existed | ||
| 139 | in the table. | ||
| 140 | |||
| 141 | If the elements of the hash table are dynamically allocated, | ||
| 142 | or own resources that are, then it is the caller's | ||
| 143 | responsibility to deallocate them. */ | ||
| 144 | struct hash_elem * | ||
| 145 | hash_delete (struct hash *h, struct hash_elem *e) | ||
| 146 | { | ||
| 147 | struct hash_elem *found = find_elem (h, find_bucket (h, e), e); | ||
| 148 | if (found != NULL) | ||
| 149 | { | ||
| 150 | remove_elem (h, found); | ||
| 151 | rehash (h); | ||
| 152 | } | ||
| 153 | return found; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Calls ACTION for each element in hash table H in arbitrary | ||
| 157 | order. | ||
| 158 | Modifying hash table H while hash_apply() is running, using | ||
| 159 | any of the functions hash_clear(), hash_destroy(), | ||
| 160 | hash_insert(), hash_replace(), or hash_delete(), yields | ||
| 161 | undefined behavior, whether done from ACTION or elsewhere. */ | ||
| 162 | void | ||
| 163 | hash_apply (struct hash *h, hash_action_func *action) | ||
| 164 | { | ||
| 165 | size_t i; | ||
| 166 | |||
| 167 | ASSERT (action != NULL); | ||
| 168 | |||
| 169 | for (i = 0; i < h->bucket_cnt; i++) | ||
| 170 | { | ||
| 171 | struct list *bucket = &h->buckets[i]; | ||
| 172 | struct list_elem *elem, *next; | ||
| 173 | |||
| 174 | for (elem = list_begin (bucket); elem != list_end (bucket); elem = next) | ||
| 175 | { | ||
| 176 | next = list_next (elem); | ||
| 177 | action (list_elem_to_hash_elem (elem), h->aux); | ||
| 178 | } | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | /* Initializes I for iterating hash table H. | ||
| 183 | |||
| 184 | Iteration idiom: | ||
| 185 | |||
| 186 | struct hash_iterator i; | ||
| 187 | |||
| 188 | hash_first (&i, h); | ||
| 189 | while (hash_next (&i)) | ||
| 190 | { | ||
| 191 | struct foo *f = hash_entry (hash_cur (&i), struct foo, elem); | ||
| 192 | ...do something with f... | ||
| 193 | } | ||
| 194 | |||
| 195 | Modifying hash table H during iteration, using any of the | ||
| 196 | functions hash_clear(), hash_destroy(), hash_insert(), | ||
| 197 | hash_replace(), or hash_delete(), invalidates all | ||
| 198 | iterators. */ | ||
| 199 | void | ||
| 200 | hash_first (struct hash_iterator *i, struct hash *h) | ||
| 201 | { | ||
| 202 | ASSERT (i != NULL); | ||
| 203 | ASSERT (h != NULL); | ||
| 204 | |||
| 205 | i->hash = h; | ||
| 206 | i->bucket = i->hash->buckets; | ||
| 207 | i->elem = list_elem_to_hash_elem (list_head (i->bucket)); | ||
| 208 | } | ||
| 209 | |||
| 210 | /* Advances I to the next element in the hash table and returns | ||
| 211 | it. Returns a null pointer if no elements are left. Elements | ||
| 212 | are returned in arbitrary order. | ||
| 213 | |||
| 214 | Modifying a hash table H during iteration, using any of the | ||
| 215 | functions hash_clear(), hash_destroy(), hash_insert(), | ||
| 216 | hash_replace(), or hash_delete(), invalidates all | ||
| 217 | iterators. */ | ||
| 218 | struct hash_elem * | ||
| 219 | hash_next (struct hash_iterator *i) | ||
| 220 | { | ||
| 221 | ASSERT (i != NULL); | ||
| 222 | |||
| 223 | i->elem = list_elem_to_hash_elem (list_next (&i->elem->list_elem)); | ||
| 224 | while (i->elem == list_elem_to_hash_elem (list_end (i->bucket))) | ||
| 225 | { | ||
| 226 | if (++i->bucket >= i->hash->buckets + i->hash->bucket_cnt) | ||
| 227 | { | ||
| 228 | i->elem = NULL; | ||
| 229 | break; | ||
| 230 | } | ||
| 231 | i->elem = list_elem_to_hash_elem (list_begin (i->bucket)); | ||
| 232 | } | ||
| 233 | |||
| 234 | return i->elem; | ||
| 235 | } | ||
| 236 | |||
| 237 | /* Returns the current element in the hash table iteration, or a | ||
| 238 | null pointer at the end of the table. Undefined behavior | ||
| 239 | after calling hash_first() but before hash_next(). */ | ||
| 240 | struct hash_elem * | ||
| 241 | hash_cur (struct hash_iterator *i) | ||
| 242 | { | ||
| 243 | return i->elem; | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Returns the number of elements in H. */ | ||
| 247 | size_t | ||
| 248 | hash_size (struct hash *h) | ||
| 249 | { | ||
| 250 | return h->elem_cnt; | ||
| 251 | } | ||
| 252 | |||
| 253 | /* Returns true if H contains no elements, false otherwise. */ | ||
| 254 | bool | ||
| 255 | hash_empty (struct hash *h) | ||
| 256 | { | ||
| 257 | return h->elem_cnt == 0; | ||
| 258 | } | ||
| 259 | |||
| 260 | /* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */ | ||
| 261 | #define FNV_32_PRIME 16777619u | ||
| 262 | #define FNV_32_BASIS 2166136261u | ||
| 263 | |||
| 264 | /* Returns a hash of the SIZE bytes in BUF. */ | ||
| 265 | unsigned | ||
| 266 | hash_bytes (const void *buf_, size_t size) | ||
| 267 | { | ||
| 268 | /* Fowler-Noll-Vo 32-bit hash, for bytes. */ | ||
| 269 | const unsigned char *buf = buf_; | ||
| 270 | unsigned hash; | ||
| 271 | |||
| 272 | ASSERT (buf != NULL); | ||
| 273 | |||
| 274 | hash = FNV_32_BASIS; | ||
| 275 | while (size-- > 0) | ||
| 276 | hash = (hash * FNV_32_PRIME) ^ *buf++; | ||
| 277 | |||
| 278 | return hash; | ||
| 279 | } | ||
| 280 | |||
| 281 | /* Returns a hash of string S. */ | ||
| 282 | unsigned | ||
| 283 | hash_string (const char *s_) | ||
| 284 | { | ||
| 285 | const unsigned char *s = (const unsigned char *) s_; | ||
| 286 | unsigned hash; | ||
| 287 | |||
| 288 | ASSERT (s != NULL); | ||
| 289 | |||
| 290 | hash = FNV_32_BASIS; | ||
| 291 | while (*s != '\0') | ||
| 292 | hash = (hash * FNV_32_PRIME) ^ *s++; | ||
| 293 | |||
| 294 | return hash; | ||
| 295 | } | ||
| 296 | |||
| 297 | /* Returns a hash of integer I. */ | ||
| 298 | unsigned | ||
| 299 | hash_int (int i) | ||
| 300 | { | ||
| 301 | return hash_bytes (&i, sizeof i); | ||
| 302 | } | ||
| 303 | |||
| 304 | /* Returns the bucket in H that E belongs in. */ | ||
| 305 | static struct list * | ||
| 306 | find_bucket (struct hash *h, struct hash_elem *e) | ||
| 307 | { | ||
| 308 | size_t bucket_idx = h->hash (e, h->aux) & (h->bucket_cnt - 1); | ||
| 309 | return &h->buckets[bucket_idx]; | ||
| 310 | } | ||
| 311 | |||
| 312 | /* Searches BUCKET in H for a hash element equal to E. Returns | ||
| 313 | it if found or a null pointer otherwise. */ | ||
| 314 | static struct hash_elem * | ||
| 315 | find_elem (struct hash *h, struct list *bucket, struct hash_elem *e) | ||
| 316 | { | ||
| 317 | struct list_elem *i; | ||
| 318 | |||
| 319 | for (i = list_begin (bucket); i != list_end (bucket); i = list_next (i)) | ||
| 320 | { | ||
| 321 | struct hash_elem *hi = list_elem_to_hash_elem (i); | ||
| 322 | if (!h->less (hi, e, h->aux) && !h->less (e, hi, h->aux)) | ||
| 323 | return hi; | ||
| 324 | } | ||
| 325 | return NULL; | ||
| 326 | } | ||
| 327 | |||
| 328 | /* Returns X with its lowest-order bit set to 1 turned off. */ | ||
| 329 | static inline size_t | ||
| 330 | turn_off_least_1bit (size_t x) | ||
| 331 | { | ||
| 332 | return x & (x - 1); | ||
| 333 | } | ||
| 334 | |||
| 335 | /* Returns true if X is a power of 2, otherwise false. */ | ||
| 336 | static inline size_t | ||
| 337 | is_power_of_2 (size_t x) | ||
| 338 | { | ||
| 339 | return x != 0 && turn_off_least_1bit (x) == 0; | ||
| 340 | } | ||
| 341 | |||
| 342 | /* Element per bucket ratios. */ | ||
| 343 | #define MIN_ELEMS_PER_BUCKET 1 /* Elems/bucket < 1: reduce # of buckets. */ | ||
| 344 | #define BEST_ELEMS_PER_BUCKET 2 /* Ideal elems/bucket. */ | ||
| 345 | #define MAX_ELEMS_PER_BUCKET 4 /* Elems/bucket > 4: increase # of buckets. */ | ||
| 346 | |||
| 347 | /* Changes the number of buckets in hash table H to match the | ||
| 348 | ideal. This function can fail because of an out-of-memory | ||
| 349 | condition, but that'll just make hash accesses less efficient; | ||
| 350 | we can still continue. */ | ||
| 351 | static void | ||
| 352 | rehash (struct hash *h) | ||
| 353 | { | ||
| 354 | size_t old_bucket_cnt, new_bucket_cnt; | ||
| 355 | struct list *new_buckets, *old_buckets; | ||
| 356 | size_t i; | ||
| 357 | |||
| 358 | ASSERT (h != NULL); | ||
| 359 | |||
| 360 | /* Save old bucket info for later use. */ | ||
| 361 | old_buckets = h->buckets; | ||
| 362 | old_bucket_cnt = h->bucket_cnt; | ||
| 363 | |||
| 364 | /* Calculate the number of buckets to use now. | ||
| 365 | We want one bucket for about every BEST_ELEMS_PER_BUCKET. | ||
| 366 | We must have at least four buckets, and the number of | ||
| 367 | buckets must be a power of 2. */ | ||
| 368 | new_bucket_cnt = h->elem_cnt / BEST_ELEMS_PER_BUCKET; | ||
| 369 | if (new_bucket_cnt < 4) | ||
| 370 | new_bucket_cnt = 4; | ||
| 371 | while (!is_power_of_2 (new_bucket_cnt)) | ||
| 372 | new_bucket_cnt = turn_off_least_1bit (new_bucket_cnt); | ||
| 373 | |||
| 374 | /* Don't do anything if the bucket count wouldn't change. */ | ||
| 375 | if (new_bucket_cnt == old_bucket_cnt) | ||
| 376 | return; | ||
| 377 | |||
| 378 | /* Allocate new buckets and initialize them as empty. */ | ||
| 379 | new_buckets = malloc (sizeof *new_buckets * new_bucket_cnt); | ||
| 380 | if (new_buckets == NULL) | ||
| 381 | { | ||
| 382 | /* Allocation failed. This means that use of the hash table will | ||
| 383 | be less efficient. However, it is still usable, so | ||
| 384 | there's no reason for it to be an error. */ | ||
| 385 | return; | ||
| 386 | } | ||
| 387 | for (i = 0; i < new_bucket_cnt; i++) | ||
| 388 | list_init (&new_buckets[i]); | ||
| 389 | |||
| 390 | /* Install new bucket info. */ | ||
| 391 | h->buckets = new_buckets; | ||
| 392 | h->bucket_cnt = new_bucket_cnt; | ||
| 393 | |||
| 394 | /* Move each old element into the appropriate new bucket. */ | ||
| 395 | for (i = 0; i < old_bucket_cnt; i++) | ||
| 396 | { | ||
| 397 | struct list *old_bucket; | ||
| 398 | struct list_elem *elem, *next; | ||
| 399 | |||
| 400 | old_bucket = &old_buckets[i]; | ||
| 401 | for (elem = list_begin (old_bucket); | ||
| 402 | elem != list_end (old_bucket); elem = next) | ||
| 403 | { | ||
| 404 | struct list *new_bucket | ||
| 405 | = find_bucket (h, list_elem_to_hash_elem (elem)); | ||
| 406 | next = list_next (elem); | ||
| 407 | list_remove (elem); | ||
| 408 | list_push_front (new_bucket, elem); | ||
| 409 | } | ||
| 410 | } | ||
| 411 | |||
| 412 | free (old_buckets); | ||
| 413 | } | ||
| 414 | |||
| 415 | /* Inserts E into BUCKET (in hash table H). */ | ||
| 416 | static void | ||
| 417 | insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e) | ||
| 418 | { | ||
| 419 | h->elem_cnt++; | ||
| 420 | list_push_front (bucket, &e->list_elem); | ||
| 421 | } | ||
| 422 | |||
| 423 | /* Removes E from hash table H. */ | ||
| 424 | static void | ||
| 425 | remove_elem (struct hash *h, struct hash_elem *e) | ||
| 426 | { | ||
| 427 | h->elem_cnt--; | ||
| 428 | list_remove (&e->list_elem); | ||
| 429 | } | ||
| 430 | |||
diff --git a/pintos-progos/lib/kernel/hash.h b/pintos-progos/lib/kernel/hash.h new file mode 100644 index 0000000..db9f674 --- /dev/null +++ b/pintos-progos/lib/kernel/hash.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | #ifndef __LIB_KERNEL_HASH_H | ||
| 2 | #define __LIB_KERNEL_HASH_H | ||
| 3 | |||
| 4 | /* Hash table. | ||
| 5 | |||
| 6 | This data structure is thoroughly documented in the Tour of | ||
| 7 | Pintos for Project 3. | ||
| 8 | |||
| 9 | This is a standard hash table with chaining. To locate an | ||
| 10 | element in the table, we compute a hash function over the | ||
| 11 | element's data and use that as an index into an array of | ||
| 12 | doubly linked lists, then linearly search the list. | ||
| 13 | |||
| 14 | The chain lists do not use dynamic allocation. Instead, each | ||
| 15 | structure that can potentially be in a hash must embed a | ||
| 16 | struct hash_elem member. All of the hash functions operate on | ||
| 17 | these `struct hash_elem's. The hash_entry macro allows | ||
| 18 | conversion from a struct hash_elem back to a structure object | ||
| 19 | that contains it. This is the same technique used in the | ||
| 20 | linked list implementation. Refer to lib/kernel/list.h for a | ||
| 21 | detailed explanation. */ | ||
| 22 | |||
| 23 | #include <stdbool.h> | ||
| 24 | #include <stddef.h> | ||
| 25 | #include <stdint.h> | ||
| 26 | #include "list.h" | ||
| 27 | |||
| 28 | /* Hash element. */ | ||
| 29 | struct hash_elem | ||
| 30 | { | ||
| 31 | struct list_elem list_elem; | ||
| 32 | }; | ||
| 33 | |||
| 34 | /* Converts pointer to hash element HASH_ELEM into a pointer to | ||
| 35 | the structure that HASH_ELEM is embedded inside. Supply the | ||
| 36 | name of the outer structure STRUCT and the member name MEMBER | ||
| 37 | of the hash element. See the big comment at the top of the | ||
| 38 | file for an example. */ | ||
| 39 | #define hash_entry(HASH_ELEM, STRUCT, MEMBER) \ | ||
| 40 | ((STRUCT *) ((uint8_t *) &(HASH_ELEM)->list_elem \ | ||
| 41 | - offsetof (STRUCT, MEMBER.list_elem))) | ||
| 42 | |||
| 43 | /* Computes and returns the hash value for hash element E, given | ||
| 44 | auxiliary data AUX. */ | ||
| 45 | typedef unsigned hash_hash_func (const struct hash_elem *e, void *aux); | ||
| 46 | |||
| 47 | /* Compares the value of two hash elements A and B, given | ||
| 48 | auxiliary data AUX. Returns true if A is less than B, or | ||
| 49 | false if A is greater than or equal to B. */ | ||
| 50 | typedef bool hash_less_func (const struct hash_elem *a, | ||
| 51 | const struct hash_elem *b, | ||
| 52 | void *aux); | ||
| 53 | |||
| 54 | /* Performs some operation on hash element E, given auxiliary | ||
| 55 | data AUX. */ | ||
| 56 | typedef void hash_action_func (struct hash_elem *e, void *aux); | ||
| 57 | |||
| 58 | /* Hash table. */ | ||
| 59 | struct hash | ||
| 60 | { | ||
| 61 | size_t elem_cnt; /* Number of elements in table. */ | ||
| 62 | size_t bucket_cnt; /* Number of buckets, a power of 2. */ | ||
| 63 | struct list *buckets; /* Array of `bucket_cnt' lists. */ | ||
| 64 | hash_hash_func *hash; /* Hash function. */ | ||
| 65 | hash_less_func *less; /* Comparison function. */ | ||
| 66 | void *aux; /* Auxiliary data for `hash' and `less'. */ | ||
| 67 | }; | ||
| 68 | |||
| 69 | /* A hash table iterator. */ | ||
| 70 | struct hash_iterator | ||
| 71 | { | ||
| 72 | struct hash *hash; /* The hash table. */ | ||
| 73 | struct list *bucket; /* Current bucket. */ | ||
| 74 | struct hash_elem *elem; /* Current hash element in current bucket. */ | ||
| 75 | }; | ||
| 76 | |||
| 77 | /* Basic life cycle. */ | ||
| 78 | bool hash_init (struct hash *, hash_hash_func *, hash_less_func *, void *aux); | ||
| 79 | void hash_clear (struct hash *, hash_action_func *); | ||
| 80 | void hash_destroy (struct hash *, hash_action_func *); | ||
| 81 | |||
| 82 | /* Search, insertion, deletion. */ | ||
| 83 | struct hash_elem *hash_insert (struct hash *, struct hash_elem *); | ||
| 84 | struct hash_elem *hash_replace (struct hash *, struct hash_elem *); | ||
| 85 | struct hash_elem *hash_find (struct hash *, struct hash_elem *); | ||
| 86 | struct hash_elem *hash_delete (struct hash *, struct hash_elem *); | ||
| 87 | |||
| 88 | /* Iteration. */ | ||
| 89 | void hash_apply (struct hash *, hash_action_func *); | ||
| 90 | void hash_first (struct hash_iterator *, struct hash *); | ||
| 91 | struct hash_elem *hash_next (struct hash_iterator *); | ||
| 92 | struct hash_elem *hash_cur (struct hash_iterator *); | ||
| 93 | |||
| 94 | /* Information. */ | ||
| 95 | size_t hash_size (struct hash *); | ||
| 96 | bool hash_empty (struct hash *); | ||
| 97 | |||
| 98 | /* Sample hash functions. */ | ||
| 99 | unsigned hash_bytes (const void *, size_t); | ||
| 100 | unsigned hash_string (const char *); | ||
| 101 | unsigned hash_int (int); | ||
| 102 | |||
| 103 | #endif /* lib/kernel/hash.h */ | ||
diff --git a/pintos-progos/lib/kernel/list.c b/pintos-progos/lib/kernel/list.c new file mode 100644 index 0000000..316d9ef --- /dev/null +++ b/pintos-progos/lib/kernel/list.c | |||
| @@ -0,0 +1,524 @@ | |||
| 1 | #include "list.h" | ||
| 2 | #include "../debug.h" | ||
| 3 | |||
| 4 | /* Our doubly linked lists have two header elements: the "head" | ||
| 5 | just before the first element and the "tail" just after the | ||
| 6 | last element. The `prev' link of the front header is null, as | ||
| 7 | is the `next' link of the back header. Their other two links | ||
| 8 | point toward each other via the interior elements of the list. | ||
| 9 | |||
| 10 | An empty list looks like this: | ||
| 11 | |||
| 12 | +------+ +------+ | ||
| 13 | <---| head |<--->| tail |---> | ||
| 14 | +------+ +------+ | ||
| 15 | |||
| 16 | A list with two elements in it looks like this: | ||
| 17 | |||
| 18 | +------+ +-------+ +-------+ +------+ | ||
| 19 | <---| head |<--->| 1 |<--->| 2 |<--->| tail |<---> | ||
| 20 | +------+ +-------+ +-------+ +------+ | ||
| 21 | |||
| 22 | The symmetry of this arrangement eliminates lots of special | ||
| 23 | cases in list processing. For example, take a look at | ||
| 24 | list_remove(): it takes only two pointer assignments and no | ||
| 25 | conditionals. That's a lot simpler than the code would be | ||
| 26 | without header elements. | ||
| 27 | |||
| 28 | (Because only one of the pointers in each header element is used, | ||
| 29 | we could in fact combine them into a single header element | ||
| 30 | without sacrificing this simplicity. But using two separate | ||
| 31 | elements allows us to do a little bit of checking on some | ||
| 32 | operations, which can be valuable.) */ | ||
| 33 | |||
| 34 | static bool is_sorted (struct list_elem *a, struct list_elem *b, | ||
| 35 | list_less_func *less, void *aux) UNUSED; | ||
| 36 | |||
| 37 | /* Returns true if ELEM is a head, false otherwise. */ | ||
| 38 | static inline bool | ||
| 39 | is_head (struct list_elem *elem) | ||
| 40 | { | ||
| 41 | return elem != NULL && elem->prev == NULL && elem->next != NULL; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Returns true if ELEM is an interior element, | ||
| 45 | false otherwise. */ | ||
| 46 | static inline bool | ||
| 47 | is_interior (struct list_elem *elem) | ||
| 48 | { | ||
| 49 | return elem != NULL && elem->prev != NULL && elem->next != NULL; | ||
| 50 | } | ||
| 51 | |||
| 52 | /* Returns true if ELEM is a tail, false otherwise. */ | ||
| 53 | static inline bool | ||
| 54 | is_tail (struct list_elem *elem) | ||
| 55 | { | ||
| 56 | return elem != NULL && elem->prev != NULL && elem->next == NULL; | ||
| 57 | } | ||
| 58 | |||
| 59 | /* Initializes LIST as an empty list. */ | ||
| 60 | void | ||
| 61 | list_init (struct list *list) | ||
| 62 | { | ||
| 63 | ASSERT (list != NULL); | ||
| 64 | list->head.prev = NULL; | ||
| 65 | list->head.next = &list->tail; | ||
| 66 | list->tail.prev = &list->head; | ||
| 67 | list->tail.next = NULL; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* Returns the beginning of LIST. */ | ||
| 71 | struct list_elem * | ||
| 72 | list_begin (struct list *list) | ||
| 73 | { | ||
| 74 | ASSERT (list != NULL); | ||
| 75 | return list->head.next; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Returns the element after ELEM in its list. If ELEM is the | ||
| 79 | last element in its list, returns the list tail. Results are | ||
| 80 | undefined if ELEM is itself a list tail. */ | ||
| 81 | struct list_elem * | ||
| 82 | list_next (struct list_elem *elem) | ||
| 83 | { | ||
| 84 | ASSERT (is_head (elem) || is_interior (elem)); | ||
| 85 | return elem->next; | ||
| 86 | } | ||
| 87 | |||
| 88 | /* Returns LIST's tail. | ||
| 89 | |||
| 90 | list_end() is often used in iterating through a list from | ||
| 91 | front to back. See the big comment at the top of list.h for | ||
| 92 | an example. */ | ||
| 93 | struct list_elem * | ||
| 94 | list_end (struct list *list) | ||
| 95 | { | ||
| 96 | ASSERT (list != NULL); | ||
| 97 | return &list->tail; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Returns the LIST's reverse beginning, for iterating through | ||
| 101 | LIST in reverse order, from back to front. */ | ||
| 102 | struct list_elem * | ||
| 103 | list_rbegin (struct list *list) | ||
| 104 | { | ||
| 105 | ASSERT (list != NULL); | ||
| 106 | return list->tail.prev; | ||
| 107 | } | ||
| 108 | |||
| 109 | /* Returns the element before ELEM in its list. If ELEM is the | ||
| 110 | first element in its list, returns the list head. Results are | ||
| 111 | undefined if ELEM is itself a list head. */ | ||
| 112 | struct list_elem * | ||
| 113 | list_prev (struct list_elem *elem) | ||
| 114 | { | ||
| 115 | ASSERT (is_interior (elem) || is_tail (elem)); | ||
| 116 | return elem->prev; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* Returns LIST's head. | ||
| 120 | |||
| 121 | list_rend() is often used in iterating through a list in | ||
| 122 | reverse order, from back to front. Here's typical usage, | ||
| 123 | following the example from the top of list.h: | ||
| 124 | |||
| 125 | for (e = list_rbegin (&foo_list); e != list_rend (&foo_list); | ||
| 126 | e = list_prev (e)) | ||
| 127 | { | ||
| 128 | struct foo *f = list_entry (e, struct foo, elem); | ||
| 129 | ...do something with f... | ||
| 130 | } | ||
| 131 | */ | ||
| 132 | struct list_elem * | ||
| 133 | list_rend (struct list *list) | ||
| 134 | { | ||
| 135 | ASSERT (list != NULL); | ||
| 136 | return &list->head; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Return's LIST's head. | ||
| 140 | |||
| 141 | list_head() can be used for an alternate style of iterating | ||
| 142 | through a list, e.g.: | ||
| 143 | |||
| 144 | e = list_head (&list); | ||
| 145 | while ((e = list_next (e)) != list_end (&list)) | ||
| 146 | { | ||
| 147 | ... | ||
| 148 | } | ||
| 149 | */ | ||
| 150 | struct list_elem * | ||
| 151 | list_head (struct list *list) | ||
| 152 | { | ||
| 153 | ASSERT (list != NULL); | ||
| 154 | return &list->head; | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Return's LIST's tail. */ | ||
| 158 | struct list_elem * | ||
| 159 | list_tail (struct list *list) | ||
| 160 | { | ||
| 161 | ASSERT (list != NULL); | ||
| 162 | return &list->tail; | ||
| 163 | } | ||
| 164 | |||
| 165 | /* Inserts ELEM just before BEFORE, which may be either an | ||
| 166 | interior element or a tail. The latter case is equivalent to | ||
| 167 | list_push_back(). */ | ||
| 168 | void | ||
| 169 | list_insert (struct list_elem *before, struct list_elem *elem) | ||
| 170 | { | ||
| 171 | ASSERT (is_interior (before) || is_tail (before)); | ||
| 172 | ASSERT (elem != NULL); | ||
| 173 | |||
| 174 | elem->prev = before->prev; | ||
| 175 | elem->next = before; | ||
| 176 | before->prev->next = elem; | ||
| 177 | before->prev = elem; | ||
| 178 | } | ||
| 179 | |||
| 180 | /* Removes elements FIRST though LAST (exclusive) from their | ||
| 181 | current list, then inserts them just before BEFORE, which may | ||
| 182 | be either an interior element or a tail. */ | ||
| 183 | void | ||
| 184 | list_splice (struct list_elem *before, | ||
| 185 | struct list_elem *first, struct list_elem *last) | ||
| 186 | { | ||
| 187 | ASSERT (is_interior (before) || is_tail (before)); | ||
| 188 | if (first == last) | ||
| 189 | return; | ||
| 190 | last = list_prev (last); | ||
| 191 | |||
| 192 | ASSERT (is_interior (first)); | ||
| 193 | ASSERT (is_interior (last)); | ||
| 194 | |||
| 195 | /* Cleanly remove FIRST...LAST from its current list. */ | ||
| 196 | first->prev->next = last->next; | ||
| 197 | last->next->prev = first->prev; | ||
| 198 | |||
| 199 | /* Splice FIRST...LAST into new list. */ | ||
| 200 | first->prev = before->prev; | ||
| 201 | last->next = before; | ||
| 202 | before->prev->next = first; | ||
| 203 | before->prev = last; | ||
| 204 | } | ||
| 205 | |||
| 206 | /* Inserts ELEM at the beginning of LIST, so that it becomes the | ||
| 207 | front in LIST. */ | ||
| 208 | void | ||
| 209 | list_push_front (struct list *list, struct list_elem *elem) | ||
| 210 | { | ||
| 211 | list_insert (list_begin (list), elem); | ||
| 212 | } | ||
| 213 | |||
| 214 | /* Inserts ELEM at the end of LIST, so that it becomes the | ||
| 215 | back in LIST. */ | ||
| 216 | void | ||
| 217 | list_push_back (struct list *list, struct list_elem *elem) | ||
| 218 | { | ||
| 219 | list_insert (list_end (list), elem); | ||
| 220 | } | ||
| 221 | |||
| 222 | /* Removes ELEM from its list and returns the element that | ||
| 223 | followed it. Undefined behavior if ELEM is not in a list. | ||
| 224 | |||
| 225 | A list element must be treated very carefully after removing | ||
| 226 | it from its list. Calling list_next() or list_prev() on ELEM | ||
| 227 | will return the item that was previously before or after ELEM, | ||
| 228 | but, e.g., list_prev(list_next(ELEM)) is no longer ELEM! | ||
| 229 | |||
| 230 | The list_remove() return value provides a convenient way to | ||
| 231 | iterate and remove elements from a list: | ||
| 232 | |||
| 233 | for (e = list_begin (&list); e != list_end (&list); e = list_remove (e)) | ||
| 234 | { | ||
| 235 | ...do something with e... | ||
| 236 | } | ||
| 237 | |||
| 238 | If you need to free() elements of the list then you need to be | ||
| 239 | more conservative. Here's an alternate strategy that works | ||
| 240 | even in that case: | ||
| 241 | |||
| 242 | while (!list_empty (&list)) | ||
| 243 | { | ||
| 244 | struct list_elem *e = list_pop_front (&list); | ||
| 245 | ...do something with e... | ||
| 246 | } | ||
| 247 | */ | ||
| 248 | struct list_elem * | ||
| 249 | list_remove (struct list_elem *elem) | ||
| 250 | { | ||
| 251 | ASSERT (is_interior (elem)); | ||
| 252 | elem->prev->next = elem->next; | ||
| 253 | elem->next->prev = elem->prev; | ||
| 254 | return elem->next; | ||
| 255 | } | ||
| 256 | |||
| 257 | /* Removes the front element from LIST and returns it. | ||
| 258 | Undefined behavior if LIST is empty before removal. */ | ||
| 259 | struct list_elem * | ||
| 260 | list_pop_front (struct list *list) | ||
| 261 | { | ||
| 262 | struct list_elem *front = list_front (list); | ||
| 263 | list_remove (front); | ||
| 264 | return front; | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Removes the back element from LIST and returns it. | ||
| 268 | Undefined behavior if LIST is empty before removal. */ | ||
| 269 | struct list_elem * | ||
| 270 | list_pop_back (struct list *list) | ||
| 271 | { | ||
| 272 | struct list_elem *back = list_back (list); | ||
| 273 | list_remove (back); | ||
| 274 | return back; | ||
| 275 | } | ||
| 276 | |||
| 277 | /* Returns the front element in LIST. | ||
| 278 | Undefined behavior if LIST is empty. */ | ||
| 279 | struct list_elem * | ||
| 280 | list_front (struct list *list) | ||
| 281 | { | ||
| 282 | ASSERT (!list_empty (list)); | ||
| 283 | return list->head.next; | ||
| 284 | } | ||
| 285 | |||
| 286 | /* Returns the back element in LIST. | ||
| 287 | Undefined behavior if LIST is empty. */ | ||
| 288 | struct list_elem * | ||
| 289 | list_back (struct list *list) | ||
| 290 | { | ||
| 291 | ASSERT (!list_empty (list)); | ||
| 292 | return list->tail.prev; | ||
| 293 | } | ||
| 294 | |||
| 295 | /* Returns the number of elements in LIST. | ||
| 296 | Runs in O(n) in the number of elements. */ | ||
| 297 | size_t | ||
| 298 | list_size (struct list *list) | ||
| 299 | { | ||
| 300 | struct list_elem *e; | ||
| 301 | size_t cnt = 0; | ||
| 302 | |||
| 303 | for (e = list_begin (list); e != list_end (list); e = list_next (e)) | ||
| 304 | cnt++; | ||
| 305 | return cnt; | ||
| 306 | } | ||
| 307 | |||
| 308 | /* Returns true if LIST is empty, false otherwise. */ | ||
| 309 | bool | ||
| 310 | list_empty (struct list *list) | ||
| 311 | { | ||
| 312 | return list_begin (list) == list_end (list); | ||
| 313 | } | ||
| 314 | |||
| 315 | /* Swaps the `struct list_elem *'s that A and B point to. */ | ||
| 316 | static void | ||
| 317 | swap (struct list_elem **a, struct list_elem **b) | ||
| 318 | { | ||
| 319 | struct list_elem *t = *a; | ||
| 320 | *a = *b; | ||
| 321 | *b = t; | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Reverses the order of LIST. */ | ||
| 325 | void | ||
| 326 | list_reverse (struct list *list) | ||
| 327 | { | ||
| 328 | if (!list_empty (list)) | ||
| 329 | { | ||
| 330 | struct list_elem *e; | ||
| 331 | |||
| 332 | for (e = list_begin (list); e != list_end (list); e = e->prev) | ||
| 333 | swap (&e->prev, &e->next); | ||
| 334 | swap (&list->head.next, &list->tail.prev); | ||
| 335 | swap (&list->head.next->prev, &list->tail.prev->next); | ||
| 336 | } | ||
| 337 | } | ||
| 338 | |||
| 339 | /* Returns true only if the list elements A through B (exclusive) | ||
| 340 | are in order according to LESS given auxiliary data AUX. */ | ||
| 341 | static bool | ||
| 342 | is_sorted (struct list_elem *a, struct list_elem *b, | ||
| 343 | list_less_func *less, void *aux) | ||
| 344 | { | ||
| 345 | if (a != b) | ||
| 346 | while ((a = list_next (a)) != b) | ||
| 347 | if (less (a, list_prev (a), aux)) | ||
| 348 | return false; | ||
| 349 | return true; | ||
| 350 | } | ||
| 351 | |||
| 352 | /* Finds a run, starting at A and ending not after B, of list | ||
| 353 | elements that are in nondecreasing order according to LESS | ||
| 354 | given auxiliary data AUX. Returns the (exclusive) end of the | ||
| 355 | run. | ||
| 356 | A through B (exclusive) must form a non-empty range. */ | ||
| 357 | static struct list_elem * | ||
| 358 | find_end_of_run (struct list_elem *a, struct list_elem *b, | ||
| 359 | list_less_func *less, void *aux) | ||
| 360 | { | ||
| 361 | ASSERT (a != NULL); | ||
| 362 | ASSERT (b != NULL); | ||
| 363 | ASSERT (less != NULL); | ||
| 364 | ASSERT (a != b); | ||
| 365 | |||
| 366 | do | ||
| 367 | { | ||
| 368 | a = list_next (a); | ||
| 369 | } | ||
| 370 | while (a != b && !less (a, list_prev (a), aux)); | ||
| 371 | return a; | ||
| 372 | } | ||
| 373 | |||
| 374 | /* Merges A0 through A1B0 (exclusive) with A1B0 through B1 | ||
| 375 | (exclusive) to form a combined range also ending at B1 | ||
| 376 | (exclusive). Both input ranges must be nonempty and sorted in | ||
| 377 | nondecreasing order according to LESS given auxiliary data | ||
| 378 | AUX. The output range will be sorted the same way. */ | ||
| 379 | static void | ||
| 380 | inplace_merge (struct list_elem *a0, struct list_elem *a1b0, | ||
| 381 | struct list_elem *b1, | ||
| 382 | list_less_func *less, void *aux) | ||
| 383 | { | ||
| 384 | ASSERT (a0 != NULL); | ||
| 385 | ASSERT (a1b0 != NULL); | ||
| 386 | ASSERT (b1 != NULL); | ||
| 387 | ASSERT (less != NULL); | ||
| 388 | ASSERT (is_sorted (a0, a1b0, less, aux)); | ||
| 389 | ASSERT (is_sorted (a1b0, b1, less, aux)); | ||
| 390 | |||
| 391 | while (a0 != a1b0 && a1b0 != b1) | ||
| 392 | if (!less (a1b0, a0, aux)) | ||
| 393 | a0 = list_next (a0); | ||
| 394 | else | ||
| 395 | { | ||
| 396 | a1b0 = list_next (a1b0); | ||
| 397 | list_splice (a0, list_prev (a1b0), a1b0); | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | /* Sorts LIST according to LESS given auxiliary data AUX, using a | ||
| 402 | natural iterative merge sort that runs in O(n lg n) time and | ||
| 403 | O(1) space in the number of elements in LIST. */ | ||
| 404 | void | ||
| 405 | list_sort (struct list *list, list_less_func *less, void *aux) | ||
| 406 | { | ||
| 407 | size_t output_run_cnt; /* Number of runs output in current pass. */ | ||
| 408 | |||
| 409 | ASSERT (list != NULL); | ||
| 410 | ASSERT (less != NULL); | ||
| 411 | |||
| 412 | /* Pass over the list repeatedly, merging adjacent runs of | ||
| 413 | nondecreasing elements, until only one run is left. */ | ||
| 414 | do | ||
| 415 | { | ||
| 416 | struct list_elem *a0; /* Start of first run. */ | ||
| 417 | struct list_elem *a1b0; /* End of first run, start of second. */ | ||
| 418 | struct list_elem *b1; /* End of second run. */ | ||
| 419 | |||
| 420 | output_run_cnt = 0; | ||
| 421 | for (a0 = list_begin (list); a0 != list_end (list); a0 = b1) | ||
| 422 | { | ||
| 423 | /* Each iteration produces one output run. */ | ||
| 424 | output_run_cnt++; | ||
| 425 | |||
| 426 | /* Locate two adjacent runs of nondecreasing elements | ||
| 427 | A0...A1B0 and A1B0...B1. */ | ||
| 428 | a1b0 = find_end_of_run (a0, list_end (list), less, aux); | ||
| 429 | if (a1b0 == list_end (list)) | ||
| 430 | break; | ||
| 431 | b1 = find_end_of_run (a1b0, list_end (list), less, aux); | ||
| 432 | |||
| 433 | /* Merge the runs. */ | ||
| 434 | inplace_merge (a0, a1b0, b1, less, aux); | ||
| 435 | } | ||
| 436 | } | ||
| 437 | while (output_run_cnt > 1); | ||
| 438 | |||
| 439 | ASSERT (is_sorted (list_begin (list), list_end (list), less, aux)); | ||
| 440 | } | ||
| 441 | |||
| 442 | /* Inserts ELEM in the proper position in LIST, which must be | ||
| 443 | sorted according to LESS given auxiliary data AUX. | ||
| 444 | Runs in O(n) average case in the number of elements in LIST. */ | ||
| 445 | void | ||
| 446 | list_insert_ordered (struct list *list, struct list_elem *elem, | ||
| 447 | list_less_func *less, void *aux) | ||
| 448 | { | ||
| 449 | struct list_elem *e; | ||
| 450 | |||
| 451 | ASSERT (list != NULL); | ||
| 452 | ASSERT (elem != NULL); | ||
| 453 | ASSERT (less != NULL); | ||
| 454 | |||
| 455 | for (e = list_begin (list); e != list_end (list); e = list_next (e)) | ||
| 456 | if (less (elem, e, aux)) | ||
| 457 | break; | ||
| 458 | return list_insert (e, elem); | ||
| 459 | } | ||
| 460 | |||
| 461 | /* Iterates through LIST and removes all but the first in each | ||
| 462 | set of adjacent elements that are equal according to LESS | ||
| 463 | given auxiliary data AUX. If DUPLICATES is non-null, then the | ||
| 464 | elements from LIST are appended to DUPLICATES. */ | ||
| 465 | void | ||
| 466 | list_unique (struct list *list, struct list *duplicates, | ||
| 467 | list_less_func *less, void *aux) | ||
| 468 | { | ||
| 469 | struct list_elem *elem, *next; | ||
| 470 | |||
| 471 | ASSERT (list != NULL); | ||
| 472 | ASSERT (less != NULL); | ||
| 473 | if (list_empty (list)) | ||
| 474 | return; | ||
| 475 | |||
| 476 | elem = list_begin (list); | ||
| 477 | while ((next = list_next (elem)) != list_end (list)) | ||
| 478 | if (!less (elem, next, aux) && !less (next, elem, aux)) | ||
| 479 | { | ||
| 480 | list_remove (next); | ||
| 481 | if (duplicates != NULL) | ||
| 482 | list_push_back (duplicates, next); | ||
| 483 | } | ||
| 484 | else | ||
| 485 | elem = next; | ||
| 486 | } | ||
| 487 | |||
| 488 | /* Returns the element in LIST with the largest value according | ||
| 489 | to LESS given auxiliary data AUX. If there is more than one | ||
| 490 | maximum, returns the one that appears earlier in the list. If | ||
| 491 | the list is empty, returns its tail. */ | ||
| 492 | struct list_elem * | ||
| 493 | list_max (struct list *list, list_less_func *less, void *aux) | ||
| 494 | { | ||
| 495 | struct list_elem *max = list_begin (list); | ||
| 496 | if (max != list_end (list)) | ||
| 497 | { | ||
| 498 | struct list_elem *e; | ||
| 499 | |||
| 500 | for (e = list_next (max); e != list_end (list); e = list_next (e)) | ||
| 501 | if (less (max, e, aux)) | ||
| 502 | max = e; | ||
| 503 | } | ||
| 504 | return max; | ||
| 505 | } | ||
| 506 | |||
| 507 | /* Returns the element in LIST with the smallest value according | ||
| 508 | to LESS given auxiliary data AUX. If there is more than one | ||
| 509 | minimum, returns the one that appears earlier in the list. If | ||
| 510 | the list is empty, returns its tail. */ | ||
| 511 | struct list_elem * | ||
| 512 | list_min (struct list *list, list_less_func *less, void *aux) | ||
| 513 | { | ||
| 514 | struct list_elem *min = list_begin (list); | ||
| 515 | if (min != list_end (list)) | ||
| 516 | { | ||
| 517 | struct list_elem *e; | ||
| 518 | |||
| 519 | for (e = list_next (min); e != list_end (list); e = list_next (e)) | ||
| 520 | if (less (e, min, aux)) | ||
| 521 | min = e; | ||
| 522 | } | ||
| 523 | return min; | ||
| 524 | } | ||
diff --git a/pintos-progos/lib/kernel/list.h b/pintos-progos/lib/kernel/list.h new file mode 100644 index 0000000..82efbb5 --- /dev/null +++ b/pintos-progos/lib/kernel/list.h | |||
| @@ -0,0 +1,181 @@ | |||
| 1 | #ifndef __LIB_KERNEL_LIST_H | ||
| 2 | #define __LIB_KERNEL_LIST_H | ||
| 3 | |||
| 4 | /* Doubly linked list. | ||
| 5 | |||
| 6 | This implementation of a doubly linked list does not require | ||
| 7 | use of dynamically allocated memory. Instead, each structure | ||
| 8 | that is a potential list element must embed a struct list_elem | ||
| 9 | member. All of the list functions operate on these `struct | ||
| 10 | list_elem's. The list_entry macro allows conversion from a | ||
| 11 | struct list_elem back to a structure object that contains it. | ||
| 12 | |||
| 13 | For example, suppose there is a needed for a list of `struct | ||
| 14 | foo'. `struct foo' should contain a `struct list_elem' | ||
| 15 | member, like so: | ||
| 16 | |||
| 17 | struct foo | ||
| 18 | { | ||
| 19 | struct list_elem elem; | ||
| 20 | int bar; | ||
| 21 | ...other members... | ||
| 22 | }; | ||
| 23 | |||
| 24 | Then a list of `struct foo' can be be declared and initialized | ||
| 25 | like so: | ||
| 26 | |||
| 27 | struct list foo_list; | ||
| 28 | |||
| 29 | list_init (&foo_list); | ||
| 30 | |||
| 31 | Iteration is a typical situation where it is necessary to | ||
| 32 | convert from a struct list_elem back to its enclosing | ||
| 33 | structure. Here's an example using foo_list: | ||
| 34 | |||
| 35 | struct list_elem *e; | ||
| 36 | |||
| 37 | for (e = list_begin (&foo_list); e != list_end (&foo_list); | ||
| 38 | e = list_next (e)) | ||
| 39 | { | ||
| 40 | struct foo *f = list_entry (e, struct foo, elem); | ||
| 41 | ...do something with f... | ||
| 42 | } | ||
| 43 | |||
| 44 | You can find real examples of list usage throughout the | ||
| 45 | source; for example, malloc.c, palloc.c, and thread.c in the | ||
| 46 | threads directory all use lists. | ||
| 47 | |||
| 48 | The interface for this list is inspired by the list<> template | ||
| 49 | in the C++ STL. If you're familiar with list<>, you should | ||
| 50 | find this easy to use. However, it should be emphasized that | ||
| 51 | these lists do *no* type checking and can't do much other | ||
| 52 | correctness checking. If you screw up, it will bite you. | ||
| 53 | |||
| 54 | Glossary of list terms: | ||
| 55 | |||
| 56 | - "front": The first element in a list. Undefined in an | ||
| 57 | empty list. Returned by list_front(). | ||
| 58 | |||
| 59 | - "back": The last element in a list. Undefined in an empty | ||
| 60 | list. Returned by list_back(). | ||
| 61 | |||
| 62 | - "tail": The element figuratively just after the last | ||
| 63 | element of a list. Well defined even in an empty list. | ||
| 64 | Returned by list_end(). Used as the end sentinel for an | ||
| 65 | iteration from front to back. | ||
| 66 | |||
| 67 | - "beginning": In a non-empty list, the front. In an empty | ||
| 68 | list, the tail. Returned by list_begin(). Used as the | ||
| 69 | starting point for an iteration from front to back. | ||
| 70 | |||
| 71 | - "head": The element figuratively just before the first | ||
| 72 | element of a list. Well defined even in an empty list. | ||
| 73 | Returned by list_rend(). Used as the end sentinel for an | ||
| 74 | iteration from back to front. | ||
| 75 | |||
| 76 | - "reverse beginning": In a non-empty list, the back. In an | ||
| 77 | empty list, the head. Returned by list_rbegin(). Used as | ||
| 78 | the starting point for an iteration from back to front. | ||
| 79 | |||
| 80 | - "interior element": An element that is not the head or | ||
| 81 | tail, that is, a real list element. An empty list does | ||
| 82 | not have any interior elements. | ||
| 83 | */ | ||
| 84 | |||
| 85 | #include <stdbool.h> | ||
| 86 | #include <stddef.h> | ||
| 87 | #include <stdint.h> | ||
| 88 | |||
| 89 | /* List element. */ | ||
| 90 | struct list_elem | ||
| 91 | { | ||
| 92 | struct list_elem *prev; /* Previous list element. */ | ||
| 93 | struct list_elem *next; /* Next list element. */ | ||
| 94 | }; | ||
| 95 | |||
| 96 | /* List. */ | ||
| 97 | struct list | ||
| 98 | { | ||
| 99 | struct list_elem head; /* List head. */ | ||
| 100 | struct list_elem tail; /* List tail. */ | ||
| 101 | }; | ||
| 102 | |||
| 103 | /* Converts pointer to list element LIST_ELEM into a pointer to | ||
| 104 | the structure that LIST_ELEM is embedded inside. Supply the | ||
| 105 | name of the outer structure STRUCT and the member name MEMBER | ||
| 106 | of the list element. See the big comment at the top of the | ||
| 107 | file for an example. */ | ||
| 108 | #define list_entry(LIST_ELEM, STRUCT, MEMBER) \ | ||
| 109 | ((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \ | ||
| 110 | - offsetof (STRUCT, MEMBER.next))) | ||
| 111 | |||
| 112 | /* List initialization. | ||
| 113 | |||
| 114 | A list may be initialized by calling list_init(): | ||
| 115 | |||
| 116 | struct list my_list; | ||
| 117 | list_init (&my_list); | ||
| 118 | |||
| 119 | or with an initializer using LIST_INITIALIZER: | ||
| 120 | |||
| 121 | struct list my_list = LIST_INITIALIZER (my_list); */ | ||
| 122 | #define LIST_INITIALIZER(NAME) { { NULL, &(NAME).tail }, \ | ||
| 123 | { &(NAME).head, NULL } } | ||
| 124 | |||
| 125 | void list_init (struct list *); | ||
| 126 | |||
| 127 | /* List traversal. */ | ||
| 128 | struct list_elem *list_begin (struct list *); | ||
| 129 | struct list_elem *list_next (struct list_elem *); | ||
| 130 | struct list_elem *list_end (struct list *); | ||
| 131 | |||
| 132 | struct list_elem *list_rbegin (struct list *); | ||
| 133 | struct list_elem *list_prev (struct list_elem *); | ||
| 134 | struct list_elem *list_rend (struct list *); | ||
| 135 | |||
| 136 | struct list_elem *list_head (struct list *); | ||
| 137 | struct list_elem *list_tail (struct list *); | ||
| 138 | |||
| 139 | /* List insertion. */ | ||
| 140 | void list_insert (struct list_elem *, struct list_elem *); | ||
| 141 | void list_splice (struct list_elem *before, | ||
| 142 | struct list_elem *first, struct list_elem *last); | ||
| 143 | void list_push_front (struct list *, struct list_elem *); | ||
| 144 | void list_push_back (struct list *, struct list_elem *); | ||
| 145 | |||
| 146 | /* List removal. */ | ||
| 147 | struct list_elem *list_remove (struct list_elem *); | ||
| 148 | struct list_elem *list_pop_front (struct list *); | ||
| 149 | struct list_elem *list_pop_back (struct list *); | ||
| 150 | |||
| 151 | /* List elements. */ | ||
| 152 | struct list_elem *list_front (struct list *); | ||
| 153 | struct list_elem *list_back (struct list *); | ||
| 154 | |||
| 155 | /* List properties. */ | ||
| 156 | size_t list_size (struct list *); | ||
| 157 | bool list_empty (struct list *); | ||
| 158 | |||
| 159 | /* Miscellaneous. */ | ||
| 160 | void list_reverse (struct list *); | ||
| 161 | |||
| 162 | /* Compares the value of two list elements A and B, given | ||
| 163 | auxiliary data AUX. Returns true if A is less than B, or | ||
| 164 | false if A is greater than or equal to B. */ | ||
| 165 | typedef bool list_less_func (const struct list_elem *a, | ||
| 166 | const struct list_elem *b, | ||
| 167 | void *aux); | ||
| 168 | |||
| 169 | /* Operations on lists with ordered elements. */ | ||
| 170 | void list_sort (struct list *, | ||
| 171 | list_less_func *, void *aux); | ||
| 172 | void list_insert_ordered (struct list *, struct list_elem *, | ||
| 173 | list_less_func *, void *aux); | ||
| 174 | void list_unique (struct list *, struct list *duplicates, | ||
| 175 | list_less_func *, void *aux); | ||
| 176 | |||
| 177 | /* Max and min. */ | ||
| 178 | struct list_elem *list_max (struct list *, list_less_func *, void *aux); | ||
| 179 | struct list_elem *list_min (struct list *, list_less_func *, void *aux); | ||
| 180 | |||
| 181 | #endif /* lib/kernel/list.h */ | ||
diff --git a/pintos-progos/lib/kernel/stdio.h b/pintos-progos/lib/kernel/stdio.h new file mode 100644 index 0000000..3e5bae9 --- /dev/null +++ b/pintos-progos/lib/kernel/stdio.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef __LIB_KERNEL_STDIO_H | ||
| 2 | #define __LIB_KERNEL_STDIO_H | ||
| 3 | |||
| 4 | void putbuf (const char *, size_t); | ||
| 5 | |||
| 6 | #endif /* lib/kernel/stdio.h */ | ||
diff --git a/pintos-progos/lib/limits.h b/pintos-progos/lib/limits.h new file mode 100644 index 0000000..c957ec4 --- /dev/null +++ b/pintos-progos/lib/limits.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #ifndef __LIB_LIMITS_H | ||
| 2 | #define __LIB_LIMITS_H | ||
| 3 | |||
| 4 | #define CHAR_BIT 8 | ||
| 5 | |||
| 6 | #define SCHAR_MAX 127 | ||
| 7 | #define SCHAR_MIN (-SCHAR_MAX - 1) | ||
| 8 | #define UCHAR_MAX 255 | ||
| 9 | |||
| 10 | #ifdef __CHAR_UNSIGNED__ | ||
| 11 | #define CHAR_MIN 0 | ||
| 12 | #define CHAR_MAX UCHAR_MAX | ||
| 13 | #else | ||
| 14 | #define CHAR_MIN SCHAR_MIN | ||
| 15 | #define CHAR_MAX SCHAR_MAX | ||
| 16 | #endif | ||
| 17 | |||
| 18 | #define SHRT_MAX 32767 | ||
| 19 | #define SHRT_MIN (-SHRT_MAX - 1) | ||
| 20 | #define USHRT_MAX 65535 | ||
| 21 | |||
| 22 | #define INT_MAX 2147483647 | ||
| 23 | #define INT_MIN (-INT_MAX - 1) | ||
| 24 | #define UINT_MAX 4294967295U | ||
| 25 | |||
| 26 | #define LONG_MAX 2147483647L | ||
| 27 | #define LONG_MIN (-LONG_MAX - 1) | ||
| 28 | #define ULONG_MAX 4294967295UL | ||
| 29 | |||
| 30 | #define LLONG_MAX 9223372036854775807LL | ||
| 31 | #define LLONG_MIN (-LLONG_MAX - 1) | ||
| 32 | #define ULLONG_MAX 18446744073709551615ULL | ||
| 33 | |||
| 34 | #endif /* lib/limits.h */ | ||
diff --git a/pintos-progos/lib/packed.h b/pintos-progos/lib/packed.h new file mode 100644 index 0000000..9a9b6e2 --- /dev/null +++ b/pintos-progos/lib/packed.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | #ifndef __LIB_PACKED_H | ||
| 2 | #define __LIB_PACKED_H | ||
| 3 | |||
| 4 | /* The "packed" attribute, when applied to a structure, prevents | ||
| 5 | GCC from inserting padding bytes between or after structure | ||
| 6 | members. It must be specified at the time of the structure's | ||
| 7 | definition, normally just after the closing brace. */ | ||
| 8 | #define PACKED __attribute__ ((packed)) | ||
| 9 | |||
| 10 | #endif /* lib/packed.h */ | ||
diff --git a/pintos-progos/lib/random.c b/pintos-progos/lib/random.c new file mode 100644 index 0000000..a4761b6 --- /dev/null +++ b/pintos-progos/lib/random.c | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | #include "random.h" | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <stdint.h> | ||
| 4 | #include "debug.h" | ||
| 5 | |||
| 6 | /* RC4-based pseudo-random number generator (PRNG). | ||
| 7 | |||
| 8 | RC4 is a stream cipher. We're not using it here for its | ||
| 9 | cryptographic properties, but because it is easy to implement | ||
| 10 | and its output is plenty random for non-cryptographic | ||
| 11 | purposes. | ||
| 12 | |||
| 13 | See http://en.wikipedia.org/wiki/RC4_(cipher) for information | ||
| 14 | on RC4.*/ | ||
| 15 | |||
| 16 | /* RC4 state. */ | ||
| 17 | static uint8_t s[256]; /* S[]. */ | ||
| 18 | static uint8_t s_i, s_j; /* i, j. */ | ||
| 19 | |||
| 20 | /* Already initialized? */ | ||
| 21 | static bool inited; | ||
| 22 | |||
| 23 | /* Swaps the bytes pointed to by A and B. */ | ||
| 24 | static inline void | ||
| 25 | swap_byte (uint8_t *a, uint8_t *b) | ||
| 26 | { | ||
| 27 | uint8_t t = *a; | ||
| 28 | *a = *b; | ||
| 29 | *b = t; | ||
| 30 | } | ||
| 31 | |||
| 32 | /* Initializes or reinitializes the PRNG with the given SEED. */ | ||
| 33 | void | ||
| 34 | random_init (unsigned seed) | ||
| 35 | { | ||
| 36 | uint8_t *seedp = (uint8_t *) &seed; | ||
| 37 | int i; | ||
| 38 | uint8_t j; | ||
| 39 | |||
| 40 | for (i = 0; i < 256; i++) | ||
| 41 | s[i] = i; | ||
| 42 | for (i = j = 0; i < 256; i++) | ||
| 43 | { | ||
| 44 | j += s[i] + seedp[i % sizeof seed]; | ||
| 45 | swap_byte (s + i, s + j); | ||
| 46 | } | ||
| 47 | |||
| 48 | s_i = s_j = 0; | ||
| 49 | inited = true; | ||
| 50 | } | ||
| 51 | |||
| 52 | /* Writes SIZE random bytes into BUF. */ | ||
| 53 | void | ||
| 54 | random_bytes (void *buf_, size_t size) | ||
| 55 | { | ||
| 56 | uint8_t *buf; | ||
| 57 | |||
| 58 | if (!inited) | ||
| 59 | random_init (0); | ||
| 60 | |||
| 61 | for (buf = buf_; size-- > 0; buf++) | ||
| 62 | { | ||
| 63 | uint8_t s_k; | ||
| 64 | |||
| 65 | s_i++; | ||
| 66 | s_j += s[s_i]; | ||
| 67 | swap_byte (s + s_i, s + s_j); | ||
| 68 | |||
| 69 | s_k = s[s_i] + s[s_j]; | ||
| 70 | *buf = s[s_k]; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Returns a pseudo-random unsigned long. | ||
| 75 | Use random_ulong() % n to obtain a random number in the range | ||
| 76 | 0...n (exclusive). */ | ||
| 77 | unsigned long | ||
| 78 | random_ulong (void) | ||
| 79 | { | ||
| 80 | unsigned long ul; | ||
| 81 | random_bytes (&ul, sizeof ul); | ||
| 82 | return ul; | ||
| 83 | } | ||
diff --git a/pintos-progos/lib/random.h b/pintos-progos/lib/random.h new file mode 100644 index 0000000..0950ae2 --- /dev/null +++ b/pintos-progos/lib/random.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | #ifndef __LIB_RANDOM_H | ||
| 2 | #define __LIB_RANDOM_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | void random_init (unsigned seed); | ||
| 7 | void random_bytes (void *, size_t); | ||
| 8 | unsigned long random_ulong (void); | ||
| 9 | |||
| 10 | #endif /* lib/random.h */ | ||
diff --git a/pintos-progos/lib/round.h b/pintos-progos/lib/round.h new file mode 100644 index 0000000..3aa6642 --- /dev/null +++ b/pintos-progos/lib/round.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #ifndef __LIB_ROUND_H | ||
| 2 | #define __LIB_ROUND_H | ||
| 3 | |||
| 4 | /* Yields X rounded up to the nearest multiple of STEP. | ||
| 5 | For X >= 0, STEP >= 1 only. */ | ||
| 6 | #define ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP) * (STEP)) | ||
| 7 | |||
| 8 | /* Yields X divided by STEP, rounded up. | ||
| 9 | For X >= 0, STEP >= 1 only. */ | ||
| 10 | #define DIV_ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP)) | ||
| 11 | |||
| 12 | /* Yields X rounded down to the nearest multiple of STEP. | ||
| 13 | For X >= 0, STEP >= 1 only. */ | ||
| 14 | #define ROUND_DOWN(X, STEP) ((X) / (STEP) * (STEP)) | ||
| 15 | |||
| 16 | /* There is no DIV_ROUND_DOWN. It would be simply X / STEP. */ | ||
| 17 | |||
| 18 | #endif /* lib/round.h */ | ||
diff --git a/pintos-progos/lib/stdarg.h b/pintos-progos/lib/stdarg.h new file mode 100644 index 0000000..32622b5 --- /dev/null +++ b/pintos-progos/lib/stdarg.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | #ifndef __LIB_STDARG_H | ||
| 2 | #define __LIB_STDARG_H | ||
| 3 | |||
| 4 | /* GCC has <stdarg.h> functionality as built-ins, | ||
| 5 | so all we need is to use it. */ | ||
| 6 | |||
| 7 | typedef __builtin_va_list va_list; | ||
| 8 | |||
| 9 | #define va_start(LIST, ARG) __builtin_va_start (LIST, ARG) | ||
| 10 | #define va_end(LIST) __builtin_va_end (LIST) | ||
| 11 | #define va_arg(LIST, TYPE) __builtin_va_arg (LIST, TYPE) | ||
| 12 | #define va_copy(DST, SRC) __builtin_va_copy (DST, SRC) | ||
| 13 | |||
| 14 | #endif /* lib/stdarg.h */ | ||
diff --git a/pintos-progos/lib/stdbool.h b/pintos-progos/lib/stdbool.h new file mode 100644 index 0000000..f173a91 --- /dev/null +++ b/pintos-progos/lib/stdbool.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef __LIB_STDBOOL_H | ||
| 2 | #define __LIB_STDBOOL_H | ||
| 3 | |||
| 4 | #define bool _Bool | ||
| 5 | #define true 1 | ||
| 6 | #define false 0 | ||
| 7 | #define __bool_true_false_are_defined 1 | ||
| 8 | |||
| 9 | #endif /* lib/stdbool.h */ | ||
diff --git a/pintos-progos/lib/stddef.h b/pintos-progos/lib/stddef.h new file mode 100644 index 0000000..4e74fa6 --- /dev/null +++ b/pintos-progos/lib/stddef.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef __LIB_STDDEF_H | ||
| 2 | #define __LIB_STDDEF_H | ||
| 3 | |||
| 4 | #define NULL ((void *) 0) | ||
| 5 | #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER) | ||
| 6 | |||
| 7 | /* GCC predefines the types we need for ptrdiff_t and size_t, | ||
| 8 | so that we don't have to guess. */ | ||
| 9 | typedef __PTRDIFF_TYPE__ ptrdiff_t; | ||
| 10 | typedef __SIZE_TYPE__ size_t; | ||
| 11 | |||
| 12 | #endif /* lib/stddef.h */ | ||
diff --git a/pintos-progos/lib/stdint.h b/pintos-progos/lib/stdint.h new file mode 100644 index 0000000..ef5f214 --- /dev/null +++ b/pintos-progos/lib/stdint.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | #ifndef __LIB_STDINT_H | ||
| 2 | #define __LIB_STDINT_H | ||
| 3 | |||
| 4 | typedef signed char int8_t; | ||
| 5 | #define INT8_MAX 127 | ||
| 6 | #define INT8_MIN (-INT8_MAX - 1) | ||
| 7 | |||
| 8 | typedef signed short int int16_t; | ||
| 9 | #define INT16_MAX 32767 | ||
| 10 | #define INT16_MIN (-INT16_MAX - 1) | ||
| 11 | |||
| 12 | typedef signed int int32_t; | ||
| 13 | #define INT32_MAX 2147483647 | ||
| 14 | #define INT32_MIN (-INT32_MAX - 1) | ||
| 15 | |||
| 16 | typedef signed long long int int64_t; | ||
| 17 | #define INT64_MAX 9223372036854775807LL | ||
| 18 | #define INT64_MIN (-INT64_MAX - 1) | ||
| 19 | |||
| 20 | typedef unsigned char uint8_t; | ||
| 21 | #define UINT8_MAX 255 | ||
| 22 | |||
| 23 | typedef unsigned short int uint16_t; | ||
| 24 | #define UINT16_MAX 65535 | ||
| 25 | |||
| 26 | typedef unsigned int uint32_t; | ||
| 27 | #define UINT32_MAX 4294967295U | ||
| 28 | |||
| 29 | typedef unsigned long long int uint64_t; | ||
| 30 | #define UINT64_MAX 18446744073709551615ULL | ||
| 31 | |||
| 32 | typedef int32_t intptr_t; | ||
| 33 | #define INTPTR_MIN INT32_MIN | ||
| 34 | #define INTPTR_MAX INT32_MAX | ||
| 35 | |||
| 36 | typedef uint32_t uintptr_t; | ||
| 37 | #define UINTPTR_MAX UINT32_MAX | ||
| 38 | |||
| 39 | typedef int64_t intmax_t; | ||
| 40 | #define INTMAX_MIN INT64_MIN | ||
| 41 | #define INTMAX_MAX INT64_MAX | ||
| 42 | |||
| 43 | typedef uint64_t uintmax_t; | ||
| 44 | #define UINTMAX_MAX UINT64_MAX | ||
| 45 | |||
| 46 | #define PTRDIFF_MIN INT32_MIN | ||
| 47 | #define PTRDIFF_MAX INT32_MAX | ||
| 48 | |||
| 49 | #define SIZE_MAX UINT32_MAX | ||
| 50 | |||
| 51 | #endif /* lib/stdint.h */ | ||
diff --git a/pintos-progos/lib/stdio.c b/pintos-progos/lib/stdio.c new file mode 100644 index 0000000..8927c50 --- /dev/null +++ b/pintos-progos/lib/stdio.c | |||
| @@ -0,0 +1,655 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <ctype.h> | ||
| 3 | #include <inttypes.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <string.h> | ||
| 7 | |||
| 8 | /* Auxiliary data for vsnprintf_helper(). */ | ||
| 9 | struct vsnprintf_aux | ||
| 10 | { | ||
| 11 | char *p; /* Current output position. */ | ||
| 12 | int length; /* Length of output string. */ | ||
| 13 | int max_length; /* Max length of output string. */ | ||
| 14 | }; | ||
| 15 | |||
| 16 | static void vsnprintf_helper (char, void *); | ||
| 17 | |||
| 18 | /* Like vprintf(), except that output is stored into BUFFER, | ||
| 19 | which must have space for BUF_SIZE characters. Writes at most | ||
| 20 | BUF_SIZE - 1 characters to BUFFER, followed by a null | ||
| 21 | terminator. BUFFER will always be null-terminated unless | ||
| 22 | BUF_SIZE is zero. Returns the number of characters that would | ||
| 23 | have been written to BUFFER, not including a null terminator, | ||
| 24 | had there been enough room. */ | ||
| 25 | int | ||
| 26 | vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args) | ||
| 27 | { | ||
| 28 | /* Set up aux data for vsnprintf_helper(). */ | ||
| 29 | struct vsnprintf_aux aux; | ||
| 30 | aux.p = buffer; | ||
| 31 | aux.length = 0; | ||
| 32 | aux.max_length = buf_size > 0 ? buf_size - 1 : 0; | ||
| 33 | |||
| 34 | /* Do most of the work. */ | ||
| 35 | __vprintf (format, args, vsnprintf_helper, &aux); | ||
| 36 | |||
| 37 | /* Add null terminator. */ | ||
| 38 | if (buf_size > 0) | ||
| 39 | *aux.p = '\0'; | ||
| 40 | |||
| 41 | return aux.length; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Helper function for vsnprintf(). */ | ||
| 45 | static void | ||
| 46 | vsnprintf_helper (char ch, void *aux_) | ||
| 47 | { | ||
| 48 | struct vsnprintf_aux *aux = aux_; | ||
| 49 | |||
| 50 | if (aux->length++ < aux->max_length) | ||
| 51 | *aux->p++ = ch; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Like printf(), except that output is stored into BUFFER, | ||
| 55 | which must have space for BUF_SIZE characters. Writes at most | ||
| 56 | BUF_SIZE - 1 characters to BUFFER, followed by a null | ||
| 57 | terminator. BUFFER will always be null-terminated unless | ||
| 58 | BUF_SIZE is zero. Returns the number of characters that would | ||
| 59 | have been written to BUFFER, not including a null terminator, | ||
| 60 | had there been enough room. */ | ||
| 61 | int | ||
| 62 | snprintf (char *buffer, size_t buf_size, const char *format, ...) | ||
| 63 | { | ||
| 64 | va_list args; | ||
| 65 | int retval; | ||
| 66 | |||
| 67 | va_start (args, format); | ||
| 68 | retval = vsnprintf (buffer, buf_size, format, args); | ||
| 69 | va_end (args); | ||
| 70 | |||
| 71 | return retval; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Writes formatted output to the console. | ||
| 75 | In the kernel, the console is both the video display and first | ||
| 76 | serial port. | ||
| 77 | In userspace, the console is file descriptor 1. */ | ||
| 78 | int | ||
| 79 | printf (const char *format, ...) | ||
| 80 | { | ||
| 81 | va_list args; | ||
| 82 | int retval; | ||
| 83 | |||
| 84 | va_start (args, format); | ||
| 85 | retval = vprintf (format, args); | ||
| 86 | va_end (args); | ||
| 87 | |||
| 88 | return retval; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* printf() formatting internals. */ | ||
| 92 | |||
| 93 | /* A printf() conversion. */ | ||
| 94 | struct printf_conversion | ||
| 95 | { | ||
| 96 | /* Flags. */ | ||
| 97 | enum | ||
| 98 | { | ||
| 99 | MINUS = 1 << 0, /* '-' */ | ||
| 100 | PLUS = 1 << 1, /* '+' */ | ||
| 101 | SPACE = 1 << 2, /* ' ' */ | ||
| 102 | POUND = 1 << 3, /* '#' */ | ||
| 103 | ZERO = 1 << 4, /* '0' */ | ||
| 104 | GROUP = 1 << 5 /* '\'' */ | ||
| 105 | } | ||
| 106 | flags; | ||
| 107 | |||
| 108 | /* Minimum field width. */ | ||
| 109 | int width; | ||
| 110 | |||
| 111 | /* Numeric precision. | ||
| 112 | -1 indicates no precision was specified. */ | ||
| 113 | int precision; | ||
| 114 | |||
| 115 | /* Type of argument to format. */ | ||
| 116 | enum | ||
| 117 | { | ||
| 118 | CHAR = 1, /* hh */ | ||
| 119 | SHORT = 2, /* h */ | ||
| 120 | INT = 3, /* (none) */ | ||
| 121 | INTMAX = 4, /* j */ | ||
| 122 | LONG = 5, /* l */ | ||
| 123 | LONGLONG = 6, /* ll */ | ||
| 124 | PTRDIFFT = 7, /* t */ | ||
| 125 | SIZET = 8 /* z */ | ||
| 126 | } | ||
| 127 | type; | ||
| 128 | }; | ||
| 129 | |||
| 130 | struct integer_base | ||
| 131 | { | ||
| 132 | int base; /* Base. */ | ||
| 133 | const char *digits; /* Collection of digits. */ | ||
| 134 | int x; /* `x' character to use, for base 16 only. */ | ||
| 135 | int group; /* Number of digits to group with ' flag. */ | ||
| 136 | }; | ||
| 137 | |||
| 138 | static const struct integer_base base_d = {10, "0123456789", 0, 3}; | ||
| 139 | static const struct integer_base base_o = {8, "01234567", 0, 3}; | ||
| 140 | static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4}; | ||
| 141 | static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4}; | ||
| 142 | |||
| 143 | static const char *parse_conversion (const char *format, | ||
| 144 | struct printf_conversion *, | ||
| 145 | va_list *); | ||
| 146 | static void format_integer (uintmax_t value, bool is_signed, bool negative, | ||
| 147 | const struct integer_base *, | ||
| 148 | const struct printf_conversion *, | ||
| 149 | void (*output) (char, void *), void *aux); | ||
| 150 | static void output_dup (char ch, size_t cnt, | ||
| 151 | void (*output) (char, void *), void *aux); | ||
| 152 | static void format_string (const char *string, int length, | ||
| 153 | struct printf_conversion *, | ||
| 154 | void (*output) (char, void *), void *aux); | ||
| 155 | |||
| 156 | void | ||
| 157 | __vprintf (const char *format, va_list args, | ||
| 158 | void (*output) (char, void *), void *aux) | ||
| 159 | { | ||
| 160 | for (; *format != '\0'; format++) | ||
| 161 | { | ||
| 162 | struct printf_conversion c; | ||
| 163 | |||
| 164 | /* Literally copy non-conversions to output. */ | ||
| 165 | if (*format != '%') | ||
| 166 | { | ||
| 167 | output (*format, aux); | ||
| 168 | continue; | ||
| 169 | } | ||
| 170 | format++; | ||
| 171 | |||
| 172 | /* %% => %. */ | ||
| 173 | if (*format == '%') | ||
| 174 | { | ||
| 175 | output ('%', aux); | ||
| 176 | continue; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Parse conversion specifiers. */ | ||
| 180 | format = parse_conversion (format, &c, &args); | ||
| 181 | |||
| 182 | /* Do conversion. */ | ||
| 183 | switch (*format) | ||
| 184 | { | ||
| 185 | case 'd': | ||
| 186 | case 'i': | ||
| 187 | { | ||
| 188 | /* Signed integer conversions. */ | ||
| 189 | intmax_t value; | ||
| 190 | |||
| 191 | switch (c.type) | ||
| 192 | { | ||
| 193 | case CHAR: | ||
| 194 | value = (signed char) va_arg (args, int); | ||
| 195 | break; | ||
| 196 | case SHORT: | ||
| 197 | value = (short) va_arg (args, int); | ||
| 198 | break; | ||
| 199 | case INT: | ||
| 200 | value = va_arg (args, int); | ||
| 201 | break; | ||
| 202 | case INTMAX: | ||
| 203 | value = va_arg (args, intmax_t); | ||
| 204 | break; | ||
| 205 | case LONG: | ||
| 206 | value = va_arg (args, long); | ||
| 207 | break; | ||
| 208 | case LONGLONG: | ||
| 209 | value = va_arg (args, long long); | ||
| 210 | break; | ||
| 211 | case PTRDIFFT: | ||
| 212 | value = va_arg (args, ptrdiff_t); | ||
| 213 | break; | ||
| 214 | case SIZET: | ||
| 215 | value = va_arg (args, size_t); | ||
| 216 | if (value > SIZE_MAX / 2) | ||
| 217 | value = value - SIZE_MAX - 1; | ||
| 218 | break; | ||
| 219 | default: | ||
| 220 | NOT_REACHED (); | ||
| 221 | } | ||
| 222 | |||
| 223 | format_integer (value < 0 ? -value : value, | ||
| 224 | true, value < 0, &base_d, &c, output, aux); | ||
| 225 | } | ||
| 226 | break; | ||
| 227 | |||
| 228 | case 'o': | ||
| 229 | case 'u': | ||
| 230 | case 'x': | ||
| 231 | case 'X': | ||
| 232 | { | ||
| 233 | /* Unsigned integer conversions. */ | ||
| 234 | uintmax_t value; | ||
| 235 | const struct integer_base *b; | ||
| 236 | |||
| 237 | switch (c.type) | ||
| 238 | { | ||
| 239 | case CHAR: | ||
| 240 | value = (unsigned char) va_arg (args, unsigned); | ||
| 241 | break; | ||
| 242 | case SHORT: | ||
| 243 | value = (unsigned short) va_arg (args, unsigned); | ||
| 244 | break; | ||
| 245 | case INT: | ||
| 246 | value = va_arg (args, unsigned); | ||
| 247 | break; | ||
| 248 | case INTMAX: | ||
| 249 | value = va_arg (args, uintmax_t); | ||
| 250 | break; | ||
| 251 | case LONG: | ||
| 252 | value = va_arg (args, unsigned long); | ||
| 253 | break; | ||
| 254 | case LONGLONG: | ||
| 255 | value = va_arg (args, unsigned long long); | ||
| 256 | break; | ||
| 257 | case PTRDIFFT: | ||
| 258 | value = va_arg (args, ptrdiff_t); | ||
| 259 | #if UINTMAX_MAX != PTRDIFF_MAX | ||
| 260 | value &= ((uintmax_t) PTRDIFF_MAX << 1) | 1; | ||
| 261 | #endif | ||
| 262 | break; | ||
| 263 | case SIZET: | ||
| 264 | value = va_arg (args, size_t); | ||
| 265 | break; | ||
| 266 | default: | ||
| 267 | NOT_REACHED (); | ||
| 268 | } | ||
| 269 | |||
| 270 | switch (*format) | ||
| 271 | { | ||
| 272 | case 'o': b = &base_o; break; | ||
| 273 | case 'u': b = &base_d; break; | ||
| 274 | case 'x': b = &base_x; break; | ||
| 275 | case 'X': b = &base_X; break; | ||
| 276 | default: NOT_REACHED (); | ||
| 277 | } | ||
| 278 | |||
| 279 | format_integer (value, false, false, b, &c, output, aux); | ||
| 280 | } | ||
| 281 | break; | ||
| 282 | |||
| 283 | case 'c': | ||
| 284 | { | ||
| 285 | /* Treat character as single-character string. */ | ||
| 286 | char ch = va_arg (args, int); | ||
| 287 | format_string (&ch, 1, &c, output, aux); | ||
| 288 | } | ||
| 289 | break; | ||
| 290 | |||
| 291 | case 's': | ||
| 292 | { | ||
| 293 | /* String conversion. */ | ||
| 294 | const char *s = va_arg (args, char *); | ||
| 295 | if (s == NULL) | ||
| 296 | s = "(null)"; | ||
| 297 | |||
| 298 | /* Limit string length according to precision. | ||
| 299 | Note: if c.precision == -1 then strnlen() will get | ||
| 300 | SIZE_MAX for MAXLEN, which is just what we want. */ | ||
| 301 | format_string (s, strnlen (s, c.precision), &c, output, aux); | ||
| 302 | } | ||
| 303 | break; | ||
| 304 | |||
| 305 | case 'p': | ||
| 306 | { | ||
| 307 | /* Pointer conversion. | ||
| 308 | Format pointers as %#x. */ | ||
| 309 | void *p = va_arg (args, void *); | ||
| 310 | |||
| 311 | c.flags = POUND; | ||
| 312 | format_integer ((uintptr_t) p, false, false, | ||
| 313 | &base_x, &c, output, aux); | ||
| 314 | } | ||
| 315 | break; | ||
| 316 | |||
| 317 | case 'f': | ||
| 318 | case 'e': | ||
| 319 | case 'E': | ||
| 320 | case 'g': | ||
| 321 | case 'G': | ||
| 322 | case 'n': | ||
| 323 | /* We don't support floating-point arithmetic, | ||
| 324 | and %n can be part of a security hole. */ | ||
| 325 | __printf ("<<no %%%c in kernel>>", output, aux, *format); | ||
| 326 | break; | ||
| 327 | |||
| 328 | default: | ||
| 329 | __printf ("<<no %%%c conversion>>", output, aux, *format); | ||
| 330 | break; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | /* Parses conversion option characters starting at FORMAT and | ||
| 336 | initializes C appropriately. Returns the character in FORMAT | ||
| 337 | that indicates the conversion (e.g. the `d' in `%d'). Uses | ||
| 338 | *ARGS for `*' field widths and precisions. */ | ||
| 339 | static const char * | ||
| 340 | parse_conversion (const char *format, struct printf_conversion *c, | ||
| 341 | va_list *args) | ||
| 342 | { | ||
| 343 | /* Parse flag characters. */ | ||
| 344 | c->flags = 0; | ||
| 345 | for (;;) | ||
| 346 | { | ||
| 347 | switch (*format++) | ||
| 348 | { | ||
| 349 | case '-': | ||
| 350 | c->flags |= MINUS; | ||
| 351 | break; | ||
| 352 | case '+': | ||
| 353 | c->flags |= PLUS; | ||
| 354 | break; | ||
| 355 | case ' ': | ||
| 356 | c->flags |= SPACE; | ||
| 357 | break; | ||
| 358 | case '#': | ||
| 359 | c->flags |= POUND; | ||
| 360 | break; | ||
| 361 | case '0': | ||
| 362 | c->flags |= ZERO; | ||
| 363 | break; | ||
| 364 | case '\'': | ||
| 365 | c->flags |= GROUP; | ||
| 366 | break; | ||
| 367 | default: | ||
| 368 | format--; | ||
| 369 | goto not_a_flag; | ||
| 370 | } | ||
| 371 | } | ||
| 372 | not_a_flag: | ||
| 373 | if (c->flags & MINUS) | ||
| 374 | c->flags &= ~ZERO; | ||
| 375 | if (c->flags & PLUS) | ||
| 376 | c->flags &= ~SPACE; | ||
| 377 | |||
| 378 | /* Parse field width. */ | ||
| 379 | c->width = 0; | ||
| 380 | if (*format == '*') | ||
| 381 | { | ||
| 382 | format++; | ||
| 383 | c->width = va_arg (*args, int); | ||
| 384 | } | ||
| 385 | else | ||
| 386 | { | ||
| 387 | for (; isdigit (*format); format++) | ||
| 388 | c->width = c->width * 10 + *format - '0'; | ||
| 389 | } | ||
| 390 | if (c->width < 0) | ||
| 391 | { | ||
| 392 | c->width = -c->width; | ||
| 393 | c->flags |= MINUS; | ||
| 394 | } | ||
| 395 | |||
| 396 | /* Parse precision. */ | ||
| 397 | c->precision = -1; | ||
| 398 | if (*format == '.') | ||
| 399 | { | ||
| 400 | format++; | ||
| 401 | if (*format == '*') | ||
| 402 | { | ||
| 403 | format++; | ||
| 404 | c->precision = va_arg (*args, int); | ||
| 405 | } | ||
| 406 | else | ||
| 407 | { | ||
| 408 | c->precision = 0; | ||
| 409 | for (; isdigit (*format); format++) | ||
| 410 | c->precision = c->precision * 10 + *format - '0'; | ||
| 411 | } | ||
| 412 | if (c->precision < 0) | ||
| 413 | c->precision = -1; | ||
| 414 | } | ||
| 415 | if (c->precision >= 0) | ||
| 416 | c->flags &= ~ZERO; | ||
| 417 | |||
| 418 | /* Parse type. */ | ||
| 419 | c->type = INT; | ||
| 420 | switch (*format++) | ||
| 421 | { | ||
| 422 | case 'h': | ||
| 423 | if (*format == 'h') | ||
| 424 | { | ||
| 425 | format++; | ||
| 426 | c->type = CHAR; | ||
| 427 | } | ||
| 428 | else | ||
| 429 | c->type = SHORT; | ||
| 430 | break; | ||
| 431 | |||
| 432 | case 'j': | ||
| 433 | c->type = INTMAX; | ||
| 434 | break; | ||
| 435 | |||
| 436 | case 'l': | ||
| 437 | if (*format == 'l') | ||
| 438 | { | ||
| 439 | format++; | ||
| 440 | c->type = LONGLONG; | ||
| 441 | } | ||
| 442 | else | ||
| 443 | c->type = LONG; | ||
| 444 | break; | ||
| 445 | |||
| 446 | case 't': | ||
| 447 | c->type = PTRDIFFT; | ||
| 448 | break; | ||
| 449 | |||
| 450 | case 'z': | ||
| 451 | c->type = SIZET; | ||
| 452 | break; | ||
| 453 | |||
| 454 | default: | ||
| 455 | format--; | ||
| 456 | break; | ||
| 457 | } | ||
| 458 | |||
| 459 | return format; | ||
| 460 | } | ||
| 461 | |||
| 462 | /* Performs an integer conversion, writing output to OUTPUT with | ||
| 463 | auxiliary data AUX. The integer converted has absolute value | ||
| 464 | VALUE. If IS_SIGNED is true, does a signed conversion with | ||
| 465 | NEGATIVE indicating a negative value; otherwise does an | ||
| 466 | unsigned conversion and ignores NEGATIVE. The output is done | ||
| 467 | according to the provided base B. Details of the conversion | ||
| 468 | are in C. */ | ||
| 469 | static void | ||
| 470 | format_integer (uintmax_t value, bool is_signed, bool negative, | ||
| 471 | const struct integer_base *b, | ||
| 472 | const struct printf_conversion *c, | ||
| 473 | void (*output) (char, void *), void *aux) | ||
| 474 | { | ||
| 475 | char buf[64], *cp; /* Buffer and current position. */ | ||
| 476 | int x; /* `x' character to use or 0 if none. */ | ||
| 477 | int sign; /* Sign character or 0 if none. */ | ||
| 478 | int precision; /* Rendered precision. */ | ||
| 479 | int pad_cnt; /* # of pad characters to fill field width. */ | ||
| 480 | int digit_cnt; /* # of digits output so far. */ | ||
| 481 | |||
| 482 | /* Determine sign character, if any. | ||
| 483 | An unsigned conversion will never have a sign character, | ||
| 484 | even if one of the flags requests one. */ | ||
| 485 | sign = 0; | ||
| 486 | if (is_signed) | ||
| 487 | { | ||
| 488 | if (c->flags & PLUS) | ||
| 489 | sign = negative ? '-' : '+'; | ||
| 490 | else if (c->flags & SPACE) | ||
| 491 | sign = negative ? '-' : ' '; | ||
| 492 | else if (negative) | ||
| 493 | sign = '-'; | ||
| 494 | } | ||
| 495 | |||
| 496 | /* Determine whether to include `0x' or `0X'. | ||
| 497 | It will only be included with a hexadecimal conversion of a | ||
| 498 | nonzero value with the # flag. */ | ||
| 499 | x = (c->flags & POUND) && value ? b->x : 0; | ||
| 500 | |||
| 501 | /* Accumulate digits into buffer. | ||
| 502 | This algorithm produces digits in reverse order, so later we | ||
| 503 | will output the buffer's content in reverse. */ | ||
| 504 | cp = buf; | ||
| 505 | digit_cnt = 0; | ||
| 506 | while (value > 0) | ||
| 507 | { | ||
| 508 | if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0) | ||
| 509 | *cp++ = ','; | ||
| 510 | *cp++ = b->digits[value % b->base]; | ||
| 511 | value /= b->base; | ||
| 512 | digit_cnt++; | ||
| 513 | } | ||
| 514 | |||
| 515 | /* Append enough zeros to match precision. | ||
| 516 | If requested precision is 0, then a value of zero is | ||
| 517 | rendered as a null string, otherwise as "0". | ||
| 518 | If the # flag is used with base 8, the result must always | ||
| 519 | begin with a zero. */ | ||
| 520 | precision = c->precision < 0 ? 1 : c->precision; | ||
| 521 | while (cp - buf < precision && cp < buf + sizeof buf - 1) | ||
| 522 | *cp++ = '0'; | ||
| 523 | if ((c->flags & POUND) && b->base == 8 && (cp == buf || cp[-1] != '0')) | ||
| 524 | *cp++ = '0'; | ||
| 525 | |||
| 526 | /* Calculate number of pad characters to fill field width. */ | ||
| 527 | pad_cnt = c->width - (cp - buf) - (x ? 2 : 0) - (sign != 0); | ||
| 528 | if (pad_cnt < 0) | ||
| 529 | pad_cnt = 0; | ||
| 530 | |||
| 531 | /* Do output. */ | ||
| 532 | if ((c->flags & (MINUS | ZERO)) == 0) | ||
| 533 | output_dup (' ', pad_cnt, output, aux); | ||
| 534 | if (sign) | ||
| 535 | output (sign, aux); | ||
| 536 | if (x) | ||
| 537 | { | ||
| 538 | output ('0', aux); | ||
| 539 | output (x, aux); | ||
| 540 | } | ||
| 541 | if (c->flags & ZERO) | ||
| 542 | output_dup ('0', pad_cnt, output, aux); | ||
| 543 | while (cp > buf) | ||
| 544 | output (*--cp, aux); | ||
| 545 | if (c->flags & MINUS) | ||
| 546 | output_dup (' ', pad_cnt, output, aux); | ||
| 547 | } | ||
| 548 | |||
| 549 | /* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */ | ||
| 550 | static void | ||
| 551 | output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) | ||
| 552 | { | ||
| 553 | while (cnt-- > 0) | ||
| 554 | output (ch, aux); | ||
| 555 | } | ||
| 556 | |||
| 557 | /* Formats the LENGTH characters starting at STRING according to | ||
| 558 | the conversion specified in C. Writes output to OUTPUT with | ||
| 559 | auxiliary data AUX. */ | ||
| 560 | static void | ||
| 561 | format_string (const char *string, int length, | ||
| 562 | struct printf_conversion *c, | ||
| 563 | void (*output) (char, void *), void *aux) | ||
| 564 | { | ||
| 565 | int i; | ||
| 566 | if (c->width > length && (c->flags & MINUS) == 0) | ||
| 567 | output_dup (' ', c->width - length, output, aux); | ||
| 568 | for (i = 0; i < length; i++) | ||
| 569 | output (string[i], aux); | ||
| 570 | if (c->width > length && (c->flags & MINUS) != 0) | ||
| 571 | output_dup (' ', c->width - length, output, aux); | ||
| 572 | } | ||
| 573 | |||
| 574 | /* Wrapper for __vprintf() that converts varargs into a | ||
| 575 | va_list. */ | ||
| 576 | void | ||
| 577 | __printf (const char *format, | ||
| 578 | void (*output) (char, void *), void *aux, ...) | ||
| 579 | { | ||
| 580 | va_list args; | ||
| 581 | |||
| 582 | va_start (args, aux); | ||
| 583 | __vprintf (format, args, output, aux); | ||
| 584 | va_end (args); | ||
| 585 | } | ||
| 586 | |||
| 587 | /* Dumps the SIZE bytes in BUF to the console as hex bytes | ||
| 588 | arranged 16 per line. Numeric offsets are also included, | ||
| 589 | starting at OFS for the first byte in BUF. If ASCII is true | ||
| 590 | then the corresponding ASCII characters are also rendered | ||
| 591 | alongside. */ | ||
| 592 | void | ||
| 593 | hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii) | ||
| 594 | { | ||
| 595 | const uint8_t *buf = buf_; | ||
| 596 | const size_t per_line = 16; /* Maximum bytes per line. */ | ||
| 597 | |||
| 598 | while (size > 0) | ||
| 599 | { | ||
| 600 | size_t start, end, n; | ||
| 601 | size_t i; | ||
| 602 | |||
| 603 | /* Number of bytes on this line. */ | ||
| 604 | start = ofs % per_line; | ||
| 605 | end = per_line; | ||
| 606 | if (end - start > size) | ||
| 607 | end = start + size; | ||
| 608 | n = end - start; | ||
| 609 | |||
| 610 | /* Print line. */ | ||
| 611 | printf ("%08jx ", (uintmax_t) ROUND_DOWN (ofs, per_line)); | ||
| 612 | for (i = 0; i < start; i++) | ||
| 613 | printf (" "); | ||
| 614 | for (; i < end; i++) | ||
| 615 | printf ("%02hhx%c", | ||
| 616 | buf[i - start], i == per_line / 2 - 1? '-' : ' '); | ||
| 617 | if (ascii) | ||
| 618 | { | ||
| 619 | for (; i < per_line; i++) | ||
| 620 | printf (" "); | ||
| 621 | printf ("|"); | ||
| 622 | for (i = 0; i < start; i++) | ||
| 623 | printf (" "); | ||
| 624 | for (; i < end; i++) | ||
| 625 | printf ("%c", | ||
| 626 | isprint (buf[i - start]) ? buf[i - start] : '.'); | ||
| 627 | for (; i < per_line; i++) | ||
| 628 | printf (" "); | ||
| 629 | printf ("|"); | ||
| 630 | } | ||
| 631 | printf ("\n"); | ||
| 632 | |||
| 633 | ofs += n; | ||
| 634 | buf += n; | ||
| 635 | size -= n; | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | /* Prints SIZE, which represents a number of bytes, in a | ||
| 640 | human-readable format, e.g. "256 kB". */ | ||
| 641 | void | ||
| 642 | print_human_readable_size (uint64_t size) | ||
| 643 | { | ||
| 644 | if (size == 1) | ||
| 645 | printf ("1 byte"); | ||
| 646 | else | ||
| 647 | { | ||
| 648 | static const char *factors[] = {"bytes", "kB", "MB", "GB", "TB", NULL}; | ||
| 649 | const char **fp; | ||
| 650 | |||
| 651 | for (fp = factors; size >= 1024 && fp[1] != NULL; fp++) | ||
| 652 | size /= 1024; | ||
| 653 | printf ("%"PRIu64" %s", size, *fp); | ||
| 654 | } | ||
| 655 | } | ||
diff --git a/pintos-progos/lib/stdio.h b/pintos-progos/lib/stdio.h new file mode 100644 index 0000000..2739c0a --- /dev/null +++ b/pintos-progos/lib/stdio.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #ifndef __LIB_STDIO_H | ||
| 2 | #define __LIB_STDIO_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdarg.h> | ||
| 6 | #include <stdbool.h> | ||
| 7 | #include <stddef.h> | ||
| 8 | #include <stdint.h> | ||
| 9 | |||
| 10 | /* Include lib/user/stdio.h or lib/kernel/stdio.h, as | ||
| 11 | appropriate. */ | ||
| 12 | #include_next <stdio.h> | ||
| 13 | |||
| 14 | /* Predefined file handles. */ | ||
| 15 | #define STDIN_FILENO 0 | ||
| 16 | #define STDOUT_FILENO 1 | ||
| 17 | |||
| 18 | /* Standard functions. */ | ||
| 19 | int printf (const char *, ...) PRINTF_FORMAT (1, 2); | ||
| 20 | int snprintf (char *, size_t, const char *, ...) PRINTF_FORMAT (3, 4); | ||
| 21 | int vprintf (const char *, va_list) PRINTF_FORMAT (1, 0); | ||
| 22 | int vsnprintf (char *, size_t, const char *, va_list) PRINTF_FORMAT (3, 0); | ||
| 23 | int putchar (int); | ||
| 24 | int puts (const char *); | ||
| 25 | |||
| 26 | /* Nonstandard functions. */ | ||
| 27 | void hex_dump (uintptr_t ofs, const void *, size_t size, bool ascii); | ||
| 28 | void print_human_readable_size (uint64_t sz); | ||
| 29 | |||
| 30 | /* Internal functions. */ | ||
| 31 | void __vprintf (const char *format, va_list args, | ||
| 32 | void (*output) (char, void *), void *aux); | ||
| 33 | void __printf (const char *format, | ||
| 34 | void (*output) (char, void *), void *aux, ...); | ||
| 35 | |||
| 36 | /* Try to be helpful. */ | ||
| 37 | #define sprintf dont_use_sprintf_use_snprintf | ||
| 38 | #define vsprintf dont_use_vsprintf_use_vsnprintf | ||
| 39 | |||
| 40 | #endif /* lib/stdio.h */ | ||
diff --git a/pintos-progos/lib/stdlib.c b/pintos-progos/lib/stdlib.c new file mode 100644 index 0000000..84c7f61 --- /dev/null +++ b/pintos-progos/lib/stdlib.c | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | #include <ctype.h> | ||
| 2 | #include <debug.h> | ||
| 3 | #include <random.h> | ||
| 4 | #include <stdlib.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | |||
| 7 | /* Converts a string representation of a signed decimal integer | ||
| 8 | in S into an `int', which is returned. */ | ||
| 9 | int | ||
| 10 | atoi (const char *s) | ||
| 11 | { | ||
| 12 | bool negative; | ||
| 13 | int value; | ||
| 14 | |||
| 15 | ASSERT (s != NULL); | ||
| 16 | |||
| 17 | /* Skip white space. */ | ||
| 18 | while (isspace ((unsigned char) *s)) | ||
| 19 | s++; | ||
| 20 | |||
| 21 | /* Parse sign. */ | ||
| 22 | negative = false; | ||
| 23 | if (*s == '+') | ||
| 24 | s++; | ||
| 25 | else if (*s == '-') | ||
| 26 | { | ||
| 27 | negative = true; | ||
| 28 | s++; | ||
| 29 | } | ||
| 30 | |||
| 31 | /* Parse digits. We always initially parse the value as | ||
| 32 | negative, and then make it positive later, because the | ||
| 33 | negative range of an int is bigger than the positive range | ||
| 34 | on a 2's complement system. */ | ||
| 35 | for (value = 0; isdigit (*s); s++) | ||
| 36 | value = value * 10 - (*s - '0'); | ||
| 37 | if (!negative) | ||
| 38 | value = -value; | ||
| 39 | |||
| 40 | return value; | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Compares A and B by calling the AUX function. */ | ||
| 44 | static int | ||
| 45 | compare_thunk (const void *a, const void *b, void *aux) | ||
| 46 | { | ||
| 47 | int (**compare) (const void *, const void *) = aux; | ||
| 48 | return (*compare) (a, b); | ||
| 49 | } | ||
| 50 | |||
| 51 | /* Sorts ARRAY, which contains CNT elements of SIZE bytes each, | ||
| 52 | using COMPARE. When COMPARE is passed a pair of elements A | ||
| 53 | and B, respectively, it must return a strcmp()-type result, | ||
| 54 | i.e. less than zero if A < B, zero if A == B, greater than | ||
| 55 | zero if A > B. Runs in O(n lg n) time and O(1) space in | ||
| 56 | CNT. */ | ||
| 57 | void | ||
| 58 | qsort (void *array, size_t cnt, size_t size, | ||
| 59 | int (*compare) (const void *, const void *)) | ||
| 60 | { | ||
| 61 | sort (array, cnt, size, compare_thunk, &compare); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Swaps elements with 1-based indexes A_IDX and B_IDX in ARRAY | ||
| 65 | with elements of SIZE bytes each. */ | ||
| 66 | static void | ||
| 67 | do_swap (unsigned char *array, size_t a_idx, size_t b_idx, size_t size) | ||
| 68 | { | ||
| 69 | unsigned char *a = array + (a_idx - 1) * size; | ||
| 70 | unsigned char *b = array + (b_idx - 1) * size; | ||
| 71 | size_t i; | ||
| 72 | |||
| 73 | for (i = 0; i < size; i++) | ||
| 74 | { | ||
| 75 | unsigned char t = a[i]; | ||
| 76 | a[i] = b[i]; | ||
| 77 | b[i] = t; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | /* Compares elements with 1-based indexes A_IDX and B_IDX in | ||
| 82 | ARRAY with elements of SIZE bytes each, using COMPARE to | ||
| 83 | compare elements, passing AUX as auxiliary data, and returns a | ||
| 84 | strcmp()-type result. */ | ||
| 85 | static int | ||
| 86 | do_compare (unsigned char *array, size_t a_idx, size_t b_idx, size_t size, | ||
| 87 | int (*compare) (const void *, const void *, void *aux), | ||
| 88 | void *aux) | ||
| 89 | { | ||
| 90 | return compare (array + (a_idx - 1) * size, array + (b_idx - 1) * size, aux); | ||
| 91 | } | ||
| 92 | |||
| 93 | /* "Float down" the element with 1-based index I in ARRAY of CNT | ||
| 94 | elements of SIZE bytes each, using COMPARE to compare | ||
| 95 | elements, passing AUX as auxiliary data. */ | ||
| 96 | static void | ||
| 97 | heapify (unsigned char *array, size_t i, size_t cnt, size_t size, | ||
| 98 | int (*compare) (const void *, const void *, void *aux), | ||
| 99 | void *aux) | ||
| 100 | { | ||
| 101 | for (;;) | ||
| 102 | { | ||
| 103 | /* Set `max' to the index of the largest element among I | ||
| 104 | and its children (if any). */ | ||
| 105 | size_t left = 2 * i; | ||
| 106 | size_t right = 2 * i + 1; | ||
| 107 | size_t max = i; | ||
| 108 | if (left <= cnt && do_compare (array, left, max, size, compare, aux) > 0) | ||
| 109 | max = left; | ||
| 110 | if (right <= cnt | ||
| 111 | && do_compare (array, right, max, size, compare, aux) > 0) | ||
| 112 | max = right; | ||
| 113 | |||
| 114 | /* If the maximum value is already in element I, we're | ||
| 115 | done. */ | ||
| 116 | if (max == i) | ||
| 117 | break; | ||
| 118 | |||
| 119 | /* Swap and continue down the heap. */ | ||
| 120 | do_swap (array, i, max, size); | ||
| 121 | i = max; | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | /* Sorts ARRAY, which contains CNT elements of SIZE bytes each, | ||
| 126 | using COMPARE to compare elements, passing AUX as auxiliary | ||
| 127 | data. When COMPARE is passed a pair of elements A and B, | ||
| 128 | respectively, it must return a strcmp()-type result, i.e. less | ||
| 129 | than zero if A < B, zero if A == B, greater than zero if A > | ||
| 130 | B. Runs in O(n lg n) time and O(1) space in CNT. */ | ||
| 131 | void | ||
| 132 | sort (void *array, size_t cnt, size_t size, | ||
| 133 | int (*compare) (const void *, const void *, void *aux), | ||
| 134 | void *aux) | ||
| 135 | { | ||
| 136 | size_t i; | ||
| 137 | |||
| 138 | ASSERT (array != NULL || cnt == 0); | ||
| 139 | ASSERT (compare != NULL); | ||
| 140 | ASSERT (size > 0); | ||
| 141 | |||
| 142 | /* Build a heap. */ | ||
| 143 | for (i = cnt / 2; i > 0; i--) | ||
| 144 | heapify (array, i, cnt, size, compare, aux); | ||
| 145 | |||
| 146 | /* Sort the heap. */ | ||
| 147 | for (i = cnt; i > 1; i--) | ||
| 148 | { | ||
| 149 | do_swap (array, 1, i, size); | ||
| 150 | heapify (array, 1, i - 1, size, compare, aux); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | /* Searches ARRAY, which contains CNT elements of SIZE bytes | ||
| 155 | each, for the given KEY. Returns a match is found, otherwise | ||
| 156 | a null pointer. If there are multiple matches, returns an | ||
| 157 | arbitrary one of them. | ||
| 158 | |||
| 159 | ARRAY must be sorted in order according to COMPARE. | ||
| 160 | |||
| 161 | Uses COMPARE to compare elements. When COMPARE is passed a | ||
| 162 | pair of elements A and B, respectively, it must return a | ||
| 163 | strcmp()-type result, i.e. less than zero if A < B, zero if A | ||
| 164 | == B, greater than zero if A > B. */ | ||
| 165 | void * | ||
| 166 | bsearch (const void *key, const void *array, size_t cnt, | ||
| 167 | size_t size, int (*compare) (const void *, const void *)) | ||
| 168 | { | ||
| 169 | return binary_search (key, array, cnt, size, compare_thunk, &compare); | ||
| 170 | } | ||
| 171 | |||
| 172 | /* Searches ARRAY, which contains CNT elements of SIZE bytes | ||
| 173 | each, for the given KEY. Returns a match is found, otherwise | ||
| 174 | a null pointer. If there are multiple matches, returns an | ||
| 175 | arbitrary one of them. | ||
| 176 | |||
| 177 | ARRAY must be sorted in order according to COMPARE. | ||
| 178 | |||
| 179 | Uses COMPARE to compare elements, passing AUX as auxiliary | ||
| 180 | data. When COMPARE is passed a pair of elements A and B, | ||
| 181 | respectively, it must return a strcmp()-type result, i.e. less | ||
| 182 | than zero if A < B, zero if A == B, greater than zero if A > | ||
| 183 | B. */ | ||
| 184 | void * | ||
| 185 | binary_search (const void *key, const void *array, size_t cnt, size_t size, | ||
| 186 | int (*compare) (const void *, const void *, void *aux), | ||
| 187 | void *aux) | ||
| 188 | { | ||
| 189 | const unsigned char *first = array; | ||
| 190 | const unsigned char *last = array + size * cnt; | ||
| 191 | |||
| 192 | while (first < last) | ||
| 193 | { | ||
| 194 | size_t range = (last - first) / size; | ||
| 195 | const unsigned char *middle = first + (range / 2) * size; | ||
| 196 | int cmp = compare (key, middle, aux); | ||
| 197 | |||
| 198 | if (cmp < 0) | ||
| 199 | last = middle; | ||
| 200 | else if (cmp > 0) | ||
| 201 | first = middle + size; | ||
| 202 | else | ||
| 203 | return (void *) middle; | ||
| 204 | } | ||
| 205 | |||
| 206 | return NULL; | ||
| 207 | } | ||
| 208 | |||
diff --git a/pintos-progos/lib/stdlib.h b/pintos-progos/lib/stdlib.h new file mode 100644 index 0000000..d14afa3 --- /dev/null +++ b/pintos-progos/lib/stdlib.h | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | #ifndef __LIB_STDLIB_H | ||
| 2 | #define __LIB_STDLIB_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | /* Standard functions. */ | ||
| 7 | int atoi (const char *); | ||
| 8 | void qsort (void *array, size_t cnt, size_t size, | ||
| 9 | int (*compare) (const void *, const void *)); | ||
| 10 | void *bsearch (const void *key, const void *array, size_t cnt, | ||
| 11 | size_t size, int (*compare) (const void *, const void *)); | ||
| 12 | |||
| 13 | /* Nonstandard functions. */ | ||
| 14 | void sort (void *array, size_t cnt, size_t size, | ||
| 15 | int (*compare) (const void *, const void *, void *aux), | ||
| 16 | void *aux); | ||
| 17 | void *binary_search (const void *key, const void *array, size_t cnt, | ||
| 18 | size_t size, | ||
| 19 | int (*compare) (const void *, const void *, void *aux), | ||
| 20 | void *aux); | ||
| 21 | |||
| 22 | #endif /* lib/stdlib.h */ | ||
diff --git a/pintos-progos/lib/string.c b/pintos-progos/lib/string.c new file mode 100644 index 0000000..d223c89 --- /dev/null +++ b/pintos-progos/lib/string.c | |||
| @@ -0,0 +1,375 @@ | |||
| 1 | #include <string.h> | ||
| 2 | #include <debug.h> | ||
| 3 | |||
| 4 | /* Copies SIZE bytes from SRC to DST, which must not overlap. | ||
| 5 | Returns DST. */ | ||
| 6 | void * | ||
| 7 | memcpy (void *dst_, const void *src_, size_t size) | ||
| 8 | { | ||
| 9 | unsigned char *dst = dst_; | ||
| 10 | const unsigned char *src = src_; | ||
| 11 | |||
| 12 | ASSERT (dst != NULL || size == 0); | ||
| 13 | ASSERT (src != NULL || size == 0); | ||
| 14 | |||
| 15 | while (size-- > 0) | ||
| 16 | *dst++ = *src++; | ||
| 17 | |||
| 18 | return dst_; | ||
| 19 | } | ||
| 20 | |||
| 21 | /* Copies SIZE bytes from SRC to DST, which are allowed to | ||
| 22 | overlap. Returns DST. */ | ||
| 23 | void * | ||
| 24 | memmove (void *dst_, const void *src_, size_t size) | ||
| 25 | { | ||
| 26 | unsigned char *dst = dst_; | ||
| 27 | const unsigned char *src = src_; | ||
| 28 | |||
| 29 | ASSERT (dst != NULL || size == 0); | ||
| 30 | ASSERT (src != NULL || size == 0); | ||
| 31 | |||
| 32 | if (dst < src) | ||
| 33 | { | ||
| 34 | while (size-- > 0) | ||
| 35 | *dst++ = *src++; | ||
| 36 | } | ||
| 37 | else | ||
| 38 | { | ||
| 39 | dst += size; | ||
| 40 | src += size; | ||
| 41 | while (size-- > 0) | ||
| 42 | *--dst = *--src; | ||
| 43 | } | ||
| 44 | |||
| 45 | return dst; | ||
| 46 | } | ||
| 47 | |||
| 48 | /* Find the first differing byte in the two blocks of SIZE bytes | ||
| 49 | at A and B. Returns a positive value if the byte in A is | ||
| 50 | greater, a negative value if the byte in B is greater, or zero | ||
| 51 | if blocks A and B are equal. */ | ||
| 52 | int | ||
| 53 | memcmp (const void *a_, const void *b_, size_t size) | ||
| 54 | { | ||
| 55 | const unsigned char *a = a_; | ||
| 56 | const unsigned char *b = b_; | ||
| 57 | |||
| 58 | ASSERT (a != NULL || size == 0); | ||
| 59 | ASSERT (b != NULL || size == 0); | ||
| 60 | |||
| 61 | for (; size-- > 0; a++, b++) | ||
| 62 | if (*a != *b) | ||
| 63 | return *a > *b ? +1 : -1; | ||
| 64 | return 0; | ||
| 65 | } | ||
| 66 | |||
| 67 | /* Finds the first differing characters in strings A and B. | ||
| 68 | Returns a positive value if the character in A (as an unsigned | ||
| 69 | char) is greater, a negative value if the character in B (as | ||
| 70 | an unsigned char) is greater, or zero if strings A and B are | ||
| 71 | equal. */ | ||
| 72 | int | ||
| 73 | strcmp (const char *a_, const char *b_) | ||
| 74 | { | ||
| 75 | const unsigned char *a = (const unsigned char *) a_; | ||
| 76 | const unsigned char *b = (const unsigned char *) b_; | ||
| 77 | |||
| 78 | ASSERT (a != NULL); | ||
| 79 | ASSERT (b != NULL); | ||
| 80 | |||
| 81 | while (*a != '\0' && *a == *b) | ||
| 82 | { | ||
| 83 | a++; | ||
| 84 | b++; | ||
| 85 | } | ||
| 86 | |||
| 87 | return *a < *b ? -1 : *a > *b; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* Returns a pointer to the first occurrence of CH in the first | ||
| 91 | SIZE bytes starting at BLOCK. Returns a null pointer if CH | ||
| 92 | does not occur in BLOCK. */ | ||
| 93 | void * | ||
| 94 | memchr (const void *block_, int ch_, size_t size) | ||
| 95 | { | ||
| 96 | const unsigned char *block = block_; | ||
| 97 | unsigned char ch = ch_; | ||
| 98 | |||
| 99 | ASSERT (block != NULL || size == 0); | ||
| 100 | |||
| 101 | for (; size-- > 0; block++) | ||
| 102 | if (*block == ch) | ||
| 103 | return (void *) block; | ||
| 104 | |||
| 105 | return NULL; | ||
| 106 | } | ||
| 107 | |||
| 108 | /* Finds and returns the first occurrence of C in STRING, or a | ||
| 109 | null pointer if C does not appear in STRING. If C == '\0' | ||
| 110 | then returns a pointer to the null terminator at the end of | ||
| 111 | STRING. */ | ||
| 112 | char * | ||
| 113 | strchr (const char *string, int c_) | ||
| 114 | { | ||
| 115 | char c = c_; | ||
| 116 | |||
| 117 | ASSERT (string != NULL); | ||
| 118 | |||
| 119 | for (;;) | ||
| 120 | if (*string == c) | ||
| 121 | return (char *) string; | ||
| 122 | else if (*string == '\0') | ||
| 123 | return NULL; | ||
| 124 | else | ||
| 125 | string++; | ||
| 126 | } | ||
| 127 | |||
| 128 | /* Returns the length of the initial substring of STRING that | ||
| 129 | consists of characters that are not in STOP. */ | ||
| 130 | size_t | ||
| 131 | strcspn (const char *string, const char *stop) | ||
| 132 | { | ||
| 133 | size_t length; | ||
| 134 | |||
| 135 | for (length = 0; string[length] != '\0'; length++) | ||
| 136 | if (strchr (stop, string[length]) != NULL) | ||
| 137 | break; | ||
| 138 | return length; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* Returns a pointer to the first character in STRING that is | ||
| 142 | also in STOP. If no character in STRING is in STOP, returns a | ||
| 143 | null pointer. */ | ||
| 144 | char * | ||
| 145 | strpbrk (const char *string, const char *stop) | ||
| 146 | { | ||
| 147 | for (; *string != '\0'; string++) | ||
| 148 | if (strchr (stop, *string) != NULL) | ||
| 149 | return (char *) string; | ||
| 150 | return NULL; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* Returns a pointer to the last occurrence of C in STRING. | ||
| 154 | Returns a null pointer if C does not occur in STRING. */ | ||
| 155 | char * | ||
| 156 | strrchr (const char *string, int c_) | ||
| 157 | { | ||
| 158 | char c = c_; | ||
| 159 | const char *p = NULL; | ||
| 160 | |||
| 161 | for (; *string != '\0'; string++) | ||
| 162 | if (*string == c) | ||
| 163 | p = string; | ||
| 164 | return (char *) p; | ||
| 165 | } | ||
| 166 | |||
| 167 | /* Returns the length of the initial substring of STRING that | ||
| 168 | consists of characters in SKIP. */ | ||
| 169 | size_t | ||
| 170 | strspn (const char *string, const char *skip) | ||
| 171 | { | ||
| 172 | size_t length; | ||
| 173 | |||
| 174 | for (length = 0; string[length] != '\0'; length++) | ||
| 175 | if (strchr (skip, string[length]) == NULL) | ||
| 176 | break; | ||
| 177 | return length; | ||
| 178 | } | ||
| 179 | |||
| 180 | /* Returns a pointer to the first occurrence of NEEDLE within | ||
| 181 | HAYSTACK. Returns a null pointer if NEEDLE does not exist | ||
| 182 | within HAYSTACK. */ | ||
| 183 | char * | ||
| 184 | strstr (const char *haystack, const char *needle) | ||
| 185 | { | ||
| 186 | size_t haystack_len = strlen (haystack); | ||
| 187 | size_t needle_len = strlen (needle); | ||
| 188 | |||
| 189 | if (haystack_len >= needle_len) | ||
| 190 | { | ||
| 191 | size_t i; | ||
| 192 | |||
| 193 | for (i = 0; i <= haystack_len - needle_len; i++) | ||
| 194 | if (!memcmp (haystack + i, needle, needle_len)) | ||
| 195 | return (char *) haystack + i; | ||
| 196 | } | ||
| 197 | |||
| 198 | return NULL; | ||
| 199 | } | ||
| 200 | |||
| 201 | /* Breaks a string into tokens separated by DELIMITERS. The | ||
| 202 | first time this function is called, S should be the string to | ||
| 203 | tokenize, and in subsequent calls it must be a null pointer. | ||
| 204 | SAVE_PTR is the address of a `char *' variable used to keep | ||
| 205 | track of the tokenizer's position. The return value each time | ||
| 206 | is the next token in the string, or a null pointer if no | ||
| 207 | tokens remain. | ||
| 208 | |||
| 209 | This function treats multiple adjacent delimiters as a single | ||
| 210 | delimiter. The returned tokens will never be length 0. | ||
| 211 | DELIMITERS may change from one call to the next within a | ||
| 212 | single string. | ||
| 213 | |||
| 214 | strtok_r() modifies the string S, changing delimiters to null | ||
| 215 | bytes. Thus, S must be a modifiable string. String literals, | ||
| 216 | in particular, are *not* modifiable in C, even though for | ||
| 217 | backward compatibility they are not `const'. | ||
| 218 | |||
| 219 | Example usage: | ||
| 220 | |||
| 221 | char s[] = " String to tokenize. "; | ||
| 222 | char *token, *save_ptr; | ||
| 223 | |||
| 224 | for (token = strtok_r (s, " ", &save_ptr); token != NULL; | ||
| 225 | token = strtok_r (NULL, " ", &save_ptr)) | ||
| 226 | printf ("'%s'\n", token); | ||
| 227 | |||
| 228 | outputs: | ||
| 229 | |||
| 230 | 'String' | ||
| 231 | 'to' | ||
| 232 | 'tokenize.' | ||
| 233 | */ | ||
| 234 | char * | ||
| 235 | strtok_r (char *s, const char *delimiters, char **save_ptr) | ||
| 236 | { | ||
| 237 | char *token; | ||
| 238 | |||
| 239 | ASSERT (delimiters != NULL); | ||
| 240 | ASSERT (save_ptr != NULL); | ||
| 241 | |||
| 242 | /* If S is nonnull, start from it. | ||
| 243 | If S is null, start from saved position. */ | ||
| 244 | if (s == NULL) | ||
| 245 | s = *save_ptr; | ||
| 246 | ASSERT (s != NULL); | ||
| 247 | |||
| 248 | /* Skip any DELIMITERS at our current position. */ | ||
| 249 | while (strchr (delimiters, *s) != NULL) | ||
| 250 | { | ||
| 251 | /* strchr() will always return nonnull if we're searching | ||
| 252 | for a null byte, because every string contains a null | ||
| 253 | byte (at the end). */ | ||
| 254 | if (*s == '\0') | ||
| 255 | { | ||
| 256 | *save_ptr = s; | ||
| 257 | return NULL; | ||
| 258 | } | ||
| 259 | |||
| 260 | s++; | ||
| 261 | } | ||
| 262 | |||
| 263 | /* Skip any non-DELIMITERS up to the end of the string. */ | ||
| 264 | token = s; | ||
| 265 | while (strchr (delimiters, *s) == NULL) | ||
| 266 | s++; | ||
| 267 | if (*s != '\0') | ||
| 268 | { | ||
| 269 | *s = '\0'; | ||
| 270 | *save_ptr = s + 1; | ||
| 271 | } | ||
| 272 | else | ||
| 273 | *save_ptr = s; | ||
| 274 | return token; | ||
| 275 | } | ||
| 276 | |||
| 277 | /* Sets the SIZE bytes in DST to VALUE. */ | ||
| 278 | void * | ||
| 279 | memset (void *dst_, int value, size_t size) | ||
| 280 | { | ||
| 281 | unsigned char *dst = dst_; | ||
| 282 | |||
| 283 | ASSERT (dst != NULL || size == 0); | ||
| 284 | |||
| 285 | while (size-- > 0) | ||
| 286 | *dst++ = value; | ||
| 287 | |||
| 288 | return dst_; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* Returns the length of STRING. */ | ||
| 292 | size_t | ||
| 293 | strlen (const char *string) | ||
| 294 | { | ||
| 295 | const char *p; | ||
| 296 | |||
| 297 | ASSERT (string != NULL); | ||
| 298 | |||
| 299 | for (p = string; *p != '\0'; p++) | ||
| 300 | continue; | ||
| 301 | return p - string; | ||
| 302 | } | ||
| 303 | |||
| 304 | /* If STRING is less than MAXLEN characters in length, returns | ||
| 305 | its actual length. Otherwise, returns MAXLEN. */ | ||
| 306 | size_t | ||
| 307 | strnlen (const char *string, size_t maxlen) | ||
| 308 | { | ||
| 309 | size_t length; | ||
| 310 | |||
| 311 | for (length = 0; string[length] != '\0' && length < maxlen; length++) | ||
| 312 | continue; | ||
| 313 | return length; | ||
| 314 | } | ||
| 315 | |||
| 316 | /* Copies string SRC to DST. If SRC is longer than SIZE - 1 | ||
| 317 | characters, only SIZE - 1 characters are copied. A null | ||
| 318 | terminator is always written to DST, unless SIZE is 0. | ||
| 319 | Returns the length of SRC, not including the null terminator. | ||
| 320 | |||
| 321 | strlcpy() is not in the standard C library, but it is an | ||
| 322 | increasingly popular extension. See | ||
| 323 | http://www.courtesan.com/todd/papers/strlcpy.html for | ||
| 324 | information on strlcpy(). */ | ||
| 325 | size_t | ||
| 326 | strlcpy (char *dst, const char *src, size_t size) | ||
| 327 | { | ||
| 328 | size_t src_len; | ||
| 329 | |||
| 330 | ASSERT (dst != NULL); | ||
| 331 | ASSERT (src != NULL); | ||
| 332 | |||
| 333 | src_len = strlen (src); | ||
| 334 | if (size > 0) | ||
| 335 | { | ||
| 336 | size_t dst_len = size - 1; | ||
| 337 | if (src_len < dst_len) | ||
| 338 | dst_len = src_len; | ||
| 339 | memcpy (dst, src, dst_len); | ||
| 340 | dst[dst_len] = '\0'; | ||
| 341 | } | ||
| 342 | return src_len; | ||
| 343 | } | ||
| 344 | |||
| 345 | /* Concatenates string SRC to DST. The concatenated string is | ||
| 346 | limited to SIZE - 1 characters. A null terminator is always | ||
| 347 | written to DST, unless SIZE is 0. Returns the length that the | ||
| 348 | concatenated string would have assuming that there was | ||
| 349 | sufficient space, not including a null terminator. | ||
| 350 | |||
| 351 | strlcat() is not in the standard C library, but it is an | ||
| 352 | increasingly popular extension. See | ||
| 353 | http://www.courtesan.com/todd/papers/strlcpy.html for | ||
| 354 | information on strlcpy(). */ | ||
| 355 | size_t | ||
| 356 | strlcat (char *dst, const char *src, size_t size) | ||
| 357 | { | ||
| 358 | size_t src_len, dst_len; | ||
| 359 | |||
| 360 | ASSERT (dst != NULL); | ||
| 361 | ASSERT (src != NULL); | ||
| 362 | |||
| 363 | src_len = strlen (src); | ||
| 364 | dst_len = strlen (dst); | ||
| 365 | if (size > 0 && dst_len < size) | ||
| 366 | { | ||
| 367 | size_t copy_cnt = size - dst_len - 1; | ||
| 368 | if (src_len < copy_cnt) | ||
| 369 | copy_cnt = src_len; | ||
| 370 | memcpy (dst + dst_len, src, copy_cnt); | ||
| 371 | dst[dst_len + copy_cnt] = '\0'; | ||
| 372 | } | ||
| 373 | return src_len + dst_len; | ||
| 374 | } | ||
| 375 | |||
diff --git a/pintos-progos/lib/string.h b/pintos-progos/lib/string.h new file mode 100644 index 0000000..1fff82a --- /dev/null +++ b/pintos-progos/lib/string.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #ifndef __LIB_STRING_H | ||
| 2 | #define __LIB_STRING_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | /* Standard. */ | ||
| 7 | void *memcpy (void *, const void *, size_t); | ||
| 8 | void *memmove (void *, const void *, size_t); | ||
| 9 | char *strncat (char *, const char *, size_t); | ||
| 10 | int memcmp (const void *, const void *, size_t); | ||
| 11 | int strcmp (const char *, const char *); | ||
| 12 | void *memchr (const void *, int, size_t); | ||
| 13 | char *strchr (const char *, int); | ||
| 14 | size_t strcspn (const char *, const char *); | ||
| 15 | char *strpbrk (const char *, const char *); | ||
| 16 | char *strrchr (const char *, int); | ||
| 17 | size_t strspn (const char *, const char *); | ||
| 18 | char *strstr (const char *, const char *); | ||
| 19 | void *memset (void *, int, size_t); | ||
| 20 | size_t strlen (const char *); | ||
| 21 | |||
| 22 | /* Extensions. */ | ||
| 23 | size_t strlcpy (char *, const char *, size_t); | ||
| 24 | size_t strlcat (char *, const char *, size_t); | ||
| 25 | char *strtok_r (char *, const char *, char **); | ||
| 26 | size_t strnlen (const char *, size_t); | ||
| 27 | |||
| 28 | /* Try to be helpful. */ | ||
| 29 | #define strcpy dont_use_strcpy_use_strlcpy | ||
| 30 | #define strncpy dont_use_strncpy_use_strlcpy | ||
| 31 | #define strcat dont_use_strcat_use_strlcat | ||
| 32 | #define strncat dont_use_strncat_use_strlcat | ||
| 33 | #define strtok dont_use_strtok_use_strtok_r | ||
| 34 | |||
| 35 | #endif /* lib/string.h */ | ||
diff --git a/pintos-progos/lib/syscall-nr.h b/pintos-progos/lib/syscall-nr.h new file mode 100644 index 0000000..21a7af9 --- /dev/null +++ b/pintos-progos/lib/syscall-nr.h | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #ifndef __LIB_SYSCALL_NR_H | ||
| 2 | #define __LIB_SYSCALL_NR_H | ||
| 3 | |||
| 4 | /* System call numbers. */ | ||
| 5 | enum | ||
| 6 | { | ||
| 7 | /* Projects 2 and later. */ | ||
| 8 | SYS_HALT, /* Halt the operating system. */ | ||
| 9 | SYS_EXIT, /* Terminate this process. */ | ||
| 10 | SYS_EXEC, /* Start another process. */ | ||
| 11 | SYS_WAIT, /* Wait for a child process to die. */ | ||
| 12 | SYS_CREATE, /* Create a file. */ | ||
| 13 | SYS_REMOVE, /* Delete a file. */ | ||
| 14 | SYS_OPEN, /* Open a file. */ | ||
| 15 | SYS_FILESIZE, /* Obtain a file's size. */ | ||
| 16 | SYS_READ, /* Read from a file. */ | ||
| 17 | SYS_WRITE, /* Write to a file. */ | ||
| 18 | SYS_SEEK, /* Change position in a file. */ | ||
| 19 | SYS_TELL, /* Report current position in a file. */ | ||
| 20 | SYS_CLOSE, /* Close a file. */ | ||
| 21 | |||
| 22 | /* Project 3 and optionally project 4. */ | ||
| 23 | SYS_MMAP, /* Map a file into memory. */ | ||
| 24 | SYS_MUNMAP, /* Remove a memory mapping. */ | ||
| 25 | |||
| 26 | /* Project 4 only. */ | ||
| 27 | SYS_CHDIR, /* Change the current directory. */ | ||
| 28 | SYS_MKDIR, /* Create a directory. */ | ||
| 29 | SYS_READDIR, /* Reads a directory entry. */ | ||
| 30 | SYS_ISDIR, /* Tests if a fd represents a directory. */ | ||
| 31 | SYS_INUMBER /* Returns the inode number for a fd. */ | ||
| 32 | }; | ||
| 33 | |||
| 34 | #endif /* lib/syscall-nr.h */ | ||
diff --git a/pintos-progos/lib/user/console.c b/pintos-progos/lib/user/console.c new file mode 100644 index 0000000..22bdc8c --- /dev/null +++ b/pintos-progos/lib/user/console.c | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <string.h> | ||
| 3 | #include <syscall.h> | ||
| 4 | #include <syscall-nr.h> | ||
| 5 | |||
| 6 | /* The standard vprintf() function, | ||
| 7 | which is like printf() but uses a va_list. */ | ||
| 8 | int | ||
| 9 | vprintf (const char *format, va_list args) | ||
| 10 | { | ||
| 11 | return vhprintf (STDOUT_FILENO, format, args); | ||
| 12 | } | ||
| 13 | |||
| 14 | /* Like printf(), but writes output to the given HANDLE. */ | ||
| 15 | int | ||
| 16 | hprintf (int handle, const char *format, ...) | ||
| 17 | { | ||
| 18 | va_list args; | ||
| 19 | int retval; | ||
| 20 | |||
| 21 | va_start (args, format); | ||
| 22 | retval = vhprintf (handle, format, args); | ||
| 23 | va_end (args); | ||
| 24 | |||
| 25 | return retval; | ||
| 26 | } | ||
| 27 | |||
| 28 | /* Writes string S to the console, followed by a new-line | ||
| 29 | character. */ | ||
| 30 | int | ||
| 31 | puts (const char *s) | ||
| 32 | { | ||
| 33 | write (STDOUT_FILENO, s, strlen (s)); | ||
| 34 | putchar ('\n'); | ||
| 35 | |||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | /* Writes C to the console. */ | ||
| 40 | int | ||
| 41 | putchar (int c) | ||
| 42 | { | ||
| 43 | char c2 = c; | ||
| 44 | write (STDOUT_FILENO, &c2, 1); | ||
| 45 | return c; | ||
| 46 | } | ||
| 47 | |||
| 48 | /* Auxiliary data for vhprintf_helper(). */ | ||
| 49 | struct vhprintf_aux | ||
| 50 | { | ||
| 51 | char buf[64]; /* Character buffer. */ | ||
| 52 | char *p; /* Current position in buffer. */ | ||
| 53 | int char_cnt; /* Total characters written so far. */ | ||
| 54 | int handle; /* Output file handle. */ | ||
| 55 | }; | ||
| 56 | |||
| 57 | static void add_char (char, void *); | ||
| 58 | static void flush (struct vhprintf_aux *); | ||
| 59 | |||
| 60 | /* Formats the printf() format specification FORMAT with | ||
| 61 | arguments given in ARGS and writes the output to the given | ||
| 62 | HANDLE. */ | ||
| 63 | int | ||
| 64 | vhprintf (int handle, const char *format, va_list args) | ||
| 65 | { | ||
| 66 | struct vhprintf_aux aux; | ||
| 67 | aux.p = aux.buf; | ||
| 68 | aux.char_cnt = 0; | ||
| 69 | aux.handle = handle; | ||
| 70 | __vprintf (format, args, add_char, &aux); | ||
| 71 | flush (&aux); | ||
| 72 | return aux.char_cnt; | ||
| 73 | } | ||
| 74 | |||
| 75 | /* Adds C to the buffer in AUX, flushing it if the buffer fills | ||
| 76 | up. */ | ||
| 77 | static void | ||
| 78 | add_char (char c, void *aux_) | ||
| 79 | { | ||
| 80 | struct vhprintf_aux *aux = aux_; | ||
| 81 | *aux->p++ = c; | ||
| 82 | if (aux->p >= aux->buf + sizeof aux->buf) | ||
| 83 | flush (aux); | ||
| 84 | aux->char_cnt++; | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Flushes the buffer in AUX. */ | ||
| 88 | static void | ||
| 89 | flush (struct vhprintf_aux *aux) | ||
| 90 | { | ||
| 91 | if (aux->p > aux->buf) | ||
| 92 | write (aux->handle, aux->buf, aux->p - aux->buf); | ||
| 93 | aux->p = aux->buf; | ||
| 94 | } | ||
diff --git a/pintos-progos/lib/user/debug.c b/pintos-progos/lib/user/debug.c new file mode 100644 index 0000000..f49b874 --- /dev/null +++ b/pintos-progos/lib/user/debug.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #include <debug.h> | ||
| 2 | #include <stdarg.h> | ||
| 3 | #include <stdbool.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | |||
| 7 | /* Aborts the user program, printing the source file name, line | ||
| 8 | number, and function name, plus a user-specific message. */ | ||
| 9 | void | ||
| 10 | debug_panic (const char *file, int line, const char *function, | ||
| 11 | const char *message, ...) | ||
| 12 | { | ||
| 13 | va_list args; | ||
| 14 | |||
| 15 | printf ("User process ABORT at %s:%d in %s(): ", file, line, function); | ||
| 16 | |||
| 17 | va_start (args, message); | ||
| 18 | vprintf (message, args); | ||
| 19 | printf ("\n"); | ||
| 20 | va_end (args); | ||
| 21 | |||
| 22 | debug_backtrace (); | ||
| 23 | |||
| 24 | exit (1); | ||
| 25 | } | ||
diff --git a/pintos-progos/lib/user/entry.c b/pintos-progos/lib/user/entry.c new file mode 100644 index 0000000..a707c70 --- /dev/null +++ b/pintos-progos/lib/user/entry.c | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | #include <syscall.h> | ||
| 2 | |||
| 3 | int main (int, char *[]); | ||
| 4 | void _start (int argc, char *argv[]); | ||
| 5 | |||
| 6 | void | ||
| 7 | _start (int argc, char *argv[]) | ||
| 8 | { | ||
| 9 | exit (main (argc, argv)); | ||
| 10 | } | ||
diff --git a/pintos-progos/lib/user/stdio.h b/pintos-progos/lib/user/stdio.h new file mode 100644 index 0000000..b9f3cc6 --- /dev/null +++ b/pintos-progos/lib/user/stdio.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef __LIB_USER_STDIO_H | ||
| 2 | #define __LIB_USER_STDIO_H | ||
| 3 | |||
| 4 | int hprintf (int, const char *, ...) PRINTF_FORMAT (2, 3); | ||
| 5 | int vhprintf (int, const char *, va_list) PRINTF_FORMAT (2, 0); | ||
| 6 | |||
| 7 | #endif /* lib/user/stdio.h */ | ||
diff --git a/pintos-progos/lib/user/syscall.c b/pintos-progos/lib/user/syscall.c new file mode 100644 index 0000000..0a467f8 --- /dev/null +++ b/pintos-progos/lib/user/syscall.c | |||
| @@ -0,0 +1,184 @@ | |||
| 1 | #include <syscall.h> | ||
| 2 | #include "../syscall-nr.h" | ||
| 3 | |||
| 4 | /* Invokes syscall NUMBER, passing no arguments, and returns the | ||
| 5 | return value as an `int'. */ | ||
| 6 | #define syscall0(NUMBER) \ | ||
| 7 | ({ \ | ||
| 8 | int retval; \ | ||
| 9 | asm volatile \ | ||
| 10 | ("pushl %[number]; int $0x30; addl $4, %%esp" \ | ||
| 11 | : "=a" (retval) \ | ||
| 12 | : [number] "i" (NUMBER) \ | ||
| 13 | : "memory"); \ | ||
| 14 | retval; \ | ||
| 15 | }) | ||
| 16 | |||
| 17 | /* Invokes syscall NUMBER, passing argument ARG0, and returns the | ||
| 18 | return value as an `int'. */ | ||
| 19 | #define syscall1(NUMBER, ARG0) \ | ||
| 20 | ({ \ | ||
| 21 | int retval; \ | ||
| 22 | asm volatile \ | ||
| 23 | ("pushl %[arg0]; pushl %[number]; int $0x30; addl $8, %%esp" \ | ||
| 24 | : "=a" (retval) \ | ||
| 25 | : [number] "i" (NUMBER), \ | ||
| 26 | [arg0] "g" (ARG0) \ | ||
| 27 | : "memory"); \ | ||
| 28 | retval; \ | ||
| 29 | }) | ||
| 30 | |||
| 31 | /* Invokes syscall NUMBER, passing arguments ARG0 and ARG1, and | ||
| 32 | returns the return value as an `int'. */ | ||
| 33 | #define syscall2(NUMBER, ARG0, ARG1) \ | ||
| 34 | ({ \ | ||
| 35 | int retval; \ | ||
| 36 | asm volatile \ | ||
| 37 | ("pushl %[arg1]; pushl %[arg0]; " \ | ||
| 38 | "pushl %[number]; int $0x30; addl $12, %%esp" \ | ||
| 39 | : "=a" (retval) \ | ||
| 40 | : [number] "i" (NUMBER), \ | ||
| 41 | [arg0] "r" (ARG0), \ | ||
| 42 | [arg1] "r" (ARG1) \ | ||
| 43 | : "memory"); \ | ||
| 44 | retval; \ | ||
| 45 | }) | ||
| 46 | |||
| 47 | /* Invokes syscall NUMBER, passing arguments ARG0, ARG1, and | ||
| 48 | ARG2, and returns the return value as an `int'. */ | ||
| 49 | #define syscall3(NUMBER, ARG0, ARG1, ARG2) \ | ||
| 50 | ({ \ | ||
| 51 | int retval; \ | ||
| 52 | asm volatile \ | ||
| 53 | ("pushl %[arg2]; pushl %[arg1]; pushl %[arg0]; " \ | ||
| 54 | "pushl %[number]; int $0x30; addl $16, %%esp" \ | ||
| 55 | : "=a" (retval) \ | ||
| 56 | : [number] "i" (NUMBER), \ | ||
| 57 | [arg0] "r" (ARG0), \ | ||
| 58 | [arg1] "r" (ARG1), \ | ||
| 59 | [arg2] "r" (ARG2) \ | ||
| 60 | : "memory"); \ | ||
| 61 | retval; \ | ||
| 62 | }) | ||
| 63 | |||
| 64 | void | ||
| 65 | halt (void) | ||
| 66 | { | ||
| 67 | syscall0 (SYS_HALT); | ||
| 68 | NOT_REACHED (); | ||
| 69 | } | ||
| 70 | |||
| 71 | void | ||
| 72 | exit (int status) | ||
| 73 | { | ||
| 74 | syscall1 (SYS_EXIT, status); | ||
| 75 | NOT_REACHED (); | ||
| 76 | } | ||
| 77 | |||
| 78 | pid_t | ||
| 79 | exec (const char *cmd_line) | ||
| 80 | { | ||
| 81 | return (pid_t) syscall1 (SYS_EXEC, cmd_line); | ||
| 82 | } | ||
| 83 | |||
| 84 | int | ||
| 85 | wait (pid_t pid) | ||
| 86 | { | ||
| 87 | return syscall1 (SYS_WAIT, pid); | ||
| 88 | } | ||
| 89 | |||
| 90 | bool | ||
| 91 | create (const char *file, unsigned initial_size) | ||
| 92 | { | ||
| 93 | return syscall2 (SYS_CREATE, file, initial_size); | ||
| 94 | } | ||
| 95 | |||
| 96 | bool | ||
| 97 | remove (const char *file) | ||
| 98 | { | ||
| 99 | return syscall1 (SYS_REMOVE, file); | ||
| 100 | } | ||
| 101 | |||
| 102 | int | ||
| 103 | open (const char *file) | ||
| 104 | { | ||
| 105 | return syscall1 (SYS_OPEN, file); | ||
| 106 | } | ||
| 107 | |||
| 108 | int | ||
| 109 | filesize (int fd) | ||
| 110 | { | ||
| 111 | return syscall1 (SYS_FILESIZE, fd); | ||
| 112 | } | ||
| 113 | |||
| 114 | int | ||
| 115 | read (int fd, void *buffer, unsigned size) | ||
| 116 | { | ||
| 117 | return syscall3 (SYS_READ, fd, buffer, size); | ||
| 118 | } | ||
| 119 | |||
| 120 | int | ||
| 121 | write (int fd, const void *buffer, unsigned size) | ||
| 122 | { | ||
| 123 | return syscall3 (SYS_WRITE, fd, buffer, size); | ||
| 124 | } | ||
| 125 | |||
| 126 | void | ||
| 127 | seek (int fd, unsigned position) | ||
| 128 | { | ||
| 129 | syscall2 (SYS_SEEK, fd, position); | ||
| 130 | } | ||
| 131 | |||
| 132 | unsigned | ||
| 133 | tell (int fd) | ||
| 134 | { | ||
| 135 | return syscall1 (SYS_TELL, fd); | ||
| 136 | } | ||
| 137 | |||
| 138 | void | ||
| 139 | close (int fd) | ||
| 140 | { | ||
| 141 | syscall1 (SYS_CLOSE, fd); | ||
| 142 | } | ||
| 143 | |||
| 144 | mapid_t | ||
| 145 | mmap (int fd, void *addr) | ||
| 146 | { | ||
| 147 | return syscall2 (SYS_MMAP, fd, addr); | ||
| 148 | } | ||
| 149 | |||
| 150 | void | ||
| 151 | munmap (mapid_t mapid) | ||
| 152 | { | ||
| 153 | syscall1 (SYS_MUNMAP, mapid); | ||
| 154 | } | ||
| 155 | |||
| 156 | bool | ||
| 157 | chdir (const char *dir) | ||
| 158 | { | ||
| 159 | return syscall1 (SYS_CHDIR, dir); | ||
| 160 | } | ||
| 161 | |||
| 162 | bool | ||
| 163 | mkdir (const char *dir) | ||
| 164 | { | ||
| 165 | return syscall1 (SYS_MKDIR, dir); | ||
| 166 | } | ||
| 167 | |||
| 168 | bool | ||
| 169 | readdir (int fd, char name[READDIR_MAX_LEN + 1]) | ||
| 170 | { | ||
| 171 | return syscall2 (SYS_READDIR, fd, name); | ||
| 172 | } | ||
| 173 | |||
| 174 | bool | ||
| 175 | isdir (int fd) | ||
| 176 | { | ||
| 177 | return syscall1 (SYS_ISDIR, fd); | ||
| 178 | } | ||
| 179 | |||
| 180 | int | ||
| 181 | inumber (int fd) | ||
| 182 | { | ||
| 183 | return syscall1 (SYS_INUMBER, fd); | ||
| 184 | } | ||
diff --git a/pintos-progos/lib/user/syscall.h b/pintos-progos/lib/user/syscall.h new file mode 100644 index 0000000..a1bcf0b --- /dev/null +++ b/pintos-progos/lib/user/syscall.h | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | #ifndef __LIB_USER_SYSCALL_H | ||
| 2 | #define __LIB_USER_SYSCALL_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <debug.h> | ||
| 6 | |||
| 7 | /* Process identifier. */ | ||
| 8 | typedef int pid_t; | ||
| 9 | #define PID_ERROR ((pid_t) -1) | ||
| 10 | |||
| 11 | /* Map region identifier. */ | ||
| 12 | typedef int mapid_t; | ||
| 13 | #define MAP_FAILED ((mapid_t) -1) | ||
| 14 | |||
| 15 | /* Maximum characters in a filename written by readdir(). */ | ||
| 16 | #define READDIR_MAX_LEN 14 | ||
| 17 | |||
| 18 | /* Typical return values from main() and arguments to exit(). */ | ||
| 19 | #define EXIT_SUCCESS 0 /* Successful execution. */ | ||
| 20 | #define EXIT_FAILURE 1 /* Unsuccessful execution. */ | ||
| 21 | |||
| 22 | /* Projects 2 and later. */ | ||
| 23 | void halt (void) NO_RETURN; | ||
| 24 | void exit (int status) NO_RETURN; | ||
| 25 | pid_t exec (const char *cmd_line); | ||
| 26 | int wait (pid_t); | ||
| 27 | bool create (const char *file, unsigned initial_size); | ||
| 28 | bool remove (const char *file); | ||
| 29 | int open (const char *file); | ||
| 30 | int filesize (int fd); | ||
| 31 | int read (int fd, void *buffer, unsigned length); | ||
| 32 | int write (int fd, const void *buffer, unsigned length); | ||
| 33 | void seek (int fd, unsigned position); | ||
| 34 | unsigned tell (int fd); | ||
| 35 | void close (int fd); | ||
| 36 | |||
| 37 | /* Project 3 and optionally project 4. */ | ||
| 38 | mapid_t mmap (int fd, void *addr); | ||
| 39 | void munmap (mapid_t); | ||
| 40 | |||
| 41 | /* Project 4 only. */ | ||
| 42 | bool chdir (const char *dir); | ||
| 43 | bool mkdir (const char *dir); | ||
| 44 | bool readdir (int fd, char name[READDIR_MAX_LEN + 1]); | ||
| 45 | bool isdir (int fd); | ||
| 46 | int inumber (int fd); | ||
| 47 | |||
| 48 | #endif /* lib/user/syscall.h */ | ||
diff --git a/pintos-progos/lib/user/user.lds b/pintos-progos/lib/user/user.lds new file mode 100644 index 0000000..cc6d6c0 --- /dev/null +++ b/pintos-progos/lib/user/user.lds | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | OUTPUT_FORMAT("elf32-i386") | ||
| 2 | OUTPUT_ARCH(i386) | ||
| 3 | ENTRY(_start) | ||
| 4 | |||
| 5 | SECTIONS | ||
| 6 | { | ||
| 7 | /* Read-only sections, merged into text segment: */ | ||
| 8 | __executable_start = 0x08048000 + SIZEOF_HEADERS; | ||
| 9 | . = 0x08048000 + SIZEOF_HEADERS; | ||
| 10 | .text : { *(.text) } = 0x90 | ||
| 11 | .rodata : { *(.rodata) } | ||
| 12 | |||
| 13 | /* Adjust the address for the data segment. We want to adjust up to | ||
| 14 | the same address within the page on the next page up. */ | ||
| 15 | . = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1)); | ||
| 16 | . = DATA_SEGMENT_ALIGN (0x1000, 0x1000); | ||
| 17 | |||
| 18 | .data : { *(.data) } | ||
| 19 | .bss : { *(.bss) } | ||
| 20 | |||
| 21 | /* Stabs debugging sections. */ | ||
| 22 | .stab 0 : { *(.stab) } | ||
| 23 | .stabstr 0 : { *(.stabstr) } | ||
| 24 | .stab.excl 0 : { *(.stab.excl) } | ||
| 25 | .stab.exclstr 0 : { *(.stab.exclstr) } | ||
| 26 | .stab.index 0 : { *(.stab.index) } | ||
| 27 | .stab.indexstr 0 : { *(.stab.indexstr) } | ||
| 28 | .comment 0 : { *(.comment) } | ||
| 29 | |||
| 30 | /* DWARF debug sections. | ||
| 31 | Symbols in the DWARF debugging sections are relative to the beginning | ||
| 32 | of the section so we begin them at 0. */ | ||
| 33 | /* DWARF 1 */ | ||
| 34 | .debug 0 : { *(.debug) } | ||
| 35 | .line 0 : { *(.line) } | ||
| 36 | /* GNU DWARF 1 extensions */ | ||
| 37 | .debug_srcinfo 0 : { *(.debug_srcinfo) } | ||
| 38 | .debug_sfnames 0 : { *(.debug_sfnames) } | ||
| 39 | /* DWARF 1.1 and DWARF 2 */ | ||
| 40 | .debug_aranges 0 : { *(.debug_aranges) } | ||
| 41 | .debug_pubnames 0 : { *(.debug_pubnames) } | ||
| 42 | /* DWARF 2 */ | ||
| 43 | .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } | ||
| 44 | .debug_abbrev 0 : { *(.debug_abbrev) } | ||
| 45 | .debug_line 0 : { *(.debug_line) } | ||
| 46 | .debug_frame 0 : { *(.debug_frame) } | ||
| 47 | .debug_str 0 : { *(.debug_str) } | ||
| 48 | .debug_loc 0 : { *(.debug_loc) } | ||
| 49 | .debug_macinfo 0 : { *(.debug_macinfo) } | ||
| 50 | /* SGI/MIPS DWARF 2 extensions */ | ||
| 51 | .debug_weaknames 0 : { *(.debug_weaknames) } | ||
| 52 | .debug_funcnames 0 : { *(.debug_funcnames) } | ||
| 53 | .debug_typenames 0 : { *(.debug_typenames) } | ||
| 54 | .debug_varnames 0 : { *(.debug_varnames) } | ||
| 55 | /DISCARD/ : { *(.note.GNU-stack) } | ||
| 56 | /DISCARD/ : { *(.eh_frame) } | ||
| 57 | } | ||
diff --git a/pintos-progos/lib/ustar.c b/pintos-progos/lib/ustar.c new file mode 100644 index 0000000..49af69a --- /dev/null +++ b/pintos-progos/lib/ustar.c | |||
| @@ -0,0 +1,228 @@ | |||
| 1 | #include <ustar.h> | ||
| 2 | #include <limits.h> | ||
| 3 | #include <packed.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | |||
| 7 | /* Header for ustar-format tar archive. See the documentation of | ||
| 8 | the "pax" utility in [SUSv3] for the the "ustar" format | ||
| 9 | specification. */ | ||
| 10 | struct ustar_header | ||
| 11 | { | ||
| 12 | char name[100]; /* File name. Null-terminated if room. */ | ||
| 13 | char mode[8]; /* Permissions as octal string. */ | ||
| 14 | char uid[8]; /* User ID as octal string. */ | ||
| 15 | char gid[8]; /* Group ID as octal string. */ | ||
| 16 | char size[12]; /* File size in bytes as octal string. */ | ||
| 17 | char mtime[12]; /* Modification time in seconds | ||
| 18 | from Jan 1, 1970, as octal string. */ | ||
| 19 | char chksum[8]; /* Sum of octets in header as octal string. */ | ||
| 20 | char typeflag; /* An enum ustar_type value. */ | ||
| 21 | char linkname[100]; /* Name of link target. | ||
| 22 | Null-terminated if room. */ | ||
| 23 | char magic[6]; /* "ustar\0" */ | ||
| 24 | char version[2]; /* "00" */ | ||
| 25 | char uname[32]; /* User name, always null-terminated. */ | ||
| 26 | char gname[32]; /* Group name, always null-terminated. */ | ||
| 27 | char devmajor[8]; /* Device major number as octal string. */ | ||
| 28 | char devminor[8]; /* Device minor number as octal string. */ | ||
| 29 | char prefix[155]; /* Prefix to file name. | ||
| 30 | Null-terminated if room. */ | ||
| 31 | char padding[12]; /* Pad to 512 bytes. */ | ||
| 32 | } | ||
| 33 | PACKED; | ||
| 34 | |||
| 35 | /* Returns the checksum for the given ustar format HEADER. */ | ||
| 36 | static unsigned int | ||
| 37 | calculate_chksum (const struct ustar_header *h) | ||
| 38 | { | ||
| 39 | const uint8_t *header = (const uint8_t *) h; | ||
| 40 | unsigned int chksum; | ||
| 41 | size_t i; | ||
| 42 | |||
| 43 | chksum = 0; | ||
| 44 | for (i = 0; i < USTAR_HEADER_SIZE; i++) | ||
| 45 | { | ||
| 46 | /* The ustar checksum is calculated as if the chksum field | ||
| 47 | were all spaces. */ | ||
| 48 | const size_t chksum_start = offsetof (struct ustar_header, chksum); | ||
| 49 | const size_t chksum_end = chksum_start + sizeof h->chksum; | ||
| 50 | bool in_chksum_field = i >= chksum_start && i < chksum_end; | ||
| 51 | chksum += in_chksum_field ? ' ' : header[i]; | ||
| 52 | } | ||
| 53 | return chksum; | ||
| 54 | } | ||
| 55 | |||
| 56 | /* Drop possibly dangerous prefixes from FILE_NAME and return the | ||
| 57 | stripped name. An archive with file names that start with "/" | ||
| 58 | or "../" could cause a naive tar extractor to write to | ||
| 59 | arbitrary parts of the file system, not just the destination | ||
| 60 | directory. We don't want to create such archives or be such a | ||
| 61 | naive extractor. | ||
| 62 | |||
| 63 | The return value can be a suffix of FILE_NAME or a string | ||
| 64 | literal. */ | ||
| 65 | static const char * | ||
| 66 | strip_antisocial_prefixes (const char *file_name) | ||
| 67 | { | ||
| 68 | while (*file_name == '/' | ||
| 69 | || !memcmp (file_name, "./", 2) | ||
| 70 | || !memcmp (file_name, "../", 3)) | ||
| 71 | file_name = strchr (file_name, '/') + 1; | ||
| 72 | return *file_name == '\0' || !strcmp (file_name, "..") ? "." : file_name; | ||
| 73 | } | ||
| 74 | |||
| 75 | /* Composes HEADER as a USTAR_HEADER_SIZE (512)-byte archive | ||
| 76 | header in ustar format for a SIZE-byte file named FILE_NAME of | ||
| 77 | the given TYPE. The caller is responsible for writing the | ||
| 78 | header to a file or device. | ||
| 79 | |||
| 80 | If successful, returns true. On failure (due to an | ||
| 81 | excessively long file name), returns false. */ | ||
| 82 | bool | ||
| 83 | ustar_make_header (const char *file_name, enum ustar_type type, | ||
| 84 | int size, char header[USTAR_HEADER_SIZE]) | ||
| 85 | { | ||
| 86 | struct ustar_header *h = (struct ustar_header *) header; | ||
| 87 | |||
| 88 | ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE); | ||
| 89 | ASSERT (type == USTAR_REGULAR || type == USTAR_DIRECTORY); | ||
| 90 | |||
| 91 | /* Check file name. */ | ||
| 92 | file_name = strip_antisocial_prefixes (file_name); | ||
| 93 | if (strlen (file_name) > 99) | ||
| 94 | { | ||
| 95 | printf ("%s: file name too long\n", file_name); | ||
| 96 | return false; | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Fill in header except for final checksum. */ | ||
| 100 | memset (h, 0, sizeof *h); | ||
| 101 | strlcpy (h->name, file_name, sizeof h->name); | ||
| 102 | snprintf (h->mode, sizeof h->mode, "%07o", | ||
| 103 | type == USTAR_REGULAR ? 0644 : 0755); | ||
| 104 | strlcpy (h->uid, "0000000", sizeof h->uid); | ||
| 105 | strlcpy (h->gid, "0000000", sizeof h->gid); | ||
| 106 | snprintf (h->size, sizeof h->size, "%011o", size); | ||
| 107 | snprintf (h->mtime, sizeof h->size, "%011o", 1136102400); | ||
| 108 | h->typeflag = type; | ||
| 109 | strlcpy (h->magic, "ustar", sizeof h->magic); | ||
| 110 | h->version[0] = h->version[1] = '0'; | ||
| 111 | strlcpy (h->gname, "root", sizeof h->gname); | ||
| 112 | strlcpy (h->uname, "root", sizeof h->uname); | ||
| 113 | |||
| 114 | /* Compute and fill in final checksum. */ | ||
| 115 | snprintf (h->chksum, sizeof h->chksum, "%07o", calculate_chksum (h)); | ||
| 116 | |||
| 117 | return true; | ||
| 118 | } | ||
| 119 | |||
| 120 | /* Parses a SIZE-byte octal field in S in the format used by | ||
| 121 | ustar format. If successful, stores the field's value in | ||
| 122 | *VALUE and returns true; on failure, returns false. | ||
| 123 | |||
| 124 | ustar octal fields consist of a sequence of octal digits | ||
| 125 | terminated by a space or a null byte. The ustar specification | ||
| 126 | seems ambiguous as to whether these fields must be padded on | ||
| 127 | the left with '0's, so we accept any field that fits in the | ||
| 128 | available space, regardless of whether it fills the space. */ | ||
| 129 | static bool | ||
| 130 | parse_octal_field (const char *s, size_t size, unsigned long int *value) | ||
| 131 | { | ||
| 132 | size_t ofs; | ||
| 133 | |||
| 134 | *value = 0; | ||
| 135 | for (ofs = 0; ofs < size; ofs++) | ||
| 136 | { | ||
| 137 | char c = s[ofs]; | ||
| 138 | if (c >= '0' && c <= '7') | ||
| 139 | { | ||
| 140 | if (*value > ULONG_MAX / 8) | ||
| 141 | { | ||
| 142 | /* Overflow. */ | ||
| 143 | return false; | ||
| 144 | } | ||
| 145 | *value = c - '0' + *value * 8; | ||
| 146 | } | ||
| 147 | else if (c == ' ' || c == '\0') | ||
| 148 | { | ||
| 149 | /* End of field, but disallow completely empty | ||
| 150 | fields. */ | ||
| 151 | return ofs > 0; | ||
| 152 | } | ||
| 153 | else | ||
| 154 | { | ||
| 155 | /* Bad character. */ | ||
| 156 | return false; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Field did not end in space or null byte. */ | ||
| 161 | return false; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* Returns true if the CNT bytes starting at BLOCK are all zero, | ||
| 165 | false otherwise. */ | ||
| 166 | static bool | ||
| 167 | is_all_zeros (const char *block, size_t cnt) | ||
| 168 | { | ||
| 169 | while (cnt-- > 0) | ||
| 170 | if (*block++ != 0) | ||
| 171 | return false; | ||
| 172 | return true; | ||
| 173 | } | ||
| 174 | |||
| 175 | /* Parses HEADER as a ustar-format archive header for a regular | ||
| 176 | file or directory. If successful, stores the archived file's | ||
| 177 | name in *FILE_NAME (as a pointer into HEADER or a string | ||
| 178 | literal), its type in *TYPE, and its size in bytes in *SIZE, | ||
| 179 | and returns a null pointer. On failure, returns a | ||
| 180 | human-readable error message. */ | ||
| 181 | const char * | ||
| 182 | ustar_parse_header (const char header[USTAR_HEADER_SIZE], | ||
| 183 | const char **file_name, enum ustar_type *type, int *size) | ||
| 184 | { | ||
| 185 | const struct ustar_header *h = (const struct ustar_header *) header; | ||
| 186 | unsigned long int chksum, size_ul; | ||
| 187 | |||
| 188 | ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE); | ||
| 189 | |||
| 190 | /* Detect end of archive. */ | ||
| 191 | if (is_all_zeros (header, USTAR_HEADER_SIZE)) | ||
| 192 | { | ||
| 193 | *file_name = NULL; | ||
| 194 | *type = USTAR_EOF; | ||
| 195 | *size = 0; | ||
| 196 | return NULL; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* Validate ustar header. */ | ||
| 200 | if (memcmp (h->magic, "ustar", 6)) | ||
| 201 | return "not a ustar archive"; | ||
| 202 | else if (h->version[0] != '0' || h->version[1] != '0') | ||
| 203 | return "invalid ustar version"; | ||
| 204 | else if (!parse_octal_field (h->chksum, sizeof h->chksum, &chksum)) | ||
| 205 | return "corrupt chksum field"; | ||
| 206 | else if (chksum != calculate_chksum (h)) | ||
| 207 | return "checksum mismatch"; | ||
| 208 | else if (h->name[sizeof h->name - 1] != '\0' || h->prefix[0] != '\0') | ||
| 209 | return "file name too long"; | ||
| 210 | else if (h->typeflag != USTAR_REGULAR && h->typeflag != USTAR_DIRECTORY) | ||
| 211 | return "unimplemented file type"; | ||
| 212 | if (h->typeflag == USTAR_REGULAR) | ||
| 213 | { | ||
| 214 | if (!parse_octal_field (h->size, sizeof h->size, &size_ul)) | ||
| 215 | return "corrupt file size field"; | ||
| 216 | else if (size_ul > INT_MAX) | ||
| 217 | return "file too large"; | ||
| 218 | } | ||
| 219 | else | ||
| 220 | size_ul = 0; | ||
| 221 | |||
| 222 | /* Success. */ | ||
| 223 | *file_name = strip_antisocial_prefixes (h->name); | ||
| 224 | *type = h->typeflag; | ||
| 225 | *size = size_ul; | ||
| 226 | return NULL; | ||
| 227 | } | ||
| 228 | |||
diff --git a/pintos-progos/lib/ustar.h b/pintos-progos/lib/ustar.h new file mode 100644 index 0000000..43a5513 --- /dev/null +++ b/pintos-progos/lib/ustar.h | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | #ifndef __LIB_USTAR_H | ||
| 2 | #define __LIB_USTAR_H | ||
| 3 | |||
| 4 | /* Support for the standard Posix "ustar" format. See the | ||
| 5 | documentation of the "pax" utility in [SUSv3] for the the | ||
| 6 | "ustar" format specification. */ | ||
| 7 | |||
| 8 | #include <stdbool.h> | ||
| 9 | |||
| 10 | /* Type of a file entry in an archive. | ||
| 11 | The values here are the bytes that appear in the file format. | ||
| 12 | Only types of interest to Pintos are listed here. */ | ||
| 13 | enum ustar_type | ||
| 14 | { | ||
| 15 | USTAR_REGULAR = '0', /* Ordinary file. */ | ||
| 16 | USTAR_DIRECTORY = '5', /* Directory. */ | ||
| 17 | USTAR_EOF = -1 /* End of archive (not an official value). */ | ||
| 18 | }; | ||
| 19 | |||
| 20 | /* Size of a ustar archive header, in bytes. */ | ||
| 21 | #define USTAR_HEADER_SIZE 512 | ||
| 22 | |||
| 23 | bool ustar_make_header (const char *file_name, enum ustar_type, | ||
| 24 | int size, char header[USTAR_HEADER_SIZE]); | ||
| 25 | const char *ustar_parse_header (const char header[USTAR_HEADER_SIZE], | ||
| 26 | const char **file_name, | ||
| 27 | enum ustar_type *, int *size); | ||
| 28 | |||
| 29 | #endif /* lib/ustar.h */ | ||
diff --git a/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch b/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch new file mode 100644 index 0000000..44190e3 --- /dev/null +++ b/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | From 5e6cfa27ba6de331ecc142e7f65b4d1c2112b4e2 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alex Busenius <s9albuse@stud.uni-saarland.de> | ||
| 3 | Date: Mon, 27 Apr 2009 15:33:37 +0200 | ||
| 4 | Subject: bochs-2.3.7 jitter | ||
| 5 | |||
| 6 | --- | ||
| 7 | bochs.h | 2 ++ | ||
| 8 | iodev/pit82c54.cc | 9 ++++++++- | ||
| 9 | main.cc | 8 ++++++++ | ||
| 10 | 3 files changed, 18 insertions(+), 1 deletions(-) | ||
| 11 | |||
| 12 | diff --git a/bochs.h b/bochs.h | ||
| 13 | index 2a643cd..75bcd96 100644 | ||
| 14 | --- a/bochs.h | ||
| 15 | +++ b/bochs.h | ||
| 16 | @@ -630,4 +630,6 @@ void bx_center_print(FILE *file, const char *line, unsigned maxwidth); | ||
| 17 | |||
| 18 | #endif | ||
| 19 | |||
| 20 | +extern int jitter; | ||
| 21 | + | ||
| 22 | #endif /* BX_BOCHS_H */ | ||
| 23 | diff --git a/iodev/pit82c54.cc b/iodev/pit82c54.cc | ||
| 24 | index 0d65768..31ac041 100644 | ||
| 25 | --- a/iodev/pit82c54.cc | ||
| 26 | +++ b/iodev/pit82c54.cc | ||
| 27 | @@ -28,6 +28,7 @@ | ||
| 28 | |||
| 29 | #include "iodev.h" | ||
| 30 | #include "pit82c54.h" | ||
| 31 | +#include <stdlib.h> | ||
| 32 | #define LOG_THIS this-> | ||
| 33 | |||
| 34 | |||
| 35 | @@ -399,7 +400,13 @@ pit_82C54::clock(Bit8u cnum) | ||
| 36 | case 2: | ||
| 37 | if (thisctr.count_written) { | ||
| 38 | if (thisctr.triggerGATE || thisctr.first_pass) { | ||
| 39 | - set_count(thisctr, thisctr.inlatch); | ||
| 40 | + unsigned n = thisctr.inlatch; | ||
| 41 | + if (jitter && n > 5) { | ||
| 42 | + n *= (double) rand() / RAND_MAX; | ||
| 43 | + if (n < 5) | ||
| 44 | + n = 5; | ||
| 45 | + } | ||
| 46 | + set_count(thisctr, n); | ||
| 47 | thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF; | ||
| 48 | thisctr.null_count=0; | ||
| 49 | if (thisctr.inlatch==1) { | ||
| 50 | diff --git a/main.cc b/main.cc | ||
| 51 | index ebdf258..09cf661 100644 | ||
| 52 | --- a/main.cc | ||
| 53 | +++ b/main.cc | ||
| 54 | @@ -112,6 +112,7 @@ BOCHSAPI BX_MEM_C bx_mem; | ||
| 55 | #endif | ||
| 56 | |||
| 57 | char *bochsrc_filename = NULL; | ||
| 58 | +int jitter = 0; | ||
| 59 | |||
| 60 | void bx_print_header () | ||
| 61 | { | ||
| 62 | @@ -541,6 +542,13 @@ int bx_init_main(int argc, char *argv[]) | ||
| 63 | else if (!strcmp("-q", argv[arg])) { | ||
| 64 | SIM->get_param_enum(BXPN_BOCHS_START)->set(BX_QUICK_START); | ||
| 65 | } | ||
| 66 | + else if (!strcmp ("-j", argv[arg])) { | ||
| 67 | + if (++arg >= argc) BX_PANIC(("-j must be followed by a number")); | ||
| 68 | + else { | ||
| 69 | + jitter = 1; | ||
| 70 | + srand(atoi(argv[arg])); | ||
| 71 | + } | ||
| 72 | + } | ||
| 73 | else if (!strcmp("-f", argv[arg])) { | ||
| 74 | if (++arg >= argc) BX_PANIC(("-f must be followed by a filename")); | ||
| 75 | else bochsrc_filename = argv[arg]; | ||
| 76 | -- | ||
| 77 | 1.6.2.3 | ||
| 78 | |||
diff --git a/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch b/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch new file mode 100644 index 0000000..c8698bd --- /dev/null +++ b/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | From 356b7e781c815c70c992d58360caa42f1776d06b Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alex Busenius <s9albuse@stud.uni-saarland.de> | ||
| 3 | Date: Mon, 27 Apr 2009 17:09:27 +0200 | ||
| 4 | Subject: bochs-2.3.7 triple fault | ||
| 5 | |||
| 6 | --- | ||
| 7 | cpu/cpu.h | 4 ++++ | ||
| 8 | cpu/exception.cc | 7 +++++++ | ||
| 9 | gdbstub.cc | 11 ++++++++--- | ||
| 10 | 3 files changed, 19 insertions(+), 3 deletions(-) | ||
| 11 | |||
| 12 | diff --git a/cpu/cpu.h b/cpu/cpu.h | ||
| 13 | index 7c7b11b..c47133a 100644 | ||
| 14 | --- a/cpu/cpu.h | ||
| 15 | +++ b/cpu/cpu.h | ||
| 16 | @@ -903,6 +903,10 @@ public: // for now... | ||
| 17 | #endif | ||
| 18 | Bit8u trace; | ||
| 19 | |||
| 20 | +#if BX_GDBSTUB | ||
| 21 | + Bit8u ispanic; | ||
| 22 | +#endif | ||
| 23 | + | ||
| 24 | // for paging | ||
| 25 | struct { | ||
| 26 | bx_TLB_entry entry[BX_TLB_SIZE] BX_CPP_AlignN(16); | ||
| 27 | diff --git a/cpu/exception.cc b/cpu/exception.cc | ||
| 28 | index c3e3777..fb3abfc 100644 | ||
| 29 | --- a/cpu/exception.cc | ||
| 30 | +++ b/cpu/exception.cc | ||
| 31 | @@ -856,6 +856,13 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code, bx_bool trap) | ||
| 32 | // trap into debugger (similar as done when PANIC occured) | ||
| 33 | bx_debug_break(); | ||
| 34 | #endif | ||
| 35 | +#if BX_GDBSTUB | ||
| 36 | + if (bx_dbg.gdbstub_enabled) { | ||
| 37 | + fprintf(stderr, "Triple fault: stopping for gdb\n"); | ||
| 38 | + BX_CPU_THIS_PTR ispanic = 1; | ||
| 39 | + longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1); | ||
| 40 | + } | ||
| 41 | +#endif | ||
| 42 | if (SIM->get_param_bool(BXPN_RESET_ON_TRIPLE_FAULT)->get()) { | ||
| 43 | BX_ERROR(("exception(): 3rd (%d) exception with no resolution, shutdown status is %02xh, resetting", vector, DEV_cmos_get_reg(0x0f))); | ||
| 44 | bx_pc_system.Reset(BX_RESET_SOFTWARE); | ||
| 45 | diff --git a/gdbstub.cc b/gdbstub.cc | ||
| 46 | index f58f866..bc5ed61 100644 | ||
| 47 | --- a/gdbstub.cc | ||
| 48 | +++ b/gdbstub.cc | ||
| 49 | @@ -471,7 +471,12 @@ static void debug_loop(void) | ||
| 50 | } | ||
| 51 | |||
| 52 | stub_trace_flag = 0; | ||
| 53 | + bx_cpu.ispanic = 0; | ||
| 54 | bx_cpu.cpu_loop(0); | ||
| 55 | + if (bx_cpu.ispanic) | ||
| 56 | + { | ||
| 57 | + last_stop_reason = GDBSTUB_EXECUTION_BREAKPOINT; | ||
| 58 | + } | ||
| 59 | |||
| 60 | DEV_vga_refresh(); | ||
| 61 | |||
| 62 | @@ -502,19 +507,19 @@ static void debug_loop(void) | ||
| 63 | |||
| 64 | BX_INFO(("stepping")); | ||
| 65 | stub_trace_flag = 1; | ||
| 66 | + bx_cpu.ispanic = 0; | ||
| 67 | bx_cpu.cpu_loop(0); | ||
| 68 | DEV_vga_refresh(); | ||
| 69 | stub_trace_flag = 0; | ||
| 70 | BX_INFO(("stopped with %x", last_stop_reason)); | ||
| 71 | buf[0] = 'S'; | ||
| 72 | - if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT || | ||
| 73 | - last_stop_reason == GDBSTUB_TRACE) | ||
| 74 | + if (last_stop_reason == GDBSTUB_TRACE && !bx_cpu.ispanic) | ||
| 75 | { | ||
| 76 | write_signal(&buf[1], SIGTRAP); | ||
| 77 | } | ||
| 78 | else | ||
| 79 | { | ||
| 80 | - write_signal(&buf[1], SIGTRAP); | ||
| 81 | + write_signal(&buf[1], SIGSEGV); | ||
| 82 | } | ||
| 83 | put_reply(buf); | ||
| 84 | break; | ||
| 85 | -- | ||
| 86 | 1.6.2.3 | ||
| 87 | |||
diff --git a/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch b/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch new file mode 100644 index 0000000..8b6e090 --- /dev/null +++ b/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch | |||
| @@ -0,0 +1,93 @@ | |||
| 1 | From 314833401978558db06bbb4f4f76e4dc7b603744 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Alex Busenius <s9albuse@stud.uni-saarland.de> | ||
| 3 | Date: Mon, 27 Apr 2009 16:33:54 +0200 | ||
| 4 | Subject: bochs-2.3.7 page fault segv | ||
| 5 | |||
| 6 | --- | ||
| 7 | bochs.h | 1 + | ||
| 8 | cpu/exception.cc | 4 ++++ | ||
| 9 | gdbstub.cc | 17 ++++++++++++++++- | ||
| 10 | 3 files changed, 21 insertions(+), 1 deletions(-) | ||
| 11 | |||
| 12 | diff --git a/bochs.h b/bochs.h | ||
| 13 | index 75bcd96..657c7b8 100644 | ||
| 14 | --- a/bochs.h | ||
| 15 | +++ b/bochs.h | ||
| 16 | @@ -433,6 +433,7 @@ BOCHSAPI extern logfunc_t *genlog; | ||
| 17 | void bx_gdbstub_init(void); | ||
| 18 | void bx_gdbstub_break(void); | ||
| 19 | int bx_gdbstub_check(unsigned int eip); | ||
| 20 | +void bx_gdbstub_exception(unsigned int nr); | ||
| 21 | #define GDBSTUB_STOP_NO_REASON (0xac0) | ||
| 22 | |||
| 23 | #if BX_SUPPORT_SMP | ||
| 24 | diff --git a/cpu/exception.cc b/cpu/exception.cc | ||
| 25 | index fb3abfc..8dac5ca 100644 | ||
| 26 | --- a/cpu/exception.cc | ||
| 27 | +++ b/cpu/exception.cc | ||
| 28 | @@ -1046,6 +1046,10 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code, bx_bool trap) | ||
| 29 | |||
| 30 | BX_CPU_THIS_PTR errorno++; | ||
| 31 | |||
| 32 | +#if BX_GDBSTUB | ||
| 33 | + bx_gdbstub_exception(vector); | ||
| 34 | +#endif | ||
| 35 | + | ||
| 36 | if (real_mode()) { | ||
| 37 | // not INT, no error code pushed | ||
| 38 | BX_CPU_THIS_PTR interrupt(vector, 0, 0, 0); | ||
| 39 | diff --git a/gdbstub.cc b/gdbstub.cc | ||
| 40 | index bc5ed61..ad59373 100644 | ||
| 41 | --- a/gdbstub.cc | ||
| 42 | +++ b/gdbstub.cc | ||
| 43 | @@ -47,6 +47,7 @@ static int last_stop_reason = GDBSTUB_STOP_NO_REASON; | ||
| 44 | #define GDBSTUB_EXECUTION_BREAKPOINT (0xac1) | ||
| 45 | #define GDBSTUB_TRACE (0xac2) | ||
| 46 | #define GDBSTUB_USER_BREAK (0xac3) | ||
| 47 | +#define GDBSTUB_EXCEPTION_0E (0xac4) | ||
| 48 | |||
| 49 | static bx_list_c *gdbstub_list; | ||
| 50 | static int listen_socket_fd; | ||
| 51 | @@ -323,6 +324,12 @@ int bx_gdbstub_check(unsigned int eip) | ||
| 52 | return GDBSTUB_STOP_NO_REASON; | ||
| 53 | } | ||
| 54 | |||
| 55 | +void bx_gdbstub_exception(unsigned int nr) | ||
| 56 | +{ | ||
| 57 | + if (nr == 0x0e) | ||
| 58 | + last_stop_reason = GDBSTUB_EXCEPTION_0E; | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | static int remove_breakpoint(unsigned int addr, int len) | ||
| 62 | { | ||
| 63 | unsigned int i; | ||
| 64 | @@ -493,6 +500,10 @@ static void debug_loop(void) | ||
| 65 | { | ||
| 66 | write_signal(&buf[1], SIGTRAP); | ||
| 67 | } | ||
| 68 | + else if (last_stop_reason == GDBSTUB_EXCEPTION_0E) | ||
| 69 | + { | ||
| 70 | + write_signal(&buf[1], SIGSEGV); | ||
| 71 | + } | ||
| 72 | else | ||
| 73 | { | ||
| 74 | write_signal(&buf[1], 0); | ||
| 75 | @@ -517,10 +528,14 @@ static void debug_loop(void) | ||
| 76 | { | ||
| 77 | write_signal(&buf[1], SIGTRAP); | ||
| 78 | } | ||
| 79 | - else | ||
| 80 | + else if (last_stop_reason == GDBSTUB_EXCEPTION_0E) | ||
| 81 | { | ||
| 82 | write_signal(&buf[1], SIGSEGV); | ||
| 83 | } | ||
| 84 | + else | ||
| 85 | + { | ||
| 86 | + write_signal(&buf[1], 0); | ||
| 87 | + } | ||
| 88 | put_reply(buf); | ||
| 89 | break; | ||
| 90 | } | ||
| 91 | -- | ||
| 92 | 1.6.2.3 | ||
| 93 | |||
diff --git a/pintos-progos/misc/bochs-2.3.7-build.sh b/pintos-progos/misc/bochs-2.3.7-build.sh new file mode 100755 index 0000000..57e35d1 --- /dev/null +++ b/pintos-progos/misc/bochs-2.3.7-build.sh | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #! /bin/sh -e | ||
| 2 | |||
| 3 | if test -z "$SRCDIR" || test -z "$PINTOSDIR" || test -z "$DSTDIR"; then | ||
| 4 | echo "usage: env SRCDIR=<srcdir> PINTOSDIR=<srcdir> DSTDIR=<dstdir> sh $0" | ||
| 5 | echo " where <srcdir> contains bochs-2.3.7.tar.gz" | ||
| 6 | echo " and <pintosdir> is the root of the pintos source tree" | ||
| 7 | echo " and <dstdir> is the installation prefix (e.g. /usr/local)" | ||
| 8 | exit 1 | ||
| 9 | fi | ||
| 10 | |||
| 11 | cd /tmp | ||
| 12 | mkdir bochs-pintos-$$ | ||
| 13 | cd bochs-pintos-$$ | ||
| 14 | mkdir bochs-2.3.7 | ||
| 15 | tar xzf $SRCDIR/bochs-2.3.7.tar.gz | ||
| 16 | cd bochs-2.3.7 | ||
| 17 | cat $PINTOSDIR/src/misc/0001-bochs-2.3.7-jitter.patch | patch -p1 | ||
| 18 | cat $PINTOSDIR/src/misc/0002-bochs-2.3.7-triple-fault.patch | patch -p1 | ||
| 19 | cat $PINTOSDIR/src/misc/0003-bochs-2.3.7-page-fault-segv.patch | patch -p1 | ||
| 20 | cat $PINTOSDIR/src/misc/bochs-2.3.7-gcc43.patch | patch -p1 | ||
| 21 | cat $PINTOSDIR/src/misc/bochs-2.3.7-typos.patch | patch -p1 | ||
| 22 | cat $PINTOSDIR/src/misc/bochs-2.3.7-linux3x.patch | patch -p1 | ||
| 23 | autoconf | ||
| 24 | |||
| 25 | CFGOPTIONAL="--enable-large-pages --enable-mmx --enable-usb --enable-pci --enable-pcidev --enable-acpi --enable-global-pages --enable-show-ips" | ||
| 26 | CFGOPTIMIZE="--enable-all-optimizations --enable-guest2host-tlb --enable-repeat-speedups --enable-trace-cache --enable-icache --enable-fast-function-calls --enable-idle-hack " | ||
| 27 | CFGOPTS="--prefix=$DSTDIR --enable-ignore-bad-msr --enable-disasm --enable-logging --enable-fpu --enable-alignment-check --enable-plugins --enable-cpu-level=6 --enable-readline --without-sdl --without-svga --without-wx --with-x --with-x11 --with-term --with-nogui $CFGOPTIONAL" | ||
| 28 | mkdir plain && | ||
| 29 | cd plain && | ||
| 30 | ../configure $CFGOPTS --enable-gdb-stub && | ||
| 31 | # make -j3 && | ||
| 32 | make && echo "done building plain" && | ||
| 33 | sudo make install && | ||
| 34 | cd .. && | ||
| 35 | mkdir with-dbg && | ||
| 36 | cd with-dbg && | ||
| 37 | ../configure --enable-debugger $CFGOPTS && | ||
| 38 | # make -j3 && | ||
| 39 | make && echo "done building with-dbg" && | ||
| 40 | sudo cp -v bochs $DSTDIR/bin/bochs-dbg && | ||
| 41 | cd .. && | ||
| 42 | echo "SUCCESS" | ||
diff --git a/pintos-progos/misc/bochs-2.3.7-gcc43.patch b/pintos-progos/misc/bochs-2.3.7-gcc43.patch new file mode 100644 index 0000000..4646edf --- /dev/null +++ b/pintos-progos/misc/bochs-2.3.7-gcc43.patch | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | --- bochs-2.3.7.orig/bx_debug/symbols.cc 2008/03/30 14:32:14 1.11 | ||
| 2 | +++ bochs-2.3.7/bx_debug/symbols.cc 2008/06/16 17:09:52 1.12 | ||
| 3 | @@ -95,6 +95,9 @@ | ||
| 4 | #endif | ||
| 5 | |||
| 6 | using namespace std; | ||
| 7 | +#ifdef __GNUC__ | ||
| 8 | +using namespace __gnu_cxx; | ||
| 9 | +#endif | ||
| 10 | |||
| 11 | struct symbol_entry_t | ||
| 12 | { | ||
diff --git a/pintos-progos/misc/bochs-2.3.7-linux3x.patch b/pintos-progos/misc/bochs-2.3.7-linux3x.patch new file mode 100644 index 0000000..1c84060 --- /dev/null +++ b/pintos-progos/misc/bochs-2.3.7-linux3x.patch | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | --- a/configure.in 2012-01-03 11:12:22.104612131 +0100 | ||
| 2 | +++ b/configure.in 2012-01-03 11:13:05.507941106 +0100 | ||
| 3 | @@ -715,7 +715,7 @@ AC_ARG_ENABLE(pcidev, | ||
| 4 | PCIDEV_MODULE_MAKE_ALL="all-kernel24" | ||
| 5 | KERNEL_MODULE_SUFFIX="o" | ||
| 6 | ;; | ||
| 7 | - 2.6*) | ||
| 8 | + 2.6*|3*) | ||
| 9 | PCIDEV_MODULE_MAKE_ALL="all-kernel26" | ||
| 10 | KERNEL_MODULE_SUFFIX="ko" | ||
| 11 | ;; | ||
diff --git a/pintos-progos/misc/bochs-2.3.7-typos.patch b/pintos-progos/misc/bochs-2.3.7-typos.patch new file mode 100644 index 0000000..c9fb168 --- /dev/null +++ b/pintos-progos/misc/bochs-2.3.7-typos.patch | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | diff -NaurwB bochs-2.3.7.orig/cpu/ia_opcodes.h bochs-2.3.7/cpu/ia_opcodes.h | ||
| 2 | --- bochs-2.3.7.orig/cpu/ia_opcodes.h 2008-05-30 22:35:08.000000000 +0200 | ||
| 3 | +++ bochs-2.3.7/cpu/ia_opcodes.h 2008-06-04 14:56:46.000000000 +0200 | ||
| 4 | @@ -891,7 +891,7 @@ | ||
| 5 | bx_define_opcode(BX_IA_PF2ID_PqQq, BX_CPU_C::PF2ID_PqQq) | ||
| 6 | bx_define_opcode(BX_IA_PF2IW_PqQq, BX_CPU_C::PF2IW_PqQq) | ||
| 7 | bx_define_opcode(BX_IA_PFACC_PqQq, BX_CPU_C::PFACC_PqQq) | ||
| 8 | -bx_define_opcode(BX_IA_PFADD_PqQq, BX_CPU_C::BX_PFADD_PqQq) | ||
| 9 | +bx_define_opcode(BX_IA_PFADD_PqQq, BX_CPU_C::PFADD_PqQq) | ||
| 10 | bx_define_opcode(BX_IA_PFCMPEQ_PqQq, BX_CPU_C::PFCMPEQ_PqQq) | ||
| 11 | bx_define_opcode(BX_IA_PFCMPGE_PqQq, BX_CPU_C::PFCMPGE_PqQq) | ||
| 12 | bx_define_opcode(BX_IA_PFCMPGT_PqQq, BX_CPU_C::PFCMPGT_PqQq) | ||
| 13 | diff -NaurwB bochs-2.3.7.orig/iodev/iodebug.h bochs-2.3.7/iodev/iodebug.h | ||
| 14 | --- bochs-2.3.7.orig/iodev/iodebug.h 2008-05-01 22:46:58.000000000 +0200 | ||
| 15 | +++ bochs-2.3.7/iodev/iodebug.h 2008-06-04 14:45:50.000000000 +0200 | ||
| 16 | @@ -18,7 +18,7 @@ | ||
| 17 | virtual void init(void); | ||
| 18 | virtual void reset (unsigned type) {} | ||
| 19 | static void mem_write(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data); | ||
| 20 | - static void mem_read(BX_CPU_C *cpu, bx_phy_addressu addr, unsigned len, void *data); | ||
| 21 | + static void mem_read(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data); | ||
| 22 | |||
| 23 | private: | ||
| 24 | static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len); | ||
diff --git a/pintos-progos/misc/gcc-3.3.6-cross-howto b/pintos-progos/misc/gcc-3.3.6-cross-howto new file mode 100644 index 0000000..ad25173 --- /dev/null +++ b/pintos-progos/misc/gcc-3.3.6-cross-howto | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | Here are the commands we used to build and install the SPARC | ||
| 2 | cross-compiler: | ||
| 3 | |||
| 4 | PINTOSROOT=$HOME/private/pintos | ||
| 5 | |||
| 6 | PREFIX=/usr/class/cs140/`uname -m` | ||
| 7 | PATH=$PATH:$PREFIX/bin | ||
| 8 | TMP=`pwd` | ||
| 9 | |||
| 10 | wget ftp://ftp.gnu.org/pub/gnu/binutils/binutils-2.15.tar.bz2 | ||
| 11 | wget ftp://sources.redhat.com/pub/newlib/newlib-1.13.0.tar.gz | ||
| 12 | wget ftp://ftp.gnu.org/pub/gnu/gcc/gcc-3.3.6/gcc-core-3.3.6.tar.bz2 | ||
| 13 | wget ftp://ftp.gnu.org/pub/gnu/gdb/gdb-6.3.tar.bz2 | ||
| 14 | |||
| 15 | bzcat binutils-2.15.tar.bz2 | tar x | ||
| 16 | tar xzf newlib-1.13.0.tar.gz | ||
| 17 | bzcat gcc-core-3.3.6.tar.bz2 | tar x | ||
| 18 | bzcat gdb-6.3.tar.bz2 | tar x | ||
| 19 | |||
| 20 | cd $TMP/binutils-2.15 | ||
| 21 | mkdir i386 | ||
| 22 | cd i386 | ||
| 23 | ../configure --target=i386-elf --prefix=$PREFIX | ||
| 24 | make LDFLAGS=-lintl | ||
| 25 | make install | ||
| 26 | |||
| 27 | cd $TMP/gcc-3.3.6 | ||
| 28 | mkdir i386 | ||
| 29 | cd i386 | ||
| 30 | ../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 | ||
| 31 | make | ||
| 32 | make install | ||
| 33 | |||
| 34 | cd $TMP/gdb-6.3 | ||
| 35 | mkdir i386 | ||
| 36 | cd i386 | ||
| 37 | ../configure --target=i386-elf --prefix=$PREFIX --disable-tui | ||
| 38 | make LDFLAGS=-lintl | ||
| 39 | make install | ||
diff --git a/pintos-progos/misc/gdb-macros b/pintos-progos/misc/gdb-macros new file mode 100644 index 0000000..a0b68c3 --- /dev/null +++ b/pintos-progos/misc/gdb-macros | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | # | ||
| 2 | # A set of useful macros that can help debug Pintos. | ||
| 3 | # | ||
| 4 | # Include with "source" cmd in gdb. | ||
| 5 | # Use "help user-defined" for help. | ||
| 6 | # | ||
| 7 | # Author: Godmar Back <gback@cs.vt.edu>, Feb 2006 | ||
| 8 | # | ||
| 9 | # $Id: gdb-macros,v 1.1 2006-04-07 18:29:34 blp Exp $ | ||
| 10 | # | ||
| 11 | |||
| 12 | # for internal use | ||
| 13 | define offsetof | ||
| 14 | set $rc = (char*)&((struct $arg0 *)0)->$arg1 - (char*)0 | ||
| 15 | end | ||
| 16 | |||
| 17 | define list_entry | ||
| 18 | offsetof $arg1 $arg2 | ||
| 19 | set $rc = ((struct $arg1 *) ((uint8_t *) ($arg0) - $rc)) | ||
| 20 | end | ||
| 21 | |||
| 22 | # dump a Pintos list | ||
| 23 | define dumplist | ||
| 24 | set $list = $arg0 | ||
| 25 | set $e = $list->head.next | ||
| 26 | set $i = 0 | ||
| 27 | while $e != &(($arg0).tail) | ||
| 28 | list_entry $e $arg1 $arg2 | ||
| 29 | set $l = $rc | ||
| 30 | printf "pintos-debug: dumplist #%d: %p ", $i++, $l | ||
| 31 | output *$l | ||
| 32 | set $e = $e->next | ||
| 33 | printf "\n" | ||
| 34 | end | ||
| 35 | end | ||
| 36 | |||
| 37 | document dumplist | ||
| 38 | Dump the content of a Pintos list, | ||
| 39 | invoke as dumplist name_of_list name_of_struct name_of_elem_in_list_struct | ||
| 40 | end | ||
| 41 | |||
| 42 | # print a thread's backtrace, given a pointer to the struct thread * | ||
| 43 | define btthread | ||
| 44 | if $arg0 == ($esp - ((unsigned)$esp % 4096)) | ||
| 45 | bt | ||
| 46 | else | ||
| 47 | set $saveEIP = $eip | ||
| 48 | set $saveESP = $esp | ||
| 49 | set $saveEBP = $ebp | ||
| 50 | |||
| 51 | set $esp = ((struct thread *)$arg0)->stack | ||
| 52 | set $ebp = ((void**)$esp)[2] | ||
| 53 | set $eip = ((void**)$esp)[4] | ||
| 54 | |||
| 55 | bt | ||
| 56 | |||
| 57 | set $eip = $saveEIP | ||
| 58 | set $esp = $saveESP | ||
| 59 | set $ebp = $saveEBP | ||
| 60 | end | ||
| 61 | end | ||
| 62 | document btthread | ||
| 63 | Show the backtrace of a thread, | ||
| 64 | invoke as btthread pointer_to_struct_thread | ||
| 65 | end | ||
| 66 | |||
| 67 | # print backtraces associated with all threads in a list | ||
| 68 | define btthreadlist | ||
| 69 | set $list = $arg0 | ||
| 70 | set $e = $list->head.next | ||
| 71 | while $e != &(($arg0).tail) | ||
| 72 | list_entry $e thread $arg1 | ||
| 73 | printf "pintos-debug: dumping backtrace of thread '%s' @%p\n", \ | ||
| 74 | ((struct thread*)$rc)->name, $rc | ||
| 75 | btthread $rc | ||
| 76 | set $e = $e->next | ||
| 77 | printf "\n" | ||
| 78 | end | ||
| 79 | end | ||
| 80 | document btthreadlist | ||
| 81 | Given a list of threads, print each thread's backtrace | ||
| 82 | invoke as btthreadlist name_of_list name_of_elem_in_list_struct | ||
| 83 | end | ||
| 84 | |||
| 85 | # print backtraces of all threads (based on 'all_list' all threads list) | ||
| 86 | define btthreadall | ||
| 87 | btthreadlist all_list allelem | ||
| 88 | end | ||
| 89 | document btthreadall | ||
| 90 | Print backtraces of all threads | ||
| 91 | end | ||
| 92 | |||
| 93 | # print a correct backtrace by adjusting $eip | ||
| 94 | # this works best right at intr0e_stub | ||
| 95 | define btpagefault | ||
| 96 | set $saveeip = $eip | ||
| 97 | set $eip = ((void**)$esp)[1] | ||
| 98 | backtrace | ||
| 99 | set $eip = $saveeip | ||
| 100 | end | ||
| 101 | document btpagefault | ||
| 102 | Print a backtrace of the current thread after a pagefault | ||
| 103 | end | ||
| 104 | |||
| 105 | # invoked whenever the program stops | ||
| 106 | define hook-stop | ||
| 107 | # stopped at stub #0E = #14 (page fault exception handler stub) | ||
| 108 | if ($eip == intr0e_stub) | ||
| 109 | set $savedeip = ((void**)$esp)[1] | ||
| 110 | # if this was in user mode, the OS should handle it | ||
| 111 | # either handle the page fault or terminate the process | ||
| 112 | if ($savedeip < 0xC0000000) | ||
| 113 | printf "pintos-debug: a page fault exception occurred in user mode\n" | ||
| 114 | printf "pintos-debug: hit 'c' to continue, or 's' to step to intr_handler\n" | ||
| 115 | else | ||
| 116 | # if this was in kernel mode, a stack trace might be useful | ||
| 117 | printf "pintos-debug: a page fault occurred in kernel mode\n" | ||
| 118 | btpagefault | ||
| 119 | end | ||
| 120 | end | ||
| 121 | end | ||
| 122 | |||
| 123 | # load symbols for a Pintos user program | ||
| 124 | define loadusersymbols | ||
| 125 | shell objdump -h $arg0 | awk '/.text/ { print "add-symbol-file $arg0 0x"$4 }' > .loadsymbols | ||
| 126 | source .loadsymbols | ||
| 127 | shell rm -f .loadsymbols | ||
| 128 | end | ||
| 129 | document loadusersymbols | ||
| 130 | Load the symbols contained in a user program's executable. | ||
| 131 | Example: | ||
| 132 | loadusersymbols tests/userprog/exec-multiple | ||
| 133 | end | ||
| 134 | |||
| 135 | define debugpintos | ||
| 136 | target remote localhost:1234 | ||
| 137 | end | ||
| 138 | document debugpintos | ||
| 139 | Attach debugger to pintos process | ||
| 140 | end | ||
diff --git a/pintos-progos/notes/1.txt b/pintos-progos/notes/1.txt new file mode 100644 index 0000000..9d478f7 --- /dev/null +++ b/pintos-progos/notes/1.txt | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | Getting Started with PINTOS | ||
| 2 | =========================== | ||
| 3 | |||
| 4 | Building Project 1 | ||
| 5 | ------------------ | ||
| 6 | |||
| 7 | pintos $ cd src/threads | ||
| 8 | threads $ make | ||
| 9 | |||
| 10 | |||
| 11 | Building Bochs | ||
| 12 | -------------- | ||
| 13 | You should have a patched bochs install available. | ||
| 14 | |||
| 15 | See | ||
| 16 | |||
| 17 | http://courses.mpi-sws.org/os-ss11/assignments/pintos/pintos_12.html#SEC160 | ||
| 18 | |||
| 19 | There is a build script src/misc/bochs-2.3.7-build.sh in the pintos fork from Saarland, | ||
| 20 | which (after small modifications) works on a modern Ubuntu x86. | ||
| 21 | |||
| 22 | For Ubuntu 11 with a Linux 3.0 kernel, you need to: | ||
| 23 | |||
| 24 | * Regenerate the configure script (autoconf configure.in) | ||
| 25 | * Patch the test for Linux 2.4 or 2.6 | ||
| 26 | |||
| 27 | After building, copy bochs and bochs-gdb to the pintos/src/utils directory | ||
| 28 | |||
| 29 | Running | ||
| 30 | ------- | ||
| 31 | |||
| 32 | # [pintos/src] | ||
| 33 | PATH=`pwd`/utils:$PATH | ||
| 34 | |||
| 35 | cd threads/build | ||
| 36 | # [pintos/src/threads/build] | ||
| 37 | pintos run alarm-multiple > logfile | ||
| 38 | |||
| 39 | |||
| 40 | ### Reproducability | ||
| 41 | |||
| 42 | This command line flags to pintos influence reproducability. | ||
| 43 | Remember: you need the patched bochs build. | ||
| 44 | |||
| 45 | -j seed ... Reproducible behavior | ||
| 46 | -r ... Real-Time behavior | ||
| 47 | |||
| 48 | Running with qemu | ||
| 49 | ----------------- | ||
| 50 | |||
| 51 | # [pintos/src] | ||
| 52 | vim utils/pintos # comment line with -no-kqemu flag | ||
| 53 | |||
| 54 | cd threads/build | ||
| 55 | # [pintos/src/threads/build] | ||
| 56 | pintos --qemu -- run alarm-multiple | ||
| 57 | |||
| 58 | Debugging | ||
| 59 | --------- | ||
| 60 | |||
| 61 | pintos $ vim utils/pintos-gdb | ||
| 62 | |||
| 63 | GDBMACROS=${PINTOS_SRC}/misc/gdb-macros | ||
| 64 | |||
| 65 | [pts/0 build] $ pintos --gdb -- run alarm-multiple | ||
| 66 | [pts/1 build] $ pintos-gdb kernel.o | ||
| 67 | (gdb) debugpintos | ||
| 68 | |||
| 69 | Testing | ||
| 70 | ------- | ||
| 71 | |||
| 72 | * Running all tests | ||
| 73 | |||
| 74 | build $ make check | ||
| 75 | |||
| 76 | * Running a single test | ||
| 77 | |||
| 78 | build $ rm tests/threads/alarm-multiple.result | ||
| 79 | build $ make tests/threads/alarm-multiple.result | ||
| 80 | |||
| 81 | |||
diff --git a/pintos-progos/notes/2.txt b/pintos-progos/notes/2.txt new file mode 100644 index 0000000..c81b980 --- /dev/null +++ b/pintos-progos/notes/2.txt | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | Projekt 1 - Threads | ||
| 2 | =================== | ||
| 3 | |||
| 4 | alarm clock | ||
| 5 | ----------- | ||
| 6 | The simplest strategy is to maintain a wait list for | ||
| 7 | all threads blocked for sleep. | ||
| 8 | |||
| 9 | * In 'timer_interrupt', check for threads which can be | ||
| 10 | unblocked from sleeping | ||
| 11 | * In 'sleep', set sleep timeout in thread, block the | ||
| 12 | thread and put it on the sleep list | ||
| 13 | |||
| 14 | Notes: | ||
| 15 | |||
| 16 | * There are three places where a thread is added to the | ||
| 17 | ready list: | ||
| 18 | - thread_init | ||
| 19 | - thread_yield | ||
| 20 | - thread_unblock | ||
| 21 | * Iterate list with removal: | ||
| 22 | for (e = list_begin (&list); e != list_end (&list); ) | ||
| 23 | if(...) | ||
| 24 | e = list_remove(e)->prev; | ||
| 25 | /* Unblock must be called AFTER removing, as thread.elem is reused */ | ||
| 26 | else | ||
| 27 | e = list_next(e); | ||
| 28 | |||
| 29 | Stats: | ||
| 30 | |||
| 31 | pintos/src/devices/timer.c | 40 ++++++++++++++++++++++++++++++++++++++-- | ||
| 32 | pintos/src/threads/thread.h | 3 +++ | ||
| 33 | 2 files changed, 41 insertions(+), 2 deletions(-) | ||
| 34 | |||
| 35 | Design & Implementation time: 4 hours | ||
| 36 | |||
| 37 | Priority Scheduler | ||
| 38 | ------------------ | ||
| 39 | |||
| 40 | A simple implementation of the priority scheduler (64 priority levels, round robin within | ||
| 41 | one priority group). | ||
| 42 | |||
| 43 | * If a new task arrives with a higher priority, switch to this group | ||
| 44 | * If the currently active group is empty, search for the group with the next highest priority | ||
| 45 | |||
| 46 | Notes: | ||
| 47 | |||
| 48 | * thread_{init,unblock,yield} now call thread_ready, which updates the lowest ready priority | ||
| 49 | * The thread_unblock operation does not yield a new thread immediately. Therefore, we need to check | ||
| 50 | later whether we need to switch to a higher priority thread (via thread_yield). | ||
| 51 | As thread_unblock is called with interrupts off, it seemed best to perform | ||
| 52 | this check when interrupts are enabled. This is only necessary if a higher priority task | ||
| 53 | is ready. | ||
| 54 | * First attempt passed alarm-priority, but failed to pass the priority-preempt test. | ||
| 55 | But the debugging facilities are fantastic, so it was easy to spot the problem | ||
| 56 | * Wolfgang suggested to yield a software interrupt when unblocking instead of modifying | ||
| 57 | interrupt_enable. | ||
| 58 | |||
| 59 | Stats: | ||
| 60 | |||
| 61 | pintos/src/threads/interrupt.c | 3 +- | ||
| 62 | pintos/src/threads/thread.c | 60 ++++++++++++++++++++++++++++++++-------- | ||
| 63 | pintos/src/threads/thread.h | 1 + | ||
| 64 | 3 files changed, 51 insertions(+), 13 deletions(-) | ||
| 65 | |||
| 66 | Design and implementation time: 3 hours | ||
| 67 | |||
| 68 | Priority Locks | ||
| 69 | -------------- | ||
| 70 | |||
| 71 | We also need to select higher priority task first from locks, semaphores and condition variables. | ||
| 72 | This easiest implementation searches for the thread with the highest priority in the wait queue. | ||
| 73 | |||
| 74 | Notes: | ||
| 75 | |||
| 76 | * It is sufficient to implement the priority based selection twice, for sema_up and | ||
| 77 | cond_signal. cond_signal is a little bit harder, as we need to store the priority | ||
| 78 | (or the waiting thread) in the semaphore_elem type | ||
| 79 | * There are some handy list utility functions; in this case, list_max does a fine job | ||
| 80 | for both sema_up and cond_signal | ||
| 81 | * It is difficult to implement this in an efficient (sublinear) way, because priority donation | ||
| 82 | may boost a thread at any time! | ||
| 83 | |||
| 84 | Stats: | ||
| 85 | |||
| 86 | pintos/src/threads/synch.c | 40 ++++++++++++++++++++++++++++++++++------ | ||
| 87 | 1 files changed, 34 insertions(+), 6 deletions(-) | ||
| 88 | |||
| 89 | Design and Implementation time: 1 hour | ||
| 90 | |||
| 91 | Priority Donation | ||
| 92 | ----------------- | ||
| 93 | If a thread aquires a lock, the lock holder needs to be boosted to the donated priority. | ||
| 94 | We need to deal with nesting and chaining: | ||
| 95 | |||
| 96 | * Lock/Thread correspondence: Each lock is associated with at most one thread that holds it. | ||
| 97 | Therefore, donated priority can be associated with a lock. | ||
| 98 | * If a thread t wants to obtain a lock L, and a thread with a lower priority holds it, | ||
| 99 | the thread holding the lock is boosted to the priority of the requesting thread | ||
| 100 | * Chaining: If the boosted thread is also blocked on a lock, than we also need to donate | ||
| 101 | the priority to that lock, in a transitive way. | ||
| 102 | * Nesting: If a thread may hold more than one lock, we need to keep track of the donation | ||
| 103 | to each lock. When a lock is released or the static priority changes, the highest priority | ||
| 104 | donated to other locks is assigned to the thread. | ||
| 105 | |||
| 106 | With this information, the following rules seem suitable (without proof of correctness): | ||
| 107 | |||
| 108 | * If thread T tries to aquire lock L | ||
| 109 | |||
| 110 | ==> if(L.owner) | ||
| 111 | T.locked_on = L | ||
| 112 | donate_priority (L, T.priority) | ||
| 113 | end | ||
| 114 | donate_priority (L ,p) := | ||
| 115 | L.priority v= p | ||
| 116 | L.holder.priority v= p | ||
| 117 | donate_priority( L.holder.locked_on, p) | ||
| 118 | end | ||
| 119 | |||
| 120 | * If a thread T aquires a lock L | ||
| 121 | |||
| 122 | ==> L.holder = T | ||
| 123 | T.locks += L | ||
| 124 | T.lock_on = none | ||
| 125 | |||
| 126 | |||
| 127 | * If a thread T releases a lock L | ||
| 128 | |||
| 129 | ==> L.holder = none | ||
| 130 | T.locks -= L | ||
| 131 | T.priority = max (T.locks.priority, static_priority) | ||
| 132 | |||
| 133 | To implement this, each thread needs to maintain a list of locks, a static priority, | ||
| 134 | and a reference to the lock blocking it. | ||
| 135 | |||
| 136 | Notes: | ||
| 137 | |||
| 138 | * Difficult to design, really a challenge. | ||
| 139 | Literature (short paper) could be helpful. | ||
| 140 | Maybe it would be interesting to ask for correctness proof sketches? | ||
| 141 | |||
| 142 | * First design was wrong, because I assumed locks are aquired in FIFO fashion | ||
| 143 | * First implementation failed to pass donate tests, due to a typo. Debugging | ||
| 144 | facilities are fantastic, no problem to spot this one. | ||
| 145 | * Next try, priority-donate-lower failed. Looking at the source code revealed that | ||
| 146 | we need to recompute the priority at the end of thread_set_priority. | ||
| 147 | * Next try, priority-donate-chain failed. Chaining is tricky to get right; | ||
| 148 | in my implementation, chained donations were lost after one lock was released. | ||
| 149 | |||
| 150 | * It would be an interesting option to ask for new test cases from the students | ||
| 151 | * I think it would also be a cool task to write a test for a RMS scheduling | ||
| 152 | scenario with blocking. | ||
| 153 | |||
| 154 | Stats: | ||
| 155 | |||
| 156 | pintos/src/threads/synch.c | 15 ++++++++++++ | ||
| 157 | pintos/src/threads/synch.h | 6 +++- | ||
| 158 | pintos/src/threads/thread.c | 53 +++++++++++++++++++++++++++++++++++++++++- | ||
| 159 | pintos/src/threads/thread.h | 9 ++++++- | ||
| 160 | pintos/src/utils/pintos | 2 +- | ||
| 161 | |||
| 162 | Design: 5h | ||
| 163 | Implementation: 3h | ||
| 164 | |||
diff --git a/pintos-progos/notes/3.txt b/pintos-progos/notes/3.txt new file mode 100644 index 0000000..bc64f88 --- /dev/null +++ b/pintos-progos/notes/3.txt | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | Project 2 | ||
| 2 | ========= | ||
| 3 | |||
| 4 | Working with Disks | ||
| 5 | ------------------ | ||
| 6 | |||
| 7 | Assumes you ran make in src/userprog and src/examples. | ||
| 8 | |||
| 9 | * Create a 2 MB hard disk for pintos | ||
| 10 | |||
| 11 | # [src/userprog/build] | ||
| 12 | pintos-mkdisk filesys.dsk --filesys-size=2 | ||
| 13 | |||
| 14 | * Format Disk | ||
| 15 | |||
| 16 | # -f ... format virtual disk | ||
| 17 | pintos -f -q | ||
| 18 | |||
| 19 | * Copy file to filesystem | ||
| 20 | |||
| 21 | # -p FILE ... file to put on virtual disk | ||
| 22 | # -a FILE ... newname on virtual disk | ||
| 23 | pintos -p ../../examples/echo -a echo -- -q | ||
| 24 | |||
| 25 | * Execute echo, and get file 'echo' from the virtual disk | ||
| 26 | |||
| 27 | pintos -g echo -- -q run 'echo x' | ||
| 28 | |||
| 29 | Putting all together, we can run an minimal example like that: | ||
| 30 | |||
| 31 | # [src/userprog/build] | ||
| 32 | pintos --filesys-size=2 -p ../../examples/halt -a halt -- -f -q run 'halt' | ||
| 33 | |||
| 34 | Getting Started | ||
| 35 | --------------- | ||
| 36 | |||
| 37 | * Fix the problem with the .note.gnu.build-id segment | ||
| 38 | |||
| 39 | * Change the stack setup in process.c#setup_stack() to | ||
| 40 | |||
| 41 | *esp = PHYS_BASE - 12; | ||
| 42 | |||
| 43 | * Change process_wait() to an infinite loop | ||
| 44 | |||
| 45 | This should be enough to see 'system call!' when executing | ||
| 46 | the 'halt' example. | ||
| 47 | |||
| 48 | Next, we need to implement user memory access and the | ||
| 49 | the system call dispatcher, as well as the basic | ||
| 50 | system calls halt, exit and write. | ||
| 51 | |||
| 52 | A simple implementation of user memory access first checks | ||
| 53 | whether the address is in user space, and the calls load_page. | ||
| 54 | |||
| 55 | For an initial system call dispatcher, we convert the stack pointer | ||
| 56 | saved by the processor during the interrupt to kernel space, and | ||
| 57 | then dispatch to halt, exit and write. For now, exit just terminates | ||
| 58 | the process, and write uses printf, ignoring the fd argument. | ||
| 59 | The return value is stored into %eax. | ||
| 60 | |||
| 61 | Notes: | ||
| 62 | * halt(): There is no function shutdown() in init.h, only | ||
| 63 | shutdown_poweroff in shutdown.h | ||
| 64 | |||
| 65 | * When accessing data from user space in kernel space, we need to be | ||
| 66 | sure that the entire address ranged accessed is in user space. | ||
| 67 | Note that pointers are not necessarily aligned, and thus might | ||
| 68 | involve two user pages. | ||
| 69 | Furthermore, buffers need to be copied to kernel space; | ||
| 70 | otherwise, concurrent user space operations could corrupt the kernel. | ||
| 71 | Linux allows at most one kernel page for such buffers; we follow | ||
| 72 | the same route. | ||
| 73 | |||
| 74 | * Debugging: the function hex_dump() is useful; no need to | ||
| 75 | reimplement it. | ||
| 76 | |||
| 77 | * Something went wrong with the write system call, and this | ||
| 78 | is rather tricky to debug. | ||
| 79 | I invoked the system call directly, using inline | ||
| 80 | assembly; this worked fine? | ||
| 81 | Then I tried to debug the user space program; to this | ||
| 82 | end, lookup the code address you are interested in, | ||
| 83 | and use gdb together with objdump for debugging: | ||
| 84 | |||
| 85 | Debugging 'write(1,"USA\n",4)' | ||
| 86 | |||
| 87 | break *0x0804820e # break at <write> | ||
| 88 | cont # pushl 0xc(%esp) | ||
| 89 | info registers # esp = 0xbfffffbc | ||
| 90 | x/1w (0xbfffffbc+0xc) # ==> 4 (length) | ||
| 91 | stepi # pushl 0x8(%esp) | ||
| 92 | info registers # esp = 0x......b8 | ||
| 93 | x/1w 0xbfffffb8 # ==> 4 (TOS) | ||
| 94 | x/1w (0xbfffffb8+8) # ==> 1 (wrong) !!! | ||
| 95 | |||
| 96 | Apparently, the inline assembler in pintos does not use | ||
| 97 | the right constraints. | ||
| 98 | |||
| 99 | Stat: | ||
| 100 | pintos/src/lib/user/syscall.c | 6 +- | ||
| 101 | pintos/src/userprog/process.c | 5 ++- | ||
| 102 | pintos/src/userprog/syscall.c | 92 ++++++++++++++++++++++++++++++++++++++-- | ||
| 103 | |||
| 104 | Reading and Implementation Time: 6 hours | ||
| 105 | Debugging Syscalls: 5 hours | ||
| 106 | |||
| 107 | |||
| 108 | Argument Passing | ||
| 109 | ---------------- | ||
| 110 | First, we tokenize the command using strtok_r, and then setup | ||
| 111 | the stack. | ||
| 112 | |||
| 113 | Notes: | ||
| 114 | * As noted in the doc, just using strtok_r seems fine. | ||
| 115 | However, as strtok_r modifies the string even if only | ||
| 116 | the first token is needed, some copying is involved | ||
| 117 | if it is used to obtain the filename. | ||
| 118 | * Due to the detailed description in the documentation, | ||
| 119 | setting up the stack is mostly implementation work. | ||
| 120 | * One of the trickier implementation aspects is that we | ||
| 121 | modify the stack in kernel space, but need to convert | ||
| 122 | pointers to user space before pushing them on the stack. | ||
| 123 | * Debugging: Optimizations were really troublesome debugging | ||
| 124 | this task; making setup_stack non-static at least helped | ||
| 125 | to set a suitable breakpoint. In the end, printf was the | ||
| 126 | better debugging aid for this task. | ||
| 127 | |||
| 128 | Stat: | ||
| 129 | pintos/src/userprog/process.c | 116 +++++++++++++++++++++++++++++++++-------- | ||
| 130 | |||
| 131 | Design and Implementation Time: 4 hours | ||
| 132 | |||
| 133 | |||
| 134 | Process Management: exec, wait and exit | ||
| 135 | --------------------------------------- | ||
| 136 | The wait system call requires that all children | ||
| 137 | of a process are known, that the exit code of | ||
| 138 | a process is stored until collected by the parent, | ||
| 139 | and that the parent can block until the child | ||
| 140 | process terminates. | ||
| 141 | |||
| 142 | One difficult aspect in the design is that kernel | ||
| 143 | threads are not processes, and that child threads | ||
| 144 | may exit after their parent. It is important to | ||
| 145 | note that threads do not need to wait for their | ||
| 146 | children, but that we need to keep the exit status | ||
| 147 | until the parent exits. | ||
| 148 | |||
| 149 | In the original design, a thread is cleaned up when | ||
| 150 | in the scheduler right after it died. In our design | ||
| 151 | we delay the cleanup if the parent thread is still alive. | ||
| 152 | |||
| 153 | Another issue is that thread_create needs to block | ||
| 154 | until the load process of the child thread has finished. | ||
| 155 | |||
| 156 | Notes: | ||
| 157 | * I wanted to use the same semaphore for startup and wait. | ||
| 158 | This works, but we need one additional variable (or bit) | ||
| 159 | to distinguish failure at load time from failure at | ||
| 160 | runtime. | ||
| 161 | * Ugly 1: thread_create only gives back a tid, | ||
| 162 | so it is not possible to directly access the semaphore | ||
| 163 | in process_execute. Therefore we need to iterate over the | ||
| 164 | child list (which is not that bad, because if loading failed, | ||
| 165 | the child needs to be removed from the list anyway). | ||
| 166 | * Ugly 2: We up the semaphore used to synchronize | ||
| 167 | with process_execute and process_wait in thread.c, for | ||
| 168 | all threads. | ||
| 169 | * As also noted by rene, it is important to identifiy memory leaks, | ||
| 170 | as early as possible. To this end, first add debug messages to | ||
| 171 | page_alloc/page_free, and then run test programs to identify leaking | ||
| 172 | pages. Then debug, add conditional breakpoints to stop when a leaking | ||
| 173 | page is allocated, and inspect the stacktrace to find the culprit. | ||
| 174 | |||
| 175 | Stats: | ||
| 176 | pintos/src/threads/thread.c | 31 +++++++++++++++++--- | ||
| 177 | pintos/src/threads/thread.h | 8 +++++ | ||
| 178 | pintos/src/userprog/process.c | 60 ++++++++++++++++++++++++++++++++-------- | ||
| 179 | pintos/src/userprog/syscall.c | 19 +++++++++--- | ||
| 180 | |||
| 181 | Design and Implementation Time: 7 hours | ||
| 182 | |||
| 183 | File I/O System Calls | ||
| 184 | --------------------- | ||
| 185 | For file I/O we need to implement synchronization (filesys is not thread safe). | ||
| 186 | The documentation states that it is not recommended to modify the code in | ||
| 187 | the filesys directory for now. A very simple solution is to use one lock for all filesystem operations, including process.c#load. | ||
| 188 | Furthermore, we need to deny writes to a a file currently running as a user | ||
| 189 | space process. | ||
| 190 | |||
| 191 | Notes: | ||
| 192 | * init_thread() must not aquire locks, and thus not allocate pages. | ||
| 193 | Otherwise, the initialization of the init thread fails. | ||
| 194 | * The {lg,sm}-full tests failed in the initial implementation; | ||
| 195 | apparently, the read/write system calls should always read/write the | ||
| 196 | full amount of bytes specified to pass this tests. This was not | ||
| 197 | clear from the assignment. | ||
| 198 | * It is not obvious that file_close calls file_allow_write. But an | ||
| 199 | executable should not be writeable during its execution. Therefore, | ||
| 200 | one needs to make sure that it stays write protected after loading | ||
| 201 | has finished. I solve this by keeping the executable open during | ||
| 202 | execution. | ||
| 203 | * The multi-oom test failed again; debugging revealed that I forgot | ||
| 204 | to close all files at process_exit. | ||
| 205 | |||
| 206 | Stats: | ||
| 207 | |||
| 208 | pintos/src/threads/thread.c | 1 + | ||
| 209 | pintos/src/threads/thread.h | 6 +- | ||
| 210 | pintos/src/userprog/process.c | 53 ++++- | ||
| 211 | pintos/src/userprog/process.h | 2 + | ||
| 212 | pintos/src/userprog/syscall.c | 435 +++++++++++++++++++++++++++++++----- | ||
| 213 | pintos/src/userprog/syscall.h | 1 + | ||
| 214 | 6 files changed, 381 insertions(+), 117 deletions(-) | ||
| 215 | Design and Implementation Time: 8 hours | ||
| 216 | |||
| 217 | Improved User Memory Access | ||
| 218 | --------------------------- | ||
| 219 | Looking at Project 3, it is a much better idea to not check whether a user | ||
| 220 | space page is valid, but just let the page fault handler do the job. | ||
| 221 | I decided to exit the process in the page fault handler if the address | ||
| 222 | is in user space. One needs to take care of temporary memory allocated | ||
| 223 | by the syscall handler, to avoid memory leaks. To this end, temporary kernel | ||
| 224 | pages allocated in the handler are recorded and either freed at the end | ||
| 225 | of the syscall or the end of the process. | ||
| 226 | |||
| 227 | Notes: | ||
| 228 | * When using this approach, it is vital to copy user buffers | ||
| 229 | before reading or writing. With virtual memory, a page fault may | ||
| 230 | require to access the file system, and thus may cause race | ||
| 231 | conditions during access to the file system | ||
| 232 | |||
| 233 | Stats: | ||
| 234 | pintos/src/threads/thread.h | 5 +- | ||
| 235 | pintos/src/userprog/exception.c | 17 ++- | ||
| 236 | pintos/src/userprog/process.c | 2 +- | ||
| 237 | pintos/src/userprog/syscall.c | 314 +++++++++++++++++++-------------------- | ||
| 238 | pintos/src/userprog/syscall.h | 2 +- | ||
| 239 | 5 files changed, 173 insertions(+), 167 deletions(-) | ||
| 240 | |||
| 241 | Implementation Time: 3 hours | ||
diff --git a/pintos-progos/tests/Algorithm/Diff.pm b/pintos-progos/tests/Algorithm/Diff.pm new file mode 100644 index 0000000..904c530 --- /dev/null +++ b/pintos-progos/tests/Algorithm/Diff.pm | |||
| @@ -0,0 +1,1713 @@ | |||
| 1 | package Algorithm::Diff; | ||
| 2 | # Skip to first "=head" line for documentation. | ||
| 3 | use strict; | ||
| 4 | |||
| 5 | use integer; # see below in _replaceNextLargerWith() for mod to make | ||
| 6 | # if you don't use this | ||
| 7 | use vars qw( $VERSION @EXPORT_OK ); | ||
| 8 | $VERSION = 1.19_01; | ||
| 9 | # ^ ^^ ^^-- Incremented at will | ||
| 10 | # | \+----- Incremented for non-trivial changes to features | ||
| 11 | # \-------- Incremented for fundamental changes | ||
| 12 | require Exporter; | ||
| 13 | *import = \&Exporter::import; | ||
| 14 | @EXPORT_OK = qw( | ||
| 15 | prepare LCS LCDidx LCS_length | ||
| 16 | diff sdiff compact_diff | ||
| 17 | traverse_sequences traverse_balanced | ||
| 18 | ); | ||
| 19 | |||
| 20 | # McIlroy-Hunt diff algorithm | ||
| 21 | # Adapted from the Smalltalk code of Mario I. Wolczko, <mario@wolczko.com> | ||
| 22 | # by Ned Konz, perl@bike-nomad.com | ||
| 23 | # Updates by Tye McQueen, http://perlmonks.org/?node=tye | ||
| 24 | |||
| 25 | # Create a hash that maps each element of $aCollection to the set of | ||
| 26 | # positions it occupies in $aCollection, restricted to the elements | ||
| 27 | # within the range of indexes specified by $start and $end. | ||
| 28 | # The fourth parameter is a subroutine reference that will be called to | ||
| 29 | # generate a string to use as a key. | ||
| 30 | # Additional parameters, if any, will be passed to this subroutine. | ||
| 31 | # | ||
| 32 | # my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen ); | ||
| 33 | |||
| 34 | sub _withPositionsOfInInterval | ||
| 35 | { | ||
| 36 | my $aCollection = shift; # array ref | ||
| 37 | my $start = shift; | ||
| 38 | my $end = shift; | ||
| 39 | my $keyGen = shift; | ||
| 40 | my %d; | ||
| 41 | my $index; | ||
| 42 | for ( $index = $start ; $index <= $end ; $index++ ) | ||
| 43 | { | ||
| 44 | my $element = $aCollection->[$index]; | ||
| 45 | my $key = &$keyGen( $element, @_ ); | ||
| 46 | if ( exists( $d{$key} ) ) | ||
| 47 | { | ||
| 48 | unshift ( @{ $d{$key} }, $index ); | ||
| 49 | } | ||
| 50 | else | ||
| 51 | { | ||
| 52 | $d{$key} = [$index]; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | return wantarray ? %d : \%d; | ||
| 56 | } | ||
| 57 | |||
| 58 | # Find the place at which aValue would normally be inserted into the | ||
| 59 | # array. If that place is already occupied by aValue, do nothing, and | ||
| 60 | # return undef. If the place does not exist (i.e., it is off the end of | ||
| 61 | # the array), add it to the end, otherwise replace the element at that | ||
| 62 | # point with aValue. It is assumed that the array's values are numeric. | ||
| 63 | # This is where the bulk (75%) of the time is spent in this module, so | ||
| 64 | # try to make it fast! | ||
| 65 | |||
| 66 | sub _replaceNextLargerWith | ||
| 67 | { | ||
| 68 | my ( $array, $aValue, $high ) = @_; | ||
| 69 | $high ||= $#$array; | ||
| 70 | |||
| 71 | # off the end? | ||
| 72 | if ( $high == -1 || $aValue > $array->[-1] ) | ||
| 73 | { | ||
| 74 | push ( @$array, $aValue ); | ||
| 75 | return $high + 1; | ||
| 76 | } | ||
| 77 | |||
| 78 | # binary search for insertion point... | ||
| 79 | my $low = 0; | ||
| 80 | my $index; | ||
| 81 | my $found; | ||
| 82 | while ( $low <= $high ) | ||
| 83 | { | ||
| 84 | $index = ( $high + $low ) / 2; | ||
| 85 | |||
| 86 | # $index = int(( $high + $low ) / 2); # without 'use integer' | ||
| 87 | $found = $array->[$index]; | ||
| 88 | |||
| 89 | if ( $aValue == $found ) | ||
| 90 | { | ||
| 91 | return undef; | ||
| 92 | } | ||
| 93 | elsif ( $aValue > $found ) | ||
| 94 | { | ||
| 95 | $low = $index + 1; | ||
| 96 | } | ||
| 97 | else | ||
| 98 | { | ||
| 99 | $high = $index - 1; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | # now insertion point is in $low. | ||
| 104 | $array->[$low] = $aValue; # overwrite next larger | ||
| 105 | return $low; | ||
| 106 | } | ||
| 107 | |||
| 108 | # This method computes the longest common subsequence in $a and $b. | ||
| 109 | |||
| 110 | # Result is array or ref, whose contents is such that | ||
| 111 | # $a->[ $i ] == $b->[ $result[ $i ] ] | ||
| 112 | # foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined. | ||
| 113 | |||
| 114 | # An additional argument may be passed; this is a hash or key generating | ||
| 115 | # function that should return a string that uniquely identifies the given | ||
| 116 | # element. It should be the case that if the key is the same, the elements | ||
| 117 | # will compare the same. If this parameter is undef or missing, the key | ||
| 118 | # will be the element as a string. | ||
| 119 | |||
| 120 | # By default, comparisons will use "eq" and elements will be turned into keys | ||
| 121 | # using the default stringizing operator '""'. | ||
| 122 | |||
| 123 | # Additional parameters, if any, will be passed to the key generation | ||
| 124 | # routine. | ||
| 125 | |||
| 126 | sub _longestCommonSubsequence | ||
| 127 | { | ||
| 128 | my $a = shift; # array ref or hash ref | ||
| 129 | my $b = shift; # array ref or hash ref | ||
| 130 | my $counting = shift; # scalar | ||
| 131 | my $keyGen = shift; # code ref | ||
| 132 | my $compare; # code ref | ||
| 133 | |||
| 134 | if ( ref($a) eq 'HASH' ) | ||
| 135 | { # prepared hash must be in $b | ||
| 136 | my $tmp = $b; | ||
| 137 | $b = $a; | ||
| 138 | $a = $tmp; | ||
| 139 | } | ||
| 140 | |||
| 141 | # Check for bogus (non-ref) argument values | ||
| 142 | if ( !ref($a) || !ref($b) ) | ||
| 143 | { | ||
| 144 | my @callerInfo = caller(1); | ||
| 145 | die 'error: must pass array or hash references to ' . $callerInfo[3]; | ||
| 146 | } | ||
| 147 | |||
| 148 | # set up code refs | ||
| 149 | # Note that these are optimized. | ||
| 150 | if ( !defined($keyGen) ) # optimize for strings | ||
| 151 | { | ||
| 152 | $keyGen = sub { $_[0] }; | ||
| 153 | $compare = sub { my ( $a, $b ) = @_; $a eq $b }; | ||
| 154 | } | ||
| 155 | else | ||
| 156 | { | ||
| 157 | $compare = sub { | ||
| 158 | my $a = shift; | ||
| 159 | my $b = shift; | ||
| 160 | &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ ); | ||
| 161 | }; | ||
| 162 | } | ||
| 163 | |||
| 164 | my ( $aStart, $aFinish, $matchVector ) = ( 0, $#$a, [] ); | ||
| 165 | my ( $prunedCount, $bMatches ) = ( 0, {} ); | ||
| 166 | |||
| 167 | if ( ref($b) eq 'HASH' ) # was $bMatches prepared for us? | ||
| 168 | { | ||
| 169 | $bMatches = $b; | ||
| 170 | } | ||
| 171 | else | ||
| 172 | { | ||
| 173 | my ( $bStart, $bFinish ) = ( 0, $#$b ); | ||
| 174 | |||
| 175 | # First we prune off any common elements at the beginning | ||
| 176 | while ( $aStart <= $aFinish | ||
| 177 | and $bStart <= $bFinish | ||
| 178 | and &$compare( $a->[$aStart], $b->[$bStart], @_ ) ) | ||
| 179 | { | ||
| 180 | $matchVector->[ $aStart++ ] = $bStart++; | ||
| 181 | $prunedCount++; | ||
| 182 | } | ||
| 183 | |||
| 184 | # now the end | ||
| 185 | while ( $aStart <= $aFinish | ||
| 186 | and $bStart <= $bFinish | ||
| 187 | and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) ) | ||
| 188 | { | ||
| 189 | $matchVector->[ $aFinish-- ] = $bFinish--; | ||
| 190 | $prunedCount++; | ||
| 191 | } | ||
| 192 | |||
| 193 | # Now compute the equivalence classes of positions of elements | ||
| 194 | $bMatches = | ||
| 195 | _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ ); | ||
| 196 | } | ||
| 197 | my $thresh = []; | ||
| 198 | my $links = []; | ||
| 199 | |||
| 200 | my ( $i, $ai, $j, $k ); | ||
| 201 | for ( $i = $aStart ; $i <= $aFinish ; $i++ ) | ||
| 202 | { | ||
| 203 | $ai = &$keyGen( $a->[$i], @_ ); | ||
| 204 | if ( exists( $bMatches->{$ai} ) ) | ||
| 205 | { | ||
| 206 | $k = 0; | ||
| 207 | for $j ( @{ $bMatches->{$ai} } ) | ||
| 208 | { | ||
| 209 | |||
| 210 | # optimization: most of the time this will be true | ||
| 211 | if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j ) | ||
| 212 | { | ||
| 213 | $thresh->[$k] = $j; | ||
| 214 | } | ||
| 215 | else | ||
| 216 | { | ||
| 217 | $k = _replaceNextLargerWith( $thresh, $j, $k ); | ||
| 218 | } | ||
| 219 | |||
| 220 | # oddly, it's faster to always test this (CPU cache?). | ||
| 221 | if ( defined($k) ) | ||
| 222 | { | ||
| 223 | $links->[$k] = | ||
| 224 | [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ]; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | if (@$thresh) | ||
| 231 | { | ||
| 232 | return $prunedCount + @$thresh if $counting; | ||
| 233 | for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] ) | ||
| 234 | { | ||
| 235 | $matchVector->[ $link->[1] ] = $link->[2]; | ||
| 236 | } | ||
| 237 | } | ||
| 238 | elsif ($counting) | ||
| 239 | { | ||
| 240 | return $prunedCount; | ||
| 241 | } | ||
| 242 | |||
| 243 | return wantarray ? @$matchVector : $matchVector; | ||
| 244 | } | ||
| 245 | |||
| 246 | sub traverse_sequences | ||
| 247 | { | ||
| 248 | my $a = shift; # array ref | ||
| 249 | my $b = shift; # array ref | ||
| 250 | my $callbacks = shift || {}; | ||
| 251 | my $keyGen = shift; | ||
| 252 | my $matchCallback = $callbacks->{'MATCH'} || sub { }; | ||
| 253 | my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; | ||
| 254 | my $finishedACallback = $callbacks->{'A_FINISHED'}; | ||
| 255 | my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; | ||
| 256 | my $finishedBCallback = $callbacks->{'B_FINISHED'}; | ||
| 257 | my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); | ||
| 258 | |||
| 259 | # Process all the lines in @$matchVector | ||
| 260 | my $lastA = $#$a; | ||
| 261 | my $lastB = $#$b; | ||
| 262 | my $bi = 0; | ||
| 263 | my $ai; | ||
| 264 | |||
| 265 | for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ ) | ||
| 266 | { | ||
| 267 | my $bLine = $matchVector->[$ai]; | ||
| 268 | if ( defined($bLine) ) # matched | ||
| 269 | { | ||
| 270 | &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine; | ||
| 271 | &$matchCallback( $ai, $bi++, @_ ); | ||
| 272 | } | ||
| 273 | else | ||
| 274 | { | ||
| 275 | &$discardACallback( $ai, $bi, @_ ); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | # The last entry (if any) processed was a match. | ||
| 280 | # $ai and $bi point just past the last matching lines in their sequences. | ||
| 281 | |||
| 282 | while ( $ai <= $lastA or $bi <= $lastB ) | ||
| 283 | { | ||
| 284 | |||
| 285 | # last A? | ||
| 286 | if ( $ai == $lastA + 1 and $bi <= $lastB ) | ||
| 287 | { | ||
| 288 | if ( defined($finishedACallback) ) | ||
| 289 | { | ||
| 290 | &$finishedACallback( $lastA, @_ ); | ||
| 291 | $finishedACallback = undef; | ||
| 292 | } | ||
| 293 | else | ||
| 294 | { | ||
| 295 | &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | # last B? | ||
| 300 | if ( $bi == $lastB + 1 and $ai <= $lastA ) | ||
| 301 | { | ||
| 302 | if ( defined($finishedBCallback) ) | ||
| 303 | { | ||
| 304 | &$finishedBCallback( $lastB, @_ ); | ||
| 305 | $finishedBCallback = undef; | ||
| 306 | } | ||
| 307 | else | ||
| 308 | { | ||
| 309 | &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA; | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA; | ||
| 314 | &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB; | ||
| 315 | } | ||
| 316 | |||
| 317 | return 1; | ||
| 318 | } | ||
| 319 | |||
| 320 | sub traverse_balanced | ||
| 321 | { | ||
| 322 | my $a = shift; # array ref | ||
| 323 | my $b = shift; # array ref | ||
| 324 | my $callbacks = shift || {}; | ||
| 325 | my $keyGen = shift; | ||
| 326 | my $matchCallback = $callbacks->{'MATCH'} || sub { }; | ||
| 327 | my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; | ||
| 328 | my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; | ||
| 329 | my $changeCallback = $callbacks->{'CHANGE'}; | ||
| 330 | my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); | ||
| 331 | |||
| 332 | # Process all the lines in match vector | ||
| 333 | my $lastA = $#$a; | ||
| 334 | my $lastB = $#$b; | ||
| 335 | my $bi = 0; | ||
| 336 | my $ai = 0; | ||
| 337 | my $ma = -1; | ||
| 338 | my $mb; | ||
| 339 | |||
| 340 | while (1) | ||
| 341 | { | ||
| 342 | |||
| 343 | # Find next match indices $ma and $mb | ||
| 344 | do { | ||
| 345 | $ma++; | ||
| 346 | } while( | ||
| 347 | $ma <= $#$matchVector | ||
| 348 | && !defined $matchVector->[$ma] | ||
| 349 | ); | ||
| 350 | |||
| 351 | last if $ma > $#$matchVector; # end of matchVector? | ||
| 352 | $mb = $matchVector->[$ma]; | ||
| 353 | |||
| 354 | # Proceed with discard a/b or change events until | ||
| 355 | # next match | ||
| 356 | while ( $ai < $ma || $bi < $mb ) | ||
| 357 | { | ||
| 358 | |||
| 359 | if ( $ai < $ma && $bi < $mb ) | ||
| 360 | { | ||
| 361 | |||
| 362 | # Change | ||
| 363 | if ( defined $changeCallback ) | ||
| 364 | { | ||
| 365 | &$changeCallback( $ai++, $bi++, @_ ); | ||
| 366 | } | ||
| 367 | else | ||
| 368 | { | ||
| 369 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 370 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 371 | } | ||
| 372 | } | ||
| 373 | elsif ( $ai < $ma ) | ||
| 374 | { | ||
| 375 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 376 | } | ||
| 377 | else | ||
| 378 | { | ||
| 379 | |||
| 380 | # $bi < $mb | ||
| 381 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | # Match | ||
| 386 | &$matchCallback( $ai++, $bi++, @_ ); | ||
| 387 | } | ||
| 388 | |||
| 389 | while ( $ai <= $lastA || $bi <= $lastB ) | ||
| 390 | { | ||
| 391 | if ( $ai <= $lastA && $bi <= $lastB ) | ||
| 392 | { | ||
| 393 | |||
| 394 | # Change | ||
| 395 | if ( defined $changeCallback ) | ||
| 396 | { | ||
| 397 | &$changeCallback( $ai++, $bi++, @_ ); | ||
| 398 | } | ||
| 399 | else | ||
| 400 | { | ||
| 401 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 402 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 403 | } | ||
| 404 | } | ||
| 405 | elsif ( $ai <= $lastA ) | ||
| 406 | { | ||
| 407 | &$discardACallback( $ai++, $bi, @_ ); | ||
| 408 | } | ||
| 409 | else | ||
| 410 | { | ||
| 411 | |||
| 412 | # $bi <= $lastB | ||
| 413 | &$discardBCallback( $ai, $bi++, @_ ); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | |||
| 417 | return 1; | ||
| 418 | } | ||
| 419 | |||
| 420 | sub prepare | ||
| 421 | { | ||
| 422 | my $a = shift; # array ref | ||
| 423 | my $keyGen = shift; # code ref | ||
| 424 | |||
| 425 | # set up code ref | ||
| 426 | $keyGen = sub { $_[0] } unless defined($keyGen); | ||
| 427 | |||
| 428 | return scalar _withPositionsOfInInterval( $a, 0, $#$a, $keyGen, @_ ); | ||
| 429 | } | ||
| 430 | |||
| 431 | sub LCS | ||
| 432 | { | ||
| 433 | my $a = shift; # array ref | ||
| 434 | my $b = shift; # array ref or hash ref | ||
| 435 | my $matchVector = _longestCommonSubsequence( $a, $b, 0, @_ ); | ||
| 436 | my @retval; | ||
| 437 | my $i; | ||
| 438 | for ( $i = 0 ; $i <= $#$matchVector ; $i++ ) | ||
| 439 | { | ||
| 440 | if ( defined( $matchVector->[$i] ) ) | ||
| 441 | { | ||
| 442 | push ( @retval, $a->[$i] ); | ||
| 443 | } | ||
| 444 | } | ||
| 445 | return wantarray ? @retval : \@retval; | ||
| 446 | } | ||
| 447 | |||
| 448 | sub LCS_length | ||
| 449 | { | ||
| 450 | my $a = shift; # array ref | ||
| 451 | my $b = shift; # array ref or hash ref | ||
| 452 | return _longestCommonSubsequence( $a, $b, 1, @_ ); | ||
| 453 | } | ||
| 454 | |||
| 455 | sub LCSidx | ||
| 456 | { | ||
| 457 | my $a= shift @_; | ||
| 458 | my $b= shift @_; | ||
| 459 | my $match= _longestCommonSubsequence( $a, $b, 0, @_ ); | ||
| 460 | my @am= grep defined $match->[$_], 0..$#$match; | ||
| 461 | my @bm= @{$match}[@am]; | ||
| 462 | return \@am, \@bm; | ||
| 463 | } | ||
| 464 | |||
| 465 | sub compact_diff | ||
| 466 | { | ||
| 467 | my $a= shift @_; | ||
| 468 | my $b= shift @_; | ||
| 469 | my( $am, $bm )= LCSidx( $a, $b, @_ ); | ||
| 470 | my @cdiff; | ||
| 471 | my( $ai, $bi )= ( 0, 0 ); | ||
| 472 | push @cdiff, $ai, $bi; | ||
| 473 | while( 1 ) { | ||
| 474 | while( @$am && $ai == $am->[0] && $bi == $bm->[0] ) { | ||
| 475 | shift @$am; | ||
| 476 | shift @$bm; | ||
| 477 | ++$ai, ++$bi; | ||
| 478 | } | ||
| 479 | push @cdiff, $ai, $bi; | ||
| 480 | last if ! @$am; | ||
| 481 | $ai = $am->[0]; | ||
| 482 | $bi = $bm->[0]; | ||
| 483 | push @cdiff, $ai, $bi; | ||
| 484 | } | ||
| 485 | push @cdiff, 0+@$a, 0+@$b | ||
| 486 | if $ai < @$a || $bi < @$b; | ||
| 487 | return wantarray ? @cdiff : \@cdiff; | ||
| 488 | } | ||
| 489 | |||
| 490 | sub diff | ||
| 491 | { | ||
| 492 | my $a = shift; # array ref | ||
| 493 | my $b = shift; # array ref | ||
| 494 | my $retval = []; | ||
| 495 | my $hunk = []; | ||
| 496 | my $discard = sub { | ||
| 497 | push @$hunk, [ '-', $_[0], $a->[ $_[0] ] ]; | ||
| 498 | }; | ||
| 499 | my $add = sub { | ||
| 500 | push @$hunk, [ '+', $_[1], $b->[ $_[1] ] ]; | ||
| 501 | }; | ||
| 502 | my $match = sub { | ||
| 503 | push @$retval, $hunk | ||
| 504 | if 0 < @$hunk; | ||
| 505 | $hunk = [] | ||
| 506 | }; | ||
| 507 | traverse_sequences( $a, $b, | ||
| 508 | { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ ); | ||
| 509 | &$match(); | ||
| 510 | return wantarray ? @$retval : $retval; | ||
| 511 | } | ||
| 512 | |||
| 513 | sub sdiff | ||
| 514 | { | ||
| 515 | my $a = shift; # array ref | ||
| 516 | my $b = shift; # array ref | ||
| 517 | my $retval = []; | ||
| 518 | my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) }; | ||
| 519 | my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) }; | ||
| 520 | my $change = sub { | ||
| 521 | push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] ); | ||
| 522 | }; | ||
| 523 | my $match = sub { | ||
| 524 | push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] ); | ||
| 525 | }; | ||
| 526 | traverse_balanced( | ||
| 527 | $a, | ||
| 528 | $b, | ||
| 529 | { | ||
| 530 | MATCH => $match, | ||
| 531 | DISCARD_A => $discard, | ||
| 532 | DISCARD_B => $add, | ||
| 533 | CHANGE => $change, | ||
| 534 | }, | ||
| 535 | @_ | ||
| 536 | ); | ||
| 537 | return wantarray ? @$retval : $retval; | ||
| 538 | } | ||
| 539 | |||
| 540 | ######################################## | ||
| 541 | my $Root= __PACKAGE__; | ||
| 542 | package Algorithm::Diff::_impl; | ||
| 543 | use strict; | ||
| 544 | |||
| 545 | sub _Idx() { 0 } # $me->[_Idx]: Ref to array of hunk indices | ||
| 546 | # 1 # $me->[1]: Ref to first sequence | ||
| 547 | # 2 # $me->[2]: Ref to second sequence | ||
| 548 | sub _End() { 3 } # $me->[_End]: Diff between forward and reverse pos | ||
| 549 | sub _Same() { 4 } # $me->[_Same]: 1 if pos 1 contains unchanged items | ||
| 550 | sub _Base() { 5 } # $me->[_Base]: Added to range's min and max | ||
| 551 | sub _Pos() { 6 } # $me->[_Pos]: Which hunk is currently selected | ||
| 552 | sub _Off() { 7 } # $me->[_Off]: Offset into _Idx for current position | ||
| 553 | sub _Min() { -2 } # Added to _Off to get min instead of max+1 | ||
| 554 | |||
| 555 | sub Die | ||
| 556 | { | ||
| 557 | require Carp; | ||
| 558 | Carp::confess( @_ ); | ||
| 559 | } | ||
| 560 | |||
| 561 | sub _ChkPos | ||
| 562 | { | ||
| 563 | my( $me )= @_; | ||
| 564 | return if $me->[_Pos]; | ||
| 565 | my $meth= ( caller(1) )[3]; | ||
| 566 | Die( "Called $meth on 'reset' object" ); | ||
| 567 | } | ||
| 568 | |||
| 569 | sub _ChkSeq | ||
| 570 | { | ||
| 571 | my( $me, $seq )= @_; | ||
| 572 | return $seq + $me->[_Off] | ||
| 573 | if 1 == $seq || 2 == $seq; | ||
| 574 | my $meth= ( caller(1) )[3]; | ||
| 575 | Die( "$meth: Invalid sequence number ($seq); must be 1 or 2" ); | ||
| 576 | } | ||
| 577 | |||
| 578 | sub getObjPkg | ||
| 579 | { | ||
| 580 | my( $us )= @_; | ||
| 581 | return ref $us if ref $us; | ||
| 582 | return $us . "::_obj"; | ||
| 583 | } | ||
| 584 | |||
| 585 | sub new | ||
| 586 | { | ||
| 587 | my( $us, $seq1, $seq2, $opts ) = @_; | ||
| 588 | my @args; | ||
| 589 | for( $opts->{keyGen} ) { | ||
| 590 | push @args, $_ if $_; | ||
| 591 | } | ||
| 592 | for( $opts->{keyGenArgs} ) { | ||
| 593 | push @args, @$_ if $_; | ||
| 594 | } | ||
| 595 | my $cdif= Algorithm::Diff::compact_diff( $seq1, $seq2, @args ); | ||
| 596 | my $same= 1; | ||
| 597 | if( 0 == $cdif->[2] && 0 == $cdif->[3] ) { | ||
| 598 | $same= 0; | ||
| 599 | splice @$cdif, 0, 2; | ||
| 600 | } | ||
| 601 | my @obj= ( $cdif, $seq1, $seq2 ); | ||
| 602 | $obj[_End] = (1+@$cdif)/2; | ||
| 603 | $obj[_Same] = $same; | ||
| 604 | $obj[_Base] = 0; | ||
| 605 | my $me = bless \@obj, $us->getObjPkg(); | ||
| 606 | $me->Reset( 0 ); | ||
| 607 | return $me; | ||
| 608 | } | ||
| 609 | |||
| 610 | sub Reset | ||
| 611 | { | ||
| 612 | my( $me, $pos )= @_; | ||
| 613 | $pos= int( $pos || 0 ); | ||
| 614 | $pos += $me->[_End] | ||
| 615 | if $pos < 0; | ||
| 616 | $pos= 0 | ||
| 617 | if $pos < 0 || $me->[_End] <= $pos; | ||
| 618 | $me->[_Pos]= $pos || !1; | ||
| 619 | $me->[_Off]= 2*$pos - 1; | ||
| 620 | return $me; | ||
| 621 | } | ||
| 622 | |||
| 623 | sub Base | ||
| 624 | { | ||
| 625 | my( $me, $base )= @_; | ||
| 626 | my $oldBase= $me->[_Base]; | ||
| 627 | $me->[_Base]= 0+$base if defined $base; | ||
| 628 | return $oldBase; | ||
| 629 | } | ||
| 630 | |||
| 631 | sub Copy | ||
| 632 | { | ||
| 633 | my( $me, $pos, $base )= @_; | ||
| 634 | my @obj= @$me; | ||
| 635 | my $you= bless \@obj, ref($me); | ||
| 636 | $you->Reset( $pos ) if defined $pos; | ||
| 637 | $you->Base( $base ); | ||
| 638 | return $you; | ||
| 639 | } | ||
| 640 | |||
| 641 | sub Next { | ||
| 642 | my( $me, $steps )= @_; | ||
| 643 | $steps= 1 if ! defined $steps; | ||
| 644 | if( $steps ) { | ||
| 645 | my $pos= $me->[_Pos]; | ||
| 646 | my $new= $pos + $steps; | ||
| 647 | $new= 0 if $pos && $new < 0; | ||
| 648 | $me->Reset( $new ) | ||
| 649 | } | ||
| 650 | return $me->[_Pos]; | ||
| 651 | } | ||
| 652 | |||
| 653 | sub Prev { | ||
| 654 | my( $me, $steps )= @_; | ||
| 655 | $steps= 1 if ! defined $steps; | ||
| 656 | my $pos= $me->Next(-$steps); | ||
| 657 | $pos -= $me->[_End] if $pos; | ||
| 658 | return $pos; | ||
| 659 | } | ||
| 660 | |||
| 661 | sub Diff { | ||
| 662 | my( $me )= @_; | ||
| 663 | $me->_ChkPos(); | ||
| 664 | return 0 if $me->[_Same] == ( 1 & $me->[_Pos] ); | ||
| 665 | my $ret= 0; | ||
| 666 | my $off= $me->[_Off]; | ||
| 667 | for my $seq ( 1, 2 ) { | ||
| 668 | $ret |= $seq | ||
| 669 | if $me->[_Idx][ $off + $seq + _Min ] | ||
| 670 | < $me->[_Idx][ $off + $seq ]; | ||
| 671 | } | ||
| 672 | return $ret; | ||
| 673 | } | ||
| 674 | |||
| 675 | sub Min { | ||
| 676 | my( $me, $seq, $base )= @_; | ||
| 677 | $me->_ChkPos(); | ||
| 678 | my $off= $me->_ChkSeq($seq); | ||
| 679 | $base= $me->[_Base] if !defined $base; | ||
| 680 | return $base + $me->[_Idx][ $off + _Min ]; | ||
| 681 | } | ||
| 682 | |||
| 683 | sub Max { | ||
| 684 | my( $me, $seq, $base )= @_; | ||
| 685 | $me->_ChkPos(); | ||
| 686 | my $off= $me->_ChkSeq($seq); | ||
| 687 | $base= $me->[_Base] if !defined $base; | ||
| 688 | return $base + $me->[_Idx][ $off ] -1; | ||
| 689 | } | ||
| 690 | |||
| 691 | sub Range { | ||
| 692 | my( $me, $seq, $base )= @_; | ||
| 693 | $me->_ChkPos(); | ||
| 694 | my $off = $me->_ChkSeq($seq); | ||
| 695 | if( !wantarray ) { | ||
| 696 | return $me->[_Idx][ $off ] | ||
| 697 | - $me->[_Idx][ $off + _Min ]; | ||
| 698 | } | ||
| 699 | $base= $me->[_Base] if !defined $base; | ||
| 700 | return ( $base + $me->[_Idx][ $off + _Min ] ) | ||
| 701 | .. ( $base + $me->[_Idx][ $off ] - 1 ); | ||
| 702 | } | ||
| 703 | |||
| 704 | sub Items { | ||
| 705 | my( $me, $seq )= @_; | ||
| 706 | $me->_ChkPos(); | ||
| 707 | my $off = $me->_ChkSeq($seq); | ||
| 708 | if( !wantarray ) { | ||
| 709 | return $me->[_Idx][ $off ] | ||
| 710 | - $me->[_Idx][ $off + _Min ]; | ||
| 711 | } | ||
| 712 | return | ||
| 713 | @{$me->[$seq]}[ | ||
| 714 | $me->[_Idx][ $off + _Min ] | ||
| 715 | .. ( $me->[_Idx][ $off ] - 1 ) | ||
| 716 | ]; | ||
| 717 | } | ||
| 718 | |||
| 719 | sub Same { | ||
| 720 | my( $me )= @_; | ||
| 721 | $me->_ChkPos(); | ||
| 722 | return wantarray ? () : 0 | ||
| 723 | if $me->[_Same] != ( 1 & $me->[_Pos] ); | ||
| 724 | return $me->Items(1); | ||
| 725 | } | ||
| 726 | |||
| 727 | my %getName; | ||
| 728 | BEGIN { | ||
| 729 | %getName= ( | ||
| 730 | same => \&Same, | ||
| 731 | diff => \&Diff, | ||
| 732 | base => \&Base, | ||
| 733 | min => \&Min, | ||
| 734 | max => \&Max, | ||
| 735 | range=> \&Range, | ||
| 736 | items=> \&Items, # same thing | ||
| 737 | ); | ||
| 738 | } | ||
| 739 | |||
| 740 | sub Get | ||
| 741 | { | ||
| 742 | my $me= shift @_; | ||
| 743 | $me->_ChkPos(); | ||
| 744 | my @value; | ||
| 745 | for my $arg ( @_ ) { | ||
| 746 | for my $word ( split ' ', $arg ) { | ||
| 747 | my $meth; | ||
| 748 | if( $word !~ /^(-?\d+)?([a-zA-Z]+)([12])?$/ | ||
| 749 | || not $meth= $getName{ lc $2 } | ||
| 750 | ) { | ||
| 751 | Die( $Root, ", Get: Invalid request ($word)" ); | ||
| 752 | } | ||
| 753 | my( $base, $name, $seq )= ( $1, $2, $3 ); | ||
| 754 | push @value, scalar( | ||
| 755 | 4 == length($name) | ||
| 756 | ? $meth->( $me ) | ||
| 757 | : $meth->( $me, $seq, $base ) | ||
| 758 | ); | ||
| 759 | } | ||
| 760 | } | ||
| 761 | if( wantarray ) { | ||
| 762 | return @value; | ||
| 763 | } elsif( 1 == @value ) { | ||
| 764 | return $value[0]; | ||
| 765 | } | ||
| 766 | Die( 0+@value, " values requested from ", | ||
| 767 | $Root, "'s Get in scalar context" ); | ||
| 768 | } | ||
| 769 | |||
| 770 | |||
| 771 | my $Obj= getObjPkg($Root); | ||
| 772 | no strict 'refs'; | ||
| 773 | |||
| 774 | for my $meth ( qw( new getObjPkg ) ) { | ||
| 775 | *{$Root."::".$meth} = \&{$meth}; | ||
| 776 | *{$Obj ."::".$meth} = \&{$meth}; | ||
| 777 | } | ||
| 778 | for my $meth ( qw( | ||
| 779 | Next Prev Reset Copy Base Diff | ||
| 780 | Same Items Range Min Max Get | ||
| 781 | _ChkPos _ChkSeq | ||
| 782 | ) ) { | ||
| 783 | *{$Obj."::".$meth} = \&{$meth}; | ||
| 784 | } | ||
| 785 | |||
| 786 | 1; | ||
| 787 | __END__ | ||
| 788 | |||
| 789 | =head1 NAME | ||
| 790 | |||
| 791 | Algorithm::Diff - Compute `intelligent' differences between two files / lists | ||
| 792 | |||
| 793 | =head1 SYNOPSIS | ||
| 794 | |||
| 795 | require Algorithm::Diff; | ||
| 796 | |||
| 797 | # This example produces traditional 'diff' output: | ||
| 798 | |||
| 799 | my $diff = Algorithm::Diff->new( \@seq1, \@seq2 ); | ||
| 800 | |||
| 801 | $diff->Base( 1 ); # Return line numbers, not indices | ||
| 802 | while( $diff->Next() ) { | ||
| 803 | next if $diff->Same(); | ||
| 804 | my $sep = ''; | ||
| 805 | if( ! $diff->Items(2) ) { | ||
| 806 | sprintf "%d,%dd%d\n", | ||
| 807 | $diff->Get(qw( Min1 Max1 Max2 )); | ||
| 808 | } elsif( ! $diff->Items(1) ) { | ||
| 809 | sprint "%da%d,%d\n", | ||
| 810 | $diff->Get(qw( Max1 Min2 Max2 )); | ||
| 811 | } else { | ||
| 812 | $sep = "---\n"; | ||
| 813 | sprintf "%d,%dc%d,%d\n", | ||
| 814 | $diff->Get(qw( Min1 Max1 Min2 Max2 )); | ||
| 815 | } | ||
| 816 | print "< $_" for $diff->Items(1); | ||
| 817 | print $sep; | ||
| 818 | print "> $_" for $diff->Items(2); | ||
| 819 | } | ||
| 820 | |||
| 821 | |||
| 822 | # Alternate interfaces: | ||
| 823 | |||
| 824 | use Algorithm::Diff qw( | ||
| 825 | LCS LCS_length LCSidx | ||
| 826 | diff sdiff compact_diff | ||
| 827 | traverse_sequences traverse_balanced ); | ||
| 828 | |||
| 829 | @lcs = LCS( \@seq1, \@seq2 ); | ||
| 830 | $lcsref = LCS( \@seq1, \@seq2 ); | ||
| 831 | $count = LCS_length( \@seq1, \@seq2 ); | ||
| 832 | |||
| 833 | ( $seq1idxref, $seq2idxref ) = LCSidx( \@seq1, \@seq2 ); | ||
| 834 | |||
| 835 | |||
| 836 | # Complicated interfaces: | ||
| 837 | |||
| 838 | @diffs = diff( \@seq1, \@seq2 ); | ||
| 839 | |||
| 840 | @sdiffs = sdiff( \@seq1, \@seq2 ); | ||
| 841 | |||
| 842 | @cdiffs = compact_diff( \@seq1, \@seq2 ); | ||
| 843 | |||
| 844 | traverse_sequences( | ||
| 845 | \@seq1, | ||
| 846 | \@seq2, | ||
| 847 | { MATCH => \&callback1, | ||
| 848 | DISCARD_A => \&callback2, | ||
| 849 | DISCARD_B => \&callback3, | ||
| 850 | }, | ||
| 851 | \&key_generator, | ||
| 852 | @extra_args, | ||
| 853 | ); | ||
| 854 | |||
| 855 | traverse_balanced( | ||
| 856 | \@seq1, | ||
| 857 | \@seq2, | ||
| 858 | { MATCH => \&callback1, | ||
| 859 | DISCARD_A => \&callback2, | ||
| 860 | DISCARD_B => \&callback3, | ||
| 861 | CHANGE => \&callback4, | ||
| 862 | }, | ||
| 863 | \&key_generator, | ||
| 864 | @extra_args, | ||
| 865 | ); | ||
| 866 | |||
| 867 | |||
| 868 | =head1 INTRODUCTION | ||
| 869 | |||
| 870 | (by Mark-Jason Dominus) | ||
| 871 | |||
| 872 | I once read an article written by the authors of C<diff>; they said | ||
| 873 | that they worked very hard on the algorithm until they found the | ||
| 874 | right one. | ||
| 875 | |||
| 876 | I think what they ended up using (and I hope someone will correct me, | ||
| 877 | because I am not very confident about this) was the `longest common | ||
| 878 | subsequence' method. In the LCS problem, you have two sequences of | ||
| 879 | items: | ||
| 880 | |||
| 881 | a b c d f g h j q z | ||
| 882 | |||
| 883 | a b c d e f g i j k r x y z | ||
| 884 | |||
| 885 | and you want to find the longest sequence of items that is present in | ||
| 886 | both original sequences in the same order. That is, you want to find | ||
| 887 | a new sequence I<S> which can be obtained from the first sequence by | ||
| 888 | deleting some items, and from the secend sequence by deleting other | ||
| 889 | items. You also want I<S> to be as long as possible. In this case I<S> | ||
| 890 | is | ||
| 891 | |||
| 892 | a b c d f g j z | ||
| 893 | |||
| 894 | From there it's only a small step to get diff-like output: | ||
| 895 | |||
| 896 | e h i k q r x y | ||
| 897 | + - + + - + + + | ||
| 898 | |||
| 899 | This module solves the LCS problem. It also includes a canned function | ||
| 900 | to generate C<diff>-like output. | ||
| 901 | |||
| 902 | It might seem from the example above that the LCS of two sequences is | ||
| 903 | always pretty obvious, but that's not always the case, especially when | ||
| 904 | the two sequences have many repeated elements. For example, consider | ||
| 905 | |||
| 906 | a x b y c z p d q | ||
| 907 | a b c a x b y c z | ||
| 908 | |||
| 909 | A naive approach might start by matching up the C<a> and C<b> that | ||
| 910 | appear at the beginning of each sequence, like this: | ||
| 911 | |||
| 912 | a x b y c z p d q | ||
| 913 | a b c a b y c z | ||
| 914 | |||
| 915 | This finds the common subsequence C<a b c z>. But actually, the LCS | ||
| 916 | is C<a x b y c z>: | ||
| 917 | |||
| 918 | a x b y c z p d q | ||
| 919 | a b c a x b y c z | ||
| 920 | |||
| 921 | or | ||
| 922 | |||
| 923 | a x b y c z p d q | ||
| 924 | a b c a x b y c z | ||
| 925 | |||
| 926 | =head1 USAGE | ||
| 927 | |||
| 928 | (See also the README file and several example | ||
| 929 | scripts include with this module.) | ||
| 930 | |||
| 931 | This module now provides an object-oriented interface that uses less | ||
| 932 | memory and is easier to use than most of the previous procedural | ||
| 933 | interfaces. It also still provides several exportable functions. We'll | ||
| 934 | deal with these in ascending order of difficulty: C<LCS>, | ||
| 935 | C<LCS_length>, C<LCSidx>, OO interface, C<prepare>, C<diff>, C<sdiff>, | ||
| 936 | C<traverse_sequences>, and C<traverse_balanced>. | ||
| 937 | |||
| 938 | =head2 C<LCS> | ||
| 939 | |||
| 940 | Given references to two lists of items, LCS returns an array containing | ||
| 941 | their longest common subsequence. In scalar context, it returns a | ||
| 942 | reference to such a list. | ||
| 943 | |||
| 944 | @lcs = LCS( \@seq1, \@seq2 ); | ||
| 945 | $lcsref = LCS( \@seq1, \@seq2 ); | ||
| 946 | |||
| 947 | C<LCS> may be passed an optional third parameter; this is a CODE | ||
| 948 | reference to a key generation function. See L</KEY GENERATION | ||
| 949 | FUNCTIONS>. | ||
| 950 | |||
| 951 | @lcs = LCS( \@seq1, \@seq2, \&keyGen, @args ); | ||
| 952 | $lcsref = LCS( \@seq1, \@seq2, \&keyGen, @args ); | ||
| 953 | |||
| 954 | Additional parameters, if any, will be passed to the key generation | ||
| 955 | routine. | ||
| 956 | |||
| 957 | =head2 C<LCS_length> | ||
| 958 | |||
| 959 | This is just like C<LCS> except it only returns the length of the | ||
| 960 | longest common subsequence. This provides a performance gain of about | ||
| 961 | 9% compared to C<LCS>. | ||
| 962 | |||
| 963 | =head2 C<LCSidx> | ||
| 964 | |||
| 965 | Like C<LCS> except it returns references to two arrays. The first array | ||
| 966 | contains the indices into @seq1 where the LCS items are located. The | ||
| 967 | second array contains the indices into @seq2 where the LCS items are located. | ||
| 968 | |||
| 969 | Therefore, the following three lists will contain the same values: | ||
| 970 | |||
| 971 | my( $idx1, $idx2 ) = LCSidx( \@seq1, \@seq2 ); | ||
| 972 | my @list1 = @seq1[ @$idx1 ]; | ||
| 973 | my @list2 = @seq2[ @$idx2 ]; | ||
| 974 | my @list3 = LCS( \@seq1, \@seq2 ); | ||
| 975 | |||
| 976 | =head2 C<new> | ||
| 977 | |||
| 978 | $diff = Algorithm::Diffs->new( \@seq1, \@seq2 ); | ||
| 979 | $diff = Algorithm::Diffs->new( \@seq1, \@seq2, \%opts ); | ||
| 980 | |||
| 981 | C<new> computes the smallest set of additions and deletions necessary | ||
| 982 | to turn the first sequence into the second and compactly records them | ||
| 983 | in the object. | ||
| 984 | |||
| 985 | You use the object to iterate over I<hunks>, where each hunk represents | ||
| 986 | a contiguous section of items which should be added, deleted, replaced, | ||
| 987 | or left unchanged. | ||
| 988 | |||
| 989 | =over 4 | ||
| 990 | |||
| 991 | The following summary of all of the methods looks a lot like Perl code | ||
| 992 | but some of the symbols have different meanings: | ||
| 993 | |||
| 994 | [ ] Encloses optional arguments | ||
| 995 | : Is followed by the default value for an optional argument | ||
| 996 | | Separates alternate return results | ||
| 997 | |||
| 998 | Method summary: | ||
| 999 | |||
| 1000 | $obj = Algorithm::Diff->new( \@seq1, \@seq2, [ \%opts ] ); | ||
| 1001 | $pos = $obj->Next( [ $count : 1 ] ); | ||
| 1002 | $revPos = $obj->Prev( [ $count : 1 ] ); | ||
| 1003 | $obj = $obj->Reset( [ $pos : 0 ] ); | ||
| 1004 | $copy = $obj->Copy( [ $pos, [ $newBase ] ] ); | ||
| 1005 | $oldBase = $obj->Base( [ $newBase ] ); | ||
| 1006 | |||
| 1007 | Note that all of the following methods C<die> if used on an object that | ||
| 1008 | is "reset" (not currently pointing at any hunk). | ||
| 1009 | |||
| 1010 | $bits = $obj->Diff( ); | ||
| 1011 | @items|$cnt = $obj->Same( ); | ||
| 1012 | @items|$cnt = $obj->Items( $seqNum ); | ||
| 1013 | @idxs |$cnt = $obj->Range( $seqNum, [ $base ] ); | ||
| 1014 | $minIdx = $obj->Min( $seqNum, [ $base ] ); | ||
| 1015 | $maxIdx = $obj->Max( $seqNum, [ $base ] ); | ||
| 1016 | @values = $obj->Get( @names ); | ||
| 1017 | |||
| 1018 | Passing in C<undef> for an optional argument is always treated the same | ||
| 1019 | as if no argument were passed in. | ||
| 1020 | |||
| 1021 | =item C<Next> | ||
| 1022 | |||
| 1023 | $pos = $diff->Next(); # Move forward 1 hunk | ||
| 1024 | $pos = $diff->Next( 2 ); # Move forward 2 hunks | ||
| 1025 | $pos = $diff->Next(-5); # Move backward 5 hunks | ||
| 1026 | |||
| 1027 | C<Next> moves the object to point at the next hunk. The object starts | ||
| 1028 | out "reset", which means it isn't pointing at any hunk. If the object | ||
| 1029 | is reset, then C<Next()> moves to the first hunk. | ||
| 1030 | |||
| 1031 | C<Next> returns a true value iff the move didn't go past the last hunk. | ||
| 1032 | So C<Next(0)> will return true iff the object is not reset. | ||
| 1033 | |||
| 1034 | Actually, C<Next> returns the object's new position, which is a number | ||
| 1035 | between 1 and the number of hunks (inclusive), or returns a false value. | ||
| 1036 | |||
| 1037 | =item C<Prev> | ||
| 1038 | |||
| 1039 | C<Prev($N)> is almost identical to C<Next(-$N)>; it moves to the $Nth | ||
| 1040 | previous hunk. On a 'reset' object, C<Prev()> [and C<Next(-1)>] move | ||
| 1041 | to the last hunk. | ||
| 1042 | |||
| 1043 | The position returned by C<Prev> is relative to the I<end> of the | ||
| 1044 | hunks; -1 for the last hunk, -2 for the second-to-last, etc. | ||
| 1045 | |||
| 1046 | =item C<Reset> | ||
| 1047 | |||
| 1048 | $diff->Reset(); # Reset the object's position | ||
| 1049 | $diff->Reset($pos); # Move to the specified hunk | ||
| 1050 | $diff->Reset(1); # Move to the first hunk | ||
| 1051 | $diff->Reset(-1); # Move to the last hunk | ||
| 1052 | |||
| 1053 | C<Reset> returns the object, so, for example, you could use | ||
| 1054 | C<< $diff->Reset()->Next(-1) >> to get the number of hunks. | ||
| 1055 | |||
| 1056 | =item C<Copy> | ||
| 1057 | |||
| 1058 | $copy = $diff->Copy( $newPos, $newBase ); | ||
| 1059 | |||
| 1060 | C<Copy> returns a copy of the object. The copy and the orignal object | ||
| 1061 | share most of their data, so making copies takes very little memory. | ||
| 1062 | The copy maintains its own position (separate from the original), which | ||
| 1063 | is the main purpose of copies. It also maintains its own base. | ||
| 1064 | |||
| 1065 | By default, the copy's position starts out the same as the original | ||
| 1066 | object's position. But C<Copy> takes an optional first argument to set the | ||
| 1067 | new position, so the following three snippets are equivalent: | ||
| 1068 | |||
| 1069 | $copy = $diff->Copy($pos); | ||
| 1070 | |||
| 1071 | $copy = $diff->Copy(); | ||
| 1072 | $copy->Reset($pos); | ||
| 1073 | |||
| 1074 | $copy = $diff->Copy()->Reset($pos); | ||
| 1075 | |||
| 1076 | C<Copy> takes an optional second argument to set the base for | ||
| 1077 | the copy. If you wish to change the base of the copy but leave | ||
| 1078 | the position the same as in the original, here are two | ||
| 1079 | equivalent ways: | ||
| 1080 | |||
| 1081 | $copy = $diff->Copy(); | ||
| 1082 | $copy->Base( 0 ); | ||
| 1083 | |||
| 1084 | $copy = $diff->Copy(undef,0); | ||
| 1085 | |||
| 1086 | Here are two equivalent way to get a "reset" copy: | ||
| 1087 | |||
| 1088 | $copy = $diff->Copy(0); | ||
| 1089 | |||
| 1090 | $copy = $diff->Copy()->Reset(); | ||
| 1091 | |||
| 1092 | =item C<Diff> | ||
| 1093 | |||
| 1094 | $bits = $obj->Diff(); | ||
| 1095 | |||
| 1096 | C<Diff> returns a true value iff the current hunk contains items that are | ||
| 1097 | different between the two sequences. It actually returns one of the | ||
| 1098 | follow 4 values: | ||
| 1099 | |||
| 1100 | =over 4 | ||
| 1101 | |||
| 1102 | =item 3 | ||
| 1103 | |||
| 1104 | C<3==(1|2)>. This hunk contains items from @seq1 and the items | ||
| 1105 | from @seq2 that should replace them. Both sequence 1 and 2 | ||
| 1106 | contain changed items so both the 1 and 2 bits are set. | ||
| 1107 | |||
| 1108 | =item 2 | ||
| 1109 | |||
| 1110 | This hunk only contains items from @seq2 that should be inserted (not | ||
| 1111 | items from @seq1). Only sequence 2 contains changed items so only the 2 | ||
| 1112 | bit is set. | ||
| 1113 | |||
| 1114 | =item 1 | ||
| 1115 | |||
| 1116 | This hunk only contains items from @seq1 that should be deleted (not | ||
| 1117 | items from @seq2). Only sequence 1 contains changed items so only the 1 | ||
| 1118 | bit is set. | ||
| 1119 | |||
| 1120 | =item 0 | ||
| 1121 | |||
| 1122 | This means that the items in this hunk are the same in both sequences. | ||
| 1123 | Neither sequence 1 nor 2 contain changed items so neither the 1 nor the | ||
| 1124 | 2 bits are set. | ||
| 1125 | |||
| 1126 | =back | ||
| 1127 | |||
| 1128 | =item C<Same> | ||
| 1129 | |||
| 1130 | C<Same> returns a true value iff the current hunk contains items that | ||
| 1131 | are the same in both sequences. It actually returns the list of items | ||
| 1132 | if they are the same or an emty list if they aren't. In a scalar | ||
| 1133 | context, it returns the size of the list. | ||
| 1134 | |||
| 1135 | =item C<Items> | ||
| 1136 | |||
| 1137 | $count = $diff->Items(2); | ||
| 1138 | @items = $diff->Items($seqNum); | ||
| 1139 | |||
| 1140 | C<Items> returns the (number of) items from the specified sequence that | ||
| 1141 | are part of the current hunk. | ||
| 1142 | |||
| 1143 | If the current hunk contains only insertions, then | ||
| 1144 | C<< $diff->Items(1) >> will return an empty list (0 in a scalar conext). | ||
| 1145 | If the current hunk contains only deletions, then C<< $diff->Items(2) >> | ||
| 1146 | will return an empty list (0 in a scalar conext). | ||
| 1147 | |||
| 1148 | If the hunk contains replacements, then both C<< $diff->Items(1) >> and | ||
| 1149 | C<< $diff->Items(2) >> will return different, non-empty lists. | ||
| 1150 | |||
| 1151 | Otherwise, the hunk contains identical items and all of the following | ||
| 1152 | will return the same lists: | ||
| 1153 | |||
| 1154 | @items = $diff->Items(1); | ||
| 1155 | @items = $diff->Items(2); | ||
| 1156 | @items = $diff->Same(); | ||
| 1157 | |||
| 1158 | =item C<Range> | ||
| 1159 | |||
| 1160 | $count = $diff->Range( $seqNum ); | ||
| 1161 | @indices = $diff->Range( $seqNum ); | ||
| 1162 | @indices = $diff->Range( $seqNum, $base ); | ||
| 1163 | |||
| 1164 | C<Range> is like C<Items> except that it returns a list of I<indices> to | ||
| 1165 | the items rather than the items themselves. By default, the index of | ||
| 1166 | the first item (in each sequence) is 0 but this can be changed by | ||
| 1167 | calling the C<Base> method. So, by default, the following two snippets | ||
| 1168 | return the same lists: | ||
| 1169 | |||
| 1170 | @list = $diff->Items(2); | ||
| 1171 | @list = @seq2[ $diff->Range(2) ]; | ||
| 1172 | |||
| 1173 | You can also specify the base to use as the second argument. So the | ||
| 1174 | following two snippets I<always> return the same lists: | ||
| 1175 | |||
| 1176 | @list = $diff->Items(1); | ||
| 1177 | @list = @seq1[ $diff->Range(1,0) ]; | ||
| 1178 | |||
| 1179 | =item C<Base> | ||
| 1180 | |||
| 1181 | $curBase = $diff->Base(); | ||
| 1182 | $oldBase = $diff->Base($newBase); | ||
| 1183 | |||
| 1184 | C<Base> sets and/or returns the current base (usually 0 or 1) that is | ||
| 1185 | used when you request range information. The base defaults to 0 so | ||
| 1186 | that range information is returned as array indices. You can set the | ||
| 1187 | base to 1 if you want to report traditional line numbers instead. | ||
| 1188 | |||
| 1189 | =item C<Min> | ||
| 1190 | |||
| 1191 | $min1 = $diff->Min(1); | ||
| 1192 | $min = $diff->Min( $seqNum, $base ); | ||
| 1193 | |||
| 1194 | C<Min> returns the first value that C<Range> would return (given the | ||
| 1195 | same arguments) or returns C<undef> if C<Range> would return an empty | ||
| 1196 | list. | ||
| 1197 | |||
| 1198 | =item C<Max> | ||
| 1199 | |||
| 1200 | C<Max> returns the last value that C<Range> would return or C<undef>. | ||
| 1201 | |||
| 1202 | =item C<Get> | ||
| 1203 | |||
| 1204 | ( $n, $x, $r ) = $diff->Get(qw( min1 max1 range1 )); | ||
| 1205 | @values = $diff->Get(qw( 0min2 1max2 range2 same base )); | ||
| 1206 | |||
| 1207 | C<Get> returns one or more scalar values. You pass in a list of the | ||
| 1208 | names of the values you want returned. Each name must match one of the | ||
| 1209 | following regexes: | ||
| 1210 | |||
| 1211 | /^(-?\d+)?(min|max)[12]$/i | ||
| 1212 | /^(range[12]|same|diff|base)$/i | ||
| 1213 | |||
| 1214 | The 1 or 2 after a name says which sequence you want the information | ||
| 1215 | for (and where allowed, it is required). The optional number before | ||
| 1216 | "min" or "max" is the base to use. So the following equalities hold: | ||
| 1217 | |||
| 1218 | $diff->Get('min1') == $diff->Min(1) | ||
| 1219 | $diff->Get('0min2') == $diff->Min(2,0) | ||
| 1220 | |||
| 1221 | Using C<Get> in a scalar context when you've passed in more than one | ||
| 1222 | name is a fatal error (C<die> is called). | ||
| 1223 | |||
| 1224 | =back | ||
| 1225 | |||
| 1226 | =head2 C<prepare> | ||
| 1227 | |||
| 1228 | Given a reference to a list of items, C<prepare> returns a reference | ||
| 1229 | to a hash which can be used when comparing this sequence to other | ||
| 1230 | sequences with C<LCS> or C<LCS_length>. | ||
| 1231 | |||
| 1232 | $prep = prepare( \@seq1 ); | ||
| 1233 | for $i ( 0 .. 10_000 ) | ||
| 1234 | { | ||
| 1235 | @lcs = LCS( $prep, $seq[$i] ); | ||
| 1236 | # do something useful with @lcs | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | C<prepare> may be passed an optional third parameter; this is a CODE | ||
| 1240 | reference to a key generation function. See L</KEY GENERATION | ||
| 1241 | FUNCTIONS>. | ||
| 1242 | |||
| 1243 | $prep = prepare( \@seq1, \&keyGen ); | ||
| 1244 | for $i ( 0 .. 10_000 ) | ||
| 1245 | { | ||
| 1246 | @lcs = LCS( $seq[$i], $prep, \&keyGen ); | ||
| 1247 | # do something useful with @lcs | ||
| 1248 | } | ||
| 1249 | |||
| 1250 | Using C<prepare> provides a performance gain of about 50% when calling LCS | ||
| 1251 | many times compared with not preparing. | ||
| 1252 | |||
| 1253 | =head2 C<diff> | ||
| 1254 | |||
| 1255 | @diffs = diff( \@seq1, \@seq2 ); | ||
| 1256 | $diffs_ref = diff( \@seq1, \@seq2 ); | ||
| 1257 | |||
| 1258 | C<diff> computes the smallest set of additions and deletions necessary | ||
| 1259 | to turn the first sequence into the second, and returns a description | ||
| 1260 | of these changes. The description is a list of I<hunks>; each hunk | ||
| 1261 | represents a contiguous section of items which should be added, | ||
| 1262 | deleted, or replaced. (Hunks containing unchanged items are not | ||
| 1263 | included.) | ||
| 1264 | |||
| 1265 | The return value of C<diff> is a list of hunks, or, in scalar context, a | ||
| 1266 | reference to such a list. If there are no differences, the list will be | ||
| 1267 | empty. | ||
| 1268 | |||
| 1269 | Here is an example. Calling C<diff> for the following two sequences: | ||
| 1270 | |||
| 1271 | a b c e h j l m n p | ||
| 1272 | b c d e f j k l m r s t | ||
| 1273 | |||
| 1274 | would produce the following list: | ||
| 1275 | |||
| 1276 | ( | ||
| 1277 | [ [ '-', 0, 'a' ] ], | ||
| 1278 | |||
| 1279 | [ [ '+', 2, 'd' ] ], | ||
| 1280 | |||
| 1281 | [ [ '-', 4, 'h' ], | ||
| 1282 | [ '+', 4, 'f' ] ], | ||
| 1283 | |||
| 1284 | [ [ '+', 6, 'k' ] ], | ||
| 1285 | |||
| 1286 | [ [ '-', 8, 'n' ], | ||
| 1287 | [ '-', 9, 'p' ], | ||
| 1288 | [ '+', 9, 'r' ], | ||
| 1289 | [ '+', 10, 's' ], | ||
| 1290 | [ '+', 11, 't' ] ], | ||
| 1291 | ) | ||
| 1292 | |||
| 1293 | There are five hunks here. The first hunk says that the C<a> at | ||
| 1294 | position 0 of the first sequence should be deleted (C<->). The second | ||
| 1295 | hunk says that the C<d> at position 2 of the second sequence should | ||
| 1296 | be inserted (C<+>). The third hunk says that the C<h> at position 4 | ||
| 1297 | of the first sequence should be removed and replaced with the C<f> | ||
| 1298 | from position 4 of the second sequence. And so on. | ||
| 1299 | |||
| 1300 | C<diff> may be passed an optional third parameter; this is a CODE | ||
| 1301 | reference to a key generation function. See L</KEY GENERATION | ||
| 1302 | FUNCTIONS>. | ||
| 1303 | |||
| 1304 | Additional parameters, if any, will be passed to the key generation | ||
| 1305 | routine. | ||
| 1306 | |||
| 1307 | =head2 C<sdiff> | ||
| 1308 | |||
| 1309 | @sdiffs = sdiff( \@seq1, \@seq2 ); | ||
| 1310 | $sdiffs_ref = sdiff( \@seq1, \@seq2 ); | ||
| 1311 | |||
| 1312 | C<sdiff> computes all necessary components to show two sequences | ||
| 1313 | and their minimized differences side by side, just like the | ||
| 1314 | Unix-utility I<sdiff> does: | ||
| 1315 | |||
| 1316 | same same | ||
| 1317 | before | after | ||
| 1318 | old < - | ||
| 1319 | - > new | ||
| 1320 | |||
| 1321 | It returns a list of array refs, each pointing to an array of | ||
| 1322 | display instructions. In scalar context it returns a reference | ||
| 1323 | to such a list. If there are no differences, the list will have one | ||
| 1324 | entry per item, each indicating that the item was unchanged. | ||
| 1325 | |||
| 1326 | Display instructions consist of three elements: A modifier indicator | ||
| 1327 | (C<+>: Element added, C<->: Element removed, C<u>: Element unmodified, | ||
| 1328 | C<c>: Element changed) and the value of the old and new elements, to | ||
| 1329 | be displayed side-by-side. | ||
| 1330 | |||
| 1331 | An C<sdiff> of the following two sequences: | ||
| 1332 | |||
| 1333 | a b c e h j l m n p | ||
| 1334 | b c d e f j k l m r s t | ||
| 1335 | |||
| 1336 | results in | ||
| 1337 | |||
| 1338 | ( [ '-', 'a', '' ], | ||
| 1339 | [ 'u', 'b', 'b' ], | ||
| 1340 | [ 'u', 'c', 'c' ], | ||
| 1341 | [ '+', '', 'd' ], | ||
| 1342 | [ 'u', 'e', 'e' ], | ||
| 1343 | [ 'c', 'h', 'f' ], | ||
| 1344 | [ 'u', 'j', 'j' ], | ||
| 1345 | [ '+', '', 'k' ], | ||
| 1346 | [ 'u', 'l', 'l' ], | ||
| 1347 | [ 'u', 'm', 'm' ], | ||
| 1348 | [ 'c', 'n', 'r' ], | ||
| 1349 | [ 'c', 'p', 's' ], | ||
| 1350 | [ '+', '', 't' ], | ||
| 1351 | ) | ||
| 1352 | |||
| 1353 | C<sdiff> may be passed an optional third parameter; this is a CODE | ||
| 1354 | reference to a key generation function. See L</KEY GENERATION | ||
| 1355 | FUNCTIONS>. | ||
| 1356 | |||
| 1357 | Additional parameters, if any, will be passed to the key generation | ||
| 1358 | routine. | ||
| 1359 | |||
| 1360 | =head2 C<compact_diff> | ||
| 1361 | |||
| 1362 | C<compact_diff> is much like C<sdiff> except it returns a much more | ||
| 1363 | compact description consisting of just one flat list of indices. An | ||
| 1364 | example helps explain the format: | ||
| 1365 | |||
| 1366 | my @a = qw( a b c e h j l m n p ); | ||
| 1367 | my @b = qw( b c d e f j k l m r s t ); | ||
| 1368 | @cdiff = compact_diff( \@a, \@b ); | ||
| 1369 | # Returns: | ||
| 1370 | # @a @b @a @b | ||
| 1371 | # start start values values | ||
| 1372 | ( 0, 0, # = | ||
| 1373 | 0, 0, # a ! | ||
| 1374 | 1, 0, # b c = b c | ||
| 1375 | 3, 2, # ! d | ||
| 1376 | 3, 3, # e = e | ||
| 1377 | 4, 4, # f ! h | ||
| 1378 | 5, 5, # j = j | ||
| 1379 | 6, 6, # ! k | ||
| 1380 | 6, 7, # l m = l m | ||
| 1381 | 8, 9, # n p ! r s t | ||
| 1382 | 10, 12, # | ||
| 1383 | ); | ||
| 1384 | |||
| 1385 | The 0th, 2nd, 4th, etc. entries are all indices into @seq1 (@a in the | ||
| 1386 | above example) indicating where a hunk begins. The 1st, 3rd, 5th, etc. | ||
| 1387 | entries are all indices into @seq2 (@b in the above example) indicating | ||
| 1388 | where the same hunk begins. | ||
| 1389 | |||
| 1390 | So each pair of indices (except the last pair) describes where a hunk | ||
| 1391 | begins (in each sequence). Since each hunk must end at the item just | ||
| 1392 | before the item that starts the next hunk, the next pair of indices can | ||
| 1393 | be used to determine where the hunk ends. | ||
| 1394 | |||
| 1395 | So, the first 4 entries (0..3) describe the first hunk. Entries 0 and 1 | ||
| 1396 | describe where the first hunk begins (and so are always both 0). | ||
| 1397 | Entries 2 and 3 describe where the next hunk begins, so subtracting 1 | ||
| 1398 | from each tells us where the first hunk ends. That is, the first hunk | ||
| 1399 | contains items C<$diff[0]> through C<$diff[2] - 1> of the first sequence | ||
| 1400 | and contains items C<$diff[1]> through C<$diff[3] - 1> of the second | ||
| 1401 | sequence. | ||
| 1402 | |||
| 1403 | In other words, the first hunk consists of the following two lists of items: | ||
| 1404 | |||
| 1405 | # 1st pair 2nd pair | ||
| 1406 | # of indices of indices | ||
| 1407 | @list1 = @a[ $cdiff[0] .. $cdiff[2]-1 ]; | ||
| 1408 | @list2 = @b[ $cdiff[1] .. $cdiff[3]-1 ]; | ||
| 1409 | # Hunk start Hunk end | ||
| 1410 | |||
| 1411 | Note that the hunks will always alternate between those that are part of | ||
| 1412 | the LCS (those that contain unchanged items) and those that contain | ||
| 1413 | changes. This means that all we need to be told is whether the first | ||
| 1414 | hunk is a 'same' or 'diff' hunk and we can determine which of the other | ||
| 1415 | hunks contain 'same' items or 'diff' items. | ||
| 1416 | |||
| 1417 | By convention, we always make the first hunk contain unchanged items. | ||
| 1418 | So the 1st, 3rd, 5th, etc. hunks (all odd-numbered hunks if you start | ||
| 1419 | counting from 1) all contain unchanged items. And the 2nd, 4th, 6th, | ||
| 1420 | etc. hunks (all even-numbered hunks if you start counting from 1) all | ||
| 1421 | contain changed items. | ||
| 1422 | |||
| 1423 | Since @a and @b don't begin with the same value, the first hunk in our | ||
| 1424 | example is empty (otherwise we'd violate the above convention). Note | ||
| 1425 | that the first 4 index values in our example are all zero. Plug these | ||
| 1426 | values into our previous code block and we get: | ||
| 1427 | |||
| 1428 | @hunk1a = @a[ 0 .. 0-1 ]; | ||
| 1429 | @hunk1b = @b[ 0 .. 0-1 ]; | ||
| 1430 | |||
| 1431 | And C<0..-1> returns the empty list. | ||
| 1432 | |||
| 1433 | Move down one pair of indices (2..5) and we get the offset ranges for | ||
| 1434 | the second hunk, which contains changed items. | ||
| 1435 | |||
| 1436 | Since C<@diff[2..5]> contains (0,0,1,0) in our example, the second hunk | ||
| 1437 | consists of these two lists of items: | ||
| 1438 | |||
| 1439 | @hunk2a = @a[ $cdiff[2] .. $cdiff[4]-1 ]; | ||
| 1440 | @hunk2b = @b[ $cdiff[3] .. $cdiff[5]-1 ]; | ||
| 1441 | # or | ||
| 1442 | @hunk2a = @a[ 0 .. 1-1 ]; | ||
| 1443 | @hunk2b = @b[ 0 .. 0-1 ]; | ||
| 1444 | # or | ||
| 1445 | @hunk2a = @a[ 0 .. 0 ]; | ||
| 1446 | @hunk2b = @b[ 0 .. -1 ]; | ||
| 1447 | # or | ||
| 1448 | @hunk2a = ( 'a' ); | ||
| 1449 | @hunk2b = ( ); | ||
| 1450 | |||
| 1451 | That is, we would delete item 0 ('a') from @a. | ||
| 1452 | |||
| 1453 | Since C<@diff[4..7]> contains (1,0,3,2) in our example, the third hunk | ||
| 1454 | consists of these two lists of items: | ||
| 1455 | |||
| 1456 | @hunk3a = @a[ $cdiff[4] .. $cdiff[6]-1 ]; | ||
| 1457 | @hunk3a = @b[ $cdiff[5] .. $cdiff[7]-1 ]; | ||
| 1458 | # or | ||
| 1459 | @hunk3a = @a[ 1 .. 3-1 ]; | ||
| 1460 | @hunk3a = @b[ 0 .. 2-1 ]; | ||
| 1461 | # or | ||
| 1462 | @hunk3a = @a[ 1 .. 2 ]; | ||
| 1463 | @hunk3a = @b[ 0 .. 1 ]; | ||
| 1464 | # or | ||
| 1465 | @hunk3a = qw( b c ); | ||
| 1466 | @hunk3a = qw( b c ); | ||
| 1467 | |||
| 1468 | Note that this third hunk contains unchanged items as our convention demands. | ||
| 1469 | |||
| 1470 | You can continue this process until you reach the last two indices, | ||
| 1471 | which will always be the number of items in each sequence. This is | ||
| 1472 | required so that subtracting one from each will give you the indices to | ||
| 1473 | the last items in each sequence. | ||
| 1474 | |||
| 1475 | =head2 C<traverse_sequences> | ||
| 1476 | |||
| 1477 | C<traverse_sequences> used to be the most general facility provided by | ||
| 1478 | this module (the new OO interface is more powerful and much easier to | ||
| 1479 | use). | ||
| 1480 | |||
| 1481 | Imagine that there are two arrows. Arrow A points to an element of | ||
| 1482 | sequence A, and arrow B points to an element of the sequence B. | ||
| 1483 | Initially, the arrows point to the first elements of the respective | ||
| 1484 | sequences. C<traverse_sequences> will advance the arrows through the | ||
| 1485 | sequences one element at a time, calling an appropriate user-specified | ||
| 1486 | callback function before each advance. It willadvance the arrows in | ||
| 1487 | such a way that if there are equal elements C<$A[$i]> and C<$B[$j]> | ||
| 1488 | which are equal and which are part of the LCS, there will be some moment | ||
| 1489 | during the execution of C<traverse_sequences> when arrow A is pointing | ||
| 1490 | to C<$A[$i]> and arrow B is pointing to C<$B[$j]>. When this happens, | ||
| 1491 | C<traverse_sequences> will call the C<MATCH> callback function and then | ||
| 1492 | it will advance both arrows. | ||
| 1493 | |||
| 1494 | Otherwise, one of the arrows is pointing to an element of its sequence | ||
| 1495 | that is not part of the LCS. C<traverse_sequences> will advance that | ||
| 1496 | arrow and will call the C<DISCARD_A> or the C<DISCARD_B> callback, | ||
| 1497 | depending on which arrow it advanced. If both arrows point to elements | ||
| 1498 | that are not part of the LCS, then C<traverse_sequences> will advance | ||
| 1499 | one of them and call the appropriate callback, but it is not specified | ||
| 1500 | which it will call. | ||
| 1501 | |||
| 1502 | The arguments to C<traverse_sequences> are the two sequences to | ||
| 1503 | traverse, and a hash which specifies the callback functions, like this: | ||
| 1504 | |||
| 1505 | traverse_sequences( | ||
| 1506 | \@seq1, \@seq2, | ||
| 1507 | { MATCH => $callback_1, | ||
| 1508 | DISCARD_A => $callback_2, | ||
| 1509 | DISCARD_B => $callback_3, | ||
| 1510 | } | ||
| 1511 | ); | ||
| 1512 | |||
| 1513 | Callbacks for MATCH, DISCARD_A, and DISCARD_B are invoked with at least | ||
| 1514 | the indices of the two arrows as their arguments. They are not expected | ||
| 1515 | to return any values. If a callback is omitted from the table, it is | ||
| 1516 | not called. | ||
| 1517 | |||
| 1518 | Callbacks for A_FINISHED and B_FINISHED are invoked with at least the | ||
| 1519 | corresponding index in A or B. | ||
| 1520 | |||
| 1521 | If arrow A reaches the end of its sequence, before arrow B does, | ||
| 1522 | C<traverse_sequences> will call the C<A_FINISHED> callback when it | ||
| 1523 | advances arrow B, if there is such a function; if not it will call | ||
| 1524 | C<DISCARD_B> instead. Similarly if arrow B finishes first. | ||
| 1525 | C<traverse_sequences> returns when both arrows are at the ends of their | ||
| 1526 | respective sequences. It returns true on success and false on failure. | ||
| 1527 | At present there is no way to fail. | ||
| 1528 | |||
| 1529 | C<traverse_sequences> may be passed an optional fourth parameter; this | ||
| 1530 | is a CODE reference to a key generation function. See L</KEY GENERATION | ||
| 1531 | FUNCTIONS>. | ||
| 1532 | |||
| 1533 | Additional parameters, if any, will be passed to the key generation function. | ||
| 1534 | |||
| 1535 | If you want to pass additional parameters to your callbacks, but don't | ||
| 1536 | need a custom key generation function, you can get the default by | ||
| 1537 | passing undef: | ||
| 1538 | |||
| 1539 | traverse_sequences( | ||
| 1540 | \@seq1, \@seq2, | ||
| 1541 | { MATCH => $callback_1, | ||
| 1542 | DISCARD_A => $callback_2, | ||
| 1543 | DISCARD_B => $callback_3, | ||
| 1544 | }, | ||
| 1545 | undef, # default key-gen | ||
| 1546 | $myArgument1, | ||
| 1547 | $myArgument2, | ||
| 1548 | $myArgument3, | ||
| 1549 | ); | ||
| 1550 | |||
| 1551 | C<traverse_sequences> does not have a useful return value; you are | ||
| 1552 | expected to plug in the appropriate behavior with the callback | ||
| 1553 | functions. | ||
| 1554 | |||
| 1555 | =head2 C<traverse_balanced> | ||
| 1556 | |||
| 1557 | C<traverse_balanced> is an alternative to C<traverse_sequences>. It | ||
| 1558 | uses a different algorithm to iterate through the entries in the | ||
| 1559 | computed LCS. Instead of sticking to one side and showing element changes | ||
| 1560 | as insertions and deletions only, it will jump back and forth between | ||
| 1561 | the two sequences and report I<changes> occurring as deletions on one | ||
| 1562 | side followed immediatly by an insertion on the other side. | ||
| 1563 | |||
| 1564 | In addition to the C<DISCARD_A>, C<DISCARD_B>, and C<MATCH> callbacks | ||
| 1565 | supported by C<traverse_sequences>, C<traverse_balanced> supports | ||
| 1566 | a C<CHANGE> callback indicating that one element got C<replaced> by another: | ||
| 1567 | |||
| 1568 | traverse_balanced( | ||
| 1569 | \@seq1, \@seq2, | ||
| 1570 | { MATCH => $callback_1, | ||
| 1571 | DISCARD_A => $callback_2, | ||
| 1572 | DISCARD_B => $callback_3, | ||
| 1573 | CHANGE => $callback_4, | ||
| 1574 | } | ||
| 1575 | ); | ||
| 1576 | |||
| 1577 | If no C<CHANGE> callback is specified, C<traverse_balanced> | ||
| 1578 | will map C<CHANGE> events to C<DISCARD_A> and C<DISCARD_B> actions, | ||
| 1579 | therefore resulting in a similar behaviour as C<traverse_sequences> | ||
| 1580 | with different order of events. | ||
| 1581 | |||
| 1582 | C<traverse_balanced> might be a bit slower than C<traverse_sequences>, | ||
| 1583 | noticable only while processing huge amounts of data. | ||
| 1584 | |||
| 1585 | The C<sdiff> function of this module | ||
| 1586 | is implemented as call to C<traverse_balanced>. | ||
| 1587 | |||
| 1588 | C<traverse_balanced> does not have a useful return value; you are expected to | ||
| 1589 | plug in the appropriate behavior with the callback functions. | ||
| 1590 | |||
| 1591 | =head1 KEY GENERATION FUNCTIONS | ||
| 1592 | |||
| 1593 | Most of the functions accept an optional extra parameter. This is a | ||
| 1594 | CODE reference to a key generating (hashing) function that should return | ||
| 1595 | a string that uniquely identifies a given element. It should be the | ||
| 1596 | case that if two elements are to be considered equal, their keys should | ||
| 1597 | be the same (and the other way around). If no key generation function | ||
| 1598 | is provided, the key will be the element as a string. | ||
| 1599 | |||
| 1600 | By default, comparisons will use "eq" and elements will be turned into keys | ||
| 1601 | using the default stringizing operator '""'. | ||
| 1602 | |||
| 1603 | Where this is important is when you're comparing something other than | ||
| 1604 | strings. If it is the case that you have multiple different objects | ||
| 1605 | that should be considered to be equal, you should supply a key | ||
| 1606 | generation function. Otherwise, you have to make sure that your arrays | ||
| 1607 | contain unique references. | ||
| 1608 | |||
| 1609 | For instance, consider this example: | ||
| 1610 | |||
| 1611 | package Person; | ||
| 1612 | |||
| 1613 | sub new | ||
| 1614 | { | ||
| 1615 | my $package = shift; | ||
| 1616 | return bless { name => '', ssn => '', @_ }, $package; | ||
| 1617 | } | ||
| 1618 | |||
| 1619 | sub clone | ||
| 1620 | { | ||
| 1621 | my $old = shift; | ||
| 1622 | my $new = bless { %$old }, ref($old); | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | sub hash | ||
| 1626 | { | ||
| 1627 | return shift()->{'ssn'}; | ||
| 1628 | } | ||
| 1629 | |||
| 1630 | my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' ); | ||
| 1631 | my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' ); | ||
| 1632 | my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' ); | ||
| 1633 | my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' ); | ||
| 1634 | my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' ); | ||
| 1635 | |||
| 1636 | If you did this: | ||
| 1637 | |||
| 1638 | my $array1 = [ $person1, $person2, $person4 ]; | ||
| 1639 | my $array2 = [ $person1, $person3, $person4, $person5 ]; | ||
| 1640 | Algorithm::Diff::diff( $array1, $array2 ); | ||
| 1641 | |||
| 1642 | everything would work out OK (each of the objects would be converted | ||
| 1643 | into a string like "Person=HASH(0x82425b0)" for comparison). | ||
| 1644 | |||
| 1645 | But if you did this: | ||
| 1646 | |||
| 1647 | my $array1 = [ $person1, $person2, $person4 ]; | ||
| 1648 | my $array2 = [ $person1, $person3, $person4->clone(), $person5 ]; | ||
| 1649 | Algorithm::Diff::diff( $array1, $array2 ); | ||
| 1650 | |||
| 1651 | $person4 and $person4->clone() (which have the same name and SSN) | ||
| 1652 | would be seen as different objects. If you wanted them to be considered | ||
| 1653 | equivalent, you would have to pass in a key generation function: | ||
| 1654 | |||
| 1655 | my $array1 = [ $person1, $person2, $person4 ]; | ||
| 1656 | my $array2 = [ $person1, $person3, $person4->clone(), $person5 ]; | ||
| 1657 | Algorithm::Diff::diff( $array1, $array2, \&Person::hash ); | ||
| 1658 | |||
| 1659 | This would use the 'ssn' field in each Person as a comparison key, and | ||
| 1660 | so would consider $person4 and $person4->clone() as equal. | ||
| 1661 | |||
| 1662 | You may also pass additional parameters to the key generation function | ||
| 1663 | if you wish. | ||
| 1664 | |||
| 1665 | =head1 ERROR CHECKING | ||
| 1666 | |||
| 1667 | If you pass these routines a non-reference and they expect a reference, | ||
| 1668 | they will die with a message. | ||
| 1669 | |||
| 1670 | =head1 AUTHOR | ||
| 1671 | |||
| 1672 | This version released by Tye McQueen (http://perlmonks.org/?node=tye). | ||
| 1673 | |||
| 1674 | =head1 LICENSE | ||
| 1675 | |||
| 1676 | Parts Copyright (c) 2000-2004 Ned Konz. All rights reserved. | ||
| 1677 | Parts by Tye McQueen. | ||
| 1678 | |||
| 1679 | This program is free software; you can redistribute it and/or modify it | ||
| 1680 | under the same terms as Perl. | ||
| 1681 | |||
| 1682 | =head1 MAILING LIST | ||
| 1683 | |||
| 1684 | Mark-Jason still maintains a mailing list. To join a low-volume mailing | ||
| 1685 | list for announcements related to diff and Algorithm::Diff, send an | ||
| 1686 | empty mail message to mjd-perl-diff-request@plover.com. | ||
| 1687 | |||
| 1688 | =head1 CREDITS | ||
| 1689 | |||
| 1690 | Versions through 0.59 (and much of this documentation) were written by: | ||
| 1691 | |||
| 1692 | Mark-Jason Dominus, mjd-perl-diff@plover.com | ||
| 1693 | |||
| 1694 | This version borrows some documentation and routine names from | ||
| 1695 | Mark-Jason's, but Diff.pm's code was completely replaced. | ||
| 1696 | |||
| 1697 | This code was adapted from the Smalltalk code of Mario Wolczko | ||
| 1698 | <mario@wolczko.com>, which is available at | ||
| 1699 | ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st | ||
| 1700 | |||
| 1701 | C<sdiff> and C<traverse_balanced> were written by Mike Schilli | ||
| 1702 | <m@perlmeister.com>. | ||
| 1703 | |||
| 1704 | The algorithm is that described in | ||
| 1705 | I<A Fast Algorithm for Computing Longest Common Subsequences>, | ||
| 1706 | CACM, vol.20, no.5, pp.350-353, May 1977, with a few | ||
| 1707 | minor improvements to improve the speed. | ||
| 1708 | |||
| 1709 | Much work was done by Ned Konz (perl@bike-nomad.com). | ||
| 1710 | |||
| 1711 | The OO interface and some other changes are by Tye McQueen. | ||
| 1712 | |||
| 1713 | =cut | ||
diff --git a/pintos-progos/tests/Make.tests b/pintos-progos/tests/Make.tests new file mode 100644 index 0000000..358e697 --- /dev/null +++ b/pintos-progos/tests/Make.tests | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS)) | ||
| 4 | |||
| 5 | PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS)) | ||
| 6 | TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS)) | ||
| 7 | EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES)) | ||
| 8 | |||
| 9 | OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES)) | ||
| 10 | ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES)) | ||
| 11 | RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES)) | ||
| 12 | |||
| 13 | ifdef PROGS | ||
| 14 | include ../../Makefile.userprog | ||
| 15 | endif | ||
| 16 | |||
| 17 | TIMEOUT = 60 | ||
| 18 | |||
| 19 | clean:: | ||
| 20 | rm -f $(OUTPUTS) $(ERRORS) $(RESULTS) | ||
| 21 | |||
| 22 | grade:: results | ||
| 23 | $(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@ | ||
| 24 | |||
| 25 | check:: results | ||
| 26 | @cat $< | ||
| 27 | @COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \ | ||
| 28 | FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \ | ||
| 29 | if [ $$FAILURES = 0 ]; then \ | ||
| 30 | echo "All $$COUNT tests passed."; \ | ||
| 31 | else \ | ||
| 32 | echo "$$FAILURES of $$COUNT tests failed."; \ | ||
| 33 | exit 1; \ | ||
| 34 | fi | ||
| 35 | |||
| 36 | results: $(RESULTS) | ||
| 37 | @for d in $(TESTS) $(EXTRA_GRADES); do \ | ||
| 38 | if echo PASS | cmp -s $$d.result -; then \ | ||
| 39 | echo "pass $$d"; \ | ||
| 40 | else \ | ||
| 41 | echo "FAIL $$d"; \ | ||
| 42 | fi; \ | ||
| 43 | done > $@ | ||
| 44 | |||
| 45 | outputs:: $(OUTPUTS) | ||
| 46 | |||
| 47 | $(foreach prog,$(PROGS),$(eval $(prog).output: $(prog))) | ||
| 48 | $(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES))) | ||
| 49 | $(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test))) | ||
| 50 | |||
| 51 | # Prevent an environment variable VERBOSE from surprising us. | ||
| 52 | VERBOSE = | ||
| 53 | |||
| 54 | TESTCMD = pintos -v -k -T $(TIMEOUT) | ||
| 55 | TESTCMD += $(SIMULATOR) | ||
| 56 | TESTCMD += $(PINTOSOPTS) | ||
| 57 | ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog) | ||
| 58 | TESTCMD += $(FILESYSSOURCE) | ||
| 59 | TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file))) | ||
| 60 | endif | ||
| 61 | ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm) | ||
| 62 | TESTCMD += --swap-size=4 | ||
| 63 | endif | ||
| 64 | TESTCMD += -- -q | ||
| 65 | TESTCMD += $(KERNELFLAGS) | ||
| 66 | ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog) | ||
| 67 | TESTCMD += -f | ||
| 68 | endif | ||
| 69 | TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F)) | ||
| 70 | TESTCMD += < /dev/null | ||
| 71 | TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output | ||
| 72 | %.output: kernel.bin loader.bin | ||
| 73 | $(TESTCMD) | ||
| 74 | |||
| 75 | %.result: %.ck %.output | ||
| 76 | perl -I$(SRCDIR) $< $* $@ | ||
diff --git a/pintos-progos/tests/arc4.c b/pintos-progos/tests/arc4.c new file mode 100644 index 0000000..b033cc6 --- /dev/null +++ b/pintos-progos/tests/arc4.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | #include <stdint.h> | ||
| 2 | #include "tests/arc4.h" | ||
| 3 | |||
| 4 | /* Swap bytes. */ | ||
| 5 | static inline void | ||
| 6 | swap_byte (uint8_t *a, uint8_t *b) | ||
| 7 | { | ||
| 8 | uint8_t t = *a; | ||
| 9 | *a = *b; | ||
| 10 | *b = t; | ||
| 11 | } | ||
| 12 | |||
| 13 | void | ||
| 14 | arc4_init (struct arc4 *arc4, const void *key_, size_t size) | ||
| 15 | { | ||
| 16 | const uint8_t *key = key_; | ||
| 17 | size_t key_idx; | ||
| 18 | uint8_t *s; | ||
| 19 | int i, j; | ||
| 20 | |||
| 21 | s = arc4->s; | ||
| 22 | arc4->i = arc4->j = 0; | ||
| 23 | for (i = 0; i < 256; i++) | ||
| 24 | s[i] = i; | ||
| 25 | for (key_idx = 0, i = j = 0; i < 256; i++) | ||
| 26 | { | ||
| 27 | j = (j + s[i] + key[key_idx]) & 255; | ||
| 28 | swap_byte (s + i, s + j); | ||
| 29 | if (++key_idx >= size) | ||
| 30 | key_idx = 0; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | void | ||
| 35 | arc4_crypt (struct arc4 *arc4, void *buf_, size_t size) | ||
| 36 | { | ||
| 37 | uint8_t *buf = buf_; | ||
| 38 | uint8_t *s; | ||
| 39 | uint8_t i, j; | ||
| 40 | |||
| 41 | s = arc4->s; | ||
| 42 | i = arc4->i; | ||
| 43 | j = arc4->j; | ||
| 44 | while (size-- > 0) | ||
| 45 | { | ||
| 46 | i += 1; | ||
| 47 | j += s[i]; | ||
| 48 | swap_byte (s + i, s + j); | ||
| 49 | *buf++ ^= s[(s[i] + s[j]) & 255]; | ||
| 50 | } | ||
| 51 | arc4->i = i; | ||
| 52 | arc4->j = j; | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/arc4.h b/pintos-progos/tests/arc4.h new file mode 100644 index 0000000..61c533a --- /dev/null +++ b/pintos-progos/tests/arc4.h | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | #ifndef TESTS_ARC4_H | ||
| 2 | #define TESTS_ARC4_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | /* Alleged RC4 algorithm encryption state. */ | ||
| 8 | struct arc4 | ||
| 9 | { | ||
| 10 | uint8_t s[256]; | ||
| 11 | uint8_t i, j; | ||
| 12 | }; | ||
| 13 | |||
| 14 | void arc4_init (struct arc4 *, const void *, size_t); | ||
| 15 | void arc4_crypt (struct arc4 *, void *, size_t); | ||
| 16 | |||
| 17 | #endif /* tests/arc4.h */ | ||
diff --git a/pintos-progos/tests/arc4.pm b/pintos-progos/tests/arc4.pm new file mode 100644 index 0000000..df19216 --- /dev/null +++ b/pintos-progos/tests/arc4.pm | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | |||
| 4 | sub arc4_init { | ||
| 5 | my ($key) = @_; | ||
| 6 | my (@s) = 0...255; | ||
| 7 | my ($j) = 0; | ||
| 8 | for my $i (0...255) { | ||
| 9 | $j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff; | ||
| 10 | @s[$i, $j] = @s[$j, $i]; | ||
| 11 | } | ||
| 12 | return (0, 0, @s); | ||
| 13 | } | ||
| 14 | |||
| 15 | sub arc4_crypt { | ||
| 16 | my ($arc4, $buf) = @_; | ||
| 17 | my ($i, $j, @s) = @$arc4; | ||
| 18 | my ($out) = ""; | ||
| 19 | for my $c (split (//, $buf)) { | ||
| 20 | $i = ($i + 1) & 0xff; | ||
| 21 | $j = ($j + $s[$i]) & 0xff; | ||
| 22 | @s[$i, $j] = @s[$j, $i]; | ||
| 23 | $out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]); | ||
| 24 | } | ||
| 25 | @$arc4 = ($i, $j, @s); | ||
| 26 | return $out; | ||
| 27 | } | ||
| 28 | |||
| 29 | 1; | ||
diff --git a/pintos-progos/tests/cksum.c b/pintos-progos/tests/cksum.c new file mode 100644 index 0000000..92a2995 --- /dev/null +++ b/pintos-progos/tests/cksum.c | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | /* crctab[] and cksum() are from the `cksum' entry in SUSv3. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include "tests/cksum.h" | ||
| 5 | |||
| 6 | static unsigned long crctab[] = { | ||
| 7 | 0x00000000, | ||
| 8 | 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, | ||
| 9 | 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, | ||
| 10 | 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, | ||
| 11 | 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, | ||
| 12 | 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, | ||
| 13 | 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, | ||
| 14 | 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, | ||
| 15 | 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, | ||
| 16 | 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, | ||
| 17 | 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, | ||
| 18 | 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, | ||
| 19 | 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, | ||
| 20 | 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, | ||
| 21 | 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, | ||
| 22 | 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, | ||
| 23 | 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, | ||
| 24 | 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, | ||
| 25 | 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, | ||
| 26 | 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, | ||
| 27 | 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, | ||
| 28 | 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, | ||
| 29 | 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, | ||
| 30 | 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, | ||
| 31 | 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, | ||
| 32 | 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, | ||
| 33 | 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, | ||
| 34 | 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, | ||
| 35 | 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, | ||
| 36 | 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, | ||
| 37 | 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, | ||
| 38 | 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, | ||
| 39 | 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, | ||
| 40 | 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, | ||
| 41 | 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, | ||
| 42 | 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, | ||
| 43 | 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, | ||
| 44 | 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, | ||
| 45 | 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, | ||
| 46 | 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, | ||
| 47 | 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, | ||
| 48 | 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, | ||
| 49 | 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, | ||
| 50 | 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, | ||
| 51 | 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, | ||
| 52 | 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, | ||
| 53 | 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, | ||
| 54 | 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, | ||
| 55 | 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, | ||
| 56 | 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, | ||
| 57 | 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, | ||
| 58 | 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 | ||
| 59 | }; | ||
| 60 | |||
| 61 | /* This is the algorithm used by the Posix `cksum' utility. */ | ||
| 62 | unsigned long | ||
| 63 | cksum (const void *b_, size_t n) | ||
| 64 | { | ||
| 65 | const unsigned char *b = b_; | ||
| 66 | uint32_t s = 0; | ||
| 67 | size_t i; | ||
| 68 | for (i = n; i > 0; --i) | ||
| 69 | { | ||
| 70 | unsigned char c = *b++; | ||
| 71 | s = (s << 8) ^ crctab[(s >> 24) ^ c]; | ||
| 72 | } | ||
| 73 | while (n != 0) | ||
| 74 | { | ||
| 75 | unsigned char c = n; | ||
| 76 | n >>= 8; | ||
| 77 | s = (s << 8) ^ crctab[(s >> 24) ^ c]; | ||
| 78 | } | ||
| 79 | return ~s; | ||
| 80 | } | ||
| 81 | |||
| 82 | #ifdef STANDALONE_TEST | ||
| 83 | #include <stdio.h> | ||
| 84 | int | ||
| 85 | main (void) | ||
| 86 | { | ||
| 87 | char buf[65536]; | ||
| 88 | int n = fread (buf, 1, sizeof buf, stdin); | ||
| 89 | printf ("%lu\n", cksum (buf, n)); | ||
| 90 | return 0; | ||
| 91 | } | ||
| 92 | #endif | ||
diff --git a/pintos-progos/tests/cksum.h b/pintos-progos/tests/cksum.h new file mode 100644 index 0000000..23a1fe9 --- /dev/null +++ b/pintos-progos/tests/cksum.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef TESTS_CKSUM_H | ||
| 2 | #define TESTS_CKSUM_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | unsigned long cksum(const void *, size_t); | ||
| 7 | |||
| 8 | #endif /* tests/cksum.h */ | ||
diff --git a/pintos-progos/tests/cksum.pm b/pintos-progos/tests/cksum.pm new file mode 100644 index 0000000..73be5f2 --- /dev/null +++ b/pintos-progos/tests/cksum.pm | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | # From the `cksum' entry in SUSv3. | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use warnings; | ||
| 5 | |||
| 6 | my (@crctab) = | ||
| 7 | (0x00000000, | ||
| 8 | 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, | ||
| 9 | 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, | ||
| 10 | 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, | ||
| 11 | 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, | ||
| 12 | 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, | ||
| 13 | 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, | ||
| 14 | 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, | ||
| 15 | 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, | ||
| 16 | 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, | ||
| 17 | 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, | ||
| 18 | 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, | ||
| 19 | 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, | ||
| 20 | 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, | ||
| 21 | 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, | ||
| 22 | 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, | ||
| 23 | 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, | ||
| 24 | 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, | ||
| 25 | 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, | ||
| 26 | 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, | ||
| 27 | 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, | ||
| 28 | 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, | ||
| 29 | 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, | ||
| 30 | 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, | ||
| 31 | 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, | ||
| 32 | 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, | ||
| 33 | 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, | ||
| 34 | 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, | ||
| 35 | 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, | ||
| 36 | 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, | ||
| 37 | 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, | ||
| 38 | 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, | ||
| 39 | 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, | ||
| 40 | 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, | ||
| 41 | 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, | ||
| 42 | 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, | ||
| 43 | 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, | ||
| 44 | 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, | ||
| 45 | 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, | ||
| 46 | 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, | ||
| 47 | 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, | ||
| 48 | 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, | ||
| 49 | 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, | ||
| 50 | 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, | ||
| 51 | 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, | ||
| 52 | 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, | ||
| 53 | 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, | ||
| 54 | 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, | ||
| 55 | 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, | ||
| 56 | 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, | ||
| 57 | 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, | ||
| 58 | 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4); | ||
| 59 | |||
| 60 | sub cksum { | ||
| 61 | my ($b) = @_; | ||
| 62 | my ($n) = length ($b); | ||
| 63 | my ($s) = 0; | ||
| 64 | for my $i (0...$n - 1) { | ||
| 65 | my ($c) = ord (substr ($b, $i, 1)); | ||
| 66 | $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c]; | ||
| 67 | $s &= 0xffff_ffff; | ||
| 68 | } | ||
| 69 | while ($n != 0) { | ||
| 70 | my ($c) = $n & 0xff; | ||
| 71 | $n >>= 8; | ||
| 72 | $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c]; | ||
| 73 | $s &= 0xffff_ffff; | ||
| 74 | } | ||
| 75 | return ~$s & 0xffff_ffff; | ||
| 76 | } | ||
| 77 | |||
| 78 | sub cksum_file { | ||
| 79 | my ($file) = @_; | ||
| 80 | open (FILE, '<', $file) or die "$file: open: $!\n"; | ||
| 81 | my ($data); | ||
| 82 | sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n"; | ||
| 83 | close (FILE); | ||
| 84 | return cksum ($data); | ||
| 85 | } | ||
| 86 | |||
| 87 | 1; | ||
diff --git a/pintos-progos/tests/filesys/Grading.no-vm b/pintos-progos/tests/filesys/Grading.no-vm new file mode 100644 index 0000000..ee98fc1 --- /dev/null +++ b/pintos-progos/tests/filesys/Grading.no-vm | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # This project is primarily about implementing the file system, but | ||
| 5 | # all the previous functionality should work too. It's not too easy | ||
| 6 | # to screw it up, thus the emphasis. | ||
| 7 | |||
| 8 | # 65% for extended file system features. | ||
| 9 | 30% tests/filesys/extended/Rubric.functionality | ||
| 10 | 15% tests/filesys/extended/Rubric.robustness | ||
| 11 | 20% tests/filesys/extended/Rubric.persistence | ||
| 12 | |||
| 13 | # 20% to not break the provided file system features. | ||
| 14 | 20% tests/filesys/base/Rubric | ||
| 15 | |||
| 16 | # 15% for the rest. | ||
| 17 | 10% tests/userprog/Rubric.functionality | ||
| 18 | 5% tests/userprog/Rubric.robustness | ||
diff --git a/pintos-progos/tests/filesys/Grading.with-vm b/pintos-progos/tests/filesys/Grading.with-vm new file mode 100644 index 0000000..e7c041e --- /dev/null +++ b/pintos-progos/tests/filesys/Grading.with-vm | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # This project is primarily about implementing the file system, but | ||
| 5 | # all the previous functionality should work too. It's not too easy | ||
| 6 | # to screw it up, thus the emphasis. | ||
| 7 | |||
| 8 | # 65% for extended file system features. | ||
| 9 | 30% tests/filesys/extended/Rubric.functionality | ||
| 10 | 15% tests/filesys/extended/Rubric.robustness | ||
| 11 | 20% tests/filesys/extended/Rubric.persistence | ||
| 12 | |||
| 13 | # 20% to not break the provided file system features. | ||
| 14 | 20% tests/filesys/base/Rubric | ||
| 15 | |||
| 16 | # 15% for the rest. | ||
| 17 | 10% tests/userprog/Rubric.functionality | ||
| 18 | 5% tests/userprog/Rubric.robustness | ||
| 19 | |||
| 20 | # Up to 10% bonus for working VM functionality. | ||
| 21 | 8% tests/vm/Rubric.functionality | ||
| 22 | 2% tests/vm/Rubric.robustness | ||
diff --git a/pintos-progos/tests/filesys/base/Make.tests b/pintos-progos/tests/filesys/base/Make.tests new file mode 100644 index 0000000..e475222 --- /dev/null +++ b/pintos-progos/tests/filesys/base/Make.tests | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \ | ||
| 4 | lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \ | ||
| 5 | sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write) | ||
| 6 | |||
| 7 | tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \ | ||
| 8 | tests/filesys/base/,child-syn-read child-syn-wrt) | ||
| 9 | |||
| 10 | $(foreach prog,$(tests/filesys/base_PROGS), \ | ||
| 11 | $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c)) | ||
| 12 | $(foreach prog,$(tests/filesys/base_TESTS), \ | ||
| 13 | $(eval $(prog)_SRC += tests/main.c)) | ||
| 14 | |||
| 15 | tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read | ||
| 16 | tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt | ||
| 17 | |||
| 18 | tests/filesys/base/syn-read.output: TIMEOUT = 300 | ||
diff --git a/pintos-progos/tests/filesys/base/Rubric b/pintos-progos/tests/filesys/base/Rubric new file mode 100644 index 0000000..49a9d15 --- /dev/null +++ b/pintos-progos/tests/filesys/base/Rubric | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | Functionality of base file system: | ||
| 2 | - Test basic support for small files. | ||
| 3 | 1 sm-create | ||
| 4 | 2 sm-full | ||
| 5 | 2 sm-random | ||
| 6 | 2 sm-seq-block | ||
| 7 | 3 sm-seq-random | ||
| 8 | |||
| 9 | - Test basic support for large files. | ||
| 10 | 1 lg-create | ||
| 11 | 2 lg-full | ||
| 12 | 2 lg-random | ||
| 13 | 2 lg-seq-block | ||
| 14 | 3 lg-seq-random | ||
| 15 | |||
| 16 | - Test synchronized multiprogram access to files. | ||
| 17 | 4 syn-read | ||
| 18 | 4 syn-write | ||
| 19 | 2 syn-remove | ||
diff --git a/pintos-progos/tests/filesys/base/child-syn-read.c b/pintos-progos/tests/filesys/base/child-syn-read.c new file mode 100644 index 0000000..77a5e26 --- /dev/null +++ b/pintos-progos/tests/filesys/base/child-syn-read.c | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /* Child process for syn-read test. | ||
| 2 | Reads the contents of a test file a byte at a time, in the | ||
| 3 | hope that this will take long enough that we can get a | ||
| 4 | significant amount of contention in the kernel file system | ||
| 5 | code. */ | ||
| 6 | |||
| 7 | #include <random.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <syscall.h> | ||
| 11 | #include "tests/lib.h" | ||
| 12 | #include "tests/filesys/base/syn-read.h" | ||
| 13 | |||
| 14 | const char *test_name = "child-syn-read"; | ||
| 15 | |||
| 16 | static char buf[BUF_SIZE]; | ||
| 17 | |||
| 18 | int | ||
| 19 | main (int argc, const char *argv[]) | ||
| 20 | { | ||
| 21 | int child_idx; | ||
| 22 | int fd; | ||
| 23 | size_t i; | ||
| 24 | |||
| 25 | quiet = true; | ||
| 26 | |||
| 27 | CHECK (argc == 2, "argc must be 2, actually %d", argc); | ||
| 28 | child_idx = atoi (argv[1]); | ||
| 29 | |||
| 30 | random_init (0); | ||
| 31 | random_bytes (buf, sizeof buf); | ||
| 32 | |||
| 33 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 34 | for (i = 0; i < sizeof buf; i++) | ||
| 35 | { | ||
| 36 | char c; | ||
| 37 | CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name); | ||
| 38 | compare_bytes (&c, buf + i, 1, i, file_name); | ||
| 39 | } | ||
| 40 | close (fd); | ||
| 41 | |||
| 42 | return child_idx; | ||
| 43 | } | ||
| 44 | |||
diff --git a/pintos-progos/tests/filesys/base/child-syn-wrt.c b/pintos-progos/tests/filesys/base/child-syn-wrt.c new file mode 100644 index 0000000..1b52584 --- /dev/null +++ b/pintos-progos/tests/filesys/base/child-syn-wrt.c | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* Child process for syn-read test. | ||
| 2 | Writes into part of a test file. Other processes will be | ||
| 3 | writing into other parts at the same time. */ | ||
| 4 | |||
| 5 | #include <random.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/filesys/base/syn-write.h" | ||
| 10 | |||
| 11 | char buf[BUF_SIZE]; | ||
| 12 | |||
| 13 | int | ||
| 14 | main (int argc, char *argv[]) | ||
| 15 | { | ||
| 16 | int child_idx; | ||
| 17 | int fd; | ||
| 18 | |||
| 19 | quiet = true; | ||
| 20 | |||
| 21 | CHECK (argc == 2, "argc must be 2, actually %d", argc); | ||
| 22 | child_idx = atoi (argv[1]); | ||
| 23 | |||
| 24 | random_init (0); | ||
| 25 | random_bytes (buf, sizeof buf); | ||
| 26 | |||
| 27 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 28 | seek (fd, CHUNK_SIZE * child_idx); | ||
| 29 | CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0, | ||
| 30 | "write \"%s\"", file_name); | ||
| 31 | msg ("close \"%s\"", file_name); | ||
| 32 | close (fd); | ||
| 33 | |||
| 34 | return child_idx; | ||
| 35 | } | ||
diff --git a/pintos-progos/tests/filesys/base/full.inc b/pintos-progos/tests/filesys/base/full.inc new file mode 100644 index 0000000..38a0396 --- /dev/null +++ b/pintos-progos/tests/filesys/base/full.inc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include "tests/filesys/seq-test.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | static char buf[TEST_SIZE]; | ||
| 7 | |||
| 8 | static size_t | ||
| 9 | return_test_size (void) | ||
| 10 | { | ||
| 11 | return TEST_SIZE; | ||
| 12 | } | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | seq_test ("quux", | ||
| 18 | buf, sizeof buf, sizeof buf, | ||
| 19 | return_test_size, NULL); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/filesys/base/lg-create.c b/pintos-progos/tests/filesys/base/lg-create.c new file mode 100644 index 0000000..5c45eee --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-create.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Tests that create properly zeros out the contents of a fairly | ||
| 2 | large file. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 75678 | ||
| 5 | #include "tests/filesys/create.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-create.ck b/pintos-progos/tests/filesys/base/lg-create.ck new file mode 100644 index 0000000..86b2c51 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-create.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-create) begin | ||
| 7 | (lg-create) create "blargle" | ||
| 8 | (lg-create) open "blargle" for verification | ||
| 9 | (lg-create) verified contents of "blargle" | ||
| 10 | (lg-create) close "blargle" | ||
| 11 | (lg-create) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-full.c b/pintos-progos/tests/filesys/base/lg-full.c new file mode 100644 index 0000000..5f7234d --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-full.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out the contents of a fairly large file all at once, | ||
| 2 | and then reads it back to make sure that it was written | ||
| 3 | properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 75678 | ||
| 6 | #include "tests/filesys/base/full.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-full.ck b/pintos-progos/tests/filesys/base/lg-full.ck new file mode 100644 index 0000000..ee6c7f9 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-full.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-full) begin | ||
| 7 | (lg-full) create "quux" | ||
| 8 | (lg-full) open "quux" | ||
| 9 | (lg-full) writing "quux" | ||
| 10 | (lg-full) close "quux" | ||
| 11 | (lg-full) open "quux" for verification | ||
| 12 | (lg-full) verified contents of "quux" | ||
| 13 | (lg-full) close "quux" | ||
| 14 | (lg-full) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-random.c b/pintos-progos/tests/filesys/base/lg-random.c new file mode 100644 index 0000000..b6f8873 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-random.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out the content of a fairly large file in random order, | ||
| 2 | then reads it back in random order to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define BLOCK_SIZE 512 | ||
| 6 | #define TEST_SIZE (512 * 150) | ||
| 7 | #include "tests/filesys/base/random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-random.ck b/pintos-progos/tests/filesys/base/lg-random.ck new file mode 100644 index 0000000..dd9f1dd --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-random.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-random) begin | ||
| 7 | (lg-random) create "bazzle" | ||
| 8 | (lg-random) open "bazzle" | ||
| 9 | (lg-random) write "bazzle" in random order | ||
| 10 | (lg-random) read "bazzle" in random order | ||
| 11 | (lg-random) close "bazzle" | ||
| 12 | (lg-random) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.c b/pintos-progos/tests/filesys/base/lg-seq-block.c new file mode 100644 index 0000000..580c30b --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-block.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out a fairly large file sequentially, one fixed-size | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 75678 | ||
| 6 | #define BLOCK_SIZE 513 | ||
| 7 | #include "tests/filesys/base/seq-block.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.ck b/pintos-progos/tests/filesys/base/lg-seq-block.ck new file mode 100644 index 0000000..b789081 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-block.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-seq-block) begin | ||
| 7 | (lg-seq-block) create "noodle" | ||
| 8 | (lg-seq-block) open "noodle" | ||
| 9 | (lg-seq-block) writing "noodle" | ||
| 10 | (lg-seq-block) close "noodle" | ||
| 11 | (lg-seq-block) open "noodle" for verification | ||
| 12 | (lg-seq-block) verified contents of "noodle" | ||
| 13 | (lg-seq-block) close "noodle" | ||
| 14 | (lg-seq-block) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.c b/pintos-progos/tests/filesys/base/lg-seq-random.c new file mode 100644 index 0000000..fbb6bba --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-random.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out a fairly large file sequentially, one random-sized | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 75678 | ||
| 6 | #include "tests/filesys/base/seq-random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.ck b/pintos-progos/tests/filesys/base/lg-seq-random.ck new file mode 100644 index 0000000..6b2dc82 --- /dev/null +++ b/pintos-progos/tests/filesys/base/lg-seq-random.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (lg-seq-random) begin | ||
| 7 | (lg-seq-random) create "nibble" | ||
| 8 | (lg-seq-random) open "nibble" | ||
| 9 | (lg-seq-random) writing "nibble" | ||
| 10 | (lg-seq-random) close "nibble" | ||
| 11 | (lg-seq-random) open "nibble" for verification | ||
| 12 | (lg-seq-random) verified contents of "nibble" | ||
| 13 | (lg-seq-random) close "nibble" | ||
| 14 | (lg-seq-random) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/random.inc b/pintos-progos/tests/filesys/base/random.inc new file mode 100644 index 0000000..eeeea68 --- /dev/null +++ b/pintos-progos/tests/filesys/base/random.inc | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <random.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | #if TEST_SIZE % BLOCK_SIZE != 0 | ||
| 11 | #error TEST_SIZE must be a multiple of BLOCK_SIZE | ||
| 12 | #endif | ||
| 13 | |||
| 14 | #define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE) | ||
| 15 | |||
| 16 | char buf[TEST_SIZE]; | ||
| 17 | int order[BLOCK_CNT]; | ||
| 18 | |||
| 19 | void | ||
| 20 | test_main (void) | ||
| 21 | { | ||
| 22 | const char *file_name = "bazzle"; | ||
| 23 | int fd; | ||
| 24 | size_t i; | ||
| 25 | |||
| 26 | random_init (57); | ||
| 27 | random_bytes (buf, sizeof buf); | ||
| 28 | |||
| 29 | for (i = 0; i < BLOCK_CNT; i++) | ||
| 30 | order[i] = i; | ||
| 31 | |||
| 32 | CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name); | ||
| 33 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 34 | |||
| 35 | msg ("write \"%s\" in random order", file_name); | ||
| 36 | shuffle (order, BLOCK_CNT, sizeof *order); | ||
| 37 | for (i = 0; i < BLOCK_CNT; i++) | ||
| 38 | { | ||
| 39 | size_t ofs = BLOCK_SIZE * order[i]; | ||
| 40 | seek (fd, ofs); | ||
| 41 | if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE) | ||
| 42 | fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs); | ||
| 43 | } | ||
| 44 | |||
| 45 | msg ("read \"%s\" in random order", file_name); | ||
| 46 | shuffle (order, BLOCK_CNT, sizeof *order); | ||
| 47 | for (i = 0; i < BLOCK_CNT; i++) | ||
| 48 | { | ||
| 49 | char block[BLOCK_SIZE]; | ||
| 50 | size_t ofs = BLOCK_SIZE * order[i]; | ||
| 51 | seek (fd, ofs); | ||
| 52 | if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE) | ||
| 53 | fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs); | ||
| 54 | compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name); | ||
| 55 | } | ||
| 56 | |||
| 57 | msg ("close \"%s\"", file_name); | ||
| 58 | close (fd); | ||
| 59 | } | ||
diff --git a/pintos-progos/tests/filesys/base/seq-block.inc b/pintos-progos/tests/filesys/base/seq-block.inc new file mode 100644 index 0000000..d4c1f57 --- /dev/null +++ b/pintos-progos/tests/filesys/base/seq-block.inc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include "tests/filesys/seq-test.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | static char buf[TEST_SIZE]; | ||
| 7 | |||
| 8 | static size_t | ||
| 9 | return_block_size (void) | ||
| 10 | { | ||
| 11 | return BLOCK_SIZE; | ||
| 12 | } | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | seq_test ("noodle", | ||
| 18 | buf, sizeof buf, sizeof buf, | ||
| 19 | return_block_size, NULL); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/filesys/base/seq-random.inc b/pintos-progos/tests/filesys/base/seq-random.inc new file mode 100644 index 0000000..a4da4c5 --- /dev/null +++ b/pintos-progos/tests/filesys/base/seq-random.inc | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <random.h> | ||
| 4 | #include "tests/filesys/seq-test.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | static char buf[TEST_SIZE]; | ||
| 8 | |||
| 9 | static size_t | ||
| 10 | return_random (void) | ||
| 11 | { | ||
| 12 | return random_ulong () % 1031 + 1; | ||
| 13 | } | ||
| 14 | |||
| 15 | void | ||
| 16 | test_main (void) | ||
| 17 | { | ||
| 18 | random_init (-1); | ||
| 19 | seq_test ("nibble", | ||
| 20 | buf, sizeof buf, sizeof buf, | ||
| 21 | return_random, NULL); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/filesys/base/sm-create.c b/pintos-progos/tests/filesys/base/sm-create.c new file mode 100644 index 0000000..6b97ac1 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-create.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Tests that create properly zeros out the contents of a fairly | ||
| 2 | small file. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 5678 | ||
| 5 | #include "tests/filesys/create.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-create.ck b/pintos-progos/tests/filesys/base/sm-create.ck new file mode 100644 index 0000000..8ca80dc --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-create.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-create) begin | ||
| 7 | (sm-create) create "blargle" | ||
| 8 | (sm-create) open "blargle" for verification | ||
| 9 | (sm-create) verified contents of "blargle" | ||
| 10 | (sm-create) close "blargle" | ||
| 11 | (sm-create) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-full.c b/pintos-progos/tests/filesys/base/sm-full.c new file mode 100644 index 0000000..23ff3d4 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-full.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out the contents of a fairly small file all at once, | ||
| 2 | and then reads it back to make sure that it was written | ||
| 3 | properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 5678 | ||
| 6 | #include "tests/filesys/base/full.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-full.ck b/pintos-progos/tests/filesys/base/sm-full.ck new file mode 100644 index 0000000..2e0eb36 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-full.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-full) begin | ||
| 7 | (sm-full) create "quux" | ||
| 8 | (sm-full) open "quux" | ||
| 9 | (sm-full) writing "quux" | ||
| 10 | (sm-full) close "quux" | ||
| 11 | (sm-full) open "quux" for verification | ||
| 12 | (sm-full) verified contents of "quux" | ||
| 13 | (sm-full) close "quux" | ||
| 14 | (sm-full) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-random.c b/pintos-progos/tests/filesys/base/sm-random.c new file mode 100644 index 0000000..42d670f --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-random.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out the content of a fairly small file in random order, | ||
| 2 | then reads it back in random order to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define BLOCK_SIZE 13 | ||
| 6 | #define TEST_SIZE (13 * 123) | ||
| 7 | #include "tests/filesys/base/random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-random.ck b/pintos-progos/tests/filesys/base/sm-random.ck new file mode 100644 index 0000000..bda049d --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-random.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-random) begin | ||
| 7 | (sm-random) create "bazzle" | ||
| 8 | (sm-random) open "bazzle" | ||
| 9 | (sm-random) write "bazzle" in random order | ||
| 10 | (sm-random) read "bazzle" in random order | ||
| 11 | (sm-random) close "bazzle" | ||
| 12 | (sm-random) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.c b/pintos-progos/tests/filesys/base/sm-seq-block.c new file mode 100644 index 0000000..e368327 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-block.c | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | /* Writes out a fairly small file sequentially, one fixed-size | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 5678 | ||
| 6 | #define BLOCK_SIZE 513 | ||
| 7 | #include "tests/filesys/base/seq-block.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.ck b/pintos-progos/tests/filesys/base/sm-seq-block.ck new file mode 100644 index 0000000..0e2939d --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-block.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-seq-block) begin | ||
| 7 | (sm-seq-block) create "noodle" | ||
| 8 | (sm-seq-block) open "noodle" | ||
| 9 | (sm-seq-block) writing "noodle" | ||
| 10 | (sm-seq-block) close "noodle" | ||
| 11 | (sm-seq-block) open "noodle" for verification | ||
| 12 | (sm-seq-block) verified contents of "noodle" | ||
| 13 | (sm-seq-block) close "noodle" | ||
| 14 | (sm-seq-block) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.c b/pintos-progos/tests/filesys/base/sm-seq-random.c new file mode 100644 index 0000000..89e5b71 --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-random.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Writes out a fairly large file sequentially, one random-sized | ||
| 2 | block at a time, then reads it back to verify that it was | ||
| 3 | written properly. */ | ||
| 4 | |||
| 5 | #define TEST_SIZE 5678 | ||
| 6 | #include "tests/filesys/base/seq-random.inc" | ||
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.ck b/pintos-progos/tests/filesys/base/sm-seq-random.ck new file mode 100644 index 0000000..2fb368b --- /dev/null +++ b/pintos-progos/tests/filesys/base/sm-seq-random.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (sm-seq-random) begin | ||
| 7 | (sm-seq-random) create "nibble" | ||
| 8 | (sm-seq-random) open "nibble" | ||
| 9 | (sm-seq-random) writing "nibble" | ||
| 10 | (sm-seq-random) close "nibble" | ||
| 11 | (sm-seq-random) open "nibble" for verification | ||
| 12 | (sm-seq-random) verified contents of "nibble" | ||
| 13 | (sm-seq-random) close "nibble" | ||
| 14 | (sm-seq-random) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-read.c b/pintos-progos/tests/filesys/base/syn-read.c new file mode 100644 index 0000000..7c36a42 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-read.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* Spawns 10 child processes, all of which read from the same | ||
| 2 | file and make sure that the contents are what they should | ||
| 3 | be. */ | ||
| 4 | |||
| 5 | #include <random.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | #include "tests/filesys/base/syn-read.h" | ||
| 11 | |||
| 12 | static char buf[BUF_SIZE]; | ||
| 13 | |||
| 14 | #define CHILD_CNT 10 | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | pid_t children[CHILD_CNT]; | ||
| 20 | int fd; | ||
| 21 | |||
| 22 | CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name); | ||
| 23 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 24 | random_bytes (buf, sizeof buf); | ||
| 25 | CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name); | ||
| 26 | msg ("close \"%s\"", file_name); | ||
| 27 | close (fd); | ||
| 28 | |||
| 29 | exec_children ("child-syn-read", children, CHILD_CNT); | ||
| 30 | wait_children (children, CHILD_CNT); | ||
| 31 | } | ||
diff --git a/pintos-progos/tests/filesys/base/syn-read.ck b/pintos-progos/tests/filesys/base/syn-read.ck new file mode 100644 index 0000000..e2f68e8 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-read.ck | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (syn-read) begin | ||
| 7 | (syn-read) create "data" | ||
| 8 | (syn-read) open "data" | ||
| 9 | (syn-read) write "data" | ||
| 10 | (syn-read) close "data" | ||
| 11 | (syn-read) exec child 1 of 10: "child-syn-read 0" | ||
| 12 | (syn-read) exec child 2 of 10: "child-syn-read 1" | ||
| 13 | (syn-read) exec child 3 of 10: "child-syn-read 2" | ||
| 14 | (syn-read) exec child 4 of 10: "child-syn-read 3" | ||
| 15 | (syn-read) exec child 5 of 10: "child-syn-read 4" | ||
| 16 | (syn-read) exec child 6 of 10: "child-syn-read 5" | ||
| 17 | (syn-read) exec child 7 of 10: "child-syn-read 6" | ||
| 18 | (syn-read) exec child 8 of 10: "child-syn-read 7" | ||
| 19 | (syn-read) exec child 9 of 10: "child-syn-read 8" | ||
| 20 | (syn-read) exec child 10 of 10: "child-syn-read 9" | ||
| 21 | (syn-read) wait for child 1 of 10 returned 0 (expected 0) | ||
| 22 | (syn-read) wait for child 2 of 10 returned 1 (expected 1) | ||
| 23 | (syn-read) wait for child 3 of 10 returned 2 (expected 2) | ||
| 24 | (syn-read) wait for child 4 of 10 returned 3 (expected 3) | ||
| 25 | (syn-read) wait for child 5 of 10 returned 4 (expected 4) | ||
| 26 | (syn-read) wait for child 6 of 10 returned 5 (expected 5) | ||
| 27 | (syn-read) wait for child 7 of 10 returned 6 (expected 6) | ||
| 28 | (syn-read) wait for child 8 of 10 returned 7 (expected 7) | ||
| 29 | (syn-read) wait for child 9 of 10 returned 8 (expected 8) | ||
| 30 | (syn-read) wait for child 10 of 10 returned 9 (expected 9) | ||
| 31 | (syn-read) end | ||
| 32 | EOF | ||
| 33 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-read.h b/pintos-progos/tests/filesys/base/syn-read.h new file mode 100644 index 0000000..bff8082 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-read.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef TESTS_FILESYS_BASE_SYN_READ_H | ||
| 2 | #define TESTS_FILESYS_BASE_SYN_READ_H | ||
| 3 | |||
| 4 | #define BUF_SIZE 1024 | ||
| 5 | static const char file_name[] = "data"; | ||
| 6 | |||
| 7 | #endif /* tests/filesys/base/syn-read.h */ | ||
diff --git a/pintos-progos/tests/filesys/base/syn-remove.c b/pintos-progos/tests/filesys/base/syn-remove.c new file mode 100644 index 0000000..c9ba110 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-remove.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* Verifies that a deleted file may still be written to and read | ||
| 2 | from. */ | ||
| 3 | |||
| 4 | #include <random.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | char buf1[1234]; | ||
| 11 | char buf2[1234]; | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | const char *file_name = "deleteme"; | ||
| 17 | int fd; | ||
| 18 | |||
| 19 | CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name); | ||
| 20 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 21 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 22 | random_bytes (buf1, sizeof buf1); | ||
| 23 | CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name); | ||
| 24 | msg ("seek \"%s\" to 0", file_name); | ||
| 25 | seek (fd, 0); | ||
| 26 | CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name); | ||
| 27 | compare_bytes (buf2, buf1, sizeof buf1, 0, file_name); | ||
| 28 | msg ("close \"%s\"", file_name); | ||
| 29 | close (fd); | ||
| 30 | } | ||
diff --git a/pintos-progos/tests/filesys/base/syn-remove.ck b/pintos-progos/tests/filesys/base/syn-remove.ck new file mode 100644 index 0000000..16ff11e --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-remove.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (syn-remove) begin | ||
| 7 | (syn-remove) create "deleteme" | ||
| 8 | (syn-remove) open "deleteme" | ||
| 9 | (syn-remove) remove "deleteme" | ||
| 10 | (syn-remove) write "deleteme" | ||
| 11 | (syn-remove) seek "deleteme" to 0 | ||
| 12 | (syn-remove) read "deleteme" | ||
| 13 | (syn-remove) close "deleteme" | ||
| 14 | (syn-remove) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-write.c b/pintos-progos/tests/filesys/base/syn-write.c new file mode 100644 index 0000000..1439862 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-write.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* Spawns several child processes to write out different parts of | ||
| 2 | the contents of a file and waits for them to finish. Then | ||
| 3 | reads back the file and verifies its contents. */ | ||
| 4 | |||
| 5 | #include <random.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include <syscall.h> | ||
| 9 | #include "tests/filesys/base/syn-write.h" | ||
| 10 | #include "tests/lib.h" | ||
| 11 | #include "tests/main.h" | ||
| 12 | |||
| 13 | char buf1[BUF_SIZE]; | ||
| 14 | char buf2[BUF_SIZE]; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | pid_t children[CHILD_CNT]; | ||
| 20 | int fd; | ||
| 21 | |||
| 22 | CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name); | ||
| 23 | |||
| 24 | exec_children ("child-syn-wrt", children, CHILD_CNT); | ||
| 25 | wait_children (children, CHILD_CNT); | ||
| 26 | |||
| 27 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 28 | CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name); | ||
| 29 | random_bytes (buf2, sizeof buf2); | ||
| 30 | compare_bytes (buf1, buf2, sizeof buf1, 0, file_name); | ||
| 31 | } | ||
diff --git a/pintos-progos/tests/filesys/base/syn-write.ck b/pintos-progos/tests/filesys/base/syn-write.ck new file mode 100644 index 0000000..629a7a2 --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-write.ck | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (syn-write) begin | ||
| 7 | (syn-write) create "stuff" | ||
| 8 | (syn-write) exec child 1 of 10: "child-syn-wrt 0" | ||
| 9 | (syn-write) exec child 2 of 10: "child-syn-wrt 1" | ||
| 10 | (syn-write) exec child 3 of 10: "child-syn-wrt 2" | ||
| 11 | (syn-write) exec child 4 of 10: "child-syn-wrt 3" | ||
| 12 | (syn-write) exec child 5 of 10: "child-syn-wrt 4" | ||
| 13 | (syn-write) exec child 6 of 10: "child-syn-wrt 5" | ||
| 14 | (syn-write) exec child 7 of 10: "child-syn-wrt 6" | ||
| 15 | (syn-write) exec child 8 of 10: "child-syn-wrt 7" | ||
| 16 | (syn-write) exec child 9 of 10: "child-syn-wrt 8" | ||
| 17 | (syn-write) exec child 10 of 10: "child-syn-wrt 9" | ||
| 18 | (syn-write) wait for child 1 of 10 returned 0 (expected 0) | ||
| 19 | (syn-write) wait for child 2 of 10 returned 1 (expected 1) | ||
| 20 | (syn-write) wait for child 3 of 10 returned 2 (expected 2) | ||
| 21 | (syn-write) wait for child 4 of 10 returned 3 (expected 3) | ||
| 22 | (syn-write) wait for child 5 of 10 returned 4 (expected 4) | ||
| 23 | (syn-write) wait for child 6 of 10 returned 5 (expected 5) | ||
| 24 | (syn-write) wait for child 7 of 10 returned 6 (expected 6) | ||
| 25 | (syn-write) wait for child 8 of 10 returned 7 (expected 7) | ||
| 26 | (syn-write) wait for child 9 of 10 returned 8 (expected 8) | ||
| 27 | (syn-write) wait for child 10 of 10 returned 9 (expected 9) | ||
| 28 | (syn-write) open "stuff" | ||
| 29 | (syn-write) read "stuff" | ||
| 30 | (syn-write) end | ||
| 31 | EOF | ||
| 32 | pass; | ||
diff --git a/pintos-progos/tests/filesys/base/syn-write.h b/pintos-progos/tests/filesys/base/syn-write.h new file mode 100644 index 0000000..07a6d5a --- /dev/null +++ b/pintos-progos/tests/filesys/base/syn-write.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef TESTS_FILESYS_BASE_SYN_WRITE_H | ||
| 2 | #define TESTS_FILESYS_BASE_SYN_WRITE_H | ||
| 3 | |||
| 4 | #define CHILD_CNT 10 | ||
| 5 | #define CHUNK_SIZE 512 | ||
| 6 | #define BUF_SIZE (CHILD_CNT * CHUNK_SIZE) | ||
| 7 | static const char file_name[] = "stuff"; | ||
| 8 | |||
| 9 | #endif /* tests/filesys/base/syn-write.h */ | ||
diff --git a/pintos-progos/tests/filesys/create.inc b/pintos-progos/tests/filesys/create.inc new file mode 100644 index 0000000..4baf771 --- /dev/null +++ b/pintos-progos/tests/filesys/create.inc | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | static char buf[TEST_SIZE]; | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | const char *file_name = "blargle"; | ||
| 13 | CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name); | ||
| 14 | check_file (file_name, buf, TEST_SIZE); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/Make.tests b/pintos-progos/tests/filesys/extended/Make.tests new file mode 100644 index 0000000..e03b98d --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Make.tests | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | raw_tests = dir-empty-name dir-mk-tree dir-mkdir dir-open \ | ||
| 4 | dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree \ | ||
| 5 | dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg \ | ||
| 6 | grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm \ | ||
| 7 | grow-sparse grow-tell grow-two-files syn-rw | ||
| 8 | |||
| 9 | tests/filesys/extended_TESTS = $(patsubst %,tests/filesys/extended/%,$(raw_tests)) | ||
| 10 | tests/filesys/extended_EXTRA_GRADES = $(patsubst %,tests/filesys/extended/%-persistence,$(raw_tests)) | ||
| 11 | |||
| 12 | tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \ | ||
| 13 | tests/filesys/extended/child-syn-rw tests/filesys/extended/tar | ||
| 14 | |||
| 15 | $(foreach prog,$(tests/filesys/extended_PROGS), \ | ||
| 16 | $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c)) | ||
| 17 | $(foreach prog,$(tests/filesys/extended_TESTS), \ | ||
| 18 | $(eval $(prog)_SRC += tests/main.c)) | ||
| 19 | $(foreach prog,$(tests/filesys/extended_TESTS), \ | ||
| 20 | $(eval $(prog)_PUTFILES += tests/filesys/extended/tar)) | ||
| 21 | # The version of GNU make 3.80 on vine barfs if this is split at | ||
| 22 | # the last comma. | ||
| 23 | $(foreach test,$(tests/filesys/extended_TESTS),$(eval $(test).output: FILESYSSOURCE = --disk=tmp.dsk)) | ||
| 24 | |||
| 25 | tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c | ||
| 26 | tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c | ||
| 27 | |||
| 28 | tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw | ||
| 29 | |||
| 30 | tests/filesys/extended/dir-vine.output: TIMEOUT = 150 | ||
| 31 | |||
| 32 | GETTIMEOUT = 60 | ||
| 33 | |||
| 34 | GETCMD = pintos -v -k -T $(GETTIMEOUT) | ||
| 35 | GETCMD += $(PINTOSOPTS) | ||
| 36 | GETCMD += $(SIMULATOR) | ||
| 37 | GETCMD += $(FILESYSSOURCE) | ||
| 38 | GETCMD += -g fs.tar -a $(TEST).tar | ||
| 39 | ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm) | ||
| 40 | GETCMD += --swap-size=4 | ||
| 41 | endif | ||
| 42 | GETCMD += -- -q | ||
| 43 | GETCMD += $(KERNELFLAGS) | ||
| 44 | GETCMD += run 'tar fs.tar /' | ||
| 45 | GETCMD += < /dev/null | ||
| 46 | GETCMD += 2> $(TEST)-persistence.errors $(if $(VERBOSE),|tee,>) $(TEST)-persistence.output | ||
| 47 | |||
| 48 | tests/filesys/extended/%.output: kernel.bin | ||
| 49 | rm -f tmp.dsk | ||
| 50 | pintos-mkdisk tmp.dsk --filesys-size=2 | ||
| 51 | $(TESTCMD) | ||
| 52 | $(GETCMD) | ||
| 53 | rm -f tmp.dsk | ||
| 54 | $(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.output: tests/filesys/extended/$(raw_test).output)) | ||
| 55 | $(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.result: tests/filesys/extended/$(raw_test).result)) | ||
| 56 | |||
| 57 | TARS = $(addsuffix .tar,$(tests/filesys/extended_TESTS)) | ||
| 58 | |||
| 59 | clean:: | ||
| 60 | rm -f $(TARS) | ||
| 61 | rm -f tests/filesys/extended/can-rmdir-cwd | ||
diff --git a/pintos-progos/tests/filesys/extended/Rubric.functionality b/pintos-progos/tests/filesys/extended/Rubric.functionality new file mode 100644 index 0000000..91ed6f0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Rubric.functionality | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | Functionality of extended file system: | ||
| 2 | - Test directory support. | ||
| 3 | 1 dir-mkdir | ||
| 4 | 3 dir-mk-tree | ||
| 5 | |||
| 6 | 1 dir-rmdir | ||
| 7 | 3 dir-rm-tree | ||
| 8 | |||
| 9 | 5 dir-vine | ||
| 10 | |||
| 11 | - Test file growth. | ||
| 12 | 1 grow-create | ||
| 13 | 1 grow-seq-sm | ||
| 14 | 3 grow-seq-lg | ||
| 15 | 3 grow-sparse | ||
| 16 | 3 grow-two-files | ||
| 17 | 1 grow-tell | ||
| 18 | 1 grow-file-size | ||
| 19 | |||
| 20 | - Test directory growth. | ||
| 21 | 1 grow-dir-lg | ||
| 22 | 1 grow-root-sm | ||
| 23 | 1 grow-root-lg | ||
| 24 | |||
| 25 | - Test writing from multiple processes. | ||
| 26 | 5 syn-rw | ||
diff --git a/pintos-progos/tests/filesys/extended/Rubric.persistence b/pintos-progos/tests/filesys/extended/Rubric.persistence new file mode 100644 index 0000000..405620a --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Rubric.persistence | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | Persistence of file system: | ||
| 2 | 1 dir-empty-name-persistence | ||
| 3 | 1 dir-mk-tree-persistence | ||
| 4 | 1 dir-mkdir-persistence | ||
| 5 | 1 dir-open-persistence | ||
| 6 | 1 dir-over-file-persistence | ||
| 7 | 1 dir-rm-cwd-persistence | ||
| 8 | 1 dir-rm-parent-persistence | ||
| 9 | 1 dir-rm-root-persistence | ||
| 10 | 1 dir-rm-tree-persistence | ||
| 11 | 1 dir-rmdir-persistence | ||
| 12 | 1 dir-under-file-persistence | ||
| 13 | 1 dir-vine-persistence | ||
| 14 | 1 grow-create-persistence | ||
| 15 | 1 grow-dir-lg-persistence | ||
| 16 | 1 grow-file-size-persistence | ||
| 17 | 1 grow-root-lg-persistence | ||
| 18 | 1 grow-root-sm-persistence | ||
| 19 | 1 grow-seq-lg-persistence | ||
| 20 | 1 grow-seq-sm-persistence | ||
| 21 | 1 grow-sparse-persistence | ||
| 22 | 1 grow-tell-persistence | ||
| 23 | 1 grow-two-files-persistence | ||
| 24 | 1 syn-rw-persistence | ||
diff --git a/pintos-progos/tests/filesys/extended/Rubric.robustness b/pintos-progos/tests/filesys/extended/Rubric.robustness new file mode 100644 index 0000000..fb9f32f --- /dev/null +++ b/pintos-progos/tests/filesys/extended/Rubric.robustness | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | Robustness of file system: | ||
| 2 | 1 dir-empty-name | ||
| 3 | 1 dir-open | ||
| 4 | 1 dir-over-file | ||
| 5 | 1 dir-under-file | ||
| 6 | |||
| 7 | 3 dir-rm-cwd | ||
| 8 | 2 dir-rm-parent | ||
| 9 | 1 dir-rm-root | ||
diff --git a/pintos-progos/tests/filesys/extended/child-syn-rw.c b/pintos-progos/tests/filesys/extended/child-syn-rw.c new file mode 100644 index 0000000..0e2217d --- /dev/null +++ b/pintos-progos/tests/filesys/extended/child-syn-rw.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* Child process for syn-rw. | ||
| 2 | Reads from a file created by our parent process, which is | ||
| 3 | growing it. We loop until we've read the whole file | ||
| 4 | successfully. Many iterations through the loop will return 0 | ||
| 5 | bytes, because the file has not grown in the meantime. That | ||
| 6 | is, we are "busy waiting" for the file to grow. | ||
| 7 | (This test could be improved by adding a "yield" system call | ||
| 8 | and calling yield whenever we receive a 0-byte read.) */ | ||
| 9 | |||
| 10 | #include <random.h> | ||
| 11 | #include <stdlib.h> | ||
| 12 | #include <syscall.h> | ||
| 13 | #include "tests/filesys/extended/syn-rw.h" | ||
| 14 | #include "tests/lib.h" | ||
| 15 | |||
| 16 | const char *test_name = "child-syn-rw"; | ||
| 17 | |||
| 18 | static char buf1[BUF_SIZE]; | ||
| 19 | static char buf2[BUF_SIZE]; | ||
| 20 | |||
| 21 | int | ||
| 22 | main (int argc, const char *argv[]) | ||
| 23 | { | ||
| 24 | int child_idx; | ||
| 25 | int fd; | ||
| 26 | size_t ofs; | ||
| 27 | |||
| 28 | quiet = true; | ||
| 29 | |||
| 30 | CHECK (argc == 2, "argc must be 2, actually %d", argc); | ||
| 31 | child_idx = atoi (argv[1]); | ||
| 32 | |||
| 33 | random_init (0); | ||
| 34 | random_bytes (buf1, sizeof buf1); | ||
| 35 | |||
| 36 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 37 | ofs = 0; | ||
| 38 | while (ofs < sizeof buf2) | ||
| 39 | { | ||
| 40 | int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs); | ||
| 41 | CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs), | ||
| 42 | "%zu-byte read on \"%s\" returned invalid value of %d", | ||
| 43 | sizeof buf2 - ofs, file_name, bytes_read); | ||
| 44 | if (bytes_read > 0) | ||
| 45 | { | ||
| 46 | compare_bytes (buf2 + ofs, buf1 + ofs, bytes_read, ofs, file_name); | ||
| 47 | ofs += bytes_read; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | close (fd); | ||
| 51 | |||
| 52 | return child_idx; | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck new file mode 100644 index 0000000..562c451 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.c b/pintos-progos/tests/filesys/extended/dir-empty-name.c new file mode 100644 index 0000000..c4859d2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-empty-name.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Tries to create a directory named as the empty string, | ||
| 2 | which must return failure. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (!mkdir (""), "mkdir \"\" (must return false)"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.ck b/pintos-progos/tests/filesys/extended/dir-empty-name.ck new file mode 100644 index 0000000..d6c5621 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-empty-name.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-empty-name) begin | ||
| 7 | (dir-empty-name) mkdir "" (must return false) | ||
| 8 | (dir-empty-name) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck new file mode 100644 index 0000000..fb16afd --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | my ($tree); | ||
| 6 | for my $a (0...3) { | ||
| 7 | for my $b (0...2) { | ||
| 8 | for my $c (0...2) { | ||
| 9 | for my $d (0...3) { | ||
| 10 | $tree->{$a}{$b}{$c}{$d} = ['']; | ||
| 11 | } | ||
| 12 | } | ||
| 13 | } | ||
| 14 | } | ||
| 15 | check_archive ($tree); | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.c b/pintos-progos/tests/filesys/extended/dir-mk-tree.c new file mode 100644 index 0000000..a714ff3 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Creates directories /0/0/0 through /3/2/2 and creates files in | ||
| 2 | the leaf directories. */ | ||
| 3 | |||
| 4 | #include "tests/filesys/extended/mk-tree.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | make_tree (4, 3, 3, 4); | ||
| 11 | } | ||
| 12 | |||
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck new file mode 100644 index 0000000..a8507e2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-mk-tree) begin | ||
| 7 | (dir-mk-tree) creating /0/0/0/0 through /3/2/2/3... | ||
| 8 | (dir-mk-tree) open "/0/2/0/3" | ||
| 9 | (dir-mk-tree) close "/0/2/0/3" | ||
| 10 | (dir-mk-tree) end | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck new file mode 100644 index 0000000..7682900 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({'a' => {'b' => ["\0" x 512]}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.c b/pintos-progos/tests/filesys/extended/dir-mkdir.c new file mode 100644 index 0000000..994f41c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mkdir.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tests mkdir(). */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 11 | CHECK (create ("a/b", 512), "create \"a/b\""); | ||
| 12 | CHECK (chdir ("a"), "chdir \"a\""); | ||
| 13 | CHECK (open ("b") > 1, "open \"b\""); | ||
| 14 | } | ||
| 15 | |||
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.ck b/pintos-progos/tests/filesys/extended/dir-mkdir.ck new file mode 100644 index 0000000..4644f80 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-mkdir.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-mkdir) begin | ||
| 7 | (dir-mkdir) mkdir "a" | ||
| 8 | (dir-mkdir) create "a/b" | ||
| 9 | (dir-mkdir) chdir "a" | ||
| 10 | (dir-mkdir) open "b" | ||
| 11 | (dir-mkdir) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-open-persistence.ck b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck new file mode 100644 index 0000000..26ff2f1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"xyzzy" => {}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-open.c b/pintos-progos/tests/filesys/extended/dir-open.c new file mode 100644 index 0000000..29d18b8 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-open.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Opens a directory, then tries to write to it, which must | ||
| 2 | fail. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int fd; | ||
| 12 | int retval; | ||
| 13 | |||
| 14 | CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\""); | ||
| 15 | CHECK ((fd = open ("xyzzy")) > 1, "open \"xyzzy\""); | ||
| 16 | |||
| 17 | msg ("write \"xyzzy\""); | ||
| 18 | retval = write (fd, "foobar", 6); | ||
| 19 | CHECK (retval == -1, | ||
| 20 | "write \"xyzzy\" (must return -1, actually %d)", retval); | ||
| 21 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-open.ck b/pintos-progos/tests/filesys/extended/dir-open.ck new file mode 100644 index 0000000..fccc563 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-open.ck | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (dir-open) begin | ||
| 7 | (dir-open) mkdir "xyzzy" | ||
| 8 | (dir-open) open "xyzzy" | ||
| 9 | (dir-open) write "xyzzy" | ||
| 10 | (dir-open) write "xyzzy" (must return -1, actually -1) | ||
| 11 | (dir-open) end | ||
| 12 | dir-open: exit(0) | ||
| 13 | EOF | ||
| 14 | (dir-open) begin | ||
| 15 | (dir-open) mkdir "xyzzy" | ||
| 16 | (dir-open) open "xyzzy" | ||
| 17 | (dir-open) write "xyzzy" | ||
| 18 | dir-open: exit(-1) | ||
| 19 | EOF | ||
| 20 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck new file mode 100644 index 0000000..56b4ed2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"abc" => {}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.c b/pintos-progos/tests/filesys/extended/dir-over-file.c new file mode 100644 index 0000000..cdd2c62 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-over-file.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to create a file with the same name as an existing | ||
| 2 | directory, which must return failure. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (mkdir ("abc"), "mkdir \"abc\""); | ||
| 12 | CHECK (!create ("abc", 0), "create \"abc\" (must return false)"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.ck b/pintos-progos/tests/filesys/extended/dir-over-file.ck new file mode 100644 index 0000000..aae1c1e --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-over-file.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-over-file) begin | ||
| 7 | (dir-over-file) mkdir "abc" | ||
| 8 | (dir-over-file) create "abc" (must return false) | ||
| 9 | (dir-over-file) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck new file mode 100644 index 0000000..7533570 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | my ($cwd_removable) = read_text_file ("tests/filesys/extended/can-rmdir-cwd"); | ||
| 6 | $cwd_removable eq 'YES' || $cwd_removable eq 'NO' or die; | ||
| 7 | check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}}); | ||
| 8 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.c b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c new file mode 100644 index 0000000..78e13de --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | /* Tries to remove the current directory, which may succeed or | ||
| 2 | fail. The requirements in each case are different; refer to | ||
| 3 | the assignment for details. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static int | ||
| 10 | wrap_open (const char *name) | ||
| 11 | { | ||
| 12 | static int fds[8], fd_cnt; | ||
| 13 | int fd, i; | ||
| 14 | |||
| 15 | CHECK ((fd = open (name)) > 1, "open \"%s\"", name); | ||
| 16 | for (i = 0; i < fd_cnt; i++) | ||
| 17 | if (fds[i] == fd) | ||
| 18 | fail ("fd returned is not unique"); | ||
| 19 | fds[fd_cnt++] = fd; | ||
| 20 | return fd; | ||
| 21 | } | ||
| 22 | |||
| 23 | void | ||
| 24 | test_main (void) | ||
| 25 | { | ||
| 26 | int root_fd, a_fd0; | ||
| 27 | char name[READDIR_MAX_LEN + 1]; | ||
| 28 | |||
| 29 | root_fd = wrap_open ("/"); | ||
| 30 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 31 | |||
| 32 | a_fd0 = wrap_open ("/a"); | ||
| 33 | CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty"); | ||
| 34 | CHECK (inumber (root_fd) != inumber (a_fd0), | ||
| 35 | "\"/\" and \"/a\" must have different inumbers"); | ||
| 36 | |||
| 37 | CHECK (chdir ("a"), "chdir \"a\""); | ||
| 38 | |||
| 39 | msg ("try to remove \"/a\""); | ||
| 40 | if (remove ("/a")) | ||
| 41 | { | ||
| 42 | msg ("remove successful"); | ||
| 43 | |||
| 44 | CHECK (open ("/a") == -1, "open \"/a\" (must fail)"); | ||
| 45 | CHECK (open (".") == -1, "open \".\" (must fail)"); | ||
| 46 | CHECK (open ("..") == -1, "open \"..\" (must fail)"); | ||
| 47 | CHECK (!create ("x", 512), "create \"x\" (must fail)"); | ||
| 48 | } | ||
| 49 | else | ||
| 50 | { | ||
| 51 | int a_fd1, a_fd2, a_fd3; | ||
| 52 | |||
| 53 | msg ("remove failed"); | ||
| 54 | |||
| 55 | CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)"); | ||
| 56 | CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)"); | ||
| 57 | CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)"); | ||
| 58 | |||
| 59 | a_fd1 = wrap_open ("/a"); | ||
| 60 | a_fd2 = wrap_open ("."); | ||
| 61 | CHECK (inumber (a_fd1) == inumber (a_fd2), | ||
| 62 | "\"/a\" and \".\" must have same inumber"); | ||
| 63 | CHECK (inumber (root_fd) != inumber (a_fd1), | ||
| 64 | "\"/\" and \"/a\" must have different inumbers"); | ||
| 65 | |||
| 66 | CHECK (chdir ("/a"), "chdir \"/a\""); | ||
| 67 | a_fd3 = wrap_open ("."); | ||
| 68 | CHECK (inumber (a_fd3) == inumber (a_fd1), | ||
| 69 | "\".\" must have same inumber as before"); | ||
| 70 | |||
| 71 | CHECK (chdir ("/"), "chdir \"/\""); | ||
| 72 | CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)"); | ||
| 73 | } | ||
| 74 | CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty"); | ||
| 75 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck new file mode 100644 index 0000000..6fa4739 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1, | ||
| 6 | {NO => <<'EOF', YES => <<'EOF'}); | ||
| 7 | (dir-rm-cwd) begin | ||
| 8 | (dir-rm-cwd) open "/" | ||
| 9 | (dir-rm-cwd) mkdir "a" | ||
| 10 | (dir-rm-cwd) open "/a" | ||
| 11 | (dir-rm-cwd) verify "/a" is empty | ||
| 12 | (dir-rm-cwd) "/" and "/a" must have different inumbers | ||
| 13 | (dir-rm-cwd) chdir "a" | ||
| 14 | (dir-rm-cwd) try to remove "/a" | ||
| 15 | (dir-rm-cwd) remove failed | ||
| 16 | (dir-rm-cwd) try to remove "../a" (must fail) | ||
| 17 | (dir-rm-cwd) try to remove ".././a" (must fail) | ||
| 18 | (dir-rm-cwd) try to remove "/./a" (must fail) | ||
| 19 | (dir-rm-cwd) open "/a" | ||
| 20 | (dir-rm-cwd) open "." | ||
| 21 | (dir-rm-cwd) "/a" and "." must have same inumber | ||
| 22 | (dir-rm-cwd) "/" and "/a" must have different inumbers | ||
| 23 | (dir-rm-cwd) chdir "/a" | ||
| 24 | (dir-rm-cwd) open "." | ||
| 25 | (dir-rm-cwd) "." must have same inumber as before | ||
| 26 | (dir-rm-cwd) chdir "/" | ||
| 27 | (dir-rm-cwd) try to remove "a" (must fail: still open) | ||
| 28 | (dir-rm-cwd) verify "/a" is empty | ||
| 29 | (dir-rm-cwd) end | ||
| 30 | EOF | ||
| 31 | (dir-rm-cwd) begin | ||
| 32 | (dir-rm-cwd) open "/" | ||
| 33 | (dir-rm-cwd) mkdir "a" | ||
| 34 | (dir-rm-cwd) open "/a" | ||
| 35 | (dir-rm-cwd) verify "/a" is empty | ||
| 36 | (dir-rm-cwd) "/" and "/a" must have different inumbers | ||
| 37 | (dir-rm-cwd) chdir "a" | ||
| 38 | (dir-rm-cwd) try to remove "/a" | ||
| 39 | (dir-rm-cwd) remove successful | ||
| 40 | (dir-rm-cwd) open "/a" (must fail) | ||
| 41 | (dir-rm-cwd) open "." (must fail) | ||
| 42 | (dir-rm-cwd) open ".." (must fail) | ||
| 43 | (dir-rm-cwd) create "x" (must fail) | ||
| 44 | (dir-rm-cwd) verify "/a" is empty | ||
| 45 | (dir-rm-cwd) end | ||
| 46 | EOF | ||
| 47 | open (CAN_RMDIR_CWD, ">tests/filesys/extended/can-rmdir-cwd") | ||
| 48 | or die "tests/filesys/extended/can-rmdir-cwd: create: $!\n"; | ||
| 49 | print CAN_RMDIR_CWD "$cwd_removable"; | ||
| 50 | close (CAN_RMDIR_CWD); | ||
| 51 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck new file mode 100644 index 0000000..f30b04a --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"a" => {"b" => {}}}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.c b/pintos-progos/tests/filesys/extended/dir-rm-parent.c new file mode 100644 index 0000000..eb43f5b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Tries to remove a parent of the current directory. This must | ||
| 2 | fail, because that directory is non-empty. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 12 | CHECK (chdir ("a"), "chdir \"a\""); | ||
| 13 | CHECK (mkdir ("b"), "mkdir \"b\""); | ||
| 14 | CHECK (chdir ("b"), "chdir \"b\""); | ||
| 15 | CHECK (!remove ("/a"), "remove \"/a\" (must fail)"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck new file mode 100644 index 0000000..9fea8f2 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rm-parent) begin | ||
| 7 | (dir-rm-parent) mkdir "a" | ||
| 8 | (dir-rm-parent) chdir "a" | ||
| 9 | (dir-rm-parent) mkdir "b" | ||
| 10 | (dir-rm-parent) chdir "b" | ||
| 11 | (dir-rm-parent) remove "/a" (must fail) | ||
| 12 | (dir-rm-parent) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck new file mode 100644 index 0000000..6315107 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"a" => ["\0" x 243]}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.c b/pintos-progos/tests/filesys/extended/dir-rm-root.c new file mode 100644 index 0000000..c47f1eb --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-root.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Try to remove the root directory. | ||
| 2 | This must fail. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (!remove ("/"), "remove \"/\" (must fail)"); | ||
| 12 | CHECK (create ("/a", 243), "create \"/a\""); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.ck b/pintos-progos/tests/filesys/extended/dir-rm-root.ck new file mode 100644 index 0000000..8a69ff3 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-root.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rm-root) begin | ||
| 7 | (dir-rm-root) remove "/" (must fail) | ||
| 8 | (dir-rm-root) create "/a" | ||
| 9 | (dir-rm-root) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck new file mode 100644 index 0000000..562c451 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.c b/pintos-progos/tests/filesys/extended/dir-rm-tree.c new file mode 100644 index 0000000..bab41a6 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.c | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | /* Creates directories /0/0/0 through /3/2/2 and files in the | ||
| 2 | leaf directories, then removes them. */ | ||
| 3 | |||
| 4 | #include <stdarg.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/filesys/extended/mk-tree.h" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | static void remove_tree (int at, int bt, int ct, int dt); | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | make_tree (4, 3, 3, 4); | ||
| 17 | remove_tree (4, 3, 3, 4); | ||
| 18 | } | ||
| 19 | |||
| 20 | static void do_remove (const char *format, ...) PRINTF_FORMAT (1, 2); | ||
| 21 | |||
| 22 | static void | ||
| 23 | remove_tree (int at, int bt, int ct, int dt) | ||
| 24 | { | ||
| 25 | char try[128]; | ||
| 26 | int a, b, c, d; | ||
| 27 | |||
| 28 | msg ("removing /0/0/0/0 through /%d/%d/%d/%d...", | ||
| 29 | at - 1, bt - 1, ct - 1, dt - 1); | ||
| 30 | quiet = true; | ||
| 31 | for (a = 0; a < at; a++) | ||
| 32 | { | ||
| 33 | for (b = 0; b < bt; b++) | ||
| 34 | { | ||
| 35 | for (c = 0; c < ct; c++) | ||
| 36 | { | ||
| 37 | for (d = 0; d < dt; d++) | ||
| 38 | do_remove ("/%d/%d/%d/%d", a, b, c, d); | ||
| 39 | do_remove ("/%d/%d/%d", a, b, c); | ||
| 40 | } | ||
| 41 | do_remove ("/%d/%d", a, b); | ||
| 42 | } | ||
| 43 | do_remove ("/%d", a); | ||
| 44 | } | ||
| 45 | quiet = false; | ||
| 46 | |||
| 47 | snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0); | ||
| 48 | CHECK (open (try) == -1, "open \"%s\" (must return -1)", try); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void | ||
| 52 | do_remove (const char *format, ...) | ||
| 53 | { | ||
| 54 | char name[128]; | ||
| 55 | va_list args; | ||
| 56 | |||
| 57 | va_start (args, format); | ||
| 58 | vsnprintf (name, sizeof name, format, args); | ||
| 59 | va_end (args); | ||
| 60 | |||
| 61 | CHECK (remove (name), "remove \"%s\"", name); | ||
| 62 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck new file mode 100644 index 0000000..587b493 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rm-tree) begin | ||
| 7 | (dir-rm-tree) creating /0/0/0/0 through /3/2/2/3... | ||
| 8 | (dir-rm-tree) open "/0/2/0/3" | ||
| 9 | (dir-rm-tree) close "/0/2/0/3" | ||
| 10 | (dir-rm-tree) removing /0/0/0/0 through /3/2/2/3... | ||
| 11 | (dir-rm-tree) open "/3/0/2/0" (must return -1) | ||
| 12 | (dir-rm-tree) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck new file mode 100644 index 0000000..562c451 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.c b/pintos-progos/tests/filesys/extended/dir-rmdir.c new file mode 100644 index 0000000..b3cbc6f --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rmdir.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Creates and removes a directory, then makes sure that it's | ||
| 2 | really gone. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (mkdir ("a"), "mkdir \"a\""); | ||
| 12 | CHECK (remove ("a"), "rmdir \"a\""); | ||
| 13 | CHECK (!chdir ("a"), "chdir \"a\" (must return false)"); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.ck b/pintos-progos/tests/filesys/extended/dir-rmdir.ck new file mode 100644 index 0000000..e0d8922 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-rmdir.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-rmdir) begin | ||
| 7 | (dir-rmdir) mkdir "a" | ||
| 8 | (dir-rmdir) rmdir "a" | ||
| 9 | (dir-rmdir) chdir "a" (must return false) | ||
| 10 | (dir-rmdir) end | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck new file mode 100644 index 0000000..67ca528 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"abc" => ['']}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.c b/pintos-progos/tests/filesys/extended/dir-under-file.c new file mode 100644 index 0000000..973a8b1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-under-file.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to create a directory with the same name as an existing | ||
| 2 | file, which must return failure. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (create ("abc", 0), "create \"abc\""); | ||
| 12 | CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.ck b/pintos-progos/tests/filesys/extended/dir-under-file.ck new file mode 100644 index 0000000..cce23b4 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-under-file.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-under-file) begin | ||
| 7 | (dir-under-file) create "abc" | ||
| 8 | (dir-under-file) mkdir "abc" (must return false) | ||
| 9 | (dir-under-file) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck new file mode 100644 index 0000000..698ef01 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | # The archive should look like this: | ||
| 6 | # | ||
| 7 | # 40642 dir-vine | ||
| 8 | # 42479 tar | ||
| 9 | # 0 start | ||
| 10 | # 11 start/file0 | ||
| 11 | # 0 start/dir0 | ||
| 12 | # 11 start/dir0/file1 | ||
| 13 | # 0 start/dir0/dir1 | ||
| 14 | # 11 start/dir0/dir1/file2 | ||
| 15 | # 0 start/dir0/dir1/dir2 | ||
| 16 | # 11 start/dir0/dir1/dir2/file3 | ||
| 17 | # 0 start/dir0/dir1/dir2/dir3 | ||
| 18 | # 11 start/dir0/dir1/dir2/dir3/file4 | ||
| 19 | # 0 start/dir0/dir1/dir2/dir3/dir4 | ||
| 20 | # 11 start/dir0/dir1/dir2/dir3/dir4/file5 | ||
| 21 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5 | ||
| 22 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/file6 | ||
| 23 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6 | ||
| 24 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7 | ||
| 25 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7 | ||
| 26 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8 | ||
| 27 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8 | ||
| 28 | # 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9 | ||
| 29 | # 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9 | ||
| 30 | my ($dir) = {}; | ||
| 31 | my ($root) = {"start" => $dir}; | ||
| 32 | for (my ($i) = 0; $i < 10; $i++) { | ||
| 33 | $dir->{"file$i"} = ["contents $i\n"]; | ||
| 34 | $dir = $dir->{"dir$i"} = {}; | ||
| 35 | } | ||
| 36 | check_archive ($root); | ||
| 37 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.c b/pintos-progos/tests/filesys/extended/dir-vine.c new file mode 100644 index 0000000..8a31c38 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-vine.c | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | /* Create a very deep "vine" of directories: /dir0/dir1/dir2/... | ||
| 2 | and an ordinary file in each of them, until we fill up the | ||
| 3 | disk. | ||
| 4 | |||
| 5 | Then delete most of them, for two reasons. First, "tar" | ||
| 6 | limits file names to 100 characters (which could be extended | ||
| 7 | to 256 without much trouble). Second, a full disk has no room | ||
| 8 | for the tar archive. */ | ||
| 9 | |||
| 10 | #include <string.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <syscall.h> | ||
| 13 | #include "tests/lib.h" | ||
| 14 | #include "tests/main.h" | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | int i; | ||
| 20 | |||
| 21 | msg ("creating many levels of files and directories..."); | ||
| 22 | quiet = true; | ||
| 23 | CHECK (mkdir ("start"), "mkdir \"start\""); | ||
| 24 | CHECK (chdir ("start"), "chdir \"start\""); | ||
| 25 | for (i = 0; ; i++) | ||
| 26 | { | ||
| 27 | char name[3][READDIR_MAX_LEN + 1]; | ||
| 28 | char file_name[16], dir_name[16]; | ||
| 29 | char contents[128]; | ||
| 30 | int fd; | ||
| 31 | |||
| 32 | /* Create file. */ | ||
| 33 | snprintf (file_name, sizeof file_name, "file%d", i); | ||
| 34 | if (!create (file_name, 0)) | ||
| 35 | break; | ||
| 36 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 37 | snprintf (contents, sizeof contents, "contents %d\n", i); | ||
| 38 | if (write (fd, contents, strlen (contents)) != (int) strlen (contents)) | ||
| 39 | { | ||
| 40 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 41 | close (fd); | ||
| 42 | break; | ||
| 43 | } | ||
| 44 | close (fd); | ||
| 45 | |||
| 46 | /* Create directory. */ | ||
| 47 | snprintf (dir_name, sizeof dir_name, "dir%d", i); | ||
| 48 | if (!mkdir (dir_name)) | ||
| 49 | { | ||
| 50 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 51 | break; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Check for file and directory. */ | ||
| 55 | CHECK ((fd = open (".")) > 1, "open \".\""); | ||
| 56 | CHECK (readdir (fd, name[0]), "readdir \".\""); | ||
| 57 | CHECK (readdir (fd, name[1]), "readdir \".\""); | ||
| 58 | CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)"); | ||
| 59 | CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name)) | ||
| 60 | || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)), | ||
| 61 | "names should be \"%s\" and \"%s\", " | ||
| 62 | "actually \"%s\" and \"%s\"", | ||
| 63 | file_name, dir_name, name[0], name[1]); | ||
| 64 | close (fd); | ||
| 65 | |||
| 66 | /* Descend into directory. */ | ||
| 67 | CHECK (chdir (dir_name), "chdir \"%s\"", dir_name); | ||
| 68 | } | ||
| 69 | CHECK (i > 200, "created files and directories only to level %d", i); | ||
| 70 | quiet = false; | ||
| 71 | |||
| 72 | msg ("removing all but top 10 levels of files and directories..."); | ||
| 73 | quiet = true; | ||
| 74 | while (i-- > 10) | ||
| 75 | { | ||
| 76 | char file_name[16], dir_name[16]; | ||
| 77 | |||
| 78 | snprintf (file_name, sizeof file_name, "file%d", i); | ||
| 79 | snprintf (dir_name, sizeof dir_name, "dir%d", i); | ||
| 80 | CHECK (chdir (".."), "chdir \"..\""); | ||
| 81 | CHECK (remove (dir_name), "remove \"%s\"", dir_name); | ||
| 82 | CHECK (remove (file_name), "remove \"%s\"", file_name); | ||
| 83 | } | ||
| 84 | quiet = false; | ||
| 85 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.ck b/pintos-progos/tests/filesys/extended/dir-vine.ck new file mode 100644 index 0000000..db452b0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/dir-vine.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (dir-vine) begin | ||
| 7 | (dir-vine) creating many levels of files and directories... | ||
| 8 | (dir-vine) removing all but top 10 levels of files and directories... | ||
| 9 | (dir-vine) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-create-persistence.ck b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck new file mode 100644 index 0000000..bbcb24f --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"blargle" => ['']}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-create.c b/pintos-progos/tests/filesys/extended/grow-create.c new file mode 100644 index 0000000..9ccc4ea --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-create.c | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | /* Create a file of size 0. */ | ||
| 2 | |||
| 3 | #define TEST_SIZE 0 | ||
| 4 | #include "tests/filesys/create.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-create.ck b/pintos-progos/tests/filesys/extended/grow-create.ck new file mode 100644 index 0000000..b2e69d1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-create.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (grow-create) begin | ||
| 7 | (grow-create) create "blargle" | ||
| 8 | (grow-create) open "blargle" for verification | ||
| 9 | (grow-create) verified contents of "blargle" | ||
| 10 | (grow-create) close "blargle" | ||
| 11 | (grow-create) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck new file mode 100644 index 0000000..989a322 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($fs); | ||
| 7 | $fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49; | ||
| 8 | check_archive ($fs); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.c b/pintos-progos/tests/filesys/extended/grow-dir-lg.c new file mode 100644 index 0000000..20a194b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.c | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | /* Creates a directory, | ||
| 2 | then creates 50 files in that directory. */ | ||
| 3 | |||
| 4 | #define FILE_CNT 50 | ||
| 5 | #define DIRECTORY "/x" | ||
| 6 | #include "tests/filesys/extended/grow-dir.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck new file mode 100644 index 0000000..ec58bd3 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck | |||
| @@ -0,0 +1,61 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-dir-lg) begin | ||
| 8 | (grow-dir-lg) mkdir /x | ||
| 9 | (grow-dir-lg) creating and checking "/x/file0" | ||
| 10 | (grow-dir-lg) creating and checking "/x/file1" | ||
| 11 | (grow-dir-lg) creating and checking "/x/file2" | ||
| 12 | (grow-dir-lg) creating and checking "/x/file3" | ||
| 13 | (grow-dir-lg) creating and checking "/x/file4" | ||
| 14 | (grow-dir-lg) creating and checking "/x/file5" | ||
| 15 | (grow-dir-lg) creating and checking "/x/file6" | ||
| 16 | (grow-dir-lg) creating and checking "/x/file7" | ||
| 17 | (grow-dir-lg) creating and checking "/x/file8" | ||
| 18 | (grow-dir-lg) creating and checking "/x/file9" | ||
| 19 | (grow-dir-lg) creating and checking "/x/file10" | ||
| 20 | (grow-dir-lg) creating and checking "/x/file11" | ||
| 21 | (grow-dir-lg) creating and checking "/x/file12" | ||
| 22 | (grow-dir-lg) creating and checking "/x/file13" | ||
| 23 | (grow-dir-lg) creating and checking "/x/file14" | ||
| 24 | (grow-dir-lg) creating and checking "/x/file15" | ||
| 25 | (grow-dir-lg) creating and checking "/x/file16" | ||
| 26 | (grow-dir-lg) creating and checking "/x/file17" | ||
| 27 | (grow-dir-lg) creating and checking "/x/file18" | ||
| 28 | (grow-dir-lg) creating and checking "/x/file19" | ||
| 29 | (grow-dir-lg) creating and checking "/x/file20" | ||
| 30 | (grow-dir-lg) creating and checking "/x/file21" | ||
| 31 | (grow-dir-lg) creating and checking "/x/file22" | ||
| 32 | (grow-dir-lg) creating and checking "/x/file23" | ||
| 33 | (grow-dir-lg) creating and checking "/x/file24" | ||
| 34 | (grow-dir-lg) creating and checking "/x/file25" | ||
| 35 | (grow-dir-lg) creating and checking "/x/file26" | ||
| 36 | (grow-dir-lg) creating and checking "/x/file27" | ||
| 37 | (grow-dir-lg) creating and checking "/x/file28" | ||
| 38 | (grow-dir-lg) creating and checking "/x/file29" | ||
| 39 | (grow-dir-lg) creating and checking "/x/file30" | ||
| 40 | (grow-dir-lg) creating and checking "/x/file31" | ||
| 41 | (grow-dir-lg) creating and checking "/x/file32" | ||
| 42 | (grow-dir-lg) creating and checking "/x/file33" | ||
| 43 | (grow-dir-lg) creating and checking "/x/file34" | ||
| 44 | (grow-dir-lg) creating and checking "/x/file35" | ||
| 45 | (grow-dir-lg) creating and checking "/x/file36" | ||
| 46 | (grow-dir-lg) creating and checking "/x/file37" | ||
| 47 | (grow-dir-lg) creating and checking "/x/file38" | ||
| 48 | (grow-dir-lg) creating and checking "/x/file39" | ||
| 49 | (grow-dir-lg) creating and checking "/x/file40" | ||
| 50 | (grow-dir-lg) creating and checking "/x/file41" | ||
| 51 | (grow-dir-lg) creating and checking "/x/file42" | ||
| 52 | (grow-dir-lg) creating and checking "/x/file43" | ||
| 53 | (grow-dir-lg) creating and checking "/x/file44" | ||
| 54 | (grow-dir-lg) creating and checking "/x/file45" | ||
| 55 | (grow-dir-lg) creating and checking "/x/file46" | ||
| 56 | (grow-dir-lg) creating and checking "/x/file47" | ||
| 57 | (grow-dir-lg) creating and checking "/x/file48" | ||
| 58 | (grow-dir-lg) creating and checking "/x/file49" | ||
| 59 | (grow-dir-lg) end | ||
| 60 | EOF | ||
| 61 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-dir.inc b/pintos-progos/tests/filesys/extended/grow-dir.inc new file mode 100644 index 0000000..bee0ba0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-dir.inc | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/filesys/seq-test.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static char buf[512]; | ||
| 10 | |||
| 11 | static size_t | ||
| 12 | return_block_size (void) | ||
| 13 | { | ||
| 14 | return sizeof buf; | ||
| 15 | } | ||
| 16 | |||
| 17 | void | ||
| 18 | test_main (void) | ||
| 19 | { | ||
| 20 | size_t i; | ||
| 21 | |||
| 22 | #ifdef DIRECTORY | ||
| 23 | CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY); | ||
| 24 | #define DIR_PREFIX DIRECTORY "/" | ||
| 25 | #else | ||
| 26 | #define DIR_PREFIX "" | ||
| 27 | #endif | ||
| 28 | for (i = 0; i < FILE_CNT; i++) | ||
| 29 | { | ||
| 30 | char file_name[128]; | ||
| 31 | snprintf (file_name, sizeof file_name, "%sfile%zu", DIR_PREFIX, i); | ||
| 32 | |||
| 33 | msg ("creating and checking \"%s\"", file_name); | ||
| 34 | |||
| 35 | quiet = true; | ||
| 36 | seq_test (file_name, | ||
| 37 | buf, sizeof buf, sizeof buf, | ||
| 38 | return_block_size, NULL); | ||
| 39 | quiet = false; | ||
| 40 | } | ||
| 41 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck new file mode 100644 index 0000000..150f383 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"testfile" => [random_bytes (2134)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.c b/pintos-progos/tests/filesys/extended/grow-file-size.c new file mode 100644 index 0000000..3ce8588 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-file-size.c | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* Grows a file from 0 bytes to 2,134 bytes, 37 bytes at a time, | ||
| 2 | and checks that the file's size is reported correctly at each | ||
| 3 | step. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/filesys/seq-test.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | static char buf[2134]; | ||
| 11 | |||
| 12 | static size_t | ||
| 13 | return_block_size (void) | ||
| 14 | { | ||
| 15 | return 37; | ||
| 16 | } | ||
| 17 | |||
| 18 | static void | ||
| 19 | check_file_size (int fd, long ofs) | ||
| 20 | { | ||
| 21 | long size = filesize (fd); | ||
| 22 | if (size != ofs) | ||
| 23 | fail ("filesize not updated properly: should be %ld, actually %ld", | ||
| 24 | ofs, size); | ||
| 25 | } | ||
| 26 | |||
| 27 | void | ||
| 28 | test_main (void) | ||
| 29 | { | ||
| 30 | seq_test ("testfile", | ||
| 31 | buf, sizeof buf, 0, | ||
| 32 | return_block_size, check_file_size); | ||
| 33 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.ck b/pintos-progos/tests/filesys/extended/grow-file-size.ck new file mode 100644 index 0000000..d81feff --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-file-size.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-file-size) begin | ||
| 8 | (grow-file-size) create "testfile" | ||
| 9 | (grow-file-size) open "testfile" | ||
| 10 | (grow-file-size) writing "testfile" | ||
| 11 | (grow-file-size) close "testfile" | ||
| 12 | (grow-file-size) open "testfile" for verification | ||
| 13 | (grow-file-size) verified contents of "testfile" | ||
| 14 | (grow-file-size) close "testfile" | ||
| 15 | (grow-file-size) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck new file mode 100644 index 0000000..1692f46 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($fs); | ||
| 7 | $fs->{"file$_"} = [random_bytes (512)] foreach 0...49; | ||
| 8 | check_archive ($fs); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.c b/pintos-progos/tests/filesys/extended/grow-root-lg.c new file mode 100644 index 0000000..d8d6c09 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-lg.c | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | /* Creates 50 files in the root directory. */ | ||
| 2 | |||
| 3 | #define FILE_CNT 50 | ||
| 4 | #include "tests/filesys/extended/grow-dir.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.ck b/pintos-progos/tests/filesys/extended/grow-root-lg.ck new file mode 100644 index 0000000..b174bc9 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-lg.ck | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-root-lg) begin | ||
| 8 | (grow-root-lg) creating and checking "file0" | ||
| 9 | (grow-root-lg) creating and checking "file1" | ||
| 10 | (grow-root-lg) creating and checking "file2" | ||
| 11 | (grow-root-lg) creating and checking "file3" | ||
| 12 | (grow-root-lg) creating and checking "file4" | ||
| 13 | (grow-root-lg) creating and checking "file5" | ||
| 14 | (grow-root-lg) creating and checking "file6" | ||
| 15 | (grow-root-lg) creating and checking "file7" | ||
| 16 | (grow-root-lg) creating and checking "file8" | ||
| 17 | (grow-root-lg) creating and checking "file9" | ||
| 18 | (grow-root-lg) creating and checking "file10" | ||
| 19 | (grow-root-lg) creating and checking "file11" | ||
| 20 | (grow-root-lg) creating and checking "file12" | ||
| 21 | (grow-root-lg) creating and checking "file13" | ||
| 22 | (grow-root-lg) creating and checking "file14" | ||
| 23 | (grow-root-lg) creating and checking "file15" | ||
| 24 | (grow-root-lg) creating and checking "file16" | ||
| 25 | (grow-root-lg) creating and checking "file17" | ||
| 26 | (grow-root-lg) creating and checking "file18" | ||
| 27 | (grow-root-lg) creating and checking "file19" | ||
| 28 | (grow-root-lg) creating and checking "file20" | ||
| 29 | (grow-root-lg) creating and checking "file21" | ||
| 30 | (grow-root-lg) creating and checking "file22" | ||
| 31 | (grow-root-lg) creating and checking "file23" | ||
| 32 | (grow-root-lg) creating and checking "file24" | ||
| 33 | (grow-root-lg) creating and checking "file25" | ||
| 34 | (grow-root-lg) creating and checking "file26" | ||
| 35 | (grow-root-lg) creating and checking "file27" | ||
| 36 | (grow-root-lg) creating and checking "file28" | ||
| 37 | (grow-root-lg) creating and checking "file29" | ||
| 38 | (grow-root-lg) creating and checking "file30" | ||
| 39 | (grow-root-lg) creating and checking "file31" | ||
| 40 | (grow-root-lg) creating and checking "file32" | ||
| 41 | (grow-root-lg) creating and checking "file33" | ||
| 42 | (grow-root-lg) creating and checking "file34" | ||
| 43 | (grow-root-lg) creating and checking "file35" | ||
| 44 | (grow-root-lg) creating and checking "file36" | ||
| 45 | (grow-root-lg) creating and checking "file37" | ||
| 46 | (grow-root-lg) creating and checking "file38" | ||
| 47 | (grow-root-lg) creating and checking "file39" | ||
| 48 | (grow-root-lg) creating and checking "file40" | ||
| 49 | (grow-root-lg) creating and checking "file41" | ||
| 50 | (grow-root-lg) creating and checking "file42" | ||
| 51 | (grow-root-lg) creating and checking "file43" | ||
| 52 | (grow-root-lg) creating and checking "file44" | ||
| 53 | (grow-root-lg) creating and checking "file45" | ||
| 54 | (grow-root-lg) creating and checking "file46" | ||
| 55 | (grow-root-lg) creating and checking "file47" | ||
| 56 | (grow-root-lg) creating and checking "file48" | ||
| 57 | (grow-root-lg) creating and checking "file49" | ||
| 58 | (grow-root-lg) end | ||
| 59 | EOF | ||
| 60 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck new file mode 100644 index 0000000..2b0b8ab --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($fs); | ||
| 7 | $fs->{"file$_"} = [random_bytes (512)] foreach 0...19; | ||
| 8 | check_archive ($fs); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.c b/pintos-progos/tests/filesys/extended/grow-root-sm.c new file mode 100644 index 0000000..ee375d5 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-sm.c | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | /* Creates 20 files in the root directory. */ | ||
| 2 | |||
| 3 | #define FILE_CNT 20 | ||
| 4 | #include "tests/filesys/extended/grow-dir.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.ck b/pintos-progos/tests/filesys/extended/grow-root-sm.ck new file mode 100644 index 0000000..1aac7e9 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-root-sm.ck | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-root-sm) begin | ||
| 8 | (grow-root-sm) creating and checking "file0" | ||
| 9 | (grow-root-sm) creating and checking "file1" | ||
| 10 | (grow-root-sm) creating and checking "file2" | ||
| 11 | (grow-root-sm) creating and checking "file3" | ||
| 12 | (grow-root-sm) creating and checking "file4" | ||
| 13 | (grow-root-sm) creating and checking "file5" | ||
| 14 | (grow-root-sm) creating and checking "file6" | ||
| 15 | (grow-root-sm) creating and checking "file7" | ||
| 16 | (grow-root-sm) creating and checking "file8" | ||
| 17 | (grow-root-sm) creating and checking "file9" | ||
| 18 | (grow-root-sm) creating and checking "file10" | ||
| 19 | (grow-root-sm) creating and checking "file11" | ||
| 20 | (grow-root-sm) creating and checking "file12" | ||
| 21 | (grow-root-sm) creating and checking "file13" | ||
| 22 | (grow-root-sm) creating and checking "file14" | ||
| 23 | (grow-root-sm) creating and checking "file15" | ||
| 24 | (grow-root-sm) creating and checking "file16" | ||
| 25 | (grow-root-sm) creating and checking "file17" | ||
| 26 | (grow-root-sm) creating and checking "file18" | ||
| 27 | (grow-root-sm) creating and checking "file19" | ||
| 28 | (grow-root-sm) end | ||
| 29 | EOF | ||
| 30 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck new file mode 100644 index 0000000..41aaae0 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"testme" => [random_bytes (72943)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.c b/pintos-progos/tests/filesys/extended/grow-seq-lg.c new file mode 100644 index 0000000..3108d17 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Grows a file from 0 bytes to 72,943 bytes, 1,234 bytes at a | ||
| 2 | time. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 72943 | ||
| 5 | #include "tests/filesys/extended/grow-seq.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck new file mode 100644 index 0000000..90fcd8c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-seq-lg) begin | ||
| 8 | (grow-seq-lg) create "testme" | ||
| 9 | (grow-seq-lg) open "testme" | ||
| 10 | (grow-seq-lg) writing "testme" | ||
| 11 | (grow-seq-lg) close "testme" | ||
| 12 | (grow-seq-lg) open "testme" for verification | ||
| 13 | (grow-seq-lg) verified contents of "testme" | ||
| 14 | (grow-seq-lg) close "testme" | ||
| 15 | (grow-seq-lg) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck new file mode 100644 index 0000000..6cb0bd8 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"testme" => [random_bytes (5678)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.c b/pintos-progos/tests/filesys/extended/grow-seq-sm.c new file mode 100644 index 0000000..3656e2e --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Grows a file from 0 bytes to 5,678 bytes, 1,234 bytes at a | ||
| 2 | time. */ | ||
| 3 | |||
| 4 | #define TEST_SIZE 5678 | ||
| 5 | #include "tests/filesys/extended/grow-seq.inc" | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck new file mode 100644 index 0000000..5cf4518 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-seq-sm) begin | ||
| 8 | (grow-seq-sm) create "testme" | ||
| 9 | (grow-seq-sm) open "testme" | ||
| 10 | (grow-seq-sm) writing "testme" | ||
| 11 | (grow-seq-sm) close "testme" | ||
| 12 | (grow-seq-sm) open "testme" for verification | ||
| 13 | (grow-seq-sm) verified contents of "testme" | ||
| 14 | (grow-seq-sm) close "testme" | ||
| 15 | (grow-seq-sm) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-seq.inc b/pintos-progos/tests/filesys/extended/grow-seq.inc new file mode 100644 index 0000000..1b7710c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-seq.inc | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include "tests/filesys/seq-test.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | static char buf[TEST_SIZE]; | ||
| 7 | |||
| 8 | static size_t | ||
| 9 | return_block_size (void) | ||
| 10 | { | ||
| 11 | return 1234; | ||
| 12 | } | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | seq_test ("testme", | ||
| 18 | buf, sizeof buf, 0, | ||
| 19 | return_block_size, NULL); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck new file mode 100644 index 0000000..3f06a5b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_archive ({"testfile" => ["\0" x 76543]}); | ||
| 6 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.c b/pintos-progos/tests/filesys/extended/grow-sparse.c new file mode 100644 index 0000000..6eab210 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-sparse.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Tests that seeking past the end of a file and writing will | ||
| 2 | properly zero out the region in between. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | static char buf[76543]; | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | const char *file_name = "testfile"; | ||
| 14 | char zero = 0; | ||
| 15 | int fd; | ||
| 16 | |||
| 17 | CHECK (create (file_name, 0), "create \"%s\"", file_name); | ||
| 18 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 19 | msg ("seek \"%s\"", file_name); | ||
| 20 | seek (fd, sizeof buf - 1); | ||
| 21 | CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", file_name); | ||
| 22 | msg ("close \"%s\"", file_name); | ||
| 23 | close (fd); | ||
| 24 | check_file (file_name, buf, sizeof buf); | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.ck b/pintos-progos/tests/filesys/extended/grow-sparse.ck new file mode 100644 index 0000000..379ba2c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-sparse.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (grow-sparse) begin | ||
| 7 | (grow-sparse) create "testfile" | ||
| 8 | (grow-sparse) open "testfile" | ||
| 9 | (grow-sparse) seek "testfile" | ||
| 10 | (grow-sparse) write "testfile" | ||
| 11 | (grow-sparse) close "testfile" | ||
| 12 | (grow-sparse) open "testfile" for verification | ||
| 13 | (grow-sparse) verified contents of "testfile" | ||
| 14 | (grow-sparse) close "testfile" | ||
| 15 | (grow-sparse) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck new file mode 100644 index 0000000..d93a422 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"foobar" => [random_bytes (2134)]}); | ||
| 7 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.c b/pintos-progos/tests/filesys/extended/grow-tell.c new file mode 100644 index 0000000..5f5da5b --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-tell.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Checks that growing a file updates the file position | ||
| 2 | correctly. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/filesys/seq-test.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static char buf[2134]; | ||
| 10 | |||
| 11 | static size_t | ||
| 12 | return_block_size (void) | ||
| 13 | { | ||
| 14 | return 37; | ||
| 15 | } | ||
| 16 | |||
| 17 | static void | ||
| 18 | check_tell (int fd, long ofs) | ||
| 19 | { | ||
| 20 | long pos = tell (fd); | ||
| 21 | if (pos != ofs) | ||
| 22 | fail ("file position not updated properly: should be %ld, actually %ld", | ||
| 23 | ofs, pos); | ||
| 24 | } | ||
| 25 | |||
| 26 | void | ||
| 27 | test_main (void) | ||
| 28 | { | ||
| 29 | seq_test ("foobar", | ||
| 30 | buf, sizeof buf, 0, | ||
| 31 | return_block_size, check_tell); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.ck b/pintos-progos/tests/filesys/extended/grow-tell.ck new file mode 100644 index 0000000..fe94707 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-tell.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-tell) begin | ||
| 8 | (grow-tell) create "foobar" | ||
| 9 | (grow-tell) open "foobar" | ||
| 10 | (grow-tell) writing "foobar" | ||
| 11 | (grow-tell) close "foobar" | ||
| 12 | (grow-tell) open "foobar" for verification | ||
| 13 | (grow-tell) verified contents of "foobar" | ||
| 14 | (grow-tell) close "foobar" | ||
| 15 | (grow-tell) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck new file mode 100644 index 0000000..1c4ced1 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | my ($a) = random_bytes (8143); | ||
| 7 | my ($b) = random_bytes (8143); | ||
| 8 | check_archive ({"a" => [$a], "b" => [$b]}); | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.c b/pintos-progos/tests/filesys/extended/grow-two-files.c new file mode 100644 index 0000000..6a8fb1c --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-two-files.c | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | /* Grows two files in parallel and checks that their contents are | ||
| 2 | correct. */ | ||
| 3 | |||
| 4 | #include <random.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | #define FILE_SIZE 8143 | ||
| 10 | static char buf_a[FILE_SIZE]; | ||
| 11 | static char buf_b[FILE_SIZE]; | ||
| 12 | |||
| 13 | static void | ||
| 14 | write_some_bytes (const char *file_name, int fd, const char *buf, size_t *ofs) | ||
| 15 | { | ||
| 16 | if (*ofs < FILE_SIZE) | ||
| 17 | { | ||
| 18 | size_t block_size = random_ulong () % (FILE_SIZE / 8) + 1; | ||
| 19 | size_t ret_val; | ||
| 20 | if (block_size > FILE_SIZE - *ofs) | ||
| 21 | block_size = FILE_SIZE - *ofs; | ||
| 22 | |||
| 23 | ret_val = write (fd, buf + *ofs, block_size); | ||
| 24 | if (ret_val != block_size) | ||
| 25 | fail ("write %zu bytes at offset %zu in \"%s\" returned %zu", | ||
| 26 | block_size, *ofs, file_name, ret_val); | ||
| 27 | *ofs += block_size; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | void | ||
| 32 | test_main (void) | ||
| 33 | { | ||
| 34 | int fd_a, fd_b; | ||
| 35 | size_t ofs_a = 0, ofs_b = 0; | ||
| 36 | |||
| 37 | random_init (0); | ||
| 38 | random_bytes (buf_a, sizeof buf_a); | ||
| 39 | random_bytes (buf_b, sizeof buf_b); | ||
| 40 | |||
| 41 | CHECK (create ("a", 0), "create \"a\""); | ||
| 42 | CHECK (create ("b", 0), "create \"b\""); | ||
| 43 | |||
| 44 | CHECK ((fd_a = open ("a")) > 1, "open \"a\""); | ||
| 45 | CHECK ((fd_b = open ("b")) > 1, "open \"b\""); | ||
| 46 | |||
| 47 | msg ("write \"a\" and \"b\" alternately"); | ||
| 48 | while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE) | ||
| 49 | { | ||
| 50 | write_some_bytes ("a", fd_a, buf_a, &ofs_a); | ||
| 51 | write_some_bytes ("b", fd_b, buf_b, &ofs_b); | ||
| 52 | } | ||
| 53 | |||
| 54 | msg ("close \"a\""); | ||
| 55 | close (fd_a); | ||
| 56 | |||
| 57 | msg ("close \"b\""); | ||
| 58 | close (fd_b); | ||
| 59 | |||
| 60 | check_file ("a", buf_a, FILE_SIZE); | ||
| 61 | check_file ("b", buf_b, FILE_SIZE); | ||
| 62 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.ck b/pintos-progos/tests/filesys/extended/grow-two-files.ck new file mode 100644 index 0000000..b5e754a --- /dev/null +++ b/pintos-progos/tests/filesys/extended/grow-two-files.ck | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (grow-two-files) begin | ||
| 8 | (grow-two-files) create "a" | ||
| 9 | (grow-two-files) create "b" | ||
| 10 | (grow-two-files) open "a" | ||
| 11 | (grow-two-files) open "b" | ||
| 12 | (grow-two-files) write "a" and "b" alternately | ||
| 13 | (grow-two-files) close "a" | ||
| 14 | (grow-two-files) close "b" | ||
| 15 | (grow-two-files) open "a" for verification | ||
| 16 | (grow-two-files) verified contents of "a" | ||
| 17 | (grow-two-files) close "a" | ||
| 18 | (grow-two-files) open "b" for verification | ||
| 19 | (grow-two-files) verified contents of "b" | ||
| 20 | (grow-two-files) close "b" | ||
| 21 | (grow-two-files) end | ||
| 22 | EOF | ||
| 23 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.c b/pintos-progos/tests/filesys/extended/mk-tree.c new file mode 100644 index 0000000..a36bb88 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/mk-tree.c | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | /* Library function for creating a tree of directories. */ | ||
| 2 | |||
| 3 | #include <stdio.h> | ||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/filesys/extended/mk-tree.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | |||
| 8 | static void do_mkdir (const char *format, ...) PRINTF_FORMAT (1, 2); | ||
| 9 | static void do_touch (const char *format, ...) PRINTF_FORMAT (1, 2); | ||
| 10 | |||
| 11 | void | ||
| 12 | make_tree (int at, int bt, int ct, int dt) | ||
| 13 | { | ||
| 14 | char try[128]; | ||
| 15 | int a, b, c, d; | ||
| 16 | int fd; | ||
| 17 | |||
| 18 | msg ("creating /0/0/0/0 through /%d/%d/%d/%d...", | ||
| 19 | at - 1, bt - 1, ct - 1, dt - 1); | ||
| 20 | quiet = true; | ||
| 21 | for (a = 0; a < at; a++) | ||
| 22 | { | ||
| 23 | do_mkdir ("/%d", a); | ||
| 24 | for (b = 0; b < bt; b++) | ||
| 25 | { | ||
| 26 | do_mkdir ("/%d/%d", a, b); | ||
| 27 | for (c = 0; c < ct; c++) | ||
| 28 | { | ||
| 29 | do_mkdir ("/%d/%d/%d", a, b, c); | ||
| 30 | for (d = 0; d < dt; d++) | ||
| 31 | do_touch ("/%d/%d/%d/%d", a, b, c, d); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | } | ||
| 35 | quiet = false; | ||
| 36 | |||
| 37 | snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1); | ||
| 38 | CHECK ((fd = open (try)) > 1, "open \"%s\"", try); | ||
| 39 | msg ("close \"%s\"", try); | ||
| 40 | close (fd); | ||
| 41 | } | ||
| 42 | |||
| 43 | static void | ||
| 44 | do_mkdir (const char *format, ...) | ||
| 45 | { | ||
| 46 | char dir[128]; | ||
| 47 | va_list args; | ||
| 48 | |||
| 49 | va_start (args, format); | ||
| 50 | vsnprintf (dir, sizeof dir, format, args); | ||
| 51 | va_end (args); | ||
| 52 | |||
| 53 | CHECK (mkdir (dir), "mkdir \"%s\"", dir); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void | ||
| 57 | do_touch (const char *format, ...) | ||
| 58 | { | ||
| 59 | char file[128]; | ||
| 60 | va_list args; | ||
| 61 | |||
| 62 | va_start (args, format); | ||
| 63 | vsnprintf (file, sizeof file, format, args); | ||
| 64 | va_end (args); | ||
| 65 | |||
| 66 | CHECK (create (file, 0), "create \"%s\"", file); | ||
| 67 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.h b/pintos-progos/tests/filesys/extended/mk-tree.h new file mode 100644 index 0000000..df0d5a6 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/mk-tree.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef TESTS_FILESYS_EXTENDED_MK_TREE_H | ||
| 2 | #define TESTS_FILESYS_EXTENDED_MK_TREE_H | ||
| 3 | |||
| 4 | void make_tree (int at, int bt, int ct, int dt); | ||
| 5 | |||
| 6 | #endif /* tests/filesys/extended/mk-tree.h */ | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck new file mode 100644 index 0000000..62d57ee --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw", | ||
| 7 | "logfile" => [random_bytes (8 * 512)]}); | ||
| 8 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.c b/pintos-progos/tests/filesys/extended/syn-rw.c new file mode 100644 index 0000000..657dfb5 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw.c | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | /* Grows a file in chunks while subprocesses read the growing | ||
| 2 | file. */ | ||
| 3 | |||
| 4 | #include <random.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/filesys/extended/syn-rw.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | char buf[BUF_SIZE]; | ||
| 11 | |||
| 12 | #define CHILD_CNT 4 | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | pid_t children[CHILD_CNT]; | ||
| 18 | size_t ofs; | ||
| 19 | int fd; | ||
| 20 | |||
| 21 | CHECK (create (file_name, 0), "create \"%s\"", file_name); | ||
| 22 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 23 | |||
| 24 | exec_children ("child-syn-rw", children, CHILD_CNT); | ||
| 25 | |||
| 26 | random_bytes (buf, sizeof buf); | ||
| 27 | quiet = true; | ||
| 28 | for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE) | ||
| 29 | CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0, | ||
| 30 | "write %d bytes at offset %zu in \"%s\"", | ||
| 31 | (int) CHUNK_SIZE, ofs, file_name); | ||
| 32 | quiet = false; | ||
| 33 | |||
| 34 | wait_children (children, CHILD_CNT); | ||
| 35 | } | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.ck b/pintos-progos/tests/filesys/extended/syn-rw.ck new file mode 100644 index 0000000..ac82aa8 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw.ck | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::random; | ||
| 6 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 7 | (syn-rw) begin | ||
| 8 | (syn-rw) create "logfile" | ||
| 9 | (syn-rw) open "logfile" | ||
| 10 | (syn-rw) exec child 1 of 4: "child-syn-rw 0" | ||
| 11 | (syn-rw) exec child 2 of 4: "child-syn-rw 1" | ||
| 12 | (syn-rw) exec child 3 of 4: "child-syn-rw 2" | ||
| 13 | (syn-rw) exec child 4 of 4: "child-syn-rw 3" | ||
| 14 | (syn-rw) wait for child 1 of 4 returned 0 (expected 0) | ||
| 15 | (syn-rw) wait for child 2 of 4 returned 1 (expected 1) | ||
| 16 | (syn-rw) wait for child 3 of 4 returned 2 (expected 2) | ||
| 17 | (syn-rw) wait for child 4 of 4 returned 3 (expected 3) | ||
| 18 | (syn-rw) end | ||
| 19 | EOF | ||
| 20 | pass; | ||
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.h b/pintos-progos/tests/filesys/extended/syn-rw.h new file mode 100644 index 0000000..170aeb4 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/syn-rw.h | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | #ifndef TESTS_FILESYS_EXTENDED_SYN_RW_H | ||
| 2 | #define TESTS_FILESYS_EXTENDED_SYN_RW_H | ||
| 3 | |||
| 4 | #define CHUNK_SIZE 8 | ||
| 5 | #define CHUNK_CNT 512 | ||
| 6 | #define BUF_SIZE (CHUNK_SIZE * CHUNK_CNT) | ||
| 7 | static const char file_name[] = "logfile"; | ||
| 8 | |||
| 9 | #endif /* tests/filesys/extended/syn-rw.h */ | ||
diff --git a/pintos-progos/tests/filesys/extended/tar.c b/pintos-progos/tests/filesys/extended/tar.c new file mode 100644 index 0000000..9801484 --- /dev/null +++ b/pintos-progos/tests/filesys/extended/tar.c | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | /* tar.c | ||
| 2 | |||
| 3 | Creates a tar archive. */ | ||
| 4 | |||
| 5 | #include <ustar.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <string.h> | ||
| 9 | |||
| 10 | static void usage (void); | ||
| 11 | static bool make_tar_archive (const char *archive_name, | ||
| 12 | char *files[], size_t file_cnt); | ||
| 13 | |||
| 14 | int | ||
| 15 | main (int argc, char *argv[]) | ||
| 16 | { | ||
| 17 | if (argc < 3) | ||
| 18 | usage (); | ||
| 19 | |||
| 20 | return (make_tar_archive (argv[1], argv + 2, argc - 2) | ||
| 21 | ? EXIT_SUCCESS : EXIT_FAILURE); | ||
| 22 | } | ||
| 23 | |||
| 24 | static void | ||
| 25 | usage (void) | ||
| 26 | { | ||
| 27 | printf ("tar, tar archive creator\n" | ||
| 28 | "Usage: tar ARCHIVE FILE...\n" | ||
| 29 | "where ARCHIVE is the tar archive to create\n" | ||
| 30 | " and FILE... is a list of files or directories to put into it.\n" | ||
| 31 | "(ARCHIVE itself will not be included in the archive, even if it\n" | ||
| 32 | "is in a directory to be archived.)\n"); | ||
| 33 | exit (EXIT_FAILURE); | ||
| 34 | } | ||
| 35 | |||
| 36 | static bool archive_file (char file_name[], size_t file_name_size, | ||
| 37 | int archive_fd, bool *write_error); | ||
| 38 | |||
| 39 | static bool archive_ordinary_file (const char *file_name, int file_fd, | ||
| 40 | int archive_fd, bool *write_error); | ||
| 41 | static bool archive_directory (char file_name[], size_t file_name_size, | ||
| 42 | int file_fd, int archive_fd, bool *write_error); | ||
| 43 | static bool write_header (const char *file_name, enum ustar_type, int size, | ||
| 44 | int archive_fd, bool *write_error); | ||
| 45 | |||
| 46 | static bool do_write (int fd, const char *buffer, int size, bool *write_error); | ||
| 47 | |||
| 48 | static bool | ||
| 49 | make_tar_archive (const char *archive_name, char *files[], size_t file_cnt) | ||
| 50 | { | ||
| 51 | static const char zeros[512]; | ||
| 52 | int archive_fd; | ||
| 53 | bool success = true; | ||
| 54 | bool write_error = false; | ||
| 55 | size_t i; | ||
| 56 | |||
| 57 | if (!create (archive_name, 0)) | ||
| 58 | { | ||
| 59 | printf ("%s: create failed\n", archive_name); | ||
| 60 | return false; | ||
| 61 | } | ||
| 62 | archive_fd = open (archive_name); | ||
| 63 | if (archive_fd < 0) | ||
| 64 | { | ||
| 65 | printf ("%s: open failed\n", archive_name); | ||
| 66 | return false; | ||
| 67 | } | ||
| 68 | |||
| 69 | for (i = 0; i < file_cnt; i++) | ||
| 70 | { | ||
| 71 | char file_name[128]; | ||
| 72 | |||
| 73 | strlcpy (file_name, files[i], sizeof file_name); | ||
| 74 | if (!archive_file (file_name, sizeof file_name, | ||
| 75 | archive_fd, &write_error)) | ||
| 76 | success = false; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (!do_write (archive_fd, zeros, 512, &write_error) | ||
| 80 | || !do_write (archive_fd, zeros, 512, &write_error)) | ||
| 81 | success = false; | ||
| 82 | |||
| 83 | close (archive_fd); | ||
| 84 | |||
| 85 | return success; | ||
| 86 | } | ||
| 87 | |||
| 88 | static bool | ||
| 89 | archive_file (char file_name[], size_t file_name_size, | ||
| 90 | int archive_fd, bool *write_error) | ||
| 91 | { | ||
| 92 | int file_fd = open (file_name); | ||
| 93 | if (file_fd >= 0) | ||
| 94 | { | ||
| 95 | bool success; | ||
| 96 | |||
| 97 | if (inumber (file_fd) != inumber (archive_fd)) | ||
| 98 | { | ||
| 99 | if (!isdir (file_fd)) | ||
| 100 | success = archive_ordinary_file (file_name, file_fd, | ||
| 101 | archive_fd, write_error); | ||
| 102 | else | ||
| 103 | success = archive_directory (file_name, file_name_size, file_fd, | ||
| 104 | archive_fd, write_error); | ||
| 105 | } | ||
| 106 | else | ||
| 107 | { | ||
| 108 | /* Nothing to do: don't try to archive the archive file. */ | ||
| 109 | success = true; | ||
| 110 | } | ||
| 111 | |||
| 112 | close (file_fd); | ||
| 113 | |||
| 114 | return success; | ||
| 115 | } | ||
| 116 | else | ||
| 117 | { | ||
| 118 | printf ("%s: open failed\n", file_name); | ||
| 119 | return false; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | static bool | ||
| 124 | archive_ordinary_file (const char *file_name, int file_fd, | ||
| 125 | int archive_fd, bool *write_error) | ||
| 126 | { | ||
| 127 | bool read_error = false; | ||
| 128 | bool success = true; | ||
| 129 | int file_size = filesize (file_fd); | ||
| 130 | |||
| 131 | if (!write_header (file_name, USTAR_REGULAR, file_size, | ||
| 132 | archive_fd, write_error)) | ||
| 133 | return false; | ||
| 134 | |||
| 135 | while (file_size > 0) | ||
| 136 | { | ||
| 137 | static char buf[512]; | ||
| 138 | int chunk_size = file_size > 512 ? 512 : file_size; | ||
| 139 | int read_retval = read (file_fd, buf, chunk_size); | ||
| 140 | int bytes_read = read_retval > 0 ? read_retval : 0; | ||
| 141 | |||
| 142 | if (bytes_read != chunk_size && !read_error) | ||
| 143 | { | ||
| 144 | printf ("%s: read error\n", file_name); | ||
| 145 | read_error = true; | ||
| 146 | success = false; | ||
| 147 | } | ||
| 148 | |||
| 149 | memset (buf + bytes_read, 0, 512 - bytes_read); | ||
| 150 | if (!do_write (archive_fd, buf, 512, write_error)) | ||
| 151 | success = false; | ||
| 152 | |||
| 153 | file_size -= chunk_size; | ||
| 154 | } | ||
| 155 | |||
| 156 | return success; | ||
| 157 | } | ||
| 158 | |||
| 159 | static bool | ||
| 160 | archive_directory (char file_name[], size_t file_name_size, int file_fd, | ||
| 161 | int archive_fd, bool *write_error) | ||
| 162 | { | ||
| 163 | size_t dir_len; | ||
| 164 | bool success = true; | ||
| 165 | |||
| 166 | dir_len = strlen (file_name); | ||
| 167 | if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size) | ||
| 168 | { | ||
| 169 | printf ("%s: file name too long\n", file_name); | ||
| 170 | return false; | ||
| 171 | } | ||
| 172 | |||
| 173 | if (!write_header (file_name, USTAR_DIRECTORY, 0, archive_fd, write_error)) | ||
| 174 | return false; | ||
| 175 | |||
| 176 | file_name[dir_len] = '/'; | ||
| 177 | while (readdir (file_fd, &file_name[dir_len + 1])) | ||
| 178 | if (!archive_file (file_name, file_name_size, archive_fd, write_error)) | ||
| 179 | success = false; | ||
| 180 | file_name[dir_len] = '\0'; | ||
| 181 | |||
| 182 | return success; | ||
| 183 | } | ||
| 184 | |||
| 185 | static bool | ||
| 186 | write_header (const char *file_name, enum ustar_type type, int size, | ||
| 187 | int archive_fd, bool *write_error) | ||
| 188 | { | ||
| 189 | static char header[512]; | ||
| 190 | return (ustar_make_header (file_name, type, size, header) | ||
| 191 | && do_write (archive_fd, header, 512, write_error)); | ||
| 192 | } | ||
| 193 | |||
| 194 | static bool | ||
| 195 | do_write (int fd, const char *buffer, int size, bool *write_error) | ||
| 196 | { | ||
| 197 | if (write (fd, buffer, size) == size) | ||
| 198 | return true; | ||
| 199 | else | ||
| 200 | { | ||
| 201 | if (!*write_error) | ||
| 202 | { | ||
| 203 | printf ("error writing archive\n"); | ||
| 204 | *write_error = true; | ||
| 205 | } | ||
| 206 | return false; | ||
| 207 | } | ||
| 208 | } | ||
diff --git a/pintos-progos/tests/filesys/seq-test.c b/pintos-progos/tests/filesys/seq-test.c new file mode 100644 index 0000000..8ce222c --- /dev/null +++ b/pintos-progos/tests/filesys/seq-test.c | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | #include "tests/filesys/seq-test.h" | ||
| 2 | #include <random.h> | ||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | seq_test (const char *file_name, void *buf, size_t size, size_t initial_size, | ||
| 8 | size_t (*block_size_func) (void), | ||
| 9 | void (*check_func) (int fd, long ofs)) | ||
| 10 | { | ||
| 11 | size_t ofs; | ||
| 12 | int fd; | ||
| 13 | |||
| 14 | random_bytes (buf, size); | ||
| 15 | CHECK (create (file_name, initial_size), "create \"%s\"", file_name); | ||
| 16 | CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name); | ||
| 17 | |||
| 18 | ofs = 0; | ||
| 19 | msg ("writing \"%s\"", file_name); | ||
| 20 | while (ofs < size) | ||
| 21 | { | ||
| 22 | size_t block_size = block_size_func (); | ||
| 23 | if (block_size > size - ofs) | ||
| 24 | block_size = size - ofs; | ||
| 25 | |||
| 26 | if (write (fd, buf + ofs, block_size) != (int) block_size) | ||
| 27 | fail ("write %zu bytes at offset %zu in \"%s\" failed", | ||
| 28 | block_size, ofs, file_name); | ||
| 29 | |||
| 30 | ofs += block_size; | ||
| 31 | if (check_func != NULL) | ||
| 32 | check_func (fd, ofs); | ||
| 33 | } | ||
| 34 | msg ("close \"%s\"", file_name); | ||
| 35 | close (fd); | ||
| 36 | check_file (file_name, buf, size); | ||
| 37 | } | ||
diff --git a/pintos-progos/tests/filesys/seq-test.h b/pintos-progos/tests/filesys/seq-test.h new file mode 100644 index 0000000..0697381 --- /dev/null +++ b/pintos-progos/tests/filesys/seq-test.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef TESTS_FILESYS_SEQ_TEST_H | ||
| 2 | #define TESTS_FILESYS_SEQ_TEST_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | void seq_test (const char *file_name, | ||
| 7 | void *buf, size_t size, size_t initial_size, | ||
| 8 | size_t (*block_size_func) (void), | ||
| 9 | void (*check_func) (int fd, long ofs)); | ||
| 10 | |||
| 11 | #endif /* tests/filesys/seq-test.h */ | ||
diff --git a/pintos-progos/tests/internal/list.c b/pintos-progos/tests/internal/list.c new file mode 100644 index 0000000..836c69e --- /dev/null +++ b/pintos-progos/tests/internal/list.c | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | /* Test program for lib/kernel/list.c. | ||
| 2 | |||
| 3 | Attempts to test the list functionality that is not | ||
| 4 | sufficiently tested elsewhere in Pintos. | ||
| 5 | |||
| 6 | This is not a test we will run on your submitted projects. | ||
| 7 | It is here for completeness. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #undef NDEBUG | ||
| 11 | #include <debug.h> | ||
| 12 | #include <list.h> | ||
| 13 | #include <random.h> | ||
| 14 | #include <stdio.h> | ||
| 15 | #include "threads/test.h" | ||
| 16 | |||
| 17 | /* Maximum number of elements in a linked list that we will | ||
| 18 | test. */ | ||
| 19 | #define MAX_SIZE 64 | ||
| 20 | |||
| 21 | /* A linked list element. */ | ||
| 22 | struct value | ||
| 23 | { | ||
| 24 | struct list_elem elem; /* List element. */ | ||
| 25 | int value; /* Item value. */ | ||
| 26 | }; | ||
| 27 | |||
| 28 | static void shuffle (struct value[], size_t); | ||
| 29 | static bool value_less (const struct list_elem *, const struct list_elem *, | ||
| 30 | void *); | ||
| 31 | static void verify_list_fwd (struct list *, int size); | ||
| 32 | static void verify_list_bkwd (struct list *, int size); | ||
| 33 | |||
| 34 | /* Test the linked list implementation. */ | ||
| 35 | void | ||
| 36 | test (void) | ||
| 37 | { | ||
| 38 | int size; | ||
| 39 | |||
| 40 | printf ("testing various size lists:"); | ||
| 41 | for (size = 0; size < MAX_SIZE; size++) | ||
| 42 | { | ||
| 43 | int repeat; | ||
| 44 | |||
| 45 | printf (" %d", size); | ||
| 46 | for (repeat = 0; repeat < 10; repeat++) | ||
| 47 | { | ||
| 48 | static struct value values[MAX_SIZE * 4]; | ||
| 49 | struct list list; | ||
| 50 | struct list_elem *e; | ||
| 51 | int i, ofs; | ||
| 52 | |||
| 53 | /* Put values 0...SIZE in random order in VALUES. */ | ||
| 54 | for (i = 0; i < size; i++) | ||
| 55 | values[i].value = i; | ||
| 56 | shuffle (values, size); | ||
| 57 | |||
| 58 | /* Assemble list. */ | ||
| 59 | list_init (&list); | ||
| 60 | for (i = 0; i < size; i++) | ||
| 61 | list_push_back (&list, &values[i].elem); | ||
| 62 | |||
| 63 | /* Verify correct minimum and maximum elements. */ | ||
| 64 | e = list_min (&list, value_less, NULL); | ||
| 65 | ASSERT (size ? list_entry (e, struct value, elem)->value == 0 | ||
| 66 | : e == list_begin (&list)); | ||
| 67 | e = list_max (&list, value_less, NULL); | ||
| 68 | ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1 | ||
| 69 | : e == list_begin (&list)); | ||
| 70 | |||
| 71 | /* Sort and verify list. */ | ||
| 72 | list_sort (&list, value_less, NULL); | ||
| 73 | verify_list_fwd (&list, size); | ||
| 74 | |||
| 75 | /* Reverse and verify list. */ | ||
| 76 | list_reverse (&list); | ||
| 77 | verify_list_bkwd (&list, size); | ||
| 78 | |||
| 79 | /* Shuffle, insert using list_insert_ordered(), | ||
| 80 | and verify ordering. */ | ||
| 81 | shuffle (values, size); | ||
| 82 | list_init (&list); | ||
| 83 | for (i = 0; i < size; i++) | ||
| 84 | list_insert_ordered (&list, &values[i].elem, | ||
| 85 | value_less, NULL); | ||
| 86 | verify_list_fwd (&list, size); | ||
| 87 | |||
| 88 | /* Duplicate some items, uniquify, and verify. */ | ||
| 89 | ofs = size; | ||
| 90 | for (e = list_begin (&list); e != list_end (&list); | ||
| 91 | e = list_next (e)) | ||
| 92 | { | ||
| 93 | struct value *v = list_entry (e, struct value, elem); | ||
| 94 | int copies = random_ulong () % 4; | ||
| 95 | while (copies-- > 0) | ||
| 96 | { | ||
| 97 | values[ofs].value = v->value; | ||
| 98 | list_insert (e, &values[ofs++].elem); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | ASSERT ((size_t) ofs < sizeof values / sizeof *values); | ||
| 102 | list_unique (&list, NULL, value_less, NULL); | ||
| 103 | verify_list_fwd (&list, size); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | printf (" done\n"); | ||
| 108 | printf ("list: PASS\n"); | ||
| 109 | } | ||
| 110 | |||
| 111 | /* Shuffles the CNT elements in ARRAY into random order. */ | ||
| 112 | static void | ||
| 113 | shuffle (struct value *array, size_t cnt) | ||
| 114 | { | ||
| 115 | size_t i; | ||
| 116 | |||
| 117 | for (i = 0; i < cnt; i++) | ||
| 118 | { | ||
| 119 | size_t j = i + random_ulong () % (cnt - i); | ||
| 120 | struct value t = array[j]; | ||
| 121 | array[j] = array[i]; | ||
| 122 | array[i] = t; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | /* Returns true if value A is less than value B, false | ||
| 127 | otherwise. */ | ||
| 128 | static bool | ||
| 129 | value_less (const struct list_elem *a_, const struct list_elem *b_, | ||
| 130 | void *aux UNUSED) | ||
| 131 | { | ||
| 132 | const struct value *a = list_entry (a_, struct value, elem); | ||
| 133 | const struct value *b = list_entry (b_, struct value, elem); | ||
| 134 | |||
| 135 | return a->value < b->value; | ||
| 136 | } | ||
| 137 | |||
| 138 | /* Verifies that LIST contains the values 0...SIZE when traversed | ||
| 139 | in forward order. */ | ||
| 140 | static void | ||
| 141 | verify_list_fwd (struct list *list, int size) | ||
| 142 | { | ||
| 143 | struct list_elem *e; | ||
| 144 | int i; | ||
| 145 | |||
| 146 | for (i = 0, e = list_begin (list); | ||
| 147 | i < size && e != list_end (list); | ||
| 148 | i++, e = list_next (e)) | ||
| 149 | { | ||
| 150 | struct value *v = list_entry (e, struct value, elem); | ||
| 151 | ASSERT (i == v->value); | ||
| 152 | } | ||
| 153 | ASSERT (i == size); | ||
| 154 | ASSERT (e == list_end (list)); | ||
| 155 | } | ||
| 156 | |||
| 157 | /* Verifies that LIST contains the values 0...SIZE when traversed | ||
| 158 | in reverse order. */ | ||
| 159 | static void | ||
| 160 | verify_list_bkwd (struct list *list, int size) | ||
| 161 | { | ||
| 162 | struct list_elem *e; | ||
| 163 | int i; | ||
| 164 | |||
| 165 | for (i = 0, e = list_rbegin (list); | ||
| 166 | i < size && e != list_rend (list); | ||
| 167 | i++, e = list_prev (e)) | ||
| 168 | { | ||
| 169 | struct value *v = list_entry (e, struct value, elem); | ||
| 170 | ASSERT (i == v->value); | ||
| 171 | } | ||
| 172 | ASSERT (i == size); | ||
| 173 | ASSERT (e == list_rend (list)); | ||
| 174 | } | ||
diff --git a/pintos-progos/tests/internal/stdio.c b/pintos-progos/tests/internal/stdio.c new file mode 100644 index 0000000..fb60cda --- /dev/null +++ b/pintos-progos/tests/internal/stdio.c | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | /* Test program for printf() in lib/stdio.c. | ||
| 2 | |||
| 3 | Attempts to test printf() functionality that is not | ||
| 4 | sufficiently tested elsewhere in Pintos. | ||
| 5 | |||
| 6 | This is not a test we will run on your submitted projects. | ||
| 7 | It is here for completeness. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #undef NDEBUG | ||
| 11 | #include <limits.h> | ||
| 12 | #include <stdarg.h> | ||
| 13 | #include <stddef.h> | ||
| 14 | #include <stdlib.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #include <string.h> | ||
| 17 | #include "threads/test.h" | ||
| 18 | |||
| 19 | /* Number of failures so far. */ | ||
| 20 | static int failure_cnt; | ||
| 21 | |||
| 22 | static void | ||
| 23 | checkf (const char *expect, const char *format, ...) | ||
| 24 | { | ||
| 25 | char output[128]; | ||
| 26 | va_list args; | ||
| 27 | |||
| 28 | printf ("\"%s\" -> \"%s\": ", format, expect); | ||
| 29 | |||
| 30 | va_start (args, format); | ||
| 31 | vsnprintf (output, sizeof output, format, args); | ||
| 32 | va_end (args); | ||
| 33 | |||
| 34 | if (strcmp (expect, output)) | ||
| 35 | { | ||
| 36 | printf ("\nFAIL: actual output \"%s\"\n", output); | ||
| 37 | failure_cnt++; | ||
| 38 | } | ||
| 39 | else | ||
| 40 | printf ("okay\n"); | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Test printf() implementation. */ | ||
| 44 | void | ||
| 45 | test (void) | ||
| 46 | { | ||
| 47 | printf ("Testing formats:"); | ||
| 48 | |||
| 49 | /* Check that commas show up in the right places, for positive | ||
| 50 | numbers. */ | ||
| 51 | checkf ("1", "%'d", 1); | ||
| 52 | checkf ("12", "%'d", 12); | ||
| 53 | checkf ("123", "%'d", 123); | ||
| 54 | checkf ("1,234", "%'d", 1234); | ||
| 55 | checkf ("12,345", "%'d", 12345); | ||
| 56 | checkf ("123,456", "%'ld", 123456L); | ||
| 57 | checkf ("1,234,567", "%'ld", 1234567L); | ||
| 58 | checkf ("12,345,678", "%'ld", 12345678L); | ||
| 59 | checkf ("123,456,789", "%'ld", 123456789L); | ||
| 60 | checkf ("1,234,567,890", "%'ld", 1234567890L); | ||
| 61 | checkf ("12,345,678,901", "%'lld", 12345678901LL); | ||
| 62 | checkf ("123,456,789,012", "%'lld", 123456789012LL); | ||
| 63 | checkf ("1,234,567,890,123", "%'lld", 1234567890123LL); | ||
| 64 | checkf ("12,345,678,901,234", "%'lld", 12345678901234LL); | ||
| 65 | checkf ("123,456,789,012,345", "%'lld", 123456789012345LL); | ||
| 66 | checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL); | ||
| 67 | checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL); | ||
| 68 | checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL); | ||
| 69 | checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL); | ||
| 70 | |||
| 71 | /* Check that commas show up in the right places, for positive | ||
| 72 | numbers. */ | ||
| 73 | checkf ("-1", "%'d", -1); | ||
| 74 | checkf ("-12", "%'d", -12); | ||
| 75 | checkf ("-123", "%'d", -123); | ||
| 76 | checkf ("-1,234", "%'d", -1234); | ||
| 77 | checkf ("-12,345", "%'d", -12345); | ||
| 78 | checkf ("-123,456", "%'ld", -123456L); | ||
| 79 | checkf ("-1,234,567", "%'ld", -1234567L); | ||
| 80 | checkf ("-12,345,678", "%'ld", -12345678L); | ||
| 81 | checkf ("-123,456,789", "%'ld", -123456789L); | ||
| 82 | checkf ("-1,234,567,890", "%'ld", -1234567890L); | ||
| 83 | checkf ("-12,345,678,901", "%'lld", -12345678901LL); | ||
| 84 | checkf ("-123,456,789,012", "%'lld", -123456789012LL); | ||
| 85 | checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL); | ||
| 86 | checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL); | ||
| 87 | checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL); | ||
| 88 | checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL); | ||
| 89 | checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL); | ||
| 90 | checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL); | ||
| 91 | checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL); | ||
| 92 | |||
| 93 | /* Check signed integer conversions. */ | ||
| 94 | checkf (" 0", "%5d", 0); | ||
| 95 | checkf ("0 ", "%-5d", 0); | ||
| 96 | checkf (" +0", "%+5d", 0); | ||
| 97 | checkf ("+0 ", "%+-5d", 0); | ||
| 98 | checkf (" 0", "% 5d", 0); | ||
| 99 | checkf ("00000", "%05d", 0); | ||
| 100 | checkf (" ", "%5.0d", 0); | ||
| 101 | checkf (" 00", "%5.2d", 0); | ||
| 102 | checkf ("0", "%d", 0); | ||
| 103 | |||
| 104 | checkf (" 1", "%5d", 1); | ||
| 105 | checkf ("1 ", "%-5d", 1); | ||
| 106 | checkf (" +1", "%+5d", 1); | ||
| 107 | checkf ("+1 ", "%+-5d", 1); | ||
| 108 | checkf (" 1", "% 5d", 1); | ||
| 109 | checkf ("00001", "%05d", 1); | ||
| 110 | checkf (" 1", "%5.0d", 1); | ||
| 111 | checkf (" 01", "%5.2d", 1); | ||
| 112 | checkf ("1", "%d", 1); | ||
| 113 | |||
| 114 | checkf (" -1", "%5d", -1); | ||
| 115 | checkf ("-1 ", "%-5d", -1); | ||
| 116 | checkf (" -1", "%+5d", -1); | ||
| 117 | checkf ("-1 ", "%+-5d", -1); | ||
| 118 | checkf (" -1", "% 5d", -1); | ||
| 119 | checkf ("-0001", "%05d", -1); | ||
| 120 | checkf (" -1", "%5.0d", -1); | ||
| 121 | checkf (" -01", "%5.2d", -1); | ||
| 122 | checkf ("-1", "%d", -1); | ||
| 123 | |||
| 124 | checkf ("12345", "%5d", 12345); | ||
| 125 | checkf ("12345", "%-5d", 12345); | ||
| 126 | checkf ("+12345", "%+5d", 12345); | ||
| 127 | checkf ("+12345", "%+-5d", 12345); | ||
| 128 | checkf (" 12345", "% 5d", 12345); | ||
| 129 | checkf ("12345", "%05d", 12345); | ||
| 130 | checkf ("12345", "%5.0d", 12345); | ||
| 131 | checkf ("12345", "%5.2d", 12345); | ||
| 132 | checkf ("12345", "%d", 12345); | ||
| 133 | |||
| 134 | checkf ("123456", "%5d", 123456); | ||
| 135 | checkf ("123456", "%-5d", 123456); | ||
| 136 | checkf ("+123456", "%+5d", 123456); | ||
| 137 | checkf ("+123456", "%+-5d", 123456); | ||
| 138 | checkf (" 123456", "% 5d", 123456); | ||
| 139 | checkf ("123456", "%05d", 123456); | ||
| 140 | checkf ("123456", "%5.0d", 123456); | ||
| 141 | checkf ("123456", "%5.2d", 123456); | ||
| 142 | checkf ("123456", "%d", 123456); | ||
| 143 | |||
| 144 | /* Check unsigned integer conversions. */ | ||
| 145 | checkf (" 0", "%5u", 0); | ||
| 146 | checkf (" 0", "%5o", 0); | ||
| 147 | checkf (" 0", "%5x", 0); | ||
| 148 | checkf (" 0", "%5X", 0); | ||
| 149 | checkf (" 0", "%#5o", 0); | ||
| 150 | checkf (" 0", "%#5x", 0); | ||
| 151 | checkf (" 0", "%#5X", 0); | ||
| 152 | checkf (" 00000000", "%#10.8x", 0); | ||
| 153 | |||
| 154 | checkf (" 1", "%5u", 1); | ||
| 155 | checkf (" 1", "%5o", 1); | ||
| 156 | checkf (" 1", "%5x", 1); | ||
| 157 | checkf (" 1", "%5X", 1); | ||
| 158 | checkf (" 01", "%#5o", 1); | ||
| 159 | checkf (" 0x1", "%#5x", 1); | ||
| 160 | checkf (" 0X1", "%#5X", 1); | ||
| 161 | checkf ("0x00000001", "%#10.8x", 1); | ||
| 162 | |||
| 163 | checkf ("123456", "%5u", 123456); | ||
| 164 | checkf ("361100", "%5o", 123456); | ||
| 165 | checkf ("1e240", "%5x", 123456); | ||
| 166 | checkf ("1E240", "%5X", 123456); | ||
| 167 | checkf ("0361100", "%#5o", 123456); | ||
| 168 | checkf ("0x1e240", "%#5x", 123456); | ||
| 169 | checkf ("0X1E240", "%#5X", 123456); | ||
| 170 | checkf ("0x0001e240", "%#10.8x", 123456); | ||
| 171 | |||
| 172 | /* Character and string conversions. */ | ||
| 173 | checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r'); | ||
| 174 | checkf (" left-right ", "%6s%s%-7s", "left", "-", "right"); | ||
| 175 | checkf ("trim", "%.4s", "trimoff"); | ||
| 176 | checkf ("%%", "%%%%"); | ||
| 177 | |||
| 178 | /* From Cristian Cadar's automatic test case generator. */ | ||
| 179 | checkf (" abcdefgh", "%9s", "abcdefgh"); | ||
| 180 | checkf ("36657730000", "%- o", (unsigned) 036657730000); | ||
| 181 | checkf ("4139757568", "%- u", (unsigned) 4139757568UL); | ||
| 182 | checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000); | ||
| 183 | checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000); | ||
| 184 | checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL); | ||
| 185 | checkf ("-155209728", "%-zi", (size_t) -155209728); | ||
| 186 | checkf ("-155209728", "%-zd", (size_t) -155209728); | ||
| 187 | checkf ("036657730000", "%+#o", (unsigned) 036657730000); | ||
| 188 | checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000); | ||
| 189 | checkf ("-155209728", "% zi", (size_t) -155209728); | ||
| 190 | checkf ("-155209728", "% zd", (size_t) -155209728); | ||
| 191 | checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL); | ||
| 192 | checkf ("036657730000", "% #o", (unsigned) 036657730000); | ||
| 193 | checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000); | ||
| 194 | checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000); | ||
| 195 | checkf ("-155209728", "%#zd", (size_t) -155209728); | ||
| 196 | checkf ("-155209728", "%0zi", (size_t) -155209728); | ||
| 197 | checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL); | ||
| 198 | checkf ("-155,209,728", "%-'d", -155209728); | ||
| 199 | checkf ("-155209728", "%.zi", (size_t) -155209728); | ||
| 200 | checkf ("-155209728", "%zi", (size_t) -155209728); | ||
| 201 | checkf ("-155209728", "%zd", (size_t) -155209728); | ||
| 202 | checkf ("-155209728", "%+zi", (size_t) -155209728); | ||
| 203 | |||
| 204 | if (failure_cnt == 0) | ||
| 205 | printf ("\nstdio: PASS\n"); | ||
| 206 | else | ||
| 207 | printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt); | ||
| 208 | } | ||
diff --git a/pintos-progos/tests/internal/stdlib.c b/pintos-progos/tests/internal/stdlib.c new file mode 100644 index 0000000..ad0f0f9 --- /dev/null +++ b/pintos-progos/tests/internal/stdlib.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* Test program for sorting and searching in lib/stdlib.c. | ||
| 2 | |||
| 3 | Attempts to test the sorting and searching functionality that | ||
| 4 | is not sufficiently tested elsewhere in Pintos. | ||
| 5 | |||
| 6 | This is not a test we will run on your submitted projects. | ||
| 7 | It is here for completeness. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #undef NDEBUG | ||
| 11 | #include <debug.h> | ||
| 12 | #include <limits.h> | ||
| 13 | #include <random.h> | ||
| 14 | #include <stdlib.h> | ||
| 15 | #include <stdio.h> | ||
| 16 | #include "threads/test.h" | ||
| 17 | |||
| 18 | /* Maximum number of elements in an array that we will test. */ | ||
| 19 | #define MAX_CNT 4096 | ||
| 20 | |||
| 21 | static void shuffle (int[], size_t); | ||
| 22 | static int compare_ints (const void *, const void *); | ||
| 23 | static void verify_order (const int[], size_t); | ||
| 24 | static void verify_bsearch (const int[], size_t); | ||
| 25 | |||
| 26 | /* Test sorting and searching implementations. */ | ||
| 27 | void | ||
| 28 | test (void) | ||
| 29 | { | ||
| 30 | int cnt; | ||
| 31 | |||
| 32 | printf ("testing various size arrays:"); | ||
| 33 | for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1) | ||
| 34 | { | ||
| 35 | int repeat; | ||
| 36 | |||
| 37 | printf (" %zu", cnt); | ||
| 38 | for (repeat = 0; repeat < 10; repeat++) | ||
| 39 | { | ||
| 40 | static int values[MAX_CNT]; | ||
| 41 | int i; | ||
| 42 | |||
| 43 | /* Put values 0...CNT in random order in VALUES. */ | ||
| 44 | for (i = 0; i < cnt; i++) | ||
| 45 | values[i] = i; | ||
| 46 | shuffle (values, cnt); | ||
| 47 | |||
| 48 | /* Sort VALUES, then verify ordering. */ | ||
| 49 | qsort (values, cnt, sizeof *values, compare_ints); | ||
| 50 | verify_order (values, cnt); | ||
| 51 | verify_bsearch (values, cnt); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | printf (" done\n"); | ||
| 56 | printf ("stdlib: PASS\n"); | ||
| 57 | } | ||
| 58 | |||
| 59 | /* Shuffles the CNT elements in ARRAY into random order. */ | ||
| 60 | static void | ||
| 61 | shuffle (int *array, size_t cnt) | ||
| 62 | { | ||
| 63 | size_t i; | ||
| 64 | |||
| 65 | for (i = 0; i < cnt; i++) | ||
| 66 | { | ||
| 67 | size_t j = i + random_ulong () % (cnt - i); | ||
| 68 | int t = array[j]; | ||
| 69 | array[j] = array[i]; | ||
| 70 | array[i] = t; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Returns 1 if *A is greater than *B, | ||
| 75 | 0 if *A equals *B, | ||
| 76 | -1 if *A is less than *B. */ | ||
| 77 | static int | ||
| 78 | compare_ints (const void *a_, const void *b_) | ||
| 79 | { | ||
| 80 | const int *a = a_; | ||
| 81 | const int *b = b_; | ||
| 82 | |||
| 83 | return *a < *b ? -1 : *a > *b; | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Verifies that ARRAY contains the CNT ints 0...CNT-1. */ | ||
| 87 | static void | ||
| 88 | verify_order (const int *array, size_t cnt) | ||
| 89 | { | ||
| 90 | int i; | ||
| 91 | |||
| 92 | for (i = 0; (size_t) i < cnt; i++) | ||
| 93 | ASSERT (array[i] == i); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Checks that bsearch() works properly in ARRAY. ARRAY must | ||
| 97 | contain the values 0...CNT-1. */ | ||
| 98 | static void | ||
| 99 | verify_bsearch (const int *array, size_t cnt) | ||
| 100 | { | ||
| 101 | int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2}; | ||
| 102 | int i; | ||
| 103 | |||
| 104 | /* Check that all the values in the array are found properly. */ | ||
| 105 | for (i = 0; (size_t) i < cnt; i++) | ||
| 106 | ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints) | ||
| 107 | == array + i); | ||
| 108 | |||
| 109 | /* Check that some values not in the array are not found. */ | ||
| 110 | not_in_array[0] = cnt; | ||
| 111 | for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++) | ||
| 112 | ASSERT (bsearch (¬_in_array[i], array, cnt, sizeof *array, compare_ints) | ||
| 113 | == NULL); | ||
| 114 | } | ||
diff --git a/pintos-progos/tests/intro/Grading b/pintos-progos/tests/intro/Grading new file mode 100644 index 0000000..b1ff6b8 --- /dev/null +++ b/pintos-progos/tests/intro/Grading | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | 50.0% tests/intro/alarm-clock/Rubric | ||
| 5 | 50.0% tests/intro/userprog-args/Rubric | ||
diff --git a/pintos-progos/tests/intro/alarm-clock/Make.tests b/pintos-progos/tests/intro/alarm-clock/Make.tests new file mode 100644 index 0000000..55ad443 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/Make.tests | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | tests/intro/alarm-clock/%.output: SIMULATOR = bochs | ||
| 3 | tests/intro/alarm-clock/%.output: PINTOSOPTS += --kernel-test | ||
| 4 | tests/intro/alarm-clock/%.output: FILESYSSOURCE = --filesys-size=1 | ||
| 5 | |||
| 6 | # Test names. | ||
| 7 | tests/intro/alarm-clock_TESTS = $(addprefix tests/intro/alarm-clock/,alarm-single \ | ||
| 8 | alarm-multiple alarm-simultaneous alarm-zero alarm-negative) | ||
| 9 | |||
| 10 | # Sources for tests. | ||
| 11 | tests/intro/alarm-clock_SRC = tests/intro/alarm-clock/tests.c | ||
| 12 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-wait.c | ||
| 13 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-simultaneous.c | ||
| 14 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-zero.c | ||
| 15 | tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-negative.c | ||
diff --git a/pintos-progos/tests/intro/alarm-clock/Rubric b/pintos-progos/tests/intro/alarm-clock/Rubric new file mode 100644 index 0000000..0cf3dc1 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/Rubric | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | Functionality and robustness of alarm clock: | ||
| 2 | 4 alarm-single | ||
| 3 | 4 alarm-multiple | ||
| 4 | 4 alarm-simultaneous | ||
| 5 | 1 alarm-zero | ||
| 6 | 1 alarm-negative | ||
| 7 | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck new file mode 120000 index 0000000..f3a9edc --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-multiple.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.c b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c new file mode 120000 index 0000000..483aa63 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-negative.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck new file mode 120000 index 0000000..279520e --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-negative.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c new file mode 120000 index 0000000..6362b61 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-simultaneous.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck new file mode 120000 index 0000000..7226d0c --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-simultaneous.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-single.ck b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck new file mode 120000 index 0000000..7f98a51 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-single.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-wait.c b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c new file mode 120000 index 0000000..2755ae5 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-wait.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.c b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c new file mode 120000 index 0000000..a1f3ca7 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-zero.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck new file mode 120000 index 0000000..3f98d64 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../threads/alarm-zero.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/alarm-clock/tests.c b/pintos-progos/tests/intro/alarm-clock/tests.c new file mode 100644 index 0000000..4a96360 --- /dev/null +++ b/pintos-progos/tests/intro/alarm-clock/tests.c | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | #include "tests/threads/tests.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | |||
| 6 | struct test | ||
| 7 | { | ||
| 8 | const char *name; | ||
| 9 | test_func *function; | ||
| 10 | }; | ||
| 11 | |||
| 12 | static const struct test tests[] = | ||
| 13 | { | ||
| 14 | {"alarm-single", test_alarm_single}, | ||
| 15 | {"alarm-multiple", test_alarm_multiple}, | ||
| 16 | {"alarm-simultaneous", test_alarm_simultaneous}, | ||
| 17 | {"alarm-zero", test_alarm_zero}, | ||
| 18 | {"alarm-negative", test_alarm_negative}, | ||
| 19 | }; | ||
| 20 | |||
| 21 | static const char *test_name; | ||
| 22 | |||
| 23 | /* Runs the test named NAME. */ | ||
| 24 | void | ||
| 25 | run_test (const char *name) | ||
| 26 | { | ||
| 27 | const struct test *t; | ||
| 28 | |||
| 29 | for (t = tests; t < tests + sizeof tests / sizeof *tests; t++) | ||
| 30 | if (!strcmp (name, t->name)) | ||
| 31 | { | ||
| 32 | test_name = name; | ||
| 33 | msg ("begin"); | ||
| 34 | t->function (); | ||
| 35 | msg ("end"); | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | PANIC ("no test named \"%s\"", name); | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Prints FORMAT as if with printf(), | ||
| 42 | prefixing the output by the name of the test | ||
| 43 | and following it with a new-line character. */ | ||
| 44 | void | ||
| 45 | msg (const char *format, ...) | ||
| 46 | { | ||
| 47 | va_list args; | ||
| 48 | |||
| 49 | printf ("(%s) ", test_name); | ||
| 50 | va_start (args, format); | ||
| 51 | vprintf (format, args); | ||
| 52 | va_end (args); | ||
| 53 | putchar ('\n'); | ||
| 54 | } | ||
| 55 | |||
| 56 | /* Prints failure message FORMAT as if with printf(), | ||
| 57 | prefixing the output by the name of the test and FAIL: | ||
| 58 | and following it with a new-line character, | ||
| 59 | and then panics the kernel. */ | ||
| 60 | void | ||
| 61 | fail (const char *format, ...) | ||
| 62 | { | ||
| 63 | va_list args; | ||
| 64 | |||
| 65 | printf ("(%s) FAIL: ", test_name); | ||
| 66 | va_start (args, format); | ||
| 67 | vprintf (format, args); | ||
| 68 | va_end (args); | ||
| 69 | putchar ('\n'); | ||
| 70 | |||
| 71 | PANIC ("test failed"); | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Prints a message indicating the current test passed. */ | ||
| 75 | void | ||
| 76 | pass (void) | ||
| 77 | { | ||
| 78 | printf ("(%s) PASS\n", test_name); | ||
| 79 | } | ||
| 80 | |||
diff --git a/pintos-progos/tests/intro/userprog-args/Make.tests b/pintos-progos/tests/intro/userprog-args/Make.tests new file mode 100644 index 0000000..6f7a474 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/Make.tests | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/intro/userprog-args/%.output: FILESYSSOURCE = --filesys-size=2 | ||
| 4 | tests/intro/userprog-args/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^) | ||
| 5 | tests/intro/userprog-args/%.output: SIMULATOR = --qemu | ||
| 6 | tests/intro/userprog-args_TESTS = $(addprefix tests/intro/userprog-args/,args-none \ | ||
| 7 | args-single args-multiple args-many args-dbl-space args-limit) | ||
| 8 | |||
| 9 | tests/intro/userprog-args_PROGS = $(tests/intro/userprog-args_TESTS) $(addprefix \ | ||
| 10 | tests/intro/userprog-args/,child-simple child-args) | ||
| 11 | |||
| 12 | tests/intro/userprog-args/args-none_SRC = tests/intro/userprog-args/args.c | ||
| 13 | tests/intro/userprog-args/args-single_SRC = tests/intro/userprog-args/args.c | ||
| 14 | tests/intro/userprog-args/args-multiple_SRC = tests/intro/userprog-args/args.c | ||
| 15 | tests/intro/userprog-args/args-many_SRC = tests/intro/userprog-args/args.c | ||
| 16 | tests/intro/userprog-args/args-dbl-space_SRC = tests/intro/userprog-args/args.c | ||
| 17 | tests/intro/userprog-args/args-limit_SRC = tests/intro/userprog-args/args-limit.c | ||
| 18 | |||
| 19 | tests/intro/userprog-args/child-simple_SRC = tests/intro/userprog-args/child-simple.c | ||
| 20 | tests/intro/userprog-args/child-args_SRC = tests/intro/userprog-args/args.c | ||
| 21 | |||
| 22 | $(foreach prog,$(tests/intro/userprog-args_PROGS),$(eval $(prog)_SRC += tests/lib.c)) | ||
| 23 | |||
| 24 | tests/intro/userprog-args/args-single_ARGS = onearg | ||
| 25 | tests/intro/userprog-args/args-multiple_ARGS = some arguments for you! | ||
| 26 | tests/intro/userprog-args/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 | ||
| 27 | tests/intro/userprog-args/args-dbl-space_ARGS = two spaces! | ||
diff --git a/pintos-progos/tests/intro/userprog-args/Rubric b/pintos-progos/tests/intro/userprog-args/Rubric new file mode 100644 index 0000000..f5474c5 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/Rubric | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | Functionality of stack setup: | ||
| 2 | 3 args-none | ||
| 3 | 3 args-single | ||
| 4 | 3 args-multiple | ||
| 5 | 3 args-many | ||
| 6 | 3 args-dbl-space | ||
| 7 | 3 args-limit | ||
diff --git a/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck new file mode 120000 index 0000000..730d787 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-dbl-space.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.c b/pintos-progos/tests/intro/userprog-args/args-limit.c new file mode 100644 index 0000000..159d868 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-limit.c | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | /* Test the limit for (1) number of arguments and (2) total size of arguments */ | ||
| 2 | #include <syscall.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | |||
| 6 | #define MAX_SIZE 4096 | ||
| 7 | |||
| 8 | static bool recurse (int, int); | ||
| 9 | |||
| 10 | char cmd[MAX_SIZE * 4]; | ||
| 11 | |||
| 12 | static bool | ||
| 13 | recurse (int argsize, int argcount) | ||
| 14 | { | ||
| 15 | int i, j; | ||
| 16 | char *p; | ||
| 17 | strlcpy (cmd, "args-limit", 11); | ||
| 18 | p = cmd+strlen(cmd); | ||
| 19 | for (i = 0; i < argcount; i++) { | ||
| 20 | *p++ = ' '; | ||
| 21 | for (j = 0; j < argsize; j++) { | ||
| 22 | *p++ = 'X'; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | *p = 0; | ||
| 26 | if (wait (exec (cmd)) < 0) { | ||
| 27 | return false; | ||
| 28 | } else { | ||
| 29 | return true; | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | int | ||
| 34 | main (int argc, char **argv) | ||
| 35 | { | ||
| 36 | test_name = argv[0]; | ||
| 37 | if(argc <= 1) { | ||
| 38 | int step; | ||
| 39 | int max_args = 0, max_size = 0; | ||
| 40 | |||
| 41 | msg ("begin"); | ||
| 42 | |||
| 43 | /* Binary search number of arguments */ | ||
| 44 | for (step = MAX_SIZE; step > 0 && max_args < MAX_SIZE; step>>=1) { | ||
| 45 | int t = max_args + step; | ||
| 46 | if (recurse (1, t)) { | ||
| 47 | max_args = t; | ||
| 48 | } | ||
| 49 | } | ||
| 50 | if (max_args > 63) { | ||
| 51 | msg ("success. at least 64 command line arguments are supported."); | ||
| 52 | } else { | ||
| 53 | msg ("FAIL: Only %d command line arguments are supported",max_args); | ||
| 54 | } | ||
| 55 | /* Binary search size of arguments */ | ||
| 56 | for (step = MAX_SIZE; step > 0 && max_size < MAX_SIZE; step>>=1) { | ||
| 57 | int t = max_size + step; | ||
| 58 | if (recurse (t, 1)) { | ||
| 59 | max_size = t; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | if (max_size >= 100) { | ||
| 63 | msg ("success. arguments with at least 100 bytes are supported."); | ||
| 64 | } else { | ||
| 65 | msg ("FAIL: Arguments with more than %d bytes are not supported.",max_size); | ||
| 66 | } | ||
| 67 | msg ("end"); | ||
| 68 | } | ||
| 69 | return 0; | ||
| 70 | } | ||
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.ck b/pintos-progos/tests/intro/userprog-args/args-limit.ck new file mode 100644 index 0000000..ed8ead2 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-limit.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (args-limit) begin | ||
| 7 | (args-limit) success. at least 64 command line arguments are supported. | ||
| 8 | (args-limit) success. arguments with at least 100 bytes are supported. | ||
| 9 | (args-limit) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/intro/userprog-args/args-many.ck b/pintos-progos/tests/intro/userprog-args/args-many.ck new file mode 120000 index 0000000..ed175bd --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-many.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-many.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-multiple.ck b/pintos-progos/tests/intro/userprog-args/args-multiple.ck new file mode 120000 index 0000000..4f9935f --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-multiple.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-multiple.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-none.ck b/pintos-progos/tests/intro/userprog-args/args-none.ck new file mode 120000 index 0000000..861d319 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-none.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-none.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args-single.ck b/pintos-progos/tests/intro/userprog-args/args-single.ck new file mode 120000 index 0000000..df8e737 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args-single.ck | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args-single.ck \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/args.c b/pintos-progos/tests/intro/userprog-args/args.c new file mode 120000 index 0000000..0d798ac --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/args.c | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/args.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/intro/userprog-args/child-simple.c b/pintos-progos/tests/intro/userprog-args/child-simple.c new file mode 120000 index 0000000..f8a3ce7 --- /dev/null +++ b/pintos-progos/tests/intro/userprog-args/child-simple.c | |||
| @@ -0,0 +1 @@ | |||
| ../../userprog/child-simple.c \ No newline at end of file | |||
diff --git a/pintos-progos/tests/lib.c b/pintos-progos/tests/lib.c new file mode 100644 index 0000000..ee36505 --- /dev/null +++ b/pintos-progos/tests/lib.c | |||
| @@ -0,0 +1,196 @@ | |||
| 1 | #include "tests/lib.h" | ||
| 2 | #include <random.h> | ||
| 3 | #include <stdarg.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | |||
| 8 | const char *test_name; | ||
| 9 | bool quiet = false; | ||
| 10 | |||
| 11 | static void | ||
| 12 | vmsg (const char *format, va_list args, const char *suffix) | ||
| 13 | { | ||
| 14 | /* We go to some trouble to stuff the entire message into a | ||
| 15 | single buffer and output it in a single system call, because | ||
| 16 | that'll (typically) ensure that it gets sent to the console | ||
| 17 | atomically. Otherwise kernel messages like "foo: exit(0)" | ||
| 18 | can end up being interleaved if we're unlucky. */ | ||
| 19 | static char buf[1024]; | ||
| 20 | |||
| 21 | snprintf (buf, sizeof buf, "(%s) ", test_name); | ||
| 22 | vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args); | ||
| 23 | strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf)); | ||
| 24 | write (STDOUT_FILENO, buf, strlen (buf)); | ||
| 25 | } | ||
| 26 | |||
| 27 | void | ||
| 28 | msg (const char *format, ...) | ||
| 29 | { | ||
| 30 | va_list args; | ||
| 31 | |||
| 32 | if (quiet) | ||
| 33 | return; | ||
| 34 | va_start (args, format); | ||
| 35 | vmsg (format, args, "\n"); | ||
| 36 | va_end (args); | ||
| 37 | } | ||
| 38 | |||
| 39 | void | ||
| 40 | fail (const char *format, ...) | ||
| 41 | { | ||
| 42 | va_list args; | ||
| 43 | |||
| 44 | va_start (args, format); | ||
| 45 | vmsg (format, args, ": FAILED\n"); | ||
| 46 | va_end (args); | ||
| 47 | |||
| 48 | exit (1); | ||
| 49 | } | ||
| 50 | |||
| 51 | static void | ||
| 52 | swap (void *a_, void *b_, size_t size) | ||
| 53 | { | ||
| 54 | uint8_t *a = a_; | ||
| 55 | uint8_t *b = b_; | ||
| 56 | size_t i; | ||
| 57 | |||
| 58 | for (i = 0; i < size; i++) | ||
| 59 | { | ||
| 60 | uint8_t t = a[i]; | ||
| 61 | a[i] = b[i]; | ||
| 62 | b[i] = t; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void | ||
| 67 | shuffle (void *buf_, size_t cnt, size_t size) | ||
| 68 | { | ||
| 69 | char *buf = buf_; | ||
| 70 | size_t i; | ||
| 71 | |||
| 72 | for (i = 0; i < cnt; i++) | ||
| 73 | { | ||
| 74 | size_t j = i + random_ulong () % (cnt - i); | ||
| 75 | swap (buf + i * size, buf + j * size, size); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | void | ||
| 80 | exec_children (const char *child_name, pid_t pids[], size_t child_cnt) | ||
| 81 | { | ||
| 82 | size_t i; | ||
| 83 | |||
| 84 | for (i = 0; i < child_cnt; i++) | ||
| 85 | { | ||
| 86 | char cmd_line[128]; | ||
| 87 | snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i); | ||
| 88 | CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR, | ||
| 89 | "exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | void | ||
| 94 | wait_children (pid_t pids[], size_t child_cnt) | ||
| 95 | { | ||
| 96 | size_t i; | ||
| 97 | |||
| 98 | for (i = 0; i < child_cnt; i++) | ||
| 99 | { | ||
| 100 | int status = wait (pids[i]); | ||
| 101 | CHECK (status == (int) i, | ||
| 102 | "wait for child %zu of %zu returned %d (expected %zu)", | ||
| 103 | i + 1, child_cnt, status, i); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | void | ||
| 108 | check_file_handle (int fd, | ||
| 109 | const char *file_name, const void *buf_, size_t size) | ||
| 110 | { | ||
| 111 | const char *buf = buf_; | ||
| 112 | size_t ofs = 0; | ||
| 113 | size_t file_size; | ||
| 114 | |||
| 115 | /* Warn about file of wrong size. Don't fail yet because we | ||
| 116 | may still be able to get more information by reading the | ||
| 117 | file. */ | ||
| 118 | file_size = filesize (fd); | ||
| 119 | if (file_size != size) | ||
| 120 | msg ("size of %s (%zu) differs from expected (%zu)", | ||
| 121 | file_name, file_size, size); | ||
| 122 | |||
| 123 | /* Read the file block-by-block, comparing data as we go. */ | ||
| 124 | while (ofs < size) | ||
| 125 | { | ||
| 126 | char block[512]; | ||
| 127 | size_t block_size, ret_val; | ||
| 128 | |||
| 129 | block_size = size - ofs; | ||
| 130 | if (block_size > sizeof block) | ||
| 131 | block_size = sizeof block; | ||
| 132 | |||
| 133 | ret_val = read (fd, block, block_size); | ||
| 134 | if (ret_val != block_size) | ||
| 135 | fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu", | ||
| 136 | block_size, ofs, file_name, ret_val); | ||
| 137 | |||
| 138 | compare_bytes (block, buf + ofs, block_size, ofs, file_name); | ||
| 139 | ofs += block_size; | ||
| 140 | } | ||
| 141 | |||
| 142 | /* Now fail due to wrong file size. */ | ||
| 143 | if (file_size != size) | ||
| 144 | fail ("size of %s (%zu) differs from expected (%zu)", | ||
| 145 | file_name, file_size, size); | ||
| 146 | |||
| 147 | msg ("verified contents of \"%s\"", file_name); | ||
| 148 | } | ||
| 149 | |||
| 150 | void | ||
| 151 | check_file (const char *file_name, const void *buf, size_t size) | ||
| 152 | { | ||
| 153 | int fd; | ||
| 154 | |||
| 155 | CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification", | ||
| 156 | file_name); | ||
| 157 | check_file_handle (fd, file_name, buf, size); | ||
| 158 | msg ("close \"%s\"", file_name); | ||
| 159 | close (fd); | ||
| 160 | } | ||
| 161 | |||
| 162 | void | ||
| 163 | compare_bytes (const void *read_data_, const void *expected_data_, size_t size, | ||
| 164 | size_t ofs, const char *file_name) | ||
| 165 | { | ||
| 166 | const uint8_t *read_data = read_data_; | ||
| 167 | const uint8_t *expected_data = expected_data_; | ||
| 168 | size_t i, j; | ||
| 169 | size_t show_cnt; | ||
| 170 | |||
| 171 | if (!memcmp (read_data, expected_data, size)) | ||
| 172 | return; | ||
| 173 | |||
| 174 | for (i = 0; i < size; i++) | ||
| 175 | if (read_data[i] != expected_data[i]) | ||
| 176 | break; | ||
| 177 | for (j = i + 1; j < size; j++) | ||
| 178 | if (read_data[j] == expected_data[j]) | ||
| 179 | break; | ||
| 180 | |||
| 181 | quiet = false; | ||
| 182 | msg ("%zu bytes read starting at offset %zu in \"%s\" differ " | ||
| 183 | "from expected.", j - i, ofs + i, file_name); | ||
| 184 | show_cnt = j - i; | ||
| 185 | if (j - i > 64) | ||
| 186 | { | ||
| 187 | show_cnt = 64; | ||
| 188 | msg ("Showing first differing %zu bytes.", show_cnt); | ||
| 189 | } | ||
| 190 | msg ("Data actually read:"); | ||
| 191 | hex_dump (ofs + i, read_data + i, show_cnt, true); | ||
| 192 | msg ("Expected data:"); | ||
| 193 | hex_dump (ofs + i, expected_data + i, show_cnt, true); | ||
| 194 | fail ("%zu bytes read starting at offset %zu in \"%s\" differ " | ||
| 195 | "from expected", j - i, ofs + i, file_name); | ||
| 196 | } | ||
diff --git a/pintos-progos/tests/lib.h b/pintos-progos/tests/lib.h new file mode 100644 index 0000000..648327b --- /dev/null +++ b/pintos-progos/tests/lib.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | #ifndef TESTS_LIB_H | ||
| 2 | #define TESTS_LIB_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | |||
| 9 | extern const char *test_name; | ||
| 10 | extern bool quiet; | ||
| 11 | |||
| 12 | void msg (const char *, ...) PRINTF_FORMAT (1, 2); | ||
| 13 | void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN; | ||
| 14 | |||
| 15 | /* Takes an expression to test for SUCCESS and a message, which | ||
| 16 | may include printf-style arguments. Logs the message, then | ||
| 17 | tests the expression. If it is zero, indicating failure, | ||
| 18 | emits the message as a failure. | ||
| 19 | |||
| 20 | Somewhat tricky to use: | ||
| 21 | |||
| 22 | - SUCCESS must not have side effects that affect the | ||
| 23 | message, because that will cause the original message and | ||
| 24 | the failure message to differ. | ||
| 25 | |||
| 26 | - The message must not have side effects of its own, because | ||
| 27 | it will be printed twice on failure, or zero times on | ||
| 28 | success if quiet is set. */ | ||
| 29 | #define CHECK(SUCCESS, ...) \ | ||
| 30 | do \ | ||
| 31 | { \ | ||
| 32 | msg (__VA_ARGS__); \ | ||
| 33 | if (!(SUCCESS)) \ | ||
| 34 | fail (__VA_ARGS__); \ | ||
| 35 | } \ | ||
| 36 | while (0) | ||
| 37 | |||
| 38 | void shuffle (void *, size_t cnt, size_t size); | ||
| 39 | |||
| 40 | void exec_children (const char *child_name, pid_t pids[], size_t child_cnt); | ||
| 41 | void wait_children (pid_t pids[], size_t child_cnt); | ||
| 42 | |||
| 43 | void check_file_handle (int fd, const char *file_name, | ||
| 44 | const void *buf_, size_t filesize); | ||
| 45 | void check_file (const char *file_name, const void *buf, size_t filesize); | ||
| 46 | |||
| 47 | void compare_bytes (const void *read_data, const void *expected_data, | ||
| 48 | size_t size, size_t ofs, const char *file_name); | ||
| 49 | |||
| 50 | #endif /* test/lib.h */ | ||
diff --git a/pintos-progos/tests/lib.pm b/pintos-progos/tests/lib.pm new file mode 100644 index 0000000..bc37ae5 --- /dev/null +++ b/pintos-progos/tests/lib.pm | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | |||
| 4 | use tests::random; | ||
| 5 | |||
| 6 | sub shuffle { | ||
| 7 | my ($in, $cnt, $sz) = @_; | ||
| 8 | $cnt * $sz == length $in or die; | ||
| 9 | my (@a) = 0...$cnt - 1; | ||
| 10 | for my $i (0...$cnt - 1) { | ||
| 11 | my ($j) = $i + random_ulong () % ($cnt - $i); | ||
| 12 | @a[$i, $j] = @a[$j, $i]; | ||
| 13 | } | ||
| 14 | my ($out) = ""; | ||
| 15 | $out .= substr ($in, $_ * $sz, $sz) foreach @a; | ||
| 16 | return $out; | ||
| 17 | } | ||
| 18 | |||
| 19 | 1; | ||
diff --git a/pintos-progos/tests/main.c b/pintos-progos/tests/main.c new file mode 100644 index 0000000..ad1b0f1 --- /dev/null +++ b/pintos-progos/tests/main.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #include <random.h> | ||
| 2 | #include "tests/lib.h" | ||
| 3 | #include "tests/main.h" | ||
| 4 | |||
| 5 | int | ||
| 6 | main (int argc UNUSED, char *argv[]) | ||
| 7 | { | ||
| 8 | test_name = argv[0]; | ||
| 9 | |||
| 10 | msg ("begin"); | ||
| 11 | random_init (0); | ||
| 12 | test_main (); | ||
| 13 | msg ("end"); | ||
| 14 | return 0; | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/main.h b/pintos-progos/tests/main.h new file mode 100644 index 0000000..f0e8818 --- /dev/null +++ b/pintos-progos/tests/main.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef TESTS_MAIN_H | ||
| 2 | #define TESTS_MAIN_H | ||
| 3 | |||
| 4 | void test_main (void); | ||
| 5 | |||
| 6 | #endif /* tests/main.h */ | ||
diff --git a/pintos-progos/tests/make-grade b/pintos-progos/tests/make-grade new file mode 100755 index 0000000..a3faa0e --- /dev/null +++ b/pintos-progos/tests/make-grade | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | #! /usr/bin/perl | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use warnings; | ||
| 5 | |||
| 6 | @ARGV == 3 || die; | ||
| 7 | my ($src_dir, $results_file, $grading_file) = @ARGV; | ||
| 8 | |||
| 9 | # Read pass/file verdicts from $results_file. | ||
| 10 | open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n"; | ||
| 11 | my (%verdicts, %verdict_counts); | ||
| 12 | while (<RESULTS>) { | ||
| 13 | my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die; | ||
| 14 | $verdicts{$test} = $verdict eq 'pass'; | ||
| 15 | } | ||
| 16 | close RESULTS; | ||
| 17 | |||
| 18 | my (@failures); | ||
| 19 | my (@overall, @rubrics, @summary); | ||
| 20 | my ($pct_actual, $pct_possible) = (0, 0); | ||
| 21 | |||
| 22 | # Read grading file. | ||
| 23 | my (@items); | ||
| 24 | open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n"; | ||
| 25 | while (<GRADING>) { | ||
| 26 | s/#.*//; | ||
| 27 | next if /^\s*$/; | ||
| 28 | my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die; | ||
| 29 | my ($dir) = $rubric_suffix =~ /^(.*)\//; | ||
| 30 | my ($rubric_file) = "$src_dir/$rubric_suffix"; | ||
| 31 | open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n"; | ||
| 32 | |||
| 33 | # Rubric file must begin with title line. | ||
| 34 | my $title = <RUBRIC>; | ||
| 35 | chomp $title; | ||
| 36 | $title =~ s/:$// or die; | ||
| 37 | $title .= " ($rubric_suffix):"; | ||
| 38 | push (@rubrics, $title); | ||
| 39 | |||
| 40 | my ($score, $possible) = (0, 0); | ||
| 41 | my ($cnt, $passed) = (0, 0); | ||
| 42 | my ($was_score) = 0; | ||
| 43 | while (<RUBRIC>) { | ||
| 44 | chomp; | ||
| 45 | push (@rubrics, "\t$_"), next if /^-/; | ||
| 46 | push (@rubrics, ""), next if /^\s*$/; | ||
| 47 | my ($poss, $name) = /^(\d+)\t(.*)$/ or die; | ||
| 48 | my ($test) = "$dir/$name"; | ||
| 49 | my ($points) = 0; | ||
| 50 | if (!defined $verdicts{$test}) { | ||
| 51 | push (@overall, "warning: $test not tested, assuming failure"); | ||
| 52 | } elsif ($verdicts{$test}) { | ||
| 53 | $points = $poss; | ||
| 54 | $passed++; | ||
| 55 | } | ||
| 56 | push (@failures, $test) if !$points; | ||
| 57 | $verdict_counts{$test}++; | ||
| 58 | push (@rubrics, sprintf ("\t%4s%2d/%2d %s", | ||
| 59 | $points ? '' : '**', $points, $poss, $test)); | ||
| 60 | $score += $points; | ||
| 61 | $possible += $poss; | ||
| 62 | $cnt++; | ||
| 63 | } | ||
| 64 | close (RUBRIC); | ||
| 65 | |||
| 66 | push (@rubrics, ""); | ||
| 67 | push (@rubrics, "\t- Section summary."); | ||
| 68 | push (@rubrics, sprintf ("\t%4s%3d/%3d %s", | ||
| 69 | '', $passed, $cnt, 'tests passed')); | ||
| 70 | push (@rubrics, sprintf ("\t%4s%3d/%3d %s", | ||
| 71 | '', $score, $possible, 'points subtotal')); | ||
| 72 | push (@rubrics, ''); | ||
| 73 | |||
| 74 | my ($pct) = ($score / $possible) * $max_pct; | ||
| 75 | push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%", | ||
| 76 | $rubric_suffix, | ||
| 77 | $score, $possible, | ||
| 78 | $pct, $max_pct)); | ||
| 79 | $pct_actual += $pct; | ||
| 80 | $pct_possible += $max_pct; | ||
| 81 | } | ||
| 82 | close GRADING; | ||
| 83 | |||
| 84 | my ($sum_line) | ||
| 85 | = "--------------------------------------------- --- --- ------ ------"; | ||
| 86 | unshift (@summary, | ||
| 87 | "SUMMARY BY TEST SET", | ||
| 88 | '', | ||
| 89 | sprintf ("%-45s %3s %3s %6s %6s", | ||
| 90 | "Test Set", "Pts", "Max", "% Ttl", "% Max"), | ||
| 91 | $sum_line); | ||
| 92 | push (@summary, | ||
| 93 | $sum_line, | ||
| 94 | sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%", | ||
| 95 | 'Total', '', '', $pct_actual, $pct_possible)); | ||
| 96 | |||
| 97 | unshift (@rubrics, | ||
| 98 | "SUMMARY OF INDIVIDUAL TESTS", | ||
| 99 | ''); | ||
| 100 | |||
| 101 | foreach my $name (keys (%verdicts)) { | ||
| 102 | my ($count) = $verdict_counts{$name}; | ||
| 103 | if (!defined ($count) || $count != 1) { | ||
| 104 | if (!defined ($count) || !$count) { | ||
| 105 | push (@overall, "warning: test $name doesn't count for grading"); | ||
| 106 | } else { | ||
| 107 | push (@overall, | ||
| 108 | "warning: test $name counted $count times in grading"); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual)); | ||
| 113 | if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) { | ||
| 114 | push (@overall, "ALL TESTED PASSED -- PERFECT SCORE"); | ||
| 115 | } | ||
| 116 | |||
| 117 | my (@divider) = ('', '- ' x 38, ''); | ||
| 118 | |||
| 119 | print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics); | ||
| 120 | |||
| 121 | for my $test (@failures) { | ||
| 122 | print map ("$_\n", @divider); | ||
| 123 | print "DETAILS OF $test FAILURE:\n\n"; | ||
| 124 | |||
| 125 | if (open (RESULT, '<', "$test.result")) { | ||
| 126 | my $first_line = <RESULT>; | ||
| 127 | my ($cnt) = 0; | ||
| 128 | while (<RESULT>) { | ||
| 129 | print; | ||
| 130 | $cnt++; | ||
| 131 | } | ||
| 132 | close (RESULT); | ||
| 133 | } | ||
| 134 | |||
| 135 | if (open (OUTPUT, '<', "$test.output")) { | ||
| 136 | print "\nOUTPUT FROM $test:\n\n"; | ||
| 137 | |||
| 138 | my ($panics, $boots) = (0, 0); | ||
| 139 | while (<OUTPUT>) { | ||
| 140 | if (/PANIC/ && ++$panics > 2) { | ||
| 141 | print "[...details of additional panic(s) omitted...]\n"; | ||
| 142 | last; | ||
| 143 | } | ||
| 144 | print; | ||
| 145 | if (/Pintos booting/ && ++$boots > 1) { | ||
| 146 | print "[...details of reboot(s) omitted...]\n"; | ||
| 147 | last; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | close (OUTPUT); | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/pintos-progos/tests/random.pm b/pintos-progos/tests/random.pm new file mode 100644 index 0000000..be008ff --- /dev/null +++ b/pintos-progos/tests/random.pm | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | |||
| 4 | use tests::arc4; | ||
| 5 | |||
| 6 | my (@arc4); | ||
| 7 | |||
| 8 | sub random_init { | ||
| 9 | if (@arc4 == 0) { | ||
| 10 | my ($seed) = @_; | ||
| 11 | $seed = 0 if !defined $seed; | ||
| 12 | @arc4 = arc4_init (pack ("V", $seed)); | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | sub random_bytes { | ||
| 17 | random_init (); | ||
| 18 | my ($n) = @_; | ||
| 19 | return arc4_crypt (\@arc4, "\0" x $n); | ||
| 20 | } | ||
| 21 | |||
| 22 | sub random_ulong { | ||
| 23 | random_init (); | ||
| 24 | return unpack ("V", random_bytes (4)); | ||
| 25 | } | ||
| 26 | |||
| 27 | 1; | ||
diff --git a/pintos-progos/tests/tests.pm b/pintos-progos/tests/tests.pm new file mode 100644 index 0000000..4599cb9 --- /dev/null +++ b/pintos-progos/tests/tests.pm | |||
| @@ -0,0 +1,625 @@ | |||
| 1 | use strict; | ||
| 2 | use warnings; | ||
| 3 | use tests::Algorithm::Diff; | ||
| 4 | use File::Temp 'tempfile'; | ||
| 5 | use Fcntl qw(SEEK_SET SEEK_CUR); | ||
| 6 | |||
| 7 | sub fail; | ||
| 8 | sub pass; | ||
| 9 | |||
| 10 | die if @ARGV != 2; | ||
| 11 | our ($test, $src_dir) = @ARGV; | ||
| 12 | |||
| 13 | my ($msg_file) = tempfile (); | ||
| 14 | select ($msg_file); | ||
| 15 | |||
| 16 | our (@prereq_tests) = (); | ||
| 17 | if ($test =~ /^(.*)-persistence$/) { | ||
| 18 | push (@prereq_tests, $1); | ||
| 19 | } | ||
| 20 | for my $prereq_test (@prereq_tests) { | ||
| 21 | my (@result) = read_text_file ("$prereq_test.result"); | ||
| 22 | fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS'; | ||
| 23 | } | ||
| 24 | |||
| 25 | |||
| 26 | # Generic testing. | ||
| 27 | |||
| 28 | sub check_expected { | ||
| 29 | my ($expected) = pop @_; | ||
| 30 | my (@options) = @_; | ||
| 31 | my (@output) = read_text_file ("$test.output"); | ||
| 32 | common_checks ("run", @output); | ||
| 33 | compare_output ("run", @options, \@output, $expected); | ||
| 34 | } | ||
| 35 | |||
| 36 | sub common_checks { | ||
| 37 | my ($run, @output) = @_; | ||
| 38 | |||
| 39 | fail "\u$run produced no output at all\n" if @output == 0; | ||
| 40 | |||
| 41 | check_for_panic ($run, @output); | ||
| 42 | check_for_keyword ($run, "FAIL", @output); | ||
| 43 | check_for_triple_fault ($run, @output); | ||
| 44 | check_for_keyword ($run, "TIMEOUT", @output); | ||
| 45 | |||
| 46 | fail "\u$run didn't start up properly: no \"Pintos booting\" message\n" | ||
| 47 | if !grep (/Pintos booting with.*kB RAM\.\.\./, @output); | ||
| 48 | fail "\u$run didn't start up properly: no \"Boot complete\" message\n" | ||
| 49 | if !grep (/Boot complete/, @output); | ||
| 50 | fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n" | ||
| 51 | if !grep (/Timer: \d+ ticks/, @output); | ||
| 52 | fail "\u$run didn't shut down properly: no \"Powering off\" message\n" | ||
| 53 | if !grep (/Powering off/, @output); | ||
| 54 | } | ||
| 55 | |||
| 56 | sub check_for_panic { | ||
| 57 | my ($run, @output) = @_; | ||
| 58 | |||
| 59 | my ($panic) = grep (/PANIC/, @output); | ||
| 60 | return unless defined $panic; | ||
| 61 | |||
| 62 | print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")), | ||
| 63 | "\n"; | ||
| 64 | |||
| 65 | my (@stack_line) = grep (/Call stack:/, @output); | ||
| 66 | if (@stack_line != 0) { | ||
| 67 | my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/; | ||
| 68 | |||
| 69 | # Find a user program to translate user virtual addresses. | ||
| 70 | my ($userprog) = ""; | ||
| 71 | $userprog = "$test" | ||
| 72 | if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test; | ||
| 73 | |||
| 74 | # Get and print the backtrace. | ||
| 75 | my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`); | ||
| 76 | print "Call stack:$addrs\n"; | ||
| 77 | print "Translation of call stack:\n"; | ||
| 78 | print $trace; | ||
| 79 | |||
| 80 | # Print disclaimer. | ||
| 81 | if ($userprog ne '' && index ($trace, $userprog) >= 0) { | ||
| 82 | print <<EOF; | ||
| 83 | Translations of user virtual addresses above are based on a guess at | ||
| 84 | the binary to use. If this guess is incorrect, then those | ||
| 85 | translations will be misleading. | ||
| 86 | EOF | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | if ($panic =~ /sec_no \< d-\>capacity/) { | ||
| 91 | print <<EOF; | ||
| 92 | \nThis assertion commonly fails when accessing a file via an inode that | ||
| 93 | has been closed and freed. Freeing an inode clears all its sector | ||
| 94 | indexes to 0xcccccccc, which is not a valid sector number for disks | ||
| 95 | smaller than about 1.6 TB. | ||
| 96 | EOF | ||
| 97 | } | ||
| 98 | |||
| 99 | fail; | ||
| 100 | } | ||
| 101 | |||
| 102 | sub check_for_keyword { | ||
| 103 | my ($run, $keyword, @output) = @_; | ||
| 104 | |||
| 105 | my ($kw_line) = grep (/$keyword/, @output); | ||
| 106 | return unless defined $kw_line; | ||
| 107 | |||
| 108 | # Most output lines are prefixed by (test-name). Eliminate this | ||
| 109 | # from our message for brevity. | ||
| 110 | $kw_line =~ s/^\([^\)]+\)\s+//; | ||
| 111 | print "$run: $kw_line\n"; | ||
| 112 | |||
| 113 | fail; | ||
| 114 | } | ||
| 115 | |||
| 116 | sub check_for_triple_fault { | ||
| 117 | my ($run, @output) = @_; | ||
| 118 | |||
| 119 | my ($reboots) = grep (/Pintos booting/, @output) - 1; | ||
| 120 | return unless $reboots > 0; | ||
| 121 | |||
| 122 | print <<EOF; | ||
| 123 | \u$run spontaneously rebooted $reboots times. | ||
| 124 | This is most often caused by unhandled page faults. | ||
| 125 | Read the Triple Faults section in the Debugging chapter | ||
| 126 | of the Pintos manual for more information. | ||
| 127 | EOF | ||
| 128 | |||
| 129 | fail; | ||
| 130 | } | ||
| 131 | |||
| 132 | # Get @output without header or trailer. | ||
| 133 | sub get_core_output { | ||
| 134 | my ($run, @output) = @_; | ||
| 135 | my ($p); | ||
| 136 | |||
| 137 | my ($process); | ||
| 138 | my ($start); | ||
| 139 | for my $i (0...$#_) { | ||
| 140 | $start = $i + 1, last | ||
| 141 | if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/; | ||
| 142 | } | ||
| 143 | |||
| 144 | my ($end); | ||
| 145 | for my $i ($start...$#output) { | ||
| 146 | $end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/; | ||
| 147 | } | ||
| 148 | |||
| 149 | fail "\u$run didn't start a thread or process\n" if !defined $start; | ||
| 150 | fail "\u$run started '$process' but it never finished\n" if !defined $end; | ||
| 151 | |||
| 152 | return @output[$start...$end]; | ||
| 153 | } | ||
| 154 | |||
| 155 | sub compare_output { | ||
| 156 | my ($run) = shift @_; | ||
| 157 | my ($expected) = pop @_; | ||
| 158 | my ($output) = pop @_; | ||
| 159 | my (%options) = @_; | ||
| 160 | |||
| 161 | my (@output) = get_core_output ($run, @$output); | ||
| 162 | fail "\u$run didn't produce any output" if !@output; | ||
| 163 | |||
| 164 | my $ignore_exit_codes = exists $options{IGNORE_EXIT_CODES}; | ||
| 165 | if ($ignore_exit_codes) { | ||
| 166 | delete $options{IGNORE_EXIT_CODES}; | ||
| 167 | @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\-?\d+\)$/, @output); | ||
| 168 | } | ||
| 169 | my $ignore_user_faults = exists $options{IGNORE_USER_FAULTS}; | ||
| 170 | if ($ignore_user_faults) { | ||
| 171 | delete $options{IGNORE_USER_FAULTS}; | ||
| 172 | @output = grep (!/^Page fault at.*in user context\.$/ | ||
| 173 | && !/: dying due to interrupt 0x0e \(.*\).$/ | ||
| 174 | && !/^Interrupt 0x0e \(.*\) at eip=/ | ||
| 175 | && !/^ cr2=.* error=.*/ | ||
| 176 | && !/^ eax=.* ebx=.* ecx=.* edx=.*/ | ||
| 177 | && !/^ esi=.* edi=.* esp=.* ebp=.*/ | ||
| 178 | && !/^ cs=.* ds=.* es=.* ss=.*/, @output); | ||
| 179 | } | ||
| 180 | die "unknown option " . (keys (%options))[0] . "\n" if %options; | ||
| 181 | |||
| 182 | my ($msg); | ||
| 183 | |||
| 184 | # Compare actual output against each allowed output. | ||
| 185 | if (ref ($expected) eq 'ARRAY') { | ||
| 186 | my ($i) = 0; | ||
| 187 | $expected = {map ((++$i => $_), @$expected)}; | ||
| 188 | } | ||
| 189 | foreach my $key (keys %$expected) { | ||
| 190 | my (@expected) = split ("\n", $expected->{$key}); | ||
| 191 | |||
| 192 | $msg .= "Acceptable output:\n"; | ||
| 193 | $msg .= join ('', map (" $_\n", @expected)); | ||
| 194 | |||
| 195 | # Check whether actual and expected match. | ||
| 196 | # If it's a perfect match, we're done. | ||
| 197 | if ($#output == $#expected) { | ||
| 198 | my ($eq) = 1; | ||
| 199 | for (my ($i) = 0; $i <= $#expected; $i++) { | ||
| 200 | $eq = 0 if $output[$i] ne $expected[$i]; | ||
| 201 | } | ||
| 202 | return $key if $eq; | ||
| 203 | } | ||
| 204 | |||
| 205 | # They differ. Output a diff. | ||
| 206 | my (@diff) = ""; | ||
| 207 | my ($d) = Algorithm::Diff->new (\@expected, \@output); | ||
| 208 | while ($d->Next ()) { | ||
| 209 | my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2)); | ||
| 210 | if ($d->Same ()) { | ||
| 211 | push (@diff, map (" $_\n", $d->Items (1))); | ||
| 212 | } else { | ||
| 213 | push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1); | ||
| 214 | push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | $msg .= "Differences in `diff -u' format:\n"; | ||
| 219 | $msg .= join ('', @diff); | ||
| 220 | } | ||
| 221 | |||
| 222 | # Failed to match. Report failure. | ||
| 223 | $msg .= "\n(Process exit codes are excluded for matching purposes.)\n" | ||
| 224 | if $ignore_exit_codes; | ||
| 225 | $msg .= "\n(User fault messages are excluded for matching purposes.)\n" | ||
| 226 | if $ignore_user_faults; | ||
| 227 | fail "Test output failed to match any acceptable form.\n\n$msg"; | ||
| 228 | } | ||
| 229 | |||
| 230 | # File system extraction. | ||
| 231 | |||
| 232 | # check_archive (\%CONTENTS) | ||
| 233 | # | ||
| 234 | # Checks that the extracted file system's contents match \%CONTENTS. | ||
| 235 | # Each key in the hash is a file name. Each value may be: | ||
| 236 | # | ||
| 237 | # - $FILE: Name of a host file containing the expected contents. | ||
| 238 | # | ||
| 239 | # - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE | ||
| 240 | # comprising the $LENGTH bytes starting at $OFFSET. | ||
| 241 | # | ||
| 242 | # - [$CONTENTS]: The literal expected file contents, as a string. | ||
| 243 | # | ||
| 244 | # - {SUBDIR}: A subdirectory, in the same form described here, | ||
| 245 | # recursively. | ||
| 246 | sub check_archive { | ||
| 247 | my ($expected_hier) = @_; | ||
| 248 | |||
| 249 | my (@output) = read_text_file ("$test.output"); | ||
| 250 | common_checks ("file system extraction run", @output); | ||
| 251 | |||
| 252 | @output = get_core_output ("file system extraction run", @output); | ||
| 253 | @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output); | ||
| 254 | fail join ("\n", "Error extracting file system:", @output) if @output; | ||
| 255 | |||
| 256 | my ($test_base_name) = $test; | ||
| 257 | $test_base_name =~ s%.*/%%; | ||
| 258 | $test_base_name =~ s%-persistence$%%; | ||
| 259 | $expected_hier->{$test_base_name} = $prereq_tests[0]; | ||
| 260 | $expected_hier->{'tar'} = 'tests/filesys/extended/tar'; | ||
| 261 | |||
| 262 | my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, "")); | ||
| 263 | my (%actual) = read_tar ("$prereq_tests[0].tar"); | ||
| 264 | |||
| 265 | my ($errors) = 0; | ||
| 266 | foreach my $name (sort keys %expected) { | ||
| 267 | if (exists $actual{$name}) { | ||
| 268 | if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) { | ||
| 269 | print "$name is a directory but should be an ordinary file.\n"; | ||
| 270 | $errors++; | ||
| 271 | } elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) { | ||
| 272 | print "$name is an ordinary file but should be a directory.\n"; | ||
| 273 | $errors++; | ||
| 274 | } | ||
| 275 | } else { | ||
| 276 | print "$name is missing from the file system.\n"; | ||
| 277 | $errors++; | ||
| 278 | } | ||
| 279 | } | ||
| 280 | foreach my $name (sort keys %actual) { | ||
| 281 | if (!exists $expected{$name}) { | ||
| 282 | if ($name =~ /^[[:print:]]+$/) { | ||
| 283 | print "$name exists in the file system but it should not.\n"; | ||
| 284 | } else { | ||
| 285 | my ($esc_name) = $name; | ||
| 286 | $esc_name =~ s/[^[:print:]]/./g; | ||
| 287 | print <<EOF; | ||
| 288 | $esc_name exists in the file system but should not. (The name | ||
| 289 | of this file contains unusual characters that were printed as `.'.) | ||
| 290 | EOF | ||
| 291 | } | ||
| 292 | $errors++; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | if ($errors) { | ||
| 296 | print "\nActual contents of file system:\n"; | ||
| 297 | print_fs (%actual); | ||
| 298 | print "\nExpected contents of file system:\n"; | ||
| 299 | print_fs (%expected); | ||
| 300 | } else { | ||
| 301 | foreach my $name (sort keys %expected) { | ||
| 302 | if (!is_dir ($expected{$name})) { | ||
| 303 | my ($exp_file, $exp_length) = open_file ($expected{$name}); | ||
| 304 | my ($act_file, $act_length) = open_file ($actual{$name}); | ||
| 305 | $errors += !compare_files ($exp_file, $exp_length, | ||
| 306 | $act_file, $act_length, $name, | ||
| 307 | !$errors); | ||
| 308 | close ($exp_file); | ||
| 309 | close ($act_file); | ||
| 310 | } | ||
| 311 | } | ||
| 312 | } | ||
| 313 | fail "Extracted file system contents are not correct.\n" if $errors; | ||
| 314 | } | ||
| 315 | |||
| 316 | # open_file ([$FILE, $OFFSET, $LENGTH]) | ||
| 317 | # open_file ([$CONTENTS]) | ||
| 318 | # | ||
| 319 | # Opens a file for the contents passed in, which must be in one of | ||
| 320 | # the two above forms that correspond to check_archive() arguments. | ||
| 321 | # | ||
| 322 | # Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and | ||
| 323 | # $LENGTH is the number of bytes in the file's content. | ||
| 324 | sub open_file { | ||
| 325 | my ($value) = @_; | ||
| 326 | die if ref ($value) ne 'ARRAY'; | ||
| 327 | |||
| 328 | my ($file) = tempfile (); | ||
| 329 | my ($length); | ||
| 330 | if (@$value == 1) { | ||
| 331 | $length = length ($value->[0]); | ||
| 332 | $file = tempfile (); | ||
| 333 | syswrite ($file, $value->[0]) == $length | ||
| 334 | or die "writing temporary file: $!\n"; | ||
| 335 | sysseek ($file, 0, SEEK_SET); | ||
| 336 | } elsif (@$value == 3) { | ||
| 337 | $length = $value->[2]; | ||
| 338 | open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n"; | ||
| 339 | die "$value->[0]: file is smaller than expected\n" | ||
| 340 | if -s $file < $value->[1] + $length; | ||
| 341 | sysseek ($file, $value->[1], SEEK_SET); | ||
| 342 | } else { | ||
| 343 | die; | ||
| 344 | } | ||
| 345 | return ($file, $length); | ||
| 346 | } | ||
| 347 | |||
| 348 | # compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE) | ||
| 349 | # | ||
| 350 | # Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B. | ||
| 351 | # ($A and $B are handles.) | ||
| 352 | # If their contents differ, prints a brief message describing | ||
| 353 | # the differences, using $NAME to identify the file. | ||
| 354 | # The message contains more detail if $VERBOSE is nonzero. | ||
| 355 | # Returns 1 if the contents are identical, 0 otherwise. | ||
| 356 | sub compare_files { | ||
| 357 | my ($a, $a_size, $b, $b_size, $name, $verbose) = @_; | ||
| 358 | my ($ofs) = 0; | ||
| 359 | select(STDOUT); | ||
| 360 | for (;;) { | ||
| 361 | my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size; | ||
| 362 | my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size; | ||
| 363 | my ($a_data, $b_data); | ||
| 364 | if (!defined (sysread ($a, $a_data, $a_amt)) | ||
| 365 | || !defined (sysread ($b, $b_data, $b_amt))) { | ||
| 366 | die "reading $name: $!\n"; | ||
| 367 | } | ||
| 368 | |||
| 369 | my ($a_len) = length $a_data; | ||
| 370 | my ($b_len) = length $b_data; | ||
| 371 | last if $a_len == 0 && $b_len == 0; | ||
| 372 | |||
| 373 | if ($a_data ne $b_data) { | ||
| 374 | my ($min_len) = $a_len < $b_len ? $a_len : $b_len; | ||
| 375 | my ($diff_ofs); | ||
| 376 | for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) { | ||
| 377 | last if (substr ($a_data, $diff_ofs, 1) | ||
| 378 | ne substr ($b_data, $diff_ofs, 1)); | ||
| 379 | } | ||
| 380 | |||
| 381 | printf "\nFile $name differs from expected " | ||
| 382 | . "starting at offset 0x%x.\n", $ofs + $diff_ofs; | ||
| 383 | if ($verbose ) { | ||
| 384 | print "Expected contents:\n"; | ||
| 385 | hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs); | ||
| 386 | print "Actual contents:\n"; | ||
| 387 | hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs); | ||
| 388 | } | ||
| 389 | return 0; | ||
| 390 | } | ||
| 391 | |||
| 392 | $ofs += $a_len; | ||
| 393 | $a_size -= $a_len; | ||
| 394 | $b_size -= $b_len; | ||
| 395 | } | ||
| 396 | return 1; | ||
| 397 | } | ||
| 398 | |||
| 399 | # hex_dump ($DATA, $OFS) | ||
| 400 | # | ||
| 401 | # Prints $DATA in hex and text formats. | ||
| 402 | # The first byte of $DATA corresponds to logical offset $OFS | ||
| 403 | # in whatever file the data comes from. | ||
| 404 | sub hex_dump { | ||
| 405 | my ($data, $ofs) = @_; | ||
| 406 | |||
| 407 | if ($data eq '') { | ||
| 408 | printf " (File ends at offset %08x.)\n", $ofs; | ||
| 409 | return; | ||
| 410 | } | ||
| 411 | |||
| 412 | my ($per_line) = 16; | ||
| 413 | while ((my $size = length ($data)) > 0) { | ||
| 414 | my ($start) = $ofs % $per_line; | ||
| 415 | my ($end) = $per_line; | ||
| 416 | $end = $start + $size if $end - $start > $size; | ||
| 417 | my ($n) = $end - $start; | ||
| 418 | |||
| 419 | printf "0x%08x ", int ($ofs / $per_line) * $per_line; | ||
| 420 | |||
| 421 | # Hex version. | ||
| 422 | print " " x $start; | ||
| 423 | for my $i ($start...$end - 1) { | ||
| 424 | printf "%02x", ord (substr ($data, $i - $start, 1)); | ||
| 425 | print $i == $per_line / 2 - 1 ? '-' : ' '; | ||
| 426 | } | ||
| 427 | print " " x ($per_line - $end); | ||
| 428 | |||
| 429 | # Character version. | ||
| 430 | my ($esc_data) = substr ($data, 0, $n); | ||
| 431 | $esc_data =~ s/[^[:print:]]/./g; | ||
| 432 | print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|"; | ||
| 433 | |||
| 434 | print "\n"; | ||
| 435 | |||
| 436 | $data = substr ($data, $n); | ||
| 437 | $ofs += $n; | ||
| 438 | } | ||
| 439 | } | ||
| 440 | |||
| 441 | # print_fs (%FS) | ||
| 442 | # | ||
| 443 | # Prints a list of files in %FS, which must be a file system | ||
| 444 | # as flattened by flatten_hierarchy() and normalized by | ||
| 445 | # normalize_fs(). | ||
| 446 | sub print_fs { | ||
| 447 | my (%fs) = @_; | ||
| 448 | foreach my $name (sort keys %fs) { | ||
| 449 | my ($esc_name) = $name; | ||
| 450 | $esc_name =~ s/[^[:print:]]/./g; | ||
| 451 | print "$esc_name: "; | ||
| 452 | if (!is_dir ($fs{$name})) { | ||
| 453 | print +file_size ($fs{$name}), "-byte file"; | ||
| 454 | } else { | ||
| 455 | print "directory"; | ||
| 456 | } | ||
| 457 | print "\n"; | ||
| 458 | } | ||
| 459 | print "(empty)\n" if !@_; | ||
| 460 | } | ||
| 461 | |||
| 462 | # normalize_fs (%FS) | ||
| 463 | # | ||
| 464 | # Takes a file system as flattened by flatten_hierarchy(). | ||
| 465 | # Returns a similar file system in which values of the form $FILE | ||
| 466 | # are replaced by those of the form [$FILE, $OFFSET, $LENGTH]. | ||
| 467 | sub normalize_fs { | ||
| 468 | my (%fs) = @_; | ||
| 469 | foreach my $name (keys %fs) { | ||
| 470 | my ($value) = $fs{$name}; | ||
| 471 | next if is_dir ($value) || ref ($value) ne ''; | ||
| 472 | die "can't open $value\n" if !stat $value; | ||
| 473 | $fs{$name} = [$value, 0, -s _]; | ||
| 474 | } | ||
| 475 | return %fs; | ||
| 476 | } | ||
| 477 | |||
| 478 | # is_dir ($VALUE) | ||
| 479 | # | ||
| 480 | # Takes a value like one in the hash returned by flatten_hierarchy() | ||
| 481 | # and returns 1 if it represents a directory, 0 otherwise. | ||
| 482 | sub is_dir { | ||
| 483 | my ($value) = @_; | ||
| 484 | return ref ($value) eq '' && $value eq 'directory'; | ||
| 485 | } | ||
| 486 | |||
| 487 | # file_size ($VALUE) | ||
| 488 | # | ||
| 489 | # Takes a value like one in the hash returned by flatten_hierarchy() | ||
| 490 | # and returns the size of the file it represents. | ||
| 491 | sub file_size { | ||
| 492 | my ($value) = @_; | ||
| 493 | die if is_dir ($value); | ||
| 494 | die if ref ($value) ne 'ARRAY'; | ||
| 495 | return @$value > 1 ? $value->[2] : length ($value->[0]); | ||
| 496 | } | ||
| 497 | |||
| 498 | # flatten_hierarchy ($HIER_FS, $PREFIX) | ||
| 499 | # | ||
| 500 | # Takes a file system in the format expected by check_archive() and | ||
| 501 | # returns a "flattened" version in which file names include all parent | ||
| 502 | # directory names and the value of directories is just "directory". | ||
| 503 | sub flatten_hierarchy { | ||
| 504 | my (%hier_fs) = %{$_[0]}; | ||
| 505 | my ($prefix) = $_[1]; | ||
| 506 | my (%flat_fs); | ||
| 507 | for my $name (keys %hier_fs) { | ||
| 508 | my ($value) = $hier_fs{$name}; | ||
| 509 | if (ref $value eq 'HASH') { | ||
| 510 | %flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/")); | ||
| 511 | $flat_fs{"$prefix$name"} = 'directory'; | ||
| 512 | } else { | ||
| 513 | $flat_fs{"$prefix$name"} = $value; | ||
| 514 | } | ||
| 515 | } | ||
| 516 | return %flat_fs; | ||
| 517 | } | ||
| 518 | |||
| 519 | # read_tar ($ARCHIVE) | ||
| 520 | # | ||
| 521 | # Reads the ustar-format tar file in $ARCHIVE | ||
| 522 | # and returns a flattened file system for it. | ||
| 523 | sub read_tar { | ||
| 524 | my ($archive) = @_; | ||
| 525 | my (%content); | ||
| 526 | open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n"; | ||
| 527 | for (;;) { | ||
| 528 | my ($header); | ||
| 529 | if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) { | ||
| 530 | fail "$archive: unexpected end of file\n" if $retval >= 0; | ||
| 531 | fail "$archive: read: $!\n"; | ||
| 532 | } | ||
| 533 | |||
| 534 | last if $header eq "\0" x 512; | ||
| 535 | |||
| 536 | # Verify magic numbers. | ||
| 537 | if (substr ($header, 257, 6) ne "ustar\0" | ||
| 538 | || substr ($header, 263, 2) ne '00') { | ||
| 539 | fail "$archive: corrupt ustar header\n"; | ||
| 540 | } | ||
| 541 | |||
| 542 | # Verify checksum. | ||
| 543 | my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8))); | ||
| 544 | my ($correct_chksum) = unpack ("%32a*", $header); | ||
| 545 | fail "$archive: bad header checksum\n" if $chksum != $correct_chksum; | ||
| 546 | |||
| 547 | # Get file name. | ||
| 548 | my ($name) = unpack ("Z100", $header); | ||
| 549 | my ($prefix) = unpack ("Z*", substr ($header, 345)); | ||
| 550 | $name = "$prefix/$name" if $prefix ne ''; | ||
| 551 | fail "$archive: contains file with empty name" if $name eq ''; | ||
| 552 | |||
| 553 | # Get type. | ||
| 554 | my ($typeflag) = substr ($header, 156, 1); | ||
| 555 | $typeflag = '0' if $typeflag eq "\0"; | ||
| 556 | fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/; | ||
| 557 | |||
| 558 | # Get size. | ||
| 559 | my ($size) = oct (unpack ("Z*", substr ($header, 124, 12))); | ||
| 560 | fail "bad size $size\n" if $size < 0; | ||
| 561 | $size = 0 if $typeflag eq '5'; | ||
| 562 | |||
| 563 | # Store content. | ||
| 564 | $name =~ s%^(/|\./|\.\./)*%%; # Strip leading "/", "./", "../". | ||
| 565 | $name = '' if $name eq '.' || $name eq '..'; | ||
| 566 | if (exists $content{$name}) { | ||
| 567 | fail "$archive: contains multiple entries for $name\n"; | ||
| 568 | } | ||
| 569 | if ($typeflag eq '5') { | ||
| 570 | $content{$name} = 'directory' if $name ne ''; | ||
| 571 | } else { | ||
| 572 | fail "$archive: contains file with empty name\n" if $name eq ''; | ||
| 573 | my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR); | ||
| 574 | $content{$name} = [$archive, $position, $size]; | ||
| 575 | sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | close (ARCHIVE); | ||
| 579 | return %content; | ||
| 580 | } | ||
| 581 | |||
| 582 | # Utilities. | ||
| 583 | |||
| 584 | sub fail { | ||
| 585 | finish ("FAIL", @_); | ||
| 586 | } | ||
| 587 | |||
| 588 | sub pass { | ||
| 589 | finish ("PASS", @_); | ||
| 590 | } | ||
| 591 | |||
| 592 | sub finish { | ||
| 593 | my ($verdict, @messages) = @_; | ||
| 594 | |||
| 595 | seek ($msg_file, 0, 0); | ||
| 596 | push (@messages, <$msg_file>); | ||
| 597 | close ($msg_file); | ||
| 598 | chomp (@messages); | ||
| 599 | |||
| 600 | my ($result_fn) = "$test.result"; | ||
| 601 | open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n"; | ||
| 602 | print RESULT "$verdict\n"; | ||
| 603 | print RESULT "$_\n" foreach @messages; | ||
| 604 | close (RESULT); | ||
| 605 | |||
| 606 | if ($verdict eq 'PASS') { | ||
| 607 | print STDOUT "pass $test\n"; | ||
| 608 | } else { | ||
| 609 | print STDOUT "FAIL $test\n"; | ||
| 610 | } | ||
| 611 | print STDOUT "$_\n" foreach @messages; | ||
| 612 | |||
| 613 | exit 0; | ||
| 614 | } | ||
| 615 | |||
| 616 | sub read_text_file { | ||
| 617 | my ($file_name) = @_; | ||
| 618 | open (FILE, '<', $file_name) or die "$file_name: open: $!\n"; | ||
| 619 | my (@content) = <FILE>; | ||
| 620 | chomp (@content); | ||
| 621 | close (FILE); | ||
| 622 | return @content; | ||
| 623 | } | ||
| 624 | |||
| 625 | 1; | ||
diff --git a/pintos-progos/tests/threads/Grading b/pintos-progos/tests/threads/Grading new file mode 100644 index 0000000..cc235f2 --- /dev/null +++ b/pintos-progos/tests/threads/Grading | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # Priority Scheduling | ||
| 5 | 90.0% tests/threads/Rubric.priority | ||
| 6 | |||
| 7 | # Robustness | ||
| 8 | 10.0% tests/threads/Rubric.alarm | ||
| 9 | |||
| 10 | # Not used in SS 2012 | ||
| 11 | # XX.0% tests/threads/Rubric.mlfqs | ||
diff --git a/pintos-progos/tests/threads/Make.tests b/pintos-progos/tests/threads/Make.tests new file mode 100644 index 0000000..dbdfd0c --- /dev/null +++ b/pintos-progos/tests/threads/Make.tests | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | # Test names. | ||
| 4 | tests/threads_TESTS = $(addprefix tests/threads/,alarm-single \ | ||
| 5 | alarm-multiple alarm-simultaneous alarm-priority alarm-zero \ | ||
| 6 | alarm-negative priority-change priority-donate-one \ | ||
| 7 | priority-donate-multiple priority-donate-multiple2 \ | ||
| 8 | priority-donate-nest priority-donate-sema priority-donate-lower \ | ||
| 9 | priority-fifo priority-preempt priority-sema priority-condvar \ | ||
| 10 | priority-donate-chain) | ||
| 11 | |||
| 12 | |||
| 13 | # Sources for tests. | ||
| 14 | tests/threads_SRC = tests/threads/tests.c | ||
| 15 | tests/threads_SRC += tests/threads/alarm-wait.c | ||
| 16 | tests/threads_SRC += tests/threads/alarm-simultaneous.c | ||
| 17 | tests/threads_SRC += tests/threads/alarm-priority.c | ||
| 18 | tests/threads_SRC += tests/threads/alarm-zero.c | ||
| 19 | tests/threads_SRC += tests/threads/alarm-negative.c | ||
| 20 | tests/threads_SRC += tests/threads/priority-change.c | ||
| 21 | tests/threads_SRC += tests/threads/priority-donate-one.c | ||
| 22 | tests/threads_SRC += tests/threads/priority-donate-multiple.c | ||
| 23 | tests/threads_SRC += tests/threads/priority-donate-multiple2.c | ||
| 24 | tests/threads_SRC += tests/threads/priority-donate-nest.c | ||
| 25 | tests/threads_SRC += tests/threads/priority-donate-sema.c | ||
| 26 | tests/threads_SRC += tests/threads/priority-donate-lower.c | ||
| 27 | tests/threads_SRC += tests/threads/priority-fifo.c | ||
| 28 | tests/threads_SRC += tests/threads/priority-preempt.c | ||
| 29 | tests/threads_SRC += tests/threads/priority-sema.c | ||
| 30 | tests/threads_SRC += tests/threads/priority-condvar.c | ||
| 31 | tests/threads_SRC += tests/threads/priority-donate-chain.c | ||
| 32 | |||
| 33 | # Not used in SS 2012 | ||
| 34 | MLFQS_TESTS = mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 \ | ||
| 35 | mlfqs-fair-2 mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block) | ||
| 36 | |||
| 37 | tests/threads_SRC += tests/threads/mlfqs-load-1.c | ||
| 38 | tests/threads_SRC += tests/threads/mlfqs-load-60.c | ||
| 39 | tests/threads_SRC += tests/threads/mlfqs-load-avg.c | ||
| 40 | tests/threads_SRC += tests/threads/mlfqs-recent-1.c | ||
| 41 | tests/threads_SRC += tests/threads/mlfqs-fair.c | ||
| 42 | tests/threads_SRC += tests/threads/mlfqs-block.c | ||
| 43 | |||
| 44 | MLFQS_OUTPUTS = \ | ||
| 45 | tests/threads/mlfqs-load-1.output \ | ||
| 46 | tests/threads/mlfqs-load-60.output \ | ||
| 47 | tests/threads/mlfqs-load-avg.output \ | ||
| 48 | tests/threads/mlfqs-recent-1.output \ | ||
| 49 | tests/threads/mlfqs-fair-2.output \ | ||
| 50 | tests/threads/mlfqs-fair-20.output \ | ||
| 51 | tests/threads/mlfqs-nice-2.output \ | ||
| 52 | tests/threads/mlfqs-nice-10.output \ | ||
| 53 | tests/threads/mlfqs-block.output | ||
| 54 | |||
| 55 | $(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs | ||
| 56 | $(MLFQS_OUTPUTS): TIMEOUT = 480 | ||
diff --git a/pintos-progos/tests/threads/Rubric.alarm b/pintos-progos/tests/threads/Rubric.alarm new file mode 100644 index 0000000..61abe85 --- /dev/null +++ b/pintos-progos/tests/threads/Rubric.alarm | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | Functionality and robustness of alarm clock: | ||
| 2 | 4 alarm-single | ||
| 3 | 4 alarm-multiple | ||
| 4 | 4 alarm-simultaneous | ||
| 5 | 4 alarm-priority | ||
| 6 | |||
| 7 | 1 alarm-zero | ||
| 8 | 1 alarm-negative | ||
diff --git a/pintos-progos/tests/threads/Rubric.mlfqs b/pintos-progos/tests/threads/Rubric.mlfqs new file mode 100644 index 0000000..f260091 --- /dev/null +++ b/pintos-progos/tests/threads/Rubric.mlfqs | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | Functionality of advanced scheduler: | ||
| 2 | 5 mlfqs-load-1 | ||
| 3 | 5 mlfqs-load-60 | ||
| 4 | 3 mlfqs-load-avg | ||
| 5 | |||
| 6 | 5 mlfqs-recent-1 | ||
| 7 | |||
| 8 | 5 mlfqs-fair-2 | ||
| 9 | 3 mlfqs-fair-20 | ||
| 10 | |||
| 11 | 4 mlfqs-nice-2 | ||
| 12 | 2 mlfqs-nice-10 | ||
| 13 | |||
| 14 | 5 mlfqs-block | ||
diff --git a/pintos-progos/tests/threads/Rubric.priority b/pintos-progos/tests/threads/Rubric.priority new file mode 100644 index 0000000..652bc99 --- /dev/null +++ b/pintos-progos/tests/threads/Rubric.priority | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | Functionality of priority scheduler: | ||
| 2 | 3 priority-change | ||
| 3 | 3 priority-preempt | ||
| 4 | |||
| 5 | 3 priority-fifo | ||
| 6 | 3 priority-sema | ||
| 7 | 3 priority-condvar | ||
| 8 | |||
| 9 | 3 priority-donate-one | ||
| 10 | 3 priority-donate-multiple | ||
| 11 | 3 priority-donate-multiple2 | ||
| 12 | 3 priority-donate-nest | ||
| 13 | 5 priority-donate-chain | ||
| 14 | 3 priority-donate-sema | ||
| 15 | 3 priority-donate-lower | ||
diff --git a/pintos-progos/tests/threads/alarm-multiple.ck b/pintos-progos/tests/threads/alarm-multiple.ck new file mode 100644 index 0000000..fd83bcd --- /dev/null +++ b/pintos-progos/tests/threads/alarm-multiple.ck | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use tests::tests; | ||
| 3 | use tests::threads::alarm; | ||
| 4 | check_alarm (7); | ||
diff --git a/pintos-progos/tests/threads/alarm-negative.c b/pintos-progos/tests/threads/alarm-negative.c new file mode 100644 index 0000000..aec52cf --- /dev/null +++ b/pintos-progos/tests/threads/alarm-negative.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tests timer_sleep(-100). Only requirement is that it not crash. */ | ||
| 2 | |||
| 3 | #include <stdio.h> | ||
| 4 | #include "tests/threads/tests.h" | ||
| 5 | #include "threads/malloc.h" | ||
| 6 | #include "threads/synch.h" | ||
| 7 | #include "threads/thread.h" | ||
| 8 | #include "devices/timer.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_alarm_negative (void) | ||
| 12 | { | ||
| 13 | timer_sleep (-100); | ||
| 14 | pass (); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-negative.ck b/pintos-progos/tests/threads/alarm-negative.ck new file mode 100644 index 0000000..0d2bab0 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-negative.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-negative) begin | ||
| 7 | (alarm-negative) PASS | ||
| 8 | (alarm-negative) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm-priority.c b/pintos-progos/tests/threads/alarm-priority.c new file mode 100644 index 0000000..2288ff6 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-priority.c | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* Checks that when the alarm clock wakes up threads, the | ||
| 2 | higher-priority threads run first. */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/threads/tests.h" | ||
| 6 | #include "threads/init.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "devices/timer.h" | ||
| 11 | |||
| 12 | static thread_func alarm_priority_thread; | ||
| 13 | static int64_t wake_time; | ||
| 14 | static struct semaphore wait_sema; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_alarm_priority (void) | ||
| 18 | { | ||
| 19 | int i; | ||
| 20 | |||
| 21 | /* This test does not work with the MLFQS. */ | ||
| 22 | ASSERT (!thread_mlfqs); | ||
| 23 | |||
| 24 | wake_time = timer_ticks () + 5 * TIMER_FREQ; | ||
| 25 | sema_init (&wait_sema, 0); | ||
| 26 | |||
| 27 | for (i = 0; i < 10; i++) | ||
| 28 | { | ||
| 29 | int priority = PRI_DEFAULT - (i + 5) % 10 - 1; | ||
| 30 | char name[16]; | ||
| 31 | snprintf (name, sizeof name, "priority %d", priority); | ||
| 32 | thread_create (name, priority, alarm_priority_thread, NULL); | ||
| 33 | } | ||
| 34 | |||
| 35 | thread_set_priority (PRI_MIN); | ||
| 36 | |||
| 37 | for (i = 0; i < 10; i++) | ||
| 38 | sema_down (&wait_sema); | ||
| 39 | } | ||
| 40 | |||
| 41 | static void | ||
| 42 | alarm_priority_thread (void *aux UNUSED) | ||
| 43 | { | ||
| 44 | /* Busy-wait until the current time changes. */ | ||
| 45 | int64_t start_time = timer_ticks (); | ||
| 46 | while (timer_elapsed (start_time) == 0) | ||
| 47 | continue; | ||
| 48 | |||
| 49 | /* Now we know we're at the very beginning of a timer tick, so | ||
| 50 | we can call timer_sleep() without worrying about races | ||
| 51 | between checking the time and a timer interrupt. */ | ||
| 52 | timer_sleep (wake_time - timer_ticks ()); | ||
| 53 | |||
| 54 | /* Print a message on wake-up. */ | ||
| 55 | msg ("Thread %s woke up.", thread_name ()); | ||
| 56 | |||
| 57 | sema_up (&wait_sema); | ||
| 58 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-priority.ck b/pintos-progos/tests/threads/alarm-priority.ck new file mode 100644 index 0000000..b57c78b --- /dev/null +++ b/pintos-progos/tests/threads/alarm-priority.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-priority) begin | ||
| 7 | (alarm-priority) Thread priority 30 woke up. | ||
| 8 | (alarm-priority) Thread priority 29 woke up. | ||
| 9 | (alarm-priority) Thread priority 28 woke up. | ||
| 10 | (alarm-priority) Thread priority 27 woke up. | ||
| 11 | (alarm-priority) Thread priority 26 woke up. | ||
| 12 | (alarm-priority) Thread priority 25 woke up. | ||
| 13 | (alarm-priority) Thread priority 24 woke up. | ||
| 14 | (alarm-priority) Thread priority 23 woke up. | ||
| 15 | (alarm-priority) Thread priority 22 woke up. | ||
| 16 | (alarm-priority) Thread priority 21 woke up. | ||
| 17 | (alarm-priority) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.c b/pintos-progos/tests/threads/alarm-simultaneous.c new file mode 100644 index 0000000..844eea4 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-simultaneous.c | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | /* Creates N threads, each of which sleeps a different, fixed | ||
| 2 | duration, M times. Records the wake-up order and verifies | ||
| 3 | that it is valid. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/threads/tests.h" | ||
| 7 | #include "threads/init.h" | ||
| 8 | #include "threads/malloc.h" | ||
| 9 | #include "threads/synch.h" | ||
| 10 | #include "threads/thread.h" | ||
| 11 | #include "devices/timer.h" | ||
| 12 | |||
| 13 | static void test_sleep (int thread_cnt, int iterations); | ||
| 14 | |||
| 15 | void | ||
| 16 | test_alarm_simultaneous (void) | ||
| 17 | { | ||
| 18 | test_sleep (3, 5); | ||
| 19 | } | ||
| 20 | |||
| 21 | /* Information about the test. */ | ||
| 22 | struct sleep_test | ||
| 23 | { | ||
| 24 | int64_t start; /* Current time at start of test. */ | ||
| 25 | int iterations; /* Number of iterations per thread. */ | ||
| 26 | int *output_pos; /* Current position in output buffer. */ | ||
| 27 | }; | ||
| 28 | |||
| 29 | static void sleeper (void *); | ||
| 30 | |||
| 31 | /* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */ | ||
| 32 | static void | ||
| 33 | test_sleep (int thread_cnt, int iterations) | ||
| 34 | { | ||
| 35 | struct sleep_test test; | ||
| 36 | int *output; | ||
| 37 | int i; | ||
| 38 | |||
| 39 | /* This test does not work with the MLFQS. */ | ||
| 40 | ASSERT (!thread_mlfqs); | ||
| 41 | |||
| 42 | msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations); | ||
| 43 | msg ("Each thread sleeps 10 ticks each time."); | ||
| 44 | msg ("Within an iteration, all threads should wake up on the same tick."); | ||
| 45 | |||
| 46 | /* Allocate memory. */ | ||
| 47 | output = malloc (sizeof *output * iterations * thread_cnt * 2); | ||
| 48 | if (output == NULL) | ||
| 49 | PANIC ("couldn't allocate memory for test"); | ||
| 50 | |||
| 51 | /* Initialize test. */ | ||
| 52 | test.start = timer_ticks () + 100; | ||
| 53 | test.iterations = iterations; | ||
| 54 | test.output_pos = output; | ||
| 55 | |||
| 56 | /* Start threads. */ | ||
| 57 | ASSERT (output != NULL); | ||
| 58 | for (i = 0; i < thread_cnt; i++) | ||
| 59 | { | ||
| 60 | char name[16]; | ||
| 61 | snprintf (name, sizeof name, "thread %d", i); | ||
| 62 | thread_create (name, PRI_DEFAULT, sleeper, &test); | ||
| 63 | } | ||
| 64 | |||
| 65 | /* Wait long enough for all the threads to finish. */ | ||
| 66 | timer_sleep (100 + iterations * 10 + 100); | ||
| 67 | |||
| 68 | /* Print completion order. */ | ||
| 69 | msg ("iteration 0, thread 0: woke up after %d ticks", output[0]); | ||
| 70 | for (i = 1; i < test.output_pos - output; i++) | ||
| 71 | msg ("iteration %d, thread %d: woke up %d ticks later", | ||
| 72 | i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]); | ||
| 73 | |||
| 74 | free (output); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Sleeper thread. */ | ||
| 78 | static void | ||
| 79 | sleeper (void *test_) | ||
| 80 | { | ||
| 81 | struct sleep_test *test = test_; | ||
| 82 | int i; | ||
| 83 | |||
| 84 | /* Make sure we're at the beginning of a timer tick. */ | ||
| 85 | timer_sleep (1); | ||
| 86 | |||
| 87 | for (i = 1; i <= test->iterations; i++) | ||
| 88 | { | ||
| 89 | int64_t sleep_until = test->start + i * 10; | ||
| 90 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 91 | *test->output_pos++ = timer_ticks () - test->start; | ||
| 92 | thread_yield (); | ||
| 93 | } | ||
| 94 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.ck b/pintos-progos/tests/threads/alarm-simultaneous.ck new file mode 100644 index 0000000..406b8b0 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-simultaneous.ck | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-simultaneous) begin | ||
| 7 | (alarm-simultaneous) Creating 3 threads to sleep 5 times each. | ||
| 8 | (alarm-simultaneous) Each thread sleeps 10 ticks each time. | ||
| 9 | (alarm-simultaneous) Within an iteration, all threads should wake up on the same tick. | ||
| 10 | (alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks | ||
| 11 | (alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later | ||
| 12 | (alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later | ||
| 13 | (alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later | ||
| 14 | (alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later | ||
| 15 | (alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later | ||
| 16 | (alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later | ||
| 17 | (alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later | ||
| 18 | (alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later | ||
| 19 | (alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later | ||
| 20 | (alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later | ||
| 21 | (alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later | ||
| 22 | (alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later | ||
| 23 | (alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later | ||
| 24 | (alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later | ||
| 25 | (alarm-simultaneous) end | ||
| 26 | EOF | ||
| 27 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm-single.ck b/pintos-progos/tests/threads/alarm-single.ck new file mode 100644 index 0000000..31215df --- /dev/null +++ b/pintos-progos/tests/threads/alarm-single.ck | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use tests::tests; | ||
| 3 | use tests::threads::alarm; | ||
| 4 | check_alarm (1); | ||
diff --git a/pintos-progos/tests/threads/alarm-wait.c b/pintos-progos/tests/threads/alarm-wait.c new file mode 100644 index 0000000..37d3afc --- /dev/null +++ b/pintos-progos/tests/threads/alarm-wait.c | |||
| @@ -0,0 +1,152 @@ | |||
| 1 | /* Creates N threads, each of which sleeps a different, fixed | ||
| 2 | duration, M times. Records the wake-up order and verifies | ||
| 3 | that it is valid. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/threads/tests.h" | ||
| 7 | #include "threads/init.h" | ||
| 8 | #include "threads/malloc.h" | ||
| 9 | #include "threads/synch.h" | ||
| 10 | #include "threads/thread.h" | ||
| 11 | #include "devices/timer.h" | ||
| 12 | |||
| 13 | static void test_sleep (int thread_cnt, int iterations); | ||
| 14 | |||
| 15 | void | ||
| 16 | test_alarm_single (void) | ||
| 17 | { | ||
| 18 | test_sleep (5, 1); | ||
| 19 | } | ||
| 20 | |||
| 21 | void | ||
| 22 | test_alarm_multiple (void) | ||
| 23 | { | ||
| 24 | test_sleep (5, 7); | ||
| 25 | } | ||
| 26 | |||
| 27 | /* Information about the test. */ | ||
| 28 | struct sleep_test | ||
| 29 | { | ||
| 30 | int64_t start; /* Current time at start of test. */ | ||
| 31 | int iterations; /* Number of iterations per thread. */ | ||
| 32 | |||
| 33 | /* Output. */ | ||
| 34 | struct lock output_lock; /* Lock protecting output buffer. */ | ||
| 35 | int *output_pos; /* Current position in output buffer. */ | ||
| 36 | }; | ||
| 37 | |||
| 38 | /* Information about an individual thread in the test. */ | ||
| 39 | struct sleep_thread | ||
| 40 | { | ||
| 41 | struct sleep_test *test; /* Info shared between all threads. */ | ||
| 42 | int id; /* Sleeper ID. */ | ||
| 43 | int duration; /* Number of ticks to sleep. */ | ||
| 44 | int iterations; /* Iterations counted so far. */ | ||
| 45 | }; | ||
| 46 | |||
| 47 | static void sleeper (void *); | ||
| 48 | |||
| 49 | /* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */ | ||
| 50 | static void | ||
| 51 | test_sleep (int thread_cnt, int iterations) | ||
| 52 | { | ||
| 53 | struct sleep_test test; | ||
| 54 | struct sleep_thread *threads; | ||
| 55 | int *output, *op; | ||
| 56 | int product; | ||
| 57 | int i; | ||
| 58 | |||
| 59 | /* This test does not work with the MLFQS. */ | ||
| 60 | ASSERT (!thread_mlfqs); | ||
| 61 | |||
| 62 | msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations); | ||
| 63 | msg ("Thread 0 sleeps 10 ticks each time,"); | ||
| 64 | msg ("thread 1 sleeps 20 ticks each time, and so on."); | ||
| 65 | msg ("If successful, product of iteration count and"); | ||
| 66 | msg ("sleep duration will appear in nondescending order."); | ||
| 67 | |||
| 68 | /* Allocate memory. */ | ||
| 69 | threads = malloc (sizeof *threads * thread_cnt); | ||
| 70 | output = malloc (sizeof *output * iterations * thread_cnt * 2); | ||
| 71 | if (threads == NULL || output == NULL) | ||
| 72 | PANIC ("couldn't allocate memory for test"); | ||
| 73 | |||
| 74 | /* Initialize test. */ | ||
| 75 | test.start = timer_ticks () + 100; | ||
| 76 | test.iterations = iterations; | ||
| 77 | lock_init (&test.output_lock); | ||
| 78 | test.output_pos = output; | ||
| 79 | |||
| 80 | /* Start threads. */ | ||
| 81 | ASSERT (output != NULL); | ||
| 82 | for (i = 0; i < thread_cnt; i++) | ||
| 83 | { | ||
| 84 | struct sleep_thread *t = threads + i; | ||
| 85 | char name[16]; | ||
| 86 | |||
| 87 | t->test = &test; | ||
| 88 | t->id = i; | ||
| 89 | t->duration = (i + 1) * 10; | ||
| 90 | t->iterations = 0; | ||
| 91 | |||
| 92 | snprintf (name, sizeof name, "thread %d", i); | ||
| 93 | thread_create (name, PRI_DEFAULT, sleeper, t); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Wait long enough for all the threads to finish. */ | ||
| 97 | timer_sleep (100 + thread_cnt * iterations * 10 + 100); | ||
| 98 | |||
| 99 | /* Acquire the output lock in case some rogue thread is still | ||
| 100 | running. */ | ||
| 101 | lock_acquire (&test.output_lock); | ||
| 102 | |||
| 103 | /* Print completion order. */ | ||
| 104 | product = 0; | ||
| 105 | for (op = output; op < test.output_pos; op++) | ||
| 106 | { | ||
| 107 | struct sleep_thread *t; | ||
| 108 | int new_prod; | ||
| 109 | |||
| 110 | ASSERT (*op >= 0 && *op < thread_cnt); | ||
| 111 | t = threads + *op; | ||
| 112 | |||
| 113 | new_prod = ++t->iterations * t->duration; | ||
| 114 | |||
| 115 | msg ("thread %d: duration=%d, iteration=%d, product=%d", | ||
| 116 | t->id, t->duration, t->iterations, new_prod); | ||
| 117 | |||
| 118 | if (new_prod >= product) | ||
| 119 | product = new_prod; | ||
| 120 | else | ||
| 121 | fail ("thread %d woke up out of order (%d > %d)!", | ||
| 122 | t->id, product, new_prod); | ||
| 123 | } | ||
| 124 | |||
| 125 | /* Verify that we had the proper number of wakeups. */ | ||
| 126 | for (i = 0; i < thread_cnt; i++) | ||
| 127 | if (threads[i].iterations != iterations) | ||
| 128 | fail ("thread %d woke up %d times instead of %d", | ||
| 129 | i, threads[i].iterations, iterations); | ||
| 130 | |||
| 131 | lock_release (&test.output_lock); | ||
| 132 | free (output); | ||
| 133 | free (threads); | ||
| 134 | } | ||
| 135 | |||
| 136 | /* Sleeper thread. */ | ||
| 137 | static void | ||
| 138 | sleeper (void *t_) | ||
| 139 | { | ||
| 140 | struct sleep_thread *t = t_; | ||
| 141 | struct sleep_test *test = t->test; | ||
| 142 | int i; | ||
| 143 | |||
| 144 | for (i = 1; i <= test->iterations; i++) | ||
| 145 | { | ||
| 146 | int64_t sleep_until = test->start + i * t->duration; | ||
| 147 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 148 | lock_acquire (&test->output_lock); | ||
| 149 | *test->output_pos++ = t->id; | ||
| 150 | lock_release (&test->output_lock); | ||
| 151 | } | ||
| 152 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-zero.c b/pintos-progos/tests/threads/alarm-zero.c new file mode 100644 index 0000000..c8a3ee2 --- /dev/null +++ b/pintos-progos/tests/threads/alarm-zero.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tests timer_sleep(0), which should return immediately. */ | ||
| 2 | |||
| 3 | #include <stdio.h> | ||
| 4 | #include "tests/threads/tests.h" | ||
| 5 | #include "threads/malloc.h" | ||
| 6 | #include "threads/synch.h" | ||
| 7 | #include "threads/thread.h" | ||
| 8 | #include "devices/timer.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_alarm_zero (void) | ||
| 12 | { | ||
| 13 | timer_sleep (0); | ||
| 14 | pass (); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/threads/alarm-zero.ck b/pintos-progos/tests/threads/alarm-zero.ck new file mode 100644 index 0000000..a6b1a3c --- /dev/null +++ b/pintos-progos/tests/threads/alarm-zero.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (alarm-zero) begin | ||
| 7 | (alarm-zero) PASS | ||
| 8 | (alarm-zero) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/threads/alarm.pm b/pintos-progos/tests/threads/alarm.pm new file mode 100644 index 0000000..84b3b7f --- /dev/null +++ b/pintos-progos/tests/threads/alarm.pm | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | sub check_alarm { | ||
| 2 | my ($iterations) = @_; | ||
| 3 | our ($test); | ||
| 4 | |||
| 5 | @output = read_text_file ("$test.output"); | ||
| 6 | common_checks ("run", @output); | ||
| 7 | |||
| 8 | my (@products); | ||
| 9 | for (my ($i) = 0; $i < $iterations; $i++) { | ||
| 10 | for (my ($t) = 0; $t < 5; $t++) { | ||
| 11 | push (@products, ($i + 1) * ($t + 1) * 10); | ||
| 12 | } | ||
| 13 | } | ||
| 14 | @products = sort {$a <=> $b} @products; | ||
| 15 | |||
| 16 | local ($_); | ||
| 17 | foreach (@output) { | ||
| 18 | fail $_ if /out of order/i; | ||
| 19 | |||
| 20 | my ($p) = /product=(\d+)$/; | ||
| 21 | next if !defined $p; | ||
| 22 | |||
| 23 | my ($q) = shift (@products); | ||
| 24 | fail "Too many wakeups.\n" if !defined $q; | ||
| 25 | fail "Out of order wakeups ($p vs. $q).\n" if $p != $q; # FIXME | ||
| 26 | } | ||
| 27 | fail scalar (@products) . " fewer wakeups than expected.\n" | ||
| 28 | if @products != 0; | ||
| 29 | pass; | ||
| 30 | } | ||
| 31 | |||
| 32 | 1; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-block.c b/pintos-progos/tests/threads/mlfqs-block.c new file mode 100644 index 0000000..6d4992d --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-block.c | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* Checks that recent_cpu and priorities are updated for blocked | ||
| 2 | threads. | ||
| 3 | |||
| 4 | The main thread sleeps for 25 seconds, spins for 5 seconds, | ||
| 5 | then releases a lock. The "block" thread spins for 20 seconds | ||
| 6 | then attempts to acquire the lock, which will block for 10 | ||
| 7 | seconds (until the main thread releases it). If recent_cpu | ||
| 8 | decays properly while the "block" thread sleeps, then the | ||
| 9 | block thread should be immediately scheduled when the main | ||
| 10 | thread releases the lock. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/malloc.h" | ||
| 16 | #include "threads/synch.h" | ||
| 17 | #include "threads/thread.h" | ||
| 18 | #include "devices/timer.h" | ||
| 19 | |||
| 20 | static void block_thread (void *lock_); | ||
| 21 | |||
| 22 | void | ||
| 23 | test_mlfqs_block (void) | ||
| 24 | { | ||
| 25 | int64_t start_time; | ||
| 26 | struct lock lock; | ||
| 27 | |||
| 28 | ASSERT (thread_mlfqs); | ||
| 29 | |||
| 30 | msg ("Main thread acquiring lock."); | ||
| 31 | lock_init (&lock); | ||
| 32 | lock_acquire (&lock); | ||
| 33 | |||
| 34 | msg ("Main thread creating block thread, sleeping 25 seconds..."); | ||
| 35 | thread_create ("block", PRI_DEFAULT, block_thread, &lock); | ||
| 36 | timer_sleep (25 * TIMER_FREQ); | ||
| 37 | |||
| 38 | msg ("Main thread spinning for 5 seconds..."); | ||
| 39 | start_time = timer_ticks (); | ||
| 40 | while (timer_elapsed (start_time) < 5 * TIMER_FREQ) | ||
| 41 | continue; | ||
| 42 | |||
| 43 | msg ("Main thread releasing lock."); | ||
| 44 | lock_release (&lock); | ||
| 45 | |||
| 46 | msg ("Block thread should have already acquired lock."); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void | ||
| 50 | block_thread (void *lock_) | ||
| 51 | { | ||
| 52 | struct lock *lock = lock_; | ||
| 53 | int64_t start_time; | ||
| 54 | |||
| 55 | msg ("Block thread spinning for 20 seconds..."); | ||
| 56 | start_time = timer_ticks (); | ||
| 57 | while (timer_elapsed (start_time) < 20 * TIMER_FREQ) | ||
| 58 | continue; | ||
| 59 | |||
| 60 | msg ("Block thread acquiring lock..."); | ||
| 61 | lock_acquire (lock); | ||
| 62 | |||
| 63 | msg ("...got it."); | ||
| 64 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-block.ck b/pintos-progos/tests/threads/mlfqs-block.ck new file mode 100644 index 0000000..8833a3a --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-block.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (mlfqs-block) begin | ||
| 7 | (mlfqs-block) Main thread acquiring lock. | ||
| 8 | (mlfqs-block) Main thread creating block thread, sleeping 25 seconds... | ||
| 9 | (mlfqs-block) Block thread spinning for 20 seconds... | ||
| 10 | (mlfqs-block) Block thread acquiring lock... | ||
| 11 | (mlfqs-block) Main thread spinning for 5 seconds... | ||
| 12 | (mlfqs-block) Main thread releasing lock. | ||
| 13 | (mlfqs-block) ...got it. | ||
| 14 | (mlfqs-block) Block thread should have already acquired lock. | ||
| 15 | (mlfqs-block) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-fair-2.ck b/pintos-progos/tests/threads/mlfqs-fair-2.ck new file mode 100644 index 0000000..5b19ff1 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-fair-2.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([0, 0], 50); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-fair-20.ck b/pintos-progos/tests/threads/mlfqs-fair-20.ck new file mode 100644 index 0000000..bb4d051 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-fair-20.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([(0) x 20], 20); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-fair.c b/pintos-progos/tests/threads/mlfqs-fair.c new file mode 100644 index 0000000..3b1bea5 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-fair.c | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* Measures the correctness of the "nice" implementation. | ||
| 2 | |||
| 3 | The "fair" tests run either 2 or 20 threads all niced to 0. | ||
| 4 | The threads should all receive approximately the same number | ||
| 5 | of ticks. Each test runs for 30 seconds, so the ticks should | ||
| 6 | also sum to approximately 30 * 100 == 3000 ticks. | ||
| 7 | |||
| 8 | The mlfqs-nice-2 test runs 2 threads, one with nice 0, the | ||
| 9 | other with nice 5, which should receive 1,904 and 1,096 ticks, | ||
| 10 | respectively, over 30 seconds. | ||
| 11 | |||
| 12 | The mlfqs-nice-10 test runs 10 threads with nice 0 through 9. | ||
| 13 | They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40, | ||
| 14 | and 8 ticks, respectively, over 30 seconds. | ||
| 15 | |||
| 16 | (The above are computed via simulation in mlfqs.pm.) */ | ||
| 17 | |||
| 18 | #include <stdio.h> | ||
| 19 | #include <inttypes.h> | ||
| 20 | #include "tests/threads/tests.h" | ||
| 21 | #include "threads/init.h" | ||
| 22 | #include "threads/malloc.h" | ||
| 23 | #include "threads/palloc.h" | ||
| 24 | #include "threads/synch.h" | ||
| 25 | #include "threads/thread.h" | ||
| 26 | #include "devices/timer.h" | ||
| 27 | |||
| 28 | static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step); | ||
| 29 | |||
| 30 | void | ||
| 31 | test_mlfqs_fair_2 (void) | ||
| 32 | { | ||
| 33 | test_mlfqs_fair (2, 0, 0); | ||
| 34 | } | ||
| 35 | |||
| 36 | void | ||
| 37 | test_mlfqs_fair_20 (void) | ||
| 38 | { | ||
| 39 | test_mlfqs_fair (20, 0, 0); | ||
| 40 | } | ||
| 41 | |||
| 42 | void | ||
| 43 | test_mlfqs_nice_2 (void) | ||
| 44 | { | ||
| 45 | test_mlfqs_fair (2, 0, 5); | ||
| 46 | } | ||
| 47 | |||
| 48 | void | ||
| 49 | test_mlfqs_nice_10 (void) | ||
| 50 | { | ||
| 51 | test_mlfqs_fair (10, 0, 1); | ||
| 52 | } | ||
| 53 | |||
| 54 | #define MAX_THREAD_CNT 20 | ||
| 55 | |||
| 56 | struct thread_info | ||
| 57 | { | ||
| 58 | int64_t start_time; | ||
| 59 | int tick_count; | ||
| 60 | int nice; | ||
| 61 | }; | ||
| 62 | |||
| 63 | static void load_thread (void *aux); | ||
| 64 | |||
| 65 | static void | ||
| 66 | test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step) | ||
| 67 | { | ||
| 68 | struct thread_info info[MAX_THREAD_CNT]; | ||
| 69 | int64_t start_time; | ||
| 70 | int nice; | ||
| 71 | int i; | ||
| 72 | |||
| 73 | ASSERT (thread_mlfqs); | ||
| 74 | ASSERT (thread_cnt <= MAX_THREAD_CNT); | ||
| 75 | ASSERT (nice_min >= -10); | ||
| 76 | ASSERT (nice_step >= 0); | ||
| 77 | ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20); | ||
| 78 | |||
| 79 | thread_set_nice (-20); | ||
| 80 | |||
| 81 | start_time = timer_ticks (); | ||
| 82 | msg ("Starting %d threads...", thread_cnt); | ||
| 83 | nice = nice_min; | ||
| 84 | for (i = 0; i < thread_cnt; i++) | ||
| 85 | { | ||
| 86 | struct thread_info *ti = &info[i]; | ||
| 87 | char name[16]; | ||
| 88 | |||
| 89 | ti->start_time = start_time; | ||
| 90 | ti->tick_count = 0; | ||
| 91 | ti->nice = nice; | ||
| 92 | |||
| 93 | snprintf(name, sizeof name, "load %d", i); | ||
| 94 | thread_create (name, PRI_DEFAULT, load_thread, ti); | ||
| 95 | |||
| 96 | nice += nice_step; | ||
| 97 | } | ||
| 98 | msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time)); | ||
| 99 | |||
| 100 | msg ("Sleeping 40 seconds to let threads run, please wait..."); | ||
| 101 | timer_sleep (40 * TIMER_FREQ); | ||
| 102 | |||
| 103 | for (i = 0; i < thread_cnt; i++) | ||
| 104 | msg ("Thread %d received %d ticks.", i, info[i].tick_count); | ||
| 105 | } | ||
| 106 | |||
| 107 | static void | ||
| 108 | load_thread (void *ti_) | ||
| 109 | { | ||
| 110 | struct thread_info *ti = ti_; | ||
| 111 | int64_t sleep_time = 5 * TIMER_FREQ; | ||
| 112 | int64_t spin_time = sleep_time + 30 * TIMER_FREQ; | ||
| 113 | int64_t last_time = 0; | ||
| 114 | |||
| 115 | thread_set_nice (ti->nice); | ||
| 116 | timer_sleep (sleep_time - timer_elapsed (ti->start_time)); | ||
| 117 | while (timer_elapsed (ti->start_time) < spin_time) | ||
| 118 | { | ||
| 119 | int64_t cur_time = timer_ticks (); | ||
| 120 | if (cur_time != last_time) | ||
| 121 | ti->tick_count++; | ||
| 122 | last_time = cur_time; | ||
| 123 | } | ||
| 124 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.c b/pintos-progos/tests/threads/mlfqs-load-1.c new file mode 100644 index 0000000..a39eea2 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-1.c | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* Verifies that a single busy thread raises the load average to | ||
| 2 | 0.5 in 38 to 45 seconds. The expected time is 42 seconds, as | ||
| 3 | you can verify: | ||
| 4 | perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"' | ||
| 5 | |||
| 6 | Then, verifies that 10 seconds of inactivity drop the load | ||
| 7 | average back below 0.5 again. */ | ||
| 8 | |||
| 9 | #include <stdio.h> | ||
| 10 | #include "tests/threads/tests.h" | ||
| 11 | #include "threads/init.h" | ||
| 12 | #include "threads/malloc.h" | ||
| 13 | #include "threads/synch.h" | ||
| 14 | #include "threads/thread.h" | ||
| 15 | #include "devices/timer.h" | ||
| 16 | |||
| 17 | void | ||
| 18 | test_mlfqs_load_1 (void) | ||
| 19 | { | ||
| 20 | int64_t start_time; | ||
| 21 | int elapsed; | ||
| 22 | int load_avg; | ||
| 23 | |||
| 24 | ASSERT (thread_mlfqs); | ||
| 25 | |||
| 26 | msg ("spinning for up to 45 seconds, please wait..."); | ||
| 27 | |||
| 28 | start_time = timer_ticks (); | ||
| 29 | for (;;) | ||
| 30 | { | ||
| 31 | load_avg = thread_get_load_avg (); | ||
| 32 | ASSERT (load_avg >= 0); | ||
| 33 | elapsed = timer_elapsed (start_time) / TIMER_FREQ; | ||
| 34 | if (load_avg > 100) | ||
| 35 | fail ("load average is %d.%02d " | ||
| 36 | "but should be between 0 and 1 (after %d seconds)", | ||
| 37 | load_avg / 100, load_avg % 100, elapsed); | ||
| 38 | else if (load_avg > 50) | ||
| 39 | break; | ||
| 40 | else if (elapsed > 45) | ||
| 41 | fail ("load average stayed below 0.5 for more than 45 seconds"); | ||
| 42 | } | ||
| 43 | |||
| 44 | if (elapsed < 38) | ||
| 45 | fail ("load average took only %d seconds to rise above 0.5", elapsed); | ||
| 46 | msg ("load average rose to 0.5 after %d seconds", elapsed); | ||
| 47 | |||
| 48 | msg ("sleeping for another 10 seconds, please wait..."); | ||
| 49 | timer_sleep (TIMER_FREQ * 10); | ||
| 50 | |||
| 51 | load_avg = thread_get_load_avg (); | ||
| 52 | if (load_avg < 0) | ||
| 53 | fail ("load average fell below 0"); | ||
| 54 | if (load_avg > 50) | ||
| 55 | fail ("load average stayed above 0.5 for more than 10 seconds"); | ||
| 56 | msg ("load average fell back below 0.5 (to %d.%02d)", | ||
| 57 | load_avg / 100, load_avg % 100); | ||
| 58 | |||
| 59 | pass (); | ||
| 60 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.ck b/pintos-progos/tests/threads/mlfqs-load-1.ck new file mode 100644 index 0000000..faf0ffa --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-1.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | |||
| 6 | our ($test); | ||
| 7 | my (@output) = read_text_file ("$test.output"); | ||
| 8 | |||
| 9 | common_checks ("run", @output); | ||
| 10 | |||
| 11 | @output = get_core_output ("run", @output); | ||
| 12 | fail "missing PASS in output" | ||
| 13 | unless grep ($_ eq '(mlfqs-load-1) PASS', @output); | ||
| 14 | |||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.c b/pintos-progos/tests/threads/mlfqs-load-60.c new file mode 100644 index 0000000..b6a3eb6 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-60.c | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | /* Starts 60 threads that each sleep for 10 seconds, then spin in | ||
| 2 | a tight loop for 60 seconds, and sleep for another 60 seconds. | ||
| 3 | Every 2 seconds after the initial sleep, the main thread | ||
| 4 | prints the load average. | ||
| 5 | |||
| 6 | The expected output is this (some margin of error is allowed): | ||
| 7 | |||
| 8 | After 0 seconds, load average=1.00. | ||
| 9 | After 2 seconds, load average=2.95. | ||
| 10 | After 4 seconds, load average=4.84. | ||
| 11 | After 6 seconds, load average=6.66. | ||
| 12 | After 8 seconds, load average=8.42. | ||
| 13 | After 10 seconds, load average=10.13. | ||
| 14 | After 12 seconds, load average=11.78. | ||
| 15 | After 14 seconds, load average=13.37. | ||
| 16 | After 16 seconds, load average=14.91. | ||
| 17 | After 18 seconds, load average=16.40. | ||
| 18 | After 20 seconds, load average=17.84. | ||
| 19 | After 22 seconds, load average=19.24. | ||
| 20 | After 24 seconds, load average=20.58. | ||
| 21 | After 26 seconds, load average=21.89. | ||
| 22 | After 28 seconds, load average=23.15. | ||
| 23 | After 30 seconds, load average=24.37. | ||
| 24 | After 32 seconds, load average=25.54. | ||
| 25 | After 34 seconds, load average=26.68. | ||
| 26 | After 36 seconds, load average=27.78. | ||
| 27 | After 38 seconds, load average=28.85. | ||
| 28 | After 40 seconds, load average=29.88. | ||
| 29 | After 42 seconds, load average=30.87. | ||
| 30 | After 44 seconds, load average=31.84. | ||
| 31 | After 46 seconds, load average=32.77. | ||
| 32 | After 48 seconds, load average=33.67. | ||
| 33 | After 50 seconds, load average=34.54. | ||
| 34 | After 52 seconds, load average=35.38. | ||
| 35 | After 54 seconds, load average=36.19. | ||
| 36 | After 56 seconds, load average=36.98. | ||
| 37 | After 58 seconds, load average=37.74. | ||
| 38 | After 60 seconds, load average=37.48. | ||
| 39 | After 62 seconds, load average=36.24. | ||
| 40 | After 64 seconds, load average=35.04. | ||
| 41 | After 66 seconds, load average=33.88. | ||
| 42 | After 68 seconds, load average=32.76. | ||
| 43 | After 70 seconds, load average=31.68. | ||
| 44 | After 72 seconds, load average=30.63. | ||
| 45 | After 74 seconds, load average=29.62. | ||
| 46 | After 76 seconds, load average=28.64. | ||
| 47 | After 78 seconds, load average=27.69. | ||
| 48 | After 80 seconds, load average=26.78. | ||
| 49 | After 82 seconds, load average=25.89. | ||
| 50 | After 84 seconds, load average=25.04. | ||
| 51 | After 86 seconds, load average=24.21. | ||
| 52 | After 88 seconds, load average=23.41. | ||
| 53 | After 90 seconds, load average=22.64. | ||
| 54 | After 92 seconds, load average=21.89. | ||
| 55 | After 94 seconds, load average=21.16. | ||
| 56 | After 96 seconds, load average=20.46. | ||
| 57 | After 98 seconds, load average=19.79. | ||
| 58 | After 100 seconds, load average=19.13. | ||
| 59 | After 102 seconds, load average=18.50. | ||
| 60 | After 104 seconds, load average=17.89. | ||
| 61 | After 106 seconds, load average=17.30. | ||
| 62 | After 108 seconds, load average=16.73. | ||
| 63 | After 110 seconds, load average=16.17. | ||
| 64 | After 112 seconds, load average=15.64. | ||
| 65 | After 114 seconds, load average=15.12. | ||
| 66 | After 116 seconds, load average=14.62. | ||
| 67 | After 118 seconds, load average=14.14. | ||
| 68 | After 120 seconds, load average=13.67. | ||
| 69 | After 122 seconds, load average=13.22. | ||
| 70 | After 124 seconds, load average=12.78. | ||
| 71 | After 126 seconds, load average=12.36. | ||
| 72 | After 128 seconds, load average=11.95. | ||
| 73 | After 130 seconds, load average=11.56. | ||
| 74 | After 132 seconds, load average=11.17. | ||
| 75 | After 134 seconds, load average=10.80. | ||
| 76 | After 136 seconds, load average=10.45. | ||
| 77 | After 138 seconds, load average=10.10. | ||
| 78 | After 140 seconds, load average=9.77. | ||
| 79 | After 142 seconds, load average=9.45. | ||
| 80 | After 144 seconds, load average=9.13. | ||
| 81 | After 146 seconds, load average=8.83. | ||
| 82 | After 148 seconds, load average=8.54. | ||
| 83 | After 150 seconds, load average=8.26. | ||
| 84 | After 152 seconds, load average=7.98. | ||
| 85 | After 154 seconds, load average=7.72. | ||
| 86 | After 156 seconds, load average=7.47. | ||
| 87 | After 158 seconds, load average=7.22. | ||
| 88 | After 160 seconds, load average=6.98. | ||
| 89 | After 162 seconds, load average=6.75. | ||
| 90 | After 164 seconds, load average=6.53. | ||
| 91 | After 166 seconds, load average=6.31. | ||
| 92 | After 168 seconds, load average=6.10. | ||
| 93 | After 170 seconds, load average=5.90. | ||
| 94 | After 172 seconds, load average=5.70. | ||
| 95 | After 174 seconds, load average=5.52. | ||
| 96 | After 176 seconds, load average=5.33. | ||
| 97 | After 178 seconds, load average=5.16. | ||
| 98 | */ | ||
| 99 | |||
| 100 | #include <stdio.h> | ||
| 101 | #include "tests/threads/tests.h" | ||
| 102 | #include "threads/init.h" | ||
| 103 | #include "threads/malloc.h" | ||
| 104 | #include "threads/synch.h" | ||
| 105 | #include "threads/thread.h" | ||
| 106 | #include "devices/timer.h" | ||
| 107 | |||
| 108 | static int64_t start_time; | ||
| 109 | |||
| 110 | static void load_thread (void *aux); | ||
| 111 | |||
| 112 | #define THREAD_CNT 60 | ||
| 113 | |||
| 114 | void | ||
| 115 | test_mlfqs_load_60 (void) | ||
| 116 | { | ||
| 117 | int i; | ||
| 118 | |||
| 119 | ASSERT (thread_mlfqs); | ||
| 120 | |||
| 121 | start_time = timer_ticks (); | ||
| 122 | msg ("Starting %d niced load threads...", THREAD_CNT); | ||
| 123 | for (i = 0; i < THREAD_CNT; i++) | ||
| 124 | { | ||
| 125 | char name[16]; | ||
| 126 | snprintf(name, sizeof name, "load %d", i); | ||
| 127 | thread_create (name, PRI_DEFAULT, load_thread, NULL); | ||
| 128 | } | ||
| 129 | msg ("Starting threads took %d seconds.", | ||
| 130 | timer_elapsed (start_time) / TIMER_FREQ); | ||
| 131 | |||
| 132 | for (i = 0; i < 90; i++) | ||
| 133 | { | ||
| 134 | int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10); | ||
| 135 | int load_avg; | ||
| 136 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 137 | load_avg = thread_get_load_avg (); | ||
| 138 | msg ("After %d seconds, load average=%d.%02d.", | ||
| 139 | i * 2, load_avg / 100, load_avg % 100); | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | static void | ||
| 144 | load_thread (void *aux UNUSED) | ||
| 145 | { | ||
| 146 | int64_t sleep_time = 10 * TIMER_FREQ; | ||
| 147 | int64_t spin_time = sleep_time + 60 * TIMER_FREQ; | ||
| 148 | int64_t exit_time = spin_time + 60 * TIMER_FREQ; | ||
| 149 | |||
| 150 | thread_set_nice (20); | ||
| 151 | timer_sleep (sleep_time - timer_elapsed (start_time)); | ||
| 152 | while (timer_elapsed (start_time) < spin_time) | ||
| 153 | continue; | ||
| 154 | timer_sleep (exit_time - timer_elapsed (start_time)); | ||
| 155 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.ck b/pintos-progos/tests/threads/mlfqs-load-60.ck new file mode 100644 index 0000000..cb69220 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-60.ck | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | our ($test); | ||
| 8 | |||
| 9 | my (@output) = read_text_file ("$test.output"); | ||
| 10 | common_checks ("run", @output); | ||
| 11 | @output = get_core_output ("run", @output); | ||
| 12 | |||
| 13 | # Get actual values. | ||
| 14 | local ($_); | ||
| 15 | my (@actual); | ||
| 16 | foreach (@output) { | ||
| 17 | my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./ | ||
| 18 | or next; | ||
| 19 | $actual[$t] = $load_avg; | ||
| 20 | } | ||
| 21 | |||
| 22 | # Calculate expected values. | ||
| 23 | my ($load_avg) = 0; | ||
| 24 | my ($recent) = 0; | ||
| 25 | my (@expected); | ||
| 26 | for (my ($t) = 0; $t < 180; $t++) { | ||
| 27 | my ($ready) = $t < 60 ? 60 : 0; | ||
| 28 | $load_avg = (59/60) * $load_avg + (1/60) * $ready; | ||
| 29 | $expected[$t] = $load_avg; | ||
| 30 | } | ||
| 31 | |||
| 32 | mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2], | ||
| 33 | "Some load average values were missing or " | ||
| 34 | . "differed from those expected " | ||
| 35 | . "by more than 3.5."); | ||
| 36 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.c b/pintos-progos/tests/threads/mlfqs-load-avg.c new file mode 100644 index 0000000..50e83e2 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-avg.c | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | /* Starts 60 threads numbered 0 through 59. Thread #i sleeps for | ||
| 2 | (10+i) seconds, then spins in a loop for 60 seconds, then | ||
| 3 | sleeps until a total of 120 seconds have passed. Every 2 | ||
| 4 | seconds, starting 10 seconds in, the main thread prints the | ||
| 5 | load average. | ||
| 6 | |||
| 7 | The expected output is listed below. Some margin of error is | ||
| 8 | allowed. | ||
| 9 | |||
| 10 | If your implementation fails this test but passes most other | ||
| 11 | tests, then consider whether you are doing too much work in | ||
| 12 | the timer interrupt. If the timer interrupt handler takes too | ||
| 13 | long, then the test's main thread will not have enough time to | ||
| 14 | do its own work (printing a message) and go back to sleep | ||
| 15 | before the next tick arrives. Then the main thread will be | ||
| 16 | ready, instead of sleeping, when the tick arrives, | ||
| 17 | artificially driving up the load average. | ||
| 18 | |||
| 19 | After 0 seconds, load average=0.00. | ||
| 20 | After 2 seconds, load average=0.05. | ||
| 21 | After 4 seconds, load average=0.16. | ||
| 22 | After 6 seconds, load average=0.34. | ||
| 23 | After 8 seconds, load average=0.58. | ||
| 24 | After 10 seconds, load average=0.87. | ||
| 25 | After 12 seconds, load average=1.22. | ||
| 26 | After 14 seconds, load average=1.63. | ||
| 27 | After 16 seconds, load average=2.09. | ||
| 28 | After 18 seconds, load average=2.60. | ||
| 29 | After 20 seconds, load average=3.16. | ||
| 30 | After 22 seconds, load average=3.76. | ||
| 31 | After 24 seconds, load average=4.42. | ||
| 32 | After 26 seconds, load average=5.11. | ||
| 33 | After 28 seconds, load average=5.85. | ||
| 34 | After 30 seconds, load average=6.63. | ||
| 35 | After 32 seconds, load average=7.46. | ||
| 36 | After 34 seconds, load average=8.32. | ||
| 37 | After 36 seconds, load average=9.22. | ||
| 38 | After 38 seconds, load average=10.15. | ||
| 39 | After 40 seconds, load average=11.12. | ||
| 40 | After 42 seconds, load average=12.13. | ||
| 41 | After 44 seconds, load average=13.16. | ||
| 42 | After 46 seconds, load average=14.23. | ||
| 43 | After 48 seconds, load average=15.33. | ||
| 44 | After 50 seconds, load average=16.46. | ||
| 45 | After 52 seconds, load average=17.62. | ||
| 46 | After 54 seconds, load average=18.81. | ||
| 47 | After 56 seconds, load average=20.02. | ||
| 48 | After 58 seconds, load average=21.26. | ||
| 49 | After 60 seconds, load average=22.52. | ||
| 50 | After 62 seconds, load average=23.71. | ||
| 51 | After 64 seconds, load average=24.80. | ||
| 52 | After 66 seconds, load average=25.78. | ||
| 53 | After 68 seconds, load average=26.66. | ||
| 54 | After 70 seconds, load average=27.45. | ||
| 55 | After 72 seconds, load average=28.14. | ||
| 56 | After 74 seconds, load average=28.75. | ||
| 57 | After 76 seconds, load average=29.27. | ||
| 58 | After 78 seconds, load average=29.71. | ||
| 59 | After 80 seconds, load average=30.06. | ||
| 60 | After 82 seconds, load average=30.34. | ||
| 61 | After 84 seconds, load average=30.55. | ||
| 62 | After 86 seconds, load average=30.68. | ||
| 63 | After 88 seconds, load average=30.74. | ||
| 64 | After 90 seconds, load average=30.73. | ||
| 65 | After 92 seconds, load average=30.66. | ||
| 66 | After 94 seconds, load average=30.52. | ||
| 67 | After 96 seconds, load average=30.32. | ||
| 68 | After 98 seconds, load average=30.06. | ||
| 69 | After 100 seconds, load average=29.74. | ||
| 70 | After 102 seconds, load average=29.37. | ||
| 71 | After 104 seconds, load average=28.95. | ||
| 72 | After 106 seconds, load average=28.47. | ||
| 73 | After 108 seconds, load average=27.94. | ||
| 74 | After 110 seconds, load average=27.36. | ||
| 75 | After 112 seconds, load average=26.74. | ||
| 76 | After 114 seconds, load average=26.07. | ||
| 77 | After 116 seconds, load average=25.36. | ||
| 78 | After 118 seconds, load average=24.60. | ||
| 79 | After 120 seconds, load average=23.81. | ||
| 80 | After 122 seconds, load average=23.02. | ||
| 81 | After 124 seconds, load average=22.26. | ||
| 82 | After 126 seconds, load average=21.52. | ||
| 83 | After 128 seconds, load average=20.81. | ||
| 84 | After 130 seconds, load average=20.12. | ||
| 85 | After 132 seconds, load average=19.46. | ||
| 86 | After 134 seconds, load average=18.81. | ||
| 87 | After 136 seconds, load average=18.19. | ||
| 88 | After 138 seconds, load average=17.59. | ||
| 89 | After 140 seconds, load average=17.01. | ||
| 90 | After 142 seconds, load average=16.45. | ||
| 91 | After 144 seconds, load average=15.90. | ||
| 92 | After 146 seconds, load average=15.38. | ||
| 93 | After 148 seconds, load average=14.87. | ||
| 94 | After 150 seconds, load average=14.38. | ||
| 95 | After 152 seconds, load average=13.90. | ||
| 96 | After 154 seconds, load average=13.44. | ||
| 97 | After 156 seconds, load average=13.00. | ||
| 98 | After 158 seconds, load average=12.57. | ||
| 99 | After 160 seconds, load average=12.15. | ||
| 100 | After 162 seconds, load average=11.75. | ||
| 101 | After 164 seconds, load average=11.36. | ||
| 102 | After 166 seconds, load average=10.99. | ||
| 103 | After 168 seconds, load average=10.62. | ||
| 104 | After 170 seconds, load average=10.27. | ||
| 105 | After 172 seconds, load average=9.93. | ||
| 106 | After 174 seconds, load average=9.61. | ||
| 107 | After 176 seconds, load average=9.29. | ||
| 108 | After 178 seconds, load average=8.98. | ||
| 109 | */ | ||
| 110 | |||
| 111 | #include <stdio.h> | ||
| 112 | #include "tests/threads/tests.h" | ||
| 113 | #include "threads/init.h" | ||
| 114 | #include "threads/malloc.h" | ||
| 115 | #include "threads/synch.h" | ||
| 116 | #include "threads/thread.h" | ||
| 117 | #include "devices/timer.h" | ||
| 118 | |||
| 119 | static int64_t start_time; | ||
| 120 | |||
| 121 | static void load_thread (void *seq_no); | ||
| 122 | |||
| 123 | #define THREAD_CNT 60 | ||
| 124 | |||
| 125 | void | ||
| 126 | test_mlfqs_load_avg (void) | ||
| 127 | { | ||
| 128 | int i; | ||
| 129 | |||
| 130 | ASSERT (thread_mlfqs); | ||
| 131 | |||
| 132 | start_time = timer_ticks (); | ||
| 133 | msg ("Starting %d load threads...", THREAD_CNT); | ||
| 134 | for (i = 0; i < THREAD_CNT; i++) | ||
| 135 | { | ||
| 136 | char name[16]; | ||
| 137 | snprintf(name, sizeof name, "load %d", i); | ||
| 138 | thread_create (name, PRI_DEFAULT, load_thread, (void *) i); | ||
| 139 | } | ||
| 140 | msg ("Starting threads took %d seconds.", | ||
| 141 | timer_elapsed (start_time) / TIMER_FREQ); | ||
| 142 | thread_set_nice (-20); | ||
| 143 | |||
| 144 | for (i = 0; i < 90; i++) | ||
| 145 | { | ||
| 146 | int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10); | ||
| 147 | int load_avg; | ||
| 148 | timer_sleep (sleep_until - timer_ticks ()); | ||
| 149 | load_avg = thread_get_load_avg (); | ||
| 150 | msg ("After %d seconds, load average=%d.%02d.", | ||
| 151 | i * 2, load_avg / 100, load_avg % 100); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | static void | ||
| 156 | load_thread (void *seq_no_) | ||
| 157 | { | ||
| 158 | int seq_no = (int) seq_no_; | ||
| 159 | int sleep_time = TIMER_FREQ * (10 + seq_no); | ||
| 160 | int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT; | ||
| 161 | int exit_time = TIMER_FREQ * (THREAD_CNT * 2); | ||
| 162 | |||
| 163 | timer_sleep (sleep_time - timer_elapsed (start_time)); | ||
| 164 | while (timer_elapsed (start_time) < spin_time) | ||
| 165 | continue; | ||
| 166 | timer_sleep (exit_time - timer_elapsed (start_time)); | ||
| 167 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.ck b/pintos-progos/tests/threads/mlfqs-load-avg.ck new file mode 100644 index 0000000..2254d05 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-load-avg.ck | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | our ($test); | ||
| 8 | my (@output) = read_text_file ("$test.output"); | ||
| 9 | |||
| 10 | common_checks ("run", @output); | ||
| 11 | @output = get_core_output ("run", @output); | ||
| 12 | |||
| 13 | # Get actual values. | ||
| 14 | local ($_); | ||
| 15 | my (@actual); | ||
| 16 | foreach (@output) { | ||
| 17 | my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./ | ||
| 18 | or next; | ||
| 19 | $actual[$t] = $load_avg; | ||
| 20 | } | ||
| 21 | |||
| 22 | # Calculate expected values. | ||
| 23 | my ($load_avg) = 0; | ||
| 24 | my ($recent) = 0; | ||
| 25 | my (@expected); | ||
| 26 | for (my ($t) = 0; $t < 180; $t++) { | ||
| 27 | my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0; | ||
| 28 | $load_avg = (59/60) * $load_avg + (1/60) * $ready; | ||
| 29 | $expected[$t] = $load_avg; | ||
| 30 | } | ||
| 31 | |||
| 32 | mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2], | ||
| 33 | "Some load average values were missing or " | ||
| 34 | . "differed from those expected " | ||
| 35 | . "by more than 2.5."); | ||
| 36 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs-nice-10.ck b/pintos-progos/tests/threads/mlfqs-nice-10.ck new file mode 100644 index 0000000..53e0abe --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-nice-10.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([0...9], 25); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-nice-2.ck b/pintos-progos/tests/threads/mlfqs-nice-2.ck new file mode 100644 index 0000000..ada366b --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-nice-2.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | check_mlfqs_fair ([0, 5], 50); | ||
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.c b/pintos-progos/tests/threads/mlfqs-recent-1.c new file mode 100644 index 0000000..4258671 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-recent-1.c | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | /* Checks that recent_cpu is calculated properly for the case of | ||
| 2 | a single ready process. | ||
| 3 | |||
| 4 | The expected output is this (some margin of error is allowed): | ||
| 5 | |||
| 6 | After 2 seconds, recent_cpu is 6.40, load_avg is 0.03. | ||
| 7 | After 4 seconds, recent_cpu is 12.60, load_avg is 0.07. | ||
| 8 | After 6 seconds, recent_cpu is 18.61, load_avg is 0.10. | ||
| 9 | After 8 seconds, recent_cpu is 24.44, load_avg is 0.13. | ||
| 10 | After 10 seconds, recent_cpu is 30.08, load_avg is 0.15. | ||
| 11 | After 12 seconds, recent_cpu is 35.54, load_avg is 0.18. | ||
| 12 | After 14 seconds, recent_cpu is 40.83, load_avg is 0.21. | ||
| 13 | After 16 seconds, recent_cpu is 45.96, load_avg is 0.24. | ||
| 14 | After 18 seconds, recent_cpu is 50.92, load_avg is 0.26. | ||
| 15 | After 20 seconds, recent_cpu is 55.73, load_avg is 0.29. | ||
| 16 | After 22 seconds, recent_cpu is 60.39, load_avg is 0.31. | ||
| 17 | After 24 seconds, recent_cpu is 64.90, load_avg is 0.33. | ||
| 18 | After 26 seconds, recent_cpu is 69.27, load_avg is 0.35. | ||
| 19 | After 28 seconds, recent_cpu is 73.50, load_avg is 0.38. | ||
| 20 | After 30 seconds, recent_cpu is 77.60, load_avg is 0.40. | ||
| 21 | After 32 seconds, recent_cpu is 81.56, load_avg is 0.42. | ||
| 22 | After 34 seconds, recent_cpu is 85.40, load_avg is 0.44. | ||
| 23 | After 36 seconds, recent_cpu is 89.12, load_avg is 0.45. | ||
| 24 | After 38 seconds, recent_cpu is 92.72, load_avg is 0.47. | ||
| 25 | After 40 seconds, recent_cpu is 96.20, load_avg is 0.49. | ||
| 26 | After 42 seconds, recent_cpu is 99.57, load_avg is 0.51. | ||
| 27 | After 44 seconds, recent_cpu is 102.84, load_avg is 0.52. | ||
| 28 | After 46 seconds, recent_cpu is 106.00, load_avg is 0.54. | ||
| 29 | After 48 seconds, recent_cpu is 109.06, load_avg is 0.55. | ||
| 30 | After 50 seconds, recent_cpu is 112.02, load_avg is 0.57. | ||
| 31 | After 52 seconds, recent_cpu is 114.89, load_avg is 0.58. | ||
| 32 | After 54 seconds, recent_cpu is 117.66, load_avg is 0.60. | ||
| 33 | After 56 seconds, recent_cpu is 120.34, load_avg is 0.61. | ||
| 34 | After 58 seconds, recent_cpu is 122.94, load_avg is 0.62. | ||
| 35 | After 60 seconds, recent_cpu is 125.46, load_avg is 0.64. | ||
| 36 | After 62 seconds, recent_cpu is 127.89, load_avg is 0.65. | ||
| 37 | After 64 seconds, recent_cpu is 130.25, load_avg is 0.66. | ||
| 38 | After 66 seconds, recent_cpu is 132.53, load_avg is 0.67. | ||
| 39 | After 68 seconds, recent_cpu is 134.73, load_avg is 0.68. | ||
| 40 | After 70 seconds, recent_cpu is 136.86, load_avg is 0.69. | ||
| 41 | After 72 seconds, recent_cpu is 138.93, load_avg is 0.70. | ||
| 42 | After 74 seconds, recent_cpu is 140.93, load_avg is 0.71. | ||
| 43 | After 76 seconds, recent_cpu is 142.86, load_avg is 0.72. | ||
| 44 | After 78 seconds, recent_cpu is 144.73, load_avg is 0.73. | ||
| 45 | After 80 seconds, recent_cpu is 146.54, load_avg is 0.74. | ||
| 46 | After 82 seconds, recent_cpu is 148.29, load_avg is 0.75. | ||
| 47 | After 84 seconds, recent_cpu is 149.99, load_avg is 0.76. | ||
| 48 | After 86 seconds, recent_cpu is 151.63, load_avg is 0.76. | ||
| 49 | After 88 seconds, recent_cpu is 153.21, load_avg is 0.77. | ||
| 50 | After 90 seconds, recent_cpu is 154.75, load_avg is 0.78. | ||
| 51 | After 92 seconds, recent_cpu is 156.23, load_avg is 0.79. | ||
| 52 | After 94 seconds, recent_cpu is 157.67, load_avg is 0.79. | ||
| 53 | After 96 seconds, recent_cpu is 159.06, load_avg is 0.80. | ||
| 54 | After 98 seconds, recent_cpu is 160.40, load_avg is 0.81. | ||
| 55 | After 100 seconds, recent_cpu is 161.70, load_avg is 0.81. | ||
| 56 | After 102 seconds, recent_cpu is 162.96, load_avg is 0.82. | ||
| 57 | After 104 seconds, recent_cpu is 164.18, load_avg is 0.83. | ||
| 58 | After 106 seconds, recent_cpu is 165.35, load_avg is 0.83. | ||
| 59 | After 108 seconds, recent_cpu is 166.49, load_avg is 0.84. | ||
| 60 | After 110 seconds, recent_cpu is 167.59, load_avg is 0.84. | ||
| 61 | After 112 seconds, recent_cpu is 168.66, load_avg is 0.85. | ||
| 62 | After 114 seconds, recent_cpu is 169.69, load_avg is 0.85. | ||
| 63 | After 116 seconds, recent_cpu is 170.69, load_avg is 0.86. | ||
| 64 | After 118 seconds, recent_cpu is 171.65, load_avg is 0.86. | ||
| 65 | After 120 seconds, recent_cpu is 172.58, load_avg is 0.87. | ||
| 66 | After 122 seconds, recent_cpu is 173.49, load_avg is 0.87. | ||
| 67 | After 124 seconds, recent_cpu is 174.36, load_avg is 0.88. | ||
| 68 | After 126 seconds, recent_cpu is 175.20, load_avg is 0.88. | ||
| 69 | After 128 seconds, recent_cpu is 176.02, load_avg is 0.88. | ||
| 70 | After 130 seconds, recent_cpu is 176.81, load_avg is 0.89. | ||
| 71 | After 132 seconds, recent_cpu is 177.57, load_avg is 0.89. | ||
| 72 | After 134 seconds, recent_cpu is 178.31, load_avg is 0.89. | ||
| 73 | After 136 seconds, recent_cpu is 179.02, load_avg is 0.90. | ||
| 74 | After 138 seconds, recent_cpu is 179.72, load_avg is 0.90. | ||
| 75 | After 140 seconds, recent_cpu is 180.38, load_avg is 0.90. | ||
| 76 | After 142 seconds, recent_cpu is 181.03, load_avg is 0.91. | ||
| 77 | After 144 seconds, recent_cpu is 181.65, load_avg is 0.91. | ||
| 78 | After 146 seconds, recent_cpu is 182.26, load_avg is 0.91. | ||
| 79 | After 148 seconds, recent_cpu is 182.84, load_avg is 0.92. | ||
| 80 | After 150 seconds, recent_cpu is 183.41, load_avg is 0.92. | ||
| 81 | After 152 seconds, recent_cpu is 183.96, load_avg is 0.92. | ||
| 82 | After 154 seconds, recent_cpu is 184.49, load_avg is 0.92. | ||
| 83 | After 156 seconds, recent_cpu is 185.00, load_avg is 0.93. | ||
| 84 | After 158 seconds, recent_cpu is 185.49, load_avg is 0.93. | ||
| 85 | After 160 seconds, recent_cpu is 185.97, load_avg is 0.93. | ||
| 86 | After 162 seconds, recent_cpu is 186.43, load_avg is 0.93. | ||
| 87 | After 164 seconds, recent_cpu is 186.88, load_avg is 0.94. | ||
| 88 | After 166 seconds, recent_cpu is 187.31, load_avg is 0.94. | ||
| 89 | After 168 seconds, recent_cpu is 187.73, load_avg is 0.94. | ||
| 90 | After 170 seconds, recent_cpu is 188.14, load_avg is 0.94. | ||
| 91 | After 172 seconds, recent_cpu is 188.53, load_avg is 0.94. | ||
| 92 | After 174 seconds, recent_cpu is 188.91, load_avg is 0.95. | ||
| 93 | After 176 seconds, recent_cpu is 189.27, load_avg is 0.95. | ||
| 94 | After 178 seconds, recent_cpu is 189.63, load_avg is 0.95. | ||
| 95 | After 180 seconds, recent_cpu is 189.97, load_avg is 0.95. | ||
| 96 | */ | ||
| 97 | |||
| 98 | #include <stdio.h> | ||
| 99 | #include "tests/threads/tests.h" | ||
| 100 | #include "threads/init.h" | ||
| 101 | #include "threads/malloc.h" | ||
| 102 | #include "threads/synch.h" | ||
| 103 | #include "threads/thread.h" | ||
| 104 | #include "devices/timer.h" | ||
| 105 | |||
| 106 | /* Sensitive to assumption that recent_cpu updates happen exactly | ||
| 107 | when timer_ticks() % TIMER_FREQ == 0. */ | ||
| 108 | |||
| 109 | void | ||
| 110 | test_mlfqs_recent_1 (void) | ||
| 111 | { | ||
| 112 | int64_t start_time; | ||
| 113 | int last_elapsed = 0; | ||
| 114 | |||
| 115 | ASSERT (thread_mlfqs); | ||
| 116 | |||
| 117 | do | ||
| 118 | { | ||
| 119 | msg ("Sleeping 10 seconds to allow recent_cpu to decay, please wait..."); | ||
| 120 | start_time = timer_ticks (); | ||
| 121 | timer_sleep (DIV_ROUND_UP (start_time, TIMER_FREQ) - start_time | ||
| 122 | + 10 * TIMER_FREQ); | ||
| 123 | } | ||
| 124 | while (thread_get_recent_cpu () > 700); | ||
| 125 | |||
| 126 | start_time = timer_ticks (); | ||
| 127 | for (;;) | ||
| 128 | { | ||
| 129 | int elapsed = timer_elapsed (start_time); | ||
| 130 | if (elapsed % (TIMER_FREQ * 2) == 0 && elapsed > last_elapsed) | ||
| 131 | { | ||
| 132 | int recent_cpu = thread_get_recent_cpu (); | ||
| 133 | int load_avg = thread_get_load_avg (); | ||
| 134 | int elapsed_seconds = elapsed / TIMER_FREQ; | ||
| 135 | msg ("After %d seconds, recent_cpu is %d.%02d, load_avg is %d.%02d.", | ||
| 136 | elapsed_seconds, | ||
| 137 | recent_cpu / 100, recent_cpu % 100, | ||
| 138 | load_avg / 100, load_avg % 100); | ||
| 139 | if (elapsed_seconds >= 180) | ||
| 140 | break; | ||
| 141 | } | ||
| 142 | last_elapsed = elapsed; | ||
| 143 | } | ||
| 144 | } | ||
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.ck b/pintos-progos/tests/threads/mlfqs-recent-1.ck new file mode 100644 index 0000000..a2ba44d --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs-recent-1.ck | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::threads::mlfqs; | ||
| 6 | |||
| 7 | our ($test); | ||
| 8 | my (@output) = read_text_file ("$test.output"); | ||
| 9 | common_checks ("run", @output); | ||
| 10 | @output = get_core_output ("run", @output); | ||
| 11 | |||
| 12 | # Get actual values. | ||
| 13 | local ($_); | ||
| 14 | my (@actual); | ||
| 15 | foreach (@output) { | ||
| 16 | my ($t, $recent_cpu) = /After (\d+) seconds, recent_cpu is (\d+\.\d+),/ | ||
| 17 | or next; | ||
| 18 | $actual[$t] = $recent_cpu; | ||
| 19 | } | ||
| 20 | |||
| 21 | # Calculate expected values. | ||
| 22 | my ($expected_load_avg, $expected_recent_cpu) | ||
| 23 | = mlfqs_expected_load ([(1) x 180], [(100) x 180]); | ||
| 24 | my (@expected) = @$expected_recent_cpu; | ||
| 25 | |||
| 26 | # Compare actual and expected values. | ||
| 27 | mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2], | ||
| 28 | "Some recent_cpu values were missing or " | ||
| 29 | . "differed from those expected " | ||
| 30 | . "by more than 2.5."); | ||
| 31 | pass; | ||
diff --git a/pintos-progos/tests/threads/mlfqs.pm b/pintos-progos/tests/threads/mlfqs.pm new file mode 100644 index 0000000..184ac16 --- /dev/null +++ b/pintos-progos/tests/threads/mlfqs.pm | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | |||
| 5 | sub mlfqs_expected_load { | ||
| 6 | my ($ready, $recent_delta) = @_; | ||
| 7 | my (@load_avg) = 0; | ||
| 8 | my (@recent_cpu) = 0; | ||
| 9 | my ($load_avg) = 0; | ||
| 10 | my ($recent_cpu) = 0; | ||
| 11 | for my $i (0...$#$ready) { | ||
| 12 | $load_avg = (59/60) * $load_avg + (1/60) * $ready->[$i]; | ||
| 13 | push (@load_avg, $load_avg); | ||
| 14 | |||
| 15 | if (defined $recent_delta->[$i]) { | ||
| 16 | my ($twice_load) = $load_avg * 2; | ||
| 17 | my ($load_factor) = $twice_load / ($twice_load + 1); | ||
| 18 | $recent_cpu = ($recent_cpu + $recent_delta->[$i]) * $load_factor; | ||
| 19 | push (@recent_cpu, $recent_cpu); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | return (\@load_avg, \@recent_cpu); | ||
| 23 | } | ||
| 24 | |||
| 25 | sub mlfqs_expected_ticks { | ||
| 26 | my (@nice) = @_; | ||
| 27 | my ($thread_cnt) = scalar (@nice); | ||
| 28 | my (@recent_cpu) = (0) x $thread_cnt; | ||
| 29 | my (@slices) = (0) x $thread_cnt; | ||
| 30 | my (@fifo) = (0) x $thread_cnt; | ||
| 31 | my ($next_fifo) = 1; | ||
| 32 | my ($load_avg) = 0; | ||
| 33 | for my $i (1...750) { | ||
| 34 | if ($i % 25 == 0) { | ||
| 35 | # Update load average. | ||
| 36 | $load_avg = (59/60) * $load_avg + (1/60) * $thread_cnt; | ||
| 37 | |||
| 38 | # Update recent_cpu. | ||
| 39 | my ($twice_load) = $load_avg * 2; | ||
| 40 | my ($load_factor) = $twice_load / ($twice_load + 1); | ||
| 41 | $recent_cpu[$_] = $recent_cpu[$_] * $load_factor + $nice[$_] | ||
| 42 | foreach 0...($thread_cnt - 1); | ||
| 43 | } | ||
| 44 | |||
| 45 | # Update priorities. | ||
| 46 | my (@priority); | ||
| 47 | foreach my $j (0...($thread_cnt - 1)) { | ||
| 48 | my ($priority) = int ($recent_cpu[$j] / 4 + $nice[$j] * 2); | ||
| 49 | $priority = 0 if $priority < 0; | ||
| 50 | $priority = 63 if $priority > 63; | ||
| 51 | push (@priority, $priority); | ||
| 52 | } | ||
| 53 | |||
| 54 | # Choose thread to run. | ||
| 55 | my $max = 0; | ||
| 56 | for my $j (1...$#priority) { | ||
| 57 | if ($priority[$j] < $priority[$max] | ||
| 58 | || ($priority[$j] == $priority[$max] | ||
| 59 | && $fifo[$j] < $fifo[$max])) { | ||
| 60 | $max = $j; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | $fifo[$max] = $next_fifo++; | ||
| 64 | |||
| 65 | # Run thread. | ||
| 66 | $recent_cpu[$max] += 4; | ||
| 67 | $slices[$max] += 4; | ||
| 68 | } | ||
| 69 | return @slices; | ||
| 70 | } | ||
| 71 | |||
| 72 | sub check_mlfqs_fair { | ||
| 73 | my ($nice, $maxdiff) = @_; | ||
| 74 | our ($test); | ||
| 75 | my (@output) = read_text_file ("$test.output"); | ||
| 76 | common_checks ("run", @output); | ||
| 77 | @output = get_core_output ("run", @output); | ||
| 78 | |||
| 79 | my (@actual); | ||
| 80 | local ($_); | ||
| 81 | foreach (@output) { | ||
| 82 | my ($id, $count) = /Thread (\d+) received (\d+) ticks\./ or next; | ||
| 83 | $actual[$id] = $count; | ||
| 84 | } | ||
| 85 | |||
| 86 | my (@expected) = mlfqs_expected_ticks (@$nice); | ||
| 87 | mlfqs_compare ("thread", "%d", | ||
| 88 | \@actual, \@expected, $maxdiff, [0, $#$nice, 1], | ||
| 89 | "Some tick counts were missing or differed from those " | ||
| 90 | . "expected by more than $maxdiff."); | ||
| 91 | pass; | ||
| 92 | } | ||
| 93 | |||
| 94 | sub mlfqs_compare { | ||
| 95 | my ($indep_var, $format, | ||
| 96 | $actual_ref, $expected_ref, $maxdiff, $t_range, $message) = @_; | ||
| 97 | my ($t_min, $t_max, $t_step) = @$t_range; | ||
| 98 | |||
| 99 | my ($ok) = 1; | ||
| 100 | for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) { | ||
| 101 | my ($actual) = $actual_ref->[$t]; | ||
| 102 | my ($expected) = $expected_ref->[$t]; | ||
| 103 | $ok = 0, last | ||
| 104 | if !defined ($actual) || abs ($actual - $expected) > $maxdiff + .01; | ||
| 105 | } | ||
| 106 | return if $ok; | ||
| 107 | |||
| 108 | print "$message\n"; | ||
| 109 | mlfqs_row ($indep_var, "actual", "<->", "expected", "explanation"); | ||
| 110 | mlfqs_row ("------", "--------", "---", "--------", '-' x 40); | ||
| 111 | for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) { | ||
| 112 | my ($actual) = $actual_ref->[$t]; | ||
| 113 | my ($expected) = $expected_ref->[$t]; | ||
| 114 | my ($diff, $rationale); | ||
| 115 | if (!defined $actual) { | ||
| 116 | $actual = 'undef' ; | ||
| 117 | $diff = ''; | ||
| 118 | $rationale = 'Missing value.'; | ||
| 119 | } else { | ||
| 120 | my ($delta) = abs ($actual - $expected); | ||
| 121 | if ($delta > $maxdiff + .01) { | ||
| 122 | my ($excess) = $delta - $maxdiff; | ||
| 123 | if ($actual > $expected) { | ||
| 124 | $diff = '>>>'; | ||
| 125 | $rationale = sprintf "Too big, by $format.", $excess; | ||
| 126 | } else { | ||
| 127 | $diff = '<<<'; | ||
| 128 | $rationale = sprintf "Too small, by $format.", $excess; | ||
| 129 | } | ||
| 130 | } else { | ||
| 131 | $diff = ' = '; | ||
| 132 | $rationale = ''; | ||
| 133 | } | ||
| 134 | $actual = sprintf ($format, $actual); | ||
| 135 | } | ||
| 136 | $expected = sprintf ($format, $expected); | ||
| 137 | mlfqs_row ($t, $actual, $diff, $expected, $rationale); | ||
| 138 | } | ||
| 139 | fail; | ||
| 140 | } | ||
| 141 | |||
| 142 | sub mlfqs_row { | ||
| 143 | printf "%6s %8s %3s %-8s %s\n", @_; | ||
| 144 | } | ||
| 145 | |||
| 146 | 1; | ||
diff --git a/pintos-progos/tests/threads/priority-change.c b/pintos-progos/tests/threads/priority-change.c new file mode 100644 index 0000000..810b05a --- /dev/null +++ b/pintos-progos/tests/threads/priority-change.c | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* Verifies that lowering a thread's priority so that it is no | ||
| 2 | longer the highest-priority thread in the system causes it to | ||
| 3 | yield immediately. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/threads/tests.h" | ||
| 7 | #include "threads/init.h" | ||
| 8 | #include "threads/thread.h" | ||
| 9 | |||
| 10 | static thread_func changing_thread; | ||
| 11 | |||
| 12 | void | ||
| 13 | test_priority_change (void) | ||
| 14 | { | ||
| 15 | /* This test does not work with the MLFQS. */ | ||
| 16 | ASSERT (!thread_mlfqs); | ||
| 17 | |||
| 18 | msg ("Creating a high-priority thread 2."); | ||
| 19 | thread_create ("thread 2", PRI_DEFAULT + 1, changing_thread, NULL); | ||
| 20 | msg ("Thread 2 should have just lowered its priority."); | ||
| 21 | thread_set_priority (PRI_DEFAULT - 2); | ||
| 22 | msg ("Thread 2 should have just exited."); | ||
| 23 | } | ||
| 24 | |||
| 25 | static void | ||
| 26 | changing_thread (void *aux UNUSED) | ||
| 27 | { | ||
| 28 | msg ("Thread 2 now lowering priority."); | ||
| 29 | thread_set_priority (PRI_DEFAULT - 1); | ||
| 30 | msg ("Thread 2 exiting."); | ||
| 31 | } | ||
diff --git a/pintos-progos/tests/threads/priority-change.ck b/pintos-progos/tests/threads/priority-change.ck new file mode 100644 index 0000000..f4d9b2f --- /dev/null +++ b/pintos-progos/tests/threads/priority-change.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-change) begin | ||
| 7 | (priority-change) Creating a high-priority thread 2. | ||
| 8 | (priority-change) Thread 2 now lowering priority. | ||
| 9 | (priority-change) Thread 2 should have just lowered its priority. | ||
| 10 | (priority-change) Thread 2 exiting. | ||
| 11 | (priority-change) Thread 2 should have just exited. | ||
| 12 | (priority-change) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-condvar.c b/pintos-progos/tests/threads/priority-condvar.c new file mode 100644 index 0000000..c1efb1b --- /dev/null +++ b/pintos-progos/tests/threads/priority-condvar.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* Tests that cond_signal() wakes up the highest-priority thread | ||
| 2 | waiting in cond_wait(). */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/threads/tests.h" | ||
| 6 | #include "threads/init.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "devices/timer.h" | ||
| 11 | |||
| 12 | static thread_func priority_condvar_thread; | ||
| 13 | static struct lock lock; | ||
| 14 | static struct condition condition; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_priority_condvar (void) | ||
| 18 | { | ||
| 19 | int i; | ||
| 20 | |||
| 21 | /* This test does not work with the MLFQS. */ | ||
| 22 | ASSERT (!thread_mlfqs); | ||
| 23 | |||
| 24 | lock_init (&lock); | ||
| 25 | cond_init (&condition); | ||
| 26 | |||
| 27 | thread_set_priority (PRI_MIN); | ||
| 28 | for (i = 0; i < 10; i++) | ||
| 29 | { | ||
| 30 | int priority = PRI_DEFAULT - (i + 7) % 10 - 1; | ||
| 31 | char name[16]; | ||
| 32 | snprintf (name, sizeof name, "priority %d", priority); | ||
| 33 | thread_create (name, priority, priority_condvar_thread, NULL); | ||
| 34 | } | ||
| 35 | |||
| 36 | for (i = 0; i < 10; i++) | ||
| 37 | { | ||
| 38 | lock_acquire (&lock); | ||
| 39 | msg ("Signaling..."); | ||
| 40 | cond_signal (&condition, &lock); | ||
| 41 | lock_release (&lock); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | static void | ||
| 46 | priority_condvar_thread (void *aux UNUSED) | ||
| 47 | { | ||
| 48 | msg ("Thread %s starting.", thread_name ()); | ||
| 49 | lock_acquire (&lock); | ||
| 50 | cond_wait (&condition, &lock); | ||
| 51 | msg ("Thread %s woke up.", thread_name ()); | ||
| 52 | lock_release (&lock); | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/threads/priority-condvar.ck b/pintos-progos/tests/threads/priority-condvar.ck new file mode 100644 index 0000000..195c1ab --- /dev/null +++ b/pintos-progos/tests/threads/priority-condvar.ck | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-condvar) begin | ||
| 7 | (priority-condvar) Thread priority 23 starting. | ||
| 8 | (priority-condvar) Thread priority 22 starting. | ||
| 9 | (priority-condvar) Thread priority 21 starting. | ||
| 10 | (priority-condvar) Thread priority 30 starting. | ||
| 11 | (priority-condvar) Thread priority 29 starting. | ||
| 12 | (priority-condvar) Thread priority 28 starting. | ||
| 13 | (priority-condvar) Thread priority 27 starting. | ||
| 14 | (priority-condvar) Thread priority 26 starting. | ||
| 15 | (priority-condvar) Thread priority 25 starting. | ||
| 16 | (priority-condvar) Thread priority 24 starting. | ||
| 17 | (priority-condvar) Signaling... | ||
| 18 | (priority-condvar) Thread priority 30 woke up. | ||
| 19 | (priority-condvar) Signaling... | ||
| 20 | (priority-condvar) Thread priority 29 woke up. | ||
| 21 | (priority-condvar) Signaling... | ||
| 22 | (priority-condvar) Thread priority 28 woke up. | ||
| 23 | (priority-condvar) Signaling... | ||
| 24 | (priority-condvar) Thread priority 27 woke up. | ||
| 25 | (priority-condvar) Signaling... | ||
| 26 | (priority-condvar) Thread priority 26 woke up. | ||
| 27 | (priority-condvar) Signaling... | ||
| 28 | (priority-condvar) Thread priority 25 woke up. | ||
| 29 | (priority-condvar) Signaling... | ||
| 30 | (priority-condvar) Thread priority 24 woke up. | ||
| 31 | (priority-condvar) Signaling... | ||
| 32 | (priority-condvar) Thread priority 23 woke up. | ||
| 33 | (priority-condvar) Signaling... | ||
| 34 | (priority-condvar) Thread priority 22 woke up. | ||
| 35 | (priority-condvar) Signaling... | ||
| 36 | (priority-condvar) Thread priority 21 woke up. | ||
| 37 | (priority-condvar) end | ||
| 38 | EOF | ||
| 39 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-chain.c b/pintos-progos/tests/threads/priority-donate-chain.c new file mode 100644 index 0000000..3ffabca --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-chain.c | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* The main thread set its priority to PRI_MIN and creates 7 threads | ||
| 2 | (thread 1..7) with priorities PRI_MIN + 3, 6, 9, 12, ... | ||
| 3 | The main thread initializes 8 locks: lock 0..7 and acquires lock 0. | ||
| 4 | |||
| 5 | When thread[i] starts, it first acquires lock[i] (unless i == 7.) | ||
| 6 | Subsequently, thread[i] attempts to acquire lock[i-1], which is held by | ||
| 7 | thread[i-1], except for lock[0], which is held by the main thread. | ||
| 8 | Because the lock is held, thread[i] donates its priority to thread[i-1], | ||
| 9 | which donates to thread[i-2], and so on until the main thread | ||
| 10 | receives the donation. | ||
| 11 | |||
| 12 | After threads[1..7] have been created and are blocked on locks[0..7], | ||
| 13 | the main thread releases lock[0], unblocking thread[1], and being | ||
| 14 | preempted by it. | ||
| 15 | Thread[1] then completes acquiring lock[0], then releases lock[0], | ||
| 16 | then releases lock[1], unblocking thread[2], etc. | ||
| 17 | Thread[7] finally acquires & releases lock[7] and exits, allowing | ||
| 18 | thread[6], then thread[5] etc. to run and exit until finally the | ||
| 19 | main thread exits. | ||
| 20 | |||
| 21 | In addition, interloper threads are created at priority levels | ||
| 22 | p = PRI_MIN + 2, 5, 8, 11, ... which should not be run until the | ||
| 23 | corresponding thread with priority p + 1 has finished. | ||
| 24 | |||
| 25 | Written by Godmar Back <gback@cs.vt.edu> */ | ||
| 26 | |||
| 27 | #include <stdio.h> | ||
| 28 | #include "tests/threads/tests.h" | ||
| 29 | #include "threads/init.h" | ||
| 30 | #include "threads/synch.h" | ||
| 31 | #include "threads/thread.h" | ||
| 32 | |||
| 33 | #define NESTING_DEPTH 8 | ||
| 34 | |||
| 35 | struct lock_pair | ||
| 36 | { | ||
| 37 | struct lock *second; | ||
| 38 | struct lock *first; | ||
| 39 | }; | ||
| 40 | |||
| 41 | static thread_func donor_thread_func; | ||
| 42 | static thread_func interloper_thread_func; | ||
| 43 | |||
| 44 | void | ||
| 45 | test_priority_donate_chain (void) | ||
| 46 | { | ||
| 47 | int i; | ||
| 48 | struct lock locks[NESTING_DEPTH - 1]; | ||
| 49 | struct lock_pair lock_pairs[NESTING_DEPTH]; | ||
| 50 | |||
| 51 | /* This test does not work with the MLFQS. */ | ||
| 52 | ASSERT (!thread_mlfqs); | ||
| 53 | |||
| 54 | thread_set_priority (PRI_MIN); | ||
| 55 | |||
| 56 | for (i = 0; i < NESTING_DEPTH - 1; i++) | ||
| 57 | lock_init (&locks[i]); | ||
| 58 | |||
| 59 | lock_acquire (&locks[0]); | ||
| 60 | msg ("%s got lock.", thread_name ()); | ||
| 61 | |||
| 62 | for (i = 1; i < NESTING_DEPTH; i++) | ||
| 63 | { | ||
| 64 | char name[16]; | ||
| 65 | int thread_priority; | ||
| 66 | |||
| 67 | snprintf (name, sizeof name, "thread %d", i); | ||
| 68 | thread_priority = PRI_MIN + i * 3; | ||
| 69 | lock_pairs[i].first = i < NESTING_DEPTH - 1 ? locks + i: NULL; | ||
| 70 | lock_pairs[i].second = locks + i - 1; | ||
| 71 | |||
| 72 | thread_create (name, thread_priority, donor_thread_func, lock_pairs + i); | ||
| 73 | msg ("%s should have priority %d. Actual priority: %d.", | ||
| 74 | thread_name (), thread_priority, thread_get_priority ()); | ||
| 75 | |||
| 76 | snprintf (name, sizeof name, "interloper %d", i); | ||
| 77 | thread_create (name, thread_priority - 1, interloper_thread_func, NULL); | ||
| 78 | } | ||
| 79 | |||
| 80 | lock_release (&locks[0]); | ||
| 81 | msg ("%s finishing with priority %d.", thread_name (), | ||
| 82 | thread_get_priority ()); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void | ||
| 86 | donor_thread_func (void *locks_) | ||
| 87 | { | ||
| 88 | struct lock_pair *locks = locks_; | ||
| 89 | |||
| 90 | if (locks->first) | ||
| 91 | lock_acquire (locks->first); | ||
| 92 | |||
| 93 | lock_acquire (locks->second); | ||
| 94 | msg ("%s got lock", thread_name ()); | ||
| 95 | |||
| 96 | lock_release (locks->second); | ||
| 97 | msg ("%s should have priority %d. Actual priority: %d", | ||
| 98 | thread_name (), (NESTING_DEPTH - 1) * 3, | ||
| 99 | thread_get_priority ()); | ||
| 100 | |||
| 101 | if (locks->first) | ||
| 102 | lock_release (locks->first); | ||
| 103 | |||
| 104 | msg ("%s finishing with priority %d.", thread_name (), | ||
| 105 | thread_get_priority ()); | ||
| 106 | } | ||
| 107 | |||
| 108 | static void | ||
| 109 | interloper_thread_func (void *arg_ UNUSED) | ||
| 110 | { | ||
| 111 | msg ("%s finished.", thread_name ()); | ||
| 112 | } | ||
| 113 | |||
| 114 | // vim: sw=2 | ||
diff --git a/pintos-progos/tests/threads/priority-donate-chain.ck b/pintos-progos/tests/threads/priority-donate-chain.ck new file mode 100644 index 0000000..213e649 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-chain.ck | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-chain) begin | ||
| 7 | (priority-donate-chain) main got lock. | ||
| 8 | (priority-donate-chain) main should have priority 3. Actual priority: 3. | ||
| 9 | (priority-donate-chain) main should have priority 6. Actual priority: 6. | ||
| 10 | (priority-donate-chain) main should have priority 9. Actual priority: 9. | ||
| 11 | (priority-donate-chain) main should have priority 12. Actual priority: 12. | ||
| 12 | (priority-donate-chain) main should have priority 15. Actual priority: 15. | ||
| 13 | (priority-donate-chain) main should have priority 18. Actual priority: 18. | ||
| 14 | (priority-donate-chain) main should have priority 21. Actual priority: 21. | ||
| 15 | (priority-donate-chain) thread 1 got lock | ||
| 16 | (priority-donate-chain) thread 1 should have priority 21. Actual priority: 21 | ||
| 17 | (priority-donate-chain) thread 2 got lock | ||
| 18 | (priority-donate-chain) thread 2 should have priority 21. Actual priority: 21 | ||
| 19 | (priority-donate-chain) thread 3 got lock | ||
| 20 | (priority-donate-chain) thread 3 should have priority 21. Actual priority: 21 | ||
| 21 | (priority-donate-chain) thread 4 got lock | ||
| 22 | (priority-donate-chain) thread 4 should have priority 21. Actual priority: 21 | ||
| 23 | (priority-donate-chain) thread 5 got lock | ||
| 24 | (priority-donate-chain) thread 5 should have priority 21. Actual priority: 21 | ||
| 25 | (priority-donate-chain) thread 6 got lock | ||
| 26 | (priority-donate-chain) thread 6 should have priority 21. Actual priority: 21 | ||
| 27 | (priority-donate-chain) thread 7 got lock | ||
| 28 | (priority-donate-chain) thread 7 should have priority 21. Actual priority: 21 | ||
| 29 | (priority-donate-chain) thread 7 finishing with priority 21. | ||
| 30 | (priority-donate-chain) interloper 7 finished. | ||
| 31 | (priority-donate-chain) thread 6 finishing with priority 18. | ||
| 32 | (priority-donate-chain) interloper 6 finished. | ||
| 33 | (priority-donate-chain) thread 5 finishing with priority 15. | ||
| 34 | (priority-donate-chain) interloper 5 finished. | ||
| 35 | (priority-donate-chain) thread 4 finishing with priority 12. | ||
| 36 | (priority-donate-chain) interloper 4 finished. | ||
| 37 | (priority-donate-chain) thread 3 finishing with priority 9. | ||
| 38 | (priority-donate-chain) interloper 3 finished. | ||
| 39 | (priority-donate-chain) thread 2 finishing with priority 6. | ||
| 40 | (priority-donate-chain) interloper 2 finished. | ||
| 41 | (priority-donate-chain) thread 1 finishing with priority 3. | ||
| 42 | (priority-donate-chain) interloper 1 finished. | ||
| 43 | (priority-donate-chain) main finishing with priority 0. | ||
| 44 | (priority-donate-chain) end | ||
| 45 | EOF | ||
| 46 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-lower.c b/pintos-progos/tests/threads/priority-donate-lower.c new file mode 100644 index 0000000..4965d75 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-lower.c | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | /* The main thread acquires a lock. Then it creates a | ||
| 2 | higher-priority thread that blocks acquiring the lock, causing | ||
| 3 | it to donate their priorities to the main thread. The main | ||
| 4 | thread attempts to lower its priority, which should not take | ||
| 5 | effect until the donation is released. */ | ||
| 6 | |||
| 7 | #include <stdio.h> | ||
| 8 | #include "tests/threads/tests.h" | ||
| 9 | #include "threads/init.h" | ||
| 10 | #include "threads/synch.h" | ||
| 11 | #include "threads/thread.h" | ||
| 12 | |||
| 13 | static thread_func acquire_thread_func; | ||
| 14 | |||
| 15 | void | ||
| 16 | test_priority_donate_lower (void) | ||
| 17 | { | ||
| 18 | struct lock lock; | ||
| 19 | |||
| 20 | /* This test does not work with the MLFQS. */ | ||
| 21 | ASSERT (!thread_mlfqs); | ||
| 22 | |||
| 23 | /* Make sure our priority is the default. */ | ||
| 24 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 25 | |||
| 26 | lock_init (&lock); | ||
| 27 | lock_acquire (&lock); | ||
| 28 | thread_create ("acquire", PRI_DEFAULT + 10, acquire_thread_func, &lock); | ||
| 29 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 30 | PRI_DEFAULT + 10, thread_get_priority ()); | ||
| 31 | |||
| 32 | msg ("Lowering base priority..."); | ||
| 33 | thread_set_priority (PRI_DEFAULT - 10); | ||
| 34 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 35 | PRI_DEFAULT + 10, thread_get_priority ()); | ||
| 36 | lock_release (&lock); | ||
| 37 | msg ("acquire must already have finished."); | ||
| 38 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 39 | PRI_DEFAULT - 10, thread_get_priority ()); | ||
| 40 | } | ||
| 41 | |||
| 42 | static void | ||
| 43 | acquire_thread_func (void *lock_) | ||
| 44 | { | ||
| 45 | struct lock *lock = lock_; | ||
| 46 | |||
| 47 | lock_acquire (lock); | ||
| 48 | msg ("acquire: got the lock"); | ||
| 49 | lock_release (lock); | ||
| 50 | msg ("acquire: done"); | ||
| 51 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-lower.ck b/pintos-progos/tests/threads/priority-donate-lower.ck new file mode 100644 index 0000000..c9bb61b --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-lower.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-lower) begin | ||
| 7 | (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. | ||
| 8 | (priority-donate-lower) Lowering base priority... | ||
| 9 | (priority-donate-lower) Main thread should have priority 41. Actual priority: 41. | ||
| 10 | (priority-donate-lower) acquire: got the lock | ||
| 11 | (priority-donate-lower) acquire: done | ||
| 12 | (priority-donate-lower) acquire must already have finished. | ||
| 13 | (priority-donate-lower) Main thread should have priority 21. Actual priority: 21. | ||
| 14 | (priority-donate-lower) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.c b/pintos-progos/tests/threads/priority-donate-multiple.c new file mode 100644 index 0000000..df4689c --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple.c | |||
| @@ -0,0 +1,77 @@ | |||
| 1 | /* The main thread acquires locks A and B, then it creates two | ||
| 2 | higher-priority threads. Each of these threads blocks | ||
| 3 | acquiring one of the locks and thus donate their priority to | ||
| 4 | the main thread. The main thread releases the locks in turn | ||
| 5 | and relinquishes its donated priorities. | ||
| 6 | |||
| 7 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 8 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 9 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 10 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | static thread_func a_thread_func; | ||
| 19 | static thread_func b_thread_func; | ||
| 20 | |||
| 21 | void | ||
| 22 | test_priority_donate_multiple (void) | ||
| 23 | { | ||
| 24 | struct lock a, b; | ||
| 25 | |||
| 26 | /* This test does not work with the MLFQS. */ | ||
| 27 | ASSERT (!thread_mlfqs); | ||
| 28 | |||
| 29 | /* Make sure our priority is the default. */ | ||
| 30 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 31 | |||
| 32 | lock_init (&a); | ||
| 33 | lock_init (&b); | ||
| 34 | |||
| 35 | lock_acquire (&a); | ||
| 36 | lock_acquire (&b); | ||
| 37 | |||
| 38 | thread_create ("a", PRI_DEFAULT + 1, a_thread_func, &a); | ||
| 39 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 40 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 41 | |||
| 42 | thread_create ("b", PRI_DEFAULT + 2, b_thread_func, &b); | ||
| 43 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 44 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 45 | |||
| 46 | lock_release (&b); | ||
| 47 | msg ("Thread b should have just finished."); | ||
| 48 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 49 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 50 | |||
| 51 | lock_release (&a); | ||
| 52 | msg ("Thread a should have just finished."); | ||
| 53 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 54 | PRI_DEFAULT, thread_get_priority ()); | ||
| 55 | } | ||
| 56 | |||
| 57 | static void | ||
| 58 | a_thread_func (void *lock_) | ||
| 59 | { | ||
| 60 | struct lock *lock = lock_; | ||
| 61 | |||
| 62 | lock_acquire (lock); | ||
| 63 | msg ("Thread a acquired lock a."); | ||
| 64 | lock_release (lock); | ||
| 65 | msg ("Thread a finished."); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void | ||
| 69 | b_thread_func (void *lock_) | ||
| 70 | { | ||
| 71 | struct lock *lock = lock_; | ||
| 72 | |||
| 73 | lock_acquire (lock); | ||
| 74 | msg ("Thread b acquired lock b."); | ||
| 75 | lock_release (lock); | ||
| 76 | msg ("Thread b finished."); | ||
| 77 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.ck b/pintos-progos/tests/threads/priority-donate-multiple.ck new file mode 100644 index 0000000..0afd20b --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-multiple) begin | ||
| 7 | (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. | ||
| 8 | (priority-donate-multiple) Main thread should have priority 33. Actual priority: 33. | ||
| 9 | (priority-donate-multiple) Thread b acquired lock b. | ||
| 10 | (priority-donate-multiple) Thread b finished. | ||
| 11 | (priority-donate-multiple) Thread b should have just finished. | ||
| 12 | (priority-donate-multiple) Main thread should have priority 32. Actual priority: 32. | ||
| 13 | (priority-donate-multiple) Thread a acquired lock a. | ||
| 14 | (priority-donate-multiple) Thread a finished. | ||
| 15 | (priority-donate-multiple) Thread a should have just finished. | ||
| 16 | (priority-donate-multiple) Main thread should have priority 31. Actual priority: 31. | ||
| 17 | (priority-donate-multiple) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.c b/pintos-progos/tests/threads/priority-donate-multiple2.c new file mode 100644 index 0000000..7f65fef --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple2.c | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | /* The main thread acquires locks A and B, then it creates three | ||
| 2 | higher-priority threads. The first two of these threads block | ||
| 3 | acquiring one of the locks and thus donate their priority to | ||
| 4 | the main thread. The main thread releases the locks in turn | ||
| 5 | and relinquishes its donated priorities, allowing the third thread | ||
| 6 | to run. | ||
| 7 | |||
| 8 | In this test, the main thread releases the locks in a different | ||
| 9 | order compared to priority-donate-multiple.c. | ||
| 10 | |||
| 11 | Written by Godmar Back <gback@cs.vt.edu>. | ||
| 12 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 13 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 14 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 15 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 16 | |||
| 17 | #include <stdio.h> | ||
| 18 | #include "tests/threads/tests.h" | ||
| 19 | #include "threads/init.h" | ||
| 20 | #include "threads/synch.h" | ||
| 21 | #include "threads/thread.h" | ||
| 22 | |||
| 23 | static thread_func a_thread_func; | ||
| 24 | static thread_func b_thread_func; | ||
| 25 | static thread_func c_thread_func; | ||
| 26 | |||
| 27 | void | ||
| 28 | test_priority_donate_multiple2 (void) | ||
| 29 | { | ||
| 30 | struct lock a, b; | ||
| 31 | |||
| 32 | /* This test does not work with the MLFQS. */ | ||
| 33 | ASSERT (!thread_mlfqs); | ||
| 34 | |||
| 35 | /* Make sure our priority is the default. */ | ||
| 36 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 37 | |||
| 38 | lock_init (&a); | ||
| 39 | lock_init (&b); | ||
| 40 | |||
| 41 | lock_acquire (&a); | ||
| 42 | lock_acquire (&b); | ||
| 43 | |||
| 44 | thread_create ("a", PRI_DEFAULT + 3, a_thread_func, &a); | ||
| 45 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 46 | PRI_DEFAULT + 3, thread_get_priority ()); | ||
| 47 | |||
| 48 | thread_create ("c", PRI_DEFAULT + 1, c_thread_func, NULL); | ||
| 49 | |||
| 50 | thread_create ("b", PRI_DEFAULT + 5, b_thread_func, &b); | ||
| 51 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 52 | PRI_DEFAULT + 5, thread_get_priority ()); | ||
| 53 | |||
| 54 | lock_release (&a); | ||
| 55 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 56 | PRI_DEFAULT + 5, thread_get_priority ()); | ||
| 57 | |||
| 58 | lock_release (&b); | ||
| 59 | msg ("Threads b, a, c should have just finished, in that order."); | ||
| 60 | msg ("Main thread should have priority %d. Actual priority: %d.", | ||
| 61 | PRI_DEFAULT, thread_get_priority ()); | ||
| 62 | } | ||
| 63 | |||
| 64 | static void | ||
| 65 | a_thread_func (void *lock_) | ||
| 66 | { | ||
| 67 | struct lock *lock = lock_; | ||
| 68 | |||
| 69 | lock_acquire (lock); | ||
| 70 | msg ("Thread a acquired lock a."); | ||
| 71 | lock_release (lock); | ||
| 72 | msg ("Thread a finished."); | ||
| 73 | } | ||
| 74 | |||
| 75 | static void | ||
| 76 | b_thread_func (void *lock_) | ||
| 77 | { | ||
| 78 | struct lock *lock = lock_; | ||
| 79 | |||
| 80 | lock_acquire (lock); | ||
| 81 | msg ("Thread b acquired lock b."); | ||
| 82 | lock_release (lock); | ||
| 83 | msg ("Thread b finished."); | ||
| 84 | } | ||
| 85 | |||
| 86 | static void | ||
| 87 | c_thread_func (void *a_ UNUSED) | ||
| 88 | { | ||
| 89 | msg ("Thread c finished."); | ||
| 90 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.ck b/pintos-progos/tests/threads/priority-donate-multiple2.ck new file mode 100644 index 0000000..b23533a --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-multiple2.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-multiple2) begin | ||
| 7 | (priority-donate-multiple2) Main thread should have priority 34. Actual priority: 34. | ||
| 8 | (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. | ||
| 9 | (priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36. | ||
| 10 | (priority-donate-multiple2) Thread b acquired lock b. | ||
| 11 | (priority-donate-multiple2) Thread b finished. | ||
| 12 | (priority-donate-multiple2) Thread a acquired lock a. | ||
| 13 | (priority-donate-multiple2) Thread a finished. | ||
| 14 | (priority-donate-multiple2) Thread c finished. | ||
| 15 | (priority-donate-multiple2) Threads b, a, c should have just finished, in that order. | ||
| 16 | (priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31. | ||
| 17 | (priority-donate-multiple2) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-nest.c b/pintos-progos/tests/threads/priority-donate-nest.c new file mode 100644 index 0000000..3a3a9a5 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-nest.c | |||
| @@ -0,0 +1,94 @@ | |||
| 1 | /* Low-priority main thread L acquires lock A. Medium-priority | ||
| 2 | thread M then acquires lock B then blocks on acquiring lock A. | ||
| 3 | High-priority thread H then blocks on acquiring lock B. Thus, | ||
| 4 | thread H donates its priority to M, which in turn donates it | ||
| 5 | to thread L. | ||
| 6 | |||
| 7 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 8 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 9 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 10 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | struct locks | ||
| 19 | { | ||
| 20 | struct lock *a; | ||
| 21 | struct lock *b; | ||
| 22 | }; | ||
| 23 | |||
| 24 | static thread_func medium_thread_func; | ||
| 25 | static thread_func high_thread_func; | ||
| 26 | |||
| 27 | void | ||
| 28 | test_priority_donate_nest (void) | ||
| 29 | { | ||
| 30 | struct lock a, b; | ||
| 31 | struct locks locks; | ||
| 32 | |||
| 33 | /* This test does not work with the MLFQS. */ | ||
| 34 | ASSERT (!thread_mlfqs); | ||
| 35 | |||
| 36 | /* Make sure our priority is the default. */ | ||
| 37 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 38 | |||
| 39 | lock_init (&a); | ||
| 40 | lock_init (&b); | ||
| 41 | |||
| 42 | lock_acquire (&a); | ||
| 43 | |||
| 44 | locks.a = &a; | ||
| 45 | locks.b = &b; | ||
| 46 | thread_create ("medium", PRI_DEFAULT + 1, medium_thread_func, &locks); | ||
| 47 | thread_yield (); | ||
| 48 | msg ("Low thread should have priority %d. Actual priority: %d.", | ||
| 49 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 50 | |||
| 51 | thread_create ("high", PRI_DEFAULT + 2, high_thread_func, &b); | ||
| 52 | thread_yield (); | ||
| 53 | msg ("Low thread should have priority %d. Actual priority: %d.", | ||
| 54 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 55 | |||
| 56 | lock_release (&a); | ||
| 57 | thread_yield (); | ||
| 58 | msg ("Medium thread should just have finished."); | ||
| 59 | msg ("Low thread should have priority %d. Actual priority: %d.", | ||
| 60 | PRI_DEFAULT, thread_get_priority ()); | ||
| 61 | } | ||
| 62 | |||
| 63 | static void | ||
| 64 | medium_thread_func (void *locks_) | ||
| 65 | { | ||
| 66 | struct locks *locks = locks_; | ||
| 67 | |||
| 68 | lock_acquire (locks->b); | ||
| 69 | lock_acquire (locks->a); | ||
| 70 | |||
| 71 | msg ("Medium thread should have priority %d. Actual priority: %d.", | ||
| 72 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 73 | msg ("Medium thread got the lock."); | ||
| 74 | |||
| 75 | lock_release (locks->a); | ||
| 76 | thread_yield (); | ||
| 77 | |||
| 78 | lock_release (locks->b); | ||
| 79 | thread_yield (); | ||
| 80 | |||
| 81 | msg ("High thread should have just finished."); | ||
| 82 | msg ("Middle thread finished."); | ||
| 83 | } | ||
| 84 | |||
| 85 | static void | ||
| 86 | high_thread_func (void *lock_) | ||
| 87 | { | ||
| 88 | struct lock *lock = lock_; | ||
| 89 | |||
| 90 | lock_acquire (lock); | ||
| 91 | msg ("High thread got the lock."); | ||
| 92 | lock_release (lock); | ||
| 93 | msg ("High thread finished."); | ||
| 94 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-nest.ck b/pintos-progos/tests/threads/priority-donate-nest.ck new file mode 100644 index 0000000..923460e --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-nest.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-nest) begin | ||
| 7 | (priority-donate-nest) Low thread should have priority 32. Actual priority: 32. | ||
| 8 | (priority-donate-nest) Low thread should have priority 33. Actual priority: 33. | ||
| 9 | (priority-donate-nest) Medium thread should have priority 33. Actual priority: 33. | ||
| 10 | (priority-donate-nest) Medium thread got the lock. | ||
| 11 | (priority-donate-nest) High thread got the lock. | ||
| 12 | (priority-donate-nest) High thread finished. | ||
| 13 | (priority-donate-nest) High thread should have just finished. | ||
| 14 | (priority-donate-nest) Middle thread finished. | ||
| 15 | (priority-donate-nest) Medium thread should just have finished. | ||
| 16 | (priority-donate-nest) Low thread should have priority 31. Actual priority: 31. | ||
| 17 | (priority-donate-nest) end | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-one.c b/pintos-progos/tests/threads/priority-donate-one.c new file mode 100644 index 0000000..3189f3a --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-one.c | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | /* The main thread acquires a lock. Then it creates two | ||
| 2 | higher-priority threads that block acquiring the lock, causing | ||
| 3 | them to donate their priorities to the main thread. When the | ||
| 4 | main thread releases the lock, the other threads should | ||
| 5 | acquire it in priority order. | ||
| 6 | |||
| 7 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 8 | winter 1999 by Matt Franklin <startled@leland.stanford.edu>, | ||
| 9 | Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu | ||
| 10 | <yph@cs.stanford.edu>. Modified by arens. */ | ||
| 11 | |||
| 12 | #include <stdio.h> | ||
| 13 | #include "tests/threads/tests.h" | ||
| 14 | #include "threads/init.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | static thread_func acquire1_thread_func; | ||
| 19 | static thread_func acquire2_thread_func; | ||
| 20 | |||
| 21 | void | ||
| 22 | test_priority_donate_one (void) | ||
| 23 | { | ||
| 24 | struct lock lock; | ||
| 25 | |||
| 26 | /* This test does not work with the MLFQS. */ | ||
| 27 | ASSERT (!thread_mlfqs); | ||
| 28 | |||
| 29 | /* Make sure our priority is the default. */ | ||
| 30 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 31 | |||
| 32 | lock_init (&lock); | ||
| 33 | lock_acquire (&lock); | ||
| 34 | thread_create ("acquire1", PRI_DEFAULT + 1, acquire1_thread_func, &lock); | ||
| 35 | msg ("This thread should have priority %d. Actual priority: %d.", | ||
| 36 | PRI_DEFAULT + 1, thread_get_priority ()); | ||
| 37 | thread_create ("acquire2", PRI_DEFAULT + 2, acquire2_thread_func, &lock); | ||
| 38 | msg ("This thread should have priority %d. Actual priority: %d.", | ||
| 39 | PRI_DEFAULT + 2, thread_get_priority ()); | ||
| 40 | lock_release (&lock); | ||
| 41 | msg ("acquire2, acquire1 must already have finished, in that order."); | ||
| 42 | msg ("This should be the last line before finishing this test."); | ||
| 43 | } | ||
| 44 | |||
| 45 | static void | ||
| 46 | acquire1_thread_func (void *lock_) | ||
| 47 | { | ||
| 48 | struct lock *lock = lock_; | ||
| 49 | |||
| 50 | lock_acquire (lock); | ||
| 51 | msg ("acquire1: got the lock"); | ||
| 52 | lock_release (lock); | ||
| 53 | msg ("acquire1: done"); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void | ||
| 57 | acquire2_thread_func (void *lock_) | ||
| 58 | { | ||
| 59 | struct lock *lock = lock_; | ||
| 60 | |||
| 61 | lock_acquire (lock); | ||
| 62 | msg ("acquire2: got the lock"); | ||
| 63 | lock_release (lock); | ||
| 64 | msg ("acquire2: done"); | ||
| 65 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-one.ck b/pintos-progos/tests/threads/priority-donate-one.ck new file mode 100644 index 0000000..b7c8e6f --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-one.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-one) begin | ||
| 7 | (priority-donate-one) This thread should have priority 32. Actual priority: 32. | ||
| 8 | (priority-donate-one) This thread should have priority 33. Actual priority: 33. | ||
| 9 | (priority-donate-one) acquire2: got the lock | ||
| 10 | (priority-donate-one) acquire2: done | ||
| 11 | (priority-donate-one) acquire1: got the lock | ||
| 12 | (priority-donate-one) acquire1: done | ||
| 13 | (priority-donate-one) acquire2, acquire1 must already have finished, in that order. | ||
| 14 | (priority-donate-one) This should be the last line before finishing this test. | ||
| 15 | (priority-donate-one) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-donate-sema.c b/pintos-progos/tests/threads/priority-donate-sema.c new file mode 100644 index 0000000..b33cb72 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-sema.c | |||
| @@ -0,0 +1,82 @@ | |||
| 1 | /* Low priority thread L acquires a lock, then blocks downing a | ||
| 2 | semaphore. Medium priority thread M then blocks waiting on | ||
| 3 | the same semaphore. Next, high priority thread H attempts to | ||
| 4 | acquire the lock, donating its priority to L. | ||
| 5 | |||
| 6 | Next, the main thread ups the semaphore, waking up L. L | ||
| 7 | releases the lock, which wakes up H. H "up"s the semaphore, | ||
| 8 | waking up M. H terminates, then M, then L, and finally the | ||
| 9 | main thread. | ||
| 10 | |||
| 11 | Written by Godmar Back <gback@cs.vt.edu>. */ | ||
| 12 | |||
| 13 | #include <stdio.h> | ||
| 14 | #include "tests/threads/tests.h" | ||
| 15 | #include "threads/init.h" | ||
| 16 | #include "threads/synch.h" | ||
| 17 | #include "threads/thread.h" | ||
| 18 | |||
| 19 | struct lock_and_sema | ||
| 20 | { | ||
| 21 | struct lock lock; | ||
| 22 | struct semaphore sema; | ||
| 23 | }; | ||
| 24 | |||
| 25 | static thread_func l_thread_func; | ||
| 26 | static thread_func m_thread_func; | ||
| 27 | static thread_func h_thread_func; | ||
| 28 | |||
| 29 | void | ||
| 30 | test_priority_donate_sema (void) | ||
| 31 | { | ||
| 32 | struct lock_and_sema ls; | ||
| 33 | |||
| 34 | /* This test does not work with the MLFQS. */ | ||
| 35 | ASSERT (!thread_mlfqs); | ||
| 36 | |||
| 37 | /* Make sure our priority is the default. */ | ||
| 38 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 39 | |||
| 40 | lock_init (&ls.lock); | ||
| 41 | sema_init (&ls.sema, 0); | ||
| 42 | thread_create ("low", PRI_DEFAULT + 1, l_thread_func, &ls); | ||
| 43 | thread_create ("med", PRI_DEFAULT + 3, m_thread_func, &ls); | ||
| 44 | thread_create ("high", PRI_DEFAULT + 5, h_thread_func, &ls); | ||
| 45 | sema_up (&ls.sema); | ||
| 46 | msg ("Main thread finished."); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void | ||
| 50 | l_thread_func (void *ls_) | ||
| 51 | { | ||
| 52 | struct lock_and_sema *ls = ls_; | ||
| 53 | |||
| 54 | lock_acquire (&ls->lock); | ||
| 55 | msg ("Thread L acquired lock."); | ||
| 56 | sema_down (&ls->sema); | ||
| 57 | msg ("Thread L downed semaphore."); | ||
| 58 | lock_release (&ls->lock); | ||
| 59 | msg ("Thread L finished."); | ||
| 60 | } | ||
| 61 | |||
| 62 | static void | ||
| 63 | m_thread_func (void *ls_) | ||
| 64 | { | ||
| 65 | struct lock_and_sema *ls = ls_; | ||
| 66 | |||
| 67 | sema_down (&ls->sema); | ||
| 68 | msg ("Thread M finished."); | ||
| 69 | } | ||
| 70 | |||
| 71 | static void | ||
| 72 | h_thread_func (void *ls_) | ||
| 73 | { | ||
| 74 | struct lock_and_sema *ls = ls_; | ||
| 75 | |||
| 76 | lock_acquire (&ls->lock); | ||
| 77 | msg ("Thread H acquired lock."); | ||
| 78 | |||
| 79 | sema_up (&ls->sema); | ||
| 80 | lock_release (&ls->lock); | ||
| 81 | msg ("Thread H finished."); | ||
| 82 | } | ||
diff --git a/pintos-progos/tests/threads/priority-donate-sema.ck b/pintos-progos/tests/threads/priority-donate-sema.ck new file mode 100644 index 0000000..92b8d07 --- /dev/null +++ b/pintos-progos/tests/threads/priority-donate-sema.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-donate-sema) begin | ||
| 7 | (priority-donate-sema) Thread L acquired lock. | ||
| 8 | (priority-donate-sema) Thread L downed semaphore. | ||
| 9 | (priority-donate-sema) Thread H acquired lock. | ||
| 10 | (priority-donate-sema) Thread H finished. | ||
| 11 | (priority-donate-sema) Thread M finished. | ||
| 12 | (priority-donate-sema) Thread L finished. | ||
| 13 | (priority-donate-sema) Main thread finished. | ||
| 14 | (priority-donate-sema) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-fifo.c b/pintos-progos/tests/threads/priority-fifo.c new file mode 100644 index 0000000..3af98a3 --- /dev/null +++ b/pintos-progos/tests/threads/priority-fifo.c | |||
| @@ -0,0 +1,99 @@ | |||
| 1 | /* Creates several threads all at the same priority and ensures | ||
| 2 | that they consistently run in the same round-robin order. | ||
| 3 | |||
| 4 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 5 | winter 1999 by by Matt Franklin | ||
| 6 | <startled@leland.stanford.edu>, Greg Hutchins | ||
| 7 | <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>. | ||
| 8 | Modified by arens. */ | ||
| 9 | |||
| 10 | #include <stdio.h> | ||
| 11 | #include "tests/threads/tests.h" | ||
| 12 | #include "threads/init.h" | ||
| 13 | #include "devices/timer.h" | ||
| 14 | #include "threads/malloc.h" | ||
| 15 | #include "threads/synch.h" | ||
| 16 | #include "threads/thread.h" | ||
| 17 | |||
| 18 | struct simple_thread_data | ||
| 19 | { | ||
| 20 | int id; /* Sleeper ID. */ | ||
| 21 | int iterations; /* Iterations so far. */ | ||
| 22 | struct lock *lock; /* Lock on output. */ | ||
| 23 | int **op; /* Output buffer position. */ | ||
| 24 | }; | ||
| 25 | |||
| 26 | #define THREAD_CNT 16 | ||
| 27 | #define ITER_CNT 16 | ||
| 28 | |||
| 29 | static thread_func simple_thread_func; | ||
| 30 | |||
| 31 | void | ||
| 32 | test_priority_fifo (void) | ||
| 33 | { | ||
| 34 | struct simple_thread_data data[THREAD_CNT]; | ||
| 35 | struct lock lock; | ||
| 36 | int *output, *op; | ||
| 37 | int i, cnt; | ||
| 38 | |||
| 39 | /* This test does not work with the MLFQS. */ | ||
| 40 | ASSERT (!thread_mlfqs); | ||
| 41 | |||
| 42 | /* Make sure our priority is the default. */ | ||
| 43 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 44 | |||
| 45 | msg ("%d threads will iterate %d times in the same order each time.", | ||
| 46 | THREAD_CNT, ITER_CNT); | ||
| 47 | msg ("If the order varies then there is a bug."); | ||
| 48 | |||
| 49 | output = op = malloc (sizeof *output * THREAD_CNT * ITER_CNT * 2); | ||
| 50 | ASSERT (output != NULL); | ||
| 51 | lock_init (&lock); | ||
| 52 | |||
| 53 | thread_set_priority (PRI_DEFAULT + 2); | ||
| 54 | for (i = 0; i < THREAD_CNT; i++) | ||
| 55 | { | ||
| 56 | char name[16]; | ||
| 57 | struct simple_thread_data *d = data + i; | ||
| 58 | snprintf (name, sizeof name, "%d", i); | ||
| 59 | d->id = i; | ||
| 60 | d->iterations = 0; | ||
| 61 | d->lock = &lock; | ||
| 62 | d->op = &op; | ||
| 63 | thread_create (name, PRI_DEFAULT + 1, simple_thread_func, d); | ||
| 64 | } | ||
| 65 | |||
| 66 | thread_set_priority (PRI_DEFAULT); | ||
| 67 | /* All the other threads now run to termination here. */ | ||
| 68 | ASSERT (lock.holder == NULL); | ||
| 69 | |||
| 70 | cnt = 0; | ||
| 71 | for (; output < op; output++) | ||
| 72 | { | ||
| 73 | struct simple_thread_data *d; | ||
| 74 | |||
| 75 | ASSERT (*output >= 0 && *output < THREAD_CNT); | ||
| 76 | d = data + *output; | ||
| 77 | if (cnt % THREAD_CNT == 0) | ||
| 78 | printf ("(priority-fifo) iteration:"); | ||
| 79 | printf (" %d", d->id); | ||
| 80 | if (++cnt % THREAD_CNT == 0) | ||
| 81 | printf ("\n"); | ||
| 82 | d->iterations++; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | static void | ||
| 87 | simple_thread_func (void *data_) | ||
| 88 | { | ||
| 89 | struct simple_thread_data *data = data_; | ||
| 90 | int i; | ||
| 91 | |||
| 92 | for (i = 0; i < ITER_CNT; i++) | ||
| 93 | { | ||
| 94 | lock_acquire (data->lock); | ||
| 95 | *(*data->op)++ = data->id; | ||
| 96 | lock_release (data->lock); | ||
| 97 | thread_yield (); | ||
| 98 | } | ||
| 99 | } | ||
diff --git a/pintos-progos/tests/threads/priority-fifo.ck b/pintos-progos/tests/threads/priority-fifo.ck new file mode 100644 index 0000000..11f1dd3 --- /dev/null +++ b/pintos-progos/tests/threads/priority-fifo.ck | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | |||
| 3 | # The expected output looks like this: | ||
| 4 | # | ||
| 5 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 6 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 7 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 8 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 9 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 10 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 11 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 12 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 13 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 14 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 15 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 16 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 17 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 18 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 19 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 20 | # (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | ||
| 21 | # | ||
| 22 | # A different permutation of 0...15 is acceptable, but every line must | ||
| 23 | # be in the same order. | ||
| 24 | |||
| 25 | use strict; | ||
| 26 | use warnings; | ||
| 27 | use tests::tests; | ||
| 28 | |||
| 29 | our ($test); | ||
| 30 | my (@output) = read_text_file ("$test.output"); | ||
| 31 | |||
| 32 | common_checks ("run", @output); | ||
| 33 | |||
| 34 | my ($thread_cnt) = 16; | ||
| 35 | my ($iter_cnt) = 16; | ||
| 36 | my (@order); | ||
| 37 | my (@t) = (-1) x $thread_cnt; | ||
| 38 | |||
| 39 | my (@iterations) = grep (/iteration:/, @output); | ||
| 40 | fail "No iterations found in output.\n" if !@iterations; | ||
| 41 | |||
| 42 | my (@numbering) = $iterations[0] =~ /(\d+)/g; | ||
| 43 | fail "First iteration does not list exactly $thread_cnt threads.\n" | ||
| 44 | if @numbering != $thread_cnt; | ||
| 45 | |||
| 46 | my (@sorted_numbering) = sort { $a <=> $b } @numbering; | ||
| 47 | for my $i (0...$#sorted_numbering) { | ||
| 48 | if ($sorted_numbering[$i] != $i) { | ||
| 49 | fail "First iteration does not list all threads " | ||
| 50 | . "0...$#sorted_numbering\n"; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | for my $i (1...$#iterations) { | ||
| 55 | if ($iterations[$i] ne $iterations[0]) { | ||
| 56 | fail "Iteration $i differs from iteration 0\n"; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | fail "$iter_cnt iterations expected but " . scalar (@iterations) . " found\n" | ||
| 61 | if $iter_cnt != @iterations; | ||
| 62 | |||
| 63 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-preempt.c b/pintos-progos/tests/threads/priority-preempt.c new file mode 100644 index 0000000..3c3aacb --- /dev/null +++ b/pintos-progos/tests/threads/priority-preempt.c | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | /* Ensures that a high-priority thread really preempts. | ||
| 2 | |||
| 3 | Based on a test originally submitted for Stanford's CS 140 in | ||
| 4 | winter 1999 by by Matt Franklin | ||
| 5 | <startled@leland.stanford.edu>, Greg Hutchins | ||
| 6 | <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>. | ||
| 7 | Modified by arens. */ | ||
| 8 | |||
| 9 | #include <stdio.h> | ||
| 10 | #include "tests/threads/tests.h" | ||
| 11 | #include "threads/init.h" | ||
| 12 | #include "threads/synch.h" | ||
| 13 | #include "threads/thread.h" | ||
| 14 | |||
| 15 | static thread_func simple_thread_func; | ||
| 16 | |||
| 17 | void | ||
| 18 | test_priority_preempt (void) | ||
| 19 | { | ||
| 20 | /* This test does not work with the MLFQS. */ | ||
| 21 | ASSERT (!thread_mlfqs); | ||
| 22 | |||
| 23 | /* Make sure our priority is the default. */ | ||
| 24 | ASSERT (thread_get_priority () == PRI_DEFAULT); | ||
| 25 | |||
| 26 | thread_create ("high-priority", PRI_DEFAULT + 1, simple_thread_func, NULL); | ||
| 27 | msg ("The high-priority thread should have already completed."); | ||
| 28 | } | ||
| 29 | |||
| 30 | static void | ||
| 31 | simple_thread_func (void *aux UNUSED) | ||
| 32 | { | ||
| 33 | int i; | ||
| 34 | |||
| 35 | for (i = 0; i < 5; i++) | ||
| 36 | { | ||
| 37 | msg ("Thread %s iteration %d", thread_name (), i); | ||
| 38 | thread_yield (); | ||
| 39 | } | ||
| 40 | msg ("Thread %s done!", thread_name ()); | ||
| 41 | } | ||
diff --git a/pintos-progos/tests/threads/priority-preempt.ck b/pintos-progos/tests/threads/priority-preempt.ck new file mode 100644 index 0000000..43a26ee --- /dev/null +++ b/pintos-progos/tests/threads/priority-preempt.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-preempt) begin | ||
| 7 | (priority-preempt) Thread high-priority iteration 0 | ||
| 8 | (priority-preempt) Thread high-priority iteration 1 | ||
| 9 | (priority-preempt) Thread high-priority iteration 2 | ||
| 10 | (priority-preempt) Thread high-priority iteration 3 | ||
| 11 | (priority-preempt) Thread high-priority iteration 4 | ||
| 12 | (priority-preempt) Thread high-priority done! | ||
| 13 | (priority-preempt) The high-priority thread should have already completed. | ||
| 14 | (priority-preempt) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/threads/priority-sema.c b/pintos-progos/tests/threads/priority-sema.c new file mode 100644 index 0000000..2834a88 --- /dev/null +++ b/pintos-progos/tests/threads/priority-sema.c | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | /* Tests that the highest-priority thread waiting on a semaphore | ||
| 2 | is the first to wake up. */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include "tests/threads/tests.h" | ||
| 6 | #include "threads/init.h" | ||
| 7 | #include "threads/malloc.h" | ||
| 8 | #include "threads/synch.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "devices/timer.h" | ||
| 11 | |||
| 12 | static thread_func priority_sema_thread; | ||
| 13 | static struct semaphore sema; | ||
| 14 | |||
| 15 | void | ||
| 16 | test_priority_sema (void) | ||
| 17 | { | ||
| 18 | int i; | ||
| 19 | |||
| 20 | /* This test does not work with the MLFQS. */ | ||
| 21 | ASSERT (!thread_mlfqs); | ||
| 22 | |||
| 23 | sema_init (&sema, 0); | ||
| 24 | thread_set_priority (PRI_MIN); | ||
| 25 | for (i = 0; i < 10; i++) | ||
| 26 | { | ||
| 27 | int priority = PRI_DEFAULT - (i + 3) % 10 - 1; | ||
| 28 | char name[16]; | ||
| 29 | snprintf (name, sizeof name, "priority %d", priority); | ||
| 30 | thread_create (name, priority, priority_sema_thread, NULL); | ||
| 31 | } | ||
| 32 | |||
| 33 | for (i = 0; i < 10; i++) | ||
| 34 | { | ||
| 35 | sema_up (&sema); | ||
| 36 | msg ("Back in main thread."); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | static void | ||
| 41 | priority_sema_thread (void *aux UNUSED) | ||
| 42 | { | ||
| 43 | sema_down (&sema); | ||
| 44 | msg ("Thread %s woke up.", thread_name ()); | ||
| 45 | } | ||
diff --git a/pintos-progos/tests/threads/priority-sema.ck b/pintos-progos/tests/threads/priority-sema.ck new file mode 100644 index 0000000..559988d --- /dev/null +++ b/pintos-progos/tests/threads/priority-sema.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (priority-sema) begin | ||
| 7 | (priority-sema) Thread priority 30 woke up. | ||
| 8 | (priority-sema) Back in main thread. | ||
| 9 | (priority-sema) Thread priority 29 woke up. | ||
| 10 | (priority-sema) Back in main thread. | ||
| 11 | (priority-sema) Thread priority 28 woke up. | ||
| 12 | (priority-sema) Back in main thread. | ||
| 13 | (priority-sema) Thread priority 27 woke up. | ||
| 14 | (priority-sema) Back in main thread. | ||
| 15 | (priority-sema) Thread priority 26 woke up. | ||
| 16 | (priority-sema) Back in main thread. | ||
| 17 | (priority-sema) Thread priority 25 woke up. | ||
| 18 | (priority-sema) Back in main thread. | ||
| 19 | (priority-sema) Thread priority 24 woke up. | ||
| 20 | (priority-sema) Back in main thread. | ||
| 21 | (priority-sema) Thread priority 23 woke up. | ||
| 22 | (priority-sema) Back in main thread. | ||
| 23 | (priority-sema) Thread priority 22 woke up. | ||
| 24 | (priority-sema) Back in main thread. | ||
| 25 | (priority-sema) Thread priority 21 woke up. | ||
| 26 | (priority-sema) Back in main thread. | ||
| 27 | (priority-sema) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/threads/tests.c b/pintos-progos/tests/threads/tests.c new file mode 100644 index 0000000..af15aee --- /dev/null +++ b/pintos-progos/tests/threads/tests.c | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | #include "tests/threads/tests.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <string.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | |||
| 6 | struct test | ||
| 7 | { | ||
| 8 | const char *name; | ||
| 9 | test_func *function; | ||
| 10 | }; | ||
| 11 | |||
| 12 | static const struct test tests[] = | ||
| 13 | { | ||
| 14 | {"alarm-single", test_alarm_single}, | ||
| 15 | {"alarm-multiple", test_alarm_multiple}, | ||
| 16 | {"alarm-simultaneous", test_alarm_simultaneous}, | ||
| 17 | {"alarm-priority", test_alarm_priority}, | ||
| 18 | {"alarm-zero", test_alarm_zero}, | ||
| 19 | {"alarm-negative", test_alarm_negative}, | ||
| 20 | {"priority-change", test_priority_change}, | ||
| 21 | {"priority-donate-one", test_priority_donate_one}, | ||
| 22 | {"priority-donate-multiple", test_priority_donate_multiple}, | ||
| 23 | {"priority-donate-multiple2", test_priority_donate_multiple2}, | ||
| 24 | {"priority-donate-nest", test_priority_donate_nest}, | ||
| 25 | {"priority-donate-sema", test_priority_donate_sema}, | ||
| 26 | {"priority-donate-lower", test_priority_donate_lower}, | ||
| 27 | {"priority-donate-chain", test_priority_donate_chain}, | ||
| 28 | {"priority-fifo", test_priority_fifo}, | ||
| 29 | {"priority-preempt", test_priority_preempt}, | ||
| 30 | {"priority-sema", test_priority_sema}, | ||
| 31 | {"priority-condvar", test_priority_condvar}, | ||
| 32 | {"mlfqs-load-1", test_mlfqs_load_1}, | ||
| 33 | {"mlfqs-load-60", test_mlfqs_load_60}, | ||
| 34 | {"mlfqs-load-avg", test_mlfqs_load_avg}, | ||
| 35 | {"mlfqs-recent-1", test_mlfqs_recent_1}, | ||
| 36 | {"mlfqs-fair-2", test_mlfqs_fair_2}, | ||
| 37 | {"mlfqs-fair-20", test_mlfqs_fair_20}, | ||
| 38 | {"mlfqs-nice-2", test_mlfqs_nice_2}, | ||
| 39 | {"mlfqs-nice-10", test_mlfqs_nice_10}, | ||
| 40 | {"mlfqs-block", test_mlfqs_block}, | ||
| 41 | }; | ||
| 42 | |||
| 43 | static const char *test_name; | ||
| 44 | |||
| 45 | /* Runs the test named NAME. */ | ||
| 46 | void | ||
| 47 | run_test (const char *name) | ||
| 48 | { | ||
| 49 | const struct test *t; | ||
| 50 | |||
| 51 | for (t = tests; t < tests + sizeof tests / sizeof *tests; t++) | ||
| 52 | if (!strcmp (name, t->name)) | ||
| 53 | { | ||
| 54 | test_name = name; | ||
| 55 | msg ("begin"); | ||
| 56 | t->function (); | ||
| 57 | msg ("end"); | ||
| 58 | return; | ||
| 59 | } | ||
| 60 | PANIC ("no test named \"%s\"", name); | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Prints FORMAT as if with printf(), | ||
| 64 | prefixing the output by the name of the test | ||
| 65 | and following it with a new-line character. */ | ||
| 66 | void | ||
| 67 | msg (const char *format, ...) | ||
| 68 | { | ||
| 69 | va_list args; | ||
| 70 | |||
| 71 | printf ("(%s) ", test_name); | ||
| 72 | va_start (args, format); | ||
| 73 | vprintf (format, args); | ||
| 74 | va_end (args); | ||
| 75 | putchar ('\n'); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Prints failure message FORMAT as if with printf(), | ||
| 79 | prefixing the output by the name of the test and FAIL: | ||
| 80 | and following it with a new-line character, | ||
| 81 | and then panics the kernel. */ | ||
| 82 | void | ||
| 83 | fail (const char *format, ...) | ||
| 84 | { | ||
| 85 | va_list args; | ||
| 86 | |||
| 87 | printf ("(%s) FAIL: ", test_name); | ||
| 88 | va_start (args, format); | ||
| 89 | vprintf (format, args); | ||
| 90 | va_end (args); | ||
| 91 | putchar ('\n'); | ||
| 92 | |||
| 93 | PANIC ("test failed"); | ||
| 94 | } | ||
| 95 | |||
| 96 | /* Prints a message indicating the current test passed. */ | ||
| 97 | void | ||
| 98 | pass (void) | ||
| 99 | { | ||
| 100 | printf ("(%s) PASS\n", test_name); | ||
| 101 | } | ||
| 102 | |||
diff --git a/pintos-progos/tests/threads/tests.h b/pintos-progos/tests/threads/tests.h new file mode 100644 index 0000000..cd9d489 --- /dev/null +++ b/pintos-progos/tests/threads/tests.h | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #ifndef TESTS_THREADS_TESTS_H | ||
| 2 | #define TESTS_THREADS_TESTS_H | ||
| 3 | |||
| 4 | void run_test (const char *); | ||
| 5 | |||
| 6 | typedef void test_func (void); | ||
| 7 | |||
| 8 | extern test_func test_alarm_single; | ||
| 9 | extern test_func test_alarm_multiple; | ||
| 10 | extern test_func test_alarm_simultaneous; | ||
| 11 | extern test_func test_alarm_priority; | ||
| 12 | extern test_func test_alarm_zero; | ||
| 13 | extern test_func test_alarm_negative; | ||
| 14 | extern test_func test_priority_change; | ||
| 15 | extern test_func test_priority_donate_one; | ||
| 16 | extern test_func test_priority_donate_multiple; | ||
| 17 | extern test_func test_priority_donate_multiple2; | ||
| 18 | extern test_func test_priority_donate_sema; | ||
| 19 | extern test_func test_priority_donate_nest; | ||
| 20 | extern test_func test_priority_donate_lower; | ||
| 21 | extern test_func test_priority_donate_chain; | ||
| 22 | extern test_func test_priority_fifo; | ||
| 23 | extern test_func test_priority_preempt; | ||
| 24 | extern test_func test_priority_sema; | ||
| 25 | extern test_func test_priority_condvar; | ||
| 26 | extern test_func test_mlfqs_load_1; | ||
| 27 | extern test_func test_mlfqs_load_60; | ||
| 28 | extern test_func test_mlfqs_load_avg; | ||
| 29 | extern test_func test_mlfqs_recent_1; | ||
| 30 | extern test_func test_mlfqs_fair_2; | ||
| 31 | extern test_func test_mlfqs_fair_20; | ||
| 32 | extern test_func test_mlfqs_nice_2; | ||
| 33 | extern test_func test_mlfqs_nice_10; | ||
| 34 | extern test_func test_mlfqs_block; | ||
| 35 | |||
| 36 | void msg (const char *, ...); | ||
| 37 | void fail (const char *, ...); | ||
| 38 | void pass (void); | ||
| 39 | |||
| 40 | #endif /* tests/threads/tests.h */ | ||
| 41 | |||
diff --git a/pintos-progos/tests/userprog/Make.tests b/pintos-progos/tests/userprog/Make.tests new file mode 100644 index 0000000..88b0737 --- /dev/null +++ b/pintos-progos/tests/userprog/Make.tests | |||
| @@ -0,0 +1,133 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/%.output: FILESYSSOURCE = --filesys-size=2 | ||
| 4 | tests/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^) | ||
| 5 | |||
| 6 | tests/userprog_TESTS = $(addprefix tests/userprog/,args-none \ | ||
| 7 | args-single args-multiple args-many args-dbl-space sc-bad-sp \ | ||
| 8 | sc-bad-arg sc-boundary sc-boundary-2 halt exit create-normal \ | ||
| 9 | create-empty create-null create-bad-ptr create-long create-exists \ | ||
| 10 | create-bound open-normal open-missing open-boundary open-empty \ | ||
| 11 | open-null open-bad-ptr open-twice close-normal close-twice close-stdin \ | ||
| 12 | close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \ | ||
| 13 | read-zero read-stdout read-bad-fd write-normal write-bad-ptr \ | ||
| 14 | write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \ | ||
| 15 | exec-multiple exec-missing exec-bad-ptr wait-simple wait-twice \ | ||
| 16 | wait-killed wait-bad-pid multi-recurse multi-child-fd rox-simple \ | ||
| 17 | rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \ | ||
| 18 | bad-jump bad-jump2) | ||
| 19 | |||
| 20 | tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \ | ||
| 21 | tests/userprog/,child-simple child-args child-bad child-close child-rox) | ||
| 22 | |||
| 23 | tests/userprog/args-none_SRC = tests/userprog/args.c | ||
| 24 | tests/userprog/args-single_SRC = tests/userprog/args.c | ||
| 25 | tests/userprog/args-multiple_SRC = tests/userprog/args.c | ||
| 26 | tests/userprog/args-many_SRC = tests/userprog/args.c | ||
| 27 | tests/userprog/args-dbl-space_SRC = tests/userprog/args.c | ||
| 28 | tests/userprog/sc-bad-sp_SRC = tests/userprog/sc-bad-sp.c tests/main.c | ||
| 29 | tests/userprog/sc-bad-arg_SRC = tests/userprog/sc-bad-arg.c tests/main.c | ||
| 30 | tests/userprog/bad-read_SRC = tests/userprog/bad-read.c tests/main.c | ||
| 31 | tests/userprog/bad-write_SRC = tests/userprog/bad-write.c tests/main.c | ||
| 32 | tests/userprog/bad-jump_SRC = tests/userprog/bad-jump.c tests/main.c | ||
| 33 | tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c | ||
| 34 | tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c | ||
| 35 | tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c | ||
| 36 | tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \ | ||
| 37 | tests/userprog/boundary.c tests/main.c | ||
| 38 | tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \ | ||
| 39 | tests/userprog/boundary.c tests/main.c | ||
| 40 | tests/userprog/halt_SRC = tests/userprog/halt.c tests/main.c | ||
| 41 | tests/userprog/exit_SRC = tests/userprog/exit.c tests/main.c | ||
| 42 | tests/userprog/create-normal_SRC = tests/userprog/create-normal.c tests/main.c | ||
| 43 | tests/userprog/create-empty_SRC = tests/userprog/create-empty.c tests/main.c | ||
| 44 | tests/userprog/create-null_SRC = tests/userprog/create-null.c tests/main.c | ||
| 45 | tests/userprog/create-bad-ptr_SRC = tests/userprog/create-bad-ptr.c \ | ||
| 46 | tests/main.c | ||
| 47 | tests/userprog/create-long_SRC = tests/userprog/create-long.c tests/main.c | ||
| 48 | tests/userprog/create-exists_SRC = tests/userprog/create-exists.c tests/main.c | ||
| 49 | tests/userprog/create-bound_SRC = tests/userprog/create-bound.c \ | ||
| 50 | tests/userprog/boundary.c tests/main.c | ||
| 51 | tests/userprog/open-normal_SRC = tests/userprog/open-normal.c tests/main.c | ||
| 52 | tests/userprog/open-missing_SRC = tests/userprog/open-missing.c tests/main.c | ||
| 53 | tests/userprog/open-boundary_SRC = tests/userprog/open-boundary.c \ | ||
| 54 | tests/userprog/boundary.c tests/main.c | ||
| 55 | tests/userprog/open-empty_SRC = tests/userprog/open-empty.c tests/main.c | ||
| 56 | tests/userprog/open-null_SRC = tests/userprog/open-null.c tests/main.c | ||
| 57 | tests/userprog/open-bad-ptr_SRC = tests/userprog/open-bad-ptr.c tests/main.c | ||
| 58 | tests/userprog/open-twice_SRC = tests/userprog/open-twice.c tests/main.c | ||
| 59 | tests/userprog/close-normal_SRC = tests/userprog/close-normal.c tests/main.c | ||
| 60 | tests/userprog/close-twice_SRC = tests/userprog/close-twice.c tests/main.c | ||
| 61 | tests/userprog/close-stdin_SRC = tests/userprog/close-stdin.c tests/main.c | ||
| 62 | tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c | ||
| 63 | tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c | ||
| 64 | tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c | ||
| 65 | tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c | ||
| 66 | tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \ | ||
| 67 | tests/userprog/boundary.c tests/main.c | ||
| 68 | tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c | ||
| 69 | tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c | ||
| 70 | tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c | ||
| 71 | tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c | ||
| 72 | tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c | ||
| 73 | tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \ | ||
| 74 | tests/userprog/boundary.c tests/main.c | ||
| 75 | tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c | ||
| 76 | tests/userprog/write-stdin_SRC = tests/userprog/write-stdin.c tests/main.c | ||
| 77 | tests/userprog/write-bad-fd_SRC = tests/userprog/write-bad-fd.c tests/main.c | ||
| 78 | tests/userprog/exec-once_SRC = tests/userprog/exec-once.c tests/main.c | ||
| 79 | tests/userprog/exec-arg_SRC = tests/userprog/exec-arg.c tests/main.c | ||
| 80 | tests/userprog/exec-multiple_SRC = tests/userprog/exec-multiple.c tests/main.c | ||
| 81 | tests/userprog/exec-missing_SRC = tests/userprog/exec-missing.c tests/main.c | ||
| 82 | tests/userprog/exec-bad-ptr_SRC = tests/userprog/exec-bad-ptr.c tests/main.c | ||
| 83 | tests/userprog/wait-simple_SRC = tests/userprog/wait-simple.c tests/main.c | ||
| 84 | tests/userprog/wait-twice_SRC = tests/userprog/wait-twice.c tests/main.c | ||
| 85 | tests/userprog/wait-killed_SRC = tests/userprog/wait-killed.c tests/main.c | ||
| 86 | tests/userprog/wait-bad-pid_SRC = tests/userprog/wait-bad-pid.c tests/main.c | ||
| 87 | tests/userprog/multi-recurse_SRC = tests/userprog/multi-recurse.c | ||
| 88 | tests/userprog/multi-child-fd_SRC = tests/userprog/multi-child-fd.c \ | ||
| 89 | tests/main.c | ||
| 90 | tests/userprog/rox-simple_SRC = tests/userprog/rox-simple.c tests/main.c | ||
| 91 | tests/userprog/rox-child_SRC = tests/userprog/rox-child.c tests/main.c | ||
| 92 | tests/userprog/rox-multichild_SRC = tests/userprog/rox-multichild.c \ | ||
| 93 | tests/main.c | ||
| 94 | |||
| 95 | tests/userprog/child-simple_SRC = tests/userprog/child-simple.c | ||
| 96 | tests/userprog/child-args_SRC = tests/userprog/args.c | ||
| 97 | tests/userprog/child-bad_SRC = tests/userprog/child-bad.c tests/main.c | ||
| 98 | tests/userprog/child-close_SRC = tests/userprog/child-close.c | ||
| 99 | tests/userprog/child-rox_SRC = tests/userprog/child-rox.c | ||
| 100 | |||
| 101 | $(foreach prog,$(tests/userprog_PROGS),$(eval $(prog)_SRC += tests/lib.c)) | ||
| 102 | |||
| 103 | tests/userprog/args-single_ARGS = onearg | ||
| 104 | tests/userprog/args-multiple_ARGS = some arguments for you! | ||
| 105 | 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 | ||
| 106 | tests/userprog/args-dbl-space_ARGS = two spaces! | ||
| 107 | tests/userprog/multi-recurse_ARGS = 15 | ||
| 108 | |||
| 109 | tests/userprog/open-normal_PUTFILES += tests/userprog/sample.txt | ||
| 110 | tests/userprog/open-boundary_PUTFILES += tests/userprog/sample.txt | ||
| 111 | tests/userprog/open-twice_PUTFILES += tests/userprog/sample.txt | ||
| 112 | tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt | ||
| 113 | tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt | ||
| 114 | tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt | ||
| 115 | tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt | ||
| 116 | tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt | ||
| 117 | tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt | ||
| 118 | tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt | ||
| 119 | tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt | ||
| 120 | tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt | ||
| 121 | tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt | ||
| 122 | tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt | ||
| 123 | |||
| 124 | tests/userprog/exec-once_PUTFILES += tests/userprog/child-simple | ||
| 125 | tests/userprog/exec-multiple_PUTFILES += tests/userprog/child-simple | ||
| 126 | tests/userprog/wait-simple_PUTFILES += tests/userprog/child-simple | ||
| 127 | tests/userprog/wait-twice_PUTFILES += tests/userprog/child-simple | ||
| 128 | |||
| 129 | tests/userprog/exec-arg_PUTFILES += tests/userprog/child-args | ||
| 130 | tests/userprog/multi-child-fd_PUTFILES += tests/userprog/child-close | ||
| 131 | tests/userprog/wait-killed_PUTFILES += tests/userprog/child-bad | ||
| 132 | tests/userprog/rox-child_PUTFILES += tests/userprog/child-rox | ||
| 133 | tests/userprog/rox-multichild_PUTFILES += tests/userprog/child-rox | ||
diff --git a/pintos-progos/tests/userprog/Rubric.functionality b/pintos-progos/tests/userprog/Rubric.functionality new file mode 100644 index 0000000..ea76c44 --- /dev/null +++ b/pintos-progos/tests/userprog/Rubric.functionality | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | Functionality of system calls: | ||
| 2 | - Test argument passing on Pintos command line. | ||
| 3 | 3 args-none | ||
| 4 | 3 args-single | ||
| 5 | 3 args-multiple | ||
| 6 | 3 args-many | ||
| 7 | 3 args-dbl-space | ||
| 8 | |||
| 9 | - Test "create" system call. | ||
| 10 | 3 create-empty | ||
| 11 | 3 create-long | ||
| 12 | 3 create-normal | ||
| 13 | 3 create-exists | ||
| 14 | |||
| 15 | - Test "open" system call. | ||
| 16 | 3 open-missing | ||
| 17 | 3 open-normal | ||
| 18 | 3 open-twice | ||
| 19 | |||
| 20 | - Test "read" system call. | ||
| 21 | 3 read-normal | ||
| 22 | 3 read-zero | ||
| 23 | |||
| 24 | - Test "write" system call. | ||
| 25 | 3 write-normal | ||
| 26 | 3 write-zero | ||
| 27 | |||
| 28 | - Test "close" system call. | ||
| 29 | 3 close-normal | ||
| 30 | |||
| 31 | - Test "exec" system call. | ||
| 32 | 5 exec-once | ||
| 33 | 5 exec-multiple | ||
| 34 | 5 exec-arg | ||
| 35 | |||
| 36 | - Test "wait" system call. | ||
| 37 | 5 wait-simple | ||
| 38 | 5 wait-twice | ||
| 39 | |||
| 40 | - Test "exit" system call. | ||
| 41 | 5 exit | ||
| 42 | |||
| 43 | - Test "halt" system call. | ||
| 44 | 3 halt | ||
| 45 | |||
| 46 | - Test recursive execution of user programs. | ||
| 47 | 15 multi-recurse | ||
| 48 | |||
| 49 | - Test read-only executable feature. | ||
| 50 | 3 rox-simple | ||
| 51 | 3 rox-child | ||
| 52 | 3 rox-multichild | ||
diff --git a/pintos-progos/tests/userprog/Rubric.robustness b/pintos-progos/tests/userprog/Rubric.robustness new file mode 100644 index 0000000..b7d1035 --- /dev/null +++ b/pintos-progos/tests/userprog/Rubric.robustness | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | Robustness of system calls: | ||
| 2 | - Test robustness of file descriptor handling. | ||
| 3 | 2 close-stdin | ||
| 4 | 2 close-stdout | ||
| 5 | 2 close-bad-fd | ||
| 6 | 2 close-twice | ||
| 7 | 2 read-bad-fd | ||
| 8 | 2 read-stdout | ||
| 9 | 2 write-bad-fd | ||
| 10 | 2 write-stdin | ||
| 11 | 2 multi-child-fd | ||
| 12 | |||
| 13 | - Test robustness of pointer handling. | ||
| 14 | 3 create-bad-ptr | ||
| 15 | 3 exec-bad-ptr | ||
| 16 | 3 open-bad-ptr | ||
| 17 | 3 read-bad-ptr | ||
| 18 | 3 write-bad-ptr | ||
| 19 | |||
| 20 | - Test robustness of buffer copying across page boundaries. | ||
| 21 | 3 create-bound | ||
| 22 | 3 open-boundary | ||
| 23 | 3 read-boundary | ||
| 24 | 3 write-boundary | ||
| 25 | |||
| 26 | - Test handling of null pointer and empty strings. | ||
| 27 | 2 create-null | ||
| 28 | 2 open-null | ||
| 29 | 2 open-empty | ||
| 30 | |||
| 31 | - Test robustness of system call implementation. | ||
| 32 | 3 sc-bad-arg | ||
| 33 | 3 sc-bad-sp | ||
| 34 | 5 sc-boundary | ||
| 35 | 5 sc-boundary-2 | ||
| 36 | |||
| 37 | - Test robustness of "exec" and "wait" system calls. | ||
| 38 | 5 exec-missing | ||
| 39 | 5 wait-bad-pid | ||
| 40 | 5 wait-killed | ||
| 41 | |||
| 42 | - Test robustness of exception handling. | ||
| 43 | 1 bad-read | ||
| 44 | 1 bad-write | ||
| 45 | 1 bad-jump | ||
| 46 | 1 bad-read2 | ||
| 47 | 1 bad-write2 | ||
| 48 | 1 bad-jump2 | ||
diff --git a/pintos-progos/tests/userprog/args-dbl-space.ck b/pintos-progos/tests/userprog/args-dbl-space.ck new file mode 100644 index 0000000..dfbcf4b --- /dev/null +++ b/pintos-progos/tests/userprog/args-dbl-space.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 3 | ||
| 8 | (args) argv[0] = 'args-dbl-space' | ||
| 9 | (args) argv[1] = 'two' | ||
| 10 | (args) argv[2] = 'spaces!' | ||
| 11 | (args) argv[3] = null | ||
| 12 | (args) end | ||
| 13 | args-dbl-space: exit(0) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-many.ck b/pintos-progos/tests/userprog/args-many.ck new file mode 100644 index 0000000..214574a --- /dev/null +++ b/pintos-progos/tests/userprog/args-many.ck | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 23 | ||
| 8 | (args) argv[0] = 'args-many' | ||
| 9 | (args) argv[1] = 'a' | ||
| 10 | (args) argv[2] = 'b' | ||
| 11 | (args) argv[3] = 'c' | ||
| 12 | (args) argv[4] = 'd' | ||
| 13 | (args) argv[5] = 'e' | ||
| 14 | (args) argv[6] = 'f' | ||
| 15 | (args) argv[7] = 'g' | ||
| 16 | (args) argv[8] = 'h' | ||
| 17 | (args) argv[9] = 'i' | ||
| 18 | (args) argv[10] = 'j' | ||
| 19 | (args) argv[11] = 'k' | ||
| 20 | (args) argv[12] = 'l' | ||
| 21 | (args) argv[13] = 'm' | ||
| 22 | (args) argv[14] = 'n' | ||
| 23 | (args) argv[15] = 'o' | ||
| 24 | (args) argv[16] = 'p' | ||
| 25 | (args) argv[17] = 'q' | ||
| 26 | (args) argv[18] = 'r' | ||
| 27 | (args) argv[19] = 's' | ||
| 28 | (args) argv[20] = 't' | ||
| 29 | (args) argv[21] = 'u' | ||
| 30 | (args) argv[22] = 'v' | ||
| 31 | (args) argv[23] = null | ||
| 32 | (args) end | ||
| 33 | args-many: exit(0) | ||
| 34 | EOF | ||
| 35 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-multiple.ck b/pintos-progos/tests/userprog/args-multiple.ck new file mode 100644 index 0000000..227e6cc --- /dev/null +++ b/pintos-progos/tests/userprog/args-multiple.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 5 | ||
| 8 | (args) argv[0] = 'args-multiple' | ||
| 9 | (args) argv[1] = 'some' | ||
| 10 | (args) argv[2] = 'arguments' | ||
| 11 | (args) argv[3] = 'for' | ||
| 12 | (args) argv[4] = 'you!' | ||
| 13 | (args) argv[5] = null | ||
| 14 | (args) end | ||
| 15 | args-multiple: exit(0) | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-none.ck b/pintos-progos/tests/userprog/args-none.ck new file mode 100644 index 0000000..146318e --- /dev/null +++ b/pintos-progos/tests/userprog/args-none.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 1 | ||
| 8 | (args) argv[0] = 'args-none' | ||
| 9 | (args) argv[1] = null | ||
| 10 | (args) end | ||
| 11 | args-none: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args-single.ck b/pintos-progos/tests/userprog/args-single.ck new file mode 100644 index 0000000..24582b4 --- /dev/null +++ b/pintos-progos/tests/userprog/args-single.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (args) begin | ||
| 7 | (args) argc = 2 | ||
| 8 | (args) argv[0] = 'args-single' | ||
| 9 | (args) argv[1] = 'onearg' | ||
| 10 | (args) argv[2] = null | ||
| 11 | (args) end | ||
| 12 | args-single: exit(0) | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/userprog/args.c b/pintos-progos/tests/userprog/args.c new file mode 100644 index 0000000..20eda44 --- /dev/null +++ b/pintos-progos/tests/userprog/args.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Prints the command-line arguments. | ||
| 2 | This program is used for all of the args-* tests. Grading is | ||
| 3 | done differently for each of the args-* tests based on the | ||
| 4 | output. */ | ||
| 5 | |||
| 6 | #include "tests/lib.h" | ||
| 7 | |||
| 8 | int | ||
| 9 | main (int argc, char *argv[]) | ||
| 10 | { | ||
| 11 | int i; | ||
| 12 | |||
| 13 | test_name = "args"; | ||
| 14 | |||
| 15 | msg ("begin"); | ||
| 16 | msg ("argc = %d", argc); | ||
| 17 | for (i = 0; i <= argc; i++) | ||
| 18 | if (argv[i] != NULL) | ||
| 19 | msg ("argv[%d] = '%s'", i, argv[i]); | ||
| 20 | else | ||
| 21 | msg ("argv[%d] = null", i); | ||
| 22 | msg ("end"); | ||
| 23 | |||
| 24 | return 0; | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-jump.c b/pintos-progos/tests/userprog/bad-jump.c new file mode 100644 index 0000000..51b7c9f --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to execute code at address 0, which is not mapped. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully called NULL: %d", | ||
| 11 | ((int (*)(void))NULL)()); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-jump.ck b/pintos-progos/tests/userprog/bad-jump.ck new file mode 100644 index 0000000..e1c178b --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-jump) begin | ||
| 7 | bad-jump: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-jump2.c b/pintos-progos/tests/userprog/bad-jump2.c new file mode 100644 index 0000000..dc7c2a7 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump2.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to execute code at a kernel virtual address. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully called kernel code: %d", | ||
| 11 | ((int (*)(void))0xC0000000)()); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-jump2.ck b/pintos-progos/tests/userprog/bad-jump2.ck new file mode 100644 index 0000000..35f0f97 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-jump2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-jump2) begin | ||
| 7 | bad-jump2: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-read.c b/pintos-progos/tests/userprog/bad-read.c new file mode 100644 index 0000000..904c278 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to read memory at an address that is not mapped. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully dereferenced NULL: %d", | ||
| 11 | *(int *)NULL); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-read.ck b/pintos-progos/tests/userprog/bad-read.ck new file mode 100644 index 0000000..4d4d926 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-read) begin | ||
| 7 | bad-read: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-read2.c b/pintos-progos/tests/userprog/bad-read2.c new file mode 100644 index 0000000..a2fc237 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read2.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* This program attempts to read kernel memory. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("Congratulations - you have successfully read kernel memory: %d", | ||
| 11 | *(int *)0xC0000000); | ||
| 12 | fail ("should have exited with -1"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-read2.ck b/pintos-progos/tests/userprog/bad-read2.ck new file mode 100644 index 0000000..fa27c7d --- /dev/null +++ b/pintos-progos/tests/userprog/bad-read2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-read2) begin | ||
| 7 | bad-read2: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-write.c b/pintos-progos/tests/userprog/bad-write.c new file mode 100644 index 0000000..000c26b --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* This program attempts to write to memory at an address that is not mapped. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | *(int *)NULL = 42; | ||
| 11 | fail ("should have exited with -1"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-write.ck b/pintos-progos/tests/userprog/bad-write.ck new file mode 100644 index 0000000..d213b49 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-write) begin | ||
| 7 | bad-write: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/bad-write2.c b/pintos-progos/tests/userprog/bad-write2.c new file mode 100644 index 0000000..753da1e --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write2.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* This program attempts to write to kernel memory. | ||
| 2 | This should terminate the process with a -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | *(int *)0xC0000000 = 42; | ||
| 11 | fail ("should have exited with -1"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/bad-write2.ck b/pintos-progos/tests/userprog/bad-write2.ck new file mode 100644 index 0000000..c6a3420 --- /dev/null +++ b/pintos-progos/tests/userprog/bad-write2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (bad-write2) begin | ||
| 7 | bad-write2: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/boundary.c b/pintos-progos/tests/userprog/boundary.c new file mode 100644 index 0000000..59907ec --- /dev/null +++ b/pintos-progos/tests/userprog/boundary.c | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* Utility function for tests that try to break system calls by | ||
| 2 | passing them data that crosses from one virtual page to | ||
| 3 | another. */ | ||
| 4 | |||
| 5 | #include <inttypes.h> | ||
| 6 | #include <round.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include "tests/userprog/boundary.h" | ||
| 9 | |||
| 10 | static char dst[8192]; | ||
| 11 | |||
| 12 | /* Returns the beginning of a page. There are at least 2048 | ||
| 13 | modifiable bytes on either side of the pointer returned. */ | ||
| 14 | void * | ||
| 15 | get_boundary_area (void) | ||
| 16 | { | ||
| 17 | char *p = (char *) ROUND_UP ((uintptr_t) dst, 4096); | ||
| 18 | if (p - dst < 2048) | ||
| 19 | p += 4096; | ||
| 20 | return p; | ||
| 21 | } | ||
| 22 | |||
| 23 | /* Returns a copy of SRC split across the boundary between two | ||
| 24 | pages. */ | ||
| 25 | char * | ||
| 26 | copy_string_across_boundary (const char *src) | ||
| 27 | { | ||
| 28 | char *p = get_boundary_area (); | ||
| 29 | p -= strlen (src) < 4096 ? strlen (src) / 2 : 4096; | ||
| 30 | strlcpy (p, src, 4096); | ||
| 31 | return p; | ||
| 32 | } | ||
| 33 | |||
diff --git a/pintos-progos/tests/userprog/boundary.h b/pintos-progos/tests/userprog/boundary.h new file mode 100644 index 0000000..c8e4b3b --- /dev/null +++ b/pintos-progos/tests/userprog/boundary.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #ifndef TESTS_USERPROG_BOUNDARY_H | ||
| 2 | #define TESTS_USERPROG_BOUNDARY_H | ||
| 3 | |||
| 4 | void *get_boundary_area (void); | ||
| 5 | char *copy_string_across_boundary (const char *); | ||
| 6 | |||
| 7 | #endif /* tests/userprog/boundary.h */ | ||
diff --git a/pintos-progos/tests/userprog/child-bad.c b/pintos-progos/tests/userprog/child-bad.c new file mode 100644 index 0000000..77d7a69 --- /dev/null +++ b/pintos-progos/tests/userprog/child-bad.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Child process run by wait-killed test. | ||
| 2 | Sets the stack pointer (%esp) to an invalid value and invokes | ||
| 3 | a system call, which should then terminate the process with a | ||
| 4 | -1 exit code. */ | ||
| 5 | |||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | asm volatile ("movl $0x20101234, %esp; int $0x30"); | ||
| 13 | fail ("should have exited with -1"); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/child-close.c b/pintos-progos/tests/userprog/child-close.c new file mode 100644 index 0000000..ac948c8 --- /dev/null +++ b/pintos-progos/tests/userprog/child-close.c | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* Child process run by multi-child-fd test. | ||
| 2 | |||
| 3 | Attempts to close the file descriptor passed as the first | ||
| 4 | command-line argument. This is invalid, because file | ||
| 5 | descriptors are not inherited in Pintos. Two results are | ||
| 6 | allowed: either the system call should return without taking | ||
| 7 | any action, or the kernel should terminate the process with a | ||
| 8 | -1 exit code. */ | ||
| 9 | |||
| 10 | #include <ctype.h> | ||
| 11 | #include <stdio.h> | ||
| 12 | #include <stdlib.h> | ||
| 13 | #include <syscall.h> | ||
| 14 | #include "tests/lib.h" | ||
| 15 | |||
| 16 | const char *test_name = "child-close"; | ||
| 17 | |||
| 18 | int | ||
| 19 | main (int argc UNUSED, char *argv[]) | ||
| 20 | { | ||
| 21 | msg ("begin"); | ||
| 22 | if (!isdigit (*argv[1])) | ||
| 23 | fail ("bad command-line arguments"); | ||
| 24 | close (atoi (argv[1])); | ||
| 25 | msg ("end"); | ||
| 26 | |||
| 27 | return 0; | ||
| 28 | } | ||
diff --git a/pintos-progos/tests/userprog/child-rox.c b/pintos-progos/tests/userprog/child-rox.c new file mode 100644 index 0000000..aba808b --- /dev/null +++ b/pintos-progos/tests/userprog/child-rox.c | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | /* Child process run by rox-child and rox-multichild tests. | ||
| 2 | Opens and tries to write to its own executable, verifying that | ||
| 3 | that is disallowed. | ||
| 4 | Then recursively executes itself to the depth indicated by the | ||
| 5 | first command-line argument. */ | ||
| 6 | |||
| 7 | #include <ctype.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <syscall.h> | ||
| 11 | #include "tests/lib.h" | ||
| 12 | |||
| 13 | const char *test_name = "child-rox"; | ||
| 14 | |||
| 15 | static void | ||
| 16 | try_write (void) | ||
| 17 | { | ||
| 18 | int handle; | ||
| 19 | char buffer[19]; | ||
| 20 | |||
| 21 | quiet = true; | ||
| 22 | CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\""); | ||
| 23 | quiet = false; | ||
| 24 | |||
| 25 | CHECK (write (handle, buffer, sizeof buffer) == 0, | ||
| 26 | "try to write \"child-rox\""); | ||
| 27 | |||
| 28 | close (handle); | ||
| 29 | } | ||
| 30 | |||
| 31 | int | ||
| 32 | main (int argc UNUSED, char *argv[]) | ||
| 33 | { | ||
| 34 | msg ("begin"); | ||
| 35 | try_write (); | ||
| 36 | |||
| 37 | if (!isdigit (*argv[1])) | ||
| 38 | fail ("bad command-line arguments"); | ||
| 39 | if (atoi (argv[1]) > 1) | ||
| 40 | { | ||
| 41 | char cmd[128]; | ||
| 42 | int child; | ||
| 43 | |||
| 44 | snprintf (cmd, sizeof cmd, "child-rox %d", atoi (argv[1]) - 1); | ||
| 45 | CHECK ((child = exec (cmd)) != -1, "exec \"%s\"", cmd); | ||
| 46 | quiet = true; | ||
| 47 | CHECK (wait (child) == 12, "wait for \"child-rox\""); | ||
| 48 | quiet = false; | ||
| 49 | } | ||
| 50 | |||
| 51 | try_write (); | ||
| 52 | msg ("end"); | ||
| 53 | |||
| 54 | return 12; | ||
| 55 | } | ||
diff --git a/pintos-progos/tests/userprog/child-simple.c b/pintos-progos/tests/userprog/child-simple.c new file mode 100644 index 0000000..0d2dacf --- /dev/null +++ b/pintos-progos/tests/userprog/child-simple.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Child process run by exec-multiple, exec-one, wait-simple, and | ||
| 2 | wait-twice tests. | ||
| 3 | Just prints a single message and terminates. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | |||
| 8 | const char *test_name = "child-simple"; | ||
| 9 | |||
| 10 | int | ||
| 11 | main (void) | ||
| 12 | { | ||
| 13 | msg ("run"); | ||
| 14 | return 81; | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/userprog/close-bad-fd.c b/pintos-progos/tests/userprog/close-bad-fd.c new file mode 100644 index 0000000..f63bb9a --- /dev/null +++ b/pintos-progos/tests/userprog/close-bad-fd.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to close an invalid fd, which must either fail silently | ||
| 2 | or terminate with exit code -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | close (0x20101234); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/close-bad-fd.ck b/pintos-progos/tests/userprog/close-bad-fd.ck new file mode 100644 index 0000000..497b17c --- /dev/null +++ b/pintos-progos/tests/userprog/close-bad-fd.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-bad-fd) begin | ||
| 7 | (close-bad-fd) end | ||
| 8 | close-bad-fd: exit(0) | ||
| 9 | EOF | ||
| 10 | (close-bad-fd) begin | ||
| 11 | close-bad-fd: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-normal.c b/pintos-progos/tests/userprog/close-normal.c new file mode 100644 index 0000000..8ce04e3 --- /dev/null +++ b/pintos-progos/tests/userprog/close-normal.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Opens a file and then closes it. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 12 | msg ("close \"sample.txt\""); | ||
| 13 | close (handle); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/close-normal.ck b/pintos-progos/tests/userprog/close-normal.ck new file mode 100644 index 0000000..fe41342 --- /dev/null +++ b/pintos-progos/tests/userprog/close-normal.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (close-normal) begin | ||
| 7 | (close-normal) open "sample.txt" | ||
| 8 | (close-normal) close "sample.txt" | ||
| 9 | (close-normal) end | ||
| 10 | close-normal: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-stdin.c b/pintos-progos/tests/userprog/close-stdin.c new file mode 100644 index 0000000..9bbf9f2 --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdin.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to close the keyboard input stream, which must either | ||
| 2 | fail silently or terminate with exit code -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | close (0); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/close-stdin.ck b/pintos-progos/tests/userprog/close-stdin.ck new file mode 100644 index 0000000..3d28507 --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdin.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-stdin) begin | ||
| 7 | (close-stdin) end | ||
| 8 | close-stdin: exit(0) | ||
| 9 | EOF | ||
| 10 | (close-stdin) begin | ||
| 11 | close-stdin: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-stdout.c b/pintos-progos/tests/userprog/close-stdout.c new file mode 100644 index 0000000..886523f --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdout.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to close the console output stream, which must either | ||
| 2 | fail silently or terminate with exit code -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | close (1); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/close-stdout.ck b/pintos-progos/tests/userprog/close-stdout.ck new file mode 100644 index 0000000..3cbbcff --- /dev/null +++ b/pintos-progos/tests/userprog/close-stdout.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-stdout) begin | ||
| 7 | (close-stdout) end | ||
| 8 | close-stdout: exit(0) | ||
| 9 | EOF | ||
| 10 | (close-stdout) begin | ||
| 11 | close-stdout: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/close-twice.c b/pintos-progos/tests/userprog/close-twice.c new file mode 100644 index 0000000..830bccf --- /dev/null +++ b/pintos-progos/tests/userprog/close-twice.c | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | /* Opens a file and then tries to close it twice. The second | ||
| 2 | close must either fail silently or terminate with exit code | ||
| 3 | -1. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | int handle; | ||
| 13 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 14 | msg ("close \"sample.txt\""); | ||
| 15 | close (handle); | ||
| 16 | msg ("close \"sample.txt\" again"); | ||
| 17 | close (handle); | ||
| 18 | } | ||
diff --git a/pintos-progos/tests/userprog/close-twice.ck b/pintos-progos/tests/userprog/close-twice.ck new file mode 100644 index 0000000..deb55a6 --- /dev/null +++ b/pintos-progos/tests/userprog/close-twice.ck | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (close-twice) begin | ||
| 7 | (close-twice) open "sample.txt" | ||
| 8 | (close-twice) close "sample.txt" | ||
| 9 | (close-twice) close "sample.txt" again | ||
| 10 | (close-twice) end | ||
| 11 | close-twice: exit(0) | ||
| 12 | EOF | ||
| 13 | (close-twice) begin | ||
| 14 | (close-twice) open "sample.txt" | ||
| 15 | (close-twice) close "sample.txt" | ||
| 16 | (close-twice) close "sample.txt" again | ||
| 17 | close-twice: exit(-1) | ||
| 18 | EOF | ||
| 19 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.c b/pintos-progos/tests/userprog/create-bad-ptr.c new file mode 100644 index 0000000..4a07bb3 --- /dev/null +++ b/pintos-progos/tests/userprog/create-bad-ptr.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Passes a bad pointer to the create system call, | ||
| 2 | which must cause the process to be terminated with exit code | ||
| 3 | -1. */ | ||
| 4 | |||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | msg ("create(0x20101234): %d", create ((char *) 0x20101234, 0)); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.ck b/pintos-progos/tests/userprog/create-bad-ptr.ck new file mode 100644 index 0000000..ac13405 --- /dev/null +++ b/pintos-progos/tests/userprog/create-bad-ptr.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-bad-ptr) begin | ||
| 7 | create-bad-ptr: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-bound.c b/pintos-progos/tests/userprog/create-bound.c new file mode 100644 index 0000000..0a829f3 --- /dev/null +++ b/pintos-progos/tests/userprog/create-bound.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Opens a file whose name spans the boundary between two pages. | ||
| 2 | This is valid, so it must succeed. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/userprog/boundary.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | msg ("create(\"quux.dat\"): %d", | ||
| 13 | create (copy_string_across_boundary ("quux.dat"), 0)); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/create-bound.ck b/pintos-progos/tests/userprog/create-bound.ck new file mode 100644 index 0000000..7656b7f --- /dev/null +++ b/pintos-progos/tests/userprog/create-bound.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-bound) begin | ||
| 7 | (create-bound) create("quux.dat"): 1 | ||
| 8 | (create-bound) end | ||
| 9 | create-bound: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-empty.c b/pintos-progos/tests/userprog/create-empty.c new file mode 100644 index 0000000..fa26b43 --- /dev/null +++ b/pintos-progos/tests/userprog/create-empty.c | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* Tries to create a file with the empty string as its name. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | msg ("create(\"\"): %d", create ("", 0)); | ||
| 10 | } | ||
diff --git a/pintos-progos/tests/userprog/create-empty.ck b/pintos-progos/tests/userprog/create-empty.ck new file mode 100644 index 0000000..93a1058 --- /dev/null +++ b/pintos-progos/tests/userprog/create-empty.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (create-empty) begin | ||
| 7 | (create-empty) create(""): 0 | ||
| 8 | (create-empty) end | ||
| 9 | create-empty: exit(0) | ||
| 10 | EOF | ||
| 11 | (create-empty) begin | ||
| 12 | create-empty: exit(-1) | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-exists.c b/pintos-progos/tests/userprog/create-exists.c new file mode 100644 index 0000000..d395008 --- /dev/null +++ b/pintos-progos/tests/userprog/create-exists.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Verifies that trying to create a file under a name that | ||
| 2 | already exists will fail. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | CHECK (create ("quux.dat", 0), "create quux.dat"); | ||
| 12 | CHECK (create ("warble.dat", 0), "create warble.dat"); | ||
| 13 | CHECK (!create ("quux.dat", 0), "try to re-create quux.dat"); | ||
| 14 | CHECK (create ("baffle.dat", 0), "create baffle.dat"); | ||
| 15 | CHECK (!create ("warble.dat", 0), "try to re-create quux.dat"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/userprog/create-exists.ck b/pintos-progos/tests/userprog/create-exists.ck new file mode 100644 index 0000000..006885e --- /dev/null +++ b/pintos-progos/tests/userprog/create-exists.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-exists) begin | ||
| 7 | (create-exists) create quux.dat | ||
| 8 | (create-exists) create warble.dat | ||
| 9 | (create-exists) try to re-create quux.dat | ||
| 10 | (create-exists) create baffle.dat | ||
| 11 | (create-exists) try to re-create quux.dat | ||
| 12 | (create-exists) end | ||
| 13 | create-exists: exit(0) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-long.c b/pintos-progos/tests/userprog/create-long.c new file mode 100644 index 0000000..16b31bd --- /dev/null +++ b/pintos-progos/tests/userprog/create-long.c | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* Tries to create a file with a name that is much too long, | ||
| 2 | which must fail. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | static char name[512]; | ||
| 13 | memset (name, 'x', sizeof name); | ||
| 14 | name[sizeof name - 1] = '\0'; | ||
| 15 | |||
| 16 | msg ("create(\"x...\"): %d", create (name, 0)); | ||
| 17 | } | ||
diff --git a/pintos-progos/tests/userprog/create-long.ck b/pintos-progos/tests/userprog/create-long.ck new file mode 100644 index 0000000..628411c --- /dev/null +++ b/pintos-progos/tests/userprog/create-long.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-long) begin | ||
| 7 | (create-long) create("x..."): 0 | ||
| 8 | (create-long) end | ||
| 9 | create-long: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-normal.c b/pintos-progos/tests/userprog/create-normal.c new file mode 100644 index 0000000..3cbc463 --- /dev/null +++ b/pintos-progos/tests/userprog/create-normal.c | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* Creates an ordinary empty file. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | CHECK (create ("quux.dat", 0), "create quux.dat"); | ||
| 10 | } | ||
diff --git a/pintos-progos/tests/userprog/create-normal.ck b/pintos-progos/tests/userprog/create-normal.ck new file mode 100644 index 0000000..ca74a6e --- /dev/null +++ b/pintos-progos/tests/userprog/create-normal.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-normal) begin | ||
| 7 | (create-normal) create quux.dat | ||
| 8 | (create-normal) end | ||
| 9 | create-normal: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/create-null.c b/pintos-progos/tests/userprog/create-null.c new file mode 100644 index 0000000..287cb23 --- /dev/null +++ b/pintos-progos/tests/userprog/create-null.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tries to create a file with the null pointer as its name. | ||
| 2 | The process must be terminated with exit code -1. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("create(NULL): %d", create (NULL, 0)); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/create-null.ck b/pintos-progos/tests/userprog/create-null.ck new file mode 100644 index 0000000..09b7872 --- /dev/null +++ b/pintos-progos/tests/userprog/create-null.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (create-null) begin | ||
| 7 | create-null: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-arg.c b/pintos-progos/tests/userprog/exec-arg.c new file mode 100644 index 0000000..82d0744 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-arg.c | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | /* Tests argument passing to child processes. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | wait (exec ("child-args childarg")); | ||
| 10 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-arg.ck b/pintos-progos/tests/userprog/exec-arg.ck new file mode 100644 index 0000000..b7533ed --- /dev/null +++ b/pintos-progos/tests/userprog/exec-arg.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exec-arg) begin | ||
| 7 | (args) begin | ||
| 8 | (args) argc = 2 | ||
| 9 | (args) argv[0] = 'child-args' | ||
| 10 | (args) argv[1] = 'childarg' | ||
| 11 | (args) argv[2] = null | ||
| 12 | (args) end | ||
| 13 | child-args: exit(0) | ||
| 14 | (exec-arg) end | ||
| 15 | exec-arg: exit(0) | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.c b/pintos-progos/tests/userprog/exec-bad-ptr.c new file mode 100644 index 0000000..0abadd3 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-bad-ptr.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Passes an invalid pointer to the exec system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | exec ((char *) 0x20101234); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.ck b/pintos-progos/tests/userprog/exec-bad-ptr.ck new file mode 100644 index 0000000..63f5f78 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-bad-ptr.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (exec-bad-ptr) begin | ||
| 7 | (exec-bad-ptr) end | ||
| 8 | exec-bad-ptr: exit(0) | ||
| 9 | EOF | ||
| 10 | (exec-bad-ptr) begin | ||
| 11 | exec-bad-ptr: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-missing.c b/pintos-progos/tests/userprog/exec-missing.c new file mode 100644 index 0000000..bf08cad --- /dev/null +++ b/pintos-progos/tests/userprog/exec-missing.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Tries to execute a nonexistent process. | ||
| 2 | The exec system call must return -1. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | msg ("exec(\"no-such-file\"): %d", exec ("no-such-file")); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-missing.ck b/pintos-progos/tests/userprog/exec-missing.ck new file mode 100644 index 0000000..0ef7aaa --- /dev/null +++ b/pintos-progos/tests/userprog/exec-missing.ck | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF', <<'EOF', <<'EOF']); | ||
| 6 | (exec-missing) begin | ||
| 7 | load: no-such-file: open failed | ||
| 8 | (exec-missing) exec("no-such-file"): -1 | ||
| 9 | (exec-missing) end | ||
| 10 | exec-missing: exit(0) | ||
| 11 | EOF | ||
| 12 | (exec-missing) begin | ||
| 13 | (exec-missing) exec("no-such-file"): -1 | ||
| 14 | (exec-missing) end | ||
| 15 | exec-missing: exit(0) | ||
| 16 | EOF | ||
| 17 | (exec-missing) begin | ||
| 18 | load: no-such-file: open failed | ||
| 19 | no-such-file: exit(-1) | ||
| 20 | (exec-missing) exec("no-such-file"): -1 | ||
| 21 | (exec-missing) end | ||
| 22 | exec-missing: exit(0) | ||
| 23 | EOF | ||
| 24 | (exec-missing) begin | ||
| 25 | load: no-such-file: open failed | ||
| 26 | (exec-missing) exec("no-such-file"): -1 | ||
| 27 | no-such-file: exit(-1) | ||
| 28 | (exec-missing) end | ||
| 29 | exec-missing: exit(0) | ||
| 30 | EOF | ||
| 31 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-multiple.c b/pintos-progos/tests/userprog/exec-multiple.c new file mode 100644 index 0000000..ba4c26e --- /dev/null +++ b/pintos-progos/tests/userprog/exec-multiple.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Executes and waits for multiple child processes. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | wait (exec ("child-simple")); | ||
| 11 | wait (exec ("child-simple")); | ||
| 12 | wait (exec ("child-simple")); | ||
| 13 | wait (exec ("child-simple")); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-multiple.ck b/pintos-progos/tests/userprog/exec-multiple.ck new file mode 100644 index 0000000..99624cd --- /dev/null +++ b/pintos-progos/tests/userprog/exec-multiple.ck | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exec-multiple) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (child-simple) run | ||
| 10 | child-simple: exit(81) | ||
| 11 | (child-simple) run | ||
| 12 | child-simple: exit(81) | ||
| 13 | (child-simple) run | ||
| 14 | child-simple: exit(81) | ||
| 15 | (exec-multiple) end | ||
| 16 | exec-multiple: exit(0) | ||
| 17 | EOF | ||
| 18 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exec-once.c b/pintos-progos/tests/userprog/exec-once.c new file mode 100644 index 0000000..7bae5a1 --- /dev/null +++ b/pintos-progos/tests/userprog/exec-once.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Executes and waits for a single child process. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | wait (exec ("child-simple")); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/exec-once.ck b/pintos-progos/tests/userprog/exec-once.ck new file mode 100644 index 0000000..00b59ed --- /dev/null +++ b/pintos-progos/tests/userprog/exec-once.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exec-once) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (exec-once) end | ||
| 10 | exec-once: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/exit.c b/pintos-progos/tests/userprog/exit.c new file mode 100644 index 0000000..cb4eb8f --- /dev/null +++ b/pintos-progos/tests/userprog/exit.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tests the exit system call. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | exit (57); | ||
| 10 | fail ("should have called exit(57)"); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/exit.ck b/pintos-progos/tests/userprog/exit.ck new file mode 100644 index 0000000..a552702 --- /dev/null +++ b/pintos-progos/tests/userprog/exit.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (exit) begin | ||
| 7 | exit: exit(57) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/halt.c b/pintos-progos/tests/userprog/halt.c new file mode 100644 index 0000000..4a99bce --- /dev/null +++ b/pintos-progos/tests/userprog/halt.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Tests the halt system call. */ | ||
| 2 | |||
| 3 | #include "tests/lib.h" | ||
| 4 | #include "tests/main.h" | ||
| 5 | |||
| 6 | void | ||
| 7 | test_main (void) | ||
| 8 | { | ||
| 9 | halt (); | ||
| 10 | fail ("should have halted"); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/halt.ck b/pintos-progos/tests/userprog/halt.ck new file mode 100644 index 0000000..1b701ed --- /dev/null +++ b/pintos-progos/tests/userprog/halt.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | |||
| 6 | our ($test); | ||
| 7 | my (@output) = read_text_file ("$test.output"); | ||
| 8 | |||
| 9 | common_checks ("run", @output); | ||
| 10 | |||
| 11 | fail "missing 'begin' message\n" | ||
| 12 | if !grep ($_ eq '(halt) begin', @output); | ||
| 13 | fail "found 'fail' message--halt didn't really halt\n" | ||
| 14 | if grep ($_ eq '(halt) fail', @output); | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/lib/.gitignore b/pintos-progos/tests/userprog/lib/.gitignore new file mode 100644 index 0000000..a438335 --- /dev/null +++ b/pintos-progos/tests/userprog/lib/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| *.d | |||
diff --git a/pintos-progos/tests/userprog/lib/user/.dummy b/pintos-progos/tests/userprog/lib/user/.dummy new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/pintos-progos/tests/userprog/lib/user/.dummy | |||
diff --git a/pintos-progos/tests/userprog/lib/user/.gitignore b/pintos-progos/tests/userprog/lib/user/.gitignore new file mode 100644 index 0000000..a438335 --- /dev/null +++ b/pintos-progos/tests/userprog/lib/user/.gitignore | |||
| @@ -0,0 +1 @@ | |||
| *.d | |||
diff --git a/pintos-progos/tests/userprog/multi-child-fd.c b/pintos-progos/tests/userprog/multi-child-fd.c new file mode 100644 index 0000000..48de4b4 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-child-fd.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Opens a file and then runs a subprocess that tries to close | ||
| 2 | the file. (Pintos does not have inheritance of file handles, | ||
| 3 | so this must fail.) The parent process then attempts to use | ||
| 4 | the file handle, which must succeed. */ | ||
| 5 | |||
| 6 | #include <stdio.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/userprog/sample.inc" | ||
| 9 | #include "tests/lib.h" | ||
| 10 | #include "tests/main.h" | ||
| 11 | |||
| 12 | void | ||
| 13 | test_main (void) | ||
| 14 | { | ||
| 15 | char child_cmd[128]; | ||
| 16 | int handle; | ||
| 17 | |||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | |||
| 20 | snprintf (child_cmd, sizeof child_cmd, "child-close %d", handle); | ||
| 21 | |||
| 22 | msg ("wait(exec()) = %d", wait (exec (child_cmd))); | ||
| 23 | |||
| 24 | check_file_handle (handle, "sample.txt", sample, sizeof sample - 1); | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/userprog/multi-child-fd.ck b/pintos-progos/tests/userprog/multi-child-fd.ck new file mode 100644 index 0000000..d0b3a33 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-child-fd.ck | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (multi-child-fd) begin | ||
| 7 | (multi-child-fd) open "sample.txt" | ||
| 8 | (child-close) begin | ||
| 9 | (child-close) end | ||
| 10 | child-close: exit(0) | ||
| 11 | (multi-child-fd) wait(exec()) = 0 | ||
| 12 | (multi-child-fd) verified contents of "sample.txt" | ||
| 13 | (multi-child-fd) end | ||
| 14 | multi-child-fd: exit(0) | ||
| 15 | EOF | ||
| 16 | (multi-child-fd) begin | ||
| 17 | (multi-child-fd) open "sample.txt" | ||
| 18 | (child-close) begin | ||
| 19 | child-close: exit(-1) | ||
| 20 | (multi-child-fd) wait(exec()) = -1 | ||
| 21 | (multi-child-fd) verified contents of "sample.txt" | ||
| 22 | (multi-child-fd) end | ||
| 23 | multi-child-fd: exit(0) | ||
| 24 | EOF | ||
| 25 | pass; | ||
diff --git a/pintos-progos/tests/userprog/multi-recurse.c b/pintos-progos/tests/userprog/multi-recurse.c new file mode 100644 index 0000000..7172ec3 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-recurse.c | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | /* Executes itself recursively to the depth indicated by the | ||
| 2 | first command-line argument. */ | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | |||
| 10 | const char *test_name = "multi-recurse"; | ||
| 11 | |||
| 12 | int | ||
| 13 | main (int argc UNUSED, char *argv[]) | ||
| 14 | { | ||
| 15 | int n = atoi (argv[1]); | ||
| 16 | |||
| 17 | msg ("begin %d", n); | ||
| 18 | if (n != 0) | ||
| 19 | { | ||
| 20 | char child_cmd[128]; | ||
| 21 | pid_t child_pid; | ||
| 22 | int code; | ||
| 23 | |||
| 24 | snprintf (child_cmd, sizeof child_cmd, "multi-recurse %d", n - 1); | ||
| 25 | CHECK ((child_pid = exec (child_cmd)) != -1, "exec(\"%s\")", child_cmd); | ||
| 26 | |||
| 27 | code = wait (child_pid); | ||
| 28 | if (code != n - 1) | ||
| 29 | fail ("wait(exec(\"%s\")) returned %d", child_cmd, code); | ||
| 30 | } | ||
| 31 | |||
| 32 | msg ("end %d", n); | ||
| 33 | return n; | ||
| 34 | } | ||
diff --git a/pintos-progos/tests/userprog/multi-recurse.ck b/pintos-progos/tests/userprog/multi-recurse.ck new file mode 100644 index 0000000..41eb4a6 --- /dev/null +++ b/pintos-progos/tests/userprog/multi-recurse.ck | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (multi-recurse) begin 15 | ||
| 7 | (multi-recurse) exec("multi-recurse 14") | ||
| 8 | (multi-recurse) begin 14 | ||
| 9 | (multi-recurse) exec("multi-recurse 13") | ||
| 10 | (multi-recurse) begin 13 | ||
| 11 | (multi-recurse) exec("multi-recurse 12") | ||
| 12 | (multi-recurse) begin 12 | ||
| 13 | (multi-recurse) exec("multi-recurse 11") | ||
| 14 | (multi-recurse) begin 11 | ||
| 15 | (multi-recurse) exec("multi-recurse 10") | ||
| 16 | (multi-recurse) begin 10 | ||
| 17 | (multi-recurse) exec("multi-recurse 9") | ||
| 18 | (multi-recurse) begin 9 | ||
| 19 | (multi-recurse) exec("multi-recurse 8") | ||
| 20 | (multi-recurse) begin 8 | ||
| 21 | (multi-recurse) exec("multi-recurse 7") | ||
| 22 | (multi-recurse) begin 7 | ||
| 23 | (multi-recurse) exec("multi-recurse 6") | ||
| 24 | (multi-recurse) begin 6 | ||
| 25 | (multi-recurse) exec("multi-recurse 5") | ||
| 26 | (multi-recurse) begin 5 | ||
| 27 | (multi-recurse) exec("multi-recurse 4") | ||
| 28 | (multi-recurse) begin 4 | ||
| 29 | (multi-recurse) exec("multi-recurse 3") | ||
| 30 | (multi-recurse) begin 3 | ||
| 31 | (multi-recurse) exec("multi-recurse 2") | ||
| 32 | (multi-recurse) begin 2 | ||
| 33 | (multi-recurse) exec("multi-recurse 1") | ||
| 34 | (multi-recurse) begin 1 | ||
| 35 | (multi-recurse) exec("multi-recurse 0") | ||
| 36 | (multi-recurse) begin 0 | ||
| 37 | (multi-recurse) end 0 | ||
| 38 | multi-recurse: exit(0) | ||
| 39 | (multi-recurse) end 1 | ||
| 40 | multi-recurse: exit(1) | ||
| 41 | (multi-recurse) end 2 | ||
| 42 | multi-recurse: exit(2) | ||
| 43 | (multi-recurse) end 3 | ||
| 44 | multi-recurse: exit(3) | ||
| 45 | (multi-recurse) end 4 | ||
| 46 | multi-recurse: exit(4) | ||
| 47 | (multi-recurse) end 5 | ||
| 48 | multi-recurse: exit(5) | ||
| 49 | (multi-recurse) end 6 | ||
| 50 | multi-recurse: exit(6) | ||
| 51 | (multi-recurse) end 7 | ||
| 52 | multi-recurse: exit(7) | ||
| 53 | (multi-recurse) end 8 | ||
| 54 | multi-recurse: exit(8) | ||
| 55 | (multi-recurse) end 9 | ||
| 56 | multi-recurse: exit(9) | ||
| 57 | (multi-recurse) end 10 | ||
| 58 | multi-recurse: exit(10) | ||
| 59 | (multi-recurse) end 11 | ||
| 60 | multi-recurse: exit(11) | ||
| 61 | (multi-recurse) end 12 | ||
| 62 | multi-recurse: exit(12) | ||
| 63 | (multi-recurse) end 13 | ||
| 64 | multi-recurse: exit(13) | ||
| 65 | (multi-recurse) end 14 | ||
| 66 | multi-recurse: exit(14) | ||
| 67 | (multi-recurse) end 15 | ||
| 68 | multi-recurse: exit(15) | ||
| 69 | EOF | ||
| 70 | pass; | ||
diff --git a/pintos-progos/tests/userprog/no-vm/Make.tests b/pintos-progos/tests/userprog/no-vm/Make.tests new file mode 100644 index 0000000..a545e18 --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/Make.tests | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/userprog/no-vm_TESTS = tests/userprog/no-vm/multi-oom | ||
| 4 | tests/userprog/no-vm_PROGS = $(tests/userprog/no-vm_TESTS) | ||
| 5 | tests/userprog/no-vm/multi-oom_SRC = tests/userprog/no-vm/multi-oom.c \ | ||
| 6 | tests/lib.c | ||
| 7 | |||
| 8 | tests/userprog/no-vm/multi-oom.output: TIMEOUT = 360 | ||
diff --git a/pintos-progos/tests/userprog/no-vm/Rubric b/pintos-progos/tests/userprog/no-vm/Rubric new file mode 100644 index 0000000..c3816c6 --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/Rubric | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | Functionality of features that VM might break: | ||
| 2 | |||
| 3 | 1 multi-oom | ||
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.c b/pintos-progos/tests/userprog/no-vm/multi-oom.c new file mode 100644 index 0000000..6a4472d --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/multi-oom.c | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | /* Recursively executes itself until the child fails to execute. | ||
| 2 | We expect that at least 30 copies can run. | ||
| 3 | |||
| 4 | We count how many children your kernel was able to execute | ||
| 5 | before it fails to start a new process. We require that, | ||
| 6 | if a process doesn't actually get to start, exec() must | ||
| 7 | return -1, not a valid PID. | ||
| 8 | |||
| 9 | We repeat this process 10 times, checking that your kernel | ||
| 10 | allows for the same level of depth every time. | ||
| 11 | |||
| 12 | In addition, some processes will spawn children that terminate | ||
| 13 | abnormally after allocating some resources. | ||
| 14 | |||
| 15 | Written by Godmar Back <godmar@gmail.com> | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <debug.h> | ||
| 19 | #include <stdio.h> | ||
| 20 | #include <string.h> | ||
| 21 | #include <stdlib.h> | ||
| 22 | #include <stdbool.h> | ||
| 23 | #include <syscall.h> | ||
| 24 | #include <random.h> | ||
| 25 | #include "tests/lib.h" | ||
| 26 | |||
| 27 | static const int EXPECTED_DEPTH_TO_PASS = 30; | ||
| 28 | static const int EXPECTED_REPETITIONS = 10; | ||
| 29 | |||
| 30 | const char *test_name = "multi-oom"; | ||
| 31 | |||
| 32 | enum child_termination_mode { RECURSE, CRASH }; | ||
| 33 | |||
| 34 | /* Spawn a recursive copy of ourselves, passing along instructions | ||
| 35 | for the child. */ | ||
| 36 | static pid_t | ||
| 37 | spawn_child (int c, enum child_termination_mode mode) | ||
| 38 | { | ||
| 39 | char child_cmd[128]; | ||
| 40 | snprintf (child_cmd, sizeof child_cmd, | ||
| 41 | "%s %d %s", test_name, c, mode == CRASH ? "-k" : ""); | ||
| 42 | return exec (child_cmd); | ||
| 43 | } | ||
| 44 | |||
| 45 | /* Open a number of files (and fail to close them). | ||
| 46 | The kernel must free any kernel resources associated | ||
| 47 | with these file descriptors. */ | ||
| 48 | static void | ||
| 49 | consume_some_resources (void) | ||
| 50 | { | ||
| 51 | int fd, fdmax = 126; | ||
| 52 | |||
| 53 | /* Open as many files as we can, up to fdmax. | ||
| 54 | Depending on how file descriptors are allocated inside | ||
| 55 | the kernel, open() may fail if the kernel is low on memory. | ||
| 56 | A low-memory condition in open() should not lead to the | ||
| 57 | termination of the process. */ | ||
| 58 | for (fd = 0; fd < fdmax; fd++) | ||
| 59 | if (open (test_name) == -1) | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* Consume some resources, then terminate this process | ||
| 64 | in some abnormal way. */ | ||
| 65 | static int NO_INLINE | ||
| 66 | consume_some_resources_and_die (int seed) | ||
| 67 | { | ||
| 68 | consume_some_resources (); | ||
| 69 | random_init (seed); | ||
| 70 | int *PHYS_BASE = (int *)0xC0000000; | ||
| 71 | |||
| 72 | switch (random_ulong () % 5) | ||
| 73 | { | ||
| 74 | case 0: | ||
| 75 | *(int *) NULL = 42; | ||
| 76 | |||
| 77 | case 1: | ||
| 78 | return *(int *) NULL; | ||
| 79 | |||
| 80 | case 2: | ||
| 81 | return *PHYS_BASE; | ||
| 82 | |||
| 83 | case 3: | ||
| 84 | *PHYS_BASE = 42; | ||
| 85 | |||
| 86 | case 4: | ||
| 87 | open ((char *)PHYS_BASE); | ||
| 88 | exit (-1); | ||
| 89 | |||
| 90 | default: | ||
| 91 | NOT_REACHED (); | ||
| 92 | } | ||
| 93 | return 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* The first copy is invoked without command line arguments. | ||
| 97 | Subsequent copies are invoked with a parameter 'depth' | ||
| 98 | that describes how many parent processes preceded them. | ||
| 99 | Each process spawns one or multiple recursive copies of | ||
| 100 | itself, passing 'depth+1' as depth. | ||
| 101 | |||
| 102 | Some children are started with the '-k' flag, which will | ||
| 103 | result in abnormal termination. | ||
| 104 | */ | ||
| 105 | int | ||
| 106 | main (int argc, char *argv[]) | ||
| 107 | { | ||
| 108 | int n; | ||
| 109 | |||
| 110 | n = argc > 1 ? atoi (argv[1]) : 0; | ||
| 111 | bool is_at_root = (n == 0); | ||
| 112 | if (is_at_root) | ||
| 113 | msg ("begin"); | ||
| 114 | |||
| 115 | /* If -k is passed, crash this process. */ | ||
| 116 | if (argc > 2 && !strcmp(argv[2], "-k")) | ||
| 117 | { | ||
| 118 | consume_some_resources_and_die (n); | ||
| 119 | NOT_REACHED (); | ||
| 120 | } | ||
| 121 | |||
| 122 | int howmany = is_at_root ? EXPECTED_REPETITIONS : 1; | ||
| 123 | int i, expected_depth = -1; | ||
| 124 | |||
| 125 | for (i = 0; i < howmany; i++) | ||
| 126 | { | ||
| 127 | pid_t child_pid; | ||
| 128 | |||
| 129 | /* Spawn a child that will be abnormally terminated. | ||
| 130 | To speed the test up, do this only for processes | ||
| 131 | spawned at a certain depth. */ | ||
| 132 | if (n > EXPECTED_DEPTH_TO_PASS/2) | ||
| 133 | { | ||
| 134 | child_pid = spawn_child (n + 1, CRASH); | ||
| 135 | if (child_pid != -1) | ||
| 136 | { | ||
| 137 | if (wait (child_pid) != -1) | ||
| 138 | fail ("crashed child should return -1."); | ||
| 139 | } | ||
| 140 | /* If spawning this child failed, so should | ||
| 141 | the next spawn_child below. */ | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Now spawn the child that will recurse. */ | ||
| 145 | child_pid = spawn_child (n + 1, RECURSE); | ||
| 146 | |||
| 147 | /* If maximum depth is reached, return result. */ | ||
| 148 | if (child_pid == -1) | ||
| 149 | return n; | ||
| 150 | |||
| 151 | /* Else wait for child to report how deeply it was able to recurse. */ | ||
| 152 | int reached_depth = wait (child_pid); | ||
| 153 | if (reached_depth == -1) | ||
| 154 | fail ("wait returned -1."); | ||
| 155 | |||
| 156 | /* Record the depth reached during the first run; on subsequent | ||
| 157 | runs, fail if those runs do not match the depth achieved on the | ||
| 158 | first run. */ | ||
| 159 | if (i == 0) | ||
| 160 | expected_depth = reached_depth; | ||
| 161 | else if (expected_depth != reached_depth) | ||
| 162 | fail ("after run %d/%d, expected depth %d, actual depth %d.", | ||
| 163 | i, howmany, expected_depth, reached_depth); | ||
| 164 | ASSERT (expected_depth == reached_depth); | ||
| 165 | } | ||
| 166 | |||
| 167 | consume_some_resources (); | ||
| 168 | |||
| 169 | if (n == 0) | ||
| 170 | { | ||
| 171 | if (expected_depth < EXPECTED_DEPTH_TO_PASS) | ||
| 172 | fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS); | ||
| 173 | msg ("success. program forked %d times.", howmany); | ||
| 174 | msg ("end"); | ||
| 175 | } | ||
| 176 | |||
| 177 | return expected_depth; | ||
| 178 | } | ||
| 179 | // vim: sw=2 | ||
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.ck b/pintos-progos/tests/userprog/no-vm/multi-oom.ck new file mode 100644 index 0000000..59a0bcd --- /dev/null +++ b/pintos-progos/tests/userprog/no-vm/multi-oom.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (multi-oom) begin | ||
| 7 | (multi-oom) success. program forked 10 times. | ||
| 8 | (multi-oom) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/null.ck b/pintos-progos/tests/userprog/null.ck new file mode 100644 index 0000000..980de35 --- /dev/null +++ b/pintos-progos/tests/userprog/null.ck | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | system call! | ||
| 7 | EOF | ||
| 8 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.c b/pintos-progos/tests/userprog/open-bad-ptr.c new file mode 100644 index 0000000..9cd4edf --- /dev/null +++ b/pintos-progos/tests/userprog/open-bad-ptr.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Passes an invalid pointer to the open system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | msg ("open(0x20101234): %d", open ((char *) 0x20101234)); | ||
| 12 | fail ("should have called exit(-1)"); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.ck b/pintos-progos/tests/userprog/open-bad-ptr.ck new file mode 100644 index 0000000..45349e2 --- /dev/null +++ b/pintos-progos/tests/userprog/open-bad-ptr.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (open-bad-ptr) begin | ||
| 7 | (open-bad-ptr) end | ||
| 8 | open-bad-ptr: exit(0) | ||
| 9 | EOF | ||
| 10 | (open-bad-ptr) begin | ||
| 11 | open-bad-ptr: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-boundary.c b/pintos-progos/tests/userprog/open-boundary.c new file mode 100644 index 0000000..cc8ff8b --- /dev/null +++ b/pintos-progos/tests/userprog/open-boundary.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Creates a file whose name spans the boundary between two pages. | ||
| 2 | This is valid, so it must succeed. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/userprog/boundary.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | CHECK (open (copy_string_across_boundary ("sample.txt")) > 1, | ||
| 13 | "open \"sample.txt\""); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/open-boundary.ck b/pintos-progos/tests/userprog/open-boundary.ck new file mode 100644 index 0000000..8060d22 --- /dev/null +++ b/pintos-progos/tests/userprog/open-boundary.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-boundary) begin | ||
| 7 | (open-boundary) open "sample.txt" | ||
| 8 | (open-boundary) end | ||
| 9 | open-boundary: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-empty.c b/pintos-progos/tests/userprog/open-empty.c new file mode 100644 index 0000000..3ea9907 --- /dev/null +++ b/pintos-progos/tests/userprog/open-empty.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to open a file with the empty string as its name. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle = open (""); | ||
| 11 | if (handle != -1) | ||
| 12 | fail ("open() returned %d instead of -1", handle); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-empty.ck b/pintos-progos/tests/userprog/open-empty.ck new file mode 100644 index 0000000..885fb41 --- /dev/null +++ b/pintos-progos/tests/userprog/open-empty.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-empty) begin | ||
| 7 | (open-empty) end | ||
| 8 | open-empty: exit(0) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-missing.c b/pintos-progos/tests/userprog/open-missing.c new file mode 100644 index 0000000..13ecbda --- /dev/null +++ b/pintos-progos/tests/userprog/open-missing.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Tries to open a nonexistent file. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle = open ("no-such-file"); | ||
| 11 | if (handle != -1) | ||
| 12 | fail ("open() returned %d", handle); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-missing.ck b/pintos-progos/tests/userprog/open-missing.ck new file mode 100644 index 0000000..d72d878 --- /dev/null +++ b/pintos-progos/tests/userprog/open-missing.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-missing) begin | ||
| 7 | (open-missing) end | ||
| 8 | open-missing: exit(0) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-normal.c b/pintos-progos/tests/userprog/open-normal.c new file mode 100644 index 0000000..5132465 --- /dev/null +++ b/pintos-progos/tests/userprog/open-normal.c | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | /* Open a file. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle = open ("sample.txt"); | ||
| 11 | if (handle < 2) | ||
| 12 | fail ("open() returned %d", handle); | ||
| 13 | } | ||
diff --git a/pintos-progos/tests/userprog/open-normal.ck b/pintos-progos/tests/userprog/open-normal.ck new file mode 100644 index 0000000..4f6c342 --- /dev/null +++ b/pintos-progos/tests/userprog/open-normal.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-normal) begin | ||
| 7 | (open-normal) end | ||
| 8 | open-normal: exit(0) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-null.c b/pintos-progos/tests/userprog/open-null.c new file mode 100644 index 0000000..bb418b8 --- /dev/null +++ b/pintos-progos/tests/userprog/open-null.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Tries to open a file with the null pointer as its name. | ||
| 2 | The process must be terminated with exit code -1. */ | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | open (NULL); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/userprog/open-null.ck b/pintos-progos/tests/userprog/open-null.ck new file mode 100644 index 0000000..b4a3bcb --- /dev/null +++ b/pintos-progos/tests/userprog/open-null.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (open-null) begin | ||
| 7 | (open-null) end | ||
| 8 | open-null: exit(0) | ||
| 9 | EOF | ||
| 10 | (open-null) begin | ||
| 11 | open-null: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/open-twice.c b/pintos-progos/tests/userprog/open-twice.c new file mode 100644 index 0000000..dd333af --- /dev/null +++ b/pintos-progos/tests/userprog/open-twice.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Tries to open the same file twice, | ||
| 2 | which must succeed and must return a different file descriptor | ||
| 3 | in each case. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | int h1 = open ("sample.txt"); | ||
| 13 | int h2 = open ("sample.txt"); | ||
| 14 | |||
| 15 | CHECK ((h1 = open ("sample.txt")) > 1, "open \"sample.txt\" once"); | ||
| 16 | CHECK ((h2 = open ("sample.txt")) > 1, "open \"sample.txt\" again"); | ||
| 17 | if (h1 == h2) | ||
| 18 | fail ("open() returned %d both times", h1); | ||
| 19 | } | ||
diff --git a/pintos-progos/tests/userprog/open-twice.ck b/pintos-progos/tests/userprog/open-twice.ck new file mode 100644 index 0000000..64fa805 --- /dev/null +++ b/pintos-progos/tests/userprog/open-twice.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (open-twice) begin | ||
| 7 | (open-twice) open "sample.txt" once | ||
| 8 | (open-twice) open "sample.txt" again | ||
| 9 | (open-twice) end | ||
| 10 | open-twice: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-bad-fd.c b/pintos-progos/tests/userprog/read-bad-fd.c new file mode 100644 index 0000000..a8b190d --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-fd.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Tries to read from an invalid fd, | ||
| 2 | which must either fail silently or terminate the process with | ||
| 3 | exit code -1. */ | ||
| 4 | |||
| 5 | #include <limits.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char buf; | ||
| 14 | read (0x20101234, &buf, 1); | ||
| 15 | read (5, &buf, 1); | ||
| 16 | read (1234, &buf, 1); | ||
| 17 | read (-1, &buf, 1); | ||
| 18 | read (-1024, &buf, 1); | ||
| 19 | read (INT_MIN, &buf, 1); | ||
| 20 | read (INT_MAX, &buf, 1); | ||
| 21 | } | ||
diff --git a/pintos-progos/tests/userprog/read-bad-fd.ck b/pintos-progos/tests/userprog/read-bad-fd.ck new file mode 100644 index 0000000..5fedcc7 --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-fd.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (read-bad-fd) begin | ||
| 7 | (read-bad-fd) end | ||
| 8 | read-bad-fd: exit(0) | ||
| 9 | EOF | ||
| 10 | (read-bad-fd) begin | ||
| 11 | read-bad-fd: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.c b/pintos-progos/tests/userprog/read-bad-ptr.c new file mode 100644 index 0000000..8fe756e --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-ptr.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Passes an invalid pointer to the read system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | |||
| 14 | read (handle, (char *) 0xc0100000, 123); | ||
| 15 | fail ("should not have survived read()"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.ck b/pintos-progos/tests/userprog/read-bad-ptr.ck new file mode 100644 index 0000000..d10accf --- /dev/null +++ b/pintos-progos/tests/userprog/read-bad-ptr.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (read-bad-ptr) begin | ||
| 7 | (read-bad-ptr) open "sample.txt" | ||
| 8 | (read-bad-ptr) end | ||
| 9 | read-bad-ptr: exit(0) | ||
| 10 | EOF | ||
| 11 | (read-bad-ptr) begin | ||
| 12 | (read-bad-ptr) open "sample.txt" | ||
| 13 | read-bad-ptr: exit(-1) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-boundary.c b/pintos-progos/tests/userprog/read-boundary.c new file mode 100644 index 0000000..9c19966 --- /dev/null +++ b/pintos-progos/tests/userprog/read-boundary.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* Reads data spanning two pages in virtual address space, | ||
| 2 | which must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/userprog/boundary.h" | ||
| 7 | #include "tests/userprog/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | int handle; | ||
| 15 | int byte_cnt; | ||
| 16 | char *buffer; | ||
| 17 | |||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | |||
| 20 | buffer = get_boundary_area () - sizeof sample / 2; | ||
| 21 | byte_cnt = read (handle, buffer, sizeof sample - 1); | ||
| 22 | if (byte_cnt != sizeof sample - 1) | ||
| 23 | fail ("read() returned %d instead of %zu", byte_cnt, sizeof sample - 1); | ||
| 24 | else if (strcmp (sample, buffer)) | ||
| 25 | { | ||
| 26 | msg ("expected text:\n%s", sample); | ||
| 27 | msg ("text actually read:\n%s", buffer); | ||
| 28 | fail ("expected text differs from actual"); | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/pintos-progos/tests/userprog/read-boundary.ck b/pintos-progos/tests/userprog/read-boundary.ck new file mode 100644 index 0000000..08dc161 --- /dev/null +++ b/pintos-progos/tests/userprog/read-boundary.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (read-boundary) begin | ||
| 7 | (read-boundary) open "sample.txt" | ||
| 8 | (read-boundary) end | ||
| 9 | read-boundary: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-normal.c b/pintos-progos/tests/userprog/read-normal.c new file mode 100644 index 0000000..16d15cc --- /dev/null +++ b/pintos-progos/tests/userprog/read-normal.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Try reading a file in the most normal way. */ | ||
| 2 | |||
| 3 | #include "tests/userprog/sample.inc" | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | check_file ("sample.txt", sample, sizeof sample - 1); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/read-normal.ck b/pintos-progos/tests/userprog/read-normal.ck new file mode 100644 index 0000000..0ed2998 --- /dev/null +++ b/pintos-progos/tests/userprog/read-normal.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (read-normal) begin | ||
| 7 | (read-normal) open "sample.txt" for verification | ||
| 8 | (read-normal) verified contents of "sample.txt" | ||
| 9 | (read-normal) close "sample.txt" | ||
| 10 | (read-normal) end | ||
| 11 | read-normal: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-stdout.c b/pintos-progos/tests/userprog/read-stdout.c new file mode 100644 index 0000000..d0630b9 --- /dev/null +++ b/pintos-progos/tests/userprog/read-stdout.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Try reading from fd 1 (stdout), | ||
| 2 | which may just fail or terminate the process with -1 exit | ||
| 3 | code. */ | ||
| 4 | |||
| 5 | #include <stdio.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char buf; | ||
| 13 | read (STDOUT_FILENO, &buf, 1); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/read-stdout.ck b/pintos-progos/tests/userprog/read-stdout.ck new file mode 100644 index 0000000..7d87b52 --- /dev/null +++ b/pintos-progos/tests/userprog/read-stdout.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (read-stdout) begin | ||
| 7 | (read-stdout) end | ||
| 8 | read-stdout: exit(0) | ||
| 9 | EOF | ||
| 10 | (read-stdout) begin | ||
| 11 | read-stdout: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/read-zero.c b/pintos-progos/tests/userprog/read-zero.c new file mode 100644 index 0000000..e441817 --- /dev/null +++ b/pintos-progos/tests/userprog/read-zero.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Try a 0-byte read, which should return 0 without reading | ||
| 2 | anything. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle, byte_cnt; | ||
| 12 | char buf; | ||
| 13 | |||
| 14 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 15 | |||
| 16 | buf = 123; | ||
| 17 | byte_cnt = read (handle, &buf, 0); | ||
| 18 | if (byte_cnt != 0) | ||
| 19 | fail ("read() returned %d instead of 0", byte_cnt); | ||
| 20 | else if (buf != 123) | ||
| 21 | fail ("0-byte read() modified buffer"); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/userprog/read-zero.ck b/pintos-progos/tests/userprog/read-zero.ck new file mode 100644 index 0000000..8346dbc --- /dev/null +++ b/pintos-progos/tests/userprog/read-zero.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (read-zero) begin | ||
| 7 | (read-zero) open "sample.txt" | ||
| 8 | (read-zero) end | ||
| 9 | read-zero: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/rox-child.c b/pintos-progos/tests/userprog/rox-child.c new file mode 100644 index 0000000..30afba2 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-child.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Ensure that the executable of a running process cannot be | ||
| 2 | modified, even by a child process. */ | ||
| 3 | |||
| 4 | #define CHILD_CNT "1" | ||
| 5 | #include "tests/userprog/rox-child.inc" | ||
diff --git a/pintos-progos/tests/userprog/rox-child.ck b/pintos-progos/tests/userprog/rox-child.ck new file mode 100644 index 0000000..e6363fb --- /dev/null +++ b/pintos-progos/tests/userprog/rox-child.ck | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (rox-child) begin | ||
| 7 | (rox-child) open "child-rox" | ||
| 8 | (rox-child) read "child-rox" | ||
| 9 | (rox-child) write "child-rox" | ||
| 10 | (rox-child) exec "child-rox 1" | ||
| 11 | (child-rox) begin | ||
| 12 | (child-rox) try to write "child-rox" | ||
| 13 | (child-rox) try to write "child-rox" | ||
| 14 | (child-rox) end | ||
| 15 | child-rox: exit(12) | ||
| 16 | (rox-child) write "child-rox" | ||
| 17 | (rox-child) end | ||
| 18 | rox-child: exit(0) | ||
| 19 | EOF | ||
| 20 | pass; | ||
diff --git a/pintos-progos/tests/userprog/rox-child.inc b/pintos-progos/tests/userprog/rox-child.inc new file mode 100644 index 0000000..1e2ade9 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-child.inc | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* -*- c -*- */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | const char *child_cmd = "child-rox " CHILD_CNT; | ||
| 11 | int handle; | ||
| 12 | pid_t child; | ||
| 13 | char buffer[16]; | ||
| 14 | |||
| 15 | /* Open child-rox, read from it, write back same data. */ | ||
| 16 | CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\""); | ||
| 17 | CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 18 | "read \"child-rox\""); | ||
| 19 | seek (handle, 0); | ||
| 20 | CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 21 | "write \"child-rox\""); | ||
| 22 | |||
| 23 | /* Execute child-rox and wait for it. */ | ||
| 24 | CHECK ((child = exec (child_cmd)) != -1, "exec \"%s\"", child_cmd); | ||
| 25 | quiet = true; | ||
| 26 | CHECK (wait (child) == 12, "wait for child"); | ||
| 27 | quiet = false; | ||
| 28 | |||
| 29 | /* Write to child-rox again. */ | ||
| 30 | seek (handle, 0); | ||
| 31 | CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 32 | "write \"child-rox\""); | ||
| 33 | } | ||
diff --git a/pintos-progos/tests/userprog/rox-multichild.c b/pintos-progos/tests/userprog/rox-multichild.c new file mode 100644 index 0000000..8e74dab --- /dev/null +++ b/pintos-progos/tests/userprog/rox-multichild.c | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | /* Ensure that the executable of a running process cannot be | ||
| 2 | modified, even in the presence of multiple children. */ | ||
| 3 | |||
| 4 | #define CHILD_CNT "5" | ||
| 5 | #include "tests/userprog/rox-child.inc" | ||
diff --git a/pintos-progos/tests/userprog/rox-multichild.ck b/pintos-progos/tests/userprog/rox-multichild.ck new file mode 100644 index 0000000..14b27db --- /dev/null +++ b/pintos-progos/tests/userprog/rox-multichild.ck | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (rox-multichild) begin | ||
| 7 | (rox-multichild) open "child-rox" | ||
| 8 | (rox-multichild) read "child-rox" | ||
| 9 | (rox-multichild) write "child-rox" | ||
| 10 | (rox-multichild) exec "child-rox 5" | ||
| 11 | (child-rox) begin | ||
| 12 | (child-rox) try to write "child-rox" | ||
| 13 | (child-rox) exec "child-rox 4" | ||
| 14 | (child-rox) begin | ||
| 15 | (child-rox) try to write "child-rox" | ||
| 16 | (child-rox) exec "child-rox 3" | ||
| 17 | (child-rox) begin | ||
| 18 | (child-rox) try to write "child-rox" | ||
| 19 | (child-rox) exec "child-rox 2" | ||
| 20 | (child-rox) begin | ||
| 21 | (child-rox) try to write "child-rox" | ||
| 22 | (child-rox) exec "child-rox 1" | ||
| 23 | (child-rox) begin | ||
| 24 | (child-rox) try to write "child-rox" | ||
| 25 | (child-rox) try to write "child-rox" | ||
| 26 | (child-rox) end | ||
| 27 | child-rox: exit(12) | ||
| 28 | (child-rox) try to write "child-rox" | ||
| 29 | (child-rox) end | ||
| 30 | child-rox: exit(12) | ||
| 31 | (child-rox) try to write "child-rox" | ||
| 32 | (child-rox) end | ||
| 33 | child-rox: exit(12) | ||
| 34 | (child-rox) try to write "child-rox" | ||
| 35 | (child-rox) end | ||
| 36 | child-rox: exit(12) | ||
| 37 | (child-rox) try to write "child-rox" | ||
| 38 | (child-rox) end | ||
| 39 | child-rox: exit(12) | ||
| 40 | (rox-multichild) write "child-rox" | ||
| 41 | (rox-multichild) end | ||
| 42 | rox-multichild: exit(0) | ||
| 43 | EOF | ||
| 44 | pass; | ||
diff --git a/pintos-progos/tests/userprog/rox-simple.c b/pintos-progos/tests/userprog/rox-simple.c new file mode 100644 index 0000000..e84a064 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-simple.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Ensure that the executable of a running process cannot be | ||
| 2 | modified. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | char buffer[16]; | ||
| 13 | |||
| 14 | CHECK ((handle = open ("rox-simple")) > 1, "open \"rox-simple\""); | ||
| 15 | CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer, | ||
| 16 | "read \"rox-simple\""); | ||
| 17 | CHECK (write (handle, buffer, sizeof buffer) == 0, | ||
| 18 | "try to write \"rox-simple\""); | ||
| 19 | } | ||
diff --git a/pintos-progos/tests/userprog/rox-simple.ck b/pintos-progos/tests/userprog/rox-simple.ck new file mode 100644 index 0000000..c9dcc66 --- /dev/null +++ b/pintos-progos/tests/userprog/rox-simple.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (rox-simple) begin | ||
| 7 | (rox-simple) open "rox-simple" | ||
| 8 | (rox-simple) read "rox-simple" | ||
| 9 | (rox-simple) try to write "rox-simple" | ||
| 10 | (rox-simple) end | ||
| 11 | rox-simple: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sample.inc b/pintos-progos/tests/userprog/sample.inc new file mode 100644 index 0000000..59f2bcb --- /dev/null +++ b/pintos-progos/tests/userprog/sample.inc | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | char sample[] = { | ||
| 2 | "\"Amazing Electronic Fact: If you scuffed your feet long enough without\n" | ||
| 3 | " touching anything, you would build up so many electrons that your\n" | ||
| 4 | " finger would explode! But this is nothing to worry about unless you\n" | ||
| 5 | " have carpeting.\" --Dave Barry\n" | ||
| 6 | }; | ||
diff --git a/pintos-progos/tests/userprog/sample.txt b/pintos-progos/tests/userprog/sample.txt new file mode 100644 index 0000000..5050fec --- /dev/null +++ b/pintos-progos/tests/userprog/sample.txt | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | "Amazing Electronic Fact: If you scuffed your feet long enough without | ||
| 2 | touching anything, you would build up so many electrons that your | ||
| 3 | finger would explode! But this is nothing to worry about unless you | ||
| 4 | have carpeting." --Dave Barry | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.c b/pintos-progos/tests/userprog/sc-bad-arg.c new file mode 100644 index 0000000..0b512a0 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-arg.c | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | /* Sticks a system call number (SYS_EXIT) at the very top of the | ||
| 2 | stack, then invokes a system call with the stack pointer | ||
| 3 | (%esp) set to its address. The process must be terminated | ||
| 4 | with -1 exit code because the argument to the system call | ||
| 5 | would be above the top of the user address space. */ | ||
| 6 | |||
| 7 | #include <syscall-nr.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | asm volatile ("movl $0xbffffffc, %%esp; movl %0, (%%esp); int $0x30" | ||
| 15 | : : "i" (SYS_EXIT)); | ||
| 16 | fail ("should have called exit(-1)"); | ||
| 17 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.ck b/pintos-progos/tests/userprog/sc-bad-arg.ck new file mode 100644 index 0000000..8981105 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-arg.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-bad-arg) begin | ||
| 7 | sc-bad-arg: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.c b/pintos-progos/tests/userprog/sc-bad-sp.c new file mode 100644 index 0000000..39cce84 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-sp.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Invokes a system call with the stack pointer (%esp) set to a | ||
| 2 | bad address. The process must be terminated with -1 exit | ||
| 3 | code. | ||
| 4 | |||
| 5 | For Project 3: The bad address lies approximately 64MB below | ||
| 6 | the code segment, so there is no ambiguity that this attempt | ||
| 7 | must be rejected even after stack growth is implemented. | ||
| 8 | Moreover, a good stack growth heuristics should probably not | ||
| 9 | grow the stack for the purpose of reading the system call | ||
| 10 | number and arguments. */ | ||
| 11 | |||
| 12 | #include "tests/lib.h" | ||
| 13 | #include "tests/main.h" | ||
| 14 | |||
| 15 | void | ||
| 16 | test_main (void) | ||
| 17 | { | ||
| 18 | asm volatile ("movl $.-(64*1024*1024), %esp; int $0x30"); | ||
| 19 | fail ("should have called exit(-1)"); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.ck b/pintos-progos/tests/userprog/sc-bad-sp.ck new file mode 100644 index 0000000..498cec1 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-bad-sp.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-bad-sp) begin | ||
| 7 | sc-bad-sp: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.c b/pintos-progos/tests/userprog/sc-boundary-2.c new file mode 100644 index 0000000..8acf036 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary-2.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Invokes a system call with one byte of the system call's | ||
| 2 | argument on a separate page from the rest of the bytes. This | ||
| 3 | must work. */ | ||
| 4 | |||
| 5 | #include <syscall-nr.h> | ||
| 6 | #include "tests/userprog/boundary.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | /* Make one byte of a syscall argument hang over into a second | ||
| 14 | page. */ | ||
| 15 | int *p = (int *) ((char *) get_boundary_area () - 7); | ||
| 16 | p[0] = SYS_EXIT; | ||
| 17 | p[1] = 67; | ||
| 18 | |||
| 19 | /* Invoke the system call. */ | ||
| 20 | asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p)); | ||
| 21 | fail ("should have called exit(67)"); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.ck b/pintos-progos/tests/userprog/sc-boundary-2.ck new file mode 100644 index 0000000..43766bf --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary-2.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-boundary-2) begin | ||
| 7 | sc-boundary-2: exit(67) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary.c b/pintos-progos/tests/userprog/sc-boundary.c new file mode 100644 index 0000000..d889535 --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Invokes a system call with the system call number and its | ||
| 2 | argument on separate pages. This must work. */ | ||
| 3 | |||
| 4 | #include <syscall-nr.h> | ||
| 5 | #include "tests/userprog/boundary.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | /* Put a syscall number at the end of one page | ||
| 13 | and its argument at the beginning of another. */ | ||
| 14 | int *p = get_boundary_area (); | ||
| 15 | p--; | ||
| 16 | p[0] = SYS_EXIT; | ||
| 17 | p[1] = 42; | ||
| 18 | |||
| 19 | /* Invoke the system call. */ | ||
| 20 | asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p)); | ||
| 21 | fail ("should have called exit(42)"); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/userprog/sc-boundary.ck b/pintos-progos/tests/userprog/sc-boundary.ck new file mode 100644 index 0000000..3f7cbaf --- /dev/null +++ b/pintos-progos/tests/userprog/sc-boundary.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (sc-boundary) begin | ||
| 7 | sc-boundary: exit(42) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.c b/pintos-progos/tests/userprog/wait-bad-pid.c new file mode 100644 index 0000000..3fe8ee4 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-bad-pid.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Waits for an invalid pid. This may fail or terminate the | ||
| 2 | process with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | wait ((pid_t) 0x0c020301); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.ck b/pintos-progos/tests/userprog/wait-bad-pid.ck new file mode 100644 index 0000000..db63fb9 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-bad-pid.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (wait-bad-pid) begin | ||
| 7 | (wait-bad-pid) end | ||
| 8 | wait-bad-pid: exit(0) | ||
| 9 | EOF | ||
| 10 | (wait-bad-pid) begin | ||
| 11 | wait-bad-pid: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-killed.c b/pintos-progos/tests/userprog/wait-killed.c new file mode 100644 index 0000000..6a2a6b5 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-killed.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Wait for a process that will be killed for bad behavior. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("wait(exec()) = %d", wait (exec ("child-bad"))); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-killed.ck b/pintos-progos/tests/userprog/wait-killed.ck new file mode 100644 index 0000000..5df0e9c --- /dev/null +++ b/pintos-progos/tests/userprog/wait-killed.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (wait-killed) begin | ||
| 7 | (child-bad) begin | ||
| 8 | child-bad: exit(-1) | ||
| 9 | (wait-killed) wait(exec()) = -1 | ||
| 10 | (wait-killed) end | ||
| 11 | wait-killed: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-simple.c b/pintos-progos/tests/userprog/wait-simple.c new file mode 100644 index 0000000..d3afcf3 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-simple.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Wait for a subprocess to finish. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | msg ("wait(exec()) = %d", wait (exec ("child-simple"))); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-simple.ck b/pintos-progos/tests/userprog/wait-simple.ck new file mode 100644 index 0000000..93dd577 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-simple.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (wait-simple) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (wait-simple) wait(exec()) = 81 | ||
| 10 | (wait-simple) end | ||
| 11 | wait-simple: exit(0) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/wait-twice.c b/pintos-progos/tests/userprog/wait-twice.c new file mode 100644 index 0000000..785e684 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-twice.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Wait for a subprocess to finish, twice. | ||
| 2 | The first call must wait in the usual way and return the exit code. | ||
| 3 | The second wait call must return -1 immediately. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | pid_t child = exec ("child-simple"); | ||
| 13 | msg ("wait(exec()) = %d", wait (child)); | ||
| 14 | msg ("wait(exec()) = %d", wait (child)); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/userprog/wait-twice.ck b/pintos-progos/tests/userprog/wait-twice.ck new file mode 100644 index 0000000..6d53843 --- /dev/null +++ b/pintos-progos/tests/userprog/wait-twice.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (wait-twice) begin | ||
| 7 | (child-simple) run | ||
| 8 | child-simple: exit(81) | ||
| 9 | (wait-twice) wait(exec()) = 81 | ||
| 10 | (wait-twice) wait(exec()) = -1 | ||
| 11 | (wait-twice) end | ||
| 12 | wait-twice: exit(0) | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-bad-fd.c b/pintos-progos/tests/userprog/write-bad-fd.c new file mode 100644 index 0000000..f3b1151 --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-fd.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Tries to write to an invalid fd, | ||
| 2 | which must either fail silently or terminate the process with | ||
| 3 | exit code -1. */ | ||
| 4 | |||
| 5 | #include <limits.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char buf = 123; | ||
| 13 | write (0x01012342, &buf, 1); | ||
| 14 | write (7, &buf, 1); | ||
| 15 | write (2546, &buf, 1); | ||
| 16 | write (-5, &buf, 1); | ||
| 17 | write (-8192, &buf, 1); | ||
| 18 | write (INT_MIN + 1, &buf, 1); | ||
| 19 | write (INT_MAX - 1, &buf, 1); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/userprog/write-bad-fd.ck b/pintos-progos/tests/userprog/write-bad-fd.ck new file mode 100644 index 0000000..8da7a8b --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-fd.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (write-bad-fd) begin | ||
| 7 | (write-bad-fd) end | ||
| 8 | write-bad-fd: exit(0) | ||
| 9 | EOF | ||
| 10 | (write-bad-fd) begin | ||
| 11 | write-bad-fd: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.c b/pintos-progos/tests/userprog/write-bad-ptr.c new file mode 100644 index 0000000..5336479 --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-ptr.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Passes an invalid pointer to the write system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | |||
| 14 | write (handle, (char *) 0x10123420, 123); | ||
| 15 | fail ("should have exited with -1"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.ck b/pintos-progos/tests/userprog/write-bad-ptr.ck new file mode 100644 index 0000000..ad9f399 --- /dev/null +++ b/pintos-progos/tests/userprog/write-bad-ptr.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (write-bad-ptr) begin | ||
| 7 | (write-bad-ptr) open "sample.txt" | ||
| 8 | (write-bad-ptr) end | ||
| 9 | write-bad-ptr: exit(0) | ||
| 10 | EOF | ||
| 11 | (write-bad-ptr) begin | ||
| 12 | (write-bad-ptr) open "sample.txt" | ||
| 13 | write-bad-ptr: exit(-1) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-boundary.c b/pintos-progos/tests/userprog/write-boundary.c new file mode 100644 index 0000000..d2de1d4 --- /dev/null +++ b/pintos-progos/tests/userprog/write-boundary.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Writes data spanning two pages in virtual address space, | ||
| 2 | which must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/userprog/boundary.h" | ||
| 7 | #include "tests/userprog/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | int handle; | ||
| 15 | int byte_cnt; | ||
| 16 | char *sample_p; | ||
| 17 | |||
| 18 | sample_p = copy_string_across_boundary (sample); | ||
| 19 | |||
| 20 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 21 | |||
| 22 | byte_cnt = write (handle, sample_p, sizeof sample - 1); | ||
| 23 | if (byte_cnt != sizeof sample - 1) | ||
| 24 | fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1); | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/userprog/write-boundary.ck b/pintos-progos/tests/userprog/write-boundary.ck new file mode 100644 index 0000000..7883781 --- /dev/null +++ b/pintos-progos/tests/userprog/write-boundary.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (write-boundary) begin | ||
| 7 | (write-boundary) open "sample.txt" | ||
| 8 | (write-boundary) end | ||
| 9 | write-boundary: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-normal.c b/pintos-progos/tests/userprog/write-normal.c new file mode 100644 index 0000000..e0297aa --- /dev/null +++ b/pintos-progos/tests/userprog/write-normal.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Try writing a file in the most normal way. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/userprog/sample.inc" | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle, byte_cnt; | ||
| 12 | |||
| 13 | CHECK (create ("test.txt", sizeof sample - 1), "create \"test.txt\""); | ||
| 14 | CHECK ((handle = open ("test.txt")) > 1, "open \"test.txt\""); | ||
| 15 | |||
| 16 | byte_cnt = write (handle, sample, sizeof sample - 1); | ||
| 17 | if (byte_cnt != sizeof sample - 1) | ||
| 18 | fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1); | ||
| 19 | } | ||
| 20 | |||
diff --git a/pintos-progos/tests/userprog/write-normal.ck b/pintos-progos/tests/userprog/write-normal.ck new file mode 100644 index 0000000..9fa6024 --- /dev/null +++ b/pintos-progos/tests/userprog/write-normal.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (write-normal) begin | ||
| 7 | (write-normal) create "test.txt" | ||
| 8 | (write-normal) open "test.txt" | ||
| 9 | (write-normal) end | ||
| 10 | write-normal: exit(0) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-stdin.c b/pintos-progos/tests/userprog/write-stdin.c new file mode 100644 index 0000000..491ea53 --- /dev/null +++ b/pintos-progos/tests/userprog/write-stdin.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Try writing to fd 0 (stdin), | ||
| 2 | which may just fail or terminate the process with -1 exit | ||
| 3 | code. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char buf = 123; | ||
| 13 | write (0, &buf, 1); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/userprog/write-stdin.ck b/pintos-progos/tests/userprog/write-stdin.ck new file mode 100644 index 0000000..a6caf81 --- /dev/null +++ b/pintos-progos/tests/userprog/write-stdin.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (write-stdin) begin | ||
| 7 | (write-stdin) end | ||
| 8 | write-stdin: exit(0) | ||
| 9 | EOF | ||
| 10 | (write-stdin) begin | ||
| 11 | write-stdin: exit(-1) | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/userprog/write-zero.c b/pintos-progos/tests/userprog/write-zero.c new file mode 100644 index 0000000..d8dac9b --- /dev/null +++ b/pintos-progos/tests/userprog/write-zero.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Try a 0-byte write, which should return 0 without writing | ||
| 2 | anything. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle, byte_cnt; | ||
| 12 | char buf; | ||
| 13 | |||
| 14 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 15 | |||
| 16 | buf = 123; | ||
| 17 | byte_cnt = write (handle, &buf, 0); | ||
| 18 | if (byte_cnt != 0) | ||
| 19 | fail("write() returned %d instead of 0", byte_cnt); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/userprog/write-zero.ck b/pintos-progos/tests/userprog/write-zero.ck new file mode 100644 index 0000000..cc4cd60 --- /dev/null +++ b/pintos-progos/tests/userprog/write-zero.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (write-zero) begin | ||
| 7 | (write-zero) open "sample.txt" | ||
| 8 | (write-zero) end | ||
| 9 | write-zero: exit(0) | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/Grading b/pintos-progos/tests/vm/Grading new file mode 100644 index 0000000..f0c2c13 --- /dev/null +++ b/pintos-progos/tests/vm/Grading | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # Percentage of the testing point total designated for each set of | ||
| 2 | # tests. | ||
| 3 | |||
| 4 | # This project is primarily about virtual memory, but all the previous | ||
| 5 | # functionality should work too, and it's easy to screw it up, thus | ||
| 6 | # the equal weight placed on each. | ||
| 7 | |||
| 8 | 50% tests/vm/Rubric.functionality | ||
| 9 | 15% tests/vm/Rubric.robustness | ||
| 10 | 10% tests/userprog/Rubric.functionality | ||
| 11 | 5% tests/userprog/Rubric.robustness | ||
| 12 | 20% tests/filesys/base/Rubric | ||
diff --git a/pintos-progos/tests/vm/Make.tests b/pintos-progos/tests/vm/Make.tests new file mode 100644 index 0000000..0307ccc --- /dev/null +++ b/pintos-progos/tests/vm/Make.tests | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha \ | ||
| 4 | pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code \ | ||
| 5 | pt-write-code2 pt-grow-stk-sc mmap-read \ | ||
| 6 | mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit \ | ||
| 7 | mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign \ | ||
| 8 | mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove \ | ||
| 9 | mmap-zero mmap-lazy-seq) | ||
| 10 | |||
| 11 | # Deactivated | ||
| 12 | PAGE_TESTS=page-linear page-parallel page-merge-seq \ | ||
| 13 | page-merge-par page-merge-stk page-merge-mm page-shuffle | ||
| 14 | |||
| 15 | tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear \ | ||
| 16 | child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit) | ||
| 17 | |||
| 18 | tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c \ | ||
| 19 | tests/cksum.c tests/lib.c tests/main.c | ||
| 20 | tests/vm/pt-grow-pusha_SRC = tests/vm/pt-grow-pusha.c tests/lib.c \ | ||
| 21 | tests/main.c | ||
| 22 | tests/vm/pt-grow-bad_SRC = tests/vm/pt-grow-bad.c tests/lib.c tests/main.c | ||
| 23 | tests/vm/pt-big-stk-obj_SRC = tests/vm/pt-big-stk-obj.c tests/arc4.c \ | ||
| 24 | tests/cksum.c tests/lib.c tests/main.c | ||
| 25 | tests/vm/pt-bad-addr_SRC = tests/vm/pt-bad-addr.c tests/lib.c tests/main.c | ||
| 26 | tests/vm/pt-bad-read_SRC = tests/vm/pt-bad-read.c tests/lib.c tests/main.c | ||
| 27 | tests/vm/pt-write-code_SRC = tests/vm/pt-write-code.c tests/lib.c tests/main.c | ||
| 28 | tests/vm/pt-write-code2_SRC = tests/vm/pt-write-code-2.c tests/lib.c tests/main.c | ||
| 29 | tests/vm/pt-grow-stk-sc_SRC = tests/vm/pt-grow-stk-sc.c tests/lib.c tests/main.c | ||
| 30 | tests/vm/page-linear_SRC = tests/vm/page-linear.c tests/arc4.c \ | ||
| 31 | tests/lib.c tests/main.c | ||
| 32 | tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c | ||
| 33 | tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c \ | ||
| 34 | tests/lib.c tests/main.c | ||
| 35 | tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \ | ||
| 36 | tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c | ||
| 37 | tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \ | ||
| 38 | tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c | ||
| 39 | tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \ | ||
| 40 | tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c | ||
| 41 | tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c \ | ||
| 42 | tests/cksum.c tests/lib.c tests/main.c | ||
| 43 | tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c | ||
| 44 | tests/vm/mmap-close_SRC = tests/vm/mmap-close.c tests/lib.c tests/main.c | ||
| 45 | tests/vm/mmap-unmap_SRC = tests/vm/mmap-unmap.c tests/lib.c tests/main.c | ||
| 46 | tests/vm/mmap-overlap_SRC = tests/vm/mmap-overlap.c tests/lib.c tests/main.c | ||
| 47 | tests/vm/mmap-twice_SRC = tests/vm/mmap-twice.c tests/lib.c tests/main.c | ||
| 48 | tests/vm/mmap-write_SRC = tests/vm/mmap-write.c tests/lib.c tests/main.c | ||
| 49 | tests/vm/mmap-lazy-seq_SRC = tests/vm/mmap-lazy-seq.c tests/lib.c tests/main.c | ||
| 50 | tests/vm/mmap-exit_SRC = tests/vm/mmap-exit.c tests/lib.c tests/main.c | ||
| 51 | tests/vm/mmap-shuffle_SRC = tests/vm/mmap-shuffle.c tests/arc4.c \ | ||
| 52 | tests/cksum.c tests/lib.c tests/main.c | ||
| 53 | tests/vm/mmap-bad-fd_SRC = tests/vm/mmap-bad-fd.c tests/lib.c tests/main.c | ||
| 54 | tests/vm/mmap-clean_SRC = tests/vm/mmap-clean.c tests/lib.c tests/main.c | ||
| 55 | tests/vm/mmap-inherit_SRC = tests/vm/mmap-inherit.c tests/lib.c tests/main.c | ||
| 56 | tests/vm/mmap-misalign_SRC = tests/vm/mmap-misalign.c tests/lib.c \ | ||
| 57 | tests/main.c | ||
| 58 | tests/vm/mmap-null_SRC = tests/vm/mmap-null.c tests/lib.c tests/main.c | ||
| 59 | tests/vm/mmap-over-code_SRC = tests/vm/mmap-over-code.c tests/lib.c \ | ||
| 60 | tests/main.c | ||
| 61 | tests/vm/mmap-over-data_SRC = tests/vm/mmap-over-data.c tests/lib.c \ | ||
| 62 | tests/main.c | ||
| 63 | tests/vm/mmap-over-stk_SRC = tests/vm/mmap-over-stk.c tests/lib.c tests/main.c | ||
| 64 | tests/vm/mmap-remove_SRC = tests/vm/mmap-remove.c tests/lib.c tests/main.c | ||
| 65 | tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c | ||
| 66 | |||
| 67 | tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c | ||
| 68 | tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c | ||
| 69 | tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \ | ||
| 70 | tests/lib.c | ||
| 71 | tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c | ||
| 72 | tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c | ||
| 73 | tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c | ||
| 74 | |||
| 75 | tests/vm/pt-bad-read_PUTFILES = tests/vm/sample.txt | ||
| 76 | tests/vm/pt-write-code2_PUTFILES = tests/vm/sample.txt | ||
| 77 | tests/vm/mmap-close_PUTFILES = tests/vm/sample.txt | ||
| 78 | tests/vm/mmap-read_PUTFILES = tests/vm/sample.txt | ||
| 79 | tests/vm/mmap-unmap_PUTFILES = tests/vm/sample.txt | ||
| 80 | tests/vm/mmap-twice_PUTFILES = tests/vm/sample.txt | ||
| 81 | tests/vm/mmap-overlap_PUTFILES = tests/vm/zeros | ||
| 82 | tests/vm/mmap-exit_PUTFILES = tests/vm/child-mm-wrt | ||
| 83 | tests/vm/page-parallel_PUTFILES = tests/vm/child-linear | ||
| 84 | tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort | ||
| 85 | tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort | ||
| 86 | tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort | ||
| 87 | tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm | ||
| 88 | tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt | ||
| 89 | tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit | ||
| 90 | tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt | ||
| 91 | tests/vm/mmap-null_PUTFILES = tests/vm/sample.txt | ||
| 92 | tests/vm/mmap-over-code_PUTFILES = tests/vm/sample.txt | ||
| 93 | tests/vm/mmap-over-data_PUTFILES = tests/vm/sample.txt | ||
| 94 | tests/vm/mmap-over-stk_PUTFILES = tests/vm/sample.txt | ||
| 95 | tests/vm/mmap-remove_PUTFILES = tests/vm/sample.txt | ||
| 96 | |||
| 97 | tests/vm/page-linear.output: TIMEOUT = 300 | ||
| 98 | tests/vm/page-shuffle.output: TIMEOUT = 600 | ||
| 99 | tests/vm/mmap-shuffle.output: TIMEOUT = 600 | ||
| 100 | tests/vm/page-merge-seq.output: TIMEOUT = 600 | ||
| 101 | tests/vm/page-merge-par.output: TIMEOUT = 600 | ||
| 102 | |||
| 103 | tests/vm/zeros: | ||
| 104 | dd if=/dev/zero of=$@ bs=1024 count=6 | ||
| 105 | |||
| 106 | clean:: | ||
| 107 | rm -f tests/vm/zeros | ||
diff --git a/pintos-progos/tests/vm/Rubric.functionality b/pintos-progos/tests/vm/Rubric.functionality new file mode 100644 index 0000000..ede0221 --- /dev/null +++ b/pintos-progos/tests/vm/Rubric.functionality | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | Functionality of virtual memory subsystem: | ||
| 2 | - Test stack growth. | ||
| 3 | 3 pt-grow-stack | ||
| 4 | 3 pt-grow-stk-sc | ||
| 5 | 3 pt-big-stk-obj | ||
| 6 | 3 pt-grow-pusha | ||
| 7 | |||
| 8 | - Test "mmap" system call. | ||
| 9 | 2 mmap-read | ||
| 10 | 2 mmap-write | ||
| 11 | 2 mmap-shuffle | ||
| 12 | |||
| 13 | 2 mmap-twice | ||
| 14 | |||
| 15 | 2 mmap-unmap | ||
| 16 | 1 mmap-exit | ||
| 17 | |||
| 18 | 3 mmap-clean | ||
| 19 | |||
| 20 | 2 mmap-close | ||
| 21 | 2 mmap-remove | ||
diff --git a/pintos-progos/tests/vm/Rubric.paging b/pintos-progos/tests/vm/Rubric.paging new file mode 100644 index 0000000..cf1d871 --- /dev/null +++ b/pintos-progos/tests/vm/Rubric.paging | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | - Test paging behavior. | ||
| 2 | 3 page-linear | ||
| 3 | 3 page-parallel | ||
| 4 | 3 page-shuffle | ||
| 5 | 4 page-merge-seq | ||
| 6 | 4 page-merge-par | ||
| 7 | 4 page-merge-mm | ||
| 8 | 4 page-merge-stk | ||
diff --git a/pintos-progos/tests/vm/Rubric.robustness b/pintos-progos/tests/vm/Rubric.robustness new file mode 100644 index 0000000..eca0af4 --- /dev/null +++ b/pintos-progos/tests/vm/Rubric.robustness | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | Robustness of virtual memory subsystem: | ||
| 2 | - Test robustness of page table support. | ||
| 3 | 2 pt-bad-addr | ||
| 4 | 3 pt-bad-read | ||
| 5 | 2 pt-write-code | ||
| 6 | 3 pt-write-code2 | ||
| 7 | 4 pt-grow-bad | ||
| 8 | |||
| 9 | - Test robustness of "mmap" system call. | ||
| 10 | 1 mmap-bad-fd | ||
| 11 | 1 mmap-inherit | ||
| 12 | 1 mmap-null | ||
| 13 | 1 mmap-zero | ||
| 14 | |||
| 15 | 2 mmap-misalign | ||
| 16 | |||
| 17 | 2 mmap-over-code | ||
| 18 | 2 mmap-over-data | ||
| 19 | 2 mmap-over-stk | ||
| 20 | 2 mmap-overlap | ||
| 21 | 4 mmap-lazy-seq | ||
diff --git a/pintos-progos/tests/vm/child-inherit.c b/pintos-progos/tests/vm/child-inherit.c new file mode 100644 index 0000000..d3186a1 --- /dev/null +++ b/pintos-progos/tests/vm/child-inherit.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Child process for mmap-inherit test. | ||
| 2 | Tries to write to a mapping present in the parent. | ||
| 3 | The process must be terminated with -1 exit code. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | memset ((char *) 0x54321000, 0, 4096); | ||
| 14 | fail ("child can modify parent's memory mappings"); | ||
| 15 | } | ||
| 16 | |||
diff --git a/pintos-progos/tests/vm/child-linear.c b/pintos-progos/tests/vm/child-linear.c new file mode 100644 index 0000000..eca3e3f --- /dev/null +++ b/pintos-progos/tests/vm/child-linear.c | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | /* Child process of page-parallel. | ||
| 2 | Encrypts 1 MB of zeros, then decrypts it, and ensures that | ||
| 3 | the zeros are back. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include "tests/arc4.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | const char *test_name = "child-linear"; | ||
| 11 | |||
| 12 | #define SIZE (1024 * 1024) | ||
| 13 | static char buf[SIZE]; | ||
| 14 | |||
| 15 | int | ||
| 16 | main (int argc, char *argv[]) | ||
| 17 | { | ||
| 18 | const char *key = argv[argc - 1]; | ||
| 19 | struct arc4 arc4; | ||
| 20 | size_t i; | ||
| 21 | |||
| 22 | /* Encrypt zeros. */ | ||
| 23 | arc4_init (&arc4, key, strlen (key)); | ||
| 24 | arc4_crypt (&arc4, buf, SIZE); | ||
| 25 | |||
| 26 | /* Decrypt back to zeros. */ | ||
| 27 | arc4_init (&arc4, key, strlen (key)); | ||
| 28 | arc4_crypt (&arc4, buf, SIZE); | ||
| 29 | |||
| 30 | /* Check that it's all zeros. */ | ||
| 31 | for (i = 0; i < SIZE; i++) | ||
| 32 | if (buf[i] != '\0') | ||
| 33 | fail ("byte %zu != 0", i); | ||
| 34 | |||
| 35 | return 0x42; | ||
| 36 | } | ||
diff --git a/pintos-progos/tests/vm/child-mm-wrt.c b/pintos-progos/tests/vm/child-mm-wrt.c new file mode 100644 index 0000000..8419788 --- /dev/null +++ b/pintos-progos/tests/vm/child-mm-wrt.c | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* Child process of mmap-exit. | ||
| 2 | Mmaps a file and writes to it via the mmap'ing, then exits | ||
| 3 | without calling munmap. The data in the mapped region must be | ||
| 4 | written out at program termination. */ | ||
| 5 | |||
| 6 | #include <string.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/vm/sample.inc" | ||
| 9 | #include "tests/lib.h" | ||
| 10 | #include "tests/main.h" | ||
| 11 | |||
| 12 | #define ACTUAL ((void *) 0x10000000) | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | int handle; | ||
| 18 | |||
| 19 | CHECK (create ("sample.txt", sizeof sample), "create \"sample.txt\""); | ||
| 20 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 21 | CHECK (mmap (handle, ACTUAL) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 22 | memcpy (ACTUAL, sample, sizeof sample); | ||
| 23 | } | ||
| 24 | |||
diff --git a/pintos-progos/tests/vm/child-qsort-mm.c b/pintos-progos/tests/vm/child-qsort-mm.c new file mode 100644 index 0000000..db45499 --- /dev/null +++ b/pintos-progos/tests/vm/child-qsort-mm.c | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | /* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort, | ||
| 2 | a multi-pass divide and conquer algorithm. */ | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | #include "tests/vm/qsort.h" | ||
| 9 | |||
| 10 | const char *test_name = "child-qsort-mm"; | ||
| 11 | |||
| 12 | int | ||
| 13 | main (int argc UNUSED, char *argv[]) | ||
| 14 | { | ||
| 15 | int handle; | ||
| 16 | unsigned char *p = (unsigned char *) 0x10000000; | ||
| 17 | |||
| 18 | quiet = true; | ||
| 19 | |||
| 20 | CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); | ||
| 21 | CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]); | ||
| 22 | qsort_bytes (p, 1024 * 128); | ||
| 23 | |||
| 24 | return 80; | ||
| 25 | } | ||
diff --git a/pintos-progos/tests/vm/child-qsort.c b/pintos-progos/tests/vm/child-qsort.c new file mode 100644 index 0000000..355f4eb --- /dev/null +++ b/pintos-progos/tests/vm/child-qsort.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Reads a 128 kB file onto the stack and "sorts" the bytes in | ||
| 2 | it, using quick sort, a multi-pass divide and conquer | ||
| 3 | algorithm. The sorted data is written back to the same file | ||
| 4 | in-place. */ | ||
| 5 | |||
| 6 | #include <debug.h> | ||
| 7 | #include <syscall.h> | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | #include "tests/vm/qsort.h" | ||
| 11 | |||
| 12 | const char *test_name = "child-qsort"; | ||
| 13 | |||
| 14 | int | ||
| 15 | main (int argc UNUSED, char *argv[]) | ||
| 16 | { | ||
| 17 | int handle; | ||
| 18 | unsigned char buf[128 * 1024]; | ||
| 19 | size_t size; | ||
| 20 | |||
| 21 | quiet = true; | ||
| 22 | |||
| 23 | CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); | ||
| 24 | |||
| 25 | size = read (handle, buf, sizeof buf); | ||
| 26 | qsort_bytes (buf, sizeof buf); | ||
| 27 | seek (handle, 0); | ||
| 28 | write (handle, buf, size); | ||
| 29 | close (handle); | ||
| 30 | |||
| 31 | return 72; | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/child-sort.c b/pintos-progos/tests/vm/child-sort.c new file mode 100644 index 0000000..dff2c77 --- /dev/null +++ b/pintos-progos/tests/vm/child-sort.c | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* Reads a 128 kB file into static data and "sorts" the bytes in | ||
| 2 | it, using counting sort, a single-pass algorithm. The sorted | ||
| 3 | data is written back to the same file in-place. */ | ||
| 4 | |||
| 5 | #include <debug.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | const char *test_name = "child-sort"; | ||
| 11 | |||
| 12 | unsigned char buf[128 * 1024]; | ||
| 13 | size_t histogram[256]; | ||
| 14 | |||
| 15 | int | ||
| 16 | main (int argc UNUSED, char *argv[]) | ||
| 17 | { | ||
| 18 | int handle; | ||
| 19 | unsigned char *p; | ||
| 20 | size_t size; | ||
| 21 | size_t i; | ||
| 22 | |||
| 23 | quiet = true; | ||
| 24 | |||
| 25 | CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]); | ||
| 26 | |||
| 27 | size = read (handle, buf, sizeof buf); | ||
| 28 | for (i = 0; i < size; i++) | ||
| 29 | histogram[buf[i]]++; | ||
| 30 | p = buf; | ||
| 31 | for (i = 0; i < sizeof histogram / sizeof *histogram; i++) | ||
| 32 | { | ||
| 33 | size_t j = histogram[i]; | ||
| 34 | while (j-- > 0) | ||
| 35 | *p++ = i; | ||
| 36 | } | ||
| 37 | seek (handle, 0); | ||
| 38 | write (handle, buf, size); | ||
| 39 | close (handle); | ||
| 40 | |||
| 41 | return 123; | ||
| 42 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.c b/pintos-progos/tests/vm/mmap-bad-fd.c new file mode 100644 index 0000000..76a7b50 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-bad-fd.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Tries to mmap an invalid fd, | ||
| 2 | which must either fail silently or terminate the process with | ||
| 3 | exit code -1. */ | ||
| 4 | |||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | CHECK (mmap (0x5678, (void *) 0x10000000) == MAP_FAILED, | ||
| 13 | "try to mmap invalid fd"); | ||
| 14 | } | ||
| 15 | |||
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.ck b/pintos-progos/tests/vm/mmap-bad-fd.ck new file mode 100644 index 0000000..f3f58d5 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-bad-fd.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF', <<'EOF']); | ||
| 6 | (mmap-bad-fd) begin | ||
| 7 | (mmap-bad-fd) try to mmap invalid fd | ||
| 8 | (mmap-bad-fd) end | ||
| 9 | mmap-bad-fd: exit(0) | ||
| 10 | EOF | ||
| 11 | (mmap-bad-fd) begin | ||
| 12 | (mmap-bad-fd) try to mmap invalid fd | ||
| 13 | mmap-bad-fd: exit(-1) | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-clean.c b/pintos-progos/tests/vm/mmap-clean.c new file mode 100644 index 0000000..ea1dc9c --- /dev/null +++ b/pintos-progos/tests/vm/mmap-clean.c | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | /* Verifies that mmap'd regions are only written back on munmap | ||
| 2 | if the data was actually modified in memory. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | static const char overwrite[] = "Now is the time for all good..."; | ||
| 14 | static char buffer[sizeof sample - 1]; | ||
| 15 | char *actual = (char *) 0x54321000; | ||
| 16 | int handle; | ||
| 17 | mapid_t map; | ||
| 18 | |||
| 19 | /* Open file, map, verify data. */ | ||
| 20 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 21 | CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 22 | if (memcmp (actual, sample, strlen (sample))) | ||
| 23 | fail ("read of mmap'd file reported bad data"); | ||
| 24 | |||
| 25 | /* Modify file. */ | ||
| 26 | CHECK (write (handle, overwrite, strlen (overwrite)) | ||
| 27 | == (int) strlen (overwrite), | ||
| 28 | "write \"sample.txt\""); | ||
| 29 | |||
| 30 | /* Close mapping. Data should not be written back, because we | ||
| 31 | didn't modify it via the mapping. */ | ||
| 32 | msg ("munmap \"sample.txt\""); | ||
| 33 | munmap (map); | ||
| 34 | |||
| 35 | /* Read file back. */ | ||
| 36 | msg ("seek \"sample.txt\""); | ||
| 37 | seek (handle, 0); | ||
| 38 | CHECK (read (handle, buffer, sizeof buffer) == sizeof buffer, | ||
| 39 | "read \"sample.txt\""); | ||
| 40 | |||
| 41 | /* Verify that file overwrite worked. */ | ||
| 42 | if (memcmp (buffer, overwrite, strlen (overwrite)) | ||
| 43 | || memcmp (buffer + strlen (overwrite), sample + strlen (overwrite), | ||
| 44 | strlen (sample) - strlen (overwrite))) | ||
| 45 | { | ||
| 46 | if (!memcmp (buffer, sample, strlen (sample))) | ||
| 47 | fail ("munmap wrote back clean page"); | ||
| 48 | else | ||
| 49 | fail ("read surprising data from file"); | ||
| 50 | } | ||
| 51 | else | ||
| 52 | msg ("file change was retained after munmap"); | ||
| 53 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-clean.ck b/pintos-progos/tests/vm/mmap-clean.ck new file mode 100644 index 0000000..1666d6c --- /dev/null +++ b/pintos-progos/tests/vm/mmap-clean.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-clean) begin | ||
| 7 | (mmap-clean) open "sample.txt" | ||
| 8 | (mmap-clean) mmap "sample.txt" | ||
| 9 | (mmap-clean) write "sample.txt" | ||
| 10 | (mmap-clean) munmap "sample.txt" | ||
| 11 | (mmap-clean) seek "sample.txt" | ||
| 12 | (mmap-clean) read "sample.txt" | ||
| 13 | (mmap-clean) file change was retained after munmap | ||
| 14 | (mmap-clean) end | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-close.c b/pintos-progos/tests/vm/mmap-close.c new file mode 100644 index 0000000..d016ee3 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-close.c | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* Verifies that memory mappings persist after file close. */ | ||
| 2 | |||
| 3 | #include <string.h> | ||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/arc4.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | #define ACTUAL ((void *) 0x10000000) | ||
| 11 | |||
| 12 | void | ||
| 13 | test_main (void) | ||
| 14 | { | ||
| 15 | int handle; | ||
| 16 | mapid_t map; | ||
| 17 | |||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 20 | |||
| 21 | close (handle); | ||
| 22 | |||
| 23 | if (memcmp (ACTUAL, sample, strlen (sample))) | ||
| 24 | fail ("read of mmap'd file reported bad data"); | ||
| 25 | |||
| 26 | munmap (map); | ||
| 27 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-close.ck b/pintos-progos/tests/vm/mmap-close.ck new file mode 100644 index 0000000..d15e41a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-close.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-close) begin | ||
| 7 | (mmap-close) open "sample.txt" | ||
| 8 | (mmap-close) mmap "sample.txt" | ||
| 9 | (mmap-close) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-exit.c b/pintos-progos/tests/vm/mmap-exit.c new file mode 100644 index 0000000..7a2278a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-exit.c | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | /* Executes child-mm-wrt and verifies that the writes that should | ||
| 2 | have occurred really did. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | pid_t child; | ||
| 13 | |||
| 14 | /* Make child write file. */ | ||
| 15 | quiet = true; | ||
| 16 | CHECK ((child = exec ("child-mm-wrt")) != -1, "exec \"child-mm-wrt\""); | ||
| 17 | CHECK (wait (child) == 0, "wait for child (should return 0)"); | ||
| 18 | quiet = false; | ||
| 19 | |||
| 20 | /* Check file contents. */ | ||
| 21 | check_file ("sample.txt", sample, sizeof sample); | ||
| 22 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-exit.ck b/pintos-progos/tests/vm/mmap-exit.ck new file mode 100644 index 0000000..457d34a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-exit.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-exit) begin | ||
| 7 | (child-mm-wrt) begin | ||
| 8 | (child-mm-wrt) create "sample.txt" | ||
| 9 | (child-mm-wrt) open "sample.txt" | ||
| 10 | (child-mm-wrt) mmap "sample.txt" | ||
| 11 | (child-mm-wrt) end | ||
| 12 | (mmap-exit) open "sample.txt" for verification | ||
| 13 | (mmap-exit) verified contents of "sample.txt" | ||
| 14 | (mmap-exit) close "sample.txt" | ||
| 15 | (mmap-exit) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-inherit.c b/pintos-progos/tests/vm/mmap-inherit.c new file mode 100644 index 0000000..7fa9607 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-inherit.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Maps a file into memory and runs child-inherit to verify that | ||
| 2 | mappings are not inherited. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *actual = (char *) 0x54321000; | ||
| 14 | int handle; | ||
| 15 | pid_t child; | ||
| 16 | |||
| 17 | /* Open file, map, verify data. */ | ||
| 18 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 19 | CHECK (mmap (handle, actual) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 20 | if (memcmp (actual, sample, strlen (sample))) | ||
| 21 | fail ("read of mmap'd file reported bad data"); | ||
| 22 | |||
| 23 | /* Spawn child and wait. */ | ||
| 24 | CHECK ((child = exec ("child-inherit")) != -1, "exec \"child-inherit\""); | ||
| 25 | quiet = true; | ||
| 26 | CHECK (wait (child) == -1, "wait for child (should return -1)"); | ||
| 27 | quiet = false; | ||
| 28 | |||
| 29 | /* Verify data again. */ | ||
| 30 | CHECK (!memcmp (actual, sample, strlen (sample)), | ||
| 31 | "checking that mmap'd file still has same data"); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-inherit.ck b/pintos-progos/tests/vm/mmap-inherit.ck new file mode 100644 index 0000000..c54638a --- /dev/null +++ b/pintos-progos/tests/vm/mmap-inherit.ck | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (mmap-inherit) begin | ||
| 7 | (mmap-inherit) open "sample.txt" | ||
| 8 | (mmap-inherit) mmap "sample.txt" | ||
| 9 | (mmap-inherit) exec "child-inherit" | ||
| 10 | (child-inherit) begin | ||
| 11 | child-inherit: exit(-1) | ||
| 12 | (mmap-inherit) checking that mmap'd file still has same data | ||
| 13 | (mmap-inherit) end | ||
| 14 | mmap-inherit: exit(0) | ||
| 15 | EOF | ||
| 16 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.c b/pintos-progos/tests/vm/mmap-lazy-seq.c new file mode 100644 index 0000000..b8f07bd --- /dev/null +++ b/pintos-progos/tests/vm/mmap-lazy-seq.c | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | /* Create a large file, and mmap it several times, writing to | ||
| 2 | different pages. Then unmaps the file, and reads the data back | ||
| 3 | to verify */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/vm/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | /* Offset needs to be larger or equal to page size */ | ||
| 12 | #define OFFSET(i) (8192*(i)) | ||
| 13 | /* Number of times file is mmapped */ | ||
| 14 | #define N (8) | ||
| 15 | /* Size of file */ | ||
| 16 | #define FILE_SIZE (1024*1024) | ||
| 17 | /* Address for mmap */ | ||
| 18 | #define ACTUAL(i) ((void *) (0x10000000 + (i)*FILE_SIZE)) | ||
| 19 | |||
| 20 | |||
| 21 | void | ||
| 22 | test_main (void) | ||
| 23 | { | ||
| 24 | int i; | ||
| 25 | int handle; | ||
| 26 | mapid_t map[N]; | ||
| 27 | char buf[1024]; | ||
| 28 | /* create file */ | ||
| 29 | CHECK (create ("sample.txt", FILE_SIZE), "create \"sample.txt\""); | ||
| 30 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 31 | /* mmap */ | ||
| 32 | for (i = 0; i < N; i++) { | ||
| 33 | CHECK ((map[i] = mmap (handle, ACTUAL(i))) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 34 | } | ||
| 35 | /* write */ | ||
| 36 | for (i = 0; i < N; i++) { | ||
| 37 | memcpy (buf, ACTUAL(i)+OFFSET(i+N), 1024); /* not checked */ | ||
| 38 | memcpy (ACTUAL(i)+OFFSET(i), sample, strlen (sample)); | ||
| 39 | } | ||
| 40 | /* munmap */ | ||
| 41 | for (i = 0; i < N; i++) { | ||
| 42 | munmap (map[i]); | ||
| 43 | } | ||
| 44 | /* Read back via read(). */ | ||
| 45 | for (i = 0; i < N; i++) { | ||
| 46 | seek (handle, OFFSET(i)); | ||
| 47 | read (handle, buf, strlen (sample)); | ||
| 48 | CHECK (!memcmp (buf, sample, strlen (sample)), | ||
| 49 | "compare read data against written data"); | ||
| 50 | } | ||
| 51 | close (handle); | ||
| 52 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.ck b/pintos-progos/tests/vm/mmap-lazy-seq.ck new file mode 100644 index 0000000..dd0e240 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-lazy-seq.ck | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-lazy-seq) begin | ||
| 7 | (mmap-lazy-seq) create "sample.txt" | ||
| 8 | (mmap-lazy-seq) open "sample.txt" | ||
| 9 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 10 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 11 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 12 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 13 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 14 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 15 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 16 | (mmap-lazy-seq) mmap "sample.txt" | ||
| 17 | (mmap-lazy-seq) compare read data against written data | ||
| 18 | (mmap-lazy-seq) compare read data against written data | ||
| 19 | (mmap-lazy-seq) compare read data against written data | ||
| 20 | (mmap-lazy-seq) compare read data against written data | ||
| 21 | (mmap-lazy-seq) compare read data against written data | ||
| 22 | (mmap-lazy-seq) compare read data against written data | ||
| 23 | (mmap-lazy-seq) compare read data against written data | ||
| 24 | (mmap-lazy-seq) compare read data against written data | ||
| 25 | (mmap-lazy-seq) end | ||
| 26 | EOF | ||
| 27 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-misalign.c b/pintos-progos/tests/vm/mmap-misalign.c new file mode 100644 index 0000000..34141a9 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-misalign.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Verifies that misaligned memory mappings are disallowed. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | |||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | CHECK (mmap (handle, (void *) 0x10001234) == MAP_FAILED, | ||
| 14 | "try to mmap at misaligned address"); | ||
| 15 | } | ||
| 16 | |||
diff --git a/pintos-progos/tests/vm/mmap-misalign.ck b/pintos-progos/tests/vm/mmap-misalign.ck new file mode 100644 index 0000000..145a2e8 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-misalign.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-misalign) begin | ||
| 7 | (mmap-misalign) open "sample.txt" | ||
| 8 | (mmap-misalign) try to mmap at misaligned address | ||
| 9 | (mmap-misalign) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-null.c b/pintos-progos/tests/vm/mmap-null.c new file mode 100644 index 0000000..f8ef075 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-null.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Verifies that memory mappings at address 0 are disallowed. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | |||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | CHECK (mmap (handle, NULL) == MAP_FAILED, "try to mmap at address 0"); | ||
| 14 | } | ||
| 15 | |||
diff --git a/pintos-progos/tests/vm/mmap-null.ck b/pintos-progos/tests/vm/mmap-null.ck new file mode 100644 index 0000000..aacdd65 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-null.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-null) begin | ||
| 7 | (mmap-null) open "sample.txt" | ||
| 8 | (mmap-null) try to mmap at address 0 | ||
| 9 | (mmap-null) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-over-code.c b/pintos-progos/tests/vm/mmap-over-code.c new file mode 100644 index 0000000..d3619a3 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-code.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Verifies that mapping over the code segment is disallowed. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | uintptr_t test_main_page = ROUND_DOWN ((uintptr_t) test_main, 4096); | ||
| 13 | int handle; | ||
| 14 | |||
| 15 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 16 | CHECK (mmap (handle, (void *) test_main_page) == MAP_FAILED, | ||
| 17 | "try to mmap over code segment"); | ||
| 18 | } | ||
| 19 | |||
diff --git a/pintos-progos/tests/vm/mmap-over-code.ck b/pintos-progos/tests/vm/mmap-over-code.ck new file mode 100644 index 0000000..b5b23c7 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-code.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-over-code) begin | ||
| 7 | (mmap-over-code) open "sample.txt" | ||
| 8 | (mmap-over-code) try to mmap over code segment | ||
| 9 | (mmap-over-code) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-over-data.c b/pintos-progos/tests/vm/mmap-over-data.c new file mode 100644 index 0000000..9ea5d49 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-data.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Verifies that mapping over the data segment is disallowed. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | static char x; | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | uintptr_t x_page = ROUND_DOWN ((uintptr_t) &x, 4096); | ||
| 15 | int handle; | ||
| 16 | |||
| 17 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 18 | CHECK (mmap (handle, (void *) x_page) == MAP_FAILED, | ||
| 19 | "try to mmap over data segment"); | ||
| 20 | } | ||
| 21 | |||
diff --git a/pintos-progos/tests/vm/mmap-over-data.ck b/pintos-progos/tests/vm/mmap-over-data.ck new file mode 100644 index 0000000..98770cc --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-data.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-over-data) begin | ||
| 7 | (mmap-over-data) open "sample.txt" | ||
| 8 | (mmap-over-data) try to mmap over data segment | ||
| 9 | (mmap-over-data) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-over-stk.c b/pintos-progos/tests/vm/mmap-over-stk.c new file mode 100644 index 0000000..4e241e8 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-stk.c | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* Verifies that mapping over the stack segment is disallowed. */ | ||
| 2 | |||
| 3 | #include <stdint.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | int handle; | ||
| 13 | uintptr_t handle_page = ROUND_DOWN ((uintptr_t) &handle, 4096); | ||
| 14 | |||
| 15 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 16 | CHECK (mmap (handle, (void *) handle_page) == MAP_FAILED, | ||
| 17 | "try to mmap over stack segment"); | ||
| 18 | } | ||
| 19 | |||
diff --git a/pintos-progos/tests/vm/mmap-over-stk.ck b/pintos-progos/tests/vm/mmap-over-stk.ck new file mode 100644 index 0000000..e6880cf --- /dev/null +++ b/pintos-progos/tests/vm/mmap-over-stk.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-over-stk) begin | ||
| 7 | (mmap-over-stk) open "sample.txt" | ||
| 8 | (mmap-over-stk) try to mmap over stack segment | ||
| 9 | (mmap-over-stk) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-overlap.c b/pintos-progos/tests/vm/mmap-overlap.c new file mode 100644 index 0000000..668ae5f --- /dev/null +++ b/pintos-progos/tests/vm/mmap-overlap.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Verifies that overlapping memory mappings are disallowed. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/vm/sample.inc" | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | char *start = (char *) 0x10000000; | ||
| 12 | int fd[2]; | ||
| 13 | |||
| 14 | CHECK ((fd[0] = open ("zeros")) > 1, "open \"zeros\" once"); | ||
| 15 | CHECK (mmap (fd[0], start) != MAP_FAILED, "mmap \"zeros\""); | ||
| 16 | CHECK ((fd[1] = open ("zeros")) > 1 && fd[0] != fd[1], | ||
| 17 | "open \"zeros\" again"); | ||
| 18 | CHECK (mmap (fd[1], start + 4096) == MAP_FAILED, | ||
| 19 | "try to mmap \"zeros\" again"); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-overlap.ck b/pintos-progos/tests/vm/mmap-overlap.ck new file mode 100644 index 0000000..f13801e --- /dev/null +++ b/pintos-progos/tests/vm/mmap-overlap.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-overlap) begin | ||
| 7 | (mmap-overlap) open "zeros" once | ||
| 8 | (mmap-overlap) mmap "zeros" | ||
| 9 | (mmap-overlap) open "zeros" again | ||
| 10 | (mmap-overlap) try to mmap "zeros" again | ||
| 11 | (mmap-overlap) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-read.c b/pintos-progos/tests/vm/mmap-read.c new file mode 100644 index 0000000..c0f23a1 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-read.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Uses a memory mapping to read a file. */ | ||
| 2 | |||
| 3 | #include <string.h> | ||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | char *actual = (char *) 0x10000000; | ||
| 13 | int handle; | ||
| 14 | mapid_t map; | ||
| 15 | size_t i; | ||
| 16 | |||
| 17 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 18 | CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 19 | |||
| 20 | /* Check that data is correct. */ | ||
| 21 | if (memcmp (actual, sample, strlen (sample))) | ||
| 22 | fail ("read of mmap'd file reported bad data"); | ||
| 23 | |||
| 24 | /* Verify that data is followed by zeros. */ | ||
| 25 | for (i = strlen (sample); i < 4096; i++) | ||
| 26 | if (actual[i] != 0) | ||
| 27 | fail ("byte %zu of mmap'd region has value %02hhx (should be 0)", | ||
| 28 | i, actual[i]); | ||
| 29 | |||
| 30 | munmap (map); | ||
| 31 | close (handle); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-read.ck b/pintos-progos/tests/vm/mmap-read.ck new file mode 100644 index 0000000..95ab790 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-read.ck | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-read) begin | ||
| 7 | (mmap-read) open "sample.txt" | ||
| 8 | (mmap-read) mmap "sample.txt" | ||
| 9 | (mmap-read) end | ||
| 10 | EOF | ||
| 11 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-remove.c b/pintos-progos/tests/vm/mmap-remove.c new file mode 100644 index 0000000..5f7444d --- /dev/null +++ b/pintos-progos/tests/vm/mmap-remove.c | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | /* Deletes and closes file that is mapped into memory | ||
| 2 | and verifies that it can still be read through the mapping. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *actual = (char *) 0x10000000; | ||
| 14 | int handle; | ||
| 15 | mapid_t map; | ||
| 16 | size_t i; | ||
| 17 | |||
| 18 | /* Map file. */ | ||
| 19 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 20 | CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 21 | |||
| 22 | /* Close file and delete it. */ | ||
| 23 | close (handle); | ||
| 24 | CHECK (remove ("sample.txt"), "remove \"sample.txt\""); | ||
| 25 | CHECK (open ("sample.txt") == -1, "try to open \"sample.txt\""); | ||
| 26 | |||
| 27 | /* Create a new file in hopes of overwriting data from the old | ||
| 28 | one, in case the file system has incorrectly freed the | ||
| 29 | file's data. */ | ||
| 30 | CHECK (create ("another", 4096 * 10), "create \"another\""); | ||
| 31 | |||
| 32 | /* Check that mapped data is correct. */ | ||
| 33 | if (memcmp (actual, sample, strlen (sample))) | ||
| 34 | fail ("read of mmap'd file reported bad data"); | ||
| 35 | |||
| 36 | /* Verify that data is followed by zeros. */ | ||
| 37 | for (i = strlen (sample); i < 4096; i++) | ||
| 38 | if (actual[i] != 0) | ||
| 39 | fail ("byte %zu of mmap'd region has value %02hhx (should be 0)", | ||
| 40 | i, actual[i]); | ||
| 41 | |||
| 42 | munmap (map); | ||
| 43 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-remove.ck b/pintos-progos/tests/vm/mmap-remove.ck new file mode 100644 index 0000000..d3cc938 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-remove.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-remove) begin | ||
| 7 | (mmap-remove) open "sample.txt" | ||
| 8 | (mmap-remove) mmap "sample.txt" | ||
| 9 | (mmap-remove) remove "sample.txt" | ||
| 10 | (mmap-remove) try to open "sample.txt" | ||
| 11 | (mmap-remove) create "another" | ||
| 12 | (mmap-remove) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-shuffle.c b/pintos-progos/tests/vm/mmap-shuffle.c new file mode 100644 index 0000000..29921ad --- /dev/null +++ b/pintos-progos/tests/vm/mmap-shuffle.c | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* Creates a 128 kB file and repeatedly shuffles data in it | ||
| 2 | through a memory mapping. */ | ||
| 3 | |||
| 4 | #include <stdio.h> | ||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/arc4.h" | ||
| 8 | #include "tests/cksum.h" | ||
| 9 | #include "tests/lib.h" | ||
| 10 | #include "tests/main.h" | ||
| 11 | |||
| 12 | #define SIZE (128 * 1024) | ||
| 13 | |||
| 14 | static char *buf = (char *) 0x10000000; | ||
| 15 | |||
| 16 | void | ||
| 17 | test_main (void) | ||
| 18 | { | ||
| 19 | size_t i; | ||
| 20 | int handle; | ||
| 21 | |||
| 22 | /* Create file, mmap. */ | ||
| 23 | CHECK (create ("buffer", SIZE), "create \"buffer\""); | ||
| 24 | CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); | ||
| 25 | CHECK (mmap (handle, buf) != MAP_FAILED, "mmap \"buffer\""); | ||
| 26 | |||
| 27 | /* Initialize. */ | ||
| 28 | for (i = 0; i < SIZE; i++) | ||
| 29 | buf[i] = i * 257; | ||
| 30 | msg ("init: cksum=%lu", cksum (buf, SIZE)); | ||
| 31 | |||
| 32 | /* Shuffle repeatedly. */ | ||
| 33 | for (i = 0; i < 10; i++) | ||
| 34 | { | ||
| 35 | shuffle (buf, SIZE, 1); | ||
| 36 | msg ("shuffle %zu: cksum=%lu", i, cksum (buf, SIZE)); | ||
| 37 | } | ||
| 38 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-shuffle.ck b/pintos-progos/tests/vm/mmap-shuffle.ck new file mode 100644 index 0000000..c158301 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-shuffle.ck | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::cksum; | ||
| 6 | use tests::lib; | ||
| 7 | |||
| 8 | my ($init, @shuffle); | ||
| 9 | if (1) { | ||
| 10 | # Use precalculated values. | ||
| 11 | $init = 3115322833; | ||
| 12 | @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467, | ||
| 13 | 3022003332, 3614934266, 2704001777, 735775156, 1864109763); | ||
| 14 | } else { | ||
| 15 | # Recalculate values. | ||
| 16 | my ($buf) = ""; | ||
| 17 | for my $i (0...128 * 1024 - 1) { | ||
| 18 | $buf .= chr (($i * 257) & 0xff); | ||
| 19 | } | ||
| 20 | $init = cksum ($buf); | ||
| 21 | |||
| 22 | random_init (0); | ||
| 23 | for my $i (1...10) { | ||
| 24 | $buf = shuffle ($buf, length ($buf), 1); | ||
| 25 | push (@shuffle, cksum ($buf)); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]); | ||
| 30 | (mmap-shuffle) begin | ||
| 31 | (mmap-shuffle) create "buffer" | ||
| 32 | (mmap-shuffle) open "buffer" | ||
| 33 | (mmap-shuffle) mmap "buffer" | ||
| 34 | (mmap-shuffle) init: cksum=$init | ||
| 35 | (mmap-shuffle) shuffle 0: cksum=$shuffle[0] | ||
| 36 | (mmap-shuffle) shuffle 1: cksum=$shuffle[1] | ||
| 37 | (mmap-shuffle) shuffle 2: cksum=$shuffle[2] | ||
| 38 | (mmap-shuffle) shuffle 3: cksum=$shuffle[3] | ||
| 39 | (mmap-shuffle) shuffle 4: cksum=$shuffle[4] | ||
| 40 | (mmap-shuffle) shuffle 5: cksum=$shuffle[5] | ||
| 41 | (mmap-shuffle) shuffle 6: cksum=$shuffle[6] | ||
| 42 | (mmap-shuffle) shuffle 7: cksum=$shuffle[7] | ||
| 43 | (mmap-shuffle) shuffle 8: cksum=$shuffle[8] | ||
| 44 | (mmap-shuffle) shuffle 9: cksum=$shuffle[9] | ||
| 45 | (mmap-shuffle) end | ||
| 46 | EOF | ||
| 47 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-twice.c b/pintos-progos/tests/vm/mmap-twice.c new file mode 100644 index 0000000..d277a37 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-twice.c | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | /* Maps the same file into memory twice and verifies that the | ||
| 2 | same data is readable in both. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include <syscall.h> | ||
| 6 | #include "tests/vm/sample.inc" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *actual[2] = {(char *) 0x10000000, (char *) 0x20000000}; | ||
| 14 | size_t i; | ||
| 15 | int handle[2]; | ||
| 16 | |||
| 17 | for (i = 0; i < 2; i++) | ||
| 18 | { | ||
| 19 | CHECK ((handle[i] = open ("sample.txt")) > 1, | ||
| 20 | "open \"sample.txt\" #%zu", i); | ||
| 21 | CHECK (mmap (handle[i], actual[i]) != MAP_FAILED, | ||
| 22 | "mmap \"sample.txt\" #%zu at %p", i, (void *) actual[i]); | ||
| 23 | } | ||
| 24 | |||
| 25 | for (i = 0; i < 2; i++) | ||
| 26 | CHECK (!memcmp (actual[i], sample, strlen (sample)), | ||
| 27 | "compare mmap'd file %zu against data", i); | ||
| 28 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-twice.ck b/pintos-progos/tests/vm/mmap-twice.ck new file mode 100644 index 0000000..05e9724 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-twice.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-twice) begin | ||
| 7 | (mmap-twice) open "sample.txt" #0 | ||
| 8 | (mmap-twice) mmap "sample.txt" #0 at 0x10000000 | ||
| 9 | (mmap-twice) open "sample.txt" #1 | ||
| 10 | (mmap-twice) mmap "sample.txt" #1 at 0x20000000 | ||
| 11 | (mmap-twice) compare mmap'd file 0 against data | ||
| 12 | (mmap-twice) compare mmap'd file 1 against data | ||
| 13 | (mmap-twice) end | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-unmap.c b/pintos-progos/tests/vm/mmap-unmap.c new file mode 100644 index 0000000..d35a79e --- /dev/null +++ b/pintos-progos/tests/vm/mmap-unmap.c | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | /* Maps and unmaps a file and verifies that the mapped region is | ||
| 2 | inaccessible afterward. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/vm/sample.inc" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | #define ACTUAL ((void *) 0x10000000) | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | int handle; | ||
| 15 | mapid_t map; | ||
| 16 | |||
| 17 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 18 | CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 19 | |||
| 20 | munmap (map); | ||
| 21 | |||
| 22 | fail ("unmapped memory is readable (%d)", *(int *) ACTUAL); | ||
| 23 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-unmap.ck b/pintos-progos/tests/vm/mmap-unmap.ck new file mode 100644 index 0000000..119658c --- /dev/null +++ b/pintos-progos/tests/vm/mmap-unmap.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::vm::process_death; | ||
| 6 | |||
| 7 | check_process_death ('mmap-unmap'); | ||
diff --git a/pintos-progos/tests/vm/mmap-write.c b/pintos-progos/tests/vm/mmap-write.c new file mode 100644 index 0000000..46e8043 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-write.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* Writes to a file through a mapping, and unmaps the file, | ||
| 2 | then reads the data in the file back using the read system | ||
| 3 | call to verify. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/vm/sample.inc" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | #define ACTUAL ((void *) 0x10000000) | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | int handle; | ||
| 17 | mapid_t map; | ||
| 18 | char buf[1024]; | ||
| 19 | |||
| 20 | /* Write file via mmap. */ | ||
| 21 | CHECK (create ("sample.txt", strlen (sample)), "create \"sample.txt\""); | ||
| 22 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 23 | CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\""); | ||
| 24 | memcpy (ACTUAL, sample, strlen (sample)); | ||
| 25 | munmap (map); | ||
| 26 | |||
| 27 | /* Read back via read(). */ | ||
| 28 | read (handle, buf, strlen (sample)); | ||
| 29 | CHECK (!memcmp (buf, sample, strlen (sample)), | ||
| 30 | "compare read data against written data"); | ||
| 31 | close (handle); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/mmap-write.ck b/pintos-progos/tests/vm/mmap-write.ck new file mode 100644 index 0000000..d2c9cc5 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-write.ck | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (mmap-write) begin | ||
| 7 | (mmap-write) create "sample.txt" | ||
| 8 | (mmap-write) open "sample.txt" | ||
| 9 | (mmap-write) mmap "sample.txt" | ||
| 10 | (mmap-write) compare read data against written data | ||
| 11 | (mmap-write) end | ||
| 12 | EOF | ||
| 13 | pass; | ||
diff --git a/pintos-progos/tests/vm/mmap-zero.c b/pintos-progos/tests/vm/mmap-zero.c new file mode 100644 index 0000000..368b759 --- /dev/null +++ b/pintos-progos/tests/vm/mmap-zero.c | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | /* Tries to map a zero-length file, which may or may not work but | ||
| 2 | should not terminate the process or crash. | ||
| 3 | Then dereferences the address that we tried to map, | ||
| 4 | and the process must be terminated with -1 exit code. */ | ||
| 5 | |||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char *data = (char *) 0x7f000000; | ||
| 14 | int handle; | ||
| 15 | |||
| 16 | CHECK (create ("empty", 0), "create empty file \"empty\""); | ||
| 17 | CHECK ((handle = open ("empty")) > 1, "open \"empty\""); | ||
| 18 | |||
| 19 | /* Calling mmap() might succeed or fail. We don't care. */ | ||
| 20 | msg ("mmap \"empty\""); | ||
| 21 | mmap (handle, data); | ||
| 22 | |||
| 23 | /* Regardless of whether the call worked, *data should cause | ||
| 24 | the process to be terminated. */ | ||
| 25 | fail ("unmapped memory is readable (%d)", *data); | ||
| 26 | } | ||
| 27 | |||
diff --git a/pintos-progos/tests/vm/mmap-zero.ck b/pintos-progos/tests/vm/mmap-zero.ck new file mode 100644 index 0000000..6033d5d --- /dev/null +++ b/pintos-progos/tests/vm/mmap-zero.ck | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (mmap-zero) begin | ||
| 7 | (mmap-zero) create empty file "empty" | ||
| 8 | (mmap-zero) open "empty" | ||
| 9 | (mmap-zero) mmap "empty" | ||
| 10 | mmap-zero: exit(-1) | ||
| 11 | EOF | ||
| 12 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-linear.c b/pintos-progos/tests/vm/page-linear.c new file mode 100644 index 0000000..652a47b --- /dev/null +++ b/pintos-progos/tests/vm/page-linear.c | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | /* Encrypts, then decrypts, 2 MB of memory and verifies that the | ||
| 2 | values are as they should be. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/lib.h" | ||
| 7 | #include "tests/main.h" | ||
| 8 | |||
| 9 | #define SIZE (2 * 1024 * 1024) | ||
| 10 | |||
| 11 | static char buf[SIZE]; | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | struct arc4 arc4; | ||
| 17 | size_t i; | ||
| 18 | |||
| 19 | /* Initialize to 0x5a. */ | ||
| 20 | msg ("initialize"); | ||
| 21 | memset (buf, 0x5a, sizeof buf); | ||
| 22 | |||
| 23 | /* Check that it's all 0x5a. */ | ||
| 24 | msg ("read pass"); | ||
| 25 | for (i = 0; i < SIZE; i++) | ||
| 26 | if (buf[i] != 0x5a) | ||
| 27 | fail ("byte %zu != 0x5a", i); | ||
| 28 | |||
| 29 | /* Encrypt zeros. */ | ||
| 30 | msg ("read/modify/write pass one"); | ||
| 31 | arc4_init (&arc4, "foobar", 6); | ||
| 32 | arc4_crypt (&arc4, buf, SIZE); | ||
| 33 | |||
| 34 | /* Decrypt back to zeros. */ | ||
| 35 | msg ("read/modify/write pass two"); | ||
| 36 | arc4_init (&arc4, "foobar", 6); | ||
| 37 | arc4_crypt (&arc4, buf, SIZE); | ||
| 38 | |||
| 39 | /* Check that it's all 0x5a. */ | ||
| 40 | msg ("read pass"); | ||
| 41 | for (i = 0; i < SIZE; i++) | ||
| 42 | if (buf[i] != 0x5a) | ||
| 43 | fail ("byte %zu != 0x5a", i); | ||
| 44 | } | ||
diff --git a/pintos-progos/tests/vm/page-linear.ck b/pintos-progos/tests/vm/page-linear.ck new file mode 100644 index 0000000..dcbc884 --- /dev/null +++ b/pintos-progos/tests/vm/page-linear.ck | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-linear) begin | ||
| 7 | (page-linear) initialize | ||
| 8 | (page-linear) read pass | ||
| 9 | (page-linear) read/modify/write pass one | ||
| 10 | (page-linear) read/modify/write pass two | ||
| 11 | (page-linear) read pass | ||
| 12 | (page-linear) end | ||
| 13 | EOF | ||
| 14 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-mm.c b/pintos-progos/tests/vm/page-merge-mm.c new file mode 100644 index 0000000..908c71c --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-mm.c | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #include "tests/main.h" | ||
| 2 | #include "tests/vm/parallel-merge.h" | ||
| 3 | |||
| 4 | void | ||
| 5 | test_main (void) | ||
| 6 | { | ||
| 7 | parallel_merge ("child-qsort-mm", 80); | ||
| 8 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-mm.ck b/pintos-progos/tests/vm/page-merge-mm.ck new file mode 100644 index 0000000..74fa980 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-mm.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-mm) begin | ||
| 7 | (page-merge-mm) init | ||
| 8 | (page-merge-mm) sort chunk 0 | ||
| 9 | (page-merge-mm) sort chunk 1 | ||
| 10 | (page-merge-mm) sort chunk 2 | ||
| 11 | (page-merge-mm) sort chunk 3 | ||
| 12 | (page-merge-mm) sort chunk 4 | ||
| 13 | (page-merge-mm) sort chunk 5 | ||
| 14 | (page-merge-mm) sort chunk 6 | ||
| 15 | (page-merge-mm) sort chunk 7 | ||
| 16 | (page-merge-mm) wait for child 0 | ||
| 17 | (page-merge-mm) wait for child 1 | ||
| 18 | (page-merge-mm) wait for child 2 | ||
| 19 | (page-merge-mm) wait for child 3 | ||
| 20 | (page-merge-mm) wait for child 4 | ||
| 21 | (page-merge-mm) wait for child 5 | ||
| 22 | (page-merge-mm) wait for child 6 | ||
| 23 | (page-merge-mm) wait for child 7 | ||
| 24 | (page-merge-mm) merge | ||
| 25 | (page-merge-mm) verify | ||
| 26 | (page-merge-mm) success, buf_idx=1,048,576 | ||
| 27 | (page-merge-mm) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-par.c b/pintos-progos/tests/vm/page-merge-par.c new file mode 100644 index 0000000..e7e1609 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-par.c | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #include "tests/main.h" | ||
| 2 | #include "tests/vm/parallel-merge.h" | ||
| 3 | |||
| 4 | void | ||
| 5 | test_main (void) | ||
| 6 | { | ||
| 7 | parallel_merge ("child-sort", 123); | ||
| 8 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-par.ck b/pintos-progos/tests/vm/page-merge-par.ck new file mode 100644 index 0000000..31f8aa7 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-par.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-par) begin | ||
| 7 | (page-merge-par) init | ||
| 8 | (page-merge-par) sort chunk 0 | ||
| 9 | (page-merge-par) sort chunk 1 | ||
| 10 | (page-merge-par) sort chunk 2 | ||
| 11 | (page-merge-par) sort chunk 3 | ||
| 12 | (page-merge-par) sort chunk 4 | ||
| 13 | (page-merge-par) sort chunk 5 | ||
| 14 | (page-merge-par) sort chunk 6 | ||
| 15 | (page-merge-par) sort chunk 7 | ||
| 16 | (page-merge-par) wait for child 0 | ||
| 17 | (page-merge-par) wait for child 1 | ||
| 18 | (page-merge-par) wait for child 2 | ||
| 19 | (page-merge-par) wait for child 3 | ||
| 20 | (page-merge-par) wait for child 4 | ||
| 21 | (page-merge-par) wait for child 5 | ||
| 22 | (page-merge-par) wait for child 6 | ||
| 23 | (page-merge-par) wait for child 7 | ||
| 24 | (page-merge-par) merge | ||
| 25 | (page-merge-par) verify | ||
| 26 | (page-merge-par) success, buf_idx=1,048,576 | ||
| 27 | (page-merge-par) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-seq.c b/pintos-progos/tests/vm/page-merge-seq.c new file mode 100644 index 0000000..12e3880 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-seq.c | |||
| @@ -0,0 +1,137 @@ | |||
| 1 | /* Generates about 1 MB of random data that is then divided into | ||
| 2 | 16 chunks. A separate subprocess sorts each chunk in | ||
| 3 | sequence. Then we merge the chunks and verify that the result | ||
| 4 | is what it should be. */ | ||
| 5 | |||
| 6 | #include <syscall.h> | ||
| 7 | #include "tests/arc4.h" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | /* This is the max file size for an older version of the Pintos | ||
| 12 | file system that had 126 direct blocks each pointing to a | ||
| 13 | single disk sector. We could raise it now. */ | ||
| 14 | #define CHUNK_SIZE (126 * 512) | ||
| 15 | #define CHUNK_CNT 16 /* Number of chunks. */ | ||
| 16 | #define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */ | ||
| 17 | |||
| 18 | unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE]; | ||
| 19 | size_t histogram[256]; | ||
| 20 | |||
| 21 | /* Initialize buf1 with random data, | ||
| 22 | then count the number of instances of each value within it. */ | ||
| 23 | static void | ||
| 24 | init (void) | ||
| 25 | { | ||
| 26 | struct arc4 arc4; | ||
| 27 | size_t i; | ||
| 28 | |||
| 29 | msg ("init"); | ||
| 30 | |||
| 31 | arc4_init (&arc4, "foobar", 6); | ||
| 32 | arc4_crypt (&arc4, buf1, sizeof buf1); | ||
| 33 | for (i = 0; i < sizeof buf1; i++) | ||
| 34 | histogram[buf1[i]]++; | ||
| 35 | } | ||
| 36 | |||
| 37 | /* Sort each chunk of buf1 using a subprocess. */ | ||
| 38 | static void | ||
| 39 | sort_chunks (void) | ||
| 40 | { | ||
| 41 | size_t i; | ||
| 42 | |||
| 43 | create ("buffer", CHUNK_SIZE); | ||
| 44 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 45 | { | ||
| 46 | pid_t child; | ||
| 47 | int handle; | ||
| 48 | |||
| 49 | msg ("sort chunk %zu", i); | ||
| 50 | |||
| 51 | /* Write this chunk to a file. */ | ||
| 52 | quiet = true; | ||
| 53 | CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); | ||
| 54 | write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 55 | close (handle); | ||
| 56 | |||
| 57 | /* Sort with subprocess. */ | ||
| 58 | CHECK ((child = exec ("child-sort buffer")) != -1, | ||
| 59 | "exec \"child-sort buffer\""); | ||
| 60 | CHECK (wait (child) == 123, "wait for child-sort"); | ||
| 61 | |||
| 62 | /* Read chunk back from file. */ | ||
| 63 | CHECK ((handle = open ("buffer")) > 1, "open \"buffer\""); | ||
| 64 | read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 65 | close (handle); | ||
| 66 | |||
| 67 | quiet = false; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | /* Merge the sorted chunks in buf1 into a fully sorted buf2. */ | ||
| 72 | static void | ||
| 73 | merge (void) | ||
| 74 | { | ||
| 75 | unsigned char *mp[CHUNK_CNT]; | ||
| 76 | size_t mp_left; | ||
| 77 | unsigned char *op; | ||
| 78 | size_t i; | ||
| 79 | |||
| 80 | msg ("merge"); | ||
| 81 | |||
| 82 | /* Initialize merge pointers. */ | ||
| 83 | mp_left = CHUNK_CNT; | ||
| 84 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 85 | mp[i] = buf1 + CHUNK_SIZE * i; | ||
| 86 | |||
| 87 | /* Merge. */ | ||
| 88 | op = buf2; | ||
| 89 | while (mp_left > 0) | ||
| 90 | { | ||
| 91 | /* Find smallest value. */ | ||
| 92 | size_t min = 0; | ||
| 93 | for (i = 1; i < mp_left; i++) | ||
| 94 | if (*mp[i] < *mp[min]) | ||
| 95 | min = i; | ||
| 96 | |||
| 97 | /* Append value to buf2. */ | ||
| 98 | *op++ = *mp[min]; | ||
| 99 | |||
| 100 | /* Advance merge pointer. | ||
| 101 | Delete this chunk from the set if it's emptied. */ | ||
| 102 | if ((++mp[min] - buf1) % CHUNK_SIZE == 0) | ||
| 103 | mp[min] = mp[--mp_left]; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | static void | ||
| 108 | verify (void) | ||
| 109 | { | ||
| 110 | size_t buf_idx; | ||
| 111 | size_t hist_idx; | ||
| 112 | |||
| 113 | msg ("verify"); | ||
| 114 | |||
| 115 | buf_idx = 0; | ||
| 116 | for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram; | ||
| 117 | hist_idx++) | ||
| 118 | { | ||
| 119 | while (histogram[hist_idx]-- > 0) | ||
| 120 | { | ||
| 121 | if (buf2[buf_idx] != hist_idx) | ||
| 122 | fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx); | ||
| 123 | buf_idx++; | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | msg ("success, buf_idx=%'zu", buf_idx); | ||
| 128 | } | ||
| 129 | |||
| 130 | void | ||
| 131 | test_main (void) | ||
| 132 | { | ||
| 133 | init (); | ||
| 134 | sort_chunks (); | ||
| 135 | merge (); | ||
| 136 | verify (); | ||
| 137 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-seq.ck b/pintos-progos/tests/vm/page-merge-seq.ck new file mode 100644 index 0000000..d78f69d --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-seq.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-seq) begin | ||
| 7 | (page-merge-seq) init | ||
| 8 | (page-merge-seq) sort chunk 0 | ||
| 9 | (page-merge-seq) sort chunk 1 | ||
| 10 | (page-merge-seq) sort chunk 2 | ||
| 11 | (page-merge-seq) sort chunk 3 | ||
| 12 | (page-merge-seq) sort chunk 4 | ||
| 13 | (page-merge-seq) sort chunk 5 | ||
| 14 | (page-merge-seq) sort chunk 6 | ||
| 15 | (page-merge-seq) sort chunk 7 | ||
| 16 | (page-merge-seq) sort chunk 8 | ||
| 17 | (page-merge-seq) sort chunk 9 | ||
| 18 | (page-merge-seq) sort chunk 10 | ||
| 19 | (page-merge-seq) sort chunk 11 | ||
| 20 | (page-merge-seq) sort chunk 12 | ||
| 21 | (page-merge-seq) sort chunk 13 | ||
| 22 | (page-merge-seq) sort chunk 14 | ||
| 23 | (page-merge-seq) sort chunk 15 | ||
| 24 | (page-merge-seq) merge | ||
| 25 | (page-merge-seq) verify | ||
| 26 | (page-merge-seq) success, buf_idx=1,032,192 | ||
| 27 | (page-merge-seq) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-merge-stk.c b/pintos-progos/tests/vm/page-merge-stk.c new file mode 100644 index 0000000..5eb1069 --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-stk.c | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #include "tests/main.h" | ||
| 2 | #include "tests/vm/parallel-merge.h" | ||
| 3 | |||
| 4 | void | ||
| 5 | test_main (void) | ||
| 6 | { | ||
| 7 | parallel_merge ("child-qsort", 72); | ||
| 8 | } | ||
diff --git a/pintos-progos/tests/vm/page-merge-stk.ck b/pintos-progos/tests/vm/page-merge-stk.ck new file mode 100644 index 0000000..c5bc1ae --- /dev/null +++ b/pintos-progos/tests/vm/page-merge-stk.ck | |||
| @@ -0,0 +1,29 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-merge-stk) begin | ||
| 7 | (page-merge-stk) init | ||
| 8 | (page-merge-stk) sort chunk 0 | ||
| 9 | (page-merge-stk) sort chunk 1 | ||
| 10 | (page-merge-stk) sort chunk 2 | ||
| 11 | (page-merge-stk) sort chunk 3 | ||
| 12 | (page-merge-stk) sort chunk 4 | ||
| 13 | (page-merge-stk) sort chunk 5 | ||
| 14 | (page-merge-stk) sort chunk 6 | ||
| 15 | (page-merge-stk) sort chunk 7 | ||
| 16 | (page-merge-stk) wait for child 0 | ||
| 17 | (page-merge-stk) wait for child 1 | ||
| 18 | (page-merge-stk) wait for child 2 | ||
| 19 | (page-merge-stk) wait for child 3 | ||
| 20 | (page-merge-stk) wait for child 4 | ||
| 21 | (page-merge-stk) wait for child 5 | ||
| 22 | (page-merge-stk) wait for child 6 | ||
| 23 | (page-merge-stk) wait for child 7 | ||
| 24 | (page-merge-stk) merge | ||
| 25 | (page-merge-stk) verify | ||
| 26 | (page-merge-stk) success, buf_idx=1,048,576 | ||
| 27 | (page-merge-stk) end | ||
| 28 | EOF | ||
| 29 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-parallel.c b/pintos-progos/tests/vm/page-parallel.c new file mode 100644 index 0000000..9d619e0 --- /dev/null +++ b/pintos-progos/tests/vm/page-parallel.c | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* Runs 4 child-linear processes at once. */ | ||
| 2 | |||
| 3 | #include <syscall.h> | ||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | #define CHILD_CNT 4 | ||
| 8 | |||
| 9 | void | ||
| 10 | test_main (void) | ||
| 11 | { | ||
| 12 | pid_t children[CHILD_CNT]; | ||
| 13 | int i; | ||
| 14 | |||
| 15 | for (i = 0; i < CHILD_CNT; i++) | ||
| 16 | CHECK ((children[i] = exec ("child-linear")) != -1, | ||
| 17 | "exec \"child-linear\""); | ||
| 18 | |||
| 19 | for (i = 0; i < CHILD_CNT; i++) | ||
| 20 | CHECK (wait (children[i]) == 0x42, "wait for child %d", i); | ||
| 21 | } | ||
diff --git a/pintos-progos/tests/vm/page-parallel.ck b/pintos-progos/tests/vm/page-parallel.ck new file mode 100644 index 0000000..90c14ef --- /dev/null +++ b/pintos-progos/tests/vm/page-parallel.ck | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (page-parallel) begin | ||
| 7 | (page-parallel) exec "child-linear" | ||
| 8 | (page-parallel) exec "child-linear" | ||
| 9 | (page-parallel) exec "child-linear" | ||
| 10 | (page-parallel) exec "child-linear" | ||
| 11 | (page-parallel) wait for child 0 | ||
| 12 | (page-parallel) wait for child 1 | ||
| 13 | (page-parallel) wait for child 2 | ||
| 14 | (page-parallel) wait for child 3 | ||
| 15 | (page-parallel) end | ||
| 16 | EOF | ||
| 17 | pass; | ||
diff --git a/pintos-progos/tests/vm/page-shuffle.c b/pintos-progos/tests/vm/page-shuffle.c new file mode 100644 index 0000000..095a9da --- /dev/null +++ b/pintos-progos/tests/vm/page-shuffle.c | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | /* Shuffles a 128 kB data buffer 10 times, printing the checksum | ||
| 2 | after each time. */ | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | #define SIZE (128 * 1024) | ||
| 11 | |||
| 12 | static char buf[SIZE]; | ||
| 13 | |||
| 14 | void | ||
| 15 | test_main (void) | ||
| 16 | { | ||
| 17 | size_t i; | ||
| 18 | |||
| 19 | /* Initialize. */ | ||
| 20 | for (i = 0; i < sizeof buf; i++) | ||
| 21 | buf[i] = i * 257; | ||
| 22 | msg ("init: cksum=%lu", cksum (buf, sizeof buf)); | ||
| 23 | |||
| 24 | /* Shuffle repeatedly. */ | ||
| 25 | for (i = 0; i < 10; i++) | ||
| 26 | { | ||
| 27 | shuffle (buf, sizeof buf, 1); | ||
| 28 | msg ("shuffle %zu: cksum=%lu", i, cksum (buf, sizeof buf)); | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/pintos-progos/tests/vm/page-shuffle.ck b/pintos-progos/tests/vm/page-shuffle.ck new file mode 100644 index 0000000..6447d38 --- /dev/null +++ b/pintos-progos/tests/vm/page-shuffle.ck | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::cksum; | ||
| 6 | use tests::lib; | ||
| 7 | |||
| 8 | my ($init, @shuffle); | ||
| 9 | if (1) { | ||
| 10 | # Use precalculated values. | ||
| 11 | $init = 3115322833; | ||
| 12 | @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467, | ||
| 13 | 3022003332, 3614934266, 2704001777, 735775156, 1864109763); | ||
| 14 | } else { | ||
| 15 | # Recalculate values. | ||
| 16 | my ($buf) = ""; | ||
| 17 | for my $i (0...128 * 1024 - 1) { | ||
| 18 | $buf .= chr (($i * 257) & 0xff); | ||
| 19 | } | ||
| 20 | $init = cksum ($buf); | ||
| 21 | |||
| 22 | random_init (0); | ||
| 23 | for my $i (1...10) { | ||
| 24 | $buf = shuffle ($buf, length ($buf), 1); | ||
| 25 | push (@shuffle, cksum ($buf)); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]); | ||
| 30 | (page-shuffle) begin | ||
| 31 | (page-shuffle) init: cksum=$init | ||
| 32 | (page-shuffle) shuffle 0: cksum=$shuffle[0] | ||
| 33 | (page-shuffle) shuffle 1: cksum=$shuffle[1] | ||
| 34 | (page-shuffle) shuffle 2: cksum=$shuffle[2] | ||
| 35 | (page-shuffle) shuffle 3: cksum=$shuffle[3] | ||
| 36 | (page-shuffle) shuffle 4: cksum=$shuffle[4] | ||
| 37 | (page-shuffle) shuffle 5: cksum=$shuffle[5] | ||
| 38 | (page-shuffle) shuffle 6: cksum=$shuffle[6] | ||
| 39 | (page-shuffle) shuffle 7: cksum=$shuffle[7] | ||
| 40 | (page-shuffle) shuffle 8: cksum=$shuffle[8] | ||
| 41 | (page-shuffle) shuffle 9: cksum=$shuffle[9] | ||
| 42 | (page-shuffle) end | ||
| 43 | EOF | ||
| 44 | pass; | ||
diff --git a/pintos-progos/tests/vm/parallel-merge.c b/pintos-progos/tests/vm/parallel-merge.c new file mode 100644 index 0000000..cc09bb1 --- /dev/null +++ b/pintos-progos/tests/vm/parallel-merge.c | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | /* Generates about 1 MB of random data that is then divided into | ||
| 2 | 16 chunks. A separate subprocess sorts each chunk; the | ||
| 3 | subprocesses run in parallel. Then we merge the chunks and | ||
| 4 | verify that the result is what it should be. */ | ||
| 5 | |||
| 6 | #include "tests/vm/parallel-merge.h" | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <syscall.h> | ||
| 9 | #include "tests/arc4.h" | ||
| 10 | #include "tests/lib.h" | ||
| 11 | #include "tests/main.h" | ||
| 12 | |||
| 13 | #define CHUNK_SIZE (128 * 1024) | ||
| 14 | #define CHUNK_CNT 8 /* Number of chunks. */ | ||
| 15 | #define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */ | ||
| 16 | |||
| 17 | unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE]; | ||
| 18 | size_t histogram[256]; | ||
| 19 | |||
| 20 | /* Initialize buf1 with random data, | ||
| 21 | then count the number of instances of each value within it. */ | ||
| 22 | static void | ||
| 23 | init (void) | ||
| 24 | { | ||
| 25 | struct arc4 arc4; | ||
| 26 | size_t i; | ||
| 27 | |||
| 28 | msg ("init"); | ||
| 29 | |||
| 30 | arc4_init (&arc4, "foobar", 6); | ||
| 31 | arc4_crypt (&arc4, buf1, sizeof buf1); | ||
| 32 | for (i = 0; i < sizeof buf1; i++) | ||
| 33 | histogram[buf1[i]]++; | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Sort each chunk of buf1 using SUBPROCESS, | ||
| 37 | which is expected to return EXIT_STATUS. */ | ||
| 38 | static void | ||
| 39 | sort_chunks (const char *subprocess, int exit_status) | ||
| 40 | { | ||
| 41 | pid_t children[CHUNK_CNT]; | ||
| 42 | size_t i; | ||
| 43 | |||
| 44 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 45 | { | ||
| 46 | char fn[128]; | ||
| 47 | char cmd[128]; | ||
| 48 | int handle; | ||
| 49 | |||
| 50 | msg ("sort chunk %zu", i); | ||
| 51 | |||
| 52 | /* Write this chunk to a file. */ | ||
| 53 | snprintf (fn, sizeof fn, "buf%zu", i); | ||
| 54 | create (fn, CHUNK_SIZE); | ||
| 55 | quiet = true; | ||
| 56 | CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); | ||
| 57 | write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 58 | close (handle); | ||
| 59 | |||
| 60 | /* Sort with subprocess. */ | ||
| 61 | snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn); | ||
| 62 | CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd); | ||
| 63 | quiet = false; | ||
| 64 | } | ||
| 65 | |||
| 66 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 67 | { | ||
| 68 | char fn[128]; | ||
| 69 | int handle; | ||
| 70 | |||
| 71 | CHECK (wait (children[i]) == exit_status, "wait for child %zu", i); | ||
| 72 | |||
| 73 | /* Read chunk back from file. */ | ||
| 74 | quiet = true; | ||
| 75 | snprintf (fn, sizeof fn, "buf%zu", i); | ||
| 76 | CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn); | ||
| 77 | read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE); | ||
| 78 | close (handle); | ||
| 79 | quiet = false; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Merge the sorted chunks in buf1 into a fully sorted buf2. */ | ||
| 84 | static void | ||
| 85 | merge (void) | ||
| 86 | { | ||
| 87 | unsigned char *mp[CHUNK_CNT]; | ||
| 88 | size_t mp_left; | ||
| 89 | unsigned char *op; | ||
| 90 | size_t i; | ||
| 91 | |||
| 92 | msg ("merge"); | ||
| 93 | |||
| 94 | /* Initialize merge pointers. */ | ||
| 95 | mp_left = CHUNK_CNT; | ||
| 96 | for (i = 0; i < CHUNK_CNT; i++) | ||
| 97 | mp[i] = buf1 + CHUNK_SIZE * i; | ||
| 98 | |||
| 99 | /* Merge. */ | ||
| 100 | op = buf2; | ||
| 101 | while (mp_left > 0) | ||
| 102 | { | ||
| 103 | /* Find smallest value. */ | ||
| 104 | size_t min = 0; | ||
| 105 | for (i = 1; i < mp_left; i++) | ||
| 106 | if (*mp[i] < *mp[min]) | ||
| 107 | min = i; | ||
| 108 | |||
| 109 | /* Append value to buf2. */ | ||
| 110 | *op++ = *mp[min]; | ||
| 111 | |||
| 112 | /* Advance merge pointer. | ||
| 113 | Delete this chunk from the set if it's emptied. */ | ||
| 114 | if ((++mp[min] - buf1) % CHUNK_SIZE == 0) | ||
| 115 | mp[min] = mp[--mp_left]; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | static void | ||
| 120 | verify (void) | ||
| 121 | { | ||
| 122 | size_t buf_idx; | ||
| 123 | size_t hist_idx; | ||
| 124 | |||
| 125 | msg ("verify"); | ||
| 126 | |||
| 127 | buf_idx = 0; | ||
| 128 | for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram; | ||
| 129 | hist_idx++) | ||
| 130 | { | ||
| 131 | while (histogram[hist_idx]-- > 0) | ||
| 132 | { | ||
| 133 | if (buf2[buf_idx] != hist_idx) | ||
| 134 | fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx); | ||
| 135 | buf_idx++; | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | msg ("success, buf_idx=%'zu", buf_idx); | ||
| 140 | } | ||
| 141 | |||
| 142 | void | ||
| 143 | parallel_merge (const char *child_name, int exit_status) | ||
| 144 | { | ||
| 145 | init (); | ||
| 146 | sort_chunks (child_name, exit_status); | ||
| 147 | merge (); | ||
| 148 | verify (); | ||
| 149 | } | ||
diff --git a/pintos-progos/tests/vm/parallel-merge.h b/pintos-progos/tests/vm/parallel-merge.h new file mode 100644 index 0000000..a6b6431 --- /dev/null +++ b/pintos-progos/tests/vm/parallel-merge.h | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | #ifndef TESTS_VM_PARALLEL_MERGE | ||
| 2 | #define TESTS_VM_PARALLEL_MERGE 1 | ||
| 3 | |||
| 4 | void parallel_merge (const char *child_name, int exit_status); | ||
| 5 | |||
| 6 | #endif /* tests/vm/parallel-merge.h */ | ||
diff --git a/pintos-progos/tests/vm/process_death.pm b/pintos-progos/tests/vm/process_death.pm new file mode 100644 index 0000000..52039a1 --- /dev/null +++ b/pintos-progos/tests/vm/process_death.pm | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | |||
| 6 | sub check_process_death { | ||
| 7 | my ($proc_name) = @_; | ||
| 8 | our ($test); | ||
| 9 | my (@output) = read_text_file ("$test.output"); | ||
| 10 | |||
| 11 | common_checks ("run", @output); | ||
| 12 | @output = get_core_output ("run", @output); | ||
| 13 | fail "First line of output is not `($proc_name) begin' message.\n" | ||
| 14 | if $output[0] ne "($proc_name) begin"; | ||
| 15 | fail "Output missing '$proc_name: exit(-1)' message.\n" | ||
| 16 | if !grep ("$proc_name: exit(-1)" eq $_, @output); | ||
| 17 | fail "Output contains '($proc_name) end' message.\n" | ||
| 18 | if grep (/\($proc_name\) end/, @output); | ||
| 19 | pass; | ||
| 20 | } | ||
| 21 | |||
| 22 | 1; | ||
diff --git a/pintos-progos/tests/vm/pt-bad-addr.c b/pintos-progos/tests/vm/pt-bad-addr.c new file mode 100644 index 0000000..3ca4084 --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-addr.c | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | /* Accesses a bad address. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | fail ("bad addr read as %d", *(int *) 0x04000000); | ||
| 11 | } | ||
diff --git a/pintos-progos/tests/vm/pt-bad-addr.ck b/pintos-progos/tests/vm/pt-bad-addr.ck new file mode 100644 index 0000000..09ea039 --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-addr.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::vm::process_death; | ||
| 6 | |||
| 7 | check_process_death ('pt-bad-addr'); | ||
diff --git a/pintos-progos/tests/vm/pt-bad-read.c b/pintos-progos/tests/vm/pt-bad-read.c new file mode 100644 index 0000000..ee791ff --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-read.c | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | /* Reads from a file into a bad address. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <syscall.h> | ||
| 5 | #include "tests/lib.h" | ||
| 6 | #include "tests/main.h" | ||
| 7 | |||
| 8 | void | ||
| 9 | test_main (void) | ||
| 10 | { | ||
| 11 | int handle; | ||
| 12 | |||
| 13 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 14 | read (handle, (char *) &handle - 4096, 1); | ||
| 15 | fail ("survived reading data into bad address"); | ||
| 16 | } | ||
diff --git a/pintos-progos/tests/vm/pt-bad-read.ck b/pintos-progos/tests/vm/pt-bad-read.ck new file mode 100644 index 0000000..1f96bb4 --- /dev/null +++ b/pintos-progos/tests/vm/pt-bad-read.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (pt-bad-read) begin | ||
| 7 | (pt-bad-read) open "sample.txt" | ||
| 8 | pt-bad-read: exit(-1) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.c b/pintos-progos/tests/vm/pt-big-stk-obj.c new file mode 100644 index 0000000..6b630ec --- /dev/null +++ b/pintos-progos/tests/vm/pt-big-stk-obj.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Allocates and writes to a 64 kB object on the stack. | ||
| 2 | This must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char stk_obj[65536]; | ||
| 14 | struct arc4 arc4; | ||
| 15 | |||
| 16 | arc4_init (&arc4, "foobar", 6); | ||
| 17 | memset (stk_obj, 0, sizeof stk_obj); | ||
| 18 | arc4_crypt (&arc4, stk_obj, sizeof stk_obj); | ||
| 19 | msg ("cksum: %lu", cksum (stk_obj, sizeof stk_obj)); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.ck b/pintos-progos/tests/vm/pt-big-stk-obj.ck new file mode 100644 index 0000000..eb5853a --- /dev/null +++ b/pintos-progos/tests/vm/pt-big-stk-obj.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-big-stk-obj) begin | ||
| 7 | (pt-big-stk-obj) cksum: 3256410166 | ||
| 8 | (pt-big-stk-obj) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-bad.c b/pintos-progos/tests/vm/pt-grow-bad.c new file mode 100644 index 0000000..d4beba2 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-bad.c | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* Read from an address 4,096 bytes below the stack pointer. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | asm volatile ("movl -4096(%esp), %eax"); | ||
| 14 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-bad.ck b/pintos-progos/tests/vm/pt-grow-bad.ck new file mode 100644 index 0000000..4c0ab8a --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-bad.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']); | ||
| 6 | (pt-grow-bad) begin | ||
| 7 | pt-grow-bad: exit(-1) | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.c b/pintos-progos/tests/vm/pt-grow-pusha.c new file mode 100644 index 0000000..f9762a5 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-pusha.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Expand the stack by 32 bytes all at once using the PUSHA | ||
| 2 | instruction. | ||
| 3 | This must succeed. */ | ||
| 4 | |||
| 5 | #include <string.h> | ||
| 6 | #include "tests/arc4.h" | ||
| 7 | #include "tests/cksum.h" | ||
| 8 | #include "tests/lib.h" | ||
| 9 | #include "tests/main.h" | ||
| 10 | |||
| 11 | void | ||
| 12 | test_main (void) | ||
| 13 | { | ||
| 14 | asm volatile | ||
| 15 | ("movl %%esp, %%eax;" /* Save a copy of the stack pointer. */ | ||
| 16 | "andl $0xfffff000, %%esp;" /* Move stack pointer to bottom of page. */ | ||
| 17 | "pushal;" /* Push 32 bytes on stack at once. */ | ||
| 18 | "movl %%eax, %%esp" /* Restore copied stack pointer. */ | ||
| 19 | : : : "eax"); /* Tell GCC we destroyed eax. */ | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.ck b/pintos-progos/tests/vm/pt-grow-pusha.ck new file mode 100644 index 0000000..5000966 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-pusha.ck | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-grow-pusha) begin | ||
| 7 | (pt-grow-pusha) end | ||
| 8 | EOF | ||
| 9 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stack.c b/pintos-progos/tests/vm/pt-grow-stack.c new file mode 100644 index 0000000..0997a00 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stack.c | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | /* Demonstrate that the stack can grow. | ||
| 2 | This must succeed. */ | ||
| 3 | |||
| 4 | #include <string.h> | ||
| 5 | #include "tests/arc4.h" | ||
| 6 | #include "tests/cksum.h" | ||
| 7 | #include "tests/lib.h" | ||
| 8 | #include "tests/main.h" | ||
| 9 | |||
| 10 | void | ||
| 11 | test_main (void) | ||
| 12 | { | ||
| 13 | char stack_obj[4096]; | ||
| 14 | struct arc4 arc4; | ||
| 15 | |||
| 16 | arc4_init (&arc4, "foobar", 6); | ||
| 17 | memset (stack_obj, 0, sizeof stack_obj); | ||
| 18 | arc4_crypt (&arc4, stack_obj, sizeof stack_obj); | ||
| 19 | msg ("cksum: %lu", cksum (stack_obj, sizeof stack_obj)); | ||
| 20 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stack.ck b/pintos-progos/tests/vm/pt-grow-stack.ck new file mode 100644 index 0000000..1e669db --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stack.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-grow-stack) begin | ||
| 7 | (pt-grow-stack) cksum: 3424492700 | ||
| 8 | (pt-grow-stack) end | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.c b/pintos-progos/tests/vm/pt-grow-stk-sc.c new file mode 100644 index 0000000..3efbb5f --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stk-sc.c | |||
| @@ -0,0 +1,32 @@ | |||
| 1 | /* This test checks that the stack is properly extended even if | ||
| 2 | the first access to a stack location occurs inside a system | ||
| 3 | call. | ||
| 4 | |||
| 5 | From Godmar Back. */ | ||
| 6 | |||
| 7 | #include <string.h> | ||
| 8 | #include <syscall.h> | ||
| 9 | #include "tests/vm/sample.inc" | ||
| 10 | #include "tests/lib.h" | ||
| 11 | #include "tests/main.h" | ||
| 12 | |||
| 13 | void | ||
| 14 | test_main (void) | ||
| 15 | { | ||
| 16 | int handle; | ||
| 17 | int slen = strlen (sample); | ||
| 18 | char buf2[65536]; | ||
| 19 | |||
| 20 | /* Write file via write(). */ | ||
| 21 | CHECK (create ("sample.txt", slen), "create \"sample.txt\""); | ||
| 22 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 23 | CHECK (write (handle, sample, slen) == slen, "write \"sample.txt\""); | ||
| 24 | close (handle); | ||
| 25 | |||
| 26 | /* Read back via read(). */ | ||
| 27 | CHECK ((handle = open ("sample.txt")) > 1, "2nd open \"sample.txt\""); | ||
| 28 | CHECK (read (handle, buf2 + 32768, slen) == slen, "read \"sample.txt\""); | ||
| 29 | |||
| 30 | CHECK (!memcmp (sample, buf2 + 32768, slen), "compare written data against read data"); | ||
| 31 | close (handle); | ||
| 32 | } | ||
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.ck b/pintos-progos/tests/vm/pt-grow-stk-sc.ck new file mode 100644 index 0000000..23d3b02 --- /dev/null +++ b/pintos-progos/tests/vm/pt-grow-stk-sc.ck | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']); | ||
| 6 | (pt-grow-stk-sc) begin | ||
| 7 | (pt-grow-stk-sc) create "sample.txt" | ||
| 8 | (pt-grow-stk-sc) open "sample.txt" | ||
| 9 | (pt-grow-stk-sc) write "sample.txt" | ||
| 10 | (pt-grow-stk-sc) 2nd open "sample.txt" | ||
| 11 | (pt-grow-stk-sc) read "sample.txt" | ||
| 12 | (pt-grow-stk-sc) compare written data against read data | ||
| 13 | (pt-grow-stk-sc) end | ||
| 14 | EOF | ||
| 15 | pass; | ||
diff --git a/pintos-progos/tests/vm/pt-write-code-2.c b/pintos-progos/tests/vm/pt-write-code-2.c new file mode 100644 index 0000000..83bcc2c --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code-2.c | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | /* Try to write to the code segment using a system call. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | int handle; | ||
| 11 | |||
| 12 | CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\""); | ||
| 13 | read (handle, (void *) test_main, 1); | ||
| 14 | fail ("survived reading data into code segment"); | ||
| 15 | } | ||
diff --git a/pintos-progos/tests/vm/pt-write-code.c b/pintos-progos/tests/vm/pt-write-code.c new file mode 100644 index 0000000..5072cec --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code.c | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | /* Try to write to the code segment. | ||
| 2 | The process must be terminated with -1 exit code. */ | ||
| 3 | |||
| 4 | #include "tests/lib.h" | ||
| 5 | #include "tests/main.h" | ||
| 6 | |||
| 7 | void | ||
| 8 | test_main (void) | ||
| 9 | { | ||
| 10 | *(int *) test_main = 0; | ||
| 11 | fail ("writing the code segment succeeded"); | ||
| 12 | } | ||
diff --git a/pintos-progos/tests/vm/pt-write-code.ck b/pintos-progos/tests/vm/pt-write-code.ck new file mode 100644 index 0000000..65610fb --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code.ck | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | use tests::vm::process_death; | ||
| 6 | |||
| 7 | check_process_death ('pt-write-code'); | ||
diff --git a/pintos-progos/tests/vm/pt-write-code2.ck b/pintos-progos/tests/vm/pt-write-code2.ck new file mode 100644 index 0000000..69ffc77 --- /dev/null +++ b/pintos-progos/tests/vm/pt-write-code2.ck | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | # -*- perl -*- | ||
| 2 | use strict; | ||
| 3 | use warnings; | ||
| 4 | use tests::tests; | ||
| 5 | check_expected ([<<'EOF']); | ||
| 6 | (pt-write-code2) begin | ||
| 7 | (pt-write-code2) open "sample.txt" | ||
| 8 | pt-write-code2: exit(-1) | ||
| 9 | EOF | ||
| 10 | pass; | ||
diff --git a/pintos-progos/tests/vm/qsort.c b/pintos-progos/tests/vm/qsort.c new file mode 100644 index 0000000..922572c --- /dev/null +++ b/pintos-progos/tests/vm/qsort.c | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | #include "tests/vm/qsort.h" | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <random.h> | ||
| 5 | |||
| 6 | /* Picks a pivot for the quicksort from the SIZE bytes in BUF. */ | ||
| 7 | static unsigned char | ||
| 8 | pick_pivot (unsigned char *buf, size_t size) | ||
| 9 | { | ||
| 10 | ASSERT (size >= 1); | ||
| 11 | return buf[random_ulong () % size]; | ||
| 12 | } | ||
| 13 | |||
| 14 | /* Checks whether the SIZE bytes in ARRAY are divided into an | ||
| 15 | initial LEFT_SIZE elements all less than PIVOT followed by | ||
| 16 | SIZE - LEFT_SIZE elements all greater than or equal to | ||
| 17 | PIVOT. */ | ||
| 18 | static bool | ||
| 19 | is_partitioned (const unsigned char *array, size_t size, | ||
| 20 | unsigned char pivot, size_t left_size) | ||
| 21 | { | ||
| 22 | size_t i; | ||
| 23 | |||
| 24 | for (i = 0; i < left_size; i++) | ||
| 25 | if (array[i] >= pivot) | ||
| 26 | return false; | ||
| 27 | |||
| 28 | for (; i < size; i++) | ||
| 29 | if (array[i] < pivot) | ||
| 30 | return false; | ||
| 31 | |||
| 32 | return true; | ||
| 33 | } | ||
| 34 | |||
| 35 | /* Swaps the bytes at *A and *B. */ | ||
| 36 | static void | ||
| 37 | swap (unsigned char *a, unsigned char *b) | ||
| 38 | { | ||
| 39 | unsigned char t = *a; | ||
| 40 | *a = *b; | ||
| 41 | *b = t; | ||
| 42 | } | ||
| 43 | |||
| 44 | /* Partitions ARRAY in-place in an initial run of bytes all less | ||
| 45 | than PIVOT, followed by a run of bytes all greater than or | ||
| 46 | equal to PIVOT. Returns the length of the initial run. */ | ||
| 47 | static size_t | ||
| 48 | partition (unsigned char *array, size_t size, int pivot) | ||
| 49 | { | ||
| 50 | size_t left_size = size; | ||
| 51 | unsigned char *first = array; | ||
| 52 | unsigned char *last = first + left_size; | ||
| 53 | |||
| 54 | for (;;) | ||
| 55 | { | ||
| 56 | /* Move FIRST forward to point to first element greater than | ||
| 57 | PIVOT. */ | ||
| 58 | for (;;) | ||
| 59 | { | ||
| 60 | if (first == last) | ||
| 61 | { | ||
| 62 | ASSERT (is_partitioned (array, size, pivot, left_size)); | ||
| 63 | return left_size; | ||
| 64 | } | ||
| 65 | else if (*first >= pivot) | ||
| 66 | break; | ||
| 67 | |||
| 68 | first++; | ||
| 69 | } | ||
| 70 | left_size--; | ||
| 71 | |||
| 72 | /* Move LAST backward to point to last element no bigger | ||
| 73 | than PIVOT. */ | ||
| 74 | for (;;) | ||
| 75 | { | ||
| 76 | last--; | ||
| 77 | |||
| 78 | if (first == last) | ||
| 79 | { | ||
| 80 | ASSERT (is_partitioned (array, size, pivot, left_size)); | ||
| 81 | return left_size; | ||
| 82 | } | ||
| 83 | else if (*last < pivot) | ||
| 84 | break; | ||
| 85 | else | ||
| 86 | left_size--; | ||
| 87 | } | ||
| 88 | |||
| 89 | /* By swapping FIRST and LAST we extend the starting and | ||
| 90 | ending sequences that pass and fail, respectively, | ||
| 91 | PREDICATE. */ | ||
| 92 | swap (first, last); | ||
| 93 | first++; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | /* Returns true if the SIZE bytes in BUF are in nondecreasing | ||
| 98 | order, false otherwise. */ | ||
| 99 | static bool | ||
| 100 | is_sorted (const unsigned char *buf, size_t size) | ||
| 101 | { | ||
| 102 | size_t i; | ||
| 103 | |||
| 104 | for (i = 1; i < size; i++) | ||
| 105 | if (buf[i - 1] > buf[i]) | ||
| 106 | return false; | ||
| 107 | |||
| 108 | return true; | ||
| 109 | } | ||
| 110 | |||
| 111 | /* Sorts the SIZE bytes in BUF into nondecreasing order, using | ||
| 112 | the quick-sort algorithm. */ | ||
| 113 | void | ||
| 114 | qsort_bytes (unsigned char *buf, size_t size) | ||
| 115 | { | ||
| 116 | if (!is_sorted (buf, size)) | ||
| 117 | { | ||
| 118 | int pivot = pick_pivot (buf, size); | ||
| 119 | |||
| 120 | unsigned char *left_half = buf; | ||
| 121 | size_t left_size = partition (buf, size, pivot); | ||
| 122 | unsigned char *right_half = left_half + left_size; | ||
| 123 | size_t right_size = size - left_size; | ||
| 124 | |||
| 125 | if (left_size <= right_size) | ||
| 126 | { | ||
| 127 | qsort_bytes (left_half, left_size); | ||
| 128 | qsort_bytes (right_half, right_size); | ||
| 129 | } | ||
| 130 | else | ||
| 131 | { | ||
| 132 | qsort_bytes (right_half, right_size); | ||
| 133 | qsort_bytes (left_half, left_size); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/pintos-progos/tests/vm/qsort.h b/pintos-progos/tests/vm/qsort.h new file mode 100644 index 0000000..61b65f3 --- /dev/null +++ b/pintos-progos/tests/vm/qsort.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef TESTS_VM_QSORT_H | ||
| 2 | #define TESTS_VM_QSORT_H 1 | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | void qsort_bytes (unsigned char *buf, size_t size); | ||
| 7 | |||
| 8 | #endif /* tests/vm/qsort.h */ | ||
diff --git a/pintos-progos/tests/vm/sample.inc b/pintos-progos/tests/vm/sample.inc new file mode 100644 index 0000000..a60a139 --- /dev/null +++ b/pintos-progos/tests/vm/sample.inc | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | char sample[] = { | ||
| 2 | "=== ALL USERS PLEASE NOTE ========================\n" | ||
| 3 | "\n" | ||
| 4 | "CAR and CDR now return extra values.\n" | ||
| 5 | "\n" | ||
| 6 | "The function CAR now returns two values. Since it has to go to the\n" | ||
| 7 | "trouble to figure out if the object is carcdr-able anyway, we figured\n" | ||
| 8 | "you might as well get both halves at once. For example, the following\n" | ||
| 9 | "code shows how to destructure a cons (SOME-CONS) into its two slots\n" | ||
| 10 | "(THE-CAR and THE-CDR):\n" | ||
| 11 | "\n" | ||
| 12 | " (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)\n" | ||
| 13 | "\n" | ||
| 14 | "For symmetry with CAR, CDR returns a second value which is the CAR of\n" | ||
| 15 | "the object. In a related change, the functions MAKE-ARRAY and CONS\n" | ||
| 16 | "have been fixed so they don't allocate any storage except on the\n" | ||
| 17 | "stack. This should hopefully help people who don't like using the\n" | ||
| 18 | "garbage collector because it cold boots the machine so often.\n" | ||
| 19 | }; | ||
diff --git a/pintos-progos/tests/vm/sample.txt b/pintos-progos/tests/vm/sample.txt new file mode 100644 index 0000000..c446830 --- /dev/null +++ b/pintos-progos/tests/vm/sample.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | === ALL USERS PLEASE NOTE ======================== | ||
| 2 | |||
| 3 | CAR and CDR now return extra values. | ||
| 4 | |||
| 5 | The function CAR now returns two values. Since it has to go to the | ||
| 6 | trouble to figure out if the object is carcdr-able anyway, we figured | ||
| 7 | you might as well get both halves at once. For example, the following | ||
| 8 | code shows how to destructure a cons (SOME-CONS) into its two slots | ||
| 9 | (THE-CAR and THE-CDR): | ||
| 10 | |||
| 11 | (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...) | ||
| 12 | |||
| 13 | For symmetry with CAR, CDR returns a second value which is the CAR of | ||
| 14 | the object. In a related change, the functions MAKE-ARRAY and CONS | ||
| 15 | have been fixed so they don't allocate any storage except on the | ||
| 16 | stack. This should hopefully help people who don't like using the | ||
| 17 | garbage collector because it cold boots the machine so often. | ||
diff --git a/pintos-progos/threads/.gitignore b/pintos-progos/threads/.gitignore new file mode 100644 index 0000000..6d5357c --- /dev/null +++ b/pintos-progos/threads/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | build | ||
| 2 | bochsrc.txt | ||
| 3 | bochsout.txt | ||
diff --git a/pintos-progos/threads/Make.vars b/pintos-progos/threads/Make.vars new file mode 100644 index 0000000..310c240 --- /dev/null +++ b/pintos-progos/threads/Make.vars | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel $(TEST_SUBDIRS) | ||
| 5 | TEST_SUBDIRS = tests/threads | ||
| 6 | GRADING_FILE = $(SRCDIR)/tests/threads/Grading | ||
| 7 | SIMULATOR = --bochs | ||
diff --git a/pintos-progos/threads/Makefile b/pintos-progos/threads/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/pintos-progos/threads/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
diff --git a/pintos-progos/threads/flags.h b/pintos-progos/threads/flags.h new file mode 100644 index 0000000..5654ac7 --- /dev/null +++ b/pintos-progos/threads/flags.h | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | #ifndef THREADS_FLAGS_H | ||
| 2 | #define THREADS_FLAGS_H | ||
| 3 | |||
| 4 | /* EFLAGS Register. */ | ||
| 5 | #define FLAG_MBS 0x00000002 /* Must be set. */ | ||
| 6 | #define FLAG_IF 0x00000200 /* Interrupt Flag. */ | ||
| 7 | |||
| 8 | #endif /* threads/flags.h */ | ||
diff --git a/pintos-progos/threads/init.c b/pintos-progos/threads/init.c new file mode 100644 index 0000000..d8feacd --- /dev/null +++ b/pintos-progos/threads/init.c | |||
| @@ -0,0 +1,453 @@ | |||
| 1 | #include "threads/init.h" | ||
| 2 | #include <console.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <inttypes.h> | ||
| 5 | #include <limits.h> | ||
| 6 | #include <random.h> | ||
| 7 | #include <stddef.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include "devices/kbd.h" | ||
| 12 | #include "devices/input.h" | ||
| 13 | #include "devices/serial.h" | ||
| 14 | #include "devices/shutdown.h" | ||
| 15 | #include "devices/timer.h" | ||
| 16 | #include "devices/vga.h" | ||
| 17 | #include "devices/rtc.h" | ||
| 18 | #include "threads/interrupt.h" | ||
| 19 | #include "threads/io.h" | ||
| 20 | #include "threads/loader.h" | ||
| 21 | #include "threads/malloc.h" | ||
| 22 | #include "threads/palloc.h" | ||
| 23 | #include "threads/pte.h" | ||
| 24 | #include "threads/thread.h" | ||
| 25 | #ifdef USERPROG | ||
| 26 | #include "userprog/process.h" | ||
| 27 | #include "userprog/exception.h" | ||
| 28 | #include "userprog/gdt.h" | ||
| 29 | #include "userprog/syscall.h" | ||
| 30 | #include "userprog/tss.h" | ||
| 31 | #else | ||
| 32 | #include "tests/threads/tests.h" | ||
| 33 | #endif | ||
| 34 | #ifdef FILESYS | ||
| 35 | #include "devices/block.h" | ||
| 36 | #include "devices/ide.h" | ||
| 37 | #include "filesys/filesys.h" | ||
| 38 | #include "filesys/fsutil.h" | ||
| 39 | #endif | ||
| 40 | |||
| 41 | |||
| 42 | /* Page directory with kernel mappings only. */ | ||
| 43 | uint32_t *init_page_dir; | ||
| 44 | |||
| 45 | #ifdef FILESYS | ||
| 46 | /* -f: Format the file system? */ | ||
| 47 | static bool format_filesys; | ||
| 48 | |||
| 49 | /* -filesys, -scratch, -swap: Names of block devices to use, | ||
| 50 | overriding the defaults. */ | ||
| 51 | static const char *filesys_bdev_name; | ||
| 52 | static const char *scratch_bdev_name; | ||
| 53 | #ifdef VM | ||
| 54 | static const char *swap_bdev_name; | ||
| 55 | #endif | ||
| 56 | #endif /* FILESYS */ | ||
| 57 | |||
| 58 | /* -kernel-test: Run kernel test instead of user program */ | ||
| 59 | static bool kernel_test = false; | ||
| 60 | |||
| 61 | /* provide weak kernel test definition if no test is available */ | ||
| 62 | void run_test (const char *param); | ||
| 63 | __attribute__((weak)) | ||
| 64 | void | ||
| 65 | run_test (const char *param UNUSED) | ||
| 66 | { | ||
| 67 | printf("No kernel test linked into kernel\n"); | ||
| 68 | } | ||
| 69 | |||
| 70 | |||
| 71 | /* -ul: Maximum number of pages to put into palloc's user pool. */ | ||
| 72 | static size_t user_page_limit = SIZE_MAX; | ||
| 73 | |||
| 74 | static void bss_init (void); | ||
| 75 | static void paging_init (void); | ||
| 76 | |||
| 77 | static char **read_command_line (void); | ||
| 78 | static char **parse_options (char **argv); | ||
| 79 | static void run_actions (char **argv); | ||
| 80 | static void usage (void); | ||
| 81 | |||
| 82 | #ifdef FILESYS | ||
| 83 | static void locate_block_devices (void); | ||
| 84 | static void locate_block_device (enum block_type, const char *name); | ||
| 85 | #endif | ||
| 86 | |||
| 87 | int main (void) NO_RETURN; | ||
| 88 | |||
| 89 | /* Pintos main program. */ | ||
| 90 | int | ||
| 91 | main (void) | ||
| 92 | { | ||
| 93 | char **argv; | ||
| 94 | |||
| 95 | /* Clear BSS. */ | ||
| 96 | bss_init (); | ||
| 97 | |||
| 98 | /* Break command line into arguments and parse options. */ | ||
| 99 | argv = read_command_line (); | ||
| 100 | argv = parse_options (argv); | ||
| 101 | |||
| 102 | /* Initialize ourselves as a thread so we can use locks, | ||
| 103 | then enable console locking. */ | ||
| 104 | thread_init (); | ||
| 105 | console_init (); | ||
| 106 | |||
| 107 | /* Greet user. */ | ||
| 108 | printf ("Pintos booting with %'"PRIu32" kB RAM...\n", | ||
| 109 | init_ram_pages * PGSIZE / 1024); | ||
| 110 | |||
| 111 | /* Initialize memory system. */ | ||
| 112 | palloc_init (user_page_limit); | ||
| 113 | malloc_init (); | ||
| 114 | paging_init (); | ||
| 115 | |||
| 116 | /* Segmentation. */ | ||
| 117 | #ifdef USERPROG | ||
| 118 | tss_init (); | ||
| 119 | gdt_init (); | ||
| 120 | #endif | ||
| 121 | |||
| 122 | /* Initialize interrupt handlers. */ | ||
| 123 | intr_init (); | ||
| 124 | timer_init (); | ||
| 125 | kbd_init (); | ||
| 126 | input_init (); | ||
| 127 | #ifdef USERPROG | ||
| 128 | exception_init (); | ||
| 129 | syscall_init (); | ||
| 130 | #endif | ||
| 131 | |||
| 132 | /* Start thread scheduler and enable interrupts. */ | ||
| 133 | thread_start (); | ||
| 134 | serial_init_queue (); | ||
| 135 | timer_calibrate (); | ||
| 136 | |||
| 137 | #ifdef FILESYS | ||
| 138 | /* Initialize file system. */ | ||
| 139 | ide_init (); | ||
| 140 | locate_block_devices (); | ||
| 141 | /* kernel tests do not need filesystem */ | ||
| 142 | if (!kernel_test) | ||
| 143 | filesys_init (format_filesys); | ||
| 144 | #endif | ||
| 145 | |||
| 146 | printf ("Boot complete.\n"); | ||
| 147 | |||
| 148 | /* Run actions specified on kernel command line. */ | ||
| 149 | run_actions (argv); | ||
| 150 | |||
| 151 | /* Finish up. */ | ||
| 152 | shutdown (); | ||
| 153 | thread_exit (); | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Clear the "BSS", a segment that should be initialized to | ||
| 157 | zeros. It isn't actually stored on disk or zeroed by the | ||
| 158 | kernel loader, so we have to zero it ourselves. | ||
| 159 | |||
| 160 | The start and end of the BSS segment is recorded by the | ||
| 161 | linker as _start_bss and _end_bss. See kernel.lds. */ | ||
| 162 | static void | ||
| 163 | bss_init (void) | ||
| 164 | { | ||
| 165 | extern char _start_bss, _end_bss; | ||
| 166 | memset (&_start_bss, 0, &_end_bss - &_start_bss); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* Populates the base page directory and page table with the | ||
| 170 | kernel virtual mapping, and then sets up the CPU to use the | ||
| 171 | new page directory. Points init_page_dir to the page | ||
| 172 | directory it creates. */ | ||
| 173 | static void | ||
| 174 | paging_init (void) | ||
| 175 | { | ||
| 176 | uint32_t *pd, *pt; | ||
| 177 | size_t page; | ||
| 178 | extern char _start, _end_kernel_text; | ||
| 179 | |||
| 180 | pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO); | ||
| 181 | pt = NULL; | ||
| 182 | for (page = 0; page < init_ram_pages; page++) | ||
| 183 | { | ||
| 184 | uintptr_t paddr = page * PGSIZE; | ||
| 185 | char *vaddr = ptov (paddr); | ||
| 186 | size_t pde_idx = pd_no (vaddr); | ||
| 187 | size_t pte_idx = pt_no (vaddr); | ||
| 188 | bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text; | ||
| 189 | |||
| 190 | if (pd[pde_idx] == 0) | ||
| 191 | { | ||
| 192 | pt = palloc_get_page (PAL_ASSERT | PAL_ZERO); | ||
| 193 | pd[pde_idx] = pde_create (pt); | ||
| 194 | } | ||
| 195 | |||
| 196 | pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text); | ||
| 197 | } | ||
| 198 | |||
| 199 | /* Store the physical address of the page directory into CR3 | ||
| 200 | aka PDBR (page directory base register). This activates our | ||
| 201 | new page tables immediately. See [IA32-v2a] "MOV--Move | ||
| 202 | to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address | ||
| 203 | of the Page Directory". */ | ||
| 204 | asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir))); | ||
| 205 | } | ||
| 206 | |||
| 207 | /* Breaks the kernel command line into words and returns them as | ||
| 208 | an argv-like array. */ | ||
| 209 | static char ** | ||
| 210 | read_command_line (void) | ||
| 211 | { | ||
| 212 | static char *argv[LOADER_ARGS_LEN / 2 + 1]; | ||
| 213 | char *p, *end; | ||
| 214 | int argc; | ||
| 215 | int i; | ||
| 216 | |||
| 217 | argc = *(uint32_t *) ptov (LOADER_ARG_CNT); | ||
| 218 | p = ptov (LOADER_ARGS); | ||
| 219 | end = p + LOADER_ARGS_LEN; | ||
| 220 | for (i = 0; i < argc; i++) | ||
| 221 | { | ||
| 222 | if (p >= end) | ||
| 223 | PANIC ("command line arguments overflow"); | ||
| 224 | |||
| 225 | argv[i] = p; | ||
| 226 | p += strnlen (p, end - p) + 1; | ||
| 227 | } | ||
| 228 | argv[argc] = NULL; | ||
| 229 | |||
| 230 | /* Print kernel command line. */ | ||
| 231 | printf ("Kernel command line:"); | ||
| 232 | for (i = 0; i < argc; i++) | ||
| 233 | if (strchr (argv[i], ' ') == NULL) | ||
| 234 | printf (" %s", argv[i]); | ||
| 235 | else | ||
| 236 | printf (" '%s'", argv[i]); | ||
| 237 | printf ("\n"); | ||
| 238 | |||
| 239 | return argv; | ||
| 240 | } | ||
| 241 | |||
| 242 | /* Parses options in ARGV[] | ||
| 243 | and returns the first non-option argument. */ | ||
| 244 | static char ** | ||
| 245 | parse_options (char **argv) | ||
| 246 | { | ||
| 247 | for (; *argv != NULL && **argv == '-'; argv++) | ||
| 248 | { | ||
| 249 | char *save_ptr; | ||
| 250 | char *name = strtok_r (*argv, "=", &save_ptr); | ||
| 251 | char *value = strtok_r (NULL, "", &save_ptr); | ||
| 252 | |||
| 253 | #ifndef USERPROG | ||
| 254 | kernel_test = true; | ||
| 255 | #endif | ||
| 256 | if (!strcmp (name, "-h")) | ||
| 257 | usage (); | ||
| 258 | else if (!strcmp (name, "-q")) | ||
| 259 | shutdown_configure (SHUTDOWN_POWER_OFF); | ||
| 260 | else if (!strcmp (name, "-r")) | ||
| 261 | shutdown_configure (SHUTDOWN_REBOOT); | ||
| 262 | #ifdef FILESYS | ||
| 263 | else if (!strcmp (name, "-f")) | ||
| 264 | format_filesys = true; | ||
| 265 | else if (!strcmp (name, "-filesys")) | ||
| 266 | filesys_bdev_name = value; | ||
| 267 | else if (!strcmp (name, "-scratch")) | ||
| 268 | scratch_bdev_name = value; | ||
| 269 | #ifdef VM | ||
| 270 | else if (!strcmp (name, "-swap")) | ||
| 271 | swap_bdev_name = value; | ||
| 272 | #endif | ||
| 273 | #endif | ||
| 274 | else if (!strcmp (name, "-rs")) | ||
| 275 | random_init (atoi (value)); | ||
| 276 | else if (!strcmp (name, "-mlfqs")) | ||
| 277 | thread_mlfqs = true; | ||
| 278 | #ifdef USERPROG | ||
| 279 | else if (!strcmp (name, "-kernel-test")) | ||
| 280 | kernel_test = true; | ||
| 281 | else if (!strcmp (name, "-ul")) | ||
| 282 | user_page_limit = atoi (value); | ||
| 283 | #endif | ||
| 284 | else | ||
| 285 | PANIC ("unknown option `%s' (use -h for help)", name); | ||
| 286 | } | ||
| 287 | |||
| 288 | /* Initialize the random number generator based on the system | ||
| 289 | time. This has no effect if an "-rs" option was specified. | ||
| 290 | |||
| 291 | When running under Bochs, this is not enough by itself to | ||
| 292 | get a good seed value, because the pintos script sets the | ||
| 293 | initial time to a predictable value, not to the local time, | ||
| 294 | for reproducibility. To fix this, give the "-r" option to | ||
| 295 | the pintos script to request real-time execution. */ | ||
| 296 | random_init (rtc_get_time ()); | ||
| 297 | |||
| 298 | return argv; | ||
| 299 | } | ||
| 300 | |||
| 301 | /* Runs the task specified in ARGV[1]. */ | ||
| 302 | static void | ||
| 303 | run_task (char **argv) | ||
| 304 | { | ||
| 305 | const char *task = argv[1]; | ||
| 306 | |||
| 307 | printf ("Executing '%s':\n", task); | ||
| 308 | #ifdef USERPROG | ||
| 309 | if (kernel_test) | ||
| 310 | run_test (task); | ||
| 311 | else | ||
| 312 | process_wait (process_execute (task)); | ||
| 313 | #else | ||
| 314 | run_test (task); | ||
| 315 | #endif | ||
| 316 | printf ("Execution of '%s' complete.\n", task); | ||
| 317 | } | ||
| 318 | |||
| 319 | /* Executes all of the actions specified in ARGV[] | ||
| 320 | up to the null pointer sentinel. */ | ||
| 321 | static void | ||
| 322 | run_actions (char **argv) | ||
| 323 | { | ||
| 324 | /* An action. */ | ||
| 325 | struct action | ||
| 326 | { | ||
| 327 | char *name; /* Action name. */ | ||
| 328 | int argc; /* # of args, including action name. */ | ||
| 329 | void (*function) (char **argv); /* Function to execute action. */ | ||
| 330 | }; | ||
| 331 | |||
| 332 | /* Table of supported actions. */ | ||
| 333 | static const struct action actions[] = | ||
| 334 | { | ||
| 335 | {"run", 2, run_task}, | ||
| 336 | #ifdef FILESYS | ||
| 337 | {"ls", 1, fsutil_ls}, | ||
| 338 | {"cat", 2, fsutil_cat}, | ||
| 339 | {"rm", 2, fsutil_rm}, | ||
| 340 | {"extract", 1, fsutil_extract}, | ||
| 341 | {"append", 2, fsutil_append}, | ||
| 342 | #endif | ||
| 343 | {NULL, 0, NULL}, | ||
| 344 | }; | ||
| 345 | |||
| 346 | while (*argv != NULL) | ||
| 347 | { | ||
| 348 | const struct action *a; | ||
| 349 | int i; | ||
| 350 | |||
| 351 | /* Find action name. */ | ||
| 352 | for (a = actions; ; a++) | ||
| 353 | if (a->name == NULL) | ||
| 354 | PANIC ("unknown action `%s' (use -h for help)", *argv); | ||
| 355 | else if (!strcmp (*argv, a->name)) | ||
| 356 | break; | ||
| 357 | |||
| 358 | /* Check for required arguments. */ | ||
| 359 | for (i = 1; i < a->argc; i++) | ||
| 360 | if (argv[i] == NULL) | ||
| 361 | PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1); | ||
| 362 | |||
| 363 | /* Invoke action and advance. */ | ||
| 364 | a->function (argv); | ||
| 365 | argv += a->argc; | ||
| 366 | } | ||
| 367 | |||
| 368 | } | ||
| 369 | |||
| 370 | /* Prints a kernel command line help message and powers off the | ||
| 371 | machine. */ | ||
| 372 | static void | ||
| 373 | usage (void) | ||
| 374 | { | ||
| 375 | printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n" | ||
| 376 | "Options must precede actions.\n" | ||
| 377 | "Actions are executed in the order specified.\n" | ||
| 378 | "\nAvailable actions:\n" | ||
| 379 | #ifdef USERPROG | ||
| 380 | " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n" | ||
| 381 | #else | ||
| 382 | " run TEST Run TEST.\n" | ||
| 383 | #endif | ||
| 384 | #ifdef FILESYS | ||
| 385 | " ls List files in the root directory.\n" | ||
| 386 | " cat FILE Print FILE to the console.\n" | ||
| 387 | " rm FILE Delete FILE.\n" | ||
| 388 | "Use these actions indirectly via `pintos' -g and -p options:\n" | ||
| 389 | " extract Untar from scratch device into file system.\n" | ||
| 390 | " append FILE Append FILE to tar file on scratch device.\n" | ||
| 391 | #endif | ||
| 392 | "\nOptions:\n" | ||
| 393 | " -h Print this help message and power off.\n" | ||
| 394 | " -q Power off VM after actions or on panic.\n" | ||
| 395 | " -r Reboot after actions.\n" | ||
| 396 | #ifdef FILESYS | ||
| 397 | " -f Format file system device during startup.\n" | ||
| 398 | " -filesys=BDEV Use BDEV for file system instead of default.\n" | ||
| 399 | " -scratch=BDEV Use BDEV for scratch instead of default.\n" | ||
| 400 | #ifdef VM | ||
| 401 | " -swap=BDEV Use BDEV for swap instead of default.\n" | ||
| 402 | #endif | ||
| 403 | #endif | ||
| 404 | " -rs=SEED Set random number seed to SEED.\n" | ||
| 405 | " -mlfqs Use multi-level feedback queue scheduler.\n" | ||
| 406 | #ifdef USERPROG | ||
| 407 | " -ul=COUNT Limit user memory to COUNT pages.\n" | ||
| 408 | #endif | ||
| 409 | ); | ||
| 410 | shutdown_power_off (); | ||
| 411 | } | ||
| 412 | |||
| 413 | #ifdef FILESYS | ||
| 414 | /* Figure out what block devices to cast in the various Pintos roles. */ | ||
| 415 | static void | ||
| 416 | locate_block_devices (void) | ||
| 417 | { | ||
| 418 | locate_block_device (BLOCK_FILESYS, filesys_bdev_name); | ||
| 419 | locate_block_device (BLOCK_SCRATCH, scratch_bdev_name); | ||
| 420 | #ifdef VM | ||
| 421 | locate_block_device (BLOCK_SWAP, swap_bdev_name); | ||
| 422 | #endif | ||
| 423 | } | ||
| 424 | |||
| 425 | /* Figures out what block device to use for the given ROLE: the | ||
| 426 | block device with the given NAME, if NAME is non-null, | ||
| 427 | otherwise the first block device in probe order of type | ||
| 428 | ROLE. */ | ||
| 429 | static void | ||
| 430 | locate_block_device (enum block_type role, const char *name) | ||
| 431 | { | ||
| 432 | struct block *block = NULL; | ||
| 433 | |||
| 434 | if (name != NULL) | ||
| 435 | { | ||
| 436 | block = block_get_by_name (name); | ||
| 437 | if (block == NULL) | ||
| 438 | PANIC ("No such block device \"%s\"", name); | ||
| 439 | } | ||
| 440 | else | ||
| 441 | { | ||
| 442 | for (block = block_first (); block != NULL; block = block_next (block)) | ||
| 443 | if (block_type (block) == role) | ||
| 444 | break; | ||
| 445 | } | ||
| 446 | |||
| 447 | if (block != NULL) | ||
| 448 | { | ||
| 449 | printf ("%s: using %s\n", block_type_name (role), block_name (block)); | ||
| 450 | block_set_role (role, block); | ||
| 451 | } | ||
| 452 | } | ||
| 453 | #endif | ||
diff --git a/pintos-progos/threads/init.h b/pintos-progos/threads/init.h new file mode 100644 index 0000000..8a3df90 --- /dev/null +++ b/pintos-progos/threads/init.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef THREADS_INIT_H | ||
| 2 | #define THREADS_INIT_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <stdint.h> | ||
| 8 | |||
| 9 | /* Page directory with kernel mappings only. */ | ||
| 10 | extern uint32_t *init_page_dir; | ||
| 11 | |||
| 12 | #endif /* threads/init.h */ | ||
diff --git a/pintos-progos/threads/interrupt.c b/pintos-progos/threads/interrupt.c new file mode 100644 index 0000000..e3b90dc --- /dev/null +++ b/pintos-progos/threads/interrupt.c | |||
| @@ -0,0 +1,438 @@ | |||
| 1 | #include "threads/interrupt.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <inttypes.h> | ||
| 4 | #include <stdint.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include "threads/flags.h" | ||
| 7 | #include "threads/intr-stubs.h" | ||
| 8 | #include "threads/io.h" | ||
| 9 | #include "threads/thread.h" | ||
| 10 | #include "threads/vaddr.h" | ||
| 11 | #include "devices/timer.h" | ||
| 12 | |||
| 13 | /* Programmable Interrupt Controller (PIC) registers. | ||
| 14 | A PC has two PICs, called the master and slave PICs, with the | ||
| 15 | slave attached ("cascaded") to the master IRQ line 2. */ | ||
| 16 | #define PIC0_CTRL 0x20 /* Master PIC control register address. */ | ||
| 17 | #define PIC0_DATA 0x21 /* Master PIC data register address. */ | ||
| 18 | #define PIC1_CTRL 0xa0 /* Slave PIC control register address. */ | ||
| 19 | #define PIC1_DATA 0xa1 /* Slave PIC data register address. */ | ||
| 20 | |||
| 21 | /* Number of x86 interrupts. */ | ||
| 22 | #define INTR_CNT 256 | ||
| 23 | |||
| 24 | /* The Interrupt Descriptor Table (IDT). The format is fixed by | ||
| 25 | the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor | ||
| 26 | Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By | ||
| 27 | Exception- or Interrupt-Handler Procedure". */ | ||
| 28 | static uint64_t idt[INTR_CNT]; | ||
| 29 | |||
| 30 | /* Interrupt handler functions for each interrupt. */ | ||
| 31 | static intr_handler_func *intr_handlers[INTR_CNT]; | ||
| 32 | |||
| 33 | /* Names for each interrupt, for debugging purposes. */ | ||
| 34 | static const char *intr_names[INTR_CNT]; | ||
| 35 | |||
| 36 | /* Number of unexpected interrupts for each vector. An | ||
| 37 | unexpected interrupt is one that has no registered handler. */ | ||
| 38 | static unsigned int unexpected_cnt[INTR_CNT]; | ||
| 39 | |||
| 40 | /* External interrupts are those generated by devices outside the | ||
| 41 | CPU, such as the timer. External interrupts run with | ||
| 42 | interrupts turned off, so they never nest, nor are they ever | ||
| 43 | pre-empted. Handlers for external interrupts also may not | ||
| 44 | sleep, although they may invoke intr_yield_on_return() to | ||
| 45 | request that a new process be scheduled just before the | ||
| 46 | interrupt returns. */ | ||
| 47 | static bool in_external_intr; /* Are we processing an external interrupt? */ | ||
| 48 | static bool yield_on_return; /* Should we yield on interrupt return? */ | ||
| 49 | |||
| 50 | /* Programmable Interrupt Controller helpers. */ | ||
| 51 | static void pic_init (void); | ||
| 52 | static void pic_end_of_interrupt (int irq); | ||
| 53 | |||
| 54 | /* Interrupt Descriptor Table helpers. */ | ||
| 55 | static uint64_t make_intr_gate (void (*) (void), int dpl); | ||
| 56 | static uint64_t make_trap_gate (void (*) (void), int dpl); | ||
| 57 | static inline uint64_t make_idtr_operand (uint16_t limit, void *base); | ||
| 58 | |||
| 59 | /* Interrupt handlers. */ | ||
| 60 | void intr_handler (struct intr_frame *args); | ||
| 61 | static void unexpected_interrupt (const struct intr_frame *); | ||
| 62 | |||
| 63 | /* Returns the current interrupt status. */ | ||
| 64 | enum intr_level | ||
| 65 | intr_get_level (void) | ||
| 66 | { | ||
| 67 | uint32_t flags; | ||
| 68 | |||
| 69 | /* Push the flags register on the processor stack, then pop the | ||
| 70 | value off the stack into `flags'. See [IA32-v2b] "PUSHF" | ||
| 71 | and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware | ||
| 72 | Interrupts". */ | ||
| 73 | asm volatile ("pushfl; popl %0" : "=g" (flags)); | ||
| 74 | |||
| 75 | return flags & FLAG_IF ? INTR_ON : INTR_OFF; | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Enables or disables interrupts as specified by LEVEL and | ||
| 79 | returns the previous interrupt status. */ | ||
| 80 | enum intr_level | ||
| 81 | intr_set_level (enum intr_level level) | ||
| 82 | { | ||
| 83 | return level == INTR_ON ? intr_enable () : intr_disable (); | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Enables interrupts and returns the previous interrupt status. */ | ||
| 87 | enum intr_level | ||
| 88 | intr_enable (void) | ||
| 89 | { | ||
| 90 | enum intr_level old_level = intr_get_level (); | ||
| 91 | ASSERT (!intr_context ()); | ||
| 92 | |||
| 93 | /* Enable interrupts by setting the interrupt flag. | ||
| 94 | |||
| 95 | See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable | ||
| 96 | Hardware Interrupts". */ | ||
| 97 | asm volatile ("sti"); | ||
| 98 | |||
| 99 | return old_level; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Disables interrupts and returns the previous interrupt status. */ | ||
| 103 | enum intr_level | ||
| 104 | intr_disable (void) | ||
| 105 | { | ||
| 106 | enum intr_level old_level = intr_get_level (); | ||
| 107 | |||
| 108 | /* Disable interrupts by clearing the interrupt flag. | ||
| 109 | See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable | ||
| 110 | Hardware Interrupts". */ | ||
| 111 | asm volatile ("cli" : : : "memory"); | ||
| 112 | |||
| 113 | return old_level; | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Initializes the interrupt system. */ | ||
| 117 | void | ||
| 118 | intr_init (void) | ||
| 119 | { | ||
| 120 | uint64_t idtr_operand; | ||
| 121 | int i; | ||
| 122 | |||
| 123 | /* Initialize interrupt controller. */ | ||
| 124 | pic_init (); | ||
| 125 | |||
| 126 | /* Initialize IDT. */ | ||
| 127 | for (i = 0; i < INTR_CNT; i++) | ||
| 128 | idt[i] = make_intr_gate (intr_stubs[i], 0); | ||
| 129 | |||
| 130 | /* Load IDT register. | ||
| 131 | See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt | ||
| 132 | Descriptor Table (IDT)". */ | ||
| 133 | idtr_operand = make_idtr_operand (sizeof idt - 1, idt); | ||
| 134 | asm volatile ("lidt %0" : : "m" (idtr_operand)); | ||
| 135 | |||
| 136 | /* Initialize intr_names. */ | ||
| 137 | for (i = 0; i < INTR_CNT; i++) | ||
| 138 | intr_names[i] = "unknown"; | ||
| 139 | intr_names[0] = "#DE Divide Error"; | ||
| 140 | intr_names[1] = "#DB Debug Exception"; | ||
| 141 | intr_names[2] = "NMI Interrupt"; | ||
| 142 | intr_names[3] = "#BP Breakpoint Exception"; | ||
| 143 | intr_names[4] = "#OF Overflow Exception"; | ||
| 144 | intr_names[5] = "#BR BOUND Range Exceeded Exception"; | ||
| 145 | intr_names[6] = "#UD Invalid Opcode Exception"; | ||
| 146 | intr_names[7] = "#NM Device Not Available Exception"; | ||
| 147 | intr_names[8] = "#DF Double Fault Exception"; | ||
| 148 | intr_names[9] = "Coprocessor Segment Overrun"; | ||
| 149 | intr_names[10] = "#TS Invalid TSS Exception"; | ||
| 150 | intr_names[11] = "#NP Segment Not Present"; | ||
| 151 | intr_names[12] = "#SS Stack Fault Exception"; | ||
| 152 | intr_names[13] = "#GP General Protection Exception"; | ||
| 153 | intr_names[14] = "#PF Page-Fault Exception"; | ||
| 154 | intr_names[16] = "#MF x87 FPU Floating-Point Error"; | ||
| 155 | intr_names[17] = "#AC Alignment Check Exception"; | ||
| 156 | intr_names[18] = "#MC Machine-Check Exception"; | ||
| 157 | intr_names[19] = "#XF SIMD Floating-Point Exception"; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Registers interrupt VEC_NO to invoke HANDLER with descriptor | ||
| 161 | privilege level DPL. Names the interrupt NAME for debugging | ||
| 162 | purposes. The interrupt handler will be invoked with | ||
| 163 | interrupt status set to LEVEL. */ | ||
| 164 | static void | ||
| 165 | register_handler (uint8_t vec_no, int dpl, enum intr_level level, | ||
| 166 | intr_handler_func *handler, const char *name) | ||
| 167 | { | ||
| 168 | ASSERT (intr_handlers[vec_no] == NULL); | ||
| 169 | if (level == INTR_ON) | ||
| 170 | idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl); | ||
| 171 | else | ||
| 172 | idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl); | ||
| 173 | intr_handlers[vec_no] = handler; | ||
| 174 | intr_names[vec_no] = name; | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Registers external interrupt VEC_NO to invoke HANDLER, which | ||
| 178 | is named NAME for debugging purposes. The handler will | ||
| 179 | execute with interrupts disabled. */ | ||
| 180 | void | ||
| 181 | intr_register_ext (uint8_t vec_no, intr_handler_func *handler, | ||
| 182 | const char *name) | ||
| 183 | { | ||
| 184 | ASSERT (vec_no >= 0x20 && vec_no <= 0x2f); | ||
| 185 | register_handler (vec_no, 0, INTR_OFF, handler, name); | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Registers internal interrupt VEC_NO to invoke HANDLER, which | ||
| 189 | is named NAME for debugging purposes. The interrupt handler | ||
| 190 | will be invoked with interrupt status LEVEL. | ||
| 191 | |||
| 192 | The handler will have descriptor privilege level DPL, meaning | ||
| 193 | that it can be invoked intentionally when the processor is in | ||
| 194 | the DPL or lower-numbered ring. In practice, DPL==3 allows | ||
| 195 | user mode to invoke the interrupts and DPL==0 prevents such | ||
| 196 | invocation. Faults and exceptions that occur in user mode | ||
| 197 | still cause interrupts with DPL==0 to be invoked. See | ||
| 198 | [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1 | ||
| 199 | "Accessing Nonconforming Code Segments" for further | ||
| 200 | discussion. */ | ||
| 201 | void | ||
| 202 | intr_register_int (uint8_t vec_no, int dpl, enum intr_level level, | ||
| 203 | intr_handler_func *handler, const char *name) | ||
| 204 | { | ||
| 205 | ASSERT (vec_no < 0x20 || vec_no > 0x2f); | ||
| 206 | register_handler (vec_no, dpl, level, handler, name); | ||
| 207 | } | ||
| 208 | |||
| 209 | /* Returns true during processing of an external interrupt | ||
| 210 | and false at all other times. */ | ||
| 211 | bool | ||
| 212 | intr_context (void) | ||
| 213 | { | ||
| 214 | return in_external_intr; | ||
| 215 | } | ||
| 216 | |||
| 217 | /* During processing of an external interrupt, directs the | ||
| 218 | interrupt handler to yield to a new process just before | ||
| 219 | returning from the interrupt. May not be called at any other | ||
| 220 | time. */ | ||
| 221 | void | ||
| 222 | intr_yield_on_return (void) | ||
| 223 | { | ||
| 224 | ASSERT (intr_context ()); | ||
| 225 | yield_on_return = true; | ||
| 226 | } | ||
| 227 | |||
| 228 | /* 8259A Programmable Interrupt Controller. */ | ||
| 229 | |||
| 230 | /* Initializes the PICs. Refer to [8259A] for details. | ||
| 231 | |||
| 232 | By default, interrupts 0...15 delivered by the PICs will go to | ||
| 233 | interrupt vectors 0...15. Those vectors are also used for CPU | ||
| 234 | traps and exceptions, so we reprogram the PICs so that | ||
| 235 | interrupts 0...15 are delivered to interrupt vectors 32...47 | ||
| 236 | (0x20...0x2f) instead. */ | ||
| 237 | static void | ||
| 238 | pic_init (void) | ||
| 239 | { | ||
| 240 | /* Mask all interrupts on both PICs. */ | ||
| 241 | outb (PIC0_DATA, 0xff); | ||
| 242 | outb (PIC1_DATA, 0xff); | ||
| 243 | |||
| 244 | /* Initialize master. */ | ||
| 245 | outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */ | ||
| 246 | outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */ | ||
| 247 | outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */ | ||
| 248 | outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */ | ||
| 249 | |||
| 250 | /* Initialize slave. */ | ||
| 251 | outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */ | ||
| 252 | outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */ | ||
| 253 | outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */ | ||
| 254 | outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */ | ||
| 255 | |||
| 256 | /* Unmask all interrupts. */ | ||
| 257 | outb (PIC0_DATA, 0x00); | ||
| 258 | outb (PIC1_DATA, 0x00); | ||
| 259 | } | ||
| 260 | |||
| 261 | /* Sends an end-of-interrupt signal to the PIC for the given IRQ. | ||
| 262 | If we don't acknowledge the IRQ, it will never be delivered to | ||
| 263 | us again, so this is important. */ | ||
| 264 | static void | ||
| 265 | pic_end_of_interrupt (int irq) | ||
| 266 | { | ||
| 267 | ASSERT (irq >= 0x20 && irq < 0x30); | ||
| 268 | |||
| 269 | /* Acknowledge master PIC. */ | ||
| 270 | outb (0x20, 0x20); | ||
| 271 | |||
| 272 | /* Acknowledge slave PIC if this is a slave interrupt. */ | ||
| 273 | if (irq >= 0x28) | ||
| 274 | outb (0xa0, 0x20); | ||
| 275 | } | ||
| 276 | |||
| 277 | /* Creates an gate that invokes FUNCTION. | ||
| 278 | |||
| 279 | The gate has descriptor privilege level DPL, meaning that it | ||
| 280 | can be invoked intentionally when the processor is in the DPL | ||
| 281 | or lower-numbered ring. In practice, DPL==3 allows user mode | ||
| 282 | to call into the gate and DPL==0 prevents such calls. Faults | ||
| 283 | and exceptions that occur in user mode still cause gates with | ||
| 284 | DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege | ||
| 285 | Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments" | ||
| 286 | for further discussion. | ||
| 287 | |||
| 288 | TYPE must be either 14 (for an interrupt gate) or 15 (for a | ||
| 289 | trap gate). The difference is that entering an interrupt gate | ||
| 290 | disables interrupts, but entering a trap gate does not. See | ||
| 291 | [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or | ||
| 292 | Interrupt-Handler Procedure" for discussion. */ | ||
| 293 | static uint64_t | ||
| 294 | make_gate (void (*function) (void), int dpl, int type) | ||
| 295 | { | ||
| 296 | uint32_t e0, e1; | ||
| 297 | |||
| 298 | ASSERT (function != NULL); | ||
| 299 | ASSERT (dpl >= 0 && dpl <= 3); | ||
| 300 | ASSERT (type >= 0 && type <= 15); | ||
| 301 | |||
| 302 | e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */ | ||
| 303 | | (SEL_KCSEG << 16)); /* Target code segment. */ | ||
| 304 | |||
| 305 | e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */ | ||
| 306 | | (1 << 15) /* Present. */ | ||
| 307 | | ((uint32_t) dpl << 13) /* Descriptor privilege level. */ | ||
| 308 | | (0 << 12) /* System. */ | ||
| 309 | | ((uint32_t) type << 8)); /* Gate type. */ | ||
| 310 | |||
| 311 | return e0 | ((uint64_t) e1 << 32); | ||
| 312 | } | ||
| 313 | |||
| 314 | /* Creates an interrupt gate that invokes FUNCTION with the given | ||
| 315 | DPL. */ | ||
| 316 | static uint64_t | ||
| 317 | make_intr_gate (void (*function) (void), int dpl) | ||
| 318 | { | ||
| 319 | return make_gate (function, dpl, 14); | ||
| 320 | } | ||
| 321 | |||
| 322 | /* Creates a trap gate that invokes FUNCTION with the given | ||
| 323 | DPL. */ | ||
| 324 | static uint64_t | ||
| 325 | make_trap_gate (void (*function) (void), int dpl) | ||
| 326 | { | ||
| 327 | return make_gate (function, dpl, 15); | ||
| 328 | } | ||
| 329 | |||
| 330 | /* Returns a descriptor that yields the given LIMIT and BASE when | ||
| 331 | used as an operand for the LIDT instruction. */ | ||
| 332 | static inline uint64_t | ||
| 333 | make_idtr_operand (uint16_t limit, void *base) | ||
| 334 | { | ||
| 335 | return limit | ((uint64_t) (uint32_t) base << 16); | ||
| 336 | } | ||
| 337 | |||
| 338 | /* Interrupt handlers. */ | ||
| 339 | |||
| 340 | /* Handler for all interrupts, faults, and exceptions. This | ||
| 341 | function is called by the assembly language interrupt stubs in | ||
| 342 | intr-stubs.S. FRAME describes the interrupt and the | ||
| 343 | interrupted thread's registers. */ | ||
| 344 | void | ||
| 345 | intr_handler (struct intr_frame *frame) | ||
| 346 | { | ||
| 347 | bool external; | ||
| 348 | intr_handler_func *handler; | ||
| 349 | |||
| 350 | /* External interrupts are special. | ||
| 351 | We only handle one at a time (so interrupts must be off) | ||
| 352 | and they need to be acknowledged on the PIC (see below). | ||
| 353 | An external interrupt handler cannot sleep. */ | ||
| 354 | external = frame->vec_no >= 0x20 && frame->vec_no < 0x30; | ||
| 355 | if (external) | ||
| 356 | { | ||
| 357 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 358 | ASSERT (!intr_context ()); | ||
| 359 | |||
| 360 | in_external_intr = true; | ||
| 361 | yield_on_return = false; | ||
| 362 | } | ||
| 363 | |||
| 364 | /* Invoke the interrupt's handler. */ | ||
| 365 | handler = intr_handlers[frame->vec_no]; | ||
| 366 | if (handler != NULL) | ||
| 367 | handler (frame); | ||
| 368 | else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f) | ||
| 369 | { | ||
| 370 | /* There is no handler, but this interrupt can trigger | ||
| 371 | spuriously due to a hardware fault or hardware race | ||
| 372 | condition. Ignore it. */ | ||
| 373 | } | ||
| 374 | else | ||
| 375 | unexpected_interrupt (frame); | ||
| 376 | |||
| 377 | /* Complete the processing of an external interrupt. */ | ||
| 378 | if (external) | ||
| 379 | { | ||
| 380 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 381 | ASSERT (intr_context ()); | ||
| 382 | |||
| 383 | in_external_intr = false; | ||
| 384 | pic_end_of_interrupt (frame->vec_no); | ||
| 385 | |||
| 386 | if (yield_on_return) | ||
| 387 | thread_yield (); | ||
| 388 | } | ||
| 389 | } | ||
| 390 | |||
| 391 | /* Handles an unexpected interrupt with interrupt frame F. An | ||
| 392 | unexpected interrupt is one that has no registered handler. */ | ||
| 393 | static void | ||
| 394 | unexpected_interrupt (const struct intr_frame *f) | ||
| 395 | { | ||
| 396 | /* Count the number so far. */ | ||
| 397 | unsigned int n = ++unexpected_cnt[f->vec_no]; | ||
| 398 | |||
| 399 | /* If the number is a power of 2, print a message. This rate | ||
| 400 | limiting means that we get information about an uncommon | ||
| 401 | unexpected interrupt the first time and fairly often after | ||
| 402 | that, but one that occurs many times will not overwhelm the | ||
| 403 | console. */ | ||
| 404 | if ((n & (n - 1)) == 0) | ||
| 405 | printf ("Unexpected interrupt %#04x (%s)\n", | ||
| 406 | f->vec_no, intr_names[f->vec_no]); | ||
| 407 | } | ||
| 408 | |||
| 409 | /* Dumps interrupt frame F to the console, for debugging. */ | ||
| 410 | void | ||
| 411 | intr_dump_frame (const struct intr_frame *f) | ||
| 412 | { | ||
| 413 | uint32_t cr2; | ||
| 414 | |||
| 415 | /* Store current value of CR2 into `cr2'. | ||
| 416 | CR2 is the linear address of the last page fault. | ||
| 417 | See [IA32-v2a] "MOV--Move to/from Control Registers" and | ||
| 418 | [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception | ||
| 419 | (#PF)". */ | ||
| 420 | asm ("movl %%cr2, %0" : "=r" (cr2)); | ||
| 421 | |||
| 422 | printf ("Interrupt %#04x (%s) at eip=%p\n", | ||
| 423 | f->vec_no, intr_names[f->vec_no], f->eip); | ||
| 424 | printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code); | ||
| 425 | printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n", | ||
| 426 | f->eax, f->ebx, f->ecx, f->edx); | ||
| 427 | printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n", | ||
| 428 | f->esi, f->edi, (uint32_t) f->esp, f->ebp); | ||
| 429 | printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n", | ||
| 430 | f->cs, f->ds, f->es, f->ss); | ||
| 431 | } | ||
| 432 | |||
| 433 | /* Returns the name of interrupt VEC. */ | ||
| 434 | const char * | ||
| 435 | intr_name (uint8_t vec) | ||
| 436 | { | ||
| 437 | return intr_names[vec]; | ||
| 438 | } | ||
diff --git a/pintos-progos/threads/interrupt.h b/pintos-progos/threads/interrupt.h new file mode 100644 index 0000000..d43e06d --- /dev/null +++ b/pintos-progos/threads/interrupt.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | #ifndef THREADS_INTERRUPT_H | ||
| 2 | #define THREADS_INTERRUPT_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | /* Interrupts on or off? */ | ||
| 8 | enum intr_level | ||
| 9 | { | ||
| 10 | INTR_OFF, /* Interrupts disabled. */ | ||
| 11 | INTR_ON /* Interrupts enabled. */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | enum intr_level intr_get_level (void); | ||
| 15 | enum intr_level intr_set_level (enum intr_level); | ||
| 16 | enum intr_level intr_enable (void); | ||
| 17 | enum intr_level intr_disable (void); | ||
| 18 | |||
| 19 | /* Interrupt stack frame. */ | ||
| 20 | struct intr_frame | ||
| 21 | { | ||
| 22 | /* Pushed by intr_entry in intr-stubs.S. | ||
| 23 | These are the interrupted task's saved registers. */ | ||
| 24 | uint32_t edi; /* Saved EDI. */ | ||
| 25 | uint32_t esi; /* Saved ESI. */ | ||
| 26 | uint32_t ebp; /* Saved EBP. */ | ||
| 27 | uint32_t esp_dummy; /* Not used. */ | ||
| 28 | uint32_t ebx; /* Saved EBX. */ | ||
| 29 | uint32_t edx; /* Saved EDX. */ | ||
| 30 | uint32_t ecx; /* Saved ECX. */ | ||
| 31 | uint32_t eax; /* Saved EAX. */ | ||
| 32 | uint16_t gs, :16; /* Saved GS segment register. */ | ||
| 33 | uint16_t fs, :16; /* Saved FS segment register. */ | ||
| 34 | uint16_t es, :16; /* Saved ES segment register. */ | ||
| 35 | uint16_t ds, :16; /* Saved DS segment register. */ | ||
| 36 | |||
| 37 | /* Pushed by intrNN_stub in intr-stubs.S. */ | ||
| 38 | uint32_t vec_no; /* Interrupt vector number. */ | ||
| 39 | |||
| 40 | /* Sometimes pushed by the CPU, | ||
| 41 | otherwise for consistency pushed as 0 by intrNN_stub. | ||
| 42 | The CPU puts it just under `eip', but we move it here. */ | ||
| 43 | uint32_t error_code; /* Error code. */ | ||
| 44 | |||
| 45 | /* Pushed by intrNN_stub in intr-stubs.S. | ||
| 46 | This frame pointer eases interpretation of backtraces. */ | ||
| 47 | void *frame_pointer; /* Saved EBP (frame pointer). */ | ||
| 48 | |||
| 49 | /* Pushed by the CPU. | ||
| 50 | These are the interrupted task's saved registers. */ | ||
| 51 | void (*eip) (void); /* Next instruction to execute. */ | ||
| 52 | uint16_t cs, :16; /* Code segment for eip. */ | ||
| 53 | uint32_t eflags; /* Saved CPU flags. */ | ||
| 54 | void *esp; /* Saved stack pointer. */ | ||
| 55 | uint16_t ss, :16; /* Data segment for esp. */ | ||
| 56 | }; | ||
| 57 | |||
| 58 | typedef void intr_handler_func (struct intr_frame *); | ||
| 59 | |||
| 60 | void intr_init (void); | ||
| 61 | void intr_register_ext (uint8_t vec, intr_handler_func *, const char *name); | ||
| 62 | void intr_register_int (uint8_t vec, int dpl, enum intr_level, | ||
| 63 | intr_handler_func *, const char *name); | ||
| 64 | bool intr_context (void); | ||
| 65 | void intr_yield_on_return (void); | ||
| 66 | |||
| 67 | void intr_dump_frame (const struct intr_frame *); | ||
| 68 | const char *intr_name (uint8_t vec); | ||
| 69 | |||
| 70 | #endif /* threads/interrupt.h */ | ||
diff --git a/pintos-progos/threads/intr-stubs.S b/pintos-progos/threads/intr-stubs.S new file mode 100644 index 0000000..adb674e --- /dev/null +++ b/pintos-progos/threads/intr-stubs.S | |||
| @@ -0,0 +1,203 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | .text | ||
| 4 | |||
| 5 | /* Main interrupt entry point. | ||
| 6 | |||
| 7 | An internal or external interrupt starts in one of the | ||
| 8 | intrNN_stub routines, which push the `struct intr_frame' | ||
| 9 | frame_pointer, error_code, and vec_no members on the stack, | ||
| 10 | then jump here. | ||
| 11 | |||
| 12 | We save the rest of the `struct intr_frame' members to the | ||
| 13 | stack, set up some registers as needed by the kernel, and then | ||
| 14 | call intr_handler(), which actually handles the interrupt. | ||
| 15 | |||
| 16 | We "fall through" to intr_exit to return from the interrupt. | ||
| 17 | */ | ||
| 18 | .func intr_entry | ||
| 19 | intr_entry: | ||
| 20 | /* Save caller's registers. */ | ||
| 21 | pushl %ds | ||
| 22 | pushl %es | ||
| 23 | pushl %fs | ||
| 24 | pushl %gs | ||
| 25 | pushal | ||
| 26 | |||
| 27 | /* Set up kernel environment. */ | ||
| 28 | cld /* String instructions go upward. */ | ||
| 29 | mov $SEL_KDSEG, %eax /* Initialize segment registers. */ | ||
| 30 | mov %eax, %ds | ||
| 31 | mov %eax, %es | ||
| 32 | leal 56(%esp), %ebp /* Set up frame pointer. */ | ||
| 33 | |||
| 34 | /* Call interrupt handler. */ | ||
| 35 | pushl %esp | ||
| 36 | .globl intr_handler | ||
| 37 | call intr_handler | ||
| 38 | addl $4, %esp | ||
| 39 | .endfunc | ||
| 40 | |||
| 41 | /* Interrupt exit. | ||
| 42 | |||
| 43 | Restores the caller's registers, discards extra data on the | ||
| 44 | stack, and returns to the caller. | ||
| 45 | |||
| 46 | This is a separate function because it is called directly when | ||
| 47 | we launch a new user process (see start_process() in | ||
| 48 | userprog/process.c). */ | ||
| 49 | .globl intr_exit | ||
| 50 | .func intr_exit | ||
| 51 | intr_exit: | ||
| 52 | /* Restore caller's registers. */ | ||
| 53 | popal | ||
| 54 | popl %gs | ||
| 55 | popl %fs | ||
| 56 | popl %es | ||
| 57 | popl %ds | ||
| 58 | |||
| 59 | /* Discard `struct intr_frame' vec_no, error_code, | ||
| 60 | frame_pointer members. */ | ||
| 61 | addl $12, %esp | ||
| 62 | |||
| 63 | /* Return to caller. */ | ||
| 64 | iret | ||
| 65 | .endfunc | ||
| 66 | |||
| 67 | /* Interrupt stubs. | ||
| 68 | |||
| 69 | This defines 256 fragments of code, named `intr00_stub' | ||
| 70 | through `intrff_stub', each of which is used as the entry | ||
| 71 | point for the corresponding interrupt vector. It also puts | ||
| 72 | the address of each of these functions in the correct spot in | ||
| 73 | `intr_stubs', an array of function pointers. | ||
| 74 | |||
| 75 | Most of the stubs do this: | ||
| 76 | |||
| 77 | 1. Push %ebp on the stack (frame_pointer in `struct intr_frame'). | ||
| 78 | |||
| 79 | 2. Push 0 on the stack (error_code). | ||
| 80 | |||
| 81 | 3. Push the interrupt number on the stack (vec_no). | ||
| 82 | |||
| 83 | The CPU pushes an extra "error code" on the stack for a few | ||
| 84 | interrupts. Because we want %ebp to be where the error code | ||
| 85 | is, we follow a different path: | ||
| 86 | |||
| 87 | 1. Push a duplicate copy of the error code on the stack. | ||
| 88 | |||
| 89 | 2. Replace the original copy of the error code by %ebp. | ||
| 90 | |||
| 91 | 3. Push the interrupt number on the stack. */ | ||
| 92 | |||
| 93 | .data | ||
| 94 | .globl intr_stubs | ||
| 95 | intr_stubs: | ||
| 96 | |||
| 97 | /* This implements steps 1 and 2, described above, in the common | ||
| 98 | case where we just push a 0 error code. */ | ||
| 99 | #define zero \ | ||
| 100 | pushl %ebp; \ | ||
| 101 | pushl $0 | ||
| 102 | |||
| 103 | /* This implements steps 1 and 2, described above, in the case | ||
| 104 | where the CPU already pushed an error code. */ | ||
| 105 | #define REAL \ | ||
| 106 | pushl (%esp); \ | ||
| 107 | movl %ebp, 4(%esp) | ||
| 108 | |||
| 109 | /* Emits a stub for interrupt vector NUMBER. | ||
| 110 | TYPE is `zero', for the case where we push a 0 error code, | ||
| 111 | or `REAL', if the CPU pushes an error code for us. */ | ||
| 112 | #define STUB(NUMBER, TYPE) \ | ||
| 113 | .text; \ | ||
| 114 | .func intr##NUMBER##_stub; \ | ||
| 115 | intr##NUMBER##_stub: \ | ||
| 116 | TYPE; \ | ||
| 117 | push $0x##NUMBER; \ | ||
| 118 | jmp intr_entry; \ | ||
| 119 | .endfunc; \ | ||
| 120 | \ | ||
| 121 | .data; \ | ||
| 122 | .long intr##NUMBER##_stub; | ||
| 123 | |||
| 124 | /* All the stubs. */ | ||
| 125 | STUB(00, zero) STUB(01, zero) STUB(02, zero) STUB(03, zero) | ||
| 126 | STUB(04, zero) STUB(05, zero) STUB(06, zero) STUB(07, zero) | ||
| 127 | STUB(08, REAL) STUB(09, zero) STUB(0a, REAL) STUB(0b, REAL) | ||
| 128 | STUB(0c, zero) STUB(0d, REAL) STUB(0e, REAL) STUB(0f, zero) | ||
| 129 | |||
| 130 | STUB(10, zero) STUB(11, REAL) STUB(12, zero) STUB(13, zero) | ||
| 131 | STUB(14, zero) STUB(15, zero) STUB(16, zero) STUB(17, zero) | ||
| 132 | STUB(18, REAL) STUB(19, zero) STUB(1a, REAL) STUB(1b, REAL) | ||
| 133 | STUB(1c, zero) STUB(1d, REAL) STUB(1e, REAL) STUB(1f, zero) | ||
| 134 | |||
| 135 | STUB(20, zero) STUB(21, zero) STUB(22, zero) STUB(23, zero) | ||
| 136 | STUB(24, zero) STUB(25, zero) STUB(26, zero) STUB(27, zero) | ||
| 137 | STUB(28, zero) STUB(29, zero) STUB(2a, zero) STUB(2b, zero) | ||
| 138 | STUB(2c, zero) STUB(2d, zero) STUB(2e, zero) STUB(2f, zero) | ||
| 139 | |||
| 140 | STUB(30, zero) STUB(31, zero) STUB(32, zero) STUB(33, zero) | ||
| 141 | STUB(34, zero) STUB(35, zero) STUB(36, zero) STUB(37, zero) | ||
| 142 | STUB(38, zero) STUB(39, zero) STUB(3a, zero) STUB(3b, zero) | ||
| 143 | STUB(3c, zero) STUB(3d, zero) STUB(3e, zero) STUB(3f, zero) | ||
| 144 | |||
| 145 | STUB(40, zero) STUB(41, zero) STUB(42, zero) STUB(43, zero) | ||
| 146 | STUB(44, zero) STUB(45, zero) STUB(46, zero) STUB(47, zero) | ||
| 147 | STUB(48, zero) STUB(49, zero) STUB(4a, zero) STUB(4b, zero) | ||
| 148 | STUB(4c, zero) STUB(4d, zero) STUB(4e, zero) STUB(4f, zero) | ||
| 149 | |||
| 150 | STUB(50, zero) STUB(51, zero) STUB(52, zero) STUB(53, zero) | ||
| 151 | STUB(54, zero) STUB(55, zero) STUB(56, zero) STUB(57, zero) | ||
| 152 | STUB(58, zero) STUB(59, zero) STUB(5a, zero) STUB(5b, zero) | ||
| 153 | STUB(5c, zero) STUB(5d, zero) STUB(5e, zero) STUB(5f, zero) | ||
| 154 | |||
| 155 | STUB(60, zero) STUB(61, zero) STUB(62, zero) STUB(63, zero) | ||
| 156 | STUB(64, zero) STUB(65, zero) STUB(66, zero) STUB(67, zero) | ||
| 157 | STUB(68, zero) STUB(69, zero) STUB(6a, zero) STUB(6b, zero) | ||
| 158 | STUB(6c, zero) STUB(6d, zero) STUB(6e, zero) STUB(6f, zero) | ||
| 159 | |||
| 160 | STUB(70, zero) STUB(71, zero) STUB(72, zero) STUB(73, zero) | ||
| 161 | STUB(74, zero) STUB(75, zero) STUB(76, zero) STUB(77, zero) | ||
| 162 | STUB(78, zero) STUB(79, zero) STUB(7a, zero) STUB(7b, zero) | ||
| 163 | STUB(7c, zero) STUB(7d, zero) STUB(7e, zero) STUB(7f, zero) | ||
| 164 | |||
| 165 | STUB(80, zero) STUB(81, zero) STUB(82, zero) STUB(83, zero) | ||
| 166 | STUB(84, zero) STUB(85, zero) STUB(86, zero) STUB(87, zero) | ||
| 167 | STUB(88, zero) STUB(89, zero) STUB(8a, zero) STUB(8b, zero) | ||
| 168 | STUB(8c, zero) STUB(8d, zero) STUB(8e, zero) STUB(8f, zero) | ||
| 169 | |||
| 170 | STUB(90, zero) STUB(91, zero) STUB(92, zero) STUB(93, zero) | ||
| 171 | STUB(94, zero) STUB(95, zero) STUB(96, zero) STUB(97, zero) | ||
| 172 | STUB(98, zero) STUB(99, zero) STUB(9a, zero) STUB(9b, zero) | ||
| 173 | STUB(9c, zero) STUB(9d, zero) STUB(9e, zero) STUB(9f, zero) | ||
| 174 | |||
| 175 | STUB(a0, zero) STUB(a1, zero) STUB(a2, zero) STUB(a3, zero) | ||
| 176 | STUB(a4, zero) STUB(a5, zero) STUB(a6, zero) STUB(a7, zero) | ||
| 177 | STUB(a8, zero) STUB(a9, zero) STUB(aa, zero) STUB(ab, zero) | ||
| 178 | STUB(ac, zero) STUB(ad, zero) STUB(ae, zero) STUB(af, zero) | ||
| 179 | |||
| 180 | STUB(b0, zero) STUB(b1, zero) STUB(b2, zero) STUB(b3, zero) | ||
| 181 | STUB(b4, zero) STUB(b5, zero) STUB(b6, zero) STUB(b7, zero) | ||
| 182 | STUB(b8, zero) STUB(b9, zero) STUB(ba, zero) STUB(bb, zero) | ||
| 183 | STUB(bc, zero) STUB(bd, zero) STUB(be, zero) STUB(bf, zero) | ||
| 184 | |||
| 185 | STUB(c0, zero) STUB(c1, zero) STUB(c2, zero) STUB(c3, zero) | ||
| 186 | STUB(c4, zero) STUB(c5, zero) STUB(c6, zero) STUB(c7, zero) | ||
| 187 | STUB(c8, zero) STUB(c9, zero) STUB(ca, zero) STUB(cb, zero) | ||
| 188 | STUB(cc, zero) STUB(cd, zero) STUB(ce, zero) STUB(cf, zero) | ||
| 189 | |||
| 190 | STUB(d0, zero) STUB(d1, zero) STUB(d2, zero) STUB(d3, zero) | ||
| 191 | STUB(d4, zero) STUB(d5, zero) STUB(d6, zero) STUB(d7, zero) | ||
| 192 | STUB(d8, zero) STUB(d9, zero) STUB(da, zero) STUB(db, zero) | ||
| 193 | STUB(dc, zero) STUB(dd, zero) STUB(de, zero) STUB(df, zero) | ||
| 194 | |||
| 195 | STUB(e0, zero) STUB(e1, zero) STUB(e2, zero) STUB(e3, zero) | ||
| 196 | STUB(e4, zero) STUB(e5, zero) STUB(e6, zero) STUB(e7, zero) | ||
| 197 | STUB(e8, zero) STUB(e9, zero) STUB(ea, zero) STUB(eb, zero) | ||
| 198 | STUB(ec, zero) STUB(ed, zero) STUB(ee, zero) STUB(ef, zero) | ||
| 199 | |||
| 200 | STUB(f0, zero) STUB(f1, zero) STUB(f2, zero) STUB(f3, zero) | ||
| 201 | STUB(f4, zero) STUB(f5, zero) STUB(f6, zero) STUB(f7, zero) | ||
| 202 | STUB(f8, zero) STUB(f9, zero) STUB(fa, zero) STUB(fb, zero) | ||
| 203 | STUB(fc, zero) STUB(fd, zero) STUB(fe, zero) STUB(ff, zero) | ||
diff --git a/pintos-progos/threads/intr-stubs.h b/pintos-progos/threads/intr-stubs.h new file mode 100644 index 0000000..9ceba15 --- /dev/null +++ b/pintos-progos/threads/intr-stubs.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | #ifndef THREADS_INTR_STUBS_H | ||
| 2 | #define THREADS_INTR_STUBS_H | ||
| 3 | |||
| 4 | /* Interrupt stubs. | ||
| 5 | |||
| 6 | These are little snippets of code in intr-stubs.S, one for | ||
| 7 | each of the 256 possible x86 interrupts. Each one does a | ||
| 8 | little bit of stack manipulation, then jumps to intr_entry(). | ||
| 9 | See intr-stubs.S for more information. | ||
| 10 | |||
| 11 | This array points to each of the interrupt stub entry points | ||
| 12 | so that intr_init() can easily find them. */ | ||
| 13 | typedef void intr_stub_func (void); | ||
| 14 | extern intr_stub_func *intr_stubs[256]; | ||
| 15 | |||
| 16 | /* Interrupt return path. */ | ||
| 17 | void intr_exit (void); | ||
| 18 | |||
| 19 | #endif /* threads/intr-stubs.h */ | ||
diff --git a/pintos-progos/threads/io.h b/pintos-progos/threads/io.h new file mode 100644 index 0000000..30d52da --- /dev/null +++ b/pintos-progos/threads/io.h | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | #ifndef THREADS_IO_H | ||
| 2 | #define THREADS_IO_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | /* Reads and returns a byte from PORT. */ | ||
| 8 | static inline uint8_t | ||
| 9 | inb (uint16_t port) | ||
| 10 | { | ||
| 11 | /* See [IA32-v2a] "IN". */ | ||
| 12 | uint8_t data; | ||
| 13 | asm volatile ("inb %w1, %b0" : "=a" (data) : "Nd" (port)); | ||
| 14 | return data; | ||
| 15 | } | ||
| 16 | |||
| 17 | /* Reads CNT bytes from PORT, one after another, and stores them | ||
| 18 | into the buffer starting at ADDR. */ | ||
| 19 | static inline void | ||
| 20 | insb (uint16_t port, void *addr, size_t cnt) | ||
| 21 | { | ||
| 22 | /* See [IA32-v2a] "INS". */ | ||
| 23 | asm volatile ("rep insb" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); | ||
| 24 | } | ||
| 25 | |||
| 26 | /* Reads and returns 16 bits from PORT. */ | ||
| 27 | static inline uint16_t | ||
| 28 | inw (uint16_t port) | ||
| 29 | { | ||
| 30 | uint16_t data; | ||
| 31 | /* See [IA32-v2a] "IN". */ | ||
| 32 | asm volatile ("inw %w1, %w0" : "=a" (data) : "Nd" (port)); | ||
| 33 | return data; | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Reads CNT 16-bit (halfword) units from PORT, one after | ||
| 37 | another, and stores them into the buffer starting at ADDR. */ | ||
| 38 | static inline void | ||
| 39 | insw (uint16_t port, void *addr, size_t cnt) | ||
| 40 | { | ||
| 41 | /* See [IA32-v2a] "INS". */ | ||
| 42 | asm volatile ("rep insw" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); | ||
| 43 | } | ||
| 44 | |||
| 45 | /* Reads and returns 32 bits from PORT. */ | ||
| 46 | static inline uint32_t | ||
| 47 | inl (uint16_t port) | ||
| 48 | { | ||
| 49 | /* See [IA32-v2a] "IN". */ | ||
| 50 | uint32_t data; | ||
| 51 | asm volatile ("inl %w1, %0" : "=a" (data) : "Nd" (port)); | ||
| 52 | return data; | ||
| 53 | } | ||
| 54 | |||
| 55 | /* Reads CNT 32-bit (word) units from PORT, one after another, | ||
| 56 | and stores them into the buffer starting at ADDR. */ | ||
| 57 | static inline void | ||
| 58 | insl (uint16_t port, void *addr, size_t cnt) | ||
| 59 | { | ||
| 60 | /* See [IA32-v2a] "INS". */ | ||
| 61 | asm volatile ("rep insl" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory"); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Writes byte DATA to PORT. */ | ||
| 65 | static inline void | ||
| 66 | outb (uint16_t port, uint8_t data) | ||
| 67 | { | ||
| 68 | /* See [IA32-v2b] "OUT". */ | ||
| 69 | asm volatile ("outb %b0, %w1" : : "a" (data), "Nd" (port)); | ||
| 70 | } | ||
| 71 | |||
| 72 | /* Writes to PORT each byte of data in the CNT-byte buffer | ||
| 73 | starting at ADDR. */ | ||
| 74 | static inline void | ||
| 75 | outsb (uint16_t port, const void *addr, size_t cnt) | ||
| 76 | { | ||
| 77 | /* See [IA32-v2b] "OUTS". */ | ||
| 78 | asm volatile ("rep outsb" : "+S" (addr), "+c" (cnt) : "d" (port)); | ||
| 79 | } | ||
| 80 | |||
| 81 | /* Writes the 16-bit DATA to PORT. */ | ||
| 82 | static inline void | ||
| 83 | outw (uint16_t port, uint16_t data) | ||
| 84 | { | ||
| 85 | /* See [IA32-v2b] "OUT". */ | ||
| 86 | asm volatile ("outw %w0, %w1" : : "a" (data), "Nd" (port)); | ||
| 87 | } | ||
| 88 | |||
| 89 | /* Writes to PORT each 16-bit unit (halfword) of data in the | ||
| 90 | CNT-halfword buffer starting at ADDR. */ | ||
| 91 | static inline void | ||
| 92 | outsw (uint16_t port, const void *addr, size_t cnt) | ||
| 93 | { | ||
| 94 | /* See [IA32-v2b] "OUTS". */ | ||
| 95 | asm volatile ("rep outsw" : "+S" (addr), "+c" (cnt) : "d" (port)); | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Writes the 32-bit DATA to PORT. */ | ||
| 99 | static inline void | ||
| 100 | outl (uint16_t port, uint32_t data) | ||
| 101 | { | ||
| 102 | /* See [IA32-v2b] "OUT". */ | ||
| 103 | asm volatile ("outl %0, %w1" : : "a" (data), "Nd" (port)); | ||
| 104 | } | ||
| 105 | |||
| 106 | /* Writes to PORT each 32-bit unit (word) of data in the CNT-word | ||
| 107 | buffer starting at ADDR. */ | ||
| 108 | static inline void | ||
| 109 | outsl (uint16_t port, const void *addr, size_t cnt) | ||
| 110 | { | ||
| 111 | /* See [IA32-v2b] "OUTS". */ | ||
| 112 | asm volatile ("rep outsl" : "+S" (addr), "+c" (cnt) : "d" (port)); | ||
| 113 | } | ||
| 114 | |||
| 115 | #endif /* threads/io.h */ | ||
diff --git a/pintos-progos/threads/kernel.lds.S b/pintos-progos/threads/kernel.lds.S new file mode 100644 index 0000000..19082d5 --- /dev/null +++ b/pintos-progos/threads/kernel.lds.S | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | OUTPUT_FORMAT("elf32-i386") | ||
| 4 | OUTPUT_ARCH("i386") | ||
| 5 | ENTRY(start) /* Kernel starts at "start" symbol. */ | ||
| 6 | SECTIONS | ||
| 7 | { | ||
| 8 | /* Specify the kernel base address. */ | ||
| 9 | _start = LOADER_PHYS_BASE + LOADER_KERN_BASE; | ||
| 10 | |||
| 11 | /* Make room for the ELF headers. */ | ||
| 12 | . = _start + SIZEOF_HEADERS; | ||
| 13 | |||
| 14 | /* Kernel starts with code, followed by read-only data and writable data. */ | ||
| 15 | .text : { *(.start) *(.text) } = 0x90 | ||
| 16 | .rodata : { *(.rodata) *(.rodata.*) | ||
| 17 | . = ALIGN(0x1000); | ||
| 18 | _end_kernel_text = .; } | ||
| 19 | .data : { *(.data) | ||
| 20 | _signature = .; LONG(0xaa55aa55) } | ||
| 21 | |||
| 22 | /* BSS (zero-initialized data) is after everything else. */ | ||
| 23 | _start_bss = .; | ||
| 24 | .bss : { *(.bss) } | ||
| 25 | _end_bss = .; | ||
| 26 | |||
| 27 | _end = .; | ||
| 28 | |||
| 29 | ASSERT (_end - _start <= 512K, "Kernel image is too big.") | ||
| 30 | } | ||
diff --git a/pintos-progos/threads/loader.S b/pintos-progos/threads/loader.S new file mode 100644 index 0000000..dd87ea1 --- /dev/null +++ b/pintos-progos/threads/loader.S | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | #### Kernel loader. | ||
| 4 | |||
| 5 | #### This code should be stored in the first sector of a hard disk. | ||
| 6 | #### When the BIOS runs, it loads this code at physical address | ||
| 7 | #### 0x7c00-0x7e00 (512 bytes) and jumps to the beginning of it, | ||
| 8 | #### in real mode. The loader loads the kernel into memory and jumps | ||
| 9 | #### to its entry point, which is the start function in start.S. | ||
| 10 | #### | ||
| 11 | #### The BIOS passes in the drive that the loader was read from as | ||
| 12 | #### DL, with floppy drives numbered 0x00, 0x01, ... and hard drives | ||
| 13 | #### numbered 0x80, 0x81, ... We want to support booting a kernel on | ||
| 14 | #### a different drive from the loader, so we don't take advantage of | ||
| 15 | #### this. | ||
| 16 | |||
| 17 | # Runs in real mode, which is a 16-bit segment. | ||
| 18 | .code16 | ||
| 19 | |||
| 20 | # Set up segment registers. | ||
| 21 | # Set stack to grow downward from 60 kB (after boot, the kernel | ||
| 22 | # continues to use this stack for its initial thread). | ||
| 23 | |||
| 24 | sub %ax, %ax | ||
| 25 | mov %ax, %ds | ||
| 26 | mov %ax, %ss | ||
| 27 | mov $0xf000, %esp | ||
| 28 | |||
| 29 | # Configure serial port so we can report progress without connected VGA. | ||
| 30 | # See [IntrList] for details. | ||
| 31 | sub %dx, %dx # Serial port 0. | ||
| 32 | mov $0xe3, %al # 9600 bps, N-8-1. | ||
| 33 | # AH is already 0 (Initialize Port). | ||
| 34 | int $0x14 # Destroys AX. | ||
| 35 | |||
| 36 | call puts | ||
| 37 | .string "PiLo" | ||
| 38 | |||
| 39 | #### Read the partition table on each system hard disk and scan for a | ||
| 40 | #### partition of type 0x20, which is the type that we use for a | ||
| 41 | #### Pintos kernel. | ||
| 42 | #### | ||
| 43 | #### Read [Partitions] for a description of the partition table format | ||
| 44 | #### that we parse. | ||
| 45 | #### | ||
| 46 | #### We print out status messages to show the disk and partition being | ||
| 47 | #### scanned, e.g. hda1234 as we scan four partitions on the first | ||
| 48 | #### hard disk. | ||
| 49 | |||
| 50 | mov $0x80, %dl # Hard disk 0. | ||
| 51 | read_mbr: | ||
| 52 | sub %ebx, %ebx # Sector 0. | ||
| 53 | mov $0x2000, %ax # Use 0x20000 for buffer. | ||
| 54 | mov %ax, %es | ||
| 55 | call read_sector | ||
| 56 | jc no_such_drive | ||
| 57 | |||
| 58 | # Print hd[a-z]. | ||
| 59 | call puts | ||
| 60 | .string " hd" | ||
| 61 | mov %dl, %al | ||
| 62 | add $'a' - 0x80, %al | ||
| 63 | call putc | ||
| 64 | |||
| 65 | # Check for MBR signature--if not present, it's not a | ||
| 66 | # partitioned hard disk. | ||
| 67 | cmpw $0xaa55, %es:510 | ||
| 68 | jne next_drive | ||
| 69 | |||
| 70 | mov $446, %si # Offset of partition table entry 1. | ||
| 71 | mov $'1', %al | ||
| 72 | check_partition: | ||
| 73 | # Is it an unused partition? | ||
| 74 | cmpl $0, %es:(%si) | ||
| 75 | je next_partition | ||
| 76 | |||
| 77 | # Print [1-4]. | ||
| 78 | call putc | ||
| 79 | |||
| 80 | # Is it a Pintos kernel partition? | ||
| 81 | cmpb $0x20, %es:4(%si) | ||
| 82 | jne next_partition | ||
| 83 | |||
| 84 | # Is it a bootable partition? | ||
| 85 | cmpb $0x80, %es:(%si) | ||
| 86 | je load_kernel | ||
| 87 | |||
| 88 | next_partition: | ||
| 89 | # No match for this partition, go on to the next one. | ||
| 90 | add $16, %si # Offset to next partition table entry. | ||
| 91 | inc %al | ||
| 92 | cmp $510, %si | ||
| 93 | jb check_partition | ||
| 94 | |||
| 95 | next_drive: | ||
| 96 | # No match on this drive, go on to the next one. | ||
| 97 | inc %dl | ||
| 98 | jnc read_mbr | ||
| 99 | |||
| 100 | no_such_drive: | ||
| 101 | no_boot_partition: | ||
| 102 | # Didn't find a Pintos kernel partition anywhere, give up. | ||
| 103 | call puts | ||
| 104 | .string "\rNot found\r" | ||
| 105 | |||
| 106 | # Notify BIOS that boot failed. See [IntrList]. | ||
| 107 | int $0x18 | ||
| 108 | |||
| 109 | #### We found a kernel. The kernel's drive is in DL. The partition | ||
| 110 | #### table entry for the kernel's partition is at ES:SI. Our job now | ||
| 111 | #### is to read the kernel from disk and jump to its start address. | ||
| 112 | |||
| 113 | load_kernel: | ||
| 114 | call puts | ||
| 115 | .string "\rLoading" | ||
| 116 | |||
| 117 | # Figure out number of sectors to read. A Pintos kernel is | ||
| 118 | # just an ELF format object, which doesn't have an | ||
| 119 | # easy-to-read field to identify its own size (see [ELF1]). | ||
| 120 | # But we limit Pintos kernels to 512 kB for other reasons, so | ||
| 121 | # it's easy enough to just read the entire contents of the | ||
| 122 | # partition or 512 kB from disk, whichever is smaller. | ||
| 123 | mov %es:12(%si), %ecx # EBP = number of sectors | ||
| 124 | cmp $1024, %ecx # Cap size at 512 kB | ||
| 125 | jbe 1f | ||
| 126 | mov $1024, %cx | ||
| 127 | 1: | ||
| 128 | |||
| 129 | mov %es:8(%si), %ebx # EBX = first sector | ||
| 130 | mov $0x2000, %ax # Start load address: 0x20000 | ||
| 131 | |||
| 132 | next_sector: | ||
| 133 | # Read one sector into memory. | ||
| 134 | mov %ax, %es # ES:0000 -> load address | ||
| 135 | call read_sector | ||
| 136 | jc read_failed | ||
| 137 | |||
| 138 | # Print '.' as progress indicator once every 16 sectors == 8 kB. | ||
| 139 | test $15, %bl | ||
| 140 | jnz 1f | ||
| 141 | call puts | ||
| 142 | .string "." | ||
| 143 | 1: | ||
| 144 | |||
| 145 | # Advance memory pointer and disk sector. | ||
| 146 | add $0x20, %ax | ||
| 147 | inc %bx | ||
| 148 | loop next_sector | ||
| 149 | |||
| 150 | call puts | ||
| 151 | .string "\r" | ||
| 152 | |||
| 153 | #### Transfer control to the kernel that we loaded. We read the start | ||
| 154 | #### address out of the ELF header (see [ELF1]) and convert it from a | ||
| 155 | #### 32-bit linear address into a 16:16 segment:offset address for | ||
| 156 | #### real mode, then jump to the converted address. The 80x86 doesn't | ||
| 157 | #### have an instruction to jump to an absolute segment:offset kept in | ||
| 158 | #### registers, so in fact we store the address in a temporary memory | ||
| 159 | #### location, then jump indirectly through that location. To save 4 | ||
| 160 | #### bytes in the loader, we reuse 4 bytes of the loader's code for | ||
| 161 | #### this temporary pointer. | ||
| 162 | |||
| 163 | mov $0x2000, %ax | ||
| 164 | mov %ax, %es | ||
| 165 | mov %es:0x18, %dx | ||
| 166 | mov %dx, start | ||
| 167 | movw $0x2000, start + 2 | ||
| 168 | ljmp *start | ||
| 169 | |||
| 170 | read_failed: | ||
| 171 | start: | ||
| 172 | # Disk sector read failed. | ||
| 173 | call puts | ||
| 174 | 1: .string "\rBad read\r" | ||
| 175 | |||
| 176 | # Notify BIOS that boot failed. See [IntrList]. | ||
| 177 | int $0x18 | ||
| 178 | |||
| 179 | #### Print string subroutine. To save space in the loader, this | ||
| 180 | #### subroutine takes its null-terminated string argument from the | ||
| 181 | #### code stream just after the call, and then returns to the byte | ||
| 182 | #### just after the terminating null. This subroutine preserves all | ||
| 183 | #### general-purpose registers. | ||
| 184 | |||
| 185 | puts: xchg %si, %ss:(%esp) | ||
| 186 | push %ax | ||
| 187 | next_char: | ||
| 188 | mov %cs:(%si), %al | ||
| 189 | inc %si | ||
| 190 | test %al, %al | ||
| 191 | jz 1f | ||
| 192 | call putc | ||
| 193 | jmp next_char | ||
| 194 | 1: pop %ax | ||
| 195 | xchg %si, %ss:(%esp) | ||
| 196 | ret | ||
| 197 | |||
| 198 | #### Character output subroutine. Prints the character in AL to the | ||
| 199 | #### VGA display and serial port 0, using BIOS services (see | ||
| 200 | #### [IntrList]). Preserves all general-purpose registers. | ||
| 201 | #### | ||
| 202 | #### If called upon to output a carriage return, this subroutine | ||
| 203 | #### automatically supplies the following line feed. | ||
| 204 | |||
| 205 | putc: pusha | ||
| 206 | |||
| 207 | 1: sub %bh, %bh # Page 0. | ||
| 208 | mov $0x0e, %ah # Teletype output service. | ||
| 209 | int $0x10 | ||
| 210 | |||
| 211 | mov $0x01, %ah # Serial port output service. | ||
| 212 | sub %dx, %dx # Serial port 0. | ||
| 213 | 2: int $0x14 # Destroys AH. | ||
| 214 | test $0x80, %ah # Output timed out? | ||
| 215 | jz 3f | ||
| 216 | movw $0x9090, 2b # Turn "int $0x14" above into NOPs. | ||
| 217 | |||
| 218 | 3: | ||
| 219 | cmp $'\r', %al | ||
| 220 | jne popa_ret | ||
| 221 | mov $'\n', %al | ||
| 222 | jmp 1b | ||
| 223 | |||
| 224 | #### Sector read subroutine. Takes a drive number in DL (0x80 = hard | ||
| 225 | #### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and | ||
| 226 | #### reads the specified sector into memory at ES:0000. Returns with | ||
| 227 | #### carry set on error, clear otherwise. Preserves all | ||
| 228 | #### general-purpose registers. | ||
| 229 | |||
| 230 | read_sector: | ||
| 231 | pusha | ||
| 232 | sub %ax, %ax | ||
| 233 | push %ax # LBA sector number [48:63] | ||
| 234 | push %ax # LBA sector number [32:47] | ||
| 235 | push %ebx # LBA sector number [0:31] | ||
| 236 | push %es # Buffer segment | ||
| 237 | push %ax # Buffer offset (always 0) | ||
| 238 | push $1 # Number of sectors to read | ||
| 239 | push $16 # Packet size | ||
| 240 | mov $0x42, %ah # Extended read | ||
| 241 | mov %sp, %si # DS:SI -> packet | ||
| 242 | int $0x13 # Error code in CF | ||
| 243 | popa # Pop 16 bytes, preserve flags | ||
| 244 | popa_ret: | ||
| 245 | popa | ||
| 246 | ret # Error code still in CF | ||
| 247 | |||
| 248 | #### Command-line arguments and their count. | ||
| 249 | #### This is written by the `pintos' utility and read by the kernel. | ||
| 250 | #### The loader itself does not do anything with the command line. | ||
| 251 | .org LOADER_ARG_CNT - LOADER_BASE | ||
| 252 | .fill LOADER_ARG_CNT_LEN, 1, 0 | ||
| 253 | |||
| 254 | .org LOADER_ARGS - LOADER_BASE | ||
| 255 | .fill LOADER_ARGS_LEN, 1, 0 | ||
| 256 | |||
| 257 | #### Partition table. | ||
| 258 | .org LOADER_PARTS - LOADER_BASE | ||
| 259 | .fill LOADER_PARTS_LEN, 1, 0 | ||
| 260 | |||
| 261 | #### Boot-sector signature for BIOS inspection. | ||
| 262 | .org LOADER_SIG - LOADER_BASE | ||
| 263 | .word 0xaa55 | ||
diff --git a/pintos-progos/threads/loader.h b/pintos-progos/threads/loader.h new file mode 100644 index 0000000..1bfe111 --- /dev/null +++ b/pintos-progos/threads/loader.h | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | #ifndef THREADS_LOADER_H | ||
| 2 | #define THREADS_LOADER_H | ||
| 3 | |||
| 4 | /* Constants fixed by the PC BIOS. */ | ||
| 5 | #define LOADER_BASE 0x7c00 /* Physical address of loader's base. */ | ||
| 6 | #define LOADER_END 0x7e00 /* Physical address of end of loader. */ | ||
| 7 | |||
| 8 | /* Physical address of kernel base. */ | ||
| 9 | #define LOADER_KERN_BASE 0x20000 /* 128 kB. */ | ||
| 10 | |||
| 11 | /* Kernel virtual address at which all physical memory is mapped. | ||
| 12 | Must be aligned on a 4 MB boundary. */ | ||
| 13 | #define LOADER_PHYS_BASE 0xc0000000 /* 3 GB. */ | ||
| 14 | |||
| 15 | /* Important loader physical addresses. */ | ||
| 16 | #define LOADER_SIG (LOADER_END - LOADER_SIG_LEN) /* 0xaa55 BIOS signature. */ | ||
| 17 | #define LOADER_PARTS (LOADER_SIG - LOADER_PARTS_LEN) /* Partition table. */ | ||
| 18 | #define LOADER_ARGS (LOADER_PARTS - LOADER_ARGS_LEN) /* Command-line args. */ | ||
| 19 | #define LOADER_ARG_CNT (LOADER_ARGS - LOADER_ARG_CNT_LEN) /* Number of args. */ | ||
| 20 | |||
| 21 | /* Sizes of loader data structures. */ | ||
| 22 | #define LOADER_SIG_LEN 2 | ||
| 23 | #define LOADER_PARTS_LEN 64 | ||
| 24 | #define LOADER_ARGS_LEN 128 | ||
| 25 | #define LOADER_ARG_CNT_LEN 4 | ||
| 26 | |||
| 27 | /* GDT selectors defined by loader. | ||
| 28 | More selectors are defined by userprog/gdt.h. */ | ||
| 29 | #define SEL_NULL 0x00 /* Null selector. */ | ||
| 30 | #define SEL_KCSEG 0x08 /* Kernel code selector. */ | ||
| 31 | #define SEL_KDSEG 0x10 /* Kernel data selector. */ | ||
| 32 | |||
| 33 | #ifndef __ASSEMBLER__ | ||
| 34 | #include <stdint.h> | ||
| 35 | |||
| 36 | /* Amount of physical memory, in 4 kB pages. */ | ||
| 37 | extern uint32_t init_ram_pages; | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #endif /* threads/loader.h */ | ||
diff --git a/pintos-progos/threads/malloc.c b/pintos-progos/threads/malloc.c new file mode 100644 index 0000000..f6f803b --- /dev/null +++ b/pintos-progos/threads/malloc.c | |||
| @@ -0,0 +1,294 @@ | |||
| 1 | #include "threads/malloc.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <list.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <stdio.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include "threads/palloc.h" | ||
| 9 | #include "threads/synch.h" | ||
| 10 | #include "threads/vaddr.h" | ||
| 11 | |||
| 12 | /* A simple implementation of malloc(). | ||
| 13 | |||
| 14 | The size of each request, in bytes, is rounded up to a power | ||
| 15 | of 2 and assigned to the "descriptor" that manages blocks of | ||
| 16 | that size. The descriptor keeps a list of free blocks. If | ||
| 17 | the free list is nonempty, one of its blocks is used to | ||
| 18 | satisfy the request. | ||
| 19 | |||
| 20 | Otherwise, a new page of memory, called an "arena", is | ||
| 21 | obtained from the page allocator (if none is available, | ||
| 22 | malloc() returns a null pointer). The new arena is divided | ||
| 23 | into blocks, all of which are added to the descriptor's free | ||
| 24 | list. Then we return one of the new blocks. | ||
| 25 | |||
| 26 | When we free a block, we add it to its descriptor's free list. | ||
| 27 | But if the arena that the block was in now has no in-use | ||
| 28 | blocks, we remove all of the arena's blocks from the free list | ||
| 29 | and give the arena back to the page allocator. | ||
| 30 | |||
| 31 | We can't handle blocks bigger than 2 kB using this scheme, | ||
| 32 | because they're too big to fit in a single page with a | ||
| 33 | descriptor. We handle those by allocating contiguous pages | ||
| 34 | with the page allocator and sticking the allocation size at | ||
| 35 | the beginning of the allocated block's arena header. */ | ||
| 36 | |||
| 37 | /* Descriptor. */ | ||
| 38 | struct desc | ||
| 39 | { | ||
| 40 | size_t block_size; /* Size of each element in bytes. */ | ||
| 41 | size_t blocks_per_arena; /* Number of blocks in an arena. */ | ||
| 42 | struct list free_list; /* List of free blocks. */ | ||
| 43 | struct lock lock; /* Lock. */ | ||
| 44 | }; | ||
| 45 | |||
| 46 | /* Magic number for detecting arena corruption. */ | ||
| 47 | #define ARENA_MAGIC 0x9a548eed | ||
| 48 | |||
| 49 | /* Arena. */ | ||
| 50 | struct arena | ||
| 51 | { | ||
| 52 | unsigned magic; /* Always set to ARENA_MAGIC. */ | ||
| 53 | struct desc *desc; /* Owning descriptor, null for big block. */ | ||
| 54 | size_t free_cnt; /* Free blocks; pages in big block. */ | ||
| 55 | }; | ||
| 56 | |||
| 57 | /* Free block. */ | ||
| 58 | struct block | ||
| 59 | { | ||
| 60 | struct list_elem free_elem; /* Free list element. */ | ||
| 61 | }; | ||
| 62 | |||
| 63 | /* Our set of descriptors. */ | ||
| 64 | static struct desc descs[10]; /* Descriptors. */ | ||
| 65 | static size_t desc_cnt; /* Number of descriptors. */ | ||
| 66 | |||
| 67 | static struct arena *block_to_arena (struct block *); | ||
| 68 | static struct block *arena_to_block (struct arena *, size_t idx); | ||
| 69 | |||
| 70 | /* Initializes the malloc() descriptors. */ | ||
| 71 | void | ||
| 72 | malloc_init (void) | ||
| 73 | { | ||
| 74 | size_t block_size; | ||
| 75 | |||
| 76 | for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2) | ||
| 77 | { | ||
| 78 | struct desc *d = &descs[desc_cnt++]; | ||
| 79 | ASSERT (desc_cnt <= sizeof descs / sizeof *descs); | ||
| 80 | d->block_size = block_size; | ||
| 81 | d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size; | ||
| 82 | list_init (&d->free_list); | ||
| 83 | lock_init (&d->lock); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | /* Obtains and returns a new block of at least SIZE bytes. | ||
| 88 | Returns a null pointer if memory is not available. */ | ||
| 89 | void * | ||
| 90 | malloc (size_t size) | ||
| 91 | { | ||
| 92 | struct desc *d; | ||
| 93 | struct block *b; | ||
| 94 | struct arena *a; | ||
| 95 | |||
| 96 | /* A null pointer satisfies a request for 0 bytes. */ | ||
| 97 | if (size == 0) | ||
| 98 | return NULL; | ||
| 99 | |||
| 100 | /* Find the smallest descriptor that satisfies a SIZE-byte | ||
| 101 | request. */ | ||
| 102 | for (d = descs; d < descs + desc_cnt; d++) | ||
| 103 | if (d->block_size >= size) | ||
| 104 | break; | ||
| 105 | if (d == descs + desc_cnt) | ||
| 106 | { | ||
| 107 | /* SIZE is too big for any descriptor. | ||
| 108 | Allocate enough pages to hold SIZE plus an arena. */ | ||
| 109 | size_t page_cnt = DIV_ROUND_UP (size + sizeof *a, PGSIZE); | ||
| 110 | a = palloc_get_multiple (0, page_cnt); | ||
| 111 | if (a == NULL) | ||
| 112 | return NULL; | ||
| 113 | |||
| 114 | /* Initialize the arena to indicate a big block of PAGE_CNT | ||
| 115 | pages, and return it. */ | ||
| 116 | a->magic = ARENA_MAGIC; | ||
| 117 | a->desc = NULL; | ||
| 118 | a->free_cnt = page_cnt; | ||
| 119 | return a + 1; | ||
| 120 | } | ||
| 121 | |||
| 122 | lock_acquire (&d->lock); | ||
| 123 | |||
| 124 | /* If the free list is empty, create a new arena. */ | ||
| 125 | if (list_empty (&d->free_list)) | ||
| 126 | { | ||
| 127 | size_t i; | ||
| 128 | |||
| 129 | /* Allocate a page. */ | ||
| 130 | a = palloc_get_page (0); | ||
| 131 | if (a == NULL) | ||
| 132 | { | ||
| 133 | lock_release (&d->lock); | ||
| 134 | return NULL; | ||
| 135 | } | ||
| 136 | |||
| 137 | /* Initialize arena and add its blocks to the free list. */ | ||
| 138 | a->magic = ARENA_MAGIC; | ||
| 139 | a->desc = d; | ||
| 140 | a->free_cnt = d->blocks_per_arena; | ||
| 141 | for (i = 0; i < d->blocks_per_arena; i++) | ||
| 142 | { | ||
| 143 | struct block *b = arena_to_block (a, i); | ||
| 144 | list_push_back (&d->free_list, &b->free_elem); | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /* Get a block from free list and return it. */ | ||
| 149 | b = list_entry (list_pop_front (&d->free_list), struct block, free_elem); | ||
| 150 | a = block_to_arena (b); | ||
| 151 | a->free_cnt--; | ||
| 152 | lock_release (&d->lock); | ||
| 153 | return b; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* Allocates and return A times B bytes initialized to zeroes. | ||
| 157 | Returns a null pointer if memory is not available. */ | ||
| 158 | void * | ||
| 159 | calloc (size_t a, size_t b) | ||
| 160 | { | ||
| 161 | void *p; | ||
| 162 | size_t size; | ||
| 163 | |||
| 164 | /* Calculate block size and make sure it fits in size_t. */ | ||
| 165 | size = a * b; | ||
| 166 | if (size < a || size < b) | ||
| 167 | return NULL; | ||
| 168 | |||
| 169 | /* Allocate and zero memory. */ | ||
| 170 | p = malloc (size); | ||
| 171 | if (p != NULL) | ||
| 172 | memset (p, 0, size); | ||
| 173 | |||
| 174 | return p; | ||
| 175 | } | ||
| 176 | |||
| 177 | /* Returns the number of bytes allocated for BLOCK. */ | ||
| 178 | static size_t | ||
| 179 | block_size (void *block) | ||
| 180 | { | ||
| 181 | struct block *b = block; | ||
| 182 | struct arena *a = block_to_arena (b); | ||
| 183 | struct desc *d = a->desc; | ||
| 184 | |||
| 185 | return d != NULL ? d->block_size : PGSIZE * a->free_cnt - pg_ofs (block); | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Attempts to resize OLD_BLOCK to NEW_SIZE bytes, possibly | ||
| 189 | moving it in the process. | ||
| 190 | If successful, returns the new block; on failure, returns a | ||
| 191 | null pointer. | ||
| 192 | A call with null OLD_BLOCK is equivalent to malloc(NEW_SIZE). | ||
| 193 | A call with zero NEW_SIZE is equivalent to free(OLD_BLOCK). */ | ||
| 194 | void * | ||
| 195 | realloc (void *old_block, size_t new_size) | ||
| 196 | { | ||
| 197 | if (new_size == 0) | ||
| 198 | { | ||
| 199 | free (old_block); | ||
| 200 | return NULL; | ||
| 201 | } | ||
| 202 | else | ||
| 203 | { | ||
| 204 | void *new_block = malloc (new_size); | ||
| 205 | if (old_block != NULL && new_block != NULL) | ||
| 206 | { | ||
| 207 | size_t old_size = block_size (old_block); | ||
| 208 | size_t min_size = new_size < old_size ? new_size : old_size; | ||
| 209 | memcpy (new_block, old_block, min_size); | ||
| 210 | free (old_block); | ||
| 211 | } | ||
| 212 | return new_block; | ||
| 213 | } | ||
| 214 | } | ||
| 215 | |||
| 216 | /* Frees block P, which must have been previously allocated with | ||
| 217 | malloc(), calloc(), or realloc(). */ | ||
| 218 | void | ||
| 219 | free (void *p) | ||
| 220 | { | ||
| 221 | if (p != NULL) | ||
| 222 | { | ||
| 223 | struct block *b = p; | ||
| 224 | struct arena *a = block_to_arena (b); | ||
| 225 | struct desc *d = a->desc; | ||
| 226 | |||
| 227 | if (d != NULL) | ||
| 228 | { | ||
| 229 | /* It's a normal block. We handle it here. */ | ||
| 230 | |||
| 231 | #ifndef NDEBUG | ||
| 232 | /* Clear the block to help detect use-after-free bugs. */ | ||
| 233 | memset (b, 0xcc, d->block_size); | ||
| 234 | #endif | ||
| 235 | |||
| 236 | lock_acquire (&d->lock); | ||
| 237 | |||
| 238 | /* Add block to free list. */ | ||
| 239 | list_push_front (&d->free_list, &b->free_elem); | ||
| 240 | |||
| 241 | /* If the arena is now entirely unused, free it. */ | ||
| 242 | if (++a->free_cnt >= d->blocks_per_arena) | ||
| 243 | { | ||
| 244 | size_t i; | ||
| 245 | |||
| 246 | ASSERT (a->free_cnt == d->blocks_per_arena); | ||
| 247 | for (i = 0; i < d->blocks_per_arena; i++) | ||
| 248 | { | ||
| 249 | struct block *b = arena_to_block (a, i); | ||
| 250 | list_remove (&b->free_elem); | ||
| 251 | } | ||
| 252 | palloc_free_page (a); | ||
| 253 | } | ||
| 254 | |||
| 255 | lock_release (&d->lock); | ||
| 256 | } | ||
| 257 | else | ||
| 258 | { | ||
| 259 | /* It's a big block. Free its pages. */ | ||
| 260 | palloc_free_multiple (a, a->free_cnt); | ||
| 261 | return; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | /* Returns the arena that block B is inside. */ | ||
| 267 | static struct arena * | ||
| 268 | block_to_arena (struct block *b) | ||
| 269 | { | ||
| 270 | struct arena *a = pg_round_down (b); | ||
| 271 | |||
| 272 | /* Check that the arena is valid. */ | ||
| 273 | ASSERT (a != NULL); | ||
| 274 | ASSERT (a->magic == ARENA_MAGIC); | ||
| 275 | |||
| 276 | /* Check that the block is properly aligned for the arena. */ | ||
| 277 | ASSERT (a->desc == NULL | ||
| 278 | || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0); | ||
| 279 | ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a); | ||
| 280 | |||
| 281 | return a; | ||
| 282 | } | ||
| 283 | |||
| 284 | /* Returns the (IDX - 1)'th block within arena A. */ | ||
| 285 | static struct block * | ||
| 286 | arena_to_block (struct arena *a, size_t idx) | ||
| 287 | { | ||
| 288 | ASSERT (a != NULL); | ||
| 289 | ASSERT (a->magic == ARENA_MAGIC); | ||
| 290 | ASSERT (idx < a->desc->blocks_per_arena); | ||
| 291 | return (struct block *) ((uint8_t *) a | ||
| 292 | + sizeof *a | ||
| 293 | + idx * a->desc->block_size); | ||
| 294 | } | ||
diff --git a/pintos-progos/threads/malloc.h b/pintos-progos/threads/malloc.h new file mode 100644 index 0000000..bc55d36 --- /dev/null +++ b/pintos-progos/threads/malloc.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #ifndef THREADS_MALLOC_H | ||
| 2 | #define THREADS_MALLOC_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stddef.h> | ||
| 6 | |||
| 7 | void malloc_init (void); | ||
| 8 | void *malloc (size_t) __attribute__ ((malloc)); | ||
| 9 | void *calloc (size_t, size_t) __attribute__ ((malloc)); | ||
| 10 | void *realloc (void *, size_t); | ||
| 11 | void free (void *); | ||
| 12 | |||
| 13 | #endif /* threads/malloc.h */ | ||
diff --git a/pintos-progos/threads/palloc.c b/pintos-progos/threads/palloc.c new file mode 100644 index 0000000..5c7ef2f --- /dev/null +++ b/pintos-progos/threads/palloc.c | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | #include "threads/palloc.h" | ||
| 2 | #include <bitmap.h> | ||
| 3 | #include <debug.h> | ||
| 4 | #include <inttypes.h> | ||
| 5 | #include <round.h> | ||
| 6 | #include <stddef.h> | ||
| 7 | #include <stdint.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include "threads/loader.h" | ||
| 11 | #include "threads/synch.h" | ||
| 12 | #include "threads/vaddr.h" | ||
| 13 | |||
| 14 | /* Page allocator. Hands out memory in page-size (or | ||
| 15 | page-multiple) chunks. See malloc.h for an allocator that | ||
| 16 | hands out smaller chunks. | ||
| 17 | |||
| 18 | System memory is divided into two "pools" called the kernel | ||
| 19 | and user pools. The user pool is for user (virtual) memory | ||
| 20 | pages, the kernel pool for everything else. The idea here is | ||
| 21 | that the kernel needs to have memory for its own operations | ||
| 22 | even if user processes are swapping like mad. | ||
| 23 | |||
| 24 | By default, half of system RAM is given to the kernel pool and | ||
| 25 | half to the user pool. That should be huge overkill for the | ||
| 26 | kernel pool, but that's just fine for demonstration purposes. */ | ||
| 27 | |||
| 28 | /* A memory pool. */ | ||
| 29 | struct pool | ||
| 30 | { | ||
| 31 | struct lock lock; /* Mutual exclusion. */ | ||
| 32 | struct bitmap *used_map; /* Bitmap of free pages. */ | ||
| 33 | uint8_t *base; /* Base of pool. */ | ||
| 34 | }; | ||
| 35 | |||
| 36 | /* Two pools: one for kernel data, one for user pages. */ | ||
| 37 | static struct pool kernel_pool, user_pool; | ||
| 38 | |||
| 39 | static void init_pool (struct pool *, void *base, size_t page_cnt, | ||
| 40 | const char *name); | ||
| 41 | static bool page_from_pool (const struct pool *, void *page); | ||
| 42 | |||
| 43 | /* Initializes the page allocator. At most USER_PAGE_LIMIT | ||
| 44 | pages are put into the user pool. */ | ||
| 45 | void | ||
| 46 | palloc_init (size_t user_page_limit) | ||
| 47 | { | ||
| 48 | /* Free memory starts at 1 MB and runs to the end of RAM. */ | ||
| 49 | uint8_t *free_start = ptov (1024 * 1024); | ||
| 50 | uint8_t *free_end = ptov (init_ram_pages * PGSIZE); | ||
| 51 | size_t free_pages = (free_end - free_start) / PGSIZE; | ||
| 52 | size_t user_pages = free_pages / 2; | ||
| 53 | size_t kernel_pages; | ||
| 54 | if (user_pages > user_page_limit) | ||
| 55 | user_pages = user_page_limit; | ||
| 56 | kernel_pages = free_pages - user_pages; | ||
| 57 | |||
| 58 | /* Give half of memory to kernel, half to user. */ | ||
| 59 | init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool"); | ||
| 60 | init_pool (&user_pool, free_start + kernel_pages * PGSIZE, | ||
| 61 | user_pages, "user pool"); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Obtains and returns a group of PAGE_CNT contiguous free pages. | ||
| 65 | If PAL_USER is set, the pages are obtained from the user pool, | ||
| 66 | otherwise from the kernel pool. If PAL_ZERO is set in FLAGS, | ||
| 67 | then the pages are filled with zeros. If too few pages are | ||
| 68 | available, returns a null pointer, unless PAL_ASSERT is set in | ||
| 69 | FLAGS, in which case the kernel panics. */ | ||
| 70 | void * | ||
| 71 | palloc_get_multiple (enum palloc_flags flags, size_t page_cnt) | ||
| 72 | { | ||
| 73 | struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool; | ||
| 74 | void *pages; | ||
| 75 | size_t page_idx; | ||
| 76 | |||
| 77 | if (page_cnt == 0) | ||
| 78 | return NULL; | ||
| 79 | |||
| 80 | lock_acquire (&pool->lock); | ||
| 81 | page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false); | ||
| 82 | lock_release (&pool->lock); | ||
| 83 | |||
| 84 | if (page_idx != BITMAP_ERROR) | ||
| 85 | pages = pool->base + PGSIZE * page_idx; | ||
| 86 | else | ||
| 87 | pages = NULL; | ||
| 88 | |||
| 89 | if (pages != NULL) | ||
| 90 | { | ||
| 91 | if (flags & PAL_ZERO) | ||
| 92 | memset (pages, 0, PGSIZE * page_cnt); | ||
| 93 | } | ||
| 94 | else | ||
| 95 | { | ||
| 96 | if (flags & PAL_ASSERT) | ||
| 97 | PANIC ("palloc_get: out of pages"); | ||
| 98 | } | ||
| 99 | |||
| 100 | return pages; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Obtains a single free page and returns its kernel virtual | ||
| 104 | address. | ||
| 105 | If PAL_USER is set, the page is obtained from the user pool, | ||
| 106 | otherwise from the kernel pool. If PAL_ZERO is set in FLAGS, | ||
| 107 | then the page is filled with zeros. If no pages are | ||
| 108 | available, returns a null pointer, unless PAL_ASSERT is set in | ||
| 109 | FLAGS, in which case the kernel panics. */ | ||
| 110 | void * | ||
| 111 | palloc_get_page (enum palloc_flags flags) | ||
| 112 | { | ||
| 113 | return palloc_get_multiple (flags, 1); | ||
| 114 | } | ||
| 115 | |||
| 116 | /* Frees the PAGE_CNT pages starting at PAGES. */ | ||
| 117 | void | ||
| 118 | palloc_free_multiple (void *pages, size_t page_cnt) | ||
| 119 | { | ||
| 120 | struct pool *pool; | ||
| 121 | size_t page_idx; | ||
| 122 | |||
| 123 | ASSERT (pg_ofs (pages) == 0); | ||
| 124 | if (pages == NULL || page_cnt == 0) | ||
| 125 | return; | ||
| 126 | |||
| 127 | if (page_from_pool (&kernel_pool, pages)) | ||
| 128 | pool = &kernel_pool; | ||
| 129 | else if (page_from_pool (&user_pool, pages)) | ||
| 130 | pool = &user_pool; | ||
| 131 | else | ||
| 132 | NOT_REACHED (); | ||
| 133 | |||
| 134 | page_idx = pg_no (pages) - pg_no (pool->base); | ||
| 135 | |||
| 136 | #ifndef NDEBUG | ||
| 137 | memset (pages, 0xcc, PGSIZE * page_cnt); | ||
| 138 | #endif | ||
| 139 | |||
| 140 | ASSERT (bitmap_all (pool->used_map, page_idx, page_cnt)); | ||
| 141 | bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false); | ||
| 142 | } | ||
| 143 | |||
| 144 | /* Frees the page at PAGE. */ | ||
| 145 | void | ||
| 146 | palloc_free_page (void *page) | ||
| 147 | { | ||
| 148 | palloc_free_multiple (page, 1); | ||
| 149 | } | ||
| 150 | |||
| 151 | /* List all used pages */ | ||
| 152 | void | ||
| 153 | palloc_dump_used_pages () | ||
| 154 | { | ||
| 155 | struct pool *pool; | ||
| 156 | int pool_index; | ||
| 157 | for (pool_index = 0; pool_index < 2; pool_index++) { | ||
| 158 | pool = (pool_index == 0) ? &kernel_pool : &user_pool; | ||
| 159 | printf("%s Pool at %p\n", (pool_index == 0) ? "kernel" : "user", pool->base); | ||
| 160 | size_t start = 0, index; | ||
| 161 | while ((index = bitmap_scan(pool->used_map,start,1,true)) != BITMAP_ERROR) { | ||
| 162 | printf(" - %p\n",pool->base + (PGSIZE * index)); | ||
| 163 | start = index + 1; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | } | ||
| 167 | |||
| 168 | /* Initializes pool P as starting at START and ending at END, | ||
| 169 | naming it NAME for debugging purposes. */ | ||
| 170 | static void | ||
| 171 | init_pool (struct pool *p, void *base, size_t page_cnt, const char *name) | ||
| 172 | { | ||
| 173 | /* We'll put the pool's used_map at its base. | ||
| 174 | Calculate the space needed for the bitmap | ||
| 175 | and subtract it from the pool's size. */ | ||
| 176 | size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE); | ||
| 177 | if (bm_pages > page_cnt) | ||
| 178 | PANIC ("Not enough memory in %s for bitmap.", name); | ||
| 179 | page_cnt -= bm_pages; | ||
| 180 | |||
| 181 | printf ("%zu pages available in %s.\n", page_cnt, name); | ||
| 182 | |||
| 183 | /* Initialize the pool. */ | ||
| 184 | lock_init (&p->lock); | ||
| 185 | p->used_map = bitmap_create_in_buf (page_cnt, base, bm_pages * PGSIZE); | ||
| 186 | p->base = base + bm_pages * PGSIZE; | ||
| 187 | } | ||
| 188 | |||
| 189 | /* Returns true if PAGE was allocated from POOL, | ||
| 190 | false otherwise. */ | ||
| 191 | static bool | ||
| 192 | page_from_pool (const struct pool *pool, void *page) | ||
| 193 | { | ||
| 194 | size_t page_no = pg_no (page); | ||
| 195 | size_t start_page = pg_no (pool->base); | ||
| 196 | size_t end_page = start_page + bitmap_size (pool->used_map); | ||
| 197 | |||
| 198 | return page_no >= start_page && page_no < end_page; | ||
| 199 | } | ||
diff --git a/pintos-progos/threads/palloc.h b/pintos-progos/threads/palloc.h new file mode 100644 index 0000000..d7f4e0b --- /dev/null +++ b/pintos-progos/threads/palloc.h | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | #ifndef THREADS_PALLOC_H | ||
| 2 | #define THREADS_PALLOC_H | ||
| 3 | |||
| 4 | #include <stddef.h> | ||
| 5 | |||
| 6 | /* How to allocate pages. */ | ||
| 7 | enum palloc_flags | ||
| 8 | { | ||
| 9 | PAL_ASSERT = 001, /* Panic on failure. */ | ||
| 10 | PAL_ZERO = 002, /* Zero page contents. */ | ||
| 11 | PAL_USER = 004 /* User page. */ | ||
| 12 | }; | ||
| 13 | void palloc_init (size_t user_page_limit); | ||
| 14 | void *palloc_get_page (enum palloc_flags); | ||
| 15 | void *palloc_get_multiple (enum palloc_flags, size_t page_cnt); | ||
| 16 | void palloc_free_page (void *); | ||
| 17 | void palloc_free_multiple (void *, size_t page_cnt); | ||
| 18 | void palloc_dump_used_pages (void); | ||
| 19 | |||
| 20 | #endif /* threads/palloc.h */ | ||
diff --git a/pintos-progos/threads/pte.h b/pintos-progos/threads/pte.h new file mode 100644 index 0000000..1660727 --- /dev/null +++ b/pintos-progos/threads/pte.h | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | #ifndef THREADS_PTE_H | ||
| 2 | #define THREADS_PTE_H | ||
| 3 | |||
| 4 | #include "threads/vaddr.h" | ||
| 5 | |||
| 6 | /* Functions and macros for working with x86 hardware page | ||
| 7 | tables. | ||
| 8 | |||
| 9 | See vaddr.h for more generic functions and macros for virtual | ||
| 10 | addresses. | ||
| 11 | |||
| 12 | Virtual addresses are structured as follows: | ||
| 13 | |||
| 14 | 31 22 21 12 11 0 | ||
| 15 | +----------------------+----------------------+----------------------+ | ||
| 16 | | Page Directory Index | Page Table Index | Page Offset | | ||
| 17 | +----------------------+----------------------+----------------------+ | ||
| 18 | */ | ||
| 19 | |||
| 20 | /* Page table index (bits 12:21). */ | ||
| 21 | #define PTSHIFT PGBITS /* First page table bit. */ | ||
| 22 | #define PTBITS 10 /* Number of page table bits. */ | ||
| 23 | #define PTSPAN (1 << PTBITS << PGBITS) /* Bytes covered by a page table. */ | ||
| 24 | #define PTMASK BITMASK(PTSHIFT, PTBITS) /* Page table bits (12:21). */ | ||
| 25 | |||
| 26 | /* Page directory index (bits 22:31). */ | ||
| 27 | #define PDSHIFT (PTSHIFT + PTBITS) /* First page directory bit. */ | ||
| 28 | #define PDBITS 10 /* Number of page dir bits. */ | ||
| 29 | #define PDMASK BITMASK(PDSHIFT, PDBITS) /* Page directory bits (22:31). */ | ||
| 30 | |||
| 31 | /* Obtains page table index from a virtual address. */ | ||
| 32 | static inline unsigned pt_no (const void *va) { | ||
| 33 | return ((uintptr_t) va & PTMASK) >> PTSHIFT; | ||
| 34 | } | ||
| 35 | |||
| 36 | /* Obtains page directory index from a virtual address. */ | ||
| 37 | static inline uintptr_t pd_no (const void *va) { | ||
| 38 | return (uintptr_t) va >> PDSHIFT; | ||
| 39 | } | ||
| 40 | |||
| 41 | /* Page directory and page table entries. | ||
| 42 | |||
| 43 | For more information see the section on page tables in the | ||
| 44 | Pintos reference guide chapter, or [IA32-v3a] 3.7.6 | ||
| 45 | "Page-Directory and Page-Table Entries". | ||
| 46 | |||
| 47 | PDEs and PTEs share a common format: | ||
| 48 | |||
| 49 | 31 12 11 0 | ||
| 50 | +------------------------------------+------------------------+ | ||
| 51 | | Physical Address | Flags | | ||
| 52 | +------------------------------------+------------------------+ | ||
| 53 | |||
| 54 | In a PDE, the physical address points to a page table. | ||
| 55 | In a PTE, the physical address points to a data or code page. | ||
| 56 | The important flags are listed below. | ||
| 57 | When a PDE or PTE is not "present", the other flags are | ||
| 58 | ignored. | ||
| 59 | A PDE or PTE that is initialized to 0 will be interpreted as | ||
| 60 | "not present", which is just fine. */ | ||
| 61 | #define PTE_FLAGS 0x00000fff /* Flag bits. */ | ||
| 62 | #define PTE_ADDR 0xfffff000 /* Address bits. */ | ||
| 63 | #define PTE_AVL 0x00000e00 /* Bits available for OS use. */ | ||
| 64 | #define PTE_P 0x1 /* 1=present, 0=not present. */ | ||
| 65 | #define PTE_W 0x2 /* 1=read/write, 0=read-only. */ | ||
| 66 | #define PTE_U 0x4 /* 1=user/kernel, 0=kernel only. */ | ||
| 67 | #define PTE_A 0x20 /* 1=accessed, 0=not acccessed. */ | ||
| 68 | #define PTE_D 0x40 /* 1=dirty, 0=not dirty (PTEs only). */ | ||
| 69 | |||
| 70 | /* Returns a PDE that points to page table PT. */ | ||
| 71 | static inline uint32_t pde_create (uint32_t *pt) { | ||
| 72 | ASSERT (pg_ofs (pt) == 0); | ||
| 73 | return vtop (pt) | PTE_U | PTE_P | PTE_W; | ||
| 74 | } | ||
| 75 | |||
| 76 | /* Returns a pointer to the page table that page directory entry | ||
| 77 | PDE, which must "present", points to. */ | ||
| 78 | static inline uint32_t *pde_get_pt (uint32_t pde) { | ||
| 79 | ASSERT (pde & PTE_P); | ||
| 80 | return ptov (pde & PTE_ADDR); | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Returns a PTE that points to PAGE. | ||
| 84 | The PTE's page is readable. | ||
| 85 | If WRITABLE is true then it will be writable as well. | ||
| 86 | The page will be usable only by ring 0 code (the kernel). */ | ||
| 87 | static inline uint32_t pte_create_kernel (void *page, bool writable) { | ||
| 88 | ASSERT (pg_ofs (page) == 0); | ||
| 89 | return vtop (page) | PTE_P | (writable ? PTE_W : 0); | ||
| 90 | } | ||
| 91 | |||
| 92 | /* Returns a PTE that points to PAGE. | ||
| 93 | The PTE's page is readable. | ||
| 94 | If WRITABLE is true then it will be writable as well. | ||
| 95 | The page will be usable by both user and kernel code. */ | ||
| 96 | static inline uint32_t pte_create_user (void *page, bool writable) { | ||
| 97 | return pte_create_kernel (page, writable) | PTE_U; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Returns a pointer to the page that page table entry PTE points | ||
| 101 | to. */ | ||
| 102 | static inline void *pte_get_page (uint32_t pte) { | ||
| 103 | return ptov (pte & PTE_ADDR); | ||
| 104 | } | ||
| 105 | |||
| 106 | #endif /* threads/pte.h */ | ||
| 107 | |||
diff --git a/pintos-progos/threads/start.S b/pintos-progos/threads/start.S new file mode 100644 index 0000000..29ffa7a --- /dev/null +++ b/pintos-progos/threads/start.S | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | #include "threads/loader.h" | ||
| 2 | |||
| 3 | #### Kernel startup code. | ||
| 4 | |||
| 5 | #### The loader (in loader.S) loads the kernel at physical address | ||
| 6 | #### 0x20000 (128 kB) and jumps to "start", defined here. This code | ||
| 7 | #### switches from real mode to 32-bit protected mode and calls | ||
| 8 | #### main(). | ||
| 9 | |||
| 10 | /* Flags in control register 0. */ | ||
| 11 | #define CR0_PE 0x00000001 /* Protection Enable. */ | ||
| 12 | #define CR0_EM 0x00000004 /* (Floating-point) Emulation. */ | ||
| 13 | #define CR0_PG 0x80000000 /* Paging. */ | ||
| 14 | #define CR0_WP 0x00010000 /* Write-Protect enable in kernel mode. */ | ||
| 15 | |||
| 16 | .section .start | ||
| 17 | |||
| 18 | # The following code runs in real mode, which is a 16-bit code segment. | ||
| 19 | .code16 | ||
| 20 | |||
| 21 | .func start | ||
| 22 | .globl start | ||
| 23 | start: | ||
| 24 | |||
| 25 | # The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000, | ||
| 26 | # but we should initialize the other segment registers. | ||
| 27 | |||
| 28 | mov $0x2000, %ax | ||
| 29 | mov %ax, %ds | ||
| 30 | mov %ax, %es | ||
| 31 | |||
| 32 | # Set string instructions to go upward. | ||
| 33 | cld | ||
| 34 | |||
| 35 | #### Get memory size, via interrupt 15h function 88h (see [IntrList]), | ||
| 36 | #### which returns AX = (kB of physical memory) - 1024. This only | ||
| 37 | #### works for memory sizes <= 65 MB, which should be fine for our | ||
| 38 | #### purposes. We cap memory at 64 MB because that's all we prepare | ||
| 39 | #### page tables for, below. | ||
| 40 | |||
| 41 | movb $0x88, %ah | ||
| 42 | int $0x15 | ||
| 43 | addl $1024, %eax # Total kB memory | ||
| 44 | cmp $0x10000, %eax # Cap at 64 MB | ||
| 45 | jbe 1f | ||
| 46 | mov $0x10000, %eax | ||
| 47 | 1: shrl $2, %eax # Total 4 kB pages | ||
| 48 | addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000 | ||
| 49 | |||
| 50 | #### Enable A20. Address line 20 is tied low when the machine boots, | ||
| 51 | #### which prevents addressing memory about 1 MB. This code fixes it. | ||
| 52 | |||
| 53 | # Poll status register while busy. | ||
| 54 | |||
| 55 | 1: inb $0x64, %al | ||
| 56 | testb $0x2, %al | ||
| 57 | jnz 1b | ||
| 58 | |||
| 59 | # Send command for writing output port. | ||
| 60 | |||
| 61 | movb $0xd1, %al | ||
| 62 | outb %al, $0x64 | ||
| 63 | |||
| 64 | # Poll status register while busy. | ||
| 65 | |||
| 66 | 1: inb $0x64, %al | ||
| 67 | testb $0x2, %al | ||
| 68 | jnz 1b | ||
| 69 | |||
| 70 | # Enable A20 line. | ||
| 71 | |||
| 72 | movb $0xdf, %al | ||
| 73 | outb %al, $0x60 | ||
| 74 | |||
| 75 | # Poll status register while busy. | ||
| 76 | |||
| 77 | 1: inb $0x64, %al | ||
| 78 | testb $0x2, %al | ||
| 79 | jnz 1b | ||
| 80 | |||
| 81 | #### Create temporary page directory and page table and set page | ||
| 82 | #### directory base register. | ||
| 83 | |||
| 84 | # Create page directory at 0xf000 (60 kB) and fill with zeroes. | ||
| 85 | mov $0xf00, %ax | ||
| 86 | mov %ax, %es | ||
| 87 | subl %eax, %eax | ||
| 88 | subl %edi, %edi | ||
| 89 | movl $0x400, %ecx | ||
| 90 | rep stosl | ||
| 91 | |||
| 92 | # Add PDEs to point to page tables for the first 64 MB of RAM. | ||
| 93 | # Also add identical PDEs starting at LOADER_PHYS_BASE. | ||
| 94 | # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" | ||
| 95 | # for a description of the bits in %eax. | ||
| 96 | |||
| 97 | movl $0x10007, %eax | ||
| 98 | movl $0x11, %ecx | ||
| 99 | subl %edi, %edi | ||
| 100 | 1: movl %eax, %es:(%di) | ||
| 101 | movl %eax, %es:LOADER_PHYS_BASE >> 20(%di) | ||
| 102 | addw $4, %di | ||
| 103 | addl $0x1000, %eax | ||
| 104 | loop 1b | ||
| 105 | |||
| 106 | # Set up page tables for one-to-map linear to physical map for the | ||
| 107 | # first 64 MB of RAM. | ||
| 108 | # See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries" | ||
| 109 | # for a description of the bits in %eax. | ||
| 110 | |||
| 111 | movw $0x1000, %ax | ||
| 112 | movw %ax, %es | ||
| 113 | movl $0x7, %eax | ||
| 114 | movl $0x4000, %ecx | ||
| 115 | subl %edi, %edi | ||
| 116 | 1: movl %eax, %es:(%di) | ||
| 117 | addw $4, %di | ||
| 118 | addl $0x1000, %eax | ||
| 119 | loop 1b | ||
| 120 | |||
| 121 | # Set page directory base register. | ||
| 122 | |||
| 123 | movl $0xf000, %eax | ||
| 124 | movl %eax, %cr3 | ||
| 125 | |||
| 126 | #### Switch to protected mode. | ||
| 127 | |||
| 128 | # First, disable interrupts. We won't set up the IDT until we get | ||
| 129 | # into C code, so any interrupt would blow us away. | ||
| 130 | |||
| 131 | cli | ||
| 132 | |||
| 133 | # Protected mode requires a GDT, so point the GDTR to our GDT. | ||
| 134 | # We need a data32 prefix to ensure that all 32 bits of the GDT | ||
| 135 | # descriptor are loaded (default is to load only 24 bits). | ||
| 136 | # The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit | ||
| 137 | # relocations. | ||
| 138 | |||
| 139 | data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000 | ||
| 140 | |||
| 141 | # Then we turn on the following bits in CR0: | ||
| 142 | # PE (Protect Enable): this turns on protected mode. | ||
| 143 | # PG (Paging): turns on paging. | ||
| 144 | # WP (Write Protect): if unset, ring 0 code ignores | ||
| 145 | # write-protect bits in page tables (!). | ||
| 146 | # EM (Emulation): forces floating-point instructions to trap. | ||
| 147 | # We don't support floating point. | ||
| 148 | |||
| 149 | movl %cr0, %eax | ||
| 150 | orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax | ||
| 151 | movl %eax, %cr0 | ||
| 152 | |||
| 153 | # We're now in protected mode in a 16-bit segment. The CPU still has | ||
| 154 | # the real-mode code segment cached in %cs's segment descriptor. We | ||
| 155 | # need to reload %cs, and the easiest way is to use a far jump. | ||
| 156 | # Because we're not running in a 32-bit segment the data32 prefix is | ||
| 157 | # needed to jump to a 32-bit offset in the target segment. | ||
| 158 | |||
| 159 | data32 ljmp $SEL_KCSEG, $1f | ||
| 160 | |||
| 161 | # We're now in protected mode in a 32-bit segment. | ||
| 162 | # Let the assembler know. | ||
| 163 | |||
| 164 | .code32 | ||
| 165 | |||
| 166 | # Reload all the other segment registers and the stack pointer to | ||
| 167 | # point into our new GDT. | ||
| 168 | |||
| 169 | 1: mov $SEL_KDSEG, %ax | ||
| 170 | mov %ax, %ds | ||
| 171 | mov %ax, %es | ||
| 172 | mov %ax, %fs | ||
| 173 | mov %ax, %gs | ||
| 174 | mov %ax, %ss | ||
| 175 | addl $LOADER_PHYS_BASE, %esp | ||
| 176 | movl $0, %ebp # Null-terminate main()'s backtrace | ||
| 177 | |||
| 178 | #### Call main(). | ||
| 179 | |||
| 180 | call main | ||
| 181 | |||
| 182 | # main() shouldn't ever return. If it does, spin. | ||
| 183 | |||
| 184 | 1: jmp 1b | ||
| 185 | .endfunc | ||
| 186 | |||
| 187 | #### GDT | ||
| 188 | |||
| 189 | .align 8 | ||
| 190 | gdt: | ||
| 191 | .quad 0x0000000000000000 # Null segment. Not used by CPU. | ||
| 192 | .quad 0x00cf9a000000ffff # System code, base 0, limit 4 GB. | ||
| 193 | .quad 0x00cf92000000ffff # System data, base 0, limit 4 GB. | ||
| 194 | |||
| 195 | gdtdesc: | ||
| 196 | .word gdtdesc - gdt - 1 # Size of the GDT, minus 1 byte. | ||
| 197 | .long gdt # Address of the GDT. | ||
| 198 | |||
| 199 | #### Physical memory size in 4 kB pages. This is exported to the rest | ||
| 200 | #### of the kernel. | ||
| 201 | .globl init_ram_pages | ||
| 202 | init_ram_pages: | ||
| 203 | .long 0 | ||
| 204 | |||
diff --git a/pintos-progos/threads/switch.S b/pintos-progos/threads/switch.S new file mode 100644 index 0000000..feca86c --- /dev/null +++ b/pintos-progos/threads/switch.S | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | #include "threads/switch.h" | ||
| 2 | |||
| 3 | #### struct thread *switch_threads (struct thread *cur, struct thread *next); | ||
| 4 | #### | ||
| 5 | #### Switches from CUR, which must be the running thread, to NEXT, | ||
| 6 | #### which must also be running switch_threads(), returning CUR in | ||
| 7 | #### NEXT's context. | ||
| 8 | #### | ||
| 9 | #### This function works by assuming that the thread we're switching | ||
| 10 | #### into is also running switch_threads(). Thus, all it has to do is | ||
| 11 | #### preserve a few registers on the stack, then switch stacks and | ||
| 12 | #### restore the registers. As part of switching stacks we record the | ||
| 13 | #### current stack pointer in CUR's thread structure. | ||
| 14 | |||
| 15 | .globl switch_threads | ||
| 16 | .func switch_threads | ||
| 17 | switch_threads: | ||
| 18 | # Save caller's register state. | ||
| 19 | # | ||
| 20 | # Note that the SVR4 ABI allows us to destroy %eax, %ecx, %edx, | ||
| 21 | # but requires us to preserve %ebx, %ebp, %esi, %edi. See | ||
| 22 | # [SysV-ABI-386] pages 3-11 and 3-12 for details. | ||
| 23 | # | ||
| 24 | # This stack frame must match the one set up by thread_create() | ||
| 25 | # in size. | ||
| 26 | pushl %ebx | ||
| 27 | pushl %ebp | ||
| 28 | pushl %esi | ||
| 29 | pushl %edi | ||
| 30 | |||
| 31 | # Get offsetof (struct thread, stack). | ||
| 32 | .globl thread_stack_ofs | ||
| 33 | mov thread_stack_ofs, %edx | ||
| 34 | |||
| 35 | # Save current stack pointer to old thread's stack, if any. | ||
| 36 | movl SWITCH_CUR(%esp), %eax | ||
| 37 | movl %esp, (%eax,%edx,1) | ||
| 38 | |||
| 39 | # Restore stack pointer from new thread's stack. | ||
| 40 | movl SWITCH_NEXT(%esp), %ecx | ||
| 41 | movl (%ecx,%edx,1), %esp | ||
| 42 | |||
| 43 | # Restore caller's register state. | ||
| 44 | popl %edi | ||
| 45 | popl %esi | ||
| 46 | popl %ebp | ||
| 47 | popl %ebx | ||
| 48 | ret | ||
| 49 | .endfunc | ||
| 50 | |||
| 51 | .globl switch_entry | ||
| 52 | .func switch_entry | ||
| 53 | switch_entry: | ||
| 54 | # Discard switch_threads() arguments. | ||
| 55 | addl $8, %esp | ||
| 56 | |||
| 57 | # Call thread_schedule_tail(prev). | ||
| 58 | pushl %eax | ||
| 59 | .globl thread_schedule_tail | ||
| 60 | call thread_schedule_tail | ||
| 61 | addl $4, %esp | ||
| 62 | |||
| 63 | # Start thread proper. | ||
| 64 | ret | ||
| 65 | .endfunc | ||
diff --git a/pintos-progos/threads/switch.h b/pintos-progos/threads/switch.h new file mode 100644 index 0000000..cc156b6 --- /dev/null +++ b/pintos-progos/threads/switch.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | #ifndef THREADS_SWITCH_H | ||
| 2 | #define THREADS_SWITCH_H | ||
| 3 | |||
| 4 | #ifndef __ASSEMBLER__ | ||
| 5 | /* switch_thread()'s stack frame. */ | ||
| 6 | struct switch_threads_frame | ||
| 7 | { | ||
| 8 | uint32_t edi; /* 0: Saved %edi. */ | ||
| 9 | uint32_t esi; /* 4: Saved %esi. */ | ||
| 10 | uint32_t ebp; /* 8: Saved %ebp. */ | ||
| 11 | uint32_t ebx; /* 12: Saved %ebx. */ | ||
| 12 | void (*eip) (void); /* 16: Return address. */ | ||
| 13 | struct thread *cur; /* 20: switch_threads()'s CUR argument. */ | ||
| 14 | struct thread *next; /* 24: switch_threads()'s NEXT argument. */ | ||
| 15 | }; | ||
| 16 | |||
| 17 | /* Switches from CUR, which must be the running thread, to NEXT, | ||
| 18 | which must also be running switch_threads(), returning CUR in | ||
| 19 | NEXT's context. */ | ||
| 20 | struct thread *switch_threads (struct thread *cur, struct thread *next); | ||
| 21 | |||
| 22 | /* Stack frame for switch_entry(). */ | ||
| 23 | struct switch_entry_frame | ||
| 24 | { | ||
| 25 | void (*eip) (void); | ||
| 26 | }; | ||
| 27 | |||
| 28 | void switch_entry (void); | ||
| 29 | |||
| 30 | /* Pops the CUR and NEXT arguments off the stack, for use in | ||
| 31 | initializing threads. */ | ||
| 32 | void switch_thunk (void); | ||
| 33 | #endif | ||
| 34 | |||
| 35 | /* Offsets used by switch.S. */ | ||
| 36 | #define SWITCH_CUR 20 | ||
| 37 | #define SWITCH_NEXT 24 | ||
| 38 | |||
| 39 | #endif /* threads/switch.h */ | ||
diff --git a/pintos-progos/threads/synch.c b/pintos-progos/threads/synch.c new file mode 100644 index 0000000..317c68a --- /dev/null +++ b/pintos-progos/threads/synch.c | |||
| @@ -0,0 +1,338 @@ | |||
| 1 | /* This file is derived from source code for the Nachos | ||
| 2 | instructional operating system. The Nachos copyright notice | ||
| 3 | is reproduced in full below. */ | ||
| 4 | |||
| 5 | /* Copyright (c) 1992-1996 The Regents of the University of California. | ||
| 6 | All rights reserved. | ||
| 7 | |||
| 8 | Permission to use, copy, modify, and distribute this software | ||
| 9 | and its documentation for any purpose, without fee, and | ||
| 10 | without written agreement is hereby granted, provided that the | ||
| 11 | above copyright notice and the following two paragraphs appear | ||
| 12 | in all copies of this software. | ||
| 13 | |||
| 14 | IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO | ||
| 15 | ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR | ||
| 16 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE | ||
| 17 | AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA | ||
| 18 | HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 19 | |||
| 20 | THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY | ||
| 21 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
| 22 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
| 23 | PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" | ||
| 24 | BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO | ||
| 25 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR | ||
| 26 | MODIFICATIONS. | ||
| 27 | */ | ||
| 28 | |||
| 29 | #include "threads/synch.h" | ||
| 30 | #include <stdio.h> | ||
| 31 | #include <string.h> | ||
| 32 | #include "threads/interrupt.h" | ||
| 33 | #include "threads/thread.h" | ||
| 34 | |||
| 35 | /* Initializes semaphore SEMA to VALUE. A semaphore is a | ||
| 36 | nonnegative integer along with two atomic operators for | ||
| 37 | manipulating it: | ||
| 38 | |||
| 39 | - down or "P": wait for the value to become positive, then | ||
| 40 | decrement it. | ||
| 41 | |||
| 42 | - up or "V": increment the value (and wake up one waiting | ||
| 43 | thread, if any). */ | ||
| 44 | void | ||
| 45 | sema_init (struct semaphore *sema, unsigned value) | ||
| 46 | { | ||
| 47 | ASSERT (sema != NULL); | ||
| 48 | |||
| 49 | sema->value = value; | ||
| 50 | list_init (&sema->waiters); | ||
| 51 | } | ||
| 52 | |||
| 53 | /* Down or "P" operation on a semaphore. Waits for SEMA's value | ||
| 54 | to become positive and then atomically decrements it. | ||
| 55 | |||
| 56 | This function may sleep, so it must not be called within an | ||
| 57 | interrupt handler. This function may be called with | ||
| 58 | interrupts disabled, but if it sleeps then the next scheduled | ||
| 59 | thread will probably turn interrupts back on. */ | ||
| 60 | void | ||
| 61 | sema_down (struct semaphore *sema) | ||
| 62 | { | ||
| 63 | enum intr_level old_level; | ||
| 64 | |||
| 65 | ASSERT (sema != NULL); | ||
| 66 | ASSERT (!intr_context ()); | ||
| 67 | |||
| 68 | old_level = intr_disable (); | ||
| 69 | while (sema->value == 0) | ||
| 70 | { | ||
| 71 | list_push_back (&sema->waiters, &thread_current ()->elem); | ||
| 72 | thread_block (); | ||
| 73 | } | ||
| 74 | sema->value--; | ||
| 75 | intr_set_level (old_level); | ||
| 76 | } | ||
| 77 | |||
| 78 | /* Down or "P" operation on a semaphore, but only if the | ||
| 79 | semaphore is not already 0. Returns true if the semaphore is | ||
| 80 | decremented, false otherwise. | ||
| 81 | |||
| 82 | This function may be called from an interrupt handler. */ | ||
| 83 | bool | ||
| 84 | sema_try_down (struct semaphore *sema) | ||
| 85 | { | ||
| 86 | enum intr_level old_level; | ||
| 87 | bool success; | ||
| 88 | |||
| 89 | ASSERT (sema != NULL); | ||
| 90 | |||
| 91 | old_level = intr_disable (); | ||
| 92 | if (sema->value > 0) | ||
| 93 | { | ||
| 94 | sema->value--; | ||
| 95 | success = true; | ||
| 96 | } | ||
| 97 | else | ||
| 98 | success = false; | ||
| 99 | intr_set_level (old_level); | ||
| 100 | |||
| 101 | return success; | ||
| 102 | } | ||
| 103 | |||
| 104 | /* Up or "V" operation on a semaphore. Increments SEMA's value | ||
| 105 | and wakes up one thread of those waiting for SEMA, if any. | ||
| 106 | |||
| 107 | This function may be called from an interrupt handler. */ | ||
| 108 | void | ||
| 109 | sema_up (struct semaphore *sema) | ||
| 110 | { | ||
| 111 | enum intr_level old_level; | ||
| 112 | |||
| 113 | ASSERT (sema != NULL); | ||
| 114 | |||
| 115 | old_level = intr_disable (); | ||
| 116 | if (!list_empty (&sema->waiters)) | ||
| 117 | thread_unblock (list_entry (list_pop_front (&sema->waiters), | ||
| 118 | struct thread, elem)); | ||
| 119 | sema->value++; | ||
| 120 | intr_set_level (old_level); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void sema_test_helper (void *sema_); | ||
| 124 | |||
| 125 | /* Self-test for semaphores that makes control "ping-pong" | ||
| 126 | between a pair of threads. Insert calls to printf() to see | ||
| 127 | what's going on. */ | ||
| 128 | void | ||
| 129 | sema_self_test (void) | ||
| 130 | { | ||
| 131 | struct semaphore sema[2]; | ||
| 132 | int i; | ||
| 133 | |||
| 134 | printf ("Testing semaphores..."); | ||
| 135 | sema_init (&sema[0], 0); | ||
| 136 | sema_init (&sema[1], 0); | ||
| 137 | thread_create ("sema-test", PRI_DEFAULT, sema_test_helper, &sema); | ||
| 138 | for (i = 0; i < 10; i++) | ||
| 139 | { | ||
| 140 | sema_up (&sema[0]); | ||
| 141 | sema_down (&sema[1]); | ||
| 142 | } | ||
| 143 | printf ("done.\n"); | ||
| 144 | } | ||
| 145 | |||
| 146 | /* Thread function used by sema_self_test(). */ | ||
| 147 | static void | ||
| 148 | sema_test_helper (void *sema_) | ||
| 149 | { | ||
| 150 | struct semaphore *sema = sema_; | ||
| 151 | int i; | ||
| 152 | |||
| 153 | for (i = 0; i < 10; i++) | ||
| 154 | { | ||
| 155 | sema_down (&sema[0]); | ||
| 156 | sema_up (&sema[1]); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /* Initializes LOCK. A lock can be held by at most a single | ||
| 161 | thread at any given time. Our locks are not "recursive", that | ||
| 162 | is, it is an error for the thread currently holding a lock to | ||
| 163 | try to acquire that lock. | ||
| 164 | |||
| 165 | A lock is a specialization of a semaphore with an initial | ||
| 166 | value of 1. The difference between a lock and such a | ||
| 167 | semaphore is twofold. First, a semaphore can have a value | ||
| 168 | greater than 1, but a lock can only be owned by a single | ||
| 169 | thread at a time. Second, a semaphore does not have an owner, | ||
| 170 | meaning that one thread can "down" the semaphore and then | ||
| 171 | another one "up" it, but with a lock the same thread must both | ||
| 172 | acquire and release it. When these restrictions prove | ||
| 173 | onerous, it's a good sign that a semaphore should be used, | ||
| 174 | instead of a lock. */ | ||
| 175 | void | ||
| 176 | lock_init (struct lock *lock) | ||
| 177 | { | ||
| 178 | ASSERT (lock != NULL); | ||
| 179 | |||
| 180 | lock->holder = NULL; | ||
| 181 | sema_init (&lock->semaphore, 1); | ||
| 182 | } | ||
| 183 | |||
| 184 | /* Acquires LOCK, sleeping until it becomes available if | ||
| 185 | necessary. The lock must not already be held by the current | ||
| 186 | thread. | ||
| 187 | |||
| 188 | This function may sleep, so it must not be called within an | ||
| 189 | interrupt handler. This function may be called with | ||
| 190 | interrupts disabled, but interrupts will be turned back on if | ||
| 191 | we need to sleep. */ | ||
| 192 | void | ||
| 193 | lock_acquire (struct lock *lock) | ||
| 194 | { | ||
| 195 | ASSERT (lock != NULL); | ||
| 196 | ASSERT (!intr_context ()); | ||
| 197 | ASSERT (!lock_held_by_current_thread (lock)); | ||
| 198 | |||
| 199 | sema_down (&lock->semaphore); | ||
| 200 | lock->holder = thread_current (); | ||
| 201 | } | ||
| 202 | |||
| 203 | /* Tries to acquires LOCK and returns true if successful or false | ||
| 204 | on failure. The lock must not already be held by the current | ||
| 205 | thread. | ||
| 206 | |||
| 207 | This function will not sleep, so it may be called within an | ||
| 208 | interrupt handler. */ | ||
| 209 | bool | ||
| 210 | lock_try_acquire (struct lock *lock) | ||
| 211 | { | ||
| 212 | bool success; | ||
| 213 | |||
| 214 | ASSERT (lock != NULL); | ||
| 215 | ASSERT (!lock_held_by_current_thread (lock)); | ||
| 216 | |||
| 217 | success = sema_try_down (&lock->semaphore); | ||
| 218 | if (success) | ||
| 219 | lock->holder = thread_current (); | ||
| 220 | return success; | ||
| 221 | } | ||
| 222 | |||
| 223 | /* Releases LOCK, which must be owned by the current thread. | ||
| 224 | |||
| 225 | An interrupt handler cannot acquire a lock, so it does not | ||
| 226 | make sense to try to release a lock within an interrupt | ||
| 227 | handler. */ | ||
| 228 | void | ||
| 229 | lock_release (struct lock *lock) | ||
| 230 | { | ||
| 231 | ASSERT (lock != NULL); | ||
| 232 | ASSERT (lock_held_by_current_thread (lock)); | ||
| 233 | |||
| 234 | lock->holder = NULL; | ||
| 235 | sema_up (&lock->semaphore); | ||
| 236 | } | ||
| 237 | |||
| 238 | /* Returns true if the current thread holds LOCK, false | ||
| 239 | otherwise. (Note that testing whether some other thread holds | ||
| 240 | a lock would be racy.) */ | ||
| 241 | bool | ||
| 242 | lock_held_by_current_thread (const struct lock *lock) | ||
| 243 | { | ||
| 244 | ASSERT (lock != NULL); | ||
| 245 | |||
| 246 | return lock->holder == thread_current (); | ||
| 247 | } | ||
| 248 | |||
| 249 | /* One semaphore in a list. */ | ||
| 250 | struct semaphore_elem | ||
| 251 | { | ||
| 252 | struct list_elem elem; /* List element. */ | ||
| 253 | struct semaphore semaphore; /* This semaphore. */ | ||
| 254 | }; | ||
| 255 | |||
| 256 | /* Initializes condition variable COND. A condition variable | ||
| 257 | allows one piece of code to signal a condition and cooperating | ||
| 258 | code to receive the signal and act upon it. */ | ||
| 259 | void | ||
| 260 | cond_init (struct condition *cond) | ||
| 261 | { | ||
| 262 | ASSERT (cond != NULL); | ||
| 263 | |||
| 264 | list_init (&cond->waiters); | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Atomically releases LOCK and waits for COND to be signaled by | ||
| 268 | some other piece of code. After COND is signaled, LOCK is | ||
| 269 | reacquired before returning. LOCK must be held before calling | ||
| 270 | this function. | ||
| 271 | |||
| 272 | The monitor implemented by this function is "Mesa" style, not | ||
| 273 | "Hoare" style, that is, sending and receiving a signal are not | ||
| 274 | an atomic operation. Thus, typically the caller must recheck | ||
| 275 | the condition after the wait completes and, if necessary, wait | ||
| 276 | again. | ||
| 277 | |||
| 278 | A given condition variable is associated with only a single | ||
| 279 | lock, but one lock may be associated with any number of | ||
| 280 | condition variables. That is, there is a one-to-many mapping | ||
| 281 | from locks to condition variables. | ||
| 282 | |||
| 283 | This function may sleep, so it must not be called within an | ||
| 284 | interrupt handler. This function may be called with | ||
| 285 | interrupts disabled, but interrupts will be turned back on if | ||
| 286 | we need to sleep. */ | ||
| 287 | void | ||
| 288 | cond_wait (struct condition *cond, struct lock *lock) | ||
| 289 | { | ||
| 290 | struct semaphore_elem waiter; | ||
| 291 | |||
| 292 | ASSERT (cond != NULL); | ||
| 293 | ASSERT (lock != NULL); | ||
| 294 | ASSERT (!intr_context ()); | ||
| 295 | ASSERT (lock_held_by_current_thread (lock)); | ||
| 296 | |||
| 297 | sema_init (&waiter.semaphore, 0); | ||
| 298 | list_push_back (&cond->waiters, &waiter.elem); | ||
| 299 | lock_release (lock); | ||
| 300 | sema_down (&waiter.semaphore); | ||
| 301 | lock_acquire (lock); | ||
| 302 | } | ||
| 303 | |||
| 304 | /* If any threads are waiting on COND (protected by LOCK), then | ||
| 305 | this function signals one of them to wake up from its wait. | ||
| 306 | LOCK must be held before calling this function. | ||
| 307 | |||
| 308 | An interrupt handler cannot acquire a lock, so it does not | ||
| 309 | make sense to try to signal a condition variable within an | ||
| 310 | interrupt handler. */ | ||
| 311 | void | ||
| 312 | cond_signal (struct condition *cond, struct lock *lock UNUSED) | ||
| 313 | { | ||
| 314 | ASSERT (cond != NULL); | ||
| 315 | ASSERT (lock != NULL); | ||
| 316 | ASSERT (!intr_context ()); | ||
| 317 | ASSERT (lock_held_by_current_thread (lock)); | ||
| 318 | |||
| 319 | if (!list_empty (&cond->waiters)) | ||
| 320 | sema_up (&list_entry (list_pop_front (&cond->waiters), | ||
| 321 | struct semaphore_elem, elem)->semaphore); | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Wakes up all threads, if any, waiting on COND (protected by | ||
| 325 | LOCK). LOCK must be held before calling this function. | ||
| 326 | |||
| 327 | An interrupt handler cannot acquire a lock, so it does not | ||
| 328 | make sense to try to signal a condition variable within an | ||
| 329 | interrupt handler. */ | ||
| 330 | void | ||
| 331 | cond_broadcast (struct condition *cond, struct lock *lock) | ||
| 332 | { | ||
| 333 | ASSERT (cond != NULL); | ||
| 334 | ASSERT (lock != NULL); | ||
| 335 | |||
| 336 | while (!list_empty (&cond->waiters)) | ||
| 337 | cond_signal (cond, lock); | ||
| 338 | } | ||
diff --git a/pintos-progos/threads/synch.h b/pintos-progos/threads/synch.h new file mode 100644 index 0000000..a19e88b --- /dev/null +++ b/pintos-progos/threads/synch.h | |||
| @@ -0,0 +1,51 @@ | |||
| 1 | #ifndef THREADS_SYNCH_H | ||
| 2 | #define THREADS_SYNCH_H | ||
| 3 | |||
| 4 | #include <list.h> | ||
| 5 | #include <stdbool.h> | ||
| 6 | |||
| 7 | /* A counting semaphore. */ | ||
| 8 | struct semaphore | ||
| 9 | { | ||
| 10 | unsigned value; /* Current value. */ | ||
| 11 | struct list waiters; /* List of waiting threads. */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | void sema_init (struct semaphore *, unsigned value); | ||
| 15 | void sema_down (struct semaphore *); | ||
| 16 | bool sema_try_down (struct semaphore *); | ||
| 17 | void sema_up (struct semaphore *); | ||
| 18 | void sema_self_test (void); | ||
| 19 | |||
| 20 | /* Lock. */ | ||
| 21 | struct lock | ||
| 22 | { | ||
| 23 | struct thread *holder; /* Thread holding lock (for debugging). */ | ||
| 24 | struct semaphore semaphore; /* Binary semaphore controlling access. */ | ||
| 25 | }; | ||
| 26 | |||
| 27 | void lock_init (struct lock *); | ||
| 28 | void lock_acquire (struct lock *); | ||
| 29 | bool lock_try_acquire (struct lock *); | ||
| 30 | void lock_release (struct lock *); | ||
| 31 | bool lock_held_by_current_thread (const struct lock *); | ||
| 32 | |||
| 33 | /* Condition variable. */ | ||
| 34 | struct condition | ||
| 35 | { | ||
| 36 | struct list waiters; /* List of waiting threads. */ | ||
| 37 | }; | ||
| 38 | |||
| 39 | void cond_init (struct condition *); | ||
| 40 | void cond_wait (struct condition *, struct lock *); | ||
| 41 | void cond_signal (struct condition *, struct lock *); | ||
| 42 | void cond_broadcast (struct condition *, struct lock *); | ||
| 43 | |||
| 44 | /* Optimization barrier. | ||
| 45 | |||
| 46 | The compiler will not reorder operations across an | ||
| 47 | optimization barrier. See "Optimization Barriers" in the | ||
| 48 | reference guide for more information.*/ | ||
| 49 | #define barrier() asm volatile ("" : : : "memory") | ||
| 50 | |||
| 51 | #endif /* threads/synch.h */ | ||
diff --git a/pintos-progos/threads/thread.c b/pintos-progos/threads/thread.c new file mode 100644 index 0000000..845f059 --- /dev/null +++ b/pintos-progos/threads/thread.c | |||
| @@ -0,0 +1,594 @@ | |||
| 1 | #include "threads/thread.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stddef.h> | ||
| 4 | #include <random.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <string.h> | ||
| 7 | #include "threads/flags.h" | ||
| 8 | #include "threads/interrupt.h" | ||
| 9 | #include "threads/intr-stubs.h" | ||
| 10 | #include "threads/palloc.h" | ||
| 11 | #include "threads/switch.h" | ||
| 12 | #include "threads/synch.h" | ||
| 13 | #include "threads/vaddr.h" | ||
| 14 | #ifdef USERPROG | ||
| 15 | #include "userprog/process.h" | ||
| 16 | #endif | ||
| 17 | |||
| 18 | /* Random value for struct thread's `magic' member. | ||
| 19 | Used to detect stack overflow. See the big comment at the top | ||
| 20 | of thread.h for details. */ | ||
| 21 | #define THREAD_MAGIC 0xcd6abf4b | ||
| 22 | |||
| 23 | /* List of processes in THREAD_READY state, that is, processes | ||
| 24 | that are ready to run but not actually running. */ | ||
| 25 | static struct list ready_list; | ||
| 26 | |||
| 27 | /* List of all processes. Processes are added to this list | ||
| 28 | when they are first scheduled and removed when they exit. */ | ||
| 29 | static struct list all_list; | ||
| 30 | |||
| 31 | /* Idle thread. */ | ||
| 32 | static struct thread *idle_thread; | ||
| 33 | |||
| 34 | /* Initial thread, the thread running init.c:main(). */ | ||
| 35 | static struct thread *initial_thread; | ||
| 36 | |||
| 37 | /* Lock used by allocate_tid(). */ | ||
| 38 | static struct lock tid_lock; | ||
| 39 | |||
| 40 | /* Stack frame for kernel_thread(). */ | ||
| 41 | struct kernel_thread_frame | ||
| 42 | { | ||
| 43 | void *eip; /* Return address. */ | ||
| 44 | thread_func *function; /* Function to call. */ | ||
| 45 | void *aux; /* Auxiliary data for function. */ | ||
| 46 | }; | ||
| 47 | |||
| 48 | /* Statistics. */ | ||
| 49 | static long long idle_ticks; /* # of timer ticks spent idle. */ | ||
| 50 | static long long kernel_ticks; /* # of timer ticks in kernel threads. */ | ||
| 51 | static long long user_ticks; /* # of timer ticks in user programs. */ | ||
| 52 | |||
| 53 | /* Scheduling. */ | ||
| 54 | #define TIME_SLICE 4 /* # of timer ticks to give each thread. */ | ||
| 55 | static unsigned thread_ticks; /* # of timer ticks since last yield. */ | ||
| 56 | |||
| 57 | /* If false (default), use round-robin scheduler. | ||
| 58 | If true, use multi-level feedback queue scheduler. | ||
| 59 | Controlled by kernel command-line option "-o mlfqs". */ | ||
| 60 | bool thread_mlfqs; | ||
| 61 | |||
| 62 | static void kernel_thread (thread_func *, void *aux); | ||
| 63 | |||
| 64 | static void idle (void *aux UNUSED); | ||
| 65 | static struct thread *running_thread (void); | ||
| 66 | static struct thread *next_thread_to_run (void); | ||
| 67 | static void init_thread (struct thread *, const char *name, int priority); | ||
| 68 | static bool is_thread (struct thread *) UNUSED; | ||
| 69 | static void *alloc_frame (struct thread *, size_t size); | ||
| 70 | static void schedule (void); | ||
| 71 | void thread_schedule_tail (struct thread *prev); | ||
| 72 | static tid_t allocate_tid (void); | ||
| 73 | |||
| 74 | /* Initializes the threading system by transforming the code | ||
| 75 | that's currently running into a thread. This can't work in | ||
| 76 | general and it is possible in this case only because loader.S | ||
| 77 | was careful to put the bottom of the stack at a page boundary. | ||
| 78 | |||
| 79 | Also initializes the run queue and the tid lock. | ||
| 80 | |||
| 81 | After calling this function, be sure to initialize the page | ||
| 82 | allocator before trying to create any threads with | ||
| 83 | thread_create(). | ||
| 84 | |||
| 85 | It is not safe to call thread_current() until this function | ||
| 86 | finishes. */ | ||
| 87 | void | ||
| 88 | thread_init (void) | ||
| 89 | { | ||
| 90 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 91 | |||
| 92 | lock_init (&tid_lock); | ||
| 93 | list_init (&ready_list); | ||
| 94 | list_init (&all_list); | ||
| 95 | |||
| 96 | #ifdef USERPROG | ||
| 97 | process_init (); | ||
| 98 | #endif | ||
| 99 | |||
| 100 | /* Set up a thread structure for the running thread. */ | ||
| 101 | initial_thread = running_thread (); | ||
| 102 | init_thread (initial_thread, "main", PRI_DEFAULT); | ||
| 103 | initial_thread->status = THREAD_RUNNING; | ||
| 104 | initial_thread->tid = allocate_tid (); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* Starts preemptive thread scheduling by enabling interrupts. | ||
| 108 | Also creates the idle thread. */ | ||
| 109 | void | ||
| 110 | thread_start (void) | ||
| 111 | { | ||
| 112 | /* Create the idle thread. */ | ||
| 113 | struct semaphore idle_started; | ||
| 114 | sema_init (&idle_started, 0); | ||
| 115 | thread_create ("idle", PRI_MIN, idle, &idle_started); | ||
| 116 | |||
| 117 | /* Start preemptive thread scheduling. */ | ||
| 118 | intr_enable (); | ||
| 119 | |||
| 120 | /* Wait for the idle thread to initialize idle_thread. */ | ||
| 121 | sema_down (&idle_started); | ||
| 122 | } | ||
| 123 | |||
| 124 | /* Called by the timer interrupt handler at each timer tick. | ||
| 125 | Thus, this function runs in an external interrupt context. */ | ||
| 126 | void | ||
| 127 | thread_tick (void) | ||
| 128 | { | ||
| 129 | struct thread *t = thread_current (); | ||
| 130 | |||
| 131 | /* Update statistics. */ | ||
| 132 | if (t == idle_thread) | ||
| 133 | idle_ticks++; | ||
| 134 | #ifdef USERPROG | ||
| 135 | else if (t->pagedir != NULL) | ||
| 136 | user_ticks++; | ||
| 137 | #endif | ||
| 138 | else | ||
| 139 | kernel_ticks++; | ||
| 140 | |||
| 141 | /* Enforce preemption. */ | ||
| 142 | if (++thread_ticks >= TIME_SLICE) | ||
| 143 | intr_yield_on_return (); | ||
| 144 | } | ||
| 145 | |||
| 146 | /* Prints thread statistics. */ | ||
| 147 | void | ||
| 148 | thread_print_stats (void) | ||
| 149 | { | ||
| 150 | printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n", | ||
| 151 | idle_ticks, kernel_ticks, user_ticks); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* Creates a new kernel thread named NAME with the given initial | ||
| 155 | PRIORITY, which executes FUNCTION passing AUX as the argument, | ||
| 156 | and adds it to the ready queue. Returns the thread identifier | ||
| 157 | for the new thread, or TID_ERROR if creation fails. | ||
| 158 | |||
| 159 | If thread_start() has been called, then the new thread may be | ||
| 160 | scheduled before thread_create() returns. It could even exit | ||
| 161 | before thread_create() returns. Contrariwise, the original | ||
| 162 | thread may run for any amount of time before the new thread is | ||
| 163 | scheduled. Use a semaphore or some other form of | ||
| 164 | synchronization if you need to ensure ordering. | ||
| 165 | |||
| 166 | The code provided sets the new thread's `priority' member to | ||
| 167 | PRIORITY, but no actual priority scheduling is implemented. | ||
| 168 | Priority scheduling is the goal of Problem 1-3. */ | ||
| 169 | tid_t | ||
| 170 | thread_create (const char *name, int priority, | ||
| 171 | thread_func *function, void *aux) | ||
| 172 | { | ||
| 173 | struct thread *t; | ||
| 174 | struct kernel_thread_frame *kf; | ||
| 175 | struct switch_entry_frame *ef; | ||
| 176 | struct switch_threads_frame *sf; | ||
| 177 | tid_t tid; | ||
| 178 | enum intr_level old_level; | ||
| 179 | |||
| 180 | ASSERT (function != NULL); | ||
| 181 | |||
| 182 | /* Allocate thread. */ | ||
| 183 | t = palloc_get_page (PAL_ZERO); | ||
| 184 | if (t == NULL) | ||
| 185 | return TID_ERROR; | ||
| 186 | |||
| 187 | /* Initialize thread. */ | ||
| 188 | init_thread (t, name, priority); | ||
| 189 | tid = t->tid = allocate_tid (); | ||
| 190 | |||
| 191 | /* Prepare thread for first run by initializing its stack. | ||
| 192 | Do this atomically so intermediate values for the 'stack' | ||
| 193 | member cannot be observed. */ | ||
| 194 | old_level = intr_disable (); | ||
| 195 | |||
| 196 | /* Stack frame for kernel_thread(). */ | ||
| 197 | kf = alloc_frame (t, sizeof *kf); | ||
| 198 | kf->eip = NULL; | ||
| 199 | kf->function = function; | ||
| 200 | kf->aux = aux; | ||
| 201 | |||
| 202 | /* Stack frame for switch_entry(). */ | ||
| 203 | ef = alloc_frame (t, sizeof *ef); | ||
| 204 | ef->eip = (void (*) (void)) kernel_thread; | ||
| 205 | |||
| 206 | /* Stack frame for switch_threads(). */ | ||
| 207 | sf = alloc_frame (t, sizeof *sf); | ||
| 208 | sf->eip = switch_entry; | ||
| 209 | sf->ebp = 0; | ||
| 210 | |||
| 211 | intr_set_level (old_level); | ||
| 212 | |||
| 213 | /* Add to run queue. */ | ||
| 214 | thread_unblock (t); | ||
| 215 | |||
| 216 | return tid; | ||
| 217 | } | ||
| 218 | |||
| 219 | /* Puts the current thread to sleep. It will not be scheduled | ||
| 220 | again until awoken by thread_unblock(). | ||
| 221 | |||
| 222 | This function must be called with interrupts turned off. It | ||
| 223 | is usually a better idea to use one of the synchronization | ||
| 224 | primitives in synch.h. */ | ||
| 225 | void | ||
| 226 | thread_block (void) | ||
| 227 | { | ||
| 228 | ASSERT (!intr_context ()); | ||
| 229 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 230 | |||
| 231 | thread_current ()->status = THREAD_BLOCKED; | ||
| 232 | schedule (); | ||
| 233 | } | ||
| 234 | |||
| 235 | /* Transitions a blocked thread T to the ready-to-run state. | ||
| 236 | This is an error if T is not blocked. (Use thread_yield() to | ||
| 237 | make the running thread ready.) | ||
| 238 | |||
| 239 | This function does not preempt the running thread. This can | ||
| 240 | be important: if the caller had disabled interrupts itself, | ||
| 241 | it may expect that it can atomically unblock a thread and | ||
| 242 | update other data. */ | ||
| 243 | void | ||
| 244 | thread_unblock (struct thread *t) | ||
| 245 | { | ||
| 246 | enum intr_level old_level; | ||
| 247 | |||
| 248 | ASSERT (is_thread (t)); | ||
| 249 | |||
| 250 | old_level = intr_disable (); | ||
| 251 | ASSERT (t->status == THREAD_BLOCKED); | ||
| 252 | list_push_back (&ready_list, &t->elem); | ||
| 253 | t->status = THREAD_READY; | ||
| 254 | intr_set_level (old_level); | ||
| 255 | } | ||
| 256 | |||
| 257 | /* Returns the name of the running thread. */ | ||
| 258 | const char * | ||
| 259 | thread_name (void) | ||
| 260 | { | ||
| 261 | return thread_current ()->name; | ||
| 262 | } | ||
| 263 | |||
| 264 | /* Returns the running thread. | ||
| 265 | This is running_thread() plus a couple of sanity checks. | ||
| 266 | See the big comment at the top of thread.h for details. */ | ||
| 267 | struct thread * | ||
| 268 | thread_current (void) | ||
| 269 | { | ||
| 270 | struct thread *t = running_thread (); | ||
| 271 | |||
| 272 | /* Make sure T is really a thread. | ||
| 273 | If either of these assertions fire, then your thread may | ||
| 274 | have overflowed its stack. Each thread has less than 4 kB | ||
| 275 | of stack, so a few big automatic arrays or moderate | ||
| 276 | recursion can cause stack overflow. */ | ||
| 277 | ASSERT (is_thread (t)); | ||
| 278 | ASSERT (t->status == THREAD_RUNNING); | ||
| 279 | |||
| 280 | return t; | ||
| 281 | } | ||
| 282 | |||
| 283 | /* Returns the running thread's tid. */ | ||
| 284 | tid_t | ||
| 285 | thread_tid (void) | ||
| 286 | { | ||
| 287 | return thread_current ()->tid; | ||
| 288 | } | ||
| 289 | |||
| 290 | /* Deschedules the current thread and destroys it. Never | ||
| 291 | returns to the caller. */ | ||
| 292 | void | ||
| 293 | thread_exit (void) | ||
| 294 | { | ||
| 295 | ASSERT (!intr_context ()); | ||
| 296 | |||
| 297 | #ifdef USERPROG | ||
| 298 | process_exit (); | ||
| 299 | #endif | ||
| 300 | |||
| 301 | /* Remove thread from all threads list, set our status to dying, | ||
| 302 | and schedule another process. That process will destroy us | ||
| 303 | when it calls thread_schedule_tail(). */ | ||
| 304 | intr_disable (); | ||
| 305 | list_remove (&thread_current()->allelem); | ||
| 306 | thread_current ()->status = THREAD_DYING; | ||
| 307 | schedule (); | ||
| 308 | NOT_REACHED (); | ||
| 309 | } | ||
| 310 | |||
| 311 | /* Yields the CPU. The current thread is not put to sleep and | ||
| 312 | may be scheduled again immediately at the scheduler's whim. */ | ||
| 313 | void | ||
| 314 | thread_yield (void) | ||
| 315 | { | ||
| 316 | struct thread *cur = thread_current (); | ||
| 317 | enum intr_level old_level; | ||
| 318 | |||
| 319 | ASSERT (!intr_context ()); | ||
| 320 | |||
| 321 | old_level = intr_disable (); | ||
| 322 | if (cur != idle_thread) | ||
| 323 | list_push_back (&ready_list, &cur->elem); | ||
| 324 | cur->status = THREAD_READY; | ||
| 325 | schedule (); | ||
| 326 | intr_set_level (old_level); | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Invoke function 'func' on all threads, passing along 'aux'. | ||
| 330 | This function must be called with interrupts off. */ | ||
| 331 | void | ||
| 332 | thread_foreach (thread_action_func *func, void *aux) | ||
| 333 | { | ||
| 334 | struct list_elem *e; | ||
| 335 | |||
| 336 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 337 | |||
| 338 | for (e = list_begin (&all_list); e != list_end (&all_list); | ||
| 339 | e = list_next (e)) | ||
| 340 | { | ||
| 341 | struct thread *t = list_entry (e, struct thread, allelem); | ||
| 342 | func (t, aux); | ||
| 343 | } | ||
| 344 | } | ||
| 345 | |||
| 346 | /* Sets the current thread's priority to NEW_PRIORITY. */ | ||
| 347 | void | ||
| 348 | thread_set_priority (int new_priority) | ||
| 349 | { | ||
| 350 | thread_current ()->priority = new_priority; | ||
| 351 | } | ||
| 352 | |||
| 353 | /* Returns the current thread's priority. */ | ||
| 354 | int | ||
| 355 | thread_get_priority (void) | ||
| 356 | { | ||
| 357 | return thread_current ()->priority; | ||
| 358 | } | ||
| 359 | |||
| 360 | /* Sets the current thread's nice value to NICE. */ | ||
| 361 | void | ||
| 362 | thread_set_nice (int nice UNUSED) | ||
| 363 | { | ||
| 364 | /* Not yet implemented. */ | ||
| 365 | } | ||
| 366 | |||
| 367 | /* Returns the current thread's nice value. */ | ||
| 368 | int | ||
| 369 | thread_get_nice (void) | ||
| 370 | { | ||
| 371 | /* Not yet implemented. */ | ||
| 372 | return 0; | ||
| 373 | } | ||
| 374 | |||
| 375 | /* Returns 100 times the system load average. */ | ||
| 376 | int | ||
| 377 | thread_get_load_avg (void) | ||
| 378 | { | ||
| 379 | /* Not yet implemented. */ | ||
| 380 | return 0; | ||
| 381 | } | ||
| 382 | |||
| 383 | /* Returns 100 times the current thread's recent_cpu value. */ | ||
| 384 | int | ||
| 385 | thread_get_recent_cpu (void) | ||
| 386 | { | ||
| 387 | /* Not yet implemented. */ | ||
| 388 | return 0; | ||
| 389 | } | ||
| 390 | |||
| 391 | /* Idle thread. Executes when no other thread is ready to run. | ||
| 392 | |||
| 393 | The idle thread is initially put on the ready list by | ||
| 394 | thread_start(). It will be scheduled once initially, at which | ||
| 395 | point it initializes idle_thread, "up"s the semaphore passed | ||
| 396 | to it to enable thread_start() to continue, and immediately | ||
| 397 | blocks. After that, the idle thread never appears in the | ||
| 398 | ready list. It is returned by next_thread_to_run() as a | ||
| 399 | special case when the ready list is empty. */ | ||
| 400 | static void | ||
| 401 | idle (void *idle_started_ UNUSED) | ||
| 402 | { | ||
| 403 | struct semaphore *idle_started = idle_started_; | ||
| 404 | idle_thread = thread_current (); | ||
| 405 | sema_up (idle_started); | ||
| 406 | |||
| 407 | for (;;) | ||
| 408 | { | ||
| 409 | /* Let someone else run. */ | ||
| 410 | intr_disable (); | ||
| 411 | thread_block (); | ||
| 412 | |||
| 413 | /* Re-enable interrupts and wait for the next one. | ||
| 414 | |||
| 415 | The `sti' instruction disables interrupts until the | ||
| 416 | completion of the next instruction, so these two | ||
| 417 | instructions are executed atomically. This atomicity is | ||
| 418 | important; otherwise, an interrupt could be handled | ||
| 419 | between re-enabling interrupts and waiting for the next | ||
| 420 | one to occur, wasting as much as one clock tick worth of | ||
| 421 | time. | ||
| 422 | |||
| 423 | See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a] | ||
| 424 | 7.11.1 "HLT Instruction". */ | ||
| 425 | asm volatile ("sti; hlt" : : : "memory"); | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 429 | /* Function used as the basis for a kernel thread. */ | ||
| 430 | static void | ||
| 431 | kernel_thread (thread_func *function, void *aux) | ||
| 432 | { | ||
| 433 | ASSERT (function != NULL); | ||
| 434 | |||
| 435 | intr_enable (); /* The scheduler runs with interrupts off. */ | ||
| 436 | function (aux); /* Execute the thread function. */ | ||
| 437 | thread_exit (); /* If function() returns, kill the thread. */ | ||
| 438 | } | ||
| 439 | |||
| 440 | /* Returns the running thread. */ | ||
| 441 | struct thread * | ||
| 442 | running_thread (void) | ||
| 443 | { | ||
| 444 | uint32_t *esp; | ||
| 445 | |||
| 446 | /* Copy the CPU's stack pointer into `esp', and then round that | ||
| 447 | down to the start of a page. Because `struct thread' is | ||
| 448 | always at the beginning of a page and the stack pointer is | ||
| 449 | somewhere in the middle, this locates the curent thread. */ | ||
| 450 | asm ("mov %%esp, %0" : "=g" (esp)); | ||
| 451 | return pg_round_down (esp); | ||
| 452 | } | ||
| 453 | |||
| 454 | /* Returns true if T appears to point to a valid thread. */ | ||
| 455 | static bool | ||
| 456 | is_thread (struct thread *t) | ||
| 457 | { | ||
| 458 | return t != NULL && t->magic == THREAD_MAGIC; | ||
| 459 | } | ||
| 460 | |||
| 461 | /* Does basic initialization of T as a blocked thread named | ||
| 462 | NAME. */ | ||
| 463 | static void | ||
| 464 | init_thread (struct thread *t, const char *name, int priority) | ||
| 465 | { | ||
| 466 | ASSERT (t != NULL); | ||
| 467 | ASSERT (PRI_MIN <= priority && priority <= PRI_MAX); | ||
| 468 | ASSERT (name != NULL); | ||
| 469 | |||
| 470 | memset (t, 0, sizeof *t); | ||
| 471 | t->status = THREAD_BLOCKED; | ||
| 472 | strlcpy (t->name, name, sizeof t->name); | ||
| 473 | t->stack = (uint8_t *) t + PGSIZE; | ||
| 474 | t->priority = priority; | ||
| 475 | t->magic = THREAD_MAGIC; | ||
| 476 | list_push_back (&all_list, &t->allelem); | ||
| 477 | #ifdef USERPROG | ||
| 478 | list_init(&t->children); | ||
| 479 | #endif | ||
| 480 | } | ||
| 481 | |||
| 482 | /* Allocates a SIZE-byte frame at the top of thread T's stack and | ||
| 483 | returns a pointer to the frame's base. */ | ||
| 484 | static void * | ||
| 485 | alloc_frame (struct thread *t, size_t size) | ||
| 486 | { | ||
| 487 | /* Stack data is always allocated in word-size units. */ | ||
| 488 | ASSERT (is_thread (t)); | ||
| 489 | ASSERT (size % sizeof (uint32_t) == 0); | ||
| 490 | |||
| 491 | t->stack -= size; | ||
| 492 | return t->stack; | ||
| 493 | } | ||
| 494 | |||
| 495 | /* Chooses and returns the next thread to be scheduled. Should | ||
| 496 | return a thread from the run queue, unless the run queue is | ||
| 497 | empty. (If the running thread can continue running, then it | ||
| 498 | will be in the run queue.) If the run queue is empty, return | ||
| 499 | idle_thread. */ | ||
| 500 | static struct thread * | ||
| 501 | next_thread_to_run (void) | ||
| 502 | { | ||
| 503 | if (list_empty (&ready_list)) | ||
| 504 | return idle_thread; | ||
| 505 | else | ||
| 506 | return list_entry (list_pop_front (&ready_list), struct thread, elem); | ||
| 507 | } | ||
| 508 | |||
| 509 | /* Completes a thread switch by activating the new thread's page | ||
| 510 | tables, and, if the previous thread is dying, destroying it. | ||
| 511 | |||
| 512 | At this function's invocation, we just switched from thread | ||
| 513 | PREV, the new thread is already running, and interrupts are | ||
| 514 | still disabled. This function is normally invoked by | ||
| 515 | thread_schedule() as its final action before returning, but | ||
| 516 | the first time a thread is scheduled it is called by | ||
| 517 | switch_entry() (see switch.S). | ||
| 518 | |||
| 519 | It's not safe to call printf() until the thread switch is | ||
| 520 | complete. In practice that means that printf()s should be | ||
| 521 | added at the end of the function. | ||
| 522 | |||
| 523 | After this function and its caller returns, the thread switch | ||
| 524 | is complete. */ | ||
| 525 | void | ||
| 526 | thread_schedule_tail (struct thread *prev) | ||
| 527 | { | ||
| 528 | struct thread *cur = running_thread (); | ||
| 529 | |||
| 530 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 531 | |||
| 532 | /* Mark us as running. */ | ||
| 533 | cur->status = THREAD_RUNNING; | ||
| 534 | |||
| 535 | /* Start new time slice. */ | ||
| 536 | thread_ticks = 0; | ||
| 537 | |||
| 538 | #ifdef USERPROG | ||
| 539 | /* Activate the new address space. */ | ||
| 540 | process_activate (); | ||
| 541 | #endif | ||
| 542 | |||
| 543 | /* If the thread we switched from is dying, destroy its struct | ||
| 544 | thread. This must happen late so that thread_exit() doesn't | ||
| 545 | pull out the rug under itself. (We don't free | ||
| 546 | initial_thread because its memory was not obtained via | ||
| 547 | palloc().) */ | ||
| 548 | if (prev != NULL && prev->status == THREAD_DYING && prev != initial_thread) | ||
| 549 | { | ||
| 550 | ASSERT (prev != cur); | ||
| 551 | palloc_free_page (prev); | ||
| 552 | } | ||
| 553 | } | ||
| 554 | |||
| 555 | /* Schedules a new process. At entry, interrupts must be off and | ||
| 556 | the running process's state must have been changed from | ||
| 557 | running to some other state. This function finds another | ||
| 558 | thread to run and switches to it. | ||
| 559 | |||
| 560 | It's not safe to call printf() until thread_schedule_tail() | ||
| 561 | has completed. */ | ||
| 562 | static void | ||
| 563 | schedule (void) | ||
| 564 | { | ||
| 565 | struct thread *cur = running_thread (); | ||
| 566 | struct thread *next = next_thread_to_run (); | ||
| 567 | struct thread *prev = NULL; | ||
| 568 | |||
| 569 | ASSERT (intr_get_level () == INTR_OFF); | ||
| 570 | ASSERT (cur->status != THREAD_RUNNING); | ||
| 571 | ASSERT (is_thread (next)); | ||
| 572 | |||
| 573 | if (cur != next) | ||
| 574 | prev = switch_threads (cur, next); | ||
| 575 | thread_schedule_tail (prev); | ||
| 576 | } | ||
| 577 | |||
| 578 | /* Returns a tid to use for a new thread. */ | ||
| 579 | static tid_t | ||
| 580 | allocate_tid (void) | ||
| 581 | { | ||
| 582 | static tid_t next_tid = 1; | ||
| 583 | tid_t tid; | ||
| 584 | |||
| 585 | lock_acquire (&tid_lock); | ||
| 586 | tid = next_tid++; | ||
| 587 | lock_release (&tid_lock); | ||
| 588 | |||
| 589 | return tid; | ||
| 590 | } | ||
| 591 | |||
| 592 | /* Offset of `stack' member within `struct thread'. | ||
| 593 | Used by switch.S, which can't figure it out on its own. */ | ||
| 594 | uint32_t thread_stack_ofs = offsetof (struct thread, stack); | ||
diff --git a/pintos-progos/threads/thread.h b/pintos-progos/threads/thread.h new file mode 100644 index 0000000..eebf42c --- /dev/null +++ b/pintos-progos/threads/thread.h | |||
| @@ -0,0 +1,144 @@ | |||
| 1 | #ifndef THREADS_THREAD_H | ||
| 2 | #define THREADS_THREAD_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <list.h> | ||
| 6 | #include <stdint.h> | ||
| 7 | #include "threads/synch.h" | ||
| 8 | |||
| 9 | /* States in a thread's life cycle. */ | ||
| 10 | enum thread_status | ||
| 11 | { | ||
| 12 | THREAD_RUNNING, /* Running thread. */ | ||
| 13 | THREAD_READY, /* Not running but ready to run. */ | ||
| 14 | THREAD_BLOCKED, /* Waiting for an event to trigger. */ | ||
| 15 | THREAD_DYING /* About to be destroyed. */ | ||
| 16 | }; | ||
| 17 | |||
| 18 | /* Thread identifier type. | ||
| 19 | You can redefine this to whatever type you like. */ | ||
| 20 | typedef int tid_t; | ||
| 21 | #define TID_ERROR ((tid_t) -1) /* Error value for tid_t. */ | ||
| 22 | |||
| 23 | /* Thread priorities. */ | ||
| 24 | #define PRI_MIN 0 /* Lowest priority. */ | ||
| 25 | #define PRI_DEFAULT 31 /* Default priority. */ | ||
| 26 | #define PRI_MAX 63 /* Highest priority. */ | ||
| 27 | |||
| 28 | /* A kernel thread or user process. | ||
| 29 | |||
| 30 | Each thread structure is stored in its own 4 kB page. The | ||
| 31 | thread structure itself sits at the very bottom of the page | ||
| 32 | (at offset 0). The rest of the page is reserved for the | ||
| 33 | thread's kernel stack, which grows downward from the top of | ||
| 34 | the page (at offset 4 kB). Here's an illustration: | ||
| 35 | |||
| 36 | 4 kB +---------------------------------+ | ||
| 37 | | kernel stack | | ||
| 38 | | | | | ||
| 39 | | | | | ||
| 40 | | V | | ||
| 41 | | grows downward | | ||
| 42 | | | | ||
| 43 | | | | ||
| 44 | | | | ||
| 45 | | | | ||
| 46 | | | | ||
| 47 | | | | ||
| 48 | | | | ||
| 49 | | | | ||
| 50 | +---------------------------------+ | ||
| 51 | | magic | | ||
| 52 | | : | | ||
| 53 | | : | | ||
| 54 | | name | | ||
| 55 | | status | | ||
| 56 | 0 kB +---------------------------------+ | ||
| 57 | |||
| 58 | The upshot of this is twofold: | ||
| 59 | |||
| 60 | 1. First, `struct thread' must not be allowed to grow too | ||
| 61 | big. If it does, then there will not be enough room for | ||
| 62 | the kernel stack. Our base `struct thread' is only a | ||
| 63 | few bytes in size. It probably should stay well under 1 | ||
| 64 | kB. | ||
| 65 | |||
| 66 | 2. Second, kernel stacks must not be allowed to grow too | ||
| 67 | large. If a stack overflows, it will corrupt the thread | ||
| 68 | state. Thus, kernel functions should not allocate large | ||
| 69 | structures or arrays as non-static local variables. Use | ||
| 70 | dynamic allocation with malloc() or palloc_get_page() | ||
| 71 | instead. | ||
| 72 | |||
| 73 | The first symptom of either of these problems will probably be | ||
| 74 | an assertion failure in thread_current(), which checks that | ||
| 75 | the `magic' member of the running thread's `struct thread' is | ||
| 76 | set to THREAD_MAGIC. Stack overflow will normally change this | ||
| 77 | value, triggering the assertion. */ | ||
| 78 | /* The `elem' member has a dual purpose. It can be an element in | ||
| 79 | the run queue (thread.c), or it can be an element in a | ||
| 80 | semaphore wait list (synch.c). It can be used these two ways | ||
| 81 | only because they are mutually exclusive: only a thread in the | ||
| 82 | ready state is on the run queue, whereas only a thread in the | ||
| 83 | blocked state is on a semaphore wait list. */ | ||
| 84 | struct thread | ||
| 85 | { | ||
| 86 | /* Owned by thread.c. */ | ||
| 87 | tid_t tid; /* Thread identifier. */ | ||
| 88 | enum thread_status status; /* Thread state. */ | ||
| 89 | char name[16]; /* Name (for debugging purposes). */ | ||
| 90 | uint8_t *stack; /* Saved stack pointer. */ | ||
| 91 | int priority; /* Priority. */ | ||
| 92 | struct list_elem allelem; /* List element for all threads list. */ | ||
| 93 | |||
| 94 | /* Shared between thread.c and synch.c. */ | ||
| 95 | struct list_elem elem; /* List element. */ | ||
| 96 | |||
| 97 | #ifdef USERPROG | ||
| 98 | /* Owned by userprog/process.c */ | ||
| 99 | struct process* process; /* Process Structure */ | ||
| 100 | struct list children; /* Threads can hold processes, but not vice versa */ | ||
| 101 | uint32_t *pagedir; /* Page directory. */ | ||
| 102 | #endif | ||
| 103 | |||
| 104 | /* Owned by thread.c. */ | ||
| 105 | unsigned magic; /* Detects stack overflow. */ | ||
| 106 | }; | ||
| 107 | |||
| 108 | /* If false (default), use round-robin scheduler. | ||
| 109 | If true, use multi-level feedback queue scheduler. | ||
| 110 | Controlled by kernel command-line option "-o mlfqs". */ | ||
| 111 | extern bool thread_mlfqs; | ||
| 112 | |||
| 113 | void thread_init (void); | ||
| 114 | void thread_start (void); | ||
| 115 | |||
| 116 | void thread_tick (void); | ||
| 117 | void thread_print_stats (void); | ||
| 118 | |||
| 119 | typedef void thread_func (void *aux); | ||
| 120 | tid_t thread_create (const char *name, int priority, thread_func *, void *); | ||
| 121 | |||
| 122 | void thread_block (void); | ||
| 123 | void thread_unblock (struct thread *); | ||
| 124 | |||
| 125 | struct thread *thread_current (void); | ||
| 126 | tid_t thread_tid (void); | ||
| 127 | const char *thread_name (void); | ||
| 128 | |||
| 129 | void thread_exit (void) NO_RETURN; | ||
| 130 | void thread_yield (void); | ||
| 131 | |||
| 132 | /* Performs some operation on thread t, given auxiliary data AUX. */ | ||
| 133 | typedef void thread_action_func (struct thread *t, void *aux); | ||
| 134 | void thread_foreach (thread_action_func *, void *); | ||
| 135 | |||
| 136 | int thread_get_priority (void); | ||
| 137 | void thread_set_priority (int); | ||
| 138 | |||
| 139 | int thread_get_nice (void); | ||
| 140 | void thread_set_nice (int); | ||
| 141 | int thread_get_recent_cpu (void); | ||
| 142 | int thread_get_load_avg (void); | ||
| 143 | |||
| 144 | #endif /* threads/thread.h */ | ||
diff --git a/pintos-progos/threads/vaddr.h b/pintos-progos/threads/vaddr.h new file mode 100644 index 0000000..184c824 --- /dev/null +++ b/pintos-progos/threads/vaddr.h | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | #ifndef THREADS_VADDR_H | ||
| 2 | #define THREADS_VADDR_H | ||
| 3 | |||
| 4 | #include <debug.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <stdbool.h> | ||
| 7 | |||
| 8 | #include "threads/loader.h" | ||
| 9 | |||
| 10 | /* Functions and macros for working with virtual addresses. | ||
| 11 | |||
| 12 | See pte.h for functions and macros specifically for x86 | ||
| 13 | hardware page tables. */ | ||
| 14 | |||
| 15 | #define BITMASK(SHIFT, CNT) (((1ul << (CNT)) - 1) << (SHIFT)) | ||
| 16 | |||
| 17 | /* Page offset (bits 0:12). */ | ||
| 18 | #define PGSHIFT 0 /* Index of first offset bit. */ | ||
| 19 | #define PGBITS 12 /* Number of offset bits. */ | ||
| 20 | #define PGSIZE (1 << PGBITS) /* Bytes in a page. */ | ||
| 21 | #define PGMASK BITMASK(PGSHIFT, PGBITS) /* Page offset bits (0:12). */ | ||
| 22 | |||
| 23 | /* Offset within a page. */ | ||
| 24 | static inline unsigned pg_ofs (const void *va) { | ||
| 25 | return (uintptr_t) va & PGMASK; | ||
| 26 | } | ||
| 27 | |||
| 28 | /* Virtual page number. */ | ||
| 29 | static inline uintptr_t pg_no (const void *va) { | ||
| 30 | return (uintptr_t) va >> PGBITS; | ||
| 31 | } | ||
| 32 | |||
| 33 | /* Round up to nearest page boundary. */ | ||
| 34 | static inline void *pg_round_up (const void *va) { | ||
| 35 | return (void *) (((uintptr_t) va + PGSIZE - 1) & ~PGMASK); | ||
| 36 | } | ||
| 37 | |||
| 38 | /* Round down to nearest page boundary. */ | ||
| 39 | static inline void *pg_round_down (const void *va) { | ||
| 40 | return (void *) ((uintptr_t) va & ~PGMASK); | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Base address of the 1:1 physical-to-virtual mapping. Physical | ||
| 44 | memory is mapped starting at this virtual address. Thus, | ||
| 45 | physical address 0 is accessible at PHYS_BASE, physical | ||
| 46 | address address 0x1234 at (uint8_t *) PHYS_BASE + 0x1234, and | ||
| 47 | so on. | ||
| 48 | |||
| 49 | This address also marks the end of user programs' address | ||
| 50 | space. Up to this point in memory, user programs are allowed | ||
| 51 | to map whatever they like. At this point and above, the | ||
| 52 | virtual address space belongs to the kernel. */ | ||
| 53 | #define PHYS_BASE ((void *) LOADER_PHYS_BASE) | ||
| 54 | |||
| 55 | /* Returns true if VADDR is a user virtual address. */ | ||
| 56 | static inline bool | ||
| 57 | is_user_vaddr (const void *vaddr) | ||
| 58 | { | ||
| 59 | return vaddr < PHYS_BASE; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* Returns true if VADDR is a kernel virtual address. */ | ||
| 63 | static inline bool | ||
| 64 | is_kernel_vaddr (const void *vaddr) | ||
| 65 | { | ||
| 66 | return vaddr >= PHYS_BASE; | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Returns kernel virtual address at which physical address PADDR | ||
| 70 | is mapped. */ | ||
| 71 | static inline void * | ||
| 72 | ptov (uintptr_t paddr) | ||
| 73 | { | ||
| 74 | ASSERT ((void *) paddr < PHYS_BASE); | ||
| 75 | |||
| 76 | return (void *) (paddr + PHYS_BASE); | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Returns physical address at which kernel virtual address VADDR | ||
| 80 | is mapped. */ | ||
| 81 | static inline uintptr_t | ||
| 82 | vtop (const void *vaddr) | ||
| 83 | { | ||
| 84 | ASSERT (is_kernel_vaddr (vaddr)); | ||
| 85 | |||
| 86 | return (uintptr_t) vaddr - (uintptr_t) PHYS_BASE; | ||
| 87 | } | ||
| 88 | |||
| 89 | #endif /* threads/vaddr.h */ | ||
diff --git a/pintos-progos/userprog/.gitignore b/pintos-progos/userprog/.gitignore new file mode 100644 index 0000000..6d5357c --- /dev/null +++ b/pintos-progos/userprog/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | build | ||
| 2 | bochsrc.txt | ||
| 3 | bochsout.txt | ||
diff --git a/pintos-progos/userprog/Make.vars b/pintos-progos/userprog/Make.vars new file mode 100644 index 0000000..e4dbb08 --- /dev/null +++ b/pintos-progos/userprog/Make.vars | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = -DUSERPROG -DFILESYS | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys | ||
| 5 | TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base | ||
| 6 | GRADING_FILE = $(SRCDIR)/tests/userprog/Grading | ||
| 7 | SIMULATOR = --qemu | ||
diff --git a/pintos-progos/userprog/Makefile b/pintos-progos/userprog/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/pintos-progos/userprog/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
diff --git a/pintos-progos/userprog/exception.c b/pintos-progos/userprog/exception.c new file mode 100644 index 0000000..17620ad --- /dev/null +++ b/pintos-progos/userprog/exception.c | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | #include "userprog/exception.h" | ||
| 2 | #include <inttypes.h> | ||
| 3 | #include <stdio.h> | ||
| 4 | #include "userprog/gdt.h" | ||
| 5 | #include "threads/interrupt.h" | ||
| 6 | #include "threads/thread.h" | ||
| 7 | #include "threads/vaddr.h" | ||
| 8 | |||
| 9 | /* Number of page faults processed. */ | ||
| 10 | static long long page_fault_cnt; | ||
| 11 | |||
| 12 | static void kill (struct intr_frame *); | ||
| 13 | static void page_fault (struct intr_frame *); | ||
| 14 | |||
| 15 | /* Registers handlers for interrupts that can be caused by user | ||
| 16 | programs. | ||
| 17 | |||
| 18 | In a real Unix-like OS, most of these interrupts would be | ||
| 19 | passed along to the user process in the form of signals, as | ||
| 20 | described in [SV-386] 3-24 and 3-25, but we don't implement | ||
| 21 | signals. Instead, we'll make them simply kill the user | ||
| 22 | process. | ||
| 23 | |||
| 24 | Page faults are an exception. Here they are treated the same | ||
| 25 | way as other exceptions, but this will need to change to | ||
| 26 | implement virtual memory. | ||
| 27 | |||
| 28 | Refer to [IA32-v3a] section 5.15 "Exception and Interrupt | ||
| 29 | Reference" for a description of each of these exceptions. */ | ||
| 30 | void | ||
| 31 | exception_init (void) | ||
| 32 | { | ||
| 33 | /* These exceptions can be raised explicitly by a user program, | ||
| 34 | e.g. via the INT, INT3, INTO, and BOUND instructions. Thus, | ||
| 35 | we set DPL==3, meaning that user programs are allowed to | ||
| 36 | invoke them via these instructions. */ | ||
| 37 | intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception"); | ||
| 38 | intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception"); | ||
| 39 | intr_register_int (5, 3, INTR_ON, kill, | ||
| 40 | "#BR BOUND Range Exceeded Exception"); | ||
| 41 | |||
| 42 | /* These exceptions have DPL==0, preventing user processes from | ||
| 43 | invoking them via the INT instruction. They can still be | ||
| 44 | caused indirectly, e.g. #DE can be caused by dividing by | ||
| 45 | 0. */ | ||
| 46 | intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error"); | ||
| 47 | intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception"); | ||
| 48 | intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception"); | ||
| 49 | intr_register_int (7, 0, INTR_ON, kill, | ||
| 50 | "#NM Device Not Available Exception"); | ||
| 51 | intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present"); | ||
| 52 | intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception"); | ||
| 53 | intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception"); | ||
| 54 | intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error"); | ||
| 55 | intr_register_int (19, 0, INTR_ON, kill, | ||
| 56 | "#XF SIMD Floating-Point Exception"); | ||
| 57 | |||
| 58 | /* Most exceptions can be handled with interrupts turned on. | ||
| 59 | We need to disable interrupts for page faults because the | ||
| 60 | fault address is stored in CR2 and needs to be preserved. */ | ||
| 61 | intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception"); | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Prints exception statistics. */ | ||
| 65 | void | ||
| 66 | exception_print_stats (void) | ||
| 67 | { | ||
| 68 | printf ("Exception: %lld page faults\n", page_fault_cnt); | ||
| 69 | } | ||
| 70 | |||
| 71 | /* Handler for an exception (probably) caused by a user process. */ | ||
| 72 | static void | ||
| 73 | kill (struct intr_frame *f) | ||
| 74 | { | ||
| 75 | /* This interrupt is one (probably) caused by a user process. | ||
| 76 | For example, the process might have tried to access unmapped | ||
| 77 | virtual memory (a page fault). For now, we simply kill the | ||
| 78 | user process. Later, we'll want to handle page faults in | ||
| 79 | the kernel. Real Unix-like operating systems pass most | ||
| 80 | exceptions back to the process via signals, but we don't | ||
| 81 | implement them. */ | ||
| 82 | |||
| 83 | /* The interrupt frame's code segment value tells us where the | ||
| 84 | exception originated. */ | ||
| 85 | switch (f->cs) | ||
| 86 | { | ||
| 87 | case SEL_UCSEG: | ||
| 88 | /* User's code segment, so it's a user exception, as we | ||
| 89 | expected. Kill the user process. */ | ||
| 90 | printf ("%s: dying due to interrupt %#04x (%s).\n", | ||
| 91 | thread_name (), f->vec_no, intr_name (f->vec_no)); | ||
| 92 | intr_dump_frame (f); | ||
| 93 | thread_exit (); | ||
| 94 | |||
| 95 | case SEL_KCSEG: | ||
| 96 | /* Kernel's code segment, which indicates a kernel bug. | ||
| 97 | Kernel code shouldn't throw exceptions. (Page faults | ||
| 98 | may cause kernel exceptions--but they shouldn't arrive | ||
| 99 | here.) Panic the kernel to make the point. */ | ||
| 100 | intr_dump_frame (f); | ||
| 101 | PANIC ("Kernel bug - unexpected interrupt in kernel"); | ||
| 102 | |||
| 103 | default: | ||
| 104 | /* Some other code segment? Shouldn't happen. Panic the | ||
| 105 | kernel. */ | ||
| 106 | printf ("Interrupt %#04x (%s) in unknown segment %04x\n", | ||
| 107 | f->vec_no, intr_name (f->vec_no), f->cs); | ||
| 108 | thread_exit (); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | /* Page fault handler. This is a skeleton that must be filled in | ||
| 113 | to implement virtual memory. Some solutions to project 2 may | ||
| 114 | also require modifying this code. | ||
| 115 | |||
| 116 | At entry, the address that faulted is in CR2 (Control Register | ||
| 117 | 2) and information about the fault, formatted as described in | ||
| 118 | the PF_* macros in exception.h, is in F's error_code member. The | ||
| 119 | example code here shows how to parse that information. You | ||
| 120 | can find more information about both of these in the | ||
| 121 | description of "Interrupt 14--Page Fault Exception (#PF)" in | ||
| 122 | [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */ | ||
| 123 | static void | ||
| 124 | page_fault (struct intr_frame *f) | ||
| 125 | { | ||
| 126 | bool not_present; /* True: not-present page, false: writing r/o page. */ | ||
| 127 | bool write; /* True: access was write, false: access was read. */ | ||
| 128 | bool user; /* True: access by user, false: access by kernel. */ | ||
| 129 | void *fault_addr; /* Fault address. */ | ||
| 130 | |||
| 131 | /* Obtain faulting address, the virtual address that was | ||
| 132 | accessed to cause the fault. It may point to code or to | ||
| 133 | data. It is not necessarily the address of the instruction | ||
| 134 | that caused the fault (that's f->eip). | ||
| 135 | See [IA32-v2a] "MOV--Move to/from Control Registers" and | ||
| 136 | [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception | ||
| 137 | (#PF)". */ | ||
| 138 | asm ("movl %%cr2, %0" : "=r" (fault_addr)); | ||
| 139 | |||
| 140 | /* Turn interrupts back on (they were only off so that we could | ||
| 141 | be assured of reading CR2 before it changed). */ | ||
| 142 | intr_enable (); | ||
| 143 | |||
| 144 | /* Count page faults. */ | ||
| 145 | page_fault_cnt++; | ||
| 146 | |||
| 147 | /* Determine cause. */ | ||
| 148 | not_present = (f->error_code & PF_P) == 0; | ||
| 149 | write = (f->error_code & PF_W) != 0; | ||
| 150 | user = (f->error_code & PF_U) != 0; | ||
| 151 | |||
| 152 | /* To implement virtual memory, adapt the rest of the function | ||
| 153 | body, adding code that brings in the page to | ||
| 154 | which fault_addr refers. */ | ||
| 155 | if (is_user_vaddr(fault_addr)) { | ||
| 156 | if (! user) { | ||
| 157 | /* syscall exception; set eax and eip */ | ||
| 158 | f->eip = (void*)f->eax; | ||
| 159 | f->eax = 0xFFFFFFFF; | ||
| 160 | return; | ||
| 161 | } else { | ||
| 162 | /* user process access violation */ | ||
| 163 | thread_exit(); | ||
| 164 | } | ||
| 165 | } else { | ||
| 166 | printf ("Page fault at %p: %s error %s page in %s context.\n", | ||
| 167 | fault_addr, | ||
| 168 | not_present ? "not present" : "rights violation", | ||
| 169 | write ? "writing" : "reading", | ||
| 170 | user ? "user" : "kernel"); | ||
| 171 | kill (f); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
diff --git a/pintos-progos/userprog/exception.h b/pintos-progos/userprog/exception.h new file mode 100644 index 0000000..f83e615 --- /dev/null +++ b/pintos-progos/userprog/exception.h | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | #ifndef USERPROG_EXCEPTION_H | ||
| 2 | #define USERPROG_EXCEPTION_H | ||
| 3 | |||
| 4 | /* Page fault error code bits that describe the cause of the exception. */ | ||
| 5 | #define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */ | ||
| 6 | #define PF_W 0x2 /* 0: read, 1: write. */ | ||
| 7 | #define PF_U 0x4 /* 0: kernel, 1: user process. */ | ||
| 8 | |||
| 9 | void exception_init (void); | ||
| 10 | void exception_print_stats (void); | ||
| 11 | |||
| 12 | #endif /* userprog/exception.h */ | ||
diff --git a/pintos-progos/userprog/gdt.c b/pintos-progos/userprog/gdt.c new file mode 100644 index 0000000..e866037 --- /dev/null +++ b/pintos-progos/userprog/gdt.c | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | #include "userprog/gdt.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include "userprog/tss.h" | ||
| 4 | #include "threads/palloc.h" | ||
| 5 | #include "threads/vaddr.h" | ||
| 6 | |||
| 7 | /* The Global Descriptor Table (GDT). | ||
| 8 | |||
| 9 | The GDT, an x86-specific structure, defines segments that can | ||
| 10 | potentially be used by all processes in a system, subject to | ||
| 11 | their permissions. There is also a per-process Local | ||
| 12 | Descriptor Table (LDT) but that is not used by modern | ||
| 13 | operating systems. | ||
| 14 | |||
| 15 | Each entry in the GDT, which is known by its byte offset in | ||
| 16 | the table, identifies a segment. For our purposes only three | ||
| 17 | types of segments are of interest: code, data, and TSS or | ||
| 18 | Task-State Segment descriptors. The former two types are | ||
| 19 | exactly what they sound like. The TSS is used primarily for | ||
| 20 | stack switching on interrupts. | ||
| 21 | |||
| 22 | For more information on the GDT as used here, refer to | ||
| 23 | [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor | ||
| 24 | Types". */ | ||
| 25 | static uint64_t gdt[SEL_CNT]; | ||
| 26 | |||
| 27 | /* GDT helpers. */ | ||
| 28 | static uint64_t make_code_desc (int dpl); | ||
| 29 | static uint64_t make_data_desc (int dpl); | ||
| 30 | static uint64_t make_tss_desc (void *laddr); | ||
| 31 | static uint64_t make_gdtr_operand (uint16_t limit, void *base); | ||
| 32 | |||
| 33 | /* Sets up a proper GDT. The bootstrap loader's GDT didn't | ||
| 34 | include user-mode selectors or a TSS, but we need both now. */ | ||
| 35 | void | ||
| 36 | gdt_init (void) | ||
| 37 | { | ||
| 38 | uint64_t gdtr_operand; | ||
| 39 | |||
| 40 | /* Initialize GDT. */ | ||
| 41 | gdt[SEL_NULL / sizeof *gdt] = 0; | ||
| 42 | gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0); | ||
| 43 | gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0); | ||
| 44 | gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3); | ||
| 45 | gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3); | ||
| 46 | gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ()); | ||
| 47 | |||
| 48 | /* Load GDTR, TR. See [IA32-v3a] 2.4.1 "Global Descriptor | ||
| 49 | Table Register (GDTR)", 2.4.4 "Task Register (TR)", and | ||
| 50 | 6.2.4 "Task Register". */ | ||
| 51 | gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt); | ||
| 52 | asm volatile ("lgdt %0" : : "m" (gdtr_operand)); | ||
| 53 | asm volatile ("ltr %w0" : : "q" (SEL_TSS)); | ||
| 54 | } | ||
| 55 | |||
| 56 | /* System segment or code/data segment? */ | ||
| 57 | enum seg_class | ||
| 58 | { | ||
| 59 | CLS_SYSTEM = 0, /* System segment. */ | ||
| 60 | CLS_CODE_DATA = 1 /* Code or data segment. */ | ||
| 61 | }; | ||
| 62 | |||
| 63 | /* Limit has byte or 4 kB page granularity? */ | ||
| 64 | enum seg_granularity | ||
| 65 | { | ||
| 66 | GRAN_BYTE = 0, /* Limit has 1-byte granularity. */ | ||
| 67 | GRAN_PAGE = 1 /* Limit has 4 kB granularity. */ | ||
| 68 | }; | ||
| 69 | |||
| 70 | /* Returns a segment descriptor with the given 32-bit BASE and | ||
| 71 | 20-bit LIMIT (whose interpretation depends on GRANULARITY). | ||
| 72 | The descriptor represents a system or code/data segment | ||
| 73 | according to CLASS, and TYPE is its type (whose interpretation | ||
| 74 | depends on the class). | ||
| 75 | |||
| 76 | The segment has descriptor privilege level DPL, meaning that | ||
| 77 | it can be used in rings numbered DPL or lower. In practice, | ||
| 78 | DPL==3 means that user processes can use the segment and | ||
| 79 | DPL==0 means that only the kernel can use the segment. See | ||
| 80 | [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */ | ||
| 81 | static uint64_t | ||
| 82 | make_seg_desc (uint32_t base, | ||
| 83 | uint32_t limit, | ||
| 84 | enum seg_class class, | ||
| 85 | int type, | ||
| 86 | int dpl, | ||
| 87 | enum seg_granularity granularity) | ||
| 88 | { | ||
| 89 | uint32_t e0, e1; | ||
| 90 | |||
| 91 | ASSERT (limit <= 0xfffff); | ||
| 92 | ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA); | ||
| 93 | ASSERT (type >= 0 && type <= 15); | ||
| 94 | ASSERT (dpl >= 0 && dpl <= 3); | ||
| 95 | ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE); | ||
| 96 | |||
| 97 | e0 = ((limit & 0xffff) /* Limit 15:0. */ | ||
| 98 | | (base << 16)); /* Base 15:0. */ | ||
| 99 | |||
| 100 | e1 = (((base >> 16) & 0xff) /* Base 23:16. */ | ||
| 101 | | (type << 8) /* Segment type. */ | ||
| 102 | | (class << 12) /* 0=system, 1=code/data. */ | ||
| 103 | | (dpl << 13) /* Descriptor privilege. */ | ||
| 104 | | (1 << 15) /* Present. */ | ||
| 105 | | (limit & 0xf0000) /* Limit 16:19. */ | ||
| 106 | | (1 << 22) /* 32-bit segment. */ | ||
| 107 | | (granularity << 23) /* Byte/page granularity. */ | ||
| 108 | | (base & 0xff000000)); /* Base 31:24. */ | ||
| 109 | |||
| 110 | return e0 | ((uint64_t) e1 << 32); | ||
| 111 | } | ||
| 112 | |||
| 113 | /* Returns a descriptor for a readable code segment with base at | ||
| 114 | 0, a limit of 4 GB, and the given DPL. */ | ||
| 115 | static uint64_t | ||
| 116 | make_code_desc (int dpl) | ||
| 117 | { | ||
| 118 | return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE); | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Returns a descriptor for a writable data segment with base at | ||
| 122 | 0, a limit of 4 GB, and the given DPL. */ | ||
| 123 | static uint64_t | ||
| 124 | make_data_desc (int dpl) | ||
| 125 | { | ||
| 126 | return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE); | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Returns a descriptor for an "available" 32-bit Task-State | ||
| 130 | Segment with its base at the given linear address, a limit of | ||
| 131 | 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. | ||
| 132 | See [IA32-v3a] 6.2.2 "TSS Descriptor". */ | ||
| 133 | static uint64_t | ||
| 134 | make_tss_desc (void *laddr) | ||
| 135 | { | ||
| 136 | return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE); | ||
| 137 | } | ||
| 138 | |||
| 139 | |||
| 140 | /* Returns a descriptor that yields the given LIMIT and BASE when | ||
| 141 | used as an operand for the LGDT instruction. */ | ||
| 142 | static uint64_t | ||
| 143 | make_gdtr_operand (uint16_t limit, void *base) | ||
| 144 | { | ||
| 145 | return limit | ((uint64_t) (uint32_t) base << 16); | ||
| 146 | } | ||
diff --git a/pintos-progos/userprog/gdt.h b/pintos-progos/userprog/gdt.h new file mode 100644 index 0000000..81fe50c --- /dev/null +++ b/pintos-progos/userprog/gdt.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #ifndef USERPROG_GDT_H | ||
| 2 | #define USERPROG_GDT_H | ||
| 3 | |||
| 4 | #include "threads/loader.h" | ||
| 5 | |||
| 6 | /* Segment selectors. | ||
| 7 | More selectors are defined by the loader in loader.h. */ | ||
| 8 | #define SEL_UCSEG 0x1B /* User code selector. */ | ||
| 9 | #define SEL_UDSEG 0x23 /* User data selector. */ | ||
| 10 | #define SEL_TSS 0x28 /* Task-state segment. */ | ||
| 11 | #define SEL_CNT 6 /* Number of segments. */ | ||
| 12 | |||
| 13 | void gdt_init (void); | ||
| 14 | |||
| 15 | #endif /* userprog/gdt.h */ | ||
diff --git a/pintos-progos/userprog/pagedir.c b/pintos-progos/userprog/pagedir.c new file mode 100644 index 0000000..a6a87b8 --- /dev/null +++ b/pintos-progos/userprog/pagedir.c | |||
| @@ -0,0 +1,263 @@ | |||
| 1 | #include "userprog/pagedir.h" | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <stddef.h> | ||
| 4 | #include <string.h> | ||
| 5 | #include "threads/init.h" | ||
| 6 | #include "threads/pte.h" | ||
| 7 | #include "threads/palloc.h" | ||
| 8 | |||
| 9 | static uint32_t *active_pd (void); | ||
| 10 | static void invalidate_pagedir (uint32_t *); | ||
| 11 | |||
| 12 | /* Creates a new page directory that has mappings for kernel | ||
| 13 | virtual addresses, but none for user virtual addresses. | ||
| 14 | Returns the new page directory, or a null pointer if memory | ||
| 15 | allocation fails. */ | ||
| 16 | uint32_t * | ||
| 17 | pagedir_create (void) | ||
| 18 | { | ||
| 19 | uint32_t *pd = palloc_get_page (0); | ||
| 20 | if (pd != NULL) | ||
| 21 | memcpy (pd, init_page_dir, PGSIZE); | ||
| 22 | return pd; | ||
| 23 | } | ||
| 24 | |||
| 25 | /* Destroys page directory PD, freeing all the pages it | ||
| 26 | references. */ | ||
| 27 | void | ||
| 28 | pagedir_destroy (uint32_t *pd) | ||
| 29 | { | ||
| 30 | uint32_t *pde; | ||
| 31 | |||
| 32 | if (pd == NULL) | ||
| 33 | return; | ||
| 34 | |||
| 35 | ASSERT (pd != init_page_dir); | ||
| 36 | for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++) | ||
| 37 | if (*pde & PTE_P) | ||
| 38 | { | ||
| 39 | uint32_t *pt = pde_get_pt (*pde); | ||
| 40 | uint32_t *pte; | ||
| 41 | |||
| 42 | for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++) | ||
| 43 | if (*pte & PTE_P) | ||
| 44 | palloc_free_page (pte_get_page (*pte)); | ||
| 45 | palloc_free_page (pt); | ||
| 46 | } | ||
| 47 | palloc_free_page (pd); | ||
| 48 | } | ||
| 49 | |||
| 50 | /* Returns the address of the page table entry for virtual | ||
| 51 | address VADDR in page directory PD. | ||
| 52 | If PD does not have a page table for VADDR, behavior depends | ||
| 53 | on CREATE. If CREATE is true, then a new page table is | ||
| 54 | created and a pointer into it is returned. Otherwise, a null | ||
| 55 | pointer is returned. */ | ||
| 56 | static uint32_t * | ||
| 57 | lookup_page (uint32_t *pd, const void *vaddr, bool create) | ||
| 58 | { | ||
| 59 | uint32_t *pt, *pde; | ||
| 60 | |||
| 61 | ASSERT (pd != NULL); | ||
| 62 | |||
| 63 | /* Shouldn't create new kernel virtual mappings. */ | ||
| 64 | ASSERT (!create || is_user_vaddr (vaddr)); | ||
| 65 | |||
| 66 | /* Check for a page table for VADDR. | ||
| 67 | If one is missing, create one if requested. */ | ||
| 68 | pde = pd + pd_no (vaddr); | ||
| 69 | if (*pde == 0) | ||
| 70 | { | ||
| 71 | if (create) | ||
| 72 | { | ||
| 73 | pt = palloc_get_page (PAL_ZERO); | ||
| 74 | if (pt == NULL) | ||
| 75 | return NULL; | ||
| 76 | |||
| 77 | *pde = pde_create (pt); | ||
| 78 | } | ||
| 79 | else | ||
| 80 | return NULL; | ||
| 81 | } | ||
| 82 | |||
| 83 | /* Return the page table entry. */ | ||
| 84 | pt = pde_get_pt (*pde); | ||
| 85 | return &pt[pt_no (vaddr)]; | ||
| 86 | } | ||
| 87 | |||
| 88 | /* Adds a mapping in page directory PD from user virtual page | ||
| 89 | UPAGE to the physical frame identified by kernel virtual | ||
| 90 | address KPAGE. | ||
| 91 | UPAGE must not already be mapped. | ||
| 92 | KPAGE should probably be a page obtained from the user pool | ||
| 93 | with palloc_get_page(). | ||
| 94 | If WRITABLE is true, the new page is read/write; | ||
| 95 | otherwise it is read-only. | ||
| 96 | Returns true if successful, false if memory allocation | ||
| 97 | failed. */ | ||
| 98 | bool | ||
| 99 | pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable) | ||
| 100 | { | ||
| 101 | uint32_t *pte; | ||
| 102 | |||
| 103 | ASSERT (pg_ofs (upage) == 0); | ||
| 104 | ASSERT (pg_ofs (kpage) == 0); | ||
| 105 | ASSERT (is_user_vaddr (upage)); | ||
| 106 | ASSERT (vtop (kpage) >> PTSHIFT < init_ram_pages); | ||
| 107 | ASSERT (pd != init_page_dir); | ||
| 108 | |||
| 109 | pte = lookup_page (pd, upage, true); | ||
| 110 | |||
| 111 | if (pte != NULL) | ||
| 112 | { | ||
| 113 | ASSERT ((*pte & PTE_P) == 0); | ||
| 114 | *pte = pte_create_user (kpage, writable); | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | else | ||
| 118 | return false; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Looks up the physical address that corresponds to user virtual | ||
| 122 | address UADDR in PD. Returns the kernel virtual address | ||
| 123 | corresponding to that physical address, or a null pointer if | ||
| 124 | UADDR is unmapped. */ | ||
| 125 | void * | ||
| 126 | pagedir_get_page (uint32_t *pd, const void *uaddr) | ||
| 127 | { | ||
| 128 | uint32_t *pte; | ||
| 129 | |||
| 130 | ASSERT (is_user_vaddr (uaddr)); | ||
| 131 | |||
| 132 | pte = lookup_page (pd, uaddr, false); | ||
| 133 | if (pte != NULL && (*pte & PTE_P) != 0) | ||
| 134 | return pte_get_page (*pte) + pg_ofs (uaddr); | ||
| 135 | else | ||
| 136 | return NULL; | ||
| 137 | } | ||
| 138 | |||
| 139 | /* Marks user virtual page UPAGE "not present" in page | ||
| 140 | directory PD. Later accesses to the page will fault. Other | ||
| 141 | bits in the page table entry are preserved. | ||
| 142 | UPAGE need not be mapped. */ | ||
| 143 | void | ||
| 144 | pagedir_clear_page (uint32_t *pd, void *upage) | ||
| 145 | { | ||
| 146 | uint32_t *pte; | ||
| 147 | |||
| 148 | ASSERT (pg_ofs (upage) == 0); | ||
| 149 | ASSERT (is_user_vaddr (upage)); | ||
| 150 | |||
| 151 | pte = lookup_page (pd, upage, false); | ||
| 152 | if (pte != NULL && (*pte & PTE_P) != 0) | ||
| 153 | { | ||
| 154 | *pte &= ~PTE_P; | ||
| 155 | invalidate_pagedir (pd); | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | /* Returns true if the PTE for virtual page VPAGE in PD is dirty, | ||
| 160 | that is, if the page has been modified since the PTE was | ||
| 161 | installed. | ||
| 162 | Returns false if PD contains no PTE for VPAGE. */ | ||
| 163 | bool | ||
| 164 | pagedir_is_dirty (uint32_t *pd, const void *vpage) | ||
| 165 | { | ||
| 166 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 167 | return pte != NULL && (*pte & PTE_D) != 0; | ||
| 168 | } | ||
| 169 | |||
| 170 | /* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE | ||
| 171 | in PD. */ | ||
| 172 | void | ||
| 173 | pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) | ||
| 174 | { | ||
| 175 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 176 | if (pte != NULL) | ||
| 177 | { | ||
| 178 | if (dirty) | ||
| 179 | *pte |= PTE_D; | ||
| 180 | else | ||
| 181 | { | ||
| 182 | *pte &= ~(uint32_t) PTE_D; | ||
| 183 | invalidate_pagedir (pd); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | /* Returns true if the PTE for virtual page VPAGE in PD has been | ||
| 189 | accessed recently, that is, between the time the PTE was | ||
| 190 | installed and the last time it was cleared. Returns false if | ||
| 191 | PD contains no PTE for VPAGE. */ | ||
| 192 | bool | ||
| 193 | pagedir_is_accessed (uint32_t *pd, const void *vpage) | ||
| 194 | { | ||
| 195 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 196 | return pte != NULL && (*pte & PTE_A) != 0; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* Sets the accessed bit to ACCESSED in the PTE for virtual page | ||
| 200 | VPAGE in PD. */ | ||
| 201 | void | ||
| 202 | pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) | ||
| 203 | { | ||
| 204 | uint32_t *pte = lookup_page (pd, vpage, false); | ||
| 205 | if (pte != NULL) | ||
| 206 | { | ||
| 207 | if (accessed) | ||
| 208 | *pte |= PTE_A; | ||
| 209 | else | ||
| 210 | { | ||
| 211 | *pte &= ~(uint32_t) PTE_A; | ||
| 212 | invalidate_pagedir (pd); | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | /* Loads page directory PD into the CPU's page directory base | ||
| 218 | register. */ | ||
| 219 | void | ||
| 220 | pagedir_activate (uint32_t *pd) | ||
| 221 | { | ||
| 222 | if (pd == NULL) | ||
| 223 | pd = init_page_dir; | ||
| 224 | |||
| 225 | /* Store the physical address of the page directory into CR3 | ||
| 226 | aka PDBR (page directory base register). This activates our | ||
| 227 | new page tables immediately. See [IA32-v2a] "MOV--Move | ||
| 228 | to/from Control Registers" and [IA32-v3a] 3.7.5 "Base | ||
| 229 | Address of the Page Directory". */ | ||
| 230 | asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory"); | ||
| 231 | } | ||
| 232 | |||
| 233 | /* Returns the currently active page directory. */ | ||
| 234 | static uint32_t * | ||
| 235 | active_pd (void) | ||
| 236 | { | ||
| 237 | /* Copy CR3, the page directory base register (PDBR), into | ||
| 238 | `pd'. | ||
| 239 | See [IA32-v2a] "MOV--Move to/from Control Registers" and | ||
| 240 | [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */ | ||
| 241 | uintptr_t pd; | ||
| 242 | asm volatile ("movl %%cr3, %0" : "=r" (pd)); | ||
| 243 | return ptov (pd); | ||
| 244 | } | ||
| 245 | |||
| 246 | /* Seom page table changes can cause the CPU's translation | ||
| 247 | lookaside buffer (TLB) to become out-of-sync with the page | ||
| 248 | table. When this happens, we have to "invalidate" the TLB by | ||
| 249 | re-activating it. | ||
| 250 | |||
| 251 | This function invalidates the TLB if PD is the active page | ||
| 252 | directory. (If PD is not active then its entries are not in | ||
| 253 | the TLB, so there is no need to invalidate anything.) */ | ||
| 254 | static void | ||
| 255 | invalidate_pagedir (uint32_t *pd) | ||
| 256 | { | ||
| 257 | if (active_pd () == pd) | ||
| 258 | { | ||
| 259 | /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12 | ||
| 260 | "Translation Lookaside Buffers (TLBs)". */ | ||
| 261 | pagedir_activate (pd); | ||
| 262 | } | ||
| 263 | } | ||
diff --git a/pintos-progos/userprog/pagedir.h b/pintos-progos/userprog/pagedir.h new file mode 100644 index 0000000..cd92447 --- /dev/null +++ b/pintos-progos/userprog/pagedir.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #ifndef USERPROG_PAGEDIR_H | ||
| 2 | #define USERPROG_PAGEDIR_H | ||
| 3 | |||
| 4 | #include <stdbool.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | |||
| 7 | uint32_t *pagedir_create (void); | ||
| 8 | void pagedir_destroy (uint32_t *pd); | ||
| 9 | bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw); | ||
| 10 | void *pagedir_get_page (uint32_t *pd, const void *upage); | ||
| 11 | void pagedir_clear_page (uint32_t *pd, void *upage); | ||
| 12 | bool pagedir_is_dirty (uint32_t *pd, const void *upage); | ||
| 13 | void pagedir_set_dirty (uint32_t *pd, const void *upage, bool dirty); | ||
| 14 | bool pagedir_is_accessed (uint32_t *pd, const void *upage); | ||
| 15 | void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed); | ||
| 16 | void pagedir_activate (uint32_t *pd); | ||
| 17 | |||
| 18 | #endif /* userprog/pagedir.h */ | ||
diff --git a/pintos-progos/userprog/process.c b/pintos-progos/userprog/process.c new file mode 100644 index 0000000..adb6b66 --- /dev/null +++ b/pintos-progos/userprog/process.c | |||
| @@ -0,0 +1,721 @@ | |||
| 1 | #include "userprog/process.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <inttypes.h> | ||
| 4 | #include <round.h> | ||
| 5 | #include <stdio.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <string.h> | ||
| 8 | #include "userprog/gdt.h" | ||
| 9 | #include "userprog/pagedir.h" | ||
| 10 | #include "userprog/tss.h" | ||
| 11 | #include "filesys/directory.h" | ||
| 12 | #include "filesys/file.h" | ||
| 13 | #include "filesys/filesys.h" | ||
| 14 | #include "threads/flags.h" | ||
| 15 | #include "threads/init.h" | ||
| 16 | #include "threads/interrupt.h" | ||
| 17 | #include "threads/palloc.h" | ||
| 18 | #include "threads/synch.h" | ||
| 19 | #include "threads/thread.h" | ||
| 20 | #include "threads/vaddr.h" | ||
| 21 | |||
| 22 | /* data structure to communicate with the thread initializing a new process */ | ||
| 23 | struct start_aux_data { | ||
| 24 | char *filename; | ||
| 25 | struct semaphore startup_sem; | ||
| 26 | struct thread *parent_thread; | ||
| 27 | struct process *new_process; | ||
| 28 | }; | ||
| 29 | |||
| 30 | /* filesystem lock */ | ||
| 31 | struct lock filesys_lock; | ||
| 32 | |||
| 33 | /* prototypes */ | ||
| 34 | static thread_func start_process NO_RETURN; | ||
| 35 | static bool load (char *filename, void (**eip) (void), void **esp); | ||
| 36 | static bool setup_stack (void **esp); | ||
| 37 | static bool init_fd_table (struct fd_table * table); | ||
| 38 | |||
| 39 | /* Initialize the filesystem lock */ | ||
| 40 | void | ||
| 41 | process_init () | ||
| 42 | { | ||
| 43 | lock_init (&filesys_lock); | ||
| 44 | } | ||
| 45 | |||
| 46 | /* Get current process (only valid for processes) */ | ||
| 47 | struct process* | ||
| 48 | process_current () | ||
| 49 | { | ||
| 50 | ASSERT (thread_current()->process != NULL); | ||
| 51 | return thread_current()->process; | ||
| 52 | } | ||
| 53 | |||
| 54 | /* Starts a new thread running a user program loaded from | ||
| 55 | `filename`. | ||
| 56 | The new thread may be scheduled (and may even exit) | ||
| 57 | before process_execute() returns. Returns the new process's | ||
| 58 | thread id, or TID_ERROR if the thread cannot be created. | ||
| 59 | |||
| 60 | In the first assignment, you should change this to function to | ||
| 61 | |||
| 62 | process_execute (const char *cmd) | ||
| 63 | |||
| 64 | and support command strings such as "echo A B C". You | ||
| 65 | will also need to change `load` and `setup_stack`. */ | ||
| 66 | tid_t | ||
| 67 | process_execute (const char *filename) | ||
| 68 | { | ||
| 69 | tid_t tid = TID_ERROR; | ||
| 70 | char *fn_copy = NULL; | ||
| 71 | struct start_aux_data *aux_data = NULL; | ||
| 72 | |||
| 73 | /* Setup the auxiliary data for starting up the new process */ | ||
| 74 | fn_copy = palloc_get_page (0); | ||
| 75 | aux_data = palloc_get_page (0); | ||
| 76 | if (aux_data == NULL || fn_copy == NULL) | ||
| 77 | goto done; | ||
| 78 | strlcpy (fn_copy, filename, PGSIZE); | ||
| 79 | aux_data->filename = fn_copy; | ||
| 80 | aux_data->parent_thread = thread_current (); | ||
| 81 | aux_data->new_process = NULL; | ||
| 82 | sema_init (&aux_data->startup_sem, 0); | ||
| 83 | |||
| 84 | /* Create a new thread to execute FILE_NAME. */ | ||
| 85 | tid = thread_create (fn_copy, PRI_DEFAULT, start_process, aux_data); | ||
| 86 | if (tid == TID_ERROR) | ||
| 87 | goto done; | ||
| 88 | |||
| 89 | /* wait for startup */ | ||
| 90 | sema_down (&aux_data->startup_sem); | ||
| 91 | if (aux_data->new_process == NULL) { | ||
| 92 | tid = TID_ERROR; | ||
| 93 | goto done; | ||
| 94 | } | ||
| 95 | /* register child process */ | ||
| 96 | list_push_back (&thread_current()->children, | ||
| 97 | &aux_data->new_process->parentelem); | ||
| 98 | |||
| 99 | done: | ||
| 100 | palloc_free_page (fn_copy); | ||
| 101 | palloc_free_page (aux_data); | ||
| 102 | return tid; | ||
| 103 | } | ||
| 104 | |||
| 105 | /* A thread function that loads a user process and starts it | ||
| 106 | running. */ | ||
| 107 | static void | ||
| 108 | start_process (void *aux) | ||
| 109 | { | ||
| 110 | struct intr_frame if_; | ||
| 111 | struct start_aux_data *aux_data = (struct start_aux_data*) aux; | ||
| 112 | struct thread *thread = thread_current (); | ||
| 113 | |||
| 114 | /* Initialize Process */ | ||
| 115 | struct process *process = palloc_get_page (PAL_ZERO); | ||
| 116 | if (process == NULL) | ||
| 117 | goto signal; | ||
| 118 | sema_init (&process->exit_sem, 0); | ||
| 119 | lock_init (&process->exit_lock); | ||
| 120 | process->exit_status = -1; | ||
| 121 | if (! init_fd_table (&process->fd_table)) | ||
| 122 | goto signal; | ||
| 123 | |||
| 124 | /* register process */ | ||
| 125 | process->thread_id = thread->tid; | ||
| 126 | process->parent_tid = aux_data->parent_thread->tid; | ||
| 127 | thread->process = process; | ||
| 128 | |||
| 129 | /* Initialize interrupt frame and load executable. */ | ||
| 130 | memset (&if_, 0, sizeof if_); | ||
| 131 | if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; | ||
| 132 | if_.cs = SEL_UCSEG; | ||
| 133 | if_.eflags = FLAG_IF | FLAG_MBS; | ||
| 134 | if (! load (aux_data->filename, &if_.eip, &if_.esp)) { | ||
| 135 | thread->process = NULL; | ||
| 136 | } else { | ||
| 137 | aux_data->new_process = thread->process; | ||
| 138 | } | ||
| 139 | |||
| 140 | signal: | ||
| 141 | /* Signal the parent process that loading is finished */ | ||
| 142 | sema_up (&aux_data->startup_sem); /* transfer ownership of aux_data */ | ||
| 143 | |||
| 144 | /* If process startup failed, quit. */ | ||
| 145 | if (thread->process == NULL) { | ||
| 146 | if (process != NULL) { | ||
| 147 | if (process->fd_table.fds != NULL) | ||
| 148 | palloc_free_page (process->fd_table.fds); | ||
| 149 | palloc_free_page (process); | ||
| 150 | } | ||
| 151 | thread_exit (); | ||
| 152 | } | ||
| 153 | |||
| 154 | /* Start the user process by simulating a return from an | ||
| 155 | interrupt, implemented by intr_exit (in | ||
| 156 | threads/intr-stubs.S). Because intr_exit takes all of its | ||
| 157 | arguments on the stack in the form of a `struct intr_frame', | ||
| 158 | we just point the stack pointer (%esp) to our stack frame | ||
| 159 | and jump to it. */ | ||
| 160 | asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory"); | ||
| 161 | NOT_REACHED (); | ||
| 162 | } | ||
| 163 | |||
| 164 | /* Waits for thread TID to die and returns its exit status. If | ||
| 165 | it was terminated by the kernel (i.e. killed due to an | ||
| 166 | exception), returns -1. If TID is invalid or if it was not a | ||
| 167 | child of the calling process, or if process_wait() has already | ||
| 168 | been successfully called for the given TID, returns -1 | ||
| 169 | immediately, without waiting. */ | ||
| 170 | int | ||
| 171 | process_wait (tid_t child_tid) | ||
| 172 | { | ||
| 173 | struct thread *cur = thread_current (); | ||
| 174 | struct process *child = NULL; | ||
| 175 | |||
| 176 | /* iterate over child processes */ | ||
| 177 | struct list_elem *e = list_head (&cur->children); | ||
| 178 | while ((e = list_next (e)) != list_end (&cur->children)) { | ||
| 179 | struct process* t = list_entry (e, struct process, parentelem); | ||
| 180 | if (t->thread_id == child_tid) { | ||
| 181 | list_remove (e); | ||
| 182 | child = t; | ||
| 183 | break; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | if (child == NULL) { | ||
| 187 | return -1; | ||
| 188 | } | ||
| 189 | sema_down (&child->exit_sem); | ||
| 190 | int exit_status = child->exit_status; | ||
| 191 | palloc_free_page (child); | ||
| 192 | return exit_status; | ||
| 193 | } | ||
| 194 | |||
| 195 | /* Free the current process's resources. */ | ||
| 196 | void | ||
| 197 | process_exit (void) | ||
| 198 | { | ||
| 199 | struct thread *thread = thread_current (); | ||
| 200 | ASSERT (thread != NULL); | ||
| 201 | |||
| 202 | /* remove (and if necessary clean up) child processes */ | ||
| 203 | struct list_elem *e = list_head (&thread->children); | ||
| 204 | while ((e = list_next (e)) != list_end (&thread->children)) { | ||
| 205 | struct process *p = list_entry (e, struct process, parentelem); | ||
| 206 | bool process_dying; | ||
| 207 | lock_acquire (&p->exit_lock); | ||
| 208 | process_dying = p->parent_tid < 0; | ||
| 209 | p->parent_tid = -1; | ||
| 210 | lock_release (&p->exit_lock); | ||
| 211 | if (process_dying) | ||
| 212 | palloc_free_page (p); | ||
| 213 | } | ||
| 214 | |||
| 215 | if (thread->process == NULL) | ||
| 216 | return; /* not a process, nothing else left to do */ | ||
| 217 | |||
| 218 | struct process *proc = thread->process; | ||
| 219 | uint32_t *pd; | ||
| 220 | |||
| 221 | printf ("%s: exit(%d)\n", thread->name, proc->exit_status); | ||
| 222 | |||
| 223 | /* close executable, allow write */ | ||
| 224 | if (proc->executable != NULL) { | ||
| 225 | lock_acquire (&filesys_lock); | ||
| 226 | file_close (proc->executable); | ||
| 227 | lock_release (&filesys_lock); | ||
| 228 | } | ||
| 229 | |||
| 230 | int fd; | ||
| 231 | for (fd = 2; fd <= proc->fd_table.fd_max; fd++) { | ||
| 232 | process_close_file (fd); | ||
| 233 | } | ||
| 234 | palloc_free_page (proc->fd_table.fds); | ||
| 235 | |||
| 236 | /* Destroy the current process's page directory and switch back | ||
| 237 | to the kernel-only page directory. */ | ||
| 238 | pd = thread->pagedir; | ||
| 239 | if (pd != NULL) { | ||
| 240 | /* Correct ordering here is crucial. We must set | ||
| 241 | cur->pagedir to NULL before switching page directories, | ||
| 242 | so that a timer interrupt can't switch back to the | ||
| 243 | process page directory. We must activate the base page | ||
| 244 | directory before destroying the process's page | ||
| 245 | directory, or our active page directory will be one | ||
| 246 | that's been freed (and cleared). */ | ||
| 247 | thread->pagedir = NULL; | ||
| 248 | pagedir_activate (NULL); | ||
| 249 | pagedir_destroy (pd); | ||
| 250 | } | ||
| 251 | |||
| 252 | /* Destroy the process structure if the parent is not alive | ||
| 253 | * any more. Atomic test and set would be sufficient here. | ||
| 254 | */ | ||
| 255 | bool parent_dead = false; | ||
| 256 | lock_acquire (&proc->exit_lock); | ||
| 257 | parent_dead = proc->parent_tid < 0; | ||
| 258 | proc->parent_tid = -1; | ||
| 259 | lock_release (&proc->exit_lock); | ||
| 260 | if (parent_dead) { | ||
| 261 | palloc_free_page (proc); | ||
| 262 | } else { | ||
| 263 | sema_up (&proc->exit_sem); | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | /* Sets up the CPU for running user code in the current | ||
| 268 | thread. | ||
| 269 | This function is called on every context switch. */ | ||
| 270 | void | ||
| 271 | process_activate (void) | ||
| 272 | { | ||
| 273 | struct thread *t = thread_current (); | ||
| 274 | /* Activate thread's page tables. */ | ||
| 275 | pagedir_activate (t->pagedir); | ||
| 276 | |||
| 277 | /* Set thread's kernel stack for use in processing | ||
| 278 | interrupts. */ | ||
| 279 | tss_update (); | ||
| 280 | } | ||
| 281 | |||
| 282 | /* We load ELF binaries. The following definitions are taken | ||
| 283 | from the ELF specification, [ELF1], more-or-less verbatim. */ | ||
| 284 | |||
| 285 | /* ELF types. See [ELF1] 1-2. */ | ||
| 286 | typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off; | ||
| 287 | typedef uint16_t Elf32_Half; | ||
| 288 | |||
| 289 | /* For use with ELF types in printf(). */ | ||
| 290 | #define PE32Wx PRIx32 /* Print Elf32_Word in hexadecimal. */ | ||
| 291 | #define PE32Ax PRIx32 /* Print Elf32_Addr in hexadecimal. */ | ||
| 292 | #define PE32Ox PRIx32 /* Print Elf32_Off in hexadecimal. */ | ||
| 293 | #define PE32Hx PRIx16 /* Print Elf32_Half in hexadecimal. */ | ||
| 294 | |||
| 295 | /* Executable header. See [ELF1] 1-4 to 1-8. | ||
| 296 | This appears at the very beginning of an ELF binary. */ | ||
| 297 | struct Elf32_Ehdr | ||
| 298 | { | ||
| 299 | unsigned char e_ident[16]; | ||
| 300 | Elf32_Half e_type; | ||
| 301 | Elf32_Half e_machine; | ||
| 302 | Elf32_Word e_version; | ||
| 303 | Elf32_Addr e_entry; | ||
| 304 | Elf32_Off e_phoff; | ||
| 305 | Elf32_Off e_shoff; | ||
| 306 | Elf32_Word e_flags; | ||
| 307 | Elf32_Half e_ehsize; | ||
| 308 | Elf32_Half e_phentsize; | ||
| 309 | Elf32_Half e_phnum; | ||
| 310 | Elf32_Half e_shentsize; | ||
| 311 | Elf32_Half e_shnum; | ||
| 312 | Elf32_Half e_shstrndx; | ||
| 313 | }; | ||
| 314 | |||
| 315 | /* Program header. See [ELF1] 2-2 to 2-4. | ||
| 316 | There are e_phnum of these, starting at file offset e_phoff | ||
| 317 | (see [ELF1] 1-6). */ | ||
| 318 | struct Elf32_Phdr | ||
| 319 | { | ||
| 320 | Elf32_Word p_type; | ||
| 321 | Elf32_Off p_offset; | ||
| 322 | Elf32_Addr p_vaddr; | ||
| 323 | Elf32_Addr p_paddr; | ||
| 324 | Elf32_Word p_filesz; | ||
| 325 | Elf32_Word p_memsz; | ||
| 326 | Elf32_Word p_flags; | ||
| 327 | Elf32_Word p_align; | ||
| 328 | }; | ||
| 329 | |||
| 330 | /* Values for p_type. See [ELF1] 2-3. */ | ||
| 331 | #define PT_NULL 0 /* Ignore. */ | ||
| 332 | #define PT_LOAD 1 /* Loadable segment. */ | ||
| 333 | #define PT_DYNAMIC 2 /* Dynamic linking info. */ | ||
| 334 | #define PT_INTERP 3 /* Name of dynamic loader. */ | ||
| 335 | #define PT_NOTE 4 /* Auxiliary info. */ | ||
| 336 | #define PT_SHLIB 5 /* Reserved. */ | ||
| 337 | #define PT_PHDR 6 /* Program header table. */ | ||
| 338 | #define PT_STACK 0x6474e551 /* Stack segment. */ | ||
| 339 | |||
| 340 | /* Flags for p_flags. See [ELF3] 2-3 and 2-4. */ | ||
| 341 | #define PF_X 1 /* Executable. */ | ||
| 342 | #define PF_W 2 /* Writable. */ | ||
| 343 | #define PF_R 4 /* Readable. */ | ||
| 344 | |||
| 345 | static bool validate_segment (const struct Elf32_Phdr *, struct file *); | ||
| 346 | static bool load_segment (struct file *file, off_t ofs, uint8_t *upage, | ||
| 347 | uint32_t read_bytes, uint32_t zero_bytes, | ||
| 348 | bool writable); | ||
| 349 | |||
| 350 | /* Loads an ELF executable from file_name (the first word of | ||
| 351 | cmd) into the current thread. | ||
| 352 | Stores the executable's entry point into *EIP | ||
| 353 | and its initial stack pointer into *ESP. | ||
| 354 | Returns true if successful, false otherwise. */ | ||
| 355 | bool | ||
| 356 | load (char *file_name, void (**eip) (void), void **esp) | ||
| 357 | { | ||
| 358 | struct thread *t = thread_current (); | ||
| 359 | struct Elf32_Ehdr ehdr; | ||
| 360 | struct file *file = NULL; | ||
| 361 | off_t file_ofs; | ||
| 362 | bool success = false; | ||
| 363 | int i; | ||
| 364 | |||
| 365 | /* Allocate and activate page directory. */ | ||
| 366 | t->pagedir = pagedir_create (); | ||
| 367 | if (t->pagedir == NULL) | ||
| 368 | return false; | ||
| 369 | process_activate (); | ||
| 370 | |||
| 371 | /* Coarse grained filesystem lock for loading */ | ||
| 372 | lock_acquire (&filesys_lock); | ||
| 373 | |||
| 374 | /* Open executable file. */ | ||
| 375 | file = filesys_open (file_name); | ||
| 376 | if (file == NULL) | ||
| 377 | goto done; | ||
| 378 | |||
| 379 | /* Deny writes to the file during loading */ | ||
| 380 | file_deny_write (file); | ||
| 381 | |||
| 382 | /* Read and verify executable header. */ | ||
| 383 | if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr | ||
| 384 | || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7) | ||
| 385 | || ehdr.e_type != 2 | ||
| 386 | || ehdr.e_machine != 3 | ||
| 387 | || ehdr.e_version != 1 | ||
| 388 | || ehdr.e_phentsize != sizeof (struct Elf32_Phdr) | ||
| 389 | || ehdr.e_phnum > 1024) | ||
| 390 | { | ||
| 391 | printf ("load: %s: error loading executable\n", file_name); | ||
| 392 | goto done; | ||
| 393 | } | ||
| 394 | |||
| 395 | /* Read program headers. */ | ||
| 396 | file_ofs = ehdr.e_phoff; | ||
| 397 | for (i = 0; i < ehdr.e_phnum; i++) | ||
| 398 | { | ||
| 399 | struct Elf32_Phdr phdr; | ||
| 400 | |||
| 401 | if (file_ofs < 0 || file_ofs > file_length (file)) | ||
| 402 | goto done; | ||
| 403 | file_seek (file, file_ofs); | ||
| 404 | |||
| 405 | if (file_read (file, &phdr, sizeof phdr) != sizeof phdr) | ||
| 406 | goto done; | ||
| 407 | file_ofs += sizeof phdr; | ||
| 408 | if (phdr.p_vaddr < PGSIZE) | ||
| 409 | continue; /* Ignore build-id segment */ | ||
| 410 | switch (phdr.p_type) | ||
| 411 | { | ||
| 412 | case PT_NULL: | ||
| 413 | case PT_NOTE: | ||
| 414 | case PT_PHDR: | ||
| 415 | case PT_STACK: | ||
| 416 | default: | ||
| 417 | /* Ignore this segment. */ | ||
| 418 | break; | ||
| 419 | case PT_DYNAMIC: | ||
| 420 | case PT_INTERP: | ||
| 421 | case PT_SHLIB: | ||
| 422 | goto done; | ||
| 423 | case PT_LOAD: | ||
| 424 | if (phdr.p_vaddr == 0) | ||
| 425 | break; // Ignore the .note.gnu.build-i segment | ||
| 426 | if (validate_segment (&phdr, file)) | ||
| 427 | { | ||
| 428 | bool writable = (phdr.p_flags & PF_W) != 0; | ||
| 429 | uint32_t file_page = phdr.p_offset & ~PGMASK; | ||
| 430 | uint32_t mem_page = phdr.p_vaddr & ~PGMASK; | ||
| 431 | uint32_t page_offset = phdr.p_vaddr & PGMASK; | ||
| 432 | uint32_t read_bytes, zero_bytes; | ||
| 433 | if (phdr.p_filesz > 0) | ||
| 434 | { | ||
| 435 | /* Normal segment. | ||
| 436 | Read initial part from disk and zero the rest. */ | ||
| 437 | read_bytes = page_offset + phdr.p_filesz; | ||
| 438 | zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE) | ||
| 439 | - read_bytes); | ||
| 440 | } | ||
| 441 | else | ||
| 442 | { | ||
| 443 | /* Entirely zero. | ||
| 444 | Don't read anything from disk. */ | ||
| 445 | read_bytes = 0; | ||
| 446 | zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE); | ||
| 447 | } | ||
| 448 | if (!load_segment (file, file_page, (void *) mem_page, | ||
| 449 | read_bytes, zero_bytes, writable)) | ||
| 450 | goto done; | ||
| 451 | } | ||
| 452 | else | ||
| 453 | goto done; | ||
| 454 | break; | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | /* Set up stack. */ | ||
| 459 | if (!setup_stack (esp)) | ||
| 460 | goto done; | ||
| 461 | |||
| 462 | /* Start address. */ | ||
| 463 | *eip = (void (*) (void)) ehdr.e_entry; | ||
| 464 | |||
| 465 | success = true; | ||
| 466 | |||
| 467 | done: | ||
| 468 | /* We arrive here whether the load is successful or not. */ | ||
| 469 | if (success) { | ||
| 470 | process_current()->executable = file; | ||
| 471 | } else { | ||
| 472 | file_close (file); | ||
| 473 | } | ||
| 474 | lock_release (&filesys_lock); | ||
| 475 | return success; | ||
| 476 | } | ||
| 477 | |||
| 478 | /* load() helpers. */ | ||
| 479 | |||
| 480 | static bool install_page (void *upage, void *kpage, bool writable); | ||
| 481 | |||
| 482 | /* Checks whether PHDR describes a valid, loadable segment in | ||
| 483 | FILE and returns true if so, false otherwise. */ | ||
| 484 | static bool | ||
| 485 | validate_segment (const struct Elf32_Phdr *phdr, struct file *file) | ||
| 486 | { | ||
| 487 | /* p_offset and p_vaddr must have the same page offset. */ | ||
| 488 | if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK)) | ||
| 489 | return false; | ||
| 490 | |||
| 491 | /* p_offset must point within FILE. */ | ||
| 492 | if (phdr->p_offset > (Elf32_Off) file_length (file)) | ||
| 493 | return false; | ||
| 494 | |||
| 495 | /* p_memsz must be at least as big as p_filesz. */ | ||
| 496 | if (phdr->p_memsz < phdr->p_filesz) | ||
| 497 | return false; | ||
| 498 | |||
| 499 | /* The segment must not be empty. */ | ||
| 500 | if (phdr->p_memsz == 0) | ||
| 501 | return false; | ||
| 502 | |||
| 503 | /* The virtual memory region must both start and end within the | ||
| 504 | user address space range. */ | ||
| 505 | if (!is_user_vaddr ((void *) phdr->p_vaddr)) | ||
| 506 | return false; | ||
| 507 | if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz))) | ||
| 508 | return false; | ||
| 509 | |||
| 510 | /* The region cannot "wrap around" across the kernel virtual | ||
| 511 | address space. */ | ||
| 512 | if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr) | ||
| 513 | return false; | ||
| 514 | |||
| 515 | /* Disallow mapping page 0. | ||
| 516 | Not only is it a bad idea to map page 0, but if we allowed | ||
| 517 | it then user code that passed a null pointer to system calls | ||
| 518 | could quite likely panic the kernel by way of null pointer | ||
| 519 | assertions in memcpy(), etc. */ | ||
| 520 | if (phdr->p_vaddr < PGSIZE) | ||
| 521 | return false; | ||
| 522 | |||
| 523 | /* It's okay. */ | ||
| 524 | return true; | ||
| 525 | } | ||
| 526 | |||
| 527 | /* Loads a segment starting at offset OFS in FILE at address | ||
| 528 | UPAGE. In total, READ_BYTES + ZERO_BYTES bytes of virtual | ||
| 529 | memory are initialized, as follows: | ||
| 530 | |||
| 531 | - READ_BYTES bytes at UPAGE must be read from FILE | ||
| 532 | starting at offset OFS. | ||
| 533 | |||
| 534 | - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed. | ||
| 535 | |||
| 536 | The pages initialized by this function must be writable by the | ||
| 537 | user process if WRITABLE is true, read-only otherwise. | ||
| 538 | |||
| 539 | Return true if successful, false if a memory allocation error | ||
| 540 | or disk read error occurs. */ | ||
| 541 | static bool | ||
| 542 | load_segment (struct file *file, off_t ofs, uint8_t *upage, | ||
| 543 | uint32_t read_bytes, uint32_t zero_bytes, bool writable) | ||
| 544 | { | ||
| 545 | ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0); | ||
| 546 | ASSERT (pg_ofs (upage) == 0); | ||
| 547 | ASSERT (ofs % PGSIZE == 0); | ||
| 548 | |||
| 549 | file_seek (file, ofs); | ||
| 550 | while (read_bytes > 0 || zero_bytes > 0) | ||
| 551 | { | ||
| 552 | /* Calculate how to fill this page. | ||
| 553 | We will read PAGE_READ_BYTES bytes from FILE | ||
| 554 | and zero the final PAGE_ZERO_BYTES bytes. */ | ||
| 555 | size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE; | ||
| 556 | size_t page_zero_bytes = PGSIZE - page_read_bytes; | ||
| 557 | |||
| 558 | /* Get a page of memory. */ | ||
| 559 | uint8_t *kpage = palloc_get_page (PAL_USER); | ||
| 560 | if (kpage == NULL) | ||
| 561 | return false; | ||
| 562 | |||
| 563 | /* Load this page. */ | ||
| 564 | if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes) | ||
| 565 | { | ||
| 566 | palloc_free_page (kpage); | ||
| 567 | return false; | ||
| 568 | } | ||
| 569 | memset (kpage + page_read_bytes, 0, page_zero_bytes); | ||
| 570 | |||
| 571 | /* Add the page to the process's address space. */ | ||
| 572 | if (!install_page (upage, kpage, writable)) | ||
| 573 | { | ||
| 574 | palloc_free_page (kpage); | ||
| 575 | return false; | ||
| 576 | } | ||
| 577 | |||
| 578 | /* Advance. */ | ||
| 579 | read_bytes -= page_read_bytes; | ||
| 580 | zero_bytes -= page_zero_bytes; | ||
| 581 | upage += PGSIZE; | ||
| 582 | } | ||
| 583 | return true; | ||
| 584 | } | ||
| 585 | |||
| 586 | /* Create a minimal stack by mapping a zeroed page at the top of | ||
| 587 | user virtual memory. | ||
| 588 | You will implement this function in the Project 0. | ||
| 589 | Consider using `hex_dump` for debugging purposes */ | ||
| 590 | static bool | ||
| 591 | setup_stack (void **esp) | ||
| 592 | { | ||
| 593 | uint8_t *kpage = NULL; | ||
| 594 | |||
| 595 | kpage = palloc_get_page (PAL_USER | PAL_ZERO); | ||
| 596 | if (kpage == NULL) | ||
| 597 | return false; | ||
| 598 | |||
| 599 | if (! install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true)) { | ||
| 600 | palloc_free_page (kpage); | ||
| 601 | return false; | ||
| 602 | } | ||
| 603 | |||
| 604 | /* Currently we assume that 'argc = 0' */ | ||
| 605 | *esp = PHYS_BASE - 12; | ||
| 606 | |||
| 607 | return true; | ||
| 608 | } | ||
| 609 | |||
| 610 | /* Adds a mapping from user virtual address UPAGE to kernel | ||
| 611 | virtual address KPAGE to the page table. | ||
| 612 | If WRITABLE is true, the user process may modify the page; | ||
| 613 | otherwise, it is read-only. | ||
| 614 | UPAGE must not already be mapped. | ||
| 615 | KPAGE should probably be a page obtained from the user pool | ||
| 616 | with palloc_get_page(). | ||
| 617 | Returns true on success, false if UPAGE is already mapped or | ||
| 618 | if memory allocation fails. */ | ||
| 619 | static bool | ||
| 620 | install_page (void *upage, void *kpage, bool writable) | ||
| 621 | { | ||
| 622 | struct thread *t = thread_current (); | ||
| 623 | |||
| 624 | /* Verify that there's not already a page at that virtual | ||
| 625 | address, then map our page there. */ | ||
| 626 | return (pagedir_get_page (t->pagedir, upage) == NULL | ||
| 627 | && pagedir_set_page (t->pagedir, upage, kpage, writable)); | ||
| 628 | } | ||
| 629 | |||
| 630 | static | ||
| 631 | bool | ||
| 632 | init_fd_table (struct fd_table *table) | ||
| 633 | { | ||
| 634 | table->fds = palloc_get_page (PAL_ZERO); | ||
| 635 | if (table->fds == NULL) | ||
| 636 | return false; | ||
| 637 | table->fd_cap = PGSIZE / sizeof (table->fds[0]); | ||
| 638 | table->fd_free = 2; | ||
| 639 | table->fd_max = 1; | ||
| 640 | return true; | ||
| 641 | } | ||
| 642 | |||
| 643 | /* Open the file with the given name; returns | ||
| 644 | a file descriptor for this file if successful, | ||
| 645 | and a negative value otherwise */ | ||
| 646 | int | ||
| 647 | process_open_file (const char* fname) | ||
| 648 | { | ||
| 649 | struct fd_table *fdt = &process_current()->fd_table; | ||
| 650 | if (fdt->fd_free >= fdt->fd_cap) | ||
| 651 | return -1; | ||
| 652 | |||
| 653 | lock_acquire (&filesys_lock); | ||
| 654 | struct file *f = filesys_open (fname); | ||
| 655 | lock_release (&filesys_lock); | ||
| 656 | |||
| 657 | if (f == NULL) | ||
| 658 | return -1; | ||
| 659 | |||
| 660 | int fd = fdt->fd_free++; | ||
| 661 | fdt->fds[fd] = f; | ||
| 662 | |||
| 663 | /* update index of free/max file descriptor index*/ | ||
| 664 | if (fd > fdt->fd_max) fdt->fd_max = fd; | ||
| 665 | while (fdt->fds[fdt->fd_free] != NULL) { | ||
| 666 | fdt->fd_free++; | ||
| 667 | if (fdt->fd_free >= fdt->fd_cap) | ||
| 668 | break; | ||
| 669 | } | ||
| 670 | return fd; | ||
| 671 | } | ||
| 672 | |||
| 673 | /* Get the file associated with the given file | ||
| 674 | descriptor; return NULL if no file is associated | ||
| 675 | with the given descriptor */ | ||
| 676 | struct file* | ||
| 677 | process_get_file (int fd) | ||
| 678 | { | ||
| 679 | struct fd_table *fdt = &process_current()->fd_table; | ||
| 680 | if (fd < 2 || fd >= fdt->fd_cap || ! fdt->fds[fd]) | ||
| 681 | return NULL; | ||
| 682 | return fdt->fds[fd]; | ||
| 683 | } | ||
| 684 | |||
| 685 | /* Acquire global lock for the filesystem */ | ||
| 686 | void process_lock_filesys (void) | ||
| 687 | { | ||
| 688 | lock_acquire (&filesys_lock); | ||
| 689 | } | ||
| 690 | |||
| 691 | /* Release global filesystem lock */ | ||
| 692 | void process_unlock_filesys (void) | ||
| 693 | { | ||
| 694 | lock_release (&filesys_lock); | ||
| 695 | } | ||
| 696 | |||
| 697 | /* Close the file associated with the given file | ||
| 698 | descriptor; returns true if close was successful */ | ||
| 699 | bool | ||
| 700 | process_close_file (int fd) | ||
| 701 | { | ||
| 702 | struct file *file = process_get_file (fd); | ||
| 703 | if (file == NULL) | ||
| 704 | return false; | ||
| 705 | |||
| 706 | lock_acquire (&filesys_lock); | ||
| 707 | file_close (file); | ||
| 708 | lock_release (&filesys_lock); | ||
| 709 | |||
| 710 | struct fd_table *fdt = &process_current()->fd_table; | ||
| 711 | fdt->fds[fd] = NULL; | ||
| 712 | |||
| 713 | /* update index of free/max file descriptor index*/ | ||
| 714 | if (fd < fdt->fd_free) fdt->fd_free = fd; | ||
| 715 | while (fdt->fds[fdt->fd_max] == NULL) { | ||
| 716 | fdt->fd_max--; | ||
| 717 | if (fdt->fd_max < 2) | ||
| 718 | break; | ||
| 719 | } | ||
| 720 | return true; | ||
| 721 | } | ||
diff --git a/pintos-progos/userprog/process.h b/pintos-progos/userprog/process.h new file mode 100644 index 0000000..1609801 --- /dev/null +++ b/pintos-progos/userprog/process.h | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #ifndef USERPROG_PROCESS_H | ||
| 2 | #define USERPROG_PROCESS_H | ||
| 3 | |||
| 4 | #include "threads/thread.h" | ||
| 5 | |||
| 6 | /* In the current implementation, the capacity is fixed to 1024 (PGSIZE/4) */ | ||
| 7 | struct fd_table { | ||
| 8 | struct file** fds; | ||
| 9 | int fd_free; /* lowest-index free FD table entry */ | ||
| 10 | int fd_max; /* highest-index used FD table entry */ | ||
| 11 | int fd_cap; /* FD table capacity */ | ||
| 12 | }; | ||
| 13 | |||
| 14 | struct process { | ||
| 15 | /* process tree */ | ||
| 16 | tid_t thread_id; | ||
| 17 | tid_t parent_tid; | ||
| 18 | struct list_elem parentelem; /* Owned by parent */ | ||
| 19 | |||
| 20 | /* communication with parent process */ | ||
| 21 | struct semaphore exit_sem; | ||
| 22 | struct lock exit_lock; | ||
| 23 | int exit_status; | ||
| 24 | |||
| 25 | /* files */ | ||
| 26 | struct file *executable; /* Loaded executable, if any. */ | ||
| 27 | struct fd_table fd_table; /* File descriptor table */ | ||
| 28 | |||
| 29 | /* Owned by syscall.c */ | ||
| 30 | void* syscall_buffer; | ||
| 31 | size_t syscall_buffer_page_cnt; | ||
| 32 | }; | ||
| 33 | |||
| 34 | void process_init (void); | ||
| 35 | struct process* process_current (void); | ||
| 36 | tid_t process_execute (const char *file_name); | ||
| 37 | int process_wait (tid_t); | ||
| 38 | void process_exit (void); | ||
| 39 | void process_activate (void); | ||
| 40 | |||
| 41 | int process_open_file(const char* fname); | ||
| 42 | struct file* process_get_file(int fd); | ||
| 43 | void process_lock_filesys (void); | ||
| 44 | void process_unlock_filesys (void); | ||
| 45 | bool process_close_file(int fd); | ||
| 46 | |||
| 47 | #endif /* userprog/process.h */ | ||
diff --git a/pintos-progos/userprog/syscall.c b/pintos-progos/userprog/syscall.c new file mode 100644 index 0000000..f8e0197 --- /dev/null +++ b/pintos-progos/userprog/syscall.c | |||
| @@ -0,0 +1,563 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <syscall-nr.h> | ||
| 3 | #include "devices/input.h" | ||
| 4 | #include "devices/shutdown.h" | ||
| 5 | #include "filesys/file.h" | ||
| 6 | #include "filesys/filesys.h" | ||
| 7 | #include "filesys/inode.h" | ||
| 8 | #include "lib/string.h" | ||
| 9 | #include "threads/interrupt.h" | ||
| 10 | #include "threads/palloc.h" | ||
| 11 | #include "threads/synch.h" | ||
| 12 | #include "threads/thread.h" | ||
| 13 | #include "threads/vaddr.h" | ||
| 14 | #include "userprog/pagedir.h" | ||
| 15 | #include "userprog/process.h" | ||
| 16 | #include "userprog/syscall.h" | ||
| 17 | |||
| 18 | #define STACK_SLOT_SIZE sizeof(int) | ||
| 19 | |||
| 20 | /* Prototypes for Utilities */ | ||
| 21 | static int get_user (const uint8_t *uaddr); | ||
| 22 | static bool put_user (uint8_t *udst, uint8_t byte); | ||
| 23 | static void* memcpy_from_user (void *kaddr, void *uaddr, size_t bytes); | ||
| 24 | static void* memcpy_to_user (void *kaddr, void *addr, size_t bytes); | ||
| 25 | static void* copy_string_arg (void *usp, bool *segfault); | ||
| 26 | static void free_string_arg_buf (void *kbuf); | ||
| 27 | |||
| 28 | /* Reads a byte at user virtual address UADDR. | ||
| 29 | UADDR must be below PHYS_BASE. | ||
| 30 | Returns the byte value if successful, -1 if a segfault | ||
| 31 | occurred. */ | ||
| 32 | static int | ||
| 33 | get_user (const uint8_t *uaddr) | ||
| 34 | { | ||
| 35 | int result; | ||
| 36 | asm ("movl $1f, %0; " /* save eip in eax */ | ||
| 37 | "movzbl %1, %0; " /* read byte from user memory into eax */ | ||
| 38 | "1:" /* continue here on page fault, with eax set to -1 */ | ||
| 39 | : "=&a" (result) : "m" (*uaddr)); | ||
| 40 | return result; | ||
| 41 | } | ||
| 42 | |||
| 43 | /* Writes BYTE to user address UDST. | ||
| 44 | UDST must be below PHYS_BASE. | ||
| 45 | Returns true if successful, false if a segfault occurred. */ | ||
| 46 | static bool | ||
| 47 | put_user (uint8_t *udst, uint8_t byte) | ||
| 48 | { | ||
| 49 | int error_code; | ||
| 50 | asm ("movl $1f, %0;" /* save EIP in EAX */ | ||
| 51 | "movb %b2, %1;" /* write byte to user memory */ | ||
| 52 | "1:" /* continue here on page fault, with eax set to -1 */ | ||
| 53 | : "=&a" (error_code), "=m" (*udst) : "q" (byte)); | ||
| 54 | return error_code != -1; | ||
| 55 | } | ||
| 56 | |||
| 57 | /* Copy bytes from user space; returns NULL if a segfault | ||
| 58 | occured, and kaddr otherwise */ | ||
| 59 | static void* | ||
| 60 | memcpy_from_user (void *kaddr, void *uaddr, size_t bytes) | ||
| 61 | { | ||
| 62 | uint8_t* kp = kaddr; | ||
| 63 | size_t i; | ||
| 64 | if (! is_user_vaddr (uaddr) || | ||
| 65 | ! is_user_vaddr (uaddr + bytes - 1)) | ||
| 66 | return false; | ||
| 67 | for (i=0;i<bytes;i++) { | ||
| 68 | int b = get_user (uaddr+i); | ||
| 69 | if (b < 0) | ||
| 70 | break; /* segfault */ | ||
| 71 | else | ||
| 72 | kp[i] = b; | ||
| 73 | } | ||
| 74 | if (i != bytes) | ||
| 75 | return NULL; /* segfault */ | ||
| 76 | return kaddr; | ||
| 77 | } | ||
| 78 | |||
| 79 | /* Copy bytes to user space; returns NULL if a segfault | ||
| 80 | occured, and uaddr otherwise */ | ||
| 81 | static void* | ||
| 82 | memcpy_to_user (void *uaddr, void *kaddr, size_t bytes) | ||
| 83 | { | ||
| 84 | uint8_t* kp = kaddr; | ||
| 85 | size_t i; | ||
| 86 | if (! is_user_vaddr (uaddr) || | ||
| 87 | ! is_user_vaddr (uaddr + bytes - 1)) | ||
| 88 | return false; | ||
| 89 | for (i=0;i<bytes;i++) { | ||
| 90 | if (! put_user (uaddr+i, kp[i])) | ||
| 91 | break; /* segfault */ | ||
| 92 | } | ||
| 93 | if (i != bytes) | ||
| 94 | return NULL; /* segfault */ | ||
| 95 | return uaddr; | ||
| 96 | } | ||
| 97 | |||
| 98 | /* Copy string (at most cnt bytes) from user memory to the kernel address | ||
| 99 | `kaddr`. The number of bytes copied is returned; 0 indicates a segfault. */ | ||
| 100 | static size_t | ||
| 101 | strncpy_from_user (void *kaddr, void *uaddr, size_t cnt) | ||
| 102 | { | ||
| 103 | uint8_t *kp = (uint8_t*) kaddr; | ||
| 104 | size_t i; | ||
| 105 | int c = 1; | ||
| 106 | for (i = 0; i < cnt && c; i++) { | ||
| 107 | if (! is_user_vaddr (uaddr+i)) | ||
| 108 | return 0; /* sefault */ | ||
| 109 | c = get_user (uaddr+i); | ||
| 110 | if (c < 0) | ||
| 111 | return 0; /* segfault */ | ||
| 112 | kp[i] = c; | ||
| 113 | } | ||
| 114 | return i; | ||
| 115 | } | ||
| 116 | |||
| 117 | /* Copy a value of scalar type `typeof(*kdst)` from user space pointer | ||
| 118 | `usrc` to kernel space variable pointed to by `kdst`. Returns | ||
| 119 | false on segfault, and true otherwise. */ | ||
| 120 | #define copy_from_user(kdst, usrc) \ | ||
| 121 | (memcpy_from_user (kdst, usrc, sizeof (*kdst)) != NULL) | ||
| 122 | |||
| 123 | /* Takes user space stack pointer, which points to a string | ||
| 124 | in user space. Copies that string (at most PGSIZE bytes) | ||
| 125 | to a freshly allocated buffer in kernel space. Reurns NULL on | ||
| 126 | error; on a segfault, additionally sets `segfault` to true */ | ||
| 127 | static void* | ||
| 128 | copy_string_arg (void *usp, bool *segfault) | ||
| 129 | { | ||
| 130 | size_t bytes_copied; | ||
| 131 | char *uptr; | ||
| 132 | char *kpage; | ||
| 133 | if (! copy_from_user (&uptr, usp)) { | ||
| 134 | *segfault = true; | ||
| 135 | return NULL; | ||
| 136 | } | ||
| 137 | kpage = palloc_get_page (PAL_ZERO); | ||
| 138 | if (kpage == NULL) | ||
| 139 | return NULL; | ||
| 140 | bytes_copied = strncpy_from_user (kpage, uptr, PGSIZE); | ||
| 141 | if (bytes_copied == 0) { | ||
| 142 | *segfault = true; | ||
| 143 | palloc_free_page (kpage); | ||
| 144 | return NULL; | ||
| 145 | } | ||
| 146 | if (kpage[bytes_copied-1] != '\0') { | ||
| 147 | palloc_free_page (kpage); | ||
| 148 | return NULL; | ||
| 149 | }; | ||
| 150 | return kpage; | ||
| 151 | } | ||
| 152 | |||
| 153 | /* free buffer allocated by `copy_string_arg` */ | ||
| 154 | static void free_string_arg_buf (void *kbuf) | ||
| 155 | { | ||
| 156 | palloc_free_page (kbuf); | ||
| 157 | } | ||
| 158 | |||
| 159 | /* Stack slot address */ | ||
| 160 | #define STACK_ADDR(sp, slot) (sp + STACK_SLOT_SIZE*slot) | ||
| 161 | |||
| 162 | /* syscall handler prototype */ | ||
| 163 | static void syscall_handler (struct intr_frame *); | ||
| 164 | |||
| 165 | typedef int (handler)(void *sp, bool *segfault); | ||
| 166 | |||
| 167 | /* Prototypes for syscall handlers */ | ||
| 168 | static handler | ||
| 169 | syscall_halt, | ||
| 170 | syscall_exit, | ||
| 171 | syscall_write, | ||
| 172 | syscall_wait , | ||
| 173 | syscall_exec, | ||
| 174 | syscall_create, | ||
| 175 | syscall_remove, | ||
| 176 | syscall_open, | ||
| 177 | syscall_filesize, | ||
| 178 | syscall_read, | ||
| 179 | syscall_seek, | ||
| 180 | syscall_tell, | ||
| 181 | syscall_close; | ||
| 182 | |||
| 183 | /* Register syscall_handler for interrupt 0x30 */ | ||
| 184 | void | ||
| 185 | syscall_init (void) | ||
| 186 | { | ||
| 187 | intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall"); | ||
| 188 | } | ||
| 189 | |||
| 190 | /* Syscall handler, delegating known syscalls to the respective implementations */ | ||
| 191 | static void | ||
| 192 | syscall_handler (struct intr_frame *f) | ||
| 193 | { | ||
| 194 | int syscall_nr; | ||
| 195 | handler* fp; | ||
| 196 | bool segfault = false; | ||
| 197 | int result; | ||
| 198 | void *sp = f->esp; | ||
| 199 | |||
| 200 | /* The system call number and the arguments are on the stack */ | ||
| 201 | if (! copy_from_user (&syscall_nr,sp)) | ||
| 202 | goto fail; | ||
| 203 | switch (syscall_nr) { | ||
| 204 | case SYS_HALT: fp = syscall_halt; break; | ||
| 205 | case SYS_EXIT: fp = syscall_exit; break; | ||
| 206 | case SYS_EXEC: fp = syscall_exec; break; | ||
| 207 | case SYS_WAIT: fp = syscall_wait; break; | ||
| 208 | case SYS_CREATE: fp = syscall_create; break; | ||
| 209 | case SYS_REMOVE: fp = syscall_remove; break; | ||
| 210 | case SYS_OPEN: fp = syscall_open; break; | ||
| 211 | case SYS_FILESIZE: fp = syscall_filesize; break; | ||
| 212 | case SYS_READ: fp = syscall_read; break; | ||
| 213 | case SYS_WRITE: fp = syscall_write; break; | ||
| 214 | case SYS_SEEK: fp = syscall_seek; break; | ||
| 215 | case SYS_TELL: fp = syscall_tell; break; | ||
| 216 | case SYS_CLOSE: fp = syscall_close; break; | ||
| 217 | default: | ||
| 218 | goto fail; | ||
| 219 | } | ||
| 220 | result = fp (sp, &segfault); | ||
| 221 | if (segfault) | ||
| 222 | goto fail; | ||
| 223 | f->eax = result; | ||
| 224 | return; | ||
| 225 | |||
| 226 | fail: | ||
| 227 | process_current()->exit_status = -1; | ||
| 228 | thread_exit (); | ||
| 229 | } | ||
| 230 | |||
| 231 | /* Shutdown machine */ | ||
| 232 | static int | ||
| 233 | syscall_halt (void *sp UNUSED, bool *segfault UNUSED) | ||
| 234 | { | ||
| 235 | shutdown (); | ||
| 236 | NOT_REACHED (); | ||
| 237 | } | ||
| 238 | |||
| 239 | /* Exit current process with given exit code */ | ||
| 240 | static int | ||
| 241 | syscall_exit (void *sp, bool *segfault) | ||
| 242 | { | ||
| 243 | int exit_status; | ||
| 244 | if (! copy_from_user (&exit_status, STACK_ADDR (sp,1))) { | ||
| 245 | *segfault = true; | ||
| 246 | return -1; | ||
| 247 | } | ||
| 248 | process_current()->exit_status = exit_status; | ||
| 249 | thread_exit (); | ||
| 250 | NOT_REACHED (); | ||
| 251 | } | ||
| 252 | |||
| 253 | /* Spawn new process executing the supplied command */ | ||
| 254 | static int | ||
| 255 | syscall_exec (void *sp, bool *segfault) | ||
| 256 | { | ||
| 257 | char *kbuf; | ||
| 258 | int result = TID_ERROR; | ||
| 259 | if ((kbuf = copy_string_arg (STACK_ADDR (sp, 1), segfault)) != NULL) { | ||
| 260 | result = process_execute (kbuf); | ||
| 261 | free_string_arg_buf (kbuf); | ||
| 262 | } | ||
| 263 | return result; | ||
| 264 | } | ||
| 265 | |||
| 266 | /* Wait until specified process exits */ | ||
| 267 | static int | ||
| 268 | syscall_wait (void *sp, bool *segfault) | ||
| 269 | { | ||
| 270 | tid_t arg; | ||
| 271 | if (! copy_from_user (&arg, STACK_ADDR (sp,1))) { | ||
| 272 | *segfault = true; | ||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | return process_wait (arg); | ||
| 276 | } | ||
| 277 | |||
| 278 | /* Create a new file with given initial size */ | ||
| 279 | static int | ||
| 280 | syscall_create (void *sp, bool *segfault) | ||
| 281 | { | ||
| 282 | bool success = false; | ||
| 283 | char *fname; | ||
| 284 | int initial_size; | ||
| 285 | |||
| 286 | if (! copy_from_user (&initial_size, STACK_ADDR (sp,2))) { | ||
| 287 | *segfault = true; | ||
| 288 | return false; | ||
| 289 | } | ||
| 290 | if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) | ||
| 291 | return false; | ||
| 292 | |||
| 293 | process_lock_filesys (); | ||
| 294 | success = filesys_create (fname, initial_size); | ||
| 295 | process_unlock_filesys (); | ||
| 296 | free_string_arg_buf (fname); | ||
| 297 | return success; | ||
| 298 | } | ||
| 299 | |||
| 300 | /* Remove name file, returns true if successful */ | ||
| 301 | static int | ||
| 302 | syscall_remove (void *sp, bool *segfault) | ||
| 303 | { | ||
| 304 | bool success; | ||
| 305 | char *fname; | ||
| 306 | |||
| 307 | if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) | ||
| 308 | return false; | ||
| 309 | process_lock_filesys (); | ||
| 310 | success = filesys_remove (fname); | ||
| 311 | process_unlock_filesys (); | ||
| 312 | free_string_arg_buf (fname); | ||
| 313 | return (int)success; | ||
| 314 | } | ||
| 315 | |||
| 316 | /* Open file, returning non-negative file descriptor if successful */ | ||
| 317 | static int | ||
| 318 | syscall_open (void *sp, bool *segfault) | ||
| 319 | { | ||
| 320 | char *fname; | ||
| 321 | int fd; | ||
| 322 | if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL) | ||
| 323 | return false; | ||
| 324 | fd = process_open_file (fname); | ||
| 325 | free_string_arg_buf (fname); | ||
| 326 | return fd; | ||
| 327 | } | ||
| 328 | |||
| 329 | /* Return size of file described by file descriptor */ | ||
| 330 | static int | ||
| 331 | syscall_filesize (void *sp, bool *segfault) | ||
| 332 | { | ||
| 333 | int fd; | ||
| 334 | struct file *f; | ||
| 335 | int size; | ||
| 336 | |||
| 337 | if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { | ||
| 338 | *segfault = true; | ||
| 339 | return -1; | ||
| 340 | } | ||
| 341 | if ((f = process_get_file (fd)) == NULL) | ||
| 342 | return -1; | ||
| 343 | process_lock_filesys (); | ||
| 344 | size = inode_length (file_get_inode (f)); | ||
| 345 | process_unlock_filesys (); | ||
| 346 | return size; | ||
| 347 | } | ||
| 348 | |||
| 349 | /* Read bytes from the file referenced by the given file | ||
| 350 | descriptor into the supplied user space buffer, returning | ||
| 351 | number of bytes read. */ | ||
| 352 | static int | ||
| 353 | syscall_read (void *sp, bool *segfault) | ||
| 354 | { | ||
| 355 | int fd; | ||
| 356 | uint8_t *user_buffer; | ||
| 357 | size_t size, bytes_to_read; | ||
| 358 | |||
| 359 | /* get arguments */ | ||
| 360 | if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || | ||
| 361 | ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) || | ||
| 362 | ! copy_from_user (&size, STACK_ADDR (sp,3))) { | ||
| 363 | *segfault = true; | ||
| 364 | return -1; | ||
| 365 | } | ||
| 366 | |||
| 367 | /* ensure buffer is in user space */ | ||
| 368 | if (! is_user_vaddr (user_buffer) || | ||
| 369 | ! is_user_vaddr (user_buffer + size - 1)) { | ||
| 370 | *segfault = true; | ||
| 371 | return -1; | ||
| 372 | } | ||
| 373 | |||
| 374 | bytes_to_read = size; | ||
| 375 | /* handle stdin */ | ||
| 376 | if (fd == STDIN_FILENO) { | ||
| 377 | char c; | ||
| 378 | while (bytes_to_read--) { | ||
| 379 | c = input_getc (); | ||
| 380 | if (! put_user (user_buffer++, c)) { | ||
| 381 | *segfault = true; | ||
| 382 | return -1; | ||
| 383 | } | ||
| 384 | } | ||
| 385 | return size; | ||
| 386 | } | ||
| 387 | /* get file */ | ||
| 388 | struct file *file = process_get_file (fd); | ||
| 389 | if (file == NULL) | ||
| 390 | return -1; | ||
| 391 | |||
| 392 | char *kbuf = palloc_get_page (0); | ||
| 393 | if (kbuf == NULL) | ||
| 394 | return -1; | ||
| 395 | |||
| 396 | /* read loop */ | ||
| 397 | do { | ||
| 398 | int bytes_read; | ||
| 399 | int blocksize = bytes_to_read; | ||
| 400 | if (bytes_to_read > PGSIZE) | ||
| 401 | blocksize = PGSIZE; | ||
| 402 | |||
| 403 | /* read bytes */ | ||
| 404 | process_lock_filesys (); | ||
| 405 | bytes_read = file_read (file, kbuf, blocksize); | ||
| 406 | process_unlock_filesys (); | ||
| 407 | |||
| 408 | /* Stop when EOF has been reached */ | ||
| 409 | if (bytes_read == 0) | ||
| 410 | break; | ||
| 411 | bytes_to_read -= bytes_read; | ||
| 412 | if (! memcpy_to_user (user_buffer, kbuf, bytes_read)) { | ||
| 413 | *segfault = true; | ||
| 414 | break; | ||
| 415 | } | ||
| 416 | user_buffer += bytes_read; | ||
| 417 | } while (bytes_to_read > 0); | ||
| 418 | |||
| 419 | palloc_free_page (kbuf); | ||
| 420 | return size - bytes_to_read; | ||
| 421 | } | ||
| 422 | |||
| 423 | /* Write bytes from user buffer into the specified | ||
| 424 | file, returning number of bytes written. */ | ||
| 425 | static int | ||
| 426 | syscall_write (void *sp, bool *segfault) | ||
| 427 | { | ||
| 428 | int fd; | ||
| 429 | size_t size, bytes_to_write; | ||
| 430 | char *user_buffer; | ||
| 431 | |||
| 432 | /* get arguments */ | ||
| 433 | if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || | ||
| 434 | ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) || | ||
| 435 | ! copy_from_user (&size, STACK_ADDR (sp,3))) { | ||
| 436 | *segfault = true; | ||
| 437 | return -1; | ||
| 438 | } | ||
| 439 | |||
| 440 | /* ensure buffer is in user space */ | ||
| 441 | if (! is_user_vaddr (user_buffer) || | ||
| 442 | ! is_user_vaddr (user_buffer + size - 1)) { | ||
| 443 | *segfault = true; | ||
| 444 | return -1; | ||
| 445 | } | ||
| 446 | |||
| 447 | /* get file handle */ | ||
| 448 | struct file *file = NULL; | ||
| 449 | if (fd != STDOUT_FILENO) { | ||
| 450 | file = process_get_file (fd); | ||
| 451 | if (file == NULL) | ||
| 452 | return -1; | ||
| 453 | } | ||
| 454 | |||
| 455 | /* allocate kernel buffer */ | ||
| 456 | char *kbuf = palloc_get_page (0); | ||
| 457 | if (kbuf == NULL) | ||
| 458 | return -1; | ||
| 459 | |||
| 460 | /* write loop */ | ||
| 461 | bytes_to_write = size; | ||
| 462 | do { | ||
| 463 | int blocksize = bytes_to_write; | ||
| 464 | if (bytes_to_write > PGSIZE) | ||
| 465 | blocksize = PGSIZE; | ||
| 466 | if (memcpy_from_user (kbuf, user_buffer, blocksize) == NULL) { | ||
| 467 | *segfault = true; | ||
| 468 | break; | ||
| 469 | } | ||
| 470 | if (fd == STDOUT_FILENO) { | ||
| 471 | putbuf (kbuf, blocksize); | ||
| 472 | bytes_to_write -= blocksize; | ||
| 473 | } else { | ||
| 474 | int bytes_written = 0; | ||
| 475 | int bytes_left_filesys = blocksize; | ||
| 476 | |||
| 477 | process_lock_filesys (); | ||
| 478 | while (bytes_left_filesys > 0) { | ||
| 479 | bytes_written = file_write (file, kbuf, bytes_left_filesys); | ||
| 480 | if (bytes_written <= 0) { | ||
| 481 | break; | ||
| 482 | } | ||
| 483 | bytes_left_filesys -= bytes_written; | ||
| 484 | } | ||
| 485 | process_unlock_filesys (); | ||
| 486 | |||
| 487 | if (bytes_written <= 0) | ||
| 488 | break; | ||
| 489 | bytes_to_write -= blocksize; | ||
| 490 | } | ||
| 491 | user_buffer += blocksize; | ||
| 492 | } while (bytes_to_write > 0); | ||
| 493 | |||
| 494 | /* return bytes written */ | ||
| 495 | palloc_free_page (kbuf); | ||
| 496 | return size - bytes_to_write; | ||
| 497 | } | ||
| 498 | |||
| 499 | /* Change the position where the next byte will be read or written */ | ||
| 500 | static int | ||
| 501 | syscall_seek (void *sp, bool *segfault) | ||
| 502 | { | ||
| 503 | int fd; | ||
| 504 | off_t new_pos; | ||
| 505 | |||
| 506 | /* get arguments */ | ||
| 507 | if (! copy_from_user (&fd, STACK_ADDR (sp,1)) || | ||
| 508 | ! copy_from_user (&new_pos, STACK_ADDR (sp, 2))) { | ||
| 509 | *segfault = true; | ||
| 510 | return 0; | ||
| 511 | } | ||
| 512 | |||
| 513 | /* no way to return something sensible (void function) */ | ||
| 514 | struct file *file = process_get_file (fd); | ||
| 515 | if (file == NULL) | ||
| 516 | return 0; | ||
| 517 | |||
| 518 | process_lock_filesys (); | ||
| 519 | file_seek (file, new_pos); | ||
| 520 | process_unlock_filesys (); | ||
| 521 | return 0; | ||
| 522 | } | ||
| 523 | |||
| 524 | /* Returns the position of the next byte to be read or written */ | ||
| 525 | static int | ||
| 526 | syscall_tell (void *sp, bool *segfault) | ||
| 527 | { | ||
| 528 | int fd; | ||
| 529 | unsigned r = 0; | ||
| 530 | |||
| 531 | /* get arguments */ | ||
| 532 | if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { | ||
| 533 | *segfault = true; | ||
| 534 | return 0; | ||
| 535 | } | ||
| 536 | |||
| 537 | /* no way to return something sensible function */ | ||
| 538 | struct file *file = process_get_file (fd); | ||
| 539 | if (file == NULL) | ||
| 540 | return 0; | ||
| 541 | |||
| 542 | process_lock_filesys (); | ||
| 543 | r = file_tell (file); | ||
| 544 | process_unlock_filesys (); | ||
| 545 | return r; | ||
| 546 | } | ||
| 547 | |||
| 548 | /* Close the given file */ | ||
| 549 | static int | ||
| 550 | syscall_close (void *sp, bool *segfault) | ||
| 551 | { | ||
| 552 | int fd; | ||
| 553 | |||
| 554 | /* get arguments */ | ||
| 555 | if (! copy_from_user (&fd, STACK_ADDR (sp,1))) { | ||
| 556 | *segfault = true; | ||
| 557 | return 0; | ||
| 558 | } | ||
| 559 | |||
| 560 | /* no way to return something sensible function (void) */ | ||
| 561 | (void) process_close_file (fd); | ||
| 562 | return 0; | ||
| 563 | } | ||
diff --git a/pintos-progos/userprog/syscall.h b/pintos-progos/userprog/syscall.h new file mode 100644 index 0000000..f7ab2f3 --- /dev/null +++ b/pintos-progos/userprog/syscall.h | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | #ifndef USERPROG_SYSCALL_H | ||
| 2 | #define USERPROG_SYSCALL_H | ||
| 3 | |||
| 4 | void syscall_init (void); | ||
| 5 | #endif /* userprog/syscall.h */ | ||
diff --git a/pintos-progos/userprog/tss.c b/pintos-progos/userprog/tss.c new file mode 100644 index 0000000..f8ed9a9 --- /dev/null +++ b/pintos-progos/userprog/tss.c | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | #include "userprog/tss.h" | ||
| 2 | #include <debug.h> | ||
| 3 | #include <stddef.h> | ||
| 4 | #include "userprog/gdt.h" | ||
| 5 | #include "threads/thread.h" | ||
| 6 | #include "threads/palloc.h" | ||
| 7 | #include "threads/vaddr.h" | ||
| 8 | |||
| 9 | /* The Task-State Segment (TSS). | ||
| 10 | |||
| 11 | Instances of the TSS, an x86-specific structure, are used to | ||
| 12 | define "tasks", a form of support for multitasking built right | ||
| 13 | into the processor. However, for various reasons including | ||
| 14 | portability, speed, and flexibility, most x86 OSes almost | ||
| 15 | completely ignore the TSS. We are no exception. | ||
| 16 | |||
| 17 | Unfortunately, there is one thing that can only be done using | ||
| 18 | a TSS: stack switching for interrupts that occur in user mode. | ||
| 19 | When an interrupt occurs in user mode (ring 3), the processor | ||
| 20 | consults the ss0 and esp0 members of the current TSS to | ||
| 21 | determine the stack to use for handling the interrupt. Thus, | ||
| 22 | we must create a TSS and initialize at least these fields, and | ||
| 23 | this is precisely what this file does. | ||
| 24 | |||
| 25 | When an interrupt is handled by an interrupt or trap gate | ||
| 26 | (which applies to all interrupts we handle), an x86 processor | ||
| 27 | works like this: | ||
| 28 | |||
| 29 | - If the code interrupted by the interrupt is in the same | ||
| 30 | ring as the interrupt handler, then no stack switch takes | ||
| 31 | place. This is the case for interrupts that happen when | ||
| 32 | we're running in the kernel. The contents of the TSS are | ||
| 33 | irrelevant for this case. | ||
| 34 | |||
| 35 | - If the interrupted code is in a different ring from the | ||
| 36 | handler, then the processor switches to the stack | ||
| 37 | specified in the TSS for the new ring. This is the case | ||
| 38 | for interrupts that happen when we're in user space. It's | ||
| 39 | important that we switch to a stack that's not already in | ||
| 40 | use, to avoid corruption. Because we're running in user | ||
| 41 | space, we know that the current process's kernel stack is | ||
| 42 | not in use, so we can always use that. Thus, when the | ||
| 43 | scheduler switches threads, it also changes the TSS's | ||
| 44 | stack pointer to point to the new thread's kernel stack. | ||
| 45 | (The call is in thread_schedule_tail() in thread.c.) | ||
| 46 | |||
| 47 | See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a | ||
| 48 | description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or | ||
| 49 | Interrupt-Handler Procedures" for a description of when and | ||
| 50 | how stack switching occurs during an interrupt. */ | ||
| 51 | struct tss | ||
| 52 | { | ||
| 53 | uint16_t back_link, :16; | ||
| 54 | void *esp0; /* Ring 0 stack virtual address. */ | ||
| 55 | uint16_t ss0, :16; /* Ring 0 stack segment selector. */ | ||
| 56 | void *esp1; | ||
| 57 | uint16_t ss1, :16; | ||
| 58 | void *esp2; | ||
| 59 | uint16_t ss2, :16; | ||
| 60 | uint32_t cr3; | ||
| 61 | void (*eip) (void); | ||
| 62 | uint32_t eflags; | ||
| 63 | uint32_t eax, ecx, edx, ebx; | ||
| 64 | uint32_t esp, ebp, esi, edi; | ||
| 65 | uint16_t es, :16; | ||
| 66 | uint16_t cs, :16; | ||
| 67 | uint16_t ss, :16; | ||
| 68 | uint16_t ds, :16; | ||
| 69 | uint16_t fs, :16; | ||
| 70 | uint16_t gs, :16; | ||
| 71 | uint16_t ldt, :16; | ||
| 72 | uint16_t trace, bitmap; | ||
| 73 | }; | ||
| 74 | |||
| 75 | /* Kernel TSS. */ | ||
| 76 | static struct tss *tss; | ||
| 77 | |||
| 78 | /* Initializes the kernel TSS. */ | ||
| 79 | void | ||
| 80 | tss_init (void) | ||
| 81 | { | ||
| 82 | /* Our TSS is never used in a call gate or task gate, so only a | ||
| 83 | few fields of it are ever referenced, and those are the only | ||
| 84 | ones we initialize. */ | ||
| 85 | tss = palloc_get_page (PAL_ASSERT | PAL_ZERO); | ||
| 86 | tss->ss0 = SEL_KDSEG; | ||
| 87 | tss->bitmap = 0xdfff; | ||
| 88 | tss_update (); | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Returns the kernel TSS. */ | ||
| 92 | struct tss * | ||
| 93 | tss_get (void) | ||
| 94 | { | ||
| 95 | ASSERT (tss != NULL); | ||
| 96 | return tss; | ||
| 97 | } | ||
| 98 | |||
| 99 | /* Sets the ring 0 stack pointer in the TSS to point to the end | ||
| 100 | of the thread stack. */ | ||
| 101 | void | ||
| 102 | tss_update (void) | ||
| 103 | { | ||
| 104 | ASSERT (tss != NULL); | ||
| 105 | tss->esp0 = (uint8_t *) thread_current () + PGSIZE; | ||
| 106 | } | ||
diff --git a/pintos-progos/userprog/tss.h b/pintos-progos/userprog/tss.h new file mode 100644 index 0000000..467bd19 --- /dev/null +++ b/pintos-progos/userprog/tss.h | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | #ifndef USERPROG_TSS_H | ||
| 2 | #define USERPROG_TSS_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | struct tss; | ||
| 7 | void tss_init (void); | ||
| 8 | struct tss *tss_get (void); | ||
| 9 | void tss_update (void); | ||
| 10 | |||
| 11 | #endif /* userprog/tss.h */ | ||
diff --git a/pintos-progos/utils/.gitignore b/pintos-progos/utils/.gitignore new file mode 100644 index 0000000..b96f278 --- /dev/null +++ b/pintos-progos/utils/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | setitimer-helper | ||
| 2 | squish-pty | ||
| 3 | squish-unix | ||
diff --git a/pintos-progos/utils/Makefile b/pintos-progos/utils/Makefile new file mode 100644 index 0000000..46a9124 --- /dev/null +++ b/pintos-progos/utils/Makefile | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | all: setitimer-helper squish-pty squish-unix | ||
| 2 | |||
| 3 | CC = gcc | ||
| 4 | CFLAGS = -Wall -W | ||
| 5 | LDFLAGS = -lm | ||
| 6 | setitimer-helper: setitimer-helper.o | ||
| 7 | squish-pty: squish-pty.o | ||
| 8 | squish-unix: squish-unix.o | ||
| 9 | |||
| 10 | clean: | ||
| 11 | rm -f *.o setitimer-helper squish-pty squish-unix | ||
diff --git a/pintos-progos/utils/Pintos.pm b/pintos-progos/utils/Pintos.pm new file mode 100644 index 0000000..70df40d --- /dev/null +++ b/pintos-progos/utils/Pintos.pm | |||
| @@ -0,0 +1,491 @@ | |||
| 1 | # Pintos helper subroutines. | ||
| 2 | |||
| 3 | # Number of bytes available for the loader at the beginning of the MBR. | ||
| 4 | # Kernel command-line arguments follow the loader. | ||
| 5 | our $LOADER_SIZE = 314; | ||
| 6 | |||
| 7 | # Partition types. | ||
| 8 | my (%role2type) = (KERNEL => 0x20, | ||
| 9 | FILESYS => 0x21, | ||
| 10 | SCRATCH => 0x22, | ||
| 11 | SWAP => 0x23); | ||
| 12 | my (%type2role) = reverse %role2type; | ||
| 13 | |||
| 14 | # Order of roles within a given disk. | ||
| 15 | our (@role_order) = qw (KERNEL FILESYS SCRATCH SWAP); | ||
| 16 | |||
| 17 | # Partitions. | ||
| 18 | # | ||
| 19 | # Valid keys are KERNEL, FILESYS, SCRATCH, SWAP. Only those | ||
| 20 | # partitions which are in use are included. | ||
| 21 | # | ||
| 22 | # Each value is a reference to a hash. If the partition's contents | ||
| 23 | # are to be obtained from a file (that will be copied into a new | ||
| 24 | # virtual disk), then the hash contains: | ||
| 25 | # | ||
| 26 | # FILE => name of file from which the partition's contents are copied | ||
| 27 | # (perhaps "/dev/zero"), | ||
| 28 | # OFFSET => offset in bytes in FILE, | ||
| 29 | # BYTES => size in bytes of contents from FILE, | ||
| 30 | # | ||
| 31 | # If the partition is taken from a virtual disk directly, then it | ||
| 32 | # contains the following. The same keys are also filled in once a | ||
| 33 | # file-based partition has been copied into a new virtual disk: | ||
| 34 | # | ||
| 35 | # DISK => name of virtual disk file, | ||
| 36 | # START => sector offset of start of partition within DISK, | ||
| 37 | # SECTORS => number of sectors of partition within DISK, which is usually | ||
| 38 | # greater than round_up (BYTES, 512) due to padding. | ||
| 39 | our (%parts); | ||
| 40 | |||
| 41 | # set_part($opt, $arg) | ||
| 42 | # | ||
| 43 | # For use as a helper function for Getopt::Long::GetOptions to set | ||
| 44 | # disk sources. | ||
| 45 | sub set_part { | ||
| 46 | my ($opt, $arg) = @_; | ||
| 47 | my ($role, $source) = $opt =~ /^([a-z]+)(?:-([a-z]+))?/ or die; | ||
| 48 | |||
| 49 | $role = uc $role; | ||
| 50 | $source = 'FILE' if $source eq ''; | ||
| 51 | |||
| 52 | die "can't have two sources for \L$role\E partition" | ||
| 53 | if exists $parts{$role}; | ||
| 54 | |||
| 55 | do_set_part ($role, $source, $arg); | ||
| 56 | } | ||
| 57 | |||
| 58 | # do_set_part($role, $source, $arg) | ||
| 59 | # | ||
| 60 | # Sets partition $role as coming from $source (one of 'file', 'from', | ||
| 61 | # or 'size'). $arg is a file name for 'file' or 'from', a size in | ||
| 62 | # megabytes for 'size'. | ||
| 63 | sub do_set_part { | ||
| 64 | my ($role, $source, $arg) = @_; | ||
| 65 | |||
| 66 | my ($p) = $parts{$role} = {}; | ||
| 67 | if ($source eq 'file') { | ||
| 68 | if (read_mbr ($arg)) { | ||
| 69 | print STDERR "warning: $arg looks like a partitioned disk "; | ||
| 70 | print STDERR "(did you want --$role-from=$arg or --disk=$arg?)\n" | ||
| 71 | } | ||
| 72 | |||
| 73 | $p->{FILE} = $arg; | ||
| 74 | $p->{OFFSET} = 0; | ||
| 75 | $p->{BYTES} = -s $arg; | ||
| 76 | } elsif ($source eq 'from') { | ||
| 77 | my (%pt) = read_partition_table ($arg); | ||
| 78 | my ($sp) = $pt{$role}; | ||
| 79 | die "$arg: does not contain \L$role\E partition\n" if !defined $sp; | ||
| 80 | |||
| 81 | $p->{FILE} = $arg; | ||
| 82 | $p->{OFFSET} = $sp->{START} * 512; | ||
| 83 | $p->{BYTES} = $sp->{SECTORS} * 512; | ||
| 84 | } elsif ($source eq 'size') { | ||
| 85 | $arg =~ /^\d+(\.\d+)?|\.\d+$/ or die "$arg: not a valid size in MB\n"; | ||
| 86 | |||
| 87 | $p->{FILE} = "/dev/zero"; | ||
| 88 | $p->{OFFSET} = 0; | ||
| 89 | $p->{BYTES} = ceil ($arg * 1024 * 1024); | ||
| 90 | } else { | ||
| 91 | die; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | # set_geometry('HEADS,SPT') | ||
| 96 | # set_geometry('zip') | ||
| 97 | # | ||
| 98 | # For use as a helper function for Getopt::Long::GetOptions to set | ||
| 99 | # disk geometry. | ||
| 100 | sub set_geometry { | ||
| 101 | local ($_) = $_[1]; | ||
| 102 | if ($_ eq 'zip') { | ||
| 103 | @geometry{'H', 'S'} = (64, 32); | ||
| 104 | } else { | ||
| 105 | @geometry{'H', 'S'} = /^(\d+)[,\s]+(\d+)$/ | ||
| 106 | or die "bad syntax for geometry\n"; | ||
| 107 | $geometry{H} <= 255 or die "heads limited to 255\n"; | ||
| 108 | $geometry{S} <= 63 or die "sectors per track limited to 63\n"; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | # set_align('bochs|full|none') | ||
| 113 | # | ||
| 114 | # For use as a helper function for Getopt::Long::GetOptions to set | ||
| 115 | # partition alignment. | ||
| 116 | sub set_align { | ||
| 117 | $align = $_[1]; | ||
| 118 | die "unknown alignment type \"$align\"\n" | ||
| 119 | if $align ne 'bochs' && $align ne 'full' && $align ne 'none'; | ||
| 120 | } | ||
| 121 | |||
| 122 | # assemble_disk(%args) | ||
| 123 | # | ||
| 124 | # Creates a virtual disk $args{DISK} containing the partitions | ||
| 125 | # described by @args{KERNEL, FILESYS, SCRATCH, SWAP}. | ||
| 126 | # | ||
| 127 | # Required arguments: | ||
| 128 | # DISK => output disk file name | ||
| 129 | # HANDLE => output file handle (will be closed) | ||
| 130 | # | ||
| 131 | # Normally at least one of the following is included: | ||
| 132 | # KERNEL, FILESYS, SCRATCH, SWAP => {input: | ||
| 133 | # FILE => file to read, | ||
| 134 | # OFFSET => byte offset in file, | ||
| 135 | # BYTES => byte count from file, | ||
| 136 | # | ||
| 137 | # output: | ||
| 138 | # DISK => output disk file name, | ||
| 139 | # START => sector offset in DISK, | ||
| 140 | # SECTORS => sector count in DISK}, | ||
| 141 | # | ||
| 142 | # Optional arguments: | ||
| 143 | # ALIGN => 'bochs' (default), 'full', or 'none' | ||
| 144 | # GEOMETRY => {H => heads, S => sectors per track} (default 16, 63) | ||
| 145 | # FORMAT => 'partitioned' (default) or 'raw' | ||
| 146 | # LOADER => $LOADER_SIZE-byte string containing the loader binary | ||
| 147 | # ARGS => ['arg 1', 'arg 2', ...] | ||
| 148 | sub assemble_disk { | ||
| 149 | my (%args) = @_; | ||
| 150 | |||
| 151 | my (%geometry) = $args{GEOMETRY} || (H => 16, S => 63); | ||
| 152 | |||
| 153 | my ($align); # Align partition start, end to cylinder boundary? | ||
| 154 | my ($pad); # Pad end of disk out to cylinder boundary? | ||
| 155 | if (!defined ($args{ALIGN}) || $args{ALIGN} eq 'bochs') { | ||
| 156 | $align = 0; | ||
| 157 | $pad = 1; | ||
| 158 | } elsif ($args{ALIGN} eq 'full') { | ||
| 159 | $align = 1; | ||
| 160 | $pad = 0; | ||
| 161 | } elsif ($args{ALIGN} eq 'none') { | ||
| 162 | $align = $pad = 0; | ||
| 163 | } else { | ||
| 164 | die; | ||
| 165 | } | ||
| 166 | |||
| 167 | my ($format) = $args{FORMAT} || 'partitioned'; | ||
| 168 | die if $format ne 'partitioned' && $format ne 'raw'; | ||
| 169 | |||
| 170 | # Check that we have apartitions to copy in. | ||
| 171 | my $part_cnt = grep (defined ($args{$_}), keys %role2type); | ||
| 172 | die "must have exactly one partition for raw output\n" | ||
| 173 | if $format eq 'raw' && $part_cnt != 1; | ||
| 174 | |||
| 175 | # Calculate the disk size. | ||
| 176 | my ($total_sectors) = 0; | ||
| 177 | if ($format eq 'partitioned') { | ||
| 178 | $total_sectors += $align ? $geometry{S} : 1; | ||
| 179 | } | ||
| 180 | for my $role (@role_order) { | ||
| 181 | my ($p) = $args{$role}; | ||
| 182 | next if !defined $p; | ||
| 183 | |||
| 184 | die if $p->{DISK}; | ||
| 185 | |||
| 186 | my ($bytes) = $p->{BYTES}; | ||
| 187 | my ($start) = $total_sectors; | ||
| 188 | my ($end) = $start + div_round_up ($bytes, 512); | ||
| 189 | $end = round_up ($end, cyl_sectors (%geometry)) if $align; | ||
| 190 | |||
| 191 | $p->{DISK} = $args{DISK}; | ||
| 192 | $p->{START} = $start; | ||
| 193 | $p->{SECTORS} = $end - $start; | ||
| 194 | $total_sectors = $end; | ||
| 195 | } | ||
| 196 | |||
| 197 | # Write the disk. | ||
| 198 | my ($disk_fn) = $args{DISK}; | ||
| 199 | my ($disk) = $args{HANDLE}; | ||
| 200 | if ($format eq 'partitioned') { | ||
| 201 | # Pack loader into MBR. | ||
| 202 | my ($loader) = $args{LOADER} || "\xcd\x18"; | ||
| 203 | my ($mbr) = pack ("a$LOADER_SIZE", $loader); | ||
| 204 | |||
| 205 | $mbr .= make_kernel_command_line (@{$args{ARGS}}); | ||
| 206 | |||
| 207 | # Pack partition table into MBR. | ||
| 208 | $mbr .= make_partition_table (\%geometry, \%args); | ||
| 209 | |||
| 210 | # Add signature to MBR. | ||
| 211 | $mbr .= pack ("v", 0xaa55); | ||
| 212 | |||
| 213 | die if length ($mbr) != 512; | ||
| 214 | write_fully ($disk, $disk_fn, $mbr); | ||
| 215 | write_zeros ($disk, $disk_fn, 512 * ($geometry{S} - 1)) if $align; | ||
| 216 | } | ||
| 217 | for my $role (@role_order) { | ||
| 218 | my ($p) = $args{$role}; | ||
| 219 | next if !defined $p; | ||
| 220 | |||
| 221 | my ($source); | ||
| 222 | my ($fn) = $p->{FILE}; | ||
| 223 | open ($source, '<', $fn) or die "$fn: open: $!\n"; | ||
| 224 | if ($p->{OFFSET}) { | ||
| 225 | sysseek ($source, $p->{OFFSET}, 0) == $p->{OFFSET} | ||
| 226 | or die "$fn: seek: $!\n"; | ||
| 227 | } | ||
| 228 | copy_file ($source, $fn, $disk, $disk_fn, $p->{BYTES}); | ||
| 229 | close ($source) or die "$fn: close: $!\n"; | ||
| 230 | |||
| 231 | write_zeros ($disk, $disk_fn, $p->{SECTORS} * 512 - $p->{BYTES}); | ||
| 232 | } | ||
| 233 | if ($pad) { | ||
| 234 | my ($pad_sectors) = round_up ($total_sectors, cyl_sectors (%geometry)); | ||
| 235 | write_zeros ($disk, $disk_fn, ($pad_sectors - $total_sectors) * 512); | ||
| 236 | } | ||
| 237 | close ($disk) or die "$disk: close: $!\n"; | ||
| 238 | } | ||
| 239 | |||
| 240 | # make_partition_table({H => heads, S => sectors}, {KERNEL => ..., ...}) | ||
| 241 | # | ||
| 242 | # Creates and returns a partition table for the given partitions and | ||
| 243 | # disk geometry. | ||
| 244 | sub make_partition_table { | ||
| 245 | my ($geometry, $partitions) = @_; | ||
| 246 | my ($table) = ''; | ||
| 247 | for my $role (@role_order) { | ||
| 248 | defined (my $p = $partitions->{$role}) or next; | ||
| 249 | |||
| 250 | my $end = $p->{START} + $p->{SECTORS} - 1; | ||
| 251 | my $bootable = $role eq 'KERNEL'; | ||
| 252 | |||
| 253 | $table .= pack ("C", $bootable ? 0x80 : 0); # Bootable? | ||
| 254 | $table .= pack_chs ($p->{START}, $geometry); # CHS of partition start | ||
| 255 | $table .= pack ("C", $role2type{$role}); # Partition type | ||
| 256 | $table .= pack_chs($end, $geometry); # CHS of partition end | ||
| 257 | $table .= pack ("V", $p->{START}); # LBA of partition start | ||
| 258 | $table .= pack ("V", $p->{SECTORS}); # Length in sectors | ||
| 259 | die if length ($table) % 16; | ||
| 260 | } | ||
| 261 | return pack ("a64", $table); | ||
| 262 | } | ||
| 263 | |||
| 264 | # make_kernel_command_line(@args) | ||
| 265 | # | ||
| 266 | # Returns the raw bytes to write to an MBR at offset $LOADER_SIZE to | ||
| 267 | # set a Pintos kernel command line. | ||
| 268 | sub make_kernel_command_line { | ||
| 269 | my (@args) = @_; | ||
| 270 | my ($args) = join ('', map ("$_\0", @args)); | ||
| 271 | die "command line exceeds 128 bytes" if length ($args) > 128; | ||
| 272 | return pack ("V a128", scalar (@args), $args); | ||
| 273 | } | ||
| 274 | |||
| 275 | # copy_file($from_handle, $from_file_name, $to_handle, $to_file_name, $size) | ||
| 276 | # | ||
| 277 | # Copies $size bytes from $from_handle to $to_handle. | ||
| 278 | # $from_file_name and $to_file_name are used in error messages. | ||
| 279 | sub copy_file { | ||
| 280 | my ($from_handle, $from_file_name, $to_handle, $to_file_name, $size) = @_; | ||
| 281 | |||
| 282 | while ($size > 0) { | ||
| 283 | my ($chunk_size) = 4096; | ||
| 284 | $chunk_size = $size if $chunk_size > $size; | ||
| 285 | $size -= $chunk_size; | ||
| 286 | |||
| 287 | my ($data) = read_fully ($from_handle, $from_file_name, $chunk_size); | ||
| 288 | write_fully ($to_handle, $to_file_name, $data); | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | # read_fully($handle, $file_name, $bytes) | ||
| 293 | # | ||
| 294 | # Reads exactly $bytes bytes from $handle and returns the data read. | ||
| 295 | # $file_name is used in error messages. | ||
| 296 | sub read_fully { | ||
| 297 | my ($handle, $file_name, $bytes) = @_; | ||
| 298 | my ($data); | ||
| 299 | my ($read_bytes) = sysread ($handle, $data, $bytes); | ||
| 300 | die "$file_name: read: $!\n" if !defined $read_bytes; | ||
| 301 | die "$file_name: unexpected end of file\n" if $read_bytes != $bytes; | ||
| 302 | return $data; | ||
| 303 | } | ||
| 304 | |||
| 305 | # write_fully($handle, $file_name, $data) | ||
| 306 | # | ||
| 307 | # Write $data to $handle. | ||
| 308 | # $file_name is used in error messages. | ||
| 309 | sub write_fully { | ||
| 310 | my ($handle, $file_name, $data) = @_; | ||
| 311 | my ($written_bytes) = syswrite ($handle, $data); | ||
| 312 | die "$file_name: write: $!\n" if !defined $written_bytes; | ||
| 313 | die "$file_name: short write\n" if $written_bytes != length $data; | ||
| 314 | } | ||
| 315 | |||
| 316 | sub write_zeros { | ||
| 317 | my ($handle, $file_name, $size) = @_; | ||
| 318 | |||
| 319 | while ($size > 0) { | ||
| 320 | my ($chunk_size) = 4096; | ||
| 321 | $chunk_size = $size if $chunk_size > $size; | ||
| 322 | $size -= $chunk_size; | ||
| 323 | |||
| 324 | write_fully ($handle, $file_name, "\0" x $chunk_size); | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | # div_round_up($x,$y) | ||
| 329 | # | ||
| 330 | # Returns $x / $y, rounded up to the nearest integer. | ||
| 331 | # $y must be an integer. | ||
| 332 | sub div_round_up { | ||
| 333 | my ($x, $y) = @_; | ||
| 334 | return int ((ceil ($x) + $y - 1) / $y); | ||
| 335 | } | ||
| 336 | |||
| 337 | # round_up($x, $y) | ||
| 338 | # | ||
| 339 | # Returns $x rounded up to the nearest multiple of $y. | ||
| 340 | # $y must be an integer. | ||
| 341 | sub round_up { | ||
| 342 | my ($x, $y) = @_; | ||
| 343 | return div_round_up ($x, $y) * $y; | ||
| 344 | } | ||
| 345 | |||
| 346 | # cyl_sectors(H => heads, S => sectors) | ||
| 347 | # | ||
| 348 | # Returns the number of sectors in a cylinder of a disk with the given | ||
| 349 | # geometry. | ||
| 350 | sub cyl_sectors { | ||
| 351 | my (%geometry) = @_; | ||
| 352 | return $geometry{H} * $geometry{S}; | ||
| 353 | } | ||
| 354 | |||
| 355 | # read_loader($file_name) | ||
| 356 | # | ||
| 357 | # Reads and returns the first $LOADER_SIZE bytes in $file_name. | ||
| 358 | # If $file_name is undefined, tries to find the default loader. | ||
| 359 | # Makes sure that the loader is a reasonable size. | ||
| 360 | sub read_loader { | ||
| 361 | my ($name) = @_; | ||
| 362 | $name = find_file ("loader.bin") if !defined $name; | ||
| 363 | die "Cannot find loader\n" if !defined $name; | ||
| 364 | |||
| 365 | my ($handle); | ||
| 366 | open ($handle, '<', $name) or die "$name: open: $!\n"; | ||
| 367 | -s $handle == $LOADER_SIZE || -s $handle == 512 | ||
| 368 | or die "$name: must be exactly $LOADER_SIZE or 512 bytes long\n"; | ||
| 369 | $loader = read_fully ($handle, $name, $LOADER_SIZE); | ||
| 370 | close ($handle) or die "$name: close: $!\n"; | ||
| 371 | return $loader; | ||
| 372 | } | ||
| 373 | |||
| 374 | # pack_chs($lba, {H => heads, S => sectors}) | ||
| 375 | # | ||
| 376 | # Converts logical sector $lba to a 3-byte packed geometrical sector | ||
| 377 | # in the format used in PC partition tables (see [Partitions]) and | ||
| 378 | # returns the geometrical sector as a 3-byte string. | ||
| 379 | sub pack_chs { | ||
| 380 | my ($lba, $geometry) = @_; | ||
| 381 | my ($cyl, $head, $sect) = lba_to_chs ($lba, $geometry); | ||
| 382 | return pack ("CCC", $head, $sect | (($cyl >> 2) & 0xc0), $cyl & 0xff); | ||
| 383 | } | ||
| 384 | |||
| 385 | # lba_to_chs($lba, {H => heads, S => sectors}) | ||
| 386 | # | ||
| 387 | # Returns the geometrical sector corresponding to logical sector $lba | ||
| 388 | # given the specified geometry. | ||
| 389 | sub lba_to_chs { | ||
| 390 | my ($lba, $geometry) = @_; | ||
| 391 | my ($hpc) = $geometry->{H}; | ||
| 392 | my ($spt) = $geometry->{S}; | ||
| 393 | |||
| 394 | # Source: | ||
| 395 | # http://en.wikipedia.org/wiki/CHS_conversion | ||
| 396 | use integer; | ||
| 397 | my $cyl = $lba / ($hpc * $spt); | ||
| 398 | my $temp = $lba % ($hpc * $spt); | ||
| 399 | my $head = $temp / $spt; | ||
| 400 | my $sect = $temp % $spt + 1; | ||
| 401 | |||
| 402 | # Source: | ||
| 403 | # http://www.cgsecurity.org/wiki/Intel_Partition_Table | ||
| 404 | if ($cyl <= 1023) { | ||
| 405 | return ($cyl, $head, $sect); | ||
| 406 | } else { | ||
| 407 | return (1023, 254, 63); ## or should this be (1023, $hpc, $spt)? | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | # read_mbr($file) | ||
| 412 | # | ||
| 413 | # Tries to read an MBR from $file. Returns the 512-byte MBR if | ||
| 414 | # successful, otherwise numeric 0. | ||
| 415 | sub read_mbr { | ||
| 416 | my ($file) = @_; | ||
| 417 | my ($retval) = 0; | ||
| 418 | open (FILE, '<', $file) or die "$file: open: $!\n"; | ||
| 419 | if (-s FILE == 0) { | ||
| 420 | die "$file: file has zero size\n"; | ||
| 421 | } elsif (-s FILE >= 512) { | ||
| 422 | my ($mbr); | ||
| 423 | sysread (FILE, $mbr, 512) == 512 or die "$file: read: $!\n"; | ||
| 424 | $retval = $mbr if unpack ("v", substr ($mbr, 510)) == 0xaa55; | ||
| 425 | } | ||
| 426 | close (FILE); | ||
| 427 | return $retval; | ||
| 428 | } | ||
| 429 | |||
| 430 | # interpret_partition_table($mbr, $disk) | ||
| 431 | # | ||
| 432 | # Parses the partition-table in the specified 512-byte $mbr and | ||
| 433 | # returns the partitions. $disk is used for error messages. | ||
| 434 | sub interpret_partition_table { | ||
| 435 | my ($mbr, $disk) = @_; | ||
| 436 | my (%parts); | ||
| 437 | for my $i (0...3) { | ||
| 438 | my ($bootable, $valid, $type, $lba_start, $lba_length) | ||
| 439 | = unpack ("C X V C x3 V V", substr ($mbr, 446 + 16 * $i, 16)); | ||
| 440 | next if !$valid; | ||
| 441 | |||
| 442 | (print STDERR "warning: invalid partition entry $i in $disk\n"), | ||
| 443 | next if $bootable != 0 && $bootable != 0x80; | ||
| 444 | |||
| 445 | my ($role) = $type2role{$type}; | ||
| 446 | (printf STDERR "warning: non-Pintos partition type 0x%02x in %s\n", | ||
| 447 | $type, $disk), | ||
| 448 | next if !defined $role; | ||
| 449 | |||
| 450 | (print STDERR "warning: duplicate \L$role\E partition in $disk\n"), | ||
| 451 | next if exists $parts{$role}; | ||
| 452 | |||
| 453 | $parts{$role} = {START => $lba_start, | ||
| 454 | SECTORS => $lba_length}; | ||
| 455 | } | ||
| 456 | return %parts; | ||
| 457 | } | ||
| 458 | |||
| 459 | # find_file($base_name) | ||
| 460 | # | ||
| 461 | # Looks for a file named $base_name in a couple of likely spots. If | ||
| 462 | # found, returns the name; otherwise, returns undef. | ||
| 463 | sub find_file { | ||
| 464 | my ($base_name) = @_; | ||
| 465 | -e && return $_ foreach $base_name, "build/$base_name"; | ||
| 466 | return undef; | ||
| 467 | } | ||
| 468 | |||
| 469 | # read_partition_table($file) | ||
| 470 | # | ||
| 471 | # Reads a partition table from $file and returns the parsed | ||
| 472 | # partitions. Dies if partitions can't be read. | ||
| 473 | sub read_partition_table { | ||
| 474 | my ($file) = @_; | ||
| 475 | my ($mbr) = read_mbr ($file); | ||
| 476 | die "$file: not a partitioned disk\n" if !$mbr; | ||
| 477 | return interpret_partition_table ($mbr, $file); | ||
| 478 | } | ||
| 479 | |||
| 480 | # max(@args) | ||
| 481 | # | ||
| 482 | # Returns the numerically largest value in @args. | ||
| 483 | sub max { | ||
| 484 | my ($max) = $_[0]; | ||
| 485 | foreach (@_[1..$#_]) { | ||
| 486 | $max = $_ if $_ > $max; | ||
| 487 | } | ||
| 488 | return $max; | ||
| 489 | } | ||
| 490 | |||
| 491 | 1; | ||
diff --git a/pintos-progos/utils/backtrace b/pintos-progos/utils/backtrace new file mode 100755 index 0000000..95e422f --- /dev/null +++ b/pintos-progos/utils/backtrace | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | #! /usr/bin/perl -w | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | |||
| 5 | # Check command line. | ||
| 6 | if (grep ($_ eq '-h' || $_ eq '--help', @ARGV)) { | ||
| 7 | print <<'EOF'; | ||
| 8 | backtrace, for converting raw addresses into symbolic backtraces | ||
| 9 | usage: backtrace [BINARY]... ADDRESS... | ||
| 10 | where BINARY is the binary file or files from which to obtain symbols | ||
| 11 | and ADDRESS is a raw address to convert to a symbol name. | ||
| 12 | |||
| 13 | If no BINARY is unspecified, the default is the first of kernel.o or | ||
| 14 | build/kernel.o that exists. If multiple binaries are specified, each | ||
| 15 | symbol printed is from the first binary that contains a match. | ||
| 16 | |||
| 17 | The ADDRESS list should be taken from the "Call stack:" printed by the | ||
| 18 | kernel. Read "Backtraces" in the "Debugging Tools" chapter of the | ||
| 19 | Pintos documentation for more information. | ||
| 20 | EOF | ||
| 21 | exit 0; | ||
| 22 | } | ||
| 23 | die "backtrace: at least one argument required (use --help for help)\n" | ||
| 24 | if @ARGV == 0; | ||
| 25 | |||
| 26 | # Drop garbage inserted by kernel. | ||
| 27 | @ARGV = grep (!/^(call|stack:?|[-+])$/i, @ARGV); | ||
| 28 | s/\.$// foreach @ARGV; | ||
| 29 | |||
| 30 | # Find binaries. | ||
| 31 | my (@binaries); | ||
| 32 | while ($ARGV[0] !~ /^0x/) { | ||
| 33 | my ($bin) = shift @ARGV; | ||
| 34 | die "backtrace: $bin: not found (use --help for help)\n" if ! -e $bin; | ||
| 35 | push (@binaries, $bin); | ||
| 36 | } | ||
| 37 | if (!@binaries) { | ||
| 38 | my ($bin); | ||
| 39 | if (-e 'kernel.o') { | ||
| 40 | $bin = 'kernel.o'; | ||
| 41 | } elsif (-e 'build/kernel.o') { | ||
| 42 | $bin = 'build/kernel.o'; | ||
| 43 | } else { | ||
| 44 | die "backtrace: no binary specified and neither \"kernel.o\" nor \"build/kernel.o\" exists (use --help for help)\n"; | ||
| 45 | } | ||
| 46 | push (@binaries, $bin); | ||
| 47 | } | ||
| 48 | |||
| 49 | # Find addr2line. | ||
| 50 | my ($a2l) = search_path ("i386-elf-addr2line") || search_path ("addr2line"); | ||
| 51 | if (!$a2l) { | ||
| 52 | die "backtrace: neither `i386-elf-addr2line' nor `addr2line' in PATH\n"; | ||
| 53 | } | ||
| 54 | sub search_path { | ||
| 55 | my ($target) = @_; | ||
| 56 | for my $dir (split (':', $ENV{PATH})) { | ||
| 57 | my ($file) = "$dir/$target"; | ||
| 58 | return $file if -e $file; | ||
| 59 | } | ||
| 60 | return undef; | ||
| 61 | } | ||
| 62 | |||
| 63 | # Figure out backtrace. | ||
| 64 | my (@locs) = map ({ADDR => $_}, @ARGV); | ||
| 65 | for my $bin (@binaries) { | ||
| 66 | open (A2L, "$a2l -fe $bin " . join (' ', map ($_->{ADDR}, @locs)) . "|"); | ||
| 67 | for (my ($i) = 0; <A2L>; $i++) { | ||
| 68 | my ($function, $line); | ||
| 69 | chomp ($function = $_); | ||
| 70 | chomp ($line = <A2L>); | ||
| 71 | next if defined $locs[$i]{BINARY}; | ||
| 72 | |||
| 73 | if ($function ne '??' || $line ne '??:0') { | ||
| 74 | $locs[$i]{FUNCTION} = $function; | ||
| 75 | $locs[$i]{LINE} = $line; | ||
| 76 | $locs[$i]{BINARY} = $bin; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | close (A2L); | ||
| 80 | } | ||
| 81 | |||
| 82 | # Print backtrace. | ||
| 83 | my ($cur_binary); | ||
| 84 | for my $loc (@locs) { | ||
| 85 | if (defined ($loc->{BINARY}) | ||
| 86 | && @binaries > 1 | ||
| 87 | && (!defined ($cur_binary) || $loc->{BINARY} ne $cur_binary)) { | ||
| 88 | $cur_binary = $loc->{BINARY}; | ||
| 89 | print "In $cur_binary:\n"; | ||
| 90 | } | ||
| 91 | |||
| 92 | my ($addr) = $loc->{ADDR}; | ||
| 93 | $addr = sprintf ("0x%08x", hex ($addr)) if $addr =~ /^0x[0-9a-f]+$/i; | ||
| 94 | |||
| 95 | print $addr, ": "; | ||
| 96 | if (defined ($loc->{BINARY})) { | ||
| 97 | my ($function) = $loc->{FUNCTION}; | ||
| 98 | my ($line) = $loc->{LINE}; | ||
| 99 | $line =~ s/^(\.\.\/)*//; | ||
| 100 | $line = "..." . substr ($line, -25) if length ($line) > 28; | ||
| 101 | print "$function ($line)"; | ||
| 102 | } else { | ||
| 103 | print "(unknown)"; | ||
| 104 | } | ||
| 105 | print "\n"; | ||
| 106 | } | ||
diff --git a/pintos-progos/utils/pintos b/pintos-progos/utils/pintos new file mode 100755 index 0000000..91f73ad --- /dev/null +++ b/pintos-progos/utils/pintos | |||
| @@ -0,0 +1,955 @@ | |||
| 1 | #! /usr/bin/perl -w | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use POSIX; | ||
| 5 | use Fcntl; | ||
| 6 | use File::Temp 'tempfile'; | ||
| 7 | use Getopt::Long qw(:config bundling); | ||
| 8 | use Fcntl qw(SEEK_SET SEEK_CUR); | ||
| 9 | |||
| 10 | # Read Pintos.pm from the same directory as this program. | ||
| 11 | BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; } | ||
| 12 | |||
| 13 | # Command-line options. | ||
| 14 | our ($start_time) = time (); | ||
| 15 | our ($sim); # Simulator: bochs, qemu, or player. | ||
| 16 | our ($debug) = "none"; # Debugger: none, monitor, or gdb. | ||
| 17 | our ($mem) = 4; # Physical RAM in MB. | ||
| 18 | our ($serial) = 1; # Use serial port for input and output? | ||
| 19 | our ($vga); # VGA output: window, terminal, or none. | ||
| 20 | our ($jitter); # Seed for random timer interrupts, if set. | ||
| 21 | our ($realtime); # Synchronize timer interrupts with real time? | ||
| 22 | our ($timeout); # Maximum runtime in seconds, if set. | ||
| 23 | our ($kill_on_failure); # Abort quickly on test failure? | ||
| 24 | our ($kernel_test); # Run kernel test instead of user program | ||
| 25 | our (@puts); # Files to copy into the VM. | ||
| 26 | our (@gets); # Files to copy out of the VM. | ||
| 27 | our ($as_ref); # Reference to last addition to @gets or @puts. | ||
| 28 | our (@kernel_args); # Arguments to pass to kernel. | ||
| 29 | our (%parts); # Partitions. | ||
| 30 | our ($make_disk); # Name of disk to create. | ||
| 31 | our ($tmp_disk) = 1; # Delete $make_disk after run? | ||
| 32 | our (@disks); # Extra disk images to pass to simulator. | ||
| 33 | our ($loader_fn); # Bootstrap loader. | ||
| 34 | our (%geometry); # IDE disk geometry. | ||
| 35 | our ($align); # Partition alignment. | ||
| 36 | |||
| 37 | parse_command_line (); | ||
| 38 | prepare_scratch_disk (); | ||
| 39 | find_disks (); | ||
| 40 | run_vm (); | ||
| 41 | finish_scratch_disk (); | ||
| 42 | |||
| 43 | exit 0; | ||
| 44 | |||
| 45 | # Parses the command line. | ||
| 46 | sub parse_command_line { | ||
| 47 | usage (0) if @ARGV == 0 || (@ARGV == 1 && $ARGV[0] eq '--help'); | ||
| 48 | |||
| 49 | @kernel_args = @ARGV; | ||
| 50 | if (grep ($_ eq '--', @kernel_args)) { | ||
| 51 | @ARGV = (); | ||
| 52 | while ((my $arg = shift (@kernel_args)) ne '--') { | ||
| 53 | push (@ARGV, $arg); | ||
| 54 | } | ||
| 55 | GetOptions ("sim=s" => sub { set_sim ($_[1]) }, | ||
| 56 | "bochs" => sub { set_sim ("bochs") }, | ||
| 57 | "qemu" => sub { set_sim ("qemu") }, | ||
| 58 | "player" => sub { set_sim ("player") }, | ||
| 59 | |||
| 60 | "debug=s" => sub { set_debug ($_[1]) }, | ||
| 61 | "no-debug" => sub { set_debug ("none") }, | ||
| 62 | "monitor" => sub { set_debug ("monitor") }, | ||
| 63 | "gdb" => sub { set_debug ("gdb") }, | ||
| 64 | |||
| 65 | "m|memory=i" => \$mem, | ||
| 66 | "j|jitter=i" => sub { set_jitter ($_[1]) }, | ||
| 67 | "r|realtime" => sub { set_realtime () }, | ||
| 68 | |||
| 69 | "T|timeout=i" => \$timeout, | ||
| 70 | "k|kill-on-failure" => \$kill_on_failure, | ||
| 71 | |||
| 72 | "v|no-vga" => sub { set_vga ('none'); }, | ||
| 73 | "s|no-serial" => sub { $serial = 0; }, | ||
| 74 | "t|terminal" => sub { set_vga ('terminal'); }, | ||
| 75 | |||
| 76 | "kernel-test" => sub { set_kernel_test(); }, | ||
| 77 | "p|put-file=s" => sub { add_file (\@puts, $_[1]); }, | ||
| 78 | "g|get-file=s" => sub { add_file (\@gets, $_[1]); }, | ||
| 79 | "a|as=s" => sub { set_as ($_[1]); }, | ||
| 80 | |||
| 81 | "h|help" => sub { usage (0); }, | ||
| 82 | |||
| 83 | "kernel=s" => \&set_part, | ||
| 84 | "filesys=s" => \&set_part, | ||
| 85 | "swap=s" => \&set_part, | ||
| 86 | |||
| 87 | "filesys-size=s" => \&set_part, | ||
| 88 | "scratch-size=s" => \&set_part, | ||
| 89 | "swap-size=s" => \&set_part, | ||
| 90 | |||
| 91 | "kernel-from=s" => \&set_part, | ||
| 92 | "filesys-from=s" => \&set_part, | ||
| 93 | "swap-from=s" => \&set_part, | ||
| 94 | |||
| 95 | "make-disk=s" => sub { $make_disk = $_[1]; | ||
| 96 | $tmp_disk = 0; }, | ||
| 97 | "disk=s" => sub { set_disk ($_[1]); }, | ||
| 98 | "loader=s" => \$loader_fn, | ||
| 99 | |||
| 100 | "geometry=s" => \&set_geometry, | ||
| 101 | "align=s" => \&set_align) | ||
| 102 | or exit 1; | ||
| 103 | } | ||
| 104 | |||
| 105 | $sim = "bochs" if !defined $sim; | ||
| 106 | $debug = "none" if !defined $debug; | ||
| 107 | $vga = exists ($ENV{DISPLAY}) ? "window" : "none" if !defined $vga; | ||
| 108 | |||
| 109 | undef $timeout, print "warning: disabling timeout with --$debug\n" | ||
| 110 | if defined ($timeout) && $debug ne 'none'; | ||
| 111 | |||
| 112 | print "warning: enabling serial port for -k or --kill-on-failure\n" | ||
| 113 | if $kill_on_failure && !$serial; | ||
| 114 | |||
| 115 | $align = "bochs", | ||
| 116 | print STDERR "warning: setting --align=bochs for Bochs support\n" | ||
| 117 | if $sim eq 'bochs' && defined ($align) && $align eq 'none'; | ||
| 118 | } | ||
| 119 | |||
| 120 | # usage($exitcode). | ||
| 121 | # Prints a usage message and exits with $exitcode. | ||
| 122 | sub usage { | ||
| 123 | my ($exitcode) = @_; | ||
| 124 | $exitcode = 1 unless defined $exitcode; | ||
| 125 | print <<'EOF'; | ||
| 126 | pintos, a utility for running Pintos in a simulator | ||
| 127 | Usage: pintos [OPTION...] -- [ARGUMENT...] | ||
| 128 | where each OPTION is one of the following options | ||
| 129 | and each ARGUMENT is passed to Pintos kernel verbatim. | ||
| 130 | Simulator selection: | ||
| 131 | --bochs (default) Use Bochs as simulator | ||
| 132 | --qemu Use QEMU as simulator | ||
| 133 | --player Use VMware Player as simulator | ||
| 134 | Debugger selection: | ||
| 135 | --no-debug (default) No debugger | ||
| 136 | --monitor Debug with simulator's monitor | ||
| 137 | --gdb Debug with gdb | ||
| 138 | Display options: (default is both VGA and serial) | ||
| 139 | -v, --no-vga No VGA display or keyboard | ||
| 140 | -s, --no-serial No serial input or output | ||
| 141 | -t, --terminal Display VGA in terminal (Bochs only) | ||
| 142 | Timing options: (Bochs only) | ||
| 143 | -j SEED Randomize timer interrupts | ||
| 144 | -r, --realtime Use realistic, not reproducible, timings | ||
| 145 | Testing options: | ||
| 146 | -T, --timeout=N Kill Pintos after N seconds CPU time or N*load_avg | ||
| 147 | seconds wall-clock time (whichever comes first) | ||
| 148 | -k, --kill-on-failure Kill Pintos a few seconds after a kernel or user | ||
| 149 | panic, test failure, or triple fault | ||
| 150 | --kernel-test Run kernel test, even though user programs are | ||
| 151 | enabled. | ||
| 152 | Configuration options: | ||
| 153 | -m, --mem=N Give Pintos N MB physical RAM (default: 4) | ||
| 154 | File system commands: | ||
| 155 | -p, --put-file=HOSTFN Copy HOSTFN into VM, by default under same name | ||
| 156 | -g, --get-file=GUESTFN Copy GUESTFN out of VM, by default under same name | ||
| 157 | -a, --as=FILENAME Specifies guest (for -p) or host (for -g) file name | ||
| 158 | Partition options: (where PARTITION is one of: kernel filesys scratch swap) | ||
| 159 | --PARTITION=FILE Use a copy of FILE for the given PARTITION | ||
| 160 | --PARTITION-size=SIZE Create an empty PARTITION of the given SIZE in MB | ||
| 161 | --PARTITION-from=DISK Use of a copy of the given PARTITION in DISK | ||
| 162 | (There is no --kernel-size, --scratch, or --scratch-from option.) | ||
| 163 | Disk configuration options: | ||
| 164 | --make-disk=DISK Name the new DISK and don't delete it after the run | ||
| 165 | --disk=DISK Also use existing DISK (may be used multiple times) | ||
| 166 | Advanced disk configuration options: | ||
| 167 | --loader=FILE Use FILE as bootstrap loader (default: loader.bin) | ||
| 168 | --geometry=H,S Use H head, S sector geometry (default: 16,63) | ||
| 169 | --geometry=zip Use 64 head, 32 sector geometry for USB-ZIP boot | ||
| 170 | (see http://syslinux.zytor.com/usbkey.php) | ||
| 171 | --align=bochs Pad out disk to cylinder to support Bochs (default) | ||
| 172 | --align=full Align partition boundaries to cylinder boundary to | ||
| 173 | let fdisk guess correct geometry and quiet warnings | ||
| 174 | --align=none Don't align partitions at all, to save space | ||
| 175 | Other options: | ||
| 176 | -h, --help Display this help message. | ||
| 177 | EOF | ||
| 178 | exit $exitcode; | ||
| 179 | } | ||
| 180 | |||
| 181 | # Sets the simulator. | ||
| 182 | sub set_sim { | ||
| 183 | my ($new_sim) = @_; | ||
| 184 | die "--$new_sim conflicts with --$sim\n" | ||
| 185 | if defined ($sim) && $sim ne $new_sim; | ||
| 186 | $sim = $new_sim; | ||
| 187 | } | ||
| 188 | |||
| 189 | # Sets the debugger. | ||
| 190 | sub set_debug { | ||
| 191 | my ($new_debug) = @_; | ||
| 192 | die "--$new_debug conflicts with --$debug\n" | ||
| 193 | if $debug ne 'none' && $new_debug ne 'none' && $debug ne $new_debug; | ||
| 194 | $debug = $new_debug; | ||
| 195 | } | ||
| 196 | |||
| 197 | # Sets VGA output destination. | ||
| 198 | sub set_vga { | ||
| 199 | my ($new_vga) = @_; | ||
| 200 | if (defined ($vga) && $vga ne $new_vga) { | ||
| 201 | print "warning: conflicting vga display options\n"; | ||
| 202 | } | ||
| 203 | $vga = $new_vga; | ||
| 204 | } | ||
| 205 | |||
| 206 | # Sets randomized timer interrupts. | ||
| 207 | sub set_jitter { | ||
| 208 | my ($new_jitter) = @_; | ||
| 209 | die "--realtime conflicts with --jitter\n" if defined $realtime; | ||
| 210 | die "different --jitter already defined\n" | ||
| 211 | if defined $jitter && $jitter != $new_jitter; | ||
| 212 | $jitter = $new_jitter; | ||
| 213 | } | ||
| 214 | |||
| 215 | # Sets real-time timer interrupts. | ||
| 216 | sub set_realtime { | ||
| 217 | die "--realtime conflicts with --jitter\n" if defined $jitter; | ||
| 218 | $realtime = 1; | ||
| 219 | } | ||
| 220 | |||
| 221 | # Sets load to run kernel test instead of user program. | ||
| 222 | # If user programs are disabled, pintos always runs a kernel test. | ||
| 223 | sub set_kernel_test { | ||
| 224 | $kernel_test = 1; | ||
| 225 | } | ||
| 226 | |||
| 227 | # add_file(\@list, $file) | ||
| 228 | # | ||
| 229 | # Adds [$file] to @list, which should be @puts or @gets. | ||
| 230 | # Sets $as_ref to point to the added element. | ||
| 231 | sub add_file { | ||
| 232 | my ($list, $file) = @_; | ||
| 233 | $as_ref = [$file]; | ||
| 234 | push (@$list, $as_ref); | ||
| 235 | } | ||
| 236 | |||
| 237 | # Sets the guest/host name for the previous put/get. | ||
| 238 | sub set_as { | ||
| 239 | my ($as) = @_; | ||
| 240 | die "-a (or --as) is only allowed after -p or -g\n" if !defined $as_ref; | ||
| 241 | die "Only one -a (or --as) is allowed after -p or -g\n" | ||
| 242 | if defined $as_ref->[1]; | ||
| 243 | $as_ref->[1] = $as; | ||
| 244 | } | ||
| 245 | |||
| 246 | # Sets $disk as a disk to be included in the VM to run. | ||
| 247 | sub set_disk { | ||
| 248 | my ($disk) = @_; | ||
| 249 | |||
| 250 | push (@disks, $disk); | ||
| 251 | |||
| 252 | my (%pt) = read_partition_table ($disk); | ||
| 253 | for my $role (keys %pt) { | ||
| 254 | die "can't have two sources for \L$role\E partition" | ||
| 255 | if exists $parts{$role}; | ||
| 256 | $parts{$role}{DISK} = $disk; | ||
| 257 | $parts{$role}{START} = $pt{$role}{START}; | ||
| 258 | $parts{$role}{SECTORS} = $pt{$role}{SECTORS}; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | # Locates the files used to back each of the virtual disks, | ||
| 263 | # and creates temporary disks. | ||
| 264 | sub find_disks { | ||
| 265 | # Find kernel, if we don't already have one. | ||
| 266 | if (!exists $parts{KERNEL}) { | ||
| 267 | my $name = find_file ('kernel.bin'); | ||
| 268 | die "Cannot find kernel\n" if !defined $name; | ||
| 269 | do_set_part ('KERNEL', 'file', $name); | ||
| 270 | } | ||
| 271 | |||
| 272 | # Try to find file system and swap disks, if we don't already have | ||
| 273 | # partitions. | ||
| 274 | if (!exists $parts{FILESYS}) { | ||
| 275 | my $name = find_file ('filesys.dsk'); | ||
| 276 | set_disk ($name) if defined $name; | ||
| 277 | } | ||
| 278 | if (!exists $parts{SWAP}) { | ||
| 279 | my $name = find_file ('swap.dsk'); | ||
| 280 | set_disk ($name) if defined $name; | ||
| 281 | } | ||
| 282 | |||
| 283 | # Warn about (potentially) missing partitions. | ||
| 284 | if (my ($project) = `pwd` =~ /\b(threads|userprog|vm|filesys)\b/) { | ||
| 285 | if ((grep ($project eq $_, qw (userprog vm filesys))) | ||
| 286 | && !defined $parts{FILESYS}) { | ||
| 287 | print STDERR "warning: it looks like you're running the $project "; | ||
| 288 | print STDERR "project, but no file system partition is present\n"; | ||
| 289 | } | ||
| 290 | if ($project eq 'vm' && !defined $parts{SWAP}) { | ||
| 291 | print STDERR "warning: it looks like you're running the $project "; | ||
| 292 | print STDERR "project, but no swap partition is present\n"; | ||
| 293 | } | ||
| 294 | } | ||
| 295 | |||
| 296 | # Open disk handle. | ||
| 297 | my ($handle); | ||
| 298 | if (!defined $make_disk) { | ||
| 299 | ($handle, $make_disk) = tempfile (UNLINK => $tmp_disk, | ||
| 300 | SUFFIX => '.dsk'); | ||
| 301 | } else { | ||
| 302 | die "$make_disk: already exists\n" if -e $make_disk; | ||
| 303 | open ($handle, '>', $make_disk) or die "$make_disk: create: $!\n"; | ||
| 304 | } | ||
| 305 | |||
| 306 | # Prepare the arguments to pass to the Pintos kernel. | ||
| 307 | my (@args); | ||
| 308 | push (@args, '-kernel-test') if $kernel_test; | ||
| 309 | push (@args, shift (@kernel_args)) | ||
| 310 | while @kernel_args && $kernel_args[0] =~ /^-/; | ||
| 311 | push (@args, 'extract') if @puts; | ||
| 312 | push (@args, @kernel_args); | ||
| 313 | push (@args, 'append', $_->[0]) foreach @gets; | ||
| 314 | |||
| 315 | # Make disk. | ||
| 316 | my (%disk); | ||
| 317 | our (@role_order); | ||
| 318 | for my $role (@role_order) { | ||
| 319 | my $p = $parts{$role}; | ||
| 320 | next if !defined $p; | ||
| 321 | next if exists $p->{DISK}; | ||
| 322 | $disk{$role} = $p; | ||
| 323 | } | ||
| 324 | $disk{DISK} = $make_disk; | ||
| 325 | $disk{HANDLE} = $handle; | ||
| 326 | $disk{ALIGN} = $align; | ||
| 327 | $disk{GEOMETRY} = %geometry; | ||
| 328 | $disk{FORMAT} = 'partitioned'; | ||
| 329 | $disk{LOADER} = read_loader ($loader_fn); | ||
| 330 | $disk{ARGS} = \@args; | ||
| 331 | assemble_disk (%disk); | ||
| 332 | |||
| 333 | # Put the disk at the front of the list of disks. | ||
| 334 | unshift (@disks, $make_disk); | ||
| 335 | die "can't use more than " . scalar (@disks) . "disks\n" if @disks > 4; | ||
| 336 | } | ||
| 337 | |||
| 338 | # Prepare the scratch disk for gets and puts. | ||
| 339 | sub prepare_scratch_disk { | ||
| 340 | return if !@gets && !@puts; | ||
| 341 | |||
| 342 | my ($p) = $parts{SCRATCH}; | ||
| 343 | # Create temporary partition and write the files to put to it, | ||
| 344 | # then write an end-of-archive marker. | ||
| 345 | my ($part_handle, $part_fn) = tempfile (UNLINK => 1, SUFFIX => '.part'); | ||
| 346 | put_scratch_file ($_->[0], defined $_->[1] ? $_->[1] : $_->[0], | ||
| 347 | $part_handle, $part_fn) | ||
| 348 | foreach @puts; | ||
| 349 | write_fully ($part_handle, $part_fn, "\0" x 1024); | ||
| 350 | |||
| 351 | # Make sure the scratch disk is big enough to get big files | ||
| 352 | # and at least as big as any requested size. | ||
| 353 | my ($size) = round_up (max (@gets * 1024 * 1024, $p->{BYTES} || 0), 512); | ||
| 354 | extend_file ($part_handle, $part_fn, $size); | ||
| 355 | close ($part_handle); | ||
| 356 | |||
| 357 | if (exists $p->{DISK}) { | ||
| 358 | # Copy the scratch partition to the disk. | ||
| 359 | die "$p->{DISK}: scratch partition too small\n" | ||
| 360 | if $p->{SECTORS} * 512 < $size; | ||
| 361 | |||
| 362 | my ($disk_handle); | ||
| 363 | open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n"; | ||
| 364 | open ($disk_handle, '+<', $p->{DISK}) or die "$p->{DISK}: open: $!\n"; | ||
| 365 | my ($start) = $p->{START} * 512; | ||
| 366 | sysseek ($disk_handle, $start, SEEK_SET) == $start | ||
| 367 | or die "$p->{DISK}: seek: $!\n"; | ||
| 368 | copy_file ($part_handle, $part_fn, $disk_handle, $p->{DISK}, $size); | ||
| 369 | close ($disk_handle) or die "$p->{DISK}: close: $!\n"; | ||
| 370 | close ($part_handle) or die "$part_fn: close: $!\n"; | ||
| 371 | } else { | ||
| 372 | # Set $part_fn as the source for the scratch partition. | ||
| 373 | do_set_part ('SCRATCH', 'file', $part_fn); | ||
| 374 | } | ||
| 375 | } | ||
| 376 | |||
| 377 | # Read "get" files from the scratch disk. | ||
| 378 | sub finish_scratch_disk { | ||
| 379 | return if !@gets; | ||
| 380 | |||
| 381 | # Open scratch partition. | ||
| 382 | my ($p) = $parts{SCRATCH}; | ||
| 383 | my ($part_handle); | ||
| 384 | my ($part_fn) = $p->{DISK}; | ||
| 385 | open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n"; | ||
| 386 | sysseek ($part_handle, $p->{START} * 512, SEEK_SET) == $p->{START} * 512 | ||
| 387 | or die "$part_fn: seek: $!\n"; | ||
| 388 | |||
| 389 | # Read each file. | ||
| 390 | # If reading fails, delete that file and all subsequent files, but | ||
| 391 | # don't die with an error, because that's a guest error not a host | ||
| 392 | # error. (If we do exit with an error code, it fouls up the | ||
| 393 | # grading process.) Instead, just make sure that the host file(s) | ||
| 394 | # we were supposed to retrieve is unlinked. | ||
| 395 | my ($ok) = 1; | ||
| 396 | my ($part_end) = ($p->{START} + $p->{SECTORS}) * 512; | ||
| 397 | foreach my $get (@gets) { | ||
| 398 | my ($name) = defined ($get->[1]) ? $get->[1] : $get->[0]; | ||
| 399 | if ($ok) { | ||
| 400 | my ($error) = get_scratch_file ($name, $part_handle, $part_fn); | ||
| 401 | if (!$error && sysseek ($part_handle, 0, SEEK_CUR) > $part_end) { | ||
| 402 | $error = "$part_fn: scratch data overflows partition"; | ||
| 403 | } | ||
| 404 | if ($error) { | ||
| 405 | print STDERR "getting $name failed ($error)\n"; | ||
| 406 | $ok = 0; | ||
| 407 | } | ||
| 408 | } | ||
| 409 | die "$name: unlink: $!\n" if !$ok && !unlink ($name) && !$!{ENOENT}; | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | # mk_ustar_field($number, $size) | ||
| 414 | # | ||
| 415 | # Returns $number in a $size-byte numeric field in the format used by | ||
| 416 | # the standard ustar archive header. | ||
| 417 | sub mk_ustar_field { | ||
| 418 | my ($number, $size) = @_; | ||
| 419 | my ($len) = $size - 1; | ||
| 420 | my ($out) = sprintf ("%0${len}o", $number) . "\0"; | ||
| 421 | die "$number: too large for $size-byte octal ustar field\n" | ||
| 422 | if length ($out) != $size; | ||
| 423 | return $out; | ||
| 424 | } | ||
| 425 | |||
| 426 | # calc_ustar_chksum($s) | ||
| 427 | # | ||
| 428 | # Calculates and returns the ustar checksum of 512-byte ustar archive | ||
| 429 | # header $s. | ||
| 430 | sub calc_ustar_chksum { | ||
| 431 | my ($s) = @_; | ||
| 432 | die if length ($s) != 512; | ||
| 433 | substr ($s, 148, 8, ' ' x 8); | ||
| 434 | return unpack ("%32a*", $s); | ||
| 435 | } | ||
| 436 | |||
| 437 | # put_scratch_file($src_file_name, $dst_file_name, | ||
| 438 | # $disk_handle, $disk_file_name). | ||
| 439 | # | ||
| 440 | # Copies $src_file_name into $disk_handle for extraction as | ||
| 441 | # $dst_file_name. $disk_file_name is used for error messages. | ||
| 442 | sub put_scratch_file { | ||
| 443 | my ($src_file_name, $dst_file_name, $disk_handle, $disk_file_name) = @_; | ||
| 444 | |||
| 445 | print "Copying $src_file_name to scratch partition...\n"; | ||
| 446 | |||
| 447 | # ustar format supports up to 100 characters for a file name, and | ||
| 448 | # even longer names given some common properties, but our code in | ||
| 449 | # the Pintos kernel only supports at most 99 characters. | ||
| 450 | die "$dst_file_name: name too long (max 99 characters)\n" | ||
| 451 | if length ($dst_file_name) > 99; | ||
| 452 | |||
| 453 | # Compose and write ustar header. | ||
| 454 | stat $src_file_name or die "$src_file_name: stat: $!\n"; | ||
| 455 | my ($size) = -s _; | ||
| 456 | my ($header) = (pack ("a100", $dst_file_name) # name | ||
| 457 | . mk_ustar_field (0644, 8) # mode | ||
| 458 | . mk_ustar_field (0, 8) # uid | ||
| 459 | . mk_ustar_field (0, 8) # gid | ||
| 460 | . mk_ustar_field ($size, 12) # size | ||
| 461 | . mk_ustar_field (1136102400, 12) # mtime | ||
| 462 | . (' ' x 8) # chksum | ||
| 463 | . '0' # typeflag | ||
| 464 | . ("\0" x 100) # linkname | ||
| 465 | . "ustar\0" # magic | ||
| 466 | . "00" # version | ||
| 467 | . "root" . ("\0" x 28) # uname | ||
| 468 | . "root" . ("\0" x 28) # gname | ||
| 469 | . "\0" x 8 # devmajor | ||
| 470 | . "\0" x 8 # devminor | ||
| 471 | . ("\0" x 155)) # prefix | ||
| 472 | . "\0" x 12; # pad to 512 bytes | ||
| 473 | substr ($header, 148, 8) = mk_ustar_field (calc_ustar_chksum ($header), 8); | ||
| 474 | write_fully ($disk_handle, $disk_file_name, $header); | ||
| 475 | |||
| 476 | # Copy file data. | ||
| 477 | my ($put_handle); | ||
| 478 | sysopen ($put_handle, $src_file_name, O_RDONLY) | ||
| 479 | or die "$src_file_name: open: $!\n"; | ||
| 480 | copy_file ($put_handle, $src_file_name, $disk_handle, $disk_file_name, | ||
| 481 | $size); | ||
| 482 | die "$src_file_name: changed size while being read\n" | ||
| 483 | if $size != -s $put_handle; | ||
| 484 | close ($put_handle); | ||
| 485 | |||
| 486 | # Round up disk data to beginning of next sector. | ||
| 487 | write_fully ($disk_handle, $disk_file_name, "\0" x (512 - $size % 512)) | ||
| 488 | if $size % 512; | ||
| 489 | } | ||
| 490 | |||
| 491 | # get_scratch_file($get_file_name, $disk_handle, $disk_file_name) | ||
| 492 | # | ||
| 493 | # Copies from $disk_handle to $get_file_name (which is created). | ||
| 494 | # $disk_file_name is used for error messages. | ||
| 495 | # Returns 1 if successful, 0 on failure. | ||
| 496 | sub get_scratch_file { | ||
| 497 | my ($get_file_name, $disk_handle, $disk_file_name) = @_; | ||
| 498 | |||
| 499 | print "Copying $get_file_name out of $disk_file_name...\n"; | ||
| 500 | |||
| 501 | # Read ustar header sector. | ||
| 502 | my ($header) = read_fully ($disk_handle, $disk_file_name, 512); | ||
| 503 | return "scratch disk tar archive ends unexpectedly" | ||
| 504 | if $header eq ("\0" x 512); | ||
| 505 | |||
| 506 | # Verify magic numbers. | ||
| 507 | return "corrupt ustar signature" if substr ($header, 257, 6) ne "ustar\0"; | ||
| 508 | return "invalid ustar version" if substr ($header, 263, 2) ne '00'; | ||
| 509 | |||
| 510 | # Verify checksum. | ||
| 511 | my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8))); | ||
| 512 | my ($correct_chksum) = calc_ustar_chksum ($header); | ||
| 513 | return "checksum mismatch" if $chksum != $correct_chksum; | ||
| 514 | |||
| 515 | # Get type. | ||
| 516 | my ($typeflag) = substr ($header, 156, 1); | ||
| 517 | return "not a regular file" if $typeflag ne '0' && $typeflag ne "\0"; | ||
| 518 | |||
| 519 | # Get size. | ||
| 520 | my ($size) = oct (unpack ("Z*", substr ($header, 124, 12))); | ||
| 521 | return "bad size $size\n" if $size < 0; | ||
| 522 | |||
| 523 | # Copy file data. | ||
| 524 | my ($get_handle); | ||
| 525 | sysopen ($get_handle, $get_file_name, O_WRONLY | O_CREAT, 0666) | ||
| 526 | or die "$get_file_name: create: $!\n"; | ||
| 527 | copy_file ($disk_handle, $disk_file_name, $get_handle, $get_file_name, | ||
| 528 | $size); | ||
| 529 | close ($get_handle); | ||
| 530 | |||
| 531 | # Skip forward in disk up to beginning of next sector. | ||
| 532 | read_fully ($disk_handle, $disk_file_name, 512 - $size % 512) | ||
| 533 | if $size % 512; | ||
| 534 | |||
| 535 | return 0; | ||
| 536 | } | ||
| 537 | |||
| 538 | # Running simulators. | ||
| 539 | |||
| 540 | # Runs the selected simulator. | ||
| 541 | sub run_vm { | ||
| 542 | if ($sim eq 'bochs') { | ||
| 543 | run_bochs (); | ||
| 544 | } elsif ($sim eq 'qemu') { | ||
| 545 | run_qemu (); | ||
| 546 | } elsif ($sim eq 'player') { | ||
| 547 | run_player (); | ||
| 548 | } else { | ||
| 549 | die "unknown simulator `$sim'\n"; | ||
| 550 | } | ||
| 551 | } | ||
| 552 | |||
| 553 | # Runs Bochs. | ||
| 554 | sub run_bochs { | ||
| 555 | # Select Bochs binary based on the chosen debugger. | ||
| 556 | my ($bin) = $debug eq 'monitor' ? 'bochs-dbg' : 'bochs'; | ||
| 557 | |||
| 558 | my ($squish_pty); | ||
| 559 | if ($serial) { | ||
| 560 | $squish_pty = find_in_path ("squish-pty"); | ||
| 561 | print "warning: can't find squish-pty, so terminal input will fail\n" | ||
| 562 | if !defined $squish_pty; | ||
| 563 | } | ||
| 564 | |||
| 565 | # Write bochsrc.txt configuration file. | ||
| 566 | open (BOCHSRC, ">", "bochsrc.txt") or die "bochsrc.txt: create: $!\n"; | ||
| 567 | print BOCHSRC <<EOF; | ||
| 568 | romimage: file=\$BXSHARE/BIOS-bochs-latest | ||
| 569 | vgaromimage: file=\$BXSHARE/VGABIOS-lgpl-latest | ||
| 570 | boot: disk | ||
| 571 | cpu: ips=1000000 | ||
| 572 | megs: $mem | ||
| 573 | log: bochsout.txt | ||
| 574 | panic: action=fatal | ||
| 575 | user_shortcut: keys=ctrlaltdel | ||
| 576 | EOF | ||
| 577 | print BOCHSRC "gdbstub: enabled=1\n" if $debug eq 'gdb'; | ||
| 578 | print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none', | ||
| 579 | ", time0=0\n"; | ||
| 580 | print BOCHSRC "ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15\n" | ||
| 581 | if @disks > 2; | ||
| 582 | print_bochs_disk_line ("ata0-master", $disks[0]); | ||
| 583 | print_bochs_disk_line ("ata0-slave", $disks[1]); | ||
| 584 | print_bochs_disk_line ("ata1-master", $disks[2]); | ||
| 585 | print_bochs_disk_line ("ata1-slave", $disks[3]); | ||
| 586 | if ($vga ne 'terminal') { | ||
| 587 | if ($serial) { | ||
| 588 | my $mode = defined ($squish_pty) ? "term" : "file"; | ||
| 589 | print BOCHSRC "com1: enabled=1, mode=$mode, dev=/dev/stdout\n"; | ||
| 590 | } | ||
| 591 | print BOCHSRC "display_library: nogui\n" if $vga eq 'none'; | ||
| 592 | } else { | ||
| 593 | print BOCHSRC "display_library: term\n"; | ||
| 594 | } | ||
| 595 | close (BOCHSRC); | ||
| 596 | |||
| 597 | # Compose Bochs command line. | ||
| 598 | my (@cmd) = ($bin, '-q'); | ||
| 599 | unshift (@cmd, $squish_pty) if defined $squish_pty; | ||
| 600 | push (@cmd, '-j', $jitter) if defined $jitter; | ||
| 601 | |||
| 602 | # Run Bochs. | ||
| 603 | print join (' ', @cmd), "\n"; | ||
| 604 | my ($exit) = xsystem (@cmd); | ||
| 605 | if (WIFEXITED ($exit)) { | ||
| 606 | # Bochs exited normally. | ||
| 607 | # Ignore the exit code; Bochs normally exits with status 1, | ||
| 608 | # which is weird. | ||
| 609 | } elsif (WIFSIGNALED ($exit)) { | ||
| 610 | die "Bochs died with signal ", WTERMSIG ($exit), "\n"; | ||
| 611 | } else { | ||
| 612 | die "Bochs died: code $exit\n"; | ||
| 613 | } | ||
| 614 | } | ||
| 615 | |||
| 616 | sub print_bochs_disk_line { | ||
| 617 | my ($device, $disk) = @_; | ||
| 618 | if (defined $disk) { | ||
| 619 | my (%geom) = disk_geometry ($disk); | ||
| 620 | print BOCHSRC "$device: type=disk, path=$disk, mode=flat, "; | ||
| 621 | print BOCHSRC "cylinders=$geom{C}, heads=$geom{H}, spt=$geom{S}, "; | ||
| 622 | print BOCHSRC "translation=none\n"; | ||
| 623 | } | ||
| 624 | } | ||
| 625 | |||
| 626 | # Runs QEMU. | ||
| 627 | sub run_qemu { | ||
| 628 | print "warning: qemu doesn't support --terminal\n" | ||
| 629 | if $vga eq 'terminal'; | ||
| 630 | print "warning: qemu doesn't support jitter\n" | ||
| 631 | if defined $jitter; | ||
| 632 | my (@cmd) = ('qemu'); | ||
| 633 | # push (@cmd, '-no-kqemu'); | ||
| 634 | push (@cmd, '-hda', $disks[0]) if defined $disks[0]; | ||
| 635 | push (@cmd, '-hdb', $disks[1]) if defined $disks[1]; | ||
| 636 | push (@cmd, '-hdc', $disks[2]) if defined $disks[2]; | ||
| 637 | push (@cmd, '-hdd', $disks[3]) if defined $disks[3]; | ||
| 638 | push (@cmd, '-m', $mem); | ||
| 639 | push (@cmd, '-net', 'none'); | ||
| 640 | push (@cmd, '-nographic') if $vga eq 'none'; | ||
| 641 | push (@cmd, '-serial', 'stdio') if $serial && $vga ne 'none'; | ||
| 642 | push (@cmd, '-S') if $debug eq 'monitor'; | ||
| 643 | push (@cmd, '-s', '-S') if $debug eq 'gdb'; | ||
| 644 | push (@cmd, '-monitor', 'null') if $vga eq 'none' && $debug eq 'none'; | ||
| 645 | run_command (@cmd); | ||
| 646 | } | ||
| 647 | |||
| 648 | # player_unsup($flag) | ||
| 649 | # | ||
| 650 | # Prints a message that $flag is unsupported by VMware Player. | ||
| 651 | sub player_unsup { | ||
| 652 | my ($flag) = @_; | ||
| 653 | print "warning: no support for $flag with VMware Player\n"; | ||
| 654 | } | ||
| 655 | |||
| 656 | # Runs VMware Player. | ||
| 657 | sub run_player { | ||
| 658 | player_unsup ("--$debug") if $debug ne 'none'; | ||
| 659 | player_unsup ("--no-vga") if $vga eq 'none'; | ||
| 660 | player_unsup ("--terminal") if $vga eq 'terminal'; | ||
| 661 | player_unsup ("--jitter") if defined $jitter; | ||
| 662 | player_unsup ("--timeout"), undef $timeout if defined $timeout; | ||
| 663 | player_unsup ("--kill-on-failure"), undef $kill_on_failure | ||
| 664 | if defined $kill_on_failure; | ||
| 665 | |||
| 666 | $mem = round_up ($mem, 4); # Memory must be multiple of 4 MB. | ||
| 667 | |||
| 668 | open (VMX, ">", "pintos.vmx") or die "pintos.vmx: create: $!\n"; | ||
| 669 | chmod 0777 & ~umask, "pintos.vmx"; | ||
| 670 | print VMX <<EOF; | ||
| 671 | #! /usr/bin/vmware -G | ||
| 672 | config.version = 8 | ||
| 673 | guestOS = "linux" | ||
| 674 | memsize = $mem | ||
| 675 | floppy0.present = FALSE | ||
| 676 | usb.present = FALSE | ||
| 677 | sound.present = FALSE | ||
| 678 | gui.exitAtPowerOff = TRUE | ||
| 679 | gui.exitOnCLIHLT = TRUE | ||
| 680 | gui.powerOnAtStartUp = TRUE | ||
| 681 | EOF | ||
| 682 | |||
| 683 | print VMX <<EOF if $serial; | ||
| 684 | serial0.present = TRUE | ||
| 685 | serial0.fileType = "pipe" | ||
| 686 | serial0.fileName = "pintos.socket" | ||
| 687 | serial0.pipe.endPoint = "client" | ||
| 688 | serial0.tryNoRxLoss = "TRUE" | ||
| 689 | EOF | ||
| 690 | |||
| 691 | for (my ($i) = 0; $i < 4; $i++) { | ||
| 692 | my ($dsk) = $disks[$i]; | ||
| 693 | last if !defined $dsk; | ||
| 694 | |||
| 695 | my ($device) = "ide" . int ($i / 2) . ":" . ($i % 2); | ||
| 696 | my ($pln) = "$device.pln"; | ||
| 697 | print VMX <<EOF; | ||
| 698 | |||
| 699 | $device.present = TRUE | ||
| 700 | $device.deviceType = "plainDisk" | ||
| 701 | $device.fileName = "$pln" | ||
| 702 | EOF | ||
| 703 | |||
| 704 | open (URANDOM, '<', '/dev/urandom') or die "/dev/urandom: open: $!\n"; | ||
| 705 | my ($bytes); | ||
| 706 | sysread (URANDOM, $bytes, 4) == 4 or die "/dev/urandom: read: $!\n"; | ||
| 707 | close (URANDOM); | ||
| 708 | my ($cid) = unpack ("L", $bytes); | ||
| 709 | |||
| 710 | my (%geom) = disk_geometry ($dsk); | ||
| 711 | open (PLN, ">", $pln) or die "$pln: create: $!\n"; | ||
| 712 | print PLN <<EOF; | ||
| 713 | version=1 | ||
| 714 | CID=$cid | ||
| 715 | parentCID=ffffffff | ||
| 716 | createType="monolithicFlat" | ||
| 717 | |||
| 718 | RW $geom{CAPACITY} FLAT "$dsk" 0 | ||
| 719 | |||
| 720 | # The Disk Data Base | ||
| 721 | #DDB | ||
| 722 | |||
| 723 | ddb.adapterType = "ide" | ||
| 724 | ddb.virtualHWVersion = "4" | ||
| 725 | ddb.toolsVersion = "2" | ||
| 726 | ddb.geometry.cylinders = "$geom{C}" | ||
| 727 | ddb.geometry.heads = "$geom{H}" | ||
| 728 | ddb.geometry.sectors = "$geom{S}" | ||
| 729 | EOF | ||
| 730 | close (PLN); | ||
| 731 | } | ||
| 732 | close (VMX); | ||
| 733 | |||
| 734 | my ($squish_unix); | ||
| 735 | if ($serial) { | ||
| 736 | $squish_unix = find_in_path ("squish-unix"); | ||
| 737 | print "warning: can't find squish-unix, so terminal input ", | ||
| 738 | "and output will fail\n" if !defined $squish_unix; | ||
| 739 | } | ||
| 740 | |||
| 741 | my ($vmx) = getcwd () . "/pintos.vmx"; | ||
| 742 | my (@cmd) = ("vmplayer", $vmx); | ||
| 743 | unshift (@cmd, $squish_unix, "pintos.socket") if $squish_unix; | ||
| 744 | print join (' ', @cmd), "\n"; | ||
| 745 | xsystem (@cmd); | ||
| 746 | } | ||
| 747 | |||
| 748 | # Disk utilities. | ||
| 749 | |||
| 750 | sub extend_file { | ||
| 751 | my ($handle, $file_name, $size) = @_; | ||
| 752 | if (-s ($handle) < $size) { | ||
| 753 | sysseek ($handle, $size - 1, 0) == $size - 1 | ||
| 754 | or die "$file_name: seek: $!\n"; | ||
| 755 | syswrite ($handle, "\0") == 1 | ||
| 756 | or die "$file_name: write: $!\n"; | ||
| 757 | } | ||
| 758 | } | ||
| 759 | |||
| 760 | # disk_geometry($file) | ||
| 761 | # | ||
| 762 | # Examines $file and returns a valid IDE disk geometry for it, as a | ||
| 763 | # hash. | ||
| 764 | sub disk_geometry { | ||
| 765 | my ($file) = @_; | ||
| 766 | my ($size) = -s $file; | ||
| 767 | die "$file: stat: $!\n" if !defined $size; | ||
| 768 | die "$file: size $size not a multiple of 512 bytes\n" if $size % 512; | ||
| 769 | my ($cyl_size) = 512 * 16 * 63; | ||
| 770 | my ($cylinders) = ceil ($size / $cyl_size); | ||
| 771 | |||
| 772 | return (CAPACITY => $size / 512, | ||
| 773 | C => $cylinders, | ||
| 774 | H => 16, | ||
| 775 | S => 63); | ||
| 776 | } | ||
| 777 | |||
| 778 | # Subprocess utilities. | ||
| 779 | |||
| 780 | # run_command(@args) | ||
| 781 | # | ||
| 782 | # Runs xsystem(@args). | ||
| 783 | # Also prints the command it's running and checks that it succeeded. | ||
| 784 | sub run_command { | ||
| 785 | print join (' ', @_), "\n"; | ||
| 786 | die "command failed\n" if xsystem (@_); | ||
| 787 | } | ||
| 788 | |||
| 789 | # xsystem(@args) | ||
| 790 | # | ||
| 791 | # Creates a subprocess via exec(@args) and waits for it to complete. | ||
| 792 | # Relays common signals to the subprocess. | ||
| 793 | # If $timeout is set then the subprocess will be killed after that long. | ||
| 794 | sub xsystem { | ||
| 795 | # QEMU turns off local echo and does not restore it if killed by a signal. | ||
| 796 | # We compensate by restoring it ourselves. | ||
| 797 | my $cleanup = sub {}; | ||
| 798 | if (isatty (0)) { | ||
| 799 | my $termios = POSIX::Termios->new; | ||
| 800 | $termios->getattr (0); | ||
| 801 | $cleanup = sub { $termios->setattr (0, &POSIX::TCSANOW); } | ||
| 802 | } | ||
| 803 | |||
| 804 | # Create pipe for filtering output. | ||
| 805 | pipe (my $in, my $out) or die "pipe: $!\n" if $kill_on_failure; | ||
| 806 | |||
| 807 | my ($pid) = fork; | ||
| 808 | if (!defined ($pid)) { | ||
| 809 | # Fork failed. | ||
| 810 | die "fork: $!\n"; | ||
| 811 | } elsif (!$pid) { | ||
| 812 | # Running in child process. | ||
| 813 | dup2 (fileno ($out), STDOUT_FILENO) or die "dup2: $!\n" | ||
| 814 | if $kill_on_failure; | ||
| 815 | exec_setitimer (@_); | ||
| 816 | } else { | ||
| 817 | # Running in parent process. | ||
| 818 | close $out if $kill_on_failure; | ||
| 819 | |||
| 820 | my ($cause); | ||
| 821 | local $SIG{ALRM} = sub { timeout ($pid, $cause, $cleanup); }; | ||
| 822 | local $SIG{INT} = sub { relay_signal ($pid, "INT", $cleanup); }; | ||
| 823 | local $SIG{TERM} = sub { relay_signal ($pid, "TERM", $cleanup); }; | ||
| 824 | alarm ($timeout * get_load_average () + 1) if defined ($timeout); | ||
| 825 | |||
| 826 | if ($kill_on_failure) { | ||
| 827 | # Filter output. | ||
| 828 | my ($buf) = ""; | ||
| 829 | my ($boots) = 0; | ||
| 830 | local ($|) = 1; | ||
| 831 | for (;;) { | ||
| 832 | if (waitpid ($pid, WNOHANG) != 0) { | ||
| 833 | # Subprocess died. Pass through any remaining data. | ||
| 834 | print $buf while sysread ($in, $buf, 4096) > 0; | ||
| 835 | last; | ||
| 836 | } | ||
| 837 | |||
| 838 | # Read and print out pipe data. | ||
| 839 | my ($len) = length ($buf); | ||
| 840 | waitpid ($pid, 0), last | ||
| 841 | if sysread ($in, $buf, 4096, $len) <= 0; | ||
| 842 | print substr ($buf, $len); | ||
| 843 | |||
| 844 | # Remove full lines from $buf and scan them for keywords. | ||
| 845 | while ((my $idx = index ($buf, "\n")) >= 0) { | ||
| 846 | local $_ = substr ($buf, 0, $idx + 1, ''); | ||
| 847 | next if defined ($cause); | ||
| 848 | if (/(Kernel PANIC|User process ABORT)/ ) { | ||
| 849 | $cause = "\L$1\E"; | ||
| 850 | alarm (5); | ||
| 851 | } elsif (/Pintos booting/ && ++$boots > 1) { | ||
| 852 | $cause = "triple fault"; | ||
| 853 | alarm (5); | ||
| 854 | } elsif (/FAILED/) { | ||
| 855 | $cause = "test failure"; | ||
| 856 | alarm (5); | ||
| 857 | } | ||
| 858 | } | ||
| 859 | } | ||
| 860 | } else { | ||
| 861 | waitpid ($pid, 0); | ||
| 862 | } | ||
| 863 | alarm (0); | ||
| 864 | &$cleanup (); | ||
| 865 | |||
| 866 | if (WIFSIGNALED ($?) && WTERMSIG ($?) == SIGVTALRM ()) { | ||
| 867 | seek (STDOUT, 0, 2); | ||
| 868 | print "\nTIMEOUT after $timeout seconds of host CPU time\n"; | ||
| 869 | exit 0; | ||
| 870 | } | ||
| 871 | |||
| 872 | return $?; | ||
| 873 | } | ||
| 874 | } | ||
| 875 | |||
| 876 | # relay_signal($pid, $signal, &$cleanup) | ||
| 877 | # | ||
| 878 | # Relays $signal to $pid and then reinvokes it for us with the default | ||
| 879 | # handler. Also cleans up temporary files and invokes $cleanup. | ||
| 880 | sub relay_signal { | ||
| 881 | my ($pid, $signal, $cleanup) = @_; | ||
| 882 | kill $signal, $pid; | ||
| 883 | eval { File::Temp::cleanup() }; # Not defined in old File::Temp. | ||
| 884 | &$cleanup (); | ||
| 885 | $SIG{$signal} = 'DEFAULT'; | ||
| 886 | kill $signal, getpid (); | ||
| 887 | } | ||
| 888 | |||
| 889 | # timeout($pid, $cause, &$cleanup) | ||
| 890 | # | ||
| 891 | # Interrupts $pid and dies with a timeout error message, | ||
| 892 | # after invoking $cleanup. | ||
| 893 | sub timeout { | ||
| 894 | my ($pid, $cause, $cleanup) = @_; | ||
| 895 | kill "INT", $pid; | ||
| 896 | waitpid ($pid, 0); | ||
| 897 | &$cleanup (); | ||
| 898 | seek (STDOUT, 0, 2); | ||
| 899 | if (!defined ($cause)) { | ||
| 900 | my ($load_avg) = `uptime` =~ /(load average:.*)$/i; | ||
| 901 | print "\nTIMEOUT after ", time () - $start_time, | ||
| 902 | " seconds of wall-clock time"; | ||
| 903 | print " - $load_avg" if defined $load_avg; | ||
| 904 | print "\n"; | ||
| 905 | } else { | ||
| 906 | print "Simulation terminated due to $cause.\n"; | ||
| 907 | } | ||
| 908 | exit 0; | ||
| 909 | } | ||
| 910 | |||
| 911 | # Returns the system load average over the last minute. | ||
| 912 | # If the load average is less than 1.0 or cannot be determined, returns 1.0. | ||
| 913 | sub get_load_average { | ||
| 914 | my ($avg) = `uptime` =~ /load average:\s*([^,]+),/; | ||
| 915 | return $avg >= 1.0 ? $avg : 1.0; | ||
| 916 | } | ||
| 917 | |||
| 918 | # Calls setitimer to set a timeout, then execs what was passed to us. | ||
| 919 | sub exec_setitimer { | ||
| 920 | if (defined $timeout) { | ||
| 921 | if ($ ge 5.8.0) { | ||
| 922 | eval " | ||
| 923 | use Time::HiRes qw(setitimer ITIMER_VIRTUAL); | ||
| 924 | setitimer (ITIMER_VIRTUAL, $timeout, 0); | ||
| 925 | "; | ||
| 926 | } else { | ||
| 927 | { exec ("setitimer-helper", $timeout, @_); }; | ||
| 928 | exit 1 if !$!{ENOENT}; | ||
| 929 | print STDERR "warning: setitimer-helper is not installed, so ", | ||
| 930 | "CPU time limit will not be enforced\n"; | ||
| 931 | } | ||
| 932 | } | ||
| 933 | exec (@_); | ||
| 934 | exit (1); | ||
| 935 | } | ||
| 936 | |||
| 937 | sub SIGVTALRM { | ||
| 938 | use Config; | ||
| 939 | my $i = 0; | ||
| 940 | foreach my $name (split(' ', $Config{sig_name})) { | ||
| 941 | return $i if $name eq 'VTALRM'; | ||
| 942 | $i++; | ||
| 943 | } | ||
| 944 | return 0; | ||
| 945 | } | ||
| 946 | |||
| 947 | # find_in_path ($program) | ||
| 948 | # | ||
| 949 | # Searches for $program in $ENV{PATH}. | ||
| 950 | # Returns $program if found, otherwise undef. | ||
| 951 | sub find_in_path { | ||
| 952 | my ($program) = @_; | ||
| 953 | -x "$_/$program" and return $program foreach split (':', $ENV{PATH}); | ||
| 954 | return; | ||
| 955 | } | ||
diff --git a/pintos-progos/utils/pintos-gdb b/pintos-progos/utils/pintos-gdb new file mode 100755 index 0000000..9c9555b --- /dev/null +++ b/pintos-progos/utils/pintos-gdb | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #! /bin/sh | ||
| 2 | |||
| 3 | # Path to GDB macros file. Customize for your site. | ||
| 4 | PINTOS_SRC="$(dirname $(dirname $(which pintos-gdb)))" | ||
| 5 | GDBMACROS="${PINTOS_SRC}/misc/gdb-macros" | ||
| 6 | |||
| 7 | # Choose correct GDB. | ||
| 8 | if command -v i386-elf-gdb >/dev/null 2>&1; then | ||
| 9 | GDB=i386-elf-gdb | ||
| 10 | else | ||
| 11 | GDB=gdb | ||
| 12 | fi | ||
| 13 | |||
| 14 | # Run GDB. | ||
| 15 | if test -f "$GDBMACROS"; then | ||
| 16 | exec $GDB -x "$GDBMACROS" "$@" | ||
| 17 | else | ||
| 18 | echo "*** $GDBMACROS does not exist ***" | ||
| 19 | echo "*** Pintos GDB macros will not be available ***" | ||
| 20 | exec $GDB "$@" | ||
| 21 | fi | ||
diff --git a/pintos-progos/utils/pintos-mkdisk b/pintos-progos/utils/pintos-mkdisk new file mode 100755 index 0000000..87b1563 --- /dev/null +++ b/pintos-progos/utils/pintos-mkdisk | |||
| @@ -0,0 +1,134 @@ | |||
| 1 | #! /usr/bin/perl | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use warnings; | ||
| 5 | use POSIX; | ||
| 6 | use Getopt::Long qw(:config bundling); | ||
| 7 | use Fcntl 'SEEK_SET'; | ||
| 8 | |||
| 9 | # Read Pintos.pm from the same directory as this program. | ||
| 10 | BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; } | ||
| 11 | |||
| 12 | our ($disk_fn); # Output disk file name. | ||
| 13 | our (%parts); # Partitions. | ||
| 14 | our ($format); # "partitioned" (default) or "raw" | ||
| 15 | our (%geometry); # IDE disk geometry. | ||
| 16 | our ($align); # Align partitions on cylinders? | ||
| 17 | our ($loader_fn); # File name of loader. | ||
| 18 | our ($include_loader); # Include loader? | ||
| 19 | our (@kernel_args); # Kernel arguments. | ||
| 20 | |||
| 21 | if (grep ($_ eq '--', @ARGV)) { | ||
| 22 | @kernel_args = @ARGV; | ||
| 23 | @ARGV = (); | ||
| 24 | while ((my $arg = shift (@kernel_args)) ne '--') { | ||
| 25 | push (@ARGV, $arg); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | GetOptions ("h|help" => sub { usage (0); }, | ||
| 30 | |||
| 31 | "kernel=s" => \&set_part, | ||
| 32 | "filesys=s" => \&set_part, | ||
| 33 | "scratch=s" => \&set_part, | ||
| 34 | "swap=s" => \&set_part, | ||
| 35 | |||
| 36 | "filesys-size=s" => \&set_part, | ||
| 37 | "scratch-size=s" => \&set_part, | ||
| 38 | "swap-size=s" => \&set_part, | ||
| 39 | |||
| 40 | "kernel-from=s" => \&set_part, | ||
| 41 | "filesys-from=s" => \&set_part, | ||
| 42 | "scratch-from=s" => \&set_part, | ||
| 43 | "swap-from=s" => \&set_part, | ||
| 44 | |||
| 45 | "format=s" => \$format, | ||
| 46 | "loader:s" => \&set_loader, | ||
| 47 | "no-loader" => \&set_no_loader, | ||
| 48 | "geometry=s" => \&set_geometry, | ||
| 49 | "align=s" => \&set_align) | ||
| 50 | or exit 1; | ||
| 51 | usage (1) if @ARGV != 1; | ||
| 52 | |||
| 53 | $disk_fn = $ARGV[0]; | ||
| 54 | die "$disk_fn: already exists\n" if -e $disk_fn; | ||
| 55 | |||
| 56 | # Sets the loader to copy to the MBR. | ||
| 57 | sub set_loader { | ||
| 58 | die "can't specify both --loader and --no-loader\n" | ||
| 59 | if defined ($include_loader) && !$include_loader; | ||
| 60 | $include_loader = 1; | ||
| 61 | $loader_fn = $_[1] if $_[1] ne ''; | ||
| 62 | } | ||
| 63 | |||
| 64 | # Disables copying a loader to the MBR. | ||
| 65 | sub set_no_loader { | ||
| 66 | die "can't specify both --loader and --no-loader\n" | ||
| 67 | if defined ($include_loader) && $include_loader; | ||
| 68 | $include_loader = 0; | ||
| 69 | } | ||
| 70 | |||
| 71 | # Figure out whether to include a loader. | ||
| 72 | $include_loader = exists ($parts{KERNEL}) && $format eq 'partitioned' | ||
| 73 | if !defined ($include_loader); | ||
| 74 | die "can't write loader to raw disk\n" if $include_loader && $format eq 'raw'; | ||
| 75 | die "can't write command-line arguments without --loader or --kernel\n" | ||
| 76 | if @kernel_args && !$include_loader; | ||
| 77 | print STDERR "warning: --loader only makes sense without --kernel " | ||
| 78 | . "if this disk will be used to load a kernel from another disk\n" | ||
| 79 | if $include_loader && !exists ($parts{KERNEL}); | ||
| 80 | |||
| 81 | # Open disk. | ||
| 82 | my ($disk_handle); | ||
| 83 | open ($disk_handle, '>', $disk_fn) or die "$disk_fn: create: $!\n"; | ||
| 84 | |||
| 85 | # Read loader. | ||
| 86 | my ($loader); | ||
| 87 | $loader = read_loader ($loader_fn) if $include_loader; | ||
| 88 | |||
| 89 | # Write disk. | ||
| 90 | my (%disk) = %parts; | ||
| 91 | $disk{DISK} = $disk_fn; | ||
| 92 | $disk{HANDLE} = $disk_handle; | ||
| 93 | $disk{ALIGN} = $align; | ||
| 94 | $disk{GEOMETRY} = %geometry; | ||
| 95 | $disk{FORMAT} = $format; | ||
| 96 | $disk{LOADER} = $loader; | ||
| 97 | $disk{ARGS} = \@kernel_args; | ||
| 98 | assemble_disk (%disk); | ||
| 99 | |||
| 100 | # Done. | ||
| 101 | exit 0; | ||
| 102 | |||
| 103 | sub usage { | ||
| 104 | print <<'EOF'; | ||
| 105 | pintos-mkdisk, a utility for creating Pintos virtual disks | ||
| 106 | Usage: pintos-mkdisk [OPTIONS] DISK [-- ARGUMENT...] | ||
| 107 | where DISK is the virtual disk to create, | ||
| 108 | each ARGUMENT is inserted into the command line written to DISK, | ||
| 109 | and each OPTION is one of the following options. | ||
| 110 | Partition options: (where PARTITION is one of: kernel filesys scratch swap) | ||
| 111 | --PARTITION=FILE Use a copy of FILE for the given PARTITION | ||
| 112 | --PARTITION-size=SIZE Create an empty PARTITION of the given SIZE in MB | ||
| 113 | --PARTITION-from=DISK Use of a copy of the given PARTITION in DISK | ||
| 114 | (There is no --kernel-size option.) | ||
| 115 | Output disk options: | ||
| 116 | --format=partitioned Write partition table to output (default) | ||
| 117 | --format=raw Do not write partition table to output | ||
| 118 | (Pintos can only use partitioned disks.) | ||
| 119 | Partitioned format output options: | ||
| 120 | --loader[=FILE] Get bootstrap loader from FILE (default: loader.bin | ||
| 121 | if --kernel option is specified, empty otherwise) | ||
| 122 | --no-loader Do not include a bootstrap loader | ||
| 123 | --geometry=H,S Use H head, S sector geometry (default: 16, 63) | ||
| 124 | --geometry=zip Use 64 head, 32 sector geometry for USB-ZIP boot | ||
| 125 | per http://syslinux.zytor.com/usbkey.php | ||
| 126 | --align=bochs Round size to cylinder for Bochs support (default) | ||
| 127 | --align=full Align partition boundaries to cylinder boundary to | ||
| 128 | let fdisk guess correct geometry and quiet warnings | ||
| 129 | --align=none Don't align partitions at all, to save space | ||
| 130 | Other options: | ||
| 131 | -h, --help Display this help message. | ||
| 132 | EOF | ||
| 133 | exit ($_[0]); | ||
| 134 | } | ||
diff --git a/pintos-progos/utils/pintos-set-cmdline b/pintos-progos/utils/pintos-set-cmdline new file mode 100644 index 0000000..8c8f702 --- /dev/null +++ b/pintos-progos/utils/pintos-set-cmdline | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #! /usr/bin/perl -w | ||
| 2 | |||
| 3 | use strict; | ||
| 4 | use Fcntl 'SEEK_SET'; | ||
| 5 | |||
| 6 | # Read Pintos.pm from the same directory as this program. | ||
| 7 | BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; } | ||
| 8 | |||
| 9 | # Get command-line arguments. | ||
| 10 | usage (0) if @ARGV == 1 && $ARGV[0] eq '--help'; | ||
| 11 | usage (1) if @ARGV < 2 || $ARGV[1] ne '--'; | ||
| 12 | my ($disk, undef, @kernel_args) = @ARGV; | ||
| 13 | |||
| 14 | # Open disk. | ||
| 15 | my ($handle); | ||
| 16 | open ($handle, '+<', $disk) or die "$disk: open: $!\n"; | ||
| 17 | |||
| 18 | # Check that it's a partitioned disk with a Pintos loader. | ||
| 19 | my ($buffer) = read_fully ($handle, $disk, 512); | ||
| 20 | unpack ("x510 v", $buffer) == 0xaa55 or die "$disk: not a partitioned disk\n"; | ||
| 21 | $buffer =~ /Pintos/ or die "$disk: does not contain Pintos loader\n"; | ||
| 22 | |||
| 23 | # Write the command line. | ||
| 24 | our ($LOADER_SIZE); | ||
| 25 | sysseek ($handle, $LOADER_SIZE, SEEK_SET) == $LOADER_SIZE | ||
| 26 | or die "$disk: seek: $!\n"; | ||
| 27 | write_fully ($handle, $disk, make_kernel_command_line (@kernel_args)); | ||
| 28 | |||
| 29 | # Close disk. | ||
| 30 | close ($handle) or die "$disk: close: $!\n"; | ||
| 31 | |||
| 32 | exit 0; | ||
| 33 | |||
| 34 | sub usage { | ||
| 35 | print <<'EOF'; | ||
| 36 | pintos-set-cmdline, a utility for changing the command line in Pintos disks | ||
| 37 | Usage: pintos-set-cmdline DISK -- [ARGUMENT...] | ||
| 38 | where DISK is a bootable disk containing a Pintos loader | ||
| 39 | and each ARGUMENT is inserted into the command line written to DISK. | ||
| 40 | EOF | ||
| 41 | exit ($_[0]); | ||
| 42 | } | ||
diff --git a/pintos-progos/utils/setitimer-helper.c b/pintos-progos/utils/setitimer-helper.c new file mode 100644 index 0000000..772d736 --- /dev/null +++ b/pintos-progos/utils/setitimer-helper.c | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #include <errno.h> | ||
| 2 | #include <limits.h> | ||
| 3 | #include <math.h> | ||
| 4 | #include <stdio.h> | ||
| 5 | #include <stdlib.h> | ||
| 6 | #include <string.h> | ||
| 7 | #include <sys/time.h> | ||
| 8 | #include <unistd.h> | ||
| 9 | |||
| 10 | int | ||
| 11 | main (int argc, char *argv[]) | ||
| 12 | { | ||
| 13 | const char *program_name = argv[0]; | ||
| 14 | double timeout; | ||
| 15 | |||
| 16 | if (argc < 3) | ||
| 17 | { | ||
| 18 | fprintf (stderr, | ||
| 19 | "setitimer-helper: runs a program with a virtual CPU limit\n" | ||
| 20 | "usage: %s TIMEOUT PROGRAM [ARG...]\n" | ||
| 21 | " where TIMEOUT is the virtual CPU limit, in seconds,\n" | ||
| 22 | " and remaining arguments specify the program to run\n" | ||
| 23 | " and its argument.\n", | ||
| 24 | program_name); | ||
| 25 | return EXIT_FAILURE; | ||
| 26 | } | ||
| 27 | |||
| 28 | timeout = strtod (argv[1], NULL); | ||
| 29 | if (timeout >= 0.0 && timeout < LONG_MAX) | ||
| 30 | { | ||
| 31 | struct itimerval it; | ||
| 32 | |||
| 33 | it.it_interval.tv_sec = 0; | ||
| 34 | it.it_interval.tv_usec = 0; | ||
| 35 | it.it_value.tv_sec = timeout; | ||
| 36 | it.it_value.tv_usec = (timeout - floor (timeout)) * 1000000; | ||
| 37 | if (setitimer (ITIMER_VIRTUAL, &it, NULL) < 0) | ||
| 38 | fprintf (stderr, "%s: setitimer: %s\n", | ||
| 39 | program_name, strerror (errno)); | ||
| 40 | } | ||
| 41 | else | ||
| 42 | fprintf (stderr, "%s: invalid timeout value \"%s\"\n", | ||
| 43 | program_name, argv[1]); | ||
| 44 | |||
| 45 | execvp (argv[2], &argv[2]); | ||
| 46 | fprintf (stderr, "%s: couldn't exec \"%s\": %s\n", | ||
| 47 | program_name, argv[2], strerror (errno)); | ||
| 48 | return EXIT_FAILURE; | ||
| 49 | } | ||
diff --git a/pintos-progos/utils/squish-pty.c b/pintos-progos/utils/squish-pty.c new file mode 100644 index 0000000..c8375a5 --- /dev/null +++ b/pintos-progos/utils/squish-pty.c | |||
| @@ -0,0 +1,355 @@ | |||
| 1 | #define _GNU_SOURCE 1 | ||
| 2 | #include <errno.h> | ||
| 3 | #include <fcntl.h> | ||
| 4 | #include <signal.h> | ||
| 5 | #include <stdarg.h> | ||
| 6 | #include <stdbool.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <stdlib.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <stropts.h> | ||
| 11 | #include <sys/ioctl.h> | ||
| 12 | #include <sys/stat.h> | ||
| 13 | #include <sys/time.h> | ||
| 14 | #include <sys/types.h> | ||
| 15 | #include <sys/wait.h> | ||
| 16 | #include <termios.h> | ||
| 17 | #include <unistd.h> | ||
| 18 | |||
| 19 | static void | ||
| 20 | fail_io (const char *msg, ...) | ||
| 21 | __attribute__ ((noreturn)) | ||
| 22 | __attribute__ ((format (printf, 1, 2))); | ||
| 23 | |||
| 24 | /* Prints MSG, formatting as with printf(), | ||
| 25 | plus an error message based on errno, | ||
| 26 | and exits. */ | ||
| 27 | static void | ||
| 28 | fail_io (const char *msg, ...) | ||
| 29 | { | ||
| 30 | va_list args; | ||
| 31 | |||
| 32 | va_start (args, msg); | ||
| 33 | vfprintf (stderr, msg, args); | ||
| 34 | va_end (args); | ||
| 35 | |||
| 36 | if (errno != 0) | ||
| 37 | fprintf (stderr, ": %s", strerror (errno)); | ||
| 38 | putc ('\n', stderr); | ||
| 39 | exit (EXIT_FAILURE); | ||
| 40 | } | ||
| 41 | |||
| 42 | /* If FD is a terminal, configures it for noncanonical input mode | ||
| 43 | with VMIN and VTIME set as indicated. | ||
| 44 | If FD is not a terminal, has no effect. */ | ||
| 45 | static void | ||
| 46 | make_noncanon (int fd, int vmin, int vtime) | ||
| 47 | { | ||
| 48 | if (isatty (fd)) | ||
| 49 | { | ||
| 50 | struct termios termios; | ||
| 51 | if (tcgetattr (fd, &termios) < 0) | ||
| 52 | fail_io ("tcgetattr"); | ||
| 53 | termios.c_lflag &= ~(ICANON | ECHO); | ||
| 54 | termios.c_cc[VMIN] = vmin; | ||
| 55 | termios.c_cc[VTIME] = vtime; | ||
| 56 | if (tcsetattr (fd, TCSANOW, &termios) < 0) | ||
| 57 | fail_io ("tcsetattr"); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /* Make FD non-blocking if NONBLOCKING is true, | ||
| 62 | or blocking if NONBLOCKING is false. */ | ||
| 63 | static void | ||
| 64 | make_nonblocking (int fd, bool nonblocking) | ||
| 65 | { | ||
| 66 | int flags = fcntl (fd, F_GETFL); | ||
| 67 | if (flags < 0) | ||
| 68 | fail_io ("fcntl"); | ||
| 69 | if (nonblocking) | ||
| 70 | flags |= O_NONBLOCK; | ||
| 71 | else | ||
| 72 | flags &= ~O_NONBLOCK; | ||
| 73 | if (fcntl (fd, F_SETFL, flags) < 0) | ||
| 74 | fail_io ("fcntl"); | ||
| 75 | } | ||
| 76 | |||
| 77 | /* Handle a read or write on *FD, which is the pty if FD_IS_PTY | ||
| 78 | is true, that returned end-of-file or error indication RETVAL. | ||
| 79 | The system call is named CALL, for use in error messages. | ||
| 80 | Returns true if processing may continue, false if we're all | ||
| 81 | done. */ | ||
| 82 | static bool | ||
| 83 | handle_error (ssize_t retval, int *fd, bool fd_is_pty, const char *call) | ||
| 84 | { | ||
| 85 | if (fd_is_pty) | ||
| 86 | { | ||
| 87 | if (retval < 0) | ||
| 88 | { | ||
| 89 | if (errno == EIO) | ||
| 90 | { | ||
| 91 | /* Slave side of pty has been closed. */ | ||
| 92 | return false; | ||
| 93 | } | ||
| 94 | else | ||
| 95 | fail_io (call); | ||
| 96 | } | ||
| 97 | else | ||
| 98 | return true; | ||
| 99 | } | ||
| 100 | else | ||
| 101 | { | ||
| 102 | if (retval == 0) | ||
| 103 | { | ||
| 104 | close (*fd); | ||
| 105 | *fd = -1; | ||
| 106 | return true; | ||
| 107 | } | ||
| 108 | else | ||
| 109 | fail_io (call); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | /* Copies data from stdin to PTY and from PTY to stdout until no | ||
| 114 | more data can be read or written. */ | ||
| 115 | static void | ||
| 116 | relay (int pty, int dead_child_fd) | ||
| 117 | { | ||
| 118 | struct pipe | ||
| 119 | { | ||
| 120 | int in, out; | ||
| 121 | char buf[BUFSIZ]; | ||
| 122 | size_t size, ofs; | ||
| 123 | bool active; | ||
| 124 | }; | ||
| 125 | struct pipe pipes[2]; | ||
| 126 | |||
| 127 | /* Make PTY, stdin, and stdout non-blocking. */ | ||
| 128 | make_nonblocking (pty, true); | ||
| 129 | make_nonblocking (STDIN_FILENO, true); | ||
| 130 | make_nonblocking (STDOUT_FILENO, true); | ||
| 131 | |||
| 132 | /* Configure noncanonical mode on PTY and stdin to avoid | ||
| 133 | waiting for end-of-line. We want to minimize context | ||
| 134 | switching on PTY (for efficiency) and minimize latency on | ||
| 135 | stdin to avoid a laggy user experience. */ | ||
| 136 | make_noncanon (pty, 16, 1); | ||
| 137 | make_noncanon (STDIN_FILENO, 1, 0); | ||
| 138 | |||
| 139 | memset (pipes, 0, sizeof pipes); | ||
| 140 | pipes[0].in = STDIN_FILENO; | ||
| 141 | pipes[0].out = pty; | ||
| 142 | pipes[1].in = pty; | ||
| 143 | pipes[1].out = STDOUT_FILENO; | ||
| 144 | |||
| 145 | while (pipes[0].in != -1 || pipes[1].in != -1) | ||
| 146 | { | ||
| 147 | fd_set read_fds, write_fds; | ||
| 148 | int retval; | ||
| 149 | int i; | ||
| 150 | |||
| 151 | FD_ZERO (&read_fds); | ||
| 152 | FD_ZERO (&write_fds); | ||
| 153 | for (i = 0; i < 2; i++) | ||
| 154 | { | ||
| 155 | struct pipe *p = &pipes[i]; | ||
| 156 | |||
| 157 | /* Don't do anything with the stdin->pty pipe until we | ||
| 158 | have some data for the pty->stdout pipe. If we get | ||
| 159 | too eager, Bochs will throw away our input. */ | ||
| 160 | if (i == 0 && !pipes[1].active) | ||
| 161 | continue; | ||
| 162 | |||
| 163 | if (p->in != -1 && p->size + p->ofs < sizeof p->buf) | ||
| 164 | FD_SET (p->in, &read_fds); | ||
| 165 | if (p->out != -1 && p->size > 0) | ||
| 166 | FD_SET (p->out, &write_fds); | ||
| 167 | } | ||
| 168 | FD_SET (dead_child_fd, &read_fds); | ||
| 169 | |||
| 170 | do | ||
| 171 | { | ||
| 172 | retval = select (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL); | ||
| 173 | } | ||
| 174 | while (retval < 0 && errno == EINTR); | ||
| 175 | if (retval < 0) | ||
| 176 | fail_io ("select"); | ||
| 177 | |||
| 178 | if (FD_ISSET (dead_child_fd, &read_fds)) | ||
| 179 | { | ||
| 180 | /* Child died. Do final relaying. */ | ||
| 181 | struct pipe *p = &pipes[1]; | ||
| 182 | if (p->out == -1) | ||
| 183 | return; | ||
| 184 | make_nonblocking (STDOUT_FILENO, false); | ||
| 185 | for (;;) | ||
| 186 | { | ||
| 187 | ssize_t n; | ||
| 188 | |||
| 189 | /* Write buffer. */ | ||
| 190 | while (p->size > 0) | ||
| 191 | { | ||
| 192 | n = write (p->out, p->buf + p->ofs, p->size); | ||
| 193 | if (n < 0) | ||
| 194 | fail_io ("write"); | ||
| 195 | else if (n == 0) | ||
| 196 | fail_io ("zero-length write"); | ||
| 197 | p->ofs += n; | ||
| 198 | p->size -= n; | ||
| 199 | } | ||
| 200 | p->ofs = 0; | ||
| 201 | |||
| 202 | p->size = n = read (p->in, p->buf, sizeof p->buf); | ||
| 203 | if (n <= 0) | ||
| 204 | return; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | for (i = 0; i < 2; i++) | ||
| 209 | { | ||
| 210 | struct pipe *p = &pipes[i]; | ||
| 211 | if (p->in != -1 && FD_ISSET (p->in, &read_fds)) | ||
| 212 | { | ||
| 213 | ssize_t n = read (p->in, p->buf + p->ofs + p->size, | ||
| 214 | sizeof p->buf - p->ofs - p->size); | ||
| 215 | if (n > 0) | ||
| 216 | { | ||
| 217 | p->active = true; | ||
| 218 | p->size += n; | ||
| 219 | if (p->size == BUFSIZ && p->ofs != 0) | ||
| 220 | { | ||
| 221 | memmove (p->buf, p->buf + p->ofs, p->size); | ||
| 222 | p->ofs = 0; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | else if (!handle_error (n, &p->in, p->in == pty, "read")) | ||
| 226 | return; | ||
| 227 | } | ||
| 228 | if (p->out != -1 && FD_ISSET (p->out, &write_fds)) | ||
| 229 | { | ||
| 230 | ssize_t n = write (p->out, p->buf + p->ofs, p->size); | ||
| 231 | if (n > 0) | ||
| 232 | { | ||
| 233 | p->ofs += n; | ||
| 234 | p->size -= n; | ||
| 235 | if (p->size == 0) | ||
| 236 | p->ofs = 0; | ||
| 237 | } | ||
| 238 | else if (!handle_error (n, &p->out, p->out == pty, "write")) | ||
| 239 | return; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | } | ||
| 244 | |||
| 245 | static int dead_child_fd; | ||
| 246 | |||
| 247 | static void | ||
| 248 | sigchld_handler (int signo __attribute__ ((unused))) | ||
| 249 | { | ||
| 250 | if (write (dead_child_fd, "", 1) < 0) | ||
| 251 | _exit (1); | ||
| 252 | } | ||
| 253 | |||
| 254 | int | ||
| 255 | main (int argc __attribute__ ((unused)), char *argv[]) | ||
| 256 | { | ||
| 257 | int master, slave; | ||
| 258 | char *name; | ||
| 259 | pid_t pid; | ||
| 260 | struct sigaction sa; | ||
| 261 | int pipe_fds[2]; | ||
| 262 | struct itimerval zero_itimerval, old_itimerval; | ||
| 263 | |||
| 264 | if (argc < 2) | ||
| 265 | { | ||
| 266 | fprintf (stderr, | ||
| 267 | "usage: squish-pty COMMAND [ARG]...\n" | ||
| 268 | "Squishes both stdin and stdout into a single pseudoterminal,\n" | ||
| 269 | "which is passed as stdout to run the specified COMMAND.\n"); | ||
| 270 | return EXIT_FAILURE; | ||
| 271 | } | ||
| 272 | |||
| 273 | /* Open master side of pty and get ready to open slave. */ | ||
| 274 | master = open ("/dev/ptmx", O_RDWR | O_NOCTTY); | ||
| 275 | if (master < 0) | ||
| 276 | fail_io ("open \"/dev/ptmx\""); | ||
| 277 | if (grantpt (master) < 0) | ||
| 278 | fail_io ("grantpt"); | ||
| 279 | if (unlockpt (master) < 0) | ||
| 280 | fail_io ("unlockpt"); | ||
| 281 | |||
| 282 | /* Open slave side of pty. */ | ||
| 283 | name = ptsname (master); | ||
| 284 | if (name == NULL) | ||
| 285 | fail_io ("ptsname"); | ||
| 286 | slave = open (name, O_RDWR); | ||
| 287 | if (slave < 0) | ||
| 288 | fail_io ("open \"%s\"", name); | ||
| 289 | |||
| 290 | /* System V implementations need STREAMS configuration for the | ||
| 291 | slave. */ | ||
| 292 | if (isastream (slave)) | ||
| 293 | { | ||
| 294 | if (ioctl (slave, I_PUSH, "ptem") < 0 | ||
| 295 | || ioctl (slave, I_PUSH, "ldterm") < 0) | ||
| 296 | fail_io ("ioctl"); | ||
| 297 | } | ||
| 298 | |||
| 299 | /* Arrange to get notified when a child dies, by writing a byte | ||
| 300 | to a pipe fd. We really want to use pselect() and | ||
| 301 | sigprocmask(), but Solaris 2.7 doesn't have it. */ | ||
| 302 | if (pipe (pipe_fds) < 0) | ||
| 303 | fail_io ("pipe"); | ||
| 304 | dead_child_fd = pipe_fds[1]; | ||
| 305 | |||
| 306 | memset (&sa, 0, sizeof sa); | ||
| 307 | sa.sa_handler = sigchld_handler; | ||
| 308 | sigemptyset (&sa.sa_mask); | ||
| 309 | sa.sa_flags = SA_RESTART; | ||
| 310 | if (sigaction (SIGCHLD, &sa, NULL) < 0) | ||
| 311 | fail_io ("sigaction"); | ||
| 312 | |||
| 313 | /* Save the virtual interval timer, which might have been set | ||
| 314 | by the process that ran us. It really should be applied to | ||
| 315 | our child process. */ | ||
| 316 | memset (&zero_itimerval, 0, sizeof zero_itimerval); | ||
| 317 | if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, &old_itimerval) < 0) | ||
| 318 | fail_io ("setitimer"); | ||
| 319 | |||
| 320 | pid = fork (); | ||
| 321 | if (pid < 0) | ||
| 322 | fail_io ("fork"); | ||
| 323 | else if (pid != 0) | ||
| 324 | { | ||
| 325 | /* Running in parent process. */ | ||
| 326 | int status; | ||
| 327 | close (slave); | ||
| 328 | relay (master, pipe_fds[0]); | ||
| 329 | |||
| 330 | /* If the subprocess has died, die in the same fashion. | ||
| 331 | In particular, dying from SIGVTALRM tells the pintos | ||
| 332 | script that we ran out of CPU time. */ | ||
| 333 | if (waitpid (pid, &status, WNOHANG) > 0) | ||
| 334 | { | ||
| 335 | if (WIFEXITED (status)) | ||
| 336 | return WEXITSTATUS (status); | ||
| 337 | else if (WIFSIGNALED (status)) | ||
| 338 | raise (WTERMSIG (status)); | ||
| 339 | } | ||
| 340 | return 0; | ||
| 341 | } | ||
| 342 | else | ||
| 343 | { | ||
| 344 | /* Running in child process. */ | ||
| 345 | if (setitimer (ITIMER_VIRTUAL, &old_itimerval, NULL) < 0) | ||
| 346 | fail_io ("setitimer"); | ||
| 347 | if (dup2 (slave, STDOUT_FILENO) < 0) | ||
| 348 | fail_io ("dup2"); | ||
| 349 | if (close (pipe_fds[0]) < 0 || close (pipe_fds[1]) < 0 | ||
| 350 | || close (slave) < 0 || close (master) < 0) | ||
| 351 | fail_io ("close"); | ||
| 352 | execvp (argv[1], argv + 1); | ||
| 353 | fail_io ("exec"); | ||
| 354 | } | ||
| 355 | } | ||
diff --git a/pintos-progos/utils/squish-unix.c b/pintos-progos/utils/squish-unix.c new file mode 100644 index 0000000..805205b --- /dev/null +++ b/pintos-progos/utils/squish-unix.c | |||
| @@ -0,0 +1,338 @@ | |||
| 1 | #define _GNU_SOURCE 1 | ||
| 2 | #include <errno.h> | ||
| 3 | #include <fcntl.h> | ||
| 4 | #include <signal.h> | ||
| 5 | #include <stdarg.h> | ||
| 6 | #include <stdbool.h> | ||
| 7 | #include <stddef.h> | ||
| 8 | #include <stdio.h> | ||
| 9 | #include <stdlib.h> | ||
| 10 | #include <string.h> | ||
| 11 | #include <stropts.h> | ||
| 12 | #include <sys/ioctl.h> | ||
| 13 | #include <sys/stat.h> | ||
| 14 | #include <sys/time.h> | ||
| 15 | #include <sys/types.h> | ||
| 16 | #include <sys/wait.h> | ||
| 17 | #include <sys/socket.h> | ||
| 18 | #include <sys/un.h> | ||
| 19 | #include <termios.h> | ||
| 20 | #include <unistd.h> | ||
| 21 | |||
| 22 | static void | ||
| 23 | fail_io (const char *msg, ...) | ||
| 24 | __attribute__ ((noreturn)) | ||
| 25 | __attribute__ ((format (printf, 1, 2))); | ||
| 26 | |||
| 27 | /* Prints MSG, formatting as with printf(), | ||
| 28 | plus an error message based on errno, | ||
| 29 | and exits. */ | ||
| 30 | static void | ||
| 31 | fail_io (const char *msg, ...) | ||
| 32 | { | ||
| 33 | va_list args; | ||
| 34 | |||
| 35 | va_start (args, msg); | ||
| 36 | vfprintf (stderr, msg, args); | ||
| 37 | va_end (args); | ||
| 38 | |||
| 39 | if (errno != 0) | ||
| 40 | fprintf (stderr, ": %s", strerror (errno)); | ||
| 41 | putc ('\n', stderr); | ||
| 42 | exit (EXIT_FAILURE); | ||
| 43 | } | ||
| 44 | |||
| 45 | /* If FD is a terminal, configures it for noncanonical input mode | ||
| 46 | with VMIN and VTIME set as indicated. | ||
| 47 | If FD is not a terminal, has no effect. */ | ||
| 48 | static void | ||
| 49 | make_noncanon (int fd, int vmin, int vtime) | ||
| 50 | { | ||
| 51 | if (isatty (fd)) | ||
| 52 | { | ||
| 53 | struct termios termios; | ||
| 54 | if (tcgetattr (fd, &termios) < 0) | ||
| 55 | fail_io ("tcgetattr"); | ||
| 56 | termios.c_lflag &= ~(ICANON | ECHO); | ||
| 57 | termios.c_cc[VMIN] = vmin; | ||
| 58 | termios.c_cc[VTIME] = vtime; | ||
| 59 | if (tcsetattr (fd, TCSANOW, &termios) < 0) | ||
| 60 | fail_io ("tcsetattr"); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | /* Make FD non-blocking if NONBLOCKING is true, | ||
| 65 | or blocking if NONBLOCKING is false. */ | ||
| 66 | static void | ||
| 67 | make_nonblocking (int fd, bool nonblocking) | ||
| 68 | { | ||
| 69 | int flags = fcntl (fd, F_GETFL); | ||
| 70 | if (flags < 0) | ||
| 71 | fail_io ("fcntl"); | ||
| 72 | if (nonblocking) | ||
| 73 | flags |= O_NONBLOCK; | ||
| 74 | else | ||
| 75 | flags &= ~O_NONBLOCK; | ||
| 76 | if (fcntl (fd, F_SETFL, flags) < 0) | ||
| 77 | fail_io ("fcntl"); | ||
| 78 | } | ||
| 79 | |||
| 80 | /* Handle a read or write on *FD, which is the socket if | ||
| 81 | FD_IS_SOCK is true, that returned end-of-file or error | ||
| 82 | indication RETVAL. The system call is named CALL, for use in | ||
| 83 | error messages. Returns true if processing may continue, | ||
| 84 | false if we're all done. */ | ||
| 85 | static bool | ||
| 86 | handle_error (ssize_t retval, int *fd, bool fd_is_sock, const char *call) | ||
| 87 | { | ||
| 88 | if (retval == 0) | ||
| 89 | { | ||
| 90 | if (fd_is_sock) | ||
| 91 | return false; | ||
| 92 | else | ||
| 93 | { | ||
| 94 | *fd = -1; | ||
| 95 | return true; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | else | ||
| 99 | fail_io (call); | ||
| 100 | } | ||
| 101 | |||
| 102 | /* Copies data from stdin to SOCK and from SOCK to stdout until no | ||
| 103 | more data can be read or written. */ | ||
| 104 | static void | ||
| 105 | relay (int sock) | ||
| 106 | { | ||
| 107 | struct pipe | ||
| 108 | { | ||
| 109 | int in, out; | ||
| 110 | char buf[BUFSIZ]; | ||
| 111 | size_t size, ofs; | ||
| 112 | bool active; | ||
| 113 | }; | ||
| 114 | struct pipe pipes[2]; | ||
| 115 | |||
| 116 | /* In case stdin is a file, go back to the beginning. | ||
| 117 | This allows replaying the input on reset. */ | ||
| 118 | lseek (STDIN_FILENO, 0, SEEK_SET); | ||
| 119 | |||
| 120 | /* Make SOCK, stdin, and stdout non-blocking. */ | ||
| 121 | make_nonblocking (sock, true); | ||
| 122 | make_nonblocking (STDIN_FILENO, true); | ||
| 123 | make_nonblocking (STDOUT_FILENO, true); | ||
| 124 | |||
| 125 | /* Configure noncanonical mode on stdin to avoid waiting for | ||
| 126 | end-of-line. */ | ||
| 127 | make_noncanon (STDIN_FILENO, 1, 0); | ||
| 128 | |||
| 129 | memset (pipes, 0, sizeof pipes); | ||
| 130 | pipes[0].in = STDIN_FILENO; | ||
| 131 | pipes[0].out = sock; | ||
| 132 | pipes[1].in = sock; | ||
| 133 | pipes[1].out = STDOUT_FILENO; | ||
| 134 | |||
| 135 | while (pipes[0].in != -1 || pipes[1].in != -1 | ||
| 136 | || (pipes[1].size && pipes[1].out != -1)) | ||
| 137 | { | ||
| 138 | fd_set read_fds, write_fds; | ||
| 139 | sigset_t empty_set; | ||
| 140 | int retval; | ||
| 141 | int i; | ||
| 142 | |||
| 143 | FD_ZERO (&read_fds); | ||
| 144 | FD_ZERO (&write_fds); | ||
| 145 | for (i = 0; i < 2; i++) | ||
| 146 | { | ||
| 147 | struct pipe *p = &pipes[i]; | ||
| 148 | |||
| 149 | /* Don't do anything with the stdin->sock pipe until we | ||
| 150 | have some data for the sock->stdout pipe. If we get | ||
| 151 | too eager, vmplayer will throw away our input. */ | ||
| 152 | if (i == 0 && !pipes[1].active) | ||
| 153 | continue; | ||
| 154 | |||
| 155 | if (p->in != -1 && p->size + p->ofs < sizeof p->buf) | ||
| 156 | FD_SET (p->in, &read_fds); | ||
| 157 | if (p->out != -1 && p->size > 0) | ||
| 158 | FD_SET (p->out, &write_fds); | ||
| 159 | } | ||
| 160 | sigemptyset (&empty_set); | ||
| 161 | retval = pselect (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL, | ||
| 162 | &empty_set); | ||
| 163 | if (retval < 0) | ||
| 164 | { | ||
| 165 | if (errno == EINTR) | ||
| 166 | { | ||
| 167 | /* Child died. Do final relaying. */ | ||
| 168 | struct pipe *p = &pipes[1]; | ||
| 169 | if (p->out == -1) | ||
| 170 | exit (0); | ||
| 171 | make_nonblocking (STDOUT_FILENO, false); | ||
| 172 | for (;;) | ||
| 173 | { | ||
| 174 | ssize_t n; | ||
| 175 | |||
| 176 | /* Write buffer. */ | ||
| 177 | while (p->size > 0) | ||
| 178 | { | ||
| 179 | n = write (p->out, p->buf + p->ofs, p->size); | ||
| 180 | if (n < 0) | ||
| 181 | fail_io ("write"); | ||
| 182 | else if (n == 0) | ||
| 183 | fail_io ("zero-length write"); | ||
| 184 | p->ofs += n; | ||
| 185 | p->size -= n; | ||
| 186 | } | ||
| 187 | p->ofs = 0; | ||
| 188 | |||
| 189 | p->size = n = read (p->in, p->buf, sizeof p->buf); | ||
| 190 | if (n <= 0) | ||
| 191 | exit (0); | ||
| 192 | } | ||
| 193 | } | ||
| 194 | fail_io ("select"); | ||
| 195 | } | ||
| 196 | |||
| 197 | for (i = 0; i < 2; i++) | ||
| 198 | { | ||
| 199 | struct pipe *p = &pipes[i]; | ||
| 200 | if (p->in != -1 && FD_ISSET (p->in, &read_fds)) | ||
| 201 | { | ||
| 202 | ssize_t n = read (p->in, p->buf + p->ofs + p->size, | ||
| 203 | sizeof p->buf - p->ofs - p->size); | ||
| 204 | if (n > 0) | ||
| 205 | { | ||
| 206 | p->active = true; | ||
| 207 | p->size += n; | ||
| 208 | if (p->size == BUFSIZ && p->ofs != 0) | ||
| 209 | { | ||
| 210 | memmove (p->buf, p->buf + p->ofs, p->size); | ||
| 211 | p->ofs = 0; | ||
| 212 | } | ||
| 213 | } | ||
| 214 | else if (!handle_error (n, &p->in, p->in == sock, "read")) | ||
| 215 | return; | ||
| 216 | } | ||
| 217 | if (p->out != -1 && FD_ISSET (p->out, &write_fds)) | ||
| 218 | { | ||
| 219 | ssize_t n = write (p->out, p->buf + p->ofs, p->size); | ||
| 220 | if (n > 0) | ||
| 221 | { | ||
| 222 | p->ofs += n; | ||
| 223 | p->size -= n; | ||
| 224 | if (p->size == 0) | ||
| 225 | p->ofs = 0; | ||
| 226 | } | ||
| 227 | else if (!handle_error (n, &p->out, p->out == sock, "write")) | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | } | ||
| 231 | } | ||
| 232 | } | ||
| 233 | |||
| 234 | static void | ||
| 235 | sigchld_handler (int signo __attribute__ ((unused))) | ||
| 236 | { | ||
| 237 | /* Nothing to do. */ | ||
| 238 | } | ||
| 239 | |||
| 240 | int | ||
| 241 | main (int argc __attribute__ ((unused)), char *argv[]) | ||
| 242 | { | ||
| 243 | pid_t pid; | ||
| 244 | struct itimerval zero_itimerval; | ||
| 245 | struct sockaddr_un sun; | ||
| 246 | sigset_t sigchld_set; | ||
| 247 | int sock; | ||
| 248 | |||
| 249 | if (argc < 3) | ||
| 250 | { | ||
| 251 | fprintf (stderr, | ||
| 252 | "usage: squish-unix SOCKET COMMAND [ARG]...\n" | ||
| 253 | "Squishes both stdin and stdout into a single Unix domain\n" | ||
| 254 | "socket named SOCKET, and runs COMMAND as a subprocess.\n"); | ||
| 255 | return EXIT_FAILURE; | ||
| 256 | } | ||
| 257 | |||
| 258 | /* Create socket. */ | ||
| 259 | sock = socket (PF_LOCAL, SOCK_STREAM, 0); | ||
| 260 | if (sock < 0) | ||
| 261 | fail_io ("socket"); | ||
| 262 | |||
| 263 | /* Configure socket. */ | ||
| 264 | sun.sun_family = AF_LOCAL; | ||
| 265 | strncpy (sun.sun_path, argv[1], sizeof sun.sun_path); | ||
| 266 | sun.sun_path[sizeof sun.sun_path - 1] = '\0'; | ||
| 267 | if (unlink (sun.sun_path) < 0 && errno != ENOENT) | ||
| 268 | fail_io ("unlink"); | ||
| 269 | if (bind (sock, (struct sockaddr *) &sun, | ||
| 270 | (offsetof (struct sockaddr_un, sun_path) | ||
| 271 | + strlen (sun.sun_path) + 1)) < 0) | ||
| 272 | fail_io ("bind"); | ||
| 273 | |||
| 274 | /* Listen on socket. */ | ||
| 275 | if (listen (sock, 1) < 0) | ||
| 276 | fail_io ("listen"); | ||
| 277 | |||
| 278 | /* Block SIGCHLD and set up a handler for it. */ | ||
| 279 | sigemptyset (&sigchld_set); | ||
| 280 | sigaddset (&sigchld_set, SIGCHLD); | ||
| 281 | if (sigprocmask (SIG_BLOCK, &sigchld_set, NULL) < 0) | ||
| 282 | fail_io ("sigprocmask"); | ||
| 283 | if (signal (SIGCHLD, sigchld_handler) == SIG_ERR) | ||
| 284 | fail_io ("signal"); | ||
| 285 | |||
| 286 | /* Save the virtual interval timer, which might have been set | ||
| 287 | by the process that ran us. It really should be applied to | ||
| 288 | our child process. */ | ||
| 289 | memset (&zero_itimerval, 0, sizeof zero_itimerval); | ||
| 290 | if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, NULL) < 0) | ||
| 291 | fail_io ("setitimer"); | ||
| 292 | |||
| 293 | pid = fork (); | ||
| 294 | if (pid < 0) | ||
| 295 | fail_io ("fork"); | ||
| 296 | else if (pid != 0) | ||
| 297 | { | ||
| 298 | /* Running in parent process. */ | ||
| 299 | make_nonblocking (sock, true); | ||
| 300 | for (;;) | ||
| 301 | { | ||
| 302 | fd_set read_fds; | ||
| 303 | sigset_t empty_set; | ||
| 304 | int retval; | ||
| 305 | int conn; | ||
| 306 | |||
| 307 | /* Wait for connection. */ | ||
| 308 | FD_ZERO (&read_fds); | ||
| 309 | FD_SET (sock, &read_fds); | ||
| 310 | sigemptyset (&empty_set); | ||
| 311 | retval = pselect (sock + 1, &read_fds, NULL, NULL, NULL, &empty_set); | ||
| 312 | if (retval < 0) | ||
| 313 | { | ||
| 314 | if (errno == EINTR) | ||
| 315 | break; | ||
| 316 | fail_io ("select"); | ||
| 317 | } | ||
| 318 | |||
| 319 | /* Accept connection. */ | ||
| 320 | conn = accept (sock, NULL, NULL); | ||
| 321 | if (conn < 0) | ||
| 322 | fail_io ("accept"); | ||
| 323 | |||
| 324 | /* Relay connection. */ | ||
| 325 | relay (conn); | ||
| 326 | close (conn); | ||
| 327 | } | ||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | else | ||
| 331 | { | ||
| 332 | /* Running in child process. */ | ||
| 333 | if (close (sock) < 0) | ||
| 334 | fail_io ("close"); | ||
| 335 | execvp (argv[2], argv + 2); | ||
| 336 | fail_io ("exec"); | ||
| 337 | } | ||
| 338 | } | ||
diff --git a/pintos-progos/vm/.gitignore b/pintos-progos/vm/.gitignore new file mode 100644 index 0000000..6d5357c --- /dev/null +++ b/pintos-progos/vm/.gitignore | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | build | ||
| 2 | bochsrc.txt | ||
| 3 | bochsout.txt | ||
diff --git a/pintos-progos/vm/Make.vars b/pintos-progos/vm/Make.vars new file mode 100644 index 0000000..e3c33a7 --- /dev/null +++ b/pintos-progos/vm/Make.vars | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | # -*- makefile -*- | ||
| 2 | |||
| 3 | kernel.bin: DEFINES = -DUSERPROG -DFILESYS -DVM | ||
| 4 | KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys vm | ||
| 5 | TEST_SUBDIRS = tests/userprog tests/vm tests/filesys/base | ||
| 6 | GRADING_FILE = $(SRCDIR)/tests/vm/Grading | ||
| 7 | SIMULATOR = --qemu | ||
diff --git a/pintos-progos/vm/Makefile b/pintos-progos/vm/Makefile new file mode 100644 index 0000000..34c10aa --- /dev/null +++ b/pintos-progos/vm/Makefile | |||
| @@ -0,0 +1 @@ | |||
| include ../Makefile.kernel | |||
