Search
lxdream.org :: lxdream/src/loader.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/loader.c
changeset 1152:6464d890cc9e
prev1117:0b14a8ec373b
next1296:30ecee61f811
author nkeynes
date Thu Dec 23 17:50:10 2010 +1000 (9 years ago)
permissions -rw-r--r--
last change Clone iso_memory_stream_new() as iso_mem_stream_new(), since current
versions of libisofs have made it unlinkable on linux
view annotate diff log raw
     1 /**
     2  * $Id$
     3  *
     4  * File loading routines, mostly for loading demos without going through the
     5  * whole procedure of manually making a CD image for them.
     6  *
     7  * Copyright (c) 2005 Nathan Keynes.
     8  *
     9  * This program is free software; you can redistribute it and/or modify
    10  * it under the terms of the GNU General Public License as published by
    11  * the Free Software Foundation; either version 2 of the License, or
    12  * (at your option) any later version.
    13  *
    14  * This program is distributed in the hope that it will be useful,
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17  * GNU General Public License for more details.
    18  */
    20 #include <unistd.h>
    21 #include <stdio.h>
    22 #include <string.h>
    23 #include <fcntl.h>
    24 #include <sys/stat.h>
    25 #include <errno.h>
    26 #include <stdint.h>
    27 #include <glib/gstrfuncs.h>
    28 #include <elf.h>
    29 #include "mem.h"
    30 #include "bootstrap.h"
    31 #include "dreamcast.h"
    32 #include "config.h"
    33 #include "loader.h"
    34 #include "drivers/cdrom/cdrom.h"
    35 #include "drivers/cdrom/isofs.h"
    36 #include "gdrom/gdrom.h"
    38 const char bootstrap_magic[32] = "SEGA SEGAKATANA SEGA ENTERPRISES";
    39 const char iso_magic[6] = "\001CD001";
    40 char *file_loader_extensions[][2] = { 
    41         { "sbi", "Self Boot Inducer" },
    42         { "bin", "SH4 Bin file" },
    43         { NULL, NULL } };
    45 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
    46 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err );
    47 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err );
    48 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err );
    52 lxdream_file_type_t file_identify( const gchar *filename, int fd, ERROR *err )
    53 {
    54     char buf[32];
    55     lxdream_file_type_t result = FILE_UNKNOWN;
    56     gboolean mustClose = FALSE;
    57     off_t posn;
    59     if( fd == -1 ) {
    60         fd = open( filename, O_RDONLY );
    61         if( fd == -1 ) {
    62             SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
    63             return FILE_ERROR;
    64         }
    65         mustClose = TRUE;
    66     } else {
    67         /* Save current file position */
    68         posn = lseek(fd, 0, SEEK_CUR);
    69         if( posn == -1 ) {
    70             SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
    71             return FILE_ERROR;
    72         }
    73     }
    75     int status = read(fd, buf, 32);
    76     if( status == -1 ) {
    77         SET_ERROR( err, LX_ERR_FILE_IOERROR, "Unable to read from file '%s' (%s)", filename, strerror(errno) );
    78         result = FILE_ERROR;
    79     } else if( status != 32 ) {
    80         result = FILE_UNKNOWN;
    81     } else if( buf[0] == 0x7F && buf[1] == 'E' &&
    82             buf[2] == 'L' && buf[3] == 'F' ) {
    83         result = FILE_ELF;
    84     } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {
    85         result = FILE_ZIP;
    86     } else if( memcmp( buf, DREAMCAST_SAVE_MAGIC, 16 ) == 0 ) {
    87         result = FILE_SAVE_STATE;
    88     } else if( lseek( fd, 32768, SEEK_SET ) == 32768 &&
    89             read( fd, buf, 8 ) == 8 &&
    90             memcmp( buf, iso_magic, 6) == 0 ) {
    91         result = FILE_ISO;
    92     } else {
    93         /* Check the file extension - .bin = sh4 binary */
    94         int len = strlen(filename);
    95         struct stat st;
    97         if( len > 4 && strcasecmp(filename + (len-4), ".bin") == 0 &&
    98             fstat(fd, &st) != -1 && st.st_size <= BINARY_MAX_SIZE ) {
    99             result = FILE_BINARY;
   100         }
   101     }
   103     if( mustClose ) {
   104         close(fd);
   105     } else {
   106         lseek( fd, posn, SEEK_SET );
   107     }
   108     return result;
   109 }
   112 gboolean file_load_exec( const gchar *filename, ERROR *err )
   113 {
   114     gboolean result = TRUE;
   116     int fd = open( filename, O_RDONLY );
   117     if( fd == -1 ) {
   118         SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
   119         return FALSE;
   120     }
   122     lxdream_file_type_t type = file_identify(filename, fd, err);
   123     switch( type ) {
   124     case FILE_ERROR:
   125         result = FALSE;
   126         break;
   127     case FILE_ELF:
   128         result = file_load_elf( filename, fd, err );
   129         break;
   130     case FILE_BINARY:
   131         result = file_load_binary( filename, fd, err );
   132         break;
   133     default:
   134         SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized as an executable binary", filename );
   135         result = FALSE;
   136         break;
   137     }
   139     close(fd);
   140     return result;
   141 }
   143 lxdream_file_type_t file_load_magic( const gchar *filename, gboolean wrap_exec, ERROR *err )
   144 {
   145     gboolean result;
   146     /* Try disc types first */
   147     cdrom_disc_t disc = cdrom_disc_open( filename, err );
   148     if( disc != NULL ) {
   149         gdrom_mount_disc(disc);
   150         return FILE_DISC;
   151     } else if( err != LX_ERR_FILE_UNKNOWN ) {
   152         return FILE_ERROR;
   153     }
   155     int fd = open( filename, O_RDONLY );
   156     if( fd == -1 ) {
   157         SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s' (%s)" ,filename, strerror(errno) );
   158         return FILE_ERROR;
   159     }
   161     lxdream_file_type_t type = file_identify(filename, fd, err);
   162     switch( type ) {
   163     case FILE_ERROR:
   164         result = FALSE;
   165         break;
   166     case FILE_ELF:
   167         if( wrap_exec ) {
   168             disc = cdrom_wrap_elf( CDROM_DISC_XA, filename, fd, err );
   169             result = disc != NULL;
   170             if( disc != NULL ) {
   171                 gdrom_mount_disc(disc);
   172             }
   173         } else {
   174             result = file_load_elf( filename, fd, err );
   175         }
   176         break;
   177     case FILE_BINARY:
   178         if( wrap_exec ) {
   179             disc = cdrom_wrap_binary( CDROM_DISC_XA, filename, fd, err );
   180             result = disc != NULL;
   181             if( disc != NULL ) {
   182                 gdrom_mount_disc(disc);
   183             }
   184         } else {
   185             result = file_load_binary( filename, fd, err );
   186         }
   187         break;
   188     case FILE_SAVE_STATE:
   189         result = dreamcast_load_state( filename );
   190         break;
   191     case FILE_ZIP:
   192         SET_ERROR( err, LX_ERR_FILE_UNSUP, "ZIP/SBI not currently supported" );
   193         result = FALSE;
   194         break;
   195     case FILE_ISO:
   196         SET_ERROR( err, LX_ERR_FILE_UNSUP, "ISO files are not currently supported" );
   197         result = FALSE;
   198         break;
   199     default:
   200         SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' could not be recognized", filename );
   201         result = FALSE;
   202         break;
   203     }
   204     close(fd);
   205     if( result ) {
   206         CLEAR_ERROR(err);
   207         return type;
   208     }
   209     return FILE_ERROR;
   210 }
   212 void file_load_postload( const gchar *filename, int pc )
   213 {
   214     gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
   215     if( bootstrap_file != NULL && bootstrap_file[0] != '\0' ) {
   216         /* Load in a bootstrap before the binary, to initialize everything
   217          * correctly
   218          */
   219         if( mem_load_block( bootstrap_file, BOOTSTRAP_LOAD_ADDR, BOOTSTRAP_SIZE ) == 0 ) {
   220             dreamcast_program_loaded( filename, BOOTSTRAP_ENTRY_ADDR );
   221             g_free(bootstrap_file);
   222             return;
   223         }
   224     }
   225     dreamcast_program_loaded( filename, pc );
   226     g_free(bootstrap_file);
   227 }    
   230 static gboolean is_sh4_elf( Elf32_Ehdr *head )
   231 {
   232     return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
   233             head->e_ident[EI_DATA] == ELFDATA2LSB &&
   234             head->e_ident[EI_VERSION] == 1 &&
   235             head->e_type == ET_EXEC &&
   236             head->e_machine == EM_SH &&
   237             head->e_version == 1 );
   238 }
   240 static gboolean is_arm_elf( Elf32_Ehdr *head )
   241 {
   242     return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
   243             head->e_ident[EI_DATA] == ELFDATA2LSB &&
   244             head->e_ident[EI_VERSION] == 1 &&
   245             head->e_type == ET_EXEC &&
   246             head->e_machine == EM_ARM &&
   247             head->e_version == 1 );
   248 }
   250 static gboolean file_load_elf( const gchar *filename, int fd, ERROR *err )
   251 {
   252     Elf32_Ehdr head;
   253     Elf32_Phdr phdr;
   254     int i;
   256     if( read( fd, &head, sizeof(head) ) != sizeof(head) )
   257         return FALSE;
   258     if( !is_sh4_elf(&head) ) {
   259         SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
   260         return FALSE;
   261     }
   263     /* Program headers */
   264     for( i=0; i<head.e_phnum; i++ ) {
   265         lseek( fd, head.e_phoff + i*head.e_phentsize, SEEK_SET );
   266         read( fd, &phdr, sizeof(phdr) );
   267         if( phdr.p_type == PT_LOAD ) {
   268             lseek( fd, phdr.p_offset, SEEK_SET );
   269             sh4ptr_t target = mem_get_region( phdr.p_vaddr );
   270             read( fd, target, phdr.p_filesz );
   271             if( phdr.p_memsz > phdr.p_filesz ) {
   272                 memset( target + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz );
   273             }
   274         }
   275     }
   277     file_load_postload( filename, head.e_entry );
   278     return TRUE;
   279 }
   281 static gboolean file_load_binary( const gchar *filename, int fd, ERROR *err )
   282 {
   283     struct stat st;
   285     if( fstat( fd, &st ) == -1 ) {
   286         SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
   287         return FALSE;
   288     }
   290     if( st.st_size > BINARY_MAX_SIZE ) {
   291         SET_ERROR( err, LX_ERR_FILE_INVALID, "Binary file '%s' is too large to fit in memory", filename );
   292         return FALSE;
   293     }
   295     sh4ptr_t target = mem_get_region( BINARY_LOAD_ADDR );
   296     if( read( fd, target, st.st_size ) != st.st_size ) {
   297         SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
   298         return FALSE;
   299     }
   301     file_load_postload( filename, BINARY_LOAD_ADDR );
   302     return TRUE;
   303 }
   305 /**
   306  * Create a new CDROM disc containing a single 1ST_READ.BIN.
   307  * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA
   308  * @param bin The binary data (takes ownership)
   309  * @param bin_size
   310  */
   311 cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,
   312                                             ERROR *err )
   313 {
   314     IsoImage *iso = NULL;
   315     unsigned char *data = bin;
   316     cdrom_lba_t start_lba = 45000; /* GDROM_START */
   317     char bootstrap[32768];
   319     /* 1. Load in the bootstrap: Note failures here are considered configuration errors */
   320     gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
   321     if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {
   322         g_free(data);
   323         SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file is not configured" );
   324         return NULL;
   325     }
   327     FILE *f = fopen( bootstrap_file, "ro" );
   328     if( f == NULL ) {
   329         g_free(data);
   330         SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
   331         return FALSE;
   332     }
   333     size_t len = fread( bootstrap, 1, 32768, f );
   334     fclose(f);
   335     if( len != 32768 ) {
   336         g_free(data);
   337         SET_ERROR( err, LX_ERR_CONFIG, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
   338         return FALSE;
   339     }
   341     /* 2. Scramble the binary if necessary (and set type settings) */
   342     if( type != CDROM_DISC_GDROM ) {
   343         /* scramble the binary if we're going the MIL-CD route */
   344         unsigned char *scramblebin = g_malloc(bin_size);
   345         bootprogram_scramble( scramblebin, bin, bin_size );
   346         data = scramblebin;
   347         start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
   348         g_free(bin);
   349     }
   351     /* 3. Frob the bootstrap data */
   352     dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;
   353     memcpy( boot_header->boot_file, "1ST_READ.BIN    ", 16 );
   354     char tmp[129];
   355     int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
   356     if( name_len < 128 )
   357         memset( tmp+name_len, ' ', 128-name_len );
   358     memcpy( boot_header->product_name, tmp, 128 );
   359 //    bootstrap_update_crc(bootstrap);
   362     /* 4. Build the ISO image */
   363     int status = iso_image_new("autocd", &iso);
   364     if( status != 1 ) {
   365         g_free(data);
   366         SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
   367         return NULL;
   368     }
   370     IsoStream *stream;
   371     if( iso_mem_stream_new(data, bin_size, &stream) != 1 ) {
   372         g_free(data);
   373         iso_image_unref(iso);
   374         SET_ERROR( err, LX_ERR_NOMEM, "Unable to create CD image: out of memory" );
   375         return NULL;
   376     }
   377     iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);
   378     sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,
   379             bootstrap, err );
   380     if( track == NULL ) {
   381         iso_image_unref(iso);
   382         return NULL;
   383     }
   385     cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba, err );
   386     iso_image_unref(iso);
   387     if( disc != NULL ) {
   388         disc->name = g_strdup(filename);
   389     } 
   390     return disc;
   391 }
   393 static cdrom_disc_t cdrom_wrap_elf( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
   394 {
   395     Elf32_Ehdr head;
   396     int i;
   398     /* Check the file header is actually an SH4 binary */
   399     if( read( fd, &head, sizeof(head) ) != sizeof(head) )
   400         return FALSE;
   402     if( !is_sh4_elf(&head) ) {
   403         SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not an SH4 ELF executable file" );
   404         return FALSE;
   405     }
   406     if( head.e_entry != BINARY_LOAD_ADDR ) {
   407         SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect entry point (should be %08X but is %08X)", BINARY_LOAD_ADDR, head.e_entry );
   408         return FALSE;
   409     }
   411     /* Load the program headers */
   412     Elf32_Phdr phdr[head.e_phnum];
   413     lseek( fd, head.e_phoff, SEEK_SET );
   414     if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {
   415         SET_ERROR( err, LX_ERR_FILE_INVALID, "File is not a valid executable file" );
   416         return FALSE;
   417     }
   419     sh4addr_t start = (sh4addr_t)-1, end=0;
   420     /* Scan program headers for memory range in use */
   421     for( i=0; i<head.e_phnum; i++ ) {
   422         if( phdr[i].p_type == PT_LOAD ) {
   423             if( phdr[i].p_vaddr < start )
   424                 start = phdr[i].p_vaddr;
   425             if( phdr[i].p_vaddr + phdr[i].p_memsz > end )
   426                 end = phdr[i].p_vaddr + phdr[i].p_memsz;
   427         }
   428     }
   430     if( start != BINARY_LOAD_ADDR ) {
   431         SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 Binary has incorrect load address (should be %08X but is %08X)", BINARY_LOAD_ADDR, start );
   432         return FALSE;
   433     }
   434     if( end >= 0x8D000000 ) {
   435         SET_ERROR( err, LX_ERR_FILE_INVALID, "SH4 binary is too large to fit in memory (end address is %08X)", end );
   436         return FALSE;
   437     }
   439     /* Load the program into memory */
   440     char *program = g_malloc0( end-start );
   441     for( i=0; i<head.e_phnum; i++ ) {
   442         if( phdr[i].p_type == PT_LOAD ) {
   443             lseek( fd, phdr[i].p_offset, SEEK_SET );
   444             uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);
   445             int status = read( fd, program + phdr[i].p_vaddr - start, size );
   446             if( status == -1 ) {
   447                 SET_ERROR( err, LX_ERR_FILE_IOERROR, "I/O error reading SH4 binary %s (%s)", filename, strerror(errno) );
   448             } else if( status != size ) {
   449                 SET_ERROR( err, LX_ERR_FILE_IOERROR, "SH4 binary %s is corrupt", filename );
   450             }            
   451         }
   452     }
   454     /* And finally pass it over to the disc wrapper */
   455     return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
   456 }
   458 static cdrom_disc_t cdrom_wrap_binary( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
   459 {
   460     struct stat st;
   461     char *data;
   462     size_t len;
   464     if( fstat(fd, &st) == -1 ) {
   465         SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
   466         return NULL;
   467     }
   469     data = g_malloc(st.st_size);
   470     len = read( fd, data, st.st_size );
   471     if( len != st.st_size ) {
   472         SET_ERROR( err, LX_ERR_FILE_IOERROR, "Error reading binary file '%s' (%s)", filename, strerror(errno) );
   473         free(data);
   474         return NULL;
   475     }
   477     return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
   478 }
   480 cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
   481 {
   482     cdrom_disc_t disc = NULL;
   484     int fd = open( filename, O_RDONLY );
   485     if( fd == -1 ) {
   486         SET_ERROR( err, LX_ERR_FILE_NOOPEN, "Unable to open file '%s'", filename );
   487         return NULL;
   488     }
   490     lxdream_file_type_t filetype = file_identify( filename, fd, err );
   491     switch( filetype ) {
   492     case FILE_ELF:
   493         disc = cdrom_wrap_elf(type, filename, fd, err);
   494         break;
   495     case FILE_BINARY:
   496         disc = cdrom_wrap_binary(type, filename, fd, err);
   497         break;
   498     default:
   499         SET_ERROR( err, LX_ERR_FILE_UNKNOWN, "File '%s' cannot be wrapped (not a recognized binary)", filename );
   500         break;
   501     }
   503     close(fd);
   504     return disc;
   506 }
.