summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
Diffstat (limited to 'util')
-rw-r--r--util/BuildImage.c415
-rw-r--r--util/Make.mk59
-rw-r--r--util/Offsets.c250
-rw-r--r--util/alternatives/Make.mk56
-rw-r--r--util/alternatives/README31
-rw-r--r--util/alternatives/kmem.c749
-rw-r--r--util/alternatives/lib.c56
-rw-r--r--util/gdbinit.tmpl31
-rw-r--r--util/listblob.c248
-rw-r--r--util/mergedep.pl86
-rw-r--r--util/mkblob.c324
11 files changed, 2305 insertions, 0 deletions
diff --git a/util/BuildImage.c b/util/BuildImage.c
new file mode 100644
index 0000000..2124c9d
--- /dev/null
+++ b/util/BuildImage.c
@@ -0,0 +1,415 @@
+/**
+** SCCS ID: @(#)BuildImage.c 2.2 1/16/25
+**
+** @file BuildImage.c
+**
+** @author K. Reek
+** @author Jon Coles
+** @author Warren R. Carithers
+** @author Garrett C. Smith
+**
+** Modify the bootstrap image to include the information
+** on the programs to be loaded, and produce the file
+** that contains the concatenation of these programs.
+**
+*/
+
+#define _POSIX_C_SOURCE 200809L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#define TRUE 1
+#define FALSE 0
+
+#define DRIVE_FLOPPY 0x00
+#define DRIVE_USB 0x80
+
+#define SECT_SIZE 512
+
+char *progname; /* invocation name of this program */
+char *bootstrap_filename; /* path of file holding bootstrap program */
+char *output_filename; /* path of disk image file */
+FILE *out; /* output stream for disk image file */
+short drive = DRIVE_USB; /* boot drive */
+
+/*
+** Array into which program information will be stored, starting at the
+** end and moving back toward the front. The array is the same size as
+** a sector, which is guaranteed to be larger than the maximum possible
+** space available for this stuff in the bootstrap image. Thus, the
+** bootstrap image itself (and the amount of space available on the
+** device) are the only limiting factors on how many program sections
+** can be loaded.
+*/
+
+#define N_INFO ( SECT_SIZE / sizeof( short ) )
+
+short info[ N_INFO ];
+int n_info = N_INFO;
+
+/**
+** quit with an appropriate message
+**
+** @param msg NULL, or a message to be printed to stderr
+** @param call_perror non-zero if perror() should be used; else,
+** fprintf() will be used
+**
+** does not return
+*/
+void quit( char *msg, int call_perror ) {
+ if( msg != NULL ){
+ // preserve the error code in case we need it
+ int err_num = errno;
+ fprintf( stderr, "%s: ", progname );
+ errno = err_num;
+ if( call_perror ){
+ perror( msg );
+ } else {
+ fprintf( stderr, "%s\n", msg );
+ }
+ }
+ if( output_filename != NULL ){
+ unlink( output_filename );
+ }
+ exit( EXIT_FAILURE );
+ // NOTREACHED
+}
+
+const char usage_error_msg[] =
+ "\nUsage: %s [ -d drive ] -b bootfile -o outfile { progfile loadpt } ...\n\n"
+ "\t'drive' is either 'floppy' or 'usb' (default 'usb')\n\n"
+ "\tThere must be at least one program file and load point.\n\n"
+ "\tLoad points may be specified either as 32-bit quantities in hex,\n"
+ "\tdecimal or octal (e.g. 0x10c00, 68608, 0206000 are all equivalent),\n"
+ "\tor as an explicit segment:offset pair whose digits are always\n"
+ "\tinterpreted as hexadecimal values (e.g. 10c0:0000, 1000:0c00 are\n"
+ "\tboth equivalent to the previous examples).\n\n";
+
+/**
+** print a usage message and then call quit()
+**
+** does not return
+*/
+void usage_error( void ){
+ fprintf( stderr, usage_error_msg, progname );
+ quit( NULL, FALSE );
+ // NOTREACHED
+}
+
+/**
+** copy the contents of a binary file into the output file, padding the
+** last sector with NUL bytes
+**
+** @param in open FILE to be read
+** @return the number of sectors copied from the file
+*/
+int copy_file( FILE *in ){
+ int n_sectors = 0;
+ char buf[ SECT_SIZE ];
+ int n_bytes;
+ int i;
+
+ /*
+ ** Copy the file to the output, being careful that the
+ ** last sector is padded with null bytes out to the
+ ** sector size.
+ */
+ n_sectors = 0;
+ while( (n_bytes = fread( buf, 1, sizeof( buf ), in )) > 0 ){
+ // pad this sector out to block size
+ if( n_bytes < sizeof( buf ) ){
+ int i;
+
+ for( i = n_bytes; i < sizeof( buf ); i += 1 ){
+ buf[ i ] = '\0';
+ }
+ }
+ if( fwrite( buf, 1, sizeof( buf ), out ) != sizeof( buf ) ){
+ quit( "Write failed or was wrong size", FALSE );
+ }
+ n_sectors += 1;
+ }
+ return n_sectors;
+}
+
+/**
+** process a file whose contents should be at a specific'
+** address in memory when the program is loaded
+**
+** @param name path to the file to be copied
+** @param addr string containing the load address
+*/
+void process_file( char *name, char *addr ){
+ long address;
+ short segment, offset;
+ int n_bytes;
+
+ /*
+ ** Open the input file.
+ */
+ FILE *in = fopen( name, "rb" );
+ if( in == NULL ){
+ quit( name, TRUE );
+ }
+
+ /*
+ ** Copy the file to the output, being careful that the
+ ** last block is padded with null bytes.
+ */
+ int n_sectors = copy_file( in );
+ fclose( in );
+
+ /*
+ ** Decode the address they gave us. We'll accept two forms:
+ ** "nnnn:nnnn" for a segment:offset value (assumed to be hex),
+ ** "nnnnnnn" for a decimal, hex, or octal value
+ */
+ int valid_address = FALSE;
+ char *cp = strchr( addr, ':' );
+ if( cp != NULL ){
+ // must be in nnnn:nnnn form exactly
+ if( strlen( addr ) == 9 && cp == addr + 4 ){
+ char *ep1, *ep2;
+ int a1, a2;
+
+ segment = strtol( addr, &ep1, 16 );
+ offset = strtol( addr + 5, &ep2, 16 );
+ address = ( segment << 4 ) + offset;
+ valid_address = *ep1 == '\0' && *ep2 == '\0';
+ } else {
+ fprintf( stderr, "Bad address format - '%s'\n", addr );
+ quit( NULL, FALSE );
+ }
+ } else {
+ // just a number, possibly hex or octal
+ char *ep;
+
+ address = strtol( addr, &ep, 0 );
+ segment = (short)( address >> 4 );
+ offset = (short)( address & 0xf );
+ valid_address = *ep == '\0' && address <= 0x0009ffff;
+ }
+
+ if( !valid_address ){
+ fprintf( stderr, "%s: Invalid address: %s\n", progname, addr );
+ quit( NULL, FALSE );
+ }
+
+ /*
+ ** Make sure the program will fit!
+ */
+ if( address + n_sectors * SECT_SIZE > 0x0009ffff ){
+ fprintf( stderr, "Program %s too large to start at 0x%08x\n",
+ name, (unsigned int) address );
+ quit( NULL, FALSE );
+ }
+
+ if( n_info < 3 ){
+ quit( "Too many programs!", FALSE );
+ }
+
+
+ /*
+ ** Looks good: report and store the information.
+ */
+ fprintf( stderr, " %s: %d sectors, loaded at 0x%x\n",
+ name, n_sectors, (unsigned int) address );
+
+ info[ --n_info ] = n_sectors;
+ info[ --n_info ] = segment;
+ info[ --n_info ] = offset;
+}
+
+/*
+** Global variables set by getopt()
+*/
+
+extern int optind, optopt;
+extern char *optarg;
+
+/**
+** process the command-line arguments
+**
+** @param ac the count of entries in av
+** @param av the argument vector
+*/
+void process_args( int ac, char **av ) {
+ int c;
+
+ while( (c=getopt(ac,av,":d:o:b:")) != EOF ) {
+
+ switch( c ) {
+
+ case ':': /* missing arg value */
+ fprintf( stderr, "missing operand after -%c\n", optopt );
+ /* FALL THROUGH */
+
+ case '?': /* error */
+ usage_error();
+ /* NOTREACHED */
+
+ case 'b': /* -b bootstrap_file */
+ bootstrap_filename = optarg;
+ break;
+
+ case 'd': /* -d drive */
+ switch( *optarg ) {
+ case 'f': drive = DRIVE_FLOPPY; break;
+ case 'u': drive = DRIVE_USB; break;
+ default: usage_error();
+ }
+ break;
+
+ case 'o': /* -o output_file */
+ output_filename = optarg;
+ break;
+
+ default:
+ usage_error();
+
+ }
+
+ }
+
+ if( !bootstrap_filename ) {
+ fprintf( stderr, "%s: no bootstrap file specified\n", progname );
+ exit( 2 );
+ }
+
+ if( !output_filename ) {
+ fprintf( stderr, "%s: no disk image file specified\n", progname );
+ exit( 2 );
+ }
+
+ /*
+ ** Must have at least two remaining arguments (file to load,
+ ** address at which it should be loaded), and must have an
+ ** even number of remaining arguments.
+ */
+ int remain = ac - optind;
+ if( remain < 2 || (remain & 1) != 0 ) {
+ usage_error();
+ }
+
+}
+
+/**
+** build a bootable image file from one or more binary files
+**
+** usage:
+** BuildImage [ -d drive ] -b bootfile -o outfile { binfile1 loadpt1 } ... ]
+**
+** @param ac command-line argument count
+** @param av command-line argument vector
+** @return EXIT_SUCCESS or EXIT_FAILURE
+*/
+int main( int ac, char **av ) {
+ FILE *bootimage;
+ int bootimage_size;
+ int n_bytes, n_words;
+ short existing_data[ N_INFO ];
+ int i;
+
+ /*
+ ** Save the program name for error messages
+ */
+ progname = strrchr( av[ 0 ], '/' );
+ if( progname != NULL ){
+ progname++;
+ } else {
+ progname = av[ 0 ];
+ }
+
+ /*
+ ** Process arguments
+ */
+ process_args( ac, av );
+
+ /*
+ ** Open the output file
+ */
+
+ out = fopen( output_filename, "wb+" );
+ if( out == NULL ){
+ quit( output_filename, TRUE );
+ }
+
+ /*
+ ** Open the bootstrap file and copy it to the output image.
+ */
+ bootimage = fopen( bootstrap_filename, "rb" );
+ if( bootimage == NULL ){
+ quit( bootstrap_filename, TRUE );
+ }
+
+ /*
+ ** Remember the size of the bootstrap for later, as we
+ ** need to patch some things into it
+ */
+ int n_sectors = copy_file( bootimage );
+ fclose( bootimage );
+
+ bootimage_size = n_sectors * SECT_SIZE;
+ fprintf( stderr, " %s: %d sectors\n", bootstrap_filename, n_sectors );
+
+ /*
+ ** Process the programs one by one
+ */
+ ac -= optind;
+ av += optind;
+ while( ac >= 2 ){
+ process_file( av[ 0 ], av[ 1 ] );
+ ac -= 2; av += 2;
+ }
+
+ /*
+ ** Check for oddball leftover argument
+ */
+ if( ac > 0 ){
+ usage_error();
+ }
+
+ /*
+ ** Seek to where the array of module data must begin and read
+ ** what's already there.
+ */
+ n_words = ( N_INFO - n_info );
+ n_bytes = n_words * sizeof( info[ 0 ] );
+ fseek( out, bootimage_size - n_bytes, SEEK_SET );
+ if( fread( existing_data, sizeof(info[0]), n_words, out ) != n_words ){
+ quit( "Read from boot image failed or was too short", FALSE );
+ }
+
+ /*
+ ** If that space is non-zero, we have a problem
+ */
+ for( i = 0; i < n_words; i += 1 ){
+ if( existing_data[ i ] != 0 ){
+ quit( "Too many programs to load!", FALSE );
+ }
+ }
+
+ /*
+ ** We know that we're only overwriting zeros at the end of
+ ** the bootstrap image, so it is ok to go ahead and do it.
+ */
+ fseek( out, bootimage_size - n_bytes, SEEK_SET );
+ if( fwrite( info + n_info, sizeof( info[ 0 ] ), n_words, out ) != n_words ){
+ quit( "Write to boot image failed or was too short", FALSE );
+ }
+
+ /*
+ ** Write the drive index to the image.
+ */
+ fseek( out, 508, SEEK_SET );
+ fwrite( (void *)&drive, sizeof(drive), 1, out );
+
+ fclose( out );
+
+ return EXIT_SUCCESS;
+
+}
diff --git a/util/Make.mk b/util/Make.mk
new file mode 100644
index 0000000..0f45625
--- /dev/null
+++ b/util/Make.mk
@@ -0,0 +1,59 @@
+#
+# Makefile fragment for the utility programs
+#
+# THIS IS NOT A COMPLETE Makefile - run GNU make in the top-level
+# directory, and this will be pulled in automatically.
+#
+
+###################
+# FILES SECTION #
+###################
+
+UTIL_BIN := BuildImage Offsets mkblob listblob
+
+###################
+# RULES SECTION #
+###################
+
+# how to make everything
+util: $(UTIL_BIN)
+
+#
+# Special rules for creating the utility programs. These are required
+# because we don't want to use the same options as for the standalone
+# binaries - we want these to be compiled as "normal" programs.
+#
+
+BuildImage: util/BuildImage.c
+ @mkdir -p $(@D)
+ $(CC) -std=c99 -ggdb -o BuildImage util/BuildImage.c
+
+mkblob: util/mkblob.c
+ @mkdir -p $(@D)
+ $(CC) -std=c99 -ggdb -o mkblob util/mkblob.c
+
+listblob: util/listblob.c
+ @mkdir -p $(@D)
+ $(CC) -std=c99 -ggdb -o listblob util/listblob.c
+
+#
+# Offsets is compiled using -mx32 to force a 32-bit execution environment
+# for a program that runs under a 64-bit operating system. This ensures
+# that pointers and long ints are 32 bits rather than 64 bits, which is
+# critical to correctly determining the size of types and byte offsets for
+# fields in structs. We also compile with "-fno-builtin" to avoid signature
+# clashes between declarations in our system and function declarations
+# built into the C compiler.
+#
+# If compiled with the CPP macro CREATE_HEADER_FILE defined, Offsets
+# accepts a command-line argument "-h". This causes it to write its
+# output as a standard C header file into a file named "include/offsets.h"
+# where it can be included into other source files (e.g., to provide
+# sizes of structs in C and assembly, or to provide byte offsets into
+# structures for use in assembly code).
+#
+
+Offsets: util/Offsets.c
+ @mkdir -p $(@D)
+ $(CC) -mx32 -std=c99 $(INCLUDES) -fno-builtin \
+ -o Offsets util/Offsets.c
diff --git a/util/Offsets.c b/util/Offsets.c
new file mode 100644
index 0000000..3cf5a46
--- /dev/null
+++ b/util/Offsets.c
@@ -0,0 +1,250 @@
+/*
+** SCCS ID: @(#)Offsets.c 2.2 1/23/25
+**
+** @file Offsets.c
+**
+** @author Warren R. Carithers
+**
+** Print byte offsets for fields in various structures.
+**
+** This program exists to simplify life. If/when fields in a structure
+** are changed, this can be modified, recompiled and executed to come up with
+** byte offsets for use in accessing structure fields from assembly language.
+** It makes use of the C 'offsetof' macro (defined since C89).
+**
+** IMPORTANT NOTE: compiling this on a 64-bit architecture will yield
+** incorrect results by default, as 64-bit GCC versions most often use
+** the LP64 model (longs and pointers are 64 bits). Add the "-mx32"
+** option to the compiler (compile for x86_64, but use 32-bit sizes),
+** and make sure you have the 'libc6-dev-i386' package installed (for
+** Ubuntu systems).
+**
+** WATCH FOR clashes with standard system functions; compile with the
+** "-fno-builtin" option to avoid warnings about clashes with builtin
+** function declarations.
+**
+** Writes to stdout. Output contains information about type sizes and
+** byte offsets for fields within structures. If invoked with the -h
+** option, the output takes the form of a standard C header file;
+** otherwise, the information is printed in ordinary text form.
+**
+** If compiled with the CREATE_HEADER_FILE macro defined, when invoked
+** with the -h option, writes the data directly into a file named
+** "offsets.h".
+*/
+
+// make sure we get all the kernel stuff
+#define KERNEL_SRC
+
+// include any of our headers that define data structures to be described
+#include <common.h>
+#include <procs.h>
+
+// avoid complaints about NULL from stdio.h
+#ifdef NULL
+#undef NULL
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+** We don't include <time.h> because of conflicts with a time_t data
+** type we may be defining; instead, we provide our own prototypes for
+** ctime() and time().
+*/
+
+extern char *ctime( const __time_t *timep );
+extern __time_t time( __time_t *tloc );
+
+/*
+** Header comment, including header guard
+**
+** No newline on the "Creation date" line, because ctime()
+** puts a newline at the end of the string it produces.
+*/
+char h_prefix[] = "/**\n"
+"** @file\toffsets.h\n"
+"**\n"
+"** GENERATED AUTOMATICALLY - DO NOT EDIT\n"
+"**\n"
+"** Creation date: %s"
+"**\n"
+"** This header file contains C Preprocessor macros which expand\n"
+"** into the byte offsets needed to reach fields within structs\n"
+"** used in the baseline system. Should those struct declarations\n"
+"** change, the Offsets program should be modified (if needed),\n"
+"** recompiled, and re-run to recreate this file.\n"
+"*/\n"
+"\n"
+"#ifndef OFFSETS_H_\n"
+"#define OFFSETS_H_\n";
+
+/*
+** Header guard suffix
+*/
+char h_suffix[] = "\n"
+"#endif\n";
+
+// are we generating the .h file?
+int genheader = 0;
+
+// header file stream
+FILE *hfile;
+
+// prefix for header file lines
+
+// produce a report line
+void process( const char *sname, const char *field, size_t bytes ) {
+ if( genheader ) {
+ char name[64];
+ sprintf( name, "%s_%s", sname, field );
+ fprintf( hfile, "#define\t%-23s\t%u\n", name, bytes );
+ } else {
+ printf( " %-10s %u\n", field, bytes );
+ }
+}
+
+#ifdef CREATE_HEADER_FILE
+// dump out the header
+void setheader( void ) {
+ // trigger output into the header file
+ genheader = 1;
+
+ hfile = fopen( "offsets.h", "w" );
+ if( hfile == NULL ) {
+ perror( "offsets.h" );
+ exit( 1 );
+ }
+
+ __time_t t;
+ (void) time( &t );
+
+ fprintf( hfile, h_prefix, ctime(&t) );
+}
+#endif /* CREATE_HEADER_FILE */
+
+// introduce an "offsets" section for structs
+void hsection( const char *name, const char *typename, size_t size ) {
+ if( genheader ) {
+ fprintf( hfile, "\n// %s structure\n\n", typename );
+ process( "SZ", name, size );
+ fputc( '\n', hfile );
+ } else {
+ printf( "Offsets into %s (%u bytes):\n", typename, size );
+ }
+}
+
+// introduce a "sizes" section for types
+void tsection( const char *name, const char *typename ) {
+ if( genheader ) {
+ fprintf( hfile, "\n// Sizes of %s types\n\n", typename );
+ } else {
+ printf( "Sizes of %s types:\n", typename );
+ }
+}
+
+int main( int argc, char *argv[] ) {
+
+ hfile = stdout;
+
+ if( argc > 1 ) {
+#ifdef CREATE_HEADER_FILE
+ // only accept one argument
+ if( argc == 2 ) {
+ // -h: produce an offsets.h header file
+ if( argv[1][0] == '-' && argv[1][1] == '-h' ) {
+ setheader();
+ }
+ } else {
+ fprintf( stderr, "usage: %s [-h]\n", argv[0] );
+ exit( 1 );
+ }
+#else
+ // we accept no arguments
+ fprintf( stderr, "usage: %s\n", argv[0] );
+ exit( 1 );
+#endif /* CREATE_HEADER_FILE */
+ }
+
+ /*
+ ** Basic and simple/opaque types
+ */
+
+ tsection( "SZ", "basic" );
+ process( "SZ", "char", sizeof(char) );
+ process( "SZ", "short", sizeof(short) );
+ process( "SZ", "int", sizeof(int) );
+ process( "SZ", "long", sizeof(long) );
+ process( "SZ", "long_long", sizeof(long long) );
+ process( "SZ", "pointer", sizeof(void *) );
+ fputc( '\n', hfile );
+
+ tsection( "SZ", "our" );
+ process( "SZ", "int8_t", sizeof(int8_t) );
+ process( "SZ", "uint8_t", sizeof(uint8_t) );
+ process( "SZ", "int16_t", sizeof(int16_t) );
+ process( "SZ", "uint16_t", sizeof(uint16_t) );
+ process( "SZ", "int32_t", sizeof(int32_t) );
+ process( "SZ", "uint32_t", sizeof(uint32_t) );
+ process( "SZ", "int64_t", sizeof(int64_t) );
+ process( "SZ", "uint64_t", sizeof(uint64_t) );
+ process( "SZ", "bool_t", sizeof(bool_t) );
+ fputc( '\n', hfile );
+
+ /*
+ ** Structured types whose fields we are describing
+ */
+
+ // add entries for each type here, as needed
+
+ hsection( "CTX", "context_t", sizeof(context_t) );
+ process( "CTX", "ss", offsetof(context_t,ss) );
+ process( "CTX", "gs", offsetof(context_t,gs) );
+ process( "CTX", "fs", offsetof(context_t,fs) );
+ process( "CTX", "es", offsetof(context_t,es) );
+ process( "CTX", "ds", offsetof(context_t,ds) );
+ process( "CTX", "edi", offsetof(context_t,edi) );
+ process( "CTX", "esi", offsetof(context_t,esi) );
+ process( "CTX", "ebp", offsetof(context_t,ebp) );
+ process( "CTX", "esp", offsetof(context_t,esp) );
+ process( "CTX", "ebx", offsetof(context_t,ebx) );
+ process( "CTX", "edx", offsetof(context_t,edx) );
+ process( "CTX", "ecx", offsetof(context_t,ecx) );
+ process( "CTX", "eax", offsetof(context_t,eax) );
+ process( "CTX", "vector", offsetof(context_t,vector) );
+ process( "CTX", "code", offsetof(context_t,code) );
+ process( "CTX", "eip", offsetof(context_t,eip) );
+ process( "CTX", "cs", offsetof(context_t,cs) );
+ process( "CTX", "eflags", offsetof(context_t,eflags) );
+ fputc( '\n', hfile );
+
+ hsection( "SCT", "section_t", sizeof(section_t) );
+ process( "SCT", "length", offsetof(section_t,length) );
+ process( "SCT", "addr", offsetof(section_t,addr) );
+ fputc( '\n', hfile );
+
+ hsection( "PCB", "pcb_t", sizeof(pcb_t) );
+ process( "PCB", "context", offsetof(pcb_t,context) );
+ process( "PCB", "next", offsetof(pcb_t,next) );
+ process( "PCB", "parent", offsetof(pcb_t,parent) );
+ process( "PCB", "wakeup", offsetof(pcb_t,wakeup) );
+ process( "PCB", "exit_status", offsetof(pcb_t,exit_status) );
+ process( "PCB", "pdir", offsetof(pcb_t,pdir) );
+ process( "PCB", "sects", offsetof(pcb_t,sects) );
+ process( "PCB", "pid", offsetof(pcb_t,pid) );
+ process( "PCB", "state", offsetof(pcb_t,state) );
+ process( "PCB", "priority", offsetof(pcb_t,priority) );
+ process( "PCB", "ticks", offsetof(pcb_t,ticks) );
+ fputc( '\n', hfile );
+
+ // finish up the offsets.h file if we need to
+ if( genheader ) {
+ fputs( h_suffix, hfile );
+ fclose( hfile );
+ }
+
+ return( 0 );
+}
diff --git a/util/alternatives/Make.mk b/util/alternatives/Make.mk
new file mode 100644
index 0000000..d5703e8
--- /dev/null
+++ b/util/alternatives/Make.mk
@@ -0,0 +1,56 @@
+#
+# Makefile fragment for the utility programs
+#
+# THIS IS NOT A COMPLETE Makefile - run GNU make in the top-level
+# directory, and this will be pulled in automatically.
+#
+
+SUBDIRS += util
+
+###################
+# RULES SECTION #
+###################
+
+# how to make everything
+util: $(BUILDDIR)/util/BuildImage $(BUILDDIR)/util/Offsets \
+ $(BUILDDIR)/util/mkblob $(BUILDDIR)/util/listblob
+
+#
+# Special rules for creating the utility programs. These are required
+# because we don't want to use the same options as for the standalone
+# binaries - we want these to be compiled as "normal" programs.
+#
+
+$(BUILDDIR)/util/BuildImage: util/BuildImage.c
+ @mkdir -p $(@D)
+ $(CC) -std=c99 -o $(BUILDDIR)/util/BuildImage util/BuildImage.c
+
+$(BUILDDIR)/util/mkblob: util/mkblob.c
+ @mkdir -p $(@D)
+ $(CC) -std=c99 -o $(BUILDDIR)/util/mkblob util/mkblob.c
+
+$(BUILDDIR)/util/listblob: util/listblob.c
+ @mkdir -p $(@D)
+ $(CC) -std=c99 -o $(BUILDDIR)/util/listblob util/listblob.c
+
+#
+# Offsets is compiled using -mx32 to force a 32-bit execution environment
+# for a program that runs under a 64-bit operating system. This ensures
+# that pointers and long ints are 32 bits rather than 64 bits, which is
+# critical to correctly determining the size of types and byte offsets for
+# fields in structs. We also compile with "-fno-builtin" to avoid signature
+# clashes between declarations in our system and function declarations
+# built into the C compiler.
+#
+# If compiled with the CPP macro CREATE_HEADER_FILE defined, Offsets
+# accepts a command-line argument "-h". This causes it to write its
+# output as a standard C header file into a file named "include/offsets.h"
+# where it can be included into other source files (e.g., to provide
+# sizes of structs in C and assembly, or to provide byte offsets into
+# structures for use in assembly code).
+#
+
+$(BUILDDIR)/util/Offsets: util/Offsets.c
+ @mkdir -p $(@D)
+ $(CC) -mx32 -std=c99 $(INCLUDES) -fno-builtin \
+ -o $(BUILDDIR)/util/Offsets util/Offsets.c
diff --git a/util/alternatives/README b/util/alternatives/README
new file mode 100644
index 0000000..ae4dfbe
--- /dev/null
+++ b/util/alternatives/README
@@ -0,0 +1,31 @@
+This directory contains "alternative" versions of some pieces of the system.
+
+Things included here:
+
+ Make.mk
+ This version of the Makefile fragment puts the utility programs
+ in build/util instead of in the top-level directory.
+
+ kmem.c
+ A version of the memory allocator that works with blocks of memory
+ that aren't exactly one page in length. During initialilzation, it
+ just adds each memory region identified during the boot process by
+ calls to the BIOS; as pages are requested, they are carved out of
+ these large blocks. The freelist is ordered by starting block
+ address, and allocation uses a first-fit strateby.
+
+ The allocation function has this prototype:
+
+ void *km_page_alloc( unsigned int count );
+
+ This allows the allocation of multiple contiguous pages. As pages
+ are freed, they are merged back into the freelist, and adjacent
+ free pages are coalesced into single, larger blocks.
+
+ lib.c
+ This file pulls in all the individual .c files for the common
+ library functions in the lib/ directory. It is intended as an
+ alternative to having the libk.a archive file for the kernel;
+ instead of linking against that library, the lib.o file can
+ just be provided to the linker when the kernel is created,
+ and all the common library functions will be available.
diff --git a/util/alternatives/kmem.c b/util/alternatives/kmem.c
new file mode 100644
index 0000000..f1abf02
--- /dev/null
+++ b/util/alternatives/kmem.c
@@ -0,0 +1,749 @@
+/**
+** @file kmem.c
+**
+** @author Warren R. Carithers
+** @author Kenneth Reek
+** @author 4003-506 class of 20013
+**
+** @brief Functions to perform dynamic memory allocation in the OS.
+**
+** NOTE: these should NOT be called by user processes!
+**
+** This allocator functions as a simple "slab" allocator; it allows
+** allocation of either 4096-byte ("page") or 1024-byte ("slice")
+** chunks of memory from the free pool. The free pool is initialized
+** using the memory map provided by the BIOS during the boot sequence,
+** and contains a series of blocks which are multiples of 4K bytes and
+** which are aligned at 4K boundaries; they are held in the free list
+** in order by base address.
+**
+** The "page" allocator allows allocation of one or more 4K blocks
+** at a time. Requests are made for a specific number of 4K pages;
+** the allocator locates the first free list entry that contains at
+** least the requested amount of space. If that entry is the exact
+** size requested, it is unlinked and returned; otherwise, the entry
+** is split into a chunk of the requested size and the remainder.
+** The chunk is returned, and the remainder replaces the original
+** block in the free list. On deallocation, the block is inserted
+** at the appropriate place in the free list, and physically adjacent
+** blocks are coalesced into single, larger blocks. If a multi-page
+** block is allocated, it should be deallocated one page at a time,
+** because there is no record of the size of the original allocation -
+** all we know is that it is N*4K bytes in length, so it's up to the
+** requesting code to figure this out.
+**
+** The "slice" allocator operates by taking blocks from the "page"
+** allocator and splitting them into four 1K slices, which it then
+** manages. Requests are made for slices one at a time. If the free
+** list contains an available slice, it is unlinked and returned;
+** otherwise, a page is requested from the page allocator, split into
+** slices, and the slices are added to the free list, after which the
+** first one is returned. The slice free list is a simple linked list
+** of these 1K blocks; because they are all the same size, no ordering
+** is done on the free list, and no coalescing is performed.
+**
+*/
+
+#define KERNEL_SRC
+
+#include <common.h>
+
+// all other framework includes are next
+#include <lib.h>
+
+#include <x86/arch.h>
+#include <x86/bios.h>
+#include <bootstrap.h>
+#include <cio.h>
+
+#include <kmem.h>
+
+/*
+** PRIVATE DEFINITIONS
+*/
+
+// parameters related to word and block sizes
+
+#define WORD_SIZE sizeof(int)
+#define LOG2_OF_WORD_SIZE 2
+
+#define LOG2_OF_PAGE_SIZE 12
+
+#define LOG2_OF_SLICE_SIZE 10
+
+// converters: pages to bytes, bytes to pages
+
+#define P2B(x) ((x) << LOG2_OF_PAGE_SIZE)
+#define B2P(x) ((x) >> LOG2_OF_PAGE_SIZE)
+
+/*
+** Name: adjacent
+**
+** Arguments: addresses of two blocks
+**
+** Description: Determines whether the second block immediately
+** follows the first one.
+*/
+#define adjacent(first,second) \
+ ( (void *) (first) + P2B((first)->pages) == (void *) (second) )
+
+/*
+** PRIVATE DATA TYPES
+*/
+
+/*
+** This structure keeps track of a single block of memory. All blocks
+** are multiples of the base size (currently, 4KB).
+*/
+
+typedef struct block_s {
+ uint32_t pages; // length of this block, in pages
+ struct block_s *next; // pointer to the next free block
+} block_t;
+
+/*
+** Memory region information returned by the BIOS
+**
+** This data consists of a 32-bit integer followed
+** by an array of region descriptor structures.
+*/
+
+// a handy union for playing with 64-bit addresses
+typedef union b64_u {
+ uint32_t part[2];
+ uint64_t all;
+} b64_t;
+
+// the halves of a 64-bit address
+#define LOW part[0]
+#define HIGH part[1]
+
+// memory region descriptor
+typedef struct memregion_s {
+ b64_t base; // base address
+ b64_t length; // region length
+ uint32_t type; // type of region
+ uint32_t acpi; // ACPI 3.0 info
+} __attribute__((packed)) region_t;
+
+/*
+** Region types
+*/
+
+#define REGION_USABLE 1
+#define REGION_RESERVED 2
+#define REGION_ACPI_RECL 3
+#define REGION_ACPI_NVS 4
+#define REGION_BAD 5
+
+/*
+** ACPI 3.0 bit fields
+*/
+
+#define REGION_IGNORE 0x01
+#define REGION_NONVOL 0x02
+
+/*
+** 32-bit and 64-bit address values as 64-bit literals
+*/
+
+#define ADDR_BIT_32 0x0000000100000000LL
+#define ADDR_LOW_HALF 0x00000000ffffffffLL
+#define ADDR_HIGH_HALR 0xffffffff00000000LL
+
+#define ADDR_32_MAX ADDR_LOW_HALF
+#define ADDR_64_FIRST ADDR_BIT_32
+
+/*
+** PRIVATE GLOBAL VARIABLES
+*/
+
+// freespace pools
+static block_t *free_pages;
+static block_t *free_slices;
+
+// initialization status
+static int km_initialized = 0;
+
+/*
+** IMPORTED GLOBAL VARIABLES
+*/
+
+extern int _end; // end of the BSS section - provided by the linker
+
+/*
+** FUNCTIONS
+*/
+
+/*
+** FREE LIST MANAGEMENT
+*/
+
+/**
+** Name: add_block
+**
+** Add a block to the free list. The contents of the block
+** will be modified.
+**
+** @param[in] base Base address of the block
+** @param[in] length Block length, in bytes
+*/
+static void add_block( uint32_t base, uint32_t length ) {
+ block_t *block;
+
+ // don't add it if it isn't at least 4K
+ if( length < SZ_PAGE ) {
+ return;
+ }
+
+ // only want to add multiples of 4K; check the lower bits
+ if( (length & 0xfff) != 0 ) {
+ // round it down to 4K
+ length &= 0xfffff000;
+ }
+
+ // create the "block"
+
+ block = (block_t *) base;
+ block->pages = B2P(length);
+ block->next = NULL;
+
+#if TRACING_KMEM_FREE
+ cio_printf( "KM: add(%08x,%u): addr %08x len %u\n",
+ base, length, (uint32_t)block, block->length );
+#endif
+
+ /*
+ ** We maintain the free list in order by address, to simplify
+ ** coalescing adjacent free blocks.
+ **
+ ** Handle the easiest case first.
+ */
+
+ if( free_pages == NULL ) {
+ free_pages = block;
+ return;
+ }
+
+ /*
+ ** Unfortunately, it's not always that easy....
+ **
+ ** Find the correct insertion spot.
+ */
+
+ block_t *prev, *curr;
+
+ prev = NULL;
+ curr = free_pages;
+
+ while( curr && curr < block ) {
+ prev = curr;
+ curr = curr->next;
+ }
+
+ // the new block always points to its successor
+ block->next = curr;
+
+ /*
+ ** If prev is NULL, we're adding at the front; otherwise,
+ ** we're adding after some other entry (middle or end).
+ */
+
+ if( prev == NULL ) {
+ // sanity check - both pointers can't be NULL
+ assert( curr );
+ // add at the beginning
+ free_pages = block;
+ } else {
+ // inserting in the middle or at the end
+ prev->next = block;
+ }
+}
+
+/**
+** Name: km_init
+**
+** Find what memory is present on the system and
+** construct the list of free memory blocks.
+**
+** Dependencies:
+** Must be called before any other init routine that uses
+** dynamic storage is called.
+*/
+void km_init( void ) {
+ int32_t entries;
+ region_t *region;
+ uint64_t cutoff;
+
+#if TRACING_INIT
+ // announce that we're starting initialization
+ cio_puts( " Kmem" );
+#endif
+
+ // initially, nothing in the free lists
+ free_slices = NULL;
+ free_pages = NULL;
+
+ /*
+ ** We ignore all memory below the end of our OS. In theory,
+ ** we should be able to re-use much of that space; in practice,
+ ** this is safer.
+ */
+
+ // set our cutoff point as the end of the BSS section
+ cutoff = (uint32_t) &_end;
+
+ // round it up to the next multiple of 4K (0x1000)
+ if( cutoff & 0xfffLL ) {
+ cutoff &= 0xfffff000LL;
+ cutoff += 0x1000LL;
+ }
+
+ // get the list length
+ entries = *((int32_t *) MMAP_ADDRESS);
+
+#if TRACING_KEMEM
+ cio_printf( "\nKmem: %d regions\n", entries );
+#endif
+
+ // if there are no entries, we have nothing to do!
+ if( entries < 1 ) { // note: entries == -1 could occur!
+ return;
+ }
+
+ // iterate through the entries, adding things to the freelist
+
+ region = ((region_t *) (MMAP_ADDRESS + 4));
+
+ for( int i = 0; i < entries; ++i, ++region ) {
+
+#if TRACING_KMEM
+ // report this region
+ cio_printf( "%3d: ", i );
+ cio_printf( " base %08x%08x",
+ region->base.HIGH, region->base.LOW );
+ cio_printf( " len %08x%08x",
+ region->length.HIGH, region->length.LOW );
+ cio_printf( " type %08x acpi %08x",
+ region->type, region->acpi );
+#endif
+
+ /*
+ ** Determine whether or not we should ignore this region.
+ **
+ ** We ignore regions for several reasons:
+ **
+ ** ACPI indicates it should be ignored
+ ** ACPI indicates it's non-volatile memory
+ ** Region type isn't "usable"
+ ** Region is above the 4GB address limit
+ **
+ ** Currently, only "normal" (type 1) regions are considered
+ ** "usable" for our purposes. We could potentially expand
+ ** this to include ACPI "reclaimable" memory.
+ */
+
+ // first, check the ACPI one-bit flags
+
+ if( ((region->acpi) & REGION_IGNORE) == 0 ) {
+ cio_puts( " IGN\n" );
+ continue;
+ }
+
+ if( ((region->acpi) & REGION_NONVOL) != 0 ) {
+ cio_puts( " NVOL\n" );
+ continue; // we'll ignore this, too
+ }
+
+ // next, the region type
+
+ if( (region->type) != REGION_USABLE ) {
+ cio_puts( " RCLM\n" );
+ continue; // we won't attempt to reclaim ACPI memory (yet)
+ }
+
+ // OK, we have a "normal" memory region - verify that it's usable
+
+ // ignore it if it's above 4GB
+ if( region->base.HIGH != 0 ) {
+ cio_puts( " 4GB+\n" );
+ continue;
+ }
+
+ // grab the two 64-bit values to simplify things
+ uint64_t base = region->base.all;
+ uint64_t length = region->length.all;
+
+ // see if it's below our arbitrary cutoff point
+ if( base < cutoff ) {
+
+ // is the whole thing too low, or just part?
+ if( (base + length) < cutoff ) {
+ // it's all below the cutoff!
+ cio_puts( " LOW\n" );
+ continue;
+ }
+
+ // recalculate the length, starting at our cutoff point
+ uint64_t loss = cutoff - base;
+
+ // reset the length and the base address
+ length -= loss;
+ base = cutoff;
+ }
+
+ // see if it extends beyond the 4GB boundary
+
+ if( (base + length) > ADDR_32_MAX ) {
+
+ // OK, it extends beyond the 32-bit limit; figure out
+ // how far over it goes, and lop off that portion
+
+ uint64_t loss = (base + length) - ADDR_64_FIRST;
+ length -= loss;
+ }
+
+ // we survived the gauntlet - add the new block
+
+ cio_puts( " OK\n" );
+
+ uint32_t b32 = base & ADDR_LOW_HALF;
+ uint32_t l32 = length & ADDR_LOW_HALF;
+
+ add_block( b32, l32 );
+ }
+
+ // record the initialization
+ km_initialized = 1;
+}
+
+/**
+** Name: km_dump
+**
+** Dump the current contents of the free list to the console
+*/
+void km_dump( void ) {
+ block_t *block;
+
+ cio_printf( "&_free_pages=%08x\n", &_free_pages );
+
+ for( block = _free_pages; block != NULL; block = block->next ) {
+ cio_printf(
+ "block @ 0x%08x 0x%08x pages (ends at 0x%08x) next @ 0x%08x\n",
+ block, block->pages, P2B(block->pages) + (uint32_t) block,
+ block->next );
+ }
+
+}
+
+/*
+** PAGE MANAGEMENT
+*/
+
+/**
+** Name: km_page_alloc
+**
+** Allocate a page of memory from the free list.
+**
+** @param[in] count Number of contiguous pages desired
+**
+** @return a pointer to the beginning of the first allocated page,
+** or NULL if no memory is available
+*/
+void *km_page_alloc( unsigned int count ) {
+
+ assert( km_initialized );
+
+ // make sure we actually need to do something!
+ if( count < 1 ) {
+ return( NULL );
+ }
+
+#if TRACING_KMEM_FREE
+ cio_printf( "KM: pg_alloc(%u)", count );
+#endif
+
+ /*
+ ** Look for the first entry that is large enough.
+ */
+
+ // pointer to the current block
+ block_t *block = free_pages;
+
+ // pointer to where the pointer to the current block is
+ block_t **pointer = &free_pages;
+
+ while( block != NULL && block->pages < count ){
+ pointer = &block->next;
+ block = *pointer;
+ }
+
+ // did we find a big enough block?
+ if( block == NULL ){
+ // nope!
+#if TRACING_KMEM_FREE
+ cio_puts( " FAIL\n" );
+#endif
+ return( NULL );
+ }
+
+ // found one! check the length
+
+ if( block->pages == count ) {
+
+ // exactly the right size - unlink it from the list
+
+ *pointer = block->next;
+
+ } else {
+
+ // bigger than we need - carve the amount we need off
+ // the beginning of this block
+
+ // remember where this chunk begins
+ block_t *chunk = block;
+
+ // how much space will be left over?
+ int excess = block->pages - count;
+
+ // find the start of the new fragment
+ block_t *fragment = (block_t *) ( (uint8_t *) block + P2B(count) );
+
+ // set the length and link for the new fragment
+ fragment->pages = excess;
+ fragment->next = block->next;
+
+ // replace this chunk with the fragment
+ *pointer = fragment;
+
+ // return this chunk
+ block = chunk;
+ }
+
+#if TRACING_KMEM_FREE
+ cio_printf( " -> %08x\n", (uint32_t) block );
+#endif
+
+ return( block );
+}
+
+/**
+** Name: km_page_free
+**
+** Returns a memory block to the list of available blocks,
+** combining it with adjacent blocks if they're present.
+**
+** CRITICAL ASSUMPTION: multi-page blocks will be freed one page
+** at a time!
+**
+** @param[in] block Pointer to the page to be returned to the free list
+*/
+void km_page_free( void *block ){
+ block_t *used;
+ block_t *prev;
+ block_t *curr;
+
+ assert( km_initialized );
+
+ /*
+ ** Don't do anything if the address is NULL.
+ */
+ if( block == NULL ){
+ return;
+ }
+
+#if TRACING_KMEM_FREE
+ cio_printf( "KM: pg_free(%08x)", (uint32_t) block );
+#endif
+
+ used = (block_t *) block;
+
+ /*
+ ** CRITICAL ASSUMPTION
+ **
+ ** We assume that any multi-page block that is being freed will
+ ** be freed one page at a time. We make this assumption because we
+ ** don't track allocation sizes. We can't use the simple "allocate
+ ** four extra bytes before the returned pointer" scheme to do this
+ ** because we're managing pages, and the pointers we return must point
+ ** to page boundaries, so we would wind up allocating an extra page
+ ** for each allocation.
+ **
+ ** Alternatively, we could keep an array of addresses and block
+ ** sizes ourselves, but that feels clunky, and would risk running out
+ ** of table entries if there are lots of allocations (assuming we use
+ ** a 4KB page to hold the table, at eight bytes per entry we would have
+ ** 512 entries per page).
+ **
+ ** IF THIS ASSUMPTION CHANGES, THIS CODE MUST BE FIXED!!!
+ */
+
+ used->pages = 1;
+
+ /*
+ ** Advance through the list until current and previous
+ ** straddle the place where the new block should be inserted.
+ */
+ prev = NULL;
+ curr = free_pages;
+
+ while( curr != NULL && curr < used ){
+ prev = curr;
+ curr = curr->next;
+ }
+
+ /*
+ ** At this point, we have the following list structure:
+ **
+ ** .... BLOCK BLOCK ....
+ ** (*prev) ^ (*curr)
+ ** |
+ ** "used" goes here
+ **
+ ** We may need to merge the inserted block with either its
+ ** predecessor or its successor (or both).
+ */
+
+ /*
+ ** If this is not the first block in the resulting list,
+ ** we may need to merge it with its predecessor.
+ */
+ if( prev != NULL ){
+
+ // There is a predecessor. Check to see if we need to merge.
+ if( adjacent( prev, used ) ){
+
+ // yes - merge them
+ prev->pages += used->pages;
+
+ // the predecessor becomes the "newly inserted" block,
+ // because we still need to check to see if we should
+ // merge with the successor
+ used = prev;
+
+ } else {
+
+ // Not adjacent - just insert the new block
+ // between the predecessor and the successor.
+ used->next = prev->next;
+ prev->next = used;
+
+ }
+
+ } else {
+
+ // Yes, it is first. Update the list pointer to insert it.
+ used->next = free_pages;
+ free_pages = used;
+
+ }
+
+ /*
+ ** If this is not the last block in the resulting list,
+ ** we may (also) need to merge it with its successor.
+ */
+ if( curr != NULL ){
+
+ // No. Check to see if it should be merged with the successor.
+ if( adjacent( used, curr ) ){
+
+ // Yes, combine them.
+ used->next = curr->next;
+ used->pages += curr->pages;
+
+ }
+ }
+}
+
+/*
+** SLICE MANAGEMENT
+*/
+
+/*
+** Slices are 1024-byte fragments from pages. We maintain a free list of
+** slices for those parts of the OS which don't need full 4096-byte chunks
+** of space (e.g., the QNode and Queue allocators).
+*/
+
+/**
+** Name: carve_slices
+**
+** Allocate a page and split it into four slices; If no
+** memory is available, we panic.
+*/
+static void carve_slices( void ) {
+ void *page;
+
+ // get a page
+ page = km_page_alloc( 1 );
+
+ // allocation failure is a show-stopping problem
+ assert( page );
+
+ // we have the page; create the four slices from it
+ uint8_t *ptr = (uint8_t *) page;
+ for( int i = 0; i < 4; ++i ) {
+ km_slice_free( (void *) ptr );
+ ptr += SZ_SLICE;
+ }
+}
+
+/**
+** Name: km_slice_alloc
+**
+** Dynamically allocates a slice (1/4 of a page). If no
+** memory is available, we panic.
+**
+** @return a pointer to the allocated slice
+*/
+void *km_slice_alloc( void ) {
+ block_t *slice;
+
+ assert( km_initialized );
+
+#if TRACING_KMEM_FREE
+ cio_printf( "KM: sl_alloc()\n" );
+#endif
+
+ // if we are out of slices, create a few more
+ if( free_slices == NULL ) {
+ carve_slices();
+ }
+
+ // take the first one from the free list
+ slice = free_slices;
+ assert( slice != NULL );
+
+ // unlink it
+ free_slices = slice->next;
+
+ // make it nice and shiny for the caller
+ memclr( (void *) slice, SZ_SLICE );
+
+ return( slice );
+}
+
+/**
+** Name: km_slice_free
+**
+** Returns a slice to the list of available slices.
+**
+** We make no attempt to merge slices, as they are independent
+** blocks of memory (unlike pages).
+**
+** @param[in] block Pointer to the slice (1/4 page) to be freed
+*/
+void km_slice_free( void *block ) {
+ block_t *slice = (block_t *) block;
+
+ assert( km_initialized );
+
+#if TRACING_KMEM_FREE
+ cio_printf( "KM: sl_free(%08x)\n", (uint32_t) block );
+#endif
+
+ // just add it to the front of the free list
+ slice->pages = SZ_SLICE;
+ slice->next = free_slices;
+ free_slices = slice;
+}
diff --git a/util/alternatives/lib.c b/util/alternatives/lib.c
new file mode 100644
index 0000000..4b7a9ed
--- /dev/null
+++ b/util/alternatives/lib.c
@@ -0,0 +1,56 @@
+/**
+** @file lib.c
+**
+** @author Numerous CSCI-452 classes
+**
+** @brief C implementations of common library functions
+**
+** These are callable from either kernel or user code. Care should be taken
+** that user code is linked against these separately from kernel code, to
+** ensure separation of the address spaces.
+**
+** This file exists to pull them all in as a single object file.
+*/
+
+#include <common.h>
+
+#include <lib.h>
+
+/*
+**********************************************
+** MEMORY MANIPULATION FUNCTIONS
+**********************************************
+*/
+
+#include "common/memset.c"
+#include "common/memclr.c"
+#include "common/memcpy.c"
+
+/*
+**********************************************
+** STRING MANIPULATION FUNCTIONS
+**********************************************
+*/
+
+#include "common/str2int.c"
+#include "common/strlen.c"
+#include "common/strcmp.c"
+#include "common/strcpy.c"
+#include "common/strcat.c"
+#include "common/pad.c"
+#include "common/padstr.c"
+#include "common/sprint.c"
+
+/*
+**********************************************
+** CONVERSION FUNCTIONS
+**********************************************
+*/
+
+#include "common/cvtuns0.c"
+#include "common/cvtuns.c"
+#include "common/cvtdec0.c"
+#include "common/cvtdec.c"
+#include "common/cvthex.c"
+#include "common/cvtoct.c"
+#include "common/bound.c"
diff --git a/util/gdbinit.tmpl b/util/gdbinit.tmpl
new file mode 100644
index 0000000..d9a981d
--- /dev/null
+++ b/util/gdbinit.tmpl
@@ -0,0 +1,31 @@
+# adapted from the xv6 .gdbinit.tmpl file
+set $lastcs = -1
+
+define hook-stop
+ # There doesn't seem to be a good way to detect if we're in 16- or
+ # 32-bit mode, but we always run with CS == 8 in 32-bit mode.
+ if $cs == 8 || $cs == 27
+ if $lastcs != 8 && $lastcs != 27
+ set architecture i386
+ end
+ x/i $pc
+ else
+ if $lastcs == -1 || $lastcs == 8 || $lastcs == 27
+ set architecture i8086
+ end
+ # Translate the segment:offset into a physical address
+ printf "[%4x:%4x] ", $cs, $eip
+ x/i $cs*16+$eip
+ end
+ set $lastcs = $cs
+end
+
+echo + target remote localhost:1234\n
+target remote localhost:1234
+
+# If this fails, it's probably because your GDB doesn't support ELF.
+# Look at the tools page at
+# http://pdos.csail.mit.edu/6.828/2009/tools.html
+# for instructions on building GDB with ELF support.
+echo + symbol-file build/kernel/kernel\n
+symbol-file build/kernel/kernel
diff --git a/util/listblob.c b/util/listblob.c
new file mode 100644
index 0000000..07523a6
--- /dev/null
+++ b/util/listblob.c
@@ -0,0 +1,248 @@
+/**
+** @file listblob.c
+**
+** @author Warren R. Carithers
+**
+** Examine a binary blob of ELF files.
+*/
+#define _DEFAULT_SOURCE
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <elf.h>
+#include <ctype.h>
+
+/*
+** Blob file organization
+**
+** The file begins with a four-byte magic number and a four-byte integer
+** indicating the number of ELF files contained in the blob. This is
+** followed by an array of 32-byte file entries, and then the contents
+** of the ELF files in the order they appear in the program file table.
+**
+** Bytes Contents
+** ----- ----------------------------
+** 0 - 3 File magic number ("BLB\0")
+** 4 - 7 Number of ELF files in blob ("n")
+** 8 - n*32+8 Program file table
+** n*32+9 - ? ELF file contents
+**
+** Each program file table entry contains the following information:
+**
+** name File name (up to 19 characters long)
+** offset Byte offset to the ELF header for this file
+** size Size of this ELF file, in bytes
+** flags Flags related to this file
+*/
+
+// blob header: 8 bytes
+typedef struct header_s {
+ char magic[4];
+ uint32_t num;
+} header_t;
+
+// The program table entry is 32 bytes long. To accomplish this, the
+// name field is 20 bytes long, which allows file names of 19 characters
+// (followed by a trailing NUL byte).
+//
+// If that field is made longer, it should be incremented in multiples
+// of four to avoid the insertion of padding bytes.
+#define NAMELEN 20
+
+// program descriptor: 32 bytes
+typedef struct prog_s {
+ char name[NAMELEN]; // truncated name (19 chars plus NUL)
+ uint32_t offset; // offset from the beginning of the blob
+ uint32_t size; // size of this ELF module
+ uint32_t flags; // miscellaneous flags
+} prog_t;
+
+// modules must be written as multiples of eight bytes
+#define FL_ROUNDUP 0x00000001
+
+// mask for mod 8 checking
+#define FSIZE_MASK 0x00000007
+
+// program list entry
+typedef struct node_s {
+ prog_t *data;
+ struct node_s *next;
+} node_t;
+
+node_t *progs, *last_prog; // list pointers
+uint32_t n_progs; // number of files being copied
+uint32_t offset; // current file area offset
+bool defs = false; // print CPP #defines?
+bool enums = false; // print C enums?
+
+// header string for the userids.h file
+const char header[] =
+"/**\n"
+"** @file userids.h\n"
+"**\n"
+"** @author Warren R. Carithers\n"
+"**\n"
+"** @brief IDs for user-level programs\n"
+"**\n"
+"** NOTE: this file is automatically generated when the user.img file\n"
+"** is created. Do not edit this manually!\n"
+"*/\n"
+"\n"
+"#ifndef USERIDS_H_\n"
+"#define USERIDS_H_\n"
+"\n"
+"#ifndef ASM_SRC\n"
+"/*\n"
+"** These IDs are used to identify the various user programs.\n"
+"** Each call to exec() will provide one of these as the first\n"
+"** argument.\n"
+"**\n"
+"** This list should be updated if/when the collection of\n"
+"** user processes changes.\n"
+"*/\n"
+"enum users_e {"
+;
+
+// trailer string for the userids.h file
+const char trailer[] =
+"\n\t// sentinel\n\t, N_USERS\n"
+"};\n"
+"#endif /* !ASM_SRC */\n"
+"\n"
+"#endif"
+;
+
+/**
+** Name: process
+**
+** Process a program list entry
+**
+** @param num Program list index
+** @param prog Pointer to the program list entry
+*/
+void process( uint32_t num, prog_t *prog ) {
+
+ if( defs || enums ) {
+
+ char *slash = strrchr( prog->name, '/' );
+ if( slash == NULL ) {
+ slash = prog->name;
+ } else {
+ ++slash;
+ }
+
+ slash[0] = toupper(slash[0]);
+
+ if( defs ) {
+
+ // just printing #define statements
+ printf( "#define %-15s %2d\n", prog->name, num );
+
+ } else {
+
+ // printing a new userids.h file
+ if( num == 0 ) {
+ // first one, so print the file header
+ puts( header );
+ putchar( '\t' );
+ } else {
+ // second or later entry; limit to 8 per line
+ fputs( ((num & 0x7) == 0 ) ? ",\n\t" : ", ", stdout );
+ }
+ printf( "%s", prog->name );
+ }
+
+ } else {
+
+ // just printing information
+ printf( "Entry %2d: ", num );
+ printf( "%-s,", prog->name );
+ printf( " offset 0x%x, size 0x%x, flags %08x\n",
+ prog->offset, prog->size, prog->flags );
+ }
+}
+
+void usage( char *name ) {
+ fprintf( stderr, "usage: %s [-d | -e] blob_name\n", name );
+}
+
+int main( int argc, char *argv[] ) {
+
+ if( argc < 2 || argc > 3) {
+ usage( argv[0] );
+ exit( 1 );
+ }
+
+ int nameix = 1;
+
+ // could use getopt() for this, but this is easy enough
+ if( argc == 3 ) {
+ if( strcmp(argv[1],"-d") == 0 ) {
+ defs = true;
+ } else if( strcmp(argv[1],"-e") == 0 ) {
+ enums = true;
+ } else {
+ usage( argv[0] );
+ exit( 1 );
+ }
+ nameix = 2;
+ }
+
+ char *name = argv[nameix];
+
+ int fd = open( name, O_RDONLY );
+ if( fd < 0 ) {
+ perror( name );
+ exit( 1 );
+ }
+
+ header_t hdr;
+
+ int n = read( fd, &hdr, sizeof(header_t) );
+ if( n != sizeof(header_t) ) {
+ fprintf( stderr, "%s: header read returned only %d bytes\n", name, n );
+ close( fd );
+ exit( 1 );
+ }
+
+ if( strcmp(hdr.magic,"BLB") != 0 ) {
+ fprintf( stderr, "%s: bad magic number\n", name );
+ close( fd );
+ exit( 1 );
+ }
+
+ if( hdr.num < 1 ) {
+ fprintf( stderr, "%s: no programs in blob?\n", name );
+ close( fd );
+ exit( 1 );
+ }
+
+ prog_t progs[hdr.num];
+
+ n = read( fd, progs, hdr.num * sizeof(prog_t) );
+ if( n != (int) (hdr.num * sizeof(prog_t)) ) {
+
+ fprintf( stderr, "%s: prog table only %d bytes, expected %lu\n",
+ name, n, hdr.num * sizeof(prog_t) );
+ close( fd );
+ exit( 1 );
+ }
+
+ for( uint32_t i = 0; i < hdr.num; ++i ) {
+ process( i, &progs[i] );
+ }
+
+ if( enums ) {
+ // print the file trailer
+ puts( trailer );
+ }
+
+ close( fd );
+ return 0;
+
+}
diff --git a/util/mergedep.pl b/util/mergedep.pl
new file mode 100644
index 0000000..1730d53
--- /dev/null
+++ b/util/mergedep.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+# Copyright 2003 Bryan Ford
+# Distributed under the GNU General Public License.
+#
+# Usage: mergedep <main-depfile> [<new-depfiles> ...]
+#
+# This script merges the contents of all <new-depfiles> specified
+# on the command line into the single file <main-depfile>,
+# which may or may not previously exist.
+# Dependencies in the <new-depfiles> will override
+# any existing dependencies for the same targets in <main-depfile>.
+# The <new-depfiles> are deleted after <main-depfile> is updated.
+#
+# The <new-depfiles> are typically generated by GCC with the -MD option,
+# and the <main-depfile> is typically included from a Makefile,
+# as shown here for GNU 'make':
+#
+# .deps: $(wildcard *.d)
+# perl mergedep $@ $^
+# -include .deps
+#
+# This script properly handles multiple dependencies per <new-depfile>,
+# including dependencies having no target,
+# so it is compatible with GCC3's -MP option.
+#
+
+sub readdeps {
+ my $filename = shift;
+
+ open(DEPFILE, $filename) or return 0;
+ while (<DEPFILE>) {
+ if (/([^:]*):([^\\:]*)([\\]?)$/) {
+ my $target = $1;
+ my $deplines = $2;
+ my $slash = $3;
+ while ($slash ne '') {
+ $_ = <DEPFILE>;
+ defined($_) or die
+ "Unterminated dependency in $filename";
+ /(^[ \t][^\\]*)([\\]?)$/ or die
+ "Bad continuation line in $filename";
+ $deplines = "$deplines\\\n$1";
+ $slash = $2;
+ }
+ #print "DEPENDENCY [[$target]]: [[$deplines]]\n";
+ $dephash{$target} = $deplines;
+ } elsif (/^[#]?[ \t]*$/) {
+ # ignore blank lines and comments
+ } else {
+ die "Bad dependency line in $filename: $_";
+ }
+ }
+ close DEPFILE;
+ return 1;
+}
+
+
+if ($#ARGV < 0) {
+ print "Usage: mergedep <main-depfile> [<new-depfiles> ..]\n";
+ exit(1);
+}
+
+%dephash = ();
+
+# Read the main dependency file
+$maindeps = $ARGV[0];
+readdeps($maindeps);
+
+# Read and merge in the new dependency files
+foreach $i (1 .. $#ARGV) {
+ readdeps($ARGV[$i]) or die "Can't open $ARGV[$i]";
+}
+
+# Update the main dependency file
+open(DEPFILE, ">$maindeps.tmp") or die "Can't open output file $maindeps.tmp";
+foreach $target (keys %dephash) {
+ print DEPFILE "$target:$dephash{$target}";
+}
+close DEPFILE;
+rename("$maindeps.tmp", "$maindeps") or die "Can't overwrite $maindeps";
+
+# Finally, delete the new dependency files
+foreach $i (1 .. $#ARGV) {
+ unlink($ARGV[$i]) or print "Error removing $ARGV[$i]\n";
+}
+
diff --git a/util/mkblob.c b/util/mkblob.c
new file mode 100644
index 0000000..32c67dd
--- /dev/null
+++ b/util/mkblob.c
@@ -0,0 +1,324 @@
+/**
+** @file mkblob.c
+**
+** @author Warren R. Carithers
+**
+** Create a binary blob from a collection of ELF files.
+*/
+#define _DEFAULT_SOURCE
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <elf.h>
+
+/*
+** Blob file organization
+**
+** The file begins with a four-byte magic number and a four-byte integer
+** indicating the number of ELF files contained in the blob. This is
+** followed by an array of 32-byte file table entries, and then the contents
+** of the ELF files in the order they appear in the program file table.
+**
+** Bytes Contents
+** ----- ----------------------------
+** 0 - 3 File magic number ("BLB\0")
+** 4 - 7 Number of ELF files in blob ("n")
+** 8 - n*32+8 Program file table
+** n*32+9 - ? ELF file contents
+**
+** Each program file table entry contains the following information:
+**
+** name File name (up to 19 characters long)
+** offset Byte offset to the ELF header for this file
+** size Size of this ELF file, in bytes
+** flags Flags related to this file
+*/
+
+// blob header
+typedef struct header_s {
+ char magic[4];
+ uint32_t num;
+} header_t;
+
+// length of the file name field
+#define NAMELEN 20
+
+// program descriptor
+typedef struct prog_s {
+ char name[NAMELEN]; // truncated name (15 chars)
+ uint32_t offset; // offset from the beginning of the blob
+ uint32_t size; // size of this ELF module
+ uint32_t flags; // miscellaneous flags
+} prog_t;
+
+// modules must be written as multiples of eight bytes
+#define FL_ROUNDUP 0x00000001
+
+// mask for mod 8 checking
+#define FSIZE_MASK 0x00000007
+
+// program list entry
+typedef struct node_s {
+ prog_t *data;
+ char *fullname;
+ struct node_s *next;
+} node_t;
+
+node_t *progs, *last_prog; // list pointers
+uint32_t n_progs; // number of files being copied
+uint32_t offset; // current file area offset
+
+/**
+** Name: process
+**
+** Do the initial processing for an ELF file
+**
+** @param name The name of the file
+*/
+void process( const char *name ) {
+ struct stat info;
+
+ // check the name length
+ if( strlen(name) >= NAMELEN ) {
+ fprintf( stderr, "%s: name exceeds length limit (%d)\n",
+ name, NAMELEN-1 );
+ return;
+ }
+
+ // does it exist?
+ if( stat(name,&info) < 0 ) {
+ perror( name );
+ return;
+ }
+
+ // is it a regular file?
+ if( !S_ISREG(info.st_mode) ) {
+ fprintf( stderr, "%s: not a regular file\n", name );
+ return;
+ }
+
+ // open it and check the file header
+ int fd = open( name, O_RDONLY );
+ if( fd < 0 ) {
+ perror( name );
+ return;
+ }
+
+ // read and check the ELF header
+ Elf32_Ehdr hdr;
+ int n = read( fd, &hdr, sizeof(Elf32_Ehdr) );
+ close( fd );
+
+ if( n != sizeof(Elf32_Ehdr) ) {
+ fprintf( stderr, "%s: header read was short - only %d\n", name, n );
+ return;
+ }
+
+ if( hdr.e_ident[EI_MAG0] != ELFMAG0 ||
+ hdr.e_ident[EI_MAG1] != ELFMAG1 ||
+ hdr.e_ident[EI_MAG2] != ELFMAG2 ||
+ hdr.e_ident[EI_MAG3] != ELFMAG3 ) {
+ fprintf( stderr, "%s: bad ELF magic number\n", name );
+ return;
+ }
+
+ // ok, it's a valid ELF file - create the prog list entry
+ prog_t *new = calloc( 1, sizeof(prog_t) );
+ if( new == NULL ) {
+ fprintf( stderr, "%s: calloc prog returned NULL\n", name );
+ return;
+ }
+
+ node_t *node = calloc( 1, sizeof(node_t) );
+ if( node == NULL ) {
+ free( new );
+ fprintf( stderr, "%s: calloc node returned NULL\n", name );
+ return;
+ }
+
+ node->data = new;
+ node->fullname = strdup( name );
+
+ // copy in the name
+
+ // only want the last component
+ const char *slash = strrchr( name, '/' );
+ if( slash == NULL ) {
+ // only the file name
+ slash = name;
+ } else {
+ // skip the slash
+ ++slash;
+ }
+
+ strncpy( new->name, slash, sizeof(new->name)-1 );
+ new->offset = offset;
+ new->size = info.st_size;
+
+ // bump our counters
+ ++n_progs;
+ offset += info.st_size;
+
+ // make sure it's a multiple of eight bytes long
+ if( (info.st_size & FSIZE_MASK) != 0 ) {
+ // nope, so we must round it up when we write it out
+ new->flags |= FL_ROUNDUP;
+ // increases the offset to the next file
+ offset += 8 - (info.st_size & FSIZE_MASK);
+ }
+
+ // add to the list
+ if( progs == NULL ) {
+ // first entry
+ progs = node;
+ } else {
+ // add to the end
+ if( last_prog == NULL ) {
+ fprintf( stderr, "%s: progs ! NULL, last_prog is NULL\n", name );
+ free( new );
+ free( node->fullname );
+ free( node );
+ return;
+ }
+ last_prog->next = node;
+ }
+ last_prog = node;
+}
+
+/**
+** Name: copy
+**
+** Copy the contents of a program list entry into the blob
+**
+** @param ofd The output FILE* to be written
+** @param prog Pointer to the program list entry for the file
+*/
+void copy( FILE *ofd, node_t *node ) {
+
+ prog_t *prog = node->data;
+
+ // open it so we can copy it
+ int fd = open( node->fullname, O_RDONLY );
+ if( fd < 0 ) {
+ perror( node->fullname );
+ return;
+ }
+
+ uint8_t buf[512];
+
+ // copy it block-by-block
+ do {
+ int n = read( fd, buf, 512);
+ // no bytes --> we're done
+ if( n < 1 ) {
+ break;
+ }
+ // copy it, and verify the copy count
+ int k = fwrite( buf, 1, n, ofd );
+ if( k != n ) {
+ fprintf( stderr, "%s: write of %d returned %d\n",
+ prog->name, n, k );
+ }
+ } while( 1 );
+
+ printf( "%s: copied %d", prog->name, prog->size );
+
+ // do we need to round up?
+ if( (prog->flags & FL_ROUNDUP) != 0 ) {
+
+ // we'll fill with NUL bytes
+ uint64_t filler = 0;
+
+ // how many filler bytes do we need?
+ int nbytes = 8 - (prog->size & FSIZE_MASK);
+
+ // do it, and check the transfer count to be sure
+ int n = fwrite( &filler, 1, nbytes, ofd );
+ if( n != nbytes ) {
+ fprintf( stderr, "%s: fill write of %d returned %d\n",
+ prog->name, nbytes, n );
+ }
+
+ // report that we added some filler bytes
+ printf( "(+%d)", n );
+ }
+ puts( " bytes" );
+
+ // all done!
+ close( fd );
+}
+
+int main( int argc, char *argv[] ) {
+
+ // construct program list
+ for( int i = 1; i < argc; ++i ) {
+ process( argv[i] );
+ }
+
+ if( n_progs < 1 ) {
+ fputs( "Nothing to do... exiting.", stderr );
+ exit( 0 );
+ }
+
+ // create the output file
+ FILE *ofd;
+ ofd = fopen( "user.img", "wb" );
+ if( ofd == NULL ) {
+ perror( "user.img" );
+ exit( 1 );
+ }
+
+ printf( "Processing %d ELF files\n", n_progs );
+
+ // we need to adjust the offset values so they are relative to the
+ // start of the blob, not relative to the start of the file area.
+ // do this by adding the sum of the file header and program entries
+ // to each offset field.
+
+ uint32_t hlen = sizeof(header_t) + n_progs * sizeof(prog_t);
+ node_t *curr = progs;
+ while( curr != NULL ) {
+ curr->data->offset += hlen;
+ curr = curr->next;
+ }
+
+ // write out the blob header
+ header_t hdr = { "BLB", n_progs };
+ if( fwrite(&hdr,sizeof(header_t),1,ofd) != 1 ) {
+ perror( "blob header" );
+ fclose( ofd );
+ exit( 1 );
+ }
+
+ // next, the program entries
+ curr = progs;
+ while( curr != NULL ) {
+ if( fwrite(curr->data,sizeof(prog_t),1,ofd) != 1 ) {
+ perror( "blob prog entry write" );
+ fclose( ofd );
+ exit( 1 );
+ }
+ curr = curr->next;
+ }
+
+ // finally, copy the files
+ curr = progs;
+ while( curr != NULL ) {
+ prog_t *prog = curr->data;
+ copy( ofd, curr );
+ node_t *tmp = curr;
+ curr = curr->next;
+ free( tmp->data );
+ free( tmp->fullname );
+ free( tmp );
+ }
+
+ fclose( ofd );
+
+ return 0;
+}