From: Jack Lange Date: Wed, 13 Feb 2008 21:37:35 +0000 (+0000) Subject: added guest vm X-Git-Tag: JACK-WORKING-STATIC-GEEKOS~3 X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=0893919c04fa1186a70039b739a6dcec0a2189ba added guest vm --- diff --git a/misc/test_vm/COPYING.geekos b/misc/test_vm/COPYING.geekos new file mode 100644 index 0000000..f07e852 --- /dev/null +++ b/misc/test_vm/COPYING.geekos @@ -0,0 +1,23 @@ +Copyright (c) 2001,2003,2004 David H. Hovemeyer +Copyright (c) 2003, Jeffrey K. Hollingsworth + +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 index 0000000..b512ff9 --- /dev/null +++ b/misc/test_vm/LICENSE-klibc.geekos @@ -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 index 0000000..332be74 --- /dev/null +++ b/misc/test_vm/README @@ -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 index 0000000..e95aa5a --- /dev/null +++ b/misc/test_vm/build/.bochsrc @@ -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 index 0000000..381ef25 --- /dev/null +++ b/misc/test_vm/build/Makefile @@ -0,0 +1,308 @@ +# Makefile for GeekOS kernel, userspace, and tools +# Copyright (c) 2004,2005 David H. Hovemeyer +# $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 index 0000000..e69de29 diff --git a/misc/test_vm/build/noboot.img b/misc/test_vm/build/noboot.img new file mode 100644 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 index 0000000..54fac8b --- /dev/null +++ b/misc/test_vm/include/geekos/argblock.h @@ -0,0 +1,34 @@ +/* + * Create and extract the command line argument block for a process + * Copyright (c) 2003 David H. Hovemeyer + * $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 index 0000000..63a0857 --- /dev/null +++ b/misc/test_vm/include/geekos/bget.h @@ -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 index 0000000..a099da8 --- /dev/null +++ b/misc/test_vm/include/geekos/blockdev.h @@ -0,0 +1,137 @@ +/* + * Block devices + * Copyright (c) 2003 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $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 +#include +#include +#include + +#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 index 0000000..bb50de1 --- /dev/null +++ b/misc/test_vm/include/geekos/bootinfo.h @@ -0,0 +1,18 @@ +/* + * Boot information structure, passed to kernel Main() routine + * Copyright (c) 2001, David H. Hovemeyer + * $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 index 0000000..e748f6b --- /dev/null +++ b/misc/test_vm/include/geekos/cpu.h @@ -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 index 0000000..1628b77 --- /dev/null +++ b/misc/test_vm/include/geekos/crc32.h @@ -0,0 +1,10 @@ +#ifndef GEEKOS_CRC32_H +#define GEEKOS_CRC32_H + +#include +#include + +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 index 0000000..07981f7 --- /dev/null +++ b/misc/test_vm/include/geekos/defs.h @@ -0,0 +1,113 @@ +/* + * Misc. kernel definitions + * Copyright (c) 2001,2004 David H. Hovemeyer + * $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< + * $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 index 0000000..d2f2572 --- /dev/null +++ b/misc/test_vm/include/geekos/fileio.h @@ -0,0 +1,83 @@ +/* + * Interface constants and typedefs shared between kernel/user space + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * Copyright (c) 2004, David H. Hovemeyer + * $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 +#include + +/* 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 index 0000000..cb3f168 --- /dev/null +++ b/misc/test_vm/include/geekos/fmtout.h @@ -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 index 0000000..3ae7b2b --- /dev/null +++ b/misc/test_vm/include/geekos/gdt.h @@ -0,0 +1,22 @@ +/* + * Initialize kernel GDT. + * Copyright (c) 2001, David H. Hovemeyer + * $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 index 0000000..5c33ecc --- /dev/null +++ b/misc/test_vm/include/geekos/ide.h @@ -0,0 +1,19 @@ +/* + * ATA (aka IDE) driver. + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $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 index 0000000..b88cf28 --- /dev/null +++ b/misc/test_vm/include/geekos/idt.h @@ -0,0 +1,67 @@ +/* + * GeekOS IDT initialization code + * Copyright (c) 2001, David H. Hovemeyer + * $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 + +/* + * 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 index 0000000..a80d142 --- /dev/null +++ b/misc/test_vm/include/geekos/int.h @@ -0,0 +1,180 @@ +/* + * GeekOS interrupt handling data structures and functions + * Copyright (c) 2001, David H. Hovemeyer + * $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 +#include +#include + +/* + * 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 index 0000000..bd23614 --- /dev/null +++ b/misc/test_vm/include/geekos/io.h @@ -0,0 +1,23 @@ +/* + * x86 port IO routines + * Copyright (c) 2001, David H. Hovemeyer + * $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 + +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 index 0000000..6e6e24a --- /dev/null +++ b/misc/test_vm/include/geekos/io_devs.h @@ -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 index 0000000..3164170 --- /dev/null +++ b/misc/test_vm/include/geekos/irq.h @@ -0,0 +1,28 @@ +/* + * This is the device-driver interface to the interrupt system. + * Copyright (c) 2001,2003 David H. Hovemeyer + * $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 + +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 index 0000000..434a8a8 --- /dev/null +++ b/misc/test_vm/include/geekos/kassert.h @@ -0,0 +1,81 @@ +/* + * Definition of KASSERT() macro, and other useful debug macros + * Copyright (c) 2001, David H. Hovemeyer + * $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 + +#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 index 0000000..1ab5c42 --- /dev/null +++ b/misc/test_vm/include/geekos/keyboard.h @@ -0,0 +1,131 @@ +/* + * Keyboard driver + * Copyright (c) 2001, David H. Hovemeyer + * $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 + +/* ---------------------------------------------------------------------- + * 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 index 0000000..f0ef23c --- /dev/null +++ b/misc/test_vm/include/geekos/kthread.h @@ -0,0 +1,158 @@ +/* + * Kernel threads + * Copyright (c) 2001,2003 David H. Hovemeyer + * $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 +#include + +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 index 0000000..7c72a3b --- /dev/null +++ b/misc/test_vm/include/geekos/ktypes.h @@ -0,0 +1,43 @@ +/* + * Kernel data types + * Copyright (c) 2001,2003 David H. Hovemeyer + * $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 + +/* + * 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 index 0000000..96a7870 --- /dev/null +++ b/misc/test_vm/include/geekos/list.h @@ -0,0 +1,131 @@ +/* + * Generic list data type + * Copyright (c) 2001,2004 David H. Hovemeyer + * $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 +#include + +/* + * 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 index 0000000..ba70e92 --- /dev/null +++ b/misc/test_vm/include/geekos/malloc.h @@ -0,0 +1,19 @@ +/* + * GeekOS memory allocation API + * Copyright (c) 2001, David H. Hovemeyer + * $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 + +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 index 0000000..87489f4 --- /dev/null +++ b/misc/test_vm/include/geekos/mem.h @@ -0,0 +1,132 @@ +/* + * Physical memory allocation + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $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 +#include +#include +#include + +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 index 0000000..fdac1aa --- /dev/null +++ b/misc/test_vm/include/geekos/mem_bak.h @@ -0,0 +1,119 @@ +/* + * Physical memory allocation + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $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 +#include +#include +#include + +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 index 0000000..1810f0b --- /dev/null +++ b/misc/test_vm/include/geekos/paging.h @@ -0,0 +1,131 @@ +/* + * Paging (virtual memory) support + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * Copyright (c) 2003,2004 David H. Hovemeyer + * $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 +#include +#include +#include + +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 index 0000000..15bd023 --- /dev/null +++ b/misc/test_vm/include/geekos/range.h @@ -0,0 +1,55 @@ +/* + * Range checking + * Copyright (c) 2003, David H. Hovemeyer + * $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 + +/* + * 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 index 0000000..e954a2c --- /dev/null +++ b/misc/test_vm/include/geekos/reboot.h @@ -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 index 0000000..241fc6f --- /dev/null +++ b/misc/test_vm/include/geekos/screen.h @@ -0,0 +1,57 @@ +/* + * GeekOS text screen output + * Copyright (c) 2001,2003 David H. Hovemeyer + * $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 + +#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 index 0000000..06f302a --- /dev/null +++ b/misc/test_vm/include/geekos/segment.h @@ -0,0 +1,90 @@ +/* + * General data structures and routines for segmentation + * Copyright (c) 2001, David H. Hovemeyer + * $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 + +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 index 0000000..4ebe0b0 --- /dev/null +++ b/misc/test_vm/include/geekos/serial.h @@ -0,0 +1,69 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#include +#include +#include +#include + +#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 index 0000000..661a148 --- /dev/null +++ b/misc/test_vm/include/geekos/string.h @@ -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 index 0000000..61e47bd --- /dev/null +++ b/misc/test_vm/include/geekos/symbol.h @@ -0,0 +1,20 @@ +/* + * Symbol mangling macros + * Copyright (c) 2001, David H. Hovemeyer + * $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 index 0000000..5cb95ef --- /dev/null +++ b/misc/test_vm/include/geekos/synch.h @@ -0,0 +1,44 @@ +/* + * Synchronization primitives + * Copyright (c) 2001, David H. Hovemeyer + * $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 + +/* + * 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 index 0000000..37dda14 --- /dev/null +++ b/misc/test_vm/include/geekos/timer.h @@ -0,0 +1,24 @@ +/* + * GeekOS timer interrupt support + * Copyright (c) 2001, David H. Hovemeyer + * $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 index 0000000..73c28b8 --- /dev/null +++ b/misc/test_vm/include/geekos/trap.h @@ -0,0 +1,15 @@ +/* + * Trap handlers + * Copyright (c) 2001, David H. Hovemeyer + * $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 index 0000000..66ea118 --- /dev/null +++ b/misc/test_vm/include/geekos/tss.h @@ -0,0 +1,89 @@ +/* + * x86 TSS data structure and routines + * Copyright (c) 2001,2004 David H. Hovemeyer + * $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 index 0000000..429d7fd --- /dev/null +++ b/misc/test_vm/include/libc/fmtout.h @@ -0,0 +1,37 @@ +/* + * Generalized support for printf()-style formatted output + * Copyright (c) 2004, David H. Hovemeyer + * $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 + +/* + * 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 index 0000000..fb146c0 --- /dev/null +++ b/misc/test_vm/include/libc/string.h @@ -0,0 +1,37 @@ +/* + * String library + * Copyright (c) 2001,2004 David H. Hovemeyer + * $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 + +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 , 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 index 0000000..98bffa6 --- /dev/null +++ b/misc/test_vm/scripts/eipToFunction @@ -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 \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 index 0000000..7b9b877 --- /dev/null +++ b/misc/test_vm/scripts/findaddr @@ -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 \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 index 0000000..1b76e81 --- /dev/null +++ b/misc/test_vm/scripts/generrs @@ -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 index 0000000..9241e3a --- /dev/null +++ b/misc/test_vm/scripts/kerninfo @@ -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 \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 index 0000000..d3f4477 --- /dev/null +++ b/misc/test_vm/scripts/mkcdisk @@ -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 \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 index 0000000..1581b4b --- /dev/null +++ b/misc/test_vm/scripts/mkuprog @@ -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 \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 index 0000000..708a893 --- /dev/null +++ b/misc/test_vm/scripts/numsecs @@ -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 \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 index 0000000..c1c5329 --- /dev/null +++ b/misc/test_vm/scripts/pad @@ -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 \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 index 0000000..d58f9fd --- /dev/null +++ b/misc/test_vm/scripts/pcat @@ -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 index 0000000..7edc728 --- /dev/null +++ b/misc/test_vm/scripts/pw @@ -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 \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 index 0000000..cbfe6dc --- /dev/null +++ b/misc/test_vm/scripts/scan @@ -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 \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 index 0000000..52e744a --- /dev/null +++ b/misc/test_vm/scripts/zerofile @@ -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 \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 index 0000000..c097eb4 --- /dev/null +++ b/misc/test_vm/src/common/fmtout.c @@ -0,0 +1,507 @@ +/* + * This code was originally part of klibc-0.103. + * It was adapted by David Hovemeyer + * 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 +#include +#include +#include +#include /* 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 ( o0 ) + 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 index 0000000..27f04a5 --- /dev/null +++ b/misc/test_vm/src/common/memmove.c @@ -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 + +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 index 0000000..7db896c --- /dev/null +++ b/misc/test_vm/src/common/string.c @@ -0,0 +1,261 @@ +/* + * String library + * Copyright (c) 2001,2004 David H. Hovemeyer + * $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 +#include + +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 index 0000000..52b6ec4 --- /dev/null +++ b/misc/test_vm/src/geekos/README.txt @@ -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 index 0000000..cccbe16 --- /dev/null +++ b/misc/test_vm/src/geekos/bget.c @@ -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 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 bytes, using the storage starting at + . You can call bpool() subsequently to contribute + additional storage to the overall buffer pool. + + void *bget(bufsize size); + + Allocate a buffer of 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 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 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 , 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 is non-NULL, whenever + a buffer allocation request fails, the 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 attempting to satisfy this + allocation request. The sequence number is 1 for the first call + on for a given allocation request, and increments on + subsequent calls, permitting the function to take + increasingly dire measures in an attempt to free up storage. If + the function returns a nonzero value, the allocation + attempt is re-tried. If 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 argument is non-NULL. At the time + the function is called, the state of the buffer + allocator is identical to that at the moment the allocation + request was made; consequently, the 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 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 function that released a buffer + in the process of being reallocated with bgetr() would lead to + disaster. Implementing a safe and effective mechanism + requires careful design of an application's memory architecture, + and cannot generally be easily retrofitted into existing code. + + If is non-NULL, that function will be called whenever an + allocation request fails. If the function succeeds in + allocating the requested space and returns a pointer to the new + area, allocation will proceed using the expanded buffer pool. If + cannot obtain the requested space, it should return NULL + and the entire allocation process will fail. + specifies the normal expansion block size. Providing an + function will cause subsequent bget() requests for buffers too + large to be managed in the linked-block scheme (in other words, + larger than minus the buffer overhead) to be satisfied + directly by calls to the function. Automatic release of + empty pool blocks will occur only if all pool blocks in the system + are the size given by . + + 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 . The total free space (sum of + all free blocks in the pool) is stored into the variable pointed + to by , and the size of the largest single block in the + free space pool is stored into the variable pointed to by + . The variables pointed to by and 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 , 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 . The variables pointed to by and + will be filled with, respectively, the number of expansion + block acquisitions and releases which have occurred. The + variables pointed to by and 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 is dumped on standard output. + + void bpoold(void *pool, int dumpalloc, int dumpfree); + + All buffers in the buffer pool , previously initialised by a + call on bpool(), are listed in ascending memory address order. If + is nonzero, the contents of allocated buffers are + dumped; if 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 // for memset() + +// Provide an assert() macro +#include +#define assert(exp) KASSERT(exp) + +#else // define (GEEKOS) + +#include + +#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 +#include + +#endif // defined (GEEKOS) + +#ifdef BufDump /* BufDump implies DumpData */ +#ifndef DumpData +#define DumpData 1 +#endif +#endif + +#ifdef DumpData +#include +#endif + +/* Declare the interface, including the requested buffer size type, + bufsize. */ + +#include + +#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 + +#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 index 0000000..5a4bf07 --- /dev/null +++ b/misc/test_vm/src/geekos/blockdev.c @@ -0,0 +1,265 @@ +/* + * Block devices + * Copyright (c) 2003 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/*#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 index 0000000..9986818 --- /dev/null +++ b/misc/test_vm/src/geekos/bootsect.asm @@ -0,0 +1,247 @@ +; Boot sector for GeekOS +; Copyright (c) 2001, David H. Hovemeyer +; $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 index 0000000..0165c3b --- /dev/null +++ b/misc/test_vm/src/geekos/crc32.c @@ -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 +#include +#include + +#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 index 0000000..72bdd08 --- /dev/null +++ b/misc/test_vm/src/geekos/defs.asm @@ -0,0 +1,84 @@ +; Definitions for use in GeekOS boot code +; Copyright (c) 2001, David H. Hovemeyer +; $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 . +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 index 0000000..6e60675 --- /dev/null +++ b/misc/test_vm/src/geekos/depend.mak @@ -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 index 0000000..d5fa120 --- /dev/null +++ b/misc/test_vm/src/geekos/fd_boot.asm @@ -0,0 +1,425 @@ +; -*- fundamental -*- +; Boot sector for GeekOS +; Copyright (c) 2001,2004 David H. Hovemeyer +; Copyright (c) 2003, Jeffrey K. Hollingsworth +; $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 index 0000000..30fb6ae --- /dev/null +++ b/misc/test_vm/src/geekos/fd_boot_bak.asm @@ -0,0 +1,472 @@ +; -*- fundamental -*- +; Boot sector for GeekOS +; Copyright (c) 2001,2004 David H. Hovemeyer +; Copyright (c) 2003, Jeffrey K. Hollingsworth +; $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 index 0000000..977c6a1 --- /dev/null +++ b/misc/test_vm/src/geekos/gdt.c @@ -0,0 +1,181 @@ +/* + * Initialize kernel GDT. + * Copyright (c) 2001,2004 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include + + +/* + * 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;iavail) { + ++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 index 0000000..22b02c2 --- /dev/null +++ b/misc/test_vm/src/geekos/ide.c @@ -0,0 +1,442 @@ +/* + * ATA (aka IDE) driver. + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * Copyright (c) 2003,2004 David H. Hovemeyer + * $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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 index 0000000..5e23989 --- /dev/null +++ b/misc/test_vm/src/geekos/idt.c @@ -0,0 +1,172 @@ +/* + * GeekOS IDT initialization code + * Copyright (c) 2001, David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- + * 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> 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 index 0000000..2c59a5f --- /dev/null +++ b/misc/test_vm/src/geekos/int.c @@ -0,0 +1,123 @@ +/* + * GeekOS interrupt handling data structures and functions + * Copyright (c) 2001,2003 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include /* x86-specific int handling stuff */ +#include +#include +#include +#include + +#include + +/* + * 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 index 0000000..3132733 --- /dev/null +++ b/misc/test_vm/src/geekos/io.c @@ -0,0 +1,80 @@ +/* + * x86 port IO routines + * Copyright (c) 2001, David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include + +/* + * 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 index 0000000..24c5104 --- /dev/null +++ b/misc/test_vm/src/geekos/irq.c @@ -0,0 +1,133 @@ +/* + * This is the device-driver interface to the interrupt system. + * Copyright (c) 2001,2003 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- + * 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 index 0000000..409d9a4 --- /dev/null +++ b/misc/test_vm/src/geekos/keyboard.c @@ -0,0 +1,329 @@ +/* + * Keyboard driver + * Copyright (c) 2001,2004 David H. Hovemeyer + * $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 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 +#include +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- + * 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 index 0000000..e2a6df2 --- /dev/null +++ b/misc/test_vm/src/geekos/kthread.c @@ -0,0 +1,818 @@ +/* + * Kernel threads + * Copyright (c) 2001,2003 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- + * 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 ¤t->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; jtlocalData[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(¤t->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 index 0000000..63f93c2 --- /dev/null +++ b/misc/test_vm/src/geekos/lowlevel.asm @@ -0,0 +1,576 @@ +; -*- fundamental -*- +; Low level interrupt/thread handling code for GeekOS. +; Copyright (c) 2001,2003,2004 David H. Hovemeyer +; Copyright (c) 2003, Jeffrey K. Hollingsworth +; $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 index 0000000..80283a4 --- /dev/null +++ b/misc/test_vm/src/geekos/main.c @@ -0,0 +1,290 @@ +/* + * GeekOS C code entry point + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * Copyright (c) 2004, Iulian Neamtiu + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* + 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"); + + + + + + + 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 index 0000000..ea76959 --- /dev/null +++ b/misc/test_vm/src/geekos/malloc.c @@ -0,0 +1,55 @@ +/* + * GeekOS memory allocation API + * Copyright (c) 2001, David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include + +/* + * 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 index 0000000..2f44520 --- /dev/null +++ b/misc/test_vm/src/geekos/mem.c @@ -0,0 +1,375 @@ +/* + * Physical memory allocation + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- + * 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 index 0000000..6b7557b --- /dev/null +++ b/misc/test_vm/src/geekos/mem_bak.c @@ -0,0 +1,336 @@ +/* + * Physical memory allocation + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include + +/* ---------------------------------------------------------------------- + * 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 index 0000000..b2837dc --- /dev/null +++ b/misc/test_vm/src/geekos/paging.c @@ -0,0 +1,430 @@ +/* + * Paging (virtual memory) support + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * Copyright (c) 2003,2004 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +#include +#include +#include + + +/* ---------------------------------------------------------------------- + * 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 %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;ipid, 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=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= 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;ipresent)); + + 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 index 0000000..08f307d --- /dev/null +++ b/misc/test_vm/src/geekos/reboot.c @@ -0,0 +1,161 @@ +#include +#include +// 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 index 0000000..9c78dde --- /dev/null +++ b/misc/test_vm/src/geekos/screen.c @@ -0,0 +1,533 @@ +/* + * GeekOS text screen output + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 index 0000000..4e83db0 --- /dev/null +++ b/misc/test_vm/src/geekos/segment.c @@ -0,0 +1,138 @@ +/* + * General data structures and routines for segmentation + * Copyright (c) 2001, David H. Hovemeyer + * $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 +#include +#include +#include + +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 index 0000000..68ef275 --- /dev/null +++ b/misc/test_vm/src/geekos/serial.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include + + + + +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=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 index 0000000..c88ed81 --- /dev/null +++ b/misc/test_vm/src/geekos/setup.asm @@ -0,0 +1,240 @@ +; -*- fundamental -*- +; GeekOS setup code +; Copyright (c) 2001,2004 David H. Hovemeyer +; $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 index 0000000..0bb1584 --- /dev/null +++ b/misc/test_vm/src/geekos/symbol.asm @@ -0,0 +1,43 @@ +; Symbol mangling macros +; Copyright (c) 2001, David H. Hovemeyer +; $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 index 0000000..86e7191 --- /dev/null +++ b/misc/test_vm/src/geekos/synch.c @@ -0,0 +1,206 @@ +/* + * Synchronization primitives + * Copyright (c) 2001,2004 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include + +/* + * 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 index 0000000..1ab713e --- /dev/null +++ b/misc/test_vm/src/geekos/testvm.s @@ -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 index 0000000..b3120d3 --- /dev/null +++ b/misc/test_vm/src/geekos/timer.c @@ -0,0 +1,222 @@ +/* + * GeekOS timer interrupt support + * Copyright (c) 2001,2003 David H. Hovemeyer + * Copyright (c) 2003, Jeffrey K. Hollingsworth + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include +#include + +#include + +#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 index 0000000..2085a99 --- /dev/null +++ b/misc/test_vm/src/geekos/trap.c @@ -0,0 +1,45 @@ +/* + * Trap handlers + * Copyright (c) 2001,2003,2004 David H. Hovemeyer + * $Revision: 1.1 $ + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "COPYING". + */ + +#include +#include +#include +#include +#include + +/* + * 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 index 0000000..b97a844 --- /dev/null +++ b/misc/test_vm/src/geekos/tss.c @@ -0,0 +1,85 @@ +/* + * x86 TSS data structure and routines + * Copyright (c) 2001,2004 David H. Hovemeyer + * $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 +#include +#include +#include +#include +#include + +#include + +/* + * 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 index 0000000..dd07dfd --- /dev/null +++ b/misc/test_vm/src/geekos/util.asm @@ -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 index 0000000..995a03b --- /dev/null +++ b/misc/test_vm/src/libc/compat.c @@ -0,0 +1,8 @@ +#include +#include + +void *Malloc(size_t n) +{ + Print("Malloc not implemented in user mode\n"); + return 0; +}