Search
lxdream.org :: lxdream/src/loader.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/loader.c
changeset 1108:305ef2082079
prev1100:50e702af9373
next1109:700c5ab26a63
author nkeynes
date Fri Jun 04 09:13:40 2010 +1000 (13 years ago)
permissions -rw-r--r--
last change Add ability to wrap a binary program up in a virtual cd image (so that we
can boot it normally)
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"
    37 const char bootstrap_magic[32] = "SEGA SEGAKATANA SEGA ENTERPRISES";
    38 const char iso_magic[6] = "\001CD001";
    39 char *file_loader_extensions[][2] = { 
    40         { "sbi", "Self Boot Inducer" },
    41         { "bin", "SH4 Bin file" },
    42         { NULL, NULL } };
    44 gboolean file_load_elf_fd( const gchar *filename, int fd );
    46 typedef enum {
    47     FILE_ERROR,
    48     FILE_BINARY,
    49     FILE_ELF,
    50     FILE_ISO,
    51     FILE_DISC,
    52     FILE_ZIP,
    53     FILE_SAVE_STATE,
    54 } lxdream_file_type_t;
    56 static lxdream_file_type_t file_magic( const gchar *filename, int fd, ERROR *err )
    57 {
    58     char buf[32];
    60     /* begin magic */
    61     if( read( fd, buf, 32 ) != 32 ) {
    62         SET_ERROR( err, errno, "Unable to read from file '%s'", filename );
    63         return FILE_ERROR;
    65     }
    67     lseek( fd, 0, SEEK_SET );
    68     if( buf[0] == 0x7F && buf[1] == 'E' &&
    69             buf[2] == 'L' && buf[3] == 'F' ) {
    70         return FILE_ELF;
    71     } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {
    72         return FILE_ZIP;
    73     } else if( memcmp( buf, DREAMCAST_SAVE_MAGIC, 16 ) == 0 ) {
    74         return FILE_SAVE_STATE;
    75     } else if( lseek( fd, 32768, SEEK_SET ) == 32768 &&
    76             read( fd, buf, 8 ) == 8 &&
    77             memcmp( buf, iso_magic, 6) == 0 ) {
    78         lseek( fd, 0, SEEK_SET );
    79         return FILE_ISO;
    80     }
    81     lseek( fd, 0, SEEK_SET );
    82     return FILE_BINARY;
    83 }
    86 gboolean file_load_magic( const gchar *filename )
    87 {
    88     char buf[32];
    89     struct stat st;
    90     gboolean result = TRUE;
    92     int fd = open( filename, O_RDONLY );
    93     if( fd == -1 ) {
    94         return FALSE;
    95     }
    97     fstat( fd, &st );
    99     /* begin magic */
   100     if( read( fd, buf, 32 ) != 32 ) {
   101         ERROR( "Unable to read from file '%s'", filename );
   102         close(fd);
   103         return FALSE;
   104     }
   105     if( memcmp( buf, bootstrap_magic, 32 ) == 0 ) {
   106         /* we have a DC bootstrap */
   107         if( st.st_size == BOOTSTRAP_SIZE ) {
   108             sh4ptr_t load = mem_get_region( BOOTSTRAP_LOAD_ADDR );
   109             lseek( fd, 0, SEEK_SET );
   110             read( fd, load, BOOTSTRAP_SIZE );
   111             bootstrap_dump( load, TRUE );
   112             dreamcast_program_loaded( filename, BOOTSTRAP_LOAD_ADDR + 0x300 );
   113         } else {
   114             /* look for a valid ISO9660 header */
   115             lseek( fd, 32768, SEEK_SET );
   116             read( fd, buf, 8 );
   117             if( memcmp( buf, iso_magic, 6 ) == 0 ) {
   118                 /* Alright, got it */
   119                 WARN( "ISO images not supported yet" );
   120             }
   121         }
   122     } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {
   123         /* ZIP file, aka SBI file */
   124         WARN( "SBI files not supported yet" );
   125         result = FALSE;
   126     } else if( memcmp( buf, DREAMCAST_SAVE_MAGIC, 16 ) == 0 ) {
   127         /* Save state */
   128         result = (dreamcast_load_state( filename )==0);
   129     } else if( buf[0] == 0x7F && buf[1] == 'E' && 
   130             buf[2] == 'L' && buf[3] == 'F' ) {
   131         /* ELF binary */
   132         lseek( fd, 0, SEEK_SET );
   133         result = file_load_elf_fd( filename, fd );
   134     } else {
   135         result = FALSE;
   136     }
   137     close(fd);
   138     return result;
   139 }
   141 void file_load_postload( const gchar *filename, int pc )
   142 {
   143     gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
   144     if( bootstrap_file != NULL && bootstrap_file[0] != '\0' ) {
   145         /* Load in a bootstrap before the binary, to initialize everything
   146          * correctly
   147          */
   148         if( mem_load_block( bootstrap_file, BOOTSTRAP_LOAD_ADDR, BOOTSTRAP_SIZE ) == 0 ) {
   149             dreamcast_program_loaded( filename, BOOTSTRAP_ENTRY_ADDR );
   150             g_free(bootstrap_file);
   151             return;
   152         }
   153     }
   154     dreamcast_program_loaded( filename, pc );
   155     g_free(bootstrap_file);
   156 }    
   159 gboolean file_load_binary( const gchar *filename )
   160 {
   161     /* Load the binary itself */
   162     if(  mem_load_block( filename, BINARY_LOAD_ADDR, -1 ) == 0 ) {
   163         file_load_postload( filename, BINARY_LOAD_ADDR );
   164         return TRUE;
   165     } else {
   166         return FALSE;
   167     }
   168 }
   170 gboolean is_sh4_elf( Elf32_Ehdr *head )
   171 {
   172     return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
   173             head->e_ident[EI_DATA] == ELFDATA2LSB &&
   174             head->e_ident[EI_VERSION] == 1 &&
   175             head->e_type == ET_EXEC &&
   176             head->e_machine == EM_SH &&
   177             head->e_version == 1 );
   178 }
   180 gboolean is_arm_elf( Elf32_Ehdr *head )
   181 {
   182     return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&
   183             head->e_ident[EI_DATA] == ELFDATA2LSB &&
   184             head->e_ident[EI_VERSION] == 1 &&
   185             head->e_type == ET_EXEC &&
   186             head->e_machine == EM_ARM &&
   187             head->e_version == 1 );
   188 }
   190 gboolean file_load_elf_fd( const gchar *filename, int fd ) 
   191 {
   192     Elf32_Ehdr head;
   193     Elf32_Phdr phdr;
   194     int i;
   196     if( read( fd, &head, sizeof(head) ) != sizeof(head) )
   197         return FALSE;
   198     if( !is_sh4_elf(&head) ) {
   199         ERROR( "File is not an SH4 ELF executable file" );
   200         return FALSE;
   201     }
   203     /* Program headers */
   204     for( i=0; i<head.e_phnum; i++ ) {
   205         lseek( fd, head.e_phoff + i*head.e_phentsize, SEEK_SET );
   206         read( fd, &phdr, sizeof(phdr) );
   207         if( phdr.p_type == PT_LOAD ) {
   208             lseek( fd, phdr.p_offset, SEEK_SET );
   209             sh4ptr_t target = mem_get_region( phdr.p_vaddr );
   210             read( fd, target, phdr.p_filesz );
   211             if( phdr.p_memsz > phdr.p_filesz ) {
   212                 memset( target + phdr.p_filesz, 0, phdr.p_memsz - phdr.p_filesz );
   213             }
   214             INFO( "Loaded %d bytes to %08X", phdr.p_filesz, phdr.p_vaddr );
   215         }
   216     }
   218     file_load_postload( filename, head.e_entry );
   219     return TRUE;
   220 }
   222 /**
   223  * Create a new CDROM disc containing a single 1ST_READ.BIN.
   224  * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA
   225  * @param bin The binary data (takes ownership)
   226  * @param bin_size
   227  */
   228 cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,
   229                                             ERROR *err )
   230 {
   231     IsoImage *iso = NULL;
   232     unsigned char *data = bin;
   233     cdrom_lba_t start_lba = 45000; /* GDROM_START */
   234     char bootstrap[32768];
   236     /* 1. Load in the bootstrap */
   237     gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);
   238     if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {
   239         g_free(data);
   240         SET_ERROR( err, ENOENT, "Unable to create CD image: bootstrap file is not configured" );
   241         return NULL;
   242     }
   244     FILE *f = fopen( bootstrap_file, "ro" );
   245     if( f == NULL ) {
   246         g_free(data);
   247         SET_ERROR( err, errno, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );
   248         return FALSE;
   249     }
   250     size_t len = fread( bootstrap, 1, 32768, f );
   251     fclose(f);
   252     if( len != 32768 ) {
   253         g_free(data);
   254         SET_ERROR( err, EINVAL, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );
   255         return FALSE;
   256     }
   258     /* 2. Scramble the binary if necessary (and set type settings) */
   259     if( type != CDROM_DISC_GDROM ) {
   260         /* scramble the binary if we're going the MIL-CD route */
   261         unsigned char *scramblebin = g_malloc(bin_size);
   262         bootprogram_scramble( scramblebin, bin, bin_size );
   263         data = scramblebin;
   264         start_lba = 0x2DB6; /* CDROM_START (does it matter?) */
   265         g_free(bin);
   266     }
   268     /* 3. Frob the bootstrap data */
   269     dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;
   270     memcpy( boot_header->boot_file, "1ST_READ.BIN    ", 16 );
   271     char tmp[129];
   272     int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );
   273     if( name_len < 128 )
   274         memset( tmp+name_len, ' ', 128-name_len );
   275     memcpy( boot_header->product_name, tmp, 128 );
   276 //    bootstrap_update_crc(bootstrap);
   279     /* 4. Build the ISO image */
   280     int status = iso_image_new("autocd", &iso);
   281     if( status != 1 ) {
   282         g_free(data);
   283         return NULL;
   284     }
   286     IsoStream *stream;
   287     if( iso_memory_stream_new(data, bin_size, &stream) != 1 ) {
   288         g_free(data);
   289         iso_image_unref(iso);
   290         return NULL;
   291     }
   292     iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);
   293     sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,
   294             bootstrap, err );
   295     if( track == NULL ) {
   296         iso_image_unref(iso);
   297         return NULL;
   298     }
   300     cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba );
   301     iso_image_unref(iso);
   302     if( disc != NULL ) {
   303         disc->name = g_strdup(filename);
   304     }
   305     return disc;
   306 }
   308 cdrom_disc_t cdrom_wrap_elf_fd( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )
   309 {
   310     Elf32_Ehdr head;
   311     int i;
   313     /* Check the file header is actually an SH4 binary */
   314     if( read( fd, &head, sizeof(head) ) != sizeof(head) )
   315         return FALSE;
   316     if( !is_sh4_elf(&head) ) {
   317         SET_ERROR( err, EINVAL, "File is not an SH4 ELF executable file" );
   318         return FALSE;
   319     }
   320     if( head.e_entry != BINARY_LOAD_ADDR ) {
   321         SET_ERROR( err, EINVAL, "SH4 Binary has incorrect entry point (should be %08X but is %08X)", BINARY_LOAD_ADDR, head.e_entry );
   322         return FALSE;
   323     }
   325     /* Load the program headers */
   326     Elf32_Phdr phdr[head.e_phnum];
   327     lseek( fd, head.e_phoff, SEEK_SET );
   328     if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {
   329         SET_ERROR( err, EINVAL, "File is not a valid executable file" );
   330         return FALSE;
   331     }
   333     sh4addr_t start = (sh4addr_t)-1, end=0;
   334     /* Scan program headers for memory range in use */
   335     for( i=0; i<head.e_phnum; i++ ) {
   336         if( phdr[i].p_type == PT_LOAD ) {
   337             if( phdr[i].p_vaddr < start )
   338                 start = phdr[i].p_vaddr;
   339             if( phdr[i].p_vaddr + phdr[i].p_memsz > end )
   340                 end = phdr[i].p_vaddr + phdr[i].p_memsz;
   341         }
   342     }
   344     if( start != BINARY_LOAD_ADDR ) {
   345         SET_ERROR( err, EINVAL, "SH4 Binary has incorrect load address (should be %08X but is %08X)", BINARY_LOAD_ADDR, start );
   346         return FALSE;
   347     }
   348     if( end >= 0x8D000000 ) {
   349         SET_ERROR( err, EINVAL, "SH4 binary is too large to fit in memory (end address is %08X)", end );
   350         return FALSE;
   351     }
   353     /* Load the program into memory */
   354     char *program = g_malloc0( end-start );
   355     for( i=0; i<head.e_phnum; i++ ) {
   356         if( phdr[i].p_type == PT_LOAD ) {
   357             lseek( fd, phdr[i].p_offset, SEEK_SET );
   358             uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);
   359             read( fd, program + phdr[i].p_vaddr, size );
   360         }
   361     }
   363     /* And finally pass it over to the disc wrapper */
   364     return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );
   365 }
   367 cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )
   368 {
   369     cdrom_disc_t disc;
   370     char *data;
   371     int len;
   372     struct stat st;
   373     int fd = open( filename, O_RDONLY );
   374     if( fd == -1 ) {
   375         SET_ERROR( err, errno, "Unable to open file '%s'", filename );
   376         return NULL;
   377     }
   380     lxdream_file_type_t filetype = file_magic( filename, fd, err );
   381     switch( filetype ) {
   382     case FILE_BINARY:
   383         fstat( fd, &st );
   384         data = g_malloc(st.st_size);
   385         len = read( fd, data, st.st_size );
   386         close(fd);
   387         if( len != st.st_size ) {
   388             SET_ERROR( err, errno, "Error reading binary file '%s'", filename );
   389             return NULL;
   390         }
   391         return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );
   392     case FILE_ELF:
   393         disc = cdrom_wrap_elf_fd(type, filename, fd, err);
   394         close(fd);
   395         return disc;
   396     default:
   397         close(fd);
   398         SET_ERROR( err, EINVAL, "File '%s' cannot be wrapped (not a binary)", filename );
   399         return NULL;
   400     }
   402 }
.