--- /dev/null
+Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software,
+and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- /dev/null
+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.
--- /dev/null
+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.
+
--- /dev/null
+# 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
+#=======================================================================
--- /dev/null
+# Makefile for GeekOS kernel, userspace, and tools
+# Copyright (c) 2004,2005 David H. Hovemeyer <daveho@cs.umd.edu>
+# $Revision: 1.1 $
+
+# This is free software. You are permitted to use,
+# redistribute, and modify it as specified in the file "COPYING".
+
+# Required software to build GeekOS:
+# - GNU Make (http://www.gnu.org/software/make)
+# - gcc 2.95.2 generating code for target (i386/ELF) and host platforms
+# - nasm (http://nasm.sourceforge.net)
+# - Perl5, AWK (any version), egrep
+#
+# Cygwin (http://cygwin.com) may be used to build GeekOS.
+# Make sure that gcc, binutils, nasm, and perl are installed.
+
+# NOTES:
+# - This makefile has been written carefully to work correctly
+# with the -j (parallel make) option. I regularly use "make -j 2"
+# to speed the build process on 2 processor systems.
+
+# Base address of kernel
+#
+# Note: at top of memory minus three pages (GDT/TSS/IDT)
+# minus maximum size
+#
+#
+# Note that the code will initially load at 0x10000
+#
+# The setup code needs to copy it up to this address and jump there
+#
+#KERNEL_BASE_ADDR := $(shell perl -e 'print sprintf("0x%x",$(TOP_OF_MEM)-4096*3-$(MAX_VMM));')
+KERNEL_BASE_ADDR := 0x100000
+
+# Kernel entry point function
+KERNEL_ENTRY = $(SYM_PFX)Main
+
+
+PROJECT_ROOT := ..
+VPATH := $(PROJECT_ROOT)/src
+
+#when -DNDEBUG is set the kassert functions are disabled
+#JRLDEBUG=-DNDEBUG
+JRLDEBUG= -DSERIAL_PRINT_DEBUG=1 -DSERIAL_PRINT_DEBUG_LEVEL=1000 -DSERIAL_PRINT=1
+
+#
+#
+#Peter's compile flags
+PAD=
+
+# Figure out if we're compiling with cygwin, http://cygwin.com
+SYSTEM_NAME := $(shell uname -s)
+ifeq ($(findstring CYGWIN,$(SYSTEM_NAME)),CYGWIN)
+SYM_PFX := _
+EXTRA_C_OPTS := -DNEED_UNDERSCORE -DGNU_WIN32
+EXTRA_NASM_OPTS := -DNEED_UNDERSCORE
+NON_ELF_SYSTEM := yes
+EXTRA_CC_USER_OPTS := -Dmain=geekos_main
+endif
+
+
+
+# ----------------------------------------------------------------------
+# Configuration -
+# Various options specifying how GeekOS should be built,
+# what source files to build, which user programs to build,
+# etc. This is generally the only section of the makefile
+# that will need to be modified.
+# ----------------------------------------------------------------------
+
+# List of targets to build by default.
+# These targets encompass everything needed to boot
+# and run GeekOS.
+ALL_TARGETS := geekos/kernel.bin fd.img
+
+
+# Kernel source files
+KERNEL_C_SRCS := idt.c int.c trap.c irq.c io.c \
+ blockdev.c ide.c \
+ keyboard.c screen.c timer.c \
+ mem.c crc32.c \
+ gdt.c tss.c segment.c \
+ bget.c malloc.c \
+ synch.c kthread.c \
+ serial.c reboot.c \
+ paging.c \
+ main.c
+
+# Kernel object files built from C source files
+KERNEL_C_OBJS := $(KERNEL_C_SRCS:%.c=geekos/%.o)
+
+# Kernel assembly files
+KERNEL_ASM_SRCS := lowlevel.asm
+
+KERNEL_GAS_SRCS :=
+
+# Kernel object files build from assembler source files
+KERNEL_ASM_OBJS := $(KERNEL_ASM_SRCS:%.asm=geekos/%.o)
+
+KERNEL_GAS_OBJS := $(KERNEL_GAS_SRCS:%.s=geekos/%.o)
+
+
+# All kernel object files
+KERNEL_OBJS := $(KERNEL_C_OBJS) \
+ $(KERNEL_ASM_OBJS) $(KERNEL_GAS_OBJS)
+
+# Common library source files.
+# This library is linked into both the kernel and user programs.
+# It provides string functions and generic printf()-style
+# formatted output.
+COMMON_C_SRCS := fmtout.c string.c memmove.c
+
+# Common library object files.
+COMMON_C_OBJS := $(COMMON_C_SRCS:%.c=common/%.o)
+
+
+
+
+# ----------------------------------------------------------------------
+# Tools -
+# This section defines programs that are used to build GeekOS.
+# ----------------------------------------------------------------------
+
+# Uncomment if cross compiling
+#TARGET_CC_PREFIX := i386-elf-
+
+# Target C compiler. gcc 2.95.2 or later should work.
+TARGET_CC := $(TARGET_CC_PREFIX)gcc
+#TARGET_CC := $(TARGET_CC_PREFIX)gcc34 -m32
+
+# Host C compiler. This is used to compile programs to execute on
+# the host platform, not the target (x86) platform. On x86/ELF
+# systems, such as Linux and FreeBSD, it can generally be the same
+# as the target C compiler.
+HOST_CC := gcc
+
+# Target linker. GNU ld is probably to only one that will work.
+TARGET_LD := $(TARGET_CC_PREFIX)ld -melf_i386
+
+# Target archiver
+TARGET_AR := $(TARGET_CC_PREFIX)ar
+
+# Target ranlib
+TARGET_RANLIB := $(TARGET_CC_PREFIX)ranlib
+
+# Target nm
+TARGET_NM := $(TARGET_CC_PREFIX)nm
+
+# Target objcopy
+TARGET_OBJCOPY := $(TARGET_CC_PREFIX)objcopy
+
+# Nasm (http://nasm.sourceforge.net)
+NASM := $(PROJECT_ROOT)/../devtools/bin/nasm
+#NASM := /opt/vmm-tools/bin/nasm
+
+AS = as --32
+
+# Tool to build PFAT filesystem images.
+BUILDFAT := tools/builtFat.exe
+
+# Perl5 or later
+PERL := perl
+
+# Pad a file so its size is a multiple of some unit (i.e., sector size)
+PAD := $(PERL) $(PROJECT_ROOT)/scripts/pad
+
+# Create a file filled with zeroes.
+ZEROFILE := $(PERL) $(PROJECT_ROOT)/scripts/zerofile
+
+# Calculate size of file in sectors
+NUMSECS := $(PERL) $(PROJECT_ROOT)/scripts/numsecs
+
+
+# ----------------------------------------------------------------------
+# Definitions -
+# Options passed to the tools.
+# ----------------------------------------------------------------------
+
+# Flags used for all C source files
+GENERAL_OPTS := -O -Wall $(EXTRA_C_OPTS) $(JRLDEBUG) $(PADFLAGS)
+CC_GENERAL_OPTS := $(GENERAL_OPTS) -Werror
+
+# Flags used for kernel C source files
+CC_KERNEL_OPTS := -g -DGEEKOS -I$(PROJECT_ROOT)/include
+
+# Flags user for kernel assembly files
+NASM_KERNEL_OPTS := -I$(PROJECT_ROOT)/src/geekos/ -f elf $(EXTRA_NASM_OPTS)
+
+# Flags used for common library and libc source files
+CC_USER_OPTS := -I$(PROJECT_ROOT)/include -I$(PROJECT_ROOT)/include/libc \
+ $(EXTRA_CC_USER_OPTS)
+
+# Flags passed to objcopy program (strip unnecessary sections from kernel.exe)
+OBJCOPY_FLAGS := -R .dynamic -R .note -R .comment
+
+# ----------------------------------------------------------------------
+# Rules -
+# Describes how to compile the source files.
+# ----------------------------------------------------------------------
+
+# Compilation of kernel C source files
+
+geekos/%.o : geekos/%.c
+ $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o
+
+
+# Compilation of kernel assembly source files
+geekos/%.o : geekos/%.asm
+ $(NASM) $(NASM_KERNEL_OPTS) $< -o geekos/$*.o
+
+# Compilation of test VM
+geekos/%.o : geekos/%.s
+ $(AS) $< -o geekos/$*.o
+
+geekos/%.o : geekos/%.S
+ $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) $< -o geekos/$*.o
+
+# Compilation of common library C source files
+common/%.o : common/%.c
+ $(TARGET_CC) -c $(CC_GENERAL_OPTS) $(CC_USER_OPTS) $< -o common/$*.o
+
+# ----------------------------------------------------------------------
+# Targets -
+# Specifies files to be built
+# ----------------------------------------------------------------------
+
+# Default target - see definition of ALL_TARGETS in Configuration section
+all : $(ALL_TARGETS)
+
+# Standard floppy image - just boots the kernel
+fd.img : geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin
+ cat geekos/fd_boot.bin geekos/setup.bin geekos/kernel.bin > $@
+
+
+# make ready to boot over PXE
+pxe: fd.img
+ cp fd.img /tftpboot/vmm.img
+ $(PAD) /tftpboot/vmm.img 1474560
+
+
+vm: geekos/kernel.bin
+ cp geekos/kernel.bin ../../vmm-hack1/build/vm_kernel
+
+geekos/test: geekos/test.o
+ $(CC) geekos/test.o -o geekos/test
+
+
+
+# Floppy boot sector (first stage boot loader).
+geekos/fd_boot.bin : geekos/setup.bin geekos/kernel.bin $(PROJECT_ROOT)/src/geekos/fd_boot.asm
+ $(NASM) -f bin \
+ -I$(PROJECT_ROOT)/src/geekos/ \
+ -DNUM_SETUP_SECTORS=`$(NUMSECS) geekos/setup.bin` \
+ -DNUM_KERN_SECTORS=`$(NUMSECS) geekos/kernel.bin` \
+ $(PROJECT_ROOT)/src/geekos/fd_boot.asm \
+ -o $@
+
+# Setup program (second stage boot loader).
+geekos/setup.bin : geekos/kernel.exe $(PROJECT_ROOT)/src/geekos/setup.asm
+ $(NASM) -f bin \
+ -I$(PROJECT_ROOT)/src/geekos/ \
+ -DENTRY_POINT=0x`egrep 'Main$$' geekos/kernel.syms |awk '{print $$1}'` \
+ $(PROJECT_ROOT)/src/geekos/setup.asm \
+ -o $@
+ $(PAD) $@ 512
+
+
+# Loadable (flat) kernel image.
+geekos/kernel.bin : geekos/kernel.exe
+ $(TARGET_OBJCOPY) $(OBJCOPY_FLAGS) -S -O binary geekos/kernel.exe geekos/kernel.bin
+ $(PAD) $@ 512
+
+# The kernel executable and symbol map.
+geekos/kernel.exe : $(KERNEL_OBJS) $(COMMON_C_OBJS)
+ $(TARGET_LD) -o geekos/kernel.exe -Ttext $(KERNEL_BASE_ADDR) -e $(KERNEL_ENTRY) \
+ $(KERNEL_OBJS) $(COMMON_C_OBJS)
+ $(TARGET_NM) geekos/kernel.exe > geekos/kernel.syms
+
+
+
+
+
+force:
+
+# Clean build directories of generated files
+clean :
+ for d in geekos common libc user tools; do \
+ (cd $$d && rm -f *); \
+ done
+
+
+# Build header file dependencies, so source files are recompiled when
+# header files they depend on are modified.
+depend : $(GENERATED_LIBC_SRCS)
+ $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_KERNEL_OPTS) \
+ $(KERNEL_C_SRCS:%.c=$(PROJECT_ROOT)/src/geekos/%.c) \
+ | $(PERL) -n -e 's,^(\S),geekos/$$1,;print' \
+ > depend.mak
+ $(TARGET_CC) -M $(CC_GENERAL_OPTS) $(CC_USER_OPTS) \
+ $(COMMON_C_SRCS:%.c=$(PROJECT_ROOT)/src/common/%.c) \
+ | $(PERL) -n -e 's,^(\S),common/$$1,;print' \
+ >> depend.mak
+
+# By default, there are no header file dependencies.
+depend.mak :
+ touch $@
+
+include depend.mak
--- /dev/null
+/*
+ * Create and extract the command line argument block for a process
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_ARGBLOCK_H
+#define GEEKOS_ARGBLOCK_H
+
+/**
+ * Header struct for accessing argument block from user mode.
+ * Just cast the address of the argument block passed by
+ * the kernel to a pointer to this struct.
+ */
+struct Argument_Block {
+ int argc;
+ char **argv;
+};
+
+#ifdef GEEKOS
+
+/*
+ * Functions used by the kernel to create the argument block.
+ */
+void Get_Argument_Block_Size(const char *command, unsigned *numArgs, ulong_t *argBlockSize);
+void Format_Argument_Block(char *argBlock, unsigned numArgs, ulong_t userAddress,
+ const char *command);
+
+#endif
+
+#endif /* GEEKOS_ARGBLOCK_H */
--- /dev/null
+/*
+
+ 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));
--- /dev/null
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_BLOCKDEV_H
+#define GEEKOS_BLOCKDEV_H
+
+#include <geekos/ktypes.h>
+#include <geekos/kthread.h>
+#include <geekos/list.h>
+#include <geekos/fileio.h>
+
+#ifdef GEEKOS
+
+/*
+ * Type of block device request.
+ */
+enum Request_Type {
+ BLOCK_READ, BLOCK_WRITE
+};
+
+/*
+ * State of a block I/O request.
+ */
+enum Request_State {
+ PENDING, COMPLETED, ERROR
+};
+
+struct Block_Request;
+
+/*
+ * List of block I/O requests.
+ */
+DEFINE_LIST(Block_Request_List, Block_Request);
+
+/*
+ * An I/O request for a block device.
+ */
+struct Block_Request {
+ struct Block_Device *dev;
+ enum Request_Type type;
+ int blockNum;
+ void *buf;
+ volatile enum Request_State state;
+ volatile int errorCode;
+ struct Thread_Queue waitQueue;
+
+ DEFINE_LINK(Block_Request_List, Block_Request);
+};
+
+IMPLEMENT_LIST(Block_Request_List, Block_Request);
+
+struct Block_Device;
+struct Block_Device_Ops;
+
+/*
+ * A block device.
+ */
+struct Block_Device {
+ char name[BLOCKDEV_MAX_NAME_LEN];
+ struct Block_Device_Ops *ops;
+ int unit;
+ bool inUse;
+ void *driverData;
+ struct Thread_Queue *waitQueue;
+ struct Block_Request_List *requestQueue;
+
+ DEFINE_LINK(Block_Device_List, Block_Device);
+};
+
+/*
+ * Operations that may be requested on block devices.
+ */
+struct Block_Device_Ops {
+ int (*Open)(struct Block_Device *dev);
+ int (*Close)(struct Block_Device *dev);
+ int (*Get_Num_Blocks)(struct Block_Device *dev);
+};
+
+/*
+ * Low level block device API.
+ * Only block device drivers need to use these functions.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+ int unit, void *driverData, struct Thread_Queue *waitQueue,
+ struct Block_Request_List *requestQueue);
+int Open_Block_Device(const char *name, struct Block_Device **pDev);
+int Close_Block_Device(struct Block_Device *dev);
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+ int blockNum, void *buf);
+void Post_Request_And_Wait(struct Block_Request *request);
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+ struct Thread_Queue *waitQueue);
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode);
+
+/*
+ * High level block device API.
+ * For use by filesystem and disk paging code.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf);
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf);
+int Get_Num_Blocks(struct Block_Device *dev);
+
+/*
+ * Misc. routines
+ */
+
+/*
+ * Round offset up to nearest sector.
+ */
+static __inline__ ulong_t Round_Up_To_Block(ulong_t offset)
+{
+ return (offset % SECTOR_SIZE) == 0
+ ? offset
+ : offset + (SECTOR_SIZE - (offset % SECTOR_SIZE));
+}
+
+/*
+ * Round offset down to nearest sector.
+ */
+static __inline__ ulong_t Round_Down_To_Block(ulong_t offset)
+{
+ return (offset % SECTOR_SIZE) == 0
+ ? offset
+ : offset - (offset % SECTOR_SIZE);
+}
+
+#endif /* GEEKOS */
+
+#endif
+
--- /dev/null
+/*
+ * Boot information structure, passed to kernel Main() routine
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_BOOTINFO_H
+#define GEEKOS_BOOTINFO_H
+
+struct Boot_Info {
+ int bootInfoSize; /* size of this struct; for versioning */
+ int memSizeKB; /* number of KB, as reported by int 15h */
+};
+
+#endif /* GEEKOS_BOOTINFO_H */
--- /dev/null
+#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
--- /dev/null
+#ifndef GEEKOS_CRC32_H
+#define GEEKOS_CRC32_H
+
+#include <stddef.h>
+#include <geekos/ktypes.h>
+
+void Init_CRC32(void);
+ulong_t crc32(ulong_t crc, char const *buf, size_t len);
+
+#endif /* GEEKOS_CRC32_H */
--- /dev/null
+/*
+ * Misc. kernel definitions
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_DEFS_H
+#define GEEKOS_DEFS_H
+
+
+
+
+/*
+ * Kernel code and data segment selectors.
+ * Keep these up to date with defs.asm.
+ */
+#define KERNEL_CS (1<<3)
+#define KERNEL_DS (2<<3)
+
+
+/*
+ * Address where kernel is loaded INITIALLY
+ * we move it up in memory soon
+ */
+#define KERNEL_START_ADDR 0x100000
+
+/*
+ * Kernel and user privilege levels
+ */
+#define KERNEL_PRIVILEGE 0
+#define USER_PRIVILEGE 3
+
+
+/*
+ * Software interrupt for syscalls
+ */
+#define SYSCALL_INT 0x90
+
+/*
+ * The windows versions of gcc use slightly different
+ * names for the bss begin and end symbols than the Linux version.
+ */
+#if defined(GNU_WIN32)
+# define BSS_START _bss_start__
+# define BSS_END _bss_end__
+#else
+# define BSS_START __bss_start
+# define BSS_END end
+#endif
+
+/*
+ * x86 has 4096 byte pages
+ */
+#define PAGE_POWER 12
+#define PAGE_SIZE (1<<PAGE_POWER)
+#define PAGE_MASK (~(0xffffffff << PAGE_POWER))
+
+
+
+/* Ultimate Memory Layout
+
+0:
+4096: KernelThreadObject (one page)
+ KernelStack (one page)
+ GDT (one page)
+ TSS (one page)
+ IDT (one page)
+0x100000: KernelImage
+ Page List (varies - must be an integral number of pages)
+ KernelHeap (varies - KERNEL_HEAP_SIZE; must be integral number of pages)
+*/
+
+
+
+
+#define GDT_SIZE PAGE_SIZE
+#define TSS_SIZE PAGE_SIZE
+#define IDT_SIZE PAGE_SIZE
+//#define KERNEL_HEAP_SIZE (256*PAGE_SIZE)
+#define KERNEL_STACK_SIZE PAGE_SIZE
+#define KERNEL_THREAD_OBJECT_SIZE PAGE_SIZE
+
+
+
+
+/*
+ * * Keep these up to date with defs.asm.
+ */
+#define GDT_LOCATION (PAGE_SIZE * 3)
+#define TSS_LOCATION (GDT_LOCATION + TSS_SIZE)
+#define IDT_LOCATION (TSS_LOCATION + IDT_SIZE)
+
+
+
+
+ #define KERNEL_THREAD_OBJECT (PAGE_SIZE)
+ #define KERNEL_STACK (KERNEL_THREAD_OBJECT + KERNEL_THREAD_OBJECT_SIZE)
+/*
+ #define KERNEL_HEAP (KERNEL_STACK + KERNEL_STACK_SIZE)
+ #define KERNEL_PAGELIST (KERNEL_HEAP + KERNEL_HEAP_SIZE)
+*/
+
+/*
+ * PC memory map
+ */
+#define ISA_HOLE_START 0x0A0000
+#define ISA_HOLE_END 0x100000
+
+
+#endif /* GEEKOS_DEFS_H */
--- /dev/null
+/*
+ * GeekOS error codes
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_ERRNO_H
+#define GEEKOS_ERRNO_H
+
+/*
+ * Error codes returned by kernel functions and
+ * system calls. These are meant to be returned to user
+ * code to describe system call failures.
+ */
+#define EUNSPECIFIED -1 /* Unspecified error */
+#define ENOTFOUND -2 /* No such file or directory */
+#define EUNSUPPORTED -3 /* Operation not supported */
+#define ENODEV -4 /* No such device */
+#define EIO -5 /* Input/output error */
+#define EBUSY -6 /* Resource in use */
+#define ENOMEM -7 /* Out of memory */
+#define ENOFILESYS -8 /* No such filesystem */
+#define ENAMETOOLONG -9 /* Name too long */
+#define EINVALIDFS -10 /* Invalid format for filesystem */
+#define EACCESS -11 /* Permission denied */
+#define EINVALID -12 /* Invalid argument */
+#define EMFILE -13 /* File descriptor table full */
+#define ENOTDIR -14 /* Not a directory */
+#define EEXIST -15 /* File or directory already exists */
+#define ENOSPACE -16 /* Out of space on device */
+#define EPIPE -17 /* Pipe has no reader */
+#define ENOEXEC -18 /* Invalid executable format */
+
+#endif /* GEEKOS_ERRNO_H */
--- /dev/null
+/*
+ * Interface constants and typedefs shared between kernel/user space
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2004, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_FILEIO_H
+#define GEEKOS_FILEIO_H
+
+#include <stddef.h>
+#include <geekos/ktypes.h>
+
+/* Maximum name of a path. */
+#define VFS_MAX_PATH_LEN 1023
+
+/* Maximum length of the name of a filesystem type: e.g., "pfat", "gosfs", etc. */
+#define VFS_MAX_FS_NAME_LEN 15
+
+/* Maximum number of ACL entries in a directory entry. */
+#define VFS_MAX_ACL_ENTRIES 10
+
+/* We assume that all block devices have 512 byte sectors. */
+#define SECTOR_SIZE 512
+
+/* Maximum length for the name of a block device, e.g. "ide0". */
+#define BLOCKDEV_MAX_NAME_LEN 15
+
+/*
+ * File permissions.
+ * These are used as flags for Open() VFS function.
+ * O_READ and O_WRITE are also used in the permissions
+ * field of struct VFS_ACL_Entry.
+ */
+#define O_CREATE 0x1 /* Create the file if it doesn't exist. */
+#define O_READ 0x2 /* Open file for reading. */
+#define O_WRITE 0x4 /* Open file for writing. */
+#define O_EXCL 0x8 /* Don't create file if it already exists. */
+
+/*
+ * An entry in an Access Control List (ACL).
+ * Represents a set of permissions for a particular user id.
+ */
+struct VFS_ACL_Entry {
+ uint_t uid:28;
+ uint_t permission:4;
+};
+
+/*
+ * Generic structure representing the metadata for a directory entry.
+ * This is filled in by the Stat() and FStat() VFS functions.
+ */
+struct VFS_File_Stat {
+ int size;
+ int isDirectory:1;
+ int isSetuid:1;
+ struct VFS_ACL_Entry acls[VFS_MAX_ACL_ENTRIES];
+};
+
+/*
+ * Generic directory entry structure.
+ * This is filled in by the Read_Entry() VFS function.
+ */
+struct VFS_Dir_Entry {
+ char name[1024];
+ struct VFS_File_Stat stats;
+};
+
+/*
+ * A request to mount a filesystem.
+ * This is passed as a struct because it would require too many registers
+ * to pass all of the information in registers.
+ */
+struct VFS_Mount_Request {
+ char devname[BLOCKDEV_MAX_NAME_LEN+1];/* Name of block device: e.g., "ide1". */
+ char prefix[VFS_MAX_PATH_LEN+1]; /* Directory prefix to mount on: e.g., "/d". */
+ char fstype[VFS_MAX_FS_NAME_LEN+1]; /* Filesystem type: e.g., "gosfs". */
+};
+
+#endif
--- /dev/null
+#include "../libc/fmtout.h"
--- /dev/null
+/*
+ * Initialize kernel GDT.
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_GDT_H
+#define GEEKOS_GDT_H
+
+struct Segment_Descriptor;
+
+void DumpGDT();
+void Init_GDT(void);
+struct Segment_Descriptor* Allocate_Segment_Descriptor(void);
+void Free_Segment_Descriptor(struct Segment_Descriptor* desc);
+int Get_Descriptor_Index(struct Segment_Descriptor* desc);
+
+
+#endif /* GEEKOS_GDT_H */
--- /dev/null
+/*
+ * ATA (aka IDE) driver.
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IDE_H
+#define GEEKOS_IDE_H
+
+#ifdef GEEKOS
+
+void Init_IDE(void);
+
+#endif /* GEEKOS */
+
+#endif /* GEEKOS_IDE_H */
--- /dev/null
+/*
+ * GeekOS IDT initialization code
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IDT_H
+#define GEEKOS_IDT_H
+
+#include <geekos/int.h>
+
+/*
+ * We'll handle all possible interrupts.
+ */
+#define NUM_IDT_ENTRIES 256
+
+/*
+ * Exceptions range from 0-17
+ */
+#define FIRST_EXCEPTION 0
+#define NUM_EXCEPTIONS 18
+
+/*
+ * External IRQs range from 32-47
+ */
+#define FIRST_EXTERNAL_INT 32
+#define NUM_EXTERNAL_INTS 16
+
+struct Interrupt_Gate {
+ ushort_t offsetLow;
+ ushort_t segmentSelector;
+ unsigned reserved : 5;
+ unsigned signature : 8;
+ unsigned dpl : 2;
+ unsigned present : 1;
+ ushort_t offsetHigh;
+};
+
+union IDT_Descriptor {
+ struct Interrupt_Gate ig;
+ /*
+ * In theory we could have members for trap gates
+ * and task gates if we wanted.
+ */
+};
+
+
+
+void DumpIDT();
+void SerialDumpIDT();
+
+void Init_IDT(void);
+void Init_Interrupt_Gate(union IDT_Descriptor* desc, ulong_t addr,
+ int dpl);
+void Install_Interrupt_Handler(int interrupt, Interrupt_Handler handler);
+
+/*
+ * This is defined in lowlevel.asm.
+ * The parameter should consist of 16 bit base,
+ * followed by 32 bit base address, describing the IDT.
+ */
+void Load_IDTR(ushort_t* limitAndBase);
+
+#endif /* GEEKOS_IDT_H */
--- /dev/null
+/*
+ * GeekOS interrupt handling data structures and functions
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * This module describes the C interface which must be implemented
+ * by interrupt handlers, and has the initialization function
+ * for the interrupt system as a whole.
+ */
+
+#ifndef GEEKOS_INT_H
+#define GEEKOS_INT_H
+
+#include <geekos/kassert.h>
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+
+/*
+ * This struct reflects the contents of the stack when
+ * a C interrupt handler function is called.
+ * It must be kept up to date with the code in "lowlevel.asm".
+ */
+struct Interrupt_State {
+ /*
+ * The register contents at the time of the exception.
+ * We save these explicitly.
+ */
+ uint_t gs;
+ uint_t fs;
+ uint_t es;
+ uint_t ds;
+ uint_t ebp;
+ uint_t edi;
+ uint_t esi;
+ uint_t edx;
+ uint_t ecx;
+ uint_t ebx;
+ uint_t eax;
+
+ /*
+ * We explicitly push the interrupt number.
+ * This makes it easy for the handler function to determine
+ * which interrupt occurred.
+ */
+ uint_t intNum;
+
+ /*
+ * This may be pushed by the processor; if not, we push
+ * a dummy error code, so the stack layout is the same
+ * for every type of interrupt.
+ */
+ uint_t errorCode;
+
+ /* These are always pushed on the stack by the processor. */
+ uint_t eip;
+ uint_t cs;
+ uint_t eflags;
+};
+
+/*
+ * An interrupt that occurred in user mode.
+ * If Is_User_Interrupt(state) returns true, then the
+ * Interrupt_State object may be cast to this kind of struct.
+ */
+struct User_Interrupt_State {
+ struct Interrupt_State state;
+ uint_t espUser;
+ uint_t ssUser;
+};
+
+static __inline__ bool Is_User_Interrupt(struct Interrupt_State *state)
+{
+ return (state->cs & 3) == USER_PRIVILEGE;
+}
+
+
+/*
+ * The signature of an interrupt handler.
+ */
+typedef void (*Interrupt_Handler)(struct Interrupt_State* state);
+
+/*
+ * Perform all low- and high-level initialization of the
+ * interrupt system.
+ */
+void Init_Interrupts(void);
+
+/*
+ * Query whether or not interrupts are currently enabled.
+ */
+bool Interrupts_Enabled(void);
+
+/*
+ * Block interrupts.
+ */
+static __inline__ void __Disable_Interrupts(void)
+{
+ __asm__ __volatile__ ("cli");
+}
+#define Disable_Interrupts() \
+do { \
+ KASSERT(Interrupts_Enabled()); \
+ __Disable_Interrupts(); \
+} while (0)
+
+/*
+ * Unblock interrupts.
+ */
+static __inline__ void __Enable_Interrupts(void)
+{
+ __asm__ __volatile__ ("sti");
+}
+#define Enable_Interrupts() \
+do { \
+ KASSERT(!Interrupts_Enabled()); \
+ __Enable_Interrupts(); \
+} while (0)
+
+/*
+ * Dump interrupt state struct to screen
+ */
+void Dump_Interrupt_State(struct Interrupt_State* state);
+
+/**
+ * Start interrupt-atomic region.
+ * @return true if interrupts were enabled at beginning of call,
+ * false otherwise.
+ */
+static __inline__ bool Begin_Int_Atomic(void)
+{
+ bool enabled = Interrupts_Enabled();
+ if (enabled)
+ Disable_Interrupts();
+ return enabled;
+}
+
+/**
+ * End interrupt-atomic region.
+ * @param iflag the value returned from the original Begin_Int_Atomic() call.
+ */
+static __inline__ void End_Int_Atomic(bool iflag)
+{
+ KASSERT(!Interrupts_Enabled());
+ if (iflag) {
+ /* Interrupts were originally enabled, so turn them back on */
+ Enable_Interrupts();
+ }
+}
+
+#define EXCEPTION_DE 0 // Divide by zero
+#define EXCEPTION_DB 1 // reserved
+#define EXCEPTION_NMI 2 // NMI
+#define EXCEPTION_BP 3 // Breakpoint
+#define EXCEPTION_OF 4 // Overflow
+#define EXCEPTION_BR 5 // Bound range exceeded
+#define EXCEPTION_UD 6 // Undefined opcode
+#define EXCEPTION_NM 7 // Math coprocessor gone missing
+#define EXCEPTION_DF 8 // Double fault
+#define EXCEPTION_CO 9 // Coprocessor segment overrrun
+#define EXCEPTION_TS 10 // Invalid TSS
+#define EXCEPTION_NP 11 // Segment not present
+#define EXCEPTION_SS 12 // Stack segment fault
+#define EXCEPTION_GP 13 // General Protection fault
+#define EXCEPTION_PF 14 // Page Fault
+#define EXCEPTION_RES 15 // reserved
+#define EXCEPTION_MF 16 // Math fault
+#define EXCEPTION_AC 17 // Alignment check
+#define EXCEPTION_MC 18 // Machine check
+#define EXCEPTION_XF 19 // SIMD FP exception
+// 20+ are reserved
+
+
+
+
+#endif /* GEEKOS_INT_H */
--- /dev/null
+/*
+ * x86 port IO routines
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IO_H
+#define GEEKOS_IO_H
+
+#include <geekos/ktypes.h>
+
+void Out_Byte(ushort_t port, uchar_t value);
+uchar_t In_Byte(ushort_t port);
+
+void Out_Word(ushort_t port, ushort_t value);
+ushort_t In_Word(ushort_t port);
+
+void IO_Delay(void);
+
+#endif /* GEEKOS_IO_H */
--- /dev/null
+#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
--- /dev/null
+/*
+ * This is the device-driver interface to the interrupt system.
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_IRQ_H
+#define GEEKOS_IRQ_H
+
+#include <geekos/int.h>
+
+void Install_IRQ(int irq, Interrupt_Handler handler);
+ushort_t Get_IRQ_Mask(void);
+void Set_IRQ_Mask(ushort_t mask);
+void Enable_IRQ(int irq);
+void Disable_IRQ(int irq);
+
+/*
+ * IRQ handlers should call these to begin and end the
+ * interrupt.
+ */
+void Begin_IRQ(struct Interrupt_State* state);
+void End_IRQ(struct Interrupt_State* state);
+
+#endif /* GEEKOS_IRQ_H */
--- /dev/null
+/*
+ * Definition of KASSERT() macro, and other useful debug macros
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KASSERT_H
+#define GEEKOS_KASSERT_H
+
+#include <geekos/screen.h>
+
+#ifndef NDEBUG
+
+struct Kernel_Thread;
+extern struct Kernel_Thread* g_currentThread;
+
+#define KASSERT(cond) \
+do { \
+ if (!(cond)) { \
+ Set_Current_Attr(ATTRIB(RED, GRAY|BRIGHT)); \
+ Print("Failed assertion in %s: %s at %s, line %d, RA=%lx, thread=%p\n",\
+ __func__, #cond, __FILE__, __LINE__, \
+ (ulong_t) __builtin_return_address(0), \
+ g_currentThread); \
+ while (1) \
+ ; \
+ } \
+} while (0)
+
+#define TODO(message) \
+do { \
+ Set_Current_Attr(ATTRIB(BLUE, GRAY|BRIGHT)); \
+ Print("Unimplemented feature: %s\n", (message)); \
+ while (1) \
+ ; \
+} while (0)
+
+/*
+ * Spin for some number of iterations.
+ * This is useful for slowing down things that go by too
+ * quickly.
+ */
+#define PAUSE(count) \
+do { \
+ ulong_t i; \
+ for (i = 0; i < (count); ++i) \
+ ; \
+} while (0)
+
+#else
+
+/*
+ * The debug macros are no-ops when NDEBUG is defined.
+ */
+#define KASSERT(cond)
+#define TODO(message)
+#define PAUSE(count)
+
+#endif
+
+/*
+ * Stop dead.
+ * Its behavior does not depend on whether or not this
+ * is a debug build.
+ */
+#define STOP() while (1)
+
+/*
+ * Panic function.
+ */
+#define Panic(args...) \
+do { \
+ Set_Current_Attr(ATTRIB(RED, GRAY|BRIGHT)); \
+ Print(args); \
+ while (1) ; \
+} while (0)
+
+#endif /* GEEKOS_KASSERT_H */
--- /dev/null
+/*
+ * Keyboard driver
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KEYBOARD_H
+#define GEEKOS_KEYBOARD_H
+
+#include <geekos/ktypes.h>
+
+/* ----------------------------------------------------------------------
+ * Hardware stuff
+ * ---------------------------------------------------------------------- */
+
+#ifdef GEEKOS
+
+#define KB_IRQ 1
+
+/*
+ * I/O ports
+ */
+#define KB_CMD 0x64
+#define KB_DATA 0x60
+
+/*
+ * Bits in status port
+ */
+#define KB_OUTPUT_FULL 0x01
+
+/*
+ * High bit in scan code is set when key is released
+ */
+#define KB_KEY_RELEASE 0x80
+
+#endif /* GEEKOS */
+
+/* ----------------------------------------------------------------------
+ * Software keycodes
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Each keyboard event generates a 16 bit code.
+ * - The low 10 bits indicate which key was used.
+ * - If bit 8 (KEY_SPECIAL_FLAG) is 0, then the low 8 bits contain
+ * the ASCII code.
+ * - The flags indicate the shift/alt/control state,
+ * and whether the event was a make or release.
+ */
+
+typedef ushort_t Keycode;
+
+/*
+ * Flags
+ */
+#define KEY_SPECIAL_FLAG 0x0100
+#define KEY_KEYPAD_FLAG 0x0200
+#define KEY_SHIFT_FLAG 0x1000
+#define KEY_ALT_FLAG 0x2000
+#define KEY_CTRL_FLAG 0x4000
+#define KEY_RELEASE_FLAG 0x8000
+
+/*
+ * Special key codes
+ */
+#define _SPECIAL(num) (KEY_SPECIAL_FLAG | (num))
+#define KEY_UNKNOWN _SPECIAL(0)
+#define KEY_F1 _SPECIAL(1)
+#define KEY_F2 _SPECIAL(2)
+#define KEY_F3 _SPECIAL(3)
+#define KEY_F4 _SPECIAL(4)
+#define KEY_F5 _SPECIAL(5)
+#define KEY_F6 _SPECIAL(6)
+#define KEY_F7 _SPECIAL(7)
+#define KEY_F8 _SPECIAL(8)
+#define KEY_F9 _SPECIAL(9)
+#define KEY_F10 _SPECIAL(10)
+#define KEY_F11 _SPECIAL(11)
+#define KEY_F12 _SPECIAL(12)
+#define KEY_LCTRL _SPECIAL(13)
+#define KEY_RCTRL _SPECIAL(14)
+#define KEY_LSHIFT _SPECIAL(15)
+#define KEY_RSHIFT _SPECIAL(16)
+#define KEY_LALT _SPECIAL(17)
+#define KEY_RALT _SPECIAL(18)
+#define KEY_PRINTSCRN _SPECIAL(19)
+#define KEY_CAPSLOCK _SPECIAL(20)
+#define KEY_NUMLOCK _SPECIAL(21)
+#define KEY_SCRLOCK _SPECIAL(22)
+#define KEY_SYSREQ _SPECIAL(23)
+
+/*
+ * Keypad keys
+ */
+#define KEYPAD_START 128
+#define _KEYPAD(num) (KEY_KEYPAD_FLAG | KEY_SPECIAL_FLAG | (num+KEYPAD_START))
+#define KEY_KPHOME _KEYPAD(0)
+#define KEY_KPUP _KEYPAD(1)
+#define KEY_KPPGUP _KEYPAD(2)
+#define KEY_KPMINUS _KEYPAD(3)
+#define KEY_KPLEFT _KEYPAD(4)
+#define KEY_KPCENTER _KEYPAD(5)
+#define KEY_KPRIGHT _KEYPAD(6)
+#define KEY_KPPLUS _KEYPAD(7)
+#define KEY_KPEND _KEYPAD(8)
+#define KEY_KPDOWN _KEYPAD(9)
+#define KEY_KPPGDN _KEYPAD(10)
+#define KEY_KPINSERT _KEYPAD(11)
+#define KEY_KPDEL _KEYPAD(12)
+
+/*
+ * ASCII codes for which there is no convenient C representation
+ */
+#define ASCII_ESC 0x1B
+#define ASCII_BS 0x08
+
+#ifdef GEEKOS
+
+/*
+ * Public functions
+ */
+void Init_Keyboard(void);
+bool Read_Key(Keycode* keycode);
+Keycode Wait_For_Key(void);
+
+#endif /* GEEKOS */
+
+#endif /* GEEKOS_KEYBOARD_H */
--- /dev/null
+/*
+ * Kernel threads
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KTHREAD_H
+#define GEEKOS_KTHREAD_H
+
+#include <geekos/ktypes.h>
+#include <geekos/list.h>
+
+struct Kernel_Thread;
+struct User_Context;
+struct Interrupt_State;
+
+/*
+ * Queue of threads.
+ * This is used for the run queue(s), and also for
+ * thread synchronization and communication.
+ */
+DEFINE_LIST(Thread_Queue, Kernel_Thread);
+
+/*
+ * List which includes all threads.
+ */
+DEFINE_LIST(All_Thread_List, Kernel_Thread);
+
+/*
+ * Kernel thread context data structure.
+ * NOTE: there is assembly code in lowlevel.asm that depends
+ * on the offsets of the fields in this struct, so if you change
+ * the layout, make sure everything gets updated.
+ */
+struct Kernel_Thread {
+ ulong_t esp; /* offset 0 */
+ volatile ulong_t numTicks; /* offset 4 */
+ int priority;
+ DEFINE_LINK(Thread_Queue, Kernel_Thread);
+ void* stackPage;
+ struct User_Context* userContext;
+ struct Kernel_Thread* owner;
+ int refCount;
+
+ /* These fields are used to implement the Join() function */
+ bool alive;
+ struct Thread_Queue joinQueue;
+ int exitCode;
+
+ /* The kernel thread id; also used as process id */
+ int pid;
+
+ /* Link fields for list of all threads in the system. */
+ DEFINE_LINK(All_Thread_List, Kernel_Thread);
+
+ /* Array of MAX_TLOCAL_KEYS pointers to thread-local data. */
+#define MAX_TLOCAL_KEYS 128
+ const void* tlocalData[MAX_TLOCAL_KEYS];
+
+};
+
+/*
+ * Define Thread_Queue and All_Thread_List access and manipulation functions.
+ */
+IMPLEMENT_LIST(Thread_Queue, Kernel_Thread);
+IMPLEMENT_LIST(All_Thread_List, Kernel_Thread);
+
+static __inline__ void Enqueue_Thread(struct Thread_Queue *queue, struct Kernel_Thread *kthread) {
+ Add_To_Back_Of_Thread_Queue(queue, kthread);
+}
+
+static __inline__ void Remove_Thread(struct Thread_Queue *queue, struct Kernel_Thread *kthread) {
+ Remove_From_Thread_Queue(queue, kthread);
+}
+
+/*
+ * Thread start functions should have this signature.
+ */
+typedef void (*Thread_Start_Func)(ulong_t arg);
+
+/*
+ * Thread priorities
+ */
+#define PRIORITY_IDLE 0
+#define PRIORITY_USER 1
+#define PRIORITY_LOW 2
+#define PRIORITY_NORMAL 5
+#define PRIORITY_HIGH 10
+
+
+/*
+ * Scheduler operations.
+ */
+void Init_Scheduler(void);
+struct Kernel_Thread* Start_Kernel_Thread(
+ Thread_Start_Func startFunc,
+ ulong_t arg,
+ int priority,
+ bool detached
+);
+struct Kernel_Thread* Start_User_Thread(struct User_Context* userContext, bool detached);
+void Make_Runnable(struct Kernel_Thread* kthread);
+void Make_Runnable_Atomic(struct Kernel_Thread* kthread);
+struct Kernel_Thread* Get_Current(void);
+struct Kernel_Thread* Get_Next_Runnable(void);
+void Schedule(void);
+void Yield(void);
+void Exit(int exitCode) __attribute__ ((noreturn));
+int Join(struct Kernel_Thread* kthread);
+struct Kernel_Thread* Lookup_Thread(int pid);
+
+/*
+ * Thread context switch function, defined in lowlevel.asm
+ */
+void Switch_To_Thread(struct Kernel_Thread*);
+
+/*
+ * Wait queue functions.
+ */
+void Wait(struct Thread_Queue* waitQueue);
+void Wake_Up(struct Thread_Queue* waitQueue);
+void Wake_Up_One(struct Thread_Queue* waitQueue);
+
+/*
+ * Pointer to currently executing thread.
+ */
+extern struct Kernel_Thread* g_currentThread;
+
+/*
+ * Boolean flag indicating that we need to choose a new runnable thread.
+ */
+extern int g_needReschedule;
+
+/*
+ * Boolean flag indicating that preemption should be disabled.
+ */
+extern volatile int g_preemptionDisabled;
+
+/*
+ * Thread-local data information
+ */
+#define MIN_DESTRUCTOR_ITERATIONS 4
+
+typedef void (*tlocal_destructor_t)(void *);
+typedef unsigned int tlocal_key_t;
+
+extern int Tlocal_Create(tlocal_key_t *, tlocal_destructor_t);
+extern void Tlocal_Put(tlocal_key_t, const void *);
+extern void *Tlocal_Get(tlocal_key_t);
+
+/* Print list of all threads, for debugging. */
+extern void Dump_All_Thread_List(void);
+
+
+#endif /* GEEKOS_KTHREAD_H */
--- /dev/null
+/*
+ * Kernel data types
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_KTYPES_H
+#define GEEKOS_KTYPES_H
+
+/*
+ * GeekOS uses the C99 bool type, with true and false
+ * constant values.
+ */
+#include <stdbool.h>
+
+/*
+ * Shorthand for commonly used integer types.
+ */
+typedef unsigned long ulong_t;
+typedef unsigned int uint_t;
+typedef unsigned short ushort_t;
+typedef unsigned char uchar_t;
+typedef unsigned long long ullong_t;
+
+/*
+ * MIN() and MAX() macros.
+ * By using gcc extensions, they are type-correct and
+ * evaulate their arguments only once.
+ */
+#define MIN(a,b) ({typeof (a) _a = (a); typeof (b) _b = (b); (_a < _b) ? _a : _b; })
+#define MAX(a,b) ({typeof (a) _a = (a); typeof (b) _b = (b); (_a < _b) ? _a : _b; })
+
+/*
+ * Some ASCII character access and manipulation macros.
+ */
+#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? ((c) + ('a' - 'A')) : (c))
+#define TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? ((c) - ('a' - 'A')) : (c))
+
+#endif /* GEEKOS_KTYPES_H */
--- /dev/null
+/*
+ * Generic list data type
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_LIST_H
+#define GEEKOS_LIST_H
+
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+
+/*
+ * Define a list type.
+ */
+#define DEFINE_LIST(listTypeName, nodeTypeName) \
+struct listTypeName { \
+ struct nodeTypeName *head, *tail; \
+}
+
+/*
+ * Define members of a struct to be used as link fields for
+ * membership in given list type.
+ */
+#define DEFINE_LINK(listTypeName, nodeTypeName) \
+ struct nodeTypeName * prev##listTypeName, * next##listTypeName
+
+/*
+ * Define inline list manipulation and access functions.
+ */
+#define IMPLEMENT_LIST(LType, NType) \
+static __inline__ void Clear_##LType(struct LType *listPtr) { \
+ listPtr->head = listPtr->tail = 0; \
+} \
+static __inline__ bool Is_Member_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \
+ struct NType *cur = listPtr->head; \
+ while (cur != 0) { \
+ if (cur == nodePtr) \
+ return true; \
+ cur = cur->next##LType; \
+ } \
+ return false; \
+} \
+static __inline__ struct NType * Get_Front_Of_##LType(struct LType *listPtr) { \
+ return listPtr->head; \
+} \
+static __inline__ struct NType * Get_Back_Of_##LType(struct LType *listPtr) { \
+ return listPtr->tail; \
+} \
+static __inline__ struct NType * Get_Next_In_##LType(struct NType *nodePtr) { \
+ return nodePtr->next##LType; \
+} \
+static __inline__ void Set_Next_In_##LType(struct NType *nodePtr, struct NType *value) { \
+ nodePtr->next##LType = value; \
+} \
+static __inline__ struct NType * Get_Prev_In_##LType(struct NType *nodePtr) { \
+ return nodePtr->prev##LType; \
+} \
+static __inline__ void Set_Prev_In_##LType(struct NType *nodePtr, struct NType *value) { \
+ nodePtr->prev##LType = value; \
+} \
+static __inline__ void Add_To_Front_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \
+ KASSERT(!Is_Member_Of_##LType(listPtr, nodePtr)); \
+ nodePtr->prev##LType = 0; \
+ if (listPtr->head == 0) { \
+ listPtr->head = listPtr->tail = nodePtr; \
+ nodePtr->next##LType = 0; \
+ } else { \
+ listPtr->head->prev##LType = nodePtr; \
+ nodePtr->next##LType = listPtr->head; \
+ listPtr->head = nodePtr; \
+ } \
+} \
+static __inline__ void Add_To_Back_Of_##LType(struct LType *listPtr, struct NType *nodePtr) { \
+ /* KASSERT(!Is_Member_Of_##LType(listPtr, nodePtr)); */ \
+ nodePtr->next##LType = 0; \
+ if (listPtr->tail == 0) { \
+ listPtr->head = listPtr->tail = nodePtr; \
+ nodePtr->prev##LType = 0; \
+ } \
+ else { \
+ listPtr->tail->next##LType = nodePtr; \
+ nodePtr->prev##LType = listPtr->tail; \
+ listPtr->tail = nodePtr; \
+ } \
+} \
+static __inline__ void Append_##LType(struct LType *listToModify, struct LType *listToAppend) { \
+ if (listToAppend->head != 0) { \
+ if (listToModify->head == 0) { \
+ listToModify->head = listToAppend->head; \
+ listToModify->tail = listToAppend->tail; \
+ } else { \
+ KASSERT(listToAppend->head != 0); \
+ KASSERT(listToModify->tail != 0); \
+ listToAppend->head->prev##LType = listToModify->tail; \
+ listToModify->tail->next##LType = listToAppend->head; \
+ listToModify->tail = listToAppend->tail; \
+ } \
+ } \
+ listToAppend->head = listToAppend->tail = 0; \
+} \
+static __inline__ struct NType * Remove_From_Front_Of_##LType(struct LType *listPtr) { \
+ struct NType *nodePtr; \
+ nodePtr = listPtr->head; \
+ KASSERT(nodePtr != 0); \
+ listPtr->head = listPtr->head->next##LType; \
+ if (listPtr->head == 0) \
+ listPtr->tail = 0; \
+ else \
+ listPtr->head->prev##LType = 0; \
+ return nodePtr; \
+} \
+static __inline__ void Remove_From_##LType(struct LType *listPtr, struct NType *nodePtr) { \
+ KASSERT(Is_Member_Of_##LType(listPtr, nodePtr)); \
+ if (nodePtr->prev##LType != 0) \
+ nodePtr->prev##LType->next##LType = nodePtr->next##LType; \
+ else \
+ listPtr->head = nodePtr->next##LType; \
+ if (nodePtr->next##LType != 0) \
+ nodePtr->next##LType->prev##LType = nodePtr->prev##LType; \
+ else \
+ listPtr->tail = nodePtr->prev##LType; \
+} \
+static __inline__ bool Is_##LType##_Empty(struct LType *listPtr) { \
+ return listPtr->head == 0; \
+}
+
+#endif /* GEEKOS_LIST_H */
--- /dev/null
+/*
+ * GeekOS memory allocation API
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_MALLOC_H
+#define GEEKOS_MALLOC_H
+
+#include <geekos/ktypes.h>
+
+void Init_Heap(ulong_t start, ulong_t size);
+void* Malloc(ulong_t size);
+void Free(void* buf);
+
+#endif /* GEEKOS_MALLOC_H */
--- /dev/null
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_MEM_H
+#define GEEKOS_MEM_H
+
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+#include <geekos/list.h>
+#include <geekos/paging.h>
+
+struct Boot_Info;
+
+/*
+ * Page flags
+ */
+#define PAGE_AVAIL 0x0000 /* page is on the freelist */
+#define PAGE_KERN 0x0001 /* page used by kernel code or data */
+#define PAGE_HW 0x0002 /* page used by hardware (e.g., ISA hole) */
+#define PAGE_ALLOCATED 0x0004 /* page is allocated */
+#define PAGE_UNUSED 0x0008 /* page is unused */
+#define PAGE_HEAP 0x0010 /* page is in kernel heap */
+#define PAGE_PAGEABLE 0x0020 /* page can be paged out */
+#define PAGE_LOCKED 0x0040 /* page is taken should not be freed */
+
+/*
+ * PC memory map
+ */
+#define ISA_HOLE_START 0x0A0000
+#define ISA_HOLE_END 0x100000
+
+/*
+ * We reserve the two pages just after the ISA hole for the initial
+ * kernel thread's context object and stack.
+ */
+//#define HIGHMEM_START (ISA_HOLE_END + 8192)
+#define HIGHMEM_START ISA_HOLE_END
+/*
+ * Make the kernel heap this size
+ */
+#define KERNEL_HEAP_SIZE (1024*1024)
+
+struct Page;
+
+/*
+ * List datatype for doubly-linked list of Pages.
+ */
+DEFINE_LIST(Page_List, Page);
+
+/*
+ * Each page of physical memory has one of these structures
+ * associated with it, to do allocation and bookkeeping.
+ */
+struct Page {
+ unsigned flags; /* Flags indicating state of page */
+ DEFINE_LINK(Page_List, Page); /* Link fields for Page_List */
+ int clock;
+ ulong_t vaddr; /* User virtual address where page is mapped */
+ pte_t *entry; /* Page table entry referring to the page */
+};
+
+IMPLEMENT_LIST(Page_List, Page);
+
+void Init_Mem(struct Boot_Info* bootInfo);
+void Init_BSS(void);
+void* Alloc_Page(void);
+void* Alloc_Pageable_Page(pte_t *entry, ulong_t vaddr);
+void Free_Page(void* pageAddr);
+
+/*
+ * Determine if given address is a multiple of the page size.
+ */
+static __inline__ bool Is_Page_Multiple(ulong_t addr)
+{
+ return addr == (addr & ~(PAGE_MASK));
+}
+
+/*
+ * Round given address up to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Up_To_Page(ulong_t addr)
+{
+ if ((addr & PAGE_MASK) != 0) {
+ addr &= ~(PAGE_MASK);
+ addr += PAGE_SIZE;
+ }
+ return addr;
+}
+
+/*
+ * Round given address down to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Down_To_Page(ulong_t addr)
+{
+ return addr & (~PAGE_MASK);
+}
+
+/*
+ * Get the index of the page in memory.
+ */
+static __inline__ int Page_Index(ulong_t addr)
+{
+ return (int) (addr >> PAGE_POWER);
+}
+
+/*
+ * Get the Page struct associated with given address.
+ */
+static __inline__ struct Page *Get_Page(ulong_t addr)
+{
+ extern struct Page* g_pageList;
+ return &g_pageList[Page_Index(addr)];
+}
+
+/*
+ * Get the physical address of the memory represented by given Page object.
+ */
+static __inline__ ulong_t Get_Page_Address(struct Page *page)
+{
+ extern struct Page* g_pageList;
+ ulong_t index = page - g_pageList;
+ return index << PAGE_POWER;
+}
+
+#endif /* GEEKOS_MEM_H */
--- /dev/null
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_MEM_H
+#define GEEKOS_MEM_H
+
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+#include <geekos/list.h>
+#include <geekos/paging.h>
+
+struct Boot_Info;
+
+/*
+ * Page flags
+ */
+#define PAGE_AVAIL 0x0000 /* page is on the freelist */
+#define PAGE_KERN 0x0001 /* page used by kernel code or data */
+#define PAGE_HW 0x0002 /* page used by hardware (e.g., ISA hole) */
+#define PAGE_ALLOCATED 0x0004 /* page is allocated */
+#define PAGE_UNUSED 0x0008 /* page is unused */
+#define PAGE_HEAP 0x0010 /* page is in kernel heap */
+#define PAGE_PAGEABLE 0x0020 /* page can be paged out */
+#define PAGE_LOCKED 0x0040 /* page is taken should not be freed */
+#define PAGE_VM 0x0080 /* page is used by the VM */
+
+
+struct Page;
+
+/*
+ * List datatype for doubly-linked list of Pages.
+ */
+DEFINE_LIST(Page_List, Page);
+
+/*
+ * Each page of physical memory has one of these structures
+ * associated with it, to do allocation and bookkeeping.
+ */
+struct Page {
+ unsigned flags; /* Flags indicating state of page */
+ DEFINE_LINK(Page_List, Page); /* Link fields for Page_List */
+ int clock;
+ ulong_t vaddr; /* User virtual address where page is mapped */
+ pte_t *entry; /* Page table entry referring to the page */
+};
+
+IMPLEMENT_LIST(Page_List, Page);
+
+void Init_Mem(struct Boot_Info* bootInfo);
+void Init_BSS(void);
+void* Alloc_Page(void);
+void* Alloc_Pageable_Page(pte_t *entry, ulong_t vaddr);
+void Free_Page(void* pageAddr);
+
+/*
+ * Determine if given address is a multiple of the page size.
+ */
+static __inline__ bool Is_Page_Multiple(ulong_t addr)
+{
+ return addr == (addr & ~(PAGE_MASK));
+}
+
+/*
+ * Round given address up to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Up_To_Page(ulong_t addr)
+{
+ if ((addr & PAGE_MASK) != 0) {
+ addr &= ~(PAGE_MASK);
+ addr += PAGE_SIZE;
+ }
+ return addr;
+}
+
+/*
+ * Round given address down to a multiple of the page size
+ */
+static __inline__ ulong_t Round_Down_To_Page(ulong_t addr)
+{
+ return addr & (~PAGE_MASK);
+}
+
+/*
+ * Get the index of the page in memory.
+ */
+static __inline__ int Page_Index(ulong_t addr)
+{
+ return (int) (addr >> PAGE_POWER);
+}
+
+
+/*
+ * Get the Page struct associated with given address.
+ */
+static __inline__ struct Page *Get_Page(ulong_t addr)
+{
+ extern struct Page* g_pageList;
+ return &g_pageList[Page_Index(addr)];
+ //return g_pageList + (sizeof(struct Page) * (int)(addr >> PAGE_POWER));
+}
+
+/*
+ * Get the physical address of the memory represented by given Page object.
+ */
+static __inline__ ulong_t Get_Page_Address(struct Page *page)
+{
+ extern struct Page* g_pageList;
+ ulong_t index = page - g_pageList;
+ return index << PAGE_POWER;
+}
+
+#endif /* GEEKOS_MEM_H */
--- /dev/null
+/*
+ * Paging (virtual memory) support
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_PAGING_H
+#define GEEKOS_PAGING_H
+
+#include <geekos/ktypes.h>
+#include <geekos/defs.h>
+#include <geekos/bootinfo.h>
+#include <geekos/list.h>
+
+struct Page;
+struct User_Context;
+
+#define NUM_PAGE_TABLE_ENTRIES 1024
+#define NUM_PAGE_DIR_ENTRIES 1024
+
+#define PAGE_DIRECTORY_INDEX(x) ((((uint_t)x) >> 22) & 0x3ff)
+#define PAGE_TABLE_INDEX(x) ((((uint_t)x) >> 12) & 0x3ff)
+#define PAGE_OFFSET(x) ((((uint_t)x) & 0xfff))
+
+#define PAGE_ALLIGNED_ADDR(x) (((uint_t) (x)) >> 12)
+#define PAGE_ADDR(x) (PAGE_ALLIGNED_ADDR(x) << 12)
+
+/*
+ * Bits for flags field of pde_t and pte_t.
+ */
+#define VM_WRITE 1 /* Memory is writable */
+#define VM_USER 2 /* Memory is accessible to user code */
+#define VM_NOCACHE 8 /* Memory should not be cached */
+#define VM_READ 0 /* Memory can be read (ignored for x86) */
+#define VM_EXEC 0 /* Memory can be executed (ignored for x86) */
+
+
+/*
+ * Page directory entry datatype.
+ * If marked as present, it specifies the physical address
+ * and permissions of a page table.
+ */
+typedef struct {
+ uint_t present:1;
+ uint_t flags:4;
+ uint_t accessed:1;
+ uint_t reserved:1;
+ uint_t largePages:1;
+ uint_t globalPage:1;
+ uint_t kernelInfo:3;
+ uint_t pageTableBaseAddr:20;
+} pde_t;
+
+/*
+ * Page table entry datatype.
+ * If marked as present, it specifies the physical address
+ * and permissions of a page of memory.
+ */
+typedef struct {
+ uint_t present:1;
+ uint_t flags:4;
+ uint_t accessed:1;
+ uint_t dirty:1;
+ uint_t pteAttribute:1;
+ uint_t globalPage:1;
+ uint_t kernelInfo:3;
+ uint_t pageBaseAddr:20;
+} pte_t;
+
+/*
+ * Datatype representing the hardware error code
+ * pushed onto the stack by the processor on a page fault.
+ * The error code is stored in the "errorCode" field
+ * of the Interrupt_State struct.
+ */
+typedef struct {
+ uint_t protectionViolation:1;
+ uint_t writeFault:1;
+ uint_t userModeFault:1;
+ uint_t reservedBitFault:1;
+ uint_t reserved:28;
+} faultcode_t;
+
+/*
+ * Bits used in the kernelInfo field of the PTE's:
+ */
+#define KINFO_PAGE_ON_DISK 0x4 /* Page not present; contents in paging file */
+
+void Init_VM(struct Boot_Info *bootInfo);
+void Init_Paging(void);
+
+extern void Flush_TLB(void);
+extern void Set_PDBR(pde_t *pageDir);
+extern pde_t *Get_PDBR(void);
+extern void Enable_Paging(pde_t *pageDir);
+
+/*
+ * Return the address that caused a page fault.
+ */
+static __inline__ ulong_t Get_Page_Fault_Address(void)
+{
+ ulong_t faultAddress;
+ __asm__ __volatile__ (
+ "mov %%cr2, %0"
+ : "=r" (faultAddress)
+ );
+ return faultAddress;
+}
+
+void SerialPrintPD(pde_t *pde);
+void SerialPrintPT(void *starting_address, pte_t *pte);
+void SerialPrintPDE(void *virtual_address, pde_t *pde);
+void SerialPrintPTE(void *virtual_address,pte_t *pte);
+void SerialDumpPageTables(pde_t *pde);
+
+pte_t *LookupPage(void *vaddr);
+
+pte_t *MapPage(void *vaddr, pte_t *pte, int alloc_pde);
+pte_t *UnMapPage(void *vaddr);
+
+int Find_Space_On_Paging_File(void);
+void Free_Space_On_Paging_File(int pagefileIndex);
+void Write_To_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex);
+void Read_From_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex);
+
+
+#endif
--- /dev/null
+/*
+ * Range checking
+ * Copyright (c) 2003, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_RANGE_H
+#define GEEKOS_RANGE_H
+
+#include <geekos/ktypes.h>
+
+/*
+ * TODO: think about these, make sure they're correct
+ */
+
+/**
+ * Check that given range is "proper".
+ * @param start the start of the range
+ * @param size the size of the range
+ * @return true if range is properly within the space
+ * 0(inclusive)..ULONG_MAX(inclusive)
+ */
+static __inline__ bool
+Check_Range_Proper(ulong_t start, ulong_t size)
+{
+ /*
+ * Wrap around is only permitted if the sum is zero.
+ * E.g., start=ULONG_MAX, size==1 is a valid range.
+ */
+ ulong_t sum = start + size;
+ return start <= sum || (sum == 0);
+}
+
+/**
+ * Check that given range lies entirely under the
+ * maximum value specified (exclusive).
+ * @param start the start of the range
+ * @param size the size of the range
+ * @param max the lowest address NOT allowed to be in the range
+ * @return true if range falls entirely within the range
+ * 0(inclusive)..max(exclusive)
+ */
+static __inline__ bool
+Check_Range_Under(ulong_t start, ulong_t size, ulong_t max)
+{
+ if (!Check_Range_Proper(start, size))
+ return false;
+
+ return start < max && (start + size) <= max;
+}
+
+#endif /* GEEKOS_RANGE_H */
--- /dev/null
+#ifndef REBOOT_H
+#define REBOOT_H
+
+void machine_real_restart();
+
+
+#endif
--- /dev/null
+/*
+ * GeekOS text screen output
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_SCREEN_H
+#define GEEKOS_SCREEN_H
+
+#include <geekos/ktypes.h>
+
+#define BLACK 0
+#define BLUE 1
+#define GREEN 2
+#define CYAN 3
+#define RED 4
+#define MAGENTA 5
+#define AMBER 6
+#define GRAY 7
+#define BRIGHT 8
+#define ATTRIB(bg,fg) ((fg)|((bg)<<4))
+
+#define NUMCOLS 80
+#define NUMROWS 25
+
+#define TABWIDTH 8
+
+#ifdef GEEKOS
+
+/*
+ * VGA hardware stuff, for accessing the text display
+ * memory and controlling the cursor
+ */
+#define VIDMEM_ADDR 0xb8000
+#define VIDMEM ((uchar_t*) VIDMEM_ADDR)
+#define CRT_ADDR_REG 0x3D4
+#define CRT_DATA_REG 0x3D5
+#define CRT_CURSOR_LOC_HIGH_REG 0x0E
+#define CRT_CURSOR_LOC_LOW_REG 0x0F
+
+void Init_Screen(void);
+void Clear_Screen(void);
+void Get_Cursor(int* row, int* col);
+bool Put_Cursor(int row, int col);
+uchar_t Get_Current_Attr(void);
+void Set_Current_Attr(uchar_t attrib);
+void Put_Char(int c);
+void Put_String(const char* s);
+void Put_Buf(const char* buf, ulong_t length);
+void Print(const char* fmt, ...) __attribute__ ((format (printf, 1, 2)));
+
+#endif /* GEEKOS */
+
+#endif /* GEEKOS_SCREEN_H */
--- /dev/null
+/*
+ * General data structures and routines for segmentation
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+#ifndef GEEKOS_SEGMENT_H
+#define GEEKOS_SEGMENT_H
+
+#include <geekos/ktypes.h>
+
+struct TSS;
+
+#if __TINYC__
+#define PACKED
+#else
+#define PACKED __attribute__((packed))
+#endif
+
+/*
+ * The general format of a segment descriptor.
+ */
+struct Segment_Descriptor {
+ ushort_t sizeLow PACKED ;
+ uint_t baseLow : 24 PACKED ;
+ uint_t type : 4 PACKED ;
+ uint_t system : 1 PACKED ;
+ uint_t dpl : 2 PACKED ;
+ uint_t present : 1 PACKED ;
+ uint_t sizeHigh : 4 PACKED ;
+ uint_t avail : 1 PACKED ;
+ uint_t reserved : 1 PACKED ; /* set to zero */
+ uint_t dbBit : 1 PACKED ;
+ uint_t granularity : 1 PACKED ;
+ uchar_t baseHigh PACKED ;
+};
+
+/**
+ * Construct a segment selector.
+ * @param rpl requestor privilege level; should be KERNEL_PRIVILEGE
+ * for kernel segments and USER_PRIVILEGE for user segments
+ * @param segmentIsInGDT true if the referenced segment descriptor
+ * is defined in the GDT, false if it is defined in the LDT
+ * @param index index of the segment descriptor
+ * @return the segment selector
+ */
+static __inline__ ushort_t Selector(int rpl, bool segmentIsInGDT, int index)
+{
+ ushort_t selector = 0;
+ selector = (rpl & 0x3) | ((segmentIsInGDT ? 0 : 1) << 2) | ((index & 0x1FFF) << 3);
+ return selector;
+}
+
+/*
+ * Routines to initialize segment descriptors.
+ * Code and data segments must start on a page-aligned address
+ * and are sized in pages.
+ */
+
+void Init_Null_Segment_Descriptor(struct Segment_Descriptor* desc);
+
+void Init_Code_Segment_Descriptor(
+ struct Segment_Descriptor* desc,
+ ulong_t baseAddr,
+ ulong_t numPages,
+ int privilegeLevel
+);
+void Init_Data_Segment_Descriptor(
+ struct Segment_Descriptor* desc,
+ ulong_t baseAddr,
+ ulong_t numPages,
+ int privilegeLevel
+);
+void Init_TSS_Descriptor(struct Segment_Descriptor* desc, struct TSS* theTSS);
+
+void Init_LDT_Descriptor(
+ struct Segment_Descriptor* desc,
+ struct Segment_Descriptor theLDT[],
+ int numEntries
+);
+
+#endif /* GEEKOS_SEGMENT_H */
--- /dev/null
+#ifndef SERIAL_H
+#define SERIAL_H
+
+#include <geekos/irq.h>
+#include <geekos/string.h>
+#include <geekos/io.h>
+#include <geekos/screen.h>
+
+#define COM1_IRQ 4
+#define DEFAULT_SERIAL_ADDR 0x3F8
+
+
+#ifndef SERIAL_PRINT
+#define SERIAL_PRINT 1
+#endif
+#ifndef SERIAL_PRINT_DEBUG
+#define SERIAL_PRINT_DEBUG 1
+#endif
+#ifndef SERIAL_PRINT_DEBUG_LEVEL
+#define SERIAL_PRINT_DEBUG_LEVEL 10
+#endif
+
+#define SERIAL_PRINT_MAXBUF 256
+
+
+#if SERIAL_PRINT
+#define SerialPrint(format, args...) \
+do { \
+ char buf[SERIAL_PRINT_MAXBUF]; \
+ snprintf( buf, SERIAL_PRINT_MAXBUF, format, ## args ) ; \
+ SerialPutLineN(buf, SERIAL_PRINT_MAXBUF); \
+} while (0)
+#else
+#define SerialPrint(format, args...) do {} while (0)
+#endif
+
+
+#define PrintBoth(format, args...) \
+do { \
+ Print(format, ## args); \
+ SerialPrint(format, ##args); \
+ } while (0)
+
+
+#if SERIAL_PRINT_DEBUG
+#define SerialPrintLevel(level, format, args...) \
+do { \
+ char buf[SERIAL_PRINT_MAXBUF]; \
+ if (level >= SERIAL_PRINT_DEBUG_LEVEL ) { \
+ snprintf( buf, SERIAL_PRINT_MAXBUF, format, ## args ) ; \
+ SerialPutLineN(buf, SERIAL_PRINT_MAXBUF); \
+ } \
+} while (0)
+#else
+#define SerialPrintLevel(level, format, args...) do {} while (0)
+#endif
+
+
+void SerialPutLine(char * line);
+void SerialPutLineN(char * line, int len);
+
+
+void SerialPrintHex(unsigned char x);
+void SerialMemDump(unsigned char *start, int n);
+
+void InitSerial();
+void InitSerialAddr(unsigned short io_addr);
+
+#endif
--- /dev/null
+#include "../libc/string.h"
--- /dev/null
+/*
+ * Symbol mangling macros
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * The _S macro mangles a symbol name into whatever format is
+ * needed for external linkage. E.g., prepend an underscore
+ * for PECOFF.
+ */
+
+#ifndef GEEKOS_SYMBOL_H
+#define GEEKOS_SYMBOL_H
+
+#ifdef NEED_UNDERSCORE
+# define _S(sym) "_" #sym
+#else
+# define _S(sym) #sym
+#endif
+
+#endif /* GEEKOS_SYMBOL_H */
--- /dev/null
+/*
+ * Synchronization primitives
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_SYNCH_H
+#define GEEKOS_SYNCH_H
+
+#include <geekos/kthread.h>
+
+/*
+ * mutex states
+ */
+enum { MUTEX_UNLOCKED, MUTEX_LOCKED };
+
+struct Mutex {
+ int state;
+ struct Kernel_Thread* owner;
+ struct Thread_Queue waitQueue;
+};
+
+#define MUTEX_INITIALIZER { MUTEX_UNLOCKED, 0, THREAD_QUEUE_INITIALIZER }
+
+struct Condition {
+ struct Thread_Queue waitQueue;
+};
+
+void Mutex_Init(struct Mutex* mutex);
+void Mutex_Lock(struct Mutex* mutex);
+void Mutex_Unlock(struct Mutex* mutex);
+
+void Cond_Init(struct Condition* cond);
+void Cond_Wait(struct Condition* cond, struct Mutex* mutex);
+void Cond_Signal(struct Condition* cond);
+void Cond_Broadcast(struct Condition* cond);
+
+#define IS_HELD(mutex) \
+ ((mutex)->state == MUTEX_LOCKED && (mutex)->owner == g_currentThread)
+
+#endif /* GEEKOS_SYNCH_H */
--- /dev/null
+/*
+ * GeekOS timer interrupt support
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_TIMER_H
+#define GEEKOS_TIMER_H
+
+#define TIMER_IRQ 0
+
+extern volatile ulong_t g_numTicks;
+
+void Init_Timer(void);
+
+void Micro_Delay(int us);
+
+
+void Micro_Delay(int us);
+
+#endif /* GEEKOS_TIMER_H */
--- /dev/null
+/*
+ * Trap handlers
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_TRAP_H
+#define GEEKOS_TRAP_H
+
+void Init_Traps(void);
+
+#endif /* GEEKOS_TRAP_H */
--- /dev/null
+/*
+ * x86 TSS data structure and routines
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef GEEKOS_TSS_H
+#define GEEKOS_TSS_H
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+/*
+ * NOTE: all reserved fields must be set to zero.
+ */
+
+struct TSS {
+ /*
+ * Link to nested task. For example, if an interrupt is handled
+ * by a task gate, the link field will contain the selector for
+ * the TSS of the interrupted task.
+ */
+ ushort_t link;
+ ushort_t reserved1;
+
+ /* Stacks for privilege levels. esp0/ss0 specifies the kernel stack. */
+ ulong_t esp0;
+ ushort_t ss0;
+ ushort_t reserved2;
+ ulong_t esp1;
+ ushort_t ss1;
+ ushort_t reserved3;
+ ulong_t esp2;
+ ushort_t ss2;
+ ushort_t reserved4;
+
+ /* Page directory register. */
+ ulong_t cr3;
+
+ /* General processor registers. */
+ ulong_t eip;
+ ulong_t eflags;
+ ulong_t eax;
+ ulong_t ecx;
+ ulong_t edx;
+ ulong_t ebx;
+ ulong_t esp;
+ ulong_t ebp;
+ ulong_t esi;
+ ulong_t edi;
+
+ /* Segment registers and padding. */
+ ushort_t es;
+ ushort_t reserved5;
+ ushort_t cs;
+ ushort_t reserved6;
+ ushort_t ss;
+ ushort_t reserved7;
+ ushort_t ds;
+ ushort_t reserved8;
+ ushort_t fs;
+ ushort_t reserved9;
+ ushort_t gs;
+ ushort_t reserved10;
+
+ /* GDT selector for the LDT descriptor. */
+ ushort_t ldt;
+ ushort_t reserved11;
+
+ /*
+ * The debug trap bit causes a debug exception upon a switch
+ * to the task specified by this TSS.
+ */
+ uint_t debugTrap : 1;
+ uint_t reserved12 : 15;
+
+ /* Offset in the TSS specifying where the io map is located. */
+ ushort_t ioMapBase;
+};
+
+void Init_TSS(void);
+void Set_Kernel_Stack_Pointer(ulong_t esp0);
+
+#endif /* GEEKOS_TSS_H */
--- /dev/null
+/*
+ * Generalized support for printf()-style formatted output
+ * Copyright (c) 2004, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef OUTPUT_H
+#define OUTPUT_H
+
+#include <stdarg.h>
+
+/*
+ * An output sink for Format_Output().
+ * Useful for emitting formatted output to a function
+ * or a buffer.
+ */
+struct Output_Sink {
+ /*
+ * Emit a single character of output.
+ * This is called for all output characters,
+ * in order.
+ */
+ void (*Emit)(struct Output_Sink *o, int ch);
+
+ /*
+ * Finish the formatted output. Called after all characters
+ * have been emitted.
+ */
+ void (*Finish)(struct Output_Sink *o);
+};
+
+int Format_Output(struct Output_Sink *q, const char *format, va_list ap);
+
+#endif /* OUTPUT_H */
--- /dev/null
+/*
+ * String library
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#ifndef STRING_H
+#define STRING_H
+
+#include <stddef.h>
+
+void* memset(void* s, int c, size_t n);
+void* memcpy(void *dst, const void* src, size_t n);
+void *memmove(void *dst, const void *src, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+size_t strlen(const char* s);
+size_t strnlen(const char *s, size_t maxlen);
+int strcmp(const char* s1, const char* s2);
+int strncmp(const char* s1, const char* s2, size_t limit);
+char *strcat(char *s1, const char *s2);
+char *strcpy(char *dest, const char *src);
+char *strncpy(char *dest, const char *src, size_t limit);
+char *strdup(const char *s1);
+int atoi(const char *buf);
+char *strchr(const char *s, int c);
+char *strrchr(const char *s, int c);
+char *strpbrk(const char *s, const char *accept);
+
+/* Note: The ISO C standard puts this in <stdio.h>, but we don't
+ * have that header in GeekOS (yet). */
+int snprintf(char *s, size_t size, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+#endif /* STRING_H */
--- /dev/null
+#! /usr/bin/perl
+
+# Find the function name from the value of the EIP (instruction pointer)
+# register from a Bochs crash report. Uses the kernel symbol
+# map (kernel.syms) produced by compiling the kernel.
+
+use strict qw(refs vars);
+use FileHandle;
+
+if (scalar(@ARGV) != 2){
+ print STDERR "Usage: eipToFunction kernel.syms <eip value>\n";
+ print STDERR " eip value should be in hex\n";
+ exit 1;
+}
+
+my $syms = shift @ARGV;
+my $eip = hex(shift @ARGV);
+
+my @text = ();
+
+my $fh = new FileHandle("<$syms");
+(defined $fh) || die "Couldn't open $syms: $!\n";
+while (<$fh>) {
+ #print $_;
+ if (/^([0-9A-Fa-f]+)\s+[Tt]\s+(\S+)\s*$/) {
+ push @text, [hex($1), $2];
+ }
+}
+$fh->close();
+#print scalar(@text),"\n";
+
+@text = sort { $a->[0] <=> $b->[0] } @text;
+
+my $last = undef;
+
+foreach my $entry (@text) {
+ last if ($eip < $entry->[0]);
+ $last = $entry;
+}
+printf("%s\n",(defined $last) ? $last->[1] : "not found");
+
+# vim:ts=4
--- /dev/null
+#! /usr/bin/perl
+
+# Find the address of a symbol in the storage map.
+
+use strict qw(refs vars);
+use FileHandle;
+
+if ( scalar(@ARGV) != 2 ) {
+ print "Usage: findaddr <storage map> <symbol name>\n";
+ exit 1;
+}
+
+my $storage = shift @ARGV;
+my $symbol = shift @ARGV;
+
+my $fh = new FileHandle("<$storage");
+(defined $fh) || die "Couldn't open storage map: $!\n";
+
+while ( <$fh> ) {
+ if ( /^\s*(0x([0-9]|[a-f]|[A-F])+)\s+\Q$symbol\E\s*$/ ) {
+ print $1, "\n";
+ last;
+ }
+}
+
+$fh->close();
--- /dev/null
+#! /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
--- /dev/null
+#! /usr/bin/perl
+
+# A script to analyze the output of "objdump -h" on the
+# kernel executable file.
+
+use strict qw(vars refs);
+use FileHandle;
+
+my $kernfile = shift @ARGV;
+(defined $kernfile) || die "usage: kernsize <kernfile>\n";
+
+my $kern_fh = new FileHandle("<$kernfile");
+(defined $kern_fh) || die "can't open $kernfile: $!\n";
+
+my $objdump_fh = new FileHandle("objdump -h $kernfile|");
+while ( <$objdump_fh> ) {
+ chop;
+ s/^\s+//;
+ my @fields = split(/\s+/, $_);
+ if ( $fields[0] =~ /^[0-9]$/ ) {
+# print "text start is ", $fields[5], "\n" if $fields[0] eq '0';
+ my $size = hex($fields[2]);
+ my $offset = hex($fields[5]);
+
+ print $fields[0], " (", $fields[1], "): size=$size, offset=$offset\n";
+
+ printf("Word at beginning of section is %08x\n", ReadWord($kern_fh,$offset) );
+ }
+}
+$objdump_fh->close();
+
+sub ReadWord {
+ my ($fh, $offset) = @_;
+ seek $fh, $offset, SEEK_SET;
+ my $buf = 'X' x 4;
+ read $fh, $buf, 4;
+ return unpack('V',$buf);
+}
--- /dev/null
+#! /usr/bin/perl
+
+# Build a binary image containing a pseudo fat filesystem with the listed files
+
+# $Revision: 1.1 $
+use FileHandle;
+
+if ( scalar(@ARGV) < 2 ) {
+ print STDERR "usage: mkuprog <diskImage> <filenames>\n";
+ exit 1;
+}
+
+
+$filename = shift @ARGV;
+$filecount = scalar ( @ARGV );
+
+$fh = new FileHandle(">$filename");
+
+write
+
+while (scalar(@ARGV)) {
+ $filename = shift @ARGV;
+
+ print "got file ", $filename, "\n";
+}
--- /dev/null
+#! /usr/bin/perl
+
+# From a binary image containing a user program, generate
+# C code initializing a User_Program struct.
+
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+if ( scalar(@ARGV) != 3 ) {
+ print STDERR "usage: mkuprog <filename> <progname> <entry addr>\n";
+ exit 1;
+}
+
+my $filename = shift @ARGV;
+my $progname = shift @ARGV;
+my $entryAddr = shift @ARGV;
+
+my $fh = new FileHandle("<$filename");
+(defined $fh) || die "Couldn't open $filename: $!\n";
+binmode $fh;
+
+my $dataArrayName = $progname . "Data";
+my $structName = $progname . "Prog";
+print "const unsigned char $dataArrayName"."[] = {\n";
+
+my $LINEWIDTH = 10;
+
+my $buf = chr(0) x $LINEWIDTH;
+my $n;
+my $size = 0;
+while ( ($n = read( $fh, $buf, $LINEWIDTH )) > 0 ) {
+ $size += $n;
+ my $i;
+ print " ";
+ for ( $i = 0; $i < $n; $i++ ) {
+ my $c = ord( substr($buf, $i, 1) );
+ printf( "0x%x,", $c );
+ }
+ print "\n";
+}
+
+print "};\n";
+
+$fh->close();
+
+print << "END";
+const struct User_Program $structName = {
+ "$progname",
+ $size,
+ $entryAddr,
+ $dataArrayName
+};
+END
--- /dev/null
+#! /usr/bin/perl
+
+# Find the number of 512-byte sectors needed to store
+# given file.
+
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+
+if ( scalar(@ARGV) != 1 ) {
+ print STDERR "Usage: numsecs <filename>\n";
+ exit 1;
+}
+
+my $filename = shift @ARGV;
+my $size = (-s $filename );
+die "Couldn't get size of $filename: $!" if ( !defined $size );
+
+my $result = int($size / 512);
+my $remainder = $size % 512;
+$result++ if ( $remainder > 0 );
+
+print "$result\n";
--- /dev/null
+#! /usr/bin/perl
+
+# Pad a file with zero bytes to make its length
+# an even multiple of some value.
+
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+if ( scalar(@ARGV) != 2 ) {
+ print STDERR "usage: pad <filename> <multiple>\n";
+ exit 1;
+}
+
+my $filename = shift @ARGV;
+my $multiple = shift @ARGV;
+
+my $size = (-s $filename);
+die "Couldn't get size of $filename: $!" if ( !defined $size );
+
+my $num_pad = ($multiple - ($size % $multiple)) % $multiple;
+
+my $buf = chr(0) x $num_pad;
+
+my $fh = new FileHandle(">>$filename");
+die "Couldn't open $filename: $!" if ( !defined $fh );
+binmode $fh;
+syswrite $fh, $buf, $num_pad, 0;
+$fh->close();
--- /dev/null
+#! /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();
+}
--- /dev/null
+#! /usr/bin/perl
+
+# Inspect a 32 word at a specified offset in a file.
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+my $filename = shift @ARGV;
+my $offset = shift @ARGV;
+
+((defined $filename) && (defined $offset))
+ || die "Usage: pw <filename> <offset>\n";
+
+my $fh = new FileHandle("<$filename");
+printf( "%08x\n", ReadWord($fh, $offset) );
+
+sub ReadWord {
+ my ($fh, $offset) = @_;
+ seek $fh, $offset, SEEK_SET;
+ my $buf = 'X' x 4;
+ read $fh, $buf, 4;
+ return unpack('V',$buf);
+}
--- /dev/null
+#! /usr/bin/perl
+
+# Scan a file for a 32-bit word with a particular value.
+# $Revision: 1.1 $
+
+use strict qw(refs vars);
+use FileHandle;
+
+my $filename = shift @ARGV;
+my $word_value = shift @ARGV;
+
+((defined $filename) && (defined $word_value))
+ || die "Usage: scan <filename> <word value in hex>\n";
+
+my $fh = new FileHandle("<$filename");
+my $val = hex($word_value);
+
+my $buf = ' ' x 4;
+
+my $offset = 0;
+while ( read( $fh, $buf, 4) == 4 ) {
+ my $out = unpack "V", $buf;
+ if ( $out == $val ) {
+ print "Found value $word_value at offset $offset\n";
+ exit;
+ }
+ $offset += 4;
+}
+print "Didn't find value $word_value\n";
--- /dev/null
+#! /usr/bin/perl
+
+# This script is used for creating a file full of zeroes,
+# which we use to create the hard disk image used by bochs.
+
+use strict qw(refs vars);
+use FileHandle;
+use IO::Seekable;
+
+if ( scalar(@ARGV) != 2 ) {
+ print "Usage: zerofile <output file> <num sectors>\n";
+ exit 1;
+}
+
+my $outfile = shift @ARGV;
+my $numsecs = shift @ARGV;
+
+my $buf = chr(0) x 1;
+
+my $fh = new FileHandle(">$outfile");
+(defined $fh) || die "Couldn't open $outfile: $!\n";
+binmode $fh;
+
+if ( !sysseek( $fh, ($numsecs * 512) - 1, SEEK_SET ) ) {
+ die "Couldn't seek in $outfile: $!\n";
+}
+if ( !syswrite( $fh, $buf, 1 ) ) {
+ die "Couldn't write to $outfile: $!\n";
+}
+
+$fh->close();
--- /dev/null
+/*
+ * This code was originally part of klibc-0.103.
+ * It was adapted by David Hovemeyer <daveho@cs.umd.edu>
+ * for use in GeekOS (http://geekos.sourceforge.net).
+ *
+ * For information/source for klibc, visit
+ * http://www.kernel.org/pub/linux/libs/klibc/
+ * http://www.zytor.com/mailman/listinfo/klibc/
+ * http://www.zytor.com/cvsweb.cgi/klibc/
+ *
+ * Modifications are marked with "DHH".
+ * Summary of modifications:
+ *
+ * 1. Use struct Output_Sink to emit formatted output, rather than a
+ * character buffer, to allow output polymorphism.
+ *
+ * 2. Buffer generated numbers so that all output is generated in order.
+ *
+ * 3. Don't use long long types: unsigned long is largest
+ * supported type. Arithmetic on 64 bit types requires runtime support
+ * (at least on x86).
+ *
+ * See the file LICENSE-klibc for license information.
+ */
+
+/*
+ * vsnprintf.c
+ *
+ * vsnprintf(), from which the rest of the printf()
+ * family is built
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <geekos/string.h>
+#include <limits.h>
+#include <geekos/fmtout.h> /* DHH: for struct Output_Sink */
+
+/*
+ * DHH: Hack, long long arithmetic requires runtime support.
+ * Use unsigned long as greatest unsigned integer supported.
+ */
+typedef long intmax_t;
+typedef unsigned long uintmax_t;
+typedef unsigned long uintptr_t;
+
+/* DHH */
+#define ASSERT(exp) \
+do { if (!(exp)) while(1); } while (0)
+
+enum flags {
+ FL_ZERO = 0x01, /* Zero modifier */
+ FL_MINUS = 0x02, /* Minus modifier */
+ FL_PLUS = 0x04, /* Plus modifier */
+ FL_TICK = 0x08, /* ' modifier */
+ FL_SPACE = 0x10, /* Space modifier */
+ FL_HASH = 0x20, /* # modifier */
+ FL_SIGNED = 0x40, /* Number is signed */
+ FL_UPPER = 0x80 /* Upper case digits */
+};
+
+/* These may have to be adjusted on certain implementations */
+enum ranks {
+ rank_char = -2,
+ rank_short = -1,
+ rank_int = 0,
+ rank_long = 1,
+#if 0
+ rank_longlong = 2,
+#endif
+};
+
+#define MIN_RANK rank_char
+#define MAX_RANK rank_long
+
+#define INTMAX_RANK rank_long
+#define SIZE_T_RANK rank_long
+#define PTRDIFF_T_RANK rank_long
+
+/* DHH */
+#define EMIT(x) do { (q)->Emit((q), (x)); } while (0)
+
+/*
+ * DHH - As a hack, we buffer this many digits when generating
+ * a number. Because this code originally was an implementation
+ * of vnsprintf(), it generated some digits backwards in a buffer.
+ * Obviously we can't do this when emitting output directly
+ * to the console or a file. So, we buffer the generated digits
+ * and then emit them in order.
+ *
+ * This value should be adequate to emit values up to 2^32-1 in
+ * bases 2 or greater, including tick marks.
+ */
+#define NDIGITS_MAX 43
+
+static size_t
+format_int(struct Output_Sink *q, uintmax_t val, enum flags flags,
+ int base, int width, int prec)
+{
+ char *qq;
+ size_t o = 0, oo;
+ static const char lcdigits[] = "0123456789abcdef";
+ static const char ucdigits[] = "0123456789ABCDEF";
+ const char *digits;
+ uintmax_t tmpval;
+ int minus = 0;
+ int ndigits = 0, nchars;
+ int tickskip, b4tick;
+ char digit_buffer[NDIGITS_MAX]; /* DHH */
+ size_t ndigits_save; /* DHH */
+
+ /* Select type of digits */
+ digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
+
+ /* If signed, separate out the minus */
+ if ( flags & FL_SIGNED && (intmax_t)val < 0 ) {
+ minus = 1;
+ val = (uintmax_t)(-(intmax_t)val);
+ }
+
+ /* Count the number of digits needed. This returns zero for 0. */
+ tmpval = val;
+ while ( tmpval ) {
+ tmpval /= base;
+ ndigits++;
+ }
+
+ /* Adjust ndigits for size of output */
+
+ if ( flags & FL_HASH && base == 8 ) {
+ if ( prec < ndigits+1 )
+ prec = ndigits+1;
+ }
+
+ if ( ndigits < prec ) {
+ ndigits = prec; /* Mandatory number padding */
+ } else if ( val == 0 ) {
+ ndigits = 1; /* Zero still requires space */
+ }
+
+ /* For ', figure out what the skip should be */
+ if ( flags & FL_TICK ) {
+ tickskip = (base == 16) ? 4 : 3;
+ } else {
+ tickskip = ndigits; /* No tick marks */
+ }
+
+ /* Tick marks aren't digits, but generated by the number converter */
+ ndigits += (ndigits-1)/tickskip;
+
+ /* Now compute the number of nondigits */
+ nchars = ndigits;
+
+ if ( minus || (flags & (FL_PLUS|FL_SPACE)) )
+ nchars++; /* Need space for sign */
+ if ( (flags & FL_HASH) && base == 16 ) {
+ nchars += 2; /* Add 0x for hex */
+ }
+
+ /* Emit early space padding */
+ if ( !(flags & (FL_MINUS|FL_ZERO)) && width > nchars ) {
+ while ( width > nchars ) {
+ EMIT(' ');
+ width--;
+ }
+ }
+
+ /* Emit nondigits */
+ if ( minus )
+ EMIT('-');
+ else if ( flags & FL_PLUS )
+ EMIT('+');
+ else if ( flags & FL_SPACE )
+ EMIT(' ');
+
+ if ( (flags & FL_HASH) && base == 16 ) {
+ EMIT('0');
+ EMIT((flags & FL_UPPER) ? 'X' : 'x');
+ }
+
+ /* Emit zero padding */
+ if ( (flags & (FL_MINUS|FL_ZERO)) == FL_ZERO && width > ndigits ) {
+ while ( width > nchars ) {
+ EMIT('0');
+ width--;
+ }
+ }
+
+ /* Generate the number. This is done from right to left. */
+ ASSERT(ndigits <= NDIGITS_MAX); /* DHH */
+ ndigits_save = ndigits;
+ qq = digit_buffer + ndigits;
+ oo = o;
+
+ /* Emit digits to temp buffer */
+ b4tick = tickskip;
+ while ( ndigits > 0 ) {
+ if ( !b4tick-- ) {
+ qq--; oo--; ndigits--;
+ *qq = '_';
+ b4tick = tickskip-1;
+ }
+ qq--; oo--; ndigits--;
+ *qq = digits[val%base];
+ val /= base;
+ }
+
+ /* Copy digits to Output_Sink */
+ for (oo = 0; oo < ndigits_save; ++oo)
+ EMIT(digit_buffer[oo]);
+
+ /* Emit late space padding */
+ while ( (flags & FL_MINUS) && width > nchars ) {
+ EMIT(' ');
+ width--;
+ }
+
+ return o;
+}
+
+/*
+ * DHH - This function was originally vsnprintf().
+ * I renamed it to Format_Output() and changed it to take
+ * a struct Output_Sink instead of a buffer. That way, it can
+ * be used for any kind of formatted output (string, console,
+ * file, etc.)
+ */
+int Format_Output(struct Output_Sink *q, const char *format, va_list ap)
+{
+ const char *p = format;
+ char ch;
+ size_t o = 0; /* Number of characters output */
+ uintmax_t val = 0;
+ int rank = rank_int; /* Default rank */
+ int width = 0;
+ int prec = -1;
+ int base;
+ size_t sz;
+ enum flags flags = 0;
+ enum {
+ st_normal, /* Ground state */
+ st_flags, /* Special flags */
+ st_width, /* Field width */
+ st_prec, /* Field precision */
+ st_modifiers /* Length or conversion modifiers */
+ } state = st_normal;
+ const char *sarg; /* %s string argument */
+ char carg; /* %c char argument */
+ int slen; /* String length */
+
+ while ( (ch = *p++) ) {
+ switch ( state ) {
+ case st_normal:
+ if ( ch == '%' ) {
+ state = st_flags;
+ flags = 0; rank = rank_int; width = 0; prec = -1;
+ } else {
+ EMIT(ch);
+ }
+ break;
+
+ case st_flags:
+ switch ( ch ) {
+ case '-':
+ flags |= FL_MINUS;
+ break;
+ case '+':
+ flags |= FL_PLUS;
+ break;
+ case '\'':
+ flags |= FL_TICK;
+ break;
+ case ' ':
+ flags |= FL_SPACE;
+ break;
+ case '#':
+ flags |= FL_HASH;
+ break;
+ case '0':
+ flags |= FL_ZERO;
+ break;
+ default:
+ state = st_width;
+ p--; /* Process this character again */
+ break;
+ }
+ break;
+
+ case st_width:
+ if ( ch >= '0' && ch <= '9' ) {
+ width = width*10+(ch-'0');
+ } else if ( ch == '*' ) {
+ width = va_arg(ap, int);
+ if ( width < 0 ) {
+ width = -width;
+ flags |= FL_MINUS;
+ }
+ } else if ( ch == '.' ) {
+ prec = 0; /* Precision given */
+ state = st_prec;
+ } else {
+ state = st_modifiers;
+ p--; /* Process this character again */
+ }
+ break;
+
+ case st_prec:
+ if ( ch >= '0' && ch <= '9' ) {
+ prec = prec*10+(ch-'0');
+ } else if ( ch == '*' ) {
+ prec = va_arg(ap, int);
+ if ( prec < 0 )
+ prec = -1;
+ } else {
+ state = st_modifiers;
+ p--; /* Process this character again */
+ }
+ break;
+
+ case st_modifiers:
+ switch ( ch ) {
+ /* Length modifiers - nonterminal sequences */
+ case 'h':
+ rank--; /* Shorter rank */
+ break;
+ case 'l':
+ rank++; /* Longer rank */
+ break;
+ case 'j':
+ rank = INTMAX_RANK;
+ break;
+ case 'z':
+ rank = SIZE_T_RANK;
+ break;
+ case 't':
+ rank = PTRDIFF_T_RANK;
+ break;
+ case 'L':
+ case 'q':
+ rank += 2;
+ break;
+ default:
+ /* Output modifiers - terminal sequences */
+ state = st_normal; /* Next state will be normal */
+ if ( rank < MIN_RANK ) /* Canonicalize rank */
+ rank = MIN_RANK;
+ else if ( rank > MAX_RANK )
+ rank = MAX_RANK;
+
+ switch ( ch ) {
+ case 'P': /* Upper case pointer */
+ flags |= FL_UPPER;
+ /* fall through */
+ case 'p': /* Pointer */
+ base = 16;
+ prec = (CHAR_BIT*sizeof(void *)+3)/4;
+ flags |= FL_HASH;
+ val = (uintmax_t)(uintptr_t)va_arg(ap, void *);
+ goto is_integer;
+
+ case 'd': /* Signed decimal output */
+ case 'i':
+ base = 10;
+ flags |= FL_SIGNED;
+ switch (rank) {
+ case rank_char:
+ /* Yes, all these casts are needed... */
+ val = (uintmax_t)(intmax_t)(signed char)va_arg(ap, signed int);
+ break;
+ case rank_short:
+ val = (uintmax_t)(intmax_t)(signed short)va_arg(ap, signed int);
+ break;
+ case rank_int:
+ val = (uintmax_t)(intmax_t)va_arg(ap, signed int);
+ break;
+ case rank_long:
+ val = (uintmax_t)(intmax_t)va_arg(ap, signed long);
+ break;
+#if 0
+ case rank_longlong:
+ val = (uintmax_t)(intmax_t)va_arg(ap, signed long long);
+ break;
+#endif
+ }
+ goto is_integer;
+ case 'o': /* Octal */
+ base = 8;
+ goto is_unsigned;
+ case 'u': /* Unsigned decimal */
+ base = 10;
+ goto is_unsigned;
+ case 'X': /* Upper case hexadecimal */
+ flags |= FL_UPPER;
+ /* fall through */
+ case 'x': /* Hexadecimal */
+ base = 16;
+ goto is_unsigned;
+
+ is_unsigned:
+ switch (rank) {
+ case rank_char:
+ val = (uintmax_t)(unsigned char)va_arg(ap, unsigned int);
+ break;
+ case rank_short:
+ val = (uintmax_t)(unsigned short)va_arg(ap, unsigned int);
+ break;
+ case rank_int:
+ val = (uintmax_t)va_arg(ap, unsigned int);
+ break;
+ case rank_long:
+ val = (uintmax_t)va_arg(ap, unsigned long);
+ break;
+#if 0
+ case rank_longlong:
+ val = (uintmax_t)va_arg(ap, unsigned long long);
+ break;
+#endif
+ }
+ /* fall through */
+
+ is_integer:
+ sz = format_int(q, val, flags, base, width, prec);
+ q += sz; o += sz;
+ break;
+
+ case 'c': /* Character */
+ carg = (char)va_arg(ap, int);
+ sarg = &carg;
+ slen = 1;
+ goto is_string;
+ case 's': /* String */
+ sarg = va_arg(ap, const char *);
+ sarg = sarg ? sarg : "(null)";
+ slen = strlen(sarg);
+ goto is_string;
+
+ is_string:
+ {
+ char sch;
+ int i;
+
+ if ( prec != -1 && slen > prec )
+ slen = prec;
+
+ if ( width > slen && !(flags & FL_MINUS) ) {
+ char pad = (flags & FL_ZERO) ? '0' : ' ';
+ while ( width > slen ) {
+ EMIT(pad);
+ width--;
+ }
+ }
+ for ( i = slen ; i ; i-- ) {
+ sch = *sarg++;
+ EMIT(sch);
+ }
+ if ( width > slen && (flags & FL_MINUS) ) {
+ while ( width > slen ) {
+ EMIT(' ');
+ width--;
+ }
+ }
+ }
+ break;
+
+ case 'n': /* Output the number of characters written */
+ {
+ switch (rank) {
+ case rank_char:
+ *va_arg(ap, signed char *) = o;
+ break;
+ case rank_short:
+ *va_arg(ap, signed short *) = o;
+ break;
+ case rank_int:
+ *va_arg(ap, signed int *) = o;
+ break;
+ case rank_long:
+ *va_arg(ap, signed long *) = o;
+ break;
+#if 0
+ case rank_longlong:
+ *va_arg(ap, signed long long *) = o;
+ break;
+#endif
+ }
+ }
+ break;
+
+ default: /* Anything else, including % */
+ EMIT(ch);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Null-terminate the string */
+#if 0
+ if ( o<n )
+ *q = '\0'; /* No overflow */
+ else if ( n>0 )
+ buffer[n-1] = '\0'; /* Overflow - terminate at end of buffer */
+#endif
+ q->Finish(q);
+
+ return o;
+}
--- /dev/null
+/***********************************************************
+Copyright 1991-1995 by Stichting Mathematisch Centrum, Amsterdam,
+The Netherlands.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI or Corporation for National Research Initiatives or
+CNRI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+While CWI is the initial source for this software, a modified version
+is made available by the Corporation for National Research Initiatives
+(CNRI) at the Internet address ftp://ftp.python.org.
+
+STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH
+REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH
+CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/* A perhaps slow but I hope correct implementation of memmove */
+
+#include <string.h>
+
+void *
+memmove(void *d, const void *s, size_t n)
+{
+ char *dst = (char*) d;
+ const char *src = (const char*) s;
+ char *realdst = dst;
+ if (n <= 0)
+ return dst;
+ if (src >= dst+n || dst >= src+n)
+ return memcpy(dst, src, n);
+ if (src > dst) {
+ while (--n >= 0)
+ *dst++ = *src++;
+ }
+ else if (src < dst) {
+ src += n;
+ dst += n;
+ while (--n >= 0)
+ *--dst = *--src;
+ }
+ return realdst;
+}
--- /dev/null
+/*
+ * String library
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * NOTE:
+ * These are slow and simple implementations of a subset of
+ * the standard C library string functions.
+ * We also have an implementation of snprintf().
+ */
+
+#include <fmtout.h>
+#include <string.h>
+
+extern void *Malloc(size_t size);
+
+void* memset(void* s, int c, size_t n)
+{
+ unsigned char* p = (unsigned char*) s;
+
+ while (n > 0) {
+ *p++ = (unsigned char) c;
+ --n;
+ }
+
+ return s;
+}
+
+void* memcpy(void *dst, const void* src, size_t n)
+{
+ unsigned char* d = (unsigned char*) dst;
+ const unsigned char* s = (const unsigned char*) src;
+
+ while (n > 0) {
+ *d++ = *s++;
+ --n;
+ }
+
+ return dst;
+}
+
+int memcmp(const void *s1_, const void *s2_, size_t n)
+{
+ const signed char *s1 = s1_, *s2 = s2_;
+
+ while (n > 0) {
+ int cmp = *s1 - *s2;
+ if (cmp != 0)
+ return cmp;
+ ++s1;
+ ++s2;
+ }
+
+ return 0;
+}
+
+size_t strlen(const char* s)
+{
+ size_t len = 0;
+ while (*s++ != '\0')
+ ++len;
+ return len;
+}
+
+/*
+ * This it a GNU extension.
+ * It is like strlen(), but it will check at most maxlen
+ * characters for the terminating nul character,
+ * returning maxlen if it doesn't find a nul.
+ * This is very useful for checking the length of untrusted
+ * strings (e.g., from user space).
+ */
+size_t strnlen(const char *s, size_t maxlen)
+{
+ size_t len = 0;
+ while (len < maxlen && *s++ != '\0')
+ ++len;
+ return len;
+}
+
+int strcmp(const char* s1, const char* s2)
+{
+ while (1) {
+ int cmp = *s1 - *s2;
+ if (cmp != 0 || *s1 == '\0' || *s2 == '\0')
+ return cmp;
+ ++s1;
+ ++s2;
+ }
+}
+
+int strncmp(const char* s1, const char* s2, size_t limit)
+{
+ size_t i = 0;
+ while (i < limit) {
+ int cmp = *s1 - *s2;
+ if (cmp != 0 || *s1 == '\0' || *s2 == '\0')
+ return cmp;
+ ++s1;
+ ++s2;
+ ++i;
+ }
+
+ /* limit reached and equal */
+ return 0;
+}
+
+char *strcat(char *s1, const char *s2)
+{
+ char *t1;
+
+ t1 = s1;
+ while (*s1) s1++;
+ while(*s2) *s1++ = *s2++;
+ *s1 = '\0';
+
+ return t1;
+}
+
+char *strcpy(char *dest, const char *src)
+{
+ char *ret = dest;
+
+ while (*src) {
+ *dest++ = *src++;
+ }
+ *dest = '\0';
+
+ return ret;
+}
+
+char *strncpy(char *dest, const char *src, size_t limit)
+{
+ char *ret = dest;
+
+ while (*src != '\0' && limit > 0) {
+ *dest++ = *src++;
+ --limit;
+ }
+ if (limit > 0)
+ *dest = '\0';
+
+ return ret;
+}
+
+char *strdup(const char *s1)
+{
+ char *ret;
+
+ ret = Malloc(strlen(s1) + 1);
+ strcpy(ret, s1);
+
+ return ret;
+}
+
+int atoi(const char *buf)
+{
+ int ret = 0;
+
+ while (*buf >= '0' && *buf <= '9') {
+ ret *= 10;
+ ret += *buf - '0';
+ buf++;
+ }
+
+ return ret;
+}
+
+char *strchr(const char *s, int c)
+{
+ while (*s != '\0') {
+ if (*s == c)
+ return (char *) s;
+ ++s;
+ }
+ return 0;
+}
+
+char *strrchr(const char *s, int c)
+{
+ size_t len = strlen(s);
+ const char *p = s + len;
+
+ while (p > s) {
+ --p;
+ if (*p == c)
+ return (char*) p;
+ }
+ return 0;
+}
+
+char *strpbrk(const char *s, const char *accept)
+{
+ size_t setLen = strlen(accept);
+
+ while (*s != '\0') {
+ size_t i;
+ for (i = 0; i < setLen; ++i) {
+ if (*s == accept[i])
+ return (char *) s;
+ }
+ ++s;
+ }
+
+ return 0;
+}
+
+struct String_Output_Sink {
+ struct Output_Sink o;
+ char *s;
+ size_t n, size;
+};
+
+static void String_Emit(struct Output_Sink *o_, int ch)
+{
+ struct String_Output_Sink *o = (struct String_Output_Sink*) o_;
+
+ if (o->n < o->size)
+ *(o->s)++ = ch;
+ ++(o->n);
+}
+
+static void String_Finish(struct Output_Sink *o_)
+{
+ struct String_Output_Sink *o = (struct String_Output_Sink*) o_;
+
+ if (o->n < o->size)
+ *(o->s) = '\0';
+ else
+ /*
+ * Output was truncated; write terminator at end of buffer
+ * (we will have advanced one character too far)
+ */
+ *(o->s - 1) = '\0';
+}
+
+int snprintf(char *s, size_t size, const char *fmt, ...)
+{
+ struct String_Output_Sink sink;
+ int rc;
+ va_list args;
+
+ /* Prepare string output sink */
+ sink.o.Emit = &String_Emit;
+ sink.o.Finish = &String_Finish;
+ sink.s = s;
+ sink.n = 0;
+ sink.size = size;
+
+ /* Format the string */
+ va_start(args, fmt);
+ rc = Format_Output(&sink.o, fmt, args);
+ va_end(args);
+
+ return rc;
+}
--- /dev/null
+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.
--- /dev/null
+// Adapted for geekos: http://www.cs.umd.edu/~daveho/geekos/
+// Original version of BGET downloaded from: http://www.fourmilab.ch/bget/
+// $Revision: 1.1 $
+
+// GeekOS changes are (mostly) confined to #if defined (GEEKOS)
+// sections.
+
+/*
+
+ B G E T
+
+ Buffer allocator
+
+ Designed and implemented in April of 1972 by John Walker, based on the
+ Case Algol OPRO$ algorithm implemented in 1966.
+
+ Reimplemented in 1975 by John Walker for the Interdata 70.
+ Reimplemented in 1977 by John Walker for the Marinchip 9900.
+ Reimplemented in 1982 by Duff Kurland for the Intel 8080.
+
+ Portable C version implemented in September of 1990 by an older, wiser
+ instance of the original implementor.
+
+ Souped up and/or weighed down slightly shortly thereafter by Greg
+ Lutz.
+
+ AMIX edition, including the new compaction call-back option, prepared
+ by John Walker in July of 1992.
+
+ Bug in built-in test program fixed, ANSI compiler warnings eradicated,
+ buffer pool validator implemented, and guaranteed repeatable test
+ added by John Walker in October of 1995.
+
+ This program is in the public domain.
+
+ 1. This is the book of the generations of Adam. In the day that God
+ created man, in the likeness of God made he him;
+ 2. Male and female created he them; and blessed them, and called
+ their name Adam, in the day when they were created.
+ 3. And Adam lived an hundred and thirty years, and begat a son in
+ his own likeness, and after his image; and called his name Seth:
+ 4. And the days of Adam after he had begotten Seth were eight
+ hundred years: and he begat sons and daughters:
+ 5. And all the days that Adam lived were nine hundred and thirty
+ years: and he died.
+ 6. And Seth lived an hundred and five years, and begat Enos:
+ 7. And Seth lived after he begat Enos eight hundred and seven years,
+ and begat sons and daughters:
+ 8. And all the days of Seth were nine hundred and twelve years: and
+ he died.
+ 9. And Enos lived ninety years, and begat Cainan:
+ 10. And Enos lived after he begat Cainan eight hundred and fifteen
+ years, and begat sons and daughters:
+ 11. And all the days of Enos were nine hundred and five years: and
+ he died.
+ 12. And Cainan lived seventy years and begat Mahalaleel:
+ 13. And Cainan lived after he begat Mahalaleel eight hundred and
+ forty years, and begat sons and daughters:
+ 14. And all the days of Cainan were nine hundred and ten years: and
+ he died.
+ 15. And Mahalaleel lived sixty and five years, and begat Jared:
+ 16. And Mahalaleel lived after he begat Jared eight hundred and
+ thirty years, and begat sons and daughters:
+ 17. And all the days of Mahalaleel were eight hundred ninety and
+ five years: and he died.
+ 18. And Jared lived an hundred sixty and two years, and he begat
+ Enoch:
+ 19. And Jared lived after he begat Enoch eight hundred years, and
+ begat sons and daughters:
+ 20. And all the days of Jared were nine hundred sixty and two years:
+ and he died.
+ 21. And Enoch lived sixty and five years, and begat Methuselah:
+ 22. And Enoch walked with God after he begat Methuselah three
+ hundred years, and begat sons and daughters:
+ 23. And all the days of Enoch were three hundred sixty and five
+ years:
+ 24. And Enoch walked with God: and he was not; for God took him.
+ 25. And Methuselah lived an hundred eighty and seven years, and
+ begat Lamech.
+ 26. And Methuselah lived after he begat Lamech seven hundred eighty
+ and two years, and begat sons and daughters:
+ 27. And all the days of Methuselah were nine hundred sixty and nine
+ years: and he died.
+ 28. And Lamech lived an hundred eighty and two years, and begat a
+ son:
+ 29. And he called his name Noah, saying, This same shall comfort us
+ concerning our work and toil of our hands, because of the ground
+ which the LORD hath cursed.
+ 30. And Lamech lived after he begat Noah five hundred ninety and
+ five years, and begat sons and daughters:
+ 31. And all the days of Lamech were seven hundred seventy and seven
+ years: and he died.
+ 32. And Noah was five hundred years old: and Noah begat Shem, Ham,
+ and Japheth.
+
+ And buffers begat buffers, and links begat links, and buffer pools
+ begat links to chains of buffer pools containing buffers, and lo the
+ buffers and links and pools of buffers and pools of links to chains of
+ pools of buffers were fruitful and they multiplied and the Operating
+ System looked down upon them and said that it was Good.
+
+
+ INTRODUCTION
+ ============
+
+ BGET is a comprehensive memory allocation package which is easily
+ configured to the needs of an application. BGET is efficient in
+ both the time needed to allocate and release buffers and in the
+ memory overhead required for buffer pool management. It
+ automatically consolidates contiguous space to minimise
+ fragmentation. BGET is configured by compile-time definitions,
+ Major options include:
+
+ * A built-in test program to exercise BGET and
+ demonstrate how the various functions are used.
+
+ * Allocation by either the "first fit" or "best fit"
+ method.
+
+ * Wiping buffers at release time to catch code which
+ references previously released storage.
+
+ * Built-in routines to dump individual buffers or the
+ entire buffer pool.
+
+ * Retrieval of allocation and pool size statistics.
+
+ * Quantisation of buffer sizes to a power of two to
+ satisfy hardware alignment constraints.
+
+ * Automatic pool compaction, growth, and shrinkage by
+ means of call-backs to user defined functions.
+
+ Applications of BGET can range from storage management in
+ ROM-based embedded programs to providing the framework upon which
+ a multitasking system incorporating garbage collection is
+ constructed. BGET incorporates extensive internal consistency
+ checking using the <assert.h> mechanism; all these checks can be
+ turned off by compiling with NDEBUG defined, yielding a version of
+ BGET with minimal size and maximum speed.
+
+ The basic algorithm underlying BGET has withstood the test of
+ time; more than 25 years have passed since the first
+ implementation of this code. And yet, it is substantially more
+ efficient than the native allocation schemes of many operating
+ systems: the Macintosh and Microsoft Windows to name two, on which
+ programs have obtained substantial speed-ups by layering BGET as
+ an application level memory manager atop the underlying system's.
+
+ BGET has been implemented on the largest mainframes and the lowest
+ of microprocessors. It has served as the core for multitasking
+ operating systems, multi-thread applications, embedded software in
+ data network switching processors, and a host of C programs. And
+ while it has accreted flexibility and additional options over the
+ years, it remains fast, memory efficient, portable, and easy to
+ integrate into your program.
+
+
+ BGET IMPLEMENTATION ASSUMPTIONS
+ ===============================
+
+ BGET is written in as portable a dialect of C as possible. The
+ only fundamental assumption about the underlying hardware
+ architecture is that memory is allocated is a linear array which
+ can be addressed as a vector of C "char" objects. On segmented
+ address space architectures, this generally means that BGET should
+ be used to allocate storage within a single segment (although some
+ compilers simulate linear address spaces on segmented
+ architectures). On segmented architectures, then, BGET buffer
+ pools may not be larger than a segment, but since BGET allows any
+ number of separate buffer pools, there is no limit on the total
+ storage which can be managed, only on the largest individual
+ object which can be allocated. Machines with a linear address
+ architecture, such as the VAX, 680x0, Sparc, MIPS, or the Intel
+ 80386 and above in native mode, may use BGET without restriction.
+
+
+ GETTING STARTED WITH BGET
+ =========================
+
+ Although BGET can be configured in a multitude of fashions, there
+ are three basic ways of working with BGET. The functions
+ mentioned below are documented in the following section. Please
+ excuse the forward references which are made in the interest of
+ providing a roadmap to guide you to the BGET functions you're
+ likely to need.
+
+ Embedded Applications
+ ---------------------
+
+ Embedded applications typically have a fixed area of memory
+ dedicated to buffer allocation (often in a separate RAM address
+ space distinct from the ROM that contains the executable code).
+ To use BGET in such an environment, simply call bpool() with the
+ start address and length of the buffer pool area in RAM, then
+ allocate buffers with bget() and release them with brel().
+ Embedded applications with very limited RAM but abundant CPU speed
+ may benefit by configuring BGET for BestFit allocation (which is
+ usually not worth it in other environments).
+
+ Malloc() Emulation
+ ------------------
+
+ If the C library malloc() function is too slow, not present in
+ your development environment (for example, an a native Windows or
+ Macintosh program), or otherwise unsuitable, you can replace it
+ with BGET. Initially define a buffer pool of an appropriate size
+ with bpool()--usually obtained by making a call to the operating
+ system's low-level memory allocator. Then allocate buffers with
+ bget(), bgetz(), and bgetr() (the last two permit the allocation
+ of buffers initialised to zero and [inefficient] re-allocation of
+ existing buffers for compatibility with C library functions).
+ Release buffers by calling brel(). If a buffer allocation request
+ fails, obtain more storage from the underlying operating system,
+ add it to the buffer pool by another call to bpool(), and continue
+ execution.
+
+ Automatic Storage Management
+ ----------------------------
+
+ You can use BGET as your application's native memory manager and
+ implement automatic storage pool expansion, contraction, and
+ optionally application-specific memory compaction by compiling
+ BGET with the BECtl variable defined, then calling bectl() and
+ supplying functions for storage compaction, acquisition, and
+ release, as well as a standard pool expansion increment. All of
+ these functions are optional (although it doesn't make much sense
+ to provide a release function without an acquisition function,
+ does it?). Once the call-back functions have been defined with
+ bectl(), you simply use bget() and brel() to allocate and release
+ storage as before. You can supply an initial buffer pool with
+ bpool() or rely on automatic allocation to acquire the entire
+ pool. When a call on bget() cannot be satisfied, BGET first
+ checks if a compaction function has been supplied. If so, it is
+ called (with the space required to satisfy the allocation request
+ and a sequence number to allow the compaction routine to be called
+ successively without looping). If the compaction function is able
+ to free any storage (it needn't know whether the storage it freed
+ was adequate) it should return a nonzero value, whereupon BGET
+ will retry the allocation request and, if it fails again, call the
+ compaction function again with the next-higher sequence number.
+
+ If the compaction function returns zero, indicating failure to
+ free space, or no compaction function is defined, BGET next tests
+ whether a non-NULL allocation function was supplied to bectl().
+ If so, that function is called with an argument indicating how
+ many bytes of additional space are required. This will be the
+ standard pool expansion increment supplied in the call to bectl()
+ unless the original bget() call requested a buffer larger than
+ this; buffers larger than the standard pool block can be managed
+ "off the books" by BGET in this mode. If the allocation function
+ succeeds in obtaining the storage, it returns a pointer to the new
+ block and BGET expands the buffer pool; if it fails, the
+ allocation request fails and returns NULL to the caller. If a
+ non-NULL release function is supplied, expansion blocks which
+ become totally empty are released to the global free pool by
+ passing their addresses to the release function.
+
+ Equipped with appropriate allocation, release, and compaction
+ functions, BGET can be used as part of very sophisticated memory
+ management strategies, including garbage collection. (Note,
+ however, that BGET is *not* a garbage collector by itself, and
+ that developing such a system requires much additional logic and
+ careful design of the application's memory allocation strategy.)
+
+
+ BGET FUNCTION DESCRIPTIONS
+ ==========================
+
+ Functions implemented in this file (some are enabled by certain of
+ the optional settings below):
+
+ void bpool(void *buffer, bufsize len);
+
+ Create a buffer pool of <len> bytes, using the storage starting at
+ <buffer>. You can call bpool() subsequently to contribute
+ additional storage to the overall buffer pool.
+
+ void *bget(bufsize size);
+
+ Allocate a buffer of <size> bytes. The address of the buffer is
+ returned, or NULL if insufficient memory was available to allocate
+ the buffer.
+
+ void *bgetz(bufsize size);
+
+ Allocate a buffer of <size> bytes and clear it to all zeroes. The
+ address of the buffer is returned, or NULL if insufficient memory
+ was available to allocate the buffer.
+
+ void *bgetr(void *buffer, bufsize newsize);
+
+ Reallocate a buffer previously allocated by bget(), changing its
+ size to <newsize> and preserving all existing data. NULL is
+ returned if insufficient memory is available to reallocate the
+ buffer, in which case the original buffer remains intact.
+
+ void brel(void *buf);
+
+ Return the buffer <buf>, previously allocated by bget(), to the
+ free space pool.
+
+ void bectl(int (*compact)(bufsize sizereq, int sequence),
+ void *(*acquire)(bufsize size),
+ void (*release)(void *buf),
+ bufsize pool_incr);
+
+ Expansion control: specify functions through which the package may
+ compact storage (or take other appropriate action) when an
+ allocation request fails, and optionally automatically acquire
+ storage for expansion blocks when necessary, and release such
+ blocks when they become empty. If <compact> is non-NULL, whenever
+ a buffer allocation request fails, the <compact> function will be
+ called with arguments specifying the number of bytes (total buffer
+ size, including header overhead) required to satisfy the
+ allocation request, and a sequence number indicating the number of
+ consecutive calls on <compact> attempting to satisfy this
+ allocation request. The sequence number is 1 for the first call
+ on <compact> for a given allocation request, and increments on
+ subsequent calls, permitting the <compact> function to take
+ increasingly dire measures in an attempt to free up storage. If
+ the <compact> function returns a nonzero value, the allocation
+ attempt is re-tried. If <compact> returns 0 (as it must if it
+ isn't able to release any space or add storage to the buffer
+ pool), the allocation request fails, which can trigger automatic
+ pool expansion if the <acquire> argument is non-NULL. At the time
+ the <compact> function is called, the state of the buffer
+ allocator is identical to that at the moment the allocation
+ request was made; consequently, the <compact> function may call
+ brel(), bpool(), bstats(), and/or directly manipulate the buffer
+ pool in any manner which would be valid were the application in
+ control. This does not, however, relieve the <compact> function
+ of the need to ensure that whatever actions it takes do not change
+ things underneath the application that made the allocation
+ request. For example, a <compact> function that released a buffer
+ in the process of being reallocated with bgetr() would lead to
+ disaster. Implementing a safe and effective <compact> mechanism
+ requires careful design of an application's memory architecture,
+ and cannot generally be easily retrofitted into existing code.
+
+ If <acquire> is non-NULL, that function will be called whenever an
+ allocation request fails. If the <acquire> function succeeds in
+ allocating the requested space and returns a pointer to the new
+ area, allocation will proceed using the expanded buffer pool. If
+ <acquire> cannot obtain the requested space, it should return NULL
+ and the entire allocation process will fail. <pool_incr>
+ specifies the normal expansion block size. Providing an <acquire>
+ function will cause subsequent bget() requests for buffers too
+ large to be managed in the linked-block scheme (in other words,
+ larger than <pool_incr> minus the buffer overhead) to be satisfied
+ directly by calls to the <acquire> function. Automatic release of
+ empty pool blocks will occur only if all pool blocks in the system
+ are the size given by <pool_incr>.
+
+ void bstats(bufsize *curalloc, bufsize *totfree,
+ bufsize *maxfree, long *nget, long *nrel);
+
+ The amount of space currently allocated is stored into the
+ variable pointed to by <curalloc>. The total free space (sum of
+ all free blocks in the pool) is stored into the variable pointed
+ to by <totfree>, and the size of the largest single block in the
+ free space pool is stored into the variable pointed to by
+ <maxfree>. The variables pointed to by <nget> and <nrel> are
+ filled, respectively, with the number of successful (non-NULL
+ return) bget() calls and the number of brel() calls.
+
+ void bstatse(bufsize *pool_incr, long *npool,
+ long *npget, long *nprel,
+ long *ndget, long *ndrel);
+
+ Extended statistics: The expansion block size will be stored into
+ the variable pointed to by <pool_incr>, or the negative thereof if
+ automatic expansion block releases are disabled. The number of
+ currently active pool blocks will be stored into the variable
+ pointed to by <npool>. The variables pointed to by <npget> and
+ <nprel> will be filled with, respectively, the number of expansion
+ block acquisitions and releases which have occurred. The
+ variables pointed to by <ndget> and <ndrel> will be filled with
+ the number of bget() and brel() calls, respectively, managed
+ through blocks directly allocated by the acquisition and release
+ functions.
+
+ void bufdump(void *buf);
+
+ The buffer pointed to by <buf> is dumped on standard output.
+
+ void bpoold(void *pool, int dumpalloc, int dumpfree);
+
+ All buffers in the buffer pool <pool>, previously initialised by a
+ call on bpool(), are listed in ascending memory address order. If
+ <dumpalloc> is nonzero, the contents of allocated buffers are
+ dumped; if <dumpfree> is nonzero, the contents of free blocks are
+ dumped.
+
+ int bpoolv(void *pool);
+
+ The named buffer pool, previously initialised by a call on
+ bpool(), is validated for bad pointers, overwritten data, etc. If
+ compiled with NDEBUG not defined, any error generates an assertion
+ failure. Otherwise 1 is returned if the pool is valid, 0 if an
+ error is found.
+
+
+ BGET CONFIGURATION
+ ==================
+*/
+
+/*#define TestProg 20000*/ /* Generate built-in test program
+ if defined. The value specifies
+ how many buffer allocation attempts
+ the test program should make. */
+
+#define SizeQuant 4 /* Buffer allocation size quantum:
+ all buffers allocated are a
+ multiple of this size. This
+ MUST be a power of two. */
+
+/*#define BufDump 1*/ /* Define this symbol to enable the
+ bpoold() function which dumps the
+ buffers in a buffer pool. */
+
+/*#define BufValid 1*/ /* Define this symbol to enable the
+ bpoolv() function for validating
+ a buffer pool. */
+
+/*#define DumpData 1*/ /* Define this symbol to enable the
+ bufdump() function which allows
+ dumping the contents of an allocated
+ or free buffer. */
+
+/*#define BufStats 1*/ /* Define this symbol to enable the
+ bstats() function which calculates
+ the total free space in the buffer
+ pool, the largest available
+ buffer, and the total space
+ currently allocated. */
+
+/*#define FreeWipe 1*/ /* Wipe free buffers to a guaranteed
+ pattern of garbage to trip up
+ miscreants who attempt to use
+ pointers into released buffers. */
+
+#define BestFit 1 /* Use a best fit algorithm when
+ searching for space for an
+ allocation request. This uses
+ memory more efficiently, but
+ allocation will be much slower. */
+
+/*#define BECtl 1*/ /* Define this symbol to enable the
+ bectl() function for automatic
+ pool space control. */
+
+#if defined (GEEKOS)
+
+#include <geekos/string.h> // for memset()
+
+// Provide an assert() macro
+#include <geekos/kassert.h>
+#define assert(exp) KASSERT(exp)
+
+#else // define (GEEKOS)
+
+#include <stdio.h>
+
+#ifdef lint
+#define NDEBUG /* Exits in asserts confuse lint */
+/* LINTLIBRARY */ /* Don't complain about def, no ref */
+extern char *sprintf(); /* Sun includes don't define sprintf */
+#endif
+
+#include <assert.h>
+#include <memory.h>
+
+#endif // defined (GEEKOS)
+
+#ifdef BufDump /* BufDump implies DumpData */
+#ifndef DumpData
+#define DumpData 1
+#endif
+#endif
+
+#ifdef DumpData
+#include <ctype.h>
+#endif
+
+/* Declare the interface, including the requested buffer size type,
+ bufsize. */
+
+#include <geekos/bget.h>
+
+#define MemSize int /* Type for size arguments to memxxx()
+ functions such as memcmp(). */
+
+/* Queue links */
+
+struct qlinks {
+ struct bfhead *flink; /* Forward link */
+ struct bfhead *blink; /* Backward link */
+};
+
+/* Header in allocated and free buffers */
+
+struct bhead {
+ bufsize prevfree; /* Relative link back to previous
+ free buffer in memory or 0 if
+ previous buffer is allocated. */
+ bufsize bsize; /* Buffer size: positive if free,
+ negative if allocated. */
+};
+#define BH(p) ((struct bhead *) (p))
+
+/* Header in directly allocated buffers (by acqfcn) */
+
+struct bdhead {
+ bufsize tsize; /* Total size, including overhead */
+ struct bhead bh; /* Common header */
+};
+#define BDH(p) ((struct bdhead *) (p))
+
+/* Header in free buffers */
+
+struct bfhead {
+ struct bhead bh; /* Common allocated/free header */
+ struct qlinks ql; /* Links on free list */
+};
+#define BFH(p) ((struct bfhead *) (p))
+
+static struct bfhead freelist = { /* List of free buffers */
+ {0, 0},
+ {&freelist, &freelist}
+};
+
+
+#ifdef BufStats
+static bufsize totalloc = 0; /* Total space currently allocated */
+static long numget = 0, numrel = 0; /* Number of bget() and brel() calls */
+#ifdef BECtl
+static long numpblk = 0; /* Number of pool blocks */
+static long numpget = 0, numprel = 0; /* Number of block gets and rels */
+static long numdget = 0, numdrel = 0; /* Number of direct gets and rels */
+#endif /* BECtl */
+#endif /* BufStats */
+
+#ifdef BECtl
+
+/* Automatic expansion block management functions */
+
+static int (*compfcn) _((bufsize sizereq, int sequence)) = NULL;
+static void *(*acqfcn) _((bufsize size)) = NULL;
+static void (*relfcn) _((void *buf)) = NULL;
+
+static bufsize exp_incr = 0; /* Expansion block size */
+static bufsize pool_len = 0; /* 0: no bpool calls have been made
+ -1: not all pool blocks are
+ the same size
+ >0: (common) block size for all
+ bpool calls made so far
+ */
+#endif
+
+/* Minimum allocation quantum: */
+
+#define QLSize (sizeof(struct qlinks))
+#define SizeQ ((SizeQuant > QLSize) ? SizeQuant : QLSize)
+
+#define V (void) /* To denote unwanted returned values */
+
+/* End sentinel: value placed in bsize field of dummy block delimiting
+ end of pool block. The most negative number which will fit in a
+ bufsize, defined in a way that the compiler will accept. */
+
+#define ESent ((bufsize) (-(((1L << (sizeof(bufsize) * 8 - 2)) - 1) * 2) - 2))
+
+/* BGET -- Allocate a buffer. */
+
+void *bget(requested_size)
+ bufsize requested_size;
+{
+ bufsize size = requested_size;
+ struct bfhead *b;
+#ifdef BestFit
+ struct bfhead *best;
+#endif
+ void *buf;
+#ifdef BECtl
+ int compactseq = 0;
+#endif
+
+ assert(size > 0);
+
+ if (size < SizeQ) { /* Need at least room for the */
+ size = SizeQ; /* queue links. */
+ }
+#ifdef SizeQuant
+#if SizeQuant > 1
+ size = (size + (SizeQuant - 1)) & (~(SizeQuant - 1));
+#endif
+#endif
+
+ size += sizeof(struct bhead); /* Add overhead in allocated buffer
+ to size required. */
+
+#ifdef BECtl
+ /* If a compact function was provided in the call to bectl(), wrap
+ a loop around the allocation process to allow compaction to
+ intervene in case we don't find a suitable buffer in the chain. */
+
+ while (1) {
+#endif
+ b = freelist.ql.flink;
+#ifdef BestFit
+ best = &freelist;
+#endif
+
+
+ /* Scan the free list searching for the first buffer big enough
+ to hold the requested size buffer. */
+
+#ifdef BestFit
+ while (b != &freelist) {
+ if (b->bh.bsize >= size) {
+ if ((best == &freelist) || (b->bh.bsize < best->bh.bsize)) {
+ best = b;
+ }
+ }
+ b = b->ql.flink; /* Link to next buffer */
+ }
+ b = best;
+#endif /* BestFit */
+
+ while (b != &freelist) {
+ if ((bufsize) b->bh.bsize >= size) {
+
+ /* Buffer is big enough to satisfy the request. Allocate it
+ to the caller. We must decide whether the buffer is large
+ enough to split into the part given to the caller and a
+ free buffer that remains on the free list, or whether the
+ entire buffer should be removed from the free list and
+ given to the caller in its entirety. We only split the
+ buffer if enough room remains for a header plus the minimum
+ quantum of allocation. */
+
+ if ((b->bh.bsize - size) > (SizeQ + (sizeof(struct bhead)))) {
+ struct bhead *ba, *bn;
+
+ ba = BH(((char *) b) + (b->bh.bsize - size));
+ bn = BH(((char *) ba) + size);
+ assert(bn->prevfree == b->bh.bsize);
+ /* Subtract size from length of free block. */
+ b->bh.bsize -= size;
+ /* Link allocated buffer to the previous free buffer. */
+ ba->prevfree = b->bh.bsize;
+ /* Plug negative size into user buffer. */
+ ba->bsize = -(bufsize) size;
+ /* Mark buffer after this one not preceded by free block. */
+ bn->prevfree = 0;
+
+#ifdef BufStats
+ totalloc += size;
+ numget++; /* Increment number of bget() calls */
+#endif
+ buf = (void *) ((((char *) ba) + sizeof(struct bhead)));
+ return buf;
+ } else {
+ struct bhead *ba;
+
+ ba = BH(((char *) b) + b->bh.bsize);
+ assert(ba->prevfree == b->bh.bsize);
+
+ /* The buffer isn't big enough to split. Give the whole
+ shebang to the caller and remove it from the free list. */
+
+ assert(b->ql.blink->ql.flink == b);
+ assert(b->ql.flink->ql.blink == b);
+ b->ql.blink->ql.flink = b->ql.flink;
+ b->ql.flink->ql.blink = b->ql.blink;
+
+#ifdef BufStats
+ totalloc += b->bh.bsize;
+ numget++; /* Increment number of bget() calls */
+#endif
+ /* Negate size to mark buffer allocated. */
+ b->bh.bsize = -(b->bh.bsize);
+
+ /* Zero the back pointer in the next buffer in memory
+ to indicate that this buffer is allocated. */
+ ba->prevfree = 0;
+
+ /* Give user buffer starting at queue links. */
+ buf = (void *) &(b->ql);
+ return buf;
+ }
+ }
+ b = b->ql.flink; /* Link to next buffer */
+ }
+#ifdef BECtl
+
+ /* We failed to find a buffer. If there's a compact function
+ defined, notify it of the size requested. If it returns
+ true, try the allocation again. */
+
+ if ((compfcn == NULL) || (!(*compfcn)(size, ++compactseq))) {
+ break;
+ }
+ }
+
+ /* No buffer available with requested size free. */
+
+ /* Don't give up yet -- look in the reserve supply. */
+
+ if (acqfcn != NULL) {
+ if (size > exp_incr - sizeof(struct bhead)) {
+
+ /* Request is too large to fit in a single expansion
+ block. Try to satisy it by a direct buffer acquisition. */
+
+ struct bdhead *bdh;
+
+ size += sizeof(struct bdhead) - sizeof(struct bhead);
+ if ((bdh = BDH((*acqfcn)((bufsize) size))) != NULL) {
+
+ /* Mark the buffer special by setting the size field
+ of its header to zero. */
+ bdh->bh.bsize = 0;
+ bdh->bh.prevfree = 0;
+ bdh->tsize = size;
+#ifdef BufStats
+ totalloc += size;
+ numget++; /* Increment number of bget() calls */
+ numdget++; /* Direct bget() call count */
+#endif
+ buf = (void *) (bdh + 1);
+ return buf;
+ }
+
+ } else {
+
+ /* Try to obtain a new expansion block */
+
+ void *newpool;
+
+ if ((newpool = (*acqfcn)((bufsize) exp_incr)) != NULL) {
+ bpool(newpool, exp_incr);
+ buf = bget(requested_size); /* This can't, I say, can't
+ get into a loop. */
+ return buf;
+ }
+ }
+ }
+
+ /* Still no buffer available */
+
+#endif /* BECtl */
+
+ return NULL;
+}
+
+/* BGETZ -- Allocate a buffer and clear its contents to zero. We clear
+ the entire contents of the buffer to zero, not just the
+ region requested by the caller. */
+
+void *bgetz(size)
+ bufsize size;
+{
+ char *buf = (char *) bget(size);
+
+ if (buf != NULL) {
+ struct bhead *b;
+ bufsize rsize;
+
+ b = BH(buf - sizeof(struct bhead));
+ rsize = -(b->bsize);
+ if (rsize == 0) {
+ struct bdhead *bd;
+
+ bd = BDH(buf - sizeof(struct bdhead));
+ rsize = bd->tsize - sizeof(struct bdhead);
+ } else {
+ rsize -= sizeof(struct bhead);
+ }
+ assert(rsize >= size);
+ V memset(buf, 0, (MemSize) rsize);
+ }
+ return ((void *) buf);
+}
+
+/* BGETR -- Reallocate a buffer. This is a minimal implementation,
+ simply in terms of brel() and bget(). It could be
+ enhanced to allow the buffer to grow into adjacent free
+ blocks and to avoid moving data unnecessarily. */
+
+void *bgetr(buf, size)
+ void *buf;
+ bufsize size;
+{
+ void *nbuf;
+ bufsize osize; /* Old size of buffer */
+ struct bhead *b;
+
+ if ((nbuf = bget(size)) == NULL) { /* Acquire new buffer */
+ return NULL;
+ }
+ if (buf == NULL) {
+ return nbuf;
+ }
+ b = BH(((char *) buf) - sizeof(struct bhead));
+ osize = -b->bsize;
+#ifdef BECtl
+ if (osize == 0) {
+ /* Buffer acquired directly through acqfcn. */
+ struct bdhead *bd;
+
+ bd = BDH(((char *) buf) - sizeof(struct bdhead));
+ osize = bd->tsize - sizeof(struct bdhead);
+ } else
+#endif
+ osize -= sizeof(struct bhead);
+ assert(osize > 0);
+ V memcpy((char *) nbuf, (char *) buf, /* Copy the data */
+ (MemSize) ((size < osize) ? size : osize));
+ brel(buf);
+ return nbuf;
+}
+
+/* BREL -- Release a buffer. */
+
+void brel(buf)
+ void *buf;
+{
+ struct bfhead *b, *bn;
+
+ b = BFH(((char *) buf) - sizeof(struct bhead));
+#ifdef BufStats
+ numrel++; /* Increment number of brel() calls */
+#endif
+ assert(buf != NULL);
+
+#ifdef BECtl
+ if (b->bh.bsize == 0) { /* Directly-acquired buffer? */
+ struct bdhead *bdh;
+
+ bdh = BDH(((char *) buf) - sizeof(struct bdhead));
+ assert(b->bh.prevfree == 0);
+#ifdef BufStats
+ totalloc -= bdh->tsize;
+ assert(totalloc >= 0);
+ numdrel++; /* Number of direct releases */
+#endif /* BufStats */
+#ifdef FreeWipe
+ V memset((char *) buf, 0x55,
+ (MemSize) (bdh->tsize - sizeof(struct bdhead)));
+#endif /* FreeWipe */
+ assert(relfcn != NULL);
+ (*relfcn)((void *) bdh); /* Release it directly. */
+ return;
+ }
+#endif /* BECtl */
+
+ /* Buffer size must be negative, indicating that the buffer is
+ allocated. */
+
+ if (b->bh.bsize >= 0) {
+ bn = NULL;
+ }
+ assert(b->bh.bsize < 0);
+
+ /* Back pointer in next buffer must be zero, indicating the
+ same thing: */
+
+ assert(BH((char *) b - b->bh.bsize)->prevfree == 0);
+
+#ifdef BufStats
+ totalloc += b->bh.bsize;
+ assert(totalloc >= 0);
+#endif
+
+ /* If the back link is nonzero, the previous buffer is free. */
+
+ if (b->bh.prevfree != 0) {
+
+ /* The previous buffer is free. Consolidate this buffer with it
+ by adding the length of this buffer to the previous free
+ buffer. Note that we subtract the size in the buffer being
+ released, since it's negative to indicate that the buffer is
+ allocated. */
+
+ register bufsize size = b->bh.bsize;
+
+ /* Make the previous buffer the one we're working on. */
+ assert(BH((char *) b - b->bh.prevfree)->bsize == b->bh.prevfree);
+ b = BFH(((char *) b) - b->bh.prevfree);
+ b->bh.bsize -= size;
+ } else {
+
+ /* The previous buffer isn't allocated. Insert this buffer
+ on the free list as an isolated free block. */
+
+ assert(freelist.ql.blink->ql.flink == &freelist);
+ assert(freelist.ql.flink->ql.blink == &freelist);
+ b->ql.flink = &freelist;
+ b->ql.blink = freelist.ql.blink;
+ freelist.ql.blink = b;
+ b->ql.blink->ql.flink = b;
+ b->bh.bsize = -b->bh.bsize;
+ }
+
+ /* Now we look at the next buffer in memory, located by advancing from
+ the start of this buffer by its size, to see if that buffer is
+ free. If it is, we combine this buffer with the next one in
+ memory, dechaining the second buffer from the free list. */
+
+ bn = BFH(((char *) b) + b->bh.bsize);
+ if (bn->bh.bsize > 0) {
+
+ /* The buffer is free. Remove it from the free list and add
+ its size to that of our buffer. */
+
+ assert(BH((char *) bn + bn->bh.bsize)->prevfree == bn->bh.bsize);
+ assert(bn->ql.blink->ql.flink == bn);
+ assert(bn->ql.flink->ql.blink == bn);
+ bn->ql.blink->ql.flink = bn->ql.flink;
+ bn->ql.flink->ql.blink = bn->ql.blink;
+ b->bh.bsize += bn->bh.bsize;
+
+ /* Finally, advance to the buffer that follows the newly
+ consolidated free block. We must set its backpointer to the
+ head of the consolidated free block. We know the next block
+ must be an allocated block because the process of recombination
+ guarantees that two free blocks will never be contiguous in
+ memory. */
+
+ bn = BFH(((char *) b) + b->bh.bsize);
+ }
+#ifdef FreeWipe
+ V memset(((char *) b) + sizeof(struct bfhead), 0x55,
+ (MemSize) (b->bh.bsize - sizeof(struct bfhead)));
+#endif
+ assert(bn->bh.bsize < 0);
+
+ /* The next buffer is allocated. Set the backpointer in it to point
+ to this buffer; the previous free buffer in memory. */
+
+ bn->bh.prevfree = b->bh.bsize;
+
+#ifdef BECtl
+
+ /* If a block-release function is defined, and this free buffer
+ constitutes the entire block, release it. Note that pool_len
+ is defined in such a way that the test will fail unless all
+ pool blocks are the same size. */
+
+ if (relfcn != NULL &&
+ ((bufsize) b->bh.bsize) == (pool_len - sizeof(struct bhead))) {
+
+ assert(b->bh.prevfree == 0);
+ assert(BH((char *) b + b->bh.bsize)->bsize == ESent);
+ assert(BH((char *) b + b->bh.bsize)->prevfree == b->bh.bsize);
+ /* Unlink the buffer from the free list */
+ b->ql.blink->ql.flink = b->ql.flink;
+ b->ql.flink->ql.blink = b->ql.blink;
+
+ (*relfcn)(b);
+#ifdef BufStats
+ numprel++; /* Nr of expansion block releases */
+ numpblk--; /* Total number of blocks */
+ assert(numpblk == numpget - numprel);
+#endif /* BufStats */
+ }
+#endif /* BECtl */
+}
+
+#ifdef BECtl
+
+/* BECTL -- Establish automatic pool expansion control */
+
+void bectl(compact, acquire, release, pool_incr)
+ int (*compact) _((bufsize sizereq, int sequence));
+ void *(*acquire) _((bufsize size));
+ void (*release) _((void *buf));
+ bufsize pool_incr;
+{
+ compfcn = compact;
+ acqfcn = acquire;
+ relfcn = release;
+ exp_incr = pool_incr;
+}
+#endif
+
+/* BPOOL -- Add a region of memory to the buffer pool. */
+
+void bpool(buf, len)
+ void *buf;
+ bufsize len;
+{
+ struct bfhead *b = BFH(buf);
+ struct bhead *bn;
+
+#ifdef SizeQuant
+ len &= ~(SizeQuant - 1);
+#endif
+#ifdef BECtl
+ if (pool_len == 0) {
+ pool_len = len;
+ } else if (len != pool_len) {
+ pool_len = -1;
+ }
+#ifdef BufStats
+ numpget++; /* Number of block acquisitions */
+ numpblk++; /* Number of blocks total */
+ assert(numpblk == numpget - numprel);
+#endif /* BufStats */
+#endif /* BECtl */
+
+ /* Since the block is initially occupied by a single free buffer,
+ it had better not be (much) larger than the largest buffer
+ whose size we can store in bhead.bsize. */
+
+ assert(len - sizeof(struct bhead) <= -((bufsize) ESent + 1));
+
+ /* Clear the backpointer at the start of the block to indicate that
+ there is no free block prior to this one. That blocks
+ recombination when the first block in memory is released. */
+
+ b->bh.prevfree = 0;
+
+ /* Chain the new block to the free list. */
+
+ assert(freelist.ql.blink->ql.flink == &freelist);
+ assert(freelist.ql.flink->ql.blink == &freelist);
+ b->ql.flink = &freelist;
+ b->ql.blink = freelist.ql.blink;
+ freelist.ql.blink = b;
+ b->ql.blink->ql.flink = b;
+
+ /* Create a dummy allocated buffer at the end of the pool. This dummy
+ buffer is seen when a buffer at the end of the pool is released and
+ blocks recombination of the last buffer with the dummy buffer at
+ the end. The length in the dummy buffer is set to the largest
+ negative number to denote the end of the pool for diagnostic
+ routines (this specific value is not counted on by the actual
+ allocation and release functions). */
+
+ len -= sizeof(struct bhead);
+ b->bh.bsize = (bufsize) len;
+#ifdef FreeWipe
+ V memset(((char *) b) + sizeof(struct bfhead), 0x55,
+ (MemSize) (len - sizeof(struct bfhead)));
+#endif
+ bn = BH(((char *) b) + len);
+ bn->prevfree = (bufsize) len;
+ /* Definition of ESent assumes two's complement! */
+ assert((~0) == -1);
+ bn->bsize = ESent;
+}
+
+#ifdef BufStats
+
+/* BSTATS -- Return buffer allocation free space statistics. */
+
+void bstats(curalloc, totfree, maxfree, nget, nrel)
+ bufsize *curalloc, *totfree, *maxfree;
+ long *nget, *nrel;
+{
+ struct bfhead *b = freelist.ql.flink;
+
+ *nget = numget;
+ *nrel = numrel;
+ *curalloc = totalloc;
+ *totfree = 0;
+ *maxfree = -1;
+ while (b != &freelist) {
+ assert(b->bh.bsize > 0);
+ *totfree += b->bh.bsize;
+ if (b->bh.bsize > *maxfree) {
+ *maxfree = b->bh.bsize;
+ }
+ b = b->ql.flink; /* Link to next buffer */
+ }
+}
+
+#ifdef BECtl
+
+/* BSTATSE -- Return extended statistics */
+
+void bstatse(pool_incr, npool, npget, nprel, ndget, ndrel)
+ bufsize *pool_incr;
+ long *npool, *npget, *nprel, *ndget, *ndrel;
+{
+ *pool_incr = (pool_len < 0) ? -exp_incr : exp_incr;
+ *npool = numpblk;
+ *npget = numpget;
+ *nprel = numprel;
+ *ndget = numdget;
+ *ndrel = numdrel;
+}
+#endif /* BECtl */
+#endif /* BufStats */
+
+#ifdef DumpData
+
+/* BUFDUMP -- Dump the data in a buffer. This is called with the user
+ data pointer, and backs up to the buffer header. It will
+ dump either a free block or an allocated one. */
+
+void bufdump(buf)
+ void *buf;
+{
+ struct bfhead *b;
+ uchar_t *bdump;
+ bufsize bdlen;
+
+ b = BFH(((char *) buf) - sizeof(struct bhead));
+ assert(b->bh.bsize != 0);
+ if (b->bh.bsize < 0) {
+ bdump = (uchar_t *) buf;
+ bdlen = (-b->bh.bsize) - sizeof(struct bhead);
+ } else {
+ bdump = (uchar_t *) (((char *) b) + sizeof(struct bfhead));
+ bdlen = b->bh.bsize - sizeof(struct bfhead);
+ }
+
+ while (bdlen > 0) {
+ int i, dupes = 0;
+ bufsize l = bdlen;
+ char bhex[50], bascii[20];
+
+ if (l > 16) {
+ l = 16;
+ }
+
+ for (i = 0; i < l; i++) {
+ V sprintf(bhex + i * 3, "%02X ", bdump[i]);
+ bascii[i] = isprint(bdump[i]) ? bdump[i] : ' ';
+ }
+ bascii[i] = 0;
+ V printf("%-48s %s\n", bhex, bascii);
+ bdump += l;
+ bdlen -= l;
+ while ((bdlen > 16) && (memcmp((char *) (bdump - 16),
+ (char *) bdump, 16) == 0)) {
+ dupes++;
+ bdump += 16;
+ bdlen -= 16;
+ }
+ if (dupes > 1) {
+ V printf(
+ " (%d lines [%d bytes] identical to above line skipped)\n",
+ dupes, dupes * 16);
+ } else if (dupes == 1) {
+ bdump -= 16;
+ bdlen += 16;
+ }
+ }
+}
+#endif
+
+#ifdef BufDump
+
+/* BPOOLD -- Dump a buffer pool. The buffer headers are always listed.
+ If DUMPALLOC is nonzero, the contents of allocated buffers
+ are dumped. If DUMPFREE is nonzero, free blocks are
+ dumped as well. If FreeWipe checking is enabled, free
+ blocks which have been clobbered will always be dumped. */
+
+void bpoold(buf, dumpalloc, dumpfree)
+ void *buf;
+ int dumpalloc, dumpfree;
+{
+ struct bfhead *b = BFH(buf);
+
+ while (b->bh.bsize != ESent) {
+ bufsize bs = b->bh.bsize;
+
+ if (bs < 0) {
+ bs = -bs;
+ V printf("Allocated buffer: size %6ld bytes.\n", (long) bs);
+ if (dumpalloc) {
+ bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+ }
+ } else {
+ char *lerr = "";
+
+ assert(bs > 0);
+ if ((b->ql.blink->ql.flink != b) ||
+ (b->ql.flink->ql.blink != b)) {
+ lerr = " (Bad free list links)";
+ }
+ V printf("Free block: size %6ld bytes.%s\n",
+ (long) bs, lerr);
+#ifdef FreeWipe
+ lerr = ((char *) b) + sizeof(struct bfhead);
+ if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) ||
+ (memcmp(lerr, lerr + 1,
+ (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) {
+ V printf(
+ "(Contents of above free block have been overstored.)\n");
+ bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+ } else
+#endif
+ if (dumpfree) {
+ bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+ }
+ }
+ b = BFH(((char *) b) + bs);
+ }
+}
+#endif /* BufDump */
+
+#ifdef BufValid
+
+/* BPOOLV -- Validate a buffer pool. If NDEBUG isn't defined,
+ any error generates an assertion failure. */
+
+int bpoolv(buf)
+ void *buf;
+{
+ struct bfhead *b = BFH(buf);
+
+ while (b->bh.bsize != ESent) {
+ bufsize bs = b->bh.bsize;
+
+ if (bs < 0) {
+ bs = -bs;
+ } else {
+ char *lerr = "";
+
+ assert(bs > 0);
+ if (bs <= 0) {
+ return 0;
+ }
+ if ((b->ql.blink->ql.flink != b) ||
+ (b->ql.flink->ql.blink != b)) {
+ V printf("Free block: size %6ld bytes. (Bad free list links)\n",
+ (long) bs);
+ assert(0);
+ return 0;
+ }
+#ifdef FreeWipe
+ lerr = ((char *) b) + sizeof(struct bfhead);
+ if ((bs > sizeof(struct bfhead)) && ((*lerr != 0x55) ||
+ (memcmp(lerr, lerr + 1,
+ (MemSize) (bs - (sizeof(struct bfhead) + 1))) != 0))) {
+ V printf(
+ "(Contents of above free block have been overstored.)\n");
+ bufdump((void *) (((char *) b) + sizeof(struct bhead)));
+ assert(0);
+ return 0;
+ }
+#endif
+ }
+ b = BFH(((char *) b) + bs);
+ }
+ return 1;
+}
+#endif /* BufValid */
+
+ /***********************\
+ * *
+ * Built-in test program *
+ * *
+ \***********************/
+
+#ifdef TestProg
+
+#define Repeatable 1 /* Repeatable pseudorandom sequence */
+ /* If Repeatable is not defined, a
+ time-seeded pseudorandom sequence
+ is generated, exercising BGET with
+ a different pattern of calls on each
+ run. */
+#define OUR_RAND /* Use our own built-in version of
+ rand() to guarantee the test is
+ 100% repeatable. */
+
+#ifdef BECtl
+#define PoolSize 300000 /* Test buffer pool size */
+#else
+#define PoolSize 50000 /* Test buffer pool size */
+#endif
+#define ExpIncr 32768 /* Test expansion block size */
+#define CompactTries 10 /* Maximum tries at compacting */
+
+#define dumpAlloc 0 /* Dump allocated buffers ? */
+#define dumpFree 0 /* Dump free buffers ? */
+
+#ifndef Repeatable
+extern long time();
+#endif
+
+extern char *malloc();
+extern int free _((char *));
+
+static char *bchain = NULL; /* Our private buffer chain */
+static char *bp = NULL; /* Our initial buffer pool */
+
+#include <math.h>
+
+#ifdef OUR_RAND
+
+static ulong_t int next = 1;
+
+/* Return next random integer */
+
+int rand()
+{
+ next = next * 1103515245L + 12345;
+ return (uint_t) (next / 65536L) % 32768L;
+}
+
+/* Set seed for random generator */
+
+void srand(seed)
+ uint_t seed;
+{
+ next = seed;
+}
+#endif
+
+/* STATS -- Edit statistics returned by bstats() or bstatse(). */
+
+static void stats(when)
+ char *when;
+{
+ bufsize cural, totfree, maxfree;
+ long nget, nfree;
+#ifdef BECtl
+ bufsize pincr;
+ long totblocks, npget, nprel, ndget, ndrel;
+#endif
+
+ bstats(&cural, &totfree, &maxfree, &nget, &nfree);
+ V printf(
+ "%s: %ld gets, %ld releases. %ld in use, %ld free, largest = %ld\n",
+ when, nget, nfree, (long) cural, (long) totfree, (long) maxfree);
+#ifdef BECtl
+ bstatse(&pincr, &totblocks, &npget, &nprel, &ndget, &ndrel);
+ V printf(
+ " Blocks: size = %ld, %ld (%ld bytes) in use, %ld gets, %ld frees\n",
+ (long)pincr, totblocks, pincr * totblocks, npget, nprel);
+ V printf(" %ld direct gets, %ld direct frees\n", ndget, ndrel);
+#endif /* BECtl */
+}
+
+#ifdef BECtl
+static int protect = 0; /* Disable compaction during bgetr() */
+
+/* BCOMPACT -- Compaction call-back function. */
+
+static int bcompact(bsize, seq)
+ bufsize bsize;
+ int seq;
+{
+#ifdef CompactTries
+ char *bc = bchain;
+ int i = rand() & 0x3;
+
+#ifdef COMPACTRACE
+ V printf("Compaction requested. %ld bytes needed, sequence %d.\n",
+ (long) bsize, seq);
+#endif
+
+ if (protect || (seq > CompactTries)) {
+#ifdef COMPACTRACE
+ V printf("Compaction gave up.\n");
+#endif
+ return 0;
+ }
+
+ /* Based on a random cast, release a random buffer in the list
+ of allocated buffers. */
+
+ while (i > 0 && bc != NULL) {
+ bc = *((char **) bc);
+ i--;
+ }
+ if (bc != NULL) {
+ char *fb;
+
+ fb = *((char **) bc);
+ if (fb != NULL) {
+ *((char **) bc) = *((char **) fb);
+ brel((void *) fb);
+ return 1;
+ }
+ }
+
+#ifdef COMPACTRACE
+ V printf("Compaction bailed out.\n");
+#endif
+#endif /* CompactTries */
+ return 0;
+}
+
+/* BEXPAND -- Expand pool call-back function. */
+
+static void *bexpand(size)
+ bufsize size;
+{
+ void *np = NULL;
+ bufsize cural, totfree, maxfree;
+ long nget, nfree;
+
+ /* Don't expand beyond the total allocated size given by PoolSize. */
+
+ bstats(&cural, &totfree, &maxfree, &nget, &nfree);
+
+ if (cural < PoolSize) {
+ np = (void *) malloc((unsigned) size);
+ }
+#ifdef EXPTRACE
+ V printf("Expand pool by %ld -- %s.\n", (long) size,
+ np == NULL ? "failed" : "succeeded");
+#endif
+ return np;
+}
+
+/* BSHRINK -- Shrink buffer pool call-back function. */
+
+static void bshrink(buf)
+ void *buf;
+{
+ if (((char *) buf) == bp) {
+#ifdef EXPTRACE
+ V printf("Initial pool released.\n");
+#endif
+ bp = NULL;
+ }
+#ifdef EXPTRACE
+ V printf("Shrink pool.\n");
+#endif
+ free((char *) buf);
+}
+
+#endif /* BECtl */
+
+/* Restrict buffer requests to those large enough to contain our pointer and
+ small enough for the CPU architecture. */
+
+static bufsize blimit(bs)
+ bufsize bs;
+{
+ if (bs < sizeof(char *)) {
+ bs = sizeof(char *);
+ }
+
+ /* This is written out in this ugly fashion because the
+ cool expression in sizeof(int) that auto-configured
+ to any length int befuddled some compilers. */
+
+ if (sizeof(int) == 2) {
+ if (bs > 32767) {
+ bs = 32767;
+ }
+ } else {
+ if (bs > 200000) {
+ bs = 200000;
+ }
+ }
+ return bs;
+}
+
+int main()
+{
+ int i;
+ double x;
+
+ /* Seed the random number generator. If Repeatable is defined, we
+ always use the same seed. Otherwise, we seed from the clock to
+ shake things up from run to run. */
+
+#ifdef Repeatable
+ V srand(1234);
+#else
+ V srand((int) time((long *) NULL));
+#endif
+
+ /* Compute x such that pow(x, p) ranges between 1 and 4*ExpIncr as
+ p ranges from 0 to ExpIncr-1, with a concentration in the lower
+ numbers. */
+
+ x = 4.0 * ExpIncr;
+ x = log(x);
+ x = exp(log(4.0 * ExpIncr) / (ExpIncr - 1.0));
+
+#ifdef BECtl
+ bectl(bcompact, bexpand, bshrink, (bufsize) ExpIncr);
+ bp = malloc(ExpIncr);
+ assert(bp != NULL);
+ bpool((void *) bp, (bufsize) ExpIncr);
+#else
+ bp = malloc(PoolSize);
+ assert(bp != NULL);
+ bpool((void *) bp, (bufsize) PoolSize);
+#endif
+
+ stats("Create pool");
+ V bpoolv((void *) bp);
+ bpoold((void *) bp, dumpAlloc, dumpFree);
+
+ for (i = 0; i < TestProg; i++) {
+ char *cb;
+ bufsize bs = pow(x, (double) (rand() & (ExpIncr - 1)));
+
+ assert(bs <= (((bufsize) 4) * ExpIncr));
+ bs = blimit(bs);
+ if (rand() & 0x400) {
+ cb = (char *) bgetz(bs);
+ } else {
+ cb = (char *) bget(bs);
+ }
+ if (cb == NULL) {
+#ifdef EasyOut
+ break;
+#else
+ char *bc = bchain;
+
+ if (bc != NULL) {
+ char *fb;
+
+ fb = *((char **) bc);
+ if (fb != NULL) {
+ *((char **) bc) = *((char **) fb);
+ brel((void *) fb);
+ }
+ continue;
+ }
+#endif
+ }
+ *((char **) cb) = (char *) bchain;
+ bchain = cb;
+
+ /* Based on a random cast, release a random buffer in the list
+ of allocated buffers. */
+
+ if ((rand() & 0x10) == 0) {
+ char *bc = bchain;
+ int i = rand() & 0x3;
+
+ while (i > 0 && bc != NULL) {
+ bc = *((char **) bc);
+ i--;
+ }
+ if (bc != NULL) {
+ char *fb;
+
+ fb = *((char **) bc);
+ if (fb != NULL) {
+ *((char **) bc) = *((char **) fb);
+ brel((void *) fb);
+ }
+ }
+ }
+
+ /* Based on a random cast, reallocate a random buffer in the list
+ to a random size */
+
+ if ((rand() & 0x20) == 0) {
+ char *bc = bchain;
+ int i = rand() & 0x3;
+
+ while (i > 0 && bc != NULL) {
+ bc = *((char **) bc);
+ i--;
+ }
+ if (bc != NULL) {
+ char *fb;
+
+ fb = *((char **) bc);
+ if (fb != NULL) {
+ char *newb;
+
+ bs = pow(x, (double) (rand() & (ExpIncr - 1)));
+ bs = blimit(bs);
+#ifdef BECtl
+ protect = 1; /* Protect against compaction */
+#endif
+ newb = (char *) bgetr((void *) fb, bs);
+#ifdef BECtl
+ protect = 0;
+#endif
+ if (newb != NULL) {
+ *((char **) bc) = newb;
+ }
+ }
+ }
+ }
+ }
+ stats("\nAfter allocation");
+ if (bp != NULL) {
+ V bpoolv((void *) bp);
+ bpoold((void *) bp, dumpAlloc, dumpFree);
+ }
+
+ while (bchain != NULL) {
+ char *buf = bchain;
+
+ bchain = *((char **) buf);
+ brel((void *) buf);
+ }
+ stats("\nAfter release");
+#ifndef BECtl
+ if (bp != NULL) {
+ V bpoolv((void *) bp);
+ bpoold((void *) bp, dumpAlloc, dumpFree);
+ }
+#endif
+
+ return 0;
+}
+#endif
--- /dev/null
+/*
+ * Block devices
+ * Copyright (c) 2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/errno.h>
+#include <geekos/screen.h>
+#include <geekos/string.h>
+#include <geekos/malloc.h>
+#include <geekos/int.h>
+#include <geekos/kthread.h>
+#include <geekos/synch.h>
+#include <geekos/blockdev.h>
+
+/*#define BLOCKDEV_DEBUG */
+#ifdef BLOCKDEV_DEBUG
+# define Debug(args...) Print(args)
+#else
+# define Debug(args...)
+#endif
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Lock protecting access/modification of block device list.
+ */
+static struct Mutex s_blockdevLock;
+
+/*
+ * List datatype for list of block devices.
+ */
+DEFINE_LIST(Block_Device_List, Block_Device);
+IMPLEMENT_LIST(Block_Device_List, Block_Device);
+
+/*
+ * The list in which all block devices in the system
+ * are registered.
+ */
+static struct Block_Device_List s_deviceList;
+
+/*
+ * Perform a block IO request.
+ * Returns 0 if successful, error code on failure.
+ */
+static int Do_Request(struct Block_Device *dev, enum Request_Type type, int blockNum, void *buf)
+{
+ struct Block_Request *request;
+ int rc;
+
+ request = Create_Request(dev, type, blockNum, buf);
+ if (request == 0)
+ return ENOMEM;
+ Post_Request_And_Wait(request);
+ rc = request->errorCode;
+ Free(request);
+ return rc;
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Register a block device.
+ * This should be called by device drivers in their Init
+ * functions to register all detected devices.
+ * Returns 0 if successful, error code otherwise.
+ */
+int Register_Block_Device(const char *name, struct Block_Device_Ops *ops,
+ int unit, void *driverData, struct Thread_Queue *waitQueue,
+ struct Block_Request_List *requestQueue)
+{
+ struct Block_Device *dev;
+
+ KASSERT(ops != 0);
+ KASSERT(waitQueue != 0);
+ KASSERT(requestQueue != 0);
+
+ dev = (struct Block_Device*) Malloc(sizeof(*dev));
+ if (dev == 0)
+ return ENOMEM;
+
+ strcpy(dev->name, name);
+ dev->ops = ops;
+ dev->unit = unit;
+ dev->inUse = false;
+ dev->driverData = driverData;
+ dev->waitQueue = waitQueue;
+ dev->requestQueue = requestQueue;
+
+ Mutex_Lock(&s_blockdevLock);
+ /* FIXME: handle name conflict with existing device */
+ Debug("Registering block device %s\n", dev->name);
+ Add_To_Back_Of_Block_Device_List(&s_deviceList, dev);
+ Mutex_Unlock(&s_blockdevLock);
+
+ return 0;
+}
+
+/*
+ * Open a named block device.
+ * Return 0 if successful, error code on error.
+ */
+int Open_Block_Device(const char *name, struct Block_Device **pDev)
+{
+ struct Block_Device *dev;
+ int rc = 0;
+
+ Mutex_Lock(&s_blockdevLock);
+
+ dev = Get_Front_Of_Block_Device_List(&s_deviceList);
+ while (dev != 0) {
+ if (strcmp(dev->name, name) == 0)
+ break;
+ dev = Get_Next_In_Block_Device_List(dev);
+ }
+
+ if (dev == 0)
+ rc = ENODEV;
+ else if (dev->inUse)
+ rc = EBUSY;
+ else {
+ rc = dev->ops->Open(dev);
+ if (rc == 0) {
+ *pDev = dev;
+ dev->inUse = true;
+ }
+ }
+
+ Mutex_Unlock(&s_blockdevLock);
+
+ return rc;
+}
+
+/*
+ * Close given block device.
+ * Return 0 if successful, error code on error.
+ */
+int Close_Block_Device(struct Block_Device *dev)
+{
+ int rc;
+
+ Mutex_Lock(&s_blockdevLock);
+
+ KASSERT(dev->inUse);
+ rc = dev->ops->Close(dev);
+ if (rc == 0)
+ dev->inUse = false;
+
+ Mutex_Unlock(&s_blockdevLock);
+
+ return rc;
+}
+
+/*
+ * Create a block device request to transfer a single block.
+ */
+struct Block_Request *Create_Request(struct Block_Device *dev, enum Request_Type type,
+ int blockNum, void *buf)
+{
+ struct Block_Request *request = Malloc(sizeof(*request));
+ if (request != 0) {
+ request->dev = dev;
+ request->type = type;
+ request->blockNum = blockNum;
+ request->buf = buf;
+ request->state = PENDING;
+ Clear_Thread_Queue(&request->waitQueue);
+ }
+ return request;
+}
+
+/*
+ * Send a block IO request to a device and wait for it to be handled.
+ * Returns when the driver completes the requests or signals
+ * an error.
+ */
+void Post_Request_And_Wait(struct Block_Request *request)
+{
+ struct Block_Device *dev;
+
+ KASSERT(request != 0);
+
+ dev = request->dev;
+ KASSERT(dev != 0);
+
+ /* Send request to the driver */
+ Debug("Posting block device request [@%x]...\n", request);
+ Disable_Interrupts();
+ Add_To_Back_Of_Block_Request_List(dev->requestQueue, request);
+ Wake_Up(dev->waitQueue);
+ Enable_Interrupts();
+
+ /* Wait for request to be processed */
+ Disable_Interrupts();
+ while (request->state == PENDING) {
+ Debug("Waiting, state=%d\n", request->state);
+ Wait(&request->waitQueue);
+ }
+ Debug("Wait completed!\n");
+ Enable_Interrupts();
+}
+
+/*
+ * Wait for a block request to arrive.
+ */
+struct Block_Request *Dequeue_Request(struct Block_Request_List *requestQueue,
+ struct Thread_Queue *waitQueue)
+{
+ struct Block_Request *request;
+
+ Disable_Interrupts();
+ while (Is_Block_Request_List_Empty(requestQueue))
+ Wait(waitQueue);
+ request = Get_Front_Of_Block_Request_List(requestQueue);
+ Remove_From_Front_Of_Block_Request_List(requestQueue);
+ Enable_Interrupts();
+
+ return request;
+}
+
+/*
+ * Signal the completion of a block request.
+ */
+void Notify_Request_Completion(struct Block_Request *request, enum Request_State state, int errorCode)
+{
+ Disable_Interrupts();
+ request->state = state;
+ request->errorCode = errorCode;
+ Wake_Up(&request->waitQueue);
+ Enable_Interrupts();
+}
+
+/*
+ * Read a block from given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Read(struct Block_Device *dev, int blockNum, void *buf)
+{
+ return Do_Request(dev, BLOCK_READ, blockNum, buf);
+}
+
+/*
+ * Write a block to given device.
+ * Return 0 if successful, error code on error.
+ */
+int Block_Write(struct Block_Device *dev, int blockNum, void *buf)
+{
+ return Do_Request(dev, BLOCK_WRITE, blockNum, buf);
+}
+
+/*
+ * Get number of blocks in given device.
+ */
+int Get_Num_Blocks(struct Block_Device *dev)
+{
+ return dev->ops->Get_Num_Blocks(dev);
+}
+
--- /dev/null
+; Boot sector for GeekOS
+; Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software. You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; Loads setup code and a program image from sectors 1..n of a floppy
+; and executes the setup code (which will in turn execute
+; the program image).
+
+; Some of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+; This macro is used to calculate padding needed
+; to ensure that the boot sector is exactly 512 bytes
+; in size. The argument is the desired offset to be
+; padded to.
+%macro PadFromStart 1
+ times (%1 - ($ - BeginText)) db 0
+%endmacro
+
+KERN_START_SEC equ (NUM_SETUP_SECTORS + 1)
+
+BIOS_SIGNATURE_OFFSET equ 510
+
+; ----------------------------------------------------------------------
+; The actual code
+; ----------------------------------------------------------------------
+
+[BITS 16]
+[ORG 0x0]
+
+BeginText: ; needed to calculate padding bytes to fill the sector
+
+ ; Copy the boot sector into INITSEG.
+ mov ax, BOOTSEG
+ mov ds, ax ; source segment for string copy
+ xor si, si ; source index for string copy
+ mov ax, INITSEG
+ mov es, ax ; destination segment for string copy
+ xor di, di ; destination index for string copy
+ cld ; clear direction flag
+ mov cx, 256 ; number of words to copy
+ rep movsw ; copy 512 bytes
+
+ jmp INITSEG:after_move
+
+after_move:
+ ; Now we're executing in INITSEG
+
+ ; We want the data segment to refer to INITSEG
+ ; (since we've defined variables in the same place as the code)
+ mov ds, ax ; ax still contains INITSEG
+
+ ; Put the stack in the place where we were originally loaded.
+ ; By definition, there is nothing important there now.
+ mov ax, 0
+ mov ss, ax
+ mov sp, (BOOTSEG << 4) + 512 - 2
+
+load_setup:
+ ; Load the setup code.
+ mov word [sec_count], 1
+.again:
+ mov ax, [sec_count]
+ push ax ; 1st param to ReadSector (log sec num)
+ push word SETUPSEG ; 2nd param to ReadSector (seg base)
+ dec ax ; convert to 0-indexed
+ shl ax, 9 ; multiply by 512
+ push ax ; ...to get 3rd param (byte offset)
+
+ ; read the sector from the floppy
+ call ReadSector
+ add sp, 6 ; clear 3 word params
+
+ ; on to next sector
+ inc word [sec_count]
+
+ ; are we done?
+ cmp word [sec_count], NUM_SETUP_SECTORS
+ jle .again
+
+load_kernel:
+ ; Load the kernel image from sectors KERN_START_SEC..n of the
+ ; floppy into memory at KERNSEG. Note that there are 128 sectors
+ ; per 64K segment. So, when figuring out which segment to
+ ; load the sector into, we shift right by 7 bits (which is
+ ; equivalent to dividing by 128).
+ mov word [sec_count], KERN_START_SEC
+.again:
+ mov ax, [sec_count] ; logical sector on the floppy
+ push ax ; 1st param to ReadSector (log sec num)
+ sub ax, KERN_START_SEC ; convert to 0-indexed
+ mov cx, ax ; save in cx
+ shr ax, 7 ; divide by 128
+ shl ax, 12 ; ...and multiply by 0x1000
+ add ax, KERNSEG ; ...to get base relative to KERNSEG
+ push ax ; 2nd param to ReadSector (seg base)
+ and cx, 0x7f ; mod sector by 128
+ shl cx, 9 ; ...and multiply by 512
+ push cx ; to get offset in segment (3rd parm)
+
+ ; read the sector from the floppy
+ call ReadSector
+ add sp, 6 ; clear 3 word params
+
+ ; on to next sector
+ inc word [sec_count]
+
+ ; have we loaded all of the sectors?
+ cmp word [sec_count], KERN_START_SEC+NUM_KERN_SECTORS
+ jl .again
+
+ ; Now we've loaded the setup code and the kernel image.
+ ; Jump to setup code.
+ jmp SETUPSEG:0
+
+; Read a sector from the floppy drive.
+; This code (and the rest of this boot sector) will have to
+; be re-written at some point so it reads more than one
+; sector at a time.
+;
+; Parameters:
+; - "logical" sector number [bp+8]
+; - destination segment [bp+6]
+; - destination offset [bp+4]
+ReadSector:
+ push bp ; set up stack frame
+ mov bp, sp ; "
+ pusha ; save all registers
+
+%if 0
+; debug params
+ mov dx, [bp+8]
+ call PrintHex
+ call PrintNL
+ mov dx, [bp+6]
+ call PrintHex
+ call PrintNL
+ mov dx, [bp+4]
+ call PrintHex
+ call PrintNL
+%endif
+
+ ; Sector = log_sec % SECTORS_PER_TRACK
+ ; Head = (log_sec / SECTORS_PER_TRACK) % HEADS
+ mov ax, [bp+8] ; get logical sector number from stack
+ xor dx, dx ; dx is high part of dividend (== 0)
+ mov bx, SECTORS_PER_TRACK ; divisor
+ div bx ; do the division
+ mov [sec], dx ; sector is the remainder
+ and ax, 1 ; same as mod by HEADS==2 (slight hack)
+ mov [head], ax
+
+ ; Track = log_sec / (SECTORS_PER_TRACK*HEADS)
+ mov ax, [bp+8] ; get logical sector number again
+ xor dx, dx ; dx is high part of dividend
+ mov bx, SECTORS_PER_TRACK*2 ; divisor
+ div bx ; do the division
+ mov [track], ax ; track is quotient
+
+%if 0
+; debugging code
+ mov dx, [sec]
+ call PrintHex
+ call PrintNL
+ mov dx, [head]
+ call PrintHex
+ call PrintNL
+ mov dx, [track]
+ call PrintHex
+ call PrintNL
+%endif
+
+ ; Now, try to actually read the sector from the floppy,
+ ; retrying up to 3 times.
+
+ mov [num_retries], byte 0
+
+.again:
+ mov ax, [bp+6] ; dest segment...
+ mov es, ax ; goes in es
+ mov ax, (0x02 << 8) | 1 ; function = 02h in ah,
+ ; # secs = 1 in al
+ mov bx, [track] ; track number...
+ mov ch, bl ; goes in ch
+ mov bx, [sec] ; sector number...
+ mov cl, bl ; goes in cl...
+ inc cl ; but it must be 1-based, not 0-based
+ mov bx, [head] ; head number...
+ mov dh, bl ; goes in dh
+ xor dl, dl ; hard code drive=0
+ mov bx, [bp+4] ; offset goes in bx
+ ; (es:bx points to buffer)
+
+ ; Call the BIOS Read Diskette Sectors service
+ int 0x13
+
+ ; If the carry flag is NOT set, then there was no error
+ ; and we're done.
+ jnc .done
+
+ ; Error - code stored in ah
+ mov dx, ax
+ call PrintHex
+ inc byte [num_retries]
+ cmp byte [num_retries], 3
+ jne .again
+
+ ; If we got here, we failed thrice, so we give up
+ mov dx, 0xdead
+ call PrintHex
+.here: jmp .here
+
+.done:
+ popa ; restore all regisiters
+ pop bp ; leave stack frame
+ ret
+
+; Include utility routines
+%include "util.asm"
+
+; ----------------------------------------------------------------------
+; Variables
+; ----------------------------------------------------------------------
+
+; These are used by ReadSector
+head: dw 0
+track: dw 0
+sec: dw 0
+num_retries: db 0
+
+
+; Used for loops reading sectors from floppy
+sec_count: dw 0
+
+
+PadFromStart BIOS_SIGNATURE_OFFSET
+Signature dw 0xAA55 ; BIOS controls this to ensure this is a boot sector
--- /dev/null
+/* Copyright abandoned; this code is in the public domain. */
+/* Provided to GNUnet by peter@horizon.com */
+
+/*
+ * Adapted for GeekOS by David Hovemeyer
+ * Code downloaded from OVM (http://www.ovmj.org)
+ */
+
+#include <geekos/crc32.h>
+#include <geekos/kassert.h>
+#include <geekos/serial.h>
+
+#define POLYNOMIAL (ulong_t)0xedb88320
+static ulong_t crc_table[256];
+
+/*
+ * This routine writes each crc_table entry exactly once,
+ * with the correct final value. Thus, it is safe to call
+ * even on a table that someone else is using concurrently.
+ */
+void Init_CRC32(void) {
+ unsigned int i, j;
+ ulong_t h = 1;
+ PrintBoth("Initializing CRC32\n");
+ crc_table[0] = 0;
+ for (i = 128; i; i >>= 1) {
+ h = (h >> 1) ^ ((h & 1) ? POLYNOMIAL : 0);
+ /* h is now crc_table[i] */
+ for (j = 0; j < 256; j += 2*i)
+ crc_table[i+j] = crc_table[j] ^ h;
+ }
+}
+
+/*
+ * This computes the standard preset and inverted CRC, as used
+ * by most networking standards. Start by passing in an initial
+ * chaining value of 0, and then pass in the return value from the
+ * previous crc32() call. The final return value is the CRC.
+ * Note that this is a little-endian CRC, which is best used with
+ * data transmitted lsbit-first, and it should, itself, be appended
+ * to data in little-endian byte and bit order to preserve the
+ * property of detecting all burst errors of length 32 bits or less.
+ */
+ulong_t crc32(ulong_t crc, char const *buf, size_t len) {
+ KASSERT(crc_table[255] != 0);
+ crc ^= 0xffffffff;
+ while (len--)
+ crc = (crc >> 8) ^ crc_table[(crc ^ *buf++) & 0xff];
+ return crc ^ 0xffffffff;
+}
+
+/* end of crc32.c */
--- /dev/null
+; Definitions for use in GeekOS boot code
+; Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software. You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; A lot of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%ifndef DEFS_ASM
+%define DEFS_ASM
+
+; BIOS loads the boot sector at offset 0 in this segment
+BOOTSEG equ 0x07C0
+
+; We'll move the boot sector up to higher memory.
+; Note that the "ISA hole" begins at segment 0xA000 == 640K.
+INITSEG equ 0x9000
+
+; Put the setup code here, just after the boot sector.
+SETUPSEG equ 0x9020
+
+; Load our "Kernel" at this segment, which starts at 64K.
+; The number of sectors in the kernel, NUM_KERN_SECTORS,
+; will be passed on the command line.
+KERNSEG equ 0x1000
+ ;
+
+; Size of PFAT boot record.
+; Keep up to date with <geekos/pfat.h>.
+PFAT_BOOT_RECORD_SIZE equ 28
+
+; Offset of BIOS signature word in boot sector.
+BIOS_SIGNATURE_OFFSET equ 510
+
+; Offset of PFAT boot record in boot sector.
+PFAT_BOOT_RECORD_OFFSET equ BIOS_SIGNATURE_OFFSET - PFAT_BOOT_RECORD_SIZE
+
+; Video memory segment
+VIDSEG equ 0xb800
+
+; The following information is correct for a 1.44M floppy.
+; Yes, I'm hard coding this.
+SECTORS_PER_TRACK equ 18
+HEADS equ 2
+CYLINDERS equ 80
+
+; 8259A PIC initialization codes.
+; Source: Linux bootsect.S, and Intel 8259A datasheet
+
+; The most important reason why we reprogram the PICs is to
+; route the hardware interrupts through vectors *above*
+; those reserved by Intel. The BIOS (for historical reasons :-)
+; routes them such that they conflict with internal processor-generated
+; interrupts.
+
+ICW1 equ 0x11 ; ICW1 - ICW4 needed, cascade mode, interval=8,
+ ; edge triggered. (I think interval is irrelevant
+ ; for x86.)
+ICW2_MASTER equ 0x20 ; put IRQs 0-7 at 0x20 (above Intel reserved ints)
+ICW2_SLAVE equ 0x28 ; put IRQs 8-15 at 0x28
+ICW3_MASTER equ 0x04 ; IR2 connected to slave
+ICW3_SLAVE equ 0x02 ; slave has id 2
+ICW4 equ 0x01 ; 8086 mode, no auto-EOI, non-buffered mode,
+ ; not special fully nested mode
+
+; Kernel code and data segment selectors.
+; Keep these up to date with defs.h.
+KERNEL_CS equ 1<<3 ; kernel code segment is GDT entry 1
+KERNEL_DS equ 2<<3 ; kernel data segment is GDT entry 2
+
+; Pages for context object and stack for initial kernel thread -
+; the one we construct for Main(). Keep these up to date with defs.h.
+; We put them at 1MB, for no particular reason.
+KERN_THREAD_OBJ equ 4096
+KERN_STACK equ KERN_THREAD_OBJ + 4096
+
+%endif
--- /dev/null
+; -*- fundamental -*-
+; Boot sector for GeekOS
+; Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software. You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; Loads setup code and a program image from sectors 1..n of a floppy
+; and executes the setup code (which will in turn execute
+; the program image).
+
+; Some of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+
+; Pad to desired offset from start symbol.
+; Usage: Pad_From_Symbol offset, symbol
+%macro Pad_From_Symbol 2
+ times (%1 - ($ - %2)) db 0
+%endmacro
+
+; ----------------------------------------------------------------------
+; The actual code
+; ----------------------------------------------------------------------
+
+[BITS 16]
+[ORG 0x0]
+
+BeginText: ; needed to calculate padding bytes to fill the sector
+
+ ; Copy the boot sector into INITSEG.
+ mov ax, BOOTSEG
+ mov ds, ax ; source segment for string copy
+ xor si, si ; source index for string copy
+ mov ax, INITSEG
+ mov es, ax ; destination segment for string copy
+ xor di, di ; destination index for string copy
+ cld ; clear direction flag
+ mov cx, 256 ; number of words to copy
+ rep movsw ; copy 512 bytes
+
+ jmp INITSEG:after_move
+
+after_move:
+ ; Now we're executing in INITSEG
+
+
+ ; We want the data segment to refer to INITSEG
+ ; (since we've defined variables in the same place as the code)
+ mov ds, ax ; ax still contains INITSEG
+
+ ; Put the stack in the place where we were originally loaded.
+ ; By definition, there is nothing important there now.
+ mov ax, 0
+ mov ss, ax
+ mov sp, (BOOTSEG << 4) + 512 - 2
+
+
+load_setup:
+ ; Load the setup code.
+ mov ax, word [setupStart]
+ mov word [sec_count], ax
+ add ax, [setupSize]
+ mov word [max_sector], ax
+.again:
+ mov ax, [sec_count]
+ push ax ; 1st param to ReadSector (log sec num)
+ push word SETUPSEG ; 2nd param to ReadSector (seg base)
+ sub ax, [setupStart] ; convert to 0-indexed
+ shl ax, 9 ; multiply by 512
+ push ax ; ...to get 3rd param (byte offset)
+
+ ; read the sector from the floppy
+ call ReadSector
+ add sp, 6 ; clear 3 word params
+
+ ; on to next sector
+ inc word [sec_count]
+
+ ; are we done?
+ mov bx, word [max_sector]
+ cmp word [sec_count], bx
+ jl .again
+
+
+load_kernel:
+ ; Load the kernel image from sectors KERN_START_SEC..n of the
+ ; floppy into memory at KERNSEG. Note that there are 128 sectors
+ ; per 64K segment. So, when figuring out which segment to
+ ; load the sector into, we shift right by 7 bits (which is
+ ; equivalent to dividing by 128).
+
+ ; Figure out start sector and max sector
+
+ mov ax, word [kernelStart]
+ mov word [sec_count], ax
+ add ax, word [kernelSize]
+ mov word [max_sector], ax
+.again:
+
+
+ mov ax, [sec_count] ; logical sector on the floppy
+; mov dx, ax
+; call PrintHex
+; call PrintNL
+ push ax ; 1st param to ReadSector (log sec num)
+ sub ax, [kernelStart] ; convert to 0-indexed
+ mov cx, ax ; save in cx
+ shr ax, 7 ; divide by 128
+ shl ax, 12 ; ...and multiply by 0x1000
+ add ax, KERNSEG ; ...to get base relative to KERNSEG
+; mov dx, ax
+; call PrintHex
+; call PrintNL
+ push ax ; 2nd param to ReadSector (seg base)
+ and cx, 0x7f ; mod sector by 128
+ shl cx, 9 ; ...and multiply by 512
+ push cx ; to get offset in segment (3rd parm)
+; mov dx, cx
+; call PrintHex
+; call PrintNL
+
+
+ ; read the sector from the floppy
+ call ReadSector
+ add sp, 6 ; clear 3 word params
+
+
+ ; on to next sector
+ inc word [sec_count]
+
+ ; have we loaded all of the sectors?
+ mov bx, word [max_sector]
+ cmp word [sec_count], bx
+ jl .again
+
+
+; execute bios call
+
+ push cx
+ push si
+ push bx
+ push es ;
+
+ mov dx, cs
+ call PrintHex
+ call PrintNL
+
+ push cs
+ pop es
+
+ mov si, bootsect_gdt
+ mov ah, 87h
+
+ ;; determine the actual kernel size
+ mov cx, word [kernelSize] ; number of 512 byte sectors
+ shl cx, 8 ; multiply by 9-1 to get number of bytes / 2 (words)
+
+ ;; Print out the size of the kernel
+ mov dx, cx
+ call PrintHex
+ call PrintNL
+
+ int 0x15
+
+ adc ax, 0
+
+ ;; Print out the return code to the screen
+ mov dx, ax
+ call PrintHex
+ call PrintNL
+
+ mov dx, KERNSEG
+ call PrintHex
+ call PrintNL
+
+ mov dx, SETUPSEG
+ call PrintHex
+ call PrintNL
+
+
+
+ mov dx, 0x4a4a
+ call PrintHex
+ call PrintNL
+
+ pop es ;
+ pop bx
+ pop si
+ pop cx
+
+
+;.loop: jmp .loop
+
+
+.skip:
+ ; Now we've loaded the setup code and the kernel image.
+ ; Jump to setup code.
+ jmp SETUPSEG:0
+
+; Read a sector from the floppy drive.
+; This code (and the rest of this boot sector) will have to
+; be re-written at some point so it reads more than one
+; sector at a time.
+;
+; Parameters:
+; - "logical" sector number [bp+8]
+; - destination segment [bp+6]
+; - destination offset [bp+4]
+ReadSector:
+ push bp ; set up stack frame
+ mov bp, sp ; "
+ pusha ; save all registers
+
+%if 0
+; debug params
+ mov dx, [bp+8]
+ call PrintHex
+ call PrintNL
+ mov dx, [bp+6]
+ call PrintHex
+ call PrintNL
+ mov dx, [bp+4]
+ call PrintHex
+ call PrintNL
+%endif
+
+ ; Sector = log_sec % SECTORS_PER_TRACK
+ ; Head = (log_sec / SECTORS_PER_TRACK) % HEADS
+ mov ax, [bp+8] ; get logical sector number from stack
+ xor dx, dx ; dx is high part of dividend (== 0)
+ mov bx, SECTORS_PER_TRACK ; divisor
+ div bx ; do the division
+ mov [sec], dx ; sector is the remainder
+ and ax, 1 ; same as mod by HEADS==2 (slight hack)
+ mov [head], ax
+
+ ; Track = log_sec / (SECTORS_PER_TRACK*HEADS)
+ mov ax, [bp+8] ; get logical sector number again
+ xor dx, dx ; dx is high part of dividend
+ mov bx, SECTORS_PER_TRACK*2 ; divisor
+ div bx ; do the division
+ mov [track], ax ; track is quotient
+
+%if 0
+; debugging code
+ mov dx, [sec]
+ call PrintHex
+ call PrintNL
+ mov dx, [head]
+ call PrintHex
+ call PrintNL
+ mov dx, [track]
+ call PrintHex
+ call PrintNL
+%endif
+
+ ; Now, try to actually read the sector from the floppy,
+ ; retrying up to 3 times.
+
+ mov [num_retries], byte 0
+
+.again:
+ mov ax, [bp+6] ; dest segment...
+ mov es, ax ; goes in es
+ mov ax, (0x02 << 8) | 1 ; function = 02h in ah,
+ ; # secs = 1 in al
+ mov bx, [track] ; track number...
+ mov ch, bl ; goes in ch
+ mov bx, [sec] ; sector number...
+ mov cl, bl ; goes in cl...
+ inc cl ; but it must be 1-based, not 0-based
+ mov bx, [head] ; head number...
+ mov dh, bl ; goes in dh
+ xor dl, dl ; hard code drive=0
+ mov bx, [bp+4] ; offset goes in bx
+ ; (es:bx points to buffer)
+ ; Call the BIOS Read Diskette Sectors service
+ int 0x13
+
+ ; If the carry flag is NOT set, then there was no error
+ ; and we're done.
+ jnc .done
+
+ ; Error - code stored in ah
+ mov dx, ax
+ call PrintHex
+ inc byte [num_retries]
+ cmp byte [num_retries], 3
+ jne .again
+
+ ; If we got here, we failed thrice, so we give up
+ mov dx, 0xdead
+ call PrintHex
+.here: jmp .here
+
+.done:
+ popa ; restore all regisiters
+ pop bp ; leave stack frame
+ ret
+
+; Include utility routines:
+;%include "util.asm"
+; REPLACED WITH FOLLOWING WHICH MUST BE COMPILED 16 FOR USE IN THIS CODE
+PrintHex:
+ pusha
+ mov cx, 4 ; 4 hex digits
+.PrintDigit:
+ rol dx, 4 ; rotate so that lowest 4 bits are used
+ mov ax, 0E0Fh ; ah = request, al = mask for nybble
+ and al, dl
+ add al, 90h ; convert al to ascii hex (four instructions)
+ daa ; I've spent 1 hour to understand how it works..
+ adc al, 40h
+ daa
+ int 10h
+ loop .PrintDigit
+ popa
+ ret
+
+; Print a newline.
+PrintNL: ; print CR and NL
+ push ax
+ mov ax, 0E0Dh ; CR
+ int 10h
+ mov al, 0Ah ; LF
+ int 10h
+ pop ax
+ ret
+
+
+
+; ----------------------------------------------------------------------
+; Variables
+; ----------------------------------------------------------------------
+
+; These are used by ReadSector
+head: dw 0
+track: dw 0
+sec: dw 0
+num_retries: db 0
+
+; Used for loops reading sectors from floppy
+sec_count: dw 0
+max_sector: dw 0
+
+
+
+
+bootsect_gdt:
+ dw 0,0,0,0
+ dw 0,0,0,0
+bootsect_src:
+ dw 0xffff
+bootsect_src_base:
+ db 0x00,0x00,0x01 ; ! base = 0x010000
+ db 0x93 ; ! typbyte
+ dw 0 ; ! limit16,base24 =0
+bootsect_dst:
+ dw 0xffff
+bootsect_dst_base:
+ db 0,0,0x10 ; ! base = 0x100000
+ db 0x93 ; ! typbyte
+ dw 0 ; ! limit16,base24 =0
+ dw 0,0,0,0 ; ! BIOS CS
+ dw 0,0,0,0 ; ! BIOS DS
+
+
+; Padding to make the PFAT Boot Record sit just before the BIOS signature.
+;Pad_From_Symbol PFAT_BOOT_RECORD_OFFSET, BeginText
+
+; PFAT boot record
+; Describes how to load the setup program and kernel.
+; The default values are appropriate for creating a boot
+; floppy by concatenating the boot sector, setup program,
+; and kernel image. The buildFat program will change
+; these values if the boot floppy is formatted as a PFAT
+; filesystem.
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+;; part of pfat boot record
+setupStart:
+ dw 1 ; by default, setup is at first sector
+
+;; part of pfat boot record
+setupSize:
+ dw NUM_SETUP_SECTORS ; number of sectors in setup
+
+;; part of pfat boot record
+kernelStart:
+ dw 1+NUM_SETUP_SECTORS ; default start sector for kernel
+
+;; part of pfat boot record
+kernelSize:
+ dw NUM_KERN_SECTORS
+
+
+
+
+ ; Finish by writing the BIOS signature to mark this as
+; a valid boot sector.
+Pad_From_Symbol BIOS_SIGNATURE_OFFSET, BeginText
+Signature dw 0xAA55 ; BIOS controls this to ensure this is a boot sector
--- /dev/null
+; -*- fundamental -*-
+; Boot sector for GeekOS
+; Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software. You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; Loads setup code and a program image from sectors 1..n of a floppy
+; and executes the setup code (which will in turn execute
+; the program image).
+
+; Some of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+; Pad to desired offset from start symbol.
+; Usage: Pad_From_Symbol offset, symbol
+%macro Pad_From_Symbol 2
+ times (%1 - ($ - %2)) db 0
+%endmacro
+
+; ----------------------------------------------------------------------
+; The actual code
+; ----------------------------------------------------------------------
+
+[BITS 16]
+[ORG 0x0]
+
+BeginText: ; needed to calculate padding bytes to fill the sector
+
+ ; Copy the boot sector into INITSEG.
+ mov ax, BOOTSEG
+ mov ds, ax ; source segment for string copy
+ xor si, si ; source index for string copy
+ mov ax, INITSEG
+ mov es, ax ; destination segment for string copy
+ xor di, di ; destination index for string copy
+ cld ; clear direction flag
+ mov cx, 256 ; number of words to copy
+ rep movsw ; copy 512 bytes
+
+ jmp INITSEG:after_move
+
+after_move:
+ ; Now we're executing in INITSEG
+
+
+ ; We want the data segment to refer to INITSEG
+ ; (since we've defined variables in the same place as the code)
+ mov ds, ax ; ax still contains INITSEG
+
+ ; Put the stack in the place where we were originally loaded.
+ ; By definition, there is nothing important there now.
+ mov ax, 0
+ mov ss, ax
+ mov sp, (BOOTSEG << 4) + 512 - 2
+
+
+load_setup:
+ ; Load the setup code.
+ mov ax, word [setupStart]
+ mov word [sec_count], ax
+ add ax, [setupSize]
+ mov word [max_sector], ax
+.again:
+ mov ax, [sec_count]
+ push ax ; 1st param to ReadSector (log sec num)
+ push word SETUPSEG ; 2nd param to ReadSector (seg base)
+ sub ax, [setupStart] ; convert to 0-indexed
+ shl ax, 9 ; multiply by 512
+ push ax ; ...to get 3rd param (byte offset)
+
+ ; read the sector from the floppy
+ call ReadSector
+ add sp, 6 ; clear 3 word params
+
+ ; on to next sector
+ inc word [sec_count]
+
+ ; are we done?
+ mov bx, word [max_sector]
+ cmp word [sec_count], bx
+ jl .again
+
+load_kernel:
+ ; Load the kernel image from sectors KERN_START_SEC..n of the
+ ; floppy into memory at KERNSEG. Note that there are 128 sectors
+ ; per 64K segment. So, when figuring out which segment to
+ ; load the sector into, we shift right by 7 bits (which is
+ ; equivalent to dividing by 128).
+
+ ; Figure out start sector and max sector
+
+ mov ax, word [kernelStart]
+ mov word [sec_count], ax
+ add ax, word [kernelSize]
+ mov word [max_sector], ax
+.again:
+ mov ax, [sec_count] ; logical sector on the floppy
+; mov dx, ax
+; call PrintHex
+; call PrintNL
+ push ax ; 1st param to ReadSector (log sec num)
+ sub ax, [kernelStart] ; convert to 0-indexed
+ mov cx, ax ; save in cx
+ shr ax, 7 ; divide by 128
+ shl ax, 12 ; ...and multiply by 0x1000
+ add ax, KERNSEG ; ...to get base relative to KERNSEG
+; mov dx, ax
+; call PrintHex
+; call PrintNL
+ push ax ; 2nd param to ReadSector (seg base)
+ and cx, 0x7f ; mod sector by 128
+ shl cx, 9 ; ...and multiply by 512
+ push cx ; to get offset in segment (3rd parm)
+; mov dx, cx
+; call PrintHex
+; call PrintNL
+
+
+ ; read the sector from the floppy
+ call ReadSector
+ add sp, 6 ; clear 3 word params
+
+
+ ; on to next sector
+ inc word [sec_count]
+
+ ; have we loaded all of the sectors?
+ mov bx, word [max_sector]
+ cmp word [sec_count], bx
+ jl .again
+
+load_vm:
+ ; Load the guest image starting at 1MB
+ ; floppy into memory at KERNSEG. Note that there are 128 sectors
+ ; per 64K segment. So, when figuring out which segment to
+ ; load the sector into, we shift right by 7 bits (which is
+ ; equivalent to dividing by 128).
+
+ ; Figure out start sector and max sector
+ mov ax, word [vmStart]
+ mov word [sec_count], ax
+ add ax, word [vmSize]
+ mov word [max_sector], ax
+.again2:
+
+ mov ax, [sec_count] ; logical sector on the floppy
+ push ax ; 1st param to ReadSector (log sec num)
+
+; mov dx, ax
+; call PrintHex
+; call PrintNL
+
+ mov ax, VMSEG ; ...to get base relative to VMSEG
+ push ax ; 2nd param to ReadSector (seg base)
+
+; mov dx, ax ;
+; call PrintHex
+; call PrintNL
+
+ mov ax, 2000h ; Always write at the start of the segment
+ push ax ; 3rd parameter
+
+; mov dx, ax
+; call PrintHex
+; call PrintNL
+
+ ; read the sector from the floppy
+ call ReadSector
+ add sp, 6 ; clear 3 word params
+
+ push VMSEG
+ pop es
+
+; mov dx, word [es:2000h] ;
+; call PrintHex
+; call PrintNL
+
+
+
+; execute bios call
+
+ push cx
+ push si
+ push bx
+ push es ;
+
+ push cs
+ pop es
+ mov si, bootsect_gdt
+ mov ah, 87h
+
+ mov cx, 100h
+
+ int 0x15
+
+ adc ax, 0
+
+; mov dx, ax
+; call PrintHex
+; call PrintNL
+
+
+
+ pop es ;
+ pop bx
+ pop si
+ pop cx
+
+ ; on to next sector
+ inc word [sec_count]
+
+
+ ; update the low->high copy table for the bios
+; mov ax, word [bootsect_src_base] ;
+; add ax, 512
+; mov dx,ax;
+; call PrintHex
+; adc byte [bootsect_src_base+2], 0
+; mov word [bootsect_src_base],ax
+
+ mov ax, word [bootsect_dst_base] ;
+ add ax, 512
+ adc byte [bootsect_dst_base+2], 0
+ mov word [bootsect_dst_base],ax
+
+ ; have we loaded all of the sectors?
+
+ mov bx, word [max_sector]
+ cmp word [sec_count], bx
+
+
+.stall
+; jmp .skip
+ jl .again2
+
+.skip
+ ; Now we've loaded the setup code and the kernel image.
+ ; Jump to setup code.
+ jmp SETUPSEG:0
+
+; Read a sector from the floppy drive.
+; This code (and the rest of this boot sector) will have to
+; be re-written at some point so it reads more than one
+; sector at a time.
+;
+; Parameters:
+; - "logical" sector number [bp+8]
+; - destination segment [bp+6]
+; - destination offset [bp+4]
+ReadSector:
+ push bp ; set up stack frame
+ mov bp, sp ; "
+ pusha ; save all registers
+
+%if 0
+; debug params
+ mov dx, [bp+8]
+ call PrintHex
+ call PrintNL
+ mov dx, [bp+6]
+ call PrintHex
+ call PrintNL
+ mov dx, [bp+4]
+ call PrintHex
+ call PrintNL
+%endif
+
+ ; Sector = log_sec % SECTORS_PER_TRACK
+ ; Head = (log_sec / SECTORS_PER_TRACK) % HEADS
+ mov ax, [bp+8] ; get logical sector number from stack
+ xor dx, dx ; dx is high part of dividend (== 0)
+ mov bx, SECTORS_PER_TRACK ; divisor
+ div bx ; do the division
+ mov [sec], dx ; sector is the remainder
+ and ax, 1 ; same as mod by HEADS==2 (slight hack)
+ mov [head], ax
+
+ ; Track = log_sec / (SECTORS_PER_TRACK*HEADS)
+ mov ax, [bp+8] ; get logical sector number again
+ xor dx, dx ; dx is high part of dividend
+ mov bx, SECTORS_PER_TRACK*2 ; divisor
+ div bx ; do the division
+ mov [track], ax ; track is quotient
+
+%if 0
+; debugging code
+ mov dx, [sec]
+ call PrintHex
+ call PrintNL
+ mov dx, [head]
+ call PrintHex
+ call PrintNL
+ mov dx, [track]
+ call PrintHex
+ call PrintNL
+%endif
+
+ ; Now, try to actually read the sector from the floppy,
+ ; retrying up to 3 times.
+
+ mov [num_retries], byte 0
+
+.again:
+ mov ax, [bp+6] ; dest segment...
+ mov es, ax ; goes in es
+ mov ax, (0x02 << 8) | 1 ; function = 02h in ah,
+ ; # secs = 1 in al
+ mov bx, [track] ; track number...
+ mov ch, bl ; goes in ch
+ mov bx, [sec] ; sector number...
+ mov cl, bl ; goes in cl...
+ inc cl ; but it must be 1-based, not 0-based
+ mov bx, [head] ; head number...
+ mov dh, bl ; goes in dh
+ xor dl, dl ; hard code drive=0
+ mov bx, [bp+4] ; offset goes in bx
+ ; (es:bx points to buffer)
+ ; Call the BIOS Read Diskette Sectors service
+ int 0x13
+
+ ; If the carry flag is NOT set, then there was no error
+ ; and we're done.
+ jnc .done
+
+ ; Error - code stored in ah
+ mov dx, ax
+ call PrintHex
+ inc byte [num_retries]
+ cmp byte [num_retries], 3
+ jne .again
+
+ ; If we got here, we failed thrice, so we give up
+ mov dx, 0xdead
+ call PrintHex
+.here: jmp .here
+
+.done:
+ popa ; restore all regisiters
+ pop bp ; leave stack frame
+ ret
+
+; Include utility routines:
+;%include "util.asm"
+; REPLACED WITH FOLLOWING WHICH MUST BE COMPILED 16 FOR USE IN THIS CODE
+PrintHex:
+ pusha
+ mov cx, 4 ; 4 hex digits
+.PrintDigit:
+ rol dx, 4 ; rotate so that lowest 4 bits are used
+ mov ax, 0E0Fh ; ah = request, al = mask for nybble
+ and al, dl
+ add al, 90h ; convert al to ascii hex (four instructions)
+ daa ; I've spent 1 hour to understand how it works..
+ adc al, 40h
+ daa
+ int 10h
+ loop .PrintDigit
+ popa
+ ret
+
+; Print a newline.
+PrintNL: ; print CR and NL
+ push ax
+ mov ax, 0E0Dh ; CR
+ int 10h
+ mov al, 0Ah ; LF
+ int 10h
+ pop ax
+ ret
+
+
+
+; ----------------------------------------------------------------------
+; Variables
+; ----------------------------------------------------------------------
+
+; These are used by ReadSector
+head: dw 0
+track: dw 0
+sec: dw 0
+num_retries: db 0
+
+; Used for loops reading sectors from floppy
+sec_count: dw 0
+max_sector: dw 0
+
+
+
+
+bootsect_gdt:
+ dw 0,0,0,0
+ dw 0,0,0,0
+bootsect_src:
+ dw 0xffff
+bootsect_src_base:
+ db 0,0x20,0x08 ; ! base = 0x082000
+ db 0x93 ; ! typbyte
+ dw 0 ; ! limit16,base24 =0
+bootsect_dst:
+ dw 0xffff
+bootsect_dst_base:
+ db 0,0,0x10 ; ! base = 0x100000
+ db 0x93 ; ! typbyte
+ dw 0 ; ! limit16,base24 =0
+ dw 0,0,0,0 ; ! BIOS CS
+ dw 0,0,0,0 ; ! BIOS DS
+
+
+; Padding to make the PFAT Boot Record sit just before the BIOS signature.
+;Pad_From_Symbol PFAT_BOOT_RECORD_OFFSET, BeginText
+
+; PFAT boot record
+; Describes how to load the setup program and kernel.
+; The default values are appropriate for creating a boot
+; floppy by concatenating the boot sector, setup program,
+; and kernel image. The buildFat program will change
+; these values if the boot floppy is formatted as a PFAT
+; filesystem.
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+ dw 0
+ dw 0
+
+;; part of pfat boot record
+setupStart:
+ dw 1 ; by default, setup is at first sector
+
+;; part of pfat boot record
+setupSize:
+ dw NUM_SETUP_SECTORS ; number of sectors in setup
+
+;; part of pfat boot record
+kernelStart:
+ dw 1+NUM_SETUP_SECTORS ; default start sector for kernel
+
+;; part of pfat boot record
+kernelSize:
+ dw NUM_KERN_SECTORS
+
+ ;; part of pfat boot record
+vmStart:
+ dw 1+NUM_SETUP_SECTORS+NUM_KERN_SECTORS
+ ;; part of pfat boot record
+
+vmSize:
+ dw NUM_VM_KERNEL_SECTORS
+
+
+ ; Finish by writing the BIOS signature to mark this as
+; a valid boot sector.
+Pad_From_Symbol BIOS_SIGNATURE_OFFSET, BeginText
+Signature dw 0xAA55 ; BIOS controls this to ensure this is a boot sector
--- /dev/null
+/*
+ * Initialize kernel GDT.
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/segment.h>
+#include <geekos/int.h>
+#include <geekos/tss.h>
+#include <geekos/gdt.h>
+#include <libc/string.h>
+
+
+/*
+ * This is defined in lowlevel.asm.
+ */
+extern void Load_GDTR(ushort_t* limitAndBase);
+
+/* ----------------------------------------------------------------------
+ * Data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Number of entries in the kernel GDT.
+ */
+#define NUM_GDT_ENTRIES 16
+
+/*
+ * This is the kernel's global descriptor table.
+ */
+// JRL: why??? Should just call Alloc_Page(), otherwise we have dependencies all over the place
+//struct Segment_Descriptor *s_GDT=(struct Segment_Descriptor *)GDT_LOCATION;
+static struct Segment_Descriptor s_GDT[NUM_GDT_ENTRIES];
+
+
+/*
+ * Number of allocated GDT entries.
+ */
+static int s_numAllocated = 0;
+
+
+
+
+
+void DumpGDT()
+{
+ int i;
+ Print("GDT Contents:\n");
+
+ for (i=0;i<NUM_GDT_ENTRIES;i++) {
+ if (s_GDT[i].present) {
+ Print("%d: base=%u, limit=%u, sizeLow=%u, baseLow=%u, type=%u, system=%u, dpl=%u, preent=%u, sizeHigh=%u, avail=%u, reserved=%u, dbBit=%u, granularity=%u, baseHigh=%u\n",
+ i,
+ (s_GDT[i].baseHigh<<24) + s_GDT[i].baseLow,
+ (s_GDT[i].sizeHigh<<16) + s_GDT[i].sizeLow,
+ s_GDT[i].sizeLow,
+ s_GDT[i].baseLow,
+ s_GDT[i].type,
+ s_GDT[i].system,
+ s_GDT[i].dpl,
+ s_GDT[i].present,
+ s_GDT[i].sizeHigh,
+ s_GDT[i].avail,
+ s_GDT[i].reserved,
+ s_GDT[i].dbBit,
+ s_GDT[i].granularity,
+ s_GDT[i].baseHigh );
+ } else {
+ Print("%d: Not Present\n",i);
+ }
+ }
+}
+
+
+/* ----------------------------------------------------------------------
+ * Functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Allocate an descriptor from the GDT.
+ * Returns null if there are none left.
+ */
+struct Segment_Descriptor* Allocate_Segment_Descriptor(void)
+{
+ struct Segment_Descriptor* result = 0;
+ int i;
+ bool iflag;
+
+ iflag = Begin_Int_Atomic();
+
+ /* Note; entry 0 is unused (thus never allocated) */
+ for (i = 1; i < NUM_GDT_ENTRIES; ++i) {
+ struct Segment_Descriptor *desc = &s_GDT[ i ];
+ if (desc->avail) {
+ ++s_numAllocated;
+ desc->avail = 0;
+ result = desc;
+ break;
+ }
+ }
+
+ End_Int_Atomic(iflag);
+
+ return result;
+}
+
+/*
+ * Free a segment descriptor.
+ */
+void Free_Segment_Descriptor(struct Segment_Descriptor* desc)
+{
+ bool iflag = Begin_Int_Atomic();
+
+ KASSERT(!desc->avail);
+
+ Init_Null_Segment_Descriptor(desc);
+ desc->avail = 1;
+ --s_numAllocated;
+
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Get the index (int the GDT) of given segment descriptor.
+ */
+int Get_Descriptor_Index(struct Segment_Descriptor* desc)
+{
+ return (int) (desc - s_GDT);
+}
+
+/*
+ * Initialize the kernel's GDT.
+ */
+void Init_GDT(void)
+{
+ ushort_t limitAndBase[3];
+ ulong_t gdtBaseAddr = (ulong_t) s_GDT;
+ struct Segment_Descriptor* desc;
+ int i;
+
+ Print("GDT Placed at %x, %d entries\n",GDT_LOCATION,NUM_GDT_ENTRIES);
+
+ KASSERT(sizeof(struct Segment_Descriptor) == 8);
+
+ /* Clear out entries. */
+ for (i = 0; i < NUM_GDT_ENTRIES; ++i) {
+ desc = &s_GDT[ i ];
+ Init_Null_Segment_Descriptor(desc);
+ desc->avail = 1;
+ }
+
+ /* Kernel code segment. */
+ desc = Allocate_Segment_Descriptor();
+ Init_Code_Segment_Descriptor(
+ desc,
+ 0, /* base address */
+ 0x100000, /* num pages (== 2^20) */
+ 0 /* privilege level (0 == kernel) */
+ );
+ KASSERT(Get_Descriptor_Index(desc) == (KERNEL_CS >> 3));
+
+ /* Kernel data segment. */
+ desc = Allocate_Segment_Descriptor();
+ Init_Data_Segment_Descriptor(
+ desc,
+ 0, /* base address */
+ 0x100000, /* num pages (== 2^20) */
+ 0 /* privilege level (0 == kernel) */
+ );
+ KASSERT(Get_Descriptor_Index(desc) == (KERNEL_DS >> 3));
+
+ /* Activate the kernel GDT. */
+ limitAndBase[0] = sizeof(struct Segment_Descriptor) * NUM_GDT_ENTRIES;
+ limitAndBase[1] = gdtBaseAddr & 0xffff;
+ limitAndBase[2] = gdtBaseAddr >> 16;
+ Load_GDTR(limitAndBase);
+}
--- /dev/null
+/*
+ * ATA (aka IDE) driver.
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * NOTES:
+ * 12/22/03 - Converted to use new block device layer with queued requests
+ * 1/20/04 - Changed probing of drives to work on Bochs 2.0 with 2 drives
+ */
+
+#include <geekos/serial.h>
+
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/errno.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/screen.h>
+#include <geekos/timer.h>
+#include <geekos/kthread.h>
+#include <geekos/blockdev.h>
+#include <geekos/ide.h>
+
+/* Registers */
+#define IDE_DATA_REGISTER 0x1f0
+#define IDE_ERROR_REGISTER 0x1f1
+#define IDE_FEATURE_REG IDE_ERROR_REGISTER
+#define IDE_SECTOR_COUNT_REGISTER 0x1f2
+#define IDE_SECTOR_NUMBER_REGISTER 0x1f3
+#define IDE_CYLINDER_LOW_REGISTER 0x1f4
+#define IDE_CYLINDER_HIGH_REGISTER 0x1f5
+#define IDE_DRIVE_HEAD_REGISTER 0x1f6
+#define IDE_STATUS_REGISTER 0x1f7
+#define IDE_COMMAND_REGISTER 0x1f7
+#define IDE_DEVICE_CONTROL_REGISTER 0x3F6
+
+/* Drives */
+#define IDE_DRIVE_0 0xa0
+#define IDE_DRIVE_1 0xb0
+
+/* Commands */
+#define IDE_COMMAND_IDENTIFY_DRIVE 0xEC
+#define IDE_COMMAND_SEEK 0x70
+#define IDE_COMMAND_READ_SECTORS 0x21
+#define IDE_COMMAND_READ_BUFFER 0xE4
+#define IDE_COMMAND_WRITE_SECTORS 0x30
+#define IDE_COMMAND_WRITE_BUFFER 0xE8
+#define IDE_COMMAND_DIAGNOSTIC 0x90
+#define IDE_COMMAND_ATAPI_IDENT_DRIVE 0xA1
+
+/* Results words from Identify Drive Request */
+#define IDE_INDENTIFY_NUM_CYLINDERS 0x01
+#define IDE_INDENTIFY_NUM_HEADS 0x03
+#define IDE_INDENTIFY_NUM_BYTES_TRACK 0x04
+#define IDE_INDENTIFY_NUM_BYTES_SECTOR 0x05
+#define IDE_INDENTIFY_NUM_SECTORS_TRACK 0x06
+
+/* bits of Status Register */
+#define IDE_STATUS_DRIVE_BUSY 0x80
+#define IDE_STATUS_DRIVE_READY 0x40
+#define IDE_STATUS_DRIVE_WRITE_FAULT 0x20
+#define IDE_STATUS_DRIVE_SEEK_COMPLETE 0x10
+#define IDE_STATUS_DRIVE_DATA_REQUEST 0x08
+#define IDE_STATUS_DRIVE_CORRECTED_DATA 0x04
+#define IDE_STATUS_DRIVE_INDEX 0x02
+#define IDE_STATUS_DRIVE_ERROR 0x01
+
+/* Bits of Device Control Register */
+#define IDE_DCR_NOINTERRUPT 0x02
+#define IDE_DCR_RESET 0x04
+
+/* Return codes from various IDE_* functions */
+#define IDE_ERROR_NO_ERROR 0
+#define IDE_ERROR_BAD_DRIVE -1
+#define IDE_ERROR_INVALID_BLOCK -2
+#define IDE_ERROR_DRIVE_ERROR -3
+
+/* Control register bits */
+#define IDE_CONTROL_REGISTER 0x3F6
+#define IDE_CONTROL_SOFTWARE_RESET 0x04
+#define IDE_CONTROL_INT_DISABLE 0x02
+
+#define LOW_BYTE(x) (x & 0xff)
+#define HIGH_BYTE(x) ((x >> 8) & 0xff)
+
+#define IDE_MAX_DRIVES 2
+
+typedef struct {
+ short num_Cylinders;
+ short num_Heads;
+ short num_SectorsPerTrack;
+ short num_BytesPerSector;
+} ideDisk;
+
+int ideDebug = 99;
+static int numDrives;
+static ideDisk drives[IDE_MAX_DRIVES];
+
+struct Thread_Queue s_ideWaitQueue;
+struct Block_Request_List s_ideRequestQueue;
+
+/*
+ * return the number of logical blocks for a particular drive.
+ *
+ */
+static int IDE_getNumBlocks(int driveNum)
+{
+ if (driveNum < 0 || driveNum > IDE_MAX_DRIVES) {
+ return IDE_ERROR_BAD_DRIVE;
+ }
+
+ return (drives[driveNum].num_Heads *
+ drives[driveNum].num_SectorsPerTrack *
+ drives[driveNum].num_Cylinders);
+}
+
+/*
+ * Read a block at the logical block number indicated.
+ */
+static int IDE_Read(int driveNum, int blockNum, char *buffer)
+{
+ int i;
+ int head;
+ int sector;
+ int cylinder;
+ short *bufferW;
+ int reEnable = 0;
+
+ if (driveNum < 0 || driveNum > (numDrives-1)) {
+ if (ideDebug) Print("ide: invalid drive %d\n", driveNum);
+ return IDE_ERROR_BAD_DRIVE;
+ }
+
+ if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+ if (ideDebug) Print("ide: invalid block %d\n", blockNum);
+ return IDE_ERROR_INVALID_BLOCK;
+ }
+
+ if (Interrupts_Enabled()) {
+ Disable_Interrupts();
+ reEnable = 1;
+ }
+
+ /* now compute the head, cylinder, and sector */
+ sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+ cylinder = blockNum / (drives[driveNum].num_Heads *
+ drives[driveNum].num_SectorsPerTrack);
+ head = (blockNum / drives[driveNum].num_SectorsPerTrack) %
+ drives[driveNum].num_Heads;
+
+ if (ideDebug >= 2) {
+ Print ("request to read block %d\n", blockNum);
+ Print (" head %d\n", head);
+ Print (" cylinder %d\n", cylinder);
+ Print (" sector %d\n", sector);
+ }
+
+ Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+ Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+ Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+ Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+ if (driveNum == 0) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+ } else if (driveNum == 1) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+ }
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_READ_SECTORS);
+
+ if (ideDebug > 2) Print("About to wait for Read \n");
+
+ /* wait for the drive */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+ Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+ return IDE_ERROR_DRIVE_ERROR;
+ }
+
+ if (ideDebug > 2) Print("got buffer \n");
+
+ bufferW = (short *) buffer;
+ for (i=0; i < 256; i++) {
+ bufferW[i] = In_Word(IDE_DATA_REGISTER);
+ }
+
+ if (reEnable) Enable_Interrupts();
+
+ return IDE_ERROR_NO_ERROR;
+}
+
+/*
+ * Write a block at the logical block number indicated.
+ */
+static int IDE_Write(int driveNum, int blockNum, char *buffer)
+{
+ int i;
+ int head;
+ int sector;
+ int cylinder;
+ short *bufferW;
+ int reEnable = 0;
+
+ if (driveNum < 0 || driveNum > (numDrives-1)) {
+ return IDE_ERROR_BAD_DRIVE;
+ }
+
+ if (blockNum < 0 || blockNum >= IDE_getNumBlocks(driveNum)) {
+ return IDE_ERROR_INVALID_BLOCK;
+ }
+
+ if (Interrupts_Enabled()) {
+ Disable_Interrupts();
+ reEnable = 1;
+ }
+
+ /* now compute the head, cylinder, and sector */
+ sector = blockNum % drives[driveNum].num_SectorsPerTrack + 1;
+ cylinder = blockNum / (drives[driveNum].num_Heads *
+ drives[driveNum].num_SectorsPerTrack);
+ head = (blockNum / drives[driveNum].num_SectorsPerTrack) %
+ drives[driveNum].num_Heads;
+
+ if (ideDebug) {
+ Print ("request to write block %d\n", blockNum);
+ Print (" head %d\n", head);
+ Print (" cylinder %d\n", cylinder);
+ Print (" sector %d\n", sector);
+ }
+
+ Out_Byte(IDE_SECTOR_COUNT_REGISTER, 1);
+ Out_Byte(IDE_SECTOR_NUMBER_REGISTER, sector);
+ Out_Byte(IDE_CYLINDER_LOW_REGISTER, LOW_BYTE(cylinder));
+ Out_Byte(IDE_CYLINDER_HIGH_REGISTER, HIGH_BYTE(cylinder));
+ if (driveNum == 0) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_0 | head);
+ } else if (driveNum == 1) {
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, IDE_DRIVE_1 | head);
+ }
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_WRITE_SECTORS);
+
+
+ /* wait for the drive */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ bufferW = (short *) buffer;
+ for (i=0; i < 256; i++) {
+ Out_Word(IDE_DATA_REGISTER, bufferW[i]);
+ }
+
+ if (ideDebug) Print("About to wait for Write \n");
+
+ /* wait for the drive */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ if (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_ERROR) {
+ Print("ERROR: Got Read %d\n", In_Byte(IDE_STATUS_REGISTER));
+ return IDE_ERROR_DRIVE_ERROR;
+ }
+
+ if (reEnable) Enable_Interrupts();
+
+ return IDE_ERROR_NO_ERROR;
+}
+
+static int IDE_Open(struct Block_Device *dev)
+{
+ KASSERT(!dev->inUse);
+ return 0;
+}
+
+static int IDE_Close(struct Block_Device *dev)
+{
+ KASSERT(dev->inUse);
+ return 0;
+}
+
+static int IDE_Get_Num_Blocks(struct Block_Device *dev)
+{
+ return IDE_getNumBlocks(dev->unit);
+}
+
+static struct Block_Device_Ops s_ideDeviceOps = {
+ IDE_Open,
+ IDE_Close,
+ IDE_Get_Num_Blocks,
+};
+
+static void IDE_Request_Thread(ulong_t arg)
+{
+ for (;;) {
+ struct Block_Request *request;
+ int rc;
+
+ /* Wait for a request to arrive */
+ request = Dequeue_Request(&s_ideRequestQueue, &s_ideWaitQueue);
+
+ /* Do the I/O */
+ if (request->type == BLOCK_READ)
+ rc = IDE_Read(request->dev->unit, request->blockNum, request->buf);
+ else
+ rc = IDE_Write(request->dev->unit, request->blockNum, request->buf);
+
+ /* Notify requesting thread of final status */
+ Notify_Request_Completion(request, rc == 0 ? COMPLETED : ERROR, rc);
+ }
+}
+
+static int readDriveConfig(int drive)
+{
+ int i;
+ int status;
+ short info[256];
+ char devname[BLOCKDEV_MAX_NAME_LEN];
+ int rc;
+
+ if (ideDebug) Print("ide: about to read drive config for drive #%d\n", drive);
+
+
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+ Print("Set head register\n");
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_IDENTIFY_DRIVE);
+
+ Print ("identify drive\n");
+
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+
+ Print ("Status no longer busy\n");
+
+ status = In_Byte(IDE_STATUS_REGISTER);
+
+ Print ("Status is %x\n",status);
+ /*
+ * simulate failure
+ * status = 0x50;
+ */
+ if ((status & IDE_STATUS_DRIVE_DATA_REQUEST)) {
+ Print("ide: probe found ATA drive\n");
+ /* drive responded to ATA probe */
+ for (i=0; i < 256; i++) {
+ info[i] = In_Word(IDE_DATA_REGISTER);
+ }
+
+ drives[drive].num_Cylinders = info[IDE_INDENTIFY_NUM_CYLINDERS];
+ drives[drive].num_Heads = info[IDE_INDENTIFY_NUM_HEADS];
+ drives[drive].num_SectorsPerTrack = info[IDE_INDENTIFY_NUM_SECTORS_TRACK];
+ drives[drive].num_BytesPerSector = info[IDE_INDENTIFY_NUM_BYTES_SECTOR];
+ } else {
+ /* try for ATAPI */
+ Print("Trying for ATAPI\n");
+ Out_Byte(IDE_FEATURE_REG, 0); /* disable dma & overlap */
+ Print("Out FEATURE REG\n");
+
+ Out_Byte(IDE_DRIVE_HEAD_REGISTER, (drive == 0) ? IDE_DRIVE_0 : IDE_DRIVE_1);
+ Print("Out Head Select\n");
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_ATAPI_IDENT_DRIVE);
+ Print("Out Ident drive\n");
+
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+ Print("No longer busy\n");
+
+ status = In_Byte(IDE_STATUS_REGISTER);
+
+ Print("status is %x\n",status);
+ Print("ide: found atapi drive\n");
+ return -1;
+ }
+
+ Print(" ide%d: cyl=%d, heads=%d, sectors=%d\n", drive, drives[drive].num_Cylinders,
+ drives[drive].num_Heads, drives[drive].num_SectorsPerTrack);
+
+ /* Register the drive as a block device */
+ snprintf(devname, sizeof(devname), "ide%d", drive);
+ rc = Register_Block_Device(devname, &s_ideDeviceOps, drive, 0, &s_ideWaitQueue, &s_ideRequestQueue);
+ if (rc != 0)
+ Print(" Error: could not create block device for %s\n", devname);
+
+ return 0;
+}
+
+
+void Init_IDE(void)
+{
+ int errorCode;
+
+ // Check to see if controller 0 is present
+ Out_Byte(0x1f3,0x88);
+ if (In_Byte(0x1f3)==0x88) {
+ Print("IDE Controller 0 is present\n");
+ }
+
+ // Check to see if controller 1 is present
+ Out_Byte(0x173,0x88);
+ if (In_Byte(0x173)==0x88) {
+ Print("IDE Controller 1 is present\n");
+ }
+
+ Print("Initializing IDE controller (0x1f0)...\n");
+
+ /* Reset the controller and drives */
+ Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT | IDE_DCR_RESET);
+ Micro_Delay(100);
+ Out_Byte(IDE_DEVICE_CONTROL_REGISTER, IDE_DCR_NOINTERRUPT);
+
+/*
+ * FIXME: This code doesn't work on Bochs 2.0.
+ * while ((In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_READY) == 0)
+ * ;
+ */
+
+ /* This code does work on Bochs 2.0. */
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY)
+ ;
+
+ if (ideDebug) Print("About to run drive Diagnosis\n");
+
+ Out_Byte(IDE_COMMAND_REGISTER, IDE_COMMAND_DIAGNOSTIC);
+ while (In_Byte(IDE_STATUS_REGISTER) & IDE_STATUS_DRIVE_BUSY);
+ errorCode = In_Byte(IDE_ERROR_REGISTER);
+ if (ideDebug) Print("ide: ide error register = %x\n", errorCode);
+
+ /* Probe and register drives */
+ if (readDriveConfig(0) == 0)
+ ++numDrives;
+ if (readDriveConfig(1) == 0)
+ ++numDrives;
+ if (ideDebug) Print("Found %d IDE drives\n", numDrives);
+
+ /* Start request thread */
+ if (numDrives > 0)
+ Start_Kernel_Thread(IDE_Request_Thread, 0, PRIORITY_NORMAL, true);
+}
--- /dev/null
+/*
+ * GeekOS IDT initialization code
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/defs.h>
+#include <geekos/idt.h>
+#include <geekos/serial.h>
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Allocated
+ */
+static union IDT_Descriptor s_IDT[ NUM_IDT_ENTRIES ];
+
+// JRL: why??? Should just call Alloc_Page(), otherwise we have dependencies all over the place
+//static union IDT_Descriptor *s_IDT = (union IDT_Descriptor *)IDT_LOCATION;
+
+/*
+ * These symbols are defined in lowlevel.asm, and define the
+ * size of the interrupt entry point table and the sizes
+ * of the individual entry points. This gives us sufficient
+ * information to build the IDT.
+ */
+extern char g_entryPointTableStart, g_entryPointTableEnd;
+extern int g_handlerSizeNoErr, g_handlerSizeErr;
+
+/*
+ * Table of C interrupt handler functions.
+ * Note that this is public only because it is used
+ * in lowlevel.asm. Other code should not refer to it.
+ */
+Interrupt_Handler g_interruptTable[ NUM_IDT_ENTRIES ];
+
+
+
+void DumpIDT()
+{
+ int i;
+ Print("IDT Contents:\n");
+
+ for (i=0;i<NUM_IDT_ENTRIES/16;i++) {
+ if (s_IDT[i].ig.present) {
+ Print("%d: segmentselector=%u, offset=%u, offsetLow=%u, segmentSelector=%u, reserved=%u, signature=%u, dpl=%u, present=%u, offsetHigh=%u\n",
+ i,
+ s_IDT[i].ig.segmentSelector,
+ (s_IDT[i].ig.offsetHigh<<16) + s_IDT[i].ig.offsetLow,
+ s_IDT[i].ig.offsetLow,
+ s_IDT[i].ig.segmentSelector,
+ s_IDT[i].ig.reserved,
+ s_IDT[i].ig.signature,
+ s_IDT[i].ig.dpl,
+ s_IDT[i].ig.present,
+ s_IDT[i].ig.offsetHigh);
+ }
+ }
+}
+
+
+void SerialDumpIDT()
+{
+ int i;
+ SerialPrint("IDT Contents:\n");
+
+ for (i=0;i<NUM_IDT_ENTRIES;i++) {
+ if (s_IDT[i].ig.present) {
+ SerialPrint("%d: segmentselector=%u, offset=%u, offsetLow=%u, segmentSelector=%u, reserved=%u, signature=%u, dpl=%u, present=%u, offsetHigh=%u\n",
+ i,
+ s_IDT[i].ig.segmentSelector,
+ (s_IDT[i].ig.offsetHigh<<16) + s_IDT[i].ig.offsetLow,
+ s_IDT[i].ig.offsetLow,
+ s_IDT[i].ig.segmentSelector,
+ s_IDT[i].ig.reserved,
+ s_IDT[i].ig.signature,
+ s_IDT[i].ig.dpl,
+ s_IDT[i].ig.present,
+ s_IDT[i].ig.offsetHigh);
+ SerialPrint("\n");
+ }
+ }
+}
+
+
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize the Interrupt Descriptor Table.
+ * This will allow us to install C handler functions
+ * for interrupts, both processor-generated and
+ * those generated by external hardware.
+ */
+void Init_IDT(void)
+{
+ int i;
+ ushort_t limitAndBase[3];
+ ulong_t idtBaseAddr = (ulong_t) s_IDT;
+ ulong_t tableBaseAddr = (ulong_t) &g_entryPointTableStart;
+ ulong_t addr;
+
+ PrintBoth("Initializing IDT\n");
+
+ /* Make sure the layout of the entry point table is as we expect. */
+ KASSERT(g_handlerSizeNoErr == g_handlerSizeErr);
+ KASSERT((&g_entryPointTableEnd - &g_entryPointTableStart) ==
+ g_handlerSizeNoErr * NUM_IDT_ENTRIES);
+
+ /*
+ * Build the IDT.
+ * We're taking advantage of the fact that all of the
+ * entry points are laid out consecutively, and that they
+ * are all padded to be the same size.
+ */
+ for (i = 0, addr = tableBaseAddr; i < NUM_IDT_ENTRIES; ++i) {
+ /*
+ * All interrupts except for the syscall interrupt
+ * must have kernel privilege to access.
+ */
+ int dpl = (i == SYSCALL_INT) ? USER_PRIVILEGE : KERNEL_PRIVILEGE;
+ Init_Interrupt_Gate(&s_IDT[i], addr, dpl);
+ addr += g_handlerSizeNoErr;
+ }
+
+ /*
+ * Cruft together a 16 bit limit and 32 bit base address
+ * to load into the IDTR.
+ */
+ limitAndBase[0] = 8 * NUM_IDT_ENTRIES;
+ limitAndBase[1] = idtBaseAddr & 0xffff;
+ limitAndBase[2] = idtBaseAddr >> 16;
+
+ /* Install the new table in the IDTR. */
+ Load_IDTR(limitAndBase);
+}
+
+/*
+ * Initialize an interrupt gate with given handler address
+ * and descriptor privilege level.
+ */
+void Init_Interrupt_Gate(union IDT_Descriptor* desc, ulong_t addr,
+ int dpl)
+{
+ desc->ig.offsetLow = addr & 0xffff;
+ desc->ig.segmentSelector = KERNEL_CS;
+ desc->ig.reserved = 0;
+ desc->ig.signature = 0x70; /* == 01110000b */
+ desc->ig.dpl = dpl;
+ desc->ig.present = 1;
+ desc->ig.offsetHigh = addr >> 16;
+}
+
+/*
+ * Install a C handler function for given interrupt.
+ * This is a lower-level notion than an "IRQ", which specifically
+ * means an interrupt triggered by external hardware.
+ * This function can install a handler for ANY interrupt.
+ */
+void Install_Interrupt_Handler(int interrupt, Interrupt_Handler handler)
+{
+ KASSERT(interrupt >= 0 && interrupt < NUM_IDT_ENTRIES);
+ g_interruptTable[interrupt] = handler;
+}
--- /dev/null
+/*
+ * GeekOS interrupt handling data structures and functions
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/idt.h> /* x86-specific int handling stuff */
+#include <geekos/screen.h>
+#include <geekos/kassert.h>
+#include <geekos/int.h>
+#include <geekos/serial.h>
+
+#include <geekos/cpu.h>
+
+/*
+ * Defined in lowlevel.asm.
+ */
+ulong_t Get_Current_EFLAGS(void);
+
+/* ----------------------------------------------------------------------
+ * Private functions and data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * A dummy interrupt handler function.
+ * Ensures that the low-level interrupt code always
+ * has a handler to call.
+ */
+static void Dummy_Interrupt_Handler(struct Interrupt_State* state)
+{
+ Begin_IRQ(state);
+
+ Print("Unexpected Interrupt! Ignoring!\n");
+ SerialPrint("*** Unexpected interrupt! *** Ignoring!\n");
+ Dump_Interrupt_State(state);
+
+ End_IRQ(state);
+
+ // STOP();
+}
+
+#if 0
+static void Print_Selector(const char* regName, uint_t value)
+{
+ Print("%s: index=%d, ti=%d, rpl=%d\n",
+ regName, value >> 3, (value >> 2) & 1, value & 3);
+}
+#endif
+
+static void SerialPrint_Selector(const char* regName, uint_t value)
+{
+ SerialPrint("%s: index=%d, ti=%d, rpl=%d\n",
+ regName, value >> 3, (value >> 2) & 1, value & 3);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize the interrupt system.
+ */
+void Init_Interrupts(void)
+{
+ int i;
+
+ PrintBoth("Initializing Interrupts\n");
+
+ /* Low-level initialization. Build and initialize the IDT. */
+ Init_IDT();
+
+ /*
+ * Initialize all entries of the handler table with a dummy handler.
+ * This will ensure that we always have a handler function to call.
+ */
+ for (i = 0; i < NUM_IDT_ENTRIES; ++i) {
+ Install_Interrupt_Handler(i, Dummy_Interrupt_Handler);
+ }
+
+ /* Re-enable interrupts */
+ Enable_Interrupts();
+}
+
+/*
+ * Query whether or not interrupts are currently enabled.
+ */
+bool Interrupts_Enabled(void)
+{
+ ulong_t eflags = Get_Current_EFLAGS();
+ return (eflags & EFLAGS_IF) != 0;
+}
+
+/*
+ * Dump interrupt state struct to screen
+ */
+void Dump_Interrupt_State(struct Interrupt_State* state)
+{
+ uint_t errorCode = state->errorCode;
+
+ SerialPrint("eax=%08x ebx=%08x ecx=%08x edx=%08x\n"
+ "esi=%08x edi=%08x ebp=%08x\n"
+ "eip=%08x cs=%08x eflags=%08x\n"
+ "Interrupt number=%d, error code=%d\n"
+ "index=%d, TI=%d, IDT=%d, EXT=%d\n",
+ state->eax, state->ebx, state->ecx, state->edx,
+ state->esi, state->edi, state->ebp,
+ state->eip, state->cs, state->eflags,
+ state->intNum, errorCode,
+ errorCode >> 3, (errorCode >> 2) & 1, (errorCode >> 1) & 1, errorCode & 1
+ );
+ if (Is_User_Interrupt(state)) {
+ struct User_Interrupt_State *ustate = (struct User_Interrupt_State*) state;
+ SerialPrint("user esp=%08x, user ss=%08x\n", ustate->espUser, ustate->ssUser);
+ }
+ SerialPrint_Selector("cs", state->cs);
+ SerialPrint_Selector("ds", state->ds);
+ SerialPrint_Selector("es", state->es);
+ SerialPrint_Selector("fs", state->fs);
+ SerialPrint_Selector("gs", state->gs);
+}
--- /dev/null
+/*
+ * x86 port IO routines
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/io.h>
+
+/*
+ * Write a byte to an I/O port.
+ */
+void Out_Byte(ushort_t port, uchar_t value)
+{
+ __asm__ __volatile__ (
+ "outb %b0, %w1"
+ :
+ : "a" (value), "Nd" (port)
+ );
+}
+
+/*
+ * Read a byte from an I/O port.
+ */
+uchar_t In_Byte(ushort_t port)
+{
+ uchar_t value;
+
+ __asm__ __volatile__ (
+ "inb %w1, %b0"
+ : "=a" (value)
+ : "Nd" (port)
+ );
+
+ return value;
+}
+
+/*
+ * Write a word to an I/O port.
+ */
+void Out_Word(ushort_t port, ushort_t value)
+{
+ __asm__ __volatile__ (
+ "outw %w0, %w1"
+ :
+ : "a" (value), "Nd" (port)
+ );
+}
+
+/*
+ * Read a byte from an I/O port.
+ */
+ushort_t In_Word(ushort_t port)
+{
+ ushort_t value;
+
+ __asm__ __volatile__ (
+ "inw %w1, %w0"
+ : "=a" (value)
+ : "Nd" (port)
+ );
+
+ return value;
+}
+
+/*
+ * Short delay. May be needed when talking to some
+ * (slow) I/O devices.
+ */
+void IO_Delay(void)
+{
+ uchar_t value = 0;
+ __asm__ __volatile__ (
+ "outb %0, $0x80"
+ :
+ : "a" (value)
+ );
+}
--- /dev/null
+/*
+ * This is the device-driver interface to the interrupt system.
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/idt.h>
+#include <geekos/io.h>
+#include <geekos/irq.h>
+
+/* ----------------------------------------------------------------------
+ * Private functions and data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Current IRQ mask.
+ * This should be kept up to date with setup.asm
+ * (which does the initial programming of the PICs).
+ */
+static ushort_t s_irqMask = 0xfffb;
+
+/*
+ * Get the master and slave parts of an IRQ mask.
+ */
+#define MASTER(mask) ((mask) & 0xff)
+#define SLAVE(mask) (((mask)>>8) & 0xff)
+
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Install a handler for given IRQ.
+ * Note that we don't unmask the IRQ.
+ */
+void Install_IRQ(int irq, Interrupt_Handler handler)
+{
+ Install_Interrupt_Handler(irq + FIRST_EXTERNAL_INT, handler);
+}
+
+/*
+ * Get current IRQ mask. Each bit position represents
+ * one of the 16 IRQ lines.
+ */
+ushort_t Get_IRQ_Mask(void)
+{
+ return s_irqMask;
+}
+
+/*
+ * Set the IRQ mask.
+ */
+void Set_IRQ_Mask(ushort_t mask)
+{
+ uchar_t oldMask, newMask;
+
+ oldMask = MASTER(s_irqMask);
+ newMask = MASTER(mask);
+ if (newMask != oldMask) {
+ Out_Byte(0x21, newMask);
+ }
+
+ oldMask = SLAVE(s_irqMask);
+ newMask = SLAVE(mask);
+ if (newMask != oldMask) {
+ Out_Byte(0xA1, newMask);
+ }
+
+ s_irqMask = mask;
+}
+
+/*
+ * Enable given IRQ.
+ */
+void Enable_IRQ(int irq)
+{
+ bool iflag = Begin_Int_Atomic();
+
+ KASSERT(irq >= 0 && irq < 16);
+ ushort_t mask = Get_IRQ_Mask();
+ mask &= ~(1 << irq);
+ Set_IRQ_Mask(mask);
+
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Disable given IRQ.
+ */
+void Disable_IRQ(int irq)
+{
+ bool iflag = Begin_Int_Atomic();
+
+ KASSERT(irq >= 0 && irq < 16);
+ ushort_t mask = Get_IRQ_Mask();
+ mask |= (1 << irq);
+ Set_IRQ_Mask(mask);
+
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Called by an IRQ handler to begin the interrupt.
+ * Currently a no-op.
+ */
+void Begin_IRQ(struct Interrupt_State* state)
+{
+}
+
+/*
+ * Called by an IRQ handler to end the interrupt.
+ * Sends an EOI command to the appropriate PIC(s).
+ */
+void End_IRQ(struct Interrupt_State* state)
+{
+ int irq = state->intNum - FIRST_EXTERNAL_INT;
+ uchar_t command = 0x60 | (irq & 0x7);
+
+ if (irq < 8) {
+ /* Specific EOI to master PIC */
+ Out_Byte(0x20, command);
+ }
+ else {
+ /* Specific EOI to slave PIC, then to master (cascade line) */
+ Out_Byte(0xA0, command);
+ Out_Byte(0x20, 0x62);
+ }
+}
--- /dev/null
+/*
+ * Keyboard driver
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Information sources:
+ * - Chapter 8 of _The Undocumented PC_, 2nd ed, by Frank van Gilluwe,
+ * ISBN 0-201-47950-8.
+ * - Pages 400-409 of _The Programmers PC Sourcebook_, by Thom Hogan,
+ * ISBN 1-55615-118-7.
+ */
+
+/*
+ * Credits:
+ * - Peter Gnodde <peter@pcswebdesign.nl> added support for
+ * the CTRL and ALT modifiers
+ */
+
+/*
+ * TODO list:
+ * - Right now we're assuming an 83-key keyboard.
+ * Should add support for 101+ keyboards.
+ * - Should toggle keyboard LEDs.
+ */
+
+#include <geekos/kthread.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/irq.h>
+#include <geekos/io.h>
+#include <geekos/keyboard.h>
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Current shift state.
+ */
+#define LEFT_SHIFT 0x01
+#define RIGHT_SHIFT 0x02
+#define LEFT_CTRL 0x04
+#define RIGHT_CTRL 0x08
+#define LEFT_ALT 0x10
+#define RIGHT_ALT 0x20
+#define SHIFT_MASK (LEFT_SHIFT | RIGHT_SHIFT)
+#define CTRL_MASK (LEFT_CTRL | RIGHT_CTRL)
+#define ALT_MASK (LEFT_ALT | RIGHT_ALT)
+static unsigned s_shiftState = 0;
+
+/*
+ * Queue for keycodes, in case they arrive faster than consumer
+ * can deal with them.
+ */
+#define QUEUE_SIZE 256
+#define QUEUE_MASK 0xff
+#define NEXT(index) (((index) + 1) & QUEUE_MASK)
+static Keycode s_queue[QUEUE_SIZE];
+static int s_queueHead, s_queueTail;
+
+/*
+ * Wait queue for thread(s) waiting for keyboard events.
+ */
+static struct Thread_Queue s_waitQueue;
+
+/*
+ * Translate from scan code to key code, when shift is not pressed.
+ */
+static const Keycode s_scanTableNoShift[] = {
+ KEY_UNKNOWN, ASCII_ESC, '1', '2', /* 0x00 - 0x03 */
+ '3', '4', '5', '6', /* 0x04 - 0x07 */
+ '7', '8', '9', '0', /* 0x08 - 0x0B */
+ '-', '=', ASCII_BS, '\t', /* 0x0C - 0x0F */
+ 'q', 'w', 'e', 'r', /* 0x10 - 0x13 */
+ 't', 'y', 'u', 'i', /* 0x14 - 0x17 */
+ 'o', 'p', '[', ']', /* 0x18 - 0x1B */
+ '\r', KEY_LCTRL, 'a', 's', /* 0x1C - 0x1F */
+ 'd', 'f', 'g', 'h', /* 0x20 - 0x23 */
+ 'j', 'k', 'l', ';', /* 0x24 - 0x27 */
+ '\'', '`', KEY_LSHIFT, '\\', /* 0x28 - 0x2B */
+ 'z', 'x', 'c', 'v', /* 0x2C - 0x2F */
+ 'b', 'n', 'm', ',', /* 0x30 - 0x33 */
+ '.', '/', KEY_RSHIFT, KEY_PRINTSCRN, /* 0x34 - 0x37 */
+ KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, /* 0x38 - 0x3B */
+ KEY_F2, KEY_F3, KEY_F4, KEY_F5, /* 0x3C - 0x3F */
+ KEY_F6, KEY_F7, KEY_F8, KEY_F9, /* 0x40 - 0x43 */
+ KEY_F10, KEY_NUMLOCK, KEY_SCRLOCK, KEY_KPHOME, /* 0x44 - 0x47 */
+ KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, KEY_KPLEFT, /* 0x48 - 0x4B */
+ KEY_KPCENTER, KEY_KPRIGHT, KEY_KPPLUS, KEY_KPEND, /* 0x4C - 0x4F */
+ KEY_KPDOWN, KEY_KPPGDN, KEY_KPINSERT, KEY_KPDEL, /* 0x50 - 0x53 */
+ KEY_SYSREQ, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, /* 0x54 - 0x57 */
+};
+#define SCAN_TABLE_SIZE (sizeof(s_scanTableNoShift) / sizeof(Keycode))
+
+/*
+ * Translate from scan code to key code, when shift *is* pressed.
+ * Keep this in sync with the unshifted table above!
+ * They must be the same size.
+ */
+static const Keycode s_scanTableWithShift[] = {
+ KEY_UNKNOWN, ASCII_ESC, '!', '@', /* 0x00 - 0x03 */
+ '#', '$', '%', '^', /* 0x04 - 0x07 */
+ '&', '*', '(', ')', /* 0x08 - 0x0B */
+ '_', '+', ASCII_BS, '\t', /* 0x0C - 0x0F */
+ 'Q', 'W', 'E', 'R', /* 0x10 - 0x13 */
+ 'T', 'Y', 'U', 'I', /* 0x14 - 0x17 */
+ 'O', 'P', '{', '}', /* 0x18 - 0x1B */
+ '\r', KEY_LCTRL, 'A', 'S', /* 0x1C - 0x1F */
+ 'D', 'F', 'G', 'H', /* 0x20 - 0x23 */
+ 'J', 'K', 'L', ':', /* 0x24 - 0x27 */
+ '"', '~', KEY_LSHIFT, '|', /* 0x28 - 0x2B */
+ 'Z', 'X', 'C', 'V', /* 0x2C - 0x2F */
+ 'B', 'N', 'M', '<', /* 0x30 - 0x33 */
+ '>', '?', KEY_RSHIFT, KEY_PRINTSCRN, /* 0x34 - 0x37 */
+ KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, /* 0x38 - 0x3B */
+ KEY_F2, KEY_F3, KEY_F4, KEY_F5, /* 0x3C - 0x3F */
+ KEY_F6, KEY_F7, KEY_F8, KEY_F9, /* 0x40 - 0x43 */
+ KEY_F10, KEY_NUMLOCK, KEY_SCRLOCK, KEY_KPHOME, /* 0x44 - 0x47 */
+ KEY_KPUP, KEY_KPPGUP, KEY_KPMINUS, KEY_KPLEFT, /* 0x48 - 0x4B */
+ KEY_KPCENTER, KEY_KPRIGHT, KEY_KPPLUS, KEY_KPEND, /* 0x4C - 0x4F */
+ KEY_KPDOWN, KEY_KPPGDN, KEY_KPINSERT, KEY_KPDEL, /* 0x50 - 0x53 */
+ KEY_SYSREQ, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, /* 0x54 - 0x57 */
+};
+
+static __inline__ bool Is_Queue_Empty(void)
+{
+ return s_queueHead == s_queueTail;
+}
+
+static __inline__ bool Is_Queue_Full(void)
+{
+ return NEXT(s_queueTail) == s_queueHead;
+}
+
+static __inline__ void Enqueue_Keycode(Keycode keycode)
+{
+ if (!Is_Queue_Full()) {
+ s_queue[ s_queueTail ] = keycode;
+ s_queueTail = NEXT(s_queueTail);
+ }
+}
+
+static __inline__ Keycode Dequeue_Keycode(void)
+{
+ Keycode result;
+ KASSERT(!Is_Queue_Empty());
+ result = s_queue[ s_queueHead ];
+ s_queueHead = NEXT(s_queueHead);
+ return result;
+}
+
+/*
+ * Handler for keyboard interrupts.
+ */
+static void Keyboard_Interrupt_Handler(struct Interrupt_State* state)
+{
+ uchar_t status, scanCode;
+ unsigned flag = 0;
+ bool release = false, shift;
+ Keycode keycode;
+
+ Begin_IRQ(state);
+
+ status = In_Byte(KB_CMD);
+ IO_Delay();
+
+ if ((status & KB_OUTPUT_FULL) != 0) {
+ /* There is a byte available */
+ scanCode = In_Byte(KB_DATA);
+ IO_Delay();
+/*
+ * Print("code=%x%s\n", scanCode, (scanCode&0x80) ? " [release]" : "");
+ */
+
+ if (scanCode & KB_KEY_RELEASE) {
+ release = true;
+ scanCode &= ~(KB_KEY_RELEASE);
+ }
+
+ if (scanCode >= SCAN_TABLE_SIZE) {
+ Print("Unknown scan code: %x\n", scanCode);
+ goto done;
+ }
+
+ /* Process the key */
+ shift = ((s_shiftState & SHIFT_MASK) != 0);
+ keycode = shift ? s_scanTableWithShift[scanCode] : s_scanTableNoShift[scanCode];
+
+ /* Update shift, control and alt state */
+ switch (keycode) {
+ case KEY_LSHIFT:
+ flag = LEFT_SHIFT;
+ break;
+ case KEY_RSHIFT:
+ flag = RIGHT_SHIFT;
+ break;
+ case KEY_LCTRL:
+ flag = LEFT_CTRL;
+ break;
+ case KEY_RCTRL:
+ flag = RIGHT_CTRL;
+ break;
+ case KEY_LALT:
+ flag = LEFT_ALT;
+ break;
+ case KEY_RALT:
+ flag = RIGHT_ALT;
+ break;
+ default:
+ goto noflagchange;
+ }
+
+ if (release)
+ s_shiftState &= ~(flag);
+ else
+ s_shiftState |= flag;
+
+ /*
+ * Shift, control and alt keys don't have to be
+ * queued, flags will be set!
+ */
+ goto done;
+
+noflagchange:
+ /* Format the new keycode */
+ if (shift)
+ keycode |= KEY_SHIFT_FLAG;
+ if ((s_shiftState & CTRL_MASK) != 0)
+ keycode |= KEY_CTRL_FLAG;
+ if ((s_shiftState & ALT_MASK) != 0)
+ keycode |= KEY_ALT_FLAG;
+ if (release)
+ keycode |= KEY_RELEASE_FLAG;
+
+ /* Put the keycode in the buffer */
+ Enqueue_Keycode(keycode);
+
+ /* Wake up event consumers */
+ Wake_Up(&s_waitQueue);
+
+ /*
+ * Pick a new thread upon return from interrupt
+ * (hopefully the one waiting for the keyboard event)
+ */
+ g_needReschedule = true;
+ }
+
+done:
+ End_IRQ(state);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+void Init_Keyboard(void)
+{
+ ushort_t irqMask;
+
+ Print("Initializing keyboard...\n");
+
+ /* Start out with no shift keys enabled. */
+ s_shiftState = 0;
+
+ /* Buffer is initially empty. */
+ s_queueHead = s_queueTail = 0;
+
+ /* Install interrupt handler */
+ Install_IRQ(KB_IRQ, Keyboard_Interrupt_Handler);
+
+ /* Enable IRQ1 (keyboard) */
+ irqMask = Get_IRQ_Mask();
+ irqMask &= ~(1 << KB_IRQ);
+ Set_IRQ_Mask(irqMask);
+}
+
+/*
+ * Poll for a key event.
+ * Returns true if a key is available,
+ * false if not. If a key event is available,
+ * it will be stored in the location pointed to
+ * by keycode.
+ */
+bool Read_Key(Keycode* keycode)
+{
+ bool result, iflag;
+
+ iflag = Begin_Int_Atomic();
+
+ result = !Is_Queue_Empty();
+ if (result) {
+ *keycode = Dequeue_Keycode();
+ }
+
+ End_Int_Atomic(iflag);
+
+ return result;
+}
+
+/*
+ * Wait for a keycode to arrive.
+ * Uses the keyboard wait queue to sleep until
+ * a keycode arrives.
+ */
+Keycode Wait_For_Key(void)
+{
+ bool gotKey, iflag;
+ Keycode keycode = KEY_UNKNOWN;
+
+ iflag = Begin_Int_Atomic();
+
+ do {
+ gotKey = !Is_Queue_Empty();
+ if (gotKey)
+ keycode = Dequeue_Keycode();
+ else
+ Wait(&s_waitQueue);
+ }
+ while (!gotKey);
+
+ End_Int_Atomic(iflag);
+
+ return keycode;
+}
--- /dev/null
+/*
+ * Kernel threads
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/defs.h>
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/mem.h>
+#include <geekos/symbol.h>
+#include <geekos/string.h>
+#include <geekos/kthread.h>
+#include <geekos/malloc.h>
+#include <geekos/serial.h>
+
+/* ----------------------------------------------------------------------
+ * Private data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * List of all threads in the system.
+ */
+static struct All_Thread_List s_allThreadList;
+
+/*
+ * Queue of runnable threads.
+ */
+static struct Thread_Queue s_runQueue;
+
+/*
+ * Current thread.
+ */
+struct Kernel_Thread* g_currentThread;
+
+/*
+ * Boolean flag indicating that we need to choose a new runnable thread.
+ * It is checked by the interrupt return code (Handle_Interrupt,
+ * in lowlevel.asm) before returning from an interrupt.
+ */
+int g_needReschedule;
+
+/*
+ * Boolean flag indicating that preemption is disabled.
+ * When set, external interrupts (such as the timer tick)
+ * will not cause a new thread to be selected.
+ */
+volatile int g_preemptionDisabled;
+
+/*
+ * Queue of finished threads needing disposal,
+ * and a wait queue used for communication between exited threads
+ * and the reaper thread.
+ */
+static struct Thread_Queue s_graveyardQueue;
+static struct Thread_Queue s_reaperWaitQueue;
+
+/*
+ * Counter for keys that access thread-local data, and an array
+ * of destructors for freeing that data when the thread dies. This is
+ * based on POSIX threads' thread-specific data functionality.
+ */
+static unsigned int s_tlocalKeyCounter = 0;
+static tlocal_destructor_t s_tlocalDestructors[MAX_TLOCAL_KEYS];
+
+/* ----------------------------------------------------------------------
+ * Private functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize a new Kernel_Thread.
+ */
+static void Init_Thread(struct Kernel_Thread* kthread, void* stackPage,
+ int priority, bool detached)
+{
+ static int nextFreePid = 1;
+
+ struct Kernel_Thread* owner = detached ? (struct Kernel_Thread*)0 : g_currentThread;
+
+ memset(kthread, '\0', sizeof(*kthread));
+ kthread->stackPage = stackPage;
+ kthread->esp = ((ulong_t) kthread->stackPage) + PAGE_SIZE;
+ kthread->numTicks = 0;
+ kthread->priority = priority;
+ kthread->userContext = 0;
+ kthread->owner = owner;
+
+ /*
+ * The thread has an implicit self-reference.
+ * If the thread is not detached, then its owner
+ * also has a reference to it.
+ */
+ kthread->refCount = detached ? 1 : 2;
+
+ kthread->alive = true;
+ Clear_Thread_Queue(&kthread->joinQueue);
+ kthread->pid = nextFreePid++;
+
+}
+
+/*
+ * Create a new raw thread object.
+ * Returns a null pointer if there isn't enough memory.
+ */
+static struct Kernel_Thread* Create_Thread(int priority, bool detached)
+{
+ struct Kernel_Thread* kthread;
+ void* stackPage = 0;
+
+ /*
+ * For now, just allocate one page each for the thread context
+ * object and the thread's stack.
+ */
+ kthread = Alloc_Page();
+ if (kthread != 0)
+ stackPage = Alloc_Page();
+
+ /* Make sure that the memory allocations succeeded. */
+ if (kthread == 0)
+ return 0;
+ if (stackPage == 0) {
+ Free_Page(kthread);
+ return 0;
+ }
+
+ /*Print("New thread @ %x, stack @ %x\n", kthread, stackPage); */
+
+ /*
+ * Initialize the stack pointer of the new thread
+ * and accounting info
+ */
+ Init_Thread(kthread, stackPage, priority, detached);
+
+ /* Add to the list of all threads in the system. */
+ Add_To_Back_Of_All_Thread_List(&s_allThreadList, kthread);
+
+ return kthread;
+}
+
+/*
+ * Push a dword value on the stack of given thread.
+ * We use this to set up some context for the thread before
+ * we make it runnable.
+ */
+static __inline__ void Push(struct Kernel_Thread* kthread, ulong_t value)
+{
+ kthread->esp -= 4;
+ *((ulong_t *) kthread->esp) = value;
+}
+
+/*
+ * Destroy given thread.
+ * This function should perform all cleanup needed to
+ * reclaim the resources used by a thread.
+ * Called with interrupts enabled.
+ */
+static void Destroy_Thread(struct Kernel_Thread* kthread)
+{
+
+ /* Dispose of the thread's memory. */
+ Disable_Interrupts();
+ Free_Page(kthread->stackPage);
+ Free_Page(kthread);
+
+ /* Remove from list of all threads */
+ Remove_From_All_Thread_List(&s_allThreadList, kthread);
+
+ Enable_Interrupts();
+
+}
+
+/*
+ * Hand given thread to the reaper for destruction.
+ * Must be called with interrupts disabled!
+ */
+static void Reap_Thread(struct Kernel_Thread* kthread)
+{
+ KASSERT(!Interrupts_Enabled());
+ Enqueue_Thread(&s_graveyardQueue, kthread);
+ Wake_Up(&s_reaperWaitQueue);
+}
+
+/*
+ * Called when a reference to the thread is broken.
+ */
+static void Detach_Thread(struct Kernel_Thread* kthread)
+{
+ KASSERT(!Interrupts_Enabled());
+ KASSERT(kthread->refCount > 0);
+
+ --kthread->refCount;
+ if (kthread->refCount == 0) {
+ Reap_Thread(kthread);
+ }
+}
+
+/*
+ * This function performs any needed initialization before
+ * a thread start function is executed. Currently we just use
+ * it to enable interrupts (since Schedule() always activates
+ * a thread with interrupts disabled).
+ */
+static void Launch_Thread(void)
+{
+ Enable_Interrupts();
+}
+
+/*
+ * Push initial values for general purpose registers.
+ */
+static void Push_General_Registers(struct Kernel_Thread* kthread)
+{
+ /*
+ * Push initial values for saved general-purpose registers.
+ * (The actual values are not important.)
+ */
+ Push(kthread, 0); /* eax */
+ Push(kthread, 0); /* ebx */
+ Push(kthread, 0); /* edx */
+ Push(kthread, 0); /* edx */
+ Push(kthread, 0); /* esi */
+ Push(kthread, 0); /* edi */
+ Push(kthread, 0); /* ebp */
+}
+
+/*
+ * Shutdown a kernel thread.
+ * This is called if a kernel thread exits by falling off
+ * the end of its start function.
+ */
+static void Shutdown_Thread(void)
+{
+ Exit(0);
+}
+
+/*
+ * Set up the initial context for a kernel-mode-only thread.
+ */
+static void Setup_Kernel_Thread(
+ struct Kernel_Thread* kthread,
+ Thread_Start_Func startFunc,
+ ulong_t arg)
+{
+ /*
+ * Push the argument to the thread start function, and the
+ * return address (the Shutdown_Thread function, so the thread will
+ * go away cleanly when the start function returns).
+ */
+ Push(kthread, arg);
+ Push(kthread, (ulong_t) &Shutdown_Thread);
+
+ /* Push the address of the start function. */
+ Push(kthread, (ulong_t) startFunc);
+
+ /*
+ * To make the thread schedulable, we need to make it look
+ * like it was suspended by an interrupt. This means pushing
+ * an "eflags, cs, eip" sequence onto the stack,
+ * as well as int num, error code, saved registers, etc.
+ */
+
+ /*
+ * The EFLAGS register will have all bits clear.
+ * The important constraint is that we want to have the IF
+ * bit clear, so that interrupts are disabled when the
+ * thread starts.
+ */
+ Push(kthread, 0UL); /* EFLAGS */
+
+ /*
+ * As the "return address" specifying where the new thread will
+ * start executing, use the Launch_Thread() function.
+ */
+ Push(kthread, KERNEL_CS);
+ Push(kthread, (ulong_t) &Launch_Thread);
+
+ /* Push fake error code and interrupt number. */
+ Push(kthread, 0);
+ Push(kthread, 0);
+
+ /* Push initial values for general-purpose registers. */
+ Push_General_Registers(kthread);
+
+ /*
+ * Push values for saved segment registers.
+ * Only the ds and es registers will contain valid selectors.
+ * The fs and gs registers are not used by any instruction
+ * generated by gcc.
+ */
+ Push(kthread, KERNEL_DS); /* ds */
+ Push(kthread, KERNEL_DS); /* es */
+ Push(kthread, 0); /* fs */
+ Push(kthread, 0); /* gs */
+}
+
+
+
+/*
+ * This is the body of the idle thread. Its job is to preserve
+ * the invariant that a runnable thread always exists,
+ * i.e., the run queue is never empty.
+ */
+static void Idle(ulong_t arg)
+{
+ while (true)
+ Yield();
+}
+
+/*
+ * The reaper thread. Its job is to de-allocate memory
+ * used by threads which have finished.
+ */
+static void Reaper(ulong_t arg)
+{
+ struct Kernel_Thread *kthread;
+
+ Disable_Interrupts();
+
+ while (true) {
+ /* See if there are any threads needing disposal. */
+ if ((kthread = s_graveyardQueue.head) == 0) {
+ /* Graveyard is empty, so wait for a thread to die. */
+ Wait(&s_reaperWaitQueue);
+ }
+ else {
+ /* Make the graveyard queue empty. */
+ Clear_Thread_Queue(&s_graveyardQueue);
+
+ /*
+ * Now we can re-enable interrupts, since we
+ * have removed all the threads needing disposal.
+ */
+ Enable_Interrupts();
+ Yield(); /* allow other threads to run? */
+
+ /* Dispose of the dead threads. */
+ while (kthread != 0) {
+ struct Kernel_Thread* next = Get_Next_In_Thread_Queue(kthread);
+#if 0
+ Print("Reaper: disposing of thread @ %x, stack @ %x\n",
+ kthread, kthread->stackPage);
+#endif
+ Destroy_Thread(kthread);
+ kthread = next;
+ }
+
+ /*
+ * Disable interrupts again, since we're going to
+ * do another iteration.
+ */
+ Disable_Interrupts();
+ }
+ }
+}
+
+/*
+ * Find the best (highest priority) thread in given
+ * thread queue. Returns null if queue is empty.
+ */
+static __inline__ struct Kernel_Thread* Find_Best(struct Thread_Queue* queue)
+{
+ /* Pick the highest priority thread */
+ struct Kernel_Thread *kthread = queue->head, *best = 0;
+ while (kthread != 0) {
+ if (best == 0 || kthread->priority > best->priority)
+ best = kthread;
+ kthread = Get_Next_In_Thread_Queue(kthread);
+ }
+ return best;
+}
+
+/*
+ * Acquires pointer to thread-local data from the current thread
+ * indexed by the given key. Assumes interrupts are off.
+ */
+static __inline__ const void** Get_Tlocal_Pointer(tlocal_key_t k)
+{
+ struct Kernel_Thread* current = g_currentThread;
+
+ KASSERT(k < MAX_TLOCAL_KEYS);
+
+ return ¤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; j<MIN_DESTRUCTOR_ITERATIONS; j++) {
+
+ for (i = 0; i<MAX_TLOCAL_KEYS; i++) {
+
+ void *x = (void *)curr->tlocalData[i];
+ if (x != NULL && s_tlocalDestructors[i] != NULL) {
+
+ curr->tlocalData[i] = NULL;
+ called = 1;
+
+ Enable_Interrupts();
+ s_tlocalDestructors[i](x);
+ Disable_Interrupts();
+ }
+ }
+ if (!called) break;
+ }
+}
+
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+void Init_Scheduler(void)
+{
+ struct Kernel_Thread* mainThread = (struct Kernel_Thread *) KERNEL_THREAD_OBJECT;
+
+ PrintBoth("Initializing Scheduler\n");
+
+ /*
+ * Create initial kernel thread context object and stack,
+ * and make them current.
+ */
+ Init_Thread(mainThread, (void *) KERNEL_STACK, PRIORITY_NORMAL, true);
+ g_currentThread = mainThread;
+ Add_To_Back_Of_All_Thread_List(&s_allThreadList, mainThread);
+
+ /*
+ * Create the idle thread.
+ */
+ /*Print("starting idle thread\n");*/
+ Start_Kernel_Thread(Idle, 0, PRIORITY_IDLE, true);
+
+ /*
+ * Create the reaper thread.
+ */
+ /*Print("starting reaper thread\n");*/
+ Start_Kernel_Thread(Reaper, 0, PRIORITY_NORMAL, true);
+}
+
+/*
+ * Start a kernel-mode-only thread, using given function as its body
+ * and passing given argument as its parameter. Returns pointer
+ * to the new thread if successful, null otherwise.
+ *
+ * startFunc - is the function to be called by the new thread
+ * arg - is a paramter to pass to the new function
+ * priority - the priority of this thread (use PRIORITY_NORMAL) for
+ * most things
+ * detached - use false for kernel threads
+ */
+struct Kernel_Thread* Start_Kernel_Thread(
+ Thread_Start_Func startFunc,
+ ulong_t arg,
+ int priority,
+ bool detached
+)
+{
+ struct Kernel_Thread* kthread = Create_Thread(priority, detached);
+ if (kthread != 0) {
+ /*
+ * Create the initial context for the thread to make
+ * it schedulable.
+ */
+ Setup_Kernel_Thread(kthread, startFunc, arg);
+
+
+ /* Atomically put the thread on the run queue. */
+ Make_Runnable_Atomic(kthread);
+ }
+
+ return kthread;
+}
+
+/*
+ * Add given thread to the run queue, so that it
+ * may be scheduled. Must be called with interrupts disabled!
+ */
+void Make_Runnable(struct Kernel_Thread* kthread)
+{
+ KASSERT(!Interrupts_Enabled());
+
+ Enqueue_Thread(&s_runQueue, kthread);
+}
+
+/*
+ * Atomically make a thread runnable.
+ * Assumes interrupts are currently enabled.
+ */
+void Make_Runnable_Atomic(struct Kernel_Thread* kthread)
+{
+ Disable_Interrupts();
+ Make_Runnable(kthread);
+ Enable_Interrupts();
+}
+
+/*
+ * Get the thread that currently has the CPU.
+ */
+struct Kernel_Thread* Get_Current(void)
+{
+ return g_currentThread;
+}
+
+/*
+ * Get the next runnable thread from the run queue.
+ * This is the scheduler.
+ */
+struct Kernel_Thread* Get_Next_Runnable(void)
+{
+ struct Kernel_Thread* best = 0;
+
+ best = Find_Best(&s_runQueue);
+ KASSERT(best != 0);
+ Remove_Thread(&s_runQueue, best);
+
+/*
+ * Print("Scheduling %x\n", best);
+ */
+ return best;
+}
+
+/*
+ * Schedule a thread that is waiting to run.
+ * Must be called with interrupts off!
+ * The current thread should already have been placed
+ * on whatever queue is appropriate (i.e., either the
+ * run queue if it is still runnable, or a wait queue
+ * if it is waiting for an event to occur).
+ */
+void Schedule(void)
+{
+ struct Kernel_Thread* runnable;
+
+ /* Make sure interrupts really are disabled */
+ KASSERT(!Interrupts_Enabled());
+
+ /* Preemption should not be disabled. */
+ KASSERT(!g_preemptionDisabled);
+
+ /* Get next thread to run from the run queue */
+ runnable = Get_Next_Runnable();
+
+ /*
+ * Activate the new thread, saving the context of the current thread.
+ * Eventually, this thread will get re-activated and Switch_To_Thread()
+ * will "return", and then Schedule() will return to wherever
+ * it was called from.
+ */
+ Switch_To_Thread(runnable);
+}
+
+/*
+ * Voluntarily give up the CPU to another thread.
+ * Does nothing if no other threads are ready to run.
+ */
+void Yield(void)
+{
+ Disable_Interrupts();
+ Make_Runnable(g_currentThread);
+ Schedule();
+ Enable_Interrupts();
+}
+
+/*
+ * Exit the current thread.
+ * Calling this function initiates a context switch.
+ */
+void Exit(int exitCode)
+{
+ struct Kernel_Thread* current = g_currentThread;
+
+ if (Interrupts_Enabled())
+ Disable_Interrupts();
+
+ /* Thread is dead */
+ current->exitCode = exitCode;
+ current->alive = false;
+
+ /* Clean up any thread-local memory */
+ Tlocal_Exit(g_currentThread);
+
+ /* Notify the thread's owner, if any */
+ Wake_Up(¤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);
+}
--- /dev/null
+; -*- fundamental -*-
+; Low level interrupt/thread handling code for GeekOS.
+; Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software. You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; This is 32 bit code to be linked into the kernel.
+; It defines low level interrupt handler entry points that we'll use
+; to populate the IDT. It also contains the interrupt handling
+; and thread context switch code.
+
+%include "defs.asm"
+%include "symbol.asm"
+%include "util.asm"
+
+
+[BITS 32]
+
+; ----------------------------------------------------------------------
+; Definitions
+; ----------------------------------------------------------------------
+
+; This is the size of the Interrupt_State struct in int.h
+INTERRUPT_STATE_SIZE equ 64
+
+; Save registers prior to calling a handler function.
+; This must be kept up to date with:
+; - Interrupt_State struct in int.h
+; - Setup_Initial_Thread_Context() in kthread.c
+%macro Save_Registers 0
+ push eax
+ push ebx
+ push ecx
+ push edx
+ push esi
+ push edi
+ push ebp
+ push ds
+ push es
+ push fs
+ push gs
+%endmacro
+
+; Restore registers and clean up the stack after calling a handler function
+; (i.e., just before we return from the interrupt via an iret instruction).
+%macro Restore_Registers 0
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ pop ebp
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ pop eax
+ add esp, 8 ; skip int num and error code
+%endmacro
+
+; Code to activate a new user context (if necessary), before returning
+; to executing a thread. Should be called just before restoring
+; registers (because the interrupt context is used).
+%macro Activate_User_Context 0
+ ; If the new thread has a user context which is not the current
+ ; one, activate it.
+ push esp ; Interrupt_State pointer
+ push dword [g_currentThread] ; Kernel_Thread pointer
+; call Switch_To_User_Context
+ add esp, 8 ; clear 2 arguments
+%endmacro
+
+; Number of bytes between the top of the stack and
+; the interrupt number after the general-purpose and segment
+; registers have been saved.
+REG_SKIP equ (11*4)
+
+; Template for entry point code for interrupts that have
+; an explicit processor-generated error code.
+; The argument is the interrupt number.
+%macro Int_With_Err 1
+align 8
+ push dword %1 ; push interrupt number
+ jmp Handle_Interrupt ; jump to common handler
+%endmacro
+
+; Template for entry point code for interrupts that do not
+; generate an explicit error code. We push a dummy error
+; code on the stack, so the stack layout is the same
+; for all interrupts.
+%macro Int_No_Err 1
+align 8
+ push dword 0 ; fake error code
+ push dword %1 ; push interrupt number
+ jmp Handle_Interrupt ; jump to common handler
+%endmacro
+
+
+; ----------------------------------------------------------------------
+; Symbol imports and exports
+; ----------------------------------------------------------------------
+
+; This symbol is defined in idt.c, and is a table of addresses
+; of C handler functions for interrupts.
+IMPORT g_interruptTable
+
+; Global variable pointing to context struct for current thread.
+IMPORT g_currentThread
+
+; Set to non-zero when we need to choose a new thread
+; in the interrupt return code.
+IMPORT g_needReschedule
+
+; Set to non-zero when preemption is disabled.
+IMPORT g_preemptionDisabled
+
+; This is the function that returns the next runnable thread.
+IMPORT Get_Next_Runnable
+
+; Function to put a thread on the run queue.
+IMPORT Make_Runnable
+
+; Function to activate a new user context (if needed).
+IMPORT Switch_To_User_Context
+
+; Sizes of interrupt handler entry points for interrupts with
+; and without error codes. The code in idt.c uses this
+; information to infer the layout of the table of interrupt
+; handler entry points, without needing a separate linker
+; symbol for each one (which is quite tedious to type :-)
+EXPORT g_handlerSizeNoErr
+EXPORT g_handlerSizeErr
+
+; Simple functions to load the IDTR, GDTR, and LDTR.
+EXPORT Load_IDTR
+EXPORT Load_GDTR
+EXPORT Load_LDTR
+
+; Beginning and end of the table of interrupt entry points.
+EXPORT g_entryPointTableStart
+EXPORT g_entryPointTableEnd
+
+; Thread context switch function.
+EXPORT Switch_To_Thread
+
+; Return current value of eflags register.
+EXPORT Get_Current_EFLAGS
+
+; Return the return address
+EXPORT Get_EIP
+; ESP
+EXPORT Get_ESP
+; EBP
+EXPORT Get_EBP
+
+; Virtual memory support.
+EXPORT Enable_Paging
+EXPORT Set_PDBR
+EXPORT Get_PDBR
+EXPORT Flush_TLB
+
+; CPUID functions
+EXPORT cpuid_ecx
+EXPORT cpuid_eax
+
+; Utility Functions
+EXPORT Set_MSR
+EXPORT Get_MSR
+
+EXPORT Get_CR2
+
+
+EXPORT Proc_test
+
+; ----------------------------------------------------------------------
+; Code
+; ----------------------------------------------------------------------
+
+[SECTION .text]
+
+; Load IDTR with 6-byte pointer whose address is passed as
+; the parameter.
+align 8
+Load_IDTR:
+ mov eax, [esp+4]
+ lidt [eax]
+ ret
+
+; Load the GDTR with 6-byte pointer whose address is
+; passed as the parameter. Assumes that interrupts
+; are disabled.
+align 8
+Load_GDTR:
+ mov eax, [esp+4]
+ lgdt [eax]
+ ; Reload segment registers
+ mov ax, KERNEL_DS
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+ jmp KERNEL_CS:.here
+.here:
+ ret
+
+; Load the LDT whose selector is passed as a parameter.
+align 8
+Load_LDTR:
+ mov eax, [esp+4]
+ lldt ax
+ ret
+
+;
+; Start paging
+; load crt3 with the passed page directory pointer
+; enable paging bit in cr2
+align 8
+Enable_Paging:
+ push ebp
+ mov ebp, esp
+ push eax
+ push ebx
+ mov eax, [ebp+8]
+ mov cr3, eax
+ mov eax, cr3
+ mov cr3, eax
+ mov ebx, cr0
+ or ebx, 0x80000000
+ mov cr0, ebx
+ pop ebx
+ pop eax
+ pop ebp
+ ret
+
+
+
+
+
+;
+; Change PDBR
+; load cr3 with the passed page directory pointer
+align 8
+Set_PDBR:
+ mov eax, [esp+4]
+ mov cr3, eax
+ ret
+
+;
+; Get the current PDBR.
+; This is useful for lazily switching address spaces;
+; only switch if the new thread has a different address space.
+;
+align 8
+Get_PDBR:
+ mov eax, cr3
+ ret
+
+;
+; Flush TLB - just need to re-load cr3 to force this to happen
+;
+align 8
+Flush_TLB:
+ mov eax, cr3
+ mov cr3, eax
+ ret
+
+
+;
+; cpuid_ecx - return the ecx register from cpuid
+;
+align 8
+cpuid_ecx:
+ push ebp
+ mov ebp, esp
+ push ecx
+ mov eax, [ebp + 8]
+ cpuid
+ mov eax, ecx
+ pop ecx
+ pop ebp
+ ret
+
+;
+; cpuid_eax - return the eax register from cpuid
+;
+align 8
+cpuid_eax:
+ mov eax, [esp+4]
+ cpuid
+ ret
+
+;
+; Set_MSR - Set the value of a given MSR
+;
+align 8
+Set_MSR:
+ push ebp
+ mov ebp, esp
+ pusha
+ mov eax, [ebp+16]
+ mov edx, [ebp+12]
+ mov ecx, [ebp+8]
+ wrmsr
+ popa
+ pop ebp
+ ret
+
+
+
+;
+; Get_MSR - Get the value of a given MSR
+; void Get_MSR(int MSR, void * high_byte, void * low_byte);
+;
+align 8
+Get_MSR:
+ push ebp
+ mov ebp, esp
+ pusha
+ mov ecx, [ebp+8]
+ rdmsr
+ mov ebx, [ebp+12]
+ mov [ebx], edx
+ mov ebx, [ebp+16]
+ mov [ebx], eax
+ popa
+ pop ebp
+ ret
+
+
+
+align 8
+Get_CR2:
+ mov eax, cr2
+ ret
+
+
+align 8
+Proc_test:
+ push ebp
+ mov ebp, esp
+ push ebx
+ mov ebx, [ebp + 8]
+ mov eax, [ebp + 12]
+
+ add eax, ebx
+ pop ebx
+ pop ebp
+ ret
+
+
+; Common interrupt handling code.
+; Save registers, call C handler function,
+; possibly choose a new thread to run, restore
+; registers, return from the interrupt.
+align 8
+Handle_Interrupt:
+ ; Save registers (general purpose and segment)
+ Save_Registers
+
+ ; Ensure that we're using the kernel data segment
+ mov ax, KERNEL_DS
+ mov ds, ax
+ mov es, ax
+
+ ; Get the address of the C handler function from the
+ ; table of handler functions.
+ mov eax, g_interruptTable ; get address of handler table
+ mov esi, [esp+REG_SKIP] ; get interrupt number
+ mov ebx, [eax+esi*4] ; get address of handler function
+
+ ; Call the handler.
+ ; The argument passed is a pointer to an Interrupt_State struct,
+ ; which describes the stack layout for all interrupts.
+ push esp
+ call ebx
+ add esp, 4 ; clear 1 argument
+
+ ; If preemption is disabled, then the current thread
+ ; keeps running.
+ cmp [g_preemptionDisabled], dword 0
+ jne .restore
+
+ ; See if we need to choose a new thread to run.
+ cmp [g_needReschedule], dword 0
+ je .restore
+
+ ; Put current thread back on the run queue
+ push dword [g_currentThread]
+ call Make_Runnable
+ add esp, 4 ; clear 1 argument
+
+ ; Save stack pointer in current thread context, and
+ ; clear numTicks field.
+ mov eax, [g_currentThread]
+ mov [eax+0], esp ; esp field
+ mov [eax+4], dword 0 ; numTicks field
+
+ ; Pick a new thread to run, and switch to its stack
+ call Get_Next_Runnable
+ mov [g_currentThread], eax
+ mov esp, [eax+0] ; esp field
+
+ ; Clear "need reschedule" flag
+ mov [g_needReschedule], dword 0
+
+.restore:
+ ; Activate the user context, if necessary.
+ Activate_User_Context
+
+ ; Restore registers
+ Restore_Registers
+
+ ; Return from the interrupt.
+ iret
+
+; ----------------------------------------------------------------------
+; Switch_To_Thread()
+; Save context of currently executing thread, and activate
+; the thread whose context object is passed as a parameter.
+;
+; Parameter:
+; - ptr to Kernel_Thread whose state should be restored and made active
+;
+; Notes:
+; Called with interrupts disabled.
+; This must be kept up to date with definition of Kernel_Thread
+; struct, in kthread.h.
+; ----------------------------------------------------------------------
+align 16
+Switch_To_Thread:
+ ; Modify the stack to allow a later return via an iret instruction.
+ ; We start with a stack that looks like this:
+ ;
+ ; thread_ptr
+ ; esp --> return addr
+ ;
+ ; We change it to look like this:
+ ;
+ ; thread_ptr
+ ; eflags
+ ; cs
+ ; esp --> return addr
+
+ push eax ; save eax
+ mov eax, [esp+4] ; get return address
+ mov [esp-4], eax ; move return addr down 8 bytes from orig loc
+ add esp, 8 ; move stack ptr up
+ pushfd ; put eflags where return address was
+ mov eax, [esp-4] ; restore saved value of eax
+ push dword KERNEL_CS ; push cs selector
+ sub esp, 4 ; point stack ptr at return address
+
+ ; Push fake error code and interrupt number
+ push dword 0
+ push dword 0
+
+ ; Save general purpose registers.
+ Save_Registers
+
+ ; Save stack pointer in the thread context struct (at offset 0).
+ mov eax, [g_currentThread]
+ mov [eax+0], esp
+
+ ; Clear numTicks field in thread context, since this
+ ; thread is being suspended.
+ mov [eax+4], dword 0
+
+ ; Load the pointer to the new thread context into eax.
+ ; We skip over the Interrupt_State struct on the stack to
+ ; get the parameter.
+ mov eax, [esp+INTERRUPT_STATE_SIZE]
+
+ ; Make the new thread current, and switch to its stack.
+ mov [g_currentThread], eax
+ mov esp, [eax+0]
+
+ ; Activate the user context, if necessary.
+ Activate_User_Context
+
+ ; Restore general purpose and segment registers, and clear interrupt
+ ; number and error code.
+ Restore_Registers
+
+ ; We'll return to the place where the thread was
+ ; executing last.
+ iret
+
+; Return current contents of eflags register.
+align 16
+Get_Current_EFLAGS:
+ pushfd ; push eflags
+ pop eax ; pop contents into eax
+ ret
+
+
+
+; Return the eip of the next instruction after the caller
+align 16
+Get_EIP:
+ mov eax, [esp]
+ ret
+
+; Return the current esp in the procedure
+align 16
+Get_ESP:
+ mov eax, esp
+ ret
+
+; Return the current ebp in the procedure
+align 16
+Get_EBP:
+ mov eax, ebp
+ ret
+
+
+
+; ----------------------------------------------------------------------
+; Generate interrupt-specific entry points for all interrupts.
+; We also define symbols to indicate the extend of the table
+; of entry points, and the size of individual entry points.
+; ----------------------------------------------------------------------
+align 8
+g_entryPointTableStart:
+
+; Handlers for processor-generated exceptions, as defined by
+; Intel 486 manual.
+Int_No_Err 0
+align 8
+Before_No_Err:
+Int_No_Err 1
+align 8
+After_No_Err:
+Int_No_Err 2 ; FIXME: not described in 486 manual
+Int_No_Err 3
+Int_No_Err 4
+Int_No_Err 5
+Int_No_Err 6
+Int_No_Err 7
+align 8
+Before_Err:
+Int_With_Err 8
+align 8
+After_Err:
+Int_No_Err 9 ; FIXME: not described in 486 manual
+Int_With_Err 10
+Int_With_Err 11
+Int_With_Err 12
+Int_With_Err 13
+Int_With_Err 14
+Int_No_Err 15 ; FIXME: not described in 486 manual
+Int_No_Err 16
+Int_With_Err 17
+
+; The remaining interrupts (18 - 255) do not have error codes.
+; We can generate them all in one go with nasm's %rep construct.
+%assign intNum 18
+%rep (256 - 18)
+Int_No_Err intNum
+%assign intNum intNum+1
+%endrep
+
+align 8
+g_entryPointTableEnd:
+
+[SECTION .data]
+
+; Exported symbols defining the size of handler entry points
+; (both with and without error codes).
+align 4
+g_handlerSizeNoErr: dd (After_No_Err - Before_No_Err)
+align 4
+g_handlerSizeErr: dd (After_Err - Before_Err)
--- /dev/null
+/*
+ * GeekOS C code entry point
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2004, Iulian Neamtiu <neamtiu@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/bootinfo.h>
+#include <geekos/string.h>
+#include <geekos/screen.h>
+#include <geekos/mem.h>
+#include <geekos/crc32.h>
+#include <geekos/tss.h>
+#include <geekos/int.h>
+#include <geekos/kthread.h>
+#include <geekos/trap.h>
+#include <geekos/timer.h>
+#include <geekos/keyboard.h>
+#include <geekos/io.h>
+#include <geekos/serial.h>
+#include <geekos/reboot.h>
+#include <geekos/mem.h>
+#include <geekos/paging.h>
+#include <geekos/ide.h>
+
+#include <geekos/gdt.h>
+
+
+/*
+ static inline unsigned int cpuid_ecx(unsigned int op)
+ {
+ unsigned int eax, ecx;
+
+ __asm__("cpuid"
+ : "=a" (eax), "=c" (ecx)
+ : "0" (op)
+ : "bx", "dx" );
+
+ return ecx;
+ }
+*/
+
+
+
+extern void Get_MSR(ulong_t msr, unsigned int *val1, unsigned int *val2);
+extern void Set_MSR(ulong_t msr, ulong_t val1, ulong_t val2);
+extern uint_t Get_EIP();
+extern uint_t Get_ESP();
+extern uint_t Get_EBP();
+
+
+int foo=42;
+
+#define SPEAKER_PORT 0x61
+
+
+void Buzz(unsigned delay, unsigned num)
+{
+ volatile int x;
+ int i,j;
+ unsigned char init;
+
+ init=In_Byte(SPEAKER_PORT);
+
+ for (i=0;i<num;i++) {
+ Out_Byte(SPEAKER_PORT, init|0x2);
+ for (j=0;j<delay;j++) {
+ x+=j;
+ }
+ Out_Byte(SPEAKER_PORT, init);
+ for (j=0;j<delay;j++) {
+ x+=j;
+ }
+ }
+}
+
+inline void MyOut_Byte(ushort_t port, uchar_t value)
+{
+ __asm__ __volatile__ (
+ "outb %b0, %w1"
+ :
+ : "a" (value), "Nd" (port)
+ );
+}
+
+/*
+ * Read a byte from an I/O port.
+ */
+inline uchar_t MyIn_Byte(ushort_t port)
+{
+ uchar_t value;
+
+ __asm__ __volatile__ (
+ "inb %w1, %b0"
+ : "=a" (value)
+ : "Nd" (port)
+ );
+
+ return value;
+}
+
+
+extern void MyBuzzVM();
+
+#define MYBUZZVM_START MyBuzzVM
+#define MYBUZZVM_LEN 0x3d
+
+void BuzzVM()
+{
+ int x;
+ int j;
+ unsigned char init;
+
+
+ init=MyIn_Byte(SPEAKER_PORT);
+
+ while (1) {
+ MyOut_Byte(SPEAKER_PORT, init|0x2);
+ for (j=0;j<1000000;j++) {
+ x+=j;
+ }
+ MyOut_Byte(SPEAKER_PORT, init);
+ for (j=0;j<1000000;j++) {
+ x+=j;
+ }
+ }
+}
+
+
+
+extern void RunVM();
+
+int vmRunning = 0;
+
+void RunVM() {
+ vmRunning = 1;
+
+ while(1);
+}
+
+
+
+
+
+void Buzzer(ulong_t arg) {
+ ulong_t *doIBuzz = (ulong_t*)arg;
+ while (1) {
+ // Quick and dirty hack to save my hearing...
+ // I'm not too worried about timing, so I'll deal with concurrency later...
+ if (*doIBuzz == 1) {
+ Buzz(1000000, 10);
+ }
+ }
+
+}
+
+
+
+void Hello(ulong_t arg)
+{
+ char *b="hello ";
+ char byte;
+ short port=0xe9;
+ int i;
+ while(1){
+ for (i=0;i<6;i++) {
+ byte=b[i];
+ __asm__ __volatile__ ("outb %b0, %w1" : : "a"(byte), "Nd"(port) );
+ }
+ }
+}
+
+void Keyboard_Listener(ulong_t arg) {
+ ulong_t * doIBuzz = (ulong_t*)arg;
+ Keycode key_press;
+
+ Print("Press F4 to turn on/off the speaker\n");
+
+ while ((key_press = Wait_For_Key())) {
+ if (key_press == KEY_F4) {
+ Print("\nToggling Speaker Port\n");
+ SerialPrintLevel(100,"\nToggling Speaker Port\n");
+ *doIBuzz = (*doIBuzz + 1) % 2;
+ } else if (key_press == KEY_F5) {
+ Print("\nMachine Restart\n");
+ SerialPrintLevel(100,"\nMachine Restart\n");
+ machine_real_restart();
+ }
+ }
+ return;
+}
+
+
+
+extern char BSS_START, BSS_END;
+
+extern char end;
+
+
+
+
+int AllocateAndMapPagesForRange(uint_t start, uint_t length, pte_t template_pte)
+{
+ uint_t address;
+
+ for (address=start;address<start+length;address+=PAGE_SIZE) {
+ void *page;
+ pte_t pte = template_pte;
+
+ page=Alloc_Page();
+ KASSERT(page);
+
+ pte.pageBaseAddr=PAGE_ALLIGNED_ADDR(page);
+
+ KASSERT(MapPage((void*)address,&pte,1));
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Kernel C code entry point.
+ * Initializes kernel subsystems, mounts filesystems,
+ * and spawns init process.
+ */
+void Main(struct Boot_Info* bootInfo)
+{
+ struct Kernel_Thread * key_thread;
+ struct Kernel_Thread * spkr_thread;
+
+
+
+ ulong_t doIBuzz = 0;
+
+ Init_BSS();
+ Init_Screen();
+ InitSerial();
+ Init_Mem(bootInfo);
+ Init_CRC32();
+ Init_TSS();
+ Init_Interrupts();
+ Init_Scheduler();
+ Init_Traps();
+ Init_Timer();
+ Init_Keyboard();
+ Init_VM(bootInfo);
+ Init_Paging();
+
+ // Init_IDE();
+
+
+
+
+
+
+
+
+ SerialPrint("\n\nHello, Welcome to this horrid output-only serial interface\n");
+ SerialPrint("Eventually, this will let us control the VMM\n\n");
+
+ SerialPrint("\n\n===>");
+
+
+
+
+
+
+ SerialPrintLevel(1000,"Launching Noisemaker and keyboard listener threads\n");
+
+ key_thread = Start_Kernel_Thread(Keyboard_Listener, (ulong_t)&doIBuzz, PRIORITY_NORMAL, false);
+ spkr_thread = Start_Kernel_Thread(Buzzer, (ulong_t)&doIBuzz, PRIORITY_NORMAL, false);
+
+
+
+
+
+ SerialPrintLevel(1000,"Next: setup GDT\n");
+
+
+ while(1);
+
+ /* Now this thread is done. */
+ Exit(0);
+}
--- /dev/null
+/*
+ * GeekOS memory allocation API
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/bget.h>
+#include <geekos/kassert.h>
+#include <geekos/malloc.h>
+
+/*
+ * Initialize the heap starting at given address and occupying
+ * specified number of bytes.
+ */
+void Init_Heap(ulong_t start, ulong_t size)
+{
+ /*Print("Creating kernel heap: start=%lx, size=%ld\n", start, size);*/
+ bpool((void*) start, size);
+}
+
+/*
+ * Dynamically allocate a buffer of given size.
+ * Returns null if there is not enough memory to satisfy the
+ * allocation.
+ */
+void* Malloc(ulong_t size)
+{
+ void *result;
+ bool iflag;
+
+ KASSERT(size > 0);
+
+ iflag = Begin_Int_Atomic();
+ result = bget(size);
+ End_Int_Atomic(iflag);
+
+ return result;
+}
+
+/*
+ * Free a buffer allocated with Malloc() or Malloc().
+ */
+void Free(void* buf)
+{
+ bool iflag;
+
+ iflag = Begin_Int_Atomic();
+ brel(buf);
+ End_Int_Atomic(iflag);
+}
--- /dev/null
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/defs.h>
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/bootinfo.h>
+#include <geekos/gdt.h>
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/paging.h>
+#include <geekos/mem.h>
+
+/* ----------------------------------------------------------------------
+ * Global data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * List of Page structures representing each page of physical memory.
+ */
+struct Page* g_pageList;
+
+/*
+ * Number of pages currently available on the freelist.
+ */
+uint_t g_freePageCount = 0;
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Defined in paging.c
+ */
+extern int debugFaults;
+#define Debug(args...) if (debugFaults) Print(args)
+
+/*
+ * List of pages available for allocation.
+ */
+static struct Page_List s_freeList;
+
+/*
+ * Total number of physical pages.
+ */
+int unsigned s_numPages;
+
+/*
+ * Add a range of pages to the inventory of physical memory.
+ */
+static void Add_Page_Range(ulong_t start, ulong_t end, int flags)
+{
+ ulong_t addr;
+
+ KASSERT(Is_Page_Multiple(start));
+ KASSERT(Is_Page_Multiple(end));
+ KASSERT(start < end);
+
+ for (addr = start; addr < end; addr += PAGE_SIZE) {
+ struct Page *page = Get_Page(addr);
+
+ page->flags = flags;
+
+ if (flags == PAGE_AVAIL) {
+ /* Add the page to the freelist */
+ Add_To_Back_Of_Page_List(&s_freeList, page);
+
+ /* Update free page count */
+ ++g_freePageCount;
+ } else {
+ Set_Next_In_Page_List(page, 0);
+ Set_Prev_In_Page_List(page, 0);
+ }
+
+ page->clock = 0;
+ page->vaddr = 0;
+ page->entry = 0;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * The linker defines this symbol to indicate the end of
+ * the executable image.
+ */
+extern char end;
+
+/*
+ * Initialize memory management data structures.
+ * Enables the use of Alloc_Page() and Free_Page() functions.
+ */
+void Init_Mem(struct Boot_Info* bootInfo)
+{
+ ulong_t numPages = bootInfo->memSizeKB >> 2;
+ ulong_t endOfMem = numPages * PAGE_SIZE;
+ unsigned numPageListBytes = sizeof(struct Page) * numPages;
+ ulong_t pageListAddr;
+ ulong_t kernEnd;
+
+ KASSERT(bootInfo->memSizeKB > 0);
+
+ /*
+ * Before we do anything, switch from setup.asm's temporary GDT
+ * to the kernel's permanent GDT.
+ */
+ Init_GDT();
+
+ /*
+ * We'll put the list of Page objects right after the end
+ * of the kernel, and mark it as "kernel". This will bootstrap
+ * us sufficiently that we can start allocating pages and
+ * keeping track of them.
+ */
+ pageListAddr = Round_Up_To_Page((ulong_t) &end);
+ g_pageList = (struct Page*) pageListAddr;
+ kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
+ s_numPages = numPages;
+
+
+
+ /*
+ * The initial kernel thread and its stack are placed
+ * just beyond the ISA hole.
+ */
+ //KASSERT(ISA_HOLE_END == KERN_THREAD_OBJ);
+ //KASSERT(KERN_STACK == KERN_THREAD_OBJ + PAGE_SIZE);
+
+ /*
+ * Memory looks like this:
+ * 0 - start: available (might want to preserve BIOS data area)
+ * start - end: kernel
+ * end - ISA_HOLE_START: available
+ * ISA_HOLE_START - ISA_HOLE_END: used by hardware (and ROM BIOS?)
+ * ISA_HOLE_END - HIGHMEM_START: used by initial kernel thread
+ * HIGHMEM_START - end of memory: available
+ * (the kernel heap is located at HIGHMEM_START; any unused memory
+ * beyond that is added to the freelist)
+ */
+
+ /* JRL CHANGE
+ * 0 - PAGE_SIZE: Unused
+ * PAGE_SIZE - (PAGE_SIZE+8192): init kernel thread stack
+ * (PAGE_SIZE+8192) - ISA_HOLE_START: Available
+ * ISA_HOLE_START - ISA_HOLE_END: HW
+ * ISA_HOLE_END - end: Kernel+PageList
+ * end - (end+1MB): heap
+ * (end+1MB) - end of mem: free
+ *
+ *
+ */
+
+
+ Add_Page_Range(0, PAGE_SIZE, PAGE_UNUSED);
+ // Add_Page_Range(PAGE_SIZE, KERNEL_START_ADDR, PAGE_AVAIL);
+ //Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN);
+ //Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL);
+ Add_Page_Range(PAGE_SIZE, PAGE_SIZE + 8192, PAGE_ALLOCATED);
+ Add_Page_Range(PAGE_SIZE + 8192, ISA_HOLE_START, PAGE_AVAIL);
+ Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW);
+ //Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED);
+ Add_Page_Range(HIGHMEM_START, kernEnd, PAGE_KERN);
+ Add_Page_Range(kernEnd, kernEnd + KERNEL_HEAP_SIZE, PAGE_HEAP);
+
+ Add_Page_Range(kernEnd + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+
+ // Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP);
+ // Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+
+ /* Initialize the kernel heap */
+ // Init_Heap(HIGHMEM_START, KERNEL_HEAP_SIZE);
+ Init_Heap(kernEnd, KERNEL_HEAP_SIZE);
+
+ Print("%uKB memory detected, %u pages in freelist, %d bytes in kernel heap\n",
+ bootInfo->memSizeKB, g_freePageCount, KERNEL_HEAP_SIZE);
+}
+
+/*
+ * Initialize the .bss section of the kernel executable image.
+ */
+void Init_BSS(void)
+{
+ extern char BSS_START, BSS_END;
+
+ /* Fill .bss with zeroes */
+ memset(&BSS_START, '\0', &BSS_END - &BSS_START);
+}
+
+/*
+ * Allocate a page of physical memory.
+ */
+void* Alloc_Page(void)
+{
+ struct Page* page;
+ void *result = 0;
+
+ bool iflag = Begin_Int_Atomic();
+
+ /* See if we have a free page */
+ if (!Is_Page_List_Empty(&s_freeList)) {
+ /* Remove the first page on the freelist. */
+ page = Get_Front_Of_Page_List(&s_freeList);
+ KASSERT((page->flags & PAGE_ALLOCATED) == 0);
+ Remove_From_Front_Of_Page_List(&s_freeList);
+
+ /* Mark page as having been allocated. */
+ page->flags |= PAGE_ALLOCATED;
+ g_freePageCount--;
+ result = (void*) Get_Page_Address(page);
+ }
+
+ End_Int_Atomic(iflag);
+
+ return result;
+}
+
+/*
+ * Choose a page to evict.
+ * Returns null if no pages are available.
+ */
+static struct Page *Find_Page_To_Page_Out()
+{
+ int i;
+ struct Page *curr, *best;
+
+ best = NULL;
+
+ for (i=0; i < s_numPages; i++) {
+ if ((g_pageList[i].flags & PAGE_PAGEABLE) &&
+ (g_pageList[i].flags & PAGE_ALLOCATED)) {
+ if (!best) best = &g_pageList[i];
+ curr = &g_pageList[i];
+ if ((curr->clock < best->clock) && (curr->flags & PAGE_PAGEABLE)) {
+ best = curr;
+ }
+ }
+ }
+
+ return best;
+}
+
+/**
+ * Allocate a page of pageable physical memory, to be mapped
+ * into a user address space.
+ *
+ * @param entry pointer to user page table entry which will
+ * refer to the allocated page
+ * @param vaddr virtual address where page will be mapped
+ * in user address space
+ */
+void* Alloc_Pageable_Page(pte_t *entry, ulong_t vaddr)
+{
+ bool iflag;
+ void* paddr = 0;
+ struct Page* page = 0;
+
+ iflag = Begin_Int_Atomic();
+
+ KASSERT(!Interrupts_Enabled());
+ KASSERT(Is_Page_Multiple(vaddr));
+
+ paddr = Alloc_Page();
+ if (paddr != 0) {
+ page = Get_Page((ulong_t) paddr);
+ KASSERT((page->flags & PAGE_PAGEABLE) == 0);
+ } else {
+ int pagefileIndex;
+
+ /* Select a page to steal from another process */
+ Debug("About to hunt for a page to page out\n");
+ page = Find_Page_To_Page_Out();
+ KASSERT(page->flags & PAGE_PAGEABLE);
+ paddr = (void*) Get_Page_Address(page);
+ Debug("Selected page at addr %p (age = %d)\n", paddr, page->clock);
+
+ /* Find a place on disk for it */
+ pagefileIndex = Find_Space_On_Paging_File();
+ if (pagefileIndex < 0)
+ /* No space available in paging file. */
+ goto done;
+ Debug("Free disk page at index %d\n", pagefileIndex);
+
+ /* Make the page temporarily unpageable (can't let another process steal it) */
+ page->flags &= ~(PAGE_PAGEABLE);
+
+ /* Lock the page so it cannot be freed while we're writing */
+ page->flags |= PAGE_LOCKED;
+
+ /* Write the page to disk. Interrupts are enabled, since the I/O may block. */
+ Debug("Writing physical frame %p to paging file at %d\n", paddr, pagefileIndex);
+ Enable_Interrupts();
+ Write_To_Paging_File(paddr, page->vaddr, pagefileIndex);
+ Disable_Interrupts();
+
+ /* While we were writing got notification this page isn't even needed anymore */
+ if (page->flags & PAGE_ALLOCATED)
+ {
+ /* The page is still in use update its bookeping info */
+ /* Update page table to reflect the page being on disk */
+ page->entry->present = 0;
+ page->entry->kernelInfo = KINFO_PAGE_ON_DISK;
+ page->entry->pageBaseAddr = pagefileIndex; /* Remember where it is located! */
+ }
+ else
+ {
+ /* The page got freed, don't need bookeeping or it on disk */
+ Free_Space_On_Paging_File(pagefileIndex);
+
+ /* Its still allocated though to us now */
+ page->flags |= PAGE_ALLOCATED;
+ }
+
+ /* Unlock the page */
+ page->flags &= ~(PAGE_LOCKED);
+
+ /* XXX - flush TLB should only flush the one page */
+ Flush_TLB();
+ }
+
+ /* Fill in accounting information for page */
+ page->flags |= PAGE_PAGEABLE;
+ page->entry = entry;
+ page->entry->kernelInfo = 0;
+ page->vaddr = vaddr;
+ KASSERT(page->flags & PAGE_ALLOCATED);
+
+done:
+ End_Int_Atomic(iflag);
+ return paddr;
+}
+
+/*
+ * Free a page of physical memory.
+ */
+void Free_Page(void* pageAddr)
+{
+ ulong_t addr = (ulong_t) pageAddr;
+ struct Page* page;
+ bool iflag;
+
+ iflag = Begin_Int_Atomic();
+
+ KASSERT(Is_Page_Multiple(addr));
+
+ /* Get the Page object for this page */
+ page = Get_Page(addr);
+ KASSERT((page->flags & PAGE_ALLOCATED) != 0);
+
+ /* Clear the allocation bit */
+ page->flags &= ~(PAGE_ALLOCATED);
+
+ /* When a page is locked, don't free it just let other thread know its not needed */
+ if (page->flags & PAGE_LOCKED)
+ return;
+
+ /* Clear the pageable bit */
+ page->flags &= ~(PAGE_PAGEABLE);
+
+ /* Put the page back on the freelist */
+ Add_To_Back_Of_Page_List(&s_freeList, page);
+ g_freePageCount++;
+
+ End_Int_Atomic(iflag);
+}
--- /dev/null
+/*
+ * Physical memory allocation
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/defs.h>
+#include <geekos/ktypes.h>
+#include <geekos/kassert.h>
+#include <geekos/bootinfo.h>
+#include <geekos/gdt.h>
+#include <geekos/screen.h>
+#include <geekos/int.h>
+#include <geekos/malloc.h>
+#include <geekos/string.h>
+#include <geekos/mem.h>
+
+
+#include <geekos/serial.h>
+
+/* ----------------------------------------------------------------------
+ * Global data
+ * ---------------------------------------------------------------------- */
+
+/*
+ * List of Page structures representing each page of physical memory.
+ */
+struct Page* g_pageList;
+
+/*
+ * Number of pages currently available on the freelist.
+ */
+uint_t g_freePageCount = 0;
+
+/* ----------------------------------------------------------------------
+ * Private data and functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Defined in paging.c
+ */
+extern int debugFaults;
+#define Debug(args...) if (debugFaults) Print(args)
+
+/*
+ * List of pages available for allocation.
+ */
+static struct Page_List s_freeList;
+
+/*
+ * Total number of physical pages.
+ */
+int unsigned s_numPages;
+
+/*
+ * Add a range of pages to the inventory of physical memory.
+ */
+static void Add_Page_Range(ulong_t start, ulong_t end, int flags)
+{
+ ulong_t addr;
+
+ PrintBoth("Start: %u, End: %u\n", (unsigned int)start, (unsigned int)end);
+
+ KASSERT(Is_Page_Multiple(start));
+ KASSERT(Is_Page_Multiple(end));
+ KASSERT(start < end);
+
+ //Print("Adding %lu pages\n", (end - start) / PAGE_SIZE);
+
+ for (addr = start; addr < end; addr += PAGE_SIZE) {
+ // Print("Adding Page at %u\n", (unsigned int)addr);
+ struct Page *page = Get_Page(addr);
+
+ page->flags = flags;
+
+ if (flags == PAGE_AVAIL) {
+ /* Add the page to the freelist */
+ Add_To_Back_Of_Page_List(&s_freeList, page);
+
+ /* Update free page count */
+ ++g_freePageCount;
+ } else {
+ Set_Next_In_Page_List(page, 0);
+ Set_Prev_In_Page_List(page, 0);
+ }
+
+ }
+ // Print("%d pages now in freelist\n", g_freePageCount);
+
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * The linker defines this symbol to indicate the end of
+ * the executable image.
+ */
+extern char end;
+
+/*
+ * Initialize memory management data structures.
+ * Enables the use of Alloc_Page() and Free_Page() functions.
+ */
+void Init_Mem(struct Boot_Info* bootInfo)
+{
+ ulong_t numPages = bootInfo->memSizeKB >> 2;
+ ulong_t endOfMem = numPages * PAGE_SIZE;
+ unsigned numPageListBytes = sizeof(struct Page) * numPages;
+ ulong_t pageListAddr;
+ ulong_t pageListEnd;
+ ulong_t kernEnd;
+
+
+ KASSERT(bootInfo->memSizeKB > 0);
+
+ if (bootInfo->memSizeKB != TOP_OF_MEM/1024) {
+ PrintBoth("Kernel compiled for %d KB machine, but machine claims %d KB\n",TOP_OF_MEM/1024,bootInfo->memSizeKB);
+ if (bootInfo->memSizeKB < TOP_OF_MEM/1024) {
+ PrintBoth("Kernel compiled for more memory than machine has. Panicking\n");
+ KASSERT(0);
+ }
+ }
+
+
+ bootInfo->memSizeKB = TOP_OF_MEM / 1024;
+
+ /*
+ * Before we do anything, switch from setup.asm's temporary GDT
+ * to the kernel's permanent GDT.
+ */
+ Init_GDT();
+
+ /*
+ * We'll put the list of Page objects right after the end
+ * of the kernel, and mark it as "kernel". This will bootstrap
+ * us sufficiently that we can start allocating pages and
+ * keeping track of them.
+ */
+
+ // JRL: This is stupid...
+ // with large mem sizes the page list overruns into the ISA
+ // hole. By blind luck this causes an unrelated assertion failure, otherwise
+ // I might never have caught it...
+ // We fix it by moving the page list after the kernel heap...
+ // For now we'll make our own stupid assumption that the mem size
+ // is large enough to accomodate the list in high mem.
+
+ PrintBoth("Total Memory Size: %u MBytes\n", bootInfo->memSizeKB/1024);
+
+
+
+ PrintBoth("Page struct size: %u bytes\n", sizeof(struct Page));
+ PrintBoth("Page List Size: %u bytes\n", numPageListBytes);
+
+
+ //pageListAddr = Round_Up_To_Page((ulong_t) &end);
+ //pageListAddr = Round_Up_To_Page(HIGHMEM_START + KERNEL_HEAP_SIZE);
+
+ // Note that this is now moved to be just above the kernel heap
+ // see defs.h for layout
+ pageListAddr=Round_Up_To_Page(KERNEL_PAGELIST);
+
+ pageListEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
+
+ g_pageList = (struct Page*) pageListAddr;
+ // kernEnd = Round_Up_To_Page(pageListAddr + numPageListBytes);
+ //
+ // PAD - Note: I am changing this so that everything through the end of
+ // the VM boot package (bioses/vmxassist) is off limits
+ //kernEnd = Round_Up_To_Page((ulong_t) &end);
+ kernEnd = Round_Up_To_Page(end);
+ s_numPages = numPages;
+
+ PrintBoth("Pagelist addr: %p\n", g_pageList);
+ PrintBoth("index: %p\n", &g_pageList[3]);
+ PrintBoth("direct offset: %p\n", g_pageList + (sizeof(struct Page) * 2));
+ // PrintBoth("Kernel Size=%lx\n", (kernEnd - KERNEL_START_ADDR));
+ // PrintBoth("Kernel Start=%x\n", KERNEL_START_ADDR);
+ PrintBoth("Kernel End=%lx\n", kernEnd);
+ //PrintBoth("end=%x\n", end);
+
+
+ /*
+ * The initial kernel thread and its stack are placed
+ * just beyond the ISA hole.
+ */
+ // This is no longer true
+ // KASSERT(ISA_HOLE_END == KERN_THREAD_OBJ);
+ // instead,
+ //KASSERT(KERN_THREAD_OBJ==(START_OF_VM+VM_SIZE));
+ //KASSERT(KERN_STACK == KERN_THREAD_OBJ + PAGE_SIZE);
+
+ /*
+ * Memory looks like this:
+ * 0 - start: available (might want to preserve BIOS data area)
+ * start - end: kernel
+ * end - ISA_HOLE_START: available
+ * ISA_HOLE_START - ISA_HOLE_END: used by hardware (and ROM BIOS?)
+ * ISA_HOLE_END - HIGHMEM_START: used by initial kernel thread
+ * HIGHMEM_START - end of memory: available
+ * (the kernel heap is located at HIGHMEM_START; any unused memory
+ * beyond that is added to the freelist)
+ */
+
+
+
+
+ // The kernel is still in low memory at this point, in the VM region
+ // Thus we will mark it as kernel use
+ // Add_Page_Range(KERNEL_START_ADDR, kernEnd, PAGE_KERN);
+
+
+ //Add_Page_Range(kernEnd, ISA_HOLE_START, PAGE_AVAIL);
+ // ISA hole remains closed (no actual memory)
+ // Add_Page_Range(ISA_HOLE_START, ISA_HOLE_END, PAGE_HW);
+
+ //Add_Page_Range(ISA_HOLE_END, HIGHMEM_START, PAGE_ALLOCATED);
+ // Add_Page_Range(HIGHMEM_START, HIGHMEM_START + KERNEL_HEAP_SIZE, PAGE_HEAP);
+ //Add_Page_Range(HIGHMEM_START + KERNEL_HEAP_SIZE, endOfMem, PAGE_AVAIL);
+ /* JRL: move page list after kernel heap */
+
+ //Now, above the VM region...
+
+ // Kernel thread object
+ Add_Page_Range(KERNEL_THREAD_OBJECT,KERNEL_THREAD_OBJECT+KERNEL_THREAD_OBJECT_SIZE,PAGE_ALLOCATED);
+ // Kernel stack
+ Add_Page_Range(KERNEL_STACK,KERNEL_STACK+KERNEL_STACK_SIZE,PAGE_ALLOCATED);
+ // Kernel heap
+ Add_Page_Range(KERNEL_HEAP,KERNEL_HEAP+KERNEL_HEAP_SIZE,PAGE_HEAP);
+ // Kernel page list
+ Add_Page_Range(pageListAddr, pageListEnd, PAGE_KERN);
+ // Free space
+ Add_Page_Range(pageListEnd,Round_Down_To_Page(FINAL_KERNEL_START), PAGE_AVAIL);
+ // The kernel
+ Add_Page_Range(Round_Down_To_Page(FINAL_KERNEL_START),Round_Up_To_Page(FINAL_VMBOOTEND+1),PAGE_KERN);
+ // The vmbootpackage
+ // IDT (this should be one page)
+ Add_Page_Range(IDT_LOCATION,TSS_LOCATION,PAGE_KERN);
+ // TSS (this should be one page)
+ Add_Page_Range(TSS_LOCATION,GDT_LOCATION, PAGE_KERN);
+ // GDT (this should be one page)
+ Add_Page_Range(GDT_LOCATION,TOP_OF_MEM, PAGE_KERN);
+
+ /* Initialize the kernel heap */
+ Init_Heap(KERNEL_HEAP, KERNEL_HEAP_SIZE);
+
+ PrintBoth("%uKB memory detected, %u pages in freelist, %d bytes in kernel heap\n",
+ bootInfo->memSizeKB, g_freePageCount, KERNEL_HEAP_SIZE);
+
+ PrintBoth("Memory Layout:\n");
+
+ PrintBoth("%x to %x - INITIAL THREAD\n",KERNEL_THREAD_OBJECT,KERNEL_THREAD_OBJECT+KERNEL_THREAD_OBJECT_SIZE-1);
+ PrintBoth("%x to %x - KERNEL STACK\n",KERNEL_STACK,KERNEL_STACK+KERNEL_STACK_SIZE-1);
+ PrintBoth("%x to %x - KERNEL HEAP\n",KERNEL_HEAP,KERNEL_HEAP+KERNEL_HEAP_SIZE-1);
+ PrintBoth("%lx to %lx - PAGE LIST\n",pageListAddr,pageListEnd-1);
+ PrintBoth("%lx to %x - FREE\n",pageListEnd,FINAL_KERNEL_START-1);
+ PrintBoth("%x to %x - KERNEL CODE\n",FINAL_KERNEL_START,FINAL_KERNEL_END);
+ PrintBoth("%x to %x - IDT\n",IDT_LOCATION,TSS_LOCATION-1);
+ PrintBoth("%x to %x - TSS\n",TSS_LOCATION,GDT_LOCATION-1);
+ PrintBoth("%x to %x - GDT\n",GDT_LOCATION,TOP_OF_MEM-1);
+
+
+}
+
+/*
+ * Initialize the .bss section of the kernel executable image.
+ */
+void Init_BSS(void)
+{
+ extern char BSS_START, BSS_END;
+
+ /* Fill .bss with zeroes */
+ memset(&BSS_START, '\0', &BSS_END - &BSS_START);
+ PrintBoth("BSS Inited, BSS_START=%x, BSS_END=%x\n",BSS_START,BSS_END);
+}
+
+/*
+ * Allocate a page of physical memory.
+ */
+void* Alloc_Page(void)
+{
+ struct Page* page;
+ void *result = 0;
+
+ bool iflag = Begin_Int_Atomic();
+
+ /* See if we have a free page */
+ if (!Is_Page_List_Empty(&s_freeList)) {
+ /* Remove the first page on the freelist. */
+ page = Get_Front_Of_Page_List(&s_freeList);
+ KASSERT((page->flags & PAGE_ALLOCATED) == 0);
+ Remove_From_Front_Of_Page_List(&s_freeList);
+
+ /* Mark page as having been allocated. */
+ page->flags |= PAGE_ALLOCATED;
+ g_freePageCount--;
+ result = (void*) Get_Page_Address(page);
+ }
+
+ End_Int_Atomic(iflag);
+
+ return result;
+}
+
+/*
+ * Free a page of physical memory.
+ */
+void Free_Page(void* pageAddr)
+{
+ ulong_t addr = (ulong_t) pageAddr;
+ struct Page* page;
+ bool iflag;
+
+ iflag = Begin_Int_Atomic();
+
+ KASSERT(Is_Page_Multiple(addr));
+
+ /* Get the Page object for this page */
+ page = Get_Page(addr);
+ KASSERT((page->flags & PAGE_ALLOCATED) != 0);
+
+ /* Clear the allocation bit */
+ page->flags &= ~(PAGE_ALLOCATED);
+
+ /* Put the page back on the freelist */
+ Add_To_Back_Of_Page_List(&s_freeList, page);
+ g_freePageCount++;
+
+ End_Int_Atomic(iflag);
+}
--- /dev/null
+/*
+ * Paging (virtual memory) support
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/string.h>
+#include <geekos/int.h>
+#include <geekos/idt.h>
+#include <geekos/kthread.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/mem.h>
+#include <geekos/malloc.h>
+#include <geekos/gdt.h>
+#include <geekos/segment.h>
+//#include <geekos/user.h>
+//#include <geekos/vfs.h>
+#include <geekos/crc32.h>
+#include <geekos/paging.h>
+#include <geekos/serial.h>
+
+
+/* ----------------------------------------------------------------------
+ * Public data
+ * ---------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+ * Private functions/data
+ * ---------------------------------------------------------------------- */
+
+#define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
+
+/*
+ * flag to indicate if debugging paging code
+ */
+int debugFaults = 0;
+#define Debug(args...) if (debugFaults) Print(args)
+
+
+
+void SerialPrintPD(pde_t *pde)
+{
+ int i;
+
+ SerialPrint("Page Directory at %p:\n",pde);
+ for (i=0;i<NUM_PAGE_DIR_ENTRIES && pde[i].present;i++) {
+ SerialPrintPDE((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),&(pde[i]));
+ }
+}
+
+void SerialPrintPT(void *starting_address, pte_t *pte)
+{
+ int i;
+
+ SerialPrint("Page Table at %p:\n",pte);
+ for (i=0;i<NUM_PAGE_TABLE_ENTRIES && pte[i].present;i++) {
+ SerialPrintPTE(starting_address + PAGE_SIZE*i,&(pte[i]));
+ }
+}
+
+
+void SerialPrintPDE(void *virtual_address, pde_t *pde)
+{
+ SerialPrint("PDE %p -> %p : present=%x, flags=%x, accessed=%x, reserved=%x, largePages=%x, globalPage=%x, kernelInfo=%x\n",
+ virtual_address,
+ (void*) (pde->pageTableBaseAddr << PAGE_POWER),
+ pde->present,
+ pde->flags,
+ pde->accessed,
+ pde->reserved,
+ pde->largePages,
+ pde->globalPage,
+ pde->kernelInfo);
+}
+
+void SerialPrintPTE(void *virtual_address, pte_t *pte)
+{
+ SerialPrint("PTE %p -> %p : present=%x, flags=%x, accessed=%x, dirty=%x, pteAttribute=%x, globalPage=%x, kernelInfo=%x\n",
+ virtual_address,
+ (void*)(pte->pageBaseAddr << PAGE_POWER),
+ pte->present,
+ pte->flags,
+ pte->accessed,
+ pte->dirty,
+ pte->pteAttribute,
+ pte->globalPage,
+ pte->kernelInfo);
+}
+
+
+void SerialDumpPageTables(pde_t *pde)
+{
+ int i;
+
+ SerialPrint("Dumping the pages starting with the pde page at %p\n",pde);
+
+ for (i=0;i<NUM_PAGE_DIR_ENTRIES && pde[i].present;i++) {
+ SerialPrintPDE((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),&(pde[i]));
+ SerialPrintPT((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),(void*)(pde[i].pageTableBaseAddr<<PAGE_POWER));
+ }
+}
+
+
+
+
+
+int checkPaging()
+{
+ unsigned long reg=0;
+ __asm__ __volatile__( "movl %%cr0, %0" : "=a" (reg));
+ Print("Paging on ? : %d\n", (reg & (1<<31)) != 0);
+ return (reg & (1<<31)) != 0;
+}
+
+
+/*
+ * Print diagnostic information for a page fault.
+ */
+static void Print_Fault_Info(uint_t address, faultcode_t faultCode)
+{
+ extern uint_t g_freePageCount;
+
+ g_freePageCount+=0;
+
+ SerialPrintLevel(100,"Pid %d, Page Fault received, at address %x (%d pages free)\n",
+ g_currentThread->pid, address, g_freePageCount);
+ if (faultCode.protectionViolation)
+ SerialPrintLevel(100," Protection Violation, ");
+ else
+ SerialPrintLevel(100," Non-present page, ");
+ if (faultCode.writeFault)
+ SerialPrintLevel(100,"Write Fault, ");
+ else
+ SerialPrintLevel(100,"Read Fault, ");
+ if (faultCode.userModeFault)
+ SerialPrintLevel(100,"in User Mode\n");
+ else
+ SerialPrintLevel(100,"in Supervisor Mode\n");
+}
+
+/*
+ * Handler for page faults.
+ * You should call the Install_Interrupt_Handler() function to
+ * register this function as the handler for interrupt 14.
+ */
+/*static*/ void Page_Fault_Handler(struct Interrupt_State* state)
+{
+ ulong_t address;
+ faultcode_t faultCode;
+
+ KASSERT(!Interrupts_Enabled());
+
+ /* Get the address that caused the page fault */
+ address = Get_Page_Fault_Address();
+ Debug("Page fault @%lx\n", address);
+
+ /* Get the fault code */
+ faultCode = *((faultcode_t *) &(state->errorCode));
+
+ /* rest of your handling code here */
+ SerialPrintLevel(100,"Unexpected Page Fault received\n");
+ Print_Fault_Info(address, faultCode);
+ Dump_Interrupt_State(state);
+ /* user faults just kill the process */
+ if (!faultCode.userModeFault) KASSERT(0);
+
+ /* For now, just kill the thread/process. */
+ Exit(-1);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+
+
+/*
+ * Initialize virtual memory by building page tables
+ * for the kernel and physical memory.
+ */
+void Init_VM(struct Boot_Info *bootInfo)
+{
+ int numpages;
+ int numpagetables;
+ int i,j;
+
+ pde_t *pd;
+ pte_t *pt;
+
+ PrintBoth("Intitialing Virtual Memory\n");
+
+ if (checkPaging()) {
+ SerialPrintLevel(100,"Paging is currently ON\n");
+ return ;
+ }
+
+ SerialPrintLevel(100,"Paging is currently OFF - initializing the pages for a 1-1 map\n");
+
+ numpages=bootInfo->memSizeKB / (PAGE_SIZE/1024);
+ numpagetables = numpages / NUM_PAGE_TABLE_ENTRIES + ((numpages % NUM_PAGE_TABLE_ENTRIES) != 0 );
+
+ SerialPrintLevel(100,"We need %d pages, and thus %d page tables, and one page directory\n",numpages, numpagetables);
+
+ pd = (pde_t*)Alloc_Page();
+
+ if (!pd) {
+ SerialPrintLevel(100,"We are giving up since we can't allocate a page directory!\n");
+ return;
+ } else {
+ SerialPrintLevel(100,"Our PDE is at physical address %p\n",pd);
+ }
+
+ for (i=0;i<NUM_PAGE_DIR_ENTRIES;i++) {
+ if (i>=numpagetables) {
+ pd[i].present=0;
+ pd[i].flags=0;
+ pd[i].accessed=0;
+ pd[i].reserved=0;
+ pd[i].largePages=0;
+ pd[i].globalPage=0;
+ pd[i].kernelInfo=0;
+ pd[i].pageTableBaseAddr=0;
+ } else {
+ pt = (pte_t*)Alloc_Page();
+ if (!pt) {
+ SerialPrintLevel(100,"We are giving up since we can't allocate page table %d\n",i);
+ } else {
+ //SerialPrintLevel(100,"Page Table %d is at physical address %p\n",i,pt);
+ }
+ pd[i].present=1;
+ pd[i].flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
+ pd[i].accessed=0;
+ pd[i].reserved=0;
+ pd[i].largePages=0;
+ pd[i].globalPage=0;
+ pd[i].kernelInfo=0;
+ pd[i].pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
+
+ for (j=0;j<NUM_PAGE_TABLE_ENTRIES;j++) {
+ if (i*NUM_PAGE_TABLE_ENTRIES + j >= numpages) {
+ pt[j].present=0;
+ pt[j].flags=0;
+ pt[j].accessed=0;
+ pt[j].dirty=0;
+ pt[j].pteAttribute=0;
+ pt[j].globalPage=0;
+ pt[j].kernelInfo=0;
+ pt[j].pageBaseAddr=0;
+ } else {
+ pt[j].present=1;
+ pt[j].flags=VM_READ | VM_WRITE | VM_EXEC | VM_USER;
+ pt[j].accessed=0;
+ pt[j].dirty=0;
+ pt[j].pteAttribute=0;
+ pt[j].globalPage=0;
+ pt[j].kernelInfo=0;
+ pt[j].pageBaseAddr=(i*NUM_PAGE_TABLE_ENTRIES + j);
+ }
+ }
+ }
+ }
+ SerialPrintLevel(100,"Done creating 1<->1 initial page tables\n");
+ SerialPrintLevel(100,"Now installing page fault handler\n");
+ // SerialDumpPageTables(pd);
+ Install_Interrupt_Handler(14,Page_Fault_Handler);
+ SerialPrintLevel(100,"Now turning on the paging bit!\n");
+ Enable_Paging(pd);
+ SerialPrintLevel(100,"We are still alive after paging turned on!\n");
+ SerialPrintLevel(100,"checkPaging returns %d\n",checkPaging());
+}
+
+
+//
+pte_t *LookupPage(void *vaddr)
+{
+ uint_t pde_offset = ((uint_t)vaddr) >> 22;
+ uint_t pte_offset = (((uint_t)vaddr) >> 12) & 0x3ff;
+ pte_t *pte;
+ pde_t *pde = Get_PDBR();
+
+ KASSERT(pde);
+
+ pde+=pde_offset;
+
+ if (!(pde->present)) {
+ return 0;
+ }
+
+ pte = (pte_t *)((pde->pageTableBaseAddr)<<12);
+
+ pte+=pte_offset;
+
+ return pte;
+}
+
+
+pte_t *CreateAndAddPageTable(void *vaddr, uint_t flags)
+{
+ int i;
+
+ KASSERT(!(PAGE_OFFSET(vaddr)));
+
+ pte_t *pt = Alloc_Page();
+
+ KASSERT(pt);
+
+ for (i=0;i<NUM_PAGE_TABLE_ENTRIES;i++) {
+ pt[i].present=0;
+ }
+
+ pde_t *pde = Get_PDBR();
+
+ pde=&(pde[PAGE_DIRECTORY_INDEX(vaddr)]);
+
+ KASSERT(!(pde->present));
+
+ pde->present=1;
+ pde->flags=flags;
+ pde->accessed=0;
+ pde->reserved=0;
+ pde->largePages=0;
+ pde->globalPage=0;
+ pde->kernelInfo=0;
+ pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
+
+ return pt;
+}
+
+pte_t *MapPage(void *vaddr, pte_t *pte, int alloc_pde)
+{
+ pte_t *oldpte = LookupPage(vaddr);
+
+ if (!pte) {
+ if (alloc_pde) {
+ CreateAndAddPageTable(vaddr,pte->flags);
+ oldpte = LookupPage(vaddr);
+ KASSERT(pte);
+ } else {
+ return 0;
+ }
+ }
+
+ *oldpte = *pte;
+
+ return oldpte;
+}
+
+pte_t *UnMapPage(void *vaddr)
+{
+ pte_t *oldpte = LookupPage(vaddr);
+
+ if (!oldpte) {
+ return 0;
+ }
+ oldpte->present=0;
+
+ return oldpte;
+}
+
+
+
+/**
+ * Initialize paging file data structures.
+ * All filesystems should be mounted before this function
+ * is called, to ensure that the paging file is available.
+ */
+void Init_Paging(void)
+{
+ PrintBoth("Initializing Paging\n");
+}
+
+/**
+ * Find a free bit of disk on the paging file for this page.
+ * Interrupts must be disabled.
+ * @return index of free page sized chunk of disk space in
+ * the paging file, or -1 if the paging file is full
+ */
+int Find_Space_On_Paging_File(void)
+{
+ KASSERT(!Interrupts_Enabled());
+ TODO("Find free page in paging file");
+}
+
+/**
+ * Free a page-sized chunk of disk space in the paging file.
+ * Interrupts must be disabled.
+ * @param pagefileIndex index of the chunk of disk space
+ */
+void Free_Space_On_Paging_File(int pagefileIndex)
+{
+ KASSERT(!Interrupts_Enabled());
+ TODO("Free page in paging file");
+}
+
+/**
+ * Write the contents of given page to the indicated block
+ * of space in the paging file.
+ * @param paddr a pointer to the physical memory of the page
+ * @param vaddr virtual address where page is mapped in user memory
+ * @param pagefileIndex the index of the page sized chunk of space
+ * in the paging file
+ */
+void Write_To_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
+{
+ struct Page *page = Get_Page((ulong_t) paddr);
+ KASSERT(!(page->flags & PAGE_PAGEABLE)); /* Page must be locked! */
+ TODO("Write page data to paging file");
+}
+
+/**
+ * Read the contents of the indicated block
+ * of space in the paging file into the given page.
+ * @param paddr a pointer to the physical memory of the page
+ * @param vaddr virtual address where page will be re-mapped in
+ * user memory
+ * @param pagefileIndex the index of the page sized chunk of space
+ * in the paging file
+ */
+void Read_From_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
+{
+ struct Page *page = Get_Page((ulong_t) paddr);
+ KASSERT(!(page->flags & PAGE_PAGEABLE)); /* Page must be locked! */
+ TODO("Read page data from paging file");
+}
+
--- /dev/null
+#include <geekos/reboot.h>
+#include <libc/string.h>
+// from linux...
+
+
+#define RTC_PORT(x) (0x70 + (x))
+
+/* The following code and data reboots the machine by switching to real
+ mode and jumping to the BIOS reset entry point, as if the CPU has
+ really been reset. The previous version asked the keyboard
+ controller to pulse the CPU reset line, which is more thorough, but
+ doesn't work with at least one type of 486 motherboard. It is easy
+ to stop this code working; hence the copious comments. */
+
+static unsigned long long
+real_mode_gdt_entries [3] =
+{
+ 0x0000000000000000ULL, /* Null descriptor */
+ 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
+ 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
+};
+
+static struct
+{
+ unsigned short size __attribute__ ((packed));
+ unsigned long long * base __attribute__ ((packed));
+}
+real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, real_mode_gdt_entries },
+ real_mode_idt = { 0x3ff, 0 };
+//no_idt = { 0, 0 };
+
+
+
+static unsigned char real_mode_switch [] =
+{
+ 0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
+ 0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
+ 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60, /* orl $0x60000000,%eax */
+ 0x66, 0x0f, 0x22, 0xc0, /* movl %eax,%cr0 */
+ 0x66, 0x0f, 0x22, 0xd8, /* movl %eax,%cr3 */
+ 0x66, 0x0f, 0x20, 0xc3, /* movl %cr0,%ebx */
+ 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60, /* andl $0x60000000,%ebx */
+ 0x74, 0x02, /* jz f */
+ 0x0f, 0x09, /* wbinvd */
+ 0x24, 0x10, /* f: andb $0x10,al */
+ 0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
+};
+static unsigned char jump_to_bios [] =
+{
+ 0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
+};
+
+
+/*
+ * Switch to real mode and then execute the code
+ * specified by the code and length parameters.
+ * We assume that length will aways be less that 100!
+ */
+void machine_real_restart() {
+ // unsigned long flags;
+
+ unsigned short rtp1, rtp2;
+ unsigned char val1, val2;
+
+ //JRL// local_irq_disable();
+
+ /* Write zero to CMOS register number 0x0f, which the BIOS POST
+ routine will recognize as telling it to do a proper reboot. (Well
+ that's what this book in front of me says -- it may only apply to
+ the Phoenix BIOS though, it's not clear). At the same time,
+ disable NMIs by setting the top bit in the CMOS address register,
+ as we're about to do peculiar things to the CPU. I'm not sure if
+ `outb_p' is needed instead of just `outb'. Use it to be on the
+ safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.)
+ */
+
+//JRL// spin_lock_irqsave(&rtc_lock, flags);
+//JRL// CMOS_WRITE(0x00, 0x8f);
+ val1 = 0x8f;
+ val2 = 0x00;
+ rtp1 = RTC_PORT(0);
+ rtp2 = RTC_PORT(1);
+
+ // outb_p(0x8f, RTC_PORT(0));
+ // outb_p(0x00, RTC_PORT(1));
+
+ __asm__ __volatile__ (
+ "outb %b0, %w1"
+ :
+ : "a" (val1), "Nd" (rtp1)
+ );
+
+ __asm__ __volatile__ (
+ "outb %b0, %w1"
+ :
+ : "a" (val2), "Nd" (rtp2)
+ );
+
+//JRL// spin_unlock_irqrestore(&rtc_lock, flags);
+
+ /* Remap the kernel at virtual address zero, as well as offset zero
+ from the kernel segment. This assumes the kernel segment starts at
+ virtual address PAGE_OFFSET. */
+
+//JRL// memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
+//JRL// sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
+
+ /*
+ * Use `swapper_pg_dir' as our page directory.
+ */
+//JRL// load_cr3(swapper_pg_dir);
+
+ /* Write 0x1234 to absolute memory location 0x472. The BIOS reads
+ this on booting to tell it to "Bypass memory test (also warm
+ boot)". This seems like a fairly standard thing that gets set by
+ REBOOT.COM programs, and the previous reset routine did this
+ too. */
+
+ *((unsigned short *)0x472) = 0x1234;
+
+ /* For the switch to real mode, copy some code to low memory. It has
+ to be in the first 64k because it is running in 16-bit mode, and it
+ has to have the same physical and virtual address, because it turns
+ off paging. Copy it near the end of the first page, out of the way
+ of BIOS variables. */
+
+ memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100),
+ real_mode_switch, sizeof (real_mode_switch));
+ memcpy ((void *) (0x1000 - 100), jump_to_bios, sizeof(jump_to_bios));
+
+ /* Set up the IDT for real mode. */
+
+ __asm__ __volatile__ ("lidt %0" : : "m" (real_mode_idt));
+
+ /* Set up a GDT from which we can load segment descriptors for real
+ mode. The GDT is not used in real mode; it is just needed here to
+ prepare the descriptors. */
+
+ __asm__ __volatile__ ("lgdt %0" : : "m" (real_mode_gdt));
+
+ /* Load the data segment registers, and thus the descriptors ready for
+ real mode. The base address of each segment is 0x100, 16 times the
+ selector value being loaded here. This is so that the segment
+ registers don't have to be reloaded after switching to real mode:
+ the values are consistent for real mode operation already. */
+
+ __asm__ __volatile__ ("movl $0x0010,%%eax\n"
+ "\tmovl %%eax,%%ds\n"
+ "\tmovl %%eax,%%es\n"
+ "\tmovl %%eax,%%fs\n"
+ "\tmovl %%eax,%%gs\n"
+ "\tmovl %%eax,%%ss" : : : "eax");
+
+ /* Jump to the 16-bit code that we copied earlier. It disables paging
+ and the cache, switches to real mode, and jumps to the BIOS reset
+ entry point. */
+
+ __asm__ __volatile__ ("ljmp $0x0008,%0"
+ :
+ : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100)));
+}
--- /dev/null
+/*
+ * GeekOS text screen output
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <stdarg.h>
+#include <geekos/kassert.h>
+#include <geekos/ktypes.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/fmtout.h>
+#include <geekos/screen.h>
+
+/*
+ * Information sources for VT100 and ANSI escape sequences:
+ * - http://www.lns.cornell.edu/~pvhp/dcl/vt100.html
+ * - http://en.wikipedia.org/wiki/ANSI_escape_code
+ */
+
+/* ----------------------------------------------------------------------
+ * Private functions and data
+ * ---------------------------------------------------------------------- */
+
+#define ESC ((char) 0x1B)
+#define DEFAULT_ATTRIBUTE ATTRIB(BLACK, GRAY)
+
+enum State {
+ S_NORMAL, /* Normal state - output is echoed verbatim */
+ S_ESC, /* Saw ESC character - begin output escape sequence */
+ S_ESC2, /* Saw '[' character - continue output escape sequence */
+ S_ARG, /* Scanning a numeric argument */
+ S_CMD, /* Command */
+};
+
+#define MAXARGS 8 /* Max args that can be passed to esc sequence */
+
+struct Console_State {
+ /* Current state information */
+ int row, col;
+ int saveRow, saveCol;
+ uchar_t currentAttr;
+
+ /* Working variables for processing escape sequences. */
+ enum State state;
+ int argList[MAXARGS];
+ int numArgs;
+};
+
+static struct Console_State s_cons;
+
+#define NUM_SCREEN_DWORDS ((NUMROWS * NUMCOLS * 2) / 4)
+#define NUM_SCROLL_DWORDS (((NUMROWS-1) * NUMCOLS * 2) / 4)
+#define NUM_DWORDS_PER_LINE ((NUMCOLS*2)/4)
+#define FILL_DWORD (0x00200020 | (s_cons.currentAttr<<24) | (s_cons.currentAttr<<8))
+
+/*
+ * Scroll the display one line.
+ * We speed things up by copying 4 bytes at a time.
+ */
+static void Scroll(void)
+{
+ uint_t* v;
+ int i, n = NUM_SCROLL_DWORDS;
+ uint_t fill = FILL_DWORD;
+
+ /* Move lines 1..NUMROWS-1 up one position. */
+ for (v = (uint_t*)VIDMEM, i = 0; i < n; ++i) {
+ *v = *(v + NUM_DWORDS_PER_LINE);
+ ++v;
+ }
+
+ /* Clear out last line. */
+ for (v = (uint_t*)VIDMEM + n, i = 0; i < NUM_DWORDS_PER_LINE; ++i)
+ *v++ = fill;
+}
+
+/*
+ * Clear current cursor position to end of line using
+ * current attribute.
+ */
+static void Clear_To_EOL(void)
+{
+ int n = (NUMCOLS - s_cons.col);
+ uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
+ while (n-- > 0) {
+ *v++ = ' ';
+ *v++ = s_cons.currentAttr;
+ }
+}
+
+/*
+ * Move to the beginning of the next line, scrolling
+ * if necessary.
+ */
+static void Newline(void)
+{
+ ++s_cons.row;
+ s_cons.col = 0;
+ if (s_cons.row == NUMROWS) {
+ Scroll();
+ s_cons.row = NUMROWS - 1;
+ }
+}
+
+/*
+ * Write the graphic representation of given character to the screen
+ * at current position, with current attribute, scrolling if
+ * necessary.
+ */
+static void Put_Graphic_Char(int c)
+{
+ uchar_t* v = VIDMEM + s_cons.row*(NUMCOLS*2) + s_cons.col*2;
+
+ /* Put character at current position */
+ *v++ = (uchar_t) c;
+ *v = s_cons.currentAttr;
+
+ if (s_cons.col < NUMCOLS - 1)
+ ++s_cons.col;
+ else
+ Newline();
+}
+
+/*
+ * Put one character to the screen using the current cursor position
+ * and attribute, scrolling if needed. The caller should update
+ * the cursor position once all characters have been written.
+ */
+static void Output_Literal_Character(int c)
+{
+ int numSpaces;
+
+ switch (c) {
+ case '\n':
+ Clear_To_EOL();
+ Newline();
+ break;
+
+ case '\t':
+ numSpaces = TABWIDTH - (s_cons.col % TABWIDTH);
+ while (numSpaces-- > 0)
+ Put_Graphic_Char(' ');
+ break;
+
+ default:
+ Put_Graphic_Char(c);
+ break;
+ }
+
+#ifndef NDEBUG
+ /*
+ * When compiled with --enable-port-e9-hack, Bochs will send writes
+ * to port E9 to the console. This helps tremendously with debugging,
+ * because it allows debug Print() statements to be visible after
+ * Bochs has exited.
+ */
+ Out_Byte(0xE9, c);
+#endif
+}
+
+/*
+ * Move the cursor to a new position, stopping at the screen borders.
+ */
+static void Move_Cursor(int row, int col)
+{
+ if (row < 0)
+ row = 0;
+ else if (row >= NUMROWS)
+ row = NUMROWS - 1;
+
+ if (col < 0)
+ col = 0;
+ else if (col >= NUMCOLS)
+ col = NUMCOLS - 1;
+
+ s_cons.row = row;
+ s_cons.col = col;
+}
+
+/*
+ * Table mapping ANSI colors to VGA text mode colors.
+ */
+static const uchar_t s_ansiToVgaColor[] = {
+ BLACK,RED,GREEN,AMBER,BLUE,MAGENTA,CYAN,GRAY
+};
+
+/*
+ * Update the attributes specified by the arguments
+ * of the escape sequence.
+ */
+static void Update_Attributes(void)
+{
+ int i;
+ int attr = s_cons.currentAttr & ~(BRIGHT);
+
+ for (i = 0; i < s_cons.numArgs; ++i) {
+ int value = s_cons.argList[i];
+ if (value == 0)
+ attr = DEFAULT_ATTRIBUTE;
+ else if (value == 1)
+ attr |= BRIGHT;
+ else if (value >= 30 && value <= 37)
+ attr = (attr & ~0x7) | s_ansiToVgaColor[value - 30];
+ else if (value >= 40 && value <= 47)
+ attr = (attr & ~(0x7 << 4)) | (s_ansiToVgaColor[value - 40] << 4);
+ }
+ s_cons.currentAttr = attr;
+}
+
+/* Reset to cancel or finish processing an escape sequence. */
+static void Reset(void)
+{
+ s_cons.state = S_NORMAL;
+ s_cons.numArgs = 0;
+}
+
+/* Start an escape sequence. */
+static void Start_Escape(void)
+{
+ s_cons.state = S_ESC;
+ s_cons.numArgs = 0;
+}
+
+/* Start a numeric argument to an escape sequence. */
+static void Start_Arg(int argNum)
+{
+ KASSERT(s_cons.numArgs == argNum);
+ s_cons.numArgs++;
+ s_cons.state = S_ARG;
+ if (argNum < MAXARGS)
+ s_cons.argList[argNum] = 0;
+}
+
+/* Save current cursor position. */
+static void Save_Cursor(void)
+{
+ s_cons.saveRow = s_cons.row;
+ s_cons.saveCol = s_cons.col;
+}
+
+/* Restore saved cursor position. */
+static void Restore_Cursor(void)
+{
+ s_cons.row = s_cons.saveRow;
+ s_cons.col = s_cons.saveCol;
+}
+
+/* Add a digit to current numeric argument. */
+static void Add_Digit(int c)
+{
+ KASSERT(ISDIGIT(c));
+ if (s_cons.numArgs < MAXARGS) {
+ int argNum = s_cons.numArgs - 1;
+ s_cons.argList[argNum] *= 10;
+ s_cons.argList[argNum] += (c - '0');
+ }
+}
+
+/*
+ * Get a numeric argument.
+ * Returns zero if that argument was not actually specified.
+ */
+static int Get_Arg(int argNum)
+{
+ return argNum < s_cons.numArgs ? s_cons.argList[argNum] : 0;
+}
+
+/*
+ * The workhorse output function.
+ * Depending on the current console output state,
+ * does literal character output or processes part of
+ * an escape sequence.
+ */
+static void Put_Char_Imp(int c)
+{
+again:
+ switch (s_cons.state) {
+ case S_NORMAL:
+ if (c == ESC)
+ Start_Escape();
+ else
+ Output_Literal_Character(c);
+ break;
+
+ case S_ESC:
+ if (c == '[')
+ s_cons.state = S_ESC2;
+ else
+ Reset();
+ break;
+
+ case S_ESC2:
+ if (ISDIGIT(c)) {
+ Start_Arg(0);
+ goto again;
+ } else if (c == ';') {
+ /* Special case: for "n;m" commands, "n" is implicitly 1 if omitted */
+ Start_Arg(0);
+ Add_Digit('1');
+ Start_Arg(1);
+ } else {
+ s_cons.state = S_CMD;
+ goto again;
+ }
+ break;
+
+ case S_ARG:
+ if (ISDIGIT(c))
+ Add_Digit(c);
+ else if (c == ';')
+ Start_Arg(s_cons.numArgs);
+ else {
+ s_cons.state = S_CMD;
+ goto again;
+ }
+ break;
+
+ case S_CMD:
+ switch (c) {
+ case 'K': Clear_To_EOL(); break;
+ case 's': Save_Cursor(); break;
+ case 'u': Restore_Cursor(); break;
+ case 'A': Move_Cursor(s_cons.row - Get_Arg(0), s_cons.col); break;
+ case 'B': Move_Cursor(s_cons.row + Get_Arg(0), s_cons.col); break;
+ case 'C': Move_Cursor(s_cons.row, s_cons.col + Get_Arg(0)); break;
+ case 'D': Move_Cursor(s_cons.row, s_cons.col - Get_Arg(0)); break;
+ case 'm': Update_Attributes(); break;
+ case 'f': case 'H':
+ if (s_cons.numArgs == 2) Move_Cursor(Get_Arg(0)-1, Get_Arg(1)-1); break;
+ case 'J':
+ if (s_cons.numArgs == 1 && Get_Arg(0) == 2) {
+ Clear_Screen();
+ Put_Cursor(0, 0);
+ }
+ break;
+ default: break;
+ }
+ Reset();
+ break;
+
+ default:
+ KASSERT(false);
+ }
+}
+
+/*
+ * Update the location of the hardware cursor.
+ */
+static void Update_Cursor(void)
+{
+ /*
+ * The cursor location is a character offset from the beginning
+ * of page memory (I think).
+ */
+ uint_t characterPos = (s_cons.row * NUMCOLS) + s_cons.col;
+ uchar_t origAddr;
+
+ /*
+ * Save original contents of CRT address register.
+ * It is considered good programming practice to restore
+ * it to its original value after modifying it.
+ */
+ origAddr = In_Byte(CRT_ADDR_REG);
+ IO_Delay();
+
+ /* Set the high cursor location byte */
+ Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG);
+ IO_Delay();
+ Out_Byte(CRT_DATA_REG, (characterPos>>8) & 0xff);
+ IO_Delay();
+
+ /* Set the low cursor location byte */
+ Out_Byte(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG);
+ IO_Delay();
+ Out_Byte(CRT_DATA_REG, characterPos & 0xff);
+ IO_Delay();
+
+ /* Restore contents of the CRT address register */
+ Out_Byte(CRT_ADDR_REG, origAddr);
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize the screen module.
+ */
+void Init_Screen(void)
+{
+ bool iflag = Begin_Int_Atomic();
+
+ s_cons.row = s_cons.col = 0;
+ s_cons.currentAttr = DEFAULT_ATTRIBUTE;
+ Clear_Screen();
+
+ End_Int_Atomic(iflag);
+ Print("Screen Inited\n");
+}
+
+/*
+ * Clear the screen using the current attribute.
+ */
+void Clear_Screen(void)
+{
+ uint_t* v = (uint_t*)VIDMEM;
+ int i;
+ uint_t fill = FILL_DWORD;
+
+ bool iflag = Begin_Int_Atomic();
+
+ for (i = 0; i < NUM_SCREEN_DWORDS; ++i)
+ *v++ = fill;
+
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Get current cursor position.
+ */
+void Get_Cursor(int* row, int* col)
+{
+ bool iflag = Begin_Int_Atomic();
+ *row = s_cons.row;
+ *col = s_cons.col;
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Set the current cursor position.
+ * Return true if successful, or false if the specified
+ * cursor position is invalid.
+ */
+bool Put_Cursor(int row, int col)
+{
+ bool iflag;
+
+ if (row < 0 || row >= NUMROWS || col < 0 || col >= NUMCOLS)
+ return false;
+
+ iflag = Begin_Int_Atomic();
+ s_cons.row = row;
+ s_cons.col = col;
+ Update_Cursor();
+ End_Int_Atomic(iflag);
+
+ return true;
+}
+
+/*
+ * Get the current character attribute.
+ */
+uchar_t Get_Current_Attr(void)
+{
+ return s_cons.currentAttr;
+}
+
+/*
+ * Set the current character attribute.
+ */
+void Set_Current_Attr(uchar_t attrib)
+{
+ bool iflag = Begin_Int_Atomic();
+ s_cons.currentAttr = attrib;
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Write a single character to the screen at current position
+ * using current attribute, handling scrolling, special characters, etc.
+ */
+void Put_Char(int c)
+{
+ bool iflag = Begin_Int_Atomic();
+ Put_Char_Imp(c);
+ Update_Cursor();
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Write a string of characters to the screen at current cursor
+ * position using current attribute.
+ */
+void Put_String(const char* s)
+{
+ bool iflag = Begin_Int_Atomic();
+ while (*s != '\0')
+ Put_Char_Imp(*s++);
+ Update_Cursor();
+ End_Int_Atomic(iflag);
+}
+
+/*
+ * Write a buffer of characters at current cursor position
+ * using current attribute.
+ */
+void Put_Buf(const char* buf, ulong_t length)
+{
+ bool iflag = Begin_Int_Atomic();
+ while (length > 0) {
+ Put_Char_Imp(*buf++);
+ --length;
+ }
+ Update_Cursor();
+ End_Int_Atomic(iflag);
+}
+
+/* Support for Print(). */
+static void Print_Emit(struct Output_Sink *o, int ch) { Put_Char_Imp(ch); }
+static void Print_Finish(struct Output_Sink *o) { Update_Cursor(); }
+static struct Output_Sink s_outputSink = { &Print_Emit, &Print_Finish };
+
+/*
+ * Print to console using printf()-style formatting.
+ * Calls into Format_Output in common library.
+ */
+void Print(const char *fmt, ...)
+{
+ va_list args;
+
+ bool iflag = Begin_Int_Atomic();
+
+ va_start(args, fmt);
+ Format_Output(&s_outputSink, fmt, args);
+ va_end(args);
+
+ End_Int_Atomic(iflag);
+}
+
--- /dev/null
+/*
+ * General data structures and routines for segmentation
+ * Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/string.h>
+#include <geekos/tss.h>
+#include <geekos/segment.h>
+
+static __inline__ void Set_Size_And_Base_Pages(
+ struct Segment_Descriptor* desc,
+ ulong_t baseAddr,
+ ulong_t numPages
+)
+{
+ /*
+ * There are 20 bits in the size fields of a segment descriptor.
+ * The maximum possible value is thus 0xFFFFF, which in terms of
+ * pages is one page less than 4GB. So, I conclude that the
+ * number of pages in the segment is one greater than the
+ * value specified in the descriptor.
+ */
+ KASSERT(numPages > 0);
+ numPages -= 1;
+
+ desc->sizeLow = numPages & 0xFFFF;
+ desc->sizeHigh = (numPages >> 16) & 0x0F;
+ desc->baseLow = baseAddr & 0xFFFFFF;
+ desc->baseHigh = (baseAddr >> 24) & 0xFF;
+ desc->granularity = 1; /* size in pages */
+}
+
+static __inline__ void Set_Size_And_Base_Bytes(
+ struct Segment_Descriptor* desc,
+ ulong_t baseAddr,
+ ulong_t numBytes
+)
+{
+ desc->sizeLow = numBytes & 0xFFFF;
+ desc->sizeHigh = (numBytes >> 16) & 0x0F;
+ desc->baseLow = baseAddr & 0xFFFFFF;
+ desc->baseHigh = (baseAddr >> 24) & 0xFF;
+ desc->granularity = 0; /* size in bytes */
+}
+
+/*
+ * Initialize an unused segment descriptor.
+ */
+void Init_Null_Segment_Descriptor(struct Segment_Descriptor* desc)
+{
+ memset(desc, '\0', sizeof(*desc));
+}
+
+/*
+ * Initialize a code segment descriptor.
+ */
+void Init_Code_Segment_Descriptor(
+ struct Segment_Descriptor* desc,
+ ulong_t baseAddr,
+ ulong_t numPages,
+ int privilegeLevel
+)
+{
+ KASSERT(privilegeLevel >= 0 && privilegeLevel <= 3);
+
+ Set_Size_And_Base_Pages(desc, baseAddr, numPages);
+ desc->type = 0x0A; /* 1010b: code, !conforming, readable, !accessed */
+ desc->system = 1;
+ desc->dpl = privilegeLevel;
+ desc->present = 1;
+ desc->reserved = 0;
+ desc->dbBit = 1; /* 32 bit code segment */
+}
+
+/*
+ * Initialize a data segment descriptor.
+ */
+void Init_Data_Segment_Descriptor(
+ struct Segment_Descriptor* desc,
+ ulong_t baseAddr,
+ ulong_t numPages,
+ int privilegeLevel
+)
+{
+ KASSERT(privilegeLevel >= 0 && privilegeLevel <= 3);
+
+ Set_Size_And_Base_Pages(desc, baseAddr, numPages);
+ desc->type = 0x02; /* 0010b: data, expand-up, writable, !accessed */
+ desc->system = 1;
+ desc->dpl = privilegeLevel;
+ desc->present = 1;
+ desc->reserved = 0;
+ desc->dbBit = 1; /* 32 bit operands */
+}
+
+/*
+ * Initialize a TSS descriptor.
+ */
+void Init_TSS_Descriptor(struct Segment_Descriptor* desc, struct TSS* theTSS)
+{
+ Set_Size_And_Base_Bytes(desc, (ulong_t) theTSS, sizeof(struct TSS));
+ desc->type = 0x09; /* 1001b: 32 bit, !busy */
+ desc->system = 0;
+ desc->dpl = 0;
+ desc->present = 1;
+ desc->reserved = 0;
+ desc->dbBit = 0; /* must be 0 in TSS */
+}
+
+/*
+ * Initialize an LDT (Local Descriptor Table) descriptor.
+ */
+void Init_LDT_Descriptor(
+ struct Segment_Descriptor* desc,
+ struct Segment_Descriptor theLDT[],
+ int numEntries
+)
+{
+ Set_Size_And_Base_Bytes(
+ desc, (ulong_t) theLDT, sizeof(struct Segment_Descriptor) * numEntries);
+
+ desc->type = 0x02; /* 0010b */
+ desc->system = 0;
+ desc->dpl = 0;
+ desc->present = 1;
+ desc->reserved = 0;
+ desc->dbBit = 0;
+}
--- /dev/null
+#include <geekos/serial.h>
+#include <geekos/reboot.h>
+#include <geekos/gdt.h>
+#include <geekos/idt.h>
+
+
+
+
+unsigned short serial_io_addr = 0;
+
+
+static void Serial_Interrupt_Handler(struct Interrupt_State * state) {
+ char rcv_byte;
+ char irq_id;
+
+ Begin_IRQ(state);
+
+ irq_id = In_Byte(serial_io_addr + 2);
+
+
+ if ((irq_id & 0x04) != 0) {
+ rcv_byte = In_Byte(serial_io_addr + 0);
+
+ if (rcv_byte == 'k') {
+ SerialPrint("Restarting Machine\r\n");
+ machine_real_restart();
+ } else if (rcv_byte=='d') {
+ SerialPrint("Dumping Machine State\n");
+ Dump_Interrupt_State(state);
+ DumpIDT();
+ DumpGDT();
+ }
+
+#if 0
+ SerialPrint("Unreserved serial byte: %d (%c)\r\n", rcv_byte, rcv_byte);
+#endif
+ }
+ End_IRQ(state);
+}
+
+void InitSerial() {
+ Print("Initialzing Serial\n");
+ Install_IRQ(COM1_IRQ, Serial_Interrupt_Handler);
+ Enable_IRQ(COM1_IRQ);
+ InitSerialAddr(DEFAULT_SERIAL_ADDR);
+}
+
+void InitSerialAddr(unsigned short io_addr) {
+ serial_io_addr = io_addr;
+
+ Print("Initializing Polled Serial Output on COM1 - 115200 N81 noflow\n");
+ // io_adr = 0x3F8; /* 3F8=COM1, 2F8=COM2, 3E8=COM3, 2E8=COM4 */
+ Out_Byte(io_addr + 3, 0x80);
+ // 115200 /* 115200 / 12 = 9600 baud */
+ Out_Byte(io_addr + 0, 1);
+ Out_Byte(io_addr + 1, 0);
+ /* 8N1 */
+ Out_Byte(io_addr + 3, 0x03);
+ /* all interrupts disabled */
+ // Out_Byte(io_addr + 1, 0);
+ Out_Byte(io_addr + 1, 0x01);
+ /* turn off FIFO, if any */
+ Out_Byte(io_addr + 2, 0);
+ /* loopback off, interrupts (Out2) off, Out1/RTS/DTR off */
+ // Out_Byte(io_addr + 4, 0);
+ // enable interrupts (bit 3)
+ Out_Byte(io_addr + 4, 0x08);
+}
+
+
+inline static void SerialPutChar(unsigned char c) {
+
+ // static unsigned short io_adr;
+ if (serial_io_addr==0) {
+ return;
+ }
+
+
+ if (c=='\n') {
+ /* wait for transmitter ready */
+ while((In_Byte(serial_io_addr + 5) & 0x40) == 0) {
+ }
+ /* send char */
+ Out_Byte(serial_io_addr + 0, '\r');
+ /* wait for transmitter ready */
+ }
+ while((In_Byte(serial_io_addr + 5) & 0x40) == 0) {
+ }
+ /* send char */
+ Out_Byte(serial_io_addr + 0, c);
+}
+
+
+
+void SerialPutLineN(char * line, int len) {
+ int i;
+ for (i = 0; i < len && line[i] != 0; i++) {
+ SerialPutChar(line[i]);
+ }
+}
+
+
+void SerialPutLine(char * line) {
+ int i;
+ for (i = 0; line[i]!= 0; i++) {
+ SerialPutChar(line[i]);
+ }
+}
+
+
+void SerialPrintHex(unsigned char x)
+{
+ unsigned char z;
+
+ z = (x>>4) & 0xf ;
+ SerialPrint("%x", z);
+ z = x & 0xf;
+ SerialPrint("%x", z);
+}
+
+void SerialMemDump(unsigned char *start, int n)
+{
+ int i, j;
+
+ for (i=0;i<n;i+=16) {
+ SerialPrint("%8x", (unsigned)(start+i));
+ for (j=i; j<i+16 && j<n; j+=2) {
+ SerialPrint(" ");
+ SerialPrintHex(*((unsigned char *)(start+j)));
+ if ((j+1)<n) {
+ SerialPrintHex(*((unsigned char *)(start+j+1)));
+ }
+ }
+ SerialPrint(" ");
+ for (j=i; j<i+16 && j<n;j++) {
+ SerialPrint("%c", ((start[j]>=32) && (start[j]<=126)) ? start[j] : '.');
+ }
+ SerialPrint("\n");
+ }
+}
--- /dev/null
+; -*- fundamental -*-
+; GeekOS setup code
+; Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This is free software. You are permitted to use,
+; redistribute, and modify it as specified in the file "COPYING".
+
+; A lot of this code is adapted from Kernel Toolkit 0.2
+; and Linux version 2.2.x, so the following copyrights apply:
+
+; Copyright (C) 1991, 1992 Linus Torvalds
+; modified by Drew Eckhardt
+; modified by Bruce Evans (bde)
+; adapted for Kernel Toolkit by Luigi Sgro
+
+%include "defs.asm"
+
+; This is SUICIDE...
+; DON'T EVER INCLUDE CODE AT THE TOP LEVEL AGAIN
+;%include "util.asm"
+
+[BITS 16]
+[ORG 0x0]
+
+start_setup:
+
+ ; Redefine the data segment so we can access variables
+ ; declared in this file.
+ mov ax, SETUPSEG
+ mov ds, ax
+
+
+ ; Use int 15h to find out size of extended memory in KB.
+ ; Extended memory is the memory above 1MB. So by
+ ; adding 1MB to this amount, we get the total amount
+ ; of system memory. We can only detect 64MB this way,
+ ; but that's OK for now.
+ ;mov ah, 0x88
+ ;int 0x15
+ ;add ax, 1024 ; 1024 KB == 1 MB
+ mov ax, 0xe801
+ int 0x15
+ add ax, 1024 ; 1024 KB == 1 MB
+ mov [mem_size_kbytes], ax
+ mov [mem_size_eblocks], bx
+
+ ; Kill the floppy motor.
+ call Kill_Motor
+
+
+
+ ; Block interrupts, since we can't meaningfully handle them yet
+ ; and we no longer need BIOS services.
+ cli
+
+ ; Set up IDT and GDT registers
+ lidt [IDT_Pointer]
+ lgdt [GDT_Pointer]
+
+ ; Initialize the interrupt controllers, and enable the
+ ; A20 address line
+ call Init_PIC
+ call Enable_A20
+
+ ; Switch to protected mode!
+ mov ax, 0x01
+ lmsw ax
+
+ ; Jump to 32 bit code.
+ jmp dword KERNEL_CS:(SETUPSEG << 4) + setup_32
+
+
+[BITS 32]
+setup_32:
+
+ ; set up data segment registers
+ mov ax, KERNEL_DS
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+
+ ; Create the stack for the initial kernel thread.
+ mov esp, KERN_STACK + 4096
+
+ ; Build Boot_Info struct on stack.
+ ; Note that we push the fields on in reverse order,
+ ; since the stack grows downwards.
+ xor eax, eax
+ mov ax, [(SETUPSEG<<4)+mem_size_kbytes]
+ xor ebx, ebx
+ mov bx, [(SETUPSEG<<4)+mem_size_eblocks]
+ shl ebx, 6
+ add eax, ebx
+ push eax ; memSizeKB
+ push dword 8 ; bootInfoSize
+
+ ; Pass pointer to Boot_Info struct as argument to kernel
+ ; entry point.
+ push esp
+
+ ; Push return address to make this look like a call
+ ; XXX - untested
+ push dword (SETUPSEG<<4)+.returnAddr
+
+
+
+ ; Far jump into kernel
+ jmp KERNEL_CS:ENTRY_POINT
+
+.returnAddr:
+ ; We shouldn't return here.
+.here: jmp .here
+
+
+
+
+
+[BITS 16]
+
+
+
+%include "util.asm"
+
+; Kill the floppy motor.
+; This code was shamelessly stolen from Linux.
+Kill_Motor:
+ mov dx, 0x3f2
+ xor al, al
+ out dx, al
+ ret
+
+Init_PIC:
+ ; Initialize master and slave PIC!
+ mov al, ICW1
+ out 0x20, al ; ICW1 to master
+ call Delay
+ out 0xA0, al ; ICW1 to slave
+ call Delay
+ mov al, ICW2_MASTER
+ out 0x21, al ; ICW2 to master
+ call Delay
+ mov al, ICW2_SLAVE
+ out 0xA1, al ; ICW2 to slave
+ call Delay
+ mov al, ICW3_MASTER
+ out 0x21, al ; ICW3 to master
+ call Delay
+ mov al, ICW3_SLAVE
+ out 0xA1, al ; ICW3 to slave
+ call Delay
+ mov al, ICW4
+ out 0x21, al ; ICW4 to master
+ call Delay
+ out 0xA1, al ; ICW4 to slave
+ call Delay
+ mov al, 0xff ; mask all ints in slave
+ out 0xA1, al ; OCW1 to slave
+ call Delay
+ mov al, 0xfb ; mask all ints but 2 in master
+ out 0x21, al ; OCW1 to master
+ call Delay
+ ret
+
+; Linux uses this code.
+; The idea is that some systems issue port I/O instructions
+; faster than the device hardware can deal with them.
+Delay:
+ jmp .done
+.done: ret
+
+; Enable the A20 address line, so we can correctly address
+; memory above 1MB.
+Enable_A20:
+ mov al, 0xD1
+ out 0x64, al
+ call Delay
+ mov al, 0xDF
+ out 0x60, al
+ call Delay
+ ret
+
+
+; ----------------------------------------------------------------------
+; Setup data
+; ----------------------------------------------------------------------
+
+mem_size_kbytes: dw 0
+mem_size_eblocks: dw 0
+
+; ----------------------------------------------------------------------
+; The GDT. Creates flat 32-bit address space for the kernel
+; code, data, and stack. Note that this GDT is just used
+; to create an environment where we can start running 32 bit
+; code. The kernel will create and manage its own GDT.
+; ----------------------------------------------------------------------
+
+; GDT initialization stuff
+NUM_GDT_ENTRIES equ 3 ; number of entries in GDT
+GDT_ENTRY_SZ equ 8 ; size of a single GDT entry
+
+align 8, db 0
+GDT:
+ ; Descriptor 0 is not used
+ dw 0
+ dw 0
+ dw 0
+ dw 0
+
+ ; Descriptor 1: kernel code segment
+ dw 0xFFFF ; bytes 0 and 1 of segment size
+ dw 0x0000 ; bytes 0 and 1 of segment base address
+ db 0x00 ; byte 2 of segment base address
+ db 0x9A ; present, DPL=0, non-system, code, non-conforming,
+ ; readable, not accessed
+ db 0xCF ; granularity=page, 32 bit code, upper nibble of size
+ db 0x00 ; byte 3 of segment base address
+
+ ; Descriptor 2: kernel data and stack segment
+ ; NOTE: what Intel calls an "expand-up" segment
+ ; actually means that the stack will grow DOWN,
+ ; towards lower memory. So, we can use this descriptor
+ ; for both data and stack references.
+ dw 0xFFFF ; bytes 0 and 1 of segment size
+ dw 0x0000 ; bytes 0 and 1 of segment base address
+ db 0x00 ; byte 2 of segment base address
+ db 0x92 ; present, DPL=0, non-system, data, expand-up,
+ ; writable, not accessed
+ db 0xCF ; granularity=page, big, upper nibble of size
+ db 0x00 ; byte 3 of segment base address
+
+GDT_Pointer:
+ dw NUM_GDT_ENTRIES*GDT_ENTRY_SZ ; limit
+ dd (SETUPSEG<<4) + GDT ; base address
+
+IDT_Pointer:
+ dw 0
+ dd 00
--- /dev/null
+; Symbol mangling macros
+; Copyright (c) 2001, David H. Hovemeyer <daveho@cs.umd.edu>
+; $Revision: 1.1 $
+
+; This file defines macros for dealing with externally-visible
+; symbols that must be mangled for some object file formats.
+; For example, PECOFF requires a leading underscore, while
+; ELF does not.
+
+; EXPORT defines a symbol as global
+; IMPORT references a symbol defined in another module
+
+; Thanks to Christopher Giese for providing the NASM macros
+; (thus saving me hours of frustration).
+
+%ifndef SYMBOL_ASM
+%define SYMBOL_ASM
+
+%ifdef NEED_UNDERSCORE
+
+%macro EXPORT 1
+[GLOBAL _%1]
+%define %1 _%1
+%endmacro
+
+%macro IMPORT 1
+[EXTERN _%1]
+%define %1 _%1
+%endmacro
+
+%else
+
+%macro EXPORT 1
+[GLOBAL %1]
+%endmacro
+
+%macro IMPORT 1
+[EXTERN %1]
+%endmacro
+
+%endif
+
+%endif
--- /dev/null
+/*
+ * Synchronization primitives
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/kthread.h>
+#include <geekos/int.h>
+#include <geekos/kassert.h>
+#include <geekos/screen.h>
+#include <geekos/synch.h>
+
+/*
+ * NOTES:
+ * - The GeekOS mutex and condition variable APIs are based on those
+ * in pthreads.
+ * - Unlike disabling interrupts, mutexes offer NO protection against
+ * concurrent execution of interrupt handlers. Mutexes and
+ * condition variables should only be used from kernel threads,
+ * with interrupts enabled.
+ */
+
+/* ----------------------------------------------------------------------
+ * Private functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * The mutex is currently locked.
+ * Atomically reenable preemption and wait in the
+ * mutex's wait queue.
+ */
+static void Mutex_Wait(struct Mutex *mutex)
+{
+ KASSERT(mutex->state == MUTEX_LOCKED);
+ KASSERT(g_preemptionDisabled);
+
+ Disable_Interrupts();
+ g_preemptionDisabled = false;
+ Wait(&mutex->waitQueue);
+ g_preemptionDisabled = true;
+ Enable_Interrupts();
+}
+
+/*
+ * Lock given mutex.
+ * Preemption must be disabled.
+ */
+static __inline__ void Mutex_Lock_Imp(struct Mutex* mutex)
+{
+ KASSERT(g_preemptionDisabled);
+
+ /* Make sure we're not already holding the mutex */
+ KASSERT(!IS_HELD(mutex));
+
+ /* Wait until the mutex is in an unlocked state */
+ while (mutex->state == MUTEX_LOCKED) {
+ Mutex_Wait(mutex);
+ }
+
+ /* Now it's ours! */
+ mutex->state = MUTEX_LOCKED;
+ mutex->owner = g_currentThread;
+}
+
+/*
+ * Unlock given mutex.
+ * Preemption must be disabled.
+ */
+static __inline__ void Mutex_Unlock_Imp(struct Mutex* mutex)
+{
+ KASSERT(g_preemptionDisabled);
+
+ /* Make sure mutex was actually acquired by this thread. */
+ KASSERT(IS_HELD(mutex));
+
+ /* Unlock the mutex. */
+ mutex->state = MUTEX_UNLOCKED;
+ mutex->owner = 0;
+
+ /*
+ * If there are threads waiting to acquire the mutex,
+ * wake one of them up. Note that it is legal to inspect
+ * the queue with interrupts enabled because preemption
+ * is disabled, and therefore we know that no thread can
+ * concurrently add itself to the queue.
+ */
+ if (!Is_Thread_Queue_Empty(&mutex->waitQueue)) {
+ Disable_Interrupts();
+ Wake_Up_One(&mutex->waitQueue);
+ Enable_Interrupts();
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+/*
+ * Initialize given mutex.
+ */
+void Mutex_Init(struct Mutex* mutex)
+{
+ mutex->state = MUTEX_UNLOCKED;
+ mutex->owner = 0;
+ Clear_Thread_Queue(&mutex->waitQueue);
+}
+
+/*
+ * Lock given mutex.
+ */
+void Mutex_Lock(struct Mutex* mutex)
+{
+ KASSERT(Interrupts_Enabled());
+
+ g_preemptionDisabled = true;
+ Mutex_Lock_Imp(mutex);
+ g_preemptionDisabled = false;
+}
+
+/*
+ * Unlock given mutex.
+ */
+void Mutex_Unlock(struct Mutex* mutex)
+{
+ KASSERT(Interrupts_Enabled());
+
+ g_preemptionDisabled = true;
+ Mutex_Unlock_Imp(mutex);
+ g_preemptionDisabled = false;
+}
+
+/*
+ * Initialize given condition.
+ */
+void Cond_Init(struct Condition* cond)
+{
+ Clear_Thread_Queue(&cond->waitQueue);
+}
+
+/*
+ * Wait on given condition (protected by given mutex).
+ */
+void Cond_Wait(struct Condition* cond, struct Mutex* mutex)
+{
+ KASSERT(Interrupts_Enabled());
+
+ /* Ensure mutex is held. */
+ KASSERT(IS_HELD(mutex));
+
+ /* Turn off scheduling. */
+ g_preemptionDisabled = true;
+
+ /*
+ * Release the mutex, but leave preemption disabled.
+ * No other threads will be able to run before this thread
+ * is able to wait. Therefore, this thread will not
+ * miss the eventual notification on the condition.
+ */
+ Mutex_Unlock_Imp(mutex);
+
+ /*
+ * Atomically reenable preemption and wait in the condition wait queue.
+ * Other threads can run while this thread is waiting,
+ * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
+ * to wake up this thread.
+ * On wakeup, disable preemption again.
+ */
+ Disable_Interrupts();
+ g_preemptionDisabled = false;
+ Wait(&cond->waitQueue);
+ g_preemptionDisabled = true;
+ Enable_Interrupts();
+
+ /* Reacquire the mutex. */
+ Mutex_Lock_Imp(mutex);
+
+ /* Turn scheduling back on. */
+ g_preemptionDisabled = false;
+}
+
+/*
+ * Wake up one thread waiting on the given condition.
+ * The mutex guarding the condition should be held!
+ */
+void Cond_Signal(struct Condition* cond)
+{
+ KASSERT(Interrupts_Enabled());
+ Disable_Interrupts(); /* prevent scheduling */
+ Wake_Up_One(&cond->waitQueue);
+ Enable_Interrupts(); /* resume scheduling */
+}
+
+/*
+ * Wake up all threads waiting on the given condition.
+ * The mutex guarding the condition should be held!
+ */
+void Cond_Broadcast(struct Condition* cond)
+{
+ KASSERT(Interrupts_Enabled());
+ Disable_Interrupts(); /* prevent scheduling */
+ Wake_Up(&cond->waitQueue);
+ Enable_Interrupts(); /* resume scheduling */
+}
--- /dev/null
+# -*- 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
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+/*
+ * GeekOS timer interrupt support
+ * Copyright (c) 2001,2003 David H. Hovemeyer <daveho@cs.umd.edu>
+ * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <limits.h>
+#include <geekos/io.h>
+#include <geekos/int.h>
+#include <geekos/irq.h>
+#include <geekos/kthread.h>
+#include <geekos/timer.h>
+
+#include <geekos/serial.h>
+
+#define HZ 100
+
+
+/*
+ * Global tick counter
+ */
+volatile ulong_t g_numTicks;
+
+/*
+ * Number of times the spin loop can execute during one timer tick
+ */
+static int s_spinCountPerTick;
+
+/*
+ * Number of ticks to wait before calibrating the delay loop.
+ */
+#define CALIBRATE_NUM_TICKS 3
+
+/*
+ * The default quantum; maximum number of ticks a thread can use before
+ * we suspend it and choose another.
+ */
+#define DEFAULT_MAX_TICKS 4
+
+/*
+ * Settable quantum.
+ */
+int g_Quantum = DEFAULT_MAX_TICKS;
+
+/*
+ * Ticks per second.
+ * FIXME: should set this to something more reasonable, like 100.
+ */
+#define TICKS_PER_SEC 18
+
+/*#define DEBUG_TIMER */
+#ifdef DEBUG_TIMER
+# define Debug(args...) Print(args)
+#else
+# define Debug(args...)
+#endif
+
+/* ----------------------------------------------------------------------
+ * Private functions
+ * ---------------------------------------------------------------------- */
+
+static void Timer_Interrupt_Handler(struct Interrupt_State* state)
+{
+ struct Kernel_Thread* current = g_currentThread;
+
+ Begin_IRQ(state);
+
+ SerialPrintLevel(10,"Host Timer Interrupt Handler Running\n");
+
+ /* Update global and per-thread number of ticks */
+ ++g_numTicks;
+ ++current->numTicks;
+
+
+ /*
+ * If thread has been running for an entire quantum,
+ * inform the interrupt return code that we want
+ * to choose a new thread.
+ */
+ if (current->numTicks >= g_Quantum) {
+ g_needReschedule = true;
+ }
+
+
+ End_IRQ(state);
+}
+
+/*
+ * Temporary timer interrupt handler used to calibrate
+ * the delay loop.
+ */
+static void Timer_Calibrate(struct Interrupt_State* state)
+{
+ Begin_IRQ(state);
+ if (g_numTicks < CALIBRATE_NUM_TICKS)
+ ++g_numTicks;
+ else {
+ /*
+ * Now we can look at EAX, which reflects how many times
+ * the loop has executed
+ */
+ /*Print("Timer_Calibrate: eax==%d\n", state->eax);*/
+ s_spinCountPerTick = INT_MAX - state->eax;
+ state->eax = 0; /* make the loop terminate */
+ }
+ End_IRQ(state);
+}
+
+/*
+ * Delay loop; spins for given number of iterations.
+ */
+static void Spin(int count)
+{
+ /*
+ * The assembly code is the logical equivalent of
+ * while (count-- > 0) { // waste some time }
+ * We rely on EAX being used as the counter
+ * variable.
+ */
+
+ int result;
+ __asm__ __volatile__ (
+ "1: decl %%eax\n\t"
+ "cmpl $0, %%eax\n\t"
+ "nop; nop; nop; nop; nop; nop\n\t"
+ "nop; nop; nop; nop; nop; nop\n\t"
+ "jg 1b"
+ : "=a" (result)
+ : "a" (count)
+ );
+}
+
+/*
+ * Calibrate the delay loop.
+ * This will initialize s_spinCountPerTick, which indicates
+ * how many iterations of the loop are executed per timer tick.
+ */
+static void Calibrate_Delay(void)
+{
+ Disable_Interrupts();
+
+ /* Install temporarily interrupt handler */
+ Install_IRQ(TIMER_IRQ, &Timer_Calibrate);
+ Enable_IRQ(TIMER_IRQ);
+
+ Enable_Interrupts();
+
+ /* Wait a few ticks */
+ while (g_numTicks < CALIBRATE_NUM_TICKS)
+ ;
+
+ /*
+ * Execute the spin loop.
+ * The temporary interrupt handler will overwrite the
+ * loop counter when the next tick occurs.
+ */
+ Spin(INT_MAX);
+
+ Disable_Interrupts();
+
+ /*
+ * Mask out the timer IRQ again,
+ * since we will be installing a real timer interrupt handler.
+ */
+ Disable_IRQ(TIMER_IRQ);
+ Enable_Interrupts();
+}
+
+/* ----------------------------------------------------------------------
+ * Public functions
+ * ---------------------------------------------------------------------- */
+
+void Init_Timer(void)
+{
+ ushort_t foo = 1193182L / HZ;
+
+ PrintBoth("Initializing timer and setting to %d Hz...\n",HZ);
+
+ /* Calibrate for delay loop */
+ Calibrate_Delay();
+
+ PrintBoth("Delay loop: %d iterations per tick\n", s_spinCountPerTick);
+
+ // Set Timer to HZ
+
+ Out_Byte(0x43,0x36); // channel 0, LSB/MSB, mode 3, binary
+ Out_Byte(0x40, foo & 0xff); // LSB
+ Out_Byte(0x40, foo >>8); // MSB
+
+ /* Install an interrupt handler for the timer IRQ */
+
+ Install_IRQ(TIMER_IRQ, &Timer_Interrupt_Handler);
+ Enable_IRQ(TIMER_IRQ);
+}
+
+
+#define US_PER_TICK (TICKS_PER_SEC * 1000000)
+
+/*
+ * Spin for at least given number of microseconds.
+ * FIXME: I'm sure this implementation leaves a lot to
+ * be desired.
+ */
+void Micro_Delay(int us)
+{
+ int num = us * s_spinCountPerTick;
+ int denom = US_PER_TICK;
+
+ int numSpins = num / denom;
+ int rem = num % denom;
+
+ if (rem > 0)
+ ++numSpins;
+
+ Debug("Micro_Delay(): num=%d, denom=%d, spin count = %d\n", num, denom, numSpins);
+
+ Spin(numSpins);
+}
--- /dev/null
+/*
+ * Trap handlers
+ * Copyright (c) 2001,2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+#include <geekos/idt.h>
+#include <geekos/kthread.h>
+#include <geekos/defs.h>
+#include <geekos/trap.h>
+#include <geekos/serial.h>
+
+/*
+ * TODO: need to add handlers for other exceptions (such as bounds
+ * check, debug, etc.)
+ */
+
+/*
+ * Handler for general protection faults and other bad errors.
+ * Kill the current thread (which caused the fault).
+ */
+static void GPF_Handler(struct Interrupt_State* state)
+{
+ /* Send the thread to the reaper... */
+ SerialPrintLevel(1000,"Exception %d received, killing thread %p\n",state->intNum, g_currentThread);
+ Dump_Interrupt_State(state);
+
+ Exit(-1);
+
+ /* We will never get here */
+ KASSERT(false);
+}
+
+/*
+ * Initialize handlers for processor traps.
+ */
+void Init_Traps(void)
+{
+ PrintBoth("Initializing Traps\n");
+ Install_Interrupt_Handler(12, &GPF_Handler); /* stack exception */
+ Install_Interrupt_Handler(13, &GPF_Handler); /* general protection fault */
+}
--- /dev/null
+/*
+ * x86 TSS data structure and routines
+ * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
+ * $Revision: 1.1 $
+ *
+ * This is free software. You are permitted to use,
+ * redistribute, and modify it as specified in the file "COPYING".
+ */
+
+/*
+ * Source: _Protected Mode Software Architecture_ by Tom Shanley,
+ * ISBN 020155447X.
+ */
+
+#include <geekos/kassert.h>
+#include <geekos/defs.h>
+#include <geekos/gdt.h>
+#include <geekos/segment.h>
+#include <geekos/string.h>
+#include <geekos/tss.h>
+
+#include <geekos/serial.h>
+
+/*
+ * We use one TSS in GeekOS.
+ */
+static struct TSS s_theTSS;
+
+// JRL: why??? Should just call Alloc_Page(), otherwise we have dependencies all over the place
+//static struct TSS *s_theTSS = (struct TSS *) TSS_LOCATION;
+
+static struct Segment_Descriptor *s_tssDesc;
+static ushort_t s_tssSelector;
+
+static void __inline__ Load_Task_Register(void)
+{
+ /* Critical: TSS must be marked as not busy */
+ s_tssDesc->type = 0x09;
+
+ /* Load the task register */
+ __asm__ __volatile__ (
+ "ltr %0"
+ :
+ : "a" (s_tssSelector)
+ );
+}
+
+/*
+ * Initialize the kernel TSS. This must be done after the memory and
+ * GDT initialization, but before the scheduler is started.
+ */
+void Init_TSS(void)
+{
+ PrintBoth("Initializing TSS\n");
+
+ s_tssDesc = Allocate_Segment_Descriptor();
+ KASSERT(s_tssDesc != 0);
+
+ memset(&s_theTSS, '\0', sizeof(struct TSS));
+ Init_TSS_Descriptor(s_tssDesc, &s_theTSS);
+
+ s_tssSelector = Selector(0, true, Get_Descriptor_Index(s_tssDesc));
+
+ Load_Task_Register();
+}
+
+/*
+ * Set kernel stack pointer.
+ * This should be called before switching to a new
+ * user process, so that interrupts occurring while executing
+ * in user mode will be delivered on the correct stack.
+ */
+void Set_Kernel_Stack_Pointer(ulong_t esp0)
+{
+ s_theTSS.ss0 = KERNEL_DS;
+ s_theTSS.esp0 = esp0;
+
+ /*
+ * NOTE: I read on alt.os.development that it is necessary to
+ * reload the task register after modifying a TSS.
+ * I haven't verified this in the IA32 documentation,
+ * but there is certainly no harm in being paranoid.
+ */
+ Load_Task_Register();
+}
--- /dev/null
+; 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
--- /dev/null
+#include <conio.h>
+#include <stddef.h>
+
+void *Malloc(size_t n)
+{
+ Print("Malloc not implemented in user mode\n");
+ return 0;
+}