Added pintos source and bochs support files
This commit is contained in:
commit
eb678bb2f9
718 changed files with 41025 additions and 0 deletions
53
.gitignore
vendored
Normal file
53
.gitignore
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.a
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
16
Vagrantfile
vendored
Executable file
16
Vagrantfile
vendored
Executable file
|
@ -0,0 +1,16 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "ubuntu/trusty64"
|
||||
|
||||
# Create a forwarded port mapping which allows access to a specific port
|
||||
# within the machine from a port on the host machine. In the example below,
|
||||
# accessing "localhost:8080" will access port 80 on the guest machine.
|
||||
# config.vm.network "forwarded_port", guest: 80, host: 8080
|
||||
|
||||
config.vm.hostname = "pintosvm"
|
||||
|
||||
config.vm.provision "shell", path: "bootstrap.sh"
|
||||
|
||||
config.vm.synced_folder "./pintos-env", "/pintos-env"
|
||||
end
|
12
bootstrap.sh
Executable file
12
bootstrap.sh
Executable file
|
@ -0,0 +1,12 @@
|
|||
sudo apt-get update
|
||||
sudo apt-get install -y gcc g++ gdb binutils \
|
||||
libxrandr2 libxrandr-dev \
|
||||
libncurses5-dev libncurses5
|
||||
cat <<EOF > /home/vagrant/.bash_profile
|
||||
PATH=/pintos-env/bin/:\$PATH
|
||||
PATH=/pintos-env/pintos/utils:\$PATH
|
||||
export PATH
|
||||
export PS1='\${debian_chroot:+(\$debian_chroot)}\[\e[1;31m\]\u@\h:\[\e[0m\]\w\\$ '
|
||||
|
||||
alias ls='ls --color'
|
||||
EOF
|
BIN
pintos-env/bin/bochs
Executable file
BIN
pintos-env/bin/bochs
Executable file
Binary file not shown.
BIN
pintos-env/bin/bxcommit
Executable file
BIN
pintos-env/bin/bxcommit
Executable file
Binary file not shown.
BIN
pintos-env/bin/bximage
Executable file
BIN
pintos-env/bin/bximage
Executable file
Binary file not shown.
80
pintos-env/pintos/.cproject
Executable file
80
pintos-env/pintos/.cproject
Executable file
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?fileVersion 4.0.0?>
|
||||
|
||||
<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||
<cconfiguration id="cdt.managedbuild.toolchain.gnu.macosx.base.1179132807">
|
||||
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.macosx.base.1179132807" moduleId="org.eclipse.cdt.core.settings" name="Default">
|
||||
<externalSettings/>
|
||||
<extensions>
|
||||
<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||
</extensions>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<configuration artifactName="pintos" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.macosx.base.1179132807" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
|
||||
<folderInfo id="cdt.managedbuild.toolchain.gnu.macosx.base.1179132807.1632126274" name="/" resourcePath="">
|
||||
<toolChain id="cdt.managedbuild.toolchain.gnu.macosx.base.2105506882" name="cdt.managedbuild.toolchain.gnu.macosx.base" superClass="cdt.managedbuild.toolchain.gnu.macosx.base">
|
||||
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.MachO64" id="cdt.managedbuild.target.gnu.platform.macosx.base.757578018" name="Debug Platform" osList="macosx" superClass="cdt.managedbuild.target.gnu.platform.macosx.base"/>
|
||||
<builder id="cdt.managedbuild.target.gnu.builder.macosx.base.1820658200" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.macosx.base"/>
|
||||
<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.base.589064000" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.base"/>
|
||||
<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.base.68314080" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.base">
|
||||
<inputType id="cdt.managedbuild.tool.macosx.cpp.linker.input.2077621586" superClass="cdt.managedbuild.tool.macosx.cpp.linker.input">
|
||||
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||
</inputType>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.assembler.macosx.base.758909021" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.base">
|
||||
<option id="gnu.both.asm.option.include.paths.178015061" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/pintos}""/>
|
||||
</option>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.499485113" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.2114690844" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base"/>
|
||||
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base.313293257" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base">
|
||||
<option id="gnu.cpp.compiler.option.include.paths.1975439215" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/pintos}""/>
|
||||
</option>
|
||||
<option id="gnu.cpp.compiler.option.preprocessor.def.1412815980" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" valueType="definedSymbols">
|
||||
<listOptionValue builtIn="false" value="USERPROG"/>
|
||||
</option>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1385484388" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||
</tool>
|
||||
<tool id="cdt.managedbuild.tool.gnu.c.compiler.macosx.base.890865259" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.base">
|
||||
<option id="gnu.c.compiler.option.include.paths.1306855401" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" valueType="includePath">
|
||||
<listOptionValue builtIn="false" value=""${workspace_loc:/pintos}""/>
|
||||
</option>
|
||||
<option id="gnu.c.compiler.option.preprocessor.def.symbols.1938406608" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" valueType="definedSymbols"/>
|
||||
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1795012765" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||
</tool>
|
||||
</toolChain>
|
||||
</folderInfo>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||
</cconfiguration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||
<project id="pintos.null.1822970005" name="pintos"/>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||
<storageModule moduleId="scannerConfiguration">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.1179132807;cdt.managedbuild.toolchain.gnu.macosx.base.1179132807.1632126274;cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base.313293257;cdt.managedbuild.tool.gnu.cpp.compiler.input.1385484388">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.1179132807;cdt.managedbuild.toolchain.gnu.macosx.base.1179132807.1632126274;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.890865259;cdt.managedbuild.tool.gnu.c.compiler.input.1795012765">
|
||||
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||
</scannerConfigBuildInfo>
|
||||
</storageModule>
|
||||
<storageModule moduleId="refreshScope" versionNumber="2">
|
||||
<configuration configurationName="Default">
|
||||
<resource resourceType="PROJECT" workspacePath="/pintos"/>
|
||||
</configuration>
|
||||
</storageModule>
|
||||
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
|
||||
</cproject>
|
27
pintos-env/pintos/.project
Executable file
27
pintos-env/pintos/.project
Executable file
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>pintos</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
95
pintos-env/pintos/LICENSE
Executable file
95
pintos-env/pintos/LICENSE
Executable file
|
@ -0,0 +1,95 @@
|
|||
Pintos, including its documentation, is subject to the following
|
||||
license:
|
||||
|
||||
Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford
|
||||
Jr. University. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
A few individual files in Pintos were originally derived from other
|
||||
projects, but they have been extensively modified for use in Pintos.
|
||||
The original code falls under the original license, and modifications
|
||||
for Pintos are additionally covered by the Pintos license above.
|
||||
|
||||
In particular, code derived from Nachos is subject to the following
|
||||
license:
|
||||
|
||||
/* Copyright (c) 1992-1996 The Regents of the University of California.
|
||||
All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software
|
||||
and its documentation for any purpose, without fee, and
|
||||
without written agreement is hereby granted, provided that the
|
||||
above copyright notice and the following two paragraphs appear
|
||||
in all copies of this software.
|
||||
|
||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
|
||||
ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
|
||||
AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
|
||||
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
|
||||
BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
||||
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
||||
MODIFICATIONS.
|
||||
*/
|
||||
|
||||
Also, code derived from MIT's 6.828 course code is subject to the
|
||||
following license:
|
||||
|
||||
/*
|
||||
* Copyright (C) 1997 Massachusetts Institute of Technology
|
||||
*
|
||||
* This software is being provided by the copyright holders under the
|
||||
* following license. By obtaining, using and/or copying this software,
|
||||
* you agree that you have read, understood, and will comply with the
|
||||
* following terms and conditions:
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose and without fee or royalty is
|
||||
* hereby granted, provided that the full text of this NOTICE appears on
|
||||
* ALL copies of the software and documentation or portions thereof,
|
||||
* including modifications, that you make.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
|
||||
* REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
|
||||
* BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
|
||||
* WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
|
||||
* THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
|
||||
* THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
|
||||
* HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
|
||||
* DOCUMENTATION.
|
||||
*
|
||||
* The name and trademarks of copyright holders may NOT be used in
|
||||
* advertising or publicity pertaining to the software without specific,
|
||||
* written prior permission. Title to copyright in this software and any
|
||||
* associated documentation will at all times remain with copyright
|
||||
* holders. See the file AUTHORS which should have accompanied this software
|
||||
* for a list of all copyright holders.
|
||||
*
|
||||
* This file may be derived from previously copyrighted software. This
|
||||
* copyright applies only to those changes made by the copyright
|
||||
* holders listed in the AUTHORS file. The rest of this file is covered by
|
||||
* the copyright notices, if any, listed below.
|
||||
*/
|
68
pintos-env/pintos/Make.config
Executable file
68
pintos-env/pintos/Make.config
Executable file
|
@ -0,0 +1,68 @@
|
|||
# -*- makefile -*-
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
VPATH = $(SRCDIR)
|
||||
|
||||
# Binary utilities.
|
||||
# If the host appears to be x86, use the normal tools.
|
||||
# If it's x86-64, use the compiler and linker in 32-bit mode.
|
||||
# Otherwise assume cross-tools are installed as i386-elf-*.
|
||||
X86 = i.86\|pentium.*\|[pk][56]\|nexgen\|viac3\|6x86\|athlon.*\|i86pc
|
||||
X86_64 = x86_64
|
||||
ifneq (0, $(shell expr `uname -m` : '$(X86)'))
|
||||
CC = gcc
|
||||
LD = ld
|
||||
OBJCOPY = objcopy
|
||||
else
|
||||
ifneq (0, $(shell expr `uname -m` : '$(X86_64)'))
|
||||
ifneq (0, $(shell expr `uname -s` : 'Darwin'))
|
||||
CC = i386-elf-gcc
|
||||
LD = i386-elf-ld
|
||||
AR = i386-elf-ar
|
||||
RANLIB = i386-elf-ranlib
|
||||
OBJCOPY = i386-elf-objcopy
|
||||
else
|
||||
CC = gcc -m32 -fno-stack-protector
|
||||
LD = ld -melf_i386
|
||||
AR = ar
|
||||
RANLIB = ranlib
|
||||
OBJCOPY = objcopy
|
||||
endif
|
||||
else
|
||||
CC = i386-elf-gcc
|
||||
LD = i386-elf-ld
|
||||
AR = i386-elf-ar
|
||||
RANLIB = i386-elf-ranlib
|
||||
OBJCOPY = i386-elf-objcopy
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(strip $(shell command -v $(CC) 2> /dev/null)),)
|
||||
$(warning *** Compiler ($(CC)) not found. Did you set $$PATH properly? Please refer to the Getting Started section in the documentation for details. ***)
|
||||
endif
|
||||
|
||||
# Compiler and assembler invocation.
|
||||
DEFINES =
|
||||
WARNINGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes -Wsystem-headers
|
||||
CFLAGS = -g -msoft-float -O
|
||||
CPPFLAGS = -nostdinc -I$(SRCDIR) -I$(SRCDIR)/lib
|
||||
ASFLAGS = -Wa,--gstabs
|
||||
LDFLAGS =
|
||||
DEPS = -MMD -MF $(@:.o=.d)
|
||||
|
||||
# Turn off -fstack-protector, which we don't support.
|
||||
ifeq ($(strip $(shell echo | $(CC) -fno-stack-protector -E - > /dev/null 2>&1; echo $$?)),0)
|
||||
CFLAGS += -fno-stack-protector
|
||||
endif
|
||||
|
||||
# Turn off --build-id in the linker, which confuses the Pintos loader.
|
||||
ifeq ($(strip $(shell $(LD) --help | grep -q build-id; echo $$?)),0)
|
||||
LDFLAGS += -Wl,--build-id=none
|
||||
endif
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(DEFINES) $(DEPS)
|
||||
|
||||
%.o: %.S
|
||||
$(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) $(DEPS)
|
29
pintos-env/pintos/Makefile
Executable file
29
pintos-env/pintos/Makefile
Executable file
|
@ -0,0 +1,29 @@
|
|||
BUILD_SUBDIRS = threads userprog vm filesys
|
||||
|
||||
all::
|
||||
@echo "Run 'make' in subdirectories: $(BUILD_SUBDIRS)."
|
||||
@echo "This top-level make has only 'clean' targets."
|
||||
|
||||
CLEAN_SUBDIRS = $(BUILD_SUBDIRS) examples utils
|
||||
|
||||
clean::
|
||||
for d in $(CLEAN_SUBDIRS); do $(MAKE) -C $$d $@; done
|
||||
rm -f TAGS tags
|
||||
|
||||
distclean:: clean
|
||||
find . -name '*~' -exec rm '{}' \;
|
||||
|
||||
TAGS_SUBDIRS = $(BUILD_SUBDIRS) devices lib
|
||||
TAGS_SOURCES = find $(TAGS_SUBDIRS) -name \*.[chS] -print
|
||||
|
||||
TAGS::
|
||||
etags --members `$(TAGS_SOURCES)`
|
||||
|
||||
tags::
|
||||
ctags -T --no-warn `$(TAGS_SOURCES)`
|
||||
|
||||
cscope.files::
|
||||
$(TAGS_SOURCES) > cscope.files
|
||||
|
||||
cscope:: cscope.files
|
||||
cscope -b -q -k
|
109
pintos-env/pintos/Makefile.build
Executable file
109
pintos-env/pintos/Makefile.build
Executable file
|
@ -0,0 +1,109 @@
|
|||
# -*- makefile -*-
|
||||
|
||||
SRCDIR = ../..
|
||||
|
||||
all: kernel.bin loader.bin
|
||||
|
||||
include ../../Make.config
|
||||
include ../Make.vars
|
||||
include ../../tests/Make.tests
|
||||
|
||||
# Compiler and assembler options.
|
||||
kernel.bin: CPPFLAGS += -I$(SRCDIR)/lib/kernel
|
||||
|
||||
# Core kernel.
|
||||
threads_SRC = threads/start.S # Startup code.
|
||||
threads_SRC += threads/init.c # Main program.
|
||||
threads_SRC += threads/thread.c # Thread management core.
|
||||
threads_SRC += threads/switch.S # Thread switch routine.
|
||||
threads_SRC += threads/interrupt.c # Interrupt core.
|
||||
threads_SRC += threads/intr-stubs.S # Interrupt stubs.
|
||||
threads_SRC += threads/synch.c # Synchronization.
|
||||
threads_SRC += threads/palloc.c # Page allocator.
|
||||
threads_SRC += threads/malloc.c # Subpage allocator.
|
||||
|
||||
# Device driver code.
|
||||
devices_SRC = devices/pit.c # Programmable interrupt timer chip.
|
||||
devices_SRC += devices/timer.c # Periodic timer device.
|
||||
devices_SRC += devices/kbd.c # Keyboard device.
|
||||
devices_SRC += devices/vga.c # Video device.
|
||||
devices_SRC += devices/serial.c # Serial port device.
|
||||
devices_SRC += devices/block.c # Block device abstraction layer.
|
||||
devices_SRC += devices/partition.c # Partition block device.
|
||||
devices_SRC += devices/ide.c # IDE disk block device.
|
||||
devices_SRC += devices/input.c # Serial and keyboard input.
|
||||
devices_SRC += devices/intq.c # Interrupt queue.
|
||||
devices_SRC += devices/rtc.c # Real-time clock.
|
||||
devices_SRC += devices/shutdown.c # Reboot and power off.
|
||||
devices_SRC += devices/speaker.c # PC speaker.
|
||||
|
||||
# Library code shared between kernel and user programs.
|
||||
lib_SRC = lib/debug.c # Debug helpers.
|
||||
lib_SRC += lib/random.c # Pseudo-random numbers.
|
||||
lib_SRC += lib/stdio.c # I/O library.
|
||||
lib_SRC += lib/stdlib.c # Utility functions.
|
||||
lib_SRC += lib/string.c # String functions.
|
||||
lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC.
|
||||
lib_SRC += lib/ustar.c # Unix standard tar format utilities.
|
||||
|
||||
# Kernel-specific library code.
|
||||
lib/kernel_SRC = lib/kernel/debug.c # Debug helpers.
|
||||
lib/kernel_SRC += lib/kernel/list.c # Doubly-linked lists.
|
||||
lib/kernel_SRC += lib/kernel/bitmap.c # Bitmaps.
|
||||
lib/kernel_SRC += lib/kernel/hash.c # Hash tables.
|
||||
lib/kernel_SRC += lib/kernel/console.c # printf(), putchar().
|
||||
|
||||
# User process code.
|
||||
userprog_SRC = userprog/process.c # Process loading.
|
||||
userprog_SRC += userprog/pagedir.c # Page directories.
|
||||
userprog_SRC += userprog/exception.c # User exception handler.
|
||||
userprog_SRC += userprog/syscall.c # System call handler.
|
||||
userprog_SRC += userprog/gdt.c # GDT initialization.
|
||||
userprog_SRC += userprog/tss.c # TSS management.
|
||||
|
||||
# No virtual memory code yet.
|
||||
#vm_SRC = vm/file.c # Some file.
|
||||
|
||||
# Filesystem code.
|
||||
filesys_SRC = filesys/filesys.c # Filesystem core.
|
||||
filesys_SRC += filesys/free-map.c # Free sector bitmap.
|
||||
filesys_SRC += filesys/file.c # Files.
|
||||
filesys_SRC += filesys/directory.c # Directories.
|
||||
filesys_SRC += filesys/inode.c # File headers.
|
||||
filesys_SRC += filesys/fsutil.c # Utilities.
|
||||
|
||||
SOURCES = $(foreach dir,$(KERNEL_SUBDIRS),$($(dir)_SRC))
|
||||
OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES)))
|
||||
DEPENDS = $(patsubst %.o,%.d,$(OBJECTS))
|
||||
|
||||
threads/kernel.lds.s: CPPFLAGS += -P
|
||||
threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h
|
||||
|
||||
kernel.o: threads/kernel.lds.s $(OBJECTS)
|
||||
$(LD) -T $< -o $@ $(OBJECTS)
|
||||
|
||||
kernel.bin: kernel.o
|
||||
$(OBJCOPY) -R .note -R .comment -S $< $@
|
||||
|
||||
threads/loader.o: threads/loader.S
|
||||
$(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES)
|
||||
|
||||
loader.bin: threads/loader.o
|
||||
$(LD) -N -e 0 -Ttext 0x7c00 --oformat binary -o $@ $<
|
||||
|
||||
os.dsk: kernel.bin
|
||||
cat $^ > $@
|
||||
|
||||
clean::
|
||||
rm -f $(OBJECTS) $(DEPENDS)
|
||||
rm -f threads/loader.o threads/kernel.lds.s threads/loader.d
|
||||
rm -f kernel.bin.tmp
|
||||
rm -f kernel.o kernel.lds.s
|
||||
rm -f kernel.bin loader.bin
|
||||
rm -f bochsout.txt bochsrc.txt
|
||||
rm -f results grade
|
||||
|
||||
Makefile: $(SRCDIR)/Makefile.build
|
||||
cp $< $@
|
||||
|
||||
-include $(DEPENDS)
|
20
pintos-env/pintos/Makefile.kernel
Executable file
20
pintos-env/pintos/Makefile.kernel
Executable file
|
@ -0,0 +1,20 @@
|
|||
# -*- makefile -*-
|
||||
|
||||
all:
|
||||
|
||||
include Make.vars
|
||||
|
||||
DIRS = $(sort $(addprefix build/,$(KERNEL_SUBDIRS) $(TEST_SUBDIRS) lib/user))
|
||||
|
||||
all grade check: $(DIRS) build/Makefile
|
||||
cd build && $(MAKE) $@
|
||||
$(DIRS):
|
||||
mkdir -p $@
|
||||
build/Makefile: ../Makefile.build
|
||||
cp $< $@
|
||||
|
||||
build/%: $(DIRS) build/Makefile
|
||||
cd build && $(MAKE) $*
|
||||
|
||||
clean:
|
||||
rm -rf build
|
52
pintos-env/pintos/Makefile.userprog
Executable file
52
pintos-env/pintos/Makefile.userprog
Executable file
|
@ -0,0 +1,52 @@
|
|||
# -*- makefile -*-
|
||||
|
||||
$(PROGS): CPPFLAGS += -I$(SRCDIR)/lib/user -I.
|
||||
|
||||
# Linker flags.
|
||||
$(PROGS): LDFLAGS += -nostdlib -static -Wl,-T,$(LDSCRIPT)
|
||||
$(PROGS): LDSCRIPT = $(SRCDIR)/lib/user/user.lds
|
||||
|
||||
# Library code shared between kernel and user programs.
|
||||
lib_SRC = lib/debug.c # Debug code.
|
||||
lib_SRC += lib/random.c # Pseudo-random numbers.
|
||||
lib_SRC += lib/stdio.c # I/O library.
|
||||
lib_SRC += lib/stdlib.c # Utility functions.
|
||||
lib_SRC += lib/string.c # String functions.
|
||||
lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC.
|
||||
lib_SRC += lib/ustar.c # Unix standard tar format utilities.
|
||||
|
||||
# User level only library code.
|
||||
lib/user_SRC = lib/user/debug.c # Debug helpers.
|
||||
lib/user_SRC += lib/user/syscall.c # System calls.
|
||||
lib/user_SRC += lib/user/console.c # Console code.
|
||||
|
||||
LIB_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(lib_SRC) $(lib/user_SRC)))
|
||||
LIB_DEP = $(patsubst %.o,%.d,$(LIB_OBJ))
|
||||
LIB = lib/user/entry.o libc.a
|
||||
|
||||
PROGS_SRC = $(foreach prog,$(PROGS),$($(prog)_SRC))
|
||||
PROGS_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PROGS_SRC)))
|
||||
PROGS_DEP = $(patsubst %.o,%.d,$(PROGS_OBJ))
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
define TEMPLATE
|
||||
$(1)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(1)_SRC)))
|
||||
$(1): $$($(1)_OBJ) $$(LIB) $$(LDSCRIPT)
|
||||
$$(CC) $$(LDFLAGS) $$($(1)_OBJ) $$(LIB) -o $$@
|
||||
endef
|
||||
|
||||
$(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog))))
|
||||
|
||||
libc.a: $(LIB_OBJ)
|
||||
rm -f $@
|
||||
$(AR) -r $@ $^
|
||||
$(RANLIB) $@
|
||||
|
||||
clean::
|
||||
rm -f $(PROGS) $(PROGS_OBJ) $(PROGS_DEP)
|
||||
rm -f $(LIB_DEP) $(LIB_OBJ) lib/user/entry.[do] libc.a
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
-include $(LIB_DEP) $(PROGS_DEP)
|
63
pintos-env/pintos/README
Executable file
63
pintos-env/pintos/README
Executable file
|
@ -0,0 +1,63 @@
|
|||
### Pintos on Mac###
|
||||
Pintos Mac - Pintos on Mac
|
||||
|
||||
Release Version: 0.1
|
||||
Release Data: 2012/9/17
|
||||
Contact: Jeremy Mao(maojie@me.com or yujie.mao@intel.com)
|
||||
Homepage: https://github.com/maojie/pintos_mac
|
||||
|
||||
Content
|
||||
1. Overview
|
||||
2. Getting Started
|
||||
3. Building Pintos
|
||||
4. Running Pintos
|
||||
|
||||
* 1 OVERVIEW
|
||||
Welcome to Pintos. Pintos is a simple operating system framework for the 80x86 architecture. It supports kernel threads, loading and running user programs, and a file system, but it implements all of these in a very simple way. In the Pintos projects, you and your project team will strengthen its support in all three of these areas. You will also add a virtual memory implementation.
|
||||
|
||||
The orignal experimental environment of Pintos is on *nix (Unix and Linux), since MacOSX comes from Unix, I am curious to make Pintos workable on MacOSX. The following instructions can help you setup your experimental environment.
|
||||
|
||||
* 2 Getting Started
|
||||
|
||||
2.0 Prerequisites
|
||||
Before next step, you at least need
|
||||
1. Hardware: Macbook, Macbook Pro, Macbook Air, Mac Mini or iMac
|
||||
2. Software: OS X Mountaion Lion (My environment), maybe workable on Lion and Snow Leopard
|
||||
|
||||
2.0.1 Setup cross build environment
|
||||
To set up the pintos working environment on mac, you should
|
||||
1. Download pintos source code from github
|
||||
git clone https://github.com/maojie/pintos_mac.git
|
||||
2. Download MacPorts from
|
||||
http://www.macports.org
|
||||
3. Install cross build tools
|
||||
sudo port install i386-elf-binutils
|
||||
sudo port install i386-elf-gcc
|
||||
4. Create gcc link
|
||||
cd /opt/local/bin
|
||||
sudo ln -s i386-elf-gcc i386-elf-gcc-4.3.2
|
||||
5. Install bochs
|
||||
sudo port install bochs
|
||||
6. Install X on Mac from
|
||||
http://xquartz.macosforge.org
|
||||
|
||||
* 3 Building Pintos
|
||||
As the next step, build the source code supplied for the first project. First, cd into the "threads" directory. Then, issue the "make" command. This will create a "build" directory under "theads", populate it with a "Makefile" and a few subdirectories, and then build the kernel inside. The entire build should take less than 30 second.
|
||||
|
||||
Watch the commands executed during the build. On the Linux machines, the ordinary system tools are used.
|
||||
Following the build, the following are the interesting files in the "build" directory:
|
||||
"Makefile"
|
||||
A copy of "pintos/src/Makefile.build". It describes how to build the kernel.
|
||||
"kernel.o"
|
||||
Object file for the entire kernel. This is the result of linking object files compiled from each individual kernel source file into a single object file. It contains debug infomation, so you can run GDB or backtrace on it.
|
||||
"kernel.bin"
|
||||
Memory image of the kernel, that is, the exact bytes loaded into memory to run the Pintos kernel. This is just "kernel.o" with debug information stripped out, which saves a lot of space, which in turn keeps the kernel from bumping up against a 512KB size limit imposed by the kernel loader's design.
|
||||
"loader.bin"
|
||||
Memory image for the kernel loader, a small chunk of code written in assembly language that reads the kernel from disk into memory and starts it up. It is exactly 512 bytes long, a size fixed by the PC BIOS.
|
||||
|
||||
Subdirectories of "build" contain object files (".o") and dependency files (".d"), both produced by the compiler. The dependency file tell make which source files need to be recompiled when other source or header files are changed.
|
||||
|
||||
* 4 Running Pintos
|
||||
cd threads/build
|
||||
pintos -- run alarm-single
|
||||
(TO BE CONTINUED)
|
223
pintos-env/pintos/devices/block.c
Executable file
223
pintos-env/pintos/devices/block.c
Executable file
|
@ -0,0 +1,223 @@
|
|||
#include "devices/block.h"
|
||||
#include <list.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "devices/ide.h"
|
||||
#include "threads/malloc.h"
|
||||
|
||||
/* A block device. */
|
||||
struct block
|
||||
{
|
||||
struct list_elem list_elem; /* Element in all_blocks. */
|
||||
|
||||
char name[16]; /* Block device name. */
|
||||
enum block_type type; /* Type of block device. */
|
||||
block_sector_t size; /* Size in sectors. */
|
||||
|
||||
const struct block_operations *ops; /* Driver operations. */
|
||||
void *aux; /* Extra data owned by driver. */
|
||||
|
||||
unsigned long long read_cnt; /* Number of sectors read. */
|
||||
unsigned long long write_cnt; /* Number of sectors written. */
|
||||
};
|
||||
|
||||
/* List of all block devices. */
|
||||
static struct list all_blocks = LIST_INITIALIZER (all_blocks);
|
||||
|
||||
/* The block block assigned to each Pintos role. */
|
||||
static struct block *block_by_role[BLOCK_ROLE_CNT];
|
||||
|
||||
static struct block *list_elem_to_block (struct list_elem *);
|
||||
|
||||
/* Returns a human-readable name for the given block device
|
||||
TYPE. */
|
||||
const char *
|
||||
block_type_name (enum block_type type)
|
||||
{
|
||||
static const char *block_type_names[BLOCK_CNT] =
|
||||
{
|
||||
"kernel",
|
||||
"filesys",
|
||||
"scratch",
|
||||
"swap",
|
||||
"raw",
|
||||
"foreign",
|
||||
};
|
||||
|
||||
ASSERT (type < BLOCK_CNT);
|
||||
return block_type_names[type];
|
||||
}
|
||||
|
||||
/* Returns the block device fulfilling the given ROLE, or a null
|
||||
pointer if no block device has been assigned that role. */
|
||||
struct block *
|
||||
block_get_role (enum block_type role)
|
||||
{
|
||||
ASSERT (role < BLOCK_ROLE_CNT);
|
||||
return block_by_role[role];
|
||||
}
|
||||
|
||||
/* Assigns BLOCK the given ROLE. */
|
||||
void
|
||||
block_set_role (enum block_type role, struct block *block)
|
||||
{
|
||||
ASSERT (role < BLOCK_ROLE_CNT);
|
||||
block_by_role[role] = block;
|
||||
}
|
||||
|
||||
/* Returns the first block device in kernel probe order, or a
|
||||
null pointer if no block devices are registered. */
|
||||
struct block *
|
||||
block_first (void)
|
||||
{
|
||||
return list_elem_to_block (list_begin (&all_blocks));
|
||||
}
|
||||
|
||||
/* Returns the block device following BLOCK in kernel probe
|
||||
order, or a null pointer if BLOCK is the last block device. */
|
||||
struct block *
|
||||
block_next (struct block *block)
|
||||
{
|
||||
return list_elem_to_block (list_next (&block->list_elem));
|
||||
}
|
||||
|
||||
/* Returns the block device with the given NAME, or a null
|
||||
pointer if no block device has that name. */
|
||||
struct block *
|
||||
block_get_by_name (const char *name)
|
||||
{
|
||||
struct list_elem *e;
|
||||
|
||||
for (e = list_begin (&all_blocks); e != list_end (&all_blocks);
|
||||
e = list_next (e))
|
||||
{
|
||||
struct block *block = list_entry (e, struct block, list_elem);
|
||||
if (!strcmp (name, block->name))
|
||||
return block;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Verifies that SECTOR is a valid offset within BLOCK.
|
||||
Panics if not. */
|
||||
static void
|
||||
check_sector (struct block *block, block_sector_t sector)
|
||||
{
|
||||
if (sector >= block->size)
|
||||
{
|
||||
/* We do not use ASSERT because we want to panic here
|
||||
regardless of whether NDEBUG is defined. */
|
||||
PANIC ("Access past end of device %s (sector=%"PRDSNu", "
|
||||
"size=%"PRDSNu")\n", block_name (block), sector, block->size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reads sector SECTOR from BLOCK into BUFFER, which must
|
||||
have room for BLOCK_SECTOR_SIZE bytes.
|
||||
Internally synchronizes accesses to block devices, so external
|
||||
per-block device locking is unneeded. */
|
||||
void
|
||||
block_read (struct block *block, block_sector_t sector, void *buffer)
|
||||
{
|
||||
check_sector (block, sector);
|
||||
block->ops->read (block->aux, sector, buffer);
|
||||
block->read_cnt++;
|
||||
}
|
||||
|
||||
/* Write sector SECTOR to BLOCK from BUFFER, which must contain
|
||||
BLOCK_SECTOR_SIZE bytes. Returns after the block device has
|
||||
acknowledged receiving the data.
|
||||
Internally synchronizes accesses to block devices, so external
|
||||
per-block device locking is unneeded. */
|
||||
void
|
||||
block_write (struct block *block, block_sector_t sector, const void *buffer)
|
||||
{
|
||||
check_sector (block, sector);
|
||||
ASSERT (block->type != BLOCK_FOREIGN);
|
||||
block->ops->write (block->aux, sector, buffer);
|
||||
block->write_cnt++;
|
||||
}
|
||||
|
||||
/* Returns the number of sectors in BLOCK. */
|
||||
block_sector_t
|
||||
block_size (struct block *block)
|
||||
{
|
||||
return block->size;
|
||||
}
|
||||
|
||||
/* Returns BLOCK's name (e.g. "hda"). */
|
||||
const char *
|
||||
block_name (struct block *block)
|
||||
{
|
||||
return block->name;
|
||||
}
|
||||
|
||||
/* Returns BLOCK's type. */
|
||||
enum block_type
|
||||
block_type (struct block *block)
|
||||
{
|
||||
return block->type;
|
||||
}
|
||||
|
||||
/* Prints statistics for each block device used for a Pintos role. */
|
||||
void
|
||||
block_print_stats (void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < BLOCK_CNT; i++)
|
||||
{
|
||||
struct block *block = block_by_role[i];
|
||||
if (block != NULL)
|
||||
{
|
||||
printf ("%s (%s): %llu reads, %llu writes\n",
|
||||
block->name, block_type_name (block->type),
|
||||
block->read_cnt, block->write_cnt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Registers a new block device with the given NAME. If
|
||||
EXTRA_INFO is non-null, it is printed as part of a user
|
||||
message. The block device's SIZE in sectors and its TYPE must
|
||||
be provided, as well as the it operation functions OPS, which
|
||||
will be passed AUX in each function call. */
|
||||
struct block *
|
||||
block_register (const char *name, enum block_type type,
|
||||
const char *extra_info, block_sector_t size,
|
||||
const struct block_operations *ops, void *aux)
|
||||
{
|
||||
struct block *block = malloc (sizeof *block);
|
||||
if (block == NULL)
|
||||
PANIC ("Failed to allocate memory for block device descriptor");
|
||||
|
||||
list_push_back (&all_blocks, &block->list_elem);
|
||||
strlcpy (block->name, name, sizeof block->name);
|
||||
block->type = type;
|
||||
block->size = size;
|
||||
block->ops = ops;
|
||||
block->aux = aux;
|
||||
block->read_cnt = 0;
|
||||
block->write_cnt = 0;
|
||||
|
||||
printf ("%s: %'"PRDSNu" sectors (", block->name, block->size);
|
||||
print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE);
|
||||
printf (")");
|
||||
if (extra_info != NULL)
|
||||
printf (", %s", extra_info);
|
||||
printf ("\n");
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/* Returns the block device corresponding to LIST_ELEM, or a null
|
||||
pointer if LIST_ELEM is the list end of all_blocks. */
|
||||
static struct block *
|
||||
list_elem_to_block (struct list_elem *list_elem)
|
||||
{
|
||||
return (list_elem != list_end (&all_blocks)
|
||||
? list_entry (list_elem, struct block, list_elem)
|
||||
: NULL);
|
||||
}
|
||||
|
74
pintos-env/pintos/devices/block.h
Executable file
74
pintos-env/pintos/devices/block.h
Executable file
|
@ -0,0 +1,74 @@
|
|||
#ifndef DEVICES_BLOCK_H
|
||||
#define DEVICES_BLOCK_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* Size of a block device sector in bytes.
|
||||
All IDE disks use this sector size, as do most USB and SCSI
|
||||
disks. It's not worth it to try to cater to other sector
|
||||
sizes in Pintos (yet). */
|
||||
#define BLOCK_SECTOR_SIZE 512
|
||||
|
||||
/* Index of a block device sector.
|
||||
Good enough for devices up to 2 TB. */
|
||||
typedef uint32_t block_sector_t;
|
||||
|
||||
/* Format specifier for printf(), e.g.:
|
||||
printf ("sector=%"PRDSNu"\n", sector); */
|
||||
#define PRDSNu PRIu32
|
||||
|
||||
/* Higher-level interface for file systems, etc. */
|
||||
|
||||
struct block;
|
||||
|
||||
/* Type of a block device. */
|
||||
enum block_type
|
||||
{
|
||||
/* Block device types that play a role in Pintos. */
|
||||
BLOCK_KERNEL, /* Pintos OS kernel. */
|
||||
BLOCK_FILESYS, /* File system. */
|
||||
BLOCK_SCRATCH, /* Scratch. */
|
||||
BLOCK_SWAP, /* Swap. */
|
||||
BLOCK_ROLE_CNT,
|
||||
|
||||
/* Other kinds of block devices that Pintos may see but does
|
||||
not interact with. */
|
||||
BLOCK_RAW = BLOCK_ROLE_CNT, /* "Raw" device with unidentified contents. */
|
||||
BLOCK_FOREIGN, /* Owned by non-Pintos operating system. */
|
||||
BLOCK_CNT /* Number of Pintos block types. */
|
||||
};
|
||||
|
||||
const char *block_type_name (enum block_type);
|
||||
|
||||
/* Finding block devices. */
|
||||
struct block *block_get_role (enum block_type);
|
||||
void block_set_role (enum block_type, struct block *);
|
||||
struct block *block_get_by_name (const char *name);
|
||||
|
||||
struct block *block_first (void);
|
||||
struct block *block_next (struct block *);
|
||||
|
||||
/* Block device operations. */
|
||||
block_sector_t block_size (struct block *);
|
||||
void block_read (struct block *, block_sector_t, void *);
|
||||
void block_write (struct block *, block_sector_t, const void *);
|
||||
const char *block_name (struct block *);
|
||||
enum block_type block_type (struct block *);
|
||||
|
||||
/* Statistics. */
|
||||
void block_print_stats (void);
|
||||
|
||||
/* Lower-level interface to block device drivers. */
|
||||
|
||||
struct block_operations
|
||||
{
|
||||
void (*read) (void *aux, block_sector_t, void *buffer);
|
||||
void (*write) (void *aux, block_sector_t, const void *buffer);
|
||||
};
|
||||
|
||||
struct block *block_register (const char *name, enum block_type,
|
||||
const char *extra_info, block_sector_t size,
|
||||
const struct block_operations *, void *aux);
|
||||
|
||||
#endif /* devices/block.h */
|
527
pintos-env/pintos/devices/ide.c
Executable file
527
pintos-env/pintos/devices/ide.c
Executable file
|
@ -0,0 +1,527 @@
|
|||
#include "devices/ide.h"
|
||||
#include <ctype.h>
|
||||
#include <debug.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "devices/block.h"
|
||||
#include "devices/partition.h"
|
||||
#include "devices/timer.h"
|
||||
#include "threads/io.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/synch.h"
|
||||
|
||||
/* The code in this file is an interface to an ATA (IDE)
|
||||
controller. It attempts to comply to [ATA-3]. */
|
||||
|
||||
/* ATA command block port addresses. */
|
||||
#define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */
|
||||
#define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */
|
||||
#define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) /* Sector Count. */
|
||||
#define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) /* LBA 0:7. */
|
||||
#define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) /* LBA 15:8. */
|
||||
#define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) /* LBA 23:16. */
|
||||
#define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) /* Device/LBA 27:24. */
|
||||
#define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) /* Status (r/o). */
|
||||
#define reg_command(CHANNEL) reg_status (CHANNEL) /* Command (w/o). */
|
||||
|
||||
/* ATA control block port addresses.
|
||||
(If we supported non-legacy ATA controllers this would not be
|
||||
flexible enough, but it's fine for what we do.) */
|
||||
#define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) /* Control (w/o). */
|
||||
#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alt Status (r/o). */
|
||||
|
||||
/* Alternate Status Register bits. */
|
||||
#define STA_BSY 0x80 /* Busy. */
|
||||
#define STA_DRDY 0x40 /* Device Ready. */
|
||||
#define STA_DRQ 0x08 /* Data Request. */
|
||||
|
||||
/* Control Register bits. */
|
||||
#define CTL_SRST 0x04 /* Software Reset. */
|
||||
|
||||
/* Device Register bits. */
|
||||
#define DEV_MBS 0xa0 /* Must be set. */
|
||||
#define DEV_LBA 0x40 /* Linear based addressing. */
|
||||
#define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */
|
||||
|
||||
/* Commands.
|
||||
Many more are defined but this is the small subset that we
|
||||
use. */
|
||||
#define CMD_IDENTIFY_DEVICE 0xec /* IDENTIFY DEVICE. */
|
||||
#define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */
|
||||
#define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */
|
||||
|
||||
/* An ATA device. */
|
||||
struct ata_disk
|
||||
{
|
||||
char name[8]; /* Name, e.g. "hda". */
|
||||
struct channel *channel; /* Channel that disk is attached to. */
|
||||
int dev_no; /* Device 0 or 1 for master or slave. */
|
||||
bool is_ata; /* Is device an ATA disk? */
|
||||
};
|
||||
|
||||
/* An ATA channel (aka controller).
|
||||
Each channel can control up to two disks. */
|
||||
struct channel
|
||||
{
|
||||
char name[8]; /* Name, e.g. "ide0". */
|
||||
uint16_t reg_base; /* Base I/O port. */
|
||||
uint8_t irq; /* Interrupt in use. */
|
||||
|
||||
struct lock lock; /* Must acquire to access the controller. */
|
||||
bool expecting_interrupt; /* True if an interrupt is expected, false if
|
||||
any interrupt would be spurious. */
|
||||
struct semaphore completion_wait; /* Up'd by interrupt handler. */
|
||||
|
||||
struct ata_disk devices[2]; /* The devices on this channel. */
|
||||
};
|
||||
|
||||
/* We support the two "legacy" ATA channels found in a standard PC. */
|
||||
#define CHANNEL_CNT 2
|
||||
static struct channel channels[CHANNEL_CNT];
|
||||
|
||||
static struct block_operations ide_operations;
|
||||
|
||||
static void reset_channel (struct channel *);
|
||||
static bool check_device_type (struct ata_disk *);
|
||||
static void identify_ata_device (struct ata_disk *);
|
||||
|
||||
static void select_sector (struct ata_disk *, block_sector_t);
|
||||
static void issue_pio_command (struct channel *, uint8_t command);
|
||||
static void input_sector (struct channel *, void *);
|
||||
static void output_sector (struct channel *, const void *);
|
||||
|
||||
static void wait_until_idle (const struct ata_disk *);
|
||||
static bool wait_while_busy (const struct ata_disk *);
|
||||
static void select_device (const struct ata_disk *);
|
||||
static void select_device_wait (const struct ata_disk *);
|
||||
|
||||
static void interrupt_handler (struct intr_frame *);
|
||||
|
||||
/* Initialize the disk subsystem and detect disks. */
|
||||
void
|
||||
ide_init (void)
|
||||
{
|
||||
size_t chan_no;
|
||||
|
||||
for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++)
|
||||
{
|
||||
struct channel *c = &channels[chan_no];
|
||||
int dev_no;
|
||||
|
||||
/* Initialize channel. */
|
||||
snprintf (c->name, sizeof c->name, "ide%zu", chan_no);
|
||||
switch (chan_no)
|
||||
{
|
||||
case 0:
|
||||
c->reg_base = 0x1f0;
|
||||
c->irq = 14 + 0x20;
|
||||
break;
|
||||
case 1:
|
||||
c->reg_base = 0x170;
|
||||
c->irq = 15 + 0x20;
|
||||
break;
|
||||
default:
|
||||
NOT_REACHED ();
|
||||
}
|
||||
lock_init (&c->lock);
|
||||
c->expecting_interrupt = false;
|
||||
sema_init (&c->completion_wait, 0);
|
||||
|
||||
/* Initialize devices. */
|
||||
for (dev_no = 0; dev_no < 2; dev_no++)
|
||||
{
|
||||
struct ata_disk *d = &c->devices[dev_no];
|
||||
snprintf (d->name, sizeof d->name,
|
||||
"hd%c", 'a' + chan_no * 2 + dev_no);
|
||||
d->channel = c;
|
||||
d->dev_no = dev_no;
|
||||
d->is_ata = false;
|
||||
}
|
||||
|
||||
/* Register interrupt handler. */
|
||||
intr_register_ext (c->irq, interrupt_handler, c->name);
|
||||
|
||||
/* Reset hardware. */
|
||||
reset_channel (c);
|
||||
|
||||
/* Distinguish ATA hard disks from other devices. */
|
||||
if (check_device_type (&c->devices[0]))
|
||||
check_device_type (&c->devices[1]);
|
||||
|
||||
/* Read hard disk identity information. */
|
||||
for (dev_no = 0; dev_no < 2; dev_no++)
|
||||
if (c->devices[dev_no].is_ata)
|
||||
identify_ata_device (&c->devices[dev_no]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Disk detection and identification. */
|
||||
|
||||
static char *descramble_ata_string (char *, int size);
|
||||
|
||||
/* Resets an ATA channel and waits for any devices present on it
|
||||
to finish the reset. */
|
||||
static void
|
||||
reset_channel (struct channel *c)
|
||||
{
|
||||
bool present[2];
|
||||
int dev_no;
|
||||
|
||||
/* The ATA reset sequence depends on which devices are present,
|
||||
so we start by detecting device presence. */
|
||||
for (dev_no = 0; dev_no < 2; dev_no++)
|
||||
{
|
||||
struct ata_disk *d = &c->devices[dev_no];
|
||||
|
||||
select_device (d);
|
||||
|
||||
outb (reg_nsect (c), 0x55);
|
||||
outb (reg_lbal (c), 0xaa);
|
||||
|
||||
outb (reg_nsect (c), 0xaa);
|
||||
outb (reg_lbal (c), 0x55);
|
||||
|
||||
outb (reg_nsect (c), 0x55);
|
||||
outb (reg_lbal (c), 0xaa);
|
||||
|
||||
present[dev_no] = (inb (reg_nsect (c)) == 0x55
|
||||
&& inb (reg_lbal (c)) == 0xaa);
|
||||
}
|
||||
|
||||
/* Issue soft reset sequence, which selects device 0 as a side effect.
|
||||
Also enable interrupts. */
|
||||
outb (reg_ctl (c), 0);
|
||||
timer_usleep (10);
|
||||
outb (reg_ctl (c), CTL_SRST);
|
||||
timer_usleep (10);
|
||||
outb (reg_ctl (c), 0);
|
||||
|
||||
timer_msleep (150);
|
||||
|
||||
/* Wait for device 0 to clear BSY. */
|
||||
if (present[0])
|
||||
{
|
||||
select_device (&c->devices[0]);
|
||||
wait_while_busy (&c->devices[0]);
|
||||
}
|
||||
|
||||
/* Wait for device 1 to clear BSY. */
|
||||
if (present[1])
|
||||
{
|
||||
int i;
|
||||
|
||||
select_device (&c->devices[1]);
|
||||
for (i = 0; i < 3000; i++)
|
||||
{
|
||||
if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1)
|
||||
break;
|
||||
timer_msleep (10);
|
||||
}
|
||||
wait_while_busy (&c->devices[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Checks whether device D is an ATA disk and sets D's is_ata
|
||||
member appropriately. If D is device 0 (master), returns true
|
||||
if it's possible that a slave (device 1) exists on this
|
||||
channel. If D is device 1 (slave), the return value is not
|
||||
meaningful. */
|
||||
static bool
|
||||
check_device_type (struct ata_disk *d)
|
||||
{
|
||||
struct channel *c = d->channel;
|
||||
uint8_t error, lbam, lbah, status;
|
||||
|
||||
select_device (d);
|
||||
|
||||
error = inb (reg_error (c));
|
||||
lbam = inb (reg_lbam (c));
|
||||
lbah = inb (reg_lbah (c));
|
||||
status = inb (reg_status (c));
|
||||
|
||||
if ((error != 1 && (error != 0x81 || d->dev_no == 1))
|
||||
|| (status & STA_DRDY) == 0
|
||||
|| (status & STA_BSY) != 0)
|
||||
{
|
||||
d->is_ata = false;
|
||||
return error != 0x81;
|
||||
}
|
||||
else
|
||||
{
|
||||
d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sends an IDENTIFY DEVICE command to disk D and reads the
|
||||
response. Registers the disk with the block device
|
||||
layer. */
|
||||
static void
|
||||
identify_ata_device (struct ata_disk *d)
|
||||
{
|
||||
struct channel *c = d->channel;
|
||||
char id[BLOCK_SECTOR_SIZE];
|
||||
block_sector_t capacity;
|
||||
char *model, *serial;
|
||||
char extra_info[128];
|
||||
struct block *block;
|
||||
|
||||
ASSERT (d->is_ata);
|
||||
|
||||
/* Send the IDENTIFY DEVICE command, wait for an interrupt
|
||||
indicating the device's response is ready, and read the data
|
||||
into our buffer. */
|
||||
select_device_wait (d);
|
||||
issue_pio_command (c, CMD_IDENTIFY_DEVICE);
|
||||
sema_down (&c->completion_wait);
|
||||
if (!wait_while_busy (d))
|
||||
{
|
||||
d->is_ata = false;
|
||||
return;
|
||||
}
|
||||
input_sector (c, id);
|
||||
|
||||
/* Calculate capacity.
|
||||
Read model name and serial number. */
|
||||
capacity = *(uint32_t *) &id[60 * 2];
|
||||
model = descramble_ata_string (&id[10 * 2], 20);
|
||||
serial = descramble_ata_string (&id[27 * 2], 40);
|
||||
snprintf (extra_info, sizeof extra_info,
|
||||
"model \"%s\", serial \"%s\"", model, serial);
|
||||
|
||||
/* Disable access to IDE disks over 1 GB, which are likely
|
||||
physical IDE disks rather than virtual ones. If we don't
|
||||
allow access to those, we're less likely to scribble on
|
||||
someone's important data. You can disable this check by
|
||||
hand if you really want to do so. */
|
||||
if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE)
|
||||
{
|
||||
printf ("%s: ignoring ", d->name);
|
||||
print_human_readable_size (capacity * 512);
|
||||
printf ("disk for safety\n");
|
||||
d->is_ata = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Register. */
|
||||
block = block_register (d->name, BLOCK_RAW, extra_info, capacity,
|
||||
&ide_operations, d);
|
||||
partition_scan (block);
|
||||
}
|
||||
|
||||
/* Translates STRING, which consists of SIZE bytes in a funky
|
||||
format, into a null-terminated string in-place. Drops
|
||||
trailing whitespace and null bytes. Returns STRING. */
|
||||
static char *
|
||||
descramble_ata_string (char *string, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Swap all pairs of bytes. */
|
||||
for (i = 0; i + 1 < size; i += 2)
|
||||
{
|
||||
char tmp = string[i];
|
||||
string[i] = string[i + 1];
|
||||
string[i + 1] = tmp;
|
||||
}
|
||||
|
||||
/* Find the last non-white, non-null character. */
|
||||
for (size--; size > 0; size--)
|
||||
{
|
||||
int c = string[size - 1];
|
||||
if (c != '\0' && !isspace (c))
|
||||
break;
|
||||
}
|
||||
string[size] = '\0';
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
/* Reads sector SEC_NO from disk D into BUFFER, which must have
|
||||
room for BLOCK_SECTOR_SIZE bytes.
|
||||
Internally synchronizes accesses to disks, so external
|
||||
per-disk locking is unneeded. */
|
||||
static void
|
||||
ide_read (void *d_, block_sector_t sec_no, void *buffer)
|
||||
{
|
||||
struct ata_disk *d = d_;
|
||||
struct channel *c = d->channel;
|
||||
lock_acquire (&c->lock);
|
||||
select_sector (d, sec_no);
|
||||
issue_pio_command (c, CMD_READ_SECTOR_RETRY);
|
||||
sema_down (&c->completion_wait);
|
||||
if (!wait_while_busy (d))
|
||||
PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
|
||||
input_sector (c, buffer);
|
||||
lock_release (&c->lock);
|
||||
}
|
||||
|
||||
/* Write sector SEC_NO to disk D from BUFFER, which must contain
|
||||
BLOCK_SECTOR_SIZE bytes. Returns after the disk has
|
||||
acknowledged receiving the data.
|
||||
Internally synchronizes accesses to disks, so external
|
||||
per-disk locking is unneeded. */
|
||||
static void
|
||||
ide_write (void *d_, block_sector_t sec_no, const void *buffer)
|
||||
{
|
||||
struct ata_disk *d = d_;
|
||||
struct channel *c = d->channel;
|
||||
lock_acquire (&c->lock);
|
||||
select_sector (d, sec_no);
|
||||
issue_pio_command (c, CMD_WRITE_SECTOR_RETRY);
|
||||
if (!wait_while_busy (d))
|
||||
PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
|
||||
output_sector (c, buffer);
|
||||
sema_down (&c->completion_wait);
|
||||
lock_release (&c->lock);
|
||||
}
|
||||
|
||||
static struct block_operations ide_operations =
|
||||
{
|
||||
ide_read,
|
||||
ide_write
|
||||
};
|
||||
|
||||
/* Selects device D, waiting for it to become ready, and then
|
||||
writes SEC_NO to the disk's sector selection registers. (We
|
||||
use LBA mode.) */
|
||||
static void
|
||||
select_sector (struct ata_disk *d, block_sector_t sec_no)
|
||||
{
|
||||
struct channel *c = d->channel;
|
||||
|
||||
ASSERT (sec_no < (1UL << 28));
|
||||
|
||||
select_device_wait (d);
|
||||
outb (reg_nsect (c), 1);
|
||||
outb (reg_lbal (c), sec_no);
|
||||
outb (reg_lbam (c), sec_no >> 8);
|
||||
outb (reg_lbah (c), (sec_no >> 16));
|
||||
outb (reg_device (c),
|
||||
DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24));
|
||||
}
|
||||
|
||||
/* Writes COMMAND to channel C and prepares for receiving a
|
||||
completion interrupt. */
|
||||
static void
|
||||
issue_pio_command (struct channel *c, uint8_t command)
|
||||
{
|
||||
/* Interrupts must be enabled or our semaphore will never be
|
||||
up'd by the completion handler. */
|
||||
ASSERT (intr_get_level () == INTR_ON);
|
||||
|
||||
c->expecting_interrupt = true;
|
||||
outb (reg_command (c), command);
|
||||
}
|
||||
|
||||
/* Reads a sector from channel C's data register in PIO mode into
|
||||
SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */
|
||||
static void
|
||||
input_sector (struct channel *c, void *sector)
|
||||
{
|
||||
insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
|
||||
}
|
||||
|
||||
/* Writes SECTOR to channel C's data register in PIO mode.
|
||||
SECTOR must contain BLOCK_SECTOR_SIZE bytes. */
|
||||
static void
|
||||
output_sector (struct channel *c, const void *sector)
|
||||
{
|
||||
outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
|
||||
}
|
||||
|
||||
/* Low-level ATA primitives. */
|
||||
|
||||
/* Wait up to 10 seconds for the controller to become idle, that
|
||||
is, for the BSY and DRQ bits to clear in the status register.
|
||||
|
||||
As a side effect, reading the status register clears any
|
||||
pending interrupt. */
|
||||
static void
|
||||
wait_until_idle (const struct ata_disk *d)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 1000; i++)
|
||||
{
|
||||
if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0)
|
||||
return;
|
||||
timer_usleep (10);
|
||||
}
|
||||
|
||||
printf ("%s: idle timeout\n", d->name);
|
||||
}
|
||||
|
||||
/* Wait up to 30 seconds for disk D to clear BSY,
|
||||
and then return the status of the DRQ bit.
|
||||
The ATA standards say that a disk may take as long as that to
|
||||
complete its reset. */
|
||||
static bool
|
||||
wait_while_busy (const struct ata_disk *d)
|
||||
{
|
||||
struct channel *c = d->channel;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3000; i++)
|
||||
{
|
||||
if (i == 700)
|
||||
printf ("%s: busy, waiting...", d->name);
|
||||
if (!(inb (reg_alt_status (c)) & STA_BSY))
|
||||
{
|
||||
if (i >= 700)
|
||||
printf ("ok\n");
|
||||
return (inb (reg_alt_status (c)) & STA_DRQ) != 0;
|
||||
}
|
||||
timer_msleep (10);
|
||||
}
|
||||
|
||||
printf ("failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Program D's channel so that D is now the selected disk. */
|
||||
static void
|
||||
select_device (const struct ata_disk *d)
|
||||
{
|
||||
struct channel *c = d->channel;
|
||||
uint8_t dev = DEV_MBS;
|
||||
if (d->dev_no == 1)
|
||||
dev |= DEV_DEV;
|
||||
outb (reg_device (c), dev);
|
||||
inb (reg_alt_status (c));
|
||||
timer_nsleep (400);
|
||||
}
|
||||
|
||||
/* Select disk D in its channel, as select_device(), but wait for
|
||||
the channel to become idle before and after. */
|
||||
static void
|
||||
select_device_wait (const struct ata_disk *d)
|
||||
{
|
||||
wait_until_idle (d);
|
||||
select_device (d);
|
||||
wait_until_idle (d);
|
||||
}
|
||||
|
||||
/* ATA interrupt handler. */
|
||||
static void
|
||||
interrupt_handler (struct intr_frame *f)
|
||||
{
|
||||
struct channel *c;
|
||||
|
||||
for (c = channels; c < channels + CHANNEL_CNT; c++)
|
||||
if (f->vec_no == c->irq)
|
||||
{
|
||||
if (c->expecting_interrupt)
|
||||
{
|
||||
inb (reg_status (c)); /* Acknowledge interrupt. */
|
||||
sema_up (&c->completion_wait); /* Wake up waiter. */
|
||||
}
|
||||
else
|
||||
printf ("%s: unexpected interrupt\n", c->name);
|
||||
return;
|
||||
}
|
||||
|
||||
NOT_REACHED ();
|
||||
}
|
||||
|
||||
|
6
pintos-env/pintos/devices/ide.h
Executable file
6
pintos-env/pintos/devices/ide.h
Executable file
|
@ -0,0 +1,6 @@
|
|||
#ifndef DEVICES_IDE_H
|
||||
#define DEVICES_IDE_H
|
||||
|
||||
void ide_init (void);
|
||||
|
||||
#endif /* devices/ide.h */
|
52
pintos-env/pintos/devices/input.c
Executable file
52
pintos-env/pintos/devices/input.c
Executable file
|
@ -0,0 +1,52 @@
|
|||
#include "devices/input.h"
|
||||
#include <debug.h>
|
||||
#include "devices/intq.h"
|
||||
#include "devices/serial.h"
|
||||
|
||||
/* Stores keys from the keyboard and serial port. */
|
||||
static struct intq buffer;
|
||||
|
||||
/* Initializes the input buffer. */
|
||||
void
|
||||
input_init (void)
|
||||
{
|
||||
intq_init (&buffer);
|
||||
}
|
||||
|
||||
/* Adds a key to the input buffer.
|
||||
Interrupts must be off and the buffer must not be full. */
|
||||
void
|
||||
input_putc (uint8_t key)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
ASSERT (!intq_full (&buffer));
|
||||
|
||||
intq_putc (&buffer, key);
|
||||
serial_notify ();
|
||||
}
|
||||
|
||||
/* Retrieves a key from the input buffer.
|
||||
If the buffer is empty, waits for a key to be pressed. */
|
||||
uint8_t
|
||||
input_getc (void)
|
||||
{
|
||||
enum intr_level old_level;
|
||||
uint8_t key;
|
||||
|
||||
old_level = intr_disable ();
|
||||
key = intq_getc (&buffer);
|
||||
serial_notify ();
|
||||
intr_set_level (old_level);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/* Returns true if the input buffer is full,
|
||||
false otherwise.
|
||||
Interrupts must be off. */
|
||||
bool
|
||||
input_full (void)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
return intq_full (&buffer);
|
||||
}
|
12
pintos-env/pintos/devices/input.h
Executable file
12
pintos-env/pintos/devices/input.h
Executable file
|
@ -0,0 +1,12 @@
|
|||
#ifndef DEVICES_INPUT_H
|
||||
#define DEVICES_INPUT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void input_init (void);
|
||||
void input_putc (uint8_t);
|
||||
uint8_t input_getc (void);
|
||||
bool input_full (void);
|
||||
|
||||
#endif /* devices/input.h */
|
114
pintos-env/pintos/devices/intq.c
Executable file
114
pintos-env/pintos/devices/intq.c
Executable file
|
@ -0,0 +1,114 @@
|
|||
#include "devices/intq.h"
|
||||
#include <debug.h>
|
||||
#include "threads/thread.h"
|
||||
|
||||
static int next (int pos);
|
||||
static void wait (struct intq *q, struct thread **waiter);
|
||||
static void signal (struct intq *q, struct thread **waiter);
|
||||
|
||||
/* Initializes interrupt queue Q. */
|
||||
void
|
||||
intq_init (struct intq *q)
|
||||
{
|
||||
lock_init (&q->lock);
|
||||
q->not_full = q->not_empty = NULL;
|
||||
q->head = q->tail = 0;
|
||||
}
|
||||
|
||||
/* Returns true if Q is empty, false otherwise. */
|
||||
bool
|
||||
intq_empty (const struct intq *q)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
return q->head == q->tail;
|
||||
}
|
||||
|
||||
/* Returns true if Q is full, false otherwise. */
|
||||
bool
|
||||
intq_full (const struct intq *q)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
return next (q->head) == q->tail;
|
||||
}
|
||||
|
||||
/* Removes a byte from Q and returns it.
|
||||
If Q is empty, sleeps until a byte is added.
|
||||
When called from an interrupt handler, Q must not be empty. */
|
||||
uint8_t
|
||||
intq_getc (struct intq *q)
|
||||
{
|
||||
uint8_t byte;
|
||||
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
while (intq_empty (q))
|
||||
{
|
||||
ASSERT (!intr_context ());
|
||||
lock_acquire (&q->lock);
|
||||
wait (q, &q->not_empty);
|
||||
lock_release (&q->lock);
|
||||
}
|
||||
|
||||
byte = q->buf[q->tail];
|
||||
q->tail = next (q->tail);
|
||||
signal (q, &q->not_full);
|
||||
return byte;
|
||||
}
|
||||
|
||||
/* Adds BYTE to the end of Q.
|
||||
If Q is full, sleeps until a byte is removed.
|
||||
When called from an interrupt handler, Q must not be full. */
|
||||
void
|
||||
intq_putc (struct intq *q, uint8_t byte)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
while (intq_full (q))
|
||||
{
|
||||
ASSERT (!intr_context ());
|
||||
lock_acquire (&q->lock);
|
||||
wait (q, &q->not_full);
|
||||
lock_release (&q->lock);
|
||||
}
|
||||
|
||||
q->buf[q->head] = byte;
|
||||
q->head = next (q->head);
|
||||
signal (q, &q->not_empty);
|
||||
}
|
||||
|
||||
/* Returns the position after POS within an intq. */
|
||||
static int
|
||||
next (int pos)
|
||||
{
|
||||
return (pos + 1) % INTQ_BUFSIZE;
|
||||
}
|
||||
|
||||
/* WAITER must be the address of Q's not_empty or not_full
|
||||
member. Waits until the given condition is true. */
|
||||
static void
|
||||
wait (struct intq *q UNUSED, struct thread **waiter)
|
||||
{
|
||||
ASSERT (!intr_context ());
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
ASSERT ((waiter == &q->not_empty && intq_empty (q))
|
||||
|| (waiter == &q->not_full && intq_full (q)));
|
||||
|
||||
*waiter = thread_current ();
|
||||
thread_block ();
|
||||
}
|
||||
|
||||
/* WAITER must be the address of Q's not_empty or not_full
|
||||
member, and the associated condition must be true. If a
|
||||
thread is waiting for the condition, wakes it up and resets
|
||||
the waiting thread. */
|
||||
static void
|
||||
signal (struct intq *q UNUSED, struct thread **waiter)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
ASSERT ((waiter == &q->not_empty && !intq_empty (q))
|
||||
|| (waiter == &q->not_full && !intq_full (q)));
|
||||
|
||||
if (*waiter != NULL)
|
||||
{
|
||||
thread_unblock (*waiter);
|
||||
*waiter = NULL;
|
||||
}
|
||||
}
|
43
pintos-env/pintos/devices/intq.h
Executable file
43
pintos-env/pintos/devices/intq.h
Executable file
|
@ -0,0 +1,43 @@
|
|||
#ifndef DEVICES_INTQ_H
|
||||
#define DEVICES_INTQ_H
|
||||
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/synch.h"
|
||||
|
||||
/* An "interrupt queue", a circular buffer shared between
|
||||
kernel threads and external interrupt handlers.
|
||||
|
||||
Interrupt queue functions can be called from kernel threads or
|
||||
from external interrupt handlers. Except for intq_init(),
|
||||
interrupts must be off in either case.
|
||||
|
||||
The interrupt queue has the structure of a "monitor". Locks
|
||||
and condition variables from threads/synch.h cannot be used in
|
||||
this case, as they normally would, because they can only
|
||||
protect kernel threads from one another, not from interrupt
|
||||
handlers. */
|
||||
|
||||
/* Queue buffer size, in bytes. */
|
||||
#define INTQ_BUFSIZE 64
|
||||
|
||||
/* A circular queue of bytes. */
|
||||
struct intq
|
||||
{
|
||||
/* Waiting threads. */
|
||||
struct lock lock; /* Only one thread may wait at once. */
|
||||
struct thread *not_full; /* Thread waiting for not-full condition. */
|
||||
struct thread *not_empty; /* Thread waiting for not-empty condition. */
|
||||
|
||||
/* Queue. */
|
||||
uint8_t buf[INTQ_BUFSIZE]; /* Buffer. */
|
||||
int head; /* New data is written here. */
|
||||
int tail; /* Old data is read here. */
|
||||
};
|
||||
|
||||
void intq_init (struct intq *);
|
||||
bool intq_empty (const struct intq *);
|
||||
bool intq_full (const struct intq *);
|
||||
uint8_t intq_getc (struct intq *);
|
||||
void intq_putc (struct intq *, uint8_t);
|
||||
|
||||
#endif /* devices/intq.h */
|
213
pintos-env/pintos/devices/kbd.c
Executable file
213
pintos-env/pintos/devices/kbd.c
Executable file
|
@ -0,0 +1,213 @@
|
|||
#include "devices/kbd.h"
|
||||
#include <ctype.h>
|
||||
#include <debug.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "devices/input.h"
|
||||
#include "devices/shutdown.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/io.h"
|
||||
|
||||
/* Keyboard data register port. */
|
||||
#define DATA_REG 0x60
|
||||
|
||||
/* Current state of shift keys.
|
||||
True if depressed, false otherwise. */
|
||||
static bool left_shift, right_shift; /* Left and right Shift keys. */
|
||||
static bool left_alt, right_alt; /* Left and right Alt keys. */
|
||||
static bool left_ctrl, right_ctrl; /* Left and right Ctl keys. */
|
||||
|
||||
/* Status of Caps Lock.
|
||||
True when on, false when off. */
|
||||
static bool caps_lock;
|
||||
|
||||
/* Number of keys pressed. */
|
||||
static int64_t key_cnt;
|
||||
|
||||
static intr_handler_func keyboard_interrupt;
|
||||
|
||||
/* Initializes the keyboard. */
|
||||
void
|
||||
kbd_init (void)
|
||||
{
|
||||
intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
|
||||
}
|
||||
|
||||
/* Prints keyboard statistics. */
|
||||
void
|
||||
kbd_print_stats (void)
|
||||
{
|
||||
printf ("Keyboard: %lld keys pressed\n", key_cnt);
|
||||
}
|
||||
|
||||
/* Maps a set of contiguous scancodes into characters. */
|
||||
struct keymap
|
||||
{
|
||||
uint8_t first_scancode; /* First scancode. */
|
||||
const char *chars; /* chars[0] has scancode first_scancode,
|
||||
chars[1] has scancode first_scancode + 1,
|
||||
and so on to the end of the string. */
|
||||
};
|
||||
|
||||
/* Keys that produce the same characters regardless of whether
|
||||
the Shift keys are down. Case of letters is an exception
|
||||
that we handle elsewhere. */
|
||||
static const struct keymap invariant_keymap[] =
|
||||
{
|
||||
{0x01, "\033"}, /* Escape. */
|
||||
{0x0e, "\b"},
|
||||
{0x0f, "\tQWERTYUIOP"},
|
||||
{0x1c, "\r"},
|
||||
{0x1e, "ASDFGHJKL"},
|
||||
{0x2c, "ZXCVBNM"},
|
||||
{0x37, "*"},
|
||||
{0x39, " "},
|
||||
{0x53, "\177"}, /* Delete. */
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
/* Characters for keys pressed without Shift, for those keys
|
||||
where it matters. */
|
||||
static const struct keymap unshifted_keymap[] =
|
||||
{
|
||||
{0x02, "1234567890-="},
|
||||
{0x1a, "[]"},
|
||||
{0x27, ";'`"},
|
||||
{0x2b, "\\"},
|
||||
{0x33, ",./"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
/* Characters for keys pressed with Shift, for those keys where
|
||||
it matters. */
|
||||
static const struct keymap shifted_keymap[] =
|
||||
{
|
||||
{0x02, "!@#$%^&*()_+"},
|
||||
{0x1a, "{}"},
|
||||
{0x27, ":\"~"},
|
||||
{0x2b, "|"},
|
||||
{0x33, "<>?"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
|
||||
|
||||
static void
|
||||
keyboard_interrupt (struct intr_frame *args UNUSED)
|
||||
{
|
||||
/* Status of shift keys. */
|
||||
bool shift = left_shift || right_shift;
|
||||
bool alt = left_alt || right_alt;
|
||||
bool ctrl = left_ctrl || right_ctrl;
|
||||
|
||||
/* Keyboard scancode. */
|
||||
unsigned code;
|
||||
|
||||
/* False if key pressed, true if key released. */
|
||||
bool release;
|
||||
|
||||
/* Character that corresponds to `code'. */
|
||||
uint8_t c;
|
||||
|
||||
/* Read scancode, including second byte if prefix code. */
|
||||
code = inb (DATA_REG);
|
||||
if (code == 0xe0)
|
||||
code = (code << 8) | inb (DATA_REG);
|
||||
|
||||
/* Bit 0x80 distinguishes key press from key release
|
||||
(even if there's a prefix). */
|
||||
release = (code & 0x80) != 0;
|
||||
code &= ~0x80u;
|
||||
|
||||
/* Interpret key. */
|
||||
if (code == 0x3a)
|
||||
{
|
||||
/* Caps Lock. */
|
||||
if (!release)
|
||||
caps_lock = !caps_lock;
|
||||
}
|
||||
else if (map_key (invariant_keymap, code, &c)
|
||||
|| (!shift && map_key (unshifted_keymap, code, &c))
|
||||
|| (shift && map_key (shifted_keymap, code, &c)))
|
||||
{
|
||||
/* Ordinary character. */
|
||||
if (!release)
|
||||
{
|
||||
/* Reboot if Ctrl+Alt+Del pressed. */
|
||||
if (c == 0177 && ctrl && alt)
|
||||
shutdown_reboot ();
|
||||
|
||||
/* Handle Ctrl, Shift.
|
||||
Note that Ctrl overrides Shift. */
|
||||
if (ctrl && c >= 0x40 && c < 0x60)
|
||||
{
|
||||
/* A is 0x41, Ctrl+A is 0x01, etc. */
|
||||
c -= 0x40;
|
||||
}
|
||||
else if (shift == caps_lock)
|
||||
c = tolower (c);
|
||||
|
||||
/* Handle Alt by setting the high bit.
|
||||
This 0x80 is unrelated to the one used to
|
||||
distinguish key press from key release. */
|
||||
if (alt)
|
||||
c += 0x80;
|
||||
|
||||
/* Append to keyboard buffer. */
|
||||
if (!input_full ())
|
||||
{
|
||||
key_cnt++;
|
||||
input_putc (c);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Maps a keycode into a shift state variable. */
|
||||
struct shift_key
|
||||
{
|
||||
unsigned scancode;
|
||||
bool *state_var;
|
||||
};
|
||||
|
||||
/* Table of shift keys. */
|
||||
static const struct shift_key shift_keys[] =
|
||||
{
|
||||
{ 0x2a, &left_shift},
|
||||
{ 0x36, &right_shift},
|
||||
{ 0x38, &left_alt},
|
||||
{0xe038, &right_alt},
|
||||
{ 0x1d, &left_ctrl},
|
||||
{0xe01d, &right_ctrl},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
const struct shift_key *key;
|
||||
|
||||
/* Scan the table. */
|
||||
for (key = shift_keys; key->scancode != 0; key++)
|
||||
if (key->scancode == code)
|
||||
{
|
||||
*key->state_var = !release;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Scans the array of keymaps K for SCANCODE.
|
||||
If found, sets *C to the corresponding character and returns
|
||||
true.
|
||||
If not found, returns false and C is ignored. */
|
||||
static bool
|
||||
map_key (const struct keymap k[], unsigned scancode, uint8_t *c)
|
||||
{
|
||||
for (; k->first_scancode != 0; k++)
|
||||
if (scancode >= k->first_scancode
|
||||
&& scancode < k->first_scancode + strlen (k->chars))
|
||||
{
|
||||
*c = k->chars[scancode - k->first_scancode];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
9
pintos-env/pintos/devices/kbd.h
Executable file
9
pintos-env/pintos/devices/kbd.h
Executable file
|
@ -0,0 +1,9 @@
|
|||
#ifndef DEVICES_KBD_H
|
||||
#define DEVICES_KBD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void kbd_init (void);
|
||||
void kbd_print_stats (void);
|
||||
|
||||
#endif /* devices/kbd.h */
|
324
pintos-env/pintos/devices/partition.c
Executable file
324
pintos-env/pintos/devices/partition.c
Executable file
|
@ -0,0 +1,324 @@
|
|||
#include "devices/partition.h"
|
||||
#include <packed.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "devices/block.h"
|
||||
#include "threads/malloc.h"
|
||||
|
||||
/* A partition of a block device. */
|
||||
struct partition
|
||||
{
|
||||
struct block *block; /* Underlying block device. */
|
||||
block_sector_t start; /* First sector within device. */
|
||||
};
|
||||
|
||||
static struct block_operations partition_operations;
|
||||
|
||||
static void read_partition_table (struct block *, block_sector_t sector,
|
||||
block_sector_t primary_extended_sector,
|
||||
int *part_nr);
|
||||
static void found_partition (struct block *, uint8_t type,
|
||||
block_sector_t start, block_sector_t size,
|
||||
int part_nr);
|
||||
static const char *partition_type_name (uint8_t);
|
||||
|
||||
/* Scans BLOCK for partitions of interest to Pintos. */
|
||||
void
|
||||
partition_scan (struct block *block)
|
||||
{
|
||||
int part_nr = 0;
|
||||
read_partition_table (block, 0, 0, &part_nr);
|
||||
if (part_nr == 0)
|
||||
printf ("%s: Device contains no partitions\n", block_name (block));
|
||||
}
|
||||
|
||||
/* Reads the partition table in the given SECTOR of BLOCK and
|
||||
scans it for partitions of interest to Pintos.
|
||||
|
||||
If SECTOR is 0, so that this is the top-level partition table
|
||||
on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful;
|
||||
otherwise, it should designate the sector of the top-level
|
||||
extended partition table that was traversed to arrive at
|
||||
SECTOR, for use in finding logical partitions (see the large
|
||||
comment below).
|
||||
|
||||
PART_NR points to the number of non-empty primary or logical
|
||||
partitions already encountered on BLOCK. It is incremented as
|
||||
partitions are found. */
|
||||
static void
|
||||
read_partition_table (struct block *block, block_sector_t sector,
|
||||
block_sector_t primary_extended_sector,
|
||||
int *part_nr)
|
||||
{
|
||||
/* Format of a partition table entry. See [Partitions]. */
|
||||
struct partition_table_entry
|
||||
{
|
||||
uint8_t bootable; /* 0x00=not bootable, 0x80=bootable. */
|
||||
uint8_t start_chs[3]; /* Encoded starting cylinder, head, sector. */
|
||||
uint8_t type; /* Partition type (see partition_type_name). */
|
||||
uint8_t end_chs[3]; /* Encoded ending cylinder, head, sector. */
|
||||
uint32_t offset; /* Start sector offset from partition table. */
|
||||
uint32_t size; /* Number of sectors. */
|
||||
}
|
||||
PACKED;
|
||||
|
||||
/* Partition table sector. */
|
||||
struct partition_table
|
||||
{
|
||||
uint8_t loader[446]; /* Loader, in top-level partition table. */
|
||||
struct partition_table_entry partitions[4]; /* Table entries. */
|
||||
uint16_t signature; /* Should be 0xaa55. */
|
||||
}
|
||||
PACKED;
|
||||
|
||||
struct partition_table *pt;
|
||||
size_t i;
|
||||
|
||||
/* Check SECTOR validity. */
|
||||
if (sector >= block_size (block))
|
||||
{
|
||||
printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n",
|
||||
block_name (block), sector);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read sector. */
|
||||
ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE);
|
||||
pt = malloc (sizeof *pt);
|
||||
if (pt == NULL)
|
||||
PANIC ("Failed to allocate memory for partition table.");
|
||||
block_read (block, 0, pt);
|
||||
|
||||
/* Check signature. */
|
||||
if (pt->signature != 0xaa55)
|
||||
{
|
||||
if (primary_extended_sector == 0)
|
||||
printf ("%s: Invalid partition table signature\n", block_name (block));
|
||||
else
|
||||
printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n",
|
||||
block_name (block), sector);
|
||||
free (pt);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse partitions. */
|
||||
for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++)
|
||||
{
|
||||
struct partition_table_entry *e = &pt->partitions[i];
|
||||
|
||||
if (e->size == 0 || e->type == 0)
|
||||
{
|
||||
/* Ignore empty partition. */
|
||||
}
|
||||
else if (e->type == 0x05 /* Extended partition. */
|
||||
|| e->type == 0x0f /* Windows 98 extended partition. */
|
||||
|| e->type == 0x85 /* Linux extended partition. */
|
||||
|| e->type == 0xc5) /* DR-DOS extended partition. */
|
||||
{
|
||||
printf ("%s: Extended partition in sector %"PRDSNu"\n",
|
||||
block_name (block), sector);
|
||||
|
||||
/* The interpretation of the offset field for extended
|
||||
partitions is bizarre. When the extended partition
|
||||
table entry is in the master boot record, that is,
|
||||
the device's primary partition table in sector 0, then
|
||||
the offset is an absolute sector number. Otherwise,
|
||||
no matter how deep the partition table we're reading
|
||||
is nested, the offset is relative to the start of
|
||||
the extended partition that the MBR points to. */
|
||||
if (sector == 0)
|
||||
read_partition_table (block, e->offset, e->offset, part_nr);
|
||||
else
|
||||
read_partition_table (block, e->offset + primary_extended_sector,
|
||||
primary_extended_sector, part_nr);
|
||||
}
|
||||
else
|
||||
{
|
||||
++*part_nr;
|
||||
|
||||
found_partition (block, e->type, e->offset + sector,
|
||||
e->size, *part_nr);
|
||||
}
|
||||
}
|
||||
|
||||
free (pt);
|
||||
}
|
||||
|
||||
/* We have found a primary or logical partition of the given TYPE
|
||||
on BLOCK, starting at sector START and continuing for SIZE
|
||||
sectors, which we are giving the partition number PART_NR.
|
||||
Check whether this is a partition of interest to Pintos, and
|
||||
if so then add it to the proper element of partitions[]. */
|
||||
static void
|
||||
found_partition (struct block *block, uint8_t part_type,
|
||||
block_sector_t start, block_sector_t size,
|
||||
int part_nr)
|
||||
{
|
||||
if (start >= block_size (block))
|
||||
printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n",
|
||||
block_name (block), part_nr, start);
|
||||
else if (start + size < start || start + size > block_size (block))
|
||||
printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n",
|
||||
block_name (block), part_nr, start + size, block_size (block));
|
||||
else
|
||||
{
|
||||
enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL
|
||||
: part_type == 0x21 ? BLOCK_FILESYS
|
||||
: part_type == 0x22 ? BLOCK_SCRATCH
|
||||
: part_type == 0x23 ? BLOCK_SWAP
|
||||
: BLOCK_FOREIGN);
|
||||
struct partition *p;
|
||||
char extra_info[128];
|
||||
char name[16];
|
||||
|
||||
p = malloc (sizeof *p);
|
||||
if (p == NULL)
|
||||
PANIC ("Failed to allocate memory for partition descriptor");
|
||||
p->block = block;
|
||||
p->start = start;
|
||||
|
||||
snprintf (name, sizeof name, "%s%d", block_name (block), part_nr);
|
||||
snprintf (extra_info, sizeof extra_info, "%s (%02x)",
|
||||
partition_type_name (part_type), part_type);
|
||||
block_register (name, type, extra_info, size, &partition_operations, p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a human-readable name for the given partition TYPE. */
|
||||
static const char *
|
||||
partition_type_name (uint8_t type)
|
||||
{
|
||||
/* Name of each known type of partition.
|
||||
From util-linux-2.12r/fdisk/i386_sys_types.c.
|
||||
This initializer makes use of a C99 feature that allows
|
||||
array elements to be initialized by index. */
|
||||
static const char *type_names[256] =
|
||||
{
|
||||
[0x00] = "Empty",
|
||||
[0x01] = "FAT12",
|
||||
[0x02] = "XENIX root",
|
||||
[0x03] = "XENIX usr",
|
||||
[0x04] = "FAT16 <32M",
|
||||
[0x05] = "Extended",
|
||||
[0x06] = "FAT16",
|
||||
[0x07] = "HPFS/NTFS",
|
||||
[0x08] = "AIX",
|
||||
[0x09] = "AIX bootable",
|
||||
[0x0a] = "OS/2 Boot Manager",
|
||||
[0x0b] = "W95 FAT32",
|
||||
[0x0c] = "W95 FAT32 (LBA)",
|
||||
[0x0e] = "W95 FAT16 (LBA)",
|
||||
[0x0f] = "W95 Ext'd (LBA)",
|
||||
[0x10] = "OPUS",
|
||||
[0x11] = "Hidden FAT12",
|
||||
[0x12] = "Compaq diagnostics",
|
||||
[0x14] = "Hidden FAT16 <32M",
|
||||
[0x16] = "Hidden FAT16",
|
||||
[0x17] = "Hidden HPFS/NTFS",
|
||||
[0x18] = "AST SmartSleep",
|
||||
[0x1b] = "Hidden W95 FAT32",
|
||||
[0x1c] = "Hidden W95 FAT32 (LBA)",
|
||||
[0x1e] = "Hidden W95 FAT16 (LBA)",
|
||||
[0x20] = "Pintos OS kernel",
|
||||
[0x21] = "Pintos file system",
|
||||
[0x22] = "Pintos scratch",
|
||||
[0x23] = "Pintos swap",
|
||||
[0x24] = "NEC DOS",
|
||||
[0x39] = "Plan 9",
|
||||
[0x3c] = "PartitionMagic recovery",
|
||||
[0x40] = "Venix 80286",
|
||||
[0x41] = "PPC PReP Boot",
|
||||
[0x42] = "SFS",
|
||||
[0x4d] = "QNX4.x",
|
||||
[0x4e] = "QNX4.x 2nd part",
|
||||
[0x4f] = "QNX4.x 3rd part",
|
||||
[0x50] = "OnTrack DM",
|
||||
[0x51] = "OnTrack DM6 Aux1",
|
||||
[0x52] = "CP/M",
|
||||
[0x53] = "OnTrack DM6 Aux3",
|
||||
[0x54] = "OnTrackDM6",
|
||||
[0x55] = "EZ-Drive",
|
||||
[0x56] = "Golden Bow",
|
||||
[0x5c] = "Priam Edisk",
|
||||
[0x61] = "SpeedStor",
|
||||
[0x63] = "GNU HURD or SysV",
|
||||
[0x64] = "Novell Netware 286",
|
||||
[0x65] = "Novell Netware 386",
|
||||
[0x70] = "DiskSecure Multi-Boot",
|
||||
[0x75] = "PC/IX",
|
||||
[0x80] = "Old Minix",
|
||||
[0x81] = "Minix / old Linux",
|
||||
[0x82] = "Linux swap / Solaris",
|
||||
[0x83] = "Linux",
|
||||
[0x84] = "OS/2 hidden C: drive",
|
||||
[0x85] = "Linux extended",
|
||||
[0x86] = "NTFS volume set",
|
||||
[0x87] = "NTFS volume set",
|
||||
[0x88] = "Linux plaintext",
|
||||
[0x8e] = "Linux LVM",
|
||||
[0x93] = "Amoeba",
|
||||
[0x94] = "Amoeba BBT",
|
||||
[0x9f] = "BSD/OS",
|
||||
[0xa0] = "IBM Thinkpad hibernation",
|
||||
[0xa5] = "FreeBSD",
|
||||
[0xa6] = "OpenBSD",
|
||||
[0xa7] = "NeXTSTEP",
|
||||
[0xa8] = "Darwin UFS",
|
||||
[0xa9] = "NetBSD",
|
||||
[0xab] = "Darwin boot",
|
||||
[0xb7] = "BSDI fs",
|
||||
[0xb8] = "BSDI swap",
|
||||
[0xbb] = "Boot Wizard hidden",
|
||||
[0xbe] = "Solaris boot",
|
||||
[0xbf] = "Solaris",
|
||||
[0xc1] = "DRDOS/sec (FAT-12)",
|
||||
[0xc4] = "DRDOS/sec (FAT-16 < 32M)",
|
||||
[0xc6] = "DRDOS/sec (FAT-16)",
|
||||
[0xc7] = "Syrinx",
|
||||
[0xda] = "Non-FS data",
|
||||
[0xdb] = "CP/M / CTOS / ...",
|
||||
[0xde] = "Dell Utility",
|
||||
[0xdf] = "BootIt",
|
||||
[0xe1] = "DOS access",
|
||||
[0xe3] = "DOS R/O",
|
||||
[0xe4] = "SpeedStor",
|
||||
[0xeb] = "BeOS fs",
|
||||
[0xee] = "EFI GPT",
|
||||
[0xef] = "EFI (FAT-12/16/32)",
|
||||
[0xf0] = "Linux/PA-RISC boot",
|
||||
[0xf1] = "SpeedStor",
|
||||
[0xf4] = "SpeedStor",
|
||||
[0xf2] = "DOS secondary",
|
||||
[0xfd] = "Linux raid autodetect",
|
||||
[0xfe] = "LANstep",
|
||||
[0xff] = "BBT",
|
||||
};
|
||||
|
||||
return type_names[type] != NULL ? type_names[type] : "Unknown";
|
||||
}
|
||||
|
||||
/* Reads sector SECTOR from partition P into BUFFER, which must
|
||||
have room for BLOCK_SECTOR_SIZE bytes. */
|
||||
static void
|
||||
partition_read (void *p_, block_sector_t sector, void *buffer)
|
||||
{
|
||||
struct partition *p = p_;
|
||||
block_read (p->block, p->start + sector, buffer);
|
||||
}
|
||||
|
||||
/* Write sector SECTOR to partition P from BUFFER, which must
|
||||
contain BLOCK_SECTOR_SIZE bytes. Returns after the block has
|
||||
acknowledged receiving the data. */
|
||||
static void
|
||||
partition_write (void *p_, block_sector_t sector, const void *buffer)
|
||||
{
|
||||
struct partition *p = p_;
|
||||
block_write (p->block, p->start + sector, buffer);
|
||||
}
|
||||
|
||||
static struct block_operations partition_operations =
|
||||
{
|
||||
partition_read,
|
||||
partition_write
|
||||
};
|
8
pintos-env/pintos/devices/partition.h
Executable file
8
pintos-env/pintos/devices/partition.h
Executable file
|
@ -0,0 +1,8 @@
|
|||
#ifndef DEVICES_PARTITION_H
|
||||
#define DEVICES_PARTITION_H
|
||||
|
||||
struct block;
|
||||
|
||||
void partition_scan (struct block *);
|
||||
|
||||
#endif /* devices/partition.h */
|
83
pintos-env/pintos/devices/pit.c
Executable file
83
pintos-env/pintos/devices/pit.c
Executable file
|
@ -0,0 +1,83 @@
|
|||
#include "devices/pit.h"
|
||||
#include <debug.h>
|
||||
#include <stdint.h>
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/io.h"
|
||||
|
||||
/* Interface to 8254 Programmable Interrupt Timer (PIT).
|
||||
Refer to [8254] for details. */
|
||||
|
||||
/* 8254 registers. */
|
||||
#define PIT_PORT_CONTROL 0x43 /* Control port. */
|
||||
#define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL)) /* Counter port. */
|
||||
|
||||
/* PIT cycles per second. */
|
||||
#define PIT_HZ 1193180
|
||||
|
||||
/* Configure the given CHANNEL in the PIT. In a PC, the PIT's
|
||||
three output channels are hooked up like this:
|
||||
|
||||
- Channel 0 is connected to interrupt line 0, so that it can
|
||||
be used as a periodic timer interrupt, as implemented in
|
||||
Pintos in devices/timer.c.
|
||||
|
||||
- Channel 1 is used for dynamic RAM refresh (in older PCs).
|
||||
No good can come of messing with this.
|
||||
|
||||
- Channel 2 is connected to the PC speaker, so that it can
|
||||
be used to play a tone, as implemented in Pintos in
|
||||
devices/speaker.c.
|
||||
|
||||
MODE specifies the form of output:
|
||||
|
||||
- Mode 2 is a periodic pulse: the channel's output is 1 for
|
||||
most of the period, but drops to 0 briefly toward the end
|
||||
of the period. This is useful for hooking up to an
|
||||
interrupt controller to generate a periodic interrupt.
|
||||
|
||||
- Mode 3 is a square wave: for the first half of the period
|
||||
it is 1, for the second half it is 0. This is useful for
|
||||
generating a tone on a speaker.
|
||||
|
||||
- Other modes are less useful.
|
||||
|
||||
FREQUENCY is the number of periods per second, in Hz. */
|
||||
void
|
||||
pit_configure_channel (int channel, int mode, int frequency)
|
||||
{
|
||||
uint16_t count;
|
||||
enum intr_level old_level;
|
||||
|
||||
ASSERT (channel == 0 || channel == 2);
|
||||
ASSERT (mode == 2 || mode == 3);
|
||||
|
||||
/* Convert FREQUENCY to a PIT counter value. The PIT has a
|
||||
clock that runs at PIT_HZ cycles per second. We must
|
||||
translate FREQUENCY into a number of these cycles. */
|
||||
if (frequency < 19)
|
||||
{
|
||||
/* Frequency is too low: the quotient would overflow the
|
||||
16-bit counter. Force it to 0, which the PIT treats as
|
||||
65536, the highest possible count. This yields a 18.2
|
||||
Hz timer, approximately. */
|
||||
count = 0;
|
||||
}
|
||||
else if (frequency > PIT_HZ)
|
||||
{
|
||||
/* Frequency is too high: the quotient would underflow to
|
||||
0, which the PIT would interpret as 65536. A count of 1
|
||||
is illegal in mode 2, so we force it to 2, which yields
|
||||
a 596.590 kHz timer, approximately. (This timer rate is
|
||||
probably too fast to be useful anyhow.) */
|
||||
count = 2;
|
||||
}
|
||||
else
|
||||
count = (PIT_HZ + frequency / 2) / frequency;
|
||||
|
||||
/* Configure the PIT mode and load its counters. */
|
||||
old_level = intr_disable ();
|
||||
outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1));
|
||||
outb (PIT_PORT_COUNTER (channel), count);
|
||||
outb (PIT_PORT_COUNTER (channel), count >> 8);
|
||||
intr_set_level (old_level);
|
||||
}
|
8
pintos-env/pintos/devices/pit.h
Executable file
8
pintos-env/pintos/devices/pit.h
Executable file
|
@ -0,0 +1,8 @@
|
|||
#ifndef DEVICES_PIT_H
|
||||
#define DEVICES_PIT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void pit_configure_channel (int channel, int mode, int frequency);
|
||||
|
||||
#endif /* devices/pit.h */
|
112
pintos-env/pintos/devices/rtc.c
Executable file
112
pintos-env/pintos/devices/rtc.c
Executable file
|
@ -0,0 +1,112 @@
|
|||
#include "devices/rtc.h"
|
||||
#include <stdio.h>
|
||||
#include "threads/io.h"
|
||||
|
||||
/* This code is an interface to the MC146818A-compatible real
|
||||
time clock found on PC motherboards. See [MC146818A] for
|
||||
hardware details. */
|
||||
|
||||
/* I/O register addresses. */
|
||||
#define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */
|
||||
#define CMOS_REG_IO 0x71 /* Contains the selected data byte. */
|
||||
|
||||
/* Indexes of CMOS registers with real-time clock functions.
|
||||
Note that all of these registers are in BCD format,
|
||||
so that 0x59 means 59, not 89. */
|
||||
#define RTC_REG_SEC 0 /* Second: 0x00...0x59. */
|
||||
#define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */
|
||||
#define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */
|
||||
#define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */
|
||||
#define RTC_REG_MON 8 /* Month: 0x01...0x12. */
|
||||
#define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */
|
||||
|
||||
/* Indexes of CMOS control registers. */
|
||||
#define RTC_REG_A 0x0a /* Register A: update-in-progress. */
|
||||
#define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */
|
||||
#define RTC_REG_C 0x0c /* Register C: pending interrupts. */
|
||||
#define RTC_REG_D 0x0d /* Register D: valid time? */
|
||||
|
||||
/* Register A. */
|
||||
#define RTCSA_UIP 0x80 /* Set while time update in progress. */
|
||||
|
||||
/* Register B. */
|
||||
#define RTCSB_SET 0x80 /* Disables update to let time be set. */
|
||||
#define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */
|
||||
#define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */
|
||||
|
||||
static int bcd_to_bin (uint8_t);
|
||||
static uint8_t cmos_read (uint8_t index);
|
||||
|
||||
/* Returns number of seconds since Unix epoch of January 1,
|
||||
1970. */
|
||||
time_t
|
||||
rtc_get_time (void)
|
||||
{
|
||||
static const int days_per_month[12] =
|
||||
{
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||
};
|
||||
int sec, min, hour, mday, mon, year;
|
||||
time_t time;
|
||||
int i;
|
||||
|
||||
/* Get time components.
|
||||
|
||||
We repeatedly read the time until it is stable from one read
|
||||
to another, in case we start our initial read in the middle
|
||||
of an update. This strategy is not recommended by the
|
||||
MC146818A datasheet, but it is simpler than any of their
|
||||
suggestions and, furthermore, it is also used by Linux.
|
||||
|
||||
The MC146818A can be configured for BCD or binary format,
|
||||
but for historical reasons everyone always uses BCD format
|
||||
except on obscure non-PC platforms, so we don't bother
|
||||
trying to detect the format in use. */
|
||||
do
|
||||
{
|
||||
sec = bcd_to_bin (cmos_read (RTC_REG_SEC));
|
||||
min = bcd_to_bin (cmos_read (RTC_REG_MIN));
|
||||
hour = bcd_to_bin (cmos_read (RTC_REG_HOUR));
|
||||
mday = bcd_to_bin (cmos_read (RTC_REG_MDAY));
|
||||
mon = bcd_to_bin (cmos_read (RTC_REG_MON));
|
||||
year = bcd_to_bin (cmos_read (RTC_REG_YEAR));
|
||||
}
|
||||
while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC)));
|
||||
|
||||
/* Translate years-since-1900 into years-since-1970.
|
||||
If it's before the epoch, assume that it has passed 2000.
|
||||
This will break at 2070, but that's long after our 31-bit
|
||||
time_t breaks in 2038. */
|
||||
if (year < 70)
|
||||
year += 100;
|
||||
year -= 70;
|
||||
|
||||
/* Break down all components into seconds. */
|
||||
time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60;
|
||||
for (i = 1; i <= mon; i++)
|
||||
time += days_per_month[i - 1] * 24 * 60 * 60;
|
||||
if (mon > 2 && year % 4 == 0)
|
||||
time += 24 * 60 * 60;
|
||||
time += (mday - 1) * 24 * 60 * 60;
|
||||
time += hour * 60 * 60;
|
||||
time += min * 60;
|
||||
time += sec;
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
/* Returns the integer value of the given BCD byte. */
|
||||
static int
|
||||
bcd_to_bin (uint8_t x)
|
||||
{
|
||||
return (x & 0x0f) + ((x >> 4) * 10);
|
||||
}
|
||||
|
||||
/* Reads a byte from the CMOS register with the given INDEX and
|
||||
returns the byte read. */
|
||||
static uint8_t
|
||||
cmos_read (uint8_t index)
|
||||
{
|
||||
outb (CMOS_REG_SET, index);
|
||||
return inb (CMOS_REG_IO);
|
||||
}
|
8
pintos-env/pintos/devices/rtc.h
Executable file
8
pintos-env/pintos/devices/rtc.h
Executable file
|
@ -0,0 +1,8 @@
|
|||
#ifndef RTC_H
|
||||
#define RTC_H
|
||||
|
||||
typedef unsigned long time_t;
|
||||
|
||||
time_t rtc_get_time (void);
|
||||
|
||||
#endif
|
228
pintos-env/pintos/devices/serial.c
Executable file
228
pintos-env/pintos/devices/serial.c
Executable file
|
@ -0,0 +1,228 @@
|
|||
#include "devices/serial.h"
|
||||
#include <debug.h>
|
||||
#include "devices/input.h"
|
||||
#include "devices/intq.h"
|
||||
#include "devices/timer.h"
|
||||
#include "threads/io.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
|
||||
/* Register definitions for the 16550A UART used in PCs.
|
||||
The 16550A has a lot more going on than shown here, but this
|
||||
is all we need.
|
||||
|
||||
Refer to [PC16650D] for hardware information. */
|
||||
|
||||
/* I/O port base address for the first serial port. */
|
||||
#define IO_BASE 0x3f8
|
||||
|
||||
/* DLAB=0 registers. */
|
||||
#define RBR_REG (IO_BASE + 0) /* Receiver Buffer Reg. (read-only). */
|
||||
#define THR_REG (IO_BASE + 0) /* Transmitter Holding Reg. (write-only). */
|
||||
#define IER_REG (IO_BASE + 1) /* Interrupt Enable Reg.. */
|
||||
|
||||
/* DLAB=1 registers. */
|
||||
#define LS_REG (IO_BASE + 0) /* Divisor Latch (LSB). */
|
||||
#define MS_REG (IO_BASE + 1) /* Divisor Latch (MSB). */
|
||||
|
||||
/* DLAB-insensitive registers. */
|
||||
#define IIR_REG (IO_BASE + 2) /* Interrupt Identification Reg. (read-only) */
|
||||
#define FCR_REG (IO_BASE + 2) /* FIFO Control Reg. (write-only). */
|
||||
#define LCR_REG (IO_BASE + 3) /* Line Control Register. */
|
||||
#define MCR_REG (IO_BASE + 4) /* MODEM Control Register. */
|
||||
#define LSR_REG (IO_BASE + 5) /* Line Status Register (read-only). */
|
||||
|
||||
/* Interrupt Enable Register bits. */
|
||||
#define IER_RECV 0x01 /* Interrupt when data received. */
|
||||
#define IER_XMIT 0x02 /* Interrupt when transmit finishes. */
|
||||
|
||||
/* Line Control Register bits. */
|
||||
#define LCR_N81 0x03 /* No parity, 8 data bits, 1 stop bit. */
|
||||
#define LCR_DLAB 0x80 /* Divisor Latch Access Bit (DLAB). */
|
||||
|
||||
/* MODEM Control Register. */
|
||||
#define MCR_OUT2 0x08 /* Output line 2. */
|
||||
|
||||
/* Line Status Register. */
|
||||
#define LSR_DR 0x01 /* Data Ready: received data byte is in RBR. */
|
||||
#define LSR_THRE 0x20 /* THR Empty. */
|
||||
|
||||
/* Transmission mode. */
|
||||
static enum { UNINIT, POLL, QUEUE } mode;
|
||||
|
||||
/* Data to be transmitted. */
|
||||
static struct intq txq;
|
||||
|
||||
static void set_serial (int bps);
|
||||
static void putc_poll (uint8_t);
|
||||
static void write_ier (void);
|
||||
static intr_handler_func serial_interrupt;
|
||||
|
||||
/* Initializes the serial port device for polling mode.
|
||||
Polling mode busy-waits for the serial port to become free
|
||||
before writing to it. It's slow, but until interrupts have
|
||||
been initialized it's all we can do. */
|
||||
static void
|
||||
init_poll (void)
|
||||
{
|
||||
ASSERT (mode == UNINIT);
|
||||
outb (IER_REG, 0); /* Turn off all interrupts. */
|
||||
outb (FCR_REG, 0); /* Disable FIFO. */
|
||||
set_serial (9600); /* 9.6 kbps, N-8-1. */
|
||||
outb (MCR_REG, MCR_OUT2); /* Required to enable interrupts. */
|
||||
intq_init (&txq);
|
||||
mode = POLL;
|
||||
}
|
||||
|
||||
/* Initializes the serial port device for queued interrupt-driven
|
||||
I/O. With interrupt-driven I/O we don't waste CPU time
|
||||
waiting for the serial device to become ready. */
|
||||
void
|
||||
serial_init_queue (void)
|
||||
{
|
||||
enum intr_level old_level;
|
||||
|
||||
if (mode == UNINIT)
|
||||
init_poll ();
|
||||
ASSERT (mode == POLL);
|
||||
|
||||
intr_register_ext (0x20 + 4, serial_interrupt, "serial");
|
||||
mode = QUEUE;
|
||||
old_level = intr_disable ();
|
||||
write_ier ();
|
||||
intr_set_level (old_level);
|
||||
}
|
||||
|
||||
/* Sends BYTE to the serial port. */
|
||||
void
|
||||
serial_putc (uint8_t byte)
|
||||
{
|
||||
enum intr_level old_level = intr_disable ();
|
||||
|
||||
if (mode != QUEUE)
|
||||
{
|
||||
/* If we're not set up for interrupt-driven I/O yet,
|
||||
use dumb polling to transmit a byte. */
|
||||
if (mode == UNINIT)
|
||||
init_poll ();
|
||||
putc_poll (byte);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, queue a byte and update the interrupt enable
|
||||
register. */
|
||||
if (old_level == INTR_OFF && intq_full (&txq))
|
||||
{
|
||||
/* Interrupts are off and the transmit queue is full.
|
||||
If we wanted to wait for the queue to empty,
|
||||
we'd have to reenable interrupts.
|
||||
That's impolite, so we'll send a character via
|
||||
polling instead. */
|
||||
putc_poll (intq_getc (&txq));
|
||||
}
|
||||
|
||||
intq_putc (&txq, byte);
|
||||
write_ier ();
|
||||
}
|
||||
|
||||
intr_set_level (old_level);
|
||||
}
|
||||
|
||||
/* Flushes anything in the serial buffer out the port in polling
|
||||
mode. */
|
||||
void
|
||||
serial_flush (void)
|
||||
{
|
||||
enum intr_level old_level = intr_disable ();
|
||||
while (!intq_empty (&txq))
|
||||
putc_poll (intq_getc (&txq));
|
||||
intr_set_level (old_level);
|
||||
}
|
||||
|
||||
/* The fullness of the input buffer may have changed. Reassess
|
||||
whether we should block receive interrupts.
|
||||
Called by the input buffer routines when characters are added
|
||||
to or removed from the buffer. */
|
||||
void
|
||||
serial_notify (void)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
if (mode == QUEUE)
|
||||
write_ier ();
|
||||
}
|
||||
|
||||
/* Configures the serial port for BPS bits per second. */
|
||||
static void
|
||||
set_serial (int bps)
|
||||
{
|
||||
int base_rate = 1843200 / 16; /* Base rate of 16550A, in Hz. */
|
||||
uint16_t divisor = base_rate / bps; /* Clock rate divisor. */
|
||||
|
||||
ASSERT (bps >= 300 && bps <= 115200);
|
||||
|
||||
/* Enable DLAB. */
|
||||
outb (LCR_REG, LCR_N81 | LCR_DLAB);
|
||||
|
||||
/* Set data rate. */
|
||||
outb (LS_REG, divisor & 0xff);
|
||||
outb (MS_REG, divisor >> 8);
|
||||
|
||||
/* Reset DLAB. */
|
||||
outb (LCR_REG, LCR_N81);
|
||||
}
|
||||
|
||||
/* Update interrupt enable register. */
|
||||
static void
|
||||
write_ier (void)
|
||||
{
|
||||
uint8_t ier = 0;
|
||||
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
|
||||
/* Enable transmit interrupt if we have any characters to
|
||||
transmit. */
|
||||
if (!intq_empty (&txq))
|
||||
ier |= IER_XMIT;
|
||||
|
||||
/* Enable receive interrupt if we have room to store any
|
||||
characters we receive. */
|
||||
if (!input_full ())
|
||||
ier |= IER_RECV;
|
||||
|
||||
outb (IER_REG, ier);
|
||||
}
|
||||
|
||||
/* Polls the serial port until it's ready,
|
||||
and then transmits BYTE. */
|
||||
static void
|
||||
putc_poll (uint8_t byte)
|
||||
{
|
||||
ASSERT (intr_get_level () == INTR_OFF);
|
||||
|
||||
while ((inb (LSR_REG) & LSR_THRE) == 0)
|
||||
continue;
|
||||
outb (THR_REG, byte);
|
||||
}
|
||||
|
||||
/* Serial interrupt handler. */
|
||||
static void
|
||||
serial_interrupt (struct intr_frame *f UNUSED)
|
||||
{
|
||||
/* Inquire about interrupt in UART. Without this, we can
|
||||
occasionally miss an interrupt running under QEMU. */
|
||||
inb (IIR_REG);
|
||||
|
||||
/* As long as we have room to receive a byte, and the hardware
|
||||
has a byte for us, receive a byte. */
|
||||
while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
|
||||
input_putc (inb (RBR_REG));
|
||||
|
||||
/* As long as we have a byte to transmit, and the hardware is
|
||||
ready to accept a byte for transmission, transmit a byte. */
|
||||
while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0)
|
||||
outb (THR_REG, intq_getc (&txq));
|
||||
|
||||
/* Update interrupt enable register based on queue status. */
|
||||
write_ier ();
|
||||
}
|
11
pintos-env/pintos/devices/serial.h
Executable file
11
pintos-env/pintos/devices/serial.h
Executable file
|
@ -0,0 +1,11 @@
|
|||
#ifndef DEVICES_SERIAL_H
|
||||
#define DEVICES_SERIAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void serial_init_queue (void);
|
||||
void serial_putc (uint8_t);
|
||||
void serial_flush (void);
|
||||
void serial_notify (void);
|
||||
|
||||
#endif /* devices/serial.h */
|
131
pintos-env/pintos/devices/shutdown.c
Executable file
131
pintos-env/pintos/devices/shutdown.c
Executable file
|
@ -0,0 +1,131 @@
|
|||
#include "devices/shutdown.h"
|
||||
#include <console.h>
|
||||
#include <stdio.h>
|
||||
#include "devices/kbd.h"
|
||||
#include "devices/serial.h"
|
||||
#include "devices/timer.h"
|
||||
#include "threads/io.h"
|
||||
#include "threads/thread.h"
|
||||
#ifdef USERPROG
|
||||
#include "userprog/exception.h"
|
||||
#endif
|
||||
#ifdef FILESYS
|
||||
#include "devices/block.h"
|
||||
#include "filesys/filesys.h"
|
||||
#endif
|
||||
|
||||
/* Keyboard control register port. */
|
||||
#define CONTROL_REG 0x64
|
||||
|
||||
/* How to shut down when shutdown() is called. */
|
||||
static enum shutdown_type how = SHUTDOWN_NONE;
|
||||
|
||||
static void print_stats (void);
|
||||
|
||||
/* Shuts down the machine in the way configured by
|
||||
shutdown_configure(). If the shutdown type is SHUTDOWN_NONE
|
||||
(which is the default), returns without doing anything. */
|
||||
void
|
||||
shutdown (void)
|
||||
{
|
||||
switch (how)
|
||||
{
|
||||
case SHUTDOWN_POWER_OFF:
|
||||
shutdown_power_off ();
|
||||
break;
|
||||
|
||||
case SHUTDOWN_REBOOT:
|
||||
shutdown_reboot ();
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sets TYPE as the way that machine will shut down when Pintos
|
||||
execution is complete. */
|
||||
void
|
||||
shutdown_configure (enum shutdown_type type)
|
||||
{
|
||||
how = type;
|
||||
}
|
||||
|
||||
/* Reboots the machine via the keyboard controller. */
|
||||
void
|
||||
shutdown_reboot (void)
|
||||
{
|
||||
printf ("Rebooting...\n");
|
||||
|
||||
/* See [kbd] for details on how to program the keyboard
|
||||
* controller. */
|
||||
for (;;)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Poll keyboard controller's status byte until
|
||||
* 'input buffer empty' is reported. */
|
||||
for (i = 0; i < 0x10000; i++)
|
||||
{
|
||||
if ((inb (CONTROL_REG) & 0x02) == 0)
|
||||
break;
|
||||
timer_udelay (2);
|
||||
}
|
||||
|
||||
timer_udelay (50);
|
||||
|
||||
/* Pulse bit 0 of the output port P2 of the keyboard controller.
|
||||
* This will reset the CPU. */
|
||||
outb (CONTROL_REG, 0xfe);
|
||||
timer_udelay (50);
|
||||
}
|
||||
}
|
||||
|
||||
/* Powers down the machine we're running on,
|
||||
as long as we're running on Bochs or QEMU. */
|
||||
void
|
||||
shutdown_power_off (void)
|
||||
{
|
||||
const char s[] = "Shutdown";
|
||||
const char *p;
|
||||
|
||||
#ifdef FILESYS
|
||||
filesys_done ();
|
||||
#endif
|
||||
|
||||
print_stats ();
|
||||
|
||||
printf ("Powering off...\n");
|
||||
serial_flush ();
|
||||
|
||||
/* This is a special power-off sequence supported by Bochs and
|
||||
QEMU, but not by physical hardware. */
|
||||
for (p = s; *p != '\0'; p++)
|
||||
outb (0x8900, *p);
|
||||
|
||||
/* This will power off a VMware VM if "gui.exitOnCLIHLT = TRUE"
|
||||
is set in its configuration file. (The "pintos" script does
|
||||
that automatically.) */
|
||||
asm volatile ("cli; hlt" : : : "memory");
|
||||
|
||||
/* None of those worked. */
|
||||
printf ("still running...\n");
|
||||
for (;;);
|
||||
}
|
||||
|
||||
/* Print statistics about Pintos execution. */
|
||||
static void
|
||||
print_stats (void)
|
||||
{
|
||||
timer_print_stats ();
|
||||
thread_print_stats ();
|
||||
#ifdef FILESYS
|
||||
block_print_stats ();
|
||||
#endif
|
||||
console_print_stats ();
|
||||
kbd_print_stats ();
|
||||
#ifdef USERPROG
|
||||
exception_print_stats ();
|
||||
#endif
|
||||
}
|
19
pintos-env/pintos/devices/shutdown.h
Executable file
19
pintos-env/pintos/devices/shutdown.h
Executable file
|
@ -0,0 +1,19 @@
|
|||
#ifndef DEVICES_SHUTDOWN_H
|
||||
#define DEVICES_SHUTDOWN_H
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
/* How to shut down when Pintos has nothing left to do. */
|
||||
enum shutdown_type
|
||||
{
|
||||
SHUTDOWN_NONE, /* Loop forever. */
|
||||
SHUTDOWN_POWER_OFF, /* Power off the machine (if possible). */
|
||||
SHUTDOWN_REBOOT, /* Reboot the machine (if possible). */
|
||||
};
|
||||
|
||||
void shutdown (void);
|
||||
void shutdown_configure (enum shutdown_type);
|
||||
void shutdown_reboot (void) NO_RETURN;
|
||||
void shutdown_power_off (void) NO_RETURN;
|
||||
|
||||
#endif /* devices/shutdown.h */
|
68
pintos-env/pintos/devices/speaker.c
Executable file
68
pintos-env/pintos/devices/speaker.c
Executable file
|
@ -0,0 +1,68 @@
|
|||
#include "devices/speaker.h"
|
||||
#include "devices/pit.h"
|
||||
#include "threads/io.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "devices/timer.h"
|
||||
|
||||
/* Speaker port enable I/O register. */
|
||||
#define SPEAKER_PORT_GATE 0x61
|
||||
|
||||
/* Speaker port enable bits. */
|
||||
#define SPEAKER_GATE_ENABLE 0x03
|
||||
|
||||
/* Sets the PC speaker to emit a tone at the given FREQUENCY, in
|
||||
Hz. */
|
||||
void
|
||||
speaker_on (int frequency)
|
||||
{
|
||||
if (frequency >= 20 && frequency <= 20000)
|
||||
{
|
||||
/* Set the timer channel that's connected to the speaker to
|
||||
output a square wave at the given FREQUENCY, then
|
||||
connect the timer channel output to the speaker. */
|
||||
enum intr_level old_level = intr_disable ();
|
||||
pit_configure_channel (2, 3, frequency);
|
||||
outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE);
|
||||
intr_set_level (old_level);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* FREQUENCY is outside the range of normal human hearing.
|
||||
Just turn off the speaker. */
|
||||
speaker_off ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Turn off the PC speaker, by disconnecting the timer channel's
|
||||
output from the speaker. */
|
||||
void
|
||||
speaker_off (void)
|
||||
{
|
||||
enum intr_level old_level = intr_disable ();
|
||||
outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE);
|
||||
intr_set_level (old_level);
|
||||
}
|
||||
|
||||
/* Briefly beep the PC speaker. */
|
||||
void
|
||||
speaker_beep (void)
|
||||
{
|
||||
/* Only attempt to beep the speaker if interrupts are enabled,
|
||||
because we don't want to freeze the machine during the beep.
|
||||
We could add a hook to the timer interrupt to avoid that
|
||||
problem, but then we'd risk failing to ever stop the beep if
|
||||
Pintos crashes for some unrelated reason. There's nothing
|
||||
more annoying than a machine whose beeping you can't stop
|
||||
without a power cycle.
|
||||
|
||||
We can't just enable interrupts while we sleep. For one
|
||||
thing, we get called (indirectly) from printf, which should
|
||||
always work, even during boot before we're ready to enable
|
||||
interrupts. */
|
||||
if (intr_get_level () == INTR_ON)
|
||||
{
|
||||
speaker_on (440);
|
||||
timer_msleep (250);
|
||||
speaker_off ();
|
||||
}
|
||||
}
|
8
pintos-env/pintos/devices/speaker.h
Executable file
8
pintos-env/pintos/devices/speaker.h
Executable file
|
@ -0,0 +1,8 @@
|
|||
#ifndef DEVICES_SPEAKER_H
|
||||
#define DEVICES_SPEAKER_H
|
||||
|
||||
void speaker_on (int frequency);
|
||||
void speaker_off (void);
|
||||
void speaker_beep (void);
|
||||
|
||||
#endif /* devices/speaker.h */
|
246
pintos-env/pintos/devices/timer.c
Executable file
246
pintos-env/pintos/devices/timer.c
Executable file
|
@ -0,0 +1,246 @@
|
|||
#include "devices/timer.h"
|
||||
#include <debug.h>
|
||||
#include <inttypes.h>
|
||||
#include <round.h>
|
||||
#include <stdio.h>
|
||||
#include "devices/pit.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/synch.h"
|
||||
#include "threads/thread.h"
|
||||
|
||||
/* See [8254] for hardware details of the 8254 timer chip. */
|
||||
|
||||
#if TIMER_FREQ < 19
|
||||
#error 8254 timer requires TIMER_FREQ >= 19
|
||||
#endif
|
||||
#if TIMER_FREQ > 1000
|
||||
#error TIMER_FREQ <= 1000 recommended
|
||||
#endif
|
||||
|
||||
/* Number of timer ticks since OS booted. */
|
||||
static int64_t ticks;
|
||||
|
||||
/* Number of loops per timer tick.
|
||||
Initialized by timer_calibrate(). */
|
||||
static unsigned loops_per_tick;
|
||||
|
||||
static intr_handler_func timer_interrupt;
|
||||
static bool too_many_loops (unsigned loops);
|
||||
static void busy_wait (int64_t loops);
|
||||
static void real_time_sleep (int64_t num, int32_t denom);
|
||||
static void real_time_delay (int64_t num, int32_t denom);
|
||||
|
||||
/* Sets up the timer to interrupt TIMER_FREQ times per second,
|
||||
and registers the corresponding interrupt. */
|
||||
void
|
||||
timer_init (void)
|
||||
{
|
||||
pit_configure_channel (0, 2, TIMER_FREQ);
|
||||
intr_register_ext (0x20, timer_interrupt, "8254 Timer");
|
||||
}
|
||||
|
||||
/* Calibrates loops_per_tick, used to implement brief delays. */
|
||||
void
|
||||
timer_calibrate (void)
|
||||
{
|
||||
unsigned high_bit, test_bit;
|
||||
|
||||
ASSERT (intr_get_level () == INTR_ON);
|
||||
printf ("Calibrating timer... ");
|
||||
|
||||
/* Approximate loops_per_tick as the largest power-of-two
|
||||
still less than one timer tick. */
|
||||
loops_per_tick = 1u << 10;
|
||||
while (!too_many_loops (loops_per_tick << 1))
|
||||
{
|
||||
loops_per_tick <<= 1;
|
||||
ASSERT (loops_per_tick != 0);
|
||||
}
|
||||
|
||||
/* Refine the next 8 bits of loops_per_tick. */
|
||||
high_bit = loops_per_tick;
|
||||
for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1)
|
||||
if (!too_many_loops (high_bit | test_bit))
|
||||
loops_per_tick |= test_bit;
|
||||
|
||||
printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ);
|
||||
}
|
||||
|
||||
/* Returns the number of timer ticks since the OS booted. */
|
||||
int64_t
|
||||
timer_ticks (void)
|
||||
{
|
||||
enum intr_level old_level = intr_disable ();
|
||||
int64_t t = ticks;
|
||||
intr_set_level (old_level);
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Returns the number of timer ticks elapsed since THEN, which
|
||||
should be a value once returned by timer_ticks(). */
|
||||
int64_t
|
||||
timer_elapsed (int64_t then)
|
||||
{
|
||||
return timer_ticks () - then;
|
||||
}
|
||||
|
||||
/* Sleeps for approximately TICKS timer ticks. Interrupts must
|
||||
be turned on. */
|
||||
void
|
||||
timer_sleep (int64_t ticks)
|
||||
{
|
||||
int64_t start = timer_ticks ();
|
||||
|
||||
ASSERT (intr_get_level () == INTR_ON);
|
||||
while (timer_elapsed (start) < ticks)
|
||||
thread_yield ();
|
||||
}
|
||||
|
||||
/* Sleeps for approximately MS milliseconds. Interrupts must be
|
||||
turned on. */
|
||||
void
|
||||
timer_msleep (int64_t ms)
|
||||
{
|
||||
real_time_sleep (ms, 1000);
|
||||
}
|
||||
|
||||
/* Sleeps for approximately US microseconds. Interrupts must be
|
||||
turned on. */
|
||||
void
|
||||
timer_usleep (int64_t us)
|
||||
{
|
||||
real_time_sleep (us, 1000 * 1000);
|
||||
}
|
||||
|
||||
/* Sleeps for approximately NS nanoseconds. Interrupts must be
|
||||
turned on. */
|
||||
void
|
||||
timer_nsleep (int64_t ns)
|
||||
{
|
||||
real_time_sleep (ns, 1000 * 1000 * 1000);
|
||||
}
|
||||
|
||||
/* Busy-waits for approximately MS milliseconds. Interrupts need
|
||||
not be turned on.
|
||||
|
||||
Busy waiting wastes CPU cycles, and busy waiting with
|
||||
interrupts off for the interval between timer ticks or longer
|
||||
will cause timer ticks to be lost. Thus, use timer_msleep()
|
||||
instead if interrupts are enabled. */
|
||||
void
|
||||
timer_mdelay (int64_t ms)
|
||||
{
|
||||
real_time_delay (ms, 1000);
|
||||
}
|
||||
|
||||
/* Sleeps for approximately US microseconds. Interrupts need not
|
||||
be turned on.
|
||||
|
||||
Busy waiting wastes CPU cycles, and busy waiting with
|
||||
interrupts off for the interval between timer ticks or longer
|
||||
will cause timer ticks to be lost. Thus, use timer_usleep()
|
||||
instead if interrupts are enabled. */
|
||||
void
|
||||
timer_udelay (int64_t us)
|
||||
{
|
||||
real_time_delay (us, 1000 * 1000);
|
||||
}
|
||||
|
||||
/* Sleeps execution for approximately NS nanoseconds. Interrupts
|
||||
need not be turned on.
|
||||
|
||||
Busy waiting wastes CPU cycles, and busy waiting with
|
||||
interrupts off for the interval between timer ticks or longer
|
||||
will cause timer ticks to be lost. Thus, use timer_nsleep()
|
||||
instead if interrupts are enabled.*/
|
||||
void
|
||||
timer_ndelay (int64_t ns)
|
||||
{
|
||||
real_time_delay (ns, 1000 * 1000 * 1000);
|
||||
}
|
||||
|
||||
/* Prints timer statistics. */
|
||||
void
|
||||
timer_print_stats (void)
|
||||
{
|
||||
printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
|
||||
}
|
||||
|
||||
/* Timer interrupt handler. */
|
||||
static void
|
||||
timer_interrupt (struct intr_frame *args UNUSED)
|
||||
{
|
||||
ticks++;
|
||||
thread_tick ();
|
||||
}
|
||||
|
||||
/* Returns true if LOOPS iterations waits for more than one timer
|
||||
tick, otherwise false. */
|
||||
static bool
|
||||
too_many_loops (unsigned loops)
|
||||
{
|
||||
/* Wait for a timer tick. */
|
||||
int64_t start = ticks;
|
||||
while (ticks == start)
|
||||
barrier ();
|
||||
|
||||
/* Run LOOPS loops. */
|
||||
start = ticks;
|
||||
busy_wait (loops);
|
||||
|
||||
/* If the tick count changed, we iterated too long. */
|
||||
barrier ();
|
||||
return start != ticks;
|
||||
}
|
||||
|
||||
/* Iterates through a simple loop LOOPS times, for implementing
|
||||
brief delays.
|
||||
|
||||
Marked NO_INLINE because code alignment can significantly
|
||||
affect timings, so that if this function was inlined
|
||||
differently in different places the results would be difficult
|
||||
to predict. */
|
||||
static void NO_INLINE
|
||||
busy_wait (int64_t loops)
|
||||
{
|
||||
while (loops-- > 0)
|
||||
barrier ();
|
||||
}
|
||||
|
||||
/* Sleep for approximately NUM/DENOM seconds. */
|
||||
static void
|
||||
real_time_sleep (int64_t num, int32_t denom)
|
||||
{
|
||||
/* Convert NUM/DENOM seconds into timer ticks, rounding down.
|
||||
|
||||
(NUM / DENOM) s
|
||||
---------------------- = NUM * TIMER_FREQ / DENOM ticks.
|
||||
1 s / TIMER_FREQ ticks
|
||||
*/
|
||||
int64_t ticks = num * TIMER_FREQ / denom;
|
||||
|
||||
ASSERT (intr_get_level () == INTR_ON);
|
||||
if (ticks > 0)
|
||||
{
|
||||
/* We're waiting for at least one full timer tick. Use
|
||||
timer_sleep() because it will yield the CPU to other
|
||||
processes. */
|
||||
timer_sleep (ticks);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Otherwise, use a busy-wait loop for more accurate
|
||||
sub-tick timing. */
|
||||
real_time_delay (num, denom);
|
||||
}
|
||||
}
|
||||
|
||||
/* Busy-wait for approximately NUM/DENOM seconds. */
|
||||
static void
|
||||
real_time_delay (int64_t num, int32_t denom)
|
||||
{
|
||||
/* Scale the numerator and denominator down by 1000 to avoid
|
||||
the possibility of overflow. */
|
||||
ASSERT (denom % 1000 == 0);
|
||||
busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000));
|
||||
}
|
29
pintos-env/pintos/devices/timer.h
Executable file
29
pintos-env/pintos/devices/timer.h
Executable file
|
@ -0,0 +1,29 @@
|
|||
#ifndef DEVICES_TIMER_H
|
||||
#define DEVICES_TIMER_H
|
||||
|
||||
#include <round.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Number of timer interrupts per second. */
|
||||
#define TIMER_FREQ 100
|
||||
|
||||
void timer_init (void);
|
||||
void timer_calibrate (void);
|
||||
|
||||
int64_t timer_ticks (void);
|
||||
int64_t timer_elapsed (int64_t);
|
||||
|
||||
/* Sleep and yield the CPU to other threads. */
|
||||
void timer_sleep (int64_t ticks);
|
||||
void timer_msleep (int64_t milliseconds);
|
||||
void timer_usleep (int64_t microseconds);
|
||||
void timer_nsleep (int64_t nanoseconds);
|
||||
|
||||
/* Busy waits. */
|
||||
void timer_mdelay (int64_t milliseconds);
|
||||
void timer_udelay (int64_t microseconds);
|
||||
void timer_ndelay (int64_t nanoseconds);
|
||||
|
||||
void timer_print_stats (void);
|
||||
|
||||
#endif /* devices/timer.h */
|
172
pintos-env/pintos/devices/vga.c
Executable file
172
pintos-env/pintos/devices/vga.c
Executable file
|
@ -0,0 +1,172 @@
|
|||
#include "devices/vga.h"
|
||||
#include <round.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "devices/speaker.h"
|
||||
#include "threads/io.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/vaddr.h"
|
||||
|
||||
/* VGA text screen support. See [FREEVGA] for more information. */
|
||||
|
||||
/* Number of columns and rows on the text display. */
|
||||
#define COL_CNT 80
|
||||
#define ROW_CNT 25
|
||||
|
||||
/* Current cursor position. (0,0) is in the upper left corner of
|
||||
the display. */
|
||||
static size_t cx, cy;
|
||||
|
||||
/* Attribute value for gray text on a black background. */
|
||||
#define GRAY_ON_BLACK 0x07
|
||||
|
||||
/* Framebuffer. See [FREEVGA] under "VGA Text Mode Operation".
|
||||
The character at (x,y) is fb[y][x][0].
|
||||
The attribute at (x,y) is fb[y][x][1]. */
|
||||
static uint8_t (*fb)[COL_CNT][2];
|
||||
|
||||
static void clear_row (size_t y);
|
||||
static void cls (void);
|
||||
static void newline (void);
|
||||
static void move_cursor (void);
|
||||
static void find_cursor (size_t *x, size_t *y);
|
||||
|
||||
/* Initializes the VGA text display. */
|
||||
static void
|
||||
init (void)
|
||||
{
|
||||
/* Already initialized? */
|
||||
static bool inited;
|
||||
if (!inited)
|
||||
{
|
||||
fb = ptov (0xb8000);
|
||||
find_cursor (&cx, &cy);
|
||||
inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Writes C to the VGA text display, interpreting control
|
||||
characters in the conventional ways. */
|
||||
void
|
||||
vga_putc (int c)
|
||||
{
|
||||
/* Disable interrupts to lock out interrupt handlers
|
||||
that might write to the console. */
|
||||
enum intr_level old_level = intr_disable ();
|
||||
|
||||
init ();
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
newline ();
|
||||
break;
|
||||
|
||||
case '\f':
|
||||
cls ();
|
||||
break;
|
||||
|
||||
case '\b':
|
||||
if (cx > 0)
|
||||
cx--;
|
||||
break;
|
||||
|
||||
case '\r':
|
||||
cx = 0;
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
cx = ROUND_UP (cx + 1, 8);
|
||||
if (cx >= COL_CNT)
|
||||
newline ();
|
||||
break;
|
||||
|
||||
case '\a':
|
||||
intr_set_level (old_level);
|
||||
speaker_beep ();
|
||||
intr_disable ();
|
||||
break;
|
||||
|
||||
default:
|
||||
fb[cy][cx][0] = c;
|
||||
fb[cy][cx][1] = GRAY_ON_BLACK;
|
||||
if (++cx >= COL_CNT)
|
||||
newline ();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Update cursor position. */
|
||||
move_cursor ();
|
||||
|
||||
intr_set_level (old_level);
|
||||
}
|
||||
|
||||
/* Clears the screen and moves the cursor to the upper left. */
|
||||
static void
|
||||
cls (void)
|
||||
{
|
||||
size_t y;
|
||||
|
||||
for (y = 0; y < ROW_CNT; y++)
|
||||
clear_row (y);
|
||||
|
||||
cx = cy = 0;
|
||||
move_cursor ();
|
||||
}
|
||||
|
||||
/* Clears row Y to spaces. */
|
||||
static void
|
||||
clear_row (size_t y)
|
||||
{
|
||||
size_t x;
|
||||
|
||||
for (x = 0; x < COL_CNT; x++)
|
||||
{
|
||||
fb[y][x][0] = ' ';
|
||||
fb[y][x][1] = GRAY_ON_BLACK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Advances the cursor to the first column in the next line on
|
||||
the screen. If the cursor is already on the last line on the
|
||||
screen, scrolls the screen upward one line. */
|
||||
static void
|
||||
newline (void)
|
||||
{
|
||||
cx = 0;
|
||||
cy++;
|
||||
if (cy >= ROW_CNT)
|
||||
{
|
||||
cy = ROW_CNT - 1;
|
||||
memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1));
|
||||
clear_row (ROW_CNT - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Moves the hardware cursor to (cx,cy). */
|
||||
static void
|
||||
move_cursor (void)
|
||||
{
|
||||
/* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
|
||||
uint16_t cp = cx + COL_CNT * cy;
|
||||
outw (0x3d4, 0x0e | (cp & 0xff00));
|
||||
outw (0x3d4, 0x0f | (cp << 8));
|
||||
}
|
||||
|
||||
/* Reads the current hardware cursor position into (*X,*Y). */
|
||||
static void
|
||||
find_cursor (size_t *x, size_t *y)
|
||||
{
|
||||
/* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
|
||||
uint16_t cp;
|
||||
|
||||
outb (0x3d4, 0x0e);
|
||||
cp = inb (0x3d5) << 8;
|
||||
|
||||
outb (0x3d4, 0x0f);
|
||||
cp |= inb (0x3d5);
|
||||
|
||||
*x = cp % COL_CNT;
|
||||
*y = cp / COL_CNT;
|
||||
}
|
6
pintos-env/pintos/devices/vga.h
Executable file
6
pintos-env/pintos/devices/vga.h
Executable file
|
@ -0,0 +1,6 @@
|
|||
#ifndef DEVICES_VGA_H
|
||||
#define DEVICES_VGA_H
|
||||
|
||||
void vga_putc (int);
|
||||
|
||||
#endif /* devices/vga.h */
|
34
pintos-env/pintos/examples/Makefile
Executable file
34
pintos-env/pintos/examples/Makefile
Executable file
|
@ -0,0 +1,34 @@
|
|||
SRCDIR = ..
|
||||
|
||||
# Test programs to compile, and a list of sources for each.
|
||||
# To add a new test, put its name on the PROGS list
|
||||
# and then add a name_SRC line that lists its source files.
|
||||
PROGS = cat cmp cp echo halt hex-dump ls mcat mcp mkdir pwd rm shell \
|
||||
bubsort insult lineup matmult recursor
|
||||
|
||||
# Should work from project 2 onward.
|
||||
cat_SRC = cat.c
|
||||
cmp_SRC = cmp.c
|
||||
cp_SRC = cp.c
|
||||
echo_SRC = echo.c
|
||||
halt_SRC = halt.c
|
||||
hex-dump_SRC = hex-dump.c
|
||||
insult_SRC = insult.c
|
||||
lineup_SRC = lineup.c
|
||||
ls_SRC = ls.c
|
||||
recursor_SRC = recursor.c
|
||||
rm_SRC = rm.c
|
||||
|
||||
# Should work in project 3; also in project 4 if VM is included.
|
||||
bubsort_SRC = bubsort.c
|
||||
matmult_SRC = matmult.c
|
||||
mcat_SRC = mcat.c
|
||||
mcp_SRC = mcp.c
|
||||
|
||||
# Should work in project 4.
|
||||
mkdir_SRC = mkdir.c
|
||||
pwd_SRC = pwd.c
|
||||
shell_SRC = shell.c
|
||||
|
||||
include $(SRCDIR)/Make.config
|
||||
include $(SRCDIR)/Makefile.userprog
|
38
pintos-env/pintos/examples/bubsort.c
Executable file
38
pintos-env/pintos/examples/bubsort.c
Executable file
|
@ -0,0 +1,38 @@
|
|||
/* sort.c
|
||||
|
||||
Test program to sort a large number of integers.
|
||||
|
||||
Intention is to stress virtual memory system.
|
||||
|
||||
Ideally, we could read the unsorted array off of the file
|
||||
system, and store the result back to the file system! */
|
||||
#include <stdio.h>
|
||||
|
||||
/* Size of array to sort. */
|
||||
#define SORT_SIZE 128
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
/* Array to sort. Static to reduce stack usage. */
|
||||
static int array[SORT_SIZE];
|
||||
|
||||
int i, j, tmp;
|
||||
|
||||
/* First initialize the array in descending order. */
|
||||
for (i = 0; i < SORT_SIZE; i++)
|
||||
array[i] = SORT_SIZE - i - 1;
|
||||
|
||||
/* Then sort in ascending order. */
|
||||
for (i = 0; i < SORT_SIZE - 1; i++)
|
||||
for (j = 0; j < SORT_SIZE - 1 - i; j++)
|
||||
if (array[j] > array[j + 1])
|
||||
{
|
||||
tmp = array[j];
|
||||
array[j] = array[j + 1];
|
||||
array[j + 1] = tmp;
|
||||
}
|
||||
|
||||
printf ("sort exiting with code %d\n", array[0]);
|
||||
return array[0];
|
||||
}
|
34
pintos-env/pintos/examples/cat.c
Executable file
34
pintos-env/pintos/examples/cat.c
Executable file
|
@ -0,0 +1,34 @@
|
|||
/* cat.c
|
||||
|
||||
Prints files specified on command line to the console. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
bool success = true;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
int fd = open (argv[i]);
|
||||
if (fd < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[i]);
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
char buffer[1024];
|
||||
int bytes_read = read (fd, buffer, sizeof buffer);
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
write (STDOUT_FILENO, buffer, bytes_read);
|
||||
}
|
||||
close (fd);
|
||||
}
|
||||
return success ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
68
pintos-env/pintos/examples/cmp.c
Executable file
68
pintos-env/pintos/examples/cmp.c
Executable file
|
@ -0,0 +1,68 @@
|
|||
/* cat.c
|
||||
|
||||
Compares two files. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int fd[2];
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf ("usage: cmp A B\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Open files. */
|
||||
fd[0] = open (argv[1]);
|
||||
if (fd[0] < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
fd[1] = open (argv[2]);
|
||||
if (fd[1] < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Compare data. */
|
||||
for (;;)
|
||||
{
|
||||
int pos;
|
||||
char buffer[2][1024];
|
||||
int bytes_read[2];
|
||||
int min_read;
|
||||
int i;
|
||||
|
||||
pos = tell (fd[0]);
|
||||
bytes_read[0] = read (fd[0], buffer[0], sizeof buffer[0]);
|
||||
bytes_read[1] = read (fd[1], buffer[1], sizeof buffer[1]);
|
||||
min_read = bytes_read[0] < bytes_read[1] ? bytes_read[0] : bytes_read[1];
|
||||
if (min_read == 0)
|
||||
break;
|
||||
|
||||
for (i = 0; i < min_read; i++)
|
||||
if (buffer[0][i] != buffer[1][i])
|
||||
{
|
||||
printf ("Byte %d is %02hhx ('%c') in %s but %02hhx ('%c') in %s\n",
|
||||
pos + i,
|
||||
buffer[0][i], buffer[0][i], argv[1],
|
||||
buffer[1][i], buffer[1][i], argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (min_read < bytes_read[1])
|
||||
printf ("%s is shorter than %s\n", argv[1], argv[2]);
|
||||
else if (min_read < bytes_read[0])
|
||||
printf ("%s is shorter than %s\n", argv[2], argv[1]);
|
||||
}
|
||||
|
||||
printf ("%s and %s are identical\n", argv[1], argv[2]);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
55
pintos-env/pintos/examples/cp.c
Executable file
55
pintos-env/pintos/examples/cp.c
Executable file
|
@ -0,0 +1,55 @@
|
|||
/* cat.c
|
||||
|
||||
Copies one file to another. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int in_fd, out_fd;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf ("usage: cp OLD NEW\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Open input file. */
|
||||
in_fd = open (argv[1]);
|
||||
if (in_fd < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Create and open output file. */
|
||||
if (!create (argv[2], filesize (in_fd)))
|
||||
{
|
||||
printf ("%s: create failed\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
out_fd = open (argv[2]);
|
||||
if (out_fd < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Copy data. */
|
||||
for (;;)
|
||||
{
|
||||
char buffer[1024];
|
||||
int bytes_read = read (in_fd, buffer, sizeof buffer);
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
if (write (out_fd, buffer, bytes_read) != bytes_read)
|
||||
{
|
||||
printf ("%s: write failed\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
14
pintos-env/pintos/examples/echo.c
Executable file
14
pintos-env/pintos/examples/echo.c
Executable file
|
@ -0,0 +1,14 @@
|
|||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
printf ("%s ", argv[i]);
|
||||
printf ("\n");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
14
pintos-env/pintos/examples/halt.c
Executable file
14
pintos-env/pintos/examples/halt.c
Executable file
|
@ -0,0 +1,14 @@
|
|||
/* halt.c
|
||||
|
||||
Simple program to test whether running a user program works.
|
||||
|
||||
Just invokes a system call that shuts down the OS. */
|
||||
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
halt ();
|
||||
/* not reached */
|
||||
}
|
35
pintos-env/pintos/examples/hex-dump.c
Executable file
35
pintos-env/pintos/examples/hex-dump.c
Executable file
|
@ -0,0 +1,35 @@
|
|||
/* hex-dump.c
|
||||
|
||||
Prints files specified on command line to the console in hex. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
bool success = true;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
int fd = open (argv[i]);
|
||||
if (fd < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[i]);
|
||||
success = false;
|
||||
continue;
|
||||
}
|
||||
for (;;)
|
||||
{
|
||||
char buffer[1024];
|
||||
int pos = tell (fd);
|
||||
int bytes_read = read (fd, buffer, sizeof buffer);
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
hex_dump (pos, buffer, bytes_read, true);
|
||||
}
|
||||
close (fd);
|
||||
}
|
||||
return success ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
369
pintos-env/pintos/examples/insult.c
Executable file
369
pintos-env/pintos/examples/insult.c
Executable file
|
@ -0,0 +1,369 @@
|
|||
/* Insult.c
|
||||
|
||||
This is a version of the famous CS 107 random sentence
|
||||
generator. I wrote a program that reads a grammar definition
|
||||
file and writes a C file containing that grammar as hard code
|
||||
static C strings. Thus the majority of the code below in
|
||||
machine generated and totally unreadable. The arrays created
|
||||
are specially designed to make generating the sentences as
|
||||
easy as possible.
|
||||
|
||||
Originally by Greg Hutchins, March 1998.
|
||||
Modified by Ben Pfaff for Pintos, Sept 2004. */
|
||||
char *start[] =
|
||||
{ "You", "1", "5", ".", "May", "13", ".", "With", "the", "19", "of", "18",
|
||||
",", "may", "13", "."
|
||||
};
|
||||
char startLoc[] = { 3, 0, 4, 7, 16 };
|
||||
char *adj[] = { "3", "4", "2", ",", "1" };
|
||||
char adjLoc[] = { 3, 0, 1, 2, 5 };
|
||||
char *adj3[] = { "3", "4" };
|
||||
char adj3Loc[] = { 2, 0, 1, 2 };
|
||||
char *adj1[] =
|
||||
{ "lame", "dried", "up", "par-broiled", "bloated", "half-baked", "spiteful",
|
||||
"egotistical", "ungrateful", "stupid", "moronic", "fat", "ugly", "puny", "pitiful",
|
||||
"insignificant", "blithering", "repulsive", "worthless", "blundering", "retarded",
|
||||
"useless", "obnoxious", "low-budget", "assinine", "neurotic", "subhuman", "crochety",
|
||||
"indescribable", "contemptible", "unspeakable", "sick", "lazy", "good-for-nothing",
|
||||
"slutty", "mentally-deficient", "creepy", "sloppy", "dismal", "pompous", "pathetic",
|
||||
"friendless", "revolting", "slovenly", "cantankerous", "uncultured", "insufferable",
|
||||
"gross", "unkempt", "defective", "crumby"
|
||||
};
|
||||
char adj1Loc[] =
|
||||
{ 50, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
|
||||
43, 44, 45, 46, 47, 48, 49, 50, 51 };
|
||||
char *adj2[] =
|
||||
{ "putrefied", "festering", "funky", "moldy", "leprous", "curdled", "fetid",
|
||||
"slimy", "crusty", "sweaty", "damp", "deranged", "smelly", "stenchy", "malignant",
|
||||
"noxious", "grimy", "reeky", "nasty", "mutilated", "sloppy", "gruesome", "grisly",
|
||||
"sloshy", "wormy", "mealy", "spoiled", "contaminated", "rancid", "musty",
|
||||
"fly-covered", "moth-eaten", "decaying", "decomposed", "freeze-dried", "defective",
|
||||
"petrified", "rotting", "scabrous", "hirsute"
|
||||
};
|
||||
char adj2Loc[] =
|
||||
{ 40, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 };
|
||||
char *name[] =
|
||||
{ "10", ",", "bad", "excuse", "for", "6", ",", "6", "for", "brains", ",",
|
||||
"4", "11", "8", "for", "brains", "offspring", "of", "a", "motherless", "10", "7", "6",
|
||||
"7", "4", "11", "8"
|
||||
};
|
||||
char nameLoc[] = { 7, 0, 1, 6, 10, 16, 21, 23, 27 };
|
||||
char *stuff[] =
|
||||
{ "shit", "toe", "jam", "filth", "puss", "earwax", "leaf", "clippings",
|
||||
"bat", "guano", "mucus", "fungus", "mung", "refuse", "earwax", "spittoon", "spittle",
|
||||
"phlegm"
|
||||
};
|
||||
char stuffLoc[] = { 14, 0, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15, 17, 18 };
|
||||
char *noun_and_prep[] =
|
||||
{ "bit", "of", "piece", "of", "vat", "of", "lump", "of", "crock", "of",
|
||||
"ball", "of", "tub", "of", "load", "of", "bucket", "of", "mound", "of", "glob", "of", "bag",
|
||||
"of", "heap", "of", "mountain", "of", "load", "of", "barrel", "of", "sack", "of", "blob", "of",
|
||||
"pile", "of", "truckload", "of", "vat", "of"
|
||||
};
|
||||
char noun_and_prepLoc[] =
|
||||
{ 21, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36,
|
||||
38, 40, 42 };
|
||||
char *organics[] =
|
||||
{ "droppings", "mung", "zits", "puckies", "tumors", "cysts", "tumors",
|
||||
"livers", "froth", "parts", "scabs", "guts", "entrails", "blubber", "carcuses", "gizards",
|
||||
"9"
|
||||
};
|
||||
char organicsLoc[] =
|
||||
{ 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
|
||||
char *body_parts[] =
|
||||
{ "kidneys", "genitals", "buttocks", "earlobes", "innards", "feet"
|
||||
};
|
||||
char body_partsLoc[] = { 6, 0, 1, 2, 3, 4, 5, 6 };
|
||||
char *noun[] =
|
||||
{ "pop", "tart", "warthog", "twinkie", "barnacle", "fondue", "pot",
|
||||
"cretin", "fuckwad", "moron", "ass", "neanderthal", "nincompoop", "simpleton", "11"
|
||||
};
|
||||
char nounLoc[] = { 13, 0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
|
||||
char *animal[] =
|
||||
{ "donkey", "llama", "dingo", "lizard", "gekko", "lemur", "moose", "camel",
|
||||
"goat", "eel"
|
||||
};
|
||||
char animalLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
char *good_verb[] =
|
||||
{ "love", "cuddle", "fondle", "adore", "smooch", "hug", "caress", "worship",
|
||||
"look", "at", "touch"
|
||||
};
|
||||
char good_verbLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11 };
|
||||
char *curse[] =
|
||||
{ "14", "20", "23", "14", "17", "20", "23", "14", "find", "your", "9",
|
||||
"suddenly", "delectable", "14", "and", "14", "seek", "a", "battleground", "23"
|
||||
};
|
||||
char curseLoc[] = { 4, 0, 3, 7, 13, 20 };
|
||||
char *afflictors[] =
|
||||
{ "15", "21", "15", "21", "15", "21", "15", "21", "a", "22", "Rush",
|
||||
"Limbaugh", "the", "hosts", "of", "Hades"
|
||||
};
|
||||
char afflictorsLoc[] = { 6, 0, 2, 4, 6, 8, 12, 16 };
|
||||
char *quantity[] =
|
||||
{ "a", "4", "hoard", "of", "a", "4", "pack", "of", "a", "truckload", "of",
|
||||
"a", "swarm", "of", "many", "an", "army", "of", "a", "4", "heard", "of", "a", "4",
|
||||
"platoon", "of", "a", "4", "and", "4", "group", "of", "16"
|
||||
};
|
||||
char quantityLoc[] = { 10, 0, 4, 8, 11, 14, 15, 18, 22, 26, 32, 33 };
|
||||
char *numbers[] =
|
||||
{ "a", "thousand", "three", "million", "ninty-nine", "nine-hundred,",
|
||||
"ninty-nine", "forty-two", "a", "gazillion", "sixty-eight", "times", "thirty-three"
|
||||
};
|
||||
char numbersLoc[] = { 7, 0, 2, 4, 5, 7, 8, 10, 13 };
|
||||
char *adv[] =
|
||||
{ "viciously", "manicly", "merrily", "happily", ",", "with", "the", "19",
|
||||
"of", "18", ",", "gleefully", ",", "with", "much", "ritualistic", "celebration", ",",
|
||||
"franticly"
|
||||
};
|
||||
char advLoc[] = { 8, 0, 1, 2, 3, 4, 11, 12, 18, 19 };
|
||||
char *metaphor[] =
|
||||
{ "an", "irate", "manticore", "Thor's", "belch", "Alah's", "fist", "16",
|
||||
"titans", "a", "particularly", "vicious", "she-bear", "in", "the", "midst", "of", "her",
|
||||
"menstrual", "cycle", "a", "pissed-off", "Jabberwock"
|
||||
};
|
||||
char metaphorLoc[] = { 6, 0, 3, 5, 7, 9, 20, 23 };
|
||||
char *force[] = { "force", "fury", "power", "rage" };
|
||||
char forceLoc[] = { 4, 0, 1, 2, 3, 4 };
|
||||
char *bad_action[] =
|
||||
{ "spit", "shimmy", "slobber", "find", "refuge", "find", "shelter", "dance",
|
||||
"retch", "vomit", "defecate", "erect", "a", "strip", "mall", "build", "a", "26", "have", "a",
|
||||
"religious", "experience", "discharge", "bodily", "waste", "fart", "dance", "drool",
|
||||
"lambada", "spill", "16", "rusty", "tacks", "bite", "you", "sneeze", "sing", "16",
|
||||
"campfire", "songs", "smite", "you", "16", "times", "construct", "a", "new", "home", "throw",
|
||||
"a", "party", "procreate"
|
||||
};
|
||||
char bad_actionLoc[] =
|
||||
{ 25, 0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 15, 18, 22, 25, 26, 27, 28, 29, 33,
|
||||
35, 36, 40, 44, 48, 51, 52 };
|
||||
char *beasties[] =
|
||||
{ "yaks", "22", "maggots", "22", "cockroaches", "stinging", "scorpions",
|
||||
"fleas", "22", "weasels", "22", "gnats", "South", "American", "killer", "bees", "spiders",
|
||||
"4", "monkeys", "22", "wiener-dogs", "22", "rats", "22", "wolverines", "4", ",", "22",
|
||||
"pit-fiends"
|
||||
};
|
||||
char beastiesLoc[] =
|
||||
{ 14, 0, 1, 3, 5, 7, 8, 10, 12, 16, 17, 19, 21, 23, 25, 29 };
|
||||
char *condition[] =
|
||||
{ "frothing", "manic", "crazed", "plague-ridden", "disease-carrying",
|
||||
"biting", "rabid", "blood-thirsty", "ravaging", "slavering"
|
||||
};
|
||||
char conditionLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
char *place[] =
|
||||
{ "in", "24", "25", "upon", "your", "mother's", "grave", "on", "24", "best",
|
||||
"rug", "in", "the", "26", "you", "call", "home", "upon", "your", "heinie"
|
||||
};
|
||||
char placeLoc[] = { 5, 0, 3, 7, 11, 17, 20 };
|
||||
char *relation[] =
|
||||
{ "your", "your", "your", "your", "father's", "your", "mother's", "your",
|
||||
"grandma's"
|
||||
};
|
||||
char relationLoc[] = { 6, 0, 1, 2, 3, 5, 7, 9 };
|
||||
char *in_something[] =
|
||||
{ "entrails", "anal", "cavity", "shoes", "house", "pantry", "general",
|
||||
"direction", "pants", "bed"
|
||||
};
|
||||
char in_somethingLoc[] = { 8, 0, 1, 3, 4, 5, 6, 8, 9, 10 };
|
||||
char *bad_place[] =
|
||||
{ "rat", "hole", "sewer", "toxic", "dump", "oil", "refinery", "landfill",
|
||||
"porto-pottie"
|
||||
};
|
||||
char bad_placeLoc[] = { 6, 0, 2, 3, 5, 7, 8, 9 };
|
||||
char **daGrammar[27];
|
||||
char *daGLoc[27];
|
||||
|
||||
static void
|
||||
init_grammar (void)
|
||||
{
|
||||
daGrammar[0] = start;
|
||||
daGLoc[0] = startLoc;
|
||||
daGrammar[1] = adj;
|
||||
daGLoc[1] = adjLoc;
|
||||
daGrammar[2] = adj3;
|
||||
daGLoc[2] = adj3Loc;
|
||||
daGrammar[3] = adj1;
|
||||
daGLoc[3] = adj1Loc;
|
||||
daGrammar[4] = adj2;
|
||||
daGLoc[4] = adj2Loc;
|
||||
daGrammar[5] = name;
|
||||
daGLoc[5] = nameLoc;
|
||||
daGrammar[6] = stuff;
|
||||
daGLoc[6] = stuffLoc;
|
||||
daGrammar[7] = noun_and_prep;
|
||||
daGLoc[7] = noun_and_prepLoc;
|
||||
daGrammar[8] = organics;
|
||||
daGLoc[8] = organicsLoc;
|
||||
daGrammar[9] = body_parts;
|
||||
daGLoc[9] = body_partsLoc;
|
||||
daGrammar[10] = noun;
|
||||
daGLoc[10] = nounLoc;
|
||||
daGrammar[11] = animal;
|
||||
daGLoc[11] = animalLoc;
|
||||
daGrammar[12] = good_verb;
|
||||
daGLoc[12] = good_verbLoc;
|
||||
daGrammar[13] = curse;
|
||||
daGLoc[13] = curseLoc;
|
||||
daGrammar[14] = afflictors;
|
||||
daGLoc[14] = afflictorsLoc;
|
||||
daGrammar[15] = quantity;
|
||||
daGLoc[15] = quantityLoc;
|
||||
daGrammar[16] = numbers;
|
||||
daGLoc[16] = numbersLoc;
|
||||
daGrammar[17] = adv;
|
||||
daGLoc[17] = advLoc;
|
||||
daGrammar[18] = metaphor;
|
||||
daGLoc[18] = metaphorLoc;
|
||||
daGrammar[19] = force;
|
||||
daGLoc[19] = forceLoc;
|
||||
daGrammar[20] = bad_action;
|
||||
daGLoc[20] = bad_actionLoc;
|
||||
daGrammar[21] = beasties;
|
||||
daGLoc[21] = beastiesLoc;
|
||||
daGrammar[22] = condition;
|
||||
daGLoc[22] = conditionLoc;
|
||||
daGrammar[23] = place;
|
||||
daGLoc[23] = placeLoc;
|
||||
daGrammar[24] = relation;
|
||||
daGLoc[24] = relationLoc;
|
||||
daGrammar[25] = in_something;
|
||||
daGLoc[25] = in_somethingLoc;
|
||||
daGrammar[26] = bad_place;
|
||||
daGLoc[26] = bad_placeLoc;
|
||||
}
|
||||
|
||||
#include <ctype.h>
|
||||
#include <debug.h>
|
||||
#include <random.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
void expand (int num, char **grammar[], char *location[], int handle);
|
||||
|
||||
static void
|
||||
usage (int ret_code, const char *message, ...) PRINTF_FORMAT (2, 3);
|
||||
|
||||
static void
|
||||
usage (int ret_code, const char *message, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
if (message != NULL)
|
||||
{
|
||||
va_start (args, message);
|
||||
vprintf (message, args);
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
printf ("\n"
|
||||
"Usage: insult [OPTION]...\n"
|
||||
"Prints random insults to screen.\n\n"
|
||||
" -h: this help message\n"
|
||||
" -s <integer>: set the random seed (default 4951)\n"
|
||||
" -n <integer>: choose number of insults (default 4)\n"
|
||||
" -f <file>: redirect output to <file>\n");
|
||||
|
||||
exit (ret_code);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int sentence_cnt, new_seed, i, file_flag, sent_flag, seed_flag;
|
||||
int handle;
|
||||
|
||||
new_seed = 4951;
|
||||
sentence_cnt = 4;
|
||||
file_flag = 0;
|
||||
seed_flag = 0;
|
||||
sent_flag = 0;
|
||||
handle = STDOUT_FILENO;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp (argv[1], "-h") == 0)
|
||||
usage (0, NULL);
|
||||
else if (strcmp (argv[i], "-s") == 0)
|
||||
{
|
||||
if (seed_flag++)
|
||||
usage (-1, "Can't have more than one seed");
|
||||
if (++i >= argc)
|
||||
usage (-1, "Missing value for -s");
|
||||
new_seed = atoi (argv[i]);
|
||||
}
|
||||
else if (strcmp (argv[i], "-n") == 0)
|
||||
{
|
||||
if (sent_flag++)
|
||||
usage (-1, "Can't have more than one sentence option");
|
||||
if (++i >= argc)
|
||||
usage (-1, "Missing value for -n");
|
||||
sentence_cnt = atoi (argv[i]);
|
||||
if (sentence_cnt < 1)
|
||||
usage (-1, "Must have at least one sentence");
|
||||
}
|
||||
else if (strcmp (argv[i], "-f") == 0)
|
||||
{
|
||||
if (file_flag++)
|
||||
usage (-1, "Can't have more than one output file");
|
||||
if (++i >= argc)
|
||||
usage (-1, "Missing value for -f");
|
||||
|
||||
/* Because files have fixed length in the basic Pintos
|
||||
file system, the 0 argument means that this option
|
||||
will not be useful until project 4 is
|
||||
implemented. */
|
||||
create (argv[i], 0);
|
||||
handle = open (argv[i]);
|
||||
if (handle < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
usage (-1, "Unrecognized flag");
|
||||
}
|
||||
|
||||
init_grammar ();
|
||||
|
||||
random_init (new_seed);
|
||||
hprintf (handle, "\n");
|
||||
|
||||
for (i = 0; i < sentence_cnt; i++)
|
||||
{
|
||||
hprintf (handle, "\n");
|
||||
expand (0, daGrammar, daGLoc, handle);
|
||||
hprintf (handle, "\n\n");
|
||||
}
|
||||
|
||||
if (file_flag)
|
||||
close (handle);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
expand (int num, char **grammar[], char *location[], int handle)
|
||||
{
|
||||
char *word;
|
||||
int i, which, listStart, listEnd;
|
||||
|
||||
which = random_ulong () % location[num][0] + 1;
|
||||
listStart = location[num][which];
|
||||
listEnd = location[num][which + 1];
|
||||
for (i = listStart; i < listEnd; i++)
|
||||
{
|
||||
word = grammar[num][i];
|
||||
if (!isdigit (*word))
|
||||
{
|
||||
if (!ispunct (*word))
|
||||
hprintf (handle, " ");
|
||||
hprintf (handle, "%s", word);
|
||||
}
|
||||
else
|
||||
expand (atoi (word), grammar, location, handle);
|
||||
}
|
||||
|
||||
}
|
1
pintos-env/pintos/examples/lib/.gitignore
vendored
Executable file
1
pintos-env/pintos/examples/lib/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
*.d
|
0
pintos-env/pintos/examples/lib/user/.dummy
Executable file
0
pintos-env/pintos/examples/lib/user/.dummy
Executable file
1
pintos-env/pintos/examples/lib/user/.gitignore
vendored
Executable file
1
pintos-env/pintos/examples/lib/user/.gitignore
vendored
Executable file
|
@ -0,0 +1 @@
|
|||
*.d
|
46
pintos-env/pintos/examples/lineup.c
Executable file
46
pintos-env/pintos/examples/lineup.c
Executable file
|
@ -0,0 +1,46 @@
|
|||
/* lineup.c
|
||||
|
||||
Converts a file to uppercase in-place.
|
||||
|
||||
Incidentally, another way to do this while avoiding the seeks
|
||||
would be to open the input file, then remove() it and reopen
|
||||
it under another handle. Because of Unix deletion semantics
|
||||
this works fine. */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
char buf[1024];
|
||||
int handle;
|
||||
|
||||
if (argc != 2)
|
||||
exit (1);
|
||||
|
||||
handle = open (argv[1]);
|
||||
if (handle < 0)
|
||||
exit (2);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int n, i;
|
||||
|
||||
n = read (handle, buf, sizeof buf);
|
||||
if (n <= 0)
|
||||
break;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
buf[i] = toupper ((unsigned char) buf[i]);
|
||||
|
||||
seek (handle, tell (handle) - n);
|
||||
if (write (handle, buf, n) != n)
|
||||
printf ("write failed\n");
|
||||
}
|
||||
|
||||
close (handle);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
90
pintos-env/pintos/examples/ls.c
Executable file
90
pintos-env/pintos/examples/ls.c
Executable file
|
@ -0,0 +1,90 @@
|
|||
/* ls.c
|
||||
|
||||
Lists the contents of the directory or directories named on
|
||||
the command line, or of the current directory if none are
|
||||
named.
|
||||
|
||||
By default, only the name of each file is printed. If "-l" is
|
||||
given as the first argument, the type, size, and inumber of
|
||||
each file is also printed. This won't work until project 4. */
|
||||
|
||||
#include <syscall.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool
|
||||
list_dir (const char *dir, bool verbose)
|
||||
{
|
||||
int dir_fd = open (dir);
|
||||
if (dir_fd == -1)
|
||||
{
|
||||
printf ("%s: not found\n", dir);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isdir (dir_fd))
|
||||
{
|
||||
char name[READDIR_MAX_LEN];
|
||||
|
||||
printf ("%s", dir);
|
||||
if (verbose)
|
||||
printf (" (inumber %d)", inumber (dir_fd));
|
||||
printf (":\n");
|
||||
|
||||
while (readdir (dir_fd, name))
|
||||
{
|
||||
printf ("%s", name);
|
||||
if (verbose)
|
||||
{
|
||||
char full_name[128];
|
||||
int entry_fd;
|
||||
|
||||
snprintf (full_name, sizeof full_name, "%s/%s", dir, name);
|
||||
entry_fd = open (full_name);
|
||||
|
||||
printf (": ");
|
||||
if (entry_fd != -1)
|
||||
{
|
||||
if (isdir (entry_fd))
|
||||
printf ("directory");
|
||||
else
|
||||
printf ("%d-byte file", filesize (entry_fd));
|
||||
printf (", inumber %d", inumber (entry_fd));
|
||||
}
|
||||
else
|
||||
printf ("open failed");
|
||||
close (entry_fd);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
printf ("%s: not a directory\n", dir);
|
||||
close (dir_fd);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
bool success = true;
|
||||
bool verbose = false;
|
||||
|
||||
if (argc > 1 && !strcmp (argv[1], "-l"))
|
||||
{
|
||||
verbose = true;
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
if (argc <= 1)
|
||||
success = list_dir (".", verbose);
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for (i = 1; i < argc; i++)
|
||||
if (!list_dir (argv[i], verbose))
|
||||
success = false;
|
||||
}
|
||||
return success ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
57
pintos-env/pintos/examples/matmult.c
Executable file
57
pintos-env/pintos/examples/matmult.c
Executable file
|
@ -0,0 +1,57 @@
|
|||
/* matmult.c
|
||||
|
||||
Test program to do matrix multiplication on large arrays.
|
||||
|
||||
Intended to stress virtual memory system.
|
||||
|
||||
Ideally, we could read the matrices off of the file system,
|
||||
and store the result back to the file system!
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
/* You should define DIM to be large enough that the arrays
|
||||
don't fit in physical memory.
|
||||
|
||||
Dim Memory
|
||||
------ --------
|
||||
16 3 kB
|
||||
64 48 kB
|
||||
128 192 kB
|
||||
256 768 kB
|
||||
512 3,072 kB
|
||||
1,024 12,288 kB
|
||||
2,048 49,152 kB
|
||||
4,096 196,608 kB
|
||||
8,192 786,432 kB
|
||||
16,384 3,145,728 kB */
|
||||
#define DIM 128
|
||||
|
||||
int A[DIM][DIM];
|
||||
int B[DIM][DIM];
|
||||
int C[DIM][DIM];
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
/* Initialize the matrices. */
|
||||
for (i = 0; i < DIM; i++)
|
||||
for (j = 0; j < DIM; j++)
|
||||
{
|
||||
A[i][j] = i;
|
||||
B[i][j] = j;
|
||||
C[i][j] = 0;
|
||||
}
|
||||
|
||||
/* Multiply matrices. */
|
||||
for (i = 0; i < DIM; i++)
|
||||
for (j = 0; j < DIM; j++)
|
||||
for (k = 0; k < DIM; k++)
|
||||
C[i][j] += A[i][k] * B[k][j];
|
||||
|
||||
/* Done. */
|
||||
exit (C[DIM - 1][DIM - 1]);
|
||||
}
|
45
pintos-env/pintos/examples/mcat.c
Executable file
45
pintos-env/pintos/examples/mcat.c
Executable file
|
@ -0,0 +1,45 @@
|
|||
/* mcat.c
|
||||
|
||||
Prints files specified on command line to the console, using
|
||||
mmap. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
int fd;
|
||||
mapid_t map;
|
||||
void *data = (void *) 0x10000000;
|
||||
int size;
|
||||
|
||||
/* Open input file. */
|
||||
fd = open (argv[i]);
|
||||
if (fd < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
size = filesize (fd);
|
||||
|
||||
/* Map files. */
|
||||
map = mmap (fd, data);
|
||||
if (map == MAP_FAILED)
|
||||
{
|
||||
printf ("%s: mmap failed\n", argv[i]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Write file to console. */
|
||||
write (STDOUT_FILENO, data, size);
|
||||
|
||||
/* Unmap files (optional). */
|
||||
munmap (map);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
68
pintos-env/pintos/examples/mcp.c
Executable file
68
pintos-env/pintos/examples/mcp.c
Executable file
|
@ -0,0 +1,68 @@
|
|||
/* mcp.c
|
||||
|
||||
Copies one file to another, using mmap. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
int in_fd, out_fd;
|
||||
mapid_t in_map, out_map;
|
||||
void *in_data = (void *) 0x10000000;
|
||||
void *out_data = (void *) 0x20000000;
|
||||
int size;
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
printf ("usage: cp OLD NEW\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Open input file. */
|
||||
in_fd = open (argv[1]);
|
||||
if (in_fd < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
size = filesize (in_fd);
|
||||
|
||||
/* Create and open output file. */
|
||||
if (!create (argv[2], size))
|
||||
{
|
||||
printf ("%s: create failed\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
out_fd = open (argv[2]);
|
||||
if (out_fd < 0)
|
||||
{
|
||||
printf ("%s: open failed\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Map files. */
|
||||
in_map = mmap (in_fd, in_data);
|
||||
if (in_map == MAP_FAILED)
|
||||
{
|
||||
printf ("%s: mmap failed\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
out_map = mmap (out_fd, out_data);
|
||||
if (out_map == MAP_FAILED)
|
||||
{
|
||||
printf ("%s: mmap failed\n", argv[2]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* Copy files. */
|
||||
memcpy (out_data, in_data, size);
|
||||
|
||||
/* Unmap files (optional). */
|
||||
munmap (in_map);
|
||||
munmap (out_map);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
24
pintos-env/pintos/examples/mkdir.c
Executable file
24
pintos-env/pintos/examples/mkdir.c
Executable file
|
@ -0,0 +1,24 @@
|
|||
/* mkdir.c
|
||||
|
||||
Creates a directory. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
printf ("usage: %s DIRECTORY\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!mkdir (argv[1]))
|
||||
{
|
||||
printf ("%s: mkdir failed\n", argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
152
pintos-env/pintos/examples/pwd.c
Executable file
152
pintos-env/pintos/examples/pwd.c
Executable file
|
@ -0,0 +1,152 @@
|
|||
/* pwd.c
|
||||
|
||||
Prints the absolute name of the present working directory. */
|
||||
|
||||
#include <syscall.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool getcwd (char *cwd, size_t cwd_size);
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
char cwd[128];
|
||||
if (getcwd (cwd, sizeof cwd))
|
||||
{
|
||||
printf ("%s\n", cwd);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf ("error\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stores the inode number for FILE_NAME in *INUM.
|
||||
Returns true if successful, false if the file could not be
|
||||
opened. */
|
||||
static bool
|
||||
get_inumber (const char *file_name, int *inum)
|
||||
{
|
||||
int fd = open (file_name);
|
||||
if (fd >= 0)
|
||||
{
|
||||
*inum = inumber (fd);
|
||||
close (fd);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Prepends PREFIX to the characters stored in the final *DST_LEN
|
||||
bytes of the DST_SIZE-byte buffer that starts at DST.
|
||||
Returns true if successful, false if adding that many
|
||||
characters, plus a null terminator, would overflow the buffer.
|
||||
(No null terminator is actually added or depended upon, but
|
||||
its space is accounted for.) */
|
||||
static bool
|
||||
prepend (const char *prefix,
|
||||
char *dst, size_t *dst_len, size_t dst_size)
|
||||
{
|
||||
size_t prefix_len = strlen (prefix);
|
||||
if (prefix_len + *dst_len + 1 <= dst_size)
|
||||
{
|
||||
*dst_len += prefix_len;
|
||||
memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Stores the current working directory, as a null-terminated
|
||||
string, in the CWD_SIZE bytes in CWD.
|
||||
Returns true if successful, false on error. Errors include
|
||||
system errors, directory trees deeper than MAX_LEVEL levels,
|
||||
and insufficient space in CWD. */
|
||||
static bool
|
||||
getcwd (char *cwd, size_t cwd_size)
|
||||
{
|
||||
size_t cwd_len = 0;
|
||||
|
||||
#define MAX_LEVEL 20
|
||||
char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1];
|
||||
char *namep;
|
||||
|
||||
int child_inum;
|
||||
|
||||
/* Make sure there's enough space for at least "/". */
|
||||
if (cwd_size < 2)
|
||||
return false;
|
||||
|
||||
/* Get inumber for current directory. */
|
||||
if (!get_inumber (".", &child_inum))
|
||||
return false;
|
||||
|
||||
namep = name;
|
||||
for (;;)
|
||||
{
|
||||
int parent_inum, parent_fd;
|
||||
|
||||
/* Compose "../../../..", etc., in NAME. */
|
||||
if ((namep - name) > MAX_LEVEL * 3)
|
||||
return false;
|
||||
*namep++ = '.';
|
||||
*namep++ = '.';
|
||||
*namep = '\0';
|
||||
|
||||
/* Open directory. */
|
||||
parent_fd = open (name);
|
||||
if (parent_fd < 0)
|
||||
return false;
|
||||
*namep++ = '/';
|
||||
|
||||
/* If parent and child have the same inumber,
|
||||
then we've arrived at the root. */
|
||||
parent_inum = inumber (parent_fd);
|
||||
if (parent_inum == child_inum)
|
||||
break;
|
||||
|
||||
/* Find name of file in parent directory with the child's
|
||||
inumber. */
|
||||
for (;;)
|
||||
{
|
||||
int test_inum;
|
||||
if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum))
|
||||
{
|
||||
close (parent_fd);
|
||||
return false;
|
||||
}
|
||||
if (test_inum == child_inum)
|
||||
break;
|
||||
}
|
||||
close (parent_fd);
|
||||
|
||||
/* Prepend "/name" to CWD. */
|
||||
if (!prepend (namep - 1, cwd, &cwd_len, cwd_size))
|
||||
return false;
|
||||
|
||||
/* Move up. */
|
||||
child_inum = parent_inum;
|
||||
}
|
||||
|
||||
/* Finalize CWD. */
|
||||
if (cwd_len > 0)
|
||||
{
|
||||
/* Move the string to the beginning of CWD,
|
||||
and null-terminate it. */
|
||||
memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len);
|
||||
cwd[cwd_len] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Special case for the root. */
|
||||
strlcpy (cwd, "/", cwd_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
34
pintos-env/pintos/examples/recursor.c
Executable file
34
pintos-env/pintos/examples/recursor.c
Executable file
|
@ -0,0 +1,34 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
char buffer[128];
|
||||
pid_t pid;
|
||||
int retval = 0;
|
||||
|
||||
if (argc != 4)
|
||||
{
|
||||
printf ("usage: recursor <string> <depth> <waitp>\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Print args. */
|
||||
printf ("%s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]);
|
||||
|
||||
/* Execute child and wait for it to finish if requested. */
|
||||
if (atoi (argv[2]) != 0)
|
||||
{
|
||||
snprintf (buffer, sizeof buffer,
|
||||
"recursor %s %d %s", argv[1], atoi (argv[2]) - 1, argv[3]);
|
||||
pid = exec (buffer);
|
||||
if (atoi (argv[3]))
|
||||
retval = wait (pid);
|
||||
}
|
||||
|
||||
/* Done. */
|
||||
printf ("%s %s: dying, retval=%d\n", argv[1], argv[2], retval);
|
||||
exit (retval);
|
||||
}
|
21
pintos-env/pintos/examples/rm.c
Executable file
21
pintos-env/pintos/examples/rm.c
Executable file
|
@ -0,0 +1,21 @@
|
|||
/* rm.c
|
||||
|
||||
Removes files specified on command line. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syscall.h>
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
bool success = true;
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
if (!remove (argv[i]))
|
||||
{
|
||||
printf ("%s: remove failed\n", argv[i]);
|
||||
success = false;
|
||||
}
|
||||
return success ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
104
pintos-env/pintos/examples/shell.c
Executable file
104
pintos-env/pintos/examples/shell.c
Executable file
|
@ -0,0 +1,104 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syscall.h>
|
||||
|
||||
static void read_line (char line[], size_t);
|
||||
static bool backspace (char **pos, char line[]);
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
printf ("Shell starting...\n");
|
||||
for (;;)
|
||||
{
|
||||
char command[80];
|
||||
|
||||
/* Read command. */
|
||||
printf ("--");
|
||||
read_line (command, sizeof command);
|
||||
|
||||
/* Execute command. */
|
||||
if (!strcmp (command, "exit"))
|
||||
break;
|
||||
else if (!memcmp (command, "cd ", 3))
|
||||
{
|
||||
if (!chdir (command + 3))
|
||||
printf ("\"%s\": chdir failed\n", command + 3);
|
||||
}
|
||||
else if (command[0] == '\0')
|
||||
{
|
||||
/* Empty command. */
|
||||
}
|
||||
else
|
||||
{
|
||||
pid_t pid = exec (command);
|
||||
if (pid != PID_ERROR)
|
||||
printf ("\"%s\": exit code %d\n", command, wait (pid));
|
||||
else
|
||||
printf ("exec failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
printf ("Shell exiting.");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Reads a line of input from the user into LINE, which has room
|
||||
for SIZE bytes. Handles backspace and Ctrl+U in the ways
|
||||
expected by Unix users. On return, LINE will always be
|
||||
null-terminated and will not end in a new-line character. */
|
||||
static void
|
||||
read_line (char line[], size_t size)
|
||||
{
|
||||
char *pos = line;
|
||||
for (;;)
|
||||
{
|
||||
char c;
|
||||
read (STDIN_FILENO, &c, 1);
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\r':
|
||||
*pos = '\0';
|
||||
putchar ('\n');
|
||||
return;
|
||||
|
||||
case '\b':
|
||||
backspace (&pos, line);
|
||||
break;
|
||||
|
||||
case ('U' - 'A') + 1: /* Ctrl+U. */
|
||||
while (backspace (&pos, line))
|
||||
continue;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Add character to line. */
|
||||
if (pos < line + size - 1)
|
||||
{
|
||||
putchar (c);
|
||||
*pos++ = c;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If *POS is past the beginning of LINE, backs up one character
|
||||
position. Returns true if successful, false if nothing was
|
||||
done. */
|
||||
static bool
|
||||
backspace (char **pos, char line[])
|
||||
{
|
||||
if (*pos > line)
|
||||
{
|
||||
/* Back up cursor, overwrite character, back up
|
||||
again. */
|
||||
printf ("\b \b");
|
||||
(*pos)--;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
13
pintos-env/pintos/filesys/Make.vars
Executable file
13
pintos-env/pintos/filesys/Make.vars
Executable file
|
@ -0,0 +1,13 @@
|
|||
# -*- makefile -*-
|
||||
|
||||
kernel.bin: DEFINES = -DUSERPROG -DFILESYS
|
||||
KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
|
||||
TEST_SUBDIRS = tests/userprog tests/filesys/base tests/filesys/extended
|
||||
GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.no-vm
|
||||
SIMULATOR = --qemu
|
||||
|
||||
# Uncomment the lines below to enable VM.
|
||||
#kernel.bin: DEFINES += -DVM
|
||||
#KERNEL_SUBDIRS += vm
|
||||
#TEST_SUBDIRS += tests/vm
|
||||
#GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.with-vm
|
1
pintos-env/pintos/filesys/Makefile
Executable file
1
pintos-env/pintos/filesys/Makefile
Executable file
|
@ -0,0 +1 @@
|
|||
include ../Makefile.kernel
|
236
pintos-env/pintos/filesys/directory.c
Executable file
236
pintos-env/pintos/filesys/directory.c
Executable file
|
@ -0,0 +1,236 @@
|
|||
#include "filesys/directory.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <list.h>
|
||||
#include "filesys/filesys.h"
|
||||
#include "filesys/inode.h"
|
||||
#include "threads/malloc.h"
|
||||
|
||||
/* A directory. */
|
||||
struct dir
|
||||
{
|
||||
struct inode *inode; /* Backing store. */
|
||||
off_t pos; /* Current position. */
|
||||
};
|
||||
|
||||
/* A single directory entry. */
|
||||
struct dir_entry
|
||||
{
|
||||
block_sector_t inode_sector; /* Sector number of header. */
|
||||
char name[NAME_MAX + 1]; /* Null terminated file name. */
|
||||
bool in_use; /* In use or free? */
|
||||
};
|
||||
|
||||
/* Creates a directory with space for ENTRY_CNT entries in the
|
||||
given SECTOR. Returns true if successful, false on failure. */
|
||||
bool
|
||||
dir_create (block_sector_t sector, size_t entry_cnt)
|
||||
{
|
||||
return inode_create (sector, entry_cnt * sizeof (struct dir_entry));
|
||||
}
|
||||
|
||||
/* Opens and returns the directory for the given INODE, of which
|
||||
it takes ownership. Returns a null pointer on failure. */
|
||||
struct dir *
|
||||
dir_open (struct inode *inode)
|
||||
{
|
||||
struct dir *dir = calloc (1, sizeof *dir);
|
||||
if (inode != NULL && dir != NULL)
|
||||
{
|
||||
dir->inode = inode;
|
||||
dir->pos = 0;
|
||||
return dir;
|
||||
}
|
||||
else
|
||||
{
|
||||
inode_close (inode);
|
||||
free (dir);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Opens the root directory and returns a directory for it.
|
||||
Return true if successful, false on failure. */
|
||||
struct dir *
|
||||
dir_open_root (void)
|
||||
{
|
||||
return dir_open (inode_open (ROOT_DIR_SECTOR));
|
||||
}
|
||||
|
||||
/* Opens and returns a new directory for the same inode as DIR.
|
||||
Returns a null pointer on failure. */
|
||||
struct dir *
|
||||
dir_reopen (struct dir *dir)
|
||||
{
|
||||
return dir_open (inode_reopen (dir->inode));
|
||||
}
|
||||
|
||||
/* Destroys DIR and frees associated resources. */
|
||||
void
|
||||
dir_close (struct dir *dir)
|
||||
{
|
||||
if (dir != NULL)
|
||||
{
|
||||
inode_close (dir->inode);
|
||||
free (dir);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the inode encapsulated by DIR. */
|
||||
struct inode *
|
||||
dir_get_inode (struct dir *dir)
|
||||
{
|
||||
return dir->inode;
|
||||
}
|
||||
|
||||
/* Searches DIR for a file with the given NAME.
|
||||
If successful, returns true, sets *EP to the directory entry
|
||||
if EP is non-null, and sets *OFSP to the byte offset of the
|
||||
directory entry if OFSP is non-null.
|
||||
otherwise, returns false and ignores EP and OFSP. */
|
||||
static bool
|
||||
lookup (const struct dir *dir, const char *name,
|
||||
struct dir_entry *ep, off_t *ofsp)
|
||||
{
|
||||
struct dir_entry e;
|
||||
size_t ofs;
|
||||
|
||||
ASSERT (dir != NULL);
|
||||
ASSERT (name != NULL);
|
||||
|
||||
for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
|
||||
ofs += sizeof e)
|
||||
if (e.in_use && !strcmp (name, e.name))
|
||||
{
|
||||
if (ep != NULL)
|
||||
*ep = e;
|
||||
if (ofsp != NULL)
|
||||
*ofsp = ofs;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Searches DIR for a file with the given NAME
|
||||
and returns true if one exists, false otherwise.
|
||||
On success, sets *INODE to an inode for the file, otherwise to
|
||||
a null pointer. The caller must close *INODE. */
|
||||
bool
|
||||
dir_lookup (const struct dir *dir, const char *name,
|
||||
struct inode **inode)
|
||||
{
|
||||
struct dir_entry e;
|
||||
|
||||
ASSERT (dir != NULL);
|
||||
ASSERT (name != NULL);
|
||||
|
||||
if (lookup (dir, name, &e, NULL))
|
||||
*inode = inode_open (e.inode_sector);
|
||||
else
|
||||
*inode = NULL;
|
||||
|
||||
return *inode != NULL;
|
||||
}
|
||||
|
||||
/* Adds a file named NAME to DIR, which must not already contain a
|
||||
file by that name. The file's inode is in sector
|
||||
INODE_SECTOR.
|
||||
Returns true if successful, false on failure.
|
||||
Fails if NAME is invalid (i.e. too long) or a disk or memory
|
||||
error occurs. */
|
||||
bool
|
||||
dir_add (struct dir *dir, const char *name, block_sector_t inode_sector)
|
||||
{
|
||||
struct dir_entry e;
|
||||
off_t ofs;
|
||||
bool success = false;
|
||||
|
||||
ASSERT (dir != NULL);
|
||||
ASSERT (name != NULL);
|
||||
|
||||
/* Check NAME for validity. */
|
||||
if (*name == '\0' || strlen (name) > NAME_MAX)
|
||||
return false;
|
||||
|
||||
/* Check that NAME is not in use. */
|
||||
if (lookup (dir, name, NULL, NULL))
|
||||
goto done;
|
||||
|
||||
/* Set OFS to offset of free slot.
|
||||
If there are no free slots, then it will be set to the
|
||||
current end-of-file.
|
||||
|
||||
inode_read_at() will only return a short read at end of file.
|
||||
Otherwise, we'd need to verify that we didn't get a short
|
||||
read due to something intermittent such as low memory. */
|
||||
for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
|
||||
ofs += sizeof e)
|
||||
if (!e.in_use)
|
||||
break;
|
||||
|
||||
/* Write slot. */
|
||||
e.in_use = true;
|
||||
strlcpy (e.name, name, sizeof e.name);
|
||||
e.inode_sector = inode_sector;
|
||||
success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
|
||||
|
||||
done:
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Removes any entry for NAME in DIR.
|
||||
Returns true if successful, false on failure,
|
||||
which occurs only if there is no file with the given NAME. */
|
||||
bool
|
||||
dir_remove (struct dir *dir, const char *name)
|
||||
{
|
||||
struct dir_entry e;
|
||||
struct inode *inode = NULL;
|
||||
bool success = false;
|
||||
off_t ofs;
|
||||
|
||||
ASSERT (dir != NULL);
|
||||
ASSERT (name != NULL);
|
||||
|
||||
/* Find directory entry. */
|
||||
if (!lookup (dir, name, &e, &ofs))
|
||||
goto done;
|
||||
|
||||
/* Open inode. */
|
||||
inode = inode_open (e.inode_sector);
|
||||
if (inode == NULL)
|
||||
goto done;
|
||||
|
||||
/* Erase directory entry. */
|
||||
e.in_use = false;
|
||||
if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e)
|
||||
goto done;
|
||||
|
||||
/* Remove inode. */
|
||||
inode_remove (inode);
|
||||
success = true;
|
||||
|
||||
done:
|
||||
inode_close (inode);
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Reads the next directory entry in DIR and stores the name in
|
||||
NAME. Returns true if successful, false if the directory
|
||||
contains no more entries. */
|
||||
bool
|
||||
dir_readdir (struct dir *dir, char name[NAME_MAX + 1])
|
||||
{
|
||||
struct dir_entry e;
|
||||
|
||||
while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e)
|
||||
{
|
||||
dir->pos += sizeof e;
|
||||
if (e.in_use)
|
||||
{
|
||||
strlcpy (name, e.name, NAME_MAX + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
30
pintos-env/pintos/filesys/directory.h
Executable file
30
pintos-env/pintos/filesys/directory.h
Executable file
|
@ -0,0 +1,30 @@
|
|||
#ifndef FILESYS_DIRECTORY_H
|
||||
#define FILESYS_DIRECTORY_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "devices/block.h"
|
||||
|
||||
/* Maximum length of a file name component.
|
||||
This is the traditional UNIX maximum length.
|
||||
After directories are implemented, this maximum length may be
|
||||
retained, but much longer full path names must be allowed. */
|
||||
#define NAME_MAX 14
|
||||
|
||||
struct inode;
|
||||
|
||||
/* Opening and closing directories. */
|
||||
bool dir_create (block_sector_t sector, size_t entry_cnt);
|
||||
struct dir *dir_open (struct inode *);
|
||||
struct dir *dir_open_root (void);
|
||||
struct dir *dir_reopen (struct dir *);
|
||||
void dir_close (struct dir *);
|
||||
struct inode *dir_get_inode (struct dir *);
|
||||
|
||||
/* Reading and writing. */
|
||||
bool dir_lookup (const struct dir *, const char *name, struct inode **);
|
||||
bool dir_add (struct dir *, const char *name, block_sector_t);
|
||||
bool dir_remove (struct dir *, const char *name);
|
||||
bool dir_readdir (struct dir *, char name[NAME_MAX + 1]);
|
||||
|
||||
#endif /* filesys/directory.h */
|
168
pintos-env/pintos/filesys/file.c
Executable file
168
pintos-env/pintos/filesys/file.c
Executable file
|
@ -0,0 +1,168 @@
|
|||
#include "filesys/file.h"
|
||||
#include <debug.h>
|
||||
#include "filesys/inode.h"
|
||||
#include "threads/malloc.h"
|
||||
|
||||
/* An open file. */
|
||||
struct file
|
||||
{
|
||||
struct inode *inode; /* File's inode. */
|
||||
off_t pos; /* Current position. */
|
||||
bool deny_write; /* Has file_deny_write() been called? */
|
||||
};
|
||||
|
||||
/* Opens a file for the given INODE, of which it takes ownership,
|
||||
and returns the new file. Returns a null pointer if an
|
||||
allocation fails or if INODE is null. */
|
||||
struct file *
|
||||
file_open (struct inode *inode)
|
||||
{
|
||||
struct file *file = calloc (1, sizeof *file);
|
||||
if (inode != NULL && file != NULL)
|
||||
{
|
||||
file->inode = inode;
|
||||
file->pos = 0;
|
||||
file->deny_write = false;
|
||||
return file;
|
||||
}
|
||||
else
|
||||
{
|
||||
inode_close (inode);
|
||||
free (file);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Opens and returns a new file for the same inode as FILE.
|
||||
Returns a null pointer if unsuccessful. */
|
||||
struct file *
|
||||
file_reopen (struct file *file)
|
||||
{
|
||||
return file_open (inode_reopen (file->inode));
|
||||
}
|
||||
|
||||
/* Closes FILE. */
|
||||
void
|
||||
file_close (struct file *file)
|
||||
{
|
||||
if (file != NULL)
|
||||
{
|
||||
file_allow_write (file);
|
||||
inode_close (file->inode);
|
||||
free (file);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the inode encapsulated by FILE. */
|
||||
struct inode *
|
||||
file_get_inode (struct file *file)
|
||||
{
|
||||
return file->inode;
|
||||
}
|
||||
|
||||
/* Reads SIZE bytes from FILE into BUFFER,
|
||||
starting at the file's current position.
|
||||
Returns the number of bytes actually read,
|
||||
which may be less than SIZE if end of file is reached.
|
||||
Advances FILE's position by the number of bytes read. */
|
||||
off_t
|
||||
file_read (struct file *file, void *buffer, off_t size)
|
||||
{
|
||||
off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos);
|
||||
file->pos += bytes_read;
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/* Reads SIZE bytes from FILE into BUFFER,
|
||||
starting at offset FILE_OFS in the file.
|
||||
Returns the number of bytes actually read,
|
||||
which may be less than SIZE if end of file is reached.
|
||||
The file's current position is unaffected. */
|
||||
off_t
|
||||
file_read_at (struct file *file, void *buffer, off_t size, off_t file_ofs)
|
||||
{
|
||||
return inode_read_at (file->inode, buffer, size, file_ofs);
|
||||
}
|
||||
|
||||
/* Writes SIZE bytes from BUFFER into FILE,
|
||||
starting at the file's current position.
|
||||
Returns the number of bytes actually written,
|
||||
which may be less than SIZE if end of file is reached.
|
||||
(Normally we'd grow the file in that case, but file growth is
|
||||
not yet implemented.)
|
||||
Advances FILE's position by the number of bytes read. */
|
||||
off_t
|
||||
file_write (struct file *file, const void *buffer, off_t size)
|
||||
{
|
||||
off_t bytes_written = inode_write_at (file->inode, buffer, size, file->pos);
|
||||
file->pos += bytes_written;
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
/* Writes SIZE bytes from BUFFER into FILE,
|
||||
starting at offset FILE_OFS in the file.
|
||||
Returns the number of bytes actually written,
|
||||
which may be less than SIZE if end of file is reached.
|
||||
(Normally we'd grow the file in that case, but file growth is
|
||||
not yet implemented.)
|
||||
The file's current position is unaffected. */
|
||||
off_t
|
||||
file_write_at (struct file *file, const void *buffer, off_t size,
|
||||
off_t file_ofs)
|
||||
{
|
||||
return inode_write_at (file->inode, buffer, size, file_ofs);
|
||||
}
|
||||
|
||||
/* Prevents write operations on FILE's underlying inode
|
||||
until file_allow_write() is called or FILE is closed. */
|
||||
void
|
||||
file_deny_write (struct file *file)
|
||||
{
|
||||
ASSERT (file != NULL);
|
||||
if (!file->deny_write)
|
||||
{
|
||||
file->deny_write = true;
|
||||
inode_deny_write (file->inode);
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-enables write operations on FILE's underlying inode.
|
||||
(Writes might still be denied by some other file that has the
|
||||
same inode open.) */
|
||||
void
|
||||
file_allow_write (struct file *file)
|
||||
{
|
||||
ASSERT (file != NULL);
|
||||
if (file->deny_write)
|
||||
{
|
||||
file->deny_write = false;
|
||||
inode_allow_write (file->inode);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns the size of FILE in bytes. */
|
||||
off_t
|
||||
file_length (struct file *file)
|
||||
{
|
||||
ASSERT (file != NULL);
|
||||
return inode_length (file->inode);
|
||||
}
|
||||
|
||||
/* Sets the current position in FILE to NEW_POS bytes from the
|
||||
start of the file. */
|
||||
void
|
||||
file_seek (struct file *file, off_t new_pos)
|
||||
{
|
||||
ASSERT (file != NULL);
|
||||
ASSERT (new_pos >= 0);
|
||||
file->pos = new_pos;
|
||||
}
|
||||
|
||||
/* Returns the current position in FILE as a byte offset from the
|
||||
start of the file. */
|
||||
off_t
|
||||
file_tell (struct file *file)
|
||||
{
|
||||
ASSERT (file != NULL);
|
||||
return file->pos;
|
||||
}
|
29
pintos-env/pintos/filesys/file.h
Executable file
29
pintos-env/pintos/filesys/file.h
Executable file
|
@ -0,0 +1,29 @@
|
|||
#ifndef FILESYS_FILE_H
|
||||
#define FILESYS_FILE_H
|
||||
|
||||
#include "filesys/off_t.h"
|
||||
|
||||
struct inode;
|
||||
|
||||
/* Opening and closing files. */
|
||||
struct file *file_open (struct inode *);
|
||||
struct file *file_reopen (struct file *);
|
||||
void file_close (struct file *);
|
||||
struct inode *file_get_inode (struct file *);
|
||||
|
||||
/* Reading and writing. */
|
||||
off_t file_read (struct file *, void *, off_t);
|
||||
off_t file_read_at (struct file *, void *, off_t size, off_t start);
|
||||
off_t file_write (struct file *, const void *, off_t);
|
||||
off_t file_write_at (struct file *, const void *, off_t size, off_t start);
|
||||
|
||||
/* Preventing writes. */
|
||||
void file_deny_write (struct file *);
|
||||
void file_allow_write (struct file *);
|
||||
|
||||
/* File position. */
|
||||
void file_seek (struct file *, off_t);
|
||||
off_t file_tell (struct file *);
|
||||
off_t file_length (struct file *);
|
||||
|
||||
#endif /* filesys/file.h */
|
103
pintos-env/pintos/filesys/filesys.c
Executable file
103
pintos-env/pintos/filesys/filesys.c
Executable file
|
@ -0,0 +1,103 @@
|
|||
#include "filesys/filesys.h"
|
||||
#include <debug.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "filesys/file.h"
|
||||
#include "filesys/free-map.h"
|
||||
#include "filesys/inode.h"
|
||||
#include "filesys/directory.h"
|
||||
|
||||
/* Partition that contains the file system. */
|
||||
struct block *fs_device;
|
||||
|
||||
static void do_format (void);
|
||||
|
||||
/* Initializes the file system module.
|
||||
If FORMAT is true, reformats the file system. */
|
||||
void
|
||||
filesys_init (bool format)
|
||||
{
|
||||
fs_device = block_get_role (BLOCK_FILESYS);
|
||||
if (fs_device == NULL)
|
||||
PANIC ("No file system device found, can't initialize file system.");
|
||||
|
||||
inode_init ();
|
||||
free_map_init ();
|
||||
|
||||
if (format)
|
||||
do_format ();
|
||||
|
||||
free_map_open ();
|
||||
}
|
||||
|
||||
/* Shuts down the file system module, writing any unwritten data
|
||||
to disk. */
|
||||
void
|
||||
filesys_done (void)
|
||||
{
|
||||
free_map_close ();
|
||||
}
|
||||
|
||||
/* Creates a file named NAME with the given INITIAL_SIZE.
|
||||
Returns true if successful, false otherwise.
|
||||
Fails if a file named NAME already exists,
|
||||
or if internal memory allocation fails. */
|
||||
bool
|
||||
filesys_create (const char *name, off_t initial_size)
|
||||
{
|
||||
block_sector_t inode_sector = 0;
|
||||
struct dir *dir = dir_open_root ();
|
||||
bool success = (dir != NULL
|
||||
&& free_map_allocate (1, &inode_sector)
|
||||
&& inode_create (inode_sector, initial_size)
|
||||
&& dir_add (dir, name, inode_sector));
|
||||
if (!success && inode_sector != 0)
|
||||
free_map_release (inode_sector, 1);
|
||||
dir_close (dir);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Opens the file with the given NAME.
|
||||
Returns the new file if successful or a null pointer
|
||||
otherwise.
|
||||
Fails if no file named NAME exists,
|
||||
or if an internal memory allocation fails. */
|
||||
struct file *
|
||||
filesys_open (const char *name)
|
||||
{
|
||||
struct dir *dir = dir_open_root ();
|
||||
struct inode *inode = NULL;
|
||||
|
||||
if (dir != NULL)
|
||||
dir_lookup (dir, name, &inode);
|
||||
dir_close (dir);
|
||||
|
||||
return file_open (inode);
|
||||
}
|
||||
|
||||
/* Deletes the file named NAME.
|
||||
Returns true if successful, false on failure.
|
||||
Fails if no file named NAME exists,
|
||||
or if an internal memory allocation fails. */
|
||||
bool
|
||||
filesys_remove (const char *name)
|
||||
{
|
||||
struct dir *dir = dir_open_root ();
|
||||
bool success = dir != NULL && dir_remove (dir, name);
|
||||
dir_close (dir);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Formats the file system. */
|
||||
static void
|
||||
do_format (void)
|
||||
{
|
||||
printf ("Formatting file system...");
|
||||
free_map_create ();
|
||||
if (!dir_create (ROOT_DIR_SECTOR, 16))
|
||||
PANIC ("root directory creation failed");
|
||||
free_map_close ();
|
||||
printf ("done.\n");
|
||||
}
|
20
pintos-env/pintos/filesys/filesys.h
Executable file
20
pintos-env/pintos/filesys/filesys.h
Executable file
|
@ -0,0 +1,20 @@
|
|||
#ifndef FILESYS_FILESYS_H
|
||||
#define FILESYS_FILESYS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "filesys/off_t.h"
|
||||
|
||||
/* Sectors of system file inodes. */
|
||||
#define FREE_MAP_SECTOR 0 /* Free map file inode sector. */
|
||||
#define ROOT_DIR_SECTOR 1 /* Root directory file inode sector. */
|
||||
|
||||
/* Block device that contains the file system. */
|
||||
struct block *fs_device;
|
||||
|
||||
void filesys_init (bool format);
|
||||
void filesys_done (void);
|
||||
bool filesys_create (const char *name, off_t initial_size);
|
||||
struct file *filesys_open (const char *name);
|
||||
bool filesys_remove (const char *name);
|
||||
|
||||
#endif /* filesys/filesys.h */
|
85
pintos-env/pintos/filesys/free-map.c
Executable file
85
pintos-env/pintos/filesys/free-map.c
Executable file
|
@ -0,0 +1,85 @@
|
|||
#include "filesys/free-map.h"
|
||||
#include <bitmap.h>
|
||||
#include <debug.h>
|
||||
#include "filesys/file.h"
|
||||
#include "filesys/filesys.h"
|
||||
#include "filesys/inode.h"
|
||||
|
||||
static struct file *free_map_file; /* Free map file. */
|
||||
static struct bitmap *free_map; /* Free map, one bit per sector. */
|
||||
|
||||
/* Initializes the free map. */
|
||||
void
|
||||
free_map_init (void)
|
||||
{
|
||||
free_map = bitmap_create (block_size (fs_device));
|
||||
if (free_map == NULL)
|
||||
PANIC ("bitmap creation failed--file system device is too large");
|
||||
bitmap_mark (free_map, FREE_MAP_SECTOR);
|
||||
bitmap_mark (free_map, ROOT_DIR_SECTOR);
|
||||
}
|
||||
|
||||
/* Allocates CNT consecutive sectors from the free map and stores
|
||||
the first into *SECTORP.
|
||||
Returns true if successful, false if not enough consecutive
|
||||
sectors were available or if the free_map file could not be
|
||||
written. */
|
||||
bool
|
||||
free_map_allocate (size_t cnt, block_sector_t *sectorp)
|
||||
{
|
||||
block_sector_t sector = bitmap_scan_and_flip (free_map, 0, cnt, false);
|
||||
if (sector != BITMAP_ERROR
|
||||
&& free_map_file != NULL
|
||||
&& !bitmap_write (free_map, free_map_file))
|
||||
{
|
||||
bitmap_set_multiple (free_map, sector, cnt, false);
|
||||
sector = BITMAP_ERROR;
|
||||
}
|
||||
if (sector != BITMAP_ERROR)
|
||||
*sectorp = sector;
|
||||
return sector != BITMAP_ERROR;
|
||||
}
|
||||
|
||||
/* Makes CNT sectors starting at SECTOR available for use. */
|
||||
void
|
||||
free_map_release (block_sector_t sector, size_t cnt)
|
||||
{
|
||||
ASSERT (bitmap_all (free_map, sector, cnt));
|
||||
bitmap_set_multiple (free_map, sector, cnt, false);
|
||||
bitmap_write (free_map, free_map_file);
|
||||
}
|
||||
|
||||
/* Opens the free map file and reads it from disk. */
|
||||
void
|
||||
free_map_open (void)
|
||||
{
|
||||
free_map_file = file_open (inode_open (FREE_MAP_SECTOR));
|
||||
if (free_map_file == NULL)
|
||||
PANIC ("can't open free map");
|
||||
if (!bitmap_read (free_map, free_map_file))
|
||||
PANIC ("can't read free map");
|
||||
}
|
||||
|
||||
/* Writes the free map to disk and closes the free map file. */
|
||||
void
|
||||
free_map_close (void)
|
||||
{
|
||||
file_close (free_map_file);
|
||||
}
|
||||
|
||||
/* Creates a new free map file on disk and writes the free map to
|
||||
it. */
|
||||
void
|
||||
free_map_create (void)
|
||||
{
|
||||
/* Create inode. */
|
||||
if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map)))
|
||||
PANIC ("free map creation failed");
|
||||
|
||||
/* Write bitmap to file. */
|
||||
free_map_file = file_open (inode_open (FREE_MAP_SECTOR));
|
||||
if (free_map_file == NULL)
|
||||
PANIC ("can't open free map");
|
||||
if (!bitmap_write (free_map, free_map_file))
|
||||
PANIC ("can't write free map");
|
||||
}
|
17
pintos-env/pintos/filesys/free-map.h
Executable file
17
pintos-env/pintos/filesys/free-map.h
Executable file
|
@ -0,0 +1,17 @@
|
|||
#ifndef FILESYS_FREE_MAP_H
|
||||
#define FILESYS_FREE_MAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "devices/block.h"
|
||||
|
||||
void free_map_init (void);
|
||||
void free_map_read (void);
|
||||
void free_map_create (void);
|
||||
void free_map_open (void);
|
||||
void free_map_close (void);
|
||||
|
||||
bool free_map_allocate (size_t, block_sector_t *);
|
||||
void free_map_release (block_sector_t, size_t);
|
||||
|
||||
#endif /* filesys/free-map.h */
|
222
pintos-env/pintos/filesys/fsutil.c
Executable file
222
pintos-env/pintos/filesys/fsutil.c
Executable file
|
@ -0,0 +1,222 @@
|
|||
#include "filesys/fsutil.h"
|
||||
#include <debug.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ustar.h>
|
||||
#include "filesys/directory.h"
|
||||
#include "filesys/file.h"
|
||||
#include "filesys/filesys.h"
|
||||
#include "threads/malloc.h"
|
||||
#include "threads/palloc.h"
|
||||
#include "threads/vaddr.h"
|
||||
|
||||
/* List files in the root directory. */
|
||||
void
|
||||
fsutil_ls (char **argv UNUSED)
|
||||
{
|
||||
struct dir *dir;
|
||||
char name[NAME_MAX + 1];
|
||||
|
||||
printf ("Files in the root directory:\n");
|
||||
dir = dir_open_root ();
|
||||
if (dir == NULL)
|
||||
PANIC ("root dir open failed");
|
||||
while (dir_readdir (dir, name))
|
||||
printf ("%s\n", name);
|
||||
printf ("End of listing.\n");
|
||||
}
|
||||
|
||||
/* Prints the contents of file ARGV[1] to the system console as
|
||||
hex and ASCII. */
|
||||
void
|
||||
fsutil_cat (char **argv)
|
||||
{
|
||||
const char *file_name = argv[1];
|
||||
|
||||
struct file *file;
|
||||
char *buffer;
|
||||
|
||||
printf ("Printing '%s' to the console...\n", file_name);
|
||||
file = filesys_open (file_name);
|
||||
if (file == NULL)
|
||||
PANIC ("%s: open failed", file_name);
|
||||
buffer = palloc_get_page (PAL_ASSERT);
|
||||
for (;;)
|
||||
{
|
||||
off_t pos = file_tell (file);
|
||||
off_t n = file_read (file, buffer, PGSIZE);
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
hex_dump (pos, buffer, n, true);
|
||||
}
|
||||
palloc_free_page (buffer);
|
||||
file_close (file);
|
||||
}
|
||||
|
||||
/* Deletes file ARGV[1]. */
|
||||
void
|
||||
fsutil_rm (char **argv)
|
||||
{
|
||||
const char *file_name = argv[1];
|
||||
|
||||
printf ("Deleting '%s'...\n", file_name);
|
||||
if (!filesys_remove (file_name))
|
||||
PANIC ("%s: delete failed\n", file_name);
|
||||
}
|
||||
|
||||
/* Extracts a ustar-format tar archive from the scratch block
|
||||
device into the Pintos file system. */
|
||||
void
|
||||
fsutil_extract (char **argv UNUSED)
|
||||
{
|
||||
static block_sector_t sector = 0;
|
||||
|
||||
struct block *src;
|
||||
void *header, *data;
|
||||
|
||||
/* Allocate buffers. */
|
||||
header = malloc (BLOCK_SECTOR_SIZE);
|
||||
data = malloc (BLOCK_SECTOR_SIZE);
|
||||
if (header == NULL || data == NULL)
|
||||
PANIC ("couldn't allocate buffers");
|
||||
|
||||
/* Open source block device. */
|
||||
src = block_get_role (BLOCK_SCRATCH);
|
||||
if (src == NULL)
|
||||
PANIC ("couldn't open scratch device");
|
||||
|
||||
printf ("Extracting ustar archive from scratch device "
|
||||
"into file system...\n");
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const char *file_name;
|
||||
const char *error;
|
||||
enum ustar_type type;
|
||||
int size;
|
||||
|
||||
/* Read and parse ustar header. */
|
||||
block_read (src, sector++, header);
|
||||
error = ustar_parse_header (header, &file_name, &type, &size);
|
||||
if (error != NULL)
|
||||
PANIC ("bad ustar header in sector %"PRDSNu" (%s)", sector - 1, error);
|
||||
|
||||
if (type == USTAR_EOF)
|
||||
{
|
||||
/* End of archive. */
|
||||
break;
|
||||
}
|
||||
else if (type == USTAR_DIRECTORY)
|
||||
printf ("ignoring directory %s\n", file_name);
|
||||
else if (type == USTAR_REGULAR)
|
||||
{
|
||||
struct file *dst;
|
||||
|
||||
printf ("Putting '%s' into the file system...\n", file_name);
|
||||
|
||||
/* Create destination file. */
|
||||
if (!filesys_create (file_name, size))
|
||||
PANIC ("%s: create failed", file_name);
|
||||
dst = filesys_open (file_name);
|
||||
if (dst == NULL)
|
||||
PANIC ("%s: open failed", file_name);
|
||||
|
||||
/* Do copy. */
|
||||
while (size > 0)
|
||||
{
|
||||
int chunk_size = (size > BLOCK_SECTOR_SIZE
|
||||
? BLOCK_SECTOR_SIZE
|
||||
: size);
|
||||
block_read (src, sector++, data);
|
||||
if (file_write (dst, data, chunk_size) != chunk_size)
|
||||
PANIC ("%s: write failed with %d bytes unwritten",
|
||||
file_name, size);
|
||||
size -= chunk_size;
|
||||
}
|
||||
|
||||
/* Finish up. */
|
||||
file_close (dst);
|
||||
}
|
||||
}
|
||||
|
||||
/* Erase the ustar header from the start of the block device,
|
||||
so that the extraction operation is idempotent. We erase
|
||||
two blocks because two blocks of zeros are the ustar
|
||||
end-of-archive marker. */
|
||||
printf ("Erasing ustar archive...\n");
|
||||
memset (header, 0, BLOCK_SECTOR_SIZE);
|
||||
block_write (src, 0, header);
|
||||
block_write (src, 1, header);
|
||||
|
||||
free (data);
|
||||
free (header);
|
||||
}
|
||||
|
||||
/* Copies file FILE_NAME from the file system to the scratch
|
||||
device, in ustar format.
|
||||
|
||||
The first call to this function will write starting at the
|
||||
beginning of the scratch device. Later calls advance across
|
||||
the device. This position is independent of that used for
|
||||
fsutil_extract(), so `extract' should precede all
|
||||
`append's. */
|
||||
void
|
||||
fsutil_append (char **argv)
|
||||
{
|
||||
static block_sector_t sector = 0;
|
||||
|
||||
const char *file_name = argv[1];
|
||||
void *buffer;
|
||||
struct file *src;
|
||||
struct block *dst;
|
||||
off_t size;
|
||||
|
||||
printf ("Appending '%s' to ustar archive on scratch device...\n", file_name);
|
||||
|
||||
/* Allocate buffer. */
|
||||
buffer = malloc (BLOCK_SECTOR_SIZE);
|
||||
if (buffer == NULL)
|
||||
PANIC ("couldn't allocate buffer");
|
||||
|
||||
/* Open source file. */
|
||||
src = filesys_open (file_name);
|
||||
if (src == NULL)
|
||||
PANIC ("%s: open failed", file_name);
|
||||
size = file_length (src);
|
||||
|
||||
/* Open target block device. */
|
||||
dst = block_get_role (BLOCK_SCRATCH);
|
||||
if (dst == NULL)
|
||||
PANIC ("couldn't open scratch device");
|
||||
|
||||
/* Write ustar header to first sector. */
|
||||
if (!ustar_make_header (file_name, USTAR_REGULAR, size, buffer))
|
||||
PANIC ("%s: name too long for ustar format", file_name);
|
||||
block_write (dst, sector++, buffer);
|
||||
|
||||
/* Do copy. */
|
||||
while (size > 0)
|
||||
{
|
||||
int chunk_size = size > BLOCK_SECTOR_SIZE ? BLOCK_SECTOR_SIZE : size;
|
||||
if (sector >= block_size (dst))
|
||||
PANIC ("%s: out of space on scratch device", file_name);
|
||||
if (file_read (src, buffer, chunk_size) != chunk_size)
|
||||
PANIC ("%s: read failed with %"PROTd" bytes unread", file_name, size);
|
||||
memset (buffer + chunk_size, 0, BLOCK_SECTOR_SIZE - chunk_size);
|
||||
block_write (dst, sector++, buffer);
|
||||
size -= chunk_size;
|
||||
}
|
||||
|
||||
/* Write ustar end-of-archive marker, which is two consecutive
|
||||
sectors full of zeros. Don't advance our position past
|
||||
them, though, in case we have more files to append. */
|
||||
memset (buffer, 0, BLOCK_SECTOR_SIZE);
|
||||
block_write (dst, sector, buffer);
|
||||
block_write (dst, sector, buffer + 1);
|
||||
|
||||
/* Finish up. */
|
||||
file_close (src);
|
||||
free (buffer);
|
||||
}
|
10
pintos-env/pintos/filesys/fsutil.h
Executable file
10
pintos-env/pintos/filesys/fsutil.h
Executable file
|
@ -0,0 +1,10 @@
|
|||
#ifndef FILESYS_FSUTIL_H
|
||||
#define FILESYS_FSUTIL_H
|
||||
|
||||
void fsutil_ls (char **argv);
|
||||
void fsutil_cat (char **argv);
|
||||
void fsutil_rm (char **argv);
|
||||
void fsutil_extract (char **argv);
|
||||
void fsutil_append (char **argv);
|
||||
|
||||
#endif /* filesys/fsutil.h */
|
345
pintos-env/pintos/filesys/inode.c
Executable file
345
pintos-env/pintos/filesys/inode.c
Executable file
|
@ -0,0 +1,345 @@
|
|||
#include "filesys/inode.h"
|
||||
#include <list.h>
|
||||
#include <debug.h>
|
||||
#include <round.h>
|
||||
#include <string.h>
|
||||
#include "filesys/filesys.h"
|
||||
#include "filesys/free-map.h"
|
||||
#include "threads/malloc.h"
|
||||
|
||||
/* Identifies an inode. */
|
||||
#define INODE_MAGIC 0x494e4f44
|
||||
|
||||
/* On-disk inode.
|
||||
Must be exactly BLOCK_SECTOR_SIZE bytes long. */
|
||||
struct inode_disk
|
||||
{
|
||||
block_sector_t start; /* First data sector. */
|
||||
off_t length; /* File size in bytes. */
|
||||
unsigned magic; /* Magic number. */
|
||||
uint32_t unused[125]; /* Not used. */
|
||||
};
|
||||
|
||||
/* Returns the number of sectors to allocate for an inode SIZE
|
||||
bytes long. */
|
||||
static inline size_t
|
||||
bytes_to_sectors (off_t size)
|
||||
{
|
||||
return DIV_ROUND_UP (size, BLOCK_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
/* In-memory inode. */
|
||||
struct inode
|
||||
{
|
||||
struct list_elem elem; /* Element in inode list. */
|
||||
block_sector_t sector; /* Sector number of disk location. */
|
||||
int open_cnt; /* Number of openers. */
|
||||
bool removed; /* True if deleted, false otherwise. */
|
||||
int deny_write_cnt; /* 0: writes ok, >0: deny writes. */
|
||||
struct inode_disk data; /* Inode content. */
|
||||
};
|
||||
|
||||
/* Returns the block device sector that contains byte offset POS
|
||||
within INODE.
|
||||
Returns -1 if INODE does not contain data for a byte at offset
|
||||
POS. */
|
||||
static block_sector_t
|
||||
byte_to_sector (const struct inode *inode, off_t pos)
|
||||
{
|
||||
ASSERT (inode != NULL);
|
||||
if (pos < inode->data.length)
|
||||
return inode->data.start + pos / BLOCK_SECTOR_SIZE;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* List of open inodes, so that opening a single inode twice
|
||||
returns the same `struct inode'. */
|
||||
static struct list open_inodes;
|
||||
|
||||
/* Initializes the inode module. */
|
||||
void
|
||||
inode_init (void)
|
||||
{
|
||||
list_init (&open_inodes);
|
||||
}
|
||||
|
||||
/* Initializes an inode with LENGTH bytes of data and
|
||||
writes the new inode to sector SECTOR on the file system
|
||||
device.
|
||||
Returns true if successful.
|
||||
Returns false if memory or disk allocation fails. */
|
||||
bool
|
||||
inode_create (block_sector_t sector, off_t length)
|
||||
{
|
||||
struct inode_disk *disk_inode = NULL;
|
||||
bool success = false;
|
||||
|
||||
ASSERT (length >= 0);
|
||||
|
||||
/* If this assertion fails, the inode structure is not exactly
|
||||
one sector in size, and you should fix that. */
|
||||
ASSERT (sizeof *disk_inode == BLOCK_SECTOR_SIZE);
|
||||
|
||||
disk_inode = calloc (1, sizeof *disk_inode);
|
||||
if (disk_inode != NULL)
|
||||
{
|
||||
size_t sectors = bytes_to_sectors (length);
|
||||
disk_inode->length = length;
|
||||
disk_inode->magic = INODE_MAGIC;
|
||||
if (free_map_allocate (sectors, &disk_inode->start))
|
||||
{
|
||||
block_write (fs_device, sector, disk_inode);
|
||||
if (sectors > 0)
|
||||
{
|
||||
static char zeros[BLOCK_SECTOR_SIZE];
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sectors; i++)
|
||||
block_write (fs_device, disk_inode->start + i, zeros);
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
free (disk_inode);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Reads an inode from SECTOR
|
||||
and returns a `struct inode' that contains it.
|
||||
Returns a null pointer if memory allocation fails. */
|
||||
struct inode *
|
||||
inode_open (block_sector_t sector)
|
||||
{
|
||||
struct list_elem *e;
|
||||
struct inode *inode;
|
||||
|
||||
/* Check whether this inode is already open. */
|
||||
for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
|
||||
e = list_next (e))
|
||||
{
|
||||
inode = list_entry (e, struct inode, elem);
|
||||
if (inode->sector == sector)
|
||||
{
|
||||
inode_reopen (inode);
|
||||
return inode;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate memory. */
|
||||
inode = malloc (sizeof *inode);
|
||||
if (inode == NULL)
|
||||
return NULL;
|
||||
|
||||
/* Initialize. */
|
||||
list_push_front (&open_inodes, &inode->elem);
|
||||
inode->sector = sector;
|
||||
inode->open_cnt = 1;
|
||||
inode->deny_write_cnt = 0;
|
||||
inode->removed = false;
|
||||
block_read (fs_device, inode->sector, &inode->data);
|
||||
return inode;
|
||||
}
|
||||
|
||||
/* Reopens and returns INODE. */
|
||||
struct inode *
|
||||
inode_reopen (struct inode *inode)
|
||||
{
|
||||
if (inode != NULL)
|
||||
inode->open_cnt++;
|
||||
return inode;
|
||||
}
|
||||
|
||||
/* Returns INODE's inode number. */
|
||||
block_sector_t
|
||||
inode_get_inumber (const struct inode *inode)
|
||||
{
|
||||
return inode->sector;
|
||||
}
|
||||
|
||||
/* Closes INODE and writes it to disk.
|
||||
If this was the last reference to INODE, frees its memory.
|
||||
If INODE was also a removed inode, frees its blocks. */
|
||||
void
|
||||
inode_close (struct inode *inode)
|
||||
{
|
||||
/* Ignore null pointer. */
|
||||
if (inode == NULL)
|
||||
return;
|
||||
|
||||
/* Release resources if this was the last opener. */
|
||||
if (--inode->open_cnt == 0)
|
||||
{
|
||||
/* Remove from inode list and release lock. */
|
||||
list_remove (&inode->elem);
|
||||
|
||||
/* Deallocate blocks if removed. */
|
||||
if (inode->removed)
|
||||
{
|
||||
free_map_release (inode->sector, 1);
|
||||
free_map_release (inode->data.start,
|
||||
bytes_to_sectors (inode->data.length));
|
||||
}
|
||||
|
||||
free (inode);
|
||||
}
|
||||
}
|
||||
|
||||
/* Marks INODE to be deleted when it is closed by the last caller who
|
||||
has it open. */
|
||||
void
|
||||
inode_remove (struct inode *inode)
|
||||
{
|
||||
ASSERT (inode != NULL);
|
||||
inode->removed = true;
|
||||
}
|
||||
|
||||
/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
|
||||
Returns the number of bytes actually read, which may be less
|
||||
than SIZE if an error occurs or end of file is reached. */
|
||||
off_t
|
||||
inode_read_at (struct inode *inode, void *buffer_, off_t size, off_t offset)
|
||||
{
|
||||
uint8_t *buffer = buffer_;
|
||||
off_t bytes_read = 0;
|
||||
uint8_t *bounce = NULL;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
/* Disk sector to read, starting byte offset within sector. */
|
||||
block_sector_t sector_idx = byte_to_sector (inode, offset);
|
||||
int sector_ofs = offset % BLOCK_SECTOR_SIZE;
|
||||
|
||||
/* Bytes left in inode, bytes left in sector, lesser of the two. */
|
||||
off_t inode_left = inode_length (inode) - offset;
|
||||
int sector_left = BLOCK_SECTOR_SIZE - sector_ofs;
|
||||
int min_left = inode_left < sector_left ? inode_left : sector_left;
|
||||
|
||||
/* Number of bytes to actually copy out of this sector. */
|
||||
int chunk_size = size < min_left ? size : min_left;
|
||||
if (chunk_size <= 0)
|
||||
break;
|
||||
|
||||
if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE)
|
||||
{
|
||||
/* Read full sector directly into caller's buffer. */
|
||||
block_read (fs_device, sector_idx, buffer + bytes_read);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Read sector into bounce buffer, then partially copy
|
||||
into caller's buffer. */
|
||||
if (bounce == NULL)
|
||||
{
|
||||
bounce = malloc (BLOCK_SECTOR_SIZE);
|
||||
if (bounce == NULL)
|
||||
break;
|
||||
}
|
||||
block_read (fs_device, sector_idx, bounce);
|
||||
memcpy (buffer + bytes_read, bounce + sector_ofs, chunk_size);
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
size -= chunk_size;
|
||||
offset += chunk_size;
|
||||
bytes_read += chunk_size;
|
||||
}
|
||||
free (bounce);
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
|
||||
Returns the number of bytes actually written, which may be
|
||||
less than SIZE if end of file is reached or an error occurs.
|
||||
(Normally a write at end of file would extend the inode, but
|
||||
growth is not yet implemented.) */
|
||||
off_t
|
||||
inode_write_at (struct inode *inode, const void *buffer_, off_t size,
|
||||
off_t offset)
|
||||
{
|
||||
const uint8_t *buffer = buffer_;
|
||||
off_t bytes_written = 0;
|
||||
uint8_t *bounce = NULL;
|
||||
|
||||
if (inode->deny_write_cnt)
|
||||
return 0;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
/* Sector to write, starting byte offset within sector. */
|
||||
block_sector_t sector_idx = byte_to_sector (inode, offset);
|
||||
int sector_ofs = offset % BLOCK_SECTOR_SIZE;
|
||||
|
||||
/* Bytes left in inode, bytes left in sector, lesser of the two. */
|
||||
off_t inode_left = inode_length (inode) - offset;
|
||||
int sector_left = BLOCK_SECTOR_SIZE - sector_ofs;
|
||||
int min_left = inode_left < sector_left ? inode_left : sector_left;
|
||||
|
||||
/* Number of bytes to actually write into this sector. */
|
||||
int chunk_size = size < min_left ? size : min_left;
|
||||
if (chunk_size <= 0)
|
||||
break;
|
||||
|
||||
if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE)
|
||||
{
|
||||
/* Write full sector directly to disk. */
|
||||
block_write (fs_device, sector_idx, buffer + bytes_written);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We need a bounce buffer. */
|
||||
if (bounce == NULL)
|
||||
{
|
||||
bounce = malloc (BLOCK_SECTOR_SIZE);
|
||||
if (bounce == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the sector contains data before or after the chunk
|
||||
we're writing, then we need to read in the sector
|
||||
first. Otherwise we start with a sector of all zeros. */
|
||||
if (sector_ofs > 0 || chunk_size < sector_left)
|
||||
block_read (fs_device, sector_idx, bounce);
|
||||
else
|
||||
memset (bounce, 0, BLOCK_SECTOR_SIZE);
|
||||
memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size);
|
||||
block_write (fs_device, sector_idx, bounce);
|
||||
}
|
||||
|
||||
/* Advance. */
|
||||
size -= chunk_size;
|
||||
offset += chunk_size;
|
||||
bytes_written += chunk_size;
|
||||
}
|
||||
free (bounce);
|
||||
|
||||
return bytes_written;
|
||||
}
|
||||
|
||||
/* Disables writes to INODE.
|
||||
May be called at most once per inode opener. */
|
||||
void
|
||||
inode_deny_write (struct inode *inode)
|
||||
{
|
||||
inode->deny_write_cnt++;
|
||||
ASSERT (inode->deny_write_cnt <= inode->open_cnt);
|
||||
}
|
||||
|
||||
/* Re-enables writes to INODE.
|
||||
Must be called once by each inode opener who has called
|
||||
inode_deny_write() on the inode, before closing the inode. */
|
||||
void
|
||||
inode_allow_write (struct inode *inode)
|
||||
{
|
||||
ASSERT (inode->deny_write_cnt > 0);
|
||||
ASSERT (inode->deny_write_cnt <= inode->open_cnt);
|
||||
inode->deny_write_cnt--;
|
||||
}
|
||||
|
||||
/* Returns the length, in bytes, of INODE's data. */
|
||||
off_t
|
||||
inode_length (const struct inode *inode)
|
||||
{
|
||||
return inode->data.length;
|
||||
}
|
23
pintos-env/pintos/filesys/inode.h
Executable file
23
pintos-env/pintos/filesys/inode.h
Executable file
|
@ -0,0 +1,23 @@
|
|||
#ifndef FILESYS_INODE_H
|
||||
#define FILESYS_INODE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "filesys/off_t.h"
|
||||
#include "devices/block.h"
|
||||
|
||||
struct bitmap;
|
||||
|
||||
void inode_init (void);
|
||||
bool inode_create (block_sector_t, off_t);
|
||||
struct inode *inode_open (block_sector_t);
|
||||
struct inode *inode_reopen (struct inode *);
|
||||
block_sector_t inode_get_inumber (const struct inode *);
|
||||
void inode_close (struct inode *);
|
||||
void inode_remove (struct inode *);
|
||||
off_t inode_read_at (struct inode *, void *, off_t size, off_t offset);
|
||||
off_t inode_write_at (struct inode *, const void *, off_t size, off_t offset);
|
||||
void inode_deny_write (struct inode *);
|
||||
void inode_allow_write (struct inode *);
|
||||
off_t inode_length (const struct inode *);
|
||||
|
||||
#endif /* filesys/inode.h */
|
15
pintos-env/pintos/filesys/off_t.h
Executable file
15
pintos-env/pintos/filesys/off_t.h
Executable file
|
@ -0,0 +1,15 @@
|
|||
#ifndef FILESYS_OFF_T_H
|
||||
#define FILESYS_OFF_T_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* An offset within a file.
|
||||
This is a separate header because multiple headers want this
|
||||
definition but not any others. */
|
||||
typedef int32_t off_t;
|
||||
|
||||
/* Format specifier for printf(), e.g.:
|
||||
printf ("offset=%"PROTd"\n", offset); */
|
||||
#define PROTd PRId32
|
||||
|
||||
#endif /* filesys/off_t.h */
|
189
pintos-env/pintos/lib/arithmetic.c
Executable file
189
pintos-env/pintos/lib/arithmetic.c
Executable file
|
@ -0,0 +1,189 @@
|
|||
#include <stdint.h>
|
||||
|
||||
/* On x86, division of one 64-bit integer by another cannot be
|
||||
done with a single instruction or a short sequence. Thus, GCC
|
||||
implements 64-bit division and remainder operations through
|
||||
function calls. These functions are normally obtained from
|
||||
libgcc, which is automatically included by GCC in any link
|
||||
that it does.
|
||||
|
||||
Some x86-64 machines, however, have a compiler and utilities
|
||||
that can generate 32-bit x86 code without having any of the
|
||||
necessary libraries, including libgcc. Thus, we can make
|
||||
Pintos work on these machines by simply implementing our own
|
||||
64-bit division routines, which are the only routines from
|
||||
libgcc that Pintos requires.
|
||||
|
||||
Completeness is another reason to include these routines. If
|
||||
Pintos is completely self-contained, then that makes it that
|
||||
much less mysterious. */
|
||||
|
||||
/* Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to
|
||||
yield a 32-bit quotient. Returns the quotient.
|
||||
Traps with a divide error (#DE) if the quotient does not fit
|
||||
in 32 bits. */
|
||||
static inline uint32_t
|
||||
divl (uint64_t n, uint32_t d)
|
||||
{
|
||||
uint32_t n1 = n >> 32;
|
||||
uint32_t n0 = n;
|
||||
uint32_t q, r;
|
||||
|
||||
asm ("divl %4"
|
||||
: "=d" (r), "=a" (q)
|
||||
: "0" (n1), "1" (n0), "rm" (d));
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
/* Returns the number of leading zero bits in X,
|
||||
which must be nonzero. */
|
||||
static int
|
||||
nlz (uint32_t x)
|
||||
{
|
||||
/* This technique is portable, but there are better ways to do
|
||||
it on particular systems. With sufficiently new enough GCC,
|
||||
you can use __builtin_clz() to take advantage of GCC's
|
||||
knowledge of how to do it. Or you can use the x86 BSR
|
||||
instruction directly. */
|
||||
int n = 0;
|
||||
if (x <= 0x0000FFFF)
|
||||
{
|
||||
n += 16;
|
||||
x <<= 16;
|
||||
}
|
||||
if (x <= 0x00FFFFFF)
|
||||
{
|
||||
n += 8;
|
||||
x <<= 8;
|
||||
}
|
||||
if (x <= 0x0FFFFFFF)
|
||||
{
|
||||
n += 4;
|
||||
x <<= 4;
|
||||
}
|
||||
if (x <= 0x3FFFFFFF)
|
||||
{
|
||||
n += 2;
|
||||
x <<= 2;
|
||||
}
|
||||
if (x <= 0x7FFFFFFF)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
|
||||
quotient. */
|
||||
static uint64_t
|
||||
udiv64 (uint64_t n, uint64_t d)
|
||||
{
|
||||
if ((d >> 32) == 0)
|
||||
{
|
||||
/* Proof of correctness:
|
||||
|
||||
Let n, d, b, n1, and n0 be defined as in this function.
|
||||
Let [x] be the "floor" of x. Let T = b[n1/d]. Assume d
|
||||
nonzero. Then:
|
||||
[n/d] = [n/d] - T + T
|
||||
= [n/d - T] + T by (1) below
|
||||
= [(b*n1 + n0)/d - T] + T by definition of n
|
||||
= [(b*n1 + n0)/d - dT/d] + T
|
||||
= [(b(n1 - d[n1/d]) + n0)/d] + T
|
||||
= [(b[n1 % d] + n0)/d] + T, by definition of %
|
||||
which is the expression calculated below.
|
||||
|
||||
(1) Note that for any real x, integer i: [x] + i = [x + i].
|
||||
|
||||
To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must
|
||||
be less than b. Assume that [n1 % d] and n0 take their
|
||||
respective maximum values of d - 1 and b - 1:
|
||||
[(b(d - 1) + (b - 1))/d] < b
|
||||
<=> [(bd - 1)/d] < b
|
||||
<=> [b - 1/d] < b
|
||||
which is a tautology.
|
||||
|
||||
Therefore, this code is correct and will not trap. */
|
||||
uint64_t b = 1ULL << 32;
|
||||
uint32_t n1 = n >> 32;
|
||||
uint32_t n0 = n;
|
||||
uint32_t d0 = d;
|
||||
|
||||
return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Based on the algorithm and proof available from
|
||||
http://www.hackersdelight.org/revisions.pdf. */
|
||||
if (n < d)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
uint32_t d1 = d >> 32;
|
||||
int s = nlz (d1);
|
||||
uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s);
|
||||
return n - (q - 1) * d < d ? q - 1 : q;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
|
||||
remainder. */
|
||||
static uint32_t
|
||||
umod64 (uint64_t n, uint64_t d)
|
||||
{
|
||||
return n - d * udiv64 (n, d);
|
||||
}
|
||||
|
||||
/* Divides signed 64-bit N by signed 64-bit D and returns the
|
||||
quotient. */
|
||||
static int64_t
|
||||
sdiv64 (int64_t n, int64_t d)
|
||||
{
|
||||
uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n;
|
||||
uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d;
|
||||
uint64_t q_abs = udiv64 (n_abs, d_abs);
|
||||
return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs;
|
||||
}
|
||||
|
||||
/* Divides signed 64-bit N by signed 64-bit D and returns the
|
||||
remainder. */
|
||||
static int32_t
|
||||
smod64 (int64_t n, int64_t d)
|
||||
{
|
||||
return n - d * sdiv64 (n, d);
|
||||
}
|
||||
|
||||
/* These are the routines that GCC calls. */
|
||||
|
||||
long long __divdi3 (long long n, long long d);
|
||||
long long __moddi3 (long long n, long long d);
|
||||
unsigned long long __udivdi3 (unsigned long long n, unsigned long long d);
|
||||
unsigned long long __umoddi3 (unsigned long long n, unsigned long long d);
|
||||
|
||||
/* Signed 64-bit division. */
|
||||
long long
|
||||
__divdi3 (long long n, long long d)
|
||||
{
|
||||
return sdiv64 (n, d);
|
||||
}
|
||||
|
||||
/* Signed 64-bit remainder. */
|
||||
long long
|
||||
__moddi3 (long long n, long long d)
|
||||
{
|
||||
return smod64 (n, d);
|
||||
}
|
||||
|
||||
/* Unsigned 64-bit division. */
|
||||
unsigned long long
|
||||
__udivdi3 (unsigned long long n, unsigned long long d)
|
||||
{
|
||||
return udiv64 (n, d);
|
||||
}
|
||||
|
||||
/* Unsigned 64-bit remainder. */
|
||||
unsigned long long
|
||||
__umoddi3 (unsigned long long n, unsigned long long d)
|
||||
{
|
||||
return umod64 (n, d);
|
||||
}
|
28
pintos-env/pintos/lib/ctype.h
Executable file
28
pintos-env/pintos/lib/ctype.h
Executable file
|
@ -0,0 +1,28 @@
|
|||
#ifndef __LIB_CTYPE_H
|
||||
#define __LIB_CTYPE_H
|
||||
|
||||
static inline int islower (int c) { return c >= 'a' && c <= 'z'; }
|
||||
static inline int isupper (int c) { return c >= 'A' && c <= 'Z'; }
|
||||
static inline int isalpha (int c) { return islower (c) || isupper (c); }
|
||||
static inline int isdigit (int c) { return c >= '0' && c <= '9'; }
|
||||
static inline int isalnum (int c) { return isalpha (c) || isdigit (c); }
|
||||
static inline int isxdigit (int c) {
|
||||
return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
}
|
||||
static inline int isspace (int c) {
|
||||
return (c == ' ' || c == '\f' || c == '\n'
|
||||
|| c == '\r' || c == '\t' || c == '\v');
|
||||
}
|
||||
static inline int isblank (int c) { return c == ' ' || c == '\t'; }
|
||||
static inline int isgraph (int c) { return c > 32 && c < 127; }
|
||||
static inline int isprint (int c) { return c >= 32 && c < 127; }
|
||||
static inline int iscntrl (int c) { return (c >= 0 && c < 32) || c == 127; }
|
||||
static inline int isascii (int c) { return c >= 0 && c < 128; }
|
||||
static inline int ispunct (int c) {
|
||||
return isprint (c) && !isalnum (c) && !isspace (c);
|
||||
}
|
||||
|
||||
static inline int tolower (int c) { return isupper (c) ? c - 'A' + 'a' : c; }
|
||||
static inline int toupper (int c) { return islower (c) ? c - 'a' + 'A' : c; }
|
||||
|
||||
#endif /* lib/ctype.h */
|
32
pintos-env/pintos/lib/debug.c
Executable file
32
pintos-env/pintos/lib/debug.c
Executable file
|
@ -0,0 +1,32 @@
|
|||
#include <debug.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Prints the call stack, that is, a list of addresses, one in
|
||||
each of the functions we are nested within. gdb or addr2line
|
||||
may be applied to kernel.o to translate these into file names,
|
||||
line numbers, and function names. */
|
||||
void
|
||||
debug_backtrace (void)
|
||||
{
|
||||
static bool explained;
|
||||
void **frame;
|
||||
|
||||
printf ("Call stack: %p", __builtin_return_address (0));
|
||||
for (frame = __builtin_frame_address (1);
|
||||
(uintptr_t) frame >= 0x1000 && frame[0] != NULL;
|
||||
frame = frame[0])
|
||||
printf (" %p", frame[1]);
|
||||
printf (".\n");
|
||||
|
||||
if (!explained)
|
||||
{
|
||||
explained = true;
|
||||
printf ("The `backtrace' program can make call stacks useful.\n"
|
||||
"Read \"Backtraces\" in the \"Debugging Tools\" chapter\n"
|
||||
"of the Pintos documentation for more information.\n");
|
||||
}
|
||||
}
|
39
pintos-env/pintos/lib/debug.h
Executable file
39
pintos-env/pintos/lib/debug.h
Executable file
|
@ -0,0 +1,39 @@
|
|||
#ifndef __LIB_DEBUG_H
|
||||
#define __LIB_DEBUG_H
|
||||
|
||||
/* GCC lets us add "attributes" to functions, function
|
||||
parameters, etc. to indicate their properties.
|
||||
See the GCC manual for details. */
|
||||
#define UNUSED __attribute__ ((unused))
|
||||
#define NO_RETURN __attribute__ ((noreturn))
|
||||
#define NO_INLINE __attribute__ ((noinline))
|
||||
#define PRINTF_FORMAT(FMT, FIRST) __attribute__ ((format (printf, FMT, FIRST)))
|
||||
|
||||
/* Halts the OS, printing the source file name, line number, and
|
||||
function name, plus a user-specific message. */
|
||||
#define PANIC(...) debug_panic (__FILE__, __LINE__, __func__, __VA_ARGS__)
|
||||
|
||||
void debug_panic (const char *file, int line, const char *function,
|
||||
const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN;
|
||||
void debug_backtrace (void);
|
||||
void debug_backtrace_all (void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* This is outside the header guard so that debug.h may be
|
||||
included multiple times with different settings of NDEBUG. */
|
||||
#undef ASSERT
|
||||
#undef NOT_REACHED
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define ASSERT(CONDITION) \
|
||||
if (CONDITION) { } else { \
|
||||
PANIC ("assertion `%s' failed.", #CONDITION); \
|
||||
}
|
||||
#define NOT_REACHED() PANIC ("executed an unreachable statement");
|
||||
#else
|
||||
#define ASSERT(CONDITION) ((void) 0)
|
||||
#define NOT_REACHED() for (;;)
|
||||
#endif /* lib/debug.h */
|
48
pintos-env/pintos/lib/inttypes.h
Executable file
48
pintos-env/pintos/lib/inttypes.h
Executable file
|
@ -0,0 +1,48 @@
|
|||
#ifndef __LIB_INTTYPES_H
|
||||
#define __LIB_INTTYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PRId8 "hhd"
|
||||
#define PRIi8 "hhi"
|
||||
#define PRIo8 "hho"
|
||||
#define PRIu8 "hhu"
|
||||
#define PRIx8 "hhx"
|
||||
#define PRIX8 "hhX"
|
||||
|
||||
#define PRId16 "hd"
|
||||
#define PRIi16 "hi"
|
||||
#define PRIo16 "ho"
|
||||
#define PRIu16 "hu"
|
||||
#define PRIx16 "hx"
|
||||
#define PRIX16 "hX"
|
||||
|
||||
#define PRId32 "d"
|
||||
#define PRIi32 "i"
|
||||
#define PRIo32 "o"
|
||||
#define PRIu32 "u"
|
||||
#define PRIx32 "x"
|
||||
#define PRIX32 "X"
|
||||
|
||||
#define PRId64 "lld"
|
||||
#define PRIi64 "lli"
|
||||
#define PRIo64 "llo"
|
||||
#define PRIu64 "llu"
|
||||
#define PRIx64 "llx"
|
||||
#define PRIX64 "llX"
|
||||
|
||||
#define PRIdMAX "jd"
|
||||
#define PRIiMAX "ji"
|
||||
#define PRIoMAX "jo"
|
||||
#define PRIuMAX "ju"
|
||||
#define PRIxMAX "jx"
|
||||
#define PRIXMAX "jX"
|
||||
|
||||
#define PRIdPTR "td"
|
||||
#define PRIiPTR "ti"
|
||||
#define PRIoPTR "to"
|
||||
#define PRIuPTR "tu"
|
||||
#define PRIxPTR "tx"
|
||||
#define PRIXPTR "tX"
|
||||
|
||||
#endif /* lib/inttypes.h */
|
372
pintos-env/pintos/lib/kernel/bitmap.c
Executable file
372
pintos-env/pintos/lib/kernel/bitmap.c
Executable file
|
@ -0,0 +1,372 @@
|
|||
#include "bitmap.h"
|
||||
#include <debug.h>
|
||||
#include <limits.h>
|
||||
#include <round.h>
|
||||
#include <stdio.h>
|
||||
#include "threads/malloc.h"
|
||||
#ifdef FILESYS
|
||||
#include "filesys/file.h"
|
||||
#endif
|
||||
|
||||
/* Element type.
|
||||
|
||||
This must be an unsigned integer type at least as wide as int.
|
||||
|
||||
Each bit represents one bit in the bitmap.
|
||||
If bit 0 in an element represents bit K in the bitmap,
|
||||
then bit 1 in the element represents bit K+1 in the bitmap,
|
||||
and so on. */
|
||||
typedef unsigned long elem_type;
|
||||
|
||||
/* Number of bits in an element. */
|
||||
#define ELEM_BITS (sizeof (elem_type) * CHAR_BIT)
|
||||
|
||||
/* From the outside, a bitmap is an array of bits. From the
|
||||
inside, it's an array of elem_type (defined above) that
|
||||
simulates an array of bits. */
|
||||
struct bitmap
|
||||
{
|
||||
size_t bit_cnt; /* Number of bits. */
|
||||
elem_type *bits; /* Elements that represent bits. */
|
||||
};
|
||||
|
||||
/* Returns the index of the element that contains the bit
|
||||
numbered BIT_IDX. */
|
||||
static inline size_t
|
||||
elem_idx (size_t bit_idx)
|
||||
{
|
||||
return bit_idx / ELEM_BITS;
|
||||
}
|
||||
|
||||
/* Returns an elem_type where only the bit corresponding to
|
||||
BIT_IDX is turned on. */
|
||||
static inline elem_type
|
||||
bit_mask (size_t bit_idx)
|
||||
{
|
||||
return (elem_type) 1 << (bit_idx % ELEM_BITS);
|
||||
}
|
||||
|
||||
/* Returns the number of elements required for BIT_CNT bits. */
|
||||
static inline size_t
|
||||
elem_cnt (size_t bit_cnt)
|
||||
{
|
||||
return DIV_ROUND_UP (bit_cnt, ELEM_BITS);
|
||||
}
|
||||
|
||||
/* Returns the number of bytes required for BIT_CNT bits. */
|
||||
static inline size_t
|
||||
byte_cnt (size_t bit_cnt)
|
||||
{
|
||||
return sizeof (elem_type) * elem_cnt (bit_cnt);
|
||||
}
|
||||
|
||||
/* Returns a bit mask in which the bits actually used in the last
|
||||
element of B's bits are set to 1 and the rest are set to 0. */
|
||||
static inline elem_type
|
||||
last_mask (const struct bitmap *b)
|
||||
{
|
||||
int last_bits = b->bit_cnt % ELEM_BITS;
|
||||
return last_bits ? ((elem_type) 1 << last_bits) - 1 : (elem_type) -1;
|
||||
}
|
||||
|
||||
/* Creation and destruction. */
|
||||
|
||||
/* Initializes B to be a bitmap of BIT_CNT bits
|
||||
and sets all of its bits to false.
|
||||
Returns true if success, false if memory allocation
|
||||
failed. */
|
||||
struct bitmap *
|
||||
bitmap_create (size_t bit_cnt)
|
||||
{
|
||||
struct bitmap *b = malloc (sizeof *b);
|
||||
if (b != NULL)
|
||||
{
|
||||
b->bit_cnt = bit_cnt;
|
||||
b->bits = malloc (byte_cnt (bit_cnt));
|
||||
if (b->bits != NULL || bit_cnt == 0)
|
||||
{
|
||||
bitmap_set_all (b, false);
|
||||
return b;
|
||||
}
|
||||
free (b);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Creates and returns a bitmap with BIT_CNT bits in the
|
||||
BLOCK_SIZE bytes of storage preallocated at BLOCK.
|
||||
BLOCK_SIZE must be at least bitmap_needed_bytes(BIT_CNT). */
|
||||
struct bitmap *
|
||||
bitmap_create_in_buf (size_t bit_cnt, void *block, size_t block_size UNUSED)
|
||||
{
|
||||
struct bitmap *b = block;
|
||||
|
||||
ASSERT (block_size >= bitmap_buf_size (bit_cnt));
|
||||
|
||||
b->bit_cnt = bit_cnt;
|
||||
b->bits = (elem_type *) (b + 1);
|
||||
bitmap_set_all (b, false);
|
||||
return b;
|
||||
}
|
||||
|
||||
/* Returns the number of bytes required to accomodate a bitmap
|
||||
with BIT_CNT bits (for use with bitmap_create_in_buf()). */
|
||||
size_t
|
||||
bitmap_buf_size (size_t bit_cnt)
|
||||
{
|
||||
return sizeof (struct bitmap) + byte_cnt (bit_cnt);
|
||||
}
|
||||
|
||||
/* Destroys bitmap B, freeing its storage.
|
||||
Not for use on bitmaps created by
|
||||
bitmap_create_preallocated(). */
|
||||
void
|
||||
bitmap_destroy (struct bitmap *b)
|
||||
{
|
||||
if (b != NULL)
|
||||
{
|
||||
free (b->bits);
|
||||
free (b);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bitmap size. */
|
||||
|
||||
/* Returns the number of bits in B. */
|
||||
size_t
|
||||
bitmap_size (const struct bitmap *b)
|
||||
{
|
||||
return b->bit_cnt;
|
||||
}
|
||||
|
||||
/* Setting and testing single bits. */
|
||||
|
||||
/* Atomically sets the bit numbered IDX in B to VALUE. */
|
||||
void
|
||||
bitmap_set (struct bitmap *b, size_t idx, bool value)
|
||||
{
|
||||
ASSERT (b != NULL);
|
||||
ASSERT (idx < b->bit_cnt);
|
||||
if (value)
|
||||
bitmap_mark (b, idx);
|
||||
else
|
||||
bitmap_reset (b, idx);
|
||||
}
|
||||
|
||||
/* Atomically sets the bit numbered BIT_IDX in B to true. */
|
||||
void
|
||||
bitmap_mark (struct bitmap *b, size_t bit_idx)
|
||||
{
|
||||
size_t idx = elem_idx (bit_idx);
|
||||
elem_type mask = bit_mask (bit_idx);
|
||||
|
||||
/* This is equivalent to `b->bits[idx] |= mask' except that it
|
||||
is guaranteed to be atomic on a uniprocessor machine. See
|
||||
the description of the OR instruction in [IA32-v2b]. */
|
||||
asm ("orl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
|
||||
}
|
||||
|
||||
/* Atomically sets the bit numbered BIT_IDX in B to false. */
|
||||
void
|
||||
bitmap_reset (struct bitmap *b, size_t bit_idx)
|
||||
{
|
||||
size_t idx = elem_idx (bit_idx);
|
||||
elem_type mask = bit_mask (bit_idx);
|
||||
|
||||
/* This is equivalent to `b->bits[idx] &= ~mask' except that it
|
||||
is guaranteed to be atomic on a uniprocessor machine. See
|
||||
the description of the AND instruction in [IA32-v2a]. */
|
||||
asm ("andl %1, %0" : "=m" (b->bits[idx]) : "r" (~mask) : "cc");
|
||||
}
|
||||
|
||||
/* Atomically toggles the bit numbered IDX in B;
|
||||
that is, if it is true, makes it false,
|
||||
and if it is false, makes it true. */
|
||||
void
|
||||
bitmap_flip (struct bitmap *b, size_t bit_idx)
|
||||
{
|
||||
size_t idx = elem_idx (bit_idx);
|
||||
elem_type mask = bit_mask (bit_idx);
|
||||
|
||||
/* This is equivalent to `b->bits[idx] ^= mask' except that it
|
||||
is guaranteed to be atomic on a uniprocessor machine. See
|
||||
the description of the XOR instruction in [IA32-v2b]. */
|
||||
asm ("xorl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
|
||||
}
|
||||
|
||||
/* Returns the value of the bit numbered IDX in B. */
|
||||
bool
|
||||
bitmap_test (const struct bitmap *b, size_t idx)
|
||||
{
|
||||
ASSERT (b != NULL);
|
||||
ASSERT (idx < b->bit_cnt);
|
||||
return (b->bits[elem_idx (idx)] & bit_mask (idx)) != 0;
|
||||
}
|
||||
|
||||
/* Setting and testing multiple bits. */
|
||||
|
||||
/* Sets all bits in B to VALUE. */
|
||||
void
|
||||
bitmap_set_all (struct bitmap *b, bool value)
|
||||
{
|
||||
ASSERT (b != NULL);
|
||||
|
||||
bitmap_set_multiple (b, 0, bitmap_size (b), value);
|
||||
}
|
||||
|
||||
/* Sets the CNT bits starting at START in B to VALUE. */
|
||||
void
|
||||
bitmap_set_multiple (struct bitmap *b, size_t start, size_t cnt, bool value)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
ASSERT (b != NULL);
|
||||
ASSERT (start <= b->bit_cnt);
|
||||
ASSERT (start + cnt <= b->bit_cnt);
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
bitmap_set (b, start + i, value);
|
||||
}
|
||||
|
||||
/* Returns the number of bits in B between START and START + CNT,
|
||||
exclusive, that are set to VALUE. */
|
||||
size_t
|
||||
bitmap_count (const struct bitmap *b, size_t start, size_t cnt, bool value)
|
||||
{
|
||||
size_t i, value_cnt;
|
||||
|
||||
ASSERT (b != NULL);
|
||||
ASSERT (start <= b->bit_cnt);
|
||||
ASSERT (start + cnt <= b->bit_cnt);
|
||||
|
||||
value_cnt = 0;
|
||||
for (i = 0; i < cnt; i++)
|
||||
if (bitmap_test (b, start + i) == value)
|
||||
value_cnt++;
|
||||
return value_cnt;
|
||||
}
|
||||
|
||||
/* Returns true if any bits in B between START and START + CNT,
|
||||
exclusive, are set to VALUE, and false otherwise. */
|
||||
bool
|
||||
bitmap_contains (const struct bitmap *b, size_t start, size_t cnt, bool value)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
ASSERT (b != NULL);
|
||||
ASSERT (start <= b->bit_cnt);
|
||||
ASSERT (start + cnt <= b->bit_cnt);
|
||||
|
||||
for (i = 0; i < cnt; i++)
|
||||
if (bitmap_test (b, start + i) == value)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns true if any bits in B between START and START + CNT,
|
||||
exclusive, are set to true, and false otherwise.*/
|
||||
bool
|
||||
bitmap_any (const struct bitmap *b, size_t start, size_t cnt)
|
||||
{
|
||||
return bitmap_contains (b, start, cnt, true);
|
||||
}
|
||||
|
||||
/* Returns true if no bits in B between START and START + CNT,
|
||||
exclusive, are set to true, and false otherwise.*/
|
||||
bool
|
||||
bitmap_none (const struct bitmap *b, size_t start, size_t cnt)
|
||||
{
|
||||
return !bitmap_contains (b, start, cnt, true);
|
||||
}
|
||||
|
||||
/* Returns true if every bit in B between START and START + CNT,
|
||||
exclusive, is set to true, and false otherwise. */
|
||||
bool
|
||||
bitmap_all (const struct bitmap *b, size_t start, size_t cnt)
|
||||
{
|
||||
return !bitmap_contains (b, start, cnt, false);
|
||||
}
|
||||
|
||||
/* Finding set or unset bits. */
|
||||
|
||||
/* Finds and returns the starting index of the first group of CNT
|
||||
consecutive bits in B at or after START that are all set to
|
||||
VALUE.
|
||||
If there is no such group, returns BITMAP_ERROR. */
|
||||
size_t
|
||||
bitmap_scan (const struct bitmap *b, size_t start, size_t cnt, bool value)
|
||||
{
|
||||
ASSERT (b != NULL);
|
||||
ASSERT (start <= b->bit_cnt);
|
||||
|
||||
if (cnt <= b->bit_cnt)
|
||||
{
|
||||
size_t last = b->bit_cnt - cnt;
|
||||
size_t i;
|
||||
for (i = start; i <= last; i++)
|
||||
if (!bitmap_contains (b, i, cnt, !value))
|
||||
return i;
|
||||
}
|
||||
return BITMAP_ERROR;
|
||||
}
|
||||
|
||||
/* Finds the first group of CNT consecutive bits in B at or after
|
||||
START that are all set to VALUE, flips them all to !VALUE,
|
||||
and returns the index of the first bit in the group.
|
||||
If there is no such group, returns BITMAP_ERROR.
|
||||
If CNT is zero, returns 0.
|
||||
Bits are set atomically, but testing bits is not atomic with
|
||||
setting them. */
|
||||
size_t
|
||||
bitmap_scan_and_flip (struct bitmap *b, size_t start, size_t cnt, bool value)
|
||||
{
|
||||
size_t idx = bitmap_scan (b, start, cnt, value);
|
||||
if (idx != BITMAP_ERROR)
|
||||
bitmap_set_multiple (b, idx, cnt, !value);
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* File input and output. */
|
||||
|
||||
#ifdef FILESYS
|
||||
/* Returns the number of bytes needed to store B in a file. */
|
||||
size_t
|
||||
bitmap_file_size (const struct bitmap *b)
|
||||
{
|
||||
return byte_cnt (b->bit_cnt);
|
||||
}
|
||||
|
||||
/* Reads B from FILE. Returns true if successful, false
|
||||
otherwise. */
|
||||
bool
|
||||
bitmap_read (struct bitmap *b, struct file *file)
|
||||
{
|
||||
bool success = true;
|
||||
if (b->bit_cnt > 0)
|
||||
{
|
||||
off_t size = byte_cnt (b->bit_cnt);
|
||||
success = file_read_at (file, b->bits, size, 0) == size;
|
||||
b->bits[elem_cnt (b->bit_cnt) - 1] &= last_mask (b);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/* Writes B to FILE. Return true if successful, false
|
||||
otherwise. */
|
||||
bool
|
||||
bitmap_write (const struct bitmap *b, struct file *file)
|
||||
{
|
||||
off_t size = byte_cnt (b->bit_cnt);
|
||||
return file_write_at (file, b->bits, size, 0) == size;
|
||||
}
|
||||
#endif /* FILESYS */
|
||||
|
||||
/* Debugging. */
|
||||
|
||||
/* Dumps the contents of B to the console as hexadecimal. */
|
||||
void
|
||||
bitmap_dump (const struct bitmap *b)
|
||||
{
|
||||
hex_dump (0, b->bits, byte_cnt (b->bit_cnt), false);
|
||||
}
|
||||
|
51
pintos-env/pintos/lib/kernel/bitmap.h
Executable file
51
pintos-env/pintos/lib/kernel/bitmap.h
Executable file
|
@ -0,0 +1,51 @@
|
|||
#ifndef __LIB_KERNEL_BITMAP_H
|
||||
#define __LIB_KERNEL_BITMAP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/* Bitmap abstract data type. */
|
||||
|
||||
/* Creation and destruction. */
|
||||
struct bitmap *bitmap_create (size_t bit_cnt);
|
||||
struct bitmap *bitmap_create_in_buf (size_t bit_cnt, void *, size_t byte_cnt);
|
||||
size_t bitmap_buf_size (size_t bit_cnt);
|
||||
void bitmap_destroy (struct bitmap *);
|
||||
|
||||
/* Bitmap size. */
|
||||
size_t bitmap_size (const struct bitmap *);
|
||||
|
||||
/* Setting and testing single bits. */
|
||||
void bitmap_set (struct bitmap *, size_t idx, bool);
|
||||
void bitmap_mark (struct bitmap *, size_t idx);
|
||||
void bitmap_reset (struct bitmap *, size_t idx);
|
||||
void bitmap_flip (struct bitmap *, size_t idx);
|
||||
bool bitmap_test (const struct bitmap *, size_t idx);
|
||||
|
||||
/* Setting and testing multiple bits. */
|
||||
void bitmap_set_all (struct bitmap *, bool);
|
||||
void bitmap_set_multiple (struct bitmap *, size_t start, size_t cnt, bool);
|
||||
size_t bitmap_count (const struct bitmap *, size_t start, size_t cnt, bool);
|
||||
bool bitmap_contains (const struct bitmap *, size_t start, size_t cnt, bool);
|
||||
bool bitmap_any (const struct bitmap *, size_t start, size_t cnt);
|
||||
bool bitmap_none (const struct bitmap *, size_t start, size_t cnt);
|
||||
bool bitmap_all (const struct bitmap *, size_t start, size_t cnt);
|
||||
|
||||
/* Finding set or unset bits. */
|
||||
#define BITMAP_ERROR SIZE_MAX
|
||||
size_t bitmap_scan (const struct bitmap *, size_t start, size_t cnt, bool);
|
||||
size_t bitmap_scan_and_flip (struct bitmap *, size_t start, size_t cnt, bool);
|
||||
|
||||
/* File input and output. */
|
||||
#ifdef FILESYS
|
||||
struct file;
|
||||
size_t bitmap_file_size (const struct bitmap *);
|
||||
bool bitmap_read (struct bitmap *, struct file *);
|
||||
bool bitmap_write (const struct bitmap *, struct file *);
|
||||
#endif
|
||||
|
||||
/* Debugging. */
|
||||
void bitmap_dump (const struct bitmap *);
|
||||
|
||||
#endif /* lib/kernel/bitmap.h */
|
191
pintos-env/pintos/lib/kernel/console.c
Executable file
191
pintos-env/pintos/lib/kernel/console.c
Executable file
|
@ -0,0 +1,191 @@
|
|||
#include <console.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include "devices/serial.h"
|
||||
#include "devices/vga.h"
|
||||
#include "threads/init.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/synch.h"
|
||||
|
||||
static void vprintf_helper (char, void *);
|
||||
static void putchar_have_lock (uint8_t c);
|
||||
|
||||
/* The console lock.
|
||||
Both the vga and serial layers do their own locking, so it's
|
||||
safe to call them at any time.
|
||||
But this lock is useful to prevent simultaneous printf() calls
|
||||
from mixing their output, which looks confusing. */
|
||||
static struct lock console_lock;
|
||||
|
||||
/* True in ordinary circumstances: we want to use the console
|
||||
lock to avoid mixing output between threads, as explained
|
||||
above.
|
||||
|
||||
False in early boot before the point that locks are functional
|
||||
or the console lock has been initialized, or after a kernel
|
||||
panics. In the former case, taking the lock would cause an
|
||||
assertion failure, which in turn would cause a panic, turning
|
||||
it into the latter case. In the latter case, if it is a buggy
|
||||
lock_acquire() implementation that caused the panic, we'll
|
||||
likely just recurse. */
|
||||
static bool use_console_lock;
|
||||
|
||||
/* It's possible, if you add enough debug output to Pintos, to
|
||||
try to recursively grab console_lock from a single thread. As
|
||||
a real example, I added a printf() call to palloc_free().
|
||||
Here's a real backtrace that resulted:
|
||||
|
||||
lock_console()
|
||||
vprintf()
|
||||
printf() - palloc() tries to grab the lock again
|
||||
palloc_free()
|
||||
thread_schedule_tail() - another thread dying as we switch threads
|
||||
schedule()
|
||||
thread_yield()
|
||||
intr_handler() - timer interrupt
|
||||
intr_set_level()
|
||||
serial_putc()
|
||||
putchar_have_lock()
|
||||
putbuf()
|
||||
sys_write() - one process writing to the console
|
||||
syscall_handler()
|
||||
intr_handler()
|
||||
|
||||
This kind of thing is very difficult to debug, so we avoid the
|
||||
problem by simulating a recursive lock with a depth
|
||||
counter. */
|
||||
static int console_lock_depth;
|
||||
|
||||
/* Number of characters written to console. */
|
||||
static int64_t write_cnt;
|
||||
|
||||
/* Enable console locking. */
|
||||
void
|
||||
console_init (void)
|
||||
{
|
||||
lock_init (&console_lock);
|
||||
use_console_lock = true;
|
||||
}
|
||||
|
||||
/* Notifies the console that a kernel panic is underway,
|
||||
which warns it to avoid trying to take the console lock from
|
||||
now on. */
|
||||
void
|
||||
console_panic (void)
|
||||
{
|
||||
use_console_lock = false;
|
||||
}
|
||||
|
||||
/* Prints console statistics. */
|
||||
void
|
||||
console_print_stats (void)
|
||||
{
|
||||
printf ("Console: %lld characters output\n", write_cnt);
|
||||
}
|
||||
|
||||
/* Acquires the console lock. */
|
||||
static void
|
||||
acquire_console (void)
|
||||
{
|
||||
if (!intr_context () && use_console_lock)
|
||||
{
|
||||
if (lock_held_by_current_thread (&console_lock))
|
||||
console_lock_depth++;
|
||||
else
|
||||
lock_acquire (&console_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Releases the console lock. */
|
||||
static void
|
||||
release_console (void)
|
||||
{
|
||||
if (!intr_context () && use_console_lock)
|
||||
{
|
||||
if (console_lock_depth > 0)
|
||||
console_lock_depth--;
|
||||
else
|
||||
lock_release (&console_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true if the current thread has the console lock,
|
||||
false otherwise. */
|
||||
static bool
|
||||
console_locked_by_current_thread (void)
|
||||
{
|
||||
return (intr_context ()
|
||||
|| !use_console_lock
|
||||
|| lock_held_by_current_thread (&console_lock));
|
||||
}
|
||||
|
||||
/* The standard vprintf() function,
|
||||
which is like printf() but uses a va_list.
|
||||
Writes its output to both vga display and serial port. */
|
||||
int
|
||||
vprintf (const char *format, va_list args)
|
||||
{
|
||||
int char_cnt = 0;
|
||||
|
||||
acquire_console ();
|
||||
__vprintf (format, args, vprintf_helper, &char_cnt);
|
||||
release_console ();
|
||||
|
||||
return char_cnt;
|
||||
}
|
||||
|
||||
/* Writes string S to the console, followed by a new-line
|
||||
character. */
|
||||
int
|
||||
puts (const char *s)
|
||||
{
|
||||
acquire_console ();
|
||||
while (*s != '\0')
|
||||
putchar_have_lock (*s++);
|
||||
putchar_have_lock ('\n');
|
||||
release_console ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Writes the N characters in BUFFER to the console. */
|
||||
void
|
||||
putbuf (const char *buffer, size_t n)
|
||||
{
|
||||
acquire_console ();
|
||||
while (n-- > 0)
|
||||
putchar_have_lock (*buffer++);
|
||||
release_console ();
|
||||
}
|
||||
|
||||
/* Writes C to the vga display and serial port. */
|
||||
int
|
||||
putchar (int c)
|
||||
{
|
||||
acquire_console ();
|
||||
putchar_have_lock (c);
|
||||
release_console ();
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Helper function for vprintf(). */
|
||||
static void
|
||||
vprintf_helper (char c, void *char_cnt_)
|
||||
{
|
||||
int *char_cnt = char_cnt_;
|
||||
(*char_cnt)++;
|
||||
putchar_have_lock (c);
|
||||
}
|
||||
|
||||
/* Writes C to the vga display and serial port.
|
||||
The caller has already acquired the console lock if
|
||||
appropriate. */
|
||||
static void
|
||||
putchar_have_lock (uint8_t c)
|
||||
{
|
||||
ASSERT (console_locked_by_current_thread ());
|
||||
write_cnt++;
|
||||
serial_putc (c);
|
||||
vga_putc (c);
|
||||
}
|
8
pintos-env/pintos/lib/kernel/console.h
Executable file
8
pintos-env/pintos/lib/kernel/console.h
Executable file
|
@ -0,0 +1,8 @@
|
|||
#ifndef __LIB_KERNEL_CONSOLE_H
|
||||
#define __LIB_KERNEL_CONSOLE_H
|
||||
|
||||
void console_init (void);
|
||||
void console_panic (void);
|
||||
void console_print_stats (void);
|
||||
|
||||
#endif /* lib/kernel/console.h */
|
123
pintos-env/pintos/lib/kernel/debug.c
Executable file
123
pintos-env/pintos/lib/kernel/debug.c
Executable file
|
@ -0,0 +1,123 @@
|
|||
#include <debug.h>
|
||||
#include <console.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "threads/init.h"
|
||||
#include "threads/interrupt.h"
|
||||
#include "threads/thread.h"
|
||||
#include "threads/switch.h"
|
||||
#include "threads/vaddr.h"
|
||||
#include "devices/serial.h"
|
||||
#include "devices/shutdown.h"
|
||||
|
||||
/* Halts the OS, printing the source file name, line number, and
|
||||
function name, plus a user-specific message. */
|
||||
void
|
||||
debug_panic (const char *file, int line, const char *function,
|
||||
const char *message, ...)
|
||||
{
|
||||
static int level;
|
||||
va_list args;
|
||||
|
||||
intr_disable ();
|
||||
console_panic ();
|
||||
|
||||
level++;
|
||||
if (level == 1)
|
||||
{
|
||||
printf ("Kernel PANIC at %s:%d in %s(): ", file, line, function);
|
||||
|
||||
va_start (args, message);
|
||||
vprintf (message, args);
|
||||
printf ("\n");
|
||||
va_end (args);
|
||||
|
||||
debug_backtrace ();
|
||||
}
|
||||
else if (level == 2)
|
||||
printf ("Kernel PANIC recursion at %s:%d in %s().\n",
|
||||
file, line, function);
|
||||
else
|
||||
{
|
||||
/* Don't print anything: that's probably why we recursed. */
|
||||
}
|
||||
|
||||
serial_flush ();
|
||||
shutdown ();
|
||||
for (;;);
|
||||
}
|
||||
|
||||
/* Print call stack of a thread.
|
||||
The thread may be running, ready, or blocked. */
|
||||
static void
|
||||
print_stacktrace(struct thread *t, void *aux UNUSED)
|
||||
{
|
||||
void *retaddr = NULL, **frame = NULL;
|
||||
const char *status = "UNKNOWN";
|
||||
|
||||
switch (t->status) {
|
||||
case THREAD_RUNNING:
|
||||
status = "RUNNING";
|
||||
break;
|
||||
|
||||
case THREAD_READY:
|
||||
status = "READY";
|
||||
break;
|
||||
|
||||
case THREAD_BLOCKED:
|
||||
status = "BLOCKED";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
printf ("Call stack of thread `%s' (status %s):", t->name, status);
|
||||
|
||||
if (t == thread_current())
|
||||
{
|
||||
frame = __builtin_frame_address (1);
|
||||
retaddr = __builtin_return_address (0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Retrieve the values of the base and instruction pointers
|
||||
as they were saved when this thread called switch_threads. */
|
||||
struct switch_threads_frame * saved_frame;
|
||||
|
||||
saved_frame = (struct switch_threads_frame *)t->stack;
|
||||
|
||||
/* Skip threads if they have been added to the all threads
|
||||
list, but have never been scheduled.
|
||||
We can identify because their `stack' member either points
|
||||
at the top of their kernel stack page, or the
|
||||
switch_threads_frame's 'eip' member points at switch_entry.
|
||||
See also threads.c. */
|
||||
if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry)
|
||||
{
|
||||
printf (" thread was never scheduled.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
frame = (void **) saved_frame->ebp;
|
||||
retaddr = (void *) saved_frame->eip;
|
||||
}
|
||||
|
||||
printf (" %p", retaddr);
|
||||
for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0])
|
||||
printf (" %p", frame[1]);
|
||||
printf (".\n");
|
||||
}
|
||||
|
||||
/* Prints call stack of all threads. */
|
||||
void
|
||||
debug_backtrace_all (void)
|
||||
{
|
||||
enum intr_level oldlevel = intr_disable ();
|
||||
|
||||
thread_foreach (print_stacktrace, 0);
|
||||
intr_set_level (oldlevel);
|
||||
}
|
430
pintos-env/pintos/lib/kernel/hash.c
Executable file
430
pintos-env/pintos/lib/kernel/hash.c
Executable file
|
@ -0,0 +1,430 @@
|
|||
/* Hash table.
|
||||
|
||||
This data structure is thoroughly documented in the Tour of
|
||||
Pintos for Project 3.
|
||||
|
||||
See hash.h for basic information. */
|
||||
|
||||
#include "hash.h"
|
||||
#include "../debug.h"
|
||||
#include "threads/malloc.h"
|
||||
|
||||
#define list_elem_to_hash_elem(LIST_ELEM) \
|
||||
list_entry(LIST_ELEM, struct hash_elem, list_elem)
|
||||
|
||||
static struct list *find_bucket (struct hash *, struct hash_elem *);
|
||||
static struct hash_elem *find_elem (struct hash *, struct list *,
|
||||
struct hash_elem *);
|
||||
static void insert_elem (struct hash *, struct list *, struct hash_elem *);
|
||||
static void remove_elem (struct hash *, struct hash_elem *);
|
||||
static void rehash (struct hash *);
|
||||
|
||||
/* Initializes hash table H to compute hash values using HASH and
|
||||
compare hash elements using LESS, given auxiliary data AUX. */
|
||||
bool
|
||||
hash_init (struct hash *h,
|
||||
hash_hash_func *hash, hash_less_func *less, void *aux)
|
||||
{
|
||||
h->elem_cnt = 0;
|
||||
h->bucket_cnt = 4;
|
||||
h->buckets = malloc (sizeof *h->buckets * h->bucket_cnt);
|
||||
h->hash = hash;
|
||||
h->less = less;
|
||||
h->aux = aux;
|
||||
|
||||
if (h->buckets != NULL)
|
||||
{
|
||||
hash_clear (h, NULL);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Removes all the elements from H.
|
||||
|
||||
If DESTRUCTOR is non-null, then it is called for each element
|
||||
in the hash. DESTRUCTOR may, if appropriate, deallocate the
|
||||
memory used by the hash element. However, modifying hash
|
||||
table H while hash_clear() is running, using any of the
|
||||
functions hash_clear(), hash_destroy(), hash_insert(),
|
||||
hash_replace(), or hash_delete(), yields undefined behavior,
|
||||
whether done in DESTRUCTOR or elsewhere. */
|
||||
void
|
||||
hash_clear (struct hash *h, hash_action_func *destructor)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < h->bucket_cnt; i++)
|
||||
{
|
||||
struct list *bucket = &h->buckets[i];
|
||||
|
||||
if (destructor != NULL)
|
||||
while (!list_empty (bucket))
|
||||
{
|
||||
struct list_elem *list_elem = list_pop_front (bucket);
|
||||
struct hash_elem *hash_elem = list_elem_to_hash_elem (list_elem);
|
||||
destructor (hash_elem, h->aux);
|
||||
}
|
||||
|
||||
list_init (bucket);
|
||||
}
|
||||
|
||||
h->elem_cnt = 0;
|
||||
}
|
||||
|
||||
/* Destroys hash table H.
|
||||
|
||||
If DESTRUCTOR is non-null, then it is first called for each
|
||||
element in the hash. DESTRUCTOR may, if appropriate,
|
||||
deallocate the memory used by the hash element. However,
|
||||
modifying hash table H while hash_clear() is running, using
|
||||
any of the functions hash_clear(), hash_destroy(),
|
||||
hash_insert(), hash_replace(), or hash_delete(), yields
|
||||
undefined behavior, whether done in DESTRUCTOR or
|
||||
elsewhere. */
|
||||
void
|
||||
hash_destroy (struct hash *h, hash_action_func *destructor)
|
||||
{
|
||||
if (destructor != NULL)
|
||||
hash_clear (h, destructor);
|
||||
free (h->buckets);
|
||||
}
|
||||
|
||||
/* Inserts NEW into hash table H and returns a null pointer, if
|
||||
no equal element is already in the table.
|
||||
If an equal element is already in the table, returns it
|
||||
without inserting NEW. */
|
||||
struct hash_elem *
|
||||
hash_insert (struct hash *h, struct hash_elem *new)
|
||||
{
|
||||
struct list *bucket = find_bucket (h, new);
|
||||
struct hash_elem *old = find_elem (h, bucket, new);
|
||||
|
||||
if (old == NULL)
|
||||
insert_elem (h, bucket, new);
|
||||
|
||||
rehash (h);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
/* Inserts NEW into hash table H, replacing any equal element
|
||||
already in the table, which is returned. */
|
||||
struct hash_elem *
|
||||
hash_replace (struct hash *h, struct hash_elem *new)
|
||||
{
|
||||
struct list *bucket = find_bucket (h, new);
|
||||
struct hash_elem *old = find_elem (h, bucket, new);
|
||||
|
||||
if (old != NULL)
|
||||
remove_elem (h, old);
|
||||
insert_elem (h, bucket, new);
|
||||
|
||||
rehash (h);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
/* Finds and returns an element equal to E in hash table H, or a
|
||||
null pointer if no equal element exists in the table. */
|
||||
struct hash_elem *
|
||||
hash_find (struct hash *h, struct hash_elem *e)
|
||||
{
|
||||
return find_elem (h, find_bucket (h, e), e);
|
||||
}
|
||||
|
||||
/* Finds, removes, and returns an element equal to E in hash
|
||||
table H. Returns a null pointer if no equal element existed
|
||||
in the table.
|
||||
|
||||
If the elements of the hash table are dynamically allocated,
|
||||
or own resources that are, then it is the caller's
|
||||
responsibility to deallocate them. */
|
||||
struct hash_elem *
|
||||
hash_delete (struct hash *h, struct hash_elem *e)
|
||||
{
|
||||
struct hash_elem *found = find_elem (h, find_bucket (h, e), e);
|
||||
if (found != NULL)
|
||||
{
|
||||
remove_elem (h, found);
|
||||
rehash (h);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/* Calls ACTION for each element in hash table H in arbitrary
|
||||
order.
|
||||
Modifying hash table H while hash_apply() is running, using
|
||||
any of the functions hash_clear(), hash_destroy(),
|
||||
hash_insert(), hash_replace(), or hash_delete(), yields
|
||||
undefined behavior, whether done from ACTION or elsewhere. */
|
||||
void
|
||||
hash_apply (struct hash *h, hash_action_func *action)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
ASSERT (action != NULL);
|
||||
|
||||
for (i = 0; i < h->bucket_cnt; i++)
|
||||
{
|
||||
struct list *bucket = &h->buckets[i];
|
||||
struct list_elem *elem, *next;
|
||||
|
||||
for (elem = list_begin (bucket); elem != list_end (bucket); elem = next)
|
||||
{
|
||||
next = list_next (elem);
|
||||
action (list_elem_to_hash_elem (elem), h->aux);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Initializes I for iterating hash table H.
|
||||
|
||||
Iteration idiom:
|
||||
|
||||
struct hash_iterator i;
|
||||
|
||||
hash_first (&i, h);
|
||||
while (hash_next (&i))
|
||||
{
|
||||
struct foo *f = hash_entry (hash_cur (&i), struct foo, elem);
|
||||
...do something with f...
|
||||
}
|
||||
|
||||
Modifying hash table H during iteration, using any of the
|
||||
functions hash_clear(), hash_destroy(), hash_insert(),
|
||||
hash_replace(), or hash_delete(), invalidates all
|
||||
iterators. */
|
||||
void
|
||||
hash_first (struct hash_iterator *i, struct hash *h)
|
||||
{
|
||||
ASSERT (i != NULL);
|
||||
ASSERT (h != NULL);
|
||||
|
||||
i->hash = h;
|
||||
i->bucket = i->hash->buckets;
|
||||
i->elem = list_elem_to_hash_elem (list_head (i->bucket));
|
||||
}
|
||||
|
||||
/* Advances I to the next element in the hash table and returns
|
||||
it. Returns a null pointer if no elements are left. Elements
|
||||
are returned in arbitrary order.
|
||||
|
||||
Modifying a hash table H during iteration, using any of the
|
||||
functions hash_clear(), hash_destroy(), hash_insert(),
|
||||
hash_replace(), or hash_delete(), invalidates all
|
||||
iterators. */
|
||||
struct hash_elem *
|
||||
hash_next (struct hash_iterator *i)
|
||||
{
|
||||
ASSERT (i != NULL);
|
||||
|
||||
i->elem = list_elem_to_hash_elem (list_next (&i->elem->list_elem));
|
||||
while (i->elem == list_elem_to_hash_elem (list_end (i->bucket)))
|
||||
{
|
||||
if (++i->bucket >= i->hash->buckets + i->hash->bucket_cnt)
|
||||
{
|
||||
i->elem = NULL;
|
||||
break;
|
||||
}
|
||||
i->elem = list_elem_to_hash_elem (list_begin (i->bucket));
|
||||
}
|
||||
|
||||
return i->elem;
|
||||
}
|
||||
|
||||
/* Returns the current element in the hash table iteration, or a
|
||||
null pointer at the end of the table. Undefined behavior
|
||||
after calling hash_first() but before hash_next(). */
|
||||
struct hash_elem *
|
||||
hash_cur (struct hash_iterator *i)
|
||||
{
|
||||
return i->elem;
|
||||
}
|
||||
|
||||
/* Returns the number of elements in H. */
|
||||
size_t
|
||||
hash_size (struct hash *h)
|
||||
{
|
||||
return h->elem_cnt;
|
||||
}
|
||||
|
||||
/* Returns true if H contains no elements, false otherwise. */
|
||||
bool
|
||||
hash_empty (struct hash *h)
|
||||
{
|
||||
return h->elem_cnt == 0;
|
||||
}
|
||||
|
||||
/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */
|
||||
#define FNV_32_PRIME 16777619u
|
||||
#define FNV_32_BASIS 2166136261u
|
||||
|
||||
/* Returns a hash of the SIZE bytes in BUF. */
|
||||
unsigned
|
||||
hash_bytes (const void *buf_, size_t size)
|
||||
{
|
||||
/* Fowler-Noll-Vo 32-bit hash, for bytes. */
|
||||
const unsigned char *buf = buf_;
|
||||
unsigned hash;
|
||||
|
||||
ASSERT (buf != NULL);
|
||||
|
||||
hash = FNV_32_BASIS;
|
||||
while (size-- > 0)
|
||||
hash = (hash * FNV_32_PRIME) ^ *buf++;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Returns a hash of string S. */
|
||||
unsigned
|
||||
hash_string (const char *s_)
|
||||
{
|
||||
const unsigned char *s = (const unsigned char *) s_;
|
||||
unsigned hash;
|
||||
|
||||
ASSERT (s != NULL);
|
||||
|
||||
hash = FNV_32_BASIS;
|
||||
while (*s != '\0')
|
||||
hash = (hash * FNV_32_PRIME) ^ *s++;
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
/* Returns a hash of integer I. */
|
||||
unsigned
|
||||
hash_int (int i)
|
||||
{
|
||||
return hash_bytes (&i, sizeof i);
|
||||
}
|
||||
|
||||
/* Returns the bucket in H that E belongs in. */
|
||||
static struct list *
|
||||
find_bucket (struct hash *h, struct hash_elem *e)
|
||||
{
|
||||
size_t bucket_idx = h->hash (e, h->aux) & (h->bucket_cnt - 1);
|
||||
return &h->buckets[bucket_idx];
|
||||
}
|
||||
|
||||
/* Searches BUCKET in H for a hash element equal to E. Returns
|
||||
it if found or a null pointer otherwise. */
|
||||
static struct hash_elem *
|
||||
find_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
|
||||
{
|
||||
struct list_elem *i;
|
||||
|
||||
for (i = list_begin (bucket); i != list_end (bucket); i = list_next (i))
|
||||
{
|
||||
struct hash_elem *hi = list_elem_to_hash_elem (i);
|
||||
if (!h->less (hi, e, h->aux) && !h->less (e, hi, h->aux))
|
||||
return hi;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Returns X with its lowest-order bit set to 1 turned off. */
|
||||
static inline size_t
|
||||
turn_off_least_1bit (size_t x)
|
||||
{
|
||||
return x & (x - 1);
|
||||
}
|
||||
|
||||
/* Returns true if X is a power of 2, otherwise false. */
|
||||
static inline size_t
|
||||
is_power_of_2 (size_t x)
|
||||
{
|
||||
return x != 0 && turn_off_least_1bit (x) == 0;
|
||||
}
|
||||
|
||||
/* Element per bucket ratios. */
|
||||
#define MIN_ELEMS_PER_BUCKET 1 /* Elems/bucket < 1: reduce # of buckets. */
|
||||
#define BEST_ELEMS_PER_BUCKET 2 /* Ideal elems/bucket. */
|
||||
#define MAX_ELEMS_PER_BUCKET 4 /* Elems/bucket > 4: increase # of buckets. */
|
||||
|
||||
/* Changes the number of buckets in hash table H to match the
|
||||
ideal. This function can fail because of an out-of-memory
|
||||
condition, but that'll just make hash accesses less efficient;
|
||||
we can still continue. */
|
||||
static void
|
||||
rehash (struct hash *h)
|
||||
{
|
||||
size_t old_bucket_cnt, new_bucket_cnt;
|
||||
struct list *new_buckets, *old_buckets;
|
||||
size_t i;
|
||||
|
||||
ASSERT (h != NULL);
|
||||
|
||||
/* Save old bucket info for later use. */
|
||||
old_buckets = h->buckets;
|
||||
old_bucket_cnt = h->bucket_cnt;
|
||||
|
||||
/* Calculate the number of buckets to use now.
|
||||
We want one bucket for about every BEST_ELEMS_PER_BUCKET.
|
||||
We must have at least four buckets, and the number of
|
||||
buckets must be a power of 2. */
|
||||
new_bucket_cnt = h->elem_cnt / BEST_ELEMS_PER_BUCKET;
|
||||
if (new_bucket_cnt < 4)
|
||||
new_bucket_cnt = 4;
|
||||
while (!is_power_of_2 (new_bucket_cnt))
|
||||
new_bucket_cnt = turn_off_least_1bit (new_bucket_cnt);
|
||||
|
||||
/* Don't do anything if the bucket count wouldn't change. */
|
||||
if (new_bucket_cnt == old_bucket_cnt)
|
||||
return;
|
||||
|
||||
/* Allocate new buckets and initialize them as empty. */
|
||||
new_buckets = malloc (sizeof *new_buckets * new_bucket_cnt);
|
||||
if (new_buckets == NULL)
|
||||
{
|
||||
/* Allocation failed. This means that use of the hash table will
|
||||
be less efficient. However, it is still usable, so
|
||||
there's no reason for it to be an error. */
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < new_bucket_cnt; i++)
|
||||
list_init (&new_buckets[i]);
|
||||
|
||||
/* Install new bucket info. */
|
||||
h->buckets = new_buckets;
|
||||
h->bucket_cnt = new_bucket_cnt;
|
||||
|
||||
/* Move each old element into the appropriate new bucket. */
|
||||
for (i = 0; i < old_bucket_cnt; i++)
|
||||
{
|
||||
struct list *old_bucket;
|
||||
struct list_elem *elem, *next;
|
||||
|
||||
old_bucket = &old_buckets[i];
|
||||
for (elem = list_begin (old_bucket);
|
||||
elem != list_end (old_bucket); elem = next)
|
||||
{
|
||||
struct list *new_bucket
|
||||
= find_bucket (h, list_elem_to_hash_elem (elem));
|
||||
next = list_next (elem);
|
||||
list_remove (elem);
|
||||
list_push_front (new_bucket, elem);
|
||||
}
|
||||
}
|
||||
|
||||
free (old_buckets);
|
||||
}
|
||||
|
||||
/* Inserts E into BUCKET (in hash table H). */
|
||||
static void
|
||||
insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
|
||||
{
|
||||
h->elem_cnt++;
|
||||
list_push_front (bucket, &e->list_elem);
|
||||
}
|
||||
|
||||
/* Removes E from hash table H. */
|
||||
static void
|
||||
remove_elem (struct hash *h, struct hash_elem *e)
|
||||
{
|
||||
h->elem_cnt--;
|
||||
list_remove (&e->list_elem);
|
||||
}
|
||||
|
103
pintos-env/pintos/lib/kernel/hash.h
Executable file
103
pintos-env/pintos/lib/kernel/hash.h
Executable file
|
@ -0,0 +1,103 @@
|
|||
#ifndef __LIB_KERNEL_HASH_H
|
||||
#define __LIB_KERNEL_HASH_H
|
||||
|
||||
/* Hash table.
|
||||
|
||||
This data structure is thoroughly documented in the Tour of
|
||||
Pintos for Project 3.
|
||||
|
||||
This is a standard hash table with chaining. To locate an
|
||||
element in the table, we compute a hash function over the
|
||||
element's data and use that as an index into an array of
|
||||
doubly linked lists, then linearly search the list.
|
||||
|
||||
The chain lists do not use dynamic allocation. Instead, each
|
||||
structure that can potentially be in a hash must embed a
|
||||
struct hash_elem member. All of the hash functions operate on
|
||||
these `struct hash_elem's. The hash_entry macro allows
|
||||
conversion from a struct hash_elem back to a structure object
|
||||
that contains it. This is the same technique used in the
|
||||
linked list implementation. Refer to lib/kernel/list.h for a
|
||||
detailed explanation. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "list.h"
|
||||
|
||||
/* Hash element. */
|
||||
struct hash_elem
|
||||
{
|
||||
struct list_elem list_elem;
|
||||
};
|
||||
|
||||
/* Converts pointer to hash element HASH_ELEM into a pointer to
|
||||
the structure that HASH_ELEM is embedded inside. Supply the
|
||||
name of the outer structure STRUCT and the member name MEMBER
|
||||
of the hash element. See the big comment at the top of the
|
||||
file for an example. */
|
||||
#define hash_entry(HASH_ELEM, STRUCT, MEMBER) \
|
||||
((STRUCT *) ((uint8_t *) &(HASH_ELEM)->list_elem \
|
||||
- offsetof (STRUCT, MEMBER.list_elem)))
|
||||
|
||||
/* Computes and returns the hash value for hash element E, given
|
||||
auxiliary data AUX. */
|
||||
typedef unsigned hash_hash_func (const struct hash_elem *e, void *aux);
|
||||
|
||||
/* Compares the value of two hash elements A and B, given
|
||||
auxiliary data AUX. Returns true if A is less than B, or
|
||||
false if A is greater than or equal to B. */
|
||||
typedef bool hash_less_func (const struct hash_elem *a,
|
||||
const struct hash_elem *b,
|
||||
void *aux);
|
||||
|
||||
/* Performs some operation on hash element E, given auxiliary
|
||||
data AUX. */
|
||||
typedef void hash_action_func (struct hash_elem *e, void *aux);
|
||||
|
||||
/* Hash table. */
|
||||
struct hash
|
||||
{
|
||||
size_t elem_cnt; /* Number of elements in table. */
|
||||
size_t bucket_cnt; /* Number of buckets, a power of 2. */
|
||||
struct list *buckets; /* Array of `bucket_cnt' lists. */
|
||||
hash_hash_func *hash; /* Hash function. */
|
||||
hash_less_func *less; /* Comparison function. */
|
||||
void *aux; /* Auxiliary data for `hash' and `less'. */
|
||||
};
|
||||
|
||||
/* A hash table iterator. */
|
||||
struct hash_iterator
|
||||
{
|
||||
struct hash *hash; /* The hash table. */
|
||||
struct list *bucket; /* Current bucket. */
|
||||
struct hash_elem *elem; /* Current hash element in current bucket. */
|
||||
};
|
||||
|
||||
/* Basic life cycle. */
|
||||
bool hash_init (struct hash *, hash_hash_func *, hash_less_func *, void *aux);
|
||||
void hash_clear (struct hash *, hash_action_func *);
|
||||
void hash_destroy (struct hash *, hash_action_func *);
|
||||
|
||||
/* Search, insertion, deletion. */
|
||||
struct hash_elem *hash_insert (struct hash *, struct hash_elem *);
|
||||
struct hash_elem *hash_replace (struct hash *, struct hash_elem *);
|
||||
struct hash_elem *hash_find (struct hash *, struct hash_elem *);
|
||||
struct hash_elem *hash_delete (struct hash *, struct hash_elem *);
|
||||
|
||||
/* Iteration. */
|
||||
void hash_apply (struct hash *, hash_action_func *);
|
||||
void hash_first (struct hash_iterator *, struct hash *);
|
||||
struct hash_elem *hash_next (struct hash_iterator *);
|
||||
struct hash_elem *hash_cur (struct hash_iterator *);
|
||||
|
||||
/* Information. */
|
||||
size_t hash_size (struct hash *);
|
||||
bool hash_empty (struct hash *);
|
||||
|
||||
/* Sample hash functions. */
|
||||
unsigned hash_bytes (const void *, size_t);
|
||||
unsigned hash_string (const char *);
|
||||
unsigned hash_int (int);
|
||||
|
||||
#endif /* lib/kernel/hash.h */
|
524
pintos-env/pintos/lib/kernel/list.c
Executable file
524
pintos-env/pintos/lib/kernel/list.c
Executable file
|
@ -0,0 +1,524 @@
|
|||
#include "list.h"
|
||||
#include "../debug.h"
|
||||
|
||||
/* Our doubly linked lists have two header elements: the "head"
|
||||
just before the first element and the "tail" just after the
|
||||
last element. The `prev' link of the front header is null, as
|
||||
is the `next' link of the back header. Their other two links
|
||||
point toward each other via the interior elements of the list.
|
||||
|
||||
An empty list looks like this:
|
||||
|
||||
+------+ +------+
|
||||
<---| head |<--->| tail |--->
|
||||
+------+ +------+
|
||||
|
||||
A list with two elements in it looks like this:
|
||||
|
||||
+------+ +-------+ +-------+ +------+
|
||||
<---| head |<--->| 1 |<--->| 2 |<--->| tail |<--->
|
||||
+------+ +-------+ +-------+ +------+
|
||||
|
||||
The symmetry of this arrangement eliminates lots of special
|
||||
cases in list processing. For example, take a look at
|
||||
list_remove(): it takes only two pointer assignments and no
|
||||
conditionals. That's a lot simpler than the code would be
|
||||
without header elements.
|
||||
|
||||
(Because only one of the pointers in each header element is used,
|
||||
we could in fact combine them into a single header element
|
||||
without sacrificing this simplicity. But using two separate
|
||||
elements allows us to do a little bit of checking on some
|
||||
operations, which can be valuable.) */
|
||||
|
||||
static bool is_sorted (struct list_elem *a, struct list_elem *b,
|
||||
list_less_func *less, void *aux) UNUSED;
|
||||
|
||||
/* Returns true if ELEM is a head, false otherwise. */
|
||||
static inline bool
|
||||
is_head (struct list_elem *elem)
|
||||
{
|
||||
return elem != NULL && elem->prev == NULL && elem->next != NULL;
|
||||
}
|
||||
|
||||
/* Returns true if ELEM is an interior element,
|
||||
false otherwise. */
|
||||
static inline bool
|
||||
is_interior (struct list_elem *elem)
|
||||
{
|
||||
return elem != NULL && elem->prev != NULL && elem->next != NULL;
|
||||
}
|
||||
|
||||
/* Returns true if ELEM is a tail, false otherwise. */
|
||||
static inline bool
|
||||
is_tail (struct list_elem *elem)
|
||||
{
|
||||
return elem != NULL && elem->prev != NULL && elem->next == NULL;
|
||||
}
|
||||
|
||||
/* Initializes LIST as an empty list. */
|
||||
void
|
||||
list_init (struct list *list)
|
||||
{
|
||||
ASSERT (list != NULL);
|
||||
list->head.prev = NULL;
|
||||
list->head.next = &list->tail;
|
||||
list->tail.prev = &list->head;
|
||||
list->tail.next = NULL;
|
||||
}
|
||||
|
||||
/* Returns the beginning of LIST. */
|
||||
struct list_elem *
|
||||
list_begin (struct list *list)
|
||||
{
|
||||
ASSERT (list != NULL);
|
||||
return list->head.next;
|
||||
}
|
||||
|
||||
/* Returns the element after ELEM in its list. If ELEM is the
|
||||
last element in its list, returns the list tail. Results are
|
||||
undefined if ELEM is itself a list tail. */
|
||||
struct list_elem *
|
||||
list_next (struct list_elem *elem)
|
||||
{
|
||||
ASSERT (is_head (elem) || is_interior (elem));
|
||||
return elem->next;
|
||||
}
|
||||
|
||||
/* Returns LIST's tail.
|
||||
|
||||
list_end() is often used in iterating through a list from
|
||||
front to back. See the big comment at the top of list.h for
|
||||
an example. */
|
||||
struct list_elem *
|
||||
list_end (struct list *list)
|
||||
{
|
||||
ASSERT (list != NULL);
|
||||
return &list->tail;
|
||||
}
|
||||
|
||||
/* Returns the LIST's reverse beginning, for iterating through
|
||||
LIST in reverse order, from back to front. */
|
||||
struct list_elem *
|
||||
list_rbegin (struct list *list)
|
||||
{
|
||||
ASSERT (list != NULL);
|
||||
return list->tail.prev;
|
||||
}
|
||||
|
||||
/* Returns the element before ELEM in its list. If ELEM is the
|
||||
first element in its list, returns the list head. Results are
|
||||
undefined if ELEM is itself a list head. */
|
||||
struct list_elem *
|
||||
list_prev (struct list_elem *elem)
|
||||
{
|
||||
ASSERT (is_interior (elem) || is_tail (elem));
|
||||
return elem->prev;
|
||||
}
|
||||
|
||||
/* Returns LIST's head.
|
||||
|
||||
list_rend() is often used in iterating through a list in
|
||||
reverse order, from back to front. Here's typical usage,
|
||||
following the example from the top of list.h:
|
||||
|
||||
for (e = list_rbegin (&foo_list); e != list_rend (&foo_list);
|
||||
e = list_prev (e))
|
||||
{
|
||||
struct foo *f = list_entry (e, struct foo, elem);
|
||||
...do something with f...
|
||||
}
|
||||
*/
|
||||
struct list_elem *
|
||||
list_rend (struct list *list)
|
||||
{
|
||||
ASSERT (list != NULL);
|
||||
return &list->head;
|
||||
}
|
||||
|
||||
/* Return's LIST's head.
|
||||
|
||||
list_head() can be used for an alternate style of iterating
|
||||
through a list, e.g.:
|
||||
|
||||
e = list_head (&list);
|
||||
while ((e = list_next (e)) != list_end (&list))
|
||||
{
|
||||
...
|
||||
}
|
||||
*/
|
||||
struct list_elem *
|
||||
list_head (struct list *list)
|
||||
{
|
||||
ASSERT (list != NULL);
|
||||
return &list->head;
|
||||
}
|
||||
|
||||
/* Return's LIST's tail. */
|
||||
struct list_elem *
|
||||
list_tail (struct list *list)
|
||||
{
|
||||
ASSERT (list != NULL);
|
||||
return &list->tail;
|
||||
}
|
||||
|
||||
/* Inserts ELEM just before BEFORE, which may be either an
|
||||
interior element or a tail. The latter case is equivalent to
|
||||
list_push_back(). */
|
||||
void
|
||||
list_insert (struct list_elem *before, struct list_elem *elem)
|
||||
{
|
||||
ASSERT (is_interior (before) || is_tail (before));
|
||||
ASSERT (elem != NULL);
|
||||
|
||||
elem->prev = before->prev;
|
||||
elem->next = before;
|
||||
before->prev->next = elem;
|
||||
before->prev = elem;
|
||||
}
|
||||
|
||||
/* Removes elements FIRST though LAST (exclusive) from their
|
||||
current list, then inserts them just before BEFORE, which may
|
||||
be either an interior element or a tail. */
|
||||
void
|
||||
list_splice (struct list_elem *before,
|
||||
struct list_elem *first, struct list_elem *last)
|
||||
{
|
||||
ASSERT (is_interior (before) || is_tail (before));
|
||||
if (first == last)
|
||||
return;
|
||||
last = list_prev (last);
|
||||
|
||||
ASSERT (is_interior (first));
|
||||
ASSERT (is_interior (last));
|
||||
|
||||
/* Cleanly remove FIRST...LAST from its current list. */
|
||||
first->prev->next = last->next;
|
||||
last->next->prev = first->prev;
|
||||
|
||||
/* Splice FIRST...LAST into new list. */
|
||||
first->prev = before->prev;
|
||||
last->next = before;
|
||||
before->prev->next = first;
|
||||
before->prev = last;
|
||||
}
|
||||
|
||||
/* Inserts ELEM at the beginning of LIST, so that it becomes the
|
||||
front in LIST. */
|
||||
void
|
||||
list_push_front (struct list *list, struct list_elem *elem)
|
||||
{
|
||||
list_insert (list_begin (list), elem);
|
||||
}
|
||||
|
||||
/* Inserts ELEM at the end of LIST, so that it becomes the
|
||||
back in LIST. */
|
||||
void
|
||||
list_push_back (struct list *list, struct list_elem *elem)
|
||||
{
|
||||
list_insert (list_end (list), elem);
|
||||
}
|
||||
|
||||
/* Removes ELEM from its list and returns the element that
|
||||
followed it. Undefined behavior if ELEM is not in a list.
|
||||
|
||||
A list element must be treated very carefully after removing
|
||||
it from its list. Calling list_next() or list_prev() on ELEM
|
||||
will return the item that was previously before or after ELEM,
|
||||
but, e.g., list_prev(list_next(ELEM)) is no longer ELEM!
|
||||
|
||||
The list_remove() return value provides a convenient way to
|
||||
iterate and remove elements from a list:
|
||||
|
||||
for (e = list_begin (&list); e != list_end (&list); e = list_remove (e))
|
||||
{
|
||||
...do something with e...
|
||||
}
|
||||
|
||||
If you need to free() elements of the list then you need to be
|
||||
more conservative. Here's an alternate strategy that works
|
||||
even in that case:
|
||||
|
||||
while (!list_empty (&list))
|
||||
{
|
||||
struct list_elem *e = list_pop_front (&list);
|
||||
...do something with e...
|
||||
}
|
||||
*/
|
||||
struct list_elem *
|
||||
list_remove (struct list_elem *elem)
|
||||
{
|
||||
ASSERT (is_interior (elem));
|
||||
elem->prev->next = elem->next;
|
||||
elem->next->prev = elem->prev;
|
||||
return elem->next;
|
||||
}
|
||||
|
||||
/* Removes the front element from LIST and returns it.
|
||||
Undefined behavior if LIST is empty before removal. */
|
||||
struct list_elem *
|
||||
list_pop_front (struct list *list)
|
||||
{
|
||||
struct list_elem *front = list_front (list);
|
||||
list_remove (front);
|
||||
return front;
|
||||
}
|
||||
|
||||
/* Removes the back element from LIST and returns it.
|
||||
Undefined behavior if LIST is empty before removal. */
|
||||
struct list_elem *
|
||||
list_pop_back (struct list *list)
|
||||
{
|
||||
struct list_elem *back = list_back (list);
|
||||
list_remove (back);
|
||||
return back;
|
||||
}
|
||||
|
||||
/* Returns the front element in LIST.
|
||||
Undefined behavior if LIST is empty. */
|
||||
struct list_elem *
|
||||
list_front (struct list *list)
|
||||
{
|
||||
ASSERT (!list_empty (list));
|
||||
return list->head.next;
|
||||
}
|
||||
|
||||
/* Returns the back element in LIST.
|
||||
Undefined behavior if LIST is empty. */
|
||||
struct list_elem *
|
||||
list_back (struct list *list)
|
||||
{
|
||||
ASSERT (!list_empty (list));
|
||||
return list->tail.prev;
|
||||
}
|
||||
|
||||
/* Returns the number of elements in LIST.
|
||||
Runs in O(n) in the number of elements. */
|
||||
size_t
|
||||
list_size (struct list *list)
|
||||
{
|
||||
struct list_elem *e;
|
||||
size_t cnt = 0;
|
||||
|
||||
for (e = list_begin (list); e != list_end (list); e = list_next (e))
|
||||
cnt++;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
/* Returns true if LIST is empty, false otherwise. */
|
||||
bool
|
||||
list_empty (struct list *list)
|
||||
{
|
||||
return list_begin (list) == list_end (list);
|
||||
}
|
||||
|
||||
/* Swaps the `struct list_elem *'s that A and B point to. */
|
||||
static void
|
||||
swap (struct list_elem **a, struct list_elem **b)
|
||||
{
|
||||
struct list_elem *t = *a;
|
||||
*a = *b;
|
||||
*b = t;
|
||||
}
|
||||
|
||||
/* Reverses the order of LIST. */
|
||||
void
|
||||
list_reverse (struct list *list)
|
||||
{
|
||||
if (!list_empty (list))
|
||||
{
|
||||
struct list_elem *e;
|
||||
|
||||
for (e = list_begin (list); e != list_end (list); e = e->prev)
|
||||
swap (&e->prev, &e->next);
|
||||
swap (&list->head.next, &list->tail.prev);
|
||||
swap (&list->head.next->prev, &list->tail.prev->next);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true only if the list elements A through B (exclusive)
|
||||
are in order according to LESS given auxiliary data AUX. */
|
||||
static bool
|
||||
is_sorted (struct list_elem *a, struct list_elem *b,
|
||||
list_less_func *less, void *aux)
|
||||
{
|
||||
if (a != b)
|
||||
while ((a = list_next (a)) != b)
|
||||
if (less (a, list_prev (a), aux))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Finds a run, starting at A and ending not after B, of list
|
||||
elements that are in nondecreasing order according to LESS
|
||||
given auxiliary data AUX. Returns the (exclusive) end of the
|
||||
run.
|
||||
A through B (exclusive) must form a non-empty range. */
|
||||
static struct list_elem *
|
||||
find_end_of_run (struct list_elem *a, struct list_elem *b,
|
||||
list_less_func *less, void *aux)
|
||||
{
|
||||
ASSERT (a != NULL);
|
||||
ASSERT (b != NULL);
|
||||
ASSERT (less != NULL);
|
||||
ASSERT (a != b);
|
||||
|
||||
do
|
||||
{
|
||||
a = list_next (a);
|
||||
}
|
||||
while (a != b && !less (a, list_prev (a), aux));
|
||||
return a;
|
||||
}
|
||||
|
||||
/* Merges A0 through A1B0 (exclusive) with A1B0 through B1
|
||||
(exclusive) to form a combined range also ending at B1
|
||||
(exclusive). Both input ranges must be nonempty and sorted in
|
||||
nondecreasing order according to LESS given auxiliary data
|
||||
AUX. The output range will be sorted the same way. */
|
||||
static void
|
||||
inplace_merge (struct list_elem *a0, struct list_elem *a1b0,
|
||||
struct list_elem *b1,
|
||||
list_less_func *less, void *aux)
|
||||
{
|
||||
ASSERT (a0 != NULL);
|
||||
ASSERT (a1b0 != NULL);
|
||||
ASSERT (b1 != NULL);
|
||||
ASSERT (less != NULL);
|
||||
ASSERT (is_sorted (a0, a1b0, less, aux));
|
||||
ASSERT (is_sorted (a1b0, b1, less, aux));
|
||||
|
||||
while (a0 != a1b0 && a1b0 != b1)
|
||||
if (!less (a1b0, a0, aux))
|
||||
a0 = list_next (a0);
|
||||
else
|
||||
{
|
||||
a1b0 = list_next (a1b0);
|
||||
list_splice (a0, list_prev (a1b0), a1b0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorts LIST according to LESS given auxiliary data AUX, using a
|
||||
natural iterative merge sort that runs in O(n lg n) time and
|
||||
O(1) space in the number of elements in LIST. */
|
||||
void
|
||||
list_sort (struct list *list, list_less_func *less, void *aux)
|
||||
{
|
||||
size_t output_run_cnt; /* Number of runs output in current pass. */
|
||||
|
||||
ASSERT (list != NULL);
|
||||
ASSERT (less != NULL);
|
||||
|
||||
/* Pass over the list repeatedly, merging adjacent runs of
|
||||
nondecreasing elements, until only one run is left. */
|
||||
do
|
||||
{
|
||||
struct list_elem *a0; /* Start of first run. */
|
||||
struct list_elem *a1b0; /* End of first run, start of second. */
|
||||
struct list_elem *b1; /* End of second run. */
|
||||
|
||||
output_run_cnt = 0;
|
||||
for (a0 = list_begin (list); a0 != list_end (list); a0 = b1)
|
||||
{
|
||||
/* Each iteration produces one output run. */
|
||||
output_run_cnt++;
|
||||
|
||||
/* Locate two adjacent runs of nondecreasing elements
|
||||
A0...A1B0 and A1B0...B1. */
|
||||
a1b0 = find_end_of_run (a0, list_end (list), less, aux);
|
||||
if (a1b0 == list_end (list))
|
||||
break;
|
||||
b1 = find_end_of_run (a1b0, list_end (list), less, aux);
|
||||
|
||||
/* Merge the runs. */
|
||||
inplace_merge (a0, a1b0, b1, less, aux);
|
||||
}
|
||||
}
|
||||
while (output_run_cnt > 1);
|
||||
|
||||
ASSERT (is_sorted (list_begin (list), list_end (list), less, aux));
|
||||
}
|
||||
|
||||
/* Inserts ELEM in the proper position in LIST, which must be
|
||||
sorted according to LESS given auxiliary data AUX.
|
||||
Runs in O(n) average case in the number of elements in LIST. */
|
||||
void
|
||||
list_insert_ordered (struct list *list, struct list_elem *elem,
|
||||
list_less_func *less, void *aux)
|
||||
{
|
||||
struct list_elem *e;
|
||||
|
||||
ASSERT (list != NULL);
|
||||
ASSERT (elem != NULL);
|
||||
ASSERT (less != NULL);
|
||||
|
||||
for (e = list_begin (list); e != list_end (list); e = list_next (e))
|
||||
if (less (elem, e, aux))
|
||||
break;
|
||||
return list_insert (e, elem);
|
||||
}
|
||||
|
||||
/* Iterates through LIST and removes all but the first in each
|
||||
set of adjacent elements that are equal according to LESS
|
||||
given auxiliary data AUX. If DUPLICATES is non-null, then the
|
||||
elements from LIST are appended to DUPLICATES. */
|
||||
void
|
||||
list_unique (struct list *list, struct list *duplicates,
|
||||
list_less_func *less, void *aux)
|
||||
{
|
||||
struct list_elem *elem, *next;
|
||||
|
||||
ASSERT (list != NULL);
|
||||
ASSERT (less != NULL);
|
||||
if (list_empty (list))
|
||||
return;
|
||||
|
||||
elem = list_begin (list);
|
||||
while ((next = list_next (elem)) != list_end (list))
|
||||
if (!less (elem, next, aux) && !less (next, elem, aux))
|
||||
{
|
||||
list_remove (next);
|
||||
if (duplicates != NULL)
|
||||
list_push_back (duplicates, next);
|
||||
}
|
||||
else
|
||||
elem = next;
|
||||
}
|
||||
|
||||
/* Returns the element in LIST with the largest value according
|
||||
to LESS given auxiliary data AUX. If there is more than one
|
||||
maximum, returns the one that appears earlier in the list. If
|
||||
the list is empty, returns its tail. */
|
||||
struct list_elem *
|
||||
list_max (struct list *list, list_less_func *less, void *aux)
|
||||
{
|
||||
struct list_elem *max = list_begin (list);
|
||||
if (max != list_end (list))
|
||||
{
|
||||
struct list_elem *e;
|
||||
|
||||
for (e = list_next (max); e != list_end (list); e = list_next (e))
|
||||
if (less (max, e, aux))
|
||||
max = e;
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
/* Returns the element in LIST with the smallest value according
|
||||
to LESS given auxiliary data AUX. If there is more than one
|
||||
minimum, returns the one that appears earlier in the list. If
|
||||
the list is empty, returns its tail. */
|
||||
struct list_elem *
|
||||
list_min (struct list *list, list_less_func *less, void *aux)
|
||||
{
|
||||
struct list_elem *min = list_begin (list);
|
||||
if (min != list_end (list))
|
||||
{
|
||||
struct list_elem *e;
|
||||
|
||||
for (e = list_next (min); e != list_end (list); e = list_next (e))
|
||||
if (less (e, min, aux))
|
||||
min = e;
|
||||
}
|
||||
return min;
|
||||
}
|
181
pintos-env/pintos/lib/kernel/list.h
Executable file
181
pintos-env/pintos/lib/kernel/list.h
Executable file
|
@ -0,0 +1,181 @@
|
|||
#ifndef __LIB_KERNEL_LIST_H
|
||||
#define __LIB_KERNEL_LIST_H
|
||||
|
||||
/* Doubly linked list.
|
||||
|
||||
This implementation of a doubly linked list does not require
|
||||
use of dynamically allocated memory. Instead, each structure
|
||||
that is a potential list element must embed a struct list_elem
|
||||
member. All of the list functions operate on these `struct
|
||||
list_elem's. The list_entry macro allows conversion from a
|
||||
struct list_elem back to a structure object that contains it.
|
||||
|
||||
For example, suppose there is a needed for a list of `struct
|
||||
foo'. `struct foo' should contain a `struct list_elem'
|
||||
member, like so:
|
||||
|
||||
struct foo
|
||||
{
|
||||
struct list_elem elem;
|
||||
int bar;
|
||||
...other members...
|
||||
};
|
||||
|
||||
Then a list of `struct foo' can be be declared and initialized
|
||||
like so:
|
||||
|
||||
struct list foo_list;
|
||||
|
||||
list_init (&foo_list);
|
||||
|
||||
Iteration is a typical situation where it is necessary to
|
||||
convert from a struct list_elem back to its enclosing
|
||||
structure. Here's an example using foo_list:
|
||||
|
||||
struct list_elem *e;
|
||||
|
||||
for (e = list_begin (&foo_list); e != list_end (&foo_list);
|
||||
e = list_next (e))
|
||||
{
|
||||
struct foo *f = list_entry (e, struct foo, elem);
|
||||
...do something with f...
|
||||
}
|
||||
|
||||
You can find real examples of list usage throughout the
|
||||
source; for example, malloc.c, palloc.c, and thread.c in the
|
||||
threads directory all use lists.
|
||||
|
||||
The interface for this list is inspired by the list<> template
|
||||
in the C++ STL. If you're familiar with list<>, you should
|
||||
find this easy to use. However, it should be emphasized that
|
||||
these lists do *no* type checking and can't do much other
|
||||
correctness checking. If you screw up, it will bite you.
|
||||
|
||||
Glossary of list terms:
|
||||
|
||||
- "front": The first element in a list. Undefined in an
|
||||
empty list. Returned by list_front().
|
||||
|
||||
- "back": The last element in a list. Undefined in an empty
|
||||
list. Returned by list_back().
|
||||
|
||||
- "tail": The element figuratively just after the last
|
||||
element of a list. Well defined even in an empty list.
|
||||
Returned by list_end(). Used as the end sentinel for an
|
||||
iteration from front to back.
|
||||
|
||||
- "beginning": In a non-empty list, the front. In an empty
|
||||
list, the tail. Returned by list_begin(). Used as the
|
||||
starting point for an iteration from front to back.
|
||||
|
||||
- "head": The element figuratively just before the first
|
||||
element of a list. Well defined even in an empty list.
|
||||
Returned by list_rend(). Used as the end sentinel for an
|
||||
iteration from back to front.
|
||||
|
||||
- "reverse beginning": In a non-empty list, the back. In an
|
||||
empty list, the head. Returned by list_rbegin(). Used as
|
||||
the starting point for an iteration from back to front.
|
||||
|
||||
- "interior element": An element that is not the head or
|
||||
tail, that is, a real list element. An empty list does
|
||||
not have any interior elements.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* List element. */
|
||||
struct list_elem
|
||||
{
|
||||
struct list_elem *prev; /* Previous list element. */
|
||||
struct list_elem *next; /* Next list element. */
|
||||
};
|
||||
|
||||
/* List. */
|
||||
struct list
|
||||
{
|
||||
struct list_elem head; /* List head. */
|
||||
struct list_elem tail; /* List tail. */
|
||||
};
|
||||
|
||||
/* Converts pointer to list element LIST_ELEM into a pointer to
|
||||
the structure that LIST_ELEM is embedded inside. Supply the
|
||||
name of the outer structure STRUCT and the member name MEMBER
|
||||
of the list element. See the big comment at the top of the
|
||||
file for an example. */
|
||||
#define list_entry(LIST_ELEM, STRUCT, MEMBER) \
|
||||
((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \
|
||||
- offsetof (STRUCT, MEMBER.next)))
|
||||
|
||||
/* List initialization.
|
||||
|
||||
A list may be initialized by calling list_init():
|
||||
|
||||
struct list my_list;
|
||||
list_init (&my_list);
|
||||
|
||||
or with an initializer using LIST_INITIALIZER:
|
||||
|
||||
struct list my_list = LIST_INITIALIZER (my_list); */
|
||||
#define LIST_INITIALIZER(NAME) { { NULL, &(NAME).tail }, \
|
||||
{ &(NAME).head, NULL } }
|
||||
|
||||
void list_init (struct list *);
|
||||
|
||||
/* List traversal. */
|
||||
struct list_elem *list_begin (struct list *);
|
||||
struct list_elem *list_next (struct list_elem *);
|
||||
struct list_elem *list_end (struct list *);
|
||||
|
||||
struct list_elem *list_rbegin (struct list *);
|
||||
struct list_elem *list_prev (struct list_elem *);
|
||||
struct list_elem *list_rend (struct list *);
|
||||
|
||||
struct list_elem *list_head (struct list *);
|
||||
struct list_elem *list_tail (struct list *);
|
||||
|
||||
/* List insertion. */
|
||||
void list_insert (struct list_elem *, struct list_elem *);
|
||||
void list_splice (struct list_elem *before,
|
||||
struct list_elem *first, struct list_elem *last);
|
||||
void list_push_front (struct list *, struct list_elem *);
|
||||
void list_push_back (struct list *, struct list_elem *);
|
||||
|
||||
/* List removal. */
|
||||
struct list_elem *list_remove (struct list_elem *);
|
||||
struct list_elem *list_pop_front (struct list *);
|
||||
struct list_elem *list_pop_back (struct list *);
|
||||
|
||||
/* List elements. */
|
||||
struct list_elem *list_front (struct list *);
|
||||
struct list_elem *list_back (struct list *);
|
||||
|
||||
/* List properties. */
|
||||
size_t list_size (struct list *);
|
||||
bool list_empty (struct list *);
|
||||
|
||||
/* Miscellaneous. */
|
||||
void list_reverse (struct list *);
|
||||
|
||||
/* Compares the value of two list elements A and B, given
|
||||
auxiliary data AUX. Returns true if A is less than B, or
|
||||
false if A is greater than or equal to B. */
|
||||
typedef bool list_less_func (const struct list_elem *a,
|
||||
const struct list_elem *b,
|
||||
void *aux);
|
||||
|
||||
/* Operations on lists with ordered elements. */
|
||||
void list_sort (struct list *,
|
||||
list_less_func *, void *aux);
|
||||
void list_insert_ordered (struct list *, struct list_elem *,
|
||||
list_less_func *, void *aux);
|
||||
void list_unique (struct list *, struct list *duplicates,
|
||||
list_less_func *, void *aux);
|
||||
|
||||
/* Max and min. */
|
||||
struct list_elem *list_max (struct list *, list_less_func *, void *aux);
|
||||
struct list_elem *list_min (struct list *, list_less_func *, void *aux);
|
||||
|
||||
#endif /* lib/kernel/list.h */
|
6
pintos-env/pintos/lib/kernel/stdio.h
Executable file
6
pintos-env/pintos/lib/kernel/stdio.h
Executable file
|
@ -0,0 +1,6 @@
|
|||
#ifndef __LIB_KERNEL_STDIO_H
|
||||
#define __LIB_KERNEL_STDIO_H
|
||||
|
||||
void putbuf (const char *, size_t);
|
||||
|
||||
#endif /* lib/kernel/stdio.h */
|
34
pintos-env/pintos/lib/limits.h
Executable file
34
pintos-env/pintos/lib/limits.h
Executable file
|
@ -0,0 +1,34 @@
|
|||
#ifndef __LIB_LIMITS_H
|
||||
#define __LIB_LIMITS_H
|
||||
|
||||
#define CHAR_BIT 8
|
||||
|
||||
#define SCHAR_MAX 127
|
||||
#define SCHAR_MIN (-SCHAR_MAX - 1)
|
||||
#define UCHAR_MAX 255
|
||||
|
||||
#ifdef __CHAR_UNSIGNED__
|
||||
#define CHAR_MIN 0
|
||||
#define CHAR_MAX UCHAR_MAX
|
||||
#else
|
||||
#define CHAR_MIN SCHAR_MIN
|
||||
#define CHAR_MAX SCHAR_MAX
|
||||
#endif
|
||||
|
||||
#define SHRT_MAX 32767
|
||||
#define SHRT_MIN (-SHRT_MAX - 1)
|
||||
#define USHRT_MAX 65535
|
||||
|
||||
#define INT_MAX 2147483647
|
||||
#define INT_MIN (-INT_MAX - 1)
|
||||
#define UINT_MAX 4294967295U
|
||||
|
||||
#define LONG_MAX 2147483647L
|
||||
#define LONG_MIN (-LONG_MAX - 1)
|
||||
#define ULONG_MAX 4294967295UL
|
||||
|
||||
#define LLONG_MAX 9223372036854775807LL
|
||||
#define LLONG_MIN (-LLONG_MAX - 1)
|
||||
#define ULLONG_MAX 18446744073709551615ULL
|
||||
|
||||
#endif /* lib/limits.h */
|
10
pintos-env/pintos/lib/packed.h
Executable file
10
pintos-env/pintos/lib/packed.h
Executable file
|
@ -0,0 +1,10 @@
|
|||
#ifndef __LIB_PACKED_H
|
||||
#define __LIB_PACKED_H
|
||||
|
||||
/* The "packed" attribute, when applied to a structure, prevents
|
||||
GCC from inserting padding bytes between or after structure
|
||||
members. It must be specified at the time of the structure's
|
||||
definition, normally just after the closing brace. */
|
||||
#define PACKED __attribute__ ((packed))
|
||||
|
||||
#endif /* lib/packed.h */
|
83
pintos-env/pintos/lib/random.c
Executable file
83
pintos-env/pintos/lib/random.c
Executable file
|
@ -0,0 +1,83 @@
|
|||
#include "random.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "debug.h"
|
||||
|
||||
/* RC4-based pseudo-random number generator (PRNG).
|
||||
|
||||
RC4 is a stream cipher. We're not using it here for its
|
||||
cryptographic properties, but because it is easy to implement
|
||||
and its output is plenty random for non-cryptographic
|
||||
purposes.
|
||||
|
||||
See http://en.wikipedia.org/wiki/RC4_(cipher) for information
|
||||
on RC4.*/
|
||||
|
||||
/* RC4 state. */
|
||||
static uint8_t s[256]; /* S[]. */
|
||||
static uint8_t s_i, s_j; /* i, j. */
|
||||
|
||||
/* Already initialized? */
|
||||
static bool inited;
|
||||
|
||||
/* Swaps the bytes pointed to by A and B. */
|
||||
static inline void
|
||||
swap_byte (uint8_t *a, uint8_t *b)
|
||||
{
|
||||
uint8_t t = *a;
|
||||
*a = *b;
|
||||
*b = t;
|
||||
}
|
||||
|
||||
/* Initializes or reinitializes the PRNG with the given SEED. */
|
||||
void
|
||||
random_init (unsigned seed)
|
||||
{
|
||||
uint8_t *seedp = (uint8_t *) &seed;
|
||||
int i;
|
||||
uint8_t j;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
s[i] = i;
|
||||
for (i = j = 0; i < 256; i++)
|
||||
{
|
||||
j += s[i] + seedp[i % sizeof seed];
|
||||
swap_byte (s + i, s + j);
|
||||
}
|
||||
|
||||
s_i = s_j = 0;
|
||||
inited = true;
|
||||
}
|
||||
|
||||
/* Writes SIZE random bytes into BUF. */
|
||||
void
|
||||
random_bytes (void *buf_, size_t size)
|
||||
{
|
||||
uint8_t *buf;
|
||||
|
||||
if (!inited)
|
||||
random_init (0);
|
||||
|
||||
for (buf = buf_; size-- > 0; buf++)
|
||||
{
|
||||
uint8_t s_k;
|
||||
|
||||
s_i++;
|
||||
s_j += s[s_i];
|
||||
swap_byte (s + s_i, s + s_j);
|
||||
|
||||
s_k = s[s_i] + s[s_j];
|
||||
*buf = s[s_k];
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a pseudo-random unsigned long.
|
||||
Use random_ulong() % n to obtain a random number in the range
|
||||
0...n (exclusive). */
|
||||
unsigned long
|
||||
random_ulong (void)
|
||||
{
|
||||
unsigned long ul;
|
||||
random_bytes (&ul, sizeof ul);
|
||||
return ul;
|
||||
}
|
10
pintos-env/pintos/lib/random.h
Executable file
10
pintos-env/pintos/lib/random.h
Executable file
|
@ -0,0 +1,10 @@
|
|||
#ifndef __LIB_RANDOM_H
|
||||
#define __LIB_RANDOM_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
void random_init (unsigned seed);
|
||||
void random_bytes (void *, size_t);
|
||||
unsigned long random_ulong (void);
|
||||
|
||||
#endif /* lib/random.h */
|
18
pintos-env/pintos/lib/round.h
Executable file
18
pintos-env/pintos/lib/round.h
Executable file
|
@ -0,0 +1,18 @@
|
|||
#ifndef __LIB_ROUND_H
|
||||
#define __LIB_ROUND_H
|
||||
|
||||
/* Yields X rounded up to the nearest multiple of STEP.
|
||||
For X >= 0, STEP >= 1 only. */
|
||||
#define ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP) * (STEP))
|
||||
|
||||
/* Yields X divided by STEP, rounded up.
|
||||
For X >= 0, STEP >= 1 only. */
|
||||
#define DIV_ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP))
|
||||
|
||||
/* Yields X rounded down to the nearest multiple of STEP.
|
||||
For X >= 0, STEP >= 1 only. */
|
||||
#define ROUND_DOWN(X, STEP) ((X) / (STEP) * (STEP))
|
||||
|
||||
/* There is no DIV_ROUND_DOWN. It would be simply X / STEP. */
|
||||
|
||||
#endif /* lib/round.h */
|
14
pintos-env/pintos/lib/stdarg.h
Executable file
14
pintos-env/pintos/lib/stdarg.h
Executable file
|
@ -0,0 +1,14 @@
|
|||
#ifndef __LIB_STDARG_H
|
||||
#define __LIB_STDARG_H
|
||||
|
||||
/* GCC has <stdarg.h> functionality as built-ins,
|
||||
so all we need is to use it. */
|
||||
|
||||
typedef __builtin_va_list va_list;
|
||||
|
||||
#define va_start(LIST, ARG) __builtin_va_start (LIST, ARG)
|
||||
#define va_end(LIST) __builtin_va_end (LIST)
|
||||
#define va_arg(LIST, TYPE) __builtin_va_arg (LIST, TYPE)
|
||||
#define va_copy(DST, SRC) __builtin_va_copy (DST, SRC)
|
||||
|
||||
#endif /* lib/stdarg.h */
|
9
pintos-env/pintos/lib/stdbool.h
Executable file
9
pintos-env/pintos/lib/stdbool.h
Executable file
|
@ -0,0 +1,9 @@
|
|||
#ifndef __LIB_STDBOOL_H
|
||||
#define __LIB_STDBOOL_H
|
||||
|
||||
#define bool _Bool
|
||||
#define true 1
|
||||
#define false 0
|
||||
#define __bool_true_false_are_defined 1
|
||||
|
||||
#endif /* lib/stdbool.h */
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue