Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


added guest vm
Jack Lange [Wed, 13 Feb 2008 21:37:35 +0000 (21:37 +0000)]
97 files changed:
misc/test_vm/COPYING.geekos [new file with mode: 0644]
misc/test_vm/LICENSE-klibc.geekos [new file with mode: 0644]
misc/test_vm/README [new file with mode: 0644]
misc/test_vm/build/.bochsrc [new file with mode: 0644]
misc/test_vm/build/Makefile [new file with mode: 0644]
misc/test_vm/build/depend.mak [new file with mode: 0644]
misc/test_vm/build/noboot.img [new file with mode: 0644]
misc/test_vm/include/geekos/argblock.h [new file with mode: 0644]
misc/test_vm/include/geekos/bget.h [new file with mode: 0644]
misc/test_vm/include/geekos/blockdev.h [new file with mode: 0644]
misc/test_vm/include/geekos/bootinfo.h [new file with mode: 0644]
misc/test_vm/include/geekos/cpu.h [new file with mode: 0644]
misc/test_vm/include/geekos/crc32.h [new file with mode: 0644]
misc/test_vm/include/geekos/defs.h [new file with mode: 0644]
misc/test_vm/include/geekos/errno.h [new file with mode: 0644]
misc/test_vm/include/geekos/fileio.h [new file with mode: 0644]
misc/test_vm/include/geekos/fmtout.h [new file with mode: 0644]
misc/test_vm/include/geekos/gdt.h [new file with mode: 0644]
misc/test_vm/include/geekos/ide.h [new file with mode: 0644]
misc/test_vm/include/geekos/idt.h [new file with mode: 0644]
misc/test_vm/include/geekos/int.h [new file with mode: 0644]
misc/test_vm/include/geekos/io.h [new file with mode: 0644]
misc/test_vm/include/geekos/io_devs.h [new file with mode: 0644]
misc/test_vm/include/geekos/irq.h [new file with mode: 0644]
misc/test_vm/include/geekos/kassert.h [new file with mode: 0644]
misc/test_vm/include/geekos/keyboard.h [new file with mode: 0644]
misc/test_vm/include/geekos/kthread.h [new file with mode: 0644]
misc/test_vm/include/geekos/ktypes.h [new file with mode: 0644]
misc/test_vm/include/geekos/list.h [new file with mode: 0644]
misc/test_vm/include/geekos/malloc.h [new file with mode: 0644]
misc/test_vm/include/geekos/mem.h [new file with mode: 0644]
misc/test_vm/include/geekos/mem_bak.h [new file with mode: 0644]
misc/test_vm/include/geekos/paging.h [new file with mode: 0644]
misc/test_vm/include/geekos/range.h [new file with mode: 0644]
misc/test_vm/include/geekos/reboot.h [new file with mode: 0644]
misc/test_vm/include/geekos/screen.h [new file with mode: 0644]
misc/test_vm/include/geekos/segment.h [new file with mode: 0644]
misc/test_vm/include/geekos/serial.h [new file with mode: 0644]
misc/test_vm/include/geekos/string.h [new file with mode: 0644]
misc/test_vm/include/geekos/symbol.h [new file with mode: 0644]
misc/test_vm/include/geekos/synch.h [new file with mode: 0644]
misc/test_vm/include/geekos/timer.h [new file with mode: 0644]
misc/test_vm/include/geekos/trap.h [new file with mode: 0644]
misc/test_vm/include/geekos/tss.h [new file with mode: 0644]
misc/test_vm/include/libc/fmtout.h [new file with mode: 0644]
misc/test_vm/include/libc/string.h [new file with mode: 0644]
misc/test_vm/scripts/eipToFunction [new file with mode: 0755]
misc/test_vm/scripts/findaddr [new file with mode: 0755]
misc/test_vm/scripts/generrs [new file with mode: 0755]
misc/test_vm/scripts/kerninfo [new file with mode: 0755]
misc/test_vm/scripts/mkcdisk [new file with mode: 0755]
misc/test_vm/scripts/mkuprog [new file with mode: 0755]
misc/test_vm/scripts/numsecs [new file with mode: 0755]
misc/test_vm/scripts/pad [new file with mode: 0755]
misc/test_vm/scripts/pcat [new file with mode: 0755]
misc/test_vm/scripts/pw [new file with mode: 0755]
misc/test_vm/scripts/scan [new file with mode: 0755]
misc/test_vm/scripts/zerofile [new file with mode: 0755]
misc/test_vm/src/common/fmtout.c [new file with mode: 0644]
misc/test_vm/src/common/memmove.c [new file with mode: 0644]
misc/test_vm/src/common/string.c [new file with mode: 0644]
misc/test_vm/src/geekos/README.txt [new file with mode: 0644]
misc/test_vm/src/geekos/bget.c [new file with mode: 0644]
misc/test_vm/src/geekos/blockdev.c [new file with mode: 0644]
misc/test_vm/src/geekos/bootsect.asm [new file with mode: 0644]
misc/test_vm/src/geekos/crc32.c [new file with mode: 0644]
misc/test_vm/src/geekos/defs.asm [new file with mode: 0644]
misc/test_vm/src/geekos/depend.mak [new file with mode: 0644]
misc/test_vm/src/geekos/fd_boot.asm [new file with mode: 0644]
misc/test_vm/src/geekos/fd_boot_bak.asm [new file with mode: 0644]
misc/test_vm/src/geekos/gdt.c [new file with mode: 0644]
misc/test_vm/src/geekos/ide.c [new file with mode: 0644]
misc/test_vm/src/geekos/idt.c [new file with mode: 0644]
misc/test_vm/src/geekos/int.c [new file with mode: 0644]
misc/test_vm/src/geekos/io.c [new file with mode: 0644]
misc/test_vm/src/geekos/irq.c [new file with mode: 0644]
misc/test_vm/src/geekos/keyboard.c [new file with mode: 0644]
misc/test_vm/src/geekos/kthread.c [new file with mode: 0644]
misc/test_vm/src/geekos/lowlevel.asm [new file with mode: 0644]
misc/test_vm/src/geekos/main.c [new file with mode: 0644]
misc/test_vm/src/geekos/malloc.c [new file with mode: 0644]
misc/test_vm/src/geekos/mem.c [new file with mode: 0644]
misc/test_vm/src/geekos/mem_bak.c [new file with mode: 0644]
misc/test_vm/src/geekos/paging.c [new file with mode: 0644]
misc/test_vm/src/geekos/reboot.c [new file with mode: 0644]
misc/test_vm/src/geekos/screen.c [new file with mode: 0644]
misc/test_vm/src/geekos/segment.c [new file with mode: 0644]
misc/test_vm/src/geekos/serial.c [new file with mode: 0644]
misc/test_vm/src/geekos/setup.asm [new file with mode: 0644]
misc/test_vm/src/geekos/symbol.asm [new file with mode: 0644]
misc/test_vm/src/geekos/synch.c [new file with mode: 0644]
misc/test_vm/src/geekos/testvm.s [new file with mode: 0644]
misc/test_vm/src/geekos/timer.c [new file with mode: 0644]
misc/test_vm/src/geekos/trap.c [new file with mode: 0644]
misc/test_vm/src/geekos/tss.c [new file with mode: 0644]
misc/test_vm/src/geekos/util.asm [new file with mode: 0644]
misc/test_vm/src/libc/compat.c [new file with mode: 0644]

diff --git a/misc/test_vm/COPYING.geekos b/misc/test_vm/COPYING.geekos
new file mode 100644 (file)
index 0000000..f07e852
--- /dev/null
@@ -0,0 +1,23 @@
+Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+
+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.
diff --git a/misc/test_vm/LICENSE-klibc.geekos b/misc/test_vm/LICENSE-klibc.geekos
new file mode 100644 (file)
index 0000000..b512ff9
--- /dev/null
@@ -0,0 +1,73 @@
+This license applies to all files in directory and its subdirectories,
+unless otherwise noted in individual files.
+
+
+Some files are derived from files derived from the include/ directory
+of the Linux kernel, and are licensed under the terms of the GNU
+General Public License, version 2, as released by the Free Software
+Foundation, Inc.; incorporated herein by reference.
+
+                               -----
+
+Some files are derived from files copyrighted by the Regents of The
+University of California, and are available under the following
+license:
+
+Note: The advertising clause in the license appearing on BSD Unix
+files was officially rescinded by the Director of the Office of
+Technology Licensing of the University of California on July 22
+1999. He states that clause 3 is "hereby deleted in its entirety."
+
+ * Copyright (c)
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+                               -----
+
+For all remaining files, the following license applies:
+
+ * 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:
+ * 
+ * Any copyright notice(s) 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.
diff --git a/misc/test_vm/README b/misc/test_vm/README
new file mode 100644 (file)
index 0000000..332be74
--- /dev/null
@@ -0,0 +1,48 @@
+This is is a VMM that uses VT-x on 32 bit machines.   
+
+It is based on GeekOS, VmxAssist, BOCHS BIOS/VGABIOS, and code
+for playing around with VT and VT-X in the GeekOS context
+
+Peter Dinda
+
+
+Here are some things you need to know:
+
+
+Serial port I/O is used to communicate with the VMM.  This means
+you need a null modem connection between your test machine and your
+dev machine.  
+
+To configure serial on your dev machine:
+
+(as root, or other user who has access to the serial port):
+  
+   stty -F /dev/ttyS0 115200   (assume 8 bit, no parity already set)
+   
+Then use kermit to communicate:
+
+   kermit -l /dev/ttyS0
+     set carrier-watch off
+     connect
+
+To access the ethernet power switch:
+
+  
+   ifconfig eth0:1 up 192.168.0.4 (this is your address)
+
+Now you should be able to connect to http://192.168.0.100
+
+
+To build from scratch:
+
+cd build
+make clean
+make 
+make generate_sizes
+make clean
+make pxe
+
+This will copy the final image into /tftpboot so that if you have PXE
+boot properly configured, the test machine will boot from it over the
+network.
+
diff --git a/misc/test_vm/build/.bochsrc b/misc/test_vm/build/.bochsrc
new file mode 100644 (file)
index 0000000..e95aa5a
--- /dev/null
@@ -0,0 +1,751 @@
+# You may now use double quotes around pathnames, in case
+# your pathname includes spaces.
+
+#=======================================================================
+# CONFIG_INTERFACE
+#
+# The configuration interface is a series of menus or dialog boxes that
+# allows you to change all the settings that control Bochs's behavior.
+# There are two choices of configuration interface: a text mode version
+# called "textconfig" and a graphical version called "wx".  The text
+# mode version uses stdin/stdout and is always compiled in.  The graphical
+# version is only available when you use "--with-wx" on the configure 
+# command.  If you do not write a config_interface line, Bochs will 
+# choose a default for you.
+#
+# NOTE: if you use the "wx" configuration interface, you must also use
+# the "wx" display library.
+#=======================================================================
+#config_interface: textconfig
+#config_interface: wx
+
+#=======================================================================
+# DISPLAY_LIBRARY
+#
+# The display library is the code that displays the Bochs VGA screen.  Bochs 
+# has a selection of about 10 different display library implementations for 
+# different platforms.  If you run configure with multiple --with-* options, 
+# the display_library command lets you choose which one you want to run with.
+# If you do not write a display_library line, Bochs will choose a default for
+# you.
+#
+# The choices are: 
+#   x              use X windows interface, cross platform
+#   win32          use native win32 libraries
+#   carbon         use Carbon library (for MacOS X)
+#   beos           use native BeOS libraries
+#   macintosh      use MacOS pre-10
+#   amigaos        use native AmigaOS libraries
+#   sdl            use SDL library, cross platform
+#   svga           use SVGALIB library for Linux, allows graphics without X11
+#   term           text only, uses curses/ncurses library, cross platform
+#   rfb            provides an interface to AT&T's VNC viewer, cross platform
+#   wx             use wxWidgets library, cross platform
+#   nogui          no display at all
+#
+# NOTE: if you use the "wx" configuration interface, you must also use
+# the "wx" display library.
+#
+# Specific options:
+# Some display libraries now support specific option to control their
+# behaviour. See the examples below for currently supported options.
+#=======================================================================
+#display_library: amigaos
+#display_library: beos
+#display_library: carbon
+#display_library: macintosh
+#display_library: nogui
+#display_library: rfb, options="timeout=60" # time to wait for client
+#display_library: sdl, options="fullscreen" # startup in fullscreen mode
+#display_library: term
+#display_library: win32, options="legacyF12" # use F12 to toggle mouse
+#display_library: wx
+display_library: x
+
+#=======================================================================
+# ROMIMAGE:
+# The ROM BIOS controls what the PC does when it first powers on.
+# Normally, you can use a precompiled BIOS in the source or binary
+# distribution called BIOS-bochs-latest. The ROM BIOS is usually loaded
+# starting at address 0xf0000, and it is exactly 64k long.
+# You can also use the environment variable $BXSHARE to specify the
+# location of the BIOS.
+# The usage of external large BIOS images (up to 512k) at memory top is
+# now supported, but we still recommend to use the BIOS distributed with
+# Bochs. Now the start address can be calculated from image size.
+#=======================================================================
+romimage: file=$BXSHARE/BIOS-bochs-latest, address=0xf0000
+#romimage: file=mybios.bin, address=0xfff80000 # 512k at memory top
+#romimage: file=mybios.bin # calculate start address from image size
+
+#=======================================================================
+# CPU:
+# This defines cpu-related parameters inside Bochs:
+#
+#  COUNT:
+#  Set the number of processors:cores per processor:threads per core 
+#  when Bochs is compiled for SMP emulation.
+#  Bochs currently supports up to 8 threads running simultaniosly. 
+#  If Bochs is compiled without SMP support, it won't accept values 
+#  different from 1.
+#
+#  QUANTUM:
+#  Maximum amount of instructions allowed to execute by processor before
+#  returning control to another cpu. This option exists only in Bochs 
+#  binary compiled with SMP support.
+#
+#  RESET_ON_TRIPLE_FAULT:
+#  Reset the CPU when triple fault occur (highly recommended) rather than
+#  PANIC. Remember that if you trying to continue after triple fault the 
+#  simulation will be completely bogus !
+#
+#  IPS:
+#  Emulated Instructions Per Second.  This is the number of IPS that bochs
+#  is capable of running on your machine. You can recompile Bochs with
+#  --enable-show-ips option enabled, to find your workstation's capability.
+#  Measured IPS value will then be logged into your log file or status bar
+#  (if supported by the gui).
+#
+#  IPS is used to calibrate many time-dependent events within the bochs 
+#  simulation.  For example, changing IPS affects the frequency of VGA
+#  updates, the duration of time before a key starts to autorepeat, and
+#  the measurement of BogoMips and other benchmarks.
+#
+#  Examples:
+#
+#  Bochs Machine/Compiler                                 Mips
+# ____________________________________________________________________
+#  2.2.6 2.6Ghz Intel Core 2 Duo with WinXP/g++ 3.4      21 to 25 Mips
+#  2.2.6 2.1Ghz Athlon XP with Linux 2.6/g++ 3.4         12 to 15 Mips
+#  2.0.1 1.6Ghz Intel P4 with Win2000/g++ 3.3             5 to  7 Mips
+#  1.4   650Mhz Athlon K-7 with Linux 2.4.4/egcs-2.91.66  2 to  2.5 Mips
+#  1.4   400Mhz Pentium II with Linux 2.0.36/egcs-1.0.3   1 to  1.8 Mips
+#=======================================================================
+cpu: count=1, ips=10000000, reset_on_triple_fault=1
+
+#=======================================================================
+# MEGS
+# Set the number of Megabytes of physical memory you want to emulate. 
+# The default is 32MB, most OS's won't need more than that.
+# The maximum amount of memory supported is 2048Mb.
+#=======================================================================
+#megs: 256
+#megs: 128
+#megs: 64
+megs: 256
+#megs: 16
+#megs: 8
+
+#=======================================================================
+# OPTROMIMAGE[1-4]:
+# You may now load up to 4 optional ROM images. Be sure to use a 
+# read-only area, typically between C8000 and EFFFF. These optional
+# ROM images should not overwrite the rombios (located at
+# F0000-FFFFF) and the videobios (located at C0000-C7FFF).
+# Those ROM images will be initialized by the bios if they contain 
+# the right signature (0x55AA) and a valid checksum.
+# It can also be a convenient way to upload some arbitrary code/data
+# in the simulation, that can be retrieved by the boot loader
+#=======================================================================
+#optromimage1: file=optionalrom.bin, address=0xd0000
+#optromimage2: file=optionalrom.bin, address=0xd1000
+#optromimage3: file=optionalrom.bin, address=0xd2000
+#optromimage4: file=optionalrom.bin, address=0xd3000
+
+#optramimage1: file=/path/file1.img, address=0x0010000
+#optramimage2: file=/path/file2.img, address=0x0020000
+#optramimage3: file=/path/file3.img, address=0x0030000
+#optramimage4: file=/path/file4.img, address=0x0040000
+
+#=======================================================================
+# VGAROMIMAGE
+# You now need to load a VGA ROM BIOS into C0000.
+#=======================================================================
+#vgaromimage: file=bios/VGABIOS-elpin-2.40
+vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest
+#vgaromimage: file=bios/VGABIOS-lgpl-latest-cirrus
+
+#=======================================================================
+# VGA:
+# Here you can specify the display extension to be used. With the value
+# 'none' you can use standard VGA with no extension. Other supported
+# values are 'vbe' for Bochs VBE and 'cirrus' for Cirrus SVGA support.
+#=======================================================================
+#vga: extension=cirrus
+#vga: extension=vbe
+#vga: extension=none
+
+#=======================================================================
+# FLOPPYA:
+# Point this to pathname of floppy image file or device
+# This should be of a bootable floppy(image/device) if you're
+# booting from 'a' (or 'floppy').
+#
+# You can set the initial status of the media to 'ejected' or 'inserted'.
+#   floppya: 2_88=path, status=ejected             (2.88M 3.5" floppy)
+#   floppya: 1_44=path, status=inserted            (1.44M 3.5" floppy)
+#   floppya: 1_2=path, status=ejected              (1.2M  5.25" floppy)
+#   floppya: 720k=path, status=inserted            (720K  3.5" floppy)
+#   floppya: 360k=path, status=inserted            (360K  5.25" floppy)
+#   floppya: 320k=path, status=inserted            (320K  5.25" floppy)
+#   floppya: 180k=path, status=inserted            (180K  5.25" floppy)
+#   floppya: 160k=path, status=inserted            (160K  5.25" floppy)
+#   floppya: image=path, status=inserted           (guess type from image size)
+#
+# The path should be the name of a disk image file.  On Unix, you can use a raw
+# device name such as /dev/fd0 on Linux.  On win32 platforms, use drive letters
+# such as a: or b: as the path.  The parameter 'image' works with image files
+# only. In that case the size must match one of the supported types.
+#=======================================================================
+floppya: 1_44=./fd.img, status=inserted
+#floppya: image=./fd.img, status=inserted
+#floppya: 1_44=/dev/fd0H1440, status=inserted
+#floppya: 1_2=../1_2, status=inserted
+#floppya: 1_44=a:, status=inserted
+#floppya: 1_44=a.img, status=inserted
+#floppya: 1_44=/dev/rfd0a, status=inserted
+
+#=======================================================================
+# FLOPPYB:
+# See FLOPPYA above for syntax
+#=======================================================================
+#floppyb: 1_44=b:, status=inserted
+#floppyb: 1_44=b.img, status=inserted
+
+#=======================================================================
+# ATA0, ATA1, ATA2, ATA3
+# ATA controller for hard disks and cdroms
+#
+# ata[0-3]: enabled=[0|1], ioaddr1=addr, ioaddr2=addr, irq=number
+# 
+# These options enables up to 4 ata channels. For each channel
+# the two base io addresses and the irq must be specified.
+# 
+# ata0 and ata1 are enabled by default with the values shown below
+#
+# Examples:
+#   ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
+#   ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
+#   ata2: enabled=1, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
+#   ata3: enabled=1, ioaddr1=0x168, ioaddr2=0x360, irq=9
+#=======================================================================
+ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
+ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15
+ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11
+ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9
+
+#=======================================================================
+# ATA[0-3]-MASTER, ATA[0-3]-SLAVE
+#
+# This defines the type and characteristics of all attached ata devices:
+#   type=       type of attached device [disk|cdrom] 
+#   mode=       only valid for disks [flat|concat|external|dll|sparse|vmware3]
+#   mode=       only valid for disks [undoable|growing|volatile]
+#   path=       path of the image
+#   cylinders=  only valid for disks
+#   heads=      only valid for disks
+#   spt=        only valid for disks
+#   status=     only valid for cdroms [inserted|ejected]
+#   biosdetect= type of biosdetection [none|auto], only for disks on ata0 [cmos]
+#   translation=type of translation of the bios, only for disks [none|lba|large|rechs|auto]
+#   model=      string returned by identify device command
+#   journal=    optional filename of the redolog for undoable and volatile disks
+#   
+# Point this at a hard disk image file, cdrom iso file, or physical cdrom
+# device.  To create a hard disk image, try running bximage.  It will help you
+# choose the size and then suggest a line that works with it.
+#
+# In UNIX it may be possible to use a raw device as a Bochs hard disk, 
+# but WE DON'T RECOMMEND IT.  In Windows there is no easy way.
+#
+# In windows, the drive letter + colon notation should be used for cdroms.
+# Depending on versions of windows and drivers, you may only be able to 
+# access the "first" cdrom in the system.  On MacOSX, use path="drive"
+# to access the physical drive.
+#
+# The path is always mandatory. Disk geometry autodetection works with images
+# created by bximage if CHS is set to 0/0/0 (cylinders are calculated using
+# heads=16 and spt=63). For other hard disk images and modes the cylinders,
+# heads, and spt are mandatory.
+#
+# Default values are:
+#   mode=flat, biosdetect=auto, translation=auto, model="Generic 1234"
+#
+# The biosdetect option has currently no effect on the bios
+#
+# Examples:
+#   ata0-master: type=disk, mode=flat, path=10M.sample, cylinders=306, heads=4, spt=17
+#   ata0-slave:  type=disk, mode=flat, path=20M.sample, cylinders=615, heads=4, spt=17
+#   ata1-master: type=disk, mode=flat, path=30M.sample, cylinders=615, heads=6, spt=17
+#   ata1-slave:  type=disk, mode=flat, path=46M.sample, cylinders=940, heads=6, spt=17
+#   ata2-master: type=disk, mode=flat, path=62M.sample, cylinders=940, heads=8, spt=17
+#   ata2-slave:  type=disk, mode=flat, path=112M.sample, cylinders=900, heads=15, spt=17
+#   ata3-master: type=disk, mode=flat, path=483M.sample, cylinders=1024, heads=15, spt=63
+#   ata3-slave:  type=cdrom, path=iso.sample, status=inserted
+#=======================================================================
+#ata0-master: type=disk, mode=flat, path="30M.sample", cylinders=615, heads=6, spt=17
+#ata0-master: type=disk, mode=flat, path="c.img", cylinders=0 # autodetect
+#ata0-slave: type=cdrom, path=D:, status=inserted
+#ata0-slave: type=cdrom, path=/dev/cdrom, status=inserted
+#ata0-slave: type=cdrom, path="drive", status=inserted
+#ata0-slave: type=cdrom, path=/dev/rcd0d, status=inserted 
+
+#=======================================================================
+# BOOT:
+# This defines the boot sequence. Now you can specify up to 3 boot drives.
+# You can either boot from 'floppy', 'disk' or 'cdrom'
+# legacy 'a' and 'c' are also supported
+# Examples:
+#   boot: floppy
+#   boot: disk
+#   boot: cdrom
+#   boot: c
+#   boot: a
+#   boot: cdrom, floppy, disk
+#=======================================================================
+boot: floppy
+#boot: disk
+
+#=======================================================================
+# CLOCK:
+# This defines the parameters of the clock inside Bochs:
+#
+#  SYNC:
+#  TO BE COMPLETED (see Greg explanation in feature request #536329)
+#
+#  TIME0:
+#  Specifies the start (boot) time of the virtual machine. Use a time 
+#  value as returned by the time(2) system call. If no time0 value is 
+#  set or if time0 equal to 1 (special case) or if time0 equal 'local', 
+#  the simulation will be started at the current local host time.
+#  If time0 equal to 2 (special case) or if time0 equal 'utc',
+#  the simulation will be started at the current utc time.
+#
+# Syntax:
+#  clock: sync=[none|slowdown|realtime|both], time0=[timeValue|local|utc]
+#
+# Example:
+#   clock: sync=none,     time0=local       # Now (localtime)
+#   clock: sync=slowdown, time0=315529200   # Tue Jan  1 00:00:00 1980
+#   clock: sync=none,     time0=631148400   # Mon Jan  1 00:00:00 1990
+#   clock: sync=realtime, time0=938581955   # Wed Sep 29 07:12:35 1999
+#   clock: sync=realtime, time0=946681200   # Sat Jan  1 00:00:00 2000
+#   clock: sync=none,     time0=1           # Now (localtime)
+#   clock: sync=none,     time0=utc         # Now (utc/gmt)
+# 
+# Default value are sync=none, time0=local
+#=======================================================================
+#clock: sync=none, time0=local
+
+
+#=======================================================================
+# FLOPPY_BOOTSIG_CHECK: disabled=[0|1]
+# Enables or disables the 0xaa55 signature check on boot floppies
+# Defaults to disabled=0
+# Examples:
+#   floppy_bootsig_check: disabled=0
+#   floppy_bootsig_check: disabled=1
+#=======================================================================
+floppy_bootsig_check: disabled=0
+
+#=======================================================================
+# LOG:
+# Give the path of the log file you'd like Bochs debug and misc. verbiage
+# to be written to. If you don't use this option or set the filename to
+# '-' the output is written to the console. If you really don't want it,
+# make it "/dev/null" (Unix) or "nul" (win32). :^(
+#
+# Examples:
+#   log: ./bochs.out
+#   log: /dev/tty
+#=======================================================================
+#log: /dev/null
+log: bochsout.txt
+
+#=======================================================================
+# LOGPREFIX:
+# This handles the format of the string prepended to each log line.
+# You may use those special tokens :
+#   %t : 11 decimal digits timer tick
+#   %i : 8 hexadecimal digits of cpu current eip (ignored in SMP configuration)
+#   %e : 1 character event type ('i'nfo, 'd'ebug, 'p'anic, 'e'rror)
+#   %d : 5 characters string of the device, between brackets
+# 
+# Default : %t%e%d
+# Examples:
+#   logprefix: %t-%e-@%i-%d
+#   logprefix: %i%e%d
+#=======================================================================
+#logprefix: %t%e%d
+
+#=======================================================================
+# LOG CONTROLS
+#
+# Bochs now has four severity levels for event logging.
+#   panic: cannot proceed.  If you choose to continue after a panic, 
+#          don't be surprised if you get strange behavior or crashes.
+#   error: something went wrong, but it is probably safe to continue the
+#          simulation.
+#   info: interesting or useful messages.
+#   debug: messages useful only when debugging the code.  This may
+#          spit out thousands per second.
+#
+# For events of each level, you can choose to crash, report, or ignore.
+# TODO: allow choice based on the facility: e.g. crash on panics from
+#       everything except the cdrom, and only report those.
+#
+# If you are experiencing many panics, it can be helpful to change
+# the panic action to report instead of fatal.  However, be aware
+# that anything executed after a panic is uncharted territory and can 
+# cause bochs to become unstable.  The panic is a "graceful exit," so
+# if you disable it you may get a spectacular disaster instead.
+#=======================================================================
+panic: action=ask
+error: action=report
+info: action=report
+debug: action=ignore
+#pass: action=fatal
+
+#=======================================================================
+# DEBUGGER_LOG:
+# Give the path of the log file you'd like Bochs to log debugger output.
+# If you really don't want it, make it /dev/null or '-'. :^(
+#
+# Examples:
+#   debugger_log: ./debugger.out
+#=======================================================================
+#debugger_log: /dev/null
+#debugger_log: debugger.out
+debugger_log: -
+
+#=======================================================================
+# COM1, COM2, COM3, COM4:
+# This defines a serial port (UART type 16550A). In the 'term' you can specify
+# a device to use as com1. This can be a real serial line, or a pty.  To use
+# a pty (under X/Unix), create two windows (xterms, usually).  One of them will
+# run bochs, and the other will act as com1. Find out the tty the com1
+# window using the `tty' command, and use that as the `dev' parameter.
+# Then do `sleep 1000000' in the com1 window to keep the shell from
+# messing with things, and run bochs in the other window.  Serial I/O to
+# com1 (port 0x3f8) will all go to the other window.
+# Other serial modes are 'null' (no input/output), 'file' (output to a file
+# specified as the 'dev' parameter), 'raw' (use the real serial port - under
+# construction for win32), 'mouse' (standard serial mouse - requires
+# mouse option setting 'type=serial' or 'type=serial_wheel') and 'socket'
+# (connect a networking socket).
+#
+# Examples:
+#   com1: enabled=1, mode=null
+#   com1: enabled=1, mode=mouse
+#   com2: enabled=1, mode=file, dev=serial.out
+#   com3: enabled=1, mode=raw, dev=com1
+#   com3: enabled=1, mode=socket, dev=localhost:8888
+#=======================================================================
+com1: enabled=1, mode=socket, dev=localhost:5001
+
+
+#=======================================================================
+# PARPORT1, PARPORT2:
+# This defines a parallel (printer) port. When turned on and an output file is
+# defined the emulated printer port sends characters printed by the guest OS
+# into the output file. On some platforms a device filename can be used to
+# send the data to the real parallel port (e.g. "/dev/lp0" on Linux, "lpt1" on
+# win32 platforms).
+#
+# Examples:
+#   parport1: enabled=1, file="parport.out"
+#   parport2: enabled=1, file="/dev/lp0"
+#   parport1: enabled=0
+#=======================================================================
+parport1: enabled=1, file="parport.out"
+
+#=======================================================================
+# SB16:
+# This defines the SB16 sound emulation. It can have several of the
+# following properties.
+# All properties are in the format sb16: property=value
+# midi: The filename is where the midi data is sent. This can be a
+#       device or just a file if you want to record the midi data.
+# midimode:
+#      0=no data
+#      1=output to device (system dependent. midi denotes the device driver)
+#      2=SMF file output, including headers
+#      3=output the midi data stream to the file (no midi headers and no
+#        delta times, just command and data bytes)
+# wave: This is the device/file where wave output is stored
+# wavemode:
+#      0=no data
+#      1=output to device (system dependent. wave denotes the device driver)
+#      2=VOC file output, incl. headers
+#      3=output the raw wave stream to the file
+# log:  The file to write the sb16 emulator messages to.
+# loglevel:
+#      0=no log
+#      1=resource changes, midi program and bank changes
+#      2=severe errors
+#      3=all errors
+#      4=all errors plus all port accesses
+#      5=all errors and port accesses plus a lot of extra info
+# dmatimer:
+#      microseconds per second for a DMA cycle.  Make it smaller to fix
+#      non-continuous sound.  750000 is usually a good value.  This needs a
+#      reasonably correct setting for the IPS parameter of the CPU option.
+#
+# For an example look at the next line:
+#=======================================================================
+
+#sb16: midimode=1, midi=/dev/midi00, wavemode=1, wave=/dev/dsp, loglevel=2, log=sb16.log, dmatimer=600000
+
+#=======================================================================
+# VGA_UPDATE_INTERVAL:
+# Video memory is scanned for updates and screen updated every so many
+# virtual seconds.  The default is 40000, about 25Hz. Keep in mind that
+# you must tweak the 'cpu: ips=N' directive to be as close to the number
+# of emulated instructions-per-second your workstation can do, for this
+# to be accurate.
+#
+# Examples:
+#   vga_update_interval: 250000
+#=======================================================================
+vga_update_interval: 300000
+
+# using for Winstone '98 tests
+#vga_update_interval:  100000
+
+#=======================================================================
+# KEYBOARD_SERIAL_DELAY:
+# Approximate time in microseconds that it takes one character to
+# be transfered from the keyboard to controller over the serial path.
+# Examples:
+#   keyboard_serial_delay: 200
+#=======================================================================
+keyboard_serial_delay: 250
+
+#=======================================================================
+# KEYBOARD_PASTE_DELAY:
+# Approximate time in microseconds between attempts to paste
+# characters to the keyboard controller. This leaves time for the
+# guest os to deal with the flow of characters.  The ideal setting
+# depends on how your operating system processes characters.  The
+# default of 100000 usec (.1 seconds) was chosen because it works 
+# consistently in Windows.
+#
+# If your OS is losing characters during a paste, increase the paste
+# delay until it stops losing characters.
+#
+# Examples:
+#   keyboard_paste_delay: 100000
+#=======================================================================
+keyboard_paste_delay: 100000
+
+#=======================================================================
+# MOUSE: 
+# This option prevents Bochs from creating mouse "events" unless a mouse
+# is  enabled. The hardware emulation itself is not disabled by this.
+# You can turn the mouse on by setting enabled to 1, or turn it off by
+# setting enabled to 0. Unless you have a particular reason for enabling
+# the mouse by default, it is recommended that you leave it off.
+# You can also toggle the mouse usage at runtime (control key + middle
+# mouse button on X11, SDL, wxWidgets and Win32).
+# With the mouse type option you can select the type of mouse to emulate.
+# The default value is 'ps2'. The other choices are 'imps2' (wheel mouse
+# on PS/2), 'serial', 'serial_wheel' (one com port requires setting
+# 'mode=mouse') and 'usb' (3-button mouse - one of the USB ports must be
+# connected with the 'mouse' device - requires PCI and USB support).
+#
+# Examples:
+#   mouse: enabled=1
+#   mouse: enabled=1, type=imps2
+#   mouse: enabled=1, type=serial
+#   mouse: enabled=0
+#=======================================================================
+mouse: enabled=0
+
+#=======================================================================
+# private_colormap: Request that the GUI create and use it's own
+#                   non-shared colormap.  This colormap will be used
+#                   when in the bochs window.  If not enabled, a
+#                   shared colormap scheme may be used.  Not implemented
+#                   on all GUI's.
+#
+# Examples:
+#   private_colormap: enabled=1
+#   private_colormap: enabled=0
+#=======================================================================
+private_colormap: enabled=0
+
+#=======================================================================
+# fullscreen: ONLY IMPLEMENTED ON AMIGA
+#             Request that Bochs occupy the entire screen instead of a 
+#             window.
+#
+# Examples:
+#   fullscreen: enabled=0
+#   fullscreen: enabled=1
+#=======================================================================
+#fullscreen: enabled=0
+#screenmode: name="sample"
+
+#=======================================================================
+# ne2k: NE2000 compatible ethernet adapter
+#
+# Examples:
+# ne2k: ioaddr=IOADDR, irq=IRQ, mac=MACADDR, ethmod=MODULE, ethdev=DEVICE, script=SCRIPT
+#
+# ioaddr, irq: You probably won't need to change ioaddr and irq, unless there
+# are IRQ conflicts.
+#
+# mac: The MAC address MUST NOT match the address of any machine on the net.
+# Also, the first byte must be an even number (bit 0 set means a multicast
+# address), and you cannot use ff:ff:ff:ff:ff:ff because that's the broadcast
+# address.  For the ethertap module, you must use fe:fd:00:00:00:01.  There may
+# be other restrictions too.  To be safe, just use the b0:c4... address.
+#
+# ethdev: The ethdev value is the name of the network interface on your host
+# platform.  On UNIX machines, you can get the name by running ifconfig.  On
+# Windows machines, you must run niclist to get the name of the ethdev.
+# Niclist source code is in misc/niclist.c and it is included in Windows 
+# binary releases.
+#
+# script: The script value is optional, and is the name of a script that 
+# is executed after bochs initialize the network interface. You can use 
+# this script to configure this network interface, or enable masquerading.
+# This is mainly useful for the tun/tap devices that only exist during
+# Bochs execution. The network interface name is supplied to the script
+# as first parameter
+#
+# If you don't want to make connections to any physical networks,
+# you can use the following 'ethmod's to simulate a virtual network.
+#   null: All packets are discarded, but logged to a few files.
+#   arpback: ARP is simulated. Disabled by default.
+#   vde:  Virtual Distributed Ethernet
+#   vnet: ARP, ICMP-echo(ping), DHCP and read/write TFTP are simulated.
+#         The virtual host uses 192.168.10.1.
+#         DHCP assigns 192.168.10.2 to the guest.
+#         TFTP uses the ethdev value for the root directory and doesn't
+#         overwrite files.
+#
+#=======================================================================
+# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=fbsd, ethdev=en0 #macosx
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=fbsd, ethdev=xl0
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:00, ethmod=linux, ethdev=eth0
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=win32, ethdev=MYCARD
+# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tap, ethdev=tap0
+# ne2k: ioaddr=0x240, irq=9, mac=fe:fd:00:00:00:01, ethmod=tuntap, ethdev=/dev/net/tun0, script=./tunconfig
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=null, ethdev=eth0
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vde, ethdev="/tmp/vde.ctl"
+# ne2k: ioaddr=0x240, irq=9, mac=b0:c4:20:00:00:01, ethmod=vnet, ethdev="c:/temp"
+
+#=======================================================================
+# KEYBOARD_MAPPING:
+# This enables a remap of a physical localized keyboard to a 
+# virtualized us keyboard, as the PC architecture expects.
+# If enabled, the keymap file must be specified.
+# 
+# Examples:
+#   keyboard_mapping: enabled=1, map=gui/keymaps/x11-pc-de.map
+#=======================================================================
+keyboard_mapping: enabled=0, map=
+
+#=======================================================================
+# KEYBOARD_TYPE:
+# Type of keyboard return by a "identify keyboard" command to the
+# keyboard controler. It must be one of "xt", "at" or "mf".
+# Defaults to "mf". It should be ok for almost everybody. A known
+# exception is french macs, that do have a "at"-like keyboard.
+#
+# Examples:
+#   keyboard_type: mf
+#=======================================================================
+#keyboard_type: mf
+
+#=======================================================================
+# USER_SHORTCUT:
+# This defines the keyboard shortcut to be sent when you press the "user"
+# button in the headerbar. The shortcut string is a combination of maximum
+# 3 key names (listed below) separated with a '-' character. The old-style
+# syntax (without the '-') still works for the key combinations supported
+# in Bochs 2.2.1.
+# Valid key names:
+# "alt", "bksl", "bksp", "ctrl", "del", "down", "end", "enter", "esc",
+# "f1", ... "f12", "home", "ins", "left", "menu", "minus", "pgdwn", "pgup",
+# "plus", "right", "shift", "space", "tab", "up", and "win".
+#
+# Example:
+#   user_shortcut: keys=ctrl-alt-del
+#=======================================================================
+#user_shortcut: keys=ctrl-alt-del
+
+#=======================================================================
+# I440FXSUPPORT:
+# This option controls the presence of the i440FX PCI chipset. You can
+# also specify the devices connected to PCI slots. Up to 5 slots are
+# available now. These devices are currently supported: ne2k, pcivga,
+# pcidev and pcipnic. If Bochs is compiled with Cirrus SVGA support
+# you'll have the additional choice 'cirrus'.
+#
+# Example:
+#   i440fxsupport: enabled=1, slot1=pcivga, slot2=ne2k
+#=======================================================================
+i440fxsupport: enabled=1
+
+#=======================================================================
+# USB1:
+# This option controls the presence of the USB root hub which is a part
+# of the i440FX PCI chipset. With the portX option you can connect devices
+# to the hub (currently supported: 'mouse' and 'keypad'). If you connect
+# the mouse to one of the ports and use the mouse option 'type=usb' you'll
+# have a 3-button USB mouse.
+#
+# Example:
+#   usb1: enabled=1, port1=mouse, port2=keypad
+#=======================================================================
+#usb1: enabled=1
+
+#=======================================================================
+# CMOSIMAGE:
+# This defines image file that can be loaded into the CMOS RAM at startup.
+# The rtc_init parameter controls whether initialize the RTC with values stored
+# in the image. By default the time0 argument given to the clock option is used.
+# With 'rtc_init=image' the image is the source for the initial time.
+#
+# Example:
+#   cmosimage: file=cmos.img, rtc_init=image
+#=======================================================================
+#cmosimage: file=cmos.img, rtc_init=time0
+
+#=======================================================================
+# other stuff
+#=======================================================================
+#magic_break: enabled=1
+#load32bitOSImage: os=nullkernel, path=../kernel.img, iolog=../vga_io.log
+#load32bitOSImage: os=linux, path=../linux.img, iolog=../vga_io.log, initrd=../initrd.img
+#text_snapshot_check: enable
+
+#-------------------------
+# PCI host device mapping
+#-------------------------
+#pcidev: vendor=0x1234, device=0x5678
+
+#=======================================================================
+# GDBSTUB:
+# Enable GDB stub. See user documentation for details.
+# Default value is enabled=0.
+#=======================================================================
+#gdbstub: enabled=0, port=1234, text_base=0, data_base=0, bss_base=0
+
+#=======================================================================
+# IPS:
+# The IPS directive is DEPRECATED. Use the parameter IPS of the CPU
+# directive instead.
+#=======================================================================
+#ips: 10000000
+
+#=======================================================================
+# for Macintosh, use the style of pathnames in the following
+# examples.
+#
+# vgaromimage: :bios:VGABIOS-elpin-2.40
+# romimage: file=:bios:BIOS-bochs-latest, address=0xf0000
+# floppya: 1_44=[fd:], status=inserted
+#=======================================================================
diff --git a/misc/test_vm/build/Makefile b/misc/test_vm/build/Makefile
new file mode 100644 (file)
index 0000000..381ef25
--- /dev/null
@@ -0,0 +1,308 @@
+# Makefile for GeekOS kernel, userspace, and tools
+# Copyright (c) 2004,2005 David H. Hovemeyer <daveho@cs.umd.edu>
+# $Revision: 1.1 $
+
+# This is free software.  You are permitted to use,
+# redistribute, and modify it as specified in the file "COPYING".
+
+# Required software to build GeekOS:
+# - GNU Make (http://www.gnu.org/software/make)
+# - gcc 2.95.2 generating code for target (i386/ELF) and host platforms
+# - nasm (http://nasm.sourceforge.net)
+# - Perl5, AWK (any version), egrep
+#
+# Cygwin (http://cygwin.com) may be used to build GeekOS.
+# Make sure that gcc, binutils, nasm, and perl are installed.
+
+# NOTES:
+# - This makefile has been written carefully to work correctly
+#   with the -j (parallel make) option.  I regularly use "make -j 2"
+#   to speed the build process on 2 processor systems.
+
+# Base address of kernel
+#
+# Note: at top of memory minus three pages (GDT/TSS/IDT) 
+# minus maximum size
+#
+#
+# Note that the code will initially load at 0x10000
+#
+# The setup code needs to copy it up to this address and jump there
+#
+#KERNEL_BASE_ADDR := $(shell perl -e 'print sprintf("0x%x",$(TOP_OF_MEM)-4096*3-$(MAX_VMM));')
+KERNEL_BASE_ADDR := 0x100000
+
+# Kernel entry point function
+KERNEL_ENTRY = $(SYM_PFX)Main
+
+
+PROJECT_ROOT := ..
+VPATH := $(PROJECT_ROOT)/src
+
+#when -DNDEBUG is set the kassert functions are disabled
+#JRLDEBUG=-DNDEBUG
+JRLDEBUG= -DSERIAL_PRINT_DEBUG=1 -DSERIAL_PRINT_DEBUG_LEVEL=1000 -DSERIAL_PRINT=1
+
+#
+#
+#Peter's compile flags
+PAD= 
+
+# Figure out if we're compiling with cygwin, http://cygwin.com
+SYSTEM_NAME := $(shell uname -s)
+ifeq ($(findstring CYGWIN,$(SYSTEM_NAME)),CYGWIN)
+SYM_PFX            := _
+EXTRA_C_OPTS       := -DNEED_UNDERSCORE -DGNU_WIN32
+EXTRA_NASM_OPTS    := -DNEED_UNDERSCORE
+NON_ELF_SYSTEM     := yes
+EXTRA_CC_USER_OPTS := -Dmain=geekos_main
+endif
+
+
+
+# ----------------------------------------------------------------------
+# Configuration -
+#   Various options specifying how GeekOS should be built,
+#   what source files to build, which user programs to build,
+#   etc.  This is generally the only section of the makefile
+#   that will need to be modified.
+# ----------------------------------------------------------------------
+
+# List of targets to build by default.
+# These targets encompass everything needed to boot
+# and run GeekOS.
+ALL_TARGETS := geekos/kernel.bin fd.img
+
+
+# Kernel source files
+KERNEL_C_SRCS := idt.c int.c trap.c irq.c io.c \
+        blockdev.c ide.c \
+       keyboard.c screen.c timer.c \
+       mem.c crc32.c \
+       gdt.c tss.c segment.c \
+       bget.c malloc.c \
+       synch.c kthread.c \
+       serial.c  reboot.c \
+        paging.c \
+       main.c
+
+# Kernel object files built from C source files
+KERNEL_C_OBJS := $(KERNEL_C_SRCS:%.c=geekos/%.o)
+
+# Kernel assembly files
+KERNEL_ASM_SRCS := lowlevel.asm
+
+KERNEL_GAS_SRCS :=
+
+# Kernel object files build from assembler source files
+KERNEL_ASM_OBJS := $(KERNEL_ASM_SRCS:%.asm=geekos/%.o) 
+
+KERNEL_GAS_OBJS := $(KERNEL_GAS_SRCS:%.s=geekos/%.o)
+
+
+# All kernel object files
+KERNEL_OBJS := $(KERNEL_C_OBJS) \
+  $(KERNEL_ASM_OBJS) $(KERNEL_GAS_OBJS)
+
+# Common library source files.
+# This library is linked into both the kernel and user programs.
+# It provides string functions and generic printf()-style
+# formatted output.
+COMMON_C_SRCS := fmtout.c string.c memmove.c
+
+# Common library object files.
+COMMON_C_OBJS := $(COMMON_C_SRCS:%.c=common/%.o)
+
+
+
+
+# ----------------------------------------------------------------------
+# Tools -
+#   This section defines programs that are used to build GeekOS.
+# ----------------------------------------------------------------------
+
+# Uncomment if cross compiling
+#TARGET_CC_PREFIX := i386-elf-
+
+# Target C compiler.  gcc 2.95.2 or later should work.
+TARGET_CC := $(TARGET_CC_PREFIX)gcc
+#TARGET_CC := $(TARGET_CC_PREFIX)gcc34 -m32
+
+# Host C compiler.  This is used to compile programs to execute on
+# the host platform, not the target (x86) platform.  On x86/ELF
+# systems, such as Linux and FreeBSD, it can generally be the same
+# as the target C compiler.
+HOST_CC := gcc
+
+# Target linker.  GNU ld is probably to only one that will work.
+TARGET_LD := $(TARGET_CC_PREFIX)ld -melf_i386
+
+# Target archiver
+TARGET_AR := $(TARGET_CC_PREFIX)ar
+
+# Target ranlib
+TARGET_RANLIB := $(TARGET_CC_PREFIX)ranlib
+
+# Target nm
+TARGET_NM := $(TARGET_CC_PREFIX)nm
+
+# Target objcopy
+TARGET_OBJCOPY := $(TARGET_CC_PREFIX)objcopy
+
+# Nasm (http://nasm.sourceforge.net)
+NASM := $(PROJECT_ROOT)/../devtools/bin/nasm
+#NASM := /opt/vmm-tools/bin/nasm
+
+AS = as --32
+
+# Tool to build PFAT filesystem images.
+BUILDFAT := tools/builtFat.exe
+
+# Perl5 or later
+PERL := perl
+
+# Pad a file so its size is a multiple of some unit (i.e., sector size)
+PAD := $(PERL) $(PROJECT_ROOT)/scripts/pad
+
+# Create a file filled with zeroes.
+ZEROFILE := $(PERL) $(PROJECT_ROOT)/scripts/zerofile
+
+# Calculate size of file in sectors
+NUMSECS := $(PERL) $(PROJECT_ROOT)/scripts/numsecs
+
+
+# ----------------------------------------------------------------------
+# Definitions -
+#   Options passed to the tools.
+# ----------------------------------------------------------------------
+
+# Flags used for all C source files
+GENERAL_OPTS := -O -Wall $(EXTRA_C_OPTS) $(JRLDEBUG) $(PADFLAGS)
+CC_GENERAL_OPTS := $(GENERAL_OPTS) -Werror 
+
+# Flags used for kernel C source files
+CC_KERNEL_OPTS := -g -DGEEKOS -I$(PROJECT_ROOT)/include
+
+# Flags user for kernel assembly files
+NASM_KERNEL_OPTS := -I$(PROJECT_ROOT)/src/geekos/ -f elf $(EXTRA_NASM_OPTS)
+
+# Flags used for common library and libc source files
+CC_USER_OPTS := -I$(PROJECT_ROOT)/include -I$(PROJECT_ROOT)/include/libc \
+       $(EXTRA_CC_USER_OPTS)
+
+# Flags passed to objcopy program (strip unnecessary sections from kernel.exe)
+OBJCOPY_FLAGS := -R .dynamic -R .note -R .comment
+
+# ----------------------------------------------------------------------
+# Rules -
+#   Describes how to compile the source files.
+# ----------------------------------------------------------------------
+
+# Compilation of kernel C source files
+
+geekos/%.o : geekos/%.c
+       $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o
+
+
+# Compilation of kernel assembly source files
+geekos/%.o : geekos/%.asm
+       $(NASM) $(NASM_KERNEL_OPTS) $< -o geekos/$*.o
+
+# Compilation of test VM
+geekos/%.o : geekos/%.s
+       $(AS) $< -o geekos/$*.o
+
+geekos/%.o : geekos/%.S
+       $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o
+
+# Compilation of common library C source files
+common/%.o : common/%.c
+       $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_USER_OPTS) $< -o common/$*.o
+
+# ----------------------------------------------------------------------
+# Targets -
+#   Specifies files to be built
+# ----------------------------------------------------------------------
+
+# Default target - see definition of ALL_TARGETS in Configuration section
+all : $(ALL_TARGETS)
+
+# Standard floppy image - just boots the kernel
+fd.img : geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin
+       cat geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin > $@
+
+
+# make ready to boot over PXE
+pxe:   fd.img
+       cp fd.img /tftpboot/vmm.img
+       $(PAD) /tftpboot/vmm.img 1474560
+
+
+vm:    geekos/kernel.bin
+       cp geekos/kernel.bin ../../vmm-hack1/build/vm_kernel
+
+geekos/test: geekos/test.o
+       $(CC) geekos/test.o -o geekos/test
+
+
+
+# Floppy boot sector (first stage boot loader).
+geekos/fd_boot.bin : geekos/setup.bin geekos/kernel.bin $(PROJECT_ROOT)/src/geekos/fd_boot.asm
+       $(NASM) -f bin \
+               -I$(PROJECT_ROOT)/src/geekos/ \
+               -DNUM_SETUP_SECTORS=`$(NUMSECS) geekos/setup.bin` \
+               -DNUM_KERN_SECTORS=`$(NUMSECS) geekos/kernel.bin` \
+               $(PROJECT_ROOT)/src/geekos/fd_boot.asm \
+               -o $@
+
+# Setup program (second stage boot loader).
+geekos/setup.bin : geekos/kernel.exe $(PROJECT_ROOT)/src/geekos/setup.asm
+       $(NASM) -f bin \
+               -I$(PROJECT_ROOT)/src/geekos/ \
+               -DENTRY_POINT=0x`egrep 'Main$$' geekos/kernel.syms |awk '{print $$1}'` \
+               $(PROJECT_ROOT)/src/geekos/setup.asm \
+               -o $@
+       $(PAD) $@ 512
+
+
+# Loadable (flat) kernel image.
+geekos/kernel.bin : geekos/kernel.exe
+       $(TARGET_OBJCOPY) $(OBJCOPY_FLAGS) -S -O binary geekos/kernel.exe geekos/kernel.bin
+       $(PAD) $@ 512
+
+# The kernel executable and symbol map.
+geekos/kernel.exe : $(KERNEL_OBJS) $(COMMON_C_OBJS)
+       $(TARGET_LD) -o geekos/kernel.exe -Ttext $(KERNEL_BASE_ADDR) -e $(KERNEL_ENTRY) \
+               $(KERNEL_OBJS) $(COMMON_C_OBJS)
+       $(TARGET_NM) geekos/kernel.exe > geekos/kernel.syms
+
+
+
+
+
+force:
+
+# Clean build directories of generated files
+clean :
+       for d in geekos common libc user tools; do \
+               (cd $$d && rm -f *); \
+       done
+
+
+# Build header file dependencies, so source files are recompiled when
+# header files they depend on are modified.
+depend : $(GENERATED_LIBC_SRCS)
+       $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) \
+               $(KERNEL_C_SRCS:%.c=$(PROJECT_ROOT)/src/geekos/%.c) \
+               | $(PERL) -n -e 's,^(\S),geekos/$$1,;print' \
+               > depend.mak
+       $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_USER_OPTS) \
+               $(COMMON_C_SRCS:%.c=$(PROJECT_ROOT)/src/common/%.c) \
+               | $(PERL) -n -e 's,^(\S),common/$$1,;print' \
+               >> depend.mak
+
+# By default, there are no header file dependencies.
+depend.mak :
+       touch $@
+
+include depend.mak
diff --git a/misc/test_vm/build/depend.mak b/misc/test_vm/build/depend.mak
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/misc/test_vm/build/noboot.img b/misc/test_vm/build/noboot.img
new file mode 100644 (file)
index 0000000..6d4f193
Binary files /dev/null and b/misc/test_vm/build/noboot.img differ
diff --git a/misc/test_vm/include/geekos/argblock.h b/misc/test_vm/include/geekos/argblock.h
new file mode 100644 (file)
index 0000000..54fac8b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Create and extract the command line argument block for a process
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_ARGBLOCK_H
+#define GEEKOS_ARGBLOCK_H
+
+/**
+ * Header struct for accessing argument block from user mode.
+ * Just cast the address of the argument block passed by
+ * the kernel to a pointer to this struct.
+ */
+struct Argument_Block {
+    int argc;
+    char **argv;
+};
+
+#ifdef GEEKOS
+
+/*
+ * Functions used by the kernel to create the argument block.
+ */
+void Get_Argument_Block_Size(const char *command, unsigned *numArgs, ulong_t *argBlockSize);
+void Format_Argument_Block(char *argBlock, unsigned numArgs, ulong_t userAddress,
+    const char *command);
+
+#endif
+
+#endif  /* GEEKOS_ARGBLOCK_H */
diff --git a/misc/test_vm/include/geekos/bget.h b/misc/test_vm/include/geekos/bget.h
new file mode 100644 (file)
index 0000000..63a0857
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+
+    Interface definitions for bget.c, the memory management package.
+
+*/
+
+#if defined (GEEKOS)
+
+// Adapted for geekos: http://www.cs.umd.edu/~daveho/geekos/
+// Original version of BGET downloaded from: http://www.fourmilab.ch/bget/
+// $Revision: 1.1 $
+
+// GeekOS changes are (mostly) confined to #if defined (GEEKOS)
+// sections.
+
+// Yes, we have prototypes :-)
+#define PROTOTYPES
+
+#endif // defined (GEEKOS)
+
+#ifndef _
+#ifdef PROTOTYPES
+#define  _(x)  x                     /* If compiler knows prototypes */
+#else
+#define  _(x)  ()                     /* It it doesn't */
+#endif /* PROTOTYPES */
+#endif
+
+typedef long bufsize;
+void   bpool       _((void *buffer, bufsize len));
+void   *bget       _((bufsize size));
+void   *bgetz      _((bufsize size));
+void   *bgetr      _((void *buffer, bufsize newsize));
+void   brel        _((void *buf));
+void   bectl       _((int (*compact)(bufsize sizereq, int sequence),
+                      void *(*acquire)(bufsize size),
+                      void (*release)(void *buf), bufsize pool_incr));
+void   bstats      _((bufsize *curalloc, bufsize *totfree, bufsize *maxfree,
+                      long *nget, long *nrel));
+void   bstatse     _((bufsize *pool_incr, long *npool, long *npget,
+                      long *nprel, long *ndget, long *ndrel));
+void   bufdump     _((void *buf));
+void   bpoold      _((void *pool, int dumpalloc, int dumpfree));
+int    bpoolv      _((void *pool));
diff --git a/misc/test_vm/include/geekos/blockdev.h b/misc/test_vm/include/geekos/blockdev.h
new file mode 100644 (file)
index 0000000..a099da8
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_BLOCKDEV_H
+#define GEEKOS_BLOCKDEV_H
+
+#include <geekos/ktypes.h>
+#include <geekos/kthread.h>
+#include <geekos/list.h>
+#include <geekos/fileio.h>
+
+#ifdef GEEKOS
+
+/*
+ * Type of block device request.
+ */
+enum Request_Type {
+    BLOCK_READ, BLOCK_WRITE
+};
+
+/*
+ * State of a block I/O request.
+ */
+enum Request_State {
+    PENDING, COMPLETED, ERROR
+};
+
+struct Block_Request;
+
+/*
+ * List of block I/O requests.
+ */
+DEFINE_LIST(Block_Request_List, Block_Request);
+
+/*
+ * An I/O request for a block device.
+ */
+struct Block_Request {
+    struct Block_Device *dev;
+    enum Request_Type type;
+    int blockNum;
+    void *buf;
+    volatile enum Request_State state;
+    volatile int errorCode;
+    struct Thread_Queue waitQueue;
+
+    DEFINE_LINK(Block_Request_List, Block_Request);
+};
+
+IMPLEMENT_LIST(Block_Request_List, Block_Request);
+
+struct Block_Device;
+struct Block_Device_Ops;
+
+/*
+ * A block device.
+ */
+struct Block_Device {
+    char name[BLOCKDEV_MAX_NAME_LEN];
+    struct Block_Device_Ops *ops;
+    int unit;
+    bool inUse;
+    void *driverData;
+    struct Thread_Queue *waitQueue;
+    struct Block_Request_List *requestQueue;
+
+    DEFINE_LINK(Block_Device_List, Block_Device);
+};
+
+/*
+ * Operations that may be requested on block devices.
+ */
+struct Block_Device_Ops {
+    int (*Open)(struct Block_Device *dev);
+    int (*Close)(struct Block_Device *dev);
+    int (*Get_Num_Blocks)(struct Block_Device *dev);
+};
+
+/*
+ * Low level block device API.
+ * Only block device drivers need to use these functions.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+    int unit, void *driverData, struct Thread_Queue *waitQueue,
+    struct Block_Request_List *requestQueue);
+int Open_Block_Device(const char *name, struct Block_Device **pDev);
+int Close_Block_Device(struct Block_Device *dev);
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+    int blockNum, void *buf);
+void Post_Request_And_Wait(struct Block_Request *request);
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+    struct Thread_Queue *waitQueue);
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode);
+
+/*
+ * High level block device API.
+ * For use by filesystem and disk paging code.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf);
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf);
+int Get_Num_Blocks(struct Block_Device *dev);
+
+/*
+ * Misc. routines
+ */
+
+/*
+ * Round offset up to nearest sector.
+ */
+static __inline__ ulong_t Round_Up_To_Block(ulong_t offset)
+{
+    return (offset % SECTOR_SIZE) == 0
+       ? offset
+       : offset + (SECTOR_SIZE - (offset % SECTOR_SIZE));
+}
+
+/*
+ * Round offset down to nearest sector.
+ */
+static __inline__ ulong_t Round_Down_To_Block(ulong_t offset)
+{
+    return (offset % SECTOR_SIZE) == 0
+       ? offset
+       : offset - (offset % SECTOR_SIZE);
+}
+
+#endif /* GEEKOS */
+
+#endif
+
diff --git a/misc/test_vm/include/geekos/bootinfo.h b/misc/test_vm/include/geekos/bootinfo.h
new file mode 100644 (file)
index 0000000..bb50de1
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Boot information structure, passed to kernel Main() routine
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_BOOTINFO_H
+#define GEEKOS_BOOTINFO_H
+
+struct Boot_Info {
+    int bootInfoSize;   /* size of this struct; for versioning */
+    int memSizeKB;      /* number of KB, as reported by int 15h */
+};
+
+#endif  /* GEEKOS_BOOTINFO_H */
diff --git a/misc/test_vm/include/geekos/cpu.h b/misc/test_vm/include/geekos/cpu.h
new file mode 100644 (file)
index 0000000..e748f6b
--- /dev/null
@@ -0,0 +1,96 @@
+#ifndef _cpu
+#define _cpu
+
+
+// EFLAGS register
+
+#define EFLAGS_CF         (1<<0)  // carry out
+// next bit reserved and must be 1
+#define EFLAGS_PF         (1<<2)  // ?
+// next bit reserved and must be zero 
+#define EFLAGS_AF         (1<<4)  // ?
+// next bit reserved and must be zero 
+#define EFLAGS_ZF         (1<<6)  // zero
+#define EFLAGS_SF         (1<<7)  // sign
+#define EFLAGS_TF         (1<<8)  // Trap flag
+#define EFLAGS_IF         (1<<9)  // Interrupt Enable
+#define EFLAGS_DF         (1<<10) // ?
+#define EFLAGS_OF         (1<<11) // overflow
+#define EFLAGS_IOPL_LO    (1<<12) // IO privilege level low bit
+#define EFLAGS_IOPL_HI    (1<<13) // IO privilege level low bit
+#define EFLAGS_NT         (1<<14) // Nested Task
+// next bit reserved and must be zero 
+#define EFLAGS_RF         (1<<16) // Resume Flag
+#define EFLAGS_VM         (1<<17) // V8086 mode
+#define EFLAGS_AC         (1<<18) // Alignment Check
+#define EFLAGS_VIF        (1<<19) // Virtual interrupt flag
+#define EFLAGS_VIP        (1<<20) // Virtual interrupt pending
+#define EFLAGS_ID         (1<<21) // identification flag
+// remaining bits reserved and must be zero 
+
+// Helpers for EFLAGS
+#define IOPL(r) ( ( (r) & (EFLAGS_IOPL_LO | EFLAGS_IOPL_HI)) >> 12 )
+
+
+
+// CR0 register
+
+#define CR0_PE            (1<<0)  // protection enabled (protected mode)
+#define CR0_MP            (1<<1)  // monitor coprocessor exists?
+#define CR0_EM            (1<<2)  // Emulation (set = no FPU)
+#define CR0_TS            (1<<3)  // Task switched (enable = avoid FPU/SSE context save on HW task switch)
+#define CR0_ET            (1<<4)  // extension type (FPU on 386/486, reserved afterward)
+#define CR0_NE            (1<<5)  // numeric error enable (FPU) (PC-style = disabled)
+// next group of bits are reserved
+#define CR0_WP            (1<<16) // write protect (disallow supervisor access to user pages)
+// next bit is reserved
+#define CR0_AM            (1<<17) // alignment mask
+// next group of bits are reserved
+#define CR0_NW            (1<<29) // not write through
+#define CR0_CD            (1<<30) // cache disable
+#define CR0_PG            (1<<31) // paging enable
+
+// CR1
+//
+// All of CR1 is reserved
+
+
+// CR2
+//
+// CR2 is the linear address of a page fault (output only)
+// only useful in a page fault context
+//
+
+// CR3
+//
+// CR3 is the page table base register and friends
+//
+#define CR3_PWT           (1<<3)  // page-level writes transparent 
+#define CR3_PCD           (1<<4)  // page-level cache disable  (avoid caching the first level page table)
+
+#define CR3_PAGE_DIRECTORY_BASE_ADDRESS(reg) ((reg)&0xfffff000)
+
+// CR4
+//
+
+#define CR4_VME           (1<<0)  // V8086 mode extensions (set = inter/except handling extensions on)
+#define CR4_PVI           (1<<1)  // Protected mode virtual interrupts (VIF)
+#define CR4_TSD           (1<<2)  // Time stamp disable (disallow rdstc for ring>0)
+#define CR4_DE            (1<<3)  // Debugging extensions (debug registers)
+#define CR4_PSE           (1<<4)  // Page size extensions (allow 4 MB pages)
+#define CR4_PAE           (1<<5)  // Physical Address Extensions (36+ bit addressing (PAE + IA32e)
+#define CR4_MCE           (1<<6)  // Machine Check enable (allow generation of MC exception)
+#define CR4_PGE           (1<<7)  // Page global enable (allow global pages)
+#define CR4_PCE           (1<<8)  // Performance counter enable (allow access to any ring)
+#define CR4_OSFXSR        (1<<9)  // OS Support for FXSAVE/RSTOR (fast FPU/SSE context instructions)
+#define CR4_OSMMEXCEPT    (1<<10)  // OS Support for unmasked SIMD FP exceptions (SSE)
+// next two bits reserved and set to zero
+#define CR4_VMXE          (1<<13)  // Allow the VMX instruction
+// rest of the bits reserved and set to zero
+
+
+
+
+
+
+#endif
diff --git a/misc/test_vm/include/geekos/crc32.h b/misc/test_vm/include/geekos/crc32.h
new file mode 100644 (file)
index 0000000..1628b77
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef GEEKOS_CRC32_H
+#define GEEKOS_CRC32_H
+
+#include <stddef.h>
+#include <geekos/ktypes.h>
+
+void Init_CRC32(void);
+ulong_t crc32(ulong_t crc, char const *buf, size_t len);
+
+#endif /* GEEKOS_CRC32_H */
diff --git a/misc/test_vm/include/geekos/defs.h b/misc/test_vm/include/geekos/defs.h
new file mode 100644 (file)
index 0000000..07981f7
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Misc. kernel definitions
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_DEFS_H
+#define GEEKOS_DEFS_H
+
+
+
+
+/*
+ * Kernel code and data segment selectors.
+ * Keep these up to date with defs.asm.
+ */
+#define KERNEL_CS  (1<<3)
+#define KERNEL_DS  (2<<3)
+
+
+/*
+ * Address where kernel is loaded INITIALLY
+ * we move it up in memory soon
+ */
+#define KERNEL_START_ADDR 0x100000
+
+/*
+ * Kernel and user privilege levels
+ */
+#define KERNEL_PRIVILEGE 0
+#define USER_PRIVILEGE 3
+
+
+/*
+ * Software interrupt for syscalls
+ */
+#define SYSCALL_INT 0x90
+
+/*
+ * The windows versions of gcc use slightly different
+ * names for the bss begin and end symbols than the Linux version.
+ */
+#if defined(GNU_WIN32)
+#  define BSS_START _bss_start__
+#  define BSS_END _bss_end__
+#else
+#  define BSS_START __bss_start
+#  define BSS_END end
+#endif
+
+/*
+ * x86 has 4096 byte pages
+ */
+#define PAGE_POWER 12
+#define PAGE_SIZE (1<<PAGE_POWER)
+#define PAGE_MASK (~(0xffffffff << PAGE_POWER))
+
+
+
+/* Ultimate Memory Layout
+
+0:
+4096: KernelThreadObject (one page)
+      KernelStack (one page)
+      GDT (one page)
+      TSS (one page)
+      IDT (one page)
+0x100000: KernelImage
+          Page List          (varies - must be an integral number of pages)
+         KernelHeap         (varies - KERNEL_HEAP_SIZE; must be integral number of pages)
+*/
+
+
+
+
+#define GDT_SIZE PAGE_SIZE
+#define TSS_SIZE PAGE_SIZE
+#define IDT_SIZE PAGE_SIZE
+//#define KERNEL_HEAP_SIZE (256*PAGE_SIZE)
+#define KERNEL_STACK_SIZE PAGE_SIZE
+#define KERNEL_THREAD_OBJECT_SIZE PAGE_SIZE
+
+
+
+
+/*
+ *  * Keep these up to date with defs.asm.
+ */
+#define GDT_LOCATION          (PAGE_SIZE * 3)
+#define TSS_LOCATION          (GDT_LOCATION + TSS_SIZE)
+#define IDT_LOCATION          (TSS_LOCATION + IDT_SIZE)
+
+
+
+
+  #define KERNEL_THREAD_OBJECT  (PAGE_SIZE)
+  #define KERNEL_STACK          (KERNEL_THREAD_OBJECT + KERNEL_THREAD_OBJECT_SIZE)
+/*
+  #define KERNEL_HEAP           (KERNEL_STACK + KERNEL_STACK_SIZE)
+  #define KERNEL_PAGELIST       (KERNEL_HEAP + KERNEL_HEAP_SIZE)
+*/
+
+/*
+ * PC memory map
+ */
+#define ISA_HOLE_START 0x0A0000
+#define ISA_HOLE_END   0x100000
+
+
+#endif  /* GEEKOS_DEFS_H */
diff --git a/misc/test_vm/include/geekos/errno.h b/misc/test_vm/include/geekos/errno.h
new file mode 100644 (file)
index 0000000..3159eae
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * GeekOS error codes
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_ERRNO_H
+#define GEEKOS_ERRNO_H
+
+/*
+ * Error codes returned by kernel functions and
+ * system calls.  These are meant to be returned to user
+ * code to describe system call failures.
+ */
+#define EUNSPECIFIED           -1       /* Unspecified error */
+#define ENOTFOUND              -2       /* No such file or directory */
+#define EUNSUPPORTED           -3       /* Operation not supported */
+#define ENODEV                 -4       /* No such device */
+#define EIO                    -5       /* Input/output error */
+#define EBUSY                  -6       /* Resource in use */
+#define ENOMEM                 -7       /* Out of memory */
+#define ENOFILESYS             -8       /* No such filesystem */
+#define ENAMETOOLONG           -9       /* Name too long */
+#define EINVALIDFS             -10      /* Invalid format for filesystem */
+#define EACCESS                        -11      /* Permission denied */
+#define EINVALID               -12      /* Invalid argument */
+#define EMFILE                 -13      /* File descriptor table full */
+#define ENOTDIR                        -14      /* Not a directory */
+#define EEXIST                 -15      /* File or directory already exists */
+#define ENOSPACE               -16      /* Out of space on device */
+#define EPIPE                  -17      /* Pipe has no reader */
+#define ENOEXEC                        -18      /* Invalid executable format */
+
+#endif  /* GEEKOS_ERRNO_H */
diff --git a/misc/test_vm/include/geekos/fileio.h b/misc/test_vm/include/geekos/fileio.h
new file mode 100644 (file)
index 0000000..d2f2572
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Interface constants and typedefs shared between kernel/user space
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2004, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_FILEIO_H
+#define GEEKOS_FILEIO_H
+
+#include <stddef.h>
+#include <geekos/ktypes.h>
+
+/* Maximum name of a path. */
+#define VFS_MAX_PATH_LEN 1023
+
+/* Maximum length of the name of a filesystem type: e.g., "pfat", "gosfs", etc. */
+#define VFS_MAX_FS_NAME_LEN 15
+
+/* Maximum number of ACL entries in a directory entry. */
+#define VFS_MAX_ACL_ENTRIES 10
+
+/* We assume that all block devices have 512 byte sectors.  */
+#define SECTOR_SIZE 512
+
+/* Maximum length for the name of a block device, e.g. "ide0".  */
+#define BLOCKDEV_MAX_NAME_LEN 15
+
+/*
+ * File permissions.
+ * These are used as flags for Open() VFS function.
+ * O_READ and O_WRITE are also used in the permissions
+ * field of struct VFS_ACL_Entry.
+ */
+#define O_CREATE        0x1    /* Create the file if it doesn't exist. */
+#define O_READ          0x2    /* Open file for reading. */
+#define O_WRITE         0x4    /* Open file for writing. */
+#define O_EXCL          0x8    /* Don't create file if it already exists. */
+
+/*
+ * An entry in an Access Control List (ACL).
+ * Represents a set of permissions for a particular user id.
+ */
+struct VFS_ACL_Entry {
+    uint_t uid:28;
+    uint_t permission:4;
+};
+
+/*
+ * Generic structure representing the metadata for a directory entry.
+ * This is filled in by the Stat() and FStat() VFS functions.
+ */
+struct VFS_File_Stat {
+    int size;
+    int isDirectory:1;
+    int isSetuid:1;
+    struct VFS_ACL_Entry acls[VFS_MAX_ACL_ENTRIES];
+};
+
+/*
+ * Generic directory entry structure.
+ * This is filled in by the Read_Entry() VFS function.
+ */
+struct VFS_Dir_Entry {
+    char name[1024];
+    struct VFS_File_Stat stats;
+};
+
+/*
+ * A request to mount a filesystem.
+ * This is passed as a struct because it would require too many registers
+ * to pass all of the information in registers.
+ */
+struct VFS_Mount_Request {
+    char devname[BLOCKDEV_MAX_NAME_LEN+1];/* Name of block device: e.g., "ide1". */
+    char prefix[VFS_MAX_PATH_LEN+1];   /* Directory prefix to mount on: e.g., "/d". */
+    char fstype[VFS_MAX_FS_NAME_LEN+1];        /* Filesystem type: e.g., "gosfs". */
+};
+
+#endif
diff --git a/misc/test_vm/include/geekos/fmtout.h b/misc/test_vm/include/geekos/fmtout.h
new file mode 100644 (file)
index 0000000..cb3f168
--- /dev/null
@@ -0,0 +1 @@
+#include "../libc/fmtout.h"
diff --git a/misc/test_vm/include/geekos/gdt.h b/misc/test_vm/include/geekos/gdt.h
new file mode 100644 (file)
index 0000000..3ae7b2b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Initialize kernel GDT.
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_GDT_H
+#define GEEKOS_GDT_H
+
+struct Segment_Descriptor;
+
+void DumpGDT();
+void Init_GDT(void);
+struct Segment_Descriptor* Allocate_Segment_Descriptor(void);
+void Free_Segment_Descriptor(struct Segment_Descriptor* desc);
+int Get_Descriptor_Index(struct Segment_Descriptor* desc);
+
+
+#endif  /* GEEKOS_GDT_H */
diff --git a/misc/test_vm/include/geekos/ide.h b/misc/test_vm/include/geekos/ide.h
new file mode 100644 (file)
index 0000000..5c33ecc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * ATA (aka IDE) driver.
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IDE_H
+#define GEEKOS_IDE_H
+
+#ifdef GEEKOS
+
+void Init_IDE(void);
+
+#endif  /* GEEKOS */
+
+#endif  /* GEEKOS_IDE_H */
diff --git a/misc/test_vm/include/geekos/idt.h b/misc/test_vm/include/geekos/idt.h
new file mode 100644 (file)
index 0000000..b88cf28
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * GeekOS IDT initialization code
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IDT_H
+#define GEEKOS_IDT_H
+
+#include <geekos/int.h>
+
+/*
+ * We'll handle all possible interrupts.
+ */
+#define NUM_IDT_ENTRIES 256
+
+/*
+ * Exceptions range from 0-17
+ */
+#define FIRST_EXCEPTION 0
+#define NUM_EXCEPTIONS 18
+
+/*
+ * External IRQs range from 32-47
+ */
+#define FIRST_EXTERNAL_INT 32
+#define NUM_EXTERNAL_INTS 16
+
+struct Interrupt_Gate {
+    ushort_t offsetLow;
+    ushort_t segmentSelector;
+    unsigned reserved : 5;
+    unsigned signature : 8;
+    unsigned dpl : 2;
+    unsigned present : 1;
+    ushort_t offsetHigh;
+};
+
+union IDT_Descriptor {
+    struct Interrupt_Gate ig;
+    /*
+     * In theory we could have members for trap gates
+     * and task gates if we wanted.
+     */
+};
+
+
+
+void DumpIDT();
+void SerialDumpIDT();
+
+void Init_IDT(void);
+void Init_Interrupt_Gate(union IDT_Descriptor* desc, ulong_t addr,
+       int dpl);
+void Install_Interrupt_Handler(int interrupt, Interrupt_Handler handler);
+
+/*
+ * This is defined in lowlevel.asm.
+ * The parameter should consist of 16 bit base,
+ * followed by 32 bit base address, describing the IDT.
+ */
+void Load_IDTR(ushort_t* limitAndBase);
+
+#endif  /* GEEKOS_IDT_H */
diff --git a/misc/test_vm/include/geekos/int.h b/misc/test_vm/include/geekos/int.h
new file mode 100644 (file)
index 0000000..a80d142
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * GeekOS interrupt handling data structures and functions
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * This module describes the C interface which must be implemented
+ * by interrupt handlers, and has the initialization function
+ * for the interrupt system as a whole.
+ */
+
+#ifndef GEEKOS_INT_H
+#define GEEKOS_INT_H
+
+#include <geekos/kassert.h>
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+
+/*
+ * This struct reflects the contents of the stack when
+ * a C interrupt handler function is called.
+ * It must be kept up to date with the code in "lowlevel.asm".
+ */
+struct Interrupt_State {
+    /*
+     * The register contents at the time of the exception.
+     * We save these explicitly.
+     */
+    uint_t gs;
+    uint_t fs;
+    uint_t es;
+    uint_t ds;
+    uint_t ebp;
+    uint_t edi;
+    uint_t esi;
+    uint_t edx;
+    uint_t ecx;
+    uint_t ebx;
+    uint_t eax;
+
+    /*
+     * We explicitly push the interrupt number.
+     * This makes it easy for the handler function to determine
+     * which interrupt occurred.
+     */
+    uint_t intNum;
+
+    /*
+     * This may be pushed by the processor; if not, we push
+     * a dummy error code, so the stack layout is the same
+     * for every type of interrupt.
+     */
+    uint_t errorCode;
+
+    /* These are always pushed on the stack by the processor. */
+    uint_t eip;
+    uint_t cs;
+    uint_t eflags;
+};
+
+/*
+ * An interrupt that occurred in user mode.
+ * If Is_User_Interrupt(state) returns true, then the
+ * Interrupt_State object may be cast to this kind of struct.
+ */
+struct User_Interrupt_State {
+    struct Interrupt_State state;
+    uint_t espUser;
+    uint_t ssUser;
+};
+
+static __inline__ bool Is_User_Interrupt(struct Interrupt_State *state)
+{
+    return (state->cs & 3) == USER_PRIVILEGE;
+}
+
+
+/*
+ * The signature of an interrupt handler.
+ */
+typedef void (*Interrupt_Handler)(struct Interrupt_State* state);
+
+/*
+ * Perform all low- and high-level initialization of the
+ * interrupt system.
+ */
+void Init_Interrupts(void);
+
+/*
+ * Query whether or not interrupts are currently enabled.
+ */
+bool Interrupts_Enabled(void);
+
+/*
+ * Block interrupts.
+ */
+static __inline__ void __Disable_Interrupts(void)
+{
+    __asm__ __volatile__ ("cli");
+}
+#define Disable_Interrupts()           \
+do {                                   \
+    KASSERT(Interrupts_Enabled());     \
+    __Disable_Interrupts();            \
+} while (0)
+
+/*
+ * Unblock interrupts.
+ */
+static __inline__ void __Enable_Interrupts(void)
+{
+    __asm__ __volatile__ ("sti");
+}
+#define Enable_Interrupts()            \
+do {                                   \
+    KASSERT(!Interrupts_Enabled());    \
+    __Enable_Interrupts();             \
+} while (0)
+
+/*
+ * Dump interrupt state struct to screen
+ */
+void Dump_Interrupt_State(struct Interrupt_State* state);
+
+/**
+ * Start interrupt-atomic region.
+ * @return true if interrupts were enabled at beginning of call,
+ * false otherwise.
+ */
+static __inline__ bool Begin_Int_Atomic(void) 
+{
+    bool enabled = Interrupts_Enabled();
+    if (enabled)
+       Disable_Interrupts();
+    return enabled;
+}
+
+/**
+ * End interrupt-atomic region.
+ * @param iflag the value returned from the original Begin_Int_Atomic() call.
+ */
+static __inline__ void End_Int_Atomic(bool iflag)
+{
+    KASSERT(!Interrupts_Enabled());
+    if (iflag) {
+       /* Interrupts were originally enabled, so turn them back on */
+       Enable_Interrupts();
+    }
+}
+
+#define EXCEPTION_DE   0   // Divide by zero
+#define EXCEPTION_DB   1   // reserved
+#define EXCEPTION_NMI  2   // NMI
+#define EXCEPTION_BP   3   // Breakpoint
+#define EXCEPTION_OF   4   // Overflow
+#define EXCEPTION_BR   5   // Bound range exceeded
+#define EXCEPTION_UD   6   // Undefined opcode
+#define EXCEPTION_NM   7   // Math coprocessor gone missing
+#define EXCEPTION_DF   8   // Double fault
+#define EXCEPTION_CO   9   // Coprocessor segment overrrun
+#define EXCEPTION_TS   10  // Invalid TSS
+#define EXCEPTION_NP   11  // Segment not present
+#define EXCEPTION_SS   12  // Stack segment fault
+#define EXCEPTION_GP   13  // General Protection fault
+#define EXCEPTION_PF   14  // Page Fault
+#define EXCEPTION_RES  15  // reserved
+#define EXCEPTION_MF   16  // Math fault
+#define EXCEPTION_AC   17  // Alignment check
+#define EXCEPTION_MC   18  // Machine check
+#define EXCEPTION_XF   19  // SIMD FP exception
+// 20+ are reserved
+
+
+
+
+#endif  /* GEEKOS_INT_H */
diff --git a/misc/test_vm/include/geekos/io.h b/misc/test_vm/include/geekos/io.h
new file mode 100644 (file)
index 0000000..bd23614
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * x86 port IO routines
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IO_H
+#define GEEKOS_IO_H
+
+#include <geekos/ktypes.h>
+
+void Out_Byte(ushort_t port, uchar_t value);
+uchar_t In_Byte(ushort_t port);
+
+void Out_Word(ushort_t port, ushort_t value);
+ushort_t In_Word(ushort_t port);
+
+void IO_Delay(void);
+
+#endif  /* GEEKOS_IO_H */
diff --git a/misc/test_vm/include/geekos/io_devs.h b/misc/test_vm/include/geekos/io_devs.h
new file mode 100644 (file)
index 0000000..6e6e24a
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef _io_devs
+#define _io_devs
+
+
+//
+//
+// PIC: Programmable Interrupt Controller
+//
+#define PIC_MASTER_CMD_ISR_PORT 0x20   // Port where the master PIC command and status register is
+#define PIC_MASTER_IMR_PORT     0x21   // Port where the master PIC interrupt mask register is
+#define PIC_SLAVE_CMD_ISR_PORT  0xa0   // Port where the slave PIC command and status register is
+#define PIC_SLAVE_IMR_PORT      0xa1   // Port where the slave PIC interrupt mask register is
+
+
+#define BOOT_STATE_CARD_PORT    0x80   // hex codes sent here for display
+
+
+
+#endif
diff --git a/misc/test_vm/include/geekos/irq.h b/misc/test_vm/include/geekos/irq.h
new file mode 100644 (file)
index 0000000..3164170
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * This is the device-driver interface to the interrupt system.
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IRQ_H
+#define GEEKOS_IRQ_H
+
+#include <geekos/int.h>
+
+void Install_IRQ(int irq, Interrupt_Handler handler);
+ushort_t Get_IRQ_Mask(void);
+void Set_IRQ_Mask(ushort_t mask);
+void Enable_IRQ(int irq);
+void Disable_IRQ(int irq);
+
+/*
+ * IRQ handlers should call these to begin and end the
+ * interrupt.
+ */
+void Begin_IRQ(struct Interrupt_State* state);
+void End_IRQ(struct Interrupt_State* state);
+
+#endif  /* GEEKOS_IRQ_H */
diff --git a/misc/test_vm/include/geekos/kassert.h b/misc/test_vm/include/geekos/kassert.h
new file mode 100644 (file)
index 0000000..434a8a8
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Definition of KASSERT() macro, and other useful debug macros
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KASSERT_H
+#define GEEKOS_KASSERT_H
+
+#include <geekos/screen.h>
+
+#ifndef NDEBUG
+
+struct Kernel_Thread;
+extern struct Kernel_Thread* g_currentThread;
+
+#define KASSERT(cond)                                  \
+do {                                                   \
+    if (!(cond)) {                                     \
+       Set_Current_Attr(ATTRIB(RED, GRAY|BRIGHT));     \
+       Print("Failed assertion in %s: %s at %s, line %d, RA=%lx, thread=%p\n",\
+               __func__, #cond, __FILE__, __LINE__,    \
+               (ulong_t) __builtin_return_address(0),  \
+               g_currentThread);                       \
+       while (1)                                       \
+          ;                                            \
+    }                                                  \
+} while (0)
+
+#define TODO(message)                                  \
+do {                                                   \
+    Set_Current_Attr(ATTRIB(BLUE, GRAY|BRIGHT));       \
+    Print("Unimplemented feature: %s\n", (message));   \
+    while (1)                                          \
+       ;                                               \
+} while (0)
+
+/*
+ * Spin for some number of iterations.
+ * This is useful for slowing down things that go by too
+ * quickly.
+ */
+#define PAUSE(count)                   \
+do {                                   \
+    ulong_t i;                         \
+    for (i = 0; i < (count); ++i)      \
+       ;                               \
+} while (0)
+
+#else
+
+/*
+ * The debug macros are no-ops when NDEBUG is defined.
+ */
+#define KASSERT(cond)
+#define TODO(message)
+#define PAUSE(count)
+
+#endif
+
+/*
+ * Stop dead.
+ * Its behavior does not depend on whether or not this
+ * is a debug build.
+ */
+#define STOP() while (1)
+
+/*
+ * Panic function.
+ */
+#define Panic(args...)                         \
+do {                                           \
+    Set_Current_Attr(ATTRIB(RED, GRAY|BRIGHT));        \
+    Print(args);                               \
+    while (1) ;                                        \
+} while (0)
+
+#endif  /* GEEKOS_KASSERT_H */
diff --git a/misc/test_vm/include/geekos/keyboard.h b/misc/test_vm/include/geekos/keyboard.h
new file mode 100644 (file)
index 0000000..1ab5c42
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Keyboard driver
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KEYBOARD_H
+#define GEEKOS_KEYBOARD_H
+
+#include <geekos/ktypes.h>
+
+/* ----------------------------------------------------------------------
+ * Hardware stuff
+ * ---------------------------------------------------------------------- */
+
+#ifdef GEEKOS
+
+#define KB_IRQ 1
+
+/*
+ * I/O ports
+ */
+#define KB_CMD 0x64
+#define KB_DATA 0x60
+
+/*
+ * Bits in status port
+ */
+#define KB_OUTPUT_FULL 0x01
+
+/*
+ * High bit in scan code is set when key is released
+ */
+#define KB_KEY_RELEASE 0x80
+
+#endif  /* GEEKOS */
+
+/* ----------------------------------------------------------------------
+ * Software keycodes
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Each keyboard event generates a 16 bit code.
+ * - The low 10 bits indicate which key was used.
+ * - If bit 8 (KEY_SPECIAL_FLAG) is 0, then the low 8 bits contain
+ *   the ASCII code.
+ * - The flags indicate the shift/alt/control state,
+ *   and whether the event was a make or release.
+ */
+
+typedef ushort_t Keycode;
+
+/*
+ * Flags
+ */
+#define KEY_SPECIAL_FLAG 0x0100
+#define KEY_KEYPAD_FLAG  0x0200
+#define KEY_SHIFT_FLAG   0x1000
+#define KEY_ALT_FLAG     0x2000
+#define KEY_CTRL_FLAG    0x4000
+#define KEY_RELEASE_FLAG 0x8000
+
+/*
+ * Special key codes
+ */
+#define _SPECIAL(num) (KEY_SPECIAL_FLAG | (num))
+#define KEY_UNKNOWN _SPECIAL(0)
+#define KEY_F1      _SPECIAL(1)
+#define KEY_F2      _SPECIAL(2)
+#define KEY_F3      _SPECIAL(3)
+#define KEY_F4      _SPECIAL(4)
+#define KEY_F5      _SPECIAL(5)
+#define KEY_F6      _SPECIAL(6)
+#define KEY_F7      _SPECIAL(7)
+#define KEY_F8      _SPECIAL(8)
+#define KEY_F9      _SPECIAL(9)
+#define KEY_F10     _SPECIAL(10)
+#define KEY_F11     _SPECIAL(11)
+#define KEY_F12     _SPECIAL(12)
+#define KEY_LCTRL   _SPECIAL(13)
+#define KEY_RCTRL   _SPECIAL(14)
+#define KEY_LSHIFT  _SPECIAL(15)
+#define KEY_RSHIFT  _SPECIAL(16)
+#define KEY_LALT    _SPECIAL(17)
+#define KEY_RALT    _SPECIAL(18)
+#define KEY_PRINTSCRN _SPECIAL(19)
+#define KEY_CAPSLOCK _SPECIAL(20)
+#define KEY_NUMLOCK _SPECIAL(21)
+#define KEY_SCRLOCK _SPECIAL(22)
+#define KEY_SYSREQ  _SPECIAL(23)
+
+/*
+ * Keypad keys
+ */
+#define KEYPAD_START 128
+#define _KEYPAD(num) (KEY_KEYPAD_FLAG | KEY_SPECIAL_FLAG | (num+KEYPAD_START))
+#define KEY_KPHOME  _KEYPAD(0)
+#define KEY_KPUP    _KEYPAD(1)
+#define KEY_KPPGUP  _KEYPAD(2)
+#define KEY_KPMINUS _KEYPAD(3)
+#define KEY_KPLEFT  _KEYPAD(4)
+#define KEY_KPCENTER _KEYPAD(5)
+#define KEY_KPRIGHT _KEYPAD(6)
+#define KEY_KPPLUS  _KEYPAD(7)
+#define KEY_KPEND   _KEYPAD(8)
+#define KEY_KPDOWN  _KEYPAD(9)
+#define KEY_KPPGDN  _KEYPAD(10)
+#define KEY_KPINSERT _KEYPAD(11)
+#define KEY_KPDEL   _KEYPAD(12)
+
+/*
+ * ASCII codes for which there is no convenient C representation
+ */
+#define ASCII_ESC 0x1B
+#define ASCII_BS  0x08
+
+#ifdef GEEKOS
+
+/*
+ * Public functions
+ */
+void Init_Keyboard(void);
+bool Read_Key(Keycode* keycode);
+Keycode Wait_For_Key(void);
+
+#endif  /* GEEKOS */
+
+#endif  /* GEEKOS_KEYBOARD_H */
diff --git a/misc/test_vm/include/geekos/kthread.h b/misc/test_vm/include/geekos/kthread.h
new file mode 100644 (file)
index 0000000..f0ef23c
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Kernel threads
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KTHREAD_H
+#define GEEKOS_KTHREAD_H
+
+#include <geekos/ktypes.h>
+#include <geekos/list.h>
+
+struct Kernel_Thread;
+struct User_Context;
+struct Interrupt_State;
+
+/*
+ * Queue of threads.
+ * This is used for the run queue(s), and also for
+ * thread synchronization and communication.
+ */
+DEFINE_LIST(Thread_Queue, Kernel_Thread);
+
+/*
+ * List which includes all threads.
+ */
+DEFINE_LIST(All_Thread_List, Kernel_Thread);
+
+/*
+ * Kernel thread context data structure.
+ * NOTE: there is assembly code in lowlevel.asm that depends
+ * on the offsets of the fields in this struct, so if you change
+ * the layout, make sure everything gets updated.
+ */
+struct Kernel_Thread {
+    ulong_t esp;                        /* offset 0 */
+    volatile ulong_t numTicks;          /* offset 4 */
+    int priority;
+    DEFINE_LINK(Thread_Queue, Kernel_Thread);
+    void* stackPage;
+    struct User_Context* userContext;
+    struct Kernel_Thread* owner;
+    int refCount;
+
+    /* These fields are used to implement the Join() function */
+    bool alive;
+    struct Thread_Queue joinQueue;
+    int exitCode;
+
+    /* The kernel thread id; also used as process id */
+    int pid;
+
+    /* Link fields for list of all threads in the system. */
+    DEFINE_LINK(All_Thread_List, Kernel_Thread);
+
+    /* Array of MAX_TLOCAL_KEYS pointers to thread-local data. */
+#define MAX_TLOCAL_KEYS 128
+    const void* tlocalData[MAX_TLOCAL_KEYS];
+
+};
+
+/*
+ * Define Thread_Queue and All_Thread_List access and manipulation functions.
+ */
+IMPLEMENT_LIST(Thread_Queue, Kernel_Thread);
+IMPLEMENT_LIST(All_Thread_List, Kernel_Thread);
+
+static __inline__ void Enqueue_Thread(struct Thread_Queue *queue, struct Kernel_Thread *kthread) {
+    Add_To_Back_Of_Thread_Queue(queue, kthread);
+}
+
+static __inline__ void Remove_Thread(struct Thread_Queue *queue, struct Kernel_Thread *kthread) {
+    Remove_From_Thread_Queue(queue, kthread);
+}
+
+/*
+ * Thread start functions should have this signature.
+ */
+typedef void (*Thread_Start_Func)(ulong_t arg);
+
+/*
+ * Thread priorities
+ */
+#define PRIORITY_IDLE    0
+#define PRIORITY_USER    1
+#define PRIORITY_LOW     2
+#define PRIORITY_NORMAL  5
+#define PRIORITY_HIGH   10
+
+
+/*
+ * Scheduler operations.
+ */
+void Init_Scheduler(void);
+struct Kernel_Thread* Start_Kernel_Thread(
+    Thread_Start_Func startFunc,
+    ulong_t arg,
+    int priority,
+    bool detached
+);
+struct Kernel_Thread* Start_User_Thread(struct User_Context* userContext, bool detached);
+void Make_Runnable(struct Kernel_Thread* kthread);
+void Make_Runnable_Atomic(struct Kernel_Thread* kthread);
+struct Kernel_Thread* Get_Current(void);
+struct Kernel_Thread* Get_Next_Runnable(void);
+void Schedule(void);
+void Yield(void);
+void Exit(int exitCode) __attribute__ ((noreturn));
+int Join(struct Kernel_Thread* kthread);
+struct Kernel_Thread* Lookup_Thread(int pid);
+
+/*
+ * Thread context switch function, defined in lowlevel.asm
+ */
+void Switch_To_Thread(struct Kernel_Thread*);
+
+/*
+ * Wait queue functions.
+ */
+void Wait(struct Thread_Queue* waitQueue);
+void Wake_Up(struct Thread_Queue* waitQueue);
+void Wake_Up_One(struct Thread_Queue* waitQueue);
+
+/*
+ * Pointer to currently executing thread.
+ */
+extern struct Kernel_Thread* g_currentThread;
+
+/*
+ * Boolean flag indicating that we need to choose a new runnable thread.
+ */
+extern int g_needReschedule;
+
+/*
+ * Boolean flag indicating that preemption should be disabled.
+ */
+extern volatile int g_preemptionDisabled;
+
+/*
+ * Thread-local data information
+ */
+#define MIN_DESTRUCTOR_ITERATIONS 4
+
+typedef void (*tlocal_destructor_t)(void *);
+typedef unsigned int tlocal_key_t;
+
+extern int Tlocal_Create(tlocal_key_t *, tlocal_destructor_t);
+extern void Tlocal_Put(tlocal_key_t, const void *);
+extern void *Tlocal_Get(tlocal_key_t);
+
+/* Print list of all threads, for debugging. */
+extern void Dump_All_Thread_List(void);
+
+
+#endif  /* GEEKOS_KTHREAD_H */
diff --git a/misc/test_vm/include/geekos/ktypes.h b/misc/test_vm/include/geekos/ktypes.h
new file mode 100644 (file)
index 0000000..7c72a3b
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Kernel data types
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KTYPES_H
+#define GEEKOS_KTYPES_H
+
+/*
+ * GeekOS uses the C99 bool type, with true and false
+ * constant values.
+ */
+#include <stdbool.h>
+
+/*
+ * Shorthand for commonly used integer types.
+ */
+typedef unsigned long ulong_t;
+typedef unsigned int uint_t;
+typedef unsigned short ushort_t;
+typedef unsigned char uchar_t;
+typedef unsigned  long long ullong_t;
+
+/*
+ * MIN() and MAX() macros.
+ * By using gcc extensions, they are type-correct and
+ * evaulate their arguments only once.
+ */
+#define MIN(a,b) ({typeof (a) _a = (a); typeof (b) _b = (b); (_a < _b) ? _a : _b; })
+#define MAX(a,b) ({typeof (a) _a = (a); typeof (b) _b = (b); (_a < _b) ? _a : _b; })
+
+/*
+ * Some ASCII character access and manipulation macros.
+ */
+#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) + ('a' - 'A')) : (c))
+#define TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? ((c) - ('a' - 'A')) : (c))
+
+#endif  /* GEEKOS_KTYPES_H */
diff --git a/misc/test_vm/include/geekos/list.h b/misc/test_vm/include/geekos/list.h
new file mode 100644 (file)
index 0000000..96a7870
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Generic list data type
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_LIST_H
+#define GEEKOS_LIST_H
+
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+
+/*
+ * Define a list type.
+ */
+#define DEFINE_LIST(listTypeName, nodeTypeName)                \
+struct listTypeName {                                  \
+    struct nodeTypeName *head, *tail;                  \
+}
+
+/*
+ * Define members of a struct to be used as link fields for
+ * membership in given list type.
+ */
+#define DEFINE_LINK(listTypeName, nodeTypeName) \
+    struct nodeTypeName * prev##listTypeName, * next##listTypeName
+
+/*
+ * Define inline list manipulation and access functions.
+ */
+#define IMPLEMENT_LIST(LType, NType)                                                           \
+static __inline__ void Clear_##LType(struct LType *listPtr) {                                  \
+    listPtr->head = listPtr->tail = 0;                                                         \
+}                                                                                              \
+static __inline__ bool Is_Member_Of_##LType(struct LType *listPtr, struct NType *nodePtr) {    \
+    struct NType *cur = listPtr->head;                                                         \
+    while (cur != 0) {                                                                         \
+       if (cur == nodePtr)                                                                     \
+           return true;                                                                        \
+       cur = cur->next##LType;                                                                 \
+    }                                                                                          \
+    return false;                                                                              \
+}                                                                                              \
+static __inline__ struct NType * Get_Front_Of_##LType(struct LType *listPtr) {                 \
+    return listPtr->head;                                                                      \
+}                                                                                              \
+static __inline__ struct NType * Get_Back_Of_##LType(struct LType *listPtr) {                  \
+    return listPtr->tail;                                                                      \
+}                                                                                              \
+static __inline__ struct NType * Get_Next_In_##LType(struct NType *nodePtr) {                  \
+    return nodePtr->next##LType;                                                               \
+}                                                                                              \
+static __inline__ void Set_Next_In_##LType(struct NType *nodePtr, struct NType *value) {       \
+    nodePtr->next##LType = value;                                                              \
+}                                                                                              \
+static __inline__ struct NType * Get_Prev_In_##LType(struct NType *nodePtr) {                  \
+    return nodePtr->prev##LType;                                                               \
+}                                                                                              \
+static __inline__ void Set_Prev_In_##LType(struct NType *nodePtr, struct NType *value) {       \
+    nodePtr->prev##LType = value;                                                              \
+}                                                                                              \
+static __inline__ void Add_To_Front_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \
+    KASSERT(!Is_Member_Of_##LType(listPtr, nodePtr));                                          \
+    nodePtr->prev##LType = 0;                                                                  \
+    if (listPtr->head == 0) {                                                                  \
+       listPtr->head = listPtr->tail = nodePtr;                                                \
+       nodePtr->next##LType = 0;                                                               \
+    } else {                                                                                   \
+       listPtr->head->prev##LType = nodePtr;                                                   \
+       nodePtr->next##LType = listPtr->head;                                                   \
+       listPtr->head = nodePtr;                                                                \
+    }                                                                                          \
+}                                                                                              \
+static __inline__ void Add_To_Back_Of_##LType(struct LType *listPtr, struct NType *nodePtr) {  \
+  /* KASSERT(!Is_Member_Of_##LType(listPtr, nodePtr)); */              \
+    nodePtr->next##LType = 0;                                                                  \
+    if (listPtr->tail == 0) {                                                                  \
+       listPtr->head = listPtr->tail = nodePtr;                                                \
+       nodePtr->prev##LType = 0;                                                               \
+    }                                                                                          \
+    else {                                                                                     \
+       listPtr->tail->next##LType = nodePtr;                                                   \
+       nodePtr->prev##LType = listPtr->tail;                                                   \
+       listPtr->tail = nodePtr;                                                                \
+    }                                                                                          \
+}                                                                                              \
+static __inline__ void Append_##LType(struct LType *listToModify, struct LType *listToAppend) {        \
+    if (listToAppend->head != 0) {                                                             \
+       if (listToModify->head == 0) {                                                          \
+           listToModify->head = listToAppend->head;                                            \
+           listToModify->tail = listToAppend->tail;                                            \
+       } else {                                                                                \
+           KASSERT(listToAppend->head != 0);                                                   \
+           KASSERT(listToModify->tail != 0);                                                   \
+           listToAppend->head->prev##LType = listToModify->tail;                               \
+           listToModify->tail->next##LType = listToAppend->head;                               \
+           listToModify->tail = listToAppend->tail;                                            \
+       }                                                                                       \
+    }                                                                                          \
+    listToAppend->head = listToAppend->tail = 0;                                               \
+}                                                                                              \
+static __inline__ struct NType * Remove_From_Front_Of_##LType(struct LType *listPtr) {         \
+    struct NType *nodePtr;                                                                     \
+    nodePtr = listPtr->head;                                                                   \
+    KASSERT(nodePtr != 0);                                                                     \
+    listPtr->head = listPtr->head->next##LType;                                                        \
+    if (listPtr->head == 0)                                                                    \
+       listPtr->tail = 0;                                                                      \
+    else                                                                                       \
+       listPtr->head->prev##LType = 0;                                                         \
+    return nodePtr;                                                                            \
+}                                                                                              \
+static __inline__ void Remove_From_##LType(struct LType *listPtr, struct NType *nodePtr) {     \
+    KASSERT(Is_Member_Of_##LType(listPtr, nodePtr));                                           \
+    if (nodePtr->prev##LType != 0)                                                             \
+       nodePtr->prev##LType->next##LType = nodePtr->next##LType;                               \
+    else                                                                                       \
+       listPtr->head = nodePtr->next##LType;                                                   \
+    if (nodePtr->next##LType != 0)                                                             \
+       nodePtr->next##LType->prev##LType = nodePtr->prev##LType;                               \
+    else                                                                                       \
+       listPtr->tail = nodePtr->prev##LType;                                                   \
+}                                                                                              \
+static __inline__ bool Is_##LType##_Empty(struct LType *listPtr) {                             \
+    return listPtr->head == 0;                                                                 \
+}
+
+#endif  /* GEEKOS_LIST_H */
diff --git a/misc/test_vm/include/geekos/malloc.h b/misc/test_vm/include/geekos/malloc.h
new file mode 100644 (file)
index 0000000..ba70e92
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * GeekOS memory allocation API
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_MALLOC_H
+#define GEEKOS_MALLOC_H
+
+#include <geekos/ktypes.h>
+
+void Init_Heap(ulong_t start, ulong_t size);
+void* Malloc(ulong_t size);
+void Free(void* buf);
+
+#endif  /* GEEKOS_MALLOC_H */
diff --git a/misc/test_vm/include/geekos/mem.h b/misc/test_vm/include/geekos/mem.h
new file mode 100644 (file)
index 0000000..87489f4
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_MEM_H
+#define GEEKOS_MEM_H
+
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+#include <geekos/list.h>
+#include <geekos/paging.h>
+
+struct Boot_Info;
+
+/*
+ * Page flags
+ */
+#define PAGE_AVAIL     0x0000   /* page is on the freelist */
+#define PAGE_KERN      0x0001   /* page used by kernel code or data */
+#define PAGE_HW        0x0002   /* page used by hardware (e.g., ISA hole) */
+#define PAGE_ALLOCATED 0x0004   /* page is allocated */
+#define PAGE_UNUSED    0x0008   /* page is unused */
+#define PAGE_HEAP      0x0010   /* page is in kernel heap */
+#define PAGE_PAGEABLE  0x0020   /* page can be paged out */
+#define PAGE_LOCKED    0x0040    /* page is taken should not be freed */
+
+/*
+ * PC memory map
+ */
+#define ISA_HOLE_START 0x0A0000
+#define ISA_HOLE_END   0x100000
+
+/*
+ * We reserve the two pages just after the ISA hole for the initial
+ * kernel thread's context object and stack.
+ */
+//#define HIGHMEM_START (ISA_HOLE_END + 8192)
+#define HIGHMEM_START ISA_HOLE_END
+/*
+ * Make the kernel heap this size
+ */
+#define KERNEL_HEAP_SIZE (1024*1024)
+
+struct Page;
+
+/*
+ * List datatype for doubly-linked list of Pages.
+ */
+DEFINE_LIST(Page_List, Page);
+
+/*
+ * Each page of physical memory has one of these structures
+ * associated with it, to do allocation and bookkeeping.
+ */
+struct Page {
+    unsigned flags;                     /* Flags indicating state of page */
+    DEFINE_LINK(Page_List, Page);       /* Link fields for Page_List */
+    int clock;
+    ulong_t vaddr;                      /* User virtual address where page is mapped */
+    pte_t *entry;                       /* Page table entry referring to the page */
+};
+
+IMPLEMENT_LIST(Page_List, Page);
+
+void Init_Mem(struct Boot_Info* bootInfo);
+void Init_BSS(void);
+void* Alloc_Page(void);
+void* Alloc_Pageable_Page(pte_t *entry, ulong_t vaddr);
+void Free_Page(void* pageAddr);
+
+/*
+ * Determine if given address is a multiple of the page size.
+ */
+static __inline__ bool Is_Page_Multiple(ulong_t addr)
+{
+    return addr == (addr & ~(PAGE_MASK));
+}
+
+/*
+ * Round given address up to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Up_To_Page(ulong_t addr)
+{
+    if ((addr & PAGE_MASK) != 0) {
+       addr &= ~(PAGE_MASK);
+       addr += PAGE_SIZE;
+    }
+    return addr;
+}
+
+/*
+ * Round given address down to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Down_To_Page(ulong_t addr)
+{
+    return addr & (~PAGE_MASK);
+}
+
+/*
+ * Get the index of the page in memory.
+ */
+static __inline__ int Page_Index(ulong_t addr)
+{
+    return (int) (addr >> PAGE_POWER);
+}
+
+/*
+ * Get the Page struct associated with given address.
+ */
+static __inline__ struct Page *Get_Page(ulong_t addr)
+{
+    extern struct Page* g_pageList;
+    return &g_pageList[Page_Index(addr)];
+}
+
+/*
+ * Get the physical address of the memory represented by given Page object.
+ */
+static __inline__ ulong_t Get_Page_Address(struct Page *page)
+{
+    extern struct Page* g_pageList;
+    ulong_t index = page - g_pageList;
+    return index << PAGE_POWER;
+}
+
+#endif  /* GEEKOS_MEM_H */
diff --git a/misc/test_vm/include/geekos/mem_bak.h b/misc/test_vm/include/geekos/mem_bak.h
new file mode 100644 (file)
index 0000000..fdac1aa
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_MEM_H
+#define GEEKOS_MEM_H
+
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+#include <geekos/list.h>
+#include <geekos/paging.h>
+
+struct Boot_Info;
+
+/*
+ * Page flags
+ */
+#define PAGE_AVAIL     0x0000   /* page is on the freelist */
+#define PAGE_KERN      0x0001   /* page used by kernel code or data */
+#define PAGE_HW        0x0002   /* page used by hardware (e.g., ISA hole) */
+#define PAGE_ALLOCATED 0x0004   /* page is allocated */
+#define PAGE_UNUSED    0x0008   /* page is unused */
+#define PAGE_HEAP      0x0010   /* page is in kernel heap */
+#define PAGE_PAGEABLE  0x0020   /* page can be paged out */
+#define PAGE_LOCKED    0x0040    /* page is taken should not be freed */
+#define PAGE_VM        0x0080    /* page is used by the VM */
+
+
+struct Page;
+
+/*
+ * List datatype for doubly-linked list of Pages.
+ */
+DEFINE_LIST(Page_List, Page);
+
+/*
+ * Each page of physical memory has one of these structures
+ * associated with it, to do allocation and bookkeeping.
+ */
+struct Page {
+    unsigned flags;                     /* Flags indicating state of page */
+    DEFINE_LINK(Page_List, Page);       /* Link fields for Page_List */
+    int clock;
+    ulong_t vaddr;                      /* User virtual address where page is mapped */
+    pte_t *entry;                       /* Page table entry referring to the page */
+};
+
+IMPLEMENT_LIST(Page_List, Page);
+
+void Init_Mem(struct Boot_Info* bootInfo);
+void Init_BSS(void);
+void* Alloc_Page(void);
+void* Alloc_Pageable_Page(pte_t *entry, ulong_t vaddr);
+void Free_Page(void* pageAddr);
+
+/*
+ * Determine if given address is a multiple of the page size.
+ */
+static __inline__ bool Is_Page_Multiple(ulong_t addr)
+{
+    return addr == (addr & ~(PAGE_MASK));
+}
+
+/*
+ * Round given address up to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Up_To_Page(ulong_t addr)
+{
+    if ((addr & PAGE_MASK) != 0) {
+       addr &= ~(PAGE_MASK);
+       addr += PAGE_SIZE;
+    }
+    return addr;
+}
+
+/*
+ * Round given address down to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Down_To_Page(ulong_t addr)
+{
+    return addr & (~PAGE_MASK);
+}
+
+/*
+ * Get the index of the page in memory.
+ */
+static __inline__ int Page_Index(ulong_t addr)
+{
+    return (int) (addr >> PAGE_POWER);
+}
+
+
+/*
+ * Get the Page struct associated with given address.
+ */
+static __inline__ struct Page *Get_Page(ulong_t addr)
+{
+    extern struct Page* g_pageList;
+      return &g_pageList[Page_Index(addr)];
+      //return g_pageList + (sizeof(struct Page) * (int)(addr >> PAGE_POWER));
+}
+
+/*
+ * Get the physical address of the memory represented by given Page object.
+ */
+static __inline__ ulong_t Get_Page_Address(struct Page *page)
+{
+    extern struct Page* g_pageList;
+    ulong_t index = page - g_pageList;
+    return index << PAGE_POWER;
+}
+
+#endif  /* GEEKOS_MEM_H */
diff --git a/misc/test_vm/include/geekos/paging.h b/misc/test_vm/include/geekos/paging.h
new file mode 100644 (file)
index 0000000..1810f0b
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Paging (virtual memory) support
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_PAGING_H
+#define GEEKOS_PAGING_H
+
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+#include <geekos/bootinfo.h>
+#include <geekos/list.h>
+
+struct Page;
+struct User_Context;
+
+#define NUM_PAGE_TABLE_ENTRIES 1024
+#define NUM_PAGE_DIR_ENTRIES   1024
+
+#define PAGE_DIRECTORY_INDEX(x)        ((((uint_t)x) >> 22) & 0x3ff)
+#define PAGE_TABLE_INDEX(x)    ((((uint_t)x) >> 12) & 0x3ff)
+#define PAGE_OFFSET(x)          ((((uint_t)x) & 0xfff))
+
+#define PAGE_ALLIGNED_ADDR(x)   (((uint_t) (x)) >> 12)
+#define PAGE_ADDR(x)   (PAGE_ALLIGNED_ADDR(x) << 12)
+
+/*
+ * Bits for flags field of pde_t and pte_t.
+ */
+#define VM_WRITE   1    /* Memory is writable */
+#define VM_USER    2    /* Memory is accessible to user code */
+#define VM_NOCACHE 8    /* Memory should not be cached */
+#define VM_READ    0    /* Memory can be read (ignored for x86) */
+#define VM_EXEC    0    /* Memory can be executed (ignored for x86) */
+
+
+/*
+ * Page directory entry datatype.
+ * If marked as present, it specifies the physical address
+ * and permissions of a page table.
+ */
+typedef struct {
+    uint_t present:1;
+    uint_t flags:4;
+    uint_t accessed:1;
+    uint_t reserved:1;
+    uint_t largePages:1;
+    uint_t globalPage:1;
+    uint_t kernelInfo:3;
+    uint_t pageTableBaseAddr:20;
+} pde_t;
+
+/*
+ * Page table entry datatype.
+ * If marked as present, it specifies the physical address
+ * and permissions of a page of memory.
+ */
+typedef struct {
+    uint_t present:1;
+    uint_t flags:4;
+    uint_t accessed:1;
+    uint_t dirty:1;
+    uint_t pteAttribute:1;
+    uint_t globalPage:1;
+    uint_t kernelInfo:3;
+    uint_t pageBaseAddr:20;
+} pte_t;
+
+/*
+ * Datatype representing the hardware error code
+ * pushed onto the stack by the processor on a page fault.
+ * The error code is stored in the "errorCode" field
+ * of the Interrupt_State struct.
+ */
+typedef struct {
+    uint_t protectionViolation:1;
+    uint_t writeFault:1;
+    uint_t userModeFault:1;
+    uint_t reservedBitFault:1;
+    uint_t reserved:28;
+} faultcode_t;
+
+/*
+ * Bits used in the kernelInfo field of the PTE's:
+ */
+#define KINFO_PAGE_ON_DISK     0x4      /* Page not present; contents in paging file */
+
+void Init_VM(struct Boot_Info *bootInfo);
+void Init_Paging(void);
+
+extern void Flush_TLB(void);
+extern void Set_PDBR(pde_t *pageDir);
+extern pde_t *Get_PDBR(void);
+extern void Enable_Paging(pde_t *pageDir);
+
+/*
+ * Return the address that caused a page fault.
+ */
+static __inline__ ulong_t Get_Page_Fault_Address(void)
+{
+    ulong_t faultAddress;
+    __asm__ __volatile__ (
+       "mov %%cr2, %0"
+       : "=r" (faultAddress)
+    );
+    return faultAddress;
+}
+
+void SerialPrintPD(pde_t *pde);
+void SerialPrintPT(void *starting_address, pte_t *pte);
+void SerialPrintPDE(void *virtual_address, pde_t *pde);
+void SerialPrintPTE(void *virtual_address,pte_t *pte);
+void SerialDumpPageTables(pde_t *pde);
+
+pte_t *LookupPage(void *vaddr);
+
+pte_t *MapPage(void *vaddr, pte_t *pte, int alloc_pde);
+pte_t *UnMapPage(void *vaddr);
+
+int Find_Space_On_Paging_File(void);
+void Free_Space_On_Paging_File(int pagefileIndex);
+void Write_To_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex);
+void Read_From_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex);
+
+
+#endif
diff --git a/misc/test_vm/include/geekos/range.h b/misc/test_vm/include/geekos/range.h
new file mode 100644 (file)
index 0000000..15bd023
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Range checking
+ * Copyright (c) 2003, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_RANGE_H
+#define GEEKOS_RANGE_H
+
+#include <geekos/ktypes.h>
+
+/*
+ * TODO: think about these, make sure they're correct
+ */
+
+/**
+ * Check that given range is "proper".
+ * @param start the start of the range
+ * @param size the size of the range
+ * @return true if range is properly within the space
+ *   0(inclusive)..ULONG_MAX(inclusive)
+ */
+static __inline__ bool
+Check_Range_Proper(ulong_t start, ulong_t size)
+{
+    /*
+     * Wrap around is only permitted if the sum is zero.
+     * E.g., start=ULONG_MAX, size==1 is a valid range.
+     */
+    ulong_t sum = start + size;
+    return start <= sum || (sum == 0);
+}
+
+/**
+ * Check that given range lies entirely under the
+ * maximum value specified (exclusive).
+ * @param start the start of the range
+ * @param size the size of the range
+ * @param max the lowest address NOT allowed to be in the range
+ * @return true if range falls entirely within the range
+ *   0(inclusive)..max(exclusive)
+ */
+static __inline__ bool
+Check_Range_Under(ulong_t start, ulong_t size, ulong_t max)
+{
+    if (!Check_Range_Proper(start, size))
+       return false;
+
+    return start < max && (start + size) <= max;
+}
+
+#endif  /* GEEKOS_RANGE_H */
diff --git a/misc/test_vm/include/geekos/reboot.h b/misc/test_vm/include/geekos/reboot.h
new file mode 100644 (file)
index 0000000..e954a2c
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef REBOOT_H
+#define REBOOT_H
+
+void machine_real_restart();
+
+
+#endif
diff --git a/misc/test_vm/include/geekos/screen.h b/misc/test_vm/include/geekos/screen.h
new file mode 100644 (file)
index 0000000..241fc6f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * GeekOS text screen output
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_SCREEN_H
+#define GEEKOS_SCREEN_H
+
+#include <geekos/ktypes.h>
+
+#define BLACK   0
+#define BLUE    1
+#define GREEN   2
+#define CYAN    3
+#define RED     4
+#define MAGENTA 5
+#define AMBER   6
+#define GRAY    7
+#define BRIGHT  8
+#define ATTRIB(bg,fg) ((fg)|((bg)<<4))
+
+#define NUMCOLS 80
+#define NUMROWS 25
+
+#define TABWIDTH 8
+
+#ifdef GEEKOS
+
+/*
+ * VGA hardware stuff, for accessing the text display
+ * memory and controlling the cursor
+ */
+#define VIDMEM_ADDR 0xb8000
+#define VIDMEM ((uchar_t*) VIDMEM_ADDR)
+#define CRT_ADDR_REG 0x3D4
+#define CRT_DATA_REG 0x3D5
+#define CRT_CURSOR_LOC_HIGH_REG 0x0E
+#define CRT_CURSOR_LOC_LOW_REG 0x0F
+
+void Init_Screen(void);
+void Clear_Screen(void);
+void Get_Cursor(int* row, int* col);
+bool Put_Cursor(int row, int col);
+uchar_t Get_Current_Attr(void);
+void Set_Current_Attr(uchar_t attrib);
+void Put_Char(int c);
+void Put_String(const char* s);
+void Put_Buf(const char* buf, ulong_t length);
+void Print(const char* fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+#endif  /* GEEKOS */
+
+#endif  /* GEEKOS_SCREEN_H */
diff --git a/misc/test_vm/include/geekos/segment.h b/misc/test_vm/include/geekos/segment.h
new file mode 100644 (file)
index 0000000..06f302a
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * General data structures and routines for segmentation
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+#ifndef GEEKOS_SEGMENT_H
+#define GEEKOS_SEGMENT_H
+
+#include <geekos/ktypes.h>
+
+struct TSS;
+
+#if __TINYC__
+#define PACKED
+#else
+#define PACKED __attribute__((packed))
+#endif
+
+/*
+ * The general format of a segment descriptor.
+ */
+struct Segment_Descriptor {
+    ushort_t sizeLow        PACKED ;
+    uint_t baseLow     : 24 PACKED ;
+    uint_t type        : 4  PACKED ;
+    uint_t system      : 1  PACKED ;
+    uint_t dpl         : 2  PACKED ;
+    uint_t present     : 1  PACKED ;
+    uint_t sizeHigh    : 4  PACKED ;
+    uint_t avail       : 1  PACKED ;
+    uint_t reserved    : 1  PACKED ;  /* set to zero */
+    uint_t dbBit       : 1  PACKED ;
+    uint_t granularity : 1  PACKED ;
+    uchar_t baseHigh        PACKED ;
+};
+
+/**
+ * Construct a segment selector.
+ * @param rpl requestor privilege level; should be KERNEL_PRIVILEGE
+ *    for kernel segments and USER_PRIVILEGE for user segments
+ * @param segmentIsInGDT true if the referenced segment descriptor
+ *    is defined in the GDT, false if it is defined in the LDT
+ * @param index index of the segment descriptor
+ * @return the segment selector
+ */
+static __inline__ ushort_t Selector(int rpl, bool segmentIsInGDT, int index)
+{
+    ushort_t selector = 0;
+    selector = (rpl & 0x3) | ((segmentIsInGDT ? 0 : 1) << 2) | ((index & 0x1FFF) << 3);
+    return selector;
+}
+
+/*
+ * Routines to initialize segment descriptors.
+ * Code and data segments must start on a page-aligned address
+ * and are sized in pages.
+ */
+
+void Init_Null_Segment_Descriptor(struct Segment_Descriptor* desc);
+
+void Init_Code_Segment_Descriptor(
+    struct Segment_Descriptor* desc,
+    ulong_t baseAddr,
+    ulong_t numPages,
+    int privilegeLevel
+);
+void Init_Data_Segment_Descriptor(
+    struct Segment_Descriptor* desc,
+    ulong_t baseAddr,
+    ulong_t numPages,
+    int privilegeLevel
+);
+void Init_TSS_Descriptor(struct Segment_Descriptor* desc, struct TSS* theTSS);
+
+void Init_LDT_Descriptor(
+    struct Segment_Descriptor* desc,
+    struct Segment_Descriptor theLDT[],
+    int numEntries
+);
+
+#endif  /* GEEKOS_SEGMENT_H */
diff --git a/misc/test_vm/include/geekos/serial.h b/misc/test_vm/include/geekos/serial.h
new file mode 100644 (file)
index 0000000..4ebe0b0
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef SERIAL_H
+#define SERIAL_H
+
+#include <geekos/irq.h>
+#include <geekos/string.h>
+#include <geekos/io.h>
+#include <geekos/screen.h>
+
+#define COM1_IRQ 4
+#define DEFAULT_SERIAL_ADDR 0x3F8
+
+
+#ifndef SERIAL_PRINT
+#define SERIAL_PRINT              1
+#endif
+#ifndef SERIAL_PRINT_DEBUG
+#define SERIAL_PRINT_DEBUG        1 
+#endif
+#ifndef SERIAL_PRINT_DEBUG_LEVEL
+#define SERIAL_PRINT_DEBUG_LEVEL  10
+#endif
+
+#define SERIAL_PRINT_MAXBUF       256
+
+#if SERIAL_PRINT                                                
+#define SerialPrint(format, args...)                             \
+do {                                                             \
+  char buf[SERIAL_PRINT_MAXBUF];                                                 \
+  snprintf( buf, SERIAL_PRINT_MAXBUF, format, ## args ) ;                       \
+  SerialPutLineN(buf, SERIAL_PRINT_MAXBUF);                                     \
+} while (0)  
+#else
+#define SerialPrint(format, args...) do {} while (0)
+#endif
+
+
+#define PrintBoth(format, args...) \
+do {  \
+  Print(format, ## args); \
+  SerialPrint(format, ##args); \
+ } while (0)
+
+
+#if SERIAL_PRINT_DEBUG
+#define SerialPrintLevel(level, format, args...)                \
+do {                                                             \
+  char buf[SERIAL_PRINT_MAXBUF];                                                 \
+  if (level >= SERIAL_PRINT_DEBUG_LEVEL  ) {                                     \
+    snprintf( buf, SERIAL_PRINT_MAXBUF, format, ## args ) ;            \
+    SerialPutLineN(buf, SERIAL_PRINT_MAXBUF);                          \
+  }                                                                     \
+} while (0)  
+#else
+#define SerialPrintLevel(level, format, args...) do {} while (0)
+#endif
+
+
+void SerialPutLine(char * line); 
+void SerialPutLineN(char * line, int len);
+
+
+void SerialPrintHex(unsigned char x);
+void SerialMemDump(unsigned char *start, int n);
+
+void InitSerial();
+void InitSerialAddr(unsigned short io_addr);
+
+#endif
diff --git a/misc/test_vm/include/geekos/string.h b/misc/test_vm/include/geekos/string.h
new file mode 100644 (file)
index 0000000..661a148
--- /dev/null
@@ -0,0 +1 @@
+#include "../libc/string.h"
diff --git a/misc/test_vm/include/geekos/symbol.h b/misc/test_vm/include/geekos/symbol.h
new file mode 100644 (file)
index 0000000..61e47bd
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Symbol mangling macros
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * The _S macro mangles a symbol name into whatever format is
+ * needed for external linkage.  E.g., prepend an underscore
+ * for PECOFF.
+ */
+
+#ifndef GEEKOS_SYMBOL_H
+#define GEEKOS_SYMBOL_H
+
+#ifdef NEED_UNDERSCORE
+#  define _S(sym) "_" #sym
+#else
+#  define _S(sym) #sym
+#endif
+
+#endif  /* GEEKOS_SYMBOL_H */
diff --git a/misc/test_vm/include/geekos/synch.h b/misc/test_vm/include/geekos/synch.h
new file mode 100644 (file)
index 0000000..5cb95ef
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Synchronization primitives
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_SYNCH_H
+#define GEEKOS_SYNCH_H
+
+#include <geekos/kthread.h>
+
+/*
+ * mutex states
+ */
+enum { MUTEX_UNLOCKED, MUTEX_LOCKED };
+
+struct Mutex {
+    int state;
+    struct Kernel_Thread* owner;
+    struct Thread_Queue waitQueue;
+};
+
+#define MUTEX_INITIALIZER { MUTEX_UNLOCKED, 0, THREAD_QUEUE_INITIALIZER }
+
+struct Condition {
+    struct Thread_Queue waitQueue;
+};
+
+void Mutex_Init(struct Mutex* mutex);
+void Mutex_Lock(struct Mutex* mutex);
+void Mutex_Unlock(struct Mutex* mutex);
+
+void Cond_Init(struct Condition* cond);
+void Cond_Wait(struct Condition* cond, struct Mutex* mutex);
+void Cond_Signal(struct Condition* cond);
+void Cond_Broadcast(struct Condition* cond);
+
+#define IS_HELD(mutex) \
+    ((mutex)->state == MUTEX_LOCKED && (mutex)->owner == g_currentThread)
+
+#endif  /* GEEKOS_SYNCH_H */
diff --git a/misc/test_vm/include/geekos/timer.h b/misc/test_vm/include/geekos/timer.h
new file mode 100644 (file)
index 0000000..37dda14
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * GeekOS timer interrupt support
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_TIMER_H
+#define GEEKOS_TIMER_H
+
+#define TIMER_IRQ 0
+
+extern volatile ulong_t g_numTicks;
+
+void Init_Timer(void);
+
+void Micro_Delay(int us);
+
+
+void Micro_Delay(int us);
+
+#endif  /* GEEKOS_TIMER_H */
diff --git a/misc/test_vm/include/geekos/trap.h b/misc/test_vm/include/geekos/trap.h
new file mode 100644 (file)
index 0000000..73c28b8
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Trap handlers
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_TRAP_H
+#define GEEKOS_TRAP_H
+
+void Init_Traps(void);
+
+#endif  /* GEEKOS_TRAP_H */
diff --git a/misc/test_vm/include/geekos/tss.h b/misc/test_vm/include/geekos/tss.h
new file mode 100644 (file)
index 0000000..66ea118
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * x86 TSS data structure and routines
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_TSS_H
+#define GEEKOS_TSS_H
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+/*
+ * NOTE: all reserved fields must be set to zero.
+ */
+
+struct TSS {
+    /*
+     * Link to nested task.  For example, if an interrupt is handled
+     * by a task gate, the link field will contain the selector for
+     * the TSS of the interrupted task.
+     */
+    ushort_t link;
+    ushort_t reserved1;
+
+    /* Stacks for privilege levels.  esp0/ss0 specifies the kernel stack. */
+    ulong_t esp0;
+    ushort_t ss0;
+    ushort_t reserved2;
+    ulong_t esp1;
+    ushort_t ss1;
+    ushort_t reserved3;
+    ulong_t esp2;
+    ushort_t ss2;
+    ushort_t reserved4;
+
+    /* Page directory register. */
+    ulong_t cr3;
+
+    /* General processor registers. */
+    ulong_t eip;
+    ulong_t eflags;
+    ulong_t eax;
+    ulong_t ecx;
+    ulong_t edx;
+    ulong_t ebx;
+    ulong_t esp;
+    ulong_t ebp;
+    ulong_t esi;
+    ulong_t edi;
+
+    /* Segment registers and padding. */
+    ushort_t es;
+    ushort_t reserved5;
+    ushort_t cs;
+    ushort_t reserved6;
+    ushort_t ss;
+    ushort_t reserved7;
+    ushort_t ds;
+    ushort_t reserved8;
+    ushort_t fs;
+    ushort_t reserved9;
+    ushort_t gs;
+    ushort_t reserved10;
+
+    /* GDT selector for the LDT descriptor. */
+    ushort_t ldt;
+    ushort_t reserved11;
+
+    /*
+     * The debug trap bit causes a debug exception upon a switch
+     * to the task specified by this TSS.
+     */
+    uint_t debugTrap : 1;
+    uint_t reserved12 : 15;
+
+    /* Offset in the TSS specifying where the io map is located. */
+    ushort_t ioMapBase;
+};
+
+void Init_TSS(void);
+void Set_Kernel_Stack_Pointer(ulong_t esp0);
+
+#endif  /* GEEKOS_TSS_H */
diff --git a/misc/test_vm/include/libc/fmtout.h b/misc/test_vm/include/libc/fmtout.h
new file mode 100644 (file)
index 0000000..429d7fd
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Generalized support for printf()-style formatted output
+ * Copyright (c) 2004, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef OUTPUT_H
+#define OUTPUT_H
+
+#include <stdarg.h>
+
+/*
+ * An output sink for Format_Output().
+ * Useful for emitting formatted output to a function
+ * or a buffer.
+ */
+struct Output_Sink {
+    /*
+     * Emit a single character of output.
+     * This is called for all output characters,
+     * in order.
+     */
+    void (*Emit)(struct Output_Sink *o, int ch);
+
+    /*
+     * Finish the formatted output. Called after all characters
+     * have been emitted.
+     */
+    void (*Finish)(struct Output_Sink *o);
+};
+
+int Format_Output(struct Output_Sink *q, const char *format, va_list ap);
+
+#endif /* OUTPUT_H */
diff --git a/misc/test_vm/include/libc/string.h b/misc/test_vm/include/libc/string.h
new file mode 100644 (file)
index 0000000..fb146c0
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * String library
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef STRING_H
+#define STRING_H
+
+#include <stddef.h>
+
+void* memset(void* s, int c, size_t n);
+void* memcpy(void *dst, const void* src, size_t n);
+void *memmove(void *dst, const void *src, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+size_t strlen(const char* s);
+size_t strnlen(const char *s, size_t maxlen);
+int strcmp(const char* s1, const char* s2);
+int strncmp(const char* s1, const char* s2, size_t limit);
+char *strcat(char *s1, const char *s2);
+char *strcpy(char *dest, const char *src);
+char *strncpy(char *dest, const char *src, size_t limit);
+char *strdup(const char *s1);
+int atoi(const char *buf);
+char *strchr(const char *s, int c);
+char *strrchr(const char *s, int c);
+char *strpbrk(const char *s, const char *accept);
+
+/* Note: The ISO C standard puts this in <stdio.h>, but we don't
+ * have that header in GeekOS (yet). */
+int snprintf(char *s, size_t size, const char *fmt, ...)
+    __attribute__ ((__format__ (__printf__, 3, 4)));
+
+#endif  /* STRING_H */
diff --git a/misc/test_vm/scripts/eipToFunction b/misc/test_vm/scripts/eipToFunction
new file mode 100755 (executable)
index 0000000..98bffa6
--- /dev/null
@@ -0,0 +1,42 @@
+#! /usr/bin/perl
+
+# Find the function name from the value of the EIP (instruction pointer)
+# register from a Bochs crash report.  Uses the kernel symbol
+# map (kernel.syms) produced by compiling the kernel.
+
+use strict qw(refs vars);
+use FileHandle;
+
+if (scalar(@ARGV) != 2){
+       print STDERR "Usage: eipToFunction kernel.syms <eip value>\n";
+       print STDERR "   eip value should be in hex\n";
+       exit 1;
+}
+
+my $syms = shift @ARGV;
+my $eip = hex(shift @ARGV);
+
+my @text = ();
+
+my $fh = new FileHandle("<$syms");
+(defined $fh) || die "Couldn't open $syms: $!\n";
+while (<$fh>) {
+       #print $_;
+       if (/^([0-9A-Fa-f]+)\s+[Tt]\s+(\S+)\s*$/) {
+               push @text, [hex($1), $2];
+       }
+}
+$fh->close();
+#print scalar(@text),"\n";
+
+@text = sort { $a->[0] <=> $b->[0] } @text;
+
+my $last = undef;
+
+foreach my $entry (@text) {
+       last if ($eip < $entry->[0]);
+       $last = $entry;
+}
+printf("%s\n",(defined $last) ? $last->[1] : "not found");
+
+# vim:ts=4
diff --git a/misc/test_vm/scripts/findaddr b/misc/test_vm/scripts/findaddr
new file mode 100755 (executable)
index 0000000..7b9b877
--- /dev/null
@@ -0,0 +1,26 @@
+#! /usr/bin/perl
+
+# Find the address of a symbol in the storage map.
+
+use strict qw(refs vars);
+use FileHandle;
+
+if ( scalar(@ARGV) != 2 ) {
+    print "Usage: findaddr <storage map> <symbol name>\n";
+    exit 1;
+}
+
+my $storage = shift @ARGV;
+my $symbol = shift @ARGV;
+
+my $fh = new FileHandle("<$storage");
+(defined $fh) || die "Couldn't open storage map: $!\n";
+
+while ( <$fh> ) {
+    if ( /^\s*(0x([0-9]|[a-f]|[A-F])+)\s+\Q$symbol\E\s*$/ ) {
+       print $1, "\n";
+       last;
+    }
+}
+
+$fh->close();
diff --git a/misc/test_vm/scripts/generrs b/misc/test_vm/scripts/generrs
new file mode 100755 (executable)
index 0000000..1b76e81
--- /dev/null
@@ -0,0 +1,28 @@
+#! /usr/bin/perl
+
+# Script to process include/geekos/errno.h to produce a table
+# of error description strings that can be compiled and
+# linked into libc.
+
+use strict qw(refs vars);
+
+my @errs = ();
+my @syms = ();
+
+$errs[0] = 'No error';
+
+while (<>) {
+       if (m,^#define\s*(\S+)\s*(-\d+)\s*/\*\s*(.*\S)\s*\*/\s*$,) {
+               $errs[- $2] = $3;
+               $syms[- $2] = $1;
+       }
+}
+
+print "const char *__strerrTable[] = {\n";
+for (my $i = 0; $i < scalar(@errs); $i++) {
+       print "    \"", $errs[$i], "\", /* ", $syms[$i], " */\n";
+}
+print "};\n";
+print "const int __strerrTableSize = sizeof(__strerrTable) / sizeof(const char *);\n";
+
+# vim:ts=4
diff --git a/misc/test_vm/scripts/kerninfo b/misc/test_vm/scripts/kerninfo
new file mode 100755 (executable)
index 0000000..9241e3a
--- /dev/null
@@ -0,0 +1,38 @@
+#! /usr/bin/perl
+
+# A script to analyze the output of "objdump -h" on the
+# kernel executable file.
+
+use strict qw(vars refs);
+use FileHandle;
+
+my $kernfile = shift @ARGV;
+(defined $kernfile) || die "usage: kernsize <kernfile>\n";
+
+my $kern_fh = new FileHandle("<$kernfile");
+(defined $kern_fh) || die "can't open $kernfile: $!\n";
+
+my $objdump_fh = new FileHandle("objdump -h $kernfile|");
+while ( <$objdump_fh> ) {
+    chop;
+    s/^\s+//;
+    my @fields = split(/\s+/, $_);
+    if ( $fields[0] =~ /^[0-9]$/ ) {
+#      print "text start is ", $fields[5], "\n" if $fields[0] eq '0';
+       my $size = hex($fields[2]);
+       my $offset = hex($fields[5]);
+
+       print $fields[0], " (", $fields[1], "): size=$size, offset=$offset\n";
+
+       printf("Word at beginning of section is %08x\n", ReadWord($kern_fh,$offset) );
+    }
+}
+$objdump_fh->close();
+
+sub ReadWord {
+    my ($fh, $offset) = @_;
+    seek $fh, $offset, SEEK_SET;
+    my $buf = 'X' x 4;
+    read $fh, $buf, 4;
+    return unpack('V',$buf);
+}
diff --git a/misc/test_vm/scripts/mkcdisk b/misc/test_vm/scripts/mkcdisk
new file mode 100755 (executable)
index 0000000..d3f4477
--- /dev/null
@@ -0,0 +1,25 @@
+#! /usr/bin/perl
+
+# Build a binary image containing a pseudo fat filesystem with the listed files
+
+# $Revision: 1.1 $
+use FileHandle; 
+
+if ( scalar(@ARGV) < 2 ) {
+    print STDERR "usage: mkuprog <diskImage> <filenames>\n";
+    exit 1;
+}
+
+
+$filename = shift @ARGV;
+$filecount = scalar ( @ARGV );
+
+$fh = new FileHandle(">$filename");
+
+write
+
+while (scalar(@ARGV)) {
+    $filename = shift @ARGV;
+
+    print  "got file ", $filename, "\n";
+}
diff --git a/misc/test_vm/scripts/mkuprog b/misc/test_vm/scripts/mkuprog
new file mode 100755 (executable)
index 0000000..1581b4b
--- /dev/null
@@ -0,0 +1,55 @@
+#! /usr/bin/perl
+
+# From a binary image containing a user program, generate
+# C code initializing a User_Program struct.
+
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+if ( scalar(@ARGV) != 3 ) {
+    print STDERR "usage: mkuprog <filename> <progname> <entry addr>\n";
+    exit 1;
+}
+
+my $filename = shift @ARGV;
+my $progname = shift @ARGV;
+my $entryAddr = shift @ARGV;
+
+my $fh = new FileHandle("<$filename");
+(defined $fh) || die "Couldn't open $filename: $!\n";
+binmode $fh;
+
+my $dataArrayName = $progname . "Data";
+my $structName = $progname . "Prog";
+print "const unsigned char $dataArrayName"."[] = {\n";
+
+my $LINEWIDTH = 10;
+
+my $buf = chr(0) x $LINEWIDTH;
+my $n;
+my $size = 0;
+while ( ($n = read( $fh, $buf, $LINEWIDTH )) > 0 ) {
+    $size += $n;
+    my $i;
+    print "    ";
+    for ( $i = 0; $i < $n; $i++ ) {
+       my $c = ord( substr($buf, $i, 1) );
+       printf( "0x%x,", $c );
+    }
+    print "\n";
+}
+
+print "};\n";
+
+$fh->close();
+
+print << "END";
+const struct User_Program $structName = {
+    "$progname",
+    $size,
+    $entryAddr,
+    $dataArrayName
+};
+END
diff --git a/misc/test_vm/scripts/numsecs b/misc/test_vm/scripts/numsecs
new file mode 100755 (executable)
index 0000000..708a893
--- /dev/null
@@ -0,0 +1,23 @@
+#! /usr/bin/perl
+
+# Find the number of 512-byte sectors needed to store
+# given file.
+
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+
+if ( scalar(@ARGV) != 1 ) {
+    print STDERR "Usage: numsecs <filename>\n";
+    exit 1;
+}
+
+my $filename = shift @ARGV;
+my $size = (-s $filename );
+die "Couldn't get size of $filename: $!" if ( !defined $size );
+
+my $result = int($size / 512);
+my $remainder = $size % 512;
+$result++ if ( $remainder > 0 );
+
+print "$result\n";
diff --git a/misc/test_vm/scripts/pad b/misc/test_vm/scripts/pad
new file mode 100755 (executable)
index 0000000..c1c5329
--- /dev/null
@@ -0,0 +1,30 @@
+#! /usr/bin/perl
+
+# Pad a file with zero bytes to make its length
+# an even multiple of some value.
+
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+if ( scalar(@ARGV) != 2 ) {
+    print STDERR "usage: pad <filename> <multiple>\n";
+    exit 1;
+}
+
+my $filename = shift @ARGV;
+my $multiple = shift @ARGV;
+
+my $size = (-s $filename);
+die "Couldn't get size of $filename: $!" if ( !defined $size );
+
+my $num_pad = ($multiple - ($size % $multiple)) % $multiple;
+
+my $buf = chr(0) x $num_pad;
+
+my $fh = new FileHandle(">>$filename");
+die "Couldn't open $filename: $!" if ( !defined $fh );
+binmode $fh;
+syswrite $fh, $buf, $num_pad, 0;
+$fh->close();
diff --git a/misc/test_vm/scripts/pcat b/misc/test_vm/scripts/pcat
new file mode 100755 (executable)
index 0000000..d58f9fd
--- /dev/null
@@ -0,0 +1,23 @@
+#! /usr/bin/perl
+
+# A version of cat written in perl.
+
+use strict qw(refs vars);
+use FileHandle;
+
+binmode STDOUT;
+
+my $buf = chr(0) x 1024;
+
+my $file;
+while ( ($file = shift @ARGV) ) {
+    my $fh = new FileHandle("<$file");
+    (defined $fh) || die "Couldn't open $file: $!\n";
+    binmode $fh;
+
+    my $n;
+    while ( ($n = sysread($fh, $buf, 1024)) > 0 ) {
+       syswrite( STDOUT, $buf, $n );
+    }
+    $fh->close();
+}
diff --git a/misc/test_vm/scripts/pw b/misc/test_vm/scripts/pw
new file mode 100755 (executable)
index 0000000..7edc728
--- /dev/null
@@ -0,0 +1,24 @@
+#! /usr/bin/perl
+
+# Inspect a 32 word at a specified offset in a file.
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+my $filename = shift @ARGV;
+my $offset = shift @ARGV;
+
+((defined $filename) && (defined $offset))
+    || die "Usage: pw <filename> <offset>\n";
+
+my $fh = new FileHandle("<$filename");
+printf( "%08x\n", ReadWord($fh, $offset) );
+
+sub ReadWord {
+    my ($fh, $offset) = @_;
+    seek $fh, $offset, SEEK_SET;
+    my $buf = 'X' x 4;
+    read $fh, $buf, 4;
+    return unpack('V',$buf);
+}
diff --git a/misc/test_vm/scripts/scan b/misc/test_vm/scripts/scan
new file mode 100755 (executable)
index 0000000..cbfe6dc
--- /dev/null
@@ -0,0 +1,29 @@
+#! /usr/bin/perl
+
+# Scan a file for a 32-bit word with a particular value.
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+my $filename = shift @ARGV;
+my $word_value = shift @ARGV;
+
+((defined $filename) && (defined $word_value))
+    || die "Usage: scan <filename> <word value in hex>\n";
+
+my $fh = new FileHandle("<$filename");
+my $val = hex($word_value);
+
+my $buf = ' ' x 4;
+
+my $offset = 0;
+while ( read( $fh, $buf, 4) == 4 ) {
+    my $out = unpack "V", $buf;
+    if ( $out == $val ) {
+       print "Found value $word_value at offset $offset\n";
+       exit;
+    }
+    $offset += 4;
+}
+print "Didn't find value $word_value\n";
diff --git a/misc/test_vm/scripts/zerofile b/misc/test_vm/scripts/zerofile
new file mode 100755 (executable)
index 0000000..52e744a
--- /dev/null
@@ -0,0 +1,31 @@
+#! /usr/bin/perl
+
+# This script is used for creating a file full of zeroes,
+# which we use to create the hard disk image used by bochs.
+
+use strict qw(refs vars);
+use FileHandle;
+use IO::Seekable;
+
+if ( scalar(@ARGV) != 2 ) {
+    print "Usage: zerofile <output file> <num sectors>\n";
+    exit 1;
+}
+
+my $outfile = shift @ARGV;
+my $numsecs = shift @ARGV;
+
+my $buf = chr(0) x 1;
+
+my $fh = new FileHandle(">$outfile");
+(defined $fh) || die "Couldn't open $outfile: $!\n";
+binmode $fh;
+
+if ( !sysseek( $fh, ($numsecs * 512) - 1, SEEK_SET ) ) {
+    die "Couldn't seek in $outfile: $!\n";
+}
+if ( !syswrite( $fh, $buf, 1 ) ) {
+    die "Couldn't write to $outfile: $!\n";
+}
+
+$fh->close();
diff --git a/misc/test_vm/src/common/fmtout.c b/misc/test_vm/src/common/fmtout.c
new file mode 100644 (file)
index 0000000..c097eb4
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * This code was originally part of klibc-0.103.
+ * It was adapted by David Hovemeyer <daveho@cs.umd.edu>
+ * for use in GeekOS (http://geekos.sourceforge.net).
+ *
+ * For information/source for klibc, visit
+ *   http://www.kernel.org/pub/linux/libs/klibc/
+ *   http://www.zytor.com/mailman/listinfo/klibc/
+ *   http://www.zytor.com/cvsweb.cgi/klibc/
+ *
+ * Modifications are marked with "DHH".
+ * Summary of modifications:
+ *
+ * 1. Use struct Output_Sink to emit formatted output, rather than a
+ *    character buffer, to allow output polymorphism.
+ *
+ * 2. Buffer generated numbers so that all output is generated in order.
+ *
+ * 3. Don't use long long types: unsigned long is largest
+ *    supported type.  Arithmetic on 64 bit types requires runtime support
+ *    (at least on x86).
+ *
+ * See the file LICENSE-klibc for license information.
+ */
+
+/*
+ * vsnprintf.c
+ *
+ * vsnprintf(), from which the rest of the printf()
+ * family is built
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <geekos/string.h>
+#include <limits.h>
+#include <geekos/fmtout.h> /* DHH: for struct Output_Sink */
+
+/*
+ * DHH: Hack, long long arithmetic requires runtime support.
+ * Use unsigned long as greatest unsigned integer supported.
+ */
+typedef long intmax_t;
+typedef unsigned long uintmax_t;
+typedef unsigned long uintptr_t;
+
+/* DHH */
+#define ASSERT(exp) \
+do { if (!(exp)) while(1); } while (0)
+
+enum flags {
+  FL_ZERO   = 0x01,            /* Zero modifier */
+  FL_MINUS  = 0x02,            /* Minus modifier */
+  FL_PLUS   = 0x04,            /* Plus modifier */
+  FL_TICK   = 0x08,            /* ' modifier */
+  FL_SPACE  = 0x10,            /* Space modifier */
+  FL_HASH   = 0x20,            /* # modifier */
+  FL_SIGNED = 0x40,            /* Number is signed */
+  FL_UPPER  = 0x80             /* Upper case digits */
+};
+
+/* These may have to be adjusted on certain implementations */
+enum ranks {
+  rank_char    = -2,
+  rank_short   = -1,
+  rank_int     = 0,
+  rank_long    = 1,
+#if 0
+  rank_longlong        = 2,
+#endif
+};
+
+#define MIN_RANK       rank_char
+#define MAX_RANK       rank_long
+
+#define INTMAX_RANK    rank_long
+#define SIZE_T_RANK    rank_long
+#define PTRDIFF_T_RANK rank_long
+
+/* DHH */
+#define EMIT(x) do { (q)->Emit((q), (x)); } while (0)
+
+/*
+ * DHH - As a hack, we buffer this many digits when generating
+ * a number.  Because this code originally was an implementation
+ * of vnsprintf(), it generated some digits backwards in a buffer.
+ * Obviously we can't do this when emitting output directly
+ * to the console or a file.  So, we buffer the generated digits
+ * and then emit them in order.
+ *
+ * This value should be adequate to emit values up to 2^32-1 in
+ * bases 2 or greater, including tick marks.
+ */
+#define NDIGITS_MAX 43
+
+static size_t
+format_int(struct Output_Sink *q, uintmax_t val, enum flags flags,
+          int base, int width, int prec)
+{
+  char *qq;
+  size_t o = 0, oo;
+  static const char lcdigits[] = "0123456789abcdef";
+  static const char ucdigits[] = "0123456789ABCDEF";
+  const char *digits;
+  uintmax_t tmpval;
+  int minus = 0;
+  int ndigits = 0, nchars;
+  int tickskip, b4tick;
+  char digit_buffer[NDIGITS_MAX]; /* DHH */
+  size_t ndigits_save; /* DHH */
+
+  /* Select type of digits */
+  digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
+
+  /* If signed, separate out the minus */
+  if ( flags & FL_SIGNED && (intmax_t)val < 0 ) {
+    minus = 1;
+    val = (uintmax_t)(-(intmax_t)val);
+  }
+
+  /* Count the number of digits needed.  This returns zero for 0. */
+  tmpval = val;
+  while ( tmpval ) {
+    tmpval /= base;
+    ndigits++;
+  }
+
+  /* Adjust ndigits for size of output */
+
+  if ( flags & FL_HASH && base == 8 ) {
+    if ( prec < ndigits+1 )
+      prec = ndigits+1;
+  }
+
+  if ( ndigits < prec ) {
+    ndigits = prec;            /* Mandatory number padding */
+  } else if ( val == 0 ) {
+    ndigits = 1;               /* Zero still requires space */
+  }
+
+  /* For ', figure out what the skip should be */
+  if ( flags & FL_TICK ) {
+    tickskip = (base == 16) ? 4 : 3;
+  } else {
+    tickskip = ndigits;                /* No tick marks */
+  }
+
+  /* Tick marks aren't digits, but generated by the number converter */
+  ndigits += (ndigits-1)/tickskip;
+
+  /* Now compute the number of nondigits */
+  nchars = ndigits;
+
+  if ( minus || (flags & (FL_PLUS|FL_SPACE)) )
+    nchars++;                  /* Need space for sign */
+  if ( (flags & FL_HASH) && base == 16 ) {
+    nchars += 2;               /* Add 0x for hex */
+  }
+
+  /* Emit early space padding */
+  if ( !(flags & (FL_MINUS|FL_ZERO)) && width > nchars ) {
+    while ( width > nchars ) {
+      EMIT(' ');
+      width--;
+    }
+  }
+
+  /* Emit nondigits */
+  if ( minus )
+    EMIT('-');
+  else if ( flags & FL_PLUS )
+    EMIT('+');
+  else if ( flags & FL_SPACE )
+    EMIT(' ');
+
+  if ( (flags & FL_HASH) && base == 16 ) {
+    EMIT('0');
+    EMIT((flags & FL_UPPER) ? 'X' : 'x');
+  }
+
+  /* Emit zero padding */
+  if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) {
+    while ( width > nchars ) {
+      EMIT('0');
+      width--;
+    }
+  }
+
+  /* Generate the number.  This is done from right to left. */
+  ASSERT(ndigits <= NDIGITS_MAX); /* DHH */
+  ndigits_save = ndigits;
+  qq = digit_buffer + ndigits;
+  oo = o;
+
+  /* Emit digits to temp buffer */
+  b4tick = tickskip;
+  while ( ndigits > 0 ) {
+    if ( !b4tick-- ) {
+      qq--; oo--; ndigits--;
+      *qq = '_';
+      b4tick = tickskip-1;
+    }
+    qq--; oo--; ndigits--;
+    *qq = digits[val%base];
+    val /= base;
+  }
+
+  /* Copy digits to Output_Sink */
+  for (oo = 0; oo < ndigits_save; ++oo)
+    EMIT(digit_buffer[oo]);
+
+  /* Emit late space padding */
+  while ( (flags & FL_MINUS) && width > nchars ) {
+    EMIT(' ');
+    width--;
+  }
+
+  return o;
+}
+
+/*
+ * DHH - This function was originally vsnprintf().
+ * I renamed it to Format_Output() and changed it to take
+ * a struct Output_Sink instead of a buffer.  That way, it can
+ * be used for any kind of formatted output (string, console,
+ * file, etc.)
+ */
+int Format_Output(struct Output_Sink *q, const char *format, va_list ap)
+{
+  const char *p = format;
+  char ch;
+  size_t o = 0;                        /* Number of characters output */
+  uintmax_t val = 0;
+  int rank = rank_int;         /* Default rank */
+  int width = 0;
+  int prec  = -1;
+  int base;
+  size_t sz;
+  enum flags flags = 0;
+  enum {
+    st_normal,                 /* Ground state */
+    st_flags,                  /* Special flags */
+    st_width,                  /* Field width */
+    st_prec,                   /* Field precision */
+    st_modifiers               /* Length or conversion modifiers */
+  } state = st_normal;
+  const char *sarg;            /* %s string argument */
+  char carg;                   /* %c char argument */
+  int slen;                    /* String length */
+
+  while ( (ch = *p++) ) {
+    switch ( state ) {
+    case st_normal:
+      if ( ch == '%' ) {
+       state = st_flags;
+       flags = 0; rank = rank_int; width = 0; prec = -1;
+      } else {
+       EMIT(ch);
+      }
+      break;
+
+    case st_flags:
+      switch ( ch ) {
+      case '-':
+       flags |= FL_MINUS;
+       break;
+      case '+':
+       flags |= FL_PLUS;
+       break;
+      case '\'':
+       flags |= FL_TICK;
+       break;
+      case ' ':
+       flags |= FL_SPACE;
+       break;
+      case '#':
+       flags |= FL_HASH;
+       break;
+      case '0':
+       flags |= FL_ZERO;
+       break;
+      default:
+       state = st_width;
+       p--;                    /* Process this character again */
+       break;
+      }
+      break;
+
+    case st_width:
+      if ( ch >= '0' && ch <= '9' ) {
+       width = width*10+(ch-'0');
+      } else if ( ch == '*' ) {
+       width = va_arg(ap, int);
+       if ( width < 0 ) {
+         width = -width;
+         flags |= FL_MINUS;
+       }
+      } else if ( ch == '.' ) {
+       prec = 0;               /* Precision given */
+       state = st_prec;
+      } else {
+       state = st_modifiers;
+       p--;                    /* Process this character again */
+      }
+      break;
+
+    case st_prec:
+      if ( ch >= '0' && ch <= '9' ) {
+       prec = prec*10+(ch-'0');
+      } else if ( ch == '*' ) {
+       prec = va_arg(ap, int);
+       if ( prec < 0 )
+         prec = -1;
+      } else {
+       state = st_modifiers;
+       p--;                    /* Process this character again */
+      }
+      break;
+
+    case st_modifiers:
+      switch ( ch ) {
+       /* Length modifiers - nonterminal sequences */
+      case 'h':
+       rank--;                 /* Shorter rank */
+       break;
+      case 'l':
+       rank++;                 /* Longer rank */
+       break;
+      case 'j':
+       rank = INTMAX_RANK;
+       break;
+      case 'z':
+       rank = SIZE_T_RANK;
+       break;
+      case 't':
+       rank = PTRDIFF_T_RANK;
+       break;
+      case 'L':
+      case 'q':
+       rank += 2;
+       break;
+      default:
+       /* Output modifiers - terminal sequences */
+       state = st_normal;      /* Next state will be normal */
+       if ( rank < MIN_RANK )  /* Canonicalize rank */
+         rank = MIN_RANK;
+       else if ( rank > MAX_RANK )
+         rank = MAX_RANK;
+
+       switch ( ch ) {
+       case 'P':               /* Upper case pointer */
+         flags |= FL_UPPER;
+         /* fall through */
+       case 'p':               /* Pointer */
+         base = 16;
+         prec = (CHAR_BIT*sizeof(void *)+3)/4;
+         flags |= FL_HASH;
+         val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
+         goto is_integer;
+
+       case 'd':               /* Signed decimal output */
+       case 'i':
+         base = 10;
+         flags |= FL_SIGNED;
+         switch (rank) {
+         case rank_char:
+           /* Yes, all these casts are needed... */
+           val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
+           break;
+         case rank_short:
+           val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
+           break;
+         case rank_int:
+           val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
+           break;
+         case rank_long:
+           val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
+           break;
+#if 0
+         case rank_longlong:
+           val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
+           break;
+#endif
+         }
+         goto is_integer;
+       case 'o':               /* Octal */
+         base = 8;
+         goto is_unsigned;
+       case 'u':               /* Unsigned decimal */
+         base = 10;
+         goto is_unsigned;
+       case 'X':               /* Upper case hexadecimal */
+         flags |= FL_UPPER;
+         /* fall through */
+       case 'x':               /* Hexadecimal */
+         base = 16;
+         goto is_unsigned;
+
+       is_unsigned:
+         switch (rank) {
+         case rank_char:
+           val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
+           break;
+         case rank_short:
+           val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
+           break;
+         case rank_int:
+           val = (uintmax_t)va_arg(ap, unsigned int);
+           break;
+         case rank_long:
+           val = (uintmax_t)va_arg(ap, unsigned long);
+           break;
+#if 0
+         case rank_longlong:
+           val = (uintmax_t)va_arg(ap, unsigned long long);
+           break;
+#endif
+         }
+         /* fall through */
+
+       is_integer:
+         sz = format_int(q, val, flags, base, width, prec);
+         q += sz; o += sz;
+         break;
+
+       case 'c':               /* Character */
+         carg = (char)va_arg(ap, int);
+         sarg = &carg;
+         slen = 1;
+         goto is_string;
+       case 's':               /* String */
+         sarg = va_arg(ap, const char *);
+         sarg = sarg ? sarg : "(null)";
+         slen = strlen(sarg);
+         goto is_string;
+
+       is_string:
+         {
+           char sch;
+           int i;
+           
+           if ( prec != -1 && slen > prec )
+             slen = prec;
+           
+           if ( width > slen && !(flags & FL_MINUS) ) {
+             char pad = (flags & FL_ZERO) ? '0' : ' ';
+             while ( width > slen ) {
+               EMIT(pad);
+               width--;
+             }
+           }
+           for ( i = slen ; i ; i-- ) {
+             sch = *sarg++;
+             EMIT(sch);
+           }
+           if ( width > slen && (flags & FL_MINUS) ) {
+             while ( width > slen ) {
+               EMIT(' ');
+               width--;
+             }
+           }
+         }
+         break;
+
+       case 'n':               /* Output the number of characters written */
+         {
+           switch (rank) {
+           case rank_char:
+             *va_arg(ap, signed char *) = o;
+             break;
+           case rank_short:
+             *va_arg(ap, signed short *) = o;
+             break;
+           case rank_int:
+             *va_arg(ap, signed int *) = o;
+             break;
+           case rank_long:
+             *va_arg(ap, signed long *) = o;
+             break;
+#if 0
+           case rank_longlong:
+             *va_arg(ap, signed long long *) = o;
+             break;
+#endif
+           }
+         }
+         break;
+         
+       default:                /* Anything else, including % */
+         EMIT(ch);
+         break;
+       }
+      }
+    }
+  }
+
+  /* Null-terminate the string */
+#if 0
+  if ( o<n )
+    *q = '\0';                 /* No overflow */
+  else if ( n>0 )
+    buffer[n-1] = '\0';                /* Overflow - terminate at end of buffer */
+#endif
+  q->Finish(q);
+
+  return o;
+}
diff --git a/misc/test_vm/src/common/memmove.c b/misc/test_vm/src/common/memmove.c
new file mode 100644 (file)
index 0000000..27f04a5
--- /dev/null
@@ -0,0 +1,57 @@
+/***********************************************************
+Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
+The Netherlands.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI or Corporation for National Research Initiatives or
+CNRI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+While CWI is the initial source for this software, a modified version
+is made available by the Corporation for National Research Initiatives
+(CNRI) at the Internet address ftp://ftp.python.org.
+
+STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH
+CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/* A perhaps slow but I hope correct implementation of memmove */
+
+#include <string.h>
+
+void *
+memmove(void *d, const void *s, size_t n)
+{
+       char *dst = (char*) d;
+       const char *src = (const char*) s;
+       char *realdst = dst;
+       if (n <= 0)
+               return dst;
+       if (src >= dst+n || dst >= src+n)
+               return memcpy(dst, src, n);
+       if (src > dst) {
+               while (--n >= 0)
+                       *dst++ = *src++;
+       }
+       else if (src < dst) {
+               src += n;
+               dst += n;
+               while (--n >= 0)
+                       *--dst = *--src;
+       }
+       return realdst;
+}
diff --git a/misc/test_vm/src/common/string.c b/misc/test_vm/src/common/string.c
new file mode 100644 (file)
index 0000000..7db896c
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * String library
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * NOTE:
+ * These are slow and simple implementations of a subset of
+ * the standard C library string functions.
+ * We also have an implementation of snprintf().
+ */
+
+#include <fmtout.h>
+#include <string.h>
+
+extern void *Malloc(size_t size);
+
+void* memset(void* s, int c, size_t n)
+{
+    unsigned char* p = (unsigned char*) s;
+
+    while (n > 0) {
+       *p++ = (unsigned char) c;
+       --n;
+    }
+
+    return s;
+}
+
+void* memcpy(void *dst, const void* src, size_t n)
+{
+    unsigned char* d = (unsigned char*) dst;
+    const unsigned char* s = (const unsigned char*) src;
+
+    while (n > 0) {
+       *d++ = *s++;
+       --n;
+    }
+
+    return dst;
+}
+
+int memcmp(const void *s1_, const void *s2_, size_t n)
+{
+    const signed char *s1 = s1_, *s2 = s2_;
+
+    while (n > 0) {
+       int cmp = *s1 - *s2;
+       if (cmp != 0)
+           return cmp;
+       ++s1;
+       ++s2;
+    }
+
+    return 0;
+}
+
+size_t strlen(const char* s)
+{
+    size_t len = 0;
+    while (*s++ != '\0')
+       ++len;
+    return len;
+}
+
+/*
+ * This it a GNU extension.
+ * It is like strlen(), but it will check at most maxlen
+ * characters for the terminating nul character,
+ * returning maxlen if it doesn't find a nul.
+ * This is very useful for checking the length of untrusted
+ * strings (e.g., from user space).
+ */
+size_t strnlen(const char *s, size_t maxlen)
+{
+    size_t len = 0;
+    while (len < maxlen && *s++ != '\0')
+       ++len;
+    return len;
+}
+
+int strcmp(const char* s1, const char* s2)
+{
+    while (1) {
+       int cmp = *s1 - *s2;
+       if (cmp != 0 || *s1 == '\0' || *s2 == '\0')
+           return cmp;
+       ++s1;
+       ++s2;
+    }
+}
+
+int strncmp(const char* s1, const char* s2, size_t limit)
+{
+    size_t i = 0;
+    while (i < limit) {
+       int cmp = *s1 - *s2;
+       if (cmp != 0 || *s1 == '\0' || *s2 == '\0')
+           return cmp;
+       ++s1;
+       ++s2;
+       ++i;
+    }
+
+    /* limit reached and equal */
+    return 0;
+}
+
+char *strcat(char *s1, const char *s2)
+{
+    char *t1;
+
+    t1 = s1;
+    while (*s1) s1++;
+    while(*s2) *s1++ = *s2++;
+    *s1 = '\0';
+
+    return t1;
+}
+
+char *strcpy(char *dest, const char *src)
+{
+    char *ret = dest;
+
+    while (*src) {
+        *dest++ = *src++;
+    }
+    *dest = '\0';
+
+    return ret;
+}
+
+char *strncpy(char *dest, const char *src, size_t limit)
+{
+    char *ret = dest;
+
+    while (*src != '\0' && limit > 0) {
+       *dest++ = *src++;
+       --limit;
+    }
+    if (limit > 0)
+       *dest = '\0';
+
+    return ret;
+}
+
+char *strdup(const char *s1)
+{
+    char *ret;
+
+    ret = Malloc(strlen(s1) + 1);
+    strcpy(ret, s1);
+
+    return ret;
+}
+
+int atoi(const char *buf) 
+{
+    int ret = 0;
+
+    while (*buf >= '0' && *buf <= '9') {
+       ret *= 10;
+       ret += *buf - '0';
+       buf++;
+    }
+
+    return ret;
+}
+
+char *strchr(const char *s, int c)
+{
+    while (*s != '\0') {
+       if (*s == c)
+           return (char *) s;
+       ++s;
+    }
+    return 0;
+}
+
+char *strrchr(const char *s, int c)
+{
+    size_t len = strlen(s);
+    const char *p = s + len;
+
+    while (p > s) {
+       --p;
+       if (*p == c)
+           return (char*) p;
+    }
+    return 0;
+}
+
+char *strpbrk(const char *s, const char *accept)
+{
+    size_t setLen = strlen(accept);
+
+    while (*s != '\0') {
+       size_t i;
+       for (i = 0; i < setLen; ++i) {
+           if (*s == accept[i])
+               return (char *) s;
+       }
+       ++s;
+    }
+
+    return 0;
+}
+
+struct String_Output_Sink {
+    struct Output_Sink o;
+    char *s;
+    size_t n, size;
+};
+
+static void String_Emit(struct Output_Sink *o_, int ch)
+{
+    struct String_Output_Sink *o = (struct String_Output_Sink*) o_;
+
+    if (o->n < o->size)
+       *(o->s)++ = ch;
+    ++(o->n);
+}
+
+static void String_Finish(struct Output_Sink *o_)
+{
+    struct String_Output_Sink *o = (struct String_Output_Sink*) o_;
+
+    if (o->n < o->size)
+       *(o->s) = '\0';
+    else
+       /*
+        * Output was truncated; write terminator at end of buffer
+        * (we will have advanced one character too far)
+        */
+       *(o->s - 1) = '\0';
+}
+
+int snprintf(char *s, size_t size, const char *fmt, ...)
+{
+    struct String_Output_Sink sink;
+    int rc;
+    va_list args;
+
+    /* Prepare string output sink */
+    sink.o.Emit = &String_Emit;
+    sink.o.Finish = &String_Finish;
+    sink.s = s;
+    sink.n = 0;
+    sink.size = size;
+
+    /* Format the string */
+    va_start(args, fmt);
+    rc = Format_Output(&sink.o, fmt, args);
+    va_end(args);
+
+    return rc;
+}
diff --git a/misc/test_vm/src/geekos/README.txt b/misc/test_vm/src/geekos/README.txt
new file mode 100644 (file)
index 0000000..52b6ec4
--- /dev/null
@@ -0,0 +1,5 @@
+GeekOS is a tiny operating system kernel for x86 PCs.  Its main purpose
+is to serve as a simple but realistic example of an OS kernel running
+on real hardware.  
+
+GeekOS is free software: see the file "COPYING" for details.
diff --git a/misc/test_vm/src/geekos/bget.c b/misc/test_vm/src/geekos/bget.c
new file mode 100644 (file)
index 0000000..cccbe16
--- /dev/null
@@ -0,0 +1,1610 @@
+// Adapted for geekos: http://www.cs.umd.edu/~daveho/geekos/
+// Original version of BGET downloaded from: http://www.fourmilab.ch/bget/
+// $Revision: 1.1 $
+
+// GeekOS changes are (mostly) confined to #if defined (GEEKOS)
+// sections.
+
+/*
+
+                              B G E T
+
+                          Buffer allocator
+
+    Designed and implemented in April of 1972 by John Walker, based on the
+    Case Algol OPRO$ algorithm implemented in 1966.
+
+    Reimplemented in 1975 by John Walker for the Interdata 70.
+    Reimplemented in 1977 by John Walker for the Marinchip 9900.
+    Reimplemented in 1982 by Duff Kurland for the Intel 8080.
+
+    Portable C version implemented in September of 1990 by an older, wiser
+    instance of the original implementor.
+
+    Souped up and/or weighed down  slightly  shortly  thereafter  by  Greg
+    Lutz.
+
+    AMIX  edition, including the new compaction call-back option, prepared
+    by John Walker in July of 1992.
+
+    Bug in built-in test program fixed, ANSI compiler warnings eradicated,
+    buffer pool validator  implemented,  and  guaranteed  repeatable  test
+    added by John Walker in October of 1995.
+
+    This program is in the public domain.
+
+     1. This is the book of the generations of Adam.   In the day that God
+       created man, in the likeness of God made he him;
+     2. Male and female created he them;  and  blessed them,  and  called
+       their name Adam, in the day when they were created.
+     3. And  Adam  lived  an hundred and thirty years, and begat a son in
+       his own likeness, and after his image; and called his name Seth:
+     4. And the days of  Adam  after  he  had  begotten  Seth  were  eight
+       hundred years: and he begat sons and daughters:
+     5. And  all  the  days  that Adam lived were nine hundred and thirty
+       years: and he died.
+     6. And Seth lived an hundred and five years, and begat Enos:
+     7. And Seth lived after he begat Enos eight hundred and seven  years,
+       and begat sons and daughters:
+     8.  And  all the days of Seth were nine hundred and twelve years: and
+        he died.
+     9. And Enos lived ninety years, and begat Cainan:
+    10. And Enos lived after he begat  Cainan eight  hundred  and  fifteen
+       years, and begat sons and daughters:
+    11. And  all  the days of Enos were nine hundred  and five years:  and
+       he died.
+    12. And Cainan lived seventy years and begat Mahalaleel:
+    13. And Cainan lived  after he  begat  Mahalaleel  eight  hundred  and
+       forty years, and begat sons and daughters:
+    14. And  all the days of Cainan were nine  hundred and ten years:  and
+       he died.
+    15. And Mahalaleel lived sixty and five years, and begat Jared:
+    16. And Mahalaleel lived  after  he  begat Jared  eight  hundred  and
+       thirty years, and begat sons and daughters:
+    17. And  all  the  days  of Mahalaleel  were eight hundred ninety and
+       five years: and he died.
+    18. And Jared lived an hundred sixty and  two  years,   and  he  begat
+       Enoch:
+    19. And  Jared  lived  after he begat Enoch  eight hundred years,  and
+       begat sons and daughters:
+    20. And all the days of Jared  were nine hundred sixty and two  years:
+       and he died.
+    21. And Enoch lived sixty and five years, and begat Methuselah:
+    22. And  Enoch  walked   with  God after  he  begat Methuselah  three
+       hundred years, and begat sons and daughters:
+    23. And all the days of  Enoch  were  three  hundred  sixty  and  five
+       years:
+    24. And Enoch walked with God: and he was not; for God took him.
+    25. And  Methuselah  lived an  hundred  eighty and  seven years,  and
+       begat Lamech.
+    26. And Methuselah lived after he  begat Lamech seven  hundred  eighty
+       and two years, and begat sons and daughters:
+    27. And  all the days of Methuselah  were nine hundred  sixty and nine
+       years: and he died.
+    28. And Lamech lived an hundred eighty  and two  years,  and  begat  a
+       son:
+    29. And  he called his name Noah, saying,  This same shall comfort us
+       concerning  our  work and toil of our hands, because of the ground
+       which the LORD hath cursed.
+    30. And  Lamech  lived  after  he begat Noah  five hundred ninety and
+       five years, and begat sons and daughters:
+    31. And all the days of Lamech were  seven hundred seventy and  seven
+       years: and he died.
+    32. And  Noah  was five hundred years old: and Noah begat Shem,  Ham,
+       and Japheth.
+
+    And buffers begat buffers, and links begat links,  and  buffer  pools
+    begat  links  to chains of buffer pools containing buffers, and lo the
+    buffers and links and pools of buffers and pools of links to chains of
+    pools  of  buffers were fruitful and they multiplied and the Operating
+    System looked down upon them and said that it was Good.
+
+
+    INTRODUCTION
+    ============
+
+    BGET  is a comprehensive memory allocation package which is easily
+    configured to the needs of an application. BGET is  efficient  in
+    both  the  time  needed to allocate and release buffers and in the
+    memory  overhead  required for  buffer   pool   management.    It
+    automatically    consolidates   contiguous  space   to   minimise
+    fragmentation.  BGET is configured by  compile-time  definitions,
+    Major options include:
+
+       *   A  built-in  test  program  to  exercise  BGET   and
+           demonstrate how the various functions are used.
+
+        *   Allocation  by  either the "first fit" or "best fit"
+           method.
+
+       *   Wiping buffers at release time to catch  code  which
+           references previously released storage.
+
+       *   Built-in  routines to dump individual buffers or the
+           entire buffer pool.
+
+       *   Retrieval of allocation and pool size statistics.
+
+       *   Quantisation of buffer sizes to a power  of  two  to
+           satisfy hardware alignment constraints.
+
+       *   Automatic  pool compaction, growth, and shrinkage by
+           means of call-backs to user defined functions.
+
+    Applications  of  BGET  can  range from  storage  management   in
+    ROM-based  embedded programs to providing the framework upon which
+    a  multitasking  system  incorporating   garbage   collection   is
+    constructed.   BGET  incorporates  extensive  internal consistency
+    checking using the <assert.h> mechanism; all these checks  can  be
+    turned off by compiling with NDEBUG defined, yielding a version of
+    BGET with minimal size and maximum speed.
+
+    The  basic algorithm  underlying  BGET  has withstood the test of
+    time;  more  than  25  years   have   passed   since   the  first
+    implementation  of this  code.  And yet, it is substantially more
+    efficient than the native allocation  schemes  of  many  operating
+    systems: the Macintosh and Microsoft Windows to name two, on which
+    programs have obtained substantial speed-ups by layering  BGET  as
+    an application level memory manager atop the underlying system's.
+
+    BGET has been implemented on the largest mainframes and the lowest
+    of microprocessors.   It  has served as the core for multitasking
+    operating systems, multi-thread applications, embedded software in
+    data  network switching processors, and a host of C programs.  And
+    while it has accreted flexibility and additional options over  the
+    years,  it remains  fast, memory efficient, portable, and easy to
+    integrate into your program.
+
+
+    BGET IMPLEMENTATION ASSUMPTIONS
+    ===============================
+
+    BGET is written in as portable a dialect of C  as  possible.   The
+    only   fundamental  assumption   about  the  underlying  hardware
+    architecture is that memory is allocated is a linear  array  which
+    can  be  addressed  as a vector of C "char" objects.  On segmented
+    address space architectures, this generally means that BGET should
+    be used to allocate storage within a single segment (although some
+    compilers  simulate   linear   address   spaces   on    segmented
+    architectures).   On  segmented  architectures,  then, BGET buffer
+    pools  may not be larger than a segment, but since BGET allows any
+    number of separate buffer pools, there is no limit on  the  total
+    storage  which  can  be  managed,  only  on the largest individual
+    object which can be allocated.  Machines  with  a  linear  address
+    architecture,  such  as  the VAX, 680x0, Sparc, MIPS, or the Intel
+    80386 and above in native mode, may use BGET without restriction.
+
+
+    GETTING STARTED WITH BGET
+    =========================
+
+    Although BGET can be configured in a multitude of fashions,  there
+    are  three basic  ways  of  working  with  BGET.   The  functions
+    mentioned below are documented in the following  section.  Please
+    excuse  the  forward  references which are made in the interest of
+    providing a roadmap to guide you  to  the  BGET  functions  you're
+    likely to need.
+
+    Embedded Applications
+    ---------------------
+
+    Embedded applications  typically  have  a  fixed  area  of memory
+    dedicated  to  buffer  allocation (often in a separate RAM address
+    space distinct from the ROM that contains  the  executable code).
+    To use  BGET in such an environment, simply call bpool() with the
+    start address and length of the buffer  pool  area in  RAM,  then
+    allocate  buffers  with  bget()  and  release  them  with  brel().
+    Embedded applications with very limited RAM but abundant CPU speed
+    may  benefit  by configuring BGET for BestFit allocation (which is
+    usually not worth it in other environments).
+
+    Malloc() Emulation
+    ------------------
+
+    If the C library malloc() function is too  slow,  not  present  in
+    your  development environment (for example, an a native Windows or
+    Macintosh program), or otherwise unsuitable, you  can  replace  it
+    with  BGET.  Initially define a buffer pool of an appropriate size
+    with bpool()--usually obtained by making a call to the  operating
+    system's  low-level  memory allocator.  Then allocate buffers with
+    bget(), bgetz(), and bgetr() (the last two permit  the  allocation
+    of buffers initialised to zero and [inefficient] re-allocation of
+    existing buffers for  compatibility  with  C  library  functions).
+    Release buffers by calling brel(). If a buffer allocation request
+    fails, obtain more storage from the underlying  operating  system,
+    add it to the buffer pool by another call to bpool(), and continue
+    execution.
+
+    Automatic Storage Management
+    ----------------------------
+
+    You can use BGET as your application's native memory  manager  and
+    implement  automatic  storage  pool  expansion,  contraction,  and
+    optionally application-specific  memory  compaction  by  compiling
+    BGET  with the  BECtl  variable defined, then calling bectl() and
+    supplying  functions  for  storage compaction,  acquisition,  and
+    release,  as  well as a standard pool expansion increment. All of
+    these functions are optional (although it doesn't make much  sense
+    to provide  a  release  function without an acquisition function,
+    does it?). Once the call-back functions have  been  defined  with
+    bectl(),  you simply use bget() and brel() to allocate and release
+    storage as before. You can supply an  initial  buffer  pool  with
+    bpool()  or  rely  on  automatic  allocation to acquire the entire
+    pool.  When a call on  bget()  cannot  be  satisfied,  BGET  first
+    checks  if a compaction function has been supplied.  If so, it is
+    called (with the space required to satisfy the allocation  request
+    and a sequence number to allow the compaction routine to be called
+    successively without looping).  If the compaction function is able
+    to  free any storage (it needn't know whether the storage it freed
+    was adequate) it should return a  nonzero  value,  whereupon  BGET
+    will retry the allocation request and, if it fails again, call the
+    compaction function again with the next-higher sequence number.
+
+    If the  compaction  function  returns zero, indicating failure to
+    free space, or no compaction function is defined, BGET next  tests
+    whether  a non-NULL  allocation function was supplied to bectl().
+    If so, that function is called with  an  argument  indicating  how
+    many  bytes  of  additional  space are required.  This will be the
+    standard pool expansion increment supplied in the call to  bectl()
+    unless  the  original  bget()  call requested a buffer larger than
+    this; buffers larger than the standard pool block can  be  managed
+    "off  the books" by BGET in this mode.  If the allocation function
+    succeeds in obtaining the storage, it returns a pointer to the new
+    block  and BGET  expands  the  buffer  pool;  if  it  fails,  the
+    allocation request fails and returns NULL to  the  caller.  If  a
+    non-NULL  release  function  is  supplied, expansion blocks which
+    become totally empty are released  to  the global  free  pool  by
+    passing their addresses to the release function.
+
+    Equipped  with  appropriate  allocation,  release, and compaction
+    functions, BGET can be used as part of very  sophisticated memory
+    management  strategies,  including  garbage  collection.   (Note,
+    however, that BGET is *not* a garbage  collector  by  itself,  and
+    that  developing  such a system requires much additional logic and
+    careful design of the application's memory allocation strategy.)
+
+
+    BGET FUNCTION DESCRIPTIONS
+    ==========================
+
+    Functions implemented in this file (some are enabled by certain of
+    the optional settings below):
+
+           void bpool(void *buffer, bufsize len);
+
+    Create a buffer pool of <len> bytes, using the storage starting at
+    <buffer>.  You  can  call  bpool()  subsequently  to   contribute
+    additional storage to the overall buffer pool.
+
+           void *bget(bufsize size);
+
+    Allocate  a  buffer of <size> bytes.  The address of the buffer is
+    returned, or NULL if insufficient memory was available to allocate
+    the buffer.
+
+           void *bgetz(bufsize size);
+
+    Allocate a buffer of <size> bytes and clear it to all zeroes.  The
+    address of the buffer is returned, or NULL if insufficient memory
+    was available to allocate the buffer.
+
+           void *bgetr(void *buffer, bufsize newsize);
+
+    Reallocate a buffer previously allocated by bget(),  changing  its
+    size  to  <newsize>  and  preserving  all  existing data.  NULL is
+    returned if insufficient memory is available  to  reallocate  the
+    buffer, in which case the original buffer remains intact.
+
+           void brel(void *buf);
+
+    Return  the  buffer  <buf>, previously allocated by bget(), to the
+    free space pool.
+
+           void bectl(int (*compact)(bufsize sizereq, int sequence),
+                      void *(*acquire)(bufsize size),
+                      void (*release)(void *buf),
+                      bufsize pool_incr);
+
+    Expansion control: specify functions through which the package may
+    compact  storage  (or  take  other appropriate  action)  when  an
+    allocation request  fails,  and  optionally automatically acquire
+    storage for expansion blocks  when necessary,  and  release  such
+    blocks when they become empty.  If <compact> is non-NULL, whenever
+    a buffer allocation request fails, the <compact> function will  be
+    called with arguments specifying the number of bytes (total buffer
+    size,  including  header  overhead)  required   to  satisfy   the
+    allocation request, and a sequence number indicating the number of
+    consecutive  calls on  <compact>  attempting  to   satisfy   this
+    allocation request.   The sequence number is 1 for the first call
+    on <compact> for a given allocation  request,  and increments  on
+    subsequent calls,  permitting  the  <compact>  function  to  take
+    increasingly dire measures in an attempt to free up  storage.   If
+    the  <compact>  function  returns  a nonzero value, the allocation
+    attempt is re-tried.  If <compact> returns 0 (as  it  must if  it
+    isn't  able  to  release  any  space  or add storage to the buffer
+    pool), the allocation request fails, which can  trigger  automatic
+    pool expansion if the <acquire> argument is non-NULL.  At the time
+    the  <compact>  function  is  called,  the state  of  the  buffer
+    allocator  is  identical  to  that at  the  moment the allocation
+    request was made; consequently, the <compact>  function  may  call
+    brel(), bpool(), bstats(), and/or directly manipulate  the buffer
+    pool  in  any  manner which would be valid were the application in
+    control.  This does not, however, relieve the  <compact>  function
+    of the need to ensure that whatever actions it takes do not change
+    things   underneath  the  application  that  made  the  allocation
+    request.  For example, a <compact> function that released a buffer
+    in the  process  of  being reallocated with bgetr() would lead to
+    disaster.  Implementing a safe and effective  <compact>  mechanism
+    requires  careful  design of an application's memory architecture,
+    and cannot generally be easily retrofitted into existing code.
+
+    If <acquire> is non-NULL, that function will be called whenever an
+    allocation request  fails.  If the <acquire> function succeeds in
+    allocating the requested space and returns a pointer  to  the  new
+    area,  allocation will proceed using the expanded buffer pool.  If
+    <acquire> cannot obtain the requested space, it should return NULL
+    and   the  entire  allocation  process  will  fail.   <pool_incr>
+    specifies the normal expansion block size. Providing an <acquire>
+    function will cause subsequent bget()  requests  for  buffers  too
+    large  to  be  managed in the linked-block scheme (in other words,
+    larger than <pool_incr> minus the buffer overhead) to be satisfied
+    directly by calls to the <acquire> function.  Automatic release of
+    empty pool blocks will occur only if all pool blocks in the system
+    are the size given by <pool_incr>.
+
+           void bstats(bufsize *curalloc, bufsize *totfree,
+                       bufsize *maxfree, long *nget, long *nrel);
+
+    The amount of  space  currently  allocated  is  stored  into  the
+    variable  pointed  to by <curalloc>.  The total free space (sum of
+    all free blocks in the pool) is stored into the  variable  pointed
+    to by  <totfree>, and the size of the largest single block in the
+    free space pool  is  stored  into  the  variable  pointed  to  by
+    <maxfree>.  The  variables  pointed  to  by <nget> and <nrel> are
+    filled, respectively, with the  number  of  successful  (non-NULL
+    return) bget() calls and the number of brel() calls.
+
+           void bstatse(bufsize *pool_incr, long *npool,
+                        long *npget, long *nprel,
+                        long *ndget, long *ndrel);
+
+    Extended  statistics: The expansion block size will be stored into
+    the variable pointed to by <pool_incr>, or the negative thereof if
+    automatic  expansion  block  releases are disabled.  The number of
+    currently active pool blocks will  be  stored  into  the  variable
+    pointed  to  by  <npool>.  The variables pointed to by <npget> and
+    <nprel> will be filled with, respectively, the number of expansion
+    block   acquisitions   and releases  which  have  occurred.   The
+    variables pointed to by <ndget> and <ndrel> will  be  filled  with
+    the  number  of  bget()  and  brel()  calls, respectively, managed
+    through blocks directly allocated by the acquisition  and  release
+    functions.
+
+           void bufdump(void *buf);
+
+    The buffer pointed to by <buf> is dumped on standard output.
+
+           void bpoold(void *pool, int dumpalloc, int dumpfree);
+
+    All buffers in the buffer pool <pool>, previously initialised by a
+    call on bpool(), are listed in ascending memory address order.  If
+    <dumpalloc> is nonzero, the  contents  of  allocated  buffers  are
+    dumped;  if <dumpfree> is nonzero, the contents of free blocks are
+    dumped.
+
+           int bpoolv(void *pool);
+
+    The  named buffer  pool,  previously  initialised  by  a  call on
+    bpool(), is validated for bad pointers, overwritten data, etc.  If
+    compiled with NDEBUG not defined, any error generates an assertion
+    failure.  Otherwise 1 is returned if the pool is valid,  0 if  an
+    error is found.
+
+
+    BGET CONFIGURATION
+    ==================
+*/
+
+/*#define TestProg    20000*/        /* Generate built-in test program
+                                        if defined.  The value specifies
+                                        how many buffer allocation attempts
+                                        the test program should make. */
+
+#define SizeQuant   4                /* Buffer allocation size quantum:
+                                        all buffers allocated are a
+                                        multiple of this size.  This
+                                        MUST be a power of two. */
+
+/*#define BufDump     1*/                    /* Define this symbol to enable the
+                                        bpoold() function which dumps the
+                                        buffers in a buffer pool. */
+
+/*#define BufValid    1*/                    /* Define this symbol to enable the
+                                        bpoolv() function for validating
+                                        a buffer pool. */ 
+
+/*#define DumpData    1*/                    /* Define this symbol to enable the
+                                        bufdump() function which allows
+                                        dumping the contents of an allocated
+                                        or free buffer. */
+
+/*#define BufStats    1*/                    /* Define this symbol to enable the
+                                        bstats() function which calculates
+                                        the total free space in the buffer
+                                        pool, the largest available
+                                        buffer, and the total space
+                                        currently allocated. */
+
+/*#define FreeWipe    1*/                    /* Wipe free buffers to a guaranteed
+                                        pattern of garbage to trip up
+                                        miscreants who attempt to use
+                                        pointers into released buffers. */
+
+#define BestFit     1                /* Use a best fit algorithm when
+                                        searching for space for an
+                                        allocation request.  This uses
+                                        memory more efficiently, but
+                                        allocation will be much slower. */
+
+/*#define BECtl            1*/               /* Define this symbol to enable the
+                                        bectl() function for automatic
+                                        pool space control.  */
+
+#if defined (GEEKOS)
+
+#include <geekos/string.h> // for memset()
+
+// Provide an assert() macro
+#include <geekos/kassert.h>
+#define assert(exp) KASSERT(exp)
+
+#else // define (GEEKOS)
+
+#include <stdio.h>
+
+#ifdef lint
+#define NDEBUG                       /* Exits in asserts confuse lint */
+/* LINTLIBRARY */                     /* Don't complain about def, no ref */
+extern char *sprintf();               /* Sun includes don't define sprintf */
+#endif
+
+#include <assert.h>
+#include <memory.h>
+
+#endif // defined (GEEKOS)
+
+#ifdef BufDump                       /* BufDump implies DumpData */
+#ifndef DumpData
+#define DumpData    1
+#endif
+#endif
+
+#ifdef DumpData
+#include <ctype.h>
+#endif
+
+/*  Declare the interface, including the requested buffer size type,
+    bufsize.  */
+
+#include <geekos/bget.h>
+
+#define MemSize     int              /* Type for size arguments to memxxx()
+                                        functions such as memcmp(). */
+
+/* Queue links */
+
+struct qlinks {
+    struct bfhead *flink;            /* Forward link */
+    struct bfhead *blink;            /* Backward link */
+};
+
+/* Header in allocated and free buffers */
+
+struct bhead {
+    bufsize prevfree;                /* Relative link back to previous
+                                        free buffer in memory or 0 if
+                                        previous buffer is allocated.  */
+    bufsize bsize;                   /* Buffer size: positive if free,
+                                        negative if allocated. */
+};
+#define BH(p)  ((struct bhead *) (p))
+
+/*  Header in directly allocated buffers (by acqfcn) */
+
+struct bdhead {
+    bufsize tsize;                   /* Total size, including overhead */
+    struct bhead bh;                 /* Common header */
+};
+#define BDH(p) ((struct bdhead *) (p))
+
+/* Header in free buffers */
+
+struct bfhead {
+    struct bhead bh;                 /* Common allocated/free header */
+    struct qlinks ql;                /* Links on free list */
+};
+#define BFH(p) ((struct bfhead *) (p))
+
+static struct bfhead freelist = {     /* List of free buffers */
+    {0, 0},
+    {&freelist, &freelist}
+};
+
+
+#ifdef BufStats
+static bufsize totalloc = 0;         /* Total space currently allocated */
+static long numget = 0, numrel = 0;   /* Number of bget() and brel() calls */
+#ifdef BECtl
+static long numpblk = 0;             /* Number of pool blocks */
+static long numpget = 0, numprel = 0; /* Number of block gets and rels */
+static long numdget = 0, numdrel = 0; /* Number of direct gets and rels */
+#endif /* BECtl */
+#endif /* BufStats */
+
+#ifdef BECtl
+
+/* Automatic expansion block management functions */
+
+static int (*compfcn) _((bufsize sizereq, int sequence)) = NULL;
+static void *(*acqfcn) _((bufsize size)) = NULL;
+static void (*relfcn) _((void *buf)) = NULL;
+
+static bufsize exp_incr = 0;         /* Expansion block size */
+static bufsize pool_len = 0;         /* 0: no bpool calls have been made
+                                        -1: not all pool blocks are
+                                            the same size
+                                        >0: (common) block size for all
+                                            bpool calls made so far
+                                     */
+#endif
+
+/*  Minimum allocation quantum: */
+
+#define QLSize (sizeof(struct qlinks))
+#define SizeQ  ((SizeQuant > QLSize) ? SizeQuant : QLSize)
+
+#define V   (void)                   /* To denote unwanted returned values */
+
+/* End sentinel: value placed in bsize field of dummy block delimiting
+   end of pool block.  The most negative number which will  fit  in  a
+   bufsize, defined in a way that the compiler will accept. */
+
+#define ESent  ((bufsize) (-(((1L << (sizeof(bufsize) * 8 - 2)) - 1) * 2) - 2))
+
+/*  BGET  --  Allocate a buffer.  */
+
+void *bget(requested_size)
+  bufsize requested_size;
+{
+    bufsize size = requested_size;
+    struct bfhead *b;
+#ifdef BestFit
+    struct bfhead *best;
+#endif
+    void *buf;
+#ifdef BECtl
+    int compactseq = 0;
+#endif
+
+    assert(size > 0);
+
+    if (size < SizeQ) {              /* Need at least room for the */
+       size = SizeQ;                 /*    queue links.  */
+    }
+#ifdef SizeQuant
+#if SizeQuant > 1
+    size = (size + (SizeQuant - 1)) & (~(SizeQuant - 1));
+#endif
+#endif
+
+    size += sizeof(struct bhead);     /* Add overhead in allocated buffer
+                                        to size required. */
+
+#ifdef BECtl
+    /* If a compact function was provided in the call to bectl(), wrap
+       a loop around the allocation process  to  allow compaction  to
+       intervene in case we don't find a suitable buffer in the chain. */
+
+    while (1) {
+#endif
+       b = freelist.ql.flink;
+#ifdef BestFit
+       best = &freelist;
+#endif
+
+
+       /* Scan the free list searching for the first buffer big enough
+          to hold the requested size buffer. */
+
+#ifdef BestFit
+       while (b != &freelist) {
+           if (b->bh.bsize >= size) {
+               if ((best == &freelist) || (b->bh.bsize < best->bh.bsize)) {
+                   best = b;
+               }
+           }
+           b = b->ql.flink;              /* Link to next buffer */
+       }
+       b = best;
+#endif /* BestFit */
+
+       while (b != &freelist) {
+           if ((bufsize) b->bh.bsize >= size) {
+
+               /* Buffer  is big enough to satisfy  the request.  Allocate it
+                  to the caller.  We must decide whether the buffer is  large
+                  enough  to  split  into  the part given to the caller and a
+                  free buffer that remains on the free list, or  whether  the
+                  entire  buffer  should  be  removed  from the free list and
+                  given to the caller in its entirety.   We  only  split  the
+                  buffer if enough room remains for a header plus the minimum
+                  quantum of allocation. */
+
+               if ((b->bh.bsize - size) > (SizeQ + (sizeof(struct bhead)))) {
+                   struct bhead *ba, *bn;
+
+                   ba = BH(((char *) b) + (b->bh.bsize - size));
+                   bn = BH(((char *) ba) + size);
+                   assert(bn->prevfree == b->bh.bsize);
+                   /* Subtract size from length of free block. */
+                   b->bh.bsize -= size;
+                   /* Link allocated buffer to the previous free buffer. */
+                   ba->prevfree = b->bh.bsize;
+                   /* Plug negative size into user buffer. */
+                   ba->bsize = -(bufsize) size;
+                   /* Mark buffer after this one not preceded by free block. */
+                   bn->prevfree = 0;
+
+#ifdef BufStats
+                   totalloc += size;
+                   numget++;             /* Increment number of bget() calls */
+#endif
+                   buf = (void *) ((((char *) ba) + sizeof(struct bhead)));
+                   return buf;
+               } else {
+                   struct bhead *ba;
+
+                   ba = BH(((char *) b) + b->bh.bsize);
+                   assert(ba->prevfree == b->bh.bsize);
+
+                    /* The buffer isn't big enough to split.  Give  the  whole
+                      shebang to the caller and remove it from the free list. */
+
+                   assert(b->ql.blink->ql.flink == b);
+                   assert(b->ql.flink->ql.blink == b);
+                   b->ql.blink->ql.flink = b->ql.flink;
+                   b->ql.flink->ql.blink = b->ql.blink;
+
+#ifdef BufStats
+                   totalloc += b->bh.bsize;
+                   numget++;             /* Increment number of bget() calls */
+#endif
+                   /* Negate size to mark buffer allocated. */
+                   b->bh.bsize = -(b->bh.bsize);
+
+                   /* Zero the back pointer in the next buffer in memory
+                      to indicate that this buffer is allocated. */
+                   ba->prevfree = 0;
+
+                   /* Give user buffer starting at queue links. */
+                   buf =  (void *) &(b->ql);
+                   return buf;
+               }
+           }
+           b = b->ql.flink;              /* Link to next buffer */
+       }
+#ifdef BECtl
+
+        /* We failed to find a buffer.  If there's a compact  function
+          defined,  notify  it  of the size requested.  If it returns
+          true, try the allocation again. */
+
+       if ((compfcn == NULL) || (!(*compfcn)(size, ++compactseq))) {
+           break;
+       }
+    }
+
+    /* No buffer available with requested size free. */
+
+    /* Don't give up yet -- look in the reserve supply. */
+
+    if (acqfcn != NULL) {
+       if (size > exp_incr - sizeof(struct bhead)) {
+
+           /* Request  is  too  large  to  fit in a single expansion
+              block.  Try to satisy it by a direct buffer acquisition. */
+
+           struct bdhead *bdh;
+
+           size += sizeof(struct bdhead) - sizeof(struct bhead);
+           if ((bdh = BDH((*acqfcn)((bufsize) size))) != NULL) {
+
+               /*  Mark the buffer special by setting the size field
+                   of its header to zero.  */
+               bdh->bh.bsize = 0;
+               bdh->bh.prevfree = 0;
+               bdh->tsize = size;
+#ifdef BufStats
+               totalloc += size;
+               numget++;             /* Increment number of bget() calls */
+               numdget++;            /* Direct bget() call count */
+#endif
+               buf =  (void *) (bdh + 1);
+               return buf;
+           }
+
+       } else {
+
+           /*  Try to obtain a new expansion block */
+
+           void *newpool;
+
+           if ((newpool = (*acqfcn)((bufsize) exp_incr)) != NULL) {
+               bpool(newpool, exp_incr);
+                buf =  bget(requested_size);  /* This can't, I say, can't
+                                                get into a loop. */
+               return buf;
+           }
+       }
+    }
+
+    /* Still no buffer available */
+
+#endif /* BECtl */
+
+    return NULL;
+}
+
+/*  BGETZ  --  Allocate a buffer and clear its contents to zero.  We clear
+              the  entire  contents  of  the buffer to zero, not just the
+              region requested by the caller. */
+
+void *bgetz(size)
+  bufsize size;
+{
+    char *buf = (char *) bget(size);
+
+    if (buf != NULL) {
+       struct bhead *b;
+       bufsize rsize;
+
+       b = BH(buf - sizeof(struct bhead));
+       rsize = -(b->bsize);
+       if (rsize == 0) {
+           struct bdhead *bd;
+
+           bd = BDH(buf - sizeof(struct bdhead));
+           rsize = bd->tsize - sizeof(struct bdhead);
+       } else {
+           rsize -= sizeof(struct bhead);
+       }
+       assert(rsize >= size);
+       V memset(buf, 0, (MemSize) rsize);
+    }
+    return ((void *) buf);
+}
+
+/*  BGETR  --  Reallocate a buffer.  This is a minimal implementation,
+              simply in terms of brel()  and  bget().   It  could  be
+              enhanced to allow the buffer to grow into adjacent free
+              blocks and to avoid moving data unnecessarily.  */
+
+void *bgetr(buf, size)
+  void *buf;
+  bufsize size;
+{
+    void *nbuf;
+    bufsize osize;                   /* Old size of buffer */
+    struct bhead *b;
+
+    if ((nbuf = bget(size)) == NULL) { /* Acquire new buffer */
+       return NULL;
+    }
+    if (buf == NULL) {
+       return nbuf;
+    }
+    b = BH(((char *) buf) - sizeof(struct bhead));
+    osize = -b->bsize;
+#ifdef BECtl
+    if (osize == 0) {
+       /*  Buffer acquired directly through acqfcn. */
+       struct bdhead *bd;
+
+       bd = BDH(((char *) buf) - sizeof(struct bdhead));
+       osize = bd->tsize - sizeof(struct bdhead);
+    } else
+#endif
+       osize -= sizeof(struct bhead);
+    assert(osize > 0);
+    V memcpy((char *) nbuf, (char *) buf, /* Copy the data */
+            (MemSize) ((size < osize) ? size : osize));
+    brel(buf);
+    return nbuf;
+}
+
+/*  BREL  --  Release a buffer.  */
+
+void brel(buf)
+  void *buf;
+{
+    struct bfhead *b, *bn;
+
+    b = BFH(((char *) buf) - sizeof(struct bhead));
+#ifdef BufStats
+    numrel++;                        /* Increment number of brel() calls */
+#endif
+    assert(buf != NULL);
+
+#ifdef BECtl
+    if (b->bh.bsize == 0) {          /* Directly-acquired buffer? */
+       struct bdhead *bdh;
+
+       bdh = BDH(((char *) buf) - sizeof(struct bdhead));
+       assert(b->bh.prevfree == 0);
+#ifdef BufStats
+       totalloc -= bdh->tsize;
+       assert(totalloc >= 0);
+       numdrel++;                    /* Number of direct releases */
+#endif /* BufStats */
+#ifdef FreeWipe
+       V memset((char *) buf, 0x55,
+                (MemSize) (bdh->tsize - sizeof(struct bdhead)));
+#endif /* FreeWipe */
+       assert(relfcn != NULL);
+       (*relfcn)((void *) bdh);      /* Release it directly. */
+       return;
+    }
+#endif /* BECtl */
+
+    /* Buffer size must be negative, indicating that the buffer is
+       allocated. */
+
+    if (b->bh.bsize >= 0) {
+       bn = NULL;
+    }
+    assert(b->bh.bsize < 0);
+
+    /* Back pointer in next buffer must be zero, indicating the
+       same thing: */
+
+    assert(BH((char *) b - b->bh.bsize)->prevfree == 0);
+
+#ifdef BufStats
+    totalloc += b->bh.bsize;
+    assert(totalloc >= 0);
+#endif
+
+    /* If the back link is nonzero, the previous buffer is free.  */
+
+    if (b->bh.prevfree != 0) {
+
+       /* The previous buffer is free.  Consolidate this buffer  with  it
+          by  adding  the  length  of  this  buffer  to the previous free
+          buffer.  Note that we subtract the size  in  the  buffer  being
+           released,  since  it's  negative to indicate that the buffer is
+          allocated. */
+
+       register bufsize size = b->bh.bsize;
+
+        /* Make the previous buffer the one we're working on. */
+       assert(BH((char *) b - b->bh.prevfree)->bsize == b->bh.prevfree);
+       b = BFH(((char *) b) - b->bh.prevfree);
+       b->bh.bsize -= size;
+    } else {
+
+        /* The previous buffer isn't allocated.  Insert this buffer
+          on the free list as an isolated free block. */
+
+       assert(freelist.ql.blink->ql.flink == &freelist);
+       assert(freelist.ql.flink->ql.blink == &freelist);
+       b->ql.flink = &freelist;
+       b->ql.blink = freelist.ql.blink;
+       freelist.ql.blink = b;
+       b->ql.blink->ql.flink = b;
+       b->bh.bsize = -b->bh.bsize;
+    }
+
+    /* Now we look at the next buffer in memory, located by advancing from
+       the  start  of  this  buffer  by its size, to see if that buffer is
+       free.  If it is, we combine  this  buffer  with the  next  one  in
+       memory, dechaining the second buffer from the free list. */
+
+    bn =  BFH(((char *) b) + b->bh.bsize);
+    if (bn->bh.bsize > 0) {
+
+       /* The buffer is free.  Remove it from the free list and add
+          its size to that of our buffer. */
+
+       assert(BH((char *) bn + bn->bh.bsize)->prevfree == bn->bh.bsize);
+       assert(bn->ql.blink->ql.flink == bn);
+       assert(bn->ql.flink->ql.blink == bn);
+       bn->ql.blink->ql.flink = bn->ql.flink;
+       bn->ql.flink->ql.blink = bn->ql.blink;
+       b->bh.bsize += bn->bh.bsize;
+
+       /* Finally,  advance  to   the  buffer  that   follows  the  newly
+          consolidated free block.  We must set its  backpointer  to  the
+          head  of  the  consolidated free block.  We know the next block
+          must be an allocated block because the process of recombination
+          guarantees  that  two  free  blocks will never be contiguous in
+          memory.  */
+
+       bn = BFH(((char *) b) + b->bh.bsize);
+    }
+#ifdef FreeWipe
+    V memset(((char *) b) + sizeof(struct bfhead), 0x55,
+           (MemSize) (b->bh.bsize - sizeof(struct bfhead)));
+#endif
+    assert(bn->bh.bsize < 0);
+
+    /* The next buffer is allocated.  Set the backpointer in it  to  point
+       to this buffer; the previous free buffer in memory. */
+
+    bn->bh.prevfree = b->bh.bsize;
+
+#ifdef BECtl
+
+    /* If  a  block-release function is defined, and this free buffer
+       constitutes the entire block, release it.  Note that  pool_len
+       is  defined  in  such a way that the test will fail unless all
+       pool blocks are the same size.  */
+
+    if (relfcn != NULL &&
+       ((bufsize) b->bh.bsize) == (pool_len - sizeof(struct bhead))) {
+
+       assert(b->bh.prevfree == 0);
+       assert(BH((char *) b + b->bh.bsize)->bsize == ESent);
+       assert(BH((char *) b + b->bh.bsize)->prevfree == b->bh.bsize);
+       /*  Unlink the buffer from the free list  */
+       b->ql.blink->ql.flink = b->ql.flink;
+       b->ql.flink->ql.blink = b->ql.blink;
+
+       (*relfcn)(b);
+#ifdef BufStats
+       numprel++;                    /* Nr of expansion block releases */
+       numpblk--;                    /* Total number of blocks */
+       assert(numpblk == numpget - numprel);
+#endif /* BufStats */
+    }
+#endif /* BECtl */
+}
+
+#ifdef BECtl
+
+/*  BECTL  --  Establish automatic pool expansion control  */
+
+void bectl(compact, acquire, release, pool_incr)
+  int (*compact) _((bufsize sizereq, int sequence));
+  void *(*acquire) _((bufsize size));
+  void (*release) _((void *buf));
+  bufsize pool_incr;
+{
+    compfcn = compact;
+    acqfcn = acquire;
+    relfcn = release;
+    exp_incr = pool_incr;
+}
+#endif
+
+/*  BPOOL  --  Add a region of memory to the buffer pool.  */
+
+void bpool(buf, len)
+  void *buf;
+  bufsize len;
+{
+    struct bfhead *b = BFH(buf);
+    struct bhead *bn;
+
+#ifdef SizeQuant
+    len &= ~(SizeQuant - 1);
+#endif
+#ifdef BECtl
+    if (pool_len == 0) {
+       pool_len = len;
+    } else if (len != pool_len) {
+       pool_len = -1;
+    }
+#ifdef BufStats
+    numpget++;                       /* Number of block acquisitions */
+    numpblk++;                       /* Number of blocks total */
+    assert(numpblk == numpget - numprel);
+#endif /* BufStats */
+#endif /* BECtl */
+
+    /* Since the block is initially occupied by a single free  buffer,
+       it  had better  not  be  (much) larger than the largest buffer
+       whose size we can store in bhead.bsize. */
+
+    assert(len - sizeof(struct bhead) <= -((bufsize) ESent + 1));
+
+    /* Clear  the  backpointer at  the start of the block to indicate that
+       there  is  no  free  block  prior  to  this   one.    That   blocks
+       recombination when the first block in memory is released. */
+
+    b->bh.prevfree = 0;
+
+    /* Chain the new block to the free list. */
+
+    assert(freelist.ql.blink->ql.flink == &freelist);
+    assert(freelist.ql.flink->ql.blink == &freelist);
+    b->ql.flink = &freelist;
+    b->ql.blink = freelist.ql.blink;
+    freelist.ql.blink = b;
+    b->ql.blink->ql.flink = b;
+
+    /* Create a dummy allocated buffer at the end of the pool. This dummy
+       buffer is seen when a buffer at the end of the pool is released and
+       blocks  recombination  of  the last buffer with the dummy buffer at
+       the end.  The length in the dummy buffer  is  set  to  the  largest
+       negative  number  to  denote  the  end  of  the pool for diagnostic
+       routines (this specific value is  not  counted  on  by  the  actual
+       allocation and release functions). */
+
+    len -= sizeof(struct bhead);
+    b->bh.bsize = (bufsize) len;
+#ifdef FreeWipe
+    V memset(((char *) b) + sizeof(struct bfhead), 0x55,
+            (MemSize) (len - sizeof(struct bfhead)));
+#endif
+    bn = BH(((char *) b) + len);
+    bn->prevfree = (bufsize) len;
+    /* Definition of ESent assumes two's complement! */
+    assert((~0) == -1);
+    bn->bsize = ESent;
+}
+
+#ifdef BufStats
+
+/*  BSTATS  -- Return buffer allocation free space statistics.  */
+
+void bstats(curalloc, totfree, maxfree, nget, nrel)
+  bufsize *curalloc, *totfree, *maxfree;
+  long *nget, *nrel;
+{
+    struct bfhead *b = freelist.ql.flink;
+
+    *nget = numget;
+    *nrel = numrel;
+    *curalloc = totalloc;
+    *totfree = 0;
+    *maxfree = -1;
+    while (b != &freelist) {
+       assert(b->bh.bsize > 0);
+       *totfree += b->bh.bsize;
+       if (b->bh.bsize > *maxfree) {
+           *maxfree = b->bh.bsize;
+       }
+       b = b->ql.flink;              /* Link to next buffer */
+    }
+}
+
+#ifdef BECtl
+
+/*  BSTATSE  --  Return extended statistics  */
+
+void bstatse(pool_incr, npool, npget, nprel, ndget, ndrel)
+  bufsize *pool_incr;
+  long *npool, *npget, *nprel, *ndget, *ndrel;
+{
+    *pool_incr = (pool_len < 0) ? -exp_incr : exp_incr;
+    *npool = numpblk;
+    *npget = numpget;
+    *nprel = numprel;
+    *ndget = numdget;
+    *ndrel = numdrel;
+}
+#endif /* BECtl */
+#endif /* BufStats */
+
+#ifdef DumpData
+
+/*  BUFDUMP  --  Dump the data in a buffer.  This is called with the  user
+                data pointer, and backs up to the buffer header.  It will
+                dump either a free block or an allocated one.  */
+
+void bufdump(buf)
+  void *buf;
+{
+    struct bfhead *b;
+    uchar_t *bdump;
+    bufsize bdlen;
+
+    b = BFH(((char *) buf) - sizeof(struct bhead));
+    assert(b->bh.bsize != 0);
+    if (b->bh.bsize < 0) {
+       bdump = (uchar_t *) buf;
+       bdlen = (-b->bh.bsize) - sizeof(struct bhead);
+    } else {
+       bdump = (uchar_t *) (((char *) b) + sizeof(struct bfhead));
+       bdlen = b->bh.bsize - sizeof(struct bfhead);
+    }
+
+    while (bdlen > 0) {
+       int i, dupes = 0;
+       bufsize l = bdlen;
+       char bhex[50], bascii[20];
+
+       if (l > 16) {
+           l = 16;
+       }
+
+       for (i = 0; i < l; i++) {
+            V sprintf(bhex + i * 3, "%02X ", bdump[i]);
+            bascii[i] = isprint(bdump[i]) ? bdump[i] : ' ';
+       }
+       bascii[i] = 0;
+        V printf("%-48s   %s\n", bhex, bascii);
+       bdump += l;
+       bdlen -= l;
+       while ((bdlen > 16) && (memcmp((char *) (bdump - 16),
+                                      (char *) bdump, 16) == 0)) {
+           dupes++;
+           bdump += 16;
+           bdlen -= 16;
+       }
+       if (dupes > 1) {
+           V printf(
+                "     (%d lines [%d bytes] identical to above line skipped)\n",
+               dupes, dupes * 16);
+       } else if (dupes == 1) {
+           bdump -= 16;
+           bdlen += 16;
+       }
+    }
+}
+#endif
+
+#ifdef BufDump
+
+/*  BPOOLD  -- Dump a buffer pool.  The buffer headers are always listed.
+               If DUMPALLOC is nonzero, the contents of allocated buffers
+               are  dumped.   If  DUMPFREE  is  nonzero,  free blocks are
+               dumped as well.  If FreeWipe  checking  is  enabled,  free
+               blocks  which  have  been clobbered will always be dumped. */
+
+void bpoold(buf, dumpalloc, dumpfree)
+  void *buf;
+  int dumpalloc, dumpfree;
+{
+    struct bfhead *b = BFH(buf);
+
+    while (b->bh.bsize != ESent) {
+       bufsize bs = b->bh.bsize;
+
+       if (bs < 0) {
+           bs = -bs;
+            V printf("Allocated buffer: size %6ld bytes.\n", (long) bs);
+           if (dumpalloc) {
+               bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+           }
+       } else {
+            char *lerr = "";
+
+           assert(bs > 0);
+           if ((b->ql.blink->ql.flink != b) ||
+               (b->ql.flink->ql.blink != b)) {
+                lerr = "  (Bad free list links)";
+           }
+            V printf("Free block:       size %6ld bytes.%s\n",
+               (long) bs, lerr);
+#ifdef FreeWipe
+           lerr = ((char *) b) + sizeof(struct bfhead);
+           if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) ||
+               (memcmp(lerr, lerr + 1,
+                 (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) {
+               V printf(
+                    "(Contents of above free block have been overstored.)\n");
+               bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+           } else
+#endif
+           if (dumpfree) {
+               bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+           }
+       }
+       b = BFH(((char *) b) + bs);
+    }
+}
+#endif /* BufDump */
+
+#ifdef BufValid
+
+/*  BPOOLV  --  Validate a buffer pool.  If NDEBUG isn't defined,
+               any error generates an assertion failure.  */
+
+int bpoolv(buf)
+  void *buf;
+{
+    struct bfhead *b = BFH(buf);
+
+    while (b->bh.bsize != ESent) {
+       bufsize bs = b->bh.bsize;
+
+       if (bs < 0) {
+           bs = -bs;
+       } else {
+            char *lerr = "";
+
+           assert(bs > 0);
+           if (bs <= 0) {
+               return 0;
+           }
+           if ((b->ql.blink->ql.flink != b) ||
+               (b->ql.flink->ql.blink != b)) {
+                V printf("Free block: size %6ld bytes.  (Bad free list links)\n",
+                    (long) bs);
+               assert(0);
+               return 0;
+           }
+#ifdef FreeWipe
+           lerr = ((char *) b) + sizeof(struct bfhead);
+           if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) ||
+               (memcmp(lerr, lerr + 1,
+                 (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) {
+               V printf(
+                    "(Contents of above free block have been overstored.)\n");
+               bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+               assert(0);
+               return 0;
+           }
+#endif
+       }
+       b = BFH(((char *) b) + bs);
+    }
+    return 1;
+}
+#endif /* BufValid */
+
+        /***********************\
+       *                       *
+       * Built-in test program *
+       *                       *
+        \***********************/
+
+#ifdef TestProg
+
+#define Repeatable  1                /* Repeatable pseudorandom sequence */
+                                     /* If Repeatable is not defined, a
+                                        time-seeded pseudorandom sequence
+                                        is generated, exercising BGET with
+                                        a different pattern of calls on each
+                                        run. */
+#define OUR_RAND                     /* Use our own built-in version of
+                                        rand() to guarantee the test is
+                                        100% repeatable. */
+
+#ifdef BECtl
+#define PoolSize    300000           /* Test buffer pool size */
+#else
+#define PoolSize    50000            /* Test buffer pool size */
+#endif
+#define ExpIncr     32768            /* Test expansion block size */
+#define CompactTries 10              /* Maximum tries at compacting */
+
+#define dumpAlloc   0                /* Dump allocated buffers ? */
+#define dumpFree    0                /* Dump free buffers ? */
+
+#ifndef Repeatable
+extern long time();
+#endif
+
+extern char *malloc();
+extern int free _((char *));
+
+static char *bchain = NULL;          /* Our private buffer chain */
+static char *bp = NULL;              /* Our initial buffer pool */
+
+#include <math.h>
+
+#ifdef OUR_RAND
+
+static ulong_t int next = 1;
+
+/* Return next random integer */
+
+int rand()
+{
+       next = next * 1103515245L + 12345;
+       return (uint_t) (next / 65536L) % 32768L;
+}
+
+/* Set seed for random generator */
+
+void srand(seed)
+  uint_t seed;
+{
+       next = seed;
+}
+#endif
+
+/*  STATS  --  Edit statistics returned by bstats() or bstatse().  */
+
+static void stats(when)
+  char *when;
+{
+    bufsize cural, totfree, maxfree;
+    long nget, nfree;
+#ifdef BECtl
+    bufsize pincr;
+    long totblocks, npget, nprel, ndget, ndrel;
+#endif
+
+    bstats(&cural, &totfree, &maxfree, &nget, &nfree);
+    V printf(
+        "%s: %ld gets, %ld releases.  %ld in use, %ld free, largest = %ld\n",
+       when, nget, nfree, (long) cural, (long) totfree, (long) maxfree);
+#ifdef BECtl
+    bstatse(&pincr, &totblocks, &npget, &nprel, &ndget, &ndrel);
+    V printf(
+         "  Blocks: size = %ld, %ld (%ld bytes) in use, %ld gets, %ld frees\n",
+        (long)pincr, totblocks, pincr * totblocks, npget, nprel);
+    V printf("  %ld direct gets, %ld direct frees\n", ndget, ndrel);
+#endif /* BECtl */
+}
+
+#ifdef BECtl
+static int protect = 0;              /* Disable compaction during bgetr() */
+
+/*  BCOMPACT  --  Compaction call-back function.  */
+
+static int bcompact(bsize, seq)
+  bufsize bsize;
+  int seq;
+{
+#ifdef CompactTries
+    char *bc = bchain;
+    int i = rand() & 0x3;
+
+#ifdef COMPACTRACE
+    V printf("Compaction requested.  %ld bytes needed, sequence %d.\n",
+       (long) bsize, seq);
+#endif
+
+    if (protect || (seq > CompactTries)) {
+#ifdef COMPACTRACE
+        V printf("Compaction gave up.\n");
+#endif
+       return 0;
+    }
+
+    /* Based on a random cast, release a random buffer in the list
+       of allocated buffers. */
+
+    while (i > 0 && bc != NULL) {
+       bc = *((char **) bc);
+       i--;
+    }
+    if (bc != NULL) {
+       char *fb;
+
+       fb = *((char **) bc);
+       if (fb != NULL) {
+           *((char **) bc) = *((char **) fb);
+           brel((void *) fb);
+           return 1;
+       }
+    }
+
+#ifdef COMPACTRACE
+    V printf("Compaction bailed out.\n");
+#endif
+#endif /* CompactTries */
+    return 0;
+}
+
+/*  BEXPAND  --  Expand pool call-back function.  */
+
+static void *bexpand(size)
+  bufsize size;
+{
+    void *np = NULL;
+    bufsize cural, totfree, maxfree;
+    long nget, nfree;
+
+    /* Don't expand beyond the total allocated size given by PoolSize. */
+
+    bstats(&cural, &totfree, &maxfree, &nget, &nfree);
+
+    if (cural < PoolSize) {
+       np = (void *) malloc((unsigned) size);
+    }
+#ifdef EXPTRACE
+    V printf("Expand pool by %ld -- %s.\n", (long) size,
+        np == NULL ? "failed" : "succeeded");
+#endif
+    return np;
+}
+
+/*  BSHRINK  --  Shrink buffer pool call-back function.  */
+
+static void bshrink(buf)
+  void *buf;
+{
+    if (((char *) buf) == bp) {
+#ifdef EXPTRACE
+        V printf("Initial pool released.\n");
+#endif
+       bp = NULL;
+    }
+#ifdef EXPTRACE
+    V printf("Shrink pool.\n");
+#endif
+    free((char *) buf);
+}
+
+#endif /* BECtl */
+
+/*  Restrict buffer requests to those large enough to contain our pointer and
+    small enough for the CPU architecture.  */
+
+static bufsize blimit(bs)
+  bufsize bs;
+{
+    if (bs < sizeof(char *)) {
+       bs = sizeof(char *);
+    }
+
+    /* This is written out in this ugly fashion because the
+       cool expression in sizeof(int) that auto-configured
+       to any length int befuddled some compilers. */
+
+    if (sizeof(int) == 2) {
+       if (bs > 32767) {
+           bs = 32767;
+       }
+    } else {
+       if (bs > 200000) {
+           bs = 200000;
+       }
+    }
+    return bs;
+}
+
+int main()
+{
+    int i;
+    double x;
+
+    /* Seed the random number generator.  If Repeatable is defined, we
+       always use the same seed.  Otherwise, we seed from the clock to
+       shake things up from run to run. */
+
+#ifdef Repeatable
+    V srand(1234);
+#else
+    V srand((int) time((long *) NULL));
+#endif
+
+    /* Compute x such that pow(x, p) ranges between 1 and 4*ExpIncr as
+       p ranges from 0 to ExpIncr-1, with a concentration in the lower
+       numbers.  */
+
+    x = 4.0 * ExpIncr;
+    x = log(x);
+    x = exp(log(4.0 * ExpIncr) / (ExpIncr - 1.0));
+
+#ifdef BECtl
+    bectl(bcompact, bexpand, bshrink, (bufsize) ExpIncr);
+    bp = malloc(ExpIncr);
+    assert(bp != NULL);
+    bpool((void *) bp, (bufsize) ExpIncr);
+#else
+    bp = malloc(PoolSize);
+    assert(bp != NULL);
+    bpool((void *) bp, (bufsize) PoolSize);
+#endif
+
+    stats("Create pool");
+    V bpoolv((void *) bp);
+    bpoold((void *) bp, dumpAlloc, dumpFree);
+
+    for (i = 0; i < TestProg; i++) {
+       char *cb;
+       bufsize bs = pow(x, (double) (rand() & (ExpIncr - 1)));
+
+       assert(bs <= (((bufsize) 4) * ExpIncr));
+       bs = blimit(bs);
+       if (rand() & 0x400) {
+           cb = (char *) bgetz(bs);
+       } else {
+           cb = (char *) bget(bs);
+       }
+       if (cb == NULL) {
+#ifdef EasyOut
+           break;
+#else
+           char *bc = bchain;
+
+           if (bc != NULL) {
+               char *fb;
+
+               fb = *((char **) bc);
+               if (fb != NULL) {
+                   *((char **) bc) = *((char **) fb);
+                   brel((void *) fb);
+               }
+               continue;
+           }
+#endif
+       }
+       *((char **) cb) = (char *) bchain;
+       bchain = cb;
+
+       /* Based on a random cast, release a random buffer in the list
+          of allocated buffers. */
+
+       if ((rand() & 0x10) == 0) {
+           char *bc = bchain;
+           int i = rand() & 0x3;
+
+           while (i > 0 && bc != NULL) {
+               bc = *((char **) bc);
+               i--;
+           }
+           if (bc != NULL) {
+               char *fb;
+
+               fb = *((char **) bc);
+               if (fb != NULL) {
+                   *((char **) bc) = *((char **) fb);
+                   brel((void *) fb);
+               }
+           }
+       }
+
+       /* Based on a random cast, reallocate a random buffer in the list
+          to a random size */
+
+       if ((rand() & 0x20) == 0) {
+           char *bc = bchain;
+           int i = rand() & 0x3;
+
+           while (i > 0 && bc != NULL) {
+               bc = *((char **) bc);
+               i--;
+           }
+           if (bc != NULL) {
+               char *fb;
+
+               fb = *((char **) bc);
+               if (fb != NULL) {
+                   char *newb;
+
+                   bs = pow(x, (double) (rand() & (ExpIncr - 1)));
+                   bs = blimit(bs);
+#ifdef BECtl
+                   protect = 1;      /* Protect against compaction */
+#endif
+                   newb = (char *) bgetr((void *) fb, bs);
+#ifdef BECtl
+                   protect = 0;
+#endif
+                   if (newb != NULL) {
+                       *((char **) bc) = newb;
+                   }
+               }
+           }
+       }
+    }
+    stats("\nAfter allocation");
+    if (bp != NULL) {
+       V bpoolv((void *) bp);
+       bpoold((void *) bp, dumpAlloc, dumpFree);
+    }
+
+    while (bchain != NULL) {
+       char *buf = bchain;
+
+       bchain = *((char **) buf);
+       brel((void *) buf);
+    }
+    stats("\nAfter release");
+#ifndef BECtl
+    if (bp != NULL) {
+       V bpoolv((void *) bp);
+       bpoold((void *) bp, dumpAlloc, dumpFree);
+    }
+#endif
+
+    return 0;
+}
+#endif
diff --git a/misc/test_vm/src/geekos/blockdev.c b/misc/test_vm/src/geekos/blockdev.c
new file mode 100644 (file)
index 0000000..5a4bf07
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/errno.h>
+#include <geekos/screen.h>
+#include <geekos/string.h>
+#include <geekos/malloc.h>
+#include <geekos/int.h>
+#include <geekos/kthread.h>
+#include <geekos/synch.h>
+#include <geekos/blockdev.h>
+
+/*#define BLOCKDEV_DEBUG */
+#ifdef BLOCKDEV_DEBUG
+#  define Debug(args...) Print(args)
+#else
+#  define Debug(args...)
+#endif
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Lock protecting access/modification of block device list.
+ */
+static struct Mutex s_blockdevLock;
+
+/*
+ * List datatype for list of block devices.
+ */
+DEFINE_LIST(Block_Device_List, Block_Device);
+IMPLEMENT_LIST(Block_Device_List, Block_Device);
+
+/*
+ * The list in which all block devices in the system
+ * are registered.
+ */
+static struct Block_Device_List s_deviceList;
+
+/*
+ * Perform a block IO request.
+ * Returns 0 if successful, error code on failure.
+ */
+static int Do_Request(struct Block_Device *dev, enum Request_Type type, int blockNum, void *buf)
+{
+    struct Block_Request *request;
+    int rc;
+
+    request = Create_Request(dev, type, blockNum, buf);
+    if (request == 0)
+       return ENOMEM;
+    Post_Request_And_Wait(request);
+    rc = request->errorCode;
+    Free(request);
+    return rc;
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Register a block device.
+ * This should be called by device drivers in their Init
+ * functions to register all detected devices.
+ * Returns 0 if successful, error code otherwise.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+    int unit, void *driverData, struct Thread_Queue *waitQueue,
+    struct Block_Request_List *requestQueue)
+{
+    struct Block_Device *dev;
+
+    KASSERT(ops != 0);
+    KASSERT(waitQueue != 0);
+    KASSERT(requestQueue != 0);
+
+    dev = (struct Block_Device*) Malloc(sizeof(*dev));
+    if (dev == 0)
+       return ENOMEM;
+
+    strcpy(dev->name, name);
+    dev->ops = ops;
+    dev->unit = unit;
+    dev->inUse = false;
+    dev->driverData = driverData;
+    dev->waitQueue = waitQueue;
+    dev->requestQueue = requestQueue;
+
+    Mutex_Lock(&s_blockdevLock);
+    /* FIXME: handle name conflict with existing device */
+    Debug("Registering block device %s\n", dev->name);
+    Add_To_Back_Of_Block_Device_List(&s_deviceList, dev);
+    Mutex_Unlock(&s_blockdevLock);
+
+    return 0;
+}
+
+/*
+ * Open a named block device.
+ * Return 0 if successful, error code on error.
+ */
+int Open_Block_Device(const char *name, struct Block_Device **pDev)
+{
+    struct Block_Device *dev;
+    int rc = 0;
+
+    Mutex_Lock(&s_blockdevLock);
+
+    dev = Get_Front_Of_Block_Device_List(&s_deviceList);
+    while (dev != 0) {
+       if (strcmp(dev->name, name) == 0)
+           break;
+       dev = Get_Next_In_Block_Device_List(dev);
+    }
+
+    if (dev == 0)
+       rc = ENODEV;
+    else if (dev->inUse)
+       rc = EBUSY;
+    else {
+       rc = dev->ops->Open(dev);
+       if (rc == 0) {
+           *pDev = dev;
+           dev->inUse = true;
+       }
+    }
+
+    Mutex_Unlock(&s_blockdevLock);
+
+    return rc;
+}
+
+/*
+ * Close given block device.
+ * Return 0 if successful, error code on error.
+ */
+int Close_Block_Device(struct Block_Device *dev)
+{
+    int rc;
+
+    Mutex_Lock(&s_blockdevLock);
+
+    KASSERT(dev->inUse);
+    rc = dev->ops->Close(dev);
+    if (rc == 0)
+       dev->inUse = false;
+
+    Mutex_Unlock(&s_blockdevLock);
+
+    return rc;
+}
+
+/*
+ * Create a block device request to transfer a single block.
+ */
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+    int blockNum, void *buf)
+{
+    struct Block_Request *request = Malloc(sizeof(*request));
+    if (request != 0) {
+       request->dev = dev;
+       request->type = type;
+       request->blockNum = blockNum;
+       request->buf = buf;
+       request->state = PENDING;
+       Clear_Thread_Queue(&request->waitQueue);
+    }
+    return request;
+}
+
+/*
+ * Send a block IO request to a device and wait for it to be handled.
+ * Returns when the driver completes the requests or signals
+ * an error.
+ */
+void Post_Request_And_Wait(struct Block_Request *request)
+{
+    struct Block_Device *dev;
+
+    KASSERT(request != 0);
+
+    dev = request->dev;
+    KASSERT(dev != 0);
+
+    /* Send request to the driver */
+    Debug("Posting block device request [@%x]...\n", request);
+    Disable_Interrupts();
+    Add_To_Back_Of_Block_Request_List(dev->requestQueue, request);
+    Wake_Up(dev->waitQueue);
+    Enable_Interrupts();
+
+    /* Wait for request to be processed */
+    Disable_Interrupts();
+    while (request->state == PENDING) {
+       Debug("Waiting, state=%d\n", request->state);
+       Wait(&request->waitQueue);
+    }
+    Debug("Wait completed!\n");
+    Enable_Interrupts();
+}
+
+/*
+ * Wait for a block request to arrive.
+ */
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+    struct Thread_Queue *waitQueue)
+{
+    struct Block_Request *request;
+
+    Disable_Interrupts();
+    while (Is_Block_Request_List_Empty(requestQueue))
+       Wait(waitQueue);
+    request = Get_Front_Of_Block_Request_List(requestQueue);
+    Remove_From_Front_Of_Block_Request_List(requestQueue);
+    Enable_Interrupts();
+
+    return request;
+}
+
+/*
+ * Signal the completion of a block request.
+ */
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode)
+{
+    Disable_Interrupts();
+    request->state = state;
+    request->errorCode = errorCode;
+    Wake_Up(&request->waitQueue);
+    Enable_Interrupts();
+}
+
+/*
+ * Read a block from given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf)
+{
+    return Do_Request(dev, BLOCK_READ, blockNum, buf);
+}
+
+/*
+ * Write a block to given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf)
+{
+    return Do_Request(dev, BLOCK_WRITE, blockNum, buf);
+}
+
+/*
+ * Get number of blocks in given device.
+ */
+int Get_Num_Blocks(struct Block_Device *dev)
+{
+    return dev->ops->Get_Num_Blocks(dev);
+}
+
diff --git a/misc/test_vm/src/geekos/bootsect.asm b/misc/test_vm/src/geekos/bootsect.asm
new file mode 100644 (file)
index 0000000..9986818
--- /dev/null
@@ -0,0 +1,247 @@
+; Boot sector for GeekOS
+; Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software.  You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; Loads setup code and a program image from sectors 1..n of a floppy
+; and executes the setup code (which will in turn execute
+; the program image).
+
+; Some of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+; This macro is used to calculate padding needed
+; to ensure that the boot sector is exactly 512 bytes
+; in size.  The argument is the desired offset to be
+; padded to.
+%macro PadFromStart 1
+       times (%1 - ($ - BeginText)) db 0
+%endmacro
+
+KERN_START_SEC equ (NUM_SETUP_SECTORS + 1)
+
+BIOS_SIGNATURE_OFFSET equ 510
+
+; ----------------------------------------------------------------------
+; The actual code
+; ----------------------------------------------------------------------
+
+[BITS 16]
+[ORG 0x0]
+
+BeginText:     ; needed to calculate padding bytes to fill the sector
+
+       ; Copy the boot sector into INITSEG.
+       mov     ax, BOOTSEG
+       mov     ds, ax                  ; source segment for string copy
+       xor     si, si                  ; source index for string copy
+       mov     ax, INITSEG
+       mov     es, ax                  ; destination segment for string copy
+       xor     di, di                  ; destination index for string copy
+       cld                             ; clear direction flag
+       mov     cx, 256                 ; number of words to copy
+       rep     movsw                   ; copy 512 bytes
+
+       jmp     INITSEG:after_move
+
+after_move:
+       ; Now we're executing in INITSEG
+
+       ; We want the data segment to refer to INITSEG
+       ; (since we've defined variables in the same place as the code)
+       mov     ds, ax                  ; ax still contains INITSEG
+
+       ; Put the stack in the place where we were originally loaded.
+       ; By definition, there is nothing important there now.
+       mov     ax, 0
+       mov     ss, ax
+       mov     sp, (BOOTSEG << 4) + 512 - 2
+
+load_setup:
+       ; Load the setup code.
+       mov     word [sec_count], 1
+.again:
+       mov     ax, [sec_count]
+       push    ax                      ; 1st param to ReadSector (log sec num)
+       push    word SETUPSEG           ; 2nd param to ReadSector (seg base)
+       dec     ax                      ; convert to 0-indexed
+       shl     ax, 9                   ; multiply by 512
+       push    ax                      ;  ...to get 3rd param (byte offset)
+
+       ; read the sector from the floppy
+       call    ReadSector
+       add     sp, 6                   ; clear 3 word params
+
+       ; on to next sector
+       inc     word [sec_count]
+
+       ; are we done?
+       cmp     word [sec_count], NUM_SETUP_SECTORS
+       jle     .again
+
+load_kernel:
+       ; Load the kernel image from sectors KERN_START_SEC..n of the
+       ; floppy into memory at KERNSEG.  Note that there are 128 sectors
+       ; per 64K segment.  So, when figuring out which segment to
+       ; load the sector into, we shift right by 7 bits (which is
+       ; equivalent to dividing by 128).
+       mov     word [sec_count], KERN_START_SEC
+.again:
+       mov     ax, [sec_count]         ; logical sector on the floppy
+       push    ax                      ; 1st param to ReadSector (log sec num)
+       sub     ax, KERN_START_SEC      ; convert to 0-indexed
+       mov     cx, ax                  ; save in cx
+       shr     ax, 7                   ; divide by 128
+       shl     ax, 12                  ;  ...and multiply by 0x1000
+       add     ax, KERNSEG             ;  ...to get base relative to KERNSEG
+       push    ax                      ; 2nd param to ReadSector (seg base)
+       and     cx, 0x7f                ; mod sector by 128
+       shl     cx, 9                   ;  ...and multiply by 512
+       push    cx                      ; to get offset in segment (3rd parm)
+
+       ; read the sector from the floppy
+       call    ReadSector
+       add     sp, 6                   ; clear 3 word params
+
+       ; on to next sector
+       inc     word [sec_count]
+
+       ; have we loaded all of the sectors?
+       cmp     word [sec_count], KERN_START_SEC+NUM_KERN_SECTORS
+       jl      .again
+
+       ; Now we've loaded the setup code and the kernel image.
+       ; Jump to setup code.
+       jmp     SETUPSEG:0
+
+; Read a sector from the floppy drive.
+; This code (and the rest of this boot sector) will have to
+; be re-written at some point so it reads more than one
+; sector at a time.
+;
+; Parameters:
+;     - "logical" sector number   [bp+8]
+;     - destination segment       [bp+6]
+;     - destination offset        [bp+4]
+ReadSector:
+       push    bp                      ; set up stack frame
+       mov     bp, sp                  ; "
+       pusha                           ; save all registers
+
+%if 0
+; debug params
+       mov     dx, [bp+8]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [bp+6]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [bp+4]
+       call    PrintHex
+       call    PrintNL
+%endif
+
+       ; Sector = log_sec % SECTORS_PER_TRACK
+       ; Head = (log_sec / SECTORS_PER_TRACK) % HEADS
+       mov     ax, [bp+8]              ; get logical sector number from stack
+       xor     dx, dx                  ; dx is high part of dividend (== 0)
+       mov     bx, SECTORS_PER_TRACK   ; divisor
+       div     bx                      ; do the division
+       mov     [sec], dx               ; sector is the remainder
+       and     ax, 1                   ; same as mod by HEADS==2 (slight hack)
+       mov     [head], ax
+
+       ; Track = log_sec / (SECTORS_PER_TRACK*HEADS)
+       mov     ax, [bp+8]              ; get logical sector number again
+       xor     dx, dx                  ; dx is high part of dividend
+       mov     bx, SECTORS_PER_TRACK*2 ; divisor
+       div     bx                      ; do the division
+       mov     [track], ax             ; track is quotient
+
+%if 0
+; debugging code
+       mov     dx, [sec]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [head]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [track]
+       call    PrintHex
+       call    PrintNL
+%endif
+
+       ; Now, try to actually read the sector from the floppy,
+       ; retrying up to 3 times.
+
+       mov     [num_retries], byte 0
+
+.again:
+       mov     ax, [bp+6]              ; dest segment...
+       mov     es, ax                  ;   goes in es
+       mov     ax, (0x02 << 8) | 1     ; function = 02h in ah,
+                                       ;   # secs = 1 in al
+       mov     bx, [track]             ; track number...
+       mov     ch, bl                  ;   goes in ch
+       mov     bx, [sec]               ; sector number...
+       mov     cl, bl                  ;   goes in cl...
+       inc     cl                      ;   but it must be 1-based, not 0-based
+       mov     bx, [head]              ; head number...
+       mov     dh, bl                  ;   goes in dh
+       xor     dl, dl                  ; hard code drive=0
+       mov     bx, [bp+4]              ; offset goes in bx
+                                       ;   (es:bx points to buffer)
+
+       ; Call the BIOS Read Diskette Sectors service
+       int     0x13
+
+       ; If the carry flag is NOT set, then there was no error
+       ; and we're done.
+       jnc     .done
+
+       ; Error - code stored in ah
+       mov     dx, ax
+       call PrintHex
+       inc     byte [num_retries]
+       cmp     byte [num_retries], 3
+       jne     .again
+
+       ; If we got here, we failed thrice, so we give up
+       mov     dx, 0xdead
+       call    PrintHex
+.here: jmp     .here
+
+.done:
+       popa                            ; restore all regisiters
+       pop     bp                      ; leave stack frame
+       ret
+
+; Include utility routines
+%include "util.asm"
+
+; ----------------------------------------------------------------------
+; Variables
+; ----------------------------------------------------------------------
+
+; These are used by ReadSector
+head: dw 0
+track: dw 0
+sec: dw 0
+num_retries: db 0
+
+
+; Used for loops reading sectors from floppy
+sec_count: dw 0
+
+
+PadFromStart BIOS_SIGNATURE_OFFSET
+Signature   dw 0xAA55   ; BIOS controls this to ensure this is a boot sector
diff --git a/misc/test_vm/src/geekos/crc32.c b/misc/test_vm/src/geekos/crc32.c
new file mode 100644 (file)
index 0000000..0165c3b
--- /dev/null
@@ -0,0 +1,52 @@
+/* Copyright abandoned; this code is in the public domain. */
+/* Provided to GNUnet by peter@horizon.com */
+
+/*
+ * Adapted for GeekOS by David Hovemeyer
+ * Code downloaded from OVM (http://www.ovmj.org)
+ */
+
+#include <geekos/crc32.h>
+#include <geekos/kassert.h>
+#include <geekos/serial.h>
+
+#define POLYNOMIAL (ulong_t)0xedb88320
+static ulong_t crc_table[256];
+
+/*
+ * This routine writes each crc_table entry exactly once,
+ * with the correct final value.  Thus, it is safe to call
+ * even on a table that someone else is using concurrently.
+ */
+void Init_CRC32(void) {
+  unsigned int i, j;
+  ulong_t h = 1;
+  PrintBoth("Initializing CRC32\n");
+  crc_table[0] = 0;
+  for (i = 128; i; i >>= 1) {
+    h = (h >> 1) ^ ((h & 1) ? POLYNOMIAL : 0);
+    /* h is now crc_table[i] */
+    for (j = 0; j < 256; j += 2*i)
+      crc_table[i+j] = crc_table[j] ^ h;
+  }
+}
+
+/*
+ * This computes the standard preset and inverted CRC, as used
+ * by most networking standards.  Start by passing in an initial
+ * chaining value of 0, and then pass in the return value from the
+ * previous crc32() call.  The final return value is the CRC.
+ * Note that this is a little-endian CRC, which is best used with
+ * data transmitted lsbit-first, and it should, itself, be appended
+ * to data in little-endian byte and bit order to preserve the
+ * property of detecting all burst errors of length 32 bits or less.
+ */
+ulong_t crc32(ulong_t crc, char const *buf, size_t len) {
+  KASSERT(crc_table[255] != 0);
+  crc ^= 0xffffffff;
+  while (len--)
+    crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff];
+  return crc ^ 0xffffffff;
+}
+
+/* end of crc32.c */
diff --git a/misc/test_vm/src/geekos/defs.asm b/misc/test_vm/src/geekos/defs.asm
new file mode 100644 (file)
index 0000000..72bdd08
--- /dev/null
@@ -0,0 +1,84 @@
+; Definitions for use in GeekOS boot code
+; Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software.  You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; A lot of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%ifndef DEFS_ASM
+%define DEFS_ASM
+       
+; BIOS loads the boot sector at offset 0 in this segment
+BOOTSEG equ 0x07C0
+; We'll move the boot sector up to higher memory.
+; Note that the "ISA hole" begins at segment 0xA000 == 640K.
+INITSEG equ 0x9000
+
+; Put the setup code here, just after the boot sector.
+SETUPSEG equ 0x9020
+
+; Load our "Kernel" at this segment, which starts at 64K.
+; The number of sectors in the kernel, NUM_KERN_SECTORS,
+; will be passed on the command line.
+KERNSEG equ 0x1000
+               ;
+
+; Size of PFAT boot record.
+; Keep up to date with <geekos/pfat.h>.
+PFAT_BOOT_RECORD_SIZE equ 28
+
+; Offset of BIOS signature word in boot sector.
+BIOS_SIGNATURE_OFFSET equ 510
+
+; Offset of PFAT boot record in boot sector.
+PFAT_BOOT_RECORD_OFFSET equ BIOS_SIGNATURE_OFFSET - PFAT_BOOT_RECORD_SIZE
+
+; Video memory segment
+VIDSEG equ 0xb800
+
+; The following information is correct for a 1.44M floppy.
+; Yes, I'm hard coding this.
+SECTORS_PER_TRACK equ 18
+HEADS equ 2
+CYLINDERS equ 80
+
+; 8259A PIC initialization codes.
+; Source: Linux bootsect.S, and Intel 8259A datasheet
+
+; The most important reason why we reprogram the PICs is to
+; route the hardware interrupts through vectors *above*
+; those reserved by Intel.  The BIOS (for historical reasons :-)
+; routes them such that they conflict with internal processor-generated
+; interrupts.
+
+ICW1 equ 0x11          ; ICW1 - ICW4 needed, cascade mode, interval=8,
+                       ;   edge triggered. (I think interval is irrelevant
+                       ;   for x86.)
+ICW2_MASTER equ 0x20   ; put IRQs 0-7 at 0x20 (above Intel reserved ints)
+ICW2_SLAVE equ 0x28    ; put IRQs 8-15 at 0x28
+ICW3_MASTER equ 0x04   ; IR2 connected to slave
+ICW3_SLAVE equ 0x02    ; slave has id 2
+ICW4 equ 0x01          ; 8086 mode, no auto-EOI, non-buffered mode,
+                       ;   not special fully nested mode
+
+; Kernel code and data segment selectors.
+; Keep these up to date with defs.h.
+KERNEL_CS equ 1<<3     ; kernel code segment is GDT entry 1
+KERNEL_DS equ 2<<3     ; kernel data segment is GDT entry 2
+
+; Pages for context object and stack for initial kernel thread -
+; the one we construct for Main().  Keep these up to date with defs.h.
+; We put them at 1MB, for no particular reason.
+KERN_THREAD_OBJ equ 4096
+KERN_STACK equ KERN_THREAD_OBJ + 4096
+
+%endif
diff --git a/misc/test_vm/src/geekos/depend.mak b/misc/test_vm/src/geekos/depend.mak
new file mode 100644 (file)
index 0000000..6e60675
--- /dev/null
@@ -0,0 +1 @@
+dummy:
diff --git a/misc/test_vm/src/geekos/fd_boot.asm b/misc/test_vm/src/geekos/fd_boot.asm
new file mode 100644 (file)
index 0000000..d5fa120
--- /dev/null
@@ -0,0 +1,425 @@
+; -*- fundamental -*-
+; Boot sector for GeekOS
+; Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software.  You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; Loads setup code and a program image from sectors 1..n of a floppy
+; and executes the setup code (which will in turn execute
+; the program image).
+
+; Some of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+
+; Pad to desired offset from start symbol.
+;    Usage: Pad_From_Symbol offset, symbol
+%macro Pad_From_Symbol 2
+       times (%1 - ($ - %2)) db 0
+%endmacro
+
+; ----------------------------------------------------------------------
+; The actual code
+; ----------------------------------------------------------------------
+
+[BITS 16]
+[ORG 0x0]
+
+BeginText:     ; needed to calculate padding bytes to fill the sector
+
+       ; Copy the boot sector into INITSEG.
+       mov     ax, BOOTSEG
+       mov     ds, ax                  ; source segment for string copy
+       xor     si, si                  ; source index for string copy
+       mov     ax, INITSEG
+       mov     es, ax                  ; destination segment for string copy
+       xor     di, di                  ; destination index for string copy
+       cld                             ; clear direction flag
+       mov     cx, 256                 ; number of words to copy
+       rep     movsw                   ; copy 512 bytes
+
+       jmp     INITSEG:after_move
+
+after_move:
+       ; Now we're executing in INITSEG
+
+
+       ; We want the data segment to refer to INITSEG
+       ; (since we've defined variables in the same place as the code)
+       mov     ds, ax                  ; ax still contains INITSEG
+
+       ; Put the stack in the place where we were originally loaded.
+       ; By definition, there is nothing important there now.
+       mov     ax, 0
+       mov     ss, ax
+       mov     sp, (BOOTSEG << 4) + 512 - 2
+
+
+load_setup:
+       ; Load the setup code.
+       mov     ax, word [setupStart]
+       mov     word [sec_count], ax
+       add     ax, [setupSize]
+       mov     word [max_sector], ax
+.again:
+       mov     ax, [sec_count]
+       push    ax                      ; 1st param to ReadSector (log sec num)
+       push    word SETUPSEG           ; 2nd param to ReadSector (seg base)
+       sub     ax, [setupStart]        ; convert to 0-indexed
+       shl     ax, 9                   ; multiply by 512
+       push    ax                      ;  ...to get 3rd param (byte offset)
+
+       ; read the sector from the floppy
+       call    ReadSector
+       add     sp, 6                   ; clear 3 word params
+
+       ; on to next sector
+       inc     word [sec_count]
+
+       ; are we done?
+       mov     bx, word [max_sector]
+       cmp     word [sec_count], bx
+       jl      .again
+
+
+load_kernel:
+       ; Load the kernel image from sectors KERN_START_SEC..n of the
+       ; floppy into memory at KERNSEG.  Note that there are 128 sectors
+       ; per 64K segment.  So, when figuring out which segment to
+       ; load the sector into, we shift right by 7 bits (which is
+       ; equivalent to dividing by 128).
+
+       ; Figure out start sector and max sector
+
+       mov     ax, word [kernelStart]
+       mov     word [sec_count], ax
+       add     ax, word [kernelSize]
+       mov     word [max_sector], ax
+.again:
+
+
+       mov     ax, [sec_count]         ; logical sector on the floppy
+;      mov     dx, ax
+;      call    PrintHex
+;      call    PrintNL
+       push    ax                      ; 1st param to ReadSector (log sec num)
+       sub     ax, [kernelStart]       ; convert to 0-indexed
+       mov     cx, ax                  ; save in cx
+       shr     ax, 7                   ; divide by 128
+       shl     ax, 12                  ;  ...and multiply by 0x1000
+       add     ax, KERNSEG             ;  ...to get base relative to KERNSEG
+;      mov     dx, ax
+;      call    PrintHex
+;      call    PrintNL
+       push    ax                      ; 2nd param to ReadSector (seg base)
+       and     cx, 0x7f                ; mod sector by 128
+       shl     cx, 9                   ;  ...and multiply by 512
+       push    cx                      ; to get offset in segment (3rd parm)
+;      mov     dx, cx
+;      call    PrintHex
+;      call    PrintNL
+
+
+       ; read the sector from the floppy
+       call    ReadSector
+       add     sp, 6                   ; clear 3 word params
+
+
+       ; on to next sector
+       inc     word [sec_count]
+
+       ; have we loaded all of the sectors?
+       mov     bx, word [max_sector]
+       cmp     word [sec_count], bx
+       jl      .again
+
+
+; execute bios call
+
+       push    cx
+       push    si
+       push    bx
+       push    es              ;
+
+       mov     dx, cs
+       call    PrintHex
+       call    PrintNL
+
+       push    cs
+       pop     es
+
+       mov     si, bootsect_gdt
+       mov     ah, 87h
+
+       ;; determine the actual kernel size
+       mov     cx, word [kernelSize]           ; number of 512 byte sectors
+       shl     cx, 8                           ; multiply by 9-1 to get number of bytes / 2 (words)
+
+       ;; Print out the size of the kernel
+       mov     dx, cx  
+       call    PrintHex
+       call    PrintNL
+
+       int     0x15
+       
+       adc     ax, 0
+
+       ;; Print out the return code to the screen
+       mov     dx, ax  
+       call    PrintHex
+       call    PrintNL
+
+       mov     dx, KERNSEG
+       call    PrintHex
+       call    PrintNL
+
+       mov     dx, SETUPSEG
+       call    PrintHex
+       call    PrintNL
+
+
+
+       mov     dx, 0x4a4a
+       call    PrintHex
+       call    PrintNL
+
+       pop     es              ;
+       pop     bx
+       pop     si
+       pop     cx
+       
+
+;.loop:        jmp .loop
+
+
+.skip:
+       ; Now we've loaded the setup code and the kernel image.
+       ; Jump to setup code.
+       jmp     SETUPSEG:0
+
+; Read a sector from the floppy drive.
+; This code (and the rest of this boot sector) will have to
+; be re-written at some point so it reads more than one
+; sector at a time.
+;
+; Parameters:
+;     - "logical" sector number   [bp+8]
+;     - destination segment       [bp+6]
+;     - destination offset        [bp+4]
+ReadSector:
+       push    bp                      ; set up stack frame
+       mov     bp, sp                  ; "
+       pusha                           ; save all registers
+
+%if 0
+; debug params
+       mov     dx, [bp+8]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [bp+6]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [bp+4]
+       call    PrintHex
+       call    PrintNL
+%endif
+
+       ; Sector = log_sec % SECTORS_PER_TRACK
+       ; Head = (log_sec / SECTORS_PER_TRACK) % HEADS
+       mov     ax, [bp+8]              ; get logical sector number from stack
+       xor     dx, dx                  ; dx is high part of dividend (== 0)
+       mov     bx, SECTORS_PER_TRACK   ; divisor
+       div     bx                      ; do the division
+       mov     [sec], dx               ; sector is the remainder
+       and     ax, 1                   ; same as mod by HEADS==2 (slight hack)
+       mov     [head], ax
+
+       ; Track = log_sec / (SECTORS_PER_TRACK*HEADS)
+       mov     ax, [bp+8]              ; get logical sector number again
+       xor     dx, dx                  ; dx is high part of dividend
+       mov     bx, SECTORS_PER_TRACK*2 ; divisor
+       div     bx                      ; do the division
+       mov     [track], ax             ; track is quotient
+
+%if 0
+; debugging code
+       mov     dx, [sec]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [head]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [track]
+       call    PrintHex
+       call    PrintNL
+%endif
+
+       ; Now, try to actually read the sector from the floppy,
+       ; retrying up to 3 times.
+
+       mov     [num_retries], byte 0
+
+.again:
+       mov     ax, [bp+6]              ; dest segment...
+       mov     es, ax                  ;   goes in es
+       mov     ax, (0x02 << 8) | 1     ; function = 02h in ah,
+                                       ;   # secs = 1 in al
+       mov     bx, [track]             ; track number...
+       mov     ch, bl                  ;   goes in ch
+       mov     bx, [sec]               ; sector number...
+       mov     cl, bl                  ;   goes in cl...
+       inc     cl                      ;   but it must be 1-based, not 0-based
+       mov     bx, [head]              ; head number...
+       mov     dh, bl                  ;   goes in dh
+       xor     dl, dl                  ; hard code drive=0
+       mov     bx, [bp+4]              ; offset goes in bx
+                                       ;   (es:bx points to buffer)
+       ; Call the BIOS Read Diskette Sectors service
+       int     0x13
+       
+       ; If the carry flag is NOT set, then there was no error
+       ; and we're done.
+       jnc     .done
+
+       ; Error - code stored in ah
+       mov     dx, ax
+       call PrintHex
+       inc     byte [num_retries]
+       cmp     byte [num_retries], 3
+       jne     .again
+
+       ; If we got here, we failed thrice, so we give up
+       mov     dx, 0xdead
+       call    PrintHex
+.here: jmp     .here
+
+.done:
+       popa                            ; restore all regisiters
+       pop     bp                      ; leave stack frame
+       ret
+
+; Include utility routines:
+;%include "util.asm"
+; REPLACED WITH FOLLOWING WHICH MUST BE COMPILED 16 FOR USE IN THIS CODE
+PrintHex:
+       pusha
+       mov   cx, 4             ; 4 hex digits
+.PrintDigit:
+       rol   dx, 4             ; rotate so that lowest 4 bits are used
+       mov   ax, 0E0Fh         ; ah = request, al = mask for nybble
+       and   al, dl
+       add   al, 90h           ; convert al to ascii hex (four instructions)
+       daa                     ; I've spent 1 hour to understand how it works..
+       adc   al, 40h
+       daa
+       int   10h
+       loop  .PrintDigit
+       popa
+       ret
+
+; Print a newline.
+PrintNL:                       ; print CR and NL
+       push    ax
+       mov     ax, 0E0Dh       ; CR
+               int     10h
+               mov     al, 0Ah         ; LF
+               int     10h
+       pop     ax
+               ret
+
+
+
+; ----------------------------------------------------------------------
+; Variables
+; ----------------------------------------------------------------------
+
+; These are used by ReadSector
+head: dw 0
+track: dw 0
+sec: dw 0
+num_retries: db 0
+
+; Used for loops reading sectors from floppy
+sec_count: dw 0
+max_sector: dw 0
+
+
+
+
+bootsect_gdt:
+       dw      0,0,0,0
+       dw      0,0,0,0
+bootsect_src:
+       dw      0xffff                
+bootsect_src_base:
+       db      0x00,0x00,0x01          ;       ! base = 0x010000
+       db      0x93            ;       ! typbyte
+       dw      0               ;       ! limit16,base24 =0
+bootsect_dst:
+       dw      0xffff
+bootsect_dst_base:
+       db      0,0,0x10        ;       ! base = 0x100000
+       db      0x93            ;       ! typbyte
+       dw      0               ;       ! limit16,base24 =0
+       dw      0,0,0,0         ;       ! BIOS CS
+       dw      0,0,0,0         ;       ! BIOS DS
+
+
+; Padding to make the PFAT Boot Record sit just before the BIOS signature.
+;Pad_From_Symbol PFAT_BOOT_RECORD_OFFSET, BeginText
+
+; PFAT boot record
+; Describes how to load the setup program and kernel.
+; The default values are appropriate for creating a boot
+; floppy by concatenating the boot sector, setup program,
+; and kernel image.  The buildFat program will change
+; these values if the boot floppy is formatted as a PFAT
+; filesystem.
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+;; part of pfat boot record
+setupStart:            
+       dw      1                       ; by default, setup is at first sector  
+
+;; part of pfat boot record
+setupSize:
+       dw      NUM_SETUP_SECTORS       ; number of sectors in setup
+
+;; part of pfat boot record
+kernelStart:
+       dw      1+NUM_SETUP_SECTORS     ; default start sector for kernel
+
+;; part of pfat boot record
+kernelSize:
+       dw      NUM_KERN_SECTORS
+
+
+
+
+       ; Finish by writing the BIOS signature to mark this as
+; a valid boot sector.
+Pad_From_Symbol BIOS_SIGNATURE_OFFSET, BeginText
+Signature   dw 0xAA55   ; BIOS controls this to ensure this is a boot sector
diff --git a/misc/test_vm/src/geekos/fd_boot_bak.asm b/misc/test_vm/src/geekos/fd_boot_bak.asm
new file mode 100644 (file)
index 0000000..30fb6ae
--- /dev/null
@@ -0,0 +1,472 @@
+; -*- fundamental -*-
+; Boot sector for GeekOS
+; Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software.  You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; Loads setup code and a program image from sectors 1..n of a floppy
+; and executes the setup code (which will in turn execute
+; the program image).
+
+; Some of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+; Pad to desired offset from start symbol.
+;    Usage: Pad_From_Symbol offset, symbol
+%macro Pad_From_Symbol 2
+       times (%1 - ($ - %2)) db 0
+%endmacro
+
+; ----------------------------------------------------------------------
+; The actual code
+; ----------------------------------------------------------------------
+
+[BITS 16]
+[ORG 0x0]
+
+BeginText:     ; needed to calculate padding bytes to fill the sector
+
+       ; Copy the boot sector into INITSEG.
+       mov     ax, BOOTSEG
+       mov     ds, ax                  ; source segment for string copy
+       xor     si, si                  ; source index for string copy
+       mov     ax, INITSEG
+       mov     es, ax                  ; destination segment for string copy
+       xor     di, di                  ; destination index for string copy
+       cld                             ; clear direction flag
+       mov     cx, 256                 ; number of words to copy
+       rep     movsw                   ; copy 512 bytes
+
+       jmp     INITSEG:after_move
+
+after_move:
+       ; Now we're executing in INITSEG
+
+
+       ; We want the data segment to refer to INITSEG
+       ; (since we've defined variables in the same place as the code)
+       mov     ds, ax                  ; ax still contains INITSEG
+
+       ; Put the stack in the place where we were originally loaded.
+       ; By definition, there is nothing important there now.
+       mov     ax, 0
+       mov     ss, ax
+       mov     sp, (BOOTSEG << 4) + 512 - 2
+
+
+load_setup:
+       ; Load the setup code.
+       mov     ax, word [setupStart]
+       mov     word [sec_count], ax
+       add     ax, [setupSize]
+       mov     word [max_sector], ax
+.again:
+       mov     ax, [sec_count]
+       push    ax                      ; 1st param to ReadSector (log sec num)
+       push    word SETUPSEG           ; 2nd param to ReadSector (seg base)
+       sub     ax, [setupStart]        ; convert to 0-indexed
+       shl     ax, 9                   ; multiply by 512
+       push    ax                      ;  ...to get 3rd param (byte offset)
+
+       ; read the sector from the floppy
+       call    ReadSector
+       add     sp, 6                   ; clear 3 word params
+
+       ; on to next sector
+       inc     word [sec_count]
+
+       ; are we done?
+       mov     bx, word [max_sector]
+       cmp     word [sec_count], bx
+       jl      .again
+
+load_kernel:
+       ; Load the kernel image from sectors KERN_START_SEC..n of the
+       ; floppy into memory at KERNSEG.  Note that there are 128 sectors
+       ; per 64K segment.  So, when figuring out which segment to
+       ; load the sector into, we shift right by 7 bits (which is
+       ; equivalent to dividing by 128).
+
+       ; Figure out start sector and max sector
+
+       mov     ax, word [kernelStart]
+       mov     word [sec_count], ax
+       add     ax, word [kernelSize]
+       mov     word [max_sector], ax
+.again:
+       mov     ax, [sec_count]         ; logical sector on the floppy
+;      mov     dx, ax
+;      call    PrintHex
+;      call    PrintNL
+       push    ax                      ; 1st param to ReadSector (log sec num)
+       sub     ax, [kernelStart]       ; convert to 0-indexed
+       mov     cx, ax                  ; save in cx
+       shr     ax, 7                   ; divide by 128
+       shl     ax, 12                  ;  ...and multiply by 0x1000
+       add     ax, KERNSEG             ;  ...to get base relative to KERNSEG
+;      mov     dx, ax
+;      call    PrintHex
+;      call    PrintNL
+       push    ax                      ; 2nd param to ReadSector (seg base)
+       and     cx, 0x7f                ; mod sector by 128
+       shl     cx, 9                   ;  ...and multiply by 512
+       push    cx                      ; to get offset in segment (3rd parm)
+;      mov     dx, cx
+;      call    PrintHex
+;      call    PrintNL
+
+
+       ; read the sector from the floppy
+       call    ReadSector
+       add     sp, 6                   ; clear 3 word params
+
+
+       ; on to next sector
+       inc     word [sec_count]
+
+       ; have we loaded all of the sectors?
+       mov     bx, word [max_sector]
+       cmp     word [sec_count], bx
+       jl      .again
+
+load_vm:
+       ; Load the guest image starting at 1MB
+       ; floppy into memory at KERNSEG.  Note that there are 128 sectors
+       ; per 64K segment.  So, when figuring out which segment to
+       ; load the sector  into, we shift right by 7 bits (which is
+       ; equivalent to dividing by 128).
+
+       ; Figure out start sector and max sector
+       mov     ax, word [vmStart]
+       mov     word [sec_count], ax
+       add     ax, word [vmSize]
+       mov     word [max_sector], ax
+.again2:
+
+       mov     ax, [sec_count]         ; logical sector on the floppy
+       push    ax                      ; 1st param to ReadSector (log sec num)
+
+;      mov     dx, ax
+;      call    PrintHex
+;      call    PrintNL
+
+       mov     ax, VMSEG               ;  ...to get base relative to VMSEG
+       push    ax                      ; 2nd param to ReadSector (seg base)
+       
+;      mov     dx, ax          ; 
+;      call    PrintHex
+;      call    PrintNL
+
+       mov     ax, 2000h               ; Always write at the start of the segment
+       push    ax              ; 3rd parameter
+
+;      mov     dx, ax
+;      call    PrintHex
+;      call    PrintNL
+
+       ; read the sector from the floppy
+       call    ReadSector
+       add     sp, 6                   ; clear 3 word params
+
+       push    VMSEG
+       pop     es
+
+;      mov     dx, word [es:2000h] ;
+;      call    PrintHex
+;      call    PrintNL
+       
+       
+
+; execute bios call
+
+       push    cx
+       push    si
+       push    bx
+       push    es              ; 
+
+       push    cs
+       pop     es
+       mov     si, bootsect_gdt
+       mov     ah, 87h
+
+       mov     cx, 100h
+
+       int     0x15
+       
+       adc     ax, 0
+
+;      mov     dx, ax  
+;      call    PrintHex
+;      call    PrintNL
+
+
+
+       pop     es              ;
+       pop     bx
+       pop     si
+       pop     cx
+       
+       ; on to next sector
+       inc     word [sec_count]
+
+
+       ; update the low->high copy table for the bios
+;      mov     ax, word [bootsect_src_base] ;
+;      add     ax, 512
+;      mov     dx,ax;
+;      call    PrintHex
+;      adc     byte [bootsect_src_base+2], 0
+;      mov     word [bootsect_src_base],ax
+
+       mov     ax, word [bootsect_dst_base] ;
+       add     ax, 512
+       adc     byte [bootsect_dst_base+2], 0
+       mov     word [bootsect_dst_base],ax
+       
+       ; have we loaded all of the sectors?
+
+       mov     bx, word [max_sector]
+       cmp     word [sec_count], bx
+
+
+.stall
+;      jmp     .skip
+       jl      .again2
+
+.skip
+       ; Now we've loaded the setup code and the kernel image.
+       ; Jump to setup code.
+       jmp     SETUPSEG:0
+
+; Read a sector from the floppy drive.
+; This code (and the rest of this boot sector) will have to
+; be re-written at some point so it reads more than one
+; sector at a time.
+;
+; Parameters:
+;     - "logical" sector number   [bp+8]
+;     - destination segment       [bp+6]
+;     - destination offset        [bp+4]
+ReadSector:
+       push    bp                      ; set up stack frame
+       mov     bp, sp                  ; "
+       pusha                           ; save all registers
+
+%if 0
+; debug params
+       mov     dx, [bp+8]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [bp+6]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [bp+4]
+       call    PrintHex
+       call    PrintNL
+%endif
+
+       ; Sector = log_sec % SECTORS_PER_TRACK
+       ; Head = (log_sec / SECTORS_PER_TRACK) % HEADS
+       mov     ax, [bp+8]              ; get logical sector number from stack
+       xor     dx, dx                  ; dx is high part of dividend (== 0)
+       mov     bx, SECTORS_PER_TRACK   ; divisor
+       div     bx                      ; do the division
+       mov     [sec], dx               ; sector is the remainder
+       and     ax, 1                   ; same as mod by HEADS==2 (slight hack)
+       mov     [head], ax
+
+       ; Track = log_sec / (SECTORS_PER_TRACK*HEADS)
+       mov     ax, [bp+8]              ; get logical sector number again
+       xor     dx, dx                  ; dx is high part of dividend
+       mov     bx, SECTORS_PER_TRACK*2 ; divisor
+       div     bx                      ; do the division
+       mov     [track], ax             ; track is quotient
+
+%if 0
+; debugging code
+       mov     dx, [sec]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [head]
+       call    PrintHex
+       call    PrintNL
+       mov     dx, [track]
+       call    PrintHex
+       call    PrintNL
+%endif
+
+       ; Now, try to actually read the sector from the floppy,
+       ; retrying up to 3 times.
+
+       mov     [num_retries], byte 0
+
+.again:
+       mov     ax, [bp+6]              ; dest segment...
+       mov     es, ax                  ;   goes in es
+       mov     ax, (0x02 << 8) | 1     ; function = 02h in ah,
+                                       ;   # secs = 1 in al
+       mov     bx, [track]             ; track number...
+       mov     ch, bl                  ;   goes in ch
+       mov     bx, [sec]               ; sector number...
+       mov     cl, bl                  ;   goes in cl...
+       inc     cl                      ;   but it must be 1-based, not 0-based
+       mov     bx, [head]              ; head number...
+       mov     dh, bl                  ;   goes in dh
+       xor     dl, dl                  ; hard code drive=0
+       mov     bx, [bp+4]              ; offset goes in bx
+                                       ;   (es:bx points to buffer)
+       ; Call the BIOS Read Diskette Sectors service
+       int     0x13
+       
+       ; If the carry flag is NOT set, then there was no error
+       ; and we're done.
+       jnc     .done
+
+       ; Error - code stored in ah
+       mov     dx, ax
+       call PrintHex
+       inc     byte [num_retries]
+       cmp     byte [num_retries], 3
+       jne     .again
+
+       ; If we got here, we failed thrice, so we give up
+       mov     dx, 0xdead
+       call    PrintHex
+.here: jmp     .here
+
+.done:
+       popa                            ; restore all regisiters
+       pop     bp                      ; leave stack frame
+       ret
+
+; Include utility routines:
+;%include "util.asm"
+; REPLACED WITH FOLLOWING WHICH MUST BE COMPILED 16 FOR USE IN THIS CODE
+PrintHex:
+       pusha
+       mov   cx, 4             ; 4 hex digits
+.PrintDigit:
+       rol   dx, 4             ; rotate so that lowest 4 bits are used
+       mov   ax, 0E0Fh         ; ah = request, al = mask for nybble
+       and   al, dl
+       add   al, 90h           ; convert al to ascii hex (four instructions)
+       daa                     ; I've spent 1 hour to understand how it works..
+       adc   al, 40h
+       daa
+       int   10h
+       loop  .PrintDigit
+       popa
+       ret
+
+; Print a newline.
+PrintNL:                       ; print CR and NL
+       push    ax
+       mov     ax, 0E0Dh       ; CR
+               int     10h
+               mov     al, 0Ah         ; LF
+               int     10h
+       pop     ax
+               ret
+
+
+
+; ----------------------------------------------------------------------
+; Variables
+; ----------------------------------------------------------------------
+
+; These are used by ReadSector
+head: dw 0
+track: dw 0
+sec: dw 0
+num_retries: db 0
+
+; Used for loops reading sectors from floppy
+sec_count: dw 0
+max_sector: dw 0
+
+
+
+
+bootsect_gdt:
+       dw      0,0,0,0
+       dw      0,0,0,0
+bootsect_src:
+       dw      0xffff                
+bootsect_src_base:
+       db      0,0x20,0x08             ;       ! base = 0x082000 
+       db      0x93            ;       ! typbyte
+       dw      0               ;       ! limit16,base24 =0
+bootsect_dst:
+       dw      0xffff
+bootsect_dst_base:
+       db      0,0,0x10        ;       ! base = 0x100000
+       db      0x93            ;       ! typbyte
+       dw      0               ;       ! limit16,base24 =0
+       dw      0,0,0,0         ;       ! BIOS CS
+       dw      0,0,0,0         ;       ! BIOS DS
+
+
+; Padding to make the PFAT Boot Record sit just before the BIOS signature.
+;Pad_From_Symbol PFAT_BOOT_RECORD_OFFSET, BeginText
+
+; PFAT boot record
+; Describes how to load the setup program and kernel.
+; The default values are appropriate for creating a boot
+; floppy by concatenating the boot sector, setup program,
+; and kernel image.  The buildFat program will change
+; these values if the boot floppy is formatted as a PFAT
+; filesystem.
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+       dw      0
+       dw      0
+
+;; part of pfat boot record
+setupStart:            
+       dw      1                       ; by default, setup is at first sector  
+
+;; part of pfat boot record
+setupSize:
+       dw      NUM_SETUP_SECTORS       ; number of sectors in setup
+
+;; part of pfat boot record
+kernelStart:
+       dw      1+NUM_SETUP_SECTORS     ; default start sector for kernel
+
+;; part of pfat boot record
+kernelSize:
+       dw      NUM_KERN_SECTORS
+
+       ;; part of pfat boot record
+vmStart:
+       dw      1+NUM_SETUP_SECTORS+NUM_KERN_SECTORS
+       ;; part of pfat boot record
+
+vmSize:
+       dw      NUM_VM_KERNEL_SECTORS
+
+
+       ; Finish by writing the BIOS signature to mark this as
+; a valid boot sector.
+Pad_From_Symbol BIOS_SIGNATURE_OFFSET, BeginText
+Signature   dw 0xAA55   ; BIOS controls this to ensure this is a boot sector
diff --git a/misc/test_vm/src/geekos/gdt.c b/misc/test_vm/src/geekos/gdt.c
new file mode 100644 (file)
index 0000000..977c6a1
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * Initialize kernel GDT.
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/segment.h>
+#include <geekos/int.h>
+#include <geekos/tss.h>
+#include <geekos/gdt.h>
+#include <libc/string.h>
+
+
+/*
+ * This is defined in lowlevel.asm.
+ */
+extern void Load_GDTR(ushort_t* limitAndBase);
+
+/* ----------------------------------------------------------------------
+ * Data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Number of entries in the kernel GDT.
+ */
+#define NUM_GDT_ENTRIES 16
+
+/*
+ * This is the kernel's global descriptor table.
+ */
+// JRL: why??? Should just call Alloc_Page(), otherwise we have dependencies all over the place
+//struct Segment_Descriptor *s_GDT=(struct Segment_Descriptor *)GDT_LOCATION;
+static struct Segment_Descriptor s_GDT[NUM_GDT_ENTRIES];
+
+
+/*
+ * Number of allocated GDT entries.
+ */
+static int s_numAllocated = 0;
+
+
+
+
+
+void DumpGDT()
+{
+  int i;
+  Print("GDT Contents:\n");
+
+  for (i=0;i<NUM_GDT_ENTRIES;i++) { 
+    if (s_GDT[i].present) { 
+      Print("%d: base=%u, limit=%u, sizeLow=%u,  baseLow=%u, type=%u, system=%u, dpl=%u, preent=%u, sizeHigh=%u, avail=%u, reserved=%u, dbBit=%u, granularity=%u, baseHigh=%u\n", 
+           i,
+           (s_GDT[i].baseHigh<<24) + s_GDT[i].baseLow,
+           (s_GDT[i].sizeHigh<<16) + s_GDT[i].sizeLow,
+           s_GDT[i].sizeLow,
+           s_GDT[i].baseLow,
+           s_GDT[i].type,
+           s_GDT[i].system,
+           s_GDT[i].dpl,
+           s_GDT[i].present,
+           s_GDT[i].sizeHigh,
+           s_GDT[i].avail,
+           s_GDT[i].reserved,
+           s_GDT[i].dbBit,
+           s_GDT[i].granularity,
+           s_GDT[i].baseHigh  );
+    } else {
+      Print("%d: Not Present\n",i);
+    }
+  }
+}
+
+
+/* ----------------------------------------------------------------------
+ * Functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Allocate an descriptor from the GDT.
+ * Returns null if there are none left.
+ */
+struct Segment_Descriptor* Allocate_Segment_Descriptor(void)
+{
+    struct Segment_Descriptor* result = 0;
+    int i;
+    bool iflag;
+
+    iflag = Begin_Int_Atomic();
+
+    /* Note; entry 0 is unused (thus never allocated) */
+    for (i = 1; i < NUM_GDT_ENTRIES; ++i) {
+       struct Segment_Descriptor *desc = &s_GDT[ i ];
+       if (desc->avail) {
+           ++s_numAllocated;
+           desc->avail = 0;
+           result = desc;
+           break;
+       }
+    }
+
+    End_Int_Atomic(iflag);
+
+    return result;
+}
+
+/*
+ * Free a segment descriptor.
+ */
+void Free_Segment_Descriptor(struct Segment_Descriptor* desc)
+{
+    bool iflag = Begin_Int_Atomic();
+
+    KASSERT(!desc->avail);
+
+    Init_Null_Segment_Descriptor(desc);
+    desc->avail = 1;
+    --s_numAllocated;
+
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Get the index (int the GDT) of given segment descriptor.
+ */
+int Get_Descriptor_Index(struct Segment_Descriptor* desc)
+{
+    return (int) (desc - s_GDT);
+}
+
+/*
+ * Initialize the kernel's GDT.
+ */
+void Init_GDT(void)
+{
+    ushort_t limitAndBase[3];
+    ulong_t gdtBaseAddr = (ulong_t) s_GDT;
+    struct Segment_Descriptor* desc;
+    int i;
+
+    Print("GDT Placed at %x, %d entries\n",GDT_LOCATION,NUM_GDT_ENTRIES);
+
+    KASSERT(sizeof(struct Segment_Descriptor) == 8);
+
+    /* Clear out entries. */
+    for (i = 0; i < NUM_GDT_ENTRIES; ++i) {
+       desc = &s_GDT[ i ];
+       Init_Null_Segment_Descriptor(desc);
+       desc->avail = 1;
+    }
+
+    /* Kernel code segment. */
+    desc = Allocate_Segment_Descriptor();
+    Init_Code_Segment_Descriptor(
+       desc,
+       0,               /* base address */
+       0x100000,        /* num pages (== 2^20) */
+       0                /* privilege level (0 == kernel) */
+    );
+    KASSERT(Get_Descriptor_Index(desc) == (KERNEL_CS >> 3));
+
+    /* Kernel data segment. */
+    desc = Allocate_Segment_Descriptor();
+    Init_Data_Segment_Descriptor(
+       desc,
+       0,               /* base address */
+       0x100000,        /* num pages (== 2^20) */
+       0                /* privilege level (0 == kernel) */
+    );
+    KASSERT(Get_Descriptor_Index(desc) == (KERNEL_DS >> 3));
+
+    /* Activate the kernel GDT. */
+    limitAndBase[0] = sizeof(struct Segment_Descriptor) * NUM_GDT_ENTRIES;
+    limitAndBase[1] = gdtBaseAddr & 0xffff;
+    limitAndBase[2] = gdtBaseAddr >> 16;
+    Load_GDTR(limitAndBase);
+}
diff --git a/misc/test_vm/src/geekos/ide.c b/misc/test_vm/src/geekos/ide.c
new file mode 100644 (file)
index 0000000..22b02c2
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * ATA (aka IDE) driver.
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * NOTES:
+ * 12/22/03 - Converted to use new block device layer with queued requests
+ *  1/20/04 - Changed probing of drives to work on Bochs 2.0 with 2 drives
+ */
+
+#include <geekos/serial.h>
+
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/errno.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/screen.h>
+#include <geekos/timer.h>
+#include <geekos/kthread.h>
+#include <geekos/blockdev.h>
+#include <geekos/ide.h>
+
+/* Registers */
+#define IDE_DATA_REGISTER              0x1f0
+#define IDE_ERROR_REGISTER             0x1f1
+#define IDE_FEATURE_REG                        IDE_ERROR_REGISTER
+#define IDE_SECTOR_COUNT_REGISTER      0x1f2
+#define IDE_SECTOR_NUMBER_REGISTER     0x1f3
+#define IDE_CYLINDER_LOW_REGISTER      0x1f4
+#define IDE_CYLINDER_HIGH_REGISTER     0x1f5
+#define IDE_DRIVE_HEAD_REGISTER                0x1f6
+#define IDE_STATUS_REGISTER            0x1f7
+#define IDE_COMMAND_REGISTER           0x1f7
+#define IDE_DEVICE_CONTROL_REGISTER    0x3F6
+
+/* Drives */
+#define IDE_DRIVE_0                    0xa0
+#define IDE_DRIVE_1                    0xb0
+
+/* Commands */
+#define IDE_COMMAND_IDENTIFY_DRIVE     0xEC
+#define IDE_COMMAND_SEEK               0x70
+#define IDE_COMMAND_READ_SECTORS       0x21
+#define IDE_COMMAND_READ_BUFFER                0xE4
+#define IDE_COMMAND_WRITE_SECTORS      0x30
+#define IDE_COMMAND_WRITE_BUFFER       0xE8
+#define IDE_COMMAND_DIAGNOSTIC         0x90
+#define IDE_COMMAND_ATAPI_IDENT_DRIVE  0xA1
+
+/* Results words from Identify Drive Request */
+#define        IDE_INDENTIFY_NUM_CYLINDERS     0x01
+#define        IDE_INDENTIFY_NUM_HEADS         0x03
+#define        IDE_INDENTIFY_NUM_BYTES_TRACK   0x04
+#define        IDE_INDENTIFY_NUM_BYTES_SECTOR  0x05
+#define        IDE_INDENTIFY_NUM_SECTORS_TRACK 0x06
+
+/* bits of Status Register */
+#define IDE_STATUS_DRIVE_BUSY          0x80
+#define IDE_STATUS_DRIVE_READY         0x40
+#define IDE_STATUS_DRIVE_WRITE_FAULT   0x20
+#define IDE_STATUS_DRIVE_SEEK_COMPLETE 0x10
+#define IDE_STATUS_DRIVE_DATA_REQUEST  0x08
+#define IDE_STATUS_DRIVE_CORRECTED_DATA        0x04
+#define IDE_STATUS_DRIVE_INDEX         0x02
+#define IDE_STATUS_DRIVE_ERROR         0x01
+
+/* Bits of Device Control Register */
+#define IDE_DCR_NOINTERRUPT            0x02
+#define IDE_DCR_RESET                  0x04
+
+/* Return codes from various IDE_* functions */
+#define        IDE_ERROR_NO_ERROR      0
+#define        IDE_ERROR_BAD_DRIVE     -1
+#define        IDE_ERROR_INVALID_BLOCK -2
+#define        IDE_ERROR_DRIVE_ERROR   -3
+
+/* Control register bits */
+#define IDE_CONTROL_REGISTER           0x3F6
+#define IDE_CONTROL_SOFTWARE_RESET     0x04
+#define IDE_CONTROL_INT_DISABLE                0x02
+
+#define LOW_BYTE(x)    (x & 0xff)
+#define HIGH_BYTE(x)   ((x >> 8) & 0xff)
+
+#define IDE_MAX_DRIVES                 2
+
+typedef struct {
+    short num_Cylinders;
+    short num_Heads;
+    short num_SectorsPerTrack;
+    short num_BytesPerSector;
+} ideDisk;
+
+int ideDebug = 99;
+static int numDrives;
+static ideDisk drives[IDE_MAX_DRIVES];
+
+struct Thread_Queue s_ideWaitQueue;
+struct Block_Request_List s_ideRequestQueue;
+
+/*
+ * return the number of logical blocks for a particular drive.
+ *
+ */
+static int IDE_getNumBlocks(int driveNum)
+{
+    if (driveNum < 0 || driveNum > IDE_MAX_DRIVES) {
+        return IDE_ERROR_BAD_DRIVE;
+    }
+
+    return (drives[driveNum].num_Heads * 
+            drives[driveNum].num_SectorsPerTrack *
+           drives[driveNum].num_Cylinders);
+}
+
+/*
+ * Read a block at the logical block number indicated.
+ */
+static int IDE_Read(int driveNum, int blockNum, char *buffer)
+{
+    int i;
+    int head;
+    int sector;
+    int cylinder;
+    short *bufferW;
+    int reEnable = 0;
+
+    if (driveNum < 0 || driveNum > (numDrives-1)) {
+       if (ideDebug) Print("ide: invalid drive %d\n", driveNum);
+        return IDE_ERROR_BAD_DRIVE;
+    }
+
+    if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+       if (ideDebug) Print("ide: invalid block %d\n", blockNum);
+        return IDE_ERROR_INVALID_BLOCK;
+    }
+
+    if (Interrupts_Enabled()) {
+       Disable_Interrupts();
+       reEnable = 1;
+    }
+
+    /* now compute the head, cylinder, and sector */
+    sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+    cylinder = blockNum / (drives[driveNum].num_Heads * 
+       drives[driveNum].num_SectorsPerTrack);
+    head = (blockNum / drives[driveNum].num_SectorsPerTrack) % 
+        drives[driveNum].num_Heads;
+
+    if (ideDebug >= 2) {
+       Print ("request to read block %d\n", blockNum);
+       Print ("    head %d\n", head);
+       Print ("    cylinder %d\n", cylinder);
+       Print ("    sector %d\n", sector);
+    }
+
+    Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+    Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+    Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+    Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+    if (driveNum == 0) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+    } else if (driveNum == 1) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+    }
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_READ_SECTORS);
+
+    if (ideDebug > 2) Print("About to wait for Read \n");
+
+    /* wait for the drive */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+       Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+       return IDE_ERROR_DRIVE_ERROR;
+    }
+
+    if (ideDebug > 2) Print("got buffer \n");
+
+    bufferW = (short *) buffer;
+    for (i=0; i < 256; i++) {
+        bufferW[i] = In_Word(IDE_DATA_REGISTER);
+    }
+
+    if (reEnable) Enable_Interrupts();
+
+    return IDE_ERROR_NO_ERROR;
+}
+
+/*
+ * Write a block at the logical block number indicated.
+ */
+static int IDE_Write(int driveNum, int blockNum, char *buffer)
+{
+    int i;
+    int head;
+    int sector;
+    int cylinder;
+    short *bufferW;
+    int reEnable = 0;
+
+    if (driveNum < 0 || driveNum > (numDrives-1)) {
+        return IDE_ERROR_BAD_DRIVE;
+    }
+
+    if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+        return IDE_ERROR_INVALID_BLOCK;
+    }
+
+    if (Interrupts_Enabled()) {
+       Disable_Interrupts();
+       reEnable = 1;
+    }
+
+    /* now compute the head, cylinder, and sector */
+    sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+    cylinder = blockNum / (drives[driveNum].num_Heads * 
+       drives[driveNum].num_SectorsPerTrack);
+    head = (blockNum / drives[driveNum].num_SectorsPerTrack) % 
+        drives[driveNum].num_Heads;
+
+    if (ideDebug) {
+       Print ("request to write block %d\n", blockNum);
+       Print ("    head %d\n", head);
+       Print ("    cylinder %d\n", cylinder);
+       Print ("    sector %d\n", sector);
+    }
+
+    Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+    Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+    Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+    Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+    if (driveNum == 0) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+    } else if (driveNum == 1) {
+       Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+    }
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_WRITE_SECTORS);
+
+
+    /* wait for the drive */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    bufferW = (short *) buffer;
+    for (i=0; i < 256; i++) {
+        Out_Word(IDE_DATA_REGISTER, bufferW[i]);
+    }
+
+    if (ideDebug) Print("About to wait for Write \n");
+
+    /* wait for the drive */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+       Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+       return IDE_ERROR_DRIVE_ERROR;
+    }
+
+    if (reEnable) Enable_Interrupts();
+
+    return IDE_ERROR_NO_ERROR;
+}
+
+static int IDE_Open(struct Block_Device *dev)
+{
+    KASSERT(!dev->inUse);
+    return 0;
+}
+
+static int IDE_Close(struct Block_Device *dev)
+{
+    KASSERT(dev->inUse);
+    return 0;
+}
+
+static int IDE_Get_Num_Blocks(struct Block_Device *dev)
+{
+    return IDE_getNumBlocks(dev->unit);
+}
+
+static struct Block_Device_Ops s_ideDeviceOps = {
+    IDE_Open,
+    IDE_Close,
+    IDE_Get_Num_Blocks,
+};
+
+static void IDE_Request_Thread(ulong_t arg)
+{
+    for (;;) {
+       struct Block_Request *request;
+       int rc;
+
+       /* Wait for a request to arrive */
+       request = Dequeue_Request(&s_ideRequestQueue, &s_ideWaitQueue);
+
+       /* Do the I/O */
+       if (request->type == BLOCK_READ)
+           rc = IDE_Read(request->dev->unit, request->blockNum, request->buf);
+       else
+           rc = IDE_Write(request->dev->unit, request->blockNum, request->buf);
+
+       /* Notify requesting thread of final status */
+       Notify_Request_Completion(request, rc == 0 ? COMPLETED : ERROR, rc);
+    }
+}
+
+static int readDriveConfig(int drive)
+{
+    int i;
+    int status;
+    short info[256];
+    char devname[BLOCKDEV_MAX_NAME_LEN];
+    int rc;
+
+    if (ideDebug) Print("ide: about to read drive config for drive #%d\n", drive);
+
+    Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+    Print("Set head register\n");
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_IDENTIFY_DRIVE);
+
+    Print ("identify drive\n");
+
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+    Print ("Status no longer busy\n");
+
+    status = In_Byte(IDE_STATUS_REGISTER);
+
+    Print ("Status is %x\n",status);
+    /*
+     * simulate failure
+     * status = 0x50;
+     */
+    if ((status & IDE_STATUS_DRIVE_DATA_REQUEST)) {
+      Print("ide: probe found ATA drive\n");
+      /* drive responded to ATA probe */
+      for (i=0; i < 256; i++) {
+       info[i] = In_Word(IDE_DATA_REGISTER);
+      }
+      
+      drives[drive].num_Cylinders = info[IDE_INDENTIFY_NUM_CYLINDERS];
+      drives[drive].num_Heads = info[IDE_INDENTIFY_NUM_HEADS];
+      drives[drive].num_SectorsPerTrack = info[IDE_INDENTIFY_NUM_SECTORS_TRACK];
+      drives[drive].num_BytesPerSector = info[IDE_INDENTIFY_NUM_BYTES_SECTOR];
+    } else {
+       /* try for ATAPI */
+      Print("Trying for ATAPI\n");
+      Out_Byte(IDE_FEATURE_REG, 0);             /* disable dma & overlap */
+      Print("Out FEATURE REG\n");
+      
+      Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+      Print("Out Head Select\n");
+      Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_ATAPI_IDENT_DRIVE);
+      Print("Out Ident drive\n");
+
+      while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+      Print("No longer busy\n");
+
+      status = In_Byte(IDE_STATUS_REGISTER);
+      
+      Print("status is %x\n",status);
+      Print("ide: found atapi drive\n");
+      return -1;
+    }
+    
+    Print("    ide%d: cyl=%d, heads=%d, sectors=%d\n", drive, drives[drive].num_Cylinders,
+         drives[drive].num_Heads, drives[drive].num_SectorsPerTrack);
+    
+    /* Register the drive as a block device */
+    snprintf(devname, sizeof(devname), "ide%d", drive);
+    rc = Register_Block_Device(devname, &s_ideDeviceOps, drive, 0, &s_ideWaitQueue, &s_ideRequestQueue);
+    if (rc != 0)
+       Print("  Error: could not create block device for %s\n", devname);
+
+    return 0;
+}
+
+
+void Init_IDE(void)
+{
+    int errorCode;
+
+    // Check to see if controller 0 is present
+    Out_Byte(0x1f3,0x88);
+    if (In_Byte(0x1f3)==0x88) { 
+      Print("IDE Controller 0 is present\n");
+    }
+
+    // Check to see if controller 1 is present
+    Out_Byte(0x173,0x88);
+    if (In_Byte(0x173)==0x88) { 
+      Print("IDE Controller 1 is present\n");
+    }
+
+    Print("Initializing IDE controller (0x1f0)...\n");
+
+    /* Reset the controller and drives */
+    Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT | IDE_DCR_RESET);
+    Micro_Delay(100);
+    Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT);
+
+/*
+ * FIXME: This code doesn't work on Bochs 2.0.
+ *    while ((In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_READY) == 0)
+ *     ;
+ */
+
+    /* This code does work on Bochs 2.0. */
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY)
+       ;
+
+    if (ideDebug) Print("About to run drive Diagnosis\n");
+
+    Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_DIAGNOSTIC);
+    while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+    errorCode = In_Byte(IDE_ERROR_REGISTER);
+    if (ideDebug) Print("ide: ide error register = %x\n", errorCode);
+
+    /* Probe and register drives */
+    if (readDriveConfig(0) == 0)
+       ++numDrives;
+    if (readDriveConfig(1) == 0)
+       ++numDrives;
+    if (ideDebug) Print("Found %d IDE drives\n", numDrives);
+
+    /* Start request thread */
+    if (numDrives > 0)
+       Start_Kernel_Thread(IDE_Request_Thread, 0, PRIORITY_NORMAL, true);
+}
diff --git a/misc/test_vm/src/geekos/idt.c b/misc/test_vm/src/geekos/idt.c
new file mode 100644 (file)
index 0000000..5e23989
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * GeekOS IDT initialization code
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/defs.h>
+#include <geekos/idt.h>
+#include <geekos/serial.h>
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Allocated
+ */
+static union IDT_Descriptor s_IDT[ NUM_IDT_ENTRIES ];
+
+// JRL: why??? Should just call Alloc_Page(), otherwise we have dependencies all over the place
+//static union IDT_Descriptor *s_IDT = (union IDT_Descriptor *)IDT_LOCATION;
+
+/*
+ * These symbols are defined in lowlevel.asm, and define the
+ * size of the interrupt entry point table and the sizes
+ * of the individual entry points.  This gives us sufficient
+ * information to build the IDT.
+ */
+extern char g_entryPointTableStart, g_entryPointTableEnd;
+extern int g_handlerSizeNoErr, g_handlerSizeErr;
+
+/*
+ * Table of C interrupt handler functions.
+ * Note that this is public only because it is used
+ * in lowlevel.asm.  Other code should not refer to it.
+ */
+Interrupt_Handler g_interruptTable[ NUM_IDT_ENTRIES ];
+
+
+
+void DumpIDT()
+{
+  int i;
+  Print("IDT Contents:\n");
+
+  for (i=0;i<NUM_IDT_ENTRIES/16;i++) { 
+    if (s_IDT[i].ig.present) { 
+      Print("%d: segmentselector=%u, offset=%u, offsetLow=%u, segmentSelector=%u, reserved=%u, signature=%u, dpl=%u, present=%u, offsetHigh=%u\n",
+           i,
+           s_IDT[i].ig.segmentSelector,
+           (s_IDT[i].ig.offsetHigh<<16) + s_IDT[i].ig.offsetLow,
+           s_IDT[i].ig.offsetLow,
+           s_IDT[i].ig.segmentSelector,
+           s_IDT[i].ig.reserved,
+           s_IDT[i].ig.signature,
+           s_IDT[i].ig.dpl,
+           s_IDT[i].ig.present,
+           s_IDT[i].ig.offsetHigh);
+    }
+  }
+}
+
+
+void SerialDumpIDT()
+{
+  int i;
+  SerialPrint("IDT Contents:\n");
+
+  for (i=0;i<NUM_IDT_ENTRIES;i++) { 
+    if (s_IDT[i].ig.present) { 
+      SerialPrint("%d: segmentselector=%u, offset=%u, offsetLow=%u, segmentSelector=%u, reserved=%u, signature=%u, dpl=%u, present=%u, offsetHigh=%u\n",
+           i,
+           s_IDT[i].ig.segmentSelector,
+           (s_IDT[i].ig.offsetHigh<<16) + s_IDT[i].ig.offsetLow,
+           s_IDT[i].ig.offsetLow,
+           s_IDT[i].ig.segmentSelector,
+           s_IDT[i].ig.reserved,
+           s_IDT[i].ig.signature,
+           s_IDT[i].ig.dpl,
+           s_IDT[i].ig.present,
+           s_IDT[i].ig.offsetHigh);
+      SerialPrint("\n");
+    }
+  }
+}
+
+
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize the Interrupt Descriptor Table.
+ * This will allow us to install C handler functions
+ * for interrupts, both processor-generated and
+ * those generated by external hardware.
+ */
+void Init_IDT(void)
+{
+    int i;
+    ushort_t limitAndBase[3];
+    ulong_t idtBaseAddr = (ulong_t) s_IDT;
+    ulong_t tableBaseAddr = (ulong_t) &g_entryPointTableStart;
+    ulong_t addr;
+
+    PrintBoth("Initializing IDT\n");
+
+    /* Make sure the layout of the entry point table is as we expect. */
+    KASSERT(g_handlerSizeNoErr == g_handlerSizeErr);
+    KASSERT((&g_entryPointTableEnd - &g_entryPointTableStart) ==
+            g_handlerSizeNoErr * NUM_IDT_ENTRIES);
+
+    /*
+     * Build the IDT.
+     * We're taking advantage of the fact that all of the
+     * entry points are laid out consecutively, and that they
+     * are all padded to be the same size.
+     */
+    for (i = 0, addr = tableBaseAddr; i < NUM_IDT_ENTRIES; ++i) {
+       /*
+        * All interrupts except for the syscall interrupt
+        * must have kernel privilege to access.
+        */
+       int dpl = (i == SYSCALL_INT) ? USER_PRIVILEGE : KERNEL_PRIVILEGE;
+       Init_Interrupt_Gate(&s_IDT[i], addr, dpl);
+       addr += g_handlerSizeNoErr;
+    }
+
+    /*
+     * Cruft together a 16 bit limit and 32 bit base address
+     * to load into the IDTR.
+     */
+    limitAndBase[0] = 8 * NUM_IDT_ENTRIES;
+    limitAndBase[1] = idtBaseAddr & 0xffff;
+    limitAndBase[2] = idtBaseAddr >> 16;
+
+    /* Install the new table in the IDTR. */
+    Load_IDTR(limitAndBase);
+}
+
+/*
+ * Initialize an interrupt gate with given handler address
+ * and descriptor privilege level.
+ */
+void Init_Interrupt_Gate(union IDT_Descriptor* desc, ulong_t addr,
+       int dpl)
+{
+    desc->ig.offsetLow = addr & 0xffff;
+    desc->ig.segmentSelector = KERNEL_CS;
+    desc->ig.reserved = 0;
+    desc->ig.signature = 0x70;  /* == 01110000b */
+    desc->ig.dpl = dpl;
+    desc->ig.present = 1;
+    desc->ig.offsetHigh = addr >> 16;
+}
+
+/*
+ * Install a C handler function for given interrupt.
+ * This is a lower-level notion than an "IRQ", which specifically
+ * means an interrupt triggered by external hardware.
+ * This function can install a handler for ANY interrupt.
+ */
+void Install_Interrupt_Handler(int interrupt, Interrupt_Handler handler)
+{
+    KASSERT(interrupt >= 0 && interrupt < NUM_IDT_ENTRIES);
+    g_interruptTable[interrupt] = handler;
+}
diff --git a/misc/test_vm/src/geekos/int.c b/misc/test_vm/src/geekos/int.c
new file mode 100644 (file)
index 0000000..2c59a5f
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * GeekOS interrupt handling data structures and functions
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/idt.h>         /* x86-specific int handling stuff */
+#include <geekos/screen.h>
+#include <geekos/kassert.h>
+#include <geekos/int.h>
+#include <geekos/serial.h>
+
+#include <geekos/cpu.h>
+
+/*
+ * Defined in lowlevel.asm.
+ */
+ulong_t Get_Current_EFLAGS(void);
+
+/* ----------------------------------------------------------------------
+ * Private functions and data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * A dummy interrupt handler function.
+ * Ensures that the low-level interrupt code always
+ * has a handler to call.
+ */
+static void Dummy_Interrupt_Handler(struct Interrupt_State* state)
+{
+  Begin_IRQ(state);
+
+  Print("Unexpected Interrupt!  Ignoring!\n");
+  SerialPrint("*** Unexpected interrupt! *** Ignoring!\n");
+  Dump_Interrupt_State(state);
+
+  End_IRQ(state);
+  
+  //  STOP();
+}
+
+#if 0
+static void Print_Selector(const char* regName, uint_t value)
+{
+    Print("%s: index=%d, ti=%d, rpl=%d\n",
+       regName, value >> 3, (value >> 2) & 1, value & 3);
+}
+#endif
+
+static void SerialPrint_Selector(const char* regName, uint_t value)
+{
+    SerialPrint("%s: index=%d, ti=%d, rpl=%d\n",
+       regName, value >> 3, (value >> 2) & 1, value & 3);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize the interrupt system.
+ */
+void Init_Interrupts(void)
+{
+    int i;
+
+    PrintBoth("Initializing Interrupts\n");
+
+    /* Low-level initialization.  Build and initialize the IDT. */
+    Init_IDT();
+
+    /*
+     * Initialize all entries of the handler table with a dummy handler.
+     * This will ensure that we always have a handler function to call.
+     */
+    for (i = 0; i < NUM_IDT_ENTRIES; ++i) {
+       Install_Interrupt_Handler(i, Dummy_Interrupt_Handler);
+    }
+
+    /* Re-enable interrupts */
+    Enable_Interrupts();
+}
+
+/*
+ * Query whether or not interrupts are currently enabled.
+ */
+bool Interrupts_Enabled(void)
+{
+    ulong_t eflags = Get_Current_EFLAGS();
+    return (eflags & EFLAGS_IF) != 0;
+}
+
+/*
+ * Dump interrupt state struct to screen
+ */
+void Dump_Interrupt_State(struct Interrupt_State* state)
+{
+    uint_t errorCode = state->errorCode;
+
+   SerialPrint("eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
+          "esi=%08x edi=%08x ebp=%08x\n"
+          "eip=%08x cs=%08x eflags=%08x\n"
+          "Interrupt number=%d, error code=%d\n"
+          "index=%d, TI=%d, IDT=%d, EXT=%d\n",
+       state->eax, state->ebx, state->ecx, state->edx,
+       state->esi, state->edi, state->ebp,
+       state->eip, state->cs, state->eflags,
+       state->intNum,  errorCode,
+       errorCode >> 3, (errorCode >> 2) & 1, (errorCode >> 1) & 1, errorCode & 1
+    );
+    if (Is_User_Interrupt(state)) {
+       struct User_Interrupt_State *ustate = (struct User_Interrupt_State*) state;
+       SerialPrint("user esp=%08x, user ss=%08x\n", ustate->espUser, ustate->ssUser);
+    }
+    SerialPrint_Selector("cs", state->cs);
+    SerialPrint_Selector("ds", state->ds);
+    SerialPrint_Selector("es", state->es);
+    SerialPrint_Selector("fs", state->fs);
+    SerialPrint_Selector("gs", state->gs);
+}
diff --git a/misc/test_vm/src/geekos/io.c b/misc/test_vm/src/geekos/io.c
new file mode 100644 (file)
index 0000000..3132733
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * x86 port IO routines
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/io.h>
+
+/*
+ * Write a byte to an I/O port.
+ */
+void Out_Byte(ushort_t port, uchar_t value)
+{
+    __asm__ __volatile__ (
+       "outb %b0, %w1"
+       :
+       : "a" (value), "Nd" (port)
+    );
+}
+
+/*
+ * Read a byte from an I/O port.
+ */
+uchar_t In_Byte(ushort_t port)
+{
+    uchar_t value;
+
+    __asm__ __volatile__ (
+       "inb %w1, %b0"
+       : "=a" (value)
+       : "Nd" (port)
+    );
+
+    return value;
+}
+
+/*
+ * Write a word to an I/O port.
+ */
+void Out_Word(ushort_t port, ushort_t value)
+{
+    __asm__ __volatile__ (
+       "outw %w0, %w1"
+       :
+       : "a" (value), "Nd" (port)
+    );
+}
+
+/*
+ * Read a byte from an I/O port.
+ */
+ushort_t In_Word(ushort_t port)
+{
+    ushort_t value;
+
+    __asm__ __volatile__ (
+       "inw %w1, %w0"
+       : "=a" (value)
+       : "Nd" (port)
+    );
+
+    return value;
+}
+
+/*
+ * Short delay.  May be needed when talking to some
+ * (slow) I/O devices.
+ */
+void IO_Delay(void)
+{
+    uchar_t value = 0;
+    __asm__ __volatile__ (
+       "outb %0, $0x80"
+       :
+       : "a" (value)
+    );
+}
diff --git a/misc/test_vm/src/geekos/irq.c b/misc/test_vm/src/geekos/irq.c
new file mode 100644 (file)
index 0000000..24c5104
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * This is the device-driver interface to the interrupt system.
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/idt.h>
+#include <geekos/io.h>
+#include <geekos/irq.h>
+
+/* ----------------------------------------------------------------------
+ * Private functions and data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Current IRQ mask.
+ * This should be kept up to date with setup.asm
+ * (which does the initial programming of the PICs).
+ */
+static ushort_t s_irqMask = 0xfffb;
+
+/*
+ * Get the master and slave parts of an IRQ mask.
+ */
+#define MASTER(mask) ((mask) & 0xff)
+#define SLAVE(mask) (((mask)>>8) & 0xff)
+
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Install a handler for given IRQ.
+ * Note that we don't unmask the IRQ.
+ */
+void Install_IRQ(int irq, Interrupt_Handler handler)
+{
+    Install_Interrupt_Handler(irq + FIRST_EXTERNAL_INT, handler);
+}
+
+/*
+ * Get current IRQ mask.  Each bit position represents
+ * one of the 16 IRQ lines.
+ */
+ushort_t Get_IRQ_Mask(void)
+{
+    return s_irqMask;
+}
+
+/*
+ * Set the IRQ mask.
+ */
+void Set_IRQ_Mask(ushort_t mask)
+{
+    uchar_t oldMask, newMask;
+
+    oldMask = MASTER(s_irqMask);
+    newMask = MASTER(mask);
+    if (newMask != oldMask) {
+       Out_Byte(0x21, newMask);
+    }
+
+    oldMask = SLAVE(s_irqMask);
+    newMask = SLAVE(mask);
+    if (newMask != oldMask) {
+       Out_Byte(0xA1, newMask);
+    }
+
+    s_irqMask = mask;
+}
+
+/*
+ * Enable given IRQ.
+ */
+void Enable_IRQ(int irq)
+{
+    bool iflag = Begin_Int_Atomic();
+
+    KASSERT(irq >= 0 && irq < 16);
+    ushort_t mask = Get_IRQ_Mask();
+    mask &= ~(1 << irq);
+    Set_IRQ_Mask(mask);
+
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Disable given IRQ.
+ */
+void Disable_IRQ(int irq)
+{
+    bool iflag = Begin_Int_Atomic();
+
+    KASSERT(irq >= 0 && irq < 16);
+    ushort_t mask = Get_IRQ_Mask();
+    mask |= (1 << irq);
+    Set_IRQ_Mask(mask);
+
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Called by an IRQ handler to begin the interrupt.
+ * Currently a no-op.
+ */
+void Begin_IRQ(struct Interrupt_State* state)
+{
+}
+
+/*
+ * Called by an IRQ handler to end the interrupt.
+ * Sends an EOI command to the appropriate PIC(s).
+ */
+void End_IRQ(struct Interrupt_State* state)
+{
+    int irq = state->intNum - FIRST_EXTERNAL_INT;
+    uchar_t command = 0x60 | (irq & 0x7);
+
+    if (irq < 8) {
+       /* Specific EOI to master PIC */
+       Out_Byte(0x20, command);
+    }
+    else {
+       /* Specific EOI to slave PIC, then to master (cascade line) */
+       Out_Byte(0xA0, command);
+       Out_Byte(0x20, 0x62);
+    }
+}
diff --git a/misc/test_vm/src/geekos/keyboard.c b/misc/test_vm/src/geekos/keyboard.c
new file mode 100644 (file)
index 0000000..409d9a4
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Keyboard driver
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Information sources:
+ * - Chapter 8 of _The Undocumented PC_, 2nd ed, by Frank van Gilluwe,
+ *   ISBN 0-201-47950-8.
+ * - Pages 400-409 of _The Programmers PC Sourcebook_, by Thom Hogan,
+ *   ISBN 1-55615-118-7.
+ */
+
+/*
+ * Credits:
+ * - Peter Gnodde <peter@pcswebdesign.nl> added support for
+ *   the CTRL and ALT modifiers
+ */
+
+/*
+ * TODO list:
+ * - Right now we're assuming an 83-key keyboard.
+ *   Should add support for 101+ keyboards.
+ * - Should toggle keyboard LEDs.
+ */
+
+#include <geekos/kthread.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/irq.h>
+#include <geekos/io.h>
+#include <geekos/keyboard.h>
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Current shift state.
+ */
+#define LEFT_SHIFT  0x01
+#define RIGHT_SHIFT 0x02
+#define LEFT_CTRL   0x04
+#define RIGHT_CTRL  0x08
+#define LEFT_ALT    0x10
+#define RIGHT_ALT   0x20
+#define SHIFT_MASK  (LEFT_SHIFT | RIGHT_SHIFT)
+#define CTRL_MASK   (LEFT_CTRL | RIGHT_CTRL)
+#define ALT_MASK    (LEFT_ALT | RIGHT_ALT)
+static unsigned s_shiftState = 0;
+
+/*
+ * Queue for keycodes, in case they arrive faster than consumer
+ * can deal with them.
+ */
+#define QUEUE_SIZE 256
+#define QUEUE_MASK 0xff
+#define NEXT(index) (((index) + 1) & QUEUE_MASK)
+static Keycode s_queue[QUEUE_SIZE];
+static int s_queueHead, s_queueTail;
+
+/*
+ * Wait queue for thread(s) waiting for keyboard events.
+ */
+static struct Thread_Queue s_waitQueue;
+
+/*
+ * Translate from scan code to key code, when shift is not pressed.
+ */
+static const Keycode s_scanTableNoShift[] = {
+    KEY_UNKNOWN, ASCII_ESC, '1', '2',   /* 0x00 - 0x03 */
+    '3', '4', '5', '6',                 /* 0x04 - 0x07 */
+    '7', '8', '9', '0',                 /* 0x08 - 0x0B */
+    '-', '=', ASCII_BS, '\t',           /* 0x0C - 0x0F */
+    'q', 'w', 'e', 'r',                 /* 0x10 - 0x13 */
+    't', 'y', 'u', 'i',                 /* 0x14 - 0x17 */
+    'o', 'p', '[', ']',                 /* 0x18 - 0x1B */
+    '\r', KEY_LCTRL, 'a', 's',          /* 0x1C - 0x1F */
+    'd', 'f', 'g', 'h',                 /* 0x20 - 0x23 */
+    'j', 'k', 'l', ';',                 /* 0x24 - 0x27 */
+    '\'', '`', KEY_LSHIFT, '\\',        /* 0x28 - 0x2B */
+    'z', 'x', 'c', 'v',                 /* 0x2C - 0x2F */
+    'b', 'n', 'm', ',',                 /* 0x30 - 0x33 */
+    '.', '/', KEY_RSHIFT, KEY_PRINTSCRN, /* 0x34 - 0x37 */
+    KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, /* 0x38 - 0x3B */
+    KEY_F2, KEY_F3, KEY_F4, KEY_F5,     /* 0x3C - 0x3F */
+    KEY_F6, KEY_F7, KEY_F8, KEY_F9,     /* 0x40 - 0x43 */
+    KEY_F10, KEY_NUMLOCK, KEY_SCRLOCK, KEY_KPHOME,  /* 0x44 - 0x47 */
+    KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, KEY_KPLEFT,  /* 0x48 - 0x4B */
+    KEY_KPCENTER, KEY_KPRIGHT, KEY_KPPLUS, KEY_KPEND,  /* 0x4C - 0x4F */
+    KEY_KPDOWN, KEY_KPPGDN, KEY_KPINSERT, KEY_KPDEL,  /* 0x50 - 0x53 */
+    KEY_SYSREQ, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,  /* 0x54 - 0x57 */
+};
+#define SCAN_TABLE_SIZE (sizeof(s_scanTableNoShift) / sizeof(Keycode))
+
+/*
+ * Translate from scan code to key code, when shift *is* pressed.
+ * Keep this in sync with the unshifted table above!
+ * They must be the same size.
+ */
+static const Keycode s_scanTableWithShift[] = {
+    KEY_UNKNOWN, ASCII_ESC, '!', '@',   /* 0x00 - 0x03 */
+    '#', '$', '%', '^',                 /* 0x04 - 0x07 */
+    '&', '*', '(', ')',                 /* 0x08 - 0x0B */
+    '_', '+', ASCII_BS, '\t',           /* 0x0C - 0x0F */
+    'Q', 'W', 'E', 'R',                 /* 0x10 - 0x13 */
+    'T', 'Y', 'U', 'I',                 /* 0x14 - 0x17 */
+    'O', 'P', '{', '}',                 /* 0x18 - 0x1B */
+    '\r', KEY_LCTRL, 'A', 'S',          /* 0x1C - 0x1F */
+    'D', 'F', 'G', 'H',                 /* 0x20 - 0x23 */
+    'J', 'K', 'L', ':',                 /* 0x24 - 0x27 */
+    '"', '~', KEY_LSHIFT, '|',          /* 0x28 - 0x2B */
+    'Z', 'X', 'C', 'V',                 /* 0x2C - 0x2F */
+    'B', 'N', 'M', '<',                 /* 0x30 - 0x33 */
+    '>', '?', KEY_RSHIFT, KEY_PRINTSCRN, /* 0x34 - 0x37 */
+    KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, /* 0x38 - 0x3B */
+    KEY_F2, KEY_F3, KEY_F4, KEY_F5,     /* 0x3C - 0x3F */
+    KEY_F6, KEY_F7, KEY_F8, KEY_F9,     /* 0x40 - 0x43 */
+    KEY_F10, KEY_NUMLOCK, KEY_SCRLOCK, KEY_KPHOME,  /* 0x44 - 0x47 */
+    KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, KEY_KPLEFT,  /* 0x48 - 0x4B */
+    KEY_KPCENTER, KEY_KPRIGHT, KEY_KPPLUS, KEY_KPEND,  /* 0x4C - 0x4F */
+    KEY_KPDOWN, KEY_KPPGDN, KEY_KPINSERT, KEY_KPDEL,  /* 0x50 - 0x53 */
+    KEY_SYSREQ, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,  /* 0x54 - 0x57 */
+};
+
+static __inline__ bool Is_Queue_Empty(void)
+{
+    return s_queueHead == s_queueTail;
+}
+
+static __inline__ bool Is_Queue_Full(void)
+{
+    return NEXT(s_queueTail) == s_queueHead;
+}
+
+static __inline__ void Enqueue_Keycode(Keycode keycode)
+{
+    if (!Is_Queue_Full()) {
+       s_queue[ s_queueTail ] = keycode;
+       s_queueTail = NEXT(s_queueTail);
+    }
+}
+
+static __inline__ Keycode Dequeue_Keycode(void)
+{
+    Keycode result;
+    KASSERT(!Is_Queue_Empty());
+    result = s_queue[ s_queueHead ];
+    s_queueHead = NEXT(s_queueHead);
+    return result;
+}
+
+/*
+ * Handler for keyboard interrupts.
+ */
+static void Keyboard_Interrupt_Handler(struct Interrupt_State* state)
+{
+    uchar_t status, scanCode;
+    unsigned flag = 0;
+    bool release = false, shift;
+    Keycode keycode;
+
+    Begin_IRQ(state);
+
+    status = In_Byte(KB_CMD);
+    IO_Delay();
+
+    if ((status & KB_OUTPUT_FULL) != 0) {
+       /* There is a byte available */
+       scanCode = In_Byte(KB_DATA);
+       IO_Delay();
+/*
+ *     Print("code=%x%s\n", scanCode, (scanCode&0x80) ? " [release]" : "");
+ */
+
+       if (scanCode & KB_KEY_RELEASE) {
+           release = true;
+           scanCode &= ~(KB_KEY_RELEASE);
+       }
+
+       if (scanCode >= SCAN_TABLE_SIZE) {
+           Print("Unknown scan code: %x\n", scanCode);
+           goto done;
+       }
+
+       /* Process the key */
+       shift = ((s_shiftState & SHIFT_MASK) != 0);
+       keycode = shift ? s_scanTableWithShift[scanCode] : s_scanTableNoShift[scanCode];
+
+       /* Update shift, control and alt state */
+       switch (keycode) {
+       case KEY_LSHIFT:
+           flag = LEFT_SHIFT;
+           break;
+       case KEY_RSHIFT:
+           flag = RIGHT_SHIFT;
+           break;
+       case KEY_LCTRL:
+           flag = LEFT_CTRL;
+           break;
+       case KEY_RCTRL:
+           flag = RIGHT_CTRL;
+           break;
+       case KEY_LALT:
+           flag = LEFT_ALT;
+           break;
+       case KEY_RALT:
+           flag = RIGHT_ALT;
+           break;
+       default:
+           goto noflagchange;
+       }
+
+       if (release)
+           s_shiftState &= ~(flag);
+       else
+           s_shiftState |= flag;
+                       
+       /*
+        * Shift, control and alt keys don't have to be
+        * queued, flags will be set!
+        */
+       goto done;
+
+noflagchange:
+       /* Format the new keycode */
+       if (shift)
+           keycode |= KEY_SHIFT_FLAG;
+       if ((s_shiftState & CTRL_MASK) != 0)
+           keycode |= KEY_CTRL_FLAG;
+       if ((s_shiftState & ALT_MASK) != 0)
+           keycode |= KEY_ALT_FLAG;
+       if (release)
+           keycode |= KEY_RELEASE_FLAG;
+               
+       /* Put the keycode in the buffer */
+       Enqueue_Keycode(keycode);
+
+       /* Wake up event consumers */
+       Wake_Up(&s_waitQueue);
+
+       /*
+        * Pick a new thread upon return from interrupt
+        * (hopefully the one waiting for the keyboard event)
+        */
+       g_needReschedule = true;
+    }
+
+done:
+    End_IRQ(state);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+void Init_Keyboard(void)
+{
+    ushort_t irqMask;
+
+    Print("Initializing keyboard...\n");
+
+    /* Start out with no shift keys enabled. */
+    s_shiftState = 0;
+
+    /* Buffer is initially empty. */
+    s_queueHead = s_queueTail = 0;
+
+    /* Install interrupt handler */
+    Install_IRQ(KB_IRQ, Keyboard_Interrupt_Handler);
+
+    /* Enable IRQ1 (keyboard) */
+    irqMask = Get_IRQ_Mask();
+    irqMask &= ~(1 << KB_IRQ);
+    Set_IRQ_Mask(irqMask);
+}
+
+/*
+ * Poll for a key event.
+ * Returns true if a key is available,
+ * false if not.  If a key event is available,
+ * it will be stored in the location pointed to
+ * by keycode.
+ */
+bool Read_Key(Keycode* keycode)
+{
+    bool result, iflag;
+
+    iflag = Begin_Int_Atomic();
+
+    result = !Is_Queue_Empty();
+    if (result) {
+       *keycode = Dequeue_Keycode();
+    }
+
+    End_Int_Atomic(iflag);
+
+    return result;
+}
+
+/*
+ * Wait for a keycode to arrive.
+ * Uses the keyboard wait queue to sleep until
+ * a keycode arrives.
+ */
+Keycode Wait_For_Key(void)
+{
+    bool gotKey, iflag;
+    Keycode keycode = KEY_UNKNOWN;
+
+    iflag = Begin_Int_Atomic();
+
+    do {
+       gotKey = !Is_Queue_Empty();
+       if (gotKey)
+           keycode = Dequeue_Keycode();
+       else
+           Wait(&s_waitQueue);
+    }
+    while (!gotKey);
+
+    End_Int_Atomic(iflag);
+
+    return keycode;
+}
diff --git a/misc/test_vm/src/geekos/kthread.c b/misc/test_vm/src/geekos/kthread.c
new file mode 100644 (file)
index 0000000..e2a6df2
--- /dev/null
@@ -0,0 +1,818 @@
+/*
+ * Kernel threads
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/defs.h>
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/mem.h>
+#include <geekos/symbol.h>
+#include <geekos/string.h>
+#include <geekos/kthread.h>
+#include <geekos/malloc.h>
+#include <geekos/serial.h>
+
+/* ----------------------------------------------------------------------
+ * Private data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * List of all threads in the system.
+ */
+static struct All_Thread_List s_allThreadList;
+
+/*
+ * Queue of runnable threads.
+ */
+static struct Thread_Queue s_runQueue;
+
+/*
+ * Current thread.
+ */
+struct Kernel_Thread* g_currentThread;
+
+/*
+ * Boolean flag indicating that we need to choose a new runnable thread.
+ * It is checked by the interrupt return code (Handle_Interrupt,
+ * in lowlevel.asm) before returning from an interrupt.
+ */
+int g_needReschedule;
+
+/*
+ * Boolean flag indicating that preemption is disabled.
+ * When set, external interrupts (such as the timer tick)
+ * will not cause a new thread to be selected.
+ */
+volatile int g_preemptionDisabled;
+
+/*
+ * Queue of finished threads needing disposal,
+ * and a wait queue used for communication between exited threads
+ * and the reaper thread.
+ */
+static struct Thread_Queue s_graveyardQueue;
+static struct Thread_Queue s_reaperWaitQueue;
+
+/*
+ * Counter for keys that access thread-local data, and an array
+ * of destructors for freeing that data when the thread dies.  This is
+ * based on POSIX threads' thread-specific data functionality.
+ */
+static unsigned int s_tlocalKeyCounter = 0;
+static tlocal_destructor_t s_tlocalDestructors[MAX_TLOCAL_KEYS];
+
+/* ----------------------------------------------------------------------
+ * Private functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize a new Kernel_Thread.
+ */
+static void Init_Thread(struct Kernel_Thread* kthread, void* stackPage,
+       int priority, bool detached)
+{
+    static int nextFreePid = 1;
+
+    struct Kernel_Thread* owner = detached ? (struct Kernel_Thread*)0 : g_currentThread;
+
+    memset(kthread, '\0', sizeof(*kthread));
+    kthread->stackPage = stackPage;
+    kthread->esp = ((ulong_t) kthread->stackPage) + PAGE_SIZE;
+    kthread->numTicks = 0;
+    kthread->priority = priority;
+    kthread->userContext = 0;
+    kthread->owner = owner;
+
+    /*
+     * The thread has an implicit self-reference.
+     * If the thread is not detached, then its owner
+     * also has a reference to it.
+     */
+    kthread->refCount = detached ? 1 : 2;
+
+    kthread->alive = true;
+    Clear_Thread_Queue(&kthread->joinQueue);
+    kthread->pid = nextFreePid++;
+
+}
+
+/*
+ * Create a new raw thread object.
+ * Returns a null pointer if there isn't enough memory.
+ */
+static struct Kernel_Thread* Create_Thread(int priority, bool detached)
+{
+    struct Kernel_Thread* kthread;
+    void* stackPage = 0;
+
+    /*
+     * For now, just allocate one page each for the thread context
+     * object and the thread's stack.
+     */
+    kthread = Alloc_Page();
+    if (kthread != 0)
+        stackPage = Alloc_Page();    
+
+    /* Make sure that the memory allocations succeeded. */
+    if (kthread == 0)
+       return 0;
+    if (stackPage == 0) {
+       Free_Page(kthread);
+       return 0;
+    }
+
+    /*Print("New thread @ %x, stack @ %x\n", kthread, stackPage); */
+
+    /*
+     * Initialize the stack pointer of the new thread
+     * and accounting info
+     */
+    Init_Thread(kthread, stackPage, priority, detached);
+
+    /* Add to the list of all threads in the system. */
+    Add_To_Back_Of_All_Thread_List(&s_allThreadList, kthread);
+
+    return kthread;
+}
+
+/*
+ * Push a dword value on the stack of given thread.
+ * We use this to set up some context for the thread before
+ * we make it runnable.
+ */
+static __inline__ void Push(struct Kernel_Thread* kthread, ulong_t value)
+{
+    kthread->esp -= 4;
+    *((ulong_t *) kthread->esp) = value;
+}
+
+/*
+ * Destroy given thread.
+ * This function should perform all cleanup needed to
+ * reclaim the resources used by a thread.
+ * Called with interrupts enabled.
+ */
+static void Destroy_Thread(struct Kernel_Thread* kthread)
+{
+
+    /* Dispose of the thread's memory. */
+    Disable_Interrupts();
+    Free_Page(kthread->stackPage);
+    Free_Page(kthread);
+
+    /* Remove from list of all threads */
+    Remove_From_All_Thread_List(&s_allThreadList, kthread);
+
+    Enable_Interrupts();
+
+}
+
+/*
+ * Hand given thread to the reaper for destruction.
+ * Must be called with interrupts disabled!
+ */
+static void Reap_Thread(struct Kernel_Thread* kthread)
+{
+    KASSERT(!Interrupts_Enabled());
+    Enqueue_Thread(&s_graveyardQueue, kthread);
+    Wake_Up(&s_reaperWaitQueue);
+}
+
+/*
+ * Called when a reference to the thread is broken.
+ */
+static void Detach_Thread(struct Kernel_Thread* kthread)
+{
+    KASSERT(!Interrupts_Enabled());
+    KASSERT(kthread->refCount > 0);
+
+    --kthread->refCount;
+    if (kthread->refCount == 0) {
+       Reap_Thread(kthread);
+    }
+}
+
+/*
+ * This function performs any needed initialization before
+ * a thread start function is executed.  Currently we just use
+ * it to enable interrupts (since Schedule() always activates
+ * a thread with interrupts disabled).
+ */
+static void Launch_Thread(void)
+{
+    Enable_Interrupts();
+}
+
+/*
+ * Push initial values for general purpose registers.
+ */
+static void Push_General_Registers(struct Kernel_Thread* kthread)
+{
+    /*
+     * Push initial values for saved general-purpose registers.
+     * (The actual values are not important.)
+     */
+    Push(kthread, 0);  /* eax */
+    Push(kthread, 0);  /* ebx */
+    Push(kthread, 0);  /* edx */
+    Push(kthread, 0);  /* edx */
+    Push(kthread, 0);  /* esi */
+    Push(kthread, 0);  /* edi */
+    Push(kthread, 0);  /* ebp */
+}
+
+/*
+ * Shutdown a kernel thread.
+ * This is called if a kernel thread exits by falling off
+ * the end of its start function.
+ */
+static void Shutdown_Thread(void)
+{
+    Exit(0);
+}
+
+/*
+ * Set up the initial context for a kernel-mode-only thread.
+ */
+static void Setup_Kernel_Thread(
+    struct Kernel_Thread* kthread,
+    Thread_Start_Func startFunc,
+    ulong_t arg)
+{
+    /*
+     * Push the argument to the thread start function, and the
+     * return address (the Shutdown_Thread function, so the thread will
+     * go away cleanly when the start function returns).
+     */
+    Push(kthread, arg);
+    Push(kthread, (ulong_t) &Shutdown_Thread);
+
+    /* Push the address of the start function. */
+    Push(kthread, (ulong_t) startFunc);
+
+    /*
+     * To make the thread schedulable, we need to make it look
+     * like it was suspended by an interrupt.  This means pushing
+     * an "eflags, cs, eip" sequence onto the stack,
+     * as well as int num, error code, saved registers, etc.
+     */
+
+    /*
+     * The EFLAGS register will have all bits clear.
+     * The important constraint is that we want to have the IF
+     * bit clear, so that interrupts are disabled when the
+     * thread starts.
+     */
+    Push(kthread, 0UL);  /* EFLAGS */
+
+    /*
+     * As the "return address" specifying where the new thread will
+     * start executing, use the Launch_Thread() function.
+     */
+    Push(kthread, KERNEL_CS);
+    Push(kthread, (ulong_t) &Launch_Thread);
+
+    /* Push fake error code and interrupt number. */
+    Push(kthread, 0);
+    Push(kthread, 0);
+
+    /* Push initial values for general-purpose registers. */
+    Push_General_Registers(kthread);
+
+    /*
+     * Push values for saved segment registers.
+     * Only the ds and es registers will contain valid selectors.
+     * The fs and gs registers are not used by any instruction
+     * generated by gcc.
+     */
+    Push(kthread, KERNEL_DS);  /* ds */
+    Push(kthread, KERNEL_DS);  /* es */
+    Push(kthread, 0);  /* fs */
+    Push(kthread, 0);  /* gs */
+}
+
+
+
+/*
+ * This is the body of the idle thread.  Its job is to preserve
+ * the invariant that a runnable thread always exists,
+ * i.e., the run queue is never empty.
+ */
+static void Idle(ulong_t arg)
+{
+    while (true)
+       Yield();
+}
+
+/*
+ * The reaper thread.  Its job is to de-allocate memory
+ * used by threads which have finished.
+ */
+static void Reaper(ulong_t arg)
+{
+    struct Kernel_Thread *kthread;
+
+    Disable_Interrupts();
+
+    while (true) {
+       /* See if there are any threads needing disposal. */
+       if ((kthread = s_graveyardQueue.head) == 0) {
+           /* Graveyard is empty, so wait for a thread to die. */
+           Wait(&s_reaperWaitQueue);
+       }
+       else {
+           /* Make the graveyard queue empty. */
+           Clear_Thread_Queue(&s_graveyardQueue);
+
+           /*
+            * Now we can re-enable interrupts, since we
+            * have removed all the threads needing disposal.
+            */
+           Enable_Interrupts();
+           Yield();   /* allow other threads to run? */
+
+           /* Dispose of the dead threads. */
+           while (kthread != 0) {
+               struct Kernel_Thread* next = Get_Next_In_Thread_Queue(kthread);
+#if 0
+               Print("Reaper: disposing of thread @ %x, stack @ %x\n",
+                   kthread, kthread->stackPage);
+#endif
+               Destroy_Thread(kthread);
+               kthread = next;
+           }
+
+           /*
+            * Disable interrupts again, since we're going to
+            * do another iteration.
+            */
+           Disable_Interrupts();
+       }
+    }
+}
+
+/*
+ * Find the best (highest priority) thread in given
+ * thread queue.  Returns null if queue is empty.
+ */
+static __inline__ struct Kernel_Thread* Find_Best(struct Thread_Queue* queue)
+{
+    /* Pick the highest priority thread */
+    struct Kernel_Thread *kthread = queue->head, *best = 0;
+    while (kthread != 0) {
+       if (best == 0 || kthread->priority > best->priority)
+           best = kthread;
+       kthread = Get_Next_In_Thread_Queue(kthread);
+    }
+    return best;
+}
+
+/*
+ * Acquires pointer to thread-local data from the current thread
+ * indexed by the given key.  Assumes interrupts are off.
+ */
+static __inline__ const void** Get_Tlocal_Pointer(tlocal_key_t k) 
+{
+    struct Kernel_Thread* current = g_currentThread;
+
+    KASSERT(k < MAX_TLOCAL_KEYS);
+
+    return &current->tlocalData[k];
+}
+
+/*
+ * Clean up any thread-local data upon thread exit.  Assumes
+ * this is called with interrupts disabled.  We follow the POSIX style
+ * of possibly invoking a destructor more than once, because a
+ * destructor to some thread-local data might cause other thread-local
+ * data to become alive once again.  If everything is NULL by the end
+ * of an iteration, we are done.
+ */
+static void Tlocal_Exit(struct Kernel_Thread* curr) {
+    int i, j, called = 0;
+
+    KASSERT(!Interrupts_Enabled());
+
+    for (j = 0; j<MIN_DESTRUCTOR_ITERATIONS; j++) {
+
+        for (i = 0; i<MAX_TLOCAL_KEYS; i++) {
+
+           void *x = (void *)curr->tlocalData[i];
+           if (x != NULL && s_tlocalDestructors[i] != NULL) {
+
+               curr->tlocalData[i] = NULL;
+               called = 1;
+
+               Enable_Interrupts();
+               s_tlocalDestructors[i](x);
+               Disable_Interrupts();
+           }
+       }
+       if (!called) break;
+    }
+}
+
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+void Init_Scheduler(void)
+{
+    struct Kernel_Thread* mainThread = (struct Kernel_Thread *) KERNEL_THREAD_OBJECT;
+
+    PrintBoth("Initializing Scheduler\n");
+
+    /*
+     * Create initial kernel thread context object and stack,
+     * and make them current.
+     */
+    Init_Thread(mainThread, (void *) KERNEL_STACK, PRIORITY_NORMAL, true);
+    g_currentThread = mainThread;
+    Add_To_Back_Of_All_Thread_List(&s_allThreadList, mainThread);
+
+    /*
+     * Create the idle thread.
+     */
+    /*Print("starting idle thread\n");*/
+    Start_Kernel_Thread(Idle, 0, PRIORITY_IDLE, true);
+
+    /*
+     * Create the reaper thread.
+     */
+    /*Print("starting reaper thread\n");*/
+    Start_Kernel_Thread(Reaper, 0, PRIORITY_NORMAL, true);
+}
+
+/*
+ * Start a kernel-mode-only thread, using given function as its body
+ * and passing given argument as its parameter.  Returns pointer
+ * to the new thread if successful, null otherwise.
+ *
+ * startFunc - is the function to be called by the new thread
+ * arg - is a paramter to pass to the new function
+ * priority - the priority of this thread (use PRIORITY_NORMAL) for
+ *    most things
+ * detached - use false for kernel threads
+ */
+struct Kernel_Thread* Start_Kernel_Thread(
+    Thread_Start_Func startFunc,
+    ulong_t arg,
+    int priority,
+    bool detached
+)
+{
+    struct Kernel_Thread* kthread = Create_Thread(priority, detached);
+    if (kthread != 0) {
+       /*
+        * Create the initial context for the thread to make
+        * it schedulable.
+        */
+       Setup_Kernel_Thread(kthread, startFunc, arg);
+
+
+       /* Atomically put the thread on the run queue. */
+       Make_Runnable_Atomic(kthread);
+    }
+
+    return kthread;
+}
+
+/*
+ * Add given thread to the run queue, so that it
+ * may be scheduled.  Must be called with interrupts disabled!
+ */
+void Make_Runnable(struct Kernel_Thread* kthread)
+{
+    KASSERT(!Interrupts_Enabled());
+
+    Enqueue_Thread(&s_runQueue, kthread);
+}
+
+/*
+ * Atomically make a thread runnable.
+ * Assumes interrupts are currently enabled.
+ */
+void Make_Runnable_Atomic(struct Kernel_Thread* kthread)
+{
+    Disable_Interrupts();
+    Make_Runnable(kthread);
+    Enable_Interrupts();
+}
+
+/*
+ * Get the thread that currently has the CPU.
+ */
+struct Kernel_Thread* Get_Current(void)
+{
+    return g_currentThread;
+}
+
+/*
+ * Get the next runnable thread from the run queue.
+ * This is the scheduler.
+ */
+struct Kernel_Thread* Get_Next_Runnable(void)
+{
+    struct Kernel_Thread* best = 0;
+
+    best = Find_Best(&s_runQueue);
+    KASSERT(best != 0);
+    Remove_Thread(&s_runQueue, best);
+
+/*
+ *    Print("Scheduling %x\n", best);
+ */
+    return best;
+}
+
+/*
+ * Schedule a thread that is waiting to run.
+ * Must be called with interrupts off!
+ * The current thread should already have been placed
+ * on whatever queue is appropriate (i.e., either the
+ * run queue if it is still runnable, or a wait queue
+ * if it is waiting for an event to occur).
+ */
+void Schedule(void)
+{
+    struct Kernel_Thread* runnable;
+
+    /* Make sure interrupts really are disabled */
+    KASSERT(!Interrupts_Enabled());
+
+    /* Preemption should not be disabled. */
+    KASSERT(!g_preemptionDisabled);
+
+    /* Get next thread to run from the run queue */
+    runnable = Get_Next_Runnable();
+
+    /*
+     * Activate the new thread, saving the context of the current thread.
+     * Eventually, this thread will get re-activated and Switch_To_Thread()
+     * will "return", and then Schedule() will return to wherever
+     * it was called from.
+     */
+    Switch_To_Thread(runnable);
+}
+
+/*
+ * Voluntarily give up the CPU to another thread.
+ * Does nothing if no other threads are ready to run.
+ */
+void Yield(void)
+{
+    Disable_Interrupts();
+    Make_Runnable(g_currentThread);
+    Schedule();
+    Enable_Interrupts();
+}
+
+/*
+ * Exit the current thread.
+ * Calling this function initiates a context switch.
+ */
+void Exit(int exitCode)
+{
+    struct Kernel_Thread* current = g_currentThread;
+
+    if (Interrupts_Enabled())
+       Disable_Interrupts();
+
+    /* Thread is dead */
+    current->exitCode = exitCode;
+    current->alive = false;
+
+    /* Clean up any thread-local memory */
+    Tlocal_Exit(g_currentThread);
+
+    /* Notify the thread's owner, if any */
+    Wake_Up(&current->joinQueue);
+
+    /* Remove the thread's implicit reference to itself. */
+    Detach_Thread(g_currentThread);
+
+    /*
+     * Schedule a new thread.
+     * Since the old thread wasn't placed on any
+     * thread queue, it won't get scheduled again.
+     */
+    Schedule();
+
+    /* Shouldn't get here */
+    KASSERT(false);
+    // the build dies on a warning for a 'noreturn' violation
+    // This ensures that this function never returns
+    while(1);
+}
+
+/*
+ * Wait for given thread to die.
+ * Interrupts must be enabled.
+ * Returns the thread exit code.
+ */
+int Join(struct Kernel_Thread* kthread)
+{
+    int exitCode;
+
+    KASSERT(Interrupts_Enabled());
+
+    /* It is only legal for the owner to join */
+    KASSERT(kthread->owner == g_currentThread);
+
+    Disable_Interrupts();
+
+    /* Wait for it to die */
+    while (kthread->alive) {
+       Wait(&kthread->joinQueue);
+    }
+
+    /* Get thread exit code. */
+    exitCode = kthread->exitCode;
+
+    /* Release our reference to the thread */
+    Detach_Thread(kthread);
+
+    Enable_Interrupts();
+
+    return exitCode;
+}
+
+/*
+ * Look up a thread by its process id.
+ * The caller must be the thread's owner.
+ */
+struct Kernel_Thread* Lookup_Thread(int pid)
+{
+    struct Kernel_Thread *result = 0;
+
+    bool iflag = Begin_Int_Atomic();
+
+    /*
+     * TODO: we could remove the requirement that the caller
+     * needs to be the thread's owner by specifying that another
+     * reference is added to the thread before it is returned.
+     */
+
+    result = Get_Front_Of_All_Thread_List(&s_allThreadList);
+    while (result != 0) {
+       if (result->pid == pid) {
+           if (g_currentThread != result->owner)
+               result = 0;
+           break;
+       }
+       result = Get_Next_In_All_Thread_List(result);
+    }
+
+    End_Int_Atomic(iflag);
+
+    return result;
+}
+
+
+/*
+ * Wait on given wait queue.
+ * Must be called with interrupts disabled!
+ * Note that the function will return with interrupts
+ * disabled.  This is desirable, because it allows us to
+ * atomically test a condition that can be affected by an interrupt
+ * and wait for it to be satisfied (if necessary).
+ * See the Wait_For_Key() function in keyboard.c
+ * for an example.
+ */
+void Wait(struct Thread_Queue* waitQueue)
+{
+    struct Kernel_Thread* current = g_currentThread;
+
+    KASSERT(!Interrupts_Enabled());
+
+    /* Add the thread to the wait queue. */
+    Enqueue_Thread(waitQueue, current);
+
+    /* Find another thread to run. */
+    Schedule();
+}
+
+/*
+ * Wake up all threads waiting on given wait queue.
+ * Must be called with interrupts disabled!
+ * See Keyboard_Interrupt_Handler() function in keyboard.c
+ * for an example.
+ */
+void Wake_Up(struct Thread_Queue* waitQueue)
+{
+    struct Kernel_Thread *kthread = waitQueue->head, *next;
+
+    KASSERT(!Interrupts_Enabled());
+
+    /*
+     * Walk throught the list of threads in the wait queue,
+     * transferring each one to the run queue.
+     */
+    while (kthread != 0) {
+       next = Get_Next_In_Thread_Queue(kthread);
+       Make_Runnable(kthread);
+       kthread = next;
+    }
+
+    /* The wait queue is now empty. */
+    Clear_Thread_Queue(waitQueue);
+}
+
+/*
+ * Wake up a single thread waiting on given wait queue
+ * (if there are any threads waiting).  Chooses the highest priority thread.
+ * Interrupts must be disabled!
+ */
+void Wake_Up_One(struct Thread_Queue* waitQueue)
+{
+    struct Kernel_Thread* best;
+
+    KASSERT(!Interrupts_Enabled());
+
+    best = Find_Best(waitQueue);
+
+    if (best != 0) {
+       Remove_Thread(waitQueue, best);
+       Make_Runnable(best);
+       /*Print("Wake_Up_One: waking up %x from %x\n", best, g_currentThread); */
+    }
+}
+
+/*
+ * Allocate a key for accessing thread-local data.
+ */
+int Tlocal_Create(tlocal_key_t *key, tlocal_destructor_t destructor) 
+{
+    KASSERT(key);
+
+    bool iflag = Begin_Int_Atomic();
+
+    if (s_tlocalKeyCounter == MAX_TLOCAL_KEYS) return -1;
+    s_tlocalDestructors[s_tlocalKeyCounter] = destructor;
+    *key = s_tlocalKeyCounter++;
+
+    End_Int_Atomic(iflag);
+  
+    return 0;
+}
+
+/*
+ * Store a value for a thread-local item
+ */
+void Tlocal_Put(tlocal_key_t k, const void *v) 
+{
+    const void **pv;
+
+    KASSERT(k < s_tlocalKeyCounter);
+
+    pv = Get_Tlocal_Pointer(k);
+    *pv = v;
+}
+
+/*
+ * Acquire a thread-local value
+ */
+void *Tlocal_Get(tlocal_key_t k) 
+{
+    const void **pv;
+
+    KASSERT(k < s_tlocalKeyCounter);
+
+    pv = Get_Tlocal_Pointer(k);
+    return (void *)*pv;
+}
+
+/*
+ * Print list of all threads in system.
+ * For debugging.
+ */
+void Dump_All_Thread_List(void)
+{
+    struct Kernel_Thread *kthread;
+    int count = 0;
+    bool iflag = Begin_Int_Atomic();
+
+    kthread = Get_Front_Of_All_Thread_List(&s_allThreadList);
+
+    Print("[");
+    while (kthread != 0) {
+       ++count;
+       Print("<%lx,%lx,%lx>",
+           (ulong_t) Get_Prev_In_All_Thread_List(kthread),
+           (ulong_t) kthread,
+           (ulong_t) Get_Next_In_All_Thread_List(kthread));
+       KASSERT(kthread != Get_Next_In_All_Thread_List(kthread));
+       kthread = Get_Next_In_All_Thread_List(kthread);
+    }
+    Print("]\n");
+    Print("%d threads are running\n", count);
+
+    End_Int_Atomic(iflag);
+}
diff --git a/misc/test_vm/src/geekos/lowlevel.asm b/misc/test_vm/src/geekos/lowlevel.asm
new file mode 100644 (file)
index 0000000..63f93c2
--- /dev/null
@@ -0,0 +1,576 @@
+; -*- fundamental -*-
+; Low level interrupt/thread handling code for GeekOS.
+; Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software.  You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; This is 32 bit code to be linked into the kernel.
+; It defines low level interrupt handler entry points that we'll use
+; to populate the IDT.  It also contains the interrupt handling
+; and thread context switch code.
+
+%include "defs.asm"
+%include "symbol.asm"
+%include "util.asm"
+
+
+[BITS 32]
+
+; ----------------------------------------------------------------------
+; Definitions
+; ----------------------------------------------------------------------
+
+; This is the size of the Interrupt_State struct in int.h
+INTERRUPT_STATE_SIZE equ 64
+
+; Save registers prior to calling a handler function.
+; This must be kept up to date with:
+;   - Interrupt_State struct in int.h
+;   - Setup_Initial_Thread_Context() in kthread.c
+%macro Save_Registers 0
+       push    eax
+       push    ebx
+       push    ecx
+       push    edx
+       push    esi
+       push    edi
+       push    ebp
+       push    ds
+       push    es
+       push    fs
+       push    gs
+%endmacro
+
+; Restore registers and clean up the stack after calling a handler function
+; (i.e., just before we return from the interrupt via an iret instruction).
+%macro Restore_Registers 0
+       pop     gs
+       pop     fs
+       pop     es
+       pop     ds
+       pop     ebp
+       pop     edi
+       pop     esi
+       pop     edx
+       pop     ecx
+       pop     ebx
+       pop     eax
+       add     esp, 8  ; skip int num and error code
+%endmacro
+
+; Code to activate a new user context (if necessary), before returning
+; to executing a thread.  Should be called just before restoring
+; registers (because the interrupt context is used).
+%macro Activate_User_Context 0 
+       ; If the new thread has a user context which is not the current
+       ; one, activate it.
+       push    esp                     ; Interrupt_State pointer
+       push    dword [g_currentThread] ; Kernel_Thread pointer
+;      call    Switch_To_User_Context 
+       add     esp, 8                  ; clear 2 arguments
+%endmacro
+
+; Number of bytes between the top of the stack and
+; the interrupt number after the general-purpose and segment
+; registers have been saved.
+REG_SKIP equ (11*4)
+
+; Template for entry point code for interrupts that have
+; an explicit processor-generated error code.
+; The argument is the interrupt number.
+%macro Int_With_Err 1
+align 8
+       push    dword %1        ; push interrupt number
+       jmp     Handle_Interrupt ; jump to common handler
+%endmacro
+
+; Template for entry point code for interrupts that do not
+; generate an explicit error code.  We push a dummy error
+; code on the stack, so the stack layout is the same
+; for all interrupts.
+%macro Int_No_Err 1
+align 8
+       push    dword 0         ; fake error code
+       push    dword %1        ; push interrupt number
+       jmp     Handle_Interrupt ; jump to common handler
+%endmacro
+
+
+; ----------------------------------------------------------------------
+; Symbol imports and exports
+; ----------------------------------------------------------------------
+
+; This symbol is defined in idt.c, and is a table of addresses
+; of C handler functions for interrupts.
+IMPORT g_interruptTable
+
+; Global variable pointing to context struct for current thread.
+IMPORT g_currentThread
+
+; Set to non-zero when we need to choose a new thread
+; in the interrupt return code.
+IMPORT g_needReschedule
+
+; Set to non-zero when preemption is disabled.
+IMPORT g_preemptionDisabled
+
+; This is the function that returns the next runnable thread.
+IMPORT Get_Next_Runnable
+
+; Function to put a thread on the run queue.
+IMPORT Make_Runnable
+
+; Function to activate a new user context (if needed).
+IMPORT Switch_To_User_Context
+
+; Sizes of interrupt handler entry points for interrupts with
+; and without error codes.  The code in idt.c uses this
+; information to infer the layout of the table of interrupt
+; handler entry points, without needing a separate linker
+; symbol for each one (which is quite tedious to type :-)
+EXPORT g_handlerSizeNoErr
+EXPORT g_handlerSizeErr
+
+; Simple functions to load the IDTR, GDTR, and LDTR.
+EXPORT Load_IDTR
+EXPORT Load_GDTR
+EXPORT Load_LDTR
+
+; Beginning and end of the table of interrupt entry points.
+EXPORT g_entryPointTableStart
+EXPORT g_entryPointTableEnd
+
+; Thread context switch function.
+EXPORT Switch_To_Thread
+
+; Return current value of eflags register.
+EXPORT Get_Current_EFLAGS
+
+; Return the return address
+EXPORT Get_EIP
+; ESP
+EXPORT Get_ESP
+; EBP
+EXPORT Get_EBP
+
+; Virtual memory support.
+EXPORT Enable_Paging
+EXPORT Set_PDBR
+EXPORT Get_PDBR
+EXPORT Flush_TLB
+
+; CPUID functions
+EXPORT cpuid_ecx
+EXPORT cpuid_eax
+
+; Utility Functions
+EXPORT Set_MSR
+EXPORT Get_MSR
+
+EXPORT Get_CR2
+
+
+EXPORT Proc_test
+
+; ----------------------------------------------------------------------
+; Code
+; ----------------------------------------------------------------------
+
+[SECTION .text]
+
+; Load IDTR with 6-byte pointer whose address is passed as
+; the parameter.
+align 8
+Load_IDTR:
+       mov     eax, [esp+4]
+       lidt    [eax]
+       ret
+
+;  Load the GDTR with 6-byte pointer whose address is
+; passed as the parameter.  Assumes that interrupts
+; are disabled.
+align 8
+Load_GDTR:
+       mov     eax, [esp+4]
+       lgdt    [eax]
+       ; Reload segment registers
+       mov     ax, KERNEL_DS
+       mov     ds, ax
+       mov     es, ax
+       mov     fs, ax
+       mov     gs, ax
+       mov     ss, ax
+       jmp     KERNEL_CS:.here
+.here:
+       ret
+
+; Load the LDT whose selector is passed as a parameter.
+align 8
+Load_LDTR:
+       mov     eax, [esp+4]
+       lldt    ax
+       ret
+
+;
+; Start paging
+;      load crt3 with the passed page directory pointer
+;      enable paging bit in cr2
+align 8
+Enable_Paging:
+       push    ebp
+       mov     ebp, esp
+       push    eax
+       push    ebx
+       mov     eax, [ebp+8]
+       mov     cr3, eax
+       mov     eax, cr3
+       mov     cr3, eax
+       mov     ebx, cr0
+       or      ebx, 0x80000000
+       mov     cr0, ebx
+       pop     ebx
+       pop     eax
+       pop     ebp
+       ret
+
+
+
+
+       
+;
+; Change PDBR 
+;      load cr3 with the passed page directory pointer
+align 8
+Set_PDBR:
+       mov     eax, [esp+4]
+       mov     cr3, eax
+       ret
+
+;
+; Get the current PDBR.
+; This is useful for lazily switching address spaces;
+; only switch if the new thread has a different address space.
+;
+align 8
+Get_PDBR:
+       mov     eax, cr3
+       ret
+
+;
+; Flush TLB - just need to re-load cr3 to force this to happen
+;
+align 8
+Flush_TLB:
+       mov     eax, cr3
+       mov     cr3, eax
+       ret
+
+
+;
+; cpuid_ecx - return the ecx register from cpuid
+;
+align 8
+cpuid_ecx:
+       push    ebp
+       mov     ebp, esp
+       push    ecx
+       mov     eax, [ebp + 8]
+       cpuid
+       mov     eax, ecx
+       pop     ecx
+       pop     ebp
+       ret
+
+;
+; cpuid_eax - return the eax register from cpuid
+;
+align 8
+cpuid_eax:
+       mov     eax, [esp+4]
+       cpuid
+       ret
+
+;
+; Set_MSR  - Set the value of a given MSR
+;
+align 8
+Set_MSR:
+       push    ebp
+       mov     ebp, esp
+       pusha
+       mov     eax, [ebp+16]
+       mov     edx, [ebp+12]
+       mov     ecx, [ebp+8]
+       wrmsr
+       popa
+       pop     ebp
+       ret
+
+
+
+;
+; Get_MSR  -  Get the value of a given MSR
+; void Get_MSR(int MSR, void * high_byte, void * low_byte);
+;
+align 8
+Get_MSR:
+       push    ebp
+       mov     ebp, esp
+       pusha
+       mov     ecx, [ebp+8]
+       rdmsr
+       mov     ebx, [ebp+12]
+       mov     [ebx], edx
+       mov     ebx, [ebp+16]
+       mov     [ebx], eax
+       popa
+       pop     ebp
+       ret
+
+
+
+align 8
+Get_CR2:
+       mov     eax, cr2
+       ret
+
+
+align 8
+Proc_test:
+       push    ebp
+       mov     ebp, esp
+       push    ebx
+       mov     ebx, [ebp + 8]
+       mov     eax, [ebp + 12]
+
+       add     eax, ebx
+       pop     ebx
+       pop     ebp
+       ret
+
+
+; Common interrupt handling code.
+; Save registers, call C handler function,
+; possibly choose a new thread to run, restore
+; registers, return from the interrupt.
+align 8
+Handle_Interrupt:
+       ; Save registers (general purpose and segment)
+       Save_Registers
+
+       ; Ensure that we're using the kernel data segment
+       mov     ax, KERNEL_DS
+       mov     ds, ax
+       mov     es, ax
+
+       ; Get the address of the C handler function from the
+       ; table of handler functions.
+       mov     eax, g_interruptTable   ; get address of handler table
+       mov     esi, [esp+REG_SKIP]     ; get interrupt number
+       mov     ebx, [eax+esi*4]        ; get address of handler function
+
+       ; Call the handler.
+       ; The argument passed is a pointer to an Interrupt_State struct,
+       ; which describes the stack layout for all interrupts.
+       push    esp
+       call    ebx
+       add     esp, 4                  ; clear 1 argument
+
+       ; If preemption is disabled, then the current thread
+       ; keeps running.
+       cmp     [g_preemptionDisabled], dword 0
+       jne     .restore
+
+       ; See if we need to choose a new thread to run.
+       cmp     [g_needReschedule], dword 0
+       je      .restore
+
+       ; Put current thread back on the run queue
+       push    dword [g_currentThread]
+       call    Make_Runnable
+       add     esp, 4                  ; clear 1 argument
+
+       ; Save stack pointer in current thread context, and
+       ; clear numTicks field.
+       mov     eax, [g_currentThread]
+       mov     [eax+0], esp            ; esp field
+       mov     [eax+4], dword 0        ; numTicks field
+
+       ; Pick a new thread to run, and switch to its stack
+       call    Get_Next_Runnable
+       mov     [g_currentThread], eax
+       mov     esp, [eax+0]            ; esp field
+
+       ; Clear "need reschedule" flag
+       mov     [g_needReschedule], dword 0
+
+.restore:
+       ; Activate the user context, if necessary.
+       Activate_User_Context
+
+       ; Restore registers
+       Restore_Registers
+
+       ; Return from the interrupt.
+       iret
+
+; ----------------------------------------------------------------------
+; Switch_To_Thread()
+;   Save context of currently executing thread, and activate
+;   the thread whose context object is passed as a parameter.
+; 
+; Parameter: 
+;   - ptr to Kernel_Thread whose state should be restored and made active
+;
+; Notes:
+; Called with interrupts disabled.
+; This must be kept up to date with definition of Kernel_Thread
+; struct, in kthread.h.
+; ----------------------------------------------------------------------
+align 16
+Switch_To_Thread:
+       ; Modify the stack to allow a later return via an iret instruction.
+       ; We start with a stack that looks like this:
+       ;
+       ;            thread_ptr
+       ;    esp --> return addr
+       ;
+       ; We change it to look like this:
+       ;
+       ;            thread_ptr
+       ;            eflags
+       ;            cs
+       ;    esp --> return addr
+
+       push    eax             ; save eax
+       mov     eax, [esp+4]    ; get return address
+       mov     [esp-4], eax    ; move return addr down 8 bytes from orig loc
+       add     esp, 8          ; move stack ptr up
+       pushfd                  ; put eflags where return address was
+       mov     eax, [esp-4]    ; restore saved value of eax
+       push    dword KERNEL_CS ; push cs selector
+       sub     esp, 4          ; point stack ptr at return address
+
+       ; Push fake error code and interrupt number
+       push    dword 0
+       push    dword 0
+
+       ; Save general purpose registers.
+       Save_Registers
+
+       ; Save stack pointer in the thread context struct (at offset 0).
+       mov     eax, [g_currentThread]
+       mov     [eax+0], esp
+
+       ; Clear numTicks field in thread context, since this
+       ; thread is being suspended.
+       mov     [eax+4], dword 0
+
+       ; Load the pointer to the new thread context into eax.
+       ; We skip over the Interrupt_State struct on the stack to
+       ; get the parameter.
+       mov     eax, [esp+INTERRUPT_STATE_SIZE]
+
+       ; Make the new thread current, and switch to its stack.
+       mov     [g_currentThread], eax
+       mov     esp, [eax+0]
+
+       ; Activate the user context, if necessary.
+       Activate_User_Context
+
+       ; Restore general purpose and segment registers, and clear interrupt
+       ; number and error code.
+       Restore_Registers
+
+       ; We'll return to the place where the thread was
+       ; executing last.
+       iret
+
+; Return current contents of eflags register.
+align 16
+Get_Current_EFLAGS:
+       pushfd                  ; push eflags
+       pop     eax             ; pop contents into eax
+       ret
+
+
+
+; Return the eip of the next instruction after the caller
+align 16
+Get_EIP:
+       mov     eax, [esp]
+       ret
+
+; Return the current esp in the procedure
+align 16
+Get_ESP:
+       mov     eax, esp
+       ret
+
+; Return the current ebp in the procedure
+align 16
+Get_EBP:
+       mov     eax, ebp
+       ret
+
+
+
+; ----------------------------------------------------------------------
+; Generate interrupt-specific entry points for all interrupts.
+; We also define symbols to indicate the extend of the table
+; of entry points, and the size of individual entry points.
+; ----------------------------------------------------------------------
+align 8
+g_entryPointTableStart:
+
+; Handlers for processor-generated exceptions, as defined by
+; Intel 486 manual.
+Int_No_Err 0
+align 8
+Before_No_Err:
+Int_No_Err 1
+align 8
+After_No_Err:
+Int_No_Err 2   ; FIXME: not described in 486 manual
+Int_No_Err 3
+Int_No_Err 4
+Int_No_Err 5
+Int_No_Err 6
+Int_No_Err 7
+align 8
+Before_Err:
+Int_With_Err 8
+align 8
+After_Err:
+Int_No_Err 9   ; FIXME: not described in 486 manual
+Int_With_Err 10
+Int_With_Err 11
+Int_With_Err 12
+Int_With_Err 13
+Int_With_Err 14
+Int_No_Err 15  ; FIXME: not described in 486 manual
+Int_No_Err 16
+Int_With_Err 17
+
+; The remaining interrupts (18 - 255) do not have error codes.
+; We can generate them all in one go with nasm's %rep construct.
+%assign intNum 18
+%rep (256 - 18)
+Int_No_Err intNum
+%assign intNum intNum+1
+%endrep
+
+align 8
+g_entryPointTableEnd:
+
+[SECTION .data]
+
+; Exported symbols defining the size of handler entry points
+; (both with and without error codes).
+align 4
+g_handlerSizeNoErr: dd (After_No_Err - Before_No_Err)
+align 4
+g_handlerSizeErr: dd (After_Err - Before_Err)
diff --git a/misc/test_vm/src/geekos/main.c b/misc/test_vm/src/geekos/main.c
new file mode 100644 (file)
index 0000000..80283a4
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * GeekOS C code entry point
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2004, Iulian Neamtiu <neamtiu@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/bootinfo.h>
+#include <geekos/string.h>
+#include <geekos/screen.h>
+#include <geekos/mem.h>
+#include <geekos/crc32.h>
+#include <geekos/tss.h>
+#include <geekos/int.h>
+#include <geekos/kthread.h>
+#include <geekos/trap.h>
+#include <geekos/timer.h>
+#include <geekos/keyboard.h>
+#include <geekos/io.h>
+#include <geekos/serial.h>
+#include <geekos/reboot.h>
+#include <geekos/mem.h>
+#include <geekos/paging.h>
+#include <geekos/ide.h>
+
+#include <geekos/gdt.h>
+
+
+/*
+  static inline unsigned int cpuid_ecx(unsigned int op)
+  {
+  unsigned int eax, ecx;
+  
+  __asm__("cpuid"
+  : "=a" (eax), "=c" (ecx)
+  : "0" (op)
+  : "bx", "dx" );
+  
+  return ecx;
+  }
+*/
+
+
+
+extern void Get_MSR(ulong_t msr, unsigned int *val1, unsigned int *val2);
+extern void Set_MSR(ulong_t msr, ulong_t val1, ulong_t val2);
+extern uint_t Get_EIP();
+extern uint_t Get_ESP();
+extern uint_t Get_EBP();
+
+
+int foo=42;
+
+#define SPEAKER_PORT 0x61
+
+
+void Buzz(unsigned delay, unsigned num)
+{
+  volatile int x;
+  int i,j;
+  unsigned char init;
+  
+  init=In_Byte(SPEAKER_PORT);
+
+  for (i=0;i<num;i++) { 
+    Out_Byte(SPEAKER_PORT, init|0x2);
+    for (j=0;j<delay;j++) { 
+      x+=j;
+    }
+    Out_Byte(SPEAKER_PORT, init);
+    for (j=0;j<delay;j++) { 
+      x+=j;
+    }
+  }
+}
+
+inline void MyOut_Byte(ushort_t port, uchar_t value)
+{
+    __asm__ __volatile__ (
+       "outb %b0, %w1"
+       :
+       : "a" (value), "Nd" (port)
+    );
+}
+
+/*
+ * Read a byte from an I/O port.
+ */
+inline uchar_t MyIn_Byte(ushort_t port)
+{
+    uchar_t value;
+
+    __asm__ __volatile__ (
+       "inb %w1, %b0"
+       : "=a" (value)
+       : "Nd" (port)
+    );
+
+    return value;
+}
+
+
+extern void MyBuzzVM();
+
+#define MYBUZZVM_START MyBuzzVM
+#define MYBUZZVM_LEN   0x3d
+
+void BuzzVM()
+{
+  int x;
+  int j;
+  unsigned char init;
+  
+  
+  init=MyIn_Byte(SPEAKER_PORT);
+
+  while (1) {
+    MyOut_Byte(SPEAKER_PORT, init|0x2);
+    for (j=0;j<1000000;j++) { 
+      x+=j;
+    }
+    MyOut_Byte(SPEAKER_PORT, init);
+    for (j=0;j<1000000;j++) { 
+      x+=j;
+    }
+  }
+}
+
+
+
+extern void RunVM();
+
+int vmRunning = 0;
+
+void RunVM() {
+  vmRunning = 1;
+
+  while(1);
+}
+
+
+
+
+
+void Buzzer(ulong_t arg) {
+  ulong_t *doIBuzz = (ulong_t*)arg;
+  while (1) {
+    // Quick and dirty hack to save my hearing...
+    // I'm not too worried about timing, so I'll deal with concurrency later...
+    if (*doIBuzz == 1) {
+      Buzz(1000000, 10);
+    }
+  }
+
+}
+
+
+
+void Hello(ulong_t arg)
+{
+  char *b="hello ";
+  char byte;
+  short port=0xe9;
+  int i;
+  while(1){
+    for (i=0;i<6;i++) { 
+      byte=b[i];
+      __asm__ __volatile__ ("outb %b0, %w1" : : "a"(byte), "Nd"(port) );
+    }
+  }
+}
+
+void Keyboard_Listener(ulong_t arg) {
+  ulong_t * doIBuzz = (ulong_t*)arg;
+  Keycode key_press;
+
+  Print("Press F4 to turn on/off the speaker\n");
+
+  while ((key_press = Wait_For_Key())) {    
+    if (key_press == KEY_F4) {
+      Print("\nToggling Speaker Port\n");
+      SerialPrintLevel(100,"\nToggling Speaker Port\n");
+      *doIBuzz = (*doIBuzz + 1) % 2;
+    } else if (key_press == KEY_F5) {
+      Print("\nMachine Restart\n");
+      SerialPrintLevel(100,"\nMachine Restart\n");
+      machine_real_restart();
+    }
+  }
+  return;
+}
+
+
+
+extern char BSS_START, BSS_END;
+
+extern char end;
+
+
+
+
+int AllocateAndMapPagesForRange(uint_t start, uint_t length, pte_t template_pte)
+{
+  uint_t address;
+
+  for (address=start;address<start+length;address+=PAGE_SIZE) { 
+    void *page;
+    pte_t pte = template_pte;
+    
+    page=Alloc_Page();
+    KASSERT(page);
+    
+    pte.pageBaseAddr=PAGE_ALLIGNED_ADDR(page);
+
+    KASSERT(MapPage((void*)address,&pte,1));
+  }
+  
+  return 0;
+}
+    
+
+
+/*
+ * Kernel C code entry point.
+ * Initializes kernel subsystems, mounts filesystems,
+ * and spawns init process.
+ */
+void Main(struct Boot_Info* bootInfo)
+{
+  struct Kernel_Thread * key_thread;
+  struct Kernel_Thread * spkr_thread;
+
+
+
+  ulong_t doIBuzz = 0;
+
+  Init_BSS();
+  Init_Screen();
+  InitSerial();
+  Init_Mem(bootInfo);
+  Init_CRC32();
+  Init_TSS();
+  Init_Interrupts();
+  Init_Scheduler();
+  Init_Traps();
+  Init_Timer();
+  Init_Keyboard();
+  Init_VM(bootInfo);
+  Init_Paging();
+
+  //  Init_IDE();
+
+
+
+
+
+
+
+
+  SerialPrint("\n\nHello, Welcome to this horrid output-only serial interface\n");
+  SerialPrint("Eventually, this will let us control the VMM\n\n");
+  SerialPrint("\n\n===>");
+  
+
+
+  
+
+  
+  SerialPrintLevel(1000,"Launching Noisemaker and keyboard listener threads\n");
+  
+  key_thread = Start_Kernel_Thread(Keyboard_Listener, (ulong_t)&doIBuzz, PRIORITY_NORMAL, false);
+  spkr_thread = Start_Kernel_Thread(Buzzer, (ulong_t)&doIBuzz, PRIORITY_NORMAL, false);
+
+
+
+
+
+  SerialPrintLevel(1000,"Next: setup GDT\n");
+
+
+  while(1);
+  
+  /* Now this thread is done. */
+  Exit(0);
+}
diff --git a/misc/test_vm/src/geekos/malloc.c b/misc/test_vm/src/geekos/malloc.c
new file mode 100644 (file)
index 0000000..ea76959
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * GeekOS memory allocation API
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/bget.h>
+#include <geekos/kassert.h>
+#include <geekos/malloc.h>
+
+/*
+ * Initialize the heap starting at given address and occupying
+ * specified number of bytes.
+ */
+void Init_Heap(ulong_t start, ulong_t size)
+{
+    /*Print("Creating kernel heap: start=%lx, size=%ld\n", start, size);*/
+    bpool((void*) start, size);
+}
+
+/*
+ * Dynamically allocate a buffer of given size.
+ * Returns null if there is not enough memory to satisfy the
+ * allocation.
+ */
+void* Malloc(ulong_t size)
+{
+    void *result;
+    bool iflag;
+
+    KASSERT(size > 0);
+
+    iflag = Begin_Int_Atomic();
+    result = bget(size);
+    End_Int_Atomic(iflag);
+
+    return result;
+}
+
+/*
+ * Free a buffer allocated with Malloc() or Malloc().
+ */
+void Free(void* buf)
+{
+    bool iflag;
+
+    iflag = Begin_Int_Atomic();
+    brel(buf);
+    End_Int_Atomic(iflag);
+}
diff --git a/misc/test_vm/src/geekos/mem.c b/misc/test_vm/src/geekos/mem.c
new file mode 100644 (file)
index 0000000..2f44520
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/defs.h>
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/bootinfo.h>
+#include <geekos/gdt.h>
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/paging.h>
+#include <geekos/mem.h>
+
+/* ----------------------------------------------------------------------
+ * Global data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * List of Page structures representing each page of physical memory.
+ */
+struct Page* g_pageList;
+
+/*
+ * Number of pages currently available on the freelist.
+ */
+uint_t g_freePageCount = 0;
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Defined in paging.c
+ */
+extern int debugFaults;
+#define Debug(args...) if (debugFaults) Print(args)
+
+/*
+ * List of pages available for allocation.
+ */
+static struct Page_List s_freeList;
+
+/*
+ * Total number of physical pages.
+ */
+int unsigned s_numPages;
+
+/*
+ * Add a range of pages to the inventory of physical memory.
+ */
+static void Add_Page_Range(ulong_t start, ulong_t end, int flags)
+{
+    ulong_t addr;
+
+    KASSERT(Is_Page_Multiple(start));
+    KASSERT(Is_Page_Multiple(end));
+    KASSERT(start < end);
+
+    for (addr = start; addr < end; addr += PAGE_SIZE) {
+       struct Page *page = Get_Page(addr);
+
+       page->flags = flags;
+
+       if (flags == PAGE_AVAIL) {
+           /* Add the page to the freelist */
+           Add_To_Back_Of_Page_List(&s_freeList, page);
+
+           /* Update free page count */
+           ++g_freePageCount;
+       } else {
+           Set_Next_In_Page_List(page, 0);
+           Set_Prev_In_Page_List(page, 0);
+       }
+
+       page->clock = 0;
+       page->vaddr = 0;
+       page->entry = 0;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * The linker defines this symbol to indicate the end of
+ * the executable image.
+ */
+extern char end;
+
+/*
+ * Initialize memory management data structures.
+ * Enables the use of Alloc_Page() and Free_Page() functions.
+ */
+void Init_Mem(struct Boot_Info* bootInfo)
+{
+    ulong_t numPages = bootInfo->memSizeKB >> 2;
+    ulong_t endOfMem = numPages * PAGE_SIZE;
+    unsigned numPageListBytes = sizeof(struct Page) * numPages;
+    ulong_t pageListAddr;
+    ulong_t kernEnd;
+
+    KASSERT(bootInfo->memSizeKB > 0);
+
+    /*
+     * Before we do anything, switch from setup.asm's temporary GDT
+     * to the kernel's permanent GDT.
+     */
+    Init_GDT();
+
+    /*
+     * We'll put the list of Page objects right after the end
+     * of the kernel, and mark it as "kernel".  This will bootstrap
+     * us sufficiently that we can start allocating pages and
+     * keeping track of them.
+     */
+    pageListAddr = Round_Up_To_Page((ulong_t) &end);
+    g_pageList = (struct Page*) pageListAddr;
+    kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
+    s_numPages = numPages;
+
+    
+
+    /*
+     * The initial kernel thread and its stack are placed
+     * just beyond the ISA hole.
+     */
+    //KASSERT(ISA_HOLE_END == KERN_THREAD_OBJ);
+    //KASSERT(KERN_STACK == KERN_THREAD_OBJ + PAGE_SIZE);
+
+    /*
+     * Memory looks like this:
+     * 0 - start: available (might want to preserve BIOS data area)
+     * start - end: kernel
+     * end - ISA_HOLE_START: available
+     * ISA_HOLE_START - ISA_HOLE_END: used by hardware (and ROM BIOS?)
+     * ISA_HOLE_END - HIGHMEM_START: used by initial kernel thread
+     * HIGHMEM_START - end of memory: available
+     *    (the kernel heap is located at HIGHMEM_START; any unused memory
+     *    beyond that is added to the freelist)
+     */
+
+    /* JRL CHANGE
+     * 0 - PAGE_SIZE: Unused
+     * PAGE_SIZE - (PAGE_SIZE+8192): init kernel thread stack
+     * (PAGE_SIZE+8192) - ISA_HOLE_START: Available
+     * ISA_HOLE_START - ISA_HOLE_END: HW
+     * ISA_HOLE_END - end: Kernel+PageList
+     * end - (end+1MB): heap
+     * (end+1MB) - end of mem: free
+     * 
+     * 
+     */
+
+
+    Add_Page_Range(0, PAGE_SIZE, PAGE_UNUSED);
+    //    Add_Page_Range(PAGE_SIZE, KERNEL_START_ADDR, PAGE_AVAIL);
+    //Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN);
+    //Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL);
+    Add_Page_Range(PAGE_SIZE, PAGE_SIZE + 8192, PAGE_ALLOCATED);
+    Add_Page_Range(PAGE_SIZE + 8192, ISA_HOLE_START, PAGE_AVAIL);
+    Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW);
+    //Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED);
+    Add_Page_Range(HIGHMEM_START, kernEnd, PAGE_KERN);
+    Add_Page_Range(kernEnd, kernEnd + KERNEL_HEAP_SIZE, PAGE_HEAP);
+
+    Add_Page_Range(kernEnd + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+
+    //    Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP);
+    //    Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+
+    /* Initialize the kernel heap */
+    //    Init_Heap(HIGHMEM_START, KERNEL_HEAP_SIZE);
+    Init_Heap(kernEnd, KERNEL_HEAP_SIZE);
+
+    Print("%uKB memory detected, %u pages in freelist, %d bytes in kernel heap\n",
+       bootInfo->memSizeKB, g_freePageCount, KERNEL_HEAP_SIZE);
+}
+
+/*
+ * Initialize the .bss section of the kernel executable image.
+ */
+void Init_BSS(void)
+{
+    extern char BSS_START, BSS_END;
+
+    /* Fill .bss with zeroes */
+    memset(&BSS_START, '\0', &BSS_END - &BSS_START);
+}
+
+/*
+ * Allocate a page of physical memory.
+ */
+void* Alloc_Page(void)
+{
+    struct Page* page;
+    void *result = 0;
+
+    bool iflag = Begin_Int_Atomic();
+
+    /* See if we have a free page */
+    if (!Is_Page_List_Empty(&s_freeList)) {
+       /* Remove the first page on the freelist. */
+       page = Get_Front_Of_Page_List(&s_freeList);
+       KASSERT((page->flags & PAGE_ALLOCATED) == 0);
+       Remove_From_Front_Of_Page_List(&s_freeList);
+
+       /* Mark page as having been allocated. */
+       page->flags |= PAGE_ALLOCATED;
+       g_freePageCount--;
+       result = (void*) Get_Page_Address(page);
+    }
+
+    End_Int_Atomic(iflag);
+
+    return result;
+}
+
+/*
+ * Choose a page to evict.
+ * Returns null if no pages are available.
+ */
+static struct Page *Find_Page_To_Page_Out()
+{
+    int i;
+    struct Page *curr, *best;
+
+    best = NULL;
+
+    for (i=0; i < s_numPages; i++) {
+       if ((g_pageList[i].flags & PAGE_PAGEABLE) &&
+           (g_pageList[i].flags & PAGE_ALLOCATED)) {
+           if (!best) best = &g_pageList[i];
+           curr = &g_pageList[i];
+           if ((curr->clock < best->clock) && (curr->flags & PAGE_PAGEABLE)) {
+               best = curr;
+           }
+       }
+    }
+
+    return best;
+}
+
+/**
+ * Allocate a page of pageable physical memory, to be mapped
+ * into a user address space.
+ *
+ * @param entry pointer to user page table entry which will
+ *   refer to the allocated page
+ * @param vaddr virtual address where page will be mapped
+ *   in user address space
+ */
+void* Alloc_Pageable_Page(pte_t *entry, ulong_t vaddr)
+{
+    bool iflag;
+    void* paddr = 0;
+    struct Page* page = 0;
+
+    iflag = Begin_Int_Atomic();
+
+    KASSERT(!Interrupts_Enabled());
+    KASSERT(Is_Page_Multiple(vaddr));
+
+    paddr = Alloc_Page();
+    if (paddr != 0) {
+       page = Get_Page((ulong_t) paddr);
+       KASSERT((page->flags & PAGE_PAGEABLE) == 0);
+    } else {
+       int pagefileIndex;
+
+        /* Select a page to steal from another process */
+       Debug("About to hunt for a page to page out\n");
+       page = Find_Page_To_Page_Out();
+       KASSERT(page->flags & PAGE_PAGEABLE);
+       paddr = (void*) Get_Page_Address(page);
+       Debug("Selected page at addr %p (age = %d)\n", paddr, page->clock);
+
+       /* Find a place on disk for it */
+       pagefileIndex = Find_Space_On_Paging_File();
+       if (pagefileIndex < 0)
+           /* No space available in paging file. */
+           goto done;
+       Debug("Free disk page at index %d\n", pagefileIndex);
+
+       /* Make the page temporarily unpageable (can't let another process steal it) */
+       page->flags &= ~(PAGE_PAGEABLE);
+
+       /* Lock the page so it cannot be freed while we're writing */
+        page->flags |= PAGE_LOCKED;
+
+       /* Write the page to disk. Interrupts are enabled, since the I/O may block. */
+       Debug("Writing physical frame %p to paging file at %d\n", paddr, pagefileIndex);
+       Enable_Interrupts();
+       Write_To_Paging_File(paddr, page->vaddr, pagefileIndex);
+       Disable_Interrupts();
+
+        /* While we were writing got notification this page isn't even needed anymore */
+        if (page->flags & PAGE_ALLOCATED)
+        {
+           /* The page is still in use update its bookeping info */
+           /* Update page table to reflect the page being on disk */
+           page->entry->present = 0;
+           page->entry->kernelInfo = KINFO_PAGE_ON_DISK;
+           page->entry->pageBaseAddr = pagefileIndex; /* Remember where it is located! */
+        }
+        else
+        {
+           /* The page got freed, don't need bookeeping or it on disk */
+           Free_Space_On_Paging_File(pagefileIndex);
+
+           /* Its still allocated though to us now */
+           page->flags |= PAGE_ALLOCATED;
+        }
+
+        /* Unlock the page */
+        page->flags &= ~(PAGE_LOCKED);
+
+       /* XXX - flush TLB should only flush the one page */
+       Flush_TLB();
+    }
+
+    /* Fill in accounting information for page */
+    page->flags |= PAGE_PAGEABLE;
+    page->entry = entry;
+    page->entry->kernelInfo = 0;
+    page->vaddr = vaddr;
+    KASSERT(page->flags & PAGE_ALLOCATED);
+
+done:
+    End_Int_Atomic(iflag);
+    return paddr;
+}
+
+/*
+ * Free a page of physical memory.
+ */
+void Free_Page(void* pageAddr)
+{
+    ulong_t addr = (ulong_t) pageAddr;
+    struct Page* page;
+    bool iflag;
+
+    iflag = Begin_Int_Atomic();
+
+    KASSERT(Is_Page_Multiple(addr));
+
+    /* Get the Page object for this page */
+    page = Get_Page(addr);
+    KASSERT((page->flags & PAGE_ALLOCATED) != 0);
+
+    /* Clear the allocation bit */
+    page->flags &= ~(PAGE_ALLOCATED);
+
+    /* When a page is locked, don't free it just let other thread know its not needed */
+    if (page->flags & PAGE_LOCKED)
+      return;
+
+    /* Clear the pageable bit */
+    page->flags &= ~(PAGE_PAGEABLE);
+
+    /* Put the page back on the freelist */
+    Add_To_Back_Of_Page_List(&s_freeList, page);
+    g_freePageCount++;
+
+    End_Int_Atomic(iflag);
+}
diff --git a/misc/test_vm/src/geekos/mem_bak.c b/misc/test_vm/src/geekos/mem_bak.c
new file mode 100644 (file)
index 0000000..6b7557b
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/defs.h>
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/bootinfo.h>
+#include <geekos/gdt.h>
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/mem.h>
+
+
+#include <geekos/serial.h>
+
+/* ----------------------------------------------------------------------
+ * Global data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * List of Page structures representing each page of physical memory.
+ */
+struct Page* g_pageList;
+
+/*
+ * Number of pages currently available on the freelist.
+ */
+uint_t g_freePageCount = 0;
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Defined in paging.c
+ */
+extern int debugFaults;
+#define Debug(args...) if (debugFaults) Print(args)
+
+/*
+ * List of pages available for allocation.
+ */
+static struct Page_List s_freeList;
+
+/*
+ * Total number of physical pages.
+ */
+int unsigned s_numPages;
+
+/*
+ * Add a range of pages to the inventory of physical memory.
+ */
+static void Add_Page_Range(ulong_t start, ulong_t end, int flags)
+{
+    ulong_t addr;
+
+    PrintBoth("Start: %u, End: %u\n", (unsigned int)start, (unsigned int)end);
+
+    KASSERT(Is_Page_Multiple(start));
+    KASSERT(Is_Page_Multiple(end));
+    KASSERT(start < end);
+
+    //Print("Adding %lu pages\n", (end - start) / PAGE_SIZE);
+
+    for (addr = start; addr < end; addr += PAGE_SIZE) {
+      //      Print("Adding Page at %u\n", (unsigned int)addr);
+       struct Page *page = Get_Page(addr);
+
+       page->flags = flags;
+
+       if (flags == PAGE_AVAIL) {
+           /* Add the page to the freelist */
+           Add_To_Back_Of_Page_List(&s_freeList, page);
+
+           /* Update free page count */
+           ++g_freePageCount;
+       } else {
+           Set_Next_In_Page_List(page, 0);
+           Set_Prev_In_Page_List(page, 0);
+       }
+
+    }
+    //   Print("%d pages now in freelist\n", g_freePageCount);
+
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * The linker defines this symbol to indicate the end of
+ * the executable image.
+ */
+extern char end;
+
+/*
+ * Initialize memory management data structures.
+ * Enables the use of Alloc_Page() and Free_Page() functions.
+ */
+void Init_Mem(struct Boot_Info* bootInfo)
+{
+    ulong_t numPages = bootInfo->memSizeKB >> 2;
+    ulong_t endOfMem = numPages * PAGE_SIZE;
+    unsigned numPageListBytes = sizeof(struct Page) * numPages;
+    ulong_t pageListAddr;
+    ulong_t pageListEnd;
+    ulong_t kernEnd;
+
+
+    KASSERT(bootInfo->memSizeKB > 0);
+
+    if (bootInfo->memSizeKB != TOP_OF_MEM/1024) { 
+      PrintBoth("Kernel compiled for %d KB machine, but machine claims %d KB\n",TOP_OF_MEM/1024,bootInfo->memSizeKB);
+      if (bootInfo->memSizeKB < TOP_OF_MEM/1024) { 
+       PrintBoth("Kernel compiled for more memory than machine has.  Panicking\n");
+       KASSERT(0);
+      }
+    }
+
+
+    bootInfo->memSizeKB = TOP_OF_MEM / 1024;
+
+    /*
+     * Before we do anything, switch from setup.asm's temporary GDT
+     * to the kernel's permanent GDT.
+     */
+    Init_GDT();
+
+    /*
+     * We'll put the list of Page objects right after the end
+     * of the kernel, and mark it as "kernel".  This will bootstrap
+     * us sufficiently that we can start allocating pages and
+     * keeping track of them.
+     */
+
+    // JRL: This is stupid... 
+    // with large mem sizes the page list overruns into the ISA 
+    // hole. By blind luck this causes an unrelated assertion failure, otherwise
+    // I might never have caught it...
+    // We fix it by moving the page list after the kernel heap...
+    // For now we'll make our own stupid assumption that the mem size
+    // is large enough to accomodate the list in high mem.
+
+    PrintBoth("Total Memory Size: %u MBytes\n", bootInfo->memSizeKB/1024);
+
+    
+
+    PrintBoth("Page struct size: %u bytes\n", sizeof(struct Page));
+    PrintBoth("Page List Size: %u bytes\n", numPageListBytes);
+
+  
+    //pageListAddr = Round_Up_To_Page((ulong_t) &end);
+    //pageListAddr = Round_Up_To_Page(HIGHMEM_START + KERNEL_HEAP_SIZE);
+
+    // Note that this is now moved to be just above the kernel heap
+    // see defs.h for layout
+    pageListAddr=Round_Up_To_Page(KERNEL_PAGELIST);
+      
+    pageListEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
+
+    g_pageList = (struct Page*) pageListAddr;
+    //    kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
+    //
+    // PAD - Note: I am changing this so that everything through the end of 
+    // the VM boot package (bioses/vmxassist) is off limits
+    //kernEnd = Round_Up_To_Page((ulong_t) &end);
+    kernEnd = Round_Up_To_Page(end);
+    s_numPages = numPages;
+
+    PrintBoth("Pagelist addr: %p\n", g_pageList);
+    PrintBoth("index: %p\n", &g_pageList[3]);
+    PrintBoth("direct offset: %p\n", g_pageList + (sizeof(struct Page) * 2));
+    //  PrintBoth("Kernel Size=%lx\n", (kernEnd - KERNEL_START_ADDR));
+    // PrintBoth("Kernel Start=%x\n", KERNEL_START_ADDR);
+    PrintBoth("Kernel End=%lx\n", kernEnd);
+    //PrintBoth("end=%x\n", end);
+
+
+    /*
+     * The initial kernel thread and its stack are placed
+     * just beyond the ISA hole.
+     */
+    // This is no longer true
+    // KASSERT(ISA_HOLE_END == KERN_THREAD_OBJ);
+    // instead, 
+    //KASSERT(KERN_THREAD_OBJ==(START_OF_VM+VM_SIZE));
+    //KASSERT(KERN_STACK == KERN_THREAD_OBJ + PAGE_SIZE);
+
+    /*
+     * Memory looks like this:
+     * 0 - start: available (might want to preserve BIOS data area)
+     * start - end: kernel
+     * end - ISA_HOLE_START: available
+     * ISA_HOLE_START - ISA_HOLE_END: used by hardware (and ROM BIOS?)
+     * ISA_HOLE_END - HIGHMEM_START: used by initial kernel thread
+     * HIGHMEM_START - end of memory: available
+     *    (the kernel heap is located at HIGHMEM_START; any unused memory
+     *    beyond that is added to the freelist)
+     */
+
+  
+
+
+    // The kernel is still in low memory at this point, in the VM region
+    // Thus we will mark it as kernel use
+    // Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN);
+    
+    
+    //Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL);
+    // ISA hole remains closed (no actual memory)
+    // Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW);
+    
+    //Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED);
+    // Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP);
+    //Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+    /* JRL: move page list after kernel heap */
+    
+    //Now, above the VM region...
+
+    // Kernel thread object
+    Add_Page_Range(KERNEL_THREAD_OBJECT,KERNEL_THREAD_OBJECT+KERNEL_THREAD_OBJECT_SIZE,PAGE_ALLOCATED);
+    // Kernel stack
+    Add_Page_Range(KERNEL_STACK,KERNEL_STACK+KERNEL_STACK_SIZE,PAGE_ALLOCATED);
+    // Kernel heap
+    Add_Page_Range(KERNEL_HEAP,KERNEL_HEAP+KERNEL_HEAP_SIZE,PAGE_HEAP);
+    // Kernel page list
+    Add_Page_Range(pageListAddr, pageListEnd, PAGE_KERN);
+    // Free space
+    Add_Page_Range(pageListEnd,Round_Down_To_Page(FINAL_KERNEL_START), PAGE_AVAIL);
+    // The kernel 
+    Add_Page_Range(Round_Down_To_Page(FINAL_KERNEL_START),Round_Up_To_Page(FINAL_VMBOOTEND+1),PAGE_KERN);
+    // The vmbootpackage 
+    // IDT (this should be one page)
+    Add_Page_Range(IDT_LOCATION,TSS_LOCATION,PAGE_KERN);
+    // TSS (this should be one page)
+    Add_Page_Range(TSS_LOCATION,GDT_LOCATION, PAGE_KERN);
+    // GDT (this should be one page)
+    Add_Page_Range(GDT_LOCATION,TOP_OF_MEM, PAGE_KERN);
+
+    /* Initialize the kernel heap */
+    Init_Heap(KERNEL_HEAP, KERNEL_HEAP_SIZE);
+
+    PrintBoth("%uKB memory detected, %u pages in freelist, %d bytes in kernel heap\n",
+       bootInfo->memSizeKB, g_freePageCount, KERNEL_HEAP_SIZE);
+
+    PrintBoth("Memory Layout:\n");
+
+    PrintBoth("%x to %x - INITIAL THREAD\n",KERNEL_THREAD_OBJECT,KERNEL_THREAD_OBJECT+KERNEL_THREAD_OBJECT_SIZE-1);
+    PrintBoth("%x to %x - KERNEL STACK\n",KERNEL_STACK,KERNEL_STACK+KERNEL_STACK_SIZE-1);
+    PrintBoth("%x to %x - KERNEL HEAP\n",KERNEL_HEAP,KERNEL_HEAP+KERNEL_HEAP_SIZE-1);
+    PrintBoth("%lx to %lx - PAGE LIST\n",pageListAddr,pageListEnd-1);
+    PrintBoth("%lx to %x - FREE\n",pageListEnd,FINAL_KERNEL_START-1);
+    PrintBoth("%x to %x - KERNEL CODE\n",FINAL_KERNEL_START,FINAL_KERNEL_END);
+    PrintBoth("%x to %x - IDT\n",IDT_LOCATION,TSS_LOCATION-1);
+    PrintBoth("%x to %x - TSS\n",TSS_LOCATION,GDT_LOCATION-1);
+    PrintBoth("%x to %x - GDT\n",GDT_LOCATION,TOP_OF_MEM-1);
+
+
+}
+
+/*
+ * Initialize the .bss section of the kernel executable image.
+ */
+void Init_BSS(void)
+{
+    extern char BSS_START, BSS_END;
+
+    /* Fill .bss with zeroes */
+    memset(&BSS_START, '\0', &BSS_END - &BSS_START);
+    PrintBoth("BSS Inited, BSS_START=%x, BSS_END=%x\n",BSS_START,BSS_END);
+}
+
+/*
+ * Allocate a page of physical memory.
+ */
+void* Alloc_Page(void)
+{
+    struct Page* page;
+    void *result = 0;
+
+    bool iflag = Begin_Int_Atomic();
+
+    /* See if we have a free page */
+    if (!Is_Page_List_Empty(&s_freeList)) {
+       /* Remove the first page on the freelist. */
+       page = Get_Front_Of_Page_List(&s_freeList);
+       KASSERT((page->flags & PAGE_ALLOCATED) == 0);
+       Remove_From_Front_Of_Page_List(&s_freeList);
+
+       /* Mark page as having been allocated. */
+       page->flags |= PAGE_ALLOCATED;
+       g_freePageCount--;
+       result = (void*) Get_Page_Address(page);
+    }
+
+    End_Int_Atomic(iflag);
+
+    return result;
+}
+
+/*
+ * Free a page of physical memory.
+ */
+void Free_Page(void* pageAddr)
+{
+    ulong_t addr = (ulong_t) pageAddr;
+    struct Page* page;
+    bool iflag;
+
+    iflag = Begin_Int_Atomic();
+
+    KASSERT(Is_Page_Multiple(addr));
+
+    /* Get the Page object for this page */
+    page = Get_Page(addr);
+    KASSERT((page->flags & PAGE_ALLOCATED) != 0);
+
+    /* Clear the allocation bit */
+    page->flags &= ~(PAGE_ALLOCATED);
+
+    /* Put the page back on the freelist */
+    Add_To_Back_Of_Page_List(&s_freeList, page);
+    g_freePageCount++;
+
+    End_Int_Atomic(iflag);
+}
diff --git a/misc/test_vm/src/geekos/paging.c b/misc/test_vm/src/geekos/paging.c
new file mode 100644 (file)
index 0000000..b2837dc
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Paging (virtual memory) support
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/string.h>
+#include <geekos/int.h>
+#include <geekos/idt.h>
+#include <geekos/kthread.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/mem.h>
+#include <geekos/malloc.h>
+#include <geekos/gdt.h>
+#include <geekos/segment.h>
+//#include <geekos/user.h>
+//#include <geekos/vfs.h>
+#include <geekos/crc32.h>
+#include <geekos/paging.h>
+#include <geekos/serial.h>
+
+
+/* ----------------------------------------------------------------------
+ * Public data
+ * ---------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+ * Private functions/data
+ * ---------------------------------------------------------------------- */
+
+#define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
+
+/*
+ * flag to indicate if debugging paging code
+ */
+int debugFaults = 0;
+#define Debug(args...) if (debugFaults) Print(args)
+
+
+
+void SerialPrintPD(pde_t *pde)
+{
+  int i;
+
+  SerialPrint("Page Directory at %p:\n",pde);
+  for (i=0;i<NUM_PAGE_DIR_ENTRIES && pde[i].present;i++) { 
+    SerialPrintPDE((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),&(pde[i]));
+  }
+}
+
+void SerialPrintPT(void *starting_address, pte_t *pte) 
+{
+  int i;
+
+  SerialPrint("Page Table at %p:\n",pte);
+  for (i=0;i<NUM_PAGE_TABLE_ENTRIES && pte[i].present;i++) { 
+    SerialPrintPTE(starting_address + PAGE_SIZE*i,&(pte[i]));
+  }
+}
+
+
+void SerialPrintPDE(void *virtual_address, pde_t *pde)
+{
+  SerialPrint("PDE %p -> %p : present=%x, flags=%x, accessed=%x, reserved=%x, largePages=%x, globalPage=%x, kernelInfo=%x\n",
+             virtual_address,
+             (void*) (pde->pageTableBaseAddr << PAGE_POWER),
+             pde->present,
+             pde->flags,
+             pde->accessed,
+             pde->reserved,
+             pde->largePages,
+             pde->globalPage,
+             pde->kernelInfo);
+}
+  
+void SerialPrintPTE(void *virtual_address, pte_t *pte)
+{
+  SerialPrint("PTE %p -> %p : present=%x, flags=%x, accessed=%x, dirty=%x, pteAttribute=%x, globalPage=%x, kernelInfo=%x\n",
+             virtual_address,
+             (void*)(pte->pageBaseAddr << PAGE_POWER),
+             pte->present,
+             pte->flags,
+             pte->accessed,
+             pte->dirty,
+             pte->pteAttribute,
+             pte->globalPage,
+             pte->kernelInfo);
+}
+
+
+void SerialDumpPageTables(pde_t *pde)
+{
+  int i;
+  
+  SerialPrint("Dumping the pages starting with the pde page at %p\n",pde);
+
+  for (i=0;i<NUM_PAGE_DIR_ENTRIES && pde[i].present;i++) { 
+    SerialPrintPDE((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),&(pde[i]));
+    SerialPrintPT((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),(void*)(pde[i].pageTableBaseAddr<<PAGE_POWER));
+  }
+}
+    
+    
+    
+
+
+int checkPaging()
+{
+  unsigned long reg=0;
+  __asm__ __volatile__( "movl %%cr0, %0" : "=a" (reg));
+  Print("Paging on ? : %d\n", (reg & (1<<31)) != 0);
+  return (reg & (1<<31)) != 0;
+}
+
+
+/*
+ * Print diagnostic information for a page fault.
+ */
+static void Print_Fault_Info(uint_t address, faultcode_t faultCode)
+{
+    extern uint_t g_freePageCount;
+
+    g_freePageCount+=0;
+
+    SerialPrintLevel(100,"Pid %d, Page Fault received, at address %x (%d pages free)\n",
+        g_currentThread->pid, address, g_freePageCount);
+    if (faultCode.protectionViolation)
+        SerialPrintLevel(100,"   Protection Violation, ");
+    else
+        SerialPrintLevel(100,"   Non-present page, ");
+    if (faultCode.writeFault)
+        SerialPrintLevel(100,"Write Fault, ");
+    else
+        SerialPrintLevel(100,"Read Fault, ");
+    if (faultCode.userModeFault)
+        SerialPrintLevel(100,"in User Mode\n");
+    else
+        SerialPrintLevel(100,"in Supervisor Mode\n");
+}
+
+/*
+ * Handler for page faults.
+ * You should call the Install_Interrupt_Handler() function to
+ * register this function as the handler for interrupt 14.
+ */
+/*static*/ void Page_Fault_Handler(struct Interrupt_State* state)
+{
+    ulong_t address;
+    faultcode_t faultCode;
+
+    KASSERT(!Interrupts_Enabled());
+
+    /* Get the address that caused the page fault */
+    address = Get_Page_Fault_Address();
+    Debug("Page fault @%lx\n", address);
+
+    /* Get the fault code */
+    faultCode = *((faultcode_t *) &(state->errorCode));
+
+    /* rest of your handling code here */
+    SerialPrintLevel(100,"Unexpected Page Fault received\n");
+    Print_Fault_Info(address, faultCode);
+    Dump_Interrupt_State(state);
+    /* user faults just kill the process */
+    if (!faultCode.userModeFault) KASSERT(0);
+
+    /* For now, just kill the thread/process. */
+    Exit(-1);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+
+
+/*
+ * Initialize virtual memory by building page tables
+ * for the kernel and physical memory.
+ */
+void Init_VM(struct Boot_Info *bootInfo)
+{
+  int numpages;
+  int numpagetables;
+  int i,j;
+
+  pde_t *pd;
+  pte_t *pt;
+
+  PrintBoth("Intitialing Virtual Memory\n");
+
+  if (checkPaging()) { 
+    SerialPrintLevel(100,"Paging is currently ON\n");
+    return ;
+  }
+
+  SerialPrintLevel(100,"Paging is currently OFF - initializing the pages for a 1-1 map\n");
+  
+  numpages=bootInfo->memSizeKB / (PAGE_SIZE/1024);
+  numpagetables = numpages / NUM_PAGE_TABLE_ENTRIES + ((numpages % NUM_PAGE_TABLE_ENTRIES) != 0 );
+
+  SerialPrintLevel(100,"We need %d pages, and thus %d page tables, and one page directory\n",numpages, numpagetables);
+  
+  pd = (pde_t*)Alloc_Page();
+  
+  if (!pd) { 
+    SerialPrintLevel(100,"We are giving up since we can't allocate a page directory!\n");
+    return;
+  } else {
+    SerialPrintLevel(100,"Our PDE is at physical address %p\n",pd);
+  }
+  
+  for (i=0;i<NUM_PAGE_DIR_ENTRIES;i++) { 
+    if (i>=numpagetables) { 
+      pd[i].present=0;
+      pd[i].flags=0;
+      pd[i].accessed=0;
+      pd[i].reserved=0;
+      pd[i].largePages=0;
+      pd[i].globalPage=0;
+      pd[i].kernelInfo=0;
+      pd[i].pageTableBaseAddr=0;
+    } else {
+      pt = (pte_t*)Alloc_Page();
+      if (!pt) { 
+       SerialPrintLevel(100,"We are giving up since we can't allocate page table %d\n",i);
+      } else {
+       //SerialPrintLevel(100,"Page Table %d is at physical address %p\n",i,pt);
+      }
+      pd[i].present=1;
+      pd[i].flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
+      pd[i].accessed=0;
+      pd[i].reserved=0;
+      pd[i].largePages=0;
+      pd[i].globalPage=0;
+      pd[i].kernelInfo=0;
+      pd[i].pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
+      
+      for (j=0;j<NUM_PAGE_TABLE_ENTRIES;j++) { 
+       if (i*NUM_PAGE_TABLE_ENTRIES + j >= numpages) {
+         pt[j].present=0;
+         pt[j].flags=0;
+         pt[j].accessed=0;
+         pt[j].dirty=0;
+         pt[j].pteAttribute=0;
+         pt[j].globalPage=0;
+         pt[j].kernelInfo=0;
+         pt[j].pageBaseAddr=0;
+       } else {
+         pt[j].present=1;
+         pt[j].flags=VM_READ | VM_WRITE | VM_EXEC | VM_USER;
+         pt[j].accessed=0;
+         pt[j].dirty=0;
+         pt[j].pteAttribute=0;
+         pt[j].globalPage=0;
+         pt[j].kernelInfo=0;
+         pt[j].pageBaseAddr=(i*NUM_PAGE_TABLE_ENTRIES + j);
+       }
+      }
+    }
+  }
+  SerialPrintLevel(100,"Done creating 1<->1 initial page tables\n");
+  SerialPrintLevel(100,"Now installing page fault handler\n");
+  //  SerialDumpPageTables(pd);
+  Install_Interrupt_Handler(14,Page_Fault_Handler);
+  SerialPrintLevel(100,"Now turning on the paging bit!\n");
+  Enable_Paging(pd);
+  SerialPrintLevel(100,"We are still alive after paging turned on!\n");
+  SerialPrintLevel(100,"checkPaging returns %d\n",checkPaging());
+}
+
+
+//
+pte_t *LookupPage(void *vaddr)
+{
+  uint_t pde_offset = ((uint_t)vaddr) >> 22;
+  uint_t pte_offset = (((uint_t)vaddr) >> 12) & 0x3ff;
+  pte_t *pte; 
+  pde_t *pde = Get_PDBR();
+
+  KASSERT(pde);
+
+  pde+=pde_offset;
+
+  if (!(pde->present)) { 
+    return 0;
+  }
+
+  pte = (pte_t *)((pde->pageTableBaseAddr)<<12);
+
+  pte+=pte_offset;
+
+  return pte;
+}
+
+
+pte_t *CreateAndAddPageTable(void *vaddr, uint_t flags)
+{
+  int i;
+
+  KASSERT(!(PAGE_OFFSET(vaddr)));
+  
+  pte_t *pt = Alloc_Page();
+  
+  KASSERT(pt);
+  
+  for (i=0;i<NUM_PAGE_TABLE_ENTRIES;i++) { 
+    pt[i].present=0;
+  }
+
+  pde_t *pde = Get_PDBR();
+  
+  pde=&(pde[PAGE_DIRECTORY_INDEX(vaddr)]);
+
+  KASSERT(!(pde->present));
+  
+  pde->present=1;
+  pde->flags=flags;
+  pde->accessed=0;
+  pde->reserved=0;
+  pde->largePages=0;
+  pde->globalPage=0;
+  pde->kernelInfo=0;
+  pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
+
+  return pt;
+}
+
+pte_t *MapPage(void *vaddr, pte_t *pte, int alloc_pde)
+{
+  pte_t *oldpte = LookupPage(vaddr);
+
+  if (!pte) {
+    if (alloc_pde) { 
+      CreateAndAddPageTable(vaddr,pte->flags);
+      oldpte = LookupPage(vaddr);
+      KASSERT(pte);
+    } else {
+      return 0;
+    }
+  }
+
+  *oldpte = *pte;
+  
+  return oldpte;
+}
+
+pte_t *UnMapPage(void *vaddr)
+{
+  pte_t *oldpte = LookupPage(vaddr);
+
+  if (!oldpte) {
+    return 0;
+  }
+  oldpte->present=0;
+  
+  return oldpte;
+}
+
+  
+
+/**
+ * Initialize paging file data structures.
+ * All filesystems should be mounted before this function
+ * is called, to ensure that the paging file is available.
+ */
+void Init_Paging(void)
+{
+  PrintBoth("Initializing Paging\n");
+}
+
+/**
+ * Find a free bit of disk on the paging file for this page.
+ * Interrupts must be disabled.
+ * @return index of free page sized chunk of disk space in
+ *   the paging file, or -1 if the paging file is full
+ */
+int Find_Space_On_Paging_File(void)
+{
+    KASSERT(!Interrupts_Enabled());
+    TODO("Find free page in paging file");
+}
+
+/**
+ * Free a page-sized chunk of disk space in the paging file.
+ * Interrupts must be disabled.
+ * @param pagefileIndex index of the chunk of disk space
+ */
+void Free_Space_On_Paging_File(int pagefileIndex)
+{
+    KASSERT(!Interrupts_Enabled());
+    TODO("Free page in paging file");
+}
+
+/**
+ * Write the contents of given page to the indicated block
+ * of space in the paging file.
+ * @param paddr a pointer to the physical memory of the page
+ * @param vaddr virtual address where page is mapped in user memory
+ * @param pagefileIndex the index of the page sized chunk of space
+ *   in the paging file
+ */
+void Write_To_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
+{
+    struct Page *page = Get_Page((ulong_t) paddr);
+    KASSERT(!(page->flags & PAGE_PAGEABLE)); /* Page must be locked! */
+    TODO("Write page data to paging file");
+}
+
+/**
+ * Read the contents of the indicated block
+ * of space in the paging file into the given page.
+ * @param paddr a pointer to the physical memory of the page
+ * @param vaddr virtual address where page will be re-mapped in
+ *   user memory
+ * @param pagefileIndex the index of the page sized chunk of space
+ *   in the paging file
+ */
+void Read_From_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
+{
+    struct Page *page = Get_Page((ulong_t) paddr);
+    KASSERT(!(page->flags & PAGE_PAGEABLE)); /* Page must be locked! */
+    TODO("Read page data from paging file");
+}
+
diff --git a/misc/test_vm/src/geekos/reboot.c b/misc/test_vm/src/geekos/reboot.c
new file mode 100644 (file)
index 0000000..08f307d
--- /dev/null
@@ -0,0 +1,161 @@
+#include <geekos/reboot.h>
+#include <libc/string.h>
+// from linux...
+
+
+#define RTC_PORT(x)     (0x70 + (x))
+
+/* The following code and data reboots the machine by switching to real
+   mode and jumping to the BIOS reset entry point, as if the CPU has
+   really been reset.  The previous version asked the keyboard
+   controller to pulse the CPU reset line, which is more thorough, but
+   doesn't work with at least one type of 486 motherboard.  It is easy
+   to stop this code working; hence the copious comments. */
+
+static unsigned long long
+real_mode_gdt_entries [3] =
+{
+       0x0000000000000000ULL,  /* Null descriptor */
+       0x00009a000000ffffULL,  /* 16-bit real-mode 64k code at 0x00000000 */
+       0x000092000100ffffULL   /* 16-bit real-mode 64k data at 0x00000100 */
+};
+
+static struct
+{
+       unsigned short       size __attribute__ ((packed));
+       unsigned long long * base __attribute__ ((packed));
+}
+real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
+  real_mode_idt = { 0x3ff, 0 };
+//no_idt = { 0, 0 };
+
+
+
+static unsigned char real_mode_switch [] =
+{
+       0x66, 0x0f, 0x20, 0xc0,                 /*    movl  %cr0,%eax        */
+       0x66, 0x83, 0xe0, 0x11,                 /*    andl  $0x00000011,%eax */
+       0x66, 0x0d, 0x00, 0x00, 0x00, 0x60,     /*    orl   $0x60000000,%eax */
+       0x66, 0x0f, 0x22, 0xc0,                 /*    movl  %eax,%cr0        */
+       0x66, 0x0f, 0x22, 0xd8,                 /*    movl  %eax,%cr3        */
+       0x66, 0x0f, 0x20, 0xc3,                 /*    movl  %cr0,%ebx        */
+       0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60,       /*    andl  $0x60000000,%ebx */
+       0x74, 0x02,                             /*    jz    f                */
+       0x0f, 0x09,                             /*    wbinvd                 */
+       0x24, 0x10,                             /* f: andb  $0x10,al         */
+       0x66, 0x0f, 0x22, 0xc0                  /*    movl  %eax,%cr0        */
+};
+static unsigned char jump_to_bios [] =
+{
+       0xea, 0x00, 0x00, 0xff, 0xff            /*    ljmp  $0xffff,$0x0000  */
+};
+
+
+/*
+ * Switch to real mode and then execute the code
+ * specified by the code and length parameters.
+ * We assume that length will aways be less that 100!
+ */
+void machine_real_restart() {
+  //   unsigned long flags;
+
+       unsigned short rtp1, rtp2;
+       unsigned char val1, val2;
+
+       //JRL// local_irq_disable();
+
+       /* Write zero to CMOS register number 0x0f, which the BIOS POST
+          routine will recognize as telling it to do a proper reboot.  (Well
+          that's what this book in front of me says -- it may only apply to
+          the Phoenix BIOS though, it's not clear).  At the same time,
+          disable NMIs by setting the top bit in the CMOS address register,
+          as we're about to do peculiar things to the CPU.  I'm not sure if
+          `outb_p' is needed instead of just `outb'.  Use it to be on the
+          safe side.  (Yes, CMOS_WRITE does outb_p's. -  Paul G.)
+        */
+
+//JRL//         spin_lock_irqsave(&rtc_lock, flags);
+//JRL//        CMOS_WRITE(0x00, 0x8f);
+       val1 = 0x8f;
+       val2 = 0x00;
+       rtp1 = RTC_PORT(0);
+       rtp2 = RTC_PORT(1);
+
+       //      outb_p(0x8f, RTC_PORT(0));
+       //      outb_p(0x00, RTC_PORT(1));
+
+       __asm__ __volatile__ (
+                             "outb %b0, %w1"
+                             :
+                             : "a" (val1), "Nd" (rtp1)
+                             );
+
+       __asm__ __volatile__ (
+                             "outb %b0, %w1"
+                             :
+                             : "a" (val2), "Nd" (rtp2)
+                             );
+
+//JRL//         spin_unlock_irqrestore(&rtc_lock, flags);
+
+       /* Remap the kernel at virtual address zero, as well as offset zero
+          from the kernel segment.  This assumes the kernel segment starts at
+          virtual address PAGE_OFFSET. */
+
+//JRL//         memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
+//JRL//                sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
+
+       /*
+        * Use `swapper_pg_dir' as our page directory.
+        */
+//JRL//        load_cr3(swapper_pg_dir);
+
+       /* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
+          this on booting to tell it to "Bypass memory test (also warm
+          boot)".  This seems like a fairly standard thing that gets set by
+          REBOOT.COM programs, and the previous reset routine did this
+          too. */
+
+       *((unsigned short *)0x472) = 0x1234;
+
+       /* For the switch to real mode, copy some code to low memory.  It has
+          to be in the first 64k because it is running in 16-bit mode, and it
+          has to have the same physical and virtual address, because it turns
+          off paging.  Copy it near the end of the first page, out of the way
+          of BIOS variables. */
+
+       memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100),
+               real_mode_switch, sizeof (real_mode_switch));
+       memcpy ((void *) (0x1000 - 100), jump_to_bios, sizeof(jump_to_bios));
+
+       /* Set up the IDT for real mode. */
+
+       __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
+
+       /* Set up a GDT from which we can load segment descriptors for real
+          mode.  The GDT is not used in real mode; it is just needed here to
+          prepare the descriptors. */
+
+       __asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt));
+
+       /* Load the data segment registers, and thus the descriptors ready for
+          real mode.  The base address of each segment is 0x100, 16 times the
+          selector value being loaded here.  This is so that the segment
+          registers don't have to be reloaded after switching to real mode:
+          the values are consistent for real mode operation already. */
+
+       __asm__ __volatile__ ("movl $0x0010,%%eax\n"
+                               "\tmovl %%eax,%%ds\n"
+                               "\tmovl %%eax,%%es\n"
+                               "\tmovl %%eax,%%fs\n"
+                               "\tmovl %%eax,%%gs\n"
+                               "\tmovl %%eax,%%ss" : : : "eax");
+
+       /* Jump to the 16-bit code that we copied earlier.  It disables paging
+          and the cache, switches to real mode, and jumps to the BIOS reset
+          entry point. */
+
+       __asm__ __volatile__ ("ljmp $0x0008,%0"
+                               :
+                               : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100)));
+}
diff --git a/misc/test_vm/src/geekos/screen.c b/misc/test_vm/src/geekos/screen.c
new file mode 100644 (file)
index 0000000..9c78dde
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * GeekOS text screen output
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <stdarg.h>
+#include <geekos/kassert.h>
+#include <geekos/ktypes.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/fmtout.h>
+#include <geekos/screen.h>
+
+/*
+ * Information sources for VT100 and ANSI escape sequences:
+ * - http://www.lns.cornell.edu/~pvhp/dcl/vt100.html
+ * - http://en.wikipedia.org/wiki/ANSI_escape_code
+ */
+
+/* ----------------------------------------------------------------------
+ * Private functions and data
+ * ---------------------------------------------------------------------- */
+
+#define ESC ((char) 0x1B)
+#define DEFAULT_ATTRIBUTE ATTRIB(BLACK, GRAY)
+
+enum State {
+    S_NORMAL,          /* Normal state - output is echoed verbatim */
+    S_ESC,             /* Saw ESC character - begin output escape sequence */
+    S_ESC2,            /* Saw '[' character - continue output escape sequence */
+    S_ARG,             /* Scanning a numeric argument */
+    S_CMD,             /* Command */
+};
+
+#define MAXARGS 8      /* Max args that can be passed to esc sequence */
+
+struct Console_State {
+    /* Current state information */
+    int row, col;
+    int saveRow, saveCol;
+    uchar_t currentAttr;
+
+    /* Working variables for processing escape sequences. */
+    enum State state;
+    int argList[MAXARGS];
+    int numArgs;
+};
+
+static struct Console_State s_cons;
+
+#define NUM_SCREEN_DWORDS ((NUMROWS * NUMCOLS * 2) / 4)
+#define NUM_SCROLL_DWORDS (((NUMROWS-1) * NUMCOLS * 2) / 4)
+#define NUM_DWORDS_PER_LINE ((NUMCOLS*2)/4)
+#define FILL_DWORD (0x00200020 | (s_cons.currentAttr<<24) | (s_cons.currentAttr<<8))
+
+/*
+ * Scroll the display one line.
+ * We speed things up by copying 4 bytes at a time.
+ */
+static void Scroll(void)
+{
+    uint_t* v;
+    int i, n = NUM_SCROLL_DWORDS;
+    uint_t fill = FILL_DWORD;
+
+    /* Move lines 1..NUMROWS-1 up one position. */
+    for (v = (uint_t*)VIDMEM, i = 0; i < n; ++i) {
+       *v = *(v + NUM_DWORDS_PER_LINE);
+       ++v;
+    }
+
+    /* Clear out last line. */
+    for (v = (uint_t*)VIDMEM + n, i = 0; i < NUM_DWORDS_PER_LINE; ++i)
+       *v++ = fill;
+}
+
+/*
+ * Clear current cursor position to end of line using
+ * current attribute.
+ */
+static void Clear_To_EOL(void)
+{
+    int n = (NUMCOLS - s_cons.col);
+    uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
+    while (n-- > 0) {
+       *v++ = ' ';
+       *v++ = s_cons.currentAttr;
+    }
+}
+
+/*
+ * Move to the beginning of the next line, scrolling
+ * if necessary.
+ */
+static void Newline(void)
+{
+    ++s_cons.row;
+    s_cons.col = 0;
+    if (s_cons.row == NUMROWS) {
+       Scroll();
+       s_cons.row = NUMROWS - 1;
+    }
+}
+
+/*
+ * Write the graphic representation of given character to the screen
+ * at current position, with current attribute, scrolling if
+ * necessary.
+ */
+static void Put_Graphic_Char(int c)
+{
+    uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
+
+    /* Put character at current position */
+    *v++ = (uchar_t) c;
+    *v = s_cons.currentAttr;
+
+    if (s_cons.col < NUMCOLS - 1)
+       ++s_cons.col;
+    else
+       Newline();
+}
+
+/*
+ * Put one character to the screen using the current cursor position
+ * and attribute, scrolling if needed.  The caller should update
+ * the cursor position once all characters have been written.
+ */
+static void Output_Literal_Character(int c)
+{
+    int numSpaces;
+
+    switch (c) {
+    case '\n':
+       Clear_To_EOL();
+       Newline();
+       break;
+
+    case '\t':
+       numSpaces = TABWIDTH - (s_cons.col % TABWIDTH);
+       while (numSpaces-- > 0)
+           Put_Graphic_Char(' ');
+       break;
+
+    default:
+       Put_Graphic_Char(c);
+       break;
+    }
+
+#ifndef NDEBUG
+    /*
+     * When compiled with --enable-port-e9-hack, Bochs will send writes
+     * to port E9 to the console.  This helps tremendously with debugging,
+     * because it allows debug Print() statements to be visible after
+     * Bochs has exited.
+     */
+    Out_Byte(0xE9, c);
+#endif
+}
+
+/*
+ * Move the cursor to a new position, stopping at the screen borders.
+ */
+static void Move_Cursor(int row, int col)
+{
+    if (row < 0)
+       row = 0;
+    else if (row >= NUMROWS)
+       row = NUMROWS - 1;
+
+    if (col < 0)
+       col = 0;
+    else if (col >= NUMCOLS)
+       col = NUMCOLS - 1;
+
+    s_cons.row = row;
+    s_cons.col = col;
+}
+
+/*
+ * Table mapping ANSI colors to VGA text mode colors.
+ */
+static const uchar_t s_ansiToVgaColor[] = {
+    BLACK,RED,GREEN,AMBER,BLUE,MAGENTA,CYAN,GRAY
+};
+
+/*
+ * Update the attributes specified by the arguments
+ * of the escape sequence.
+ */
+static void Update_Attributes(void)
+{
+    int i;
+    int attr = s_cons.currentAttr & ~(BRIGHT);
+
+    for (i = 0; i < s_cons.numArgs; ++i) {
+       int value = s_cons.argList[i];
+       if (value == 0)
+           attr = DEFAULT_ATTRIBUTE;
+       else if (value == 1)
+           attr |= BRIGHT;
+       else if (value >= 30 && value <= 37)
+           attr = (attr & ~0x7) | s_ansiToVgaColor[value - 30];
+       else if (value >= 40 && value <= 47)
+           attr = (attr & ~(0x7 << 4)) | (s_ansiToVgaColor[value - 40] << 4);
+    }
+    s_cons.currentAttr = attr;
+}
+
+/* Reset to cancel or finish processing an escape sequence. */
+static void Reset(void)
+{
+    s_cons.state = S_NORMAL;
+    s_cons.numArgs = 0;
+}
+
+/* Start an escape sequence. */
+static void Start_Escape(void)
+{
+    s_cons.state = S_ESC;
+    s_cons.numArgs = 0;
+}
+
+/* Start a numeric argument to an escape sequence. */
+static void Start_Arg(int argNum)
+{
+    KASSERT(s_cons.numArgs == argNum);
+    s_cons.numArgs++;
+    s_cons.state = S_ARG;
+    if (argNum < MAXARGS)
+       s_cons.argList[argNum] = 0;
+}
+
+/* Save current cursor position. */
+static void Save_Cursor(void)
+{
+    s_cons.saveRow = s_cons.row;
+    s_cons.saveCol = s_cons.col;
+}
+
+/* Restore saved cursor position. */
+static void Restore_Cursor(void)
+{
+    s_cons.row = s_cons.saveRow;
+    s_cons.col = s_cons.saveCol;
+}
+
+/* Add a digit to current numeric argument. */
+static void Add_Digit(int c)
+{
+    KASSERT(ISDIGIT(c));
+    if (s_cons.numArgs < MAXARGS) {
+       int argNum = s_cons.numArgs - 1;
+       s_cons.argList[argNum] *= 10;
+       s_cons.argList[argNum] += (c - '0');
+    }
+}
+
+/*
+ * Get a numeric argument.
+ * Returns zero if that argument was not actually specified.
+ */
+static int Get_Arg(int argNum)
+{
+    return argNum < s_cons.numArgs ? s_cons.argList[argNum] : 0;
+}
+
+/*
+ * The workhorse output function.
+ * Depending on the current console output state,
+ * does literal character output or processes part of
+ * an escape sequence.
+ */
+static void Put_Char_Imp(int c)
+{
+again:
+    switch (s_cons.state) {
+    case S_NORMAL:
+       if (c == ESC)
+           Start_Escape();
+       else
+           Output_Literal_Character(c);
+       break;
+
+    case S_ESC:
+       if (c == '[')
+           s_cons.state = S_ESC2;
+       else
+           Reset();
+       break;
+
+    case S_ESC2:
+       if (ISDIGIT(c)) {
+           Start_Arg(0);
+           goto again;
+       } else if (c == ';') {
+           /* Special case: for "n;m" commands, "n" is implicitly 1 if omitted */
+           Start_Arg(0);
+           Add_Digit('1');
+           Start_Arg(1);
+       } else {
+           s_cons.state = S_CMD;
+           goto again;
+       }
+       break;
+
+    case S_ARG:
+       if (ISDIGIT(c))
+           Add_Digit(c);
+       else if (c == ';')
+           Start_Arg(s_cons.numArgs);
+       else {
+           s_cons.state = S_CMD;
+           goto again;
+       }
+       break;
+
+    case S_CMD:
+       switch (c) {
+       case 'K': Clear_To_EOL(); break;
+       case 's': Save_Cursor(); break;
+       case 'u': Restore_Cursor(); break;
+       case 'A': Move_Cursor(s_cons.row - Get_Arg(0), s_cons.col); break;
+       case 'B': Move_Cursor(s_cons.row + Get_Arg(0), s_cons.col); break;
+       case 'C': Move_Cursor(s_cons.row, s_cons.col + Get_Arg(0)); break;
+       case 'D': Move_Cursor(s_cons.row, s_cons.col - Get_Arg(0)); break;
+       case 'm': Update_Attributes(); break;
+       case 'f': case 'H':
+           if (s_cons.numArgs == 2) Move_Cursor(Get_Arg(0)-1, Get_Arg(1)-1); break;
+       case 'J':
+           if (s_cons.numArgs == 1 && Get_Arg(0) == 2) {
+               Clear_Screen();
+               Put_Cursor(0, 0);
+           }
+           break;
+       default: break;
+       }
+       Reset();
+       break;
+
+    default:
+       KASSERT(false);
+    }
+}
+
+/*
+ * Update the location of the hardware cursor.
+ */
+static void Update_Cursor(void)
+{
+    /*
+     * The cursor location is a character offset from the beginning
+     * of page memory (I think).
+     */
+    uint_t characterPos = (s_cons.row * NUMCOLS) + s_cons.col;
+    uchar_t origAddr;
+
+    /*
+     * Save original contents of CRT address register.
+     * It is considered good programming practice to restore
+     * it to its original value after modifying it.
+     */
+    origAddr = In_Byte(CRT_ADDR_REG);
+    IO_Delay();
+
+    /* Set the high cursor location byte */
+    Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG);
+    IO_Delay();
+    Out_Byte(CRT_DATA_REG, (characterPos>>8) & 0xff);
+    IO_Delay();
+
+    /* Set the low cursor location byte */
+    Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG);
+    IO_Delay();
+    Out_Byte(CRT_DATA_REG, characterPos & 0xff);
+    IO_Delay();
+
+    /* Restore contents of the CRT address register */
+    Out_Byte(CRT_ADDR_REG, origAddr);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize the screen module.
+ */
+void Init_Screen(void)
+{
+    bool iflag = Begin_Int_Atomic();
+
+    s_cons.row = s_cons.col = 0;
+    s_cons.currentAttr = DEFAULT_ATTRIBUTE;
+    Clear_Screen();
+
+    End_Int_Atomic(iflag);
+    Print("Screen Inited\n");
+}
+
+/*
+ * Clear the screen using the current attribute.
+ */
+void Clear_Screen(void)
+{
+    uint_t* v = (uint_t*)VIDMEM;
+    int i;
+    uint_t fill = FILL_DWORD;
+
+    bool iflag = Begin_Int_Atomic();
+
+    for (i = 0; i < NUM_SCREEN_DWORDS; ++i)
+       *v++ = fill;
+
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Get current cursor position.
+ */
+void Get_Cursor(int* row, int* col)
+{
+    bool iflag = Begin_Int_Atomic();
+    *row = s_cons.row;
+    *col = s_cons.col;
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Set the current cursor position.
+ * Return true if successful, or false if the specified
+ * cursor position is invalid.
+ */
+bool Put_Cursor(int row, int col)
+{
+    bool iflag;
+
+    if (row < 0 || row >= NUMROWS || col < 0 || col >= NUMCOLS)
+       return false;
+
+    iflag = Begin_Int_Atomic();
+    s_cons.row = row;
+    s_cons.col = col;
+    Update_Cursor();
+    End_Int_Atomic(iflag);
+
+    return true;
+}
+
+/*
+ * Get the current character attribute.
+ */
+uchar_t Get_Current_Attr(void)
+{
+    return s_cons.currentAttr;
+}
+
+/*
+ * Set the current character attribute.
+ */
+void Set_Current_Attr(uchar_t attrib)
+{
+    bool iflag = Begin_Int_Atomic();
+    s_cons.currentAttr = attrib;
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Write a single character to the screen at current position
+ * using current attribute, handling scrolling, special characters, etc.
+ */
+void Put_Char(int c)
+{
+    bool iflag = Begin_Int_Atomic();
+    Put_Char_Imp(c);
+    Update_Cursor();
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Write a string of characters to the screen at current cursor
+ * position using current attribute.
+ */
+void Put_String(const char* s)
+{
+    bool iflag = Begin_Int_Atomic();
+    while (*s != '\0')
+       Put_Char_Imp(*s++);
+    Update_Cursor();
+    End_Int_Atomic(iflag);
+}
+
+/*
+ * Write a buffer of characters at current cursor position
+ * using current attribute.
+ */
+void Put_Buf(const char* buf, ulong_t length)
+{
+    bool iflag = Begin_Int_Atomic();
+    while (length > 0) {
+       Put_Char_Imp(*buf++);
+       --length;
+    }
+    Update_Cursor();
+    End_Int_Atomic(iflag);
+}
+
+/* Support for Print(). */
+static void Print_Emit(struct Output_Sink *o, int ch) { Put_Char_Imp(ch); }
+static void Print_Finish(struct Output_Sink *o) { Update_Cursor(); }
+static struct Output_Sink s_outputSink = { &Print_Emit, &Print_Finish };
+
+/*
+ * Print to console using printf()-style formatting.
+ * Calls into Format_Output in common library.
+ */
+void Print(const char *fmt, ...)
+{
+    va_list args;
+
+    bool iflag = Begin_Int_Atomic();
+
+    va_start(args, fmt);
+    Format_Output(&s_outputSink, fmt, args);
+    va_end(args);
+
+    End_Int_Atomic(iflag);
+}
+
diff --git a/misc/test_vm/src/geekos/segment.c b/misc/test_vm/src/geekos/segment.c
new file mode 100644 (file)
index 0000000..4e83db0
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * General data structures and routines for segmentation
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/string.h>
+#include <geekos/tss.h>
+#include <geekos/segment.h>
+
+static __inline__ void Set_Size_And_Base_Pages(
+    struct Segment_Descriptor* desc,
+    ulong_t baseAddr,
+    ulong_t numPages
+)
+{
+    /*
+     * There are 20 bits in the size fields of a segment descriptor.
+     * The maximum possible value is thus 0xFFFFF, which in terms of
+     * pages is one page less than 4GB.  So, I conclude that the
+     * number of pages in the segment is one greater than the
+     * value specified in the descriptor.
+     */
+    KASSERT(numPages > 0);
+    numPages -= 1;
+
+    desc->sizeLow     = numPages & 0xFFFF;
+    desc->sizeHigh    = (numPages >> 16) & 0x0F;
+    desc->baseLow     = baseAddr & 0xFFFFFF;
+    desc->baseHigh    = (baseAddr >> 24) & 0xFF;
+    desc->granularity = 1;  /* size in pages */
+}
+
+static __inline__ void Set_Size_And_Base_Bytes(
+    struct Segment_Descriptor* desc,
+    ulong_t baseAddr,
+    ulong_t numBytes
+)
+{
+    desc->sizeLow     = numBytes & 0xFFFF;
+    desc->sizeHigh    = (numBytes >> 16) & 0x0F;
+    desc->baseLow     = baseAddr & 0xFFFFFF;
+    desc->baseHigh    = (baseAddr >> 24) & 0xFF;
+    desc->granularity = 0;  /* size in bytes */
+}
+
+/*
+ * Initialize an unused segment descriptor.
+ */
+void Init_Null_Segment_Descriptor(struct Segment_Descriptor* desc)
+{
+    memset(desc, '\0', sizeof(*desc));
+}
+
+/*
+ * Initialize a code segment descriptor.
+ */
+void Init_Code_Segment_Descriptor(
+    struct Segment_Descriptor* desc,
+    ulong_t baseAddr,
+    ulong_t numPages,
+    int privilegeLevel
+)
+{
+    KASSERT(privilegeLevel >= 0 && privilegeLevel <= 3);
+
+    Set_Size_And_Base_Pages(desc, baseAddr, numPages);
+    desc->type     = 0x0A;   /* 1010b: code, !conforming, readable, !accessed */
+    desc->system   = 1;
+    desc->dpl      = privilegeLevel;
+    desc->present  = 1;
+    desc->reserved = 0;
+    desc->dbBit    = 1;  /* 32 bit code segment */
+}
+
+/*
+ * Initialize a data segment descriptor.
+ */
+void Init_Data_Segment_Descriptor(
+    struct Segment_Descriptor* desc,
+    ulong_t baseAddr,
+    ulong_t numPages,
+    int privilegeLevel
+)
+{
+    KASSERT(privilegeLevel >= 0 && privilegeLevel <= 3);
+
+    Set_Size_And_Base_Pages(desc, baseAddr, numPages);
+    desc->type     = 0x02;  /* 0010b: data, expand-up, writable, !accessed */
+    desc->system   = 1;
+    desc->dpl      = privilegeLevel;
+    desc->present  = 1;
+    desc->reserved = 0;
+    desc->dbBit    = 1;  /* 32 bit operands */
+}
+
+/*
+ * Initialize a TSS descriptor.
+ */
+void Init_TSS_Descriptor(struct Segment_Descriptor* desc, struct TSS* theTSS)
+{
+    Set_Size_And_Base_Bytes(desc, (ulong_t) theTSS, sizeof(struct TSS));
+    desc->type     = 0x09;  /* 1001b: 32 bit, !busy */
+    desc->system   = 0;
+    desc->dpl      = 0;
+    desc->present  = 1;
+    desc->reserved = 0;
+    desc->dbBit    = 0;  /* must be 0 in TSS */
+}
+
+/*
+ * Initialize an LDT (Local Descriptor Table) descriptor.
+ */
+void Init_LDT_Descriptor(
+    struct Segment_Descriptor* desc,
+    struct Segment_Descriptor theLDT[],
+    int numEntries
+)
+{
+    Set_Size_And_Base_Bytes(
+       desc, (ulong_t) theLDT, sizeof(struct Segment_Descriptor) * numEntries);
+
+    desc->type     = 0x02;  /* 0010b */
+    desc->system   = 0;
+    desc->dpl      = 0;
+    desc->present  = 1;
+    desc->reserved = 0;
+    desc->dbBit    = 0;
+}
diff --git a/misc/test_vm/src/geekos/serial.c b/misc/test_vm/src/geekos/serial.c
new file mode 100644 (file)
index 0000000..68ef275
--- /dev/null
@@ -0,0 +1,140 @@
+#include <geekos/serial.h>
+#include <geekos/reboot.h>
+#include <geekos/gdt.h>
+#include <geekos/idt.h>
+
+
+
+
+unsigned short serial_io_addr = 0;
+
+
+static void Serial_Interrupt_Handler(struct Interrupt_State * state) {
+  char rcv_byte;
+  char irq_id;
+
+  Begin_IRQ(state);
+
+  irq_id = In_Byte(serial_io_addr + 2);
+
+
+  if ((irq_id & 0x04) != 0) {
+    rcv_byte = In_Byte(serial_io_addr + 0);
+
+    if (rcv_byte == 'k') {
+      SerialPrint("Restarting Machine\r\n");
+      machine_real_restart();
+    } else if (rcv_byte=='d') { 
+      SerialPrint("Dumping Machine State\n");
+      Dump_Interrupt_State(state);
+      DumpIDT();
+      DumpGDT();
+    }
+      
+#if 0
+      SerialPrint("Unreserved serial byte: %d (%c)\r\n", rcv_byte, rcv_byte);
+#endif
+  }
+  End_IRQ(state);
+}
+
+void InitSerial() {
+  Print("Initialzing Serial\n");
+  Install_IRQ(COM1_IRQ, Serial_Interrupt_Handler);
+  Enable_IRQ(COM1_IRQ);
+  InitSerialAddr(DEFAULT_SERIAL_ADDR);
+}
+
+void InitSerialAddr(unsigned short io_addr) {
+  serial_io_addr = io_addr;
+
+  Print("Initializing Polled Serial Output on COM1 - 115200 N81 noflow\n");
+  //  io_adr = 0x3F8;  /* 3F8=COM1, 2F8=COM2, 3E8=COM3, 2E8=COM4 */
+  Out_Byte(io_addr + 3, 0x80);
+  // 115200 /* 115200 / 12 = 9600 baud */
+  Out_Byte(io_addr + 0, 1);
+  Out_Byte(io_addr + 1, 0);
+  /* 8N1 */
+  Out_Byte(io_addr + 3, 0x03);
+  /* all interrupts disabled */
+  //  Out_Byte(io_addr + 1, 0);
+  Out_Byte(io_addr + 1, 0x01);
+  /* turn off FIFO, if any */
+  Out_Byte(io_addr + 2, 0);
+  /* loopback off, interrupts (Out2) off, Out1/RTS/DTR off */
+  //  Out_Byte(io_addr + 4, 0);
+  // enable interrupts (bit 3)
+  Out_Byte(io_addr + 4, 0x08);
+}
+
+
+inline static void SerialPutChar(unsigned char c) {
+ //  static unsigned short io_adr;
+  if (serial_io_addr==0) { 
+    return;
+  }
+
+
+  if (c=='\n') { 
+    /* wait for transmitter ready */
+    while((In_Byte(serial_io_addr + 5) & 0x40) == 0) {
+    }
+    /* send char */
+    Out_Byte(serial_io_addr + 0, '\r');
+    /* wait for transmitter ready */
+  }
+  while((In_Byte(serial_io_addr + 5) & 0x40) == 0) {
+  }
+  /* send char */
+  Out_Byte(serial_io_addr + 0, c);
+}
+
+
+
+void SerialPutLineN(char * line, int len) {
+  int i;
+  for (i = 0; i < len && line[i] != 0; i++) { 
+    SerialPutChar(line[i]); 
+  }
+}
+
+
+void SerialPutLine(char * line) {
+  int i;
+  for (i = 0; line[i]!= 0; i++) { 
+    SerialPutChar(line[i]); 
+  }
+}
+
+
+void SerialPrintHex(unsigned char x)
+{
+  unsigned char z;
+  
+  z = (x>>4) & 0xf ;
+  SerialPrint("%x", z);
+  z = x & 0xf;
+  SerialPrint("%x", z);
+}
+
+void SerialMemDump(unsigned char *start, int n)
+{
+  int i, j;
+
+  for (i=0;i<n;i+=16) {
+    SerialPrint("%8x", (unsigned)(start+i));
+    for (j=i; j<i+16 && j<n; j+=2) {
+      SerialPrint(" ");
+      SerialPrintHex(*((unsigned char *)(start+j)));
+      if ((j+1)<n) { 
+       SerialPrintHex(*((unsigned char *)(start+j+1)));
+      }
+    }
+    SerialPrint(" ");
+    for (j=i; j<i+16 && j<n;j++) {
+      SerialPrint("%c", ((start[j]>=32) && (start[j]<=126)) ? start[j] : '.');
+    }
+    SerialPrint("\n");
+  }
+}
diff --git a/misc/test_vm/src/geekos/setup.asm b/misc/test_vm/src/geekos/setup.asm
new file mode 100644 (file)
index 0000000..c88ed81
--- /dev/null
@@ -0,0 +1,240 @@
+; -*- fundamental -*-
+; GeekOS setup code
+; Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software.  You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; A lot of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+; This is SUICIDE...
+; DON'T EVER INCLUDE CODE AT THE TOP LEVEL AGAIN
+;%include "util.asm"
+
+[BITS 16]
+[ORG 0x0]
+
+start_setup:
+
+       ; Redefine the data segment so we can access variables
+       ; declared in this file.
+       mov     ax, SETUPSEG
+       mov     ds, ax
+
+
+       ; Use int 15h to find out size of extended memory in KB.
+       ; Extended memory is the memory above 1MB.  So by
+       ; adding 1MB to this amount, we get the total amount
+       ; of system memory.  We can only detect 64MB this way,
+       ; but that's OK for now.
+       ;mov    ah, 0x88
+       ;int    0x15
+       ;add    ax, 1024        ; 1024 KB == 1 MB
+       mov     ax, 0xe801
+       int     0x15
+       add     ax, 1024        ; 1024 KB == 1 MB
+       mov     [mem_size_kbytes], ax
+       mov     [mem_size_eblocks], bx
+
+       ; Kill the floppy motor.
+       call    Kill_Motor
+
+
+
+       ; Block interrupts, since we can't meaningfully handle them yet
+       ; and we no longer need BIOS services.
+       cli
+
+       ; Set up IDT and GDT registers
+       lidt    [IDT_Pointer]
+       lgdt    [GDT_Pointer]
+
+       ; Initialize the interrupt controllers, and enable the
+       ; A20 address line
+       call    Init_PIC
+       call    Enable_A20
+
+       ; Switch to protected mode!
+       mov     ax, 0x01
+       lmsw    ax
+
+       ; Jump to 32 bit code.
+       jmp     dword KERNEL_CS:(SETUPSEG << 4) + setup_32
+
+
+[BITS 32]
+setup_32:
+
+       ; set up data segment registers
+       mov     ax, KERNEL_DS
+       mov     ds, ax
+       mov     es, ax
+       mov     fs, ax
+       mov     gs, ax
+       mov     ss, ax
+
+       ; Create the stack for the initial kernel thread.
+       mov     esp, KERN_STACK + 4096
+
+       ; Build Boot_Info struct on stack.
+       ; Note that we push the fields on in reverse order,
+       ; since the stack grows downwards.
+       xor     eax, eax
+       mov     ax, [(SETUPSEG<<4)+mem_size_kbytes]
+       xor     ebx, ebx
+       mov     bx, [(SETUPSEG<<4)+mem_size_eblocks]
+       shl     ebx, 6
+       add     eax, ebx
+       push    eax             ; memSizeKB
+       push    dword 8         ; bootInfoSize
+
+       ; Pass pointer to Boot_Info struct as argument to kernel
+       ; entry point.
+       push    esp
+
+       ; Push return address to make this look like a call
+       ; XXX - untested
+       push    dword (SETUPSEG<<4)+.returnAddr
+
+
+
+       ; Far jump into kernel
+       jmp     KERNEL_CS:ENTRY_POINT
+
+.returnAddr:
+       ; We shouldn't return here.
+.here: jmp .here
+
+
+
+
+
+[BITS 16]
+
+
+
+%include "util.asm"
+
+; Kill the floppy motor.
+; This code was shamelessly stolen from Linux.
+Kill_Motor:
+       mov     dx, 0x3f2
+       xor     al, al
+       out     dx, al
+       ret
+
+Init_PIC:
+       ; Initialize master and slave PIC!
+       mov     al, ICW1
+       out     0x20, al                ; ICW1 to master
+       call    Delay
+       out     0xA0, al                ; ICW1 to slave
+       call    Delay
+       mov     al, ICW2_MASTER
+       out     0x21, al                ; ICW2 to master
+       call    Delay
+       mov     al, ICW2_SLAVE
+       out     0xA1, al                ; ICW2 to slave
+       call    Delay
+       mov     al, ICW3_MASTER
+       out     0x21, al                ; ICW3 to master
+       call    Delay
+       mov     al, ICW3_SLAVE
+       out     0xA1, al                ; ICW3 to slave
+       call    Delay
+       mov     al, ICW4
+       out     0x21, al                ; ICW4 to master
+       call    Delay
+       out     0xA1, al                ; ICW4 to slave
+       call    Delay
+       mov     al, 0xff                ; mask all ints in slave
+       out     0xA1, al                ; OCW1 to slave
+       call    Delay
+       mov     al, 0xfb                ; mask all ints but 2 in master
+       out     0x21, al                ; OCW1 to master
+       call    Delay
+       ret
+
+; Linux uses this code.
+; The idea is that some systems issue port I/O instructions
+; faster than the device hardware can deal with them.
+Delay:
+       jmp     .done
+.done: ret
+
+; Enable the A20 address line, so we can correctly address
+; memory above 1MB.
+Enable_A20:
+       mov     al, 0xD1
+       out     0x64, al
+       call    Delay
+       mov     al, 0xDF
+       out     0x60, al
+       call    Delay
+       ret
+
+
+; ----------------------------------------------------------------------
+; Setup data
+; ----------------------------------------------------------------------
+
+mem_size_kbytes: dw 0
+mem_size_eblocks: dw 0
+
+; ----------------------------------------------------------------------
+; The GDT.  Creates flat 32-bit address space for the kernel
+; code, data, and stack.  Note that this GDT is just used
+; to create an environment where we can start running 32 bit
+; code.  The kernel will create and manage its own GDT.
+; ----------------------------------------------------------------------
+
+; GDT initialization stuff
+NUM_GDT_ENTRIES equ 3          ; number of entries in GDT
+GDT_ENTRY_SZ equ 8             ; size of a single GDT entry
+
+align 8, db 0
+GDT:
+       ; Descriptor 0 is not used
+       dw 0
+       dw 0
+       dw 0
+       dw 0
+
+       ; Descriptor 1: kernel code segment
+       dw 0xFFFF       ; bytes 0 and 1 of segment size
+       dw 0x0000       ; bytes 0 and 1 of segment base address
+       db 0x00         ; byte 2 of segment base address
+       db 0x9A         ; present, DPL=0, non-system, code, non-conforming,
+                       ;   readable, not accessed
+       db 0xCF         ; granularity=page, 32 bit code, upper nibble of size
+       db 0x00         ; byte 3 of segment base address
+
+       ; Descriptor 2: kernel data and stack segment
+       ; NOTE: what Intel calls an "expand-up" segment
+       ; actually means that the stack will grow DOWN,
+       ; towards lower memory.  So, we can use this descriptor
+       ; for both data and stack references.
+       dw 0xFFFF       ; bytes 0 and 1 of segment size
+       dw 0x0000       ; bytes 0 and 1 of segment base address
+       db 0x00         ; byte 2 of segment base address
+       db 0x92         ; present, DPL=0, non-system, data, expand-up,
+                       ;   writable, not accessed
+       db 0xCF         ; granularity=page, big, upper nibble of size
+       db 0x00         ; byte 3 of segment base address
+
+GDT_Pointer:
+       dw NUM_GDT_ENTRIES*GDT_ENTRY_SZ ; limit
+       dd (SETUPSEG<<4) + GDT          ; base address
+
+IDT_Pointer:
+       dw 0
+       dd 00
diff --git a/misc/test_vm/src/geekos/symbol.asm b/misc/test_vm/src/geekos/symbol.asm
new file mode 100644 (file)
index 0000000..0bb1584
--- /dev/null
@@ -0,0 +1,43 @@
+; Symbol mangling macros
+; Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This file defines macros for dealing with externally-visible
+; symbols that must be mangled for some object file formats.
+; For example, PECOFF requires a leading underscore, while
+; ELF does not.
+
+; EXPORT defines a symbol as global
+; IMPORT references a symbol defined in another module
+
+; Thanks to Christopher Giese for providing the NASM macros
+; (thus saving me hours of frustration).
+
+%ifndef SYMBOL_ASM
+%define SYMBOL_ASM
+
+%ifdef NEED_UNDERSCORE
+
+%macro EXPORT 1
+[GLOBAL _%1]
+%define %1 _%1
+%endmacro
+
+%macro IMPORT 1
+[EXTERN _%1]
+%define %1 _%1
+%endmacro
+
+%else
+
+%macro EXPORT 1
+[GLOBAL %1]
+%endmacro
+
+%macro IMPORT 1
+[EXTERN %1]
+%endmacro
+
+%endif
+
+%endif
diff --git a/misc/test_vm/src/geekos/synch.c b/misc/test_vm/src/geekos/synch.c
new file mode 100644 (file)
index 0000000..86e7191
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Synchronization primitives
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kthread.h>
+#include <geekos/int.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/synch.h>
+
+/*
+ * NOTES:
+ * - The GeekOS mutex and condition variable APIs are based on those
+ *   in pthreads.
+ * - Unlike disabling interrupts, mutexes offer NO protection against
+ *   concurrent execution of interrupt handlers.  Mutexes and
+ *   condition variables should only be used from kernel threads,
+ *   with interrupts enabled.
+ */
+
+/* ----------------------------------------------------------------------
+ * Private functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * The mutex is currently locked.
+ * Atomically reenable preemption and wait in the
+ * mutex's wait queue.
+ */
+static void Mutex_Wait(struct Mutex *mutex)
+{
+    KASSERT(mutex->state == MUTEX_LOCKED);
+    KASSERT(g_preemptionDisabled);
+
+    Disable_Interrupts();
+    g_preemptionDisabled = false;
+    Wait(&mutex->waitQueue);
+    g_preemptionDisabled = true;
+    Enable_Interrupts();
+}
+
+/*
+ * Lock given mutex.
+ * Preemption must be disabled.
+ */
+static __inline__ void Mutex_Lock_Imp(struct Mutex* mutex)
+{
+    KASSERT(g_preemptionDisabled);
+
+    /* Make sure we're not already holding the mutex */
+    KASSERT(!IS_HELD(mutex));
+
+    /* Wait until the mutex is in an unlocked state */
+    while (mutex->state == MUTEX_LOCKED) {
+       Mutex_Wait(mutex);
+    }
+
+    /* Now it's ours! */
+    mutex->state = MUTEX_LOCKED;
+    mutex->owner = g_currentThread;
+}
+
+/*
+ * Unlock given mutex.
+ * Preemption must be disabled.
+ */
+static __inline__ void Mutex_Unlock_Imp(struct Mutex* mutex)
+{
+    KASSERT(g_preemptionDisabled);
+
+    /* Make sure mutex was actually acquired by this thread. */
+    KASSERT(IS_HELD(mutex));
+
+    /* Unlock the mutex. */
+    mutex->state = MUTEX_UNLOCKED;
+    mutex->owner = 0;
+
+    /*
+     * If there are threads waiting to acquire the mutex,
+     * wake one of them up.  Note that it is legal to inspect
+     * the queue with interrupts enabled because preemption
+     * is disabled, and therefore we know that no thread can
+     * concurrently add itself to the queue.
+     */
+    if (!Is_Thread_Queue_Empty(&mutex->waitQueue)) {
+       Disable_Interrupts();
+       Wake_Up_One(&mutex->waitQueue);
+       Enable_Interrupts();
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize given mutex.
+ */
+void Mutex_Init(struct Mutex* mutex)
+{
+    mutex->state = MUTEX_UNLOCKED;
+    mutex->owner = 0;
+    Clear_Thread_Queue(&mutex->waitQueue);
+}
+
+/*
+ * Lock given mutex.
+ */
+void Mutex_Lock(struct Mutex* mutex)
+{
+    KASSERT(Interrupts_Enabled());
+
+    g_preemptionDisabled = true;
+    Mutex_Lock_Imp(mutex);
+    g_preemptionDisabled = false;
+}
+
+/*
+ * Unlock given mutex.
+ */
+void Mutex_Unlock(struct Mutex* mutex)
+{
+    KASSERT(Interrupts_Enabled());
+
+    g_preemptionDisabled = true;
+    Mutex_Unlock_Imp(mutex);
+    g_preemptionDisabled = false;
+}
+
+/*
+ * Initialize given condition.
+ */
+void Cond_Init(struct Condition* cond)
+{
+    Clear_Thread_Queue(&cond->waitQueue);
+}
+
+/*
+ * Wait on given condition (protected by given mutex).
+ */
+void Cond_Wait(struct Condition* cond, struct Mutex* mutex)
+{
+    KASSERT(Interrupts_Enabled());
+
+    /* Ensure mutex is held. */
+    KASSERT(IS_HELD(mutex));
+
+    /* Turn off scheduling. */
+    g_preemptionDisabled = true;
+
+    /*
+     * Release the mutex, but leave preemption disabled.
+     * No other threads will be able to run before this thread
+     * is able to wait.  Therefore, this thread will not
+     * miss the eventual notification on the condition.
+     */
+    Mutex_Unlock_Imp(mutex);
+
+    /*
+     * Atomically reenable preemption and wait in the condition wait queue.
+     * Other threads can run while this thread is waiting,
+     * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
+     * to wake up this thread.
+     * On wakeup, disable preemption again.
+     */
+    Disable_Interrupts();
+    g_preemptionDisabled = false;
+    Wait(&cond->waitQueue);
+    g_preemptionDisabled = true;
+    Enable_Interrupts();
+
+    /* Reacquire the mutex. */
+    Mutex_Lock_Imp(mutex);
+
+    /* Turn scheduling back on. */
+    g_preemptionDisabled = false;
+}
+
+/*
+ * Wake up one thread waiting on the given condition.
+ * The mutex guarding the condition should be held!
+ */
+void Cond_Signal(struct Condition* cond)
+{
+    KASSERT(Interrupts_Enabled());
+    Disable_Interrupts();  /* prevent scheduling */
+    Wake_Up_One(&cond->waitQueue);
+    Enable_Interrupts();  /* resume scheduling */
+}
+
+/*
+ * Wake up all threads waiting on the given condition.
+ * The mutex guarding the condition should be held!
+ */
+void Cond_Broadcast(struct Condition* cond)
+{
+    KASSERT(Interrupts_Enabled());
+    Disable_Interrupts();  /* prevent scheduling */
+    Wake_Up(&cond->waitQueue);
+    Enable_Interrupts();  /* resume scheduling */
+}
diff --git a/misc/test_vm/src/geekos/testvm.s b/misc/test_vm/src/geekos/testvm.s
new file mode 100644 (file)
index 0000000..1ab713e
--- /dev/null
@@ -0,0 +1,43 @@
+# -*- fundamental -*-
+
+.global MyBuzzVM
+MyBuzzVM:
+       pushl   %ebp
+       movl    %esp, %ebp
+       pushl   %esi
+       pushl   %ebx
+       inb $97, %al            
+       movl    $97, %ecx
+       movb    %al, %bl
+       orl     $2, %ebx
+       movl    %eax, %esi
+.L24:
+       movb    %bl, %al
+       movl    %ecx, %edx
+       outb    %al, %dx
+       movl    $0, %edx
+.L30:
+       incl    %edx
+       cmpl    $999999, %edx
+       jle     .L30
+       movl    %esi, %eax
+       movl    %ecx, %edx
+       outb %al, %dx
+       movl    $0, %edx
+.L35:
+       incl    %edx
+       cmpl    $999999, %edx
+       jle     .L35
+       jmp     .L24
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
+       
diff --git a/misc/test_vm/src/geekos/timer.c b/misc/test_vm/src/geekos/timer.c
new file mode 100644 (file)
index 0000000..b3120d3
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * GeekOS timer interrupt support
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <limits.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/irq.h>
+#include <geekos/kthread.h>
+#include <geekos/timer.h>
+
+#include <geekos/serial.h>
+
+#define HZ 100
+
+
+/*
+ * Global tick counter
+ */
+volatile ulong_t g_numTicks;
+
+/*
+ * Number of times the spin loop can execute during one timer tick
+ */
+static int s_spinCountPerTick;
+
+/*
+ * Number of ticks to wait before calibrating the delay loop.
+ */
+#define CALIBRATE_NUM_TICKS    3
+
+/*
+ * The default quantum; maximum number of ticks a thread can use before
+ * we suspend it and choose another.
+ */
+#define DEFAULT_MAX_TICKS 4
+
+/*
+ * Settable quantum.
+ */
+int g_Quantum = DEFAULT_MAX_TICKS;
+
+/*
+ * Ticks per second.
+ * FIXME: should set this to something more reasonable, like 100.
+ */
+#define TICKS_PER_SEC 18
+
+/*#define DEBUG_TIMER */
+#ifdef DEBUG_TIMER
+#  define Debug(args...) Print(args)
+#else
+#  define Debug(args...)
+#endif
+
+/* ----------------------------------------------------------------------
+ * Private functions
+ * ---------------------------------------------------------------------- */
+
+static void Timer_Interrupt_Handler(struct Interrupt_State* state)
+{
+    struct Kernel_Thread* current = g_currentThread;
+
+    Begin_IRQ(state);
+
+    SerialPrintLevel(10,"Host Timer Interrupt Handler Running\n");
+
+    /* Update global and per-thread number of ticks */
+    ++g_numTicks;
+    ++current->numTicks;
+
+
+    /*
+     * If thread has been running for an entire quantum,
+     * inform the interrupt return code that we want
+     * to choose a new thread.
+     */
+    if (current->numTicks >= g_Quantum) {
+       g_needReschedule = true;
+    }
+
+
+    End_IRQ(state);
+}
+
+/*
+ * Temporary timer interrupt handler used to calibrate
+ * the delay loop.
+ */
+static void Timer_Calibrate(struct Interrupt_State* state)
+{
+    Begin_IRQ(state);
+    if (g_numTicks < CALIBRATE_NUM_TICKS)
+       ++g_numTicks;
+    else {
+       /*
+        * Now we can look at EAX, which reflects how many times
+        * the loop has executed
+        */
+       /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
+       s_spinCountPerTick = INT_MAX  - state->eax;
+       state->eax = 0;  /* make the loop terminate */
+    }
+    End_IRQ(state);
+}
+
+/*
+ * Delay loop; spins for given number of iterations.
+ */
+static void Spin(int count)
+{
+    /*
+     * The assembly code is the logical equivalent of
+     *      while (count-- > 0) { // waste some time }
+     * We rely on EAX being used as the counter
+     * variable.
+     */
+
+    int result;
+    __asm__ __volatile__ (
+       "1: decl %%eax\n\t"
+       "cmpl $0, %%eax\n\t"
+       "nop; nop; nop; nop; nop; nop\n\t"
+       "nop; nop; nop; nop; nop; nop\n\t"
+       "jg 1b"
+       : "=a" (result)
+       : "a" (count)
+    );
+}
+
+/*
+ * Calibrate the delay loop.
+ * This will initialize s_spinCountPerTick, which indicates
+ * how many iterations of the loop are executed per timer tick.
+ */
+static void Calibrate_Delay(void)
+{
+    Disable_Interrupts();
+
+    /* Install temporarily interrupt handler */
+    Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
+    Enable_IRQ(TIMER_IRQ);
+
+    Enable_Interrupts();
+
+    /* Wait a few ticks */
+    while (g_numTicks < CALIBRATE_NUM_TICKS)
+       ;
+
+    /*
+     * Execute the spin loop.
+     * The temporary interrupt handler will overwrite the
+     * loop counter when the next tick occurs.
+     */
+    Spin(INT_MAX);
+
+    Disable_Interrupts();
+
+    /*
+     * Mask out the timer IRQ again,
+     * since we will be installing a real timer interrupt handler.
+     */
+    Disable_IRQ(TIMER_IRQ);
+    Enable_Interrupts();
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+void Init_Timer(void)
+{
+  ushort_t foo = 1193182L / HZ;
+  
+  PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
+
+  /* Calibrate for delay loop */
+  Calibrate_Delay();
+
+  PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
+  
+  // Set Timer to HZ
+
+  Out_Byte(0x43,0x36);          // channel 0, LSB/MSB, mode 3, binary
+  Out_Byte(0x40, foo & 0xff);   // LSB
+  Out_Byte(0x40, foo >>8);      // MSB
+    
+  /* Install an interrupt handler for the timer IRQ */
+
+  Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
+  Enable_IRQ(TIMER_IRQ);
+}
+
+
+#define US_PER_TICK (TICKS_PER_SEC * 1000000)
+
+/*
+ * Spin for at least given number of microseconds.
+ * FIXME: I'm sure this implementation leaves a lot to
+ * be desired.
+ */
+void Micro_Delay(int us)
+{
+    int num = us * s_spinCountPerTick;
+    int denom = US_PER_TICK;
+
+    int numSpins = num / denom;
+    int rem = num % denom;
+
+    if (rem > 0)
+       ++numSpins;
+
+    Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
+
+    Spin(numSpins);
+}
diff --git a/misc/test_vm/src/geekos/trap.c b/misc/test_vm/src/geekos/trap.c
new file mode 100644 (file)
index 0000000..2085a99
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Trap handlers
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/idt.h>
+#include <geekos/kthread.h>
+#include <geekos/defs.h>
+#include <geekos/trap.h>
+#include <geekos/serial.h>
+
+/*
+ * TODO: need to add handlers for other exceptions (such as bounds
+ * check, debug, etc.)
+ */
+
+/*
+ * Handler for general protection faults and other bad errors.
+ * Kill the current thread (which caused the fault).
+ */
+static void GPF_Handler(struct Interrupt_State* state)
+{
+    /* Send the thread to the reaper... */
+  SerialPrintLevel(1000,"Exception %d received, killing thread %p\n",state->intNum, g_currentThread);
+  Dump_Interrupt_State(state);
+  
+  Exit(-1);
+  
+  /* We will never get here */
+  KASSERT(false);
+}
+
+/*
+ * Initialize handlers for processor traps.
+ */
+void Init_Traps(void)
+{
+  PrintBoth("Initializing Traps\n");
+  Install_Interrupt_Handler(12, &GPF_Handler);  /* stack exception */
+  Install_Interrupt_Handler(13, &GPF_Handler);  /* general protection fault */
+}
diff --git a/misc/test_vm/src/geekos/tss.c b/misc/test_vm/src/geekos/tss.c
new file mode 100644 (file)
index 0000000..b97a844
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * x86 TSS data structure and routines
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ * 
+ * This is free software.  You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/defs.h>
+#include <geekos/gdt.h>
+#include <geekos/segment.h>
+#include <geekos/string.h>
+#include <geekos/tss.h>
+
+#include <geekos/serial.h>
+
+/*
+ * We use one TSS in GeekOS.
+ */
+static struct TSS s_theTSS;
+
+// JRL: why??? Should just call Alloc_Page(), otherwise we have dependencies all over the place
+//static struct TSS *s_theTSS = (struct TSS *) TSS_LOCATION;
+
+static struct Segment_Descriptor *s_tssDesc;
+static ushort_t s_tssSelector;
+
+static void __inline__ Load_Task_Register(void)
+{
+    /* Critical: TSS must be marked as not busy */
+    s_tssDesc->type = 0x09;
+
+    /* Load the task register */
+    __asm__ __volatile__ (
+       "ltr %0"
+       :
+       : "a" (s_tssSelector)
+    );
+}
+
+/*
+ * Initialize the kernel TSS.  This must be done after the memory and
+ * GDT initialization, but before the scheduler is started.
+ */
+void Init_TSS(void)
+{
+  PrintBoth("Initializing TSS\n");
+
+    s_tssDesc = Allocate_Segment_Descriptor();
+    KASSERT(s_tssDesc != 0);
+
+    memset(&s_theTSS, '\0', sizeof(struct TSS));
+    Init_TSS_Descriptor(s_tssDesc, &s_theTSS);
+
+    s_tssSelector = Selector(0, true, Get_Descriptor_Index(s_tssDesc));
+
+    Load_Task_Register();
+}
+
+/*
+ * Set kernel stack pointer.
+ * This should be called before switching to a new
+ * user process, so that interrupts occurring while executing
+ * in user mode will be delivered on the correct stack.
+ */
+void Set_Kernel_Stack_Pointer(ulong_t esp0)
+{
+    s_theTSS.ss0 = KERNEL_DS;
+    s_theTSS.esp0 = esp0;
+
+    /*
+     * NOTE: I read on alt.os.development that it is necessary to
+     * reload the task register after modifying a TSS.
+     * I haven't verified this in the IA32 documentation,
+     * but there is certainly no harm in being paranoid.
+     */
+    Load_Task_Register();
+}
diff --git a/misc/test_vm/src/geekos/util.asm b/misc/test_vm/src/geekos/util.asm
new file mode 100644 (file)
index 0000000..dd07dfd
--- /dev/null
@@ -0,0 +1,53 @@
+; This code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%ifndef UTIL_ASM
+%define UTIL_ASM
+%include "defs.asm"
+%include "symbol.asm"
+       
+[BITS 16]
+       
+; The following were copied from ktk-0.2 bootsect.asm, and were presumably
+; from the Linux bootsect code.  I changed them a little so they
+; don't clobber the caller's registers.
+
+EXPORT PrintHex
+
+[SECTION .text]
+; Print the word contained in the dx register to the screen.
+align 8
+PrintHex:
+       pusha
+       mov   cx, 4             ; 4 hex digits
+.PrintDigit:
+       rol   dx, 4             ; rotate so that lowest 4 bits are used
+       mov   ax, 0E0Fh         ; ah = request, al = mask for nybble
+       and   al, dl
+       add   al, 90h           ; convert al to ascii hex (four instructions)
+       daa                     ; I've spent 1 hour to understand how it works..
+       adc   al, 40h
+       daa
+       int   10h
+       loop  .PrintDigit
+       popa
+       ret
+
+; Print a newline.
+align 8
+PrintNL:                       ; print CR and NL
+       push    ax
+       mov     ax, 0E0Dh       ; CR
+               int     10h
+               mov     al, 0Ah         ; LF
+               int     10h
+       pop     ax
+               ret
+
+       
+%endif
diff --git a/misc/test_vm/src/libc/compat.c b/misc/test_vm/src/libc/compat.c
new file mode 100644 (file)
index 0000000..995a03b
--- /dev/null
@@ -0,0 +1,8 @@
+#include <conio.h>
+#include <stddef.h>
+
+void *Malloc(size_t n)
+{
+    Print("Malloc not implemented in user mode\n");
+    return 0;
+}