diff options
author | Freya Murphy <freya@freyacat.org> | 2025-03-25 17:36:52 -0400 |
---|---|---|
committer | Freya Murphy <freya@freyacat.org> | 2025-03-25 17:38:22 -0400 |
commit | 6af21e6a4f2251e71353562d5df7f376fdffc270 (patch) | |
tree | de20c7afc9878422c81e34f30c6b010075e9e69a /util | |
download | comus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.gz comus-6af21e6a4f2251e71353562d5df7f376fdffc270.tar.bz2 comus-6af21e6a4f2251e71353562d5df7f376fdffc270.zip |
initial checkout from wrc
Diffstat (limited to 'util')
-rw-r--r-- | util/BuildImage.c | 415 | ||||
-rw-r--r-- | util/Make.mk | 59 | ||||
-rw-r--r-- | util/Offsets.c | 250 | ||||
-rw-r--r-- | util/alternatives/Make.mk | 56 | ||||
-rw-r--r-- | util/alternatives/README | 31 | ||||
-rw-r--r-- | util/alternatives/kmem.c | 749 | ||||
-rw-r--r-- | util/alternatives/lib.c | 56 | ||||
-rw-r--r-- | util/gdbinit.tmpl | 31 | ||||
-rw-r--r-- | util/listblob.c | 248 | ||||
-rw-r--r-- | util/mergedep.pl | 86 | ||||
-rw-r--r-- | util/mkblob.c | 324 |
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; +} |