filename | src/loader.c |
changeset | 1108:305ef2082079 |
prev | 1100:50e702af9373 |
next | 1109: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) |
file | annotate | diff | log | raw |
1.1 --- a/src/loader.c Mon Feb 15 17:27:14 2010 +10001.2 +++ b/src/loader.c Fri Jun 04 09:13:40 2010 +10001.3 @@ -2,7 +2,7 @@1.4 * $Id$1.5 *1.6 * File loading routines, mostly for loading demos without going through the1.7 - * whole procedure of making a CD image for them.1.8 + * whole procedure of manually making a CD image for them.1.9 *1.10 * Copyright (c) 2005 Nathan Keynes.1.11 *1.12 @@ -24,24 +24,63 @@1.13 #include <sys/stat.h>1.14 #include <errno.h>1.15 #include <stdint.h>1.16 +#include <glib/gstrfuncs.h>1.17 #include <elf.h>1.18 #include "mem.h"1.19 #include "bootstrap.h"1.20 #include "dreamcast.h"1.21 #include "config.h"1.22 #include "loader.h"1.23 +#include "drivers/cdrom/cdrom.h"1.24 +#include "drivers/cdrom/isofs.h"1.26 -char bootstrap_magic[32] = "SEGA SEGAKATANA SEGA ENTERPRISES";1.27 -char iso_magic[6] = "\001CD001";1.28 +const char bootstrap_magic[32] = "SEGA SEGAKATANA SEGA ENTERPRISES";1.29 +const char iso_magic[6] = "\001CD001";1.30 char *file_loader_extensions[][2] = {1.31 { "sbi", "Self Boot Inducer" },1.32 { "bin", "SH4 Bin file" },1.33 { NULL, NULL } };1.35 -#define CDI_V2 0x800000041.36 -#define CDI_V3 0x800000051.37 +gboolean file_load_elf_fd( const gchar *filename, int fd );1.39 -gboolean file_load_elf_fd( const gchar *filename, int fd );1.40 +typedef enum {1.41 + FILE_ERROR,1.42 + FILE_BINARY,1.43 + FILE_ELF,1.44 + FILE_ISO,1.45 + FILE_DISC,1.46 + FILE_ZIP,1.47 + FILE_SAVE_STATE,1.48 +} lxdream_file_type_t;1.49 +1.50 +static lxdream_file_type_t file_magic( const gchar *filename, int fd, ERROR *err )1.51 +{1.52 + char buf[32];1.53 +1.54 + /* begin magic */1.55 + if( read( fd, buf, 32 ) != 32 ) {1.56 + SET_ERROR( err, errno, "Unable to read from file '%s'", filename );1.57 + return FILE_ERROR;1.58 +1.59 + }1.60 +1.61 + lseek( fd, 0, SEEK_SET );1.62 + if( buf[0] == 0x7F && buf[1] == 'E' &&1.63 + buf[2] == 'L' && buf[3] == 'F' ) {1.64 + return FILE_ELF;1.65 + } else if( memcmp( buf, "PK\x03\x04", 4 ) == 0 ) {1.66 + return FILE_ZIP;1.67 + } else if( memcmp( buf, DREAMCAST_SAVE_MAGIC, 16 ) == 0 ) {1.68 + return FILE_SAVE_STATE;1.69 + } else if( lseek( fd, 32768, SEEK_SET ) == 32768 &&1.70 + read( fd, buf, 8 ) == 8 &&1.71 + memcmp( buf, iso_magic, 6) == 0 ) {1.72 + lseek( fd, 0, SEEK_SET );1.73 + return FILE_ISO;1.74 + }1.75 + lseek( fd, 0, SEEK_SET );1.76 + return FILE_BINARY;1.77 +}1.80 gboolean file_load_magic( const gchar *filename )1.81 @@ -128,6 +167,26 @@1.82 }1.83 }1.85 +gboolean is_sh4_elf( Elf32_Ehdr *head )1.86 +{1.87 + return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&1.88 + head->e_ident[EI_DATA] == ELFDATA2LSB &&1.89 + head->e_ident[EI_VERSION] == 1 &&1.90 + head->e_type == ET_EXEC &&1.91 + head->e_machine == EM_SH &&1.92 + head->e_version == 1 );1.93 +}1.94 +1.95 +gboolean is_arm_elf( Elf32_Ehdr *head )1.96 +{1.97 + return ( head->e_ident[EI_CLASS] == ELFCLASS32 &&1.98 + head->e_ident[EI_DATA] == ELFDATA2LSB &&1.99 + head->e_ident[EI_VERSION] == 1 &&1.100 + head->e_type == ET_EXEC &&1.101 + head->e_machine == EM_ARM &&1.102 + head->e_version == 1 );1.103 +}1.104 +1.105 gboolean file_load_elf_fd( const gchar *filename, int fd )1.106 {1.107 Elf32_Ehdr head;1.108 @@ -136,12 +195,7 @@1.110 if( read( fd, &head, sizeof(head) ) != sizeof(head) )1.111 return FALSE;1.112 - if( head.e_ident[EI_CLASS] != ELFCLASS32 ||1.113 - head.e_ident[EI_DATA] != ELFDATA2LSB ||1.114 - head.e_ident[EI_VERSION] != 1 ||1.115 - head.e_type != ET_EXEC ||1.116 - head.e_machine != EM_SH ||1.117 - head.e_version != 1 ) {1.118 + if( !is_sh4_elf(&head) ) {1.119 ERROR( "File is not an SH4 ELF executable file" );1.120 return FALSE;1.121 }1.122 @@ -164,3 +218,185 @@1.123 file_load_postload( filename, head.e_entry );1.124 return TRUE;1.125 }1.126 +1.127 +/**1.128 + * Create a new CDROM disc containing a single 1ST_READ.BIN.1.129 + * @param type The disc type - must be CDROM_DISC_GDROM or CDROM_DISC_XA1.130 + * @param bin The binary data (takes ownership)1.131 + * @param bin_size1.132 + */1.133 +cdrom_disc_t cdrom_disc_new_wrapped_binary( cdrom_disc_type_t type, const gchar *filename, unsigned char *bin, size_t bin_size,1.134 + ERROR *err )1.135 +{1.136 + IsoImage *iso = NULL;1.137 + unsigned char *data = bin;1.138 + cdrom_lba_t start_lba = 45000; /* GDROM_START */1.139 + char bootstrap[32768];1.140 +1.141 + /* 1. Load in the bootstrap */1.142 + gchar *bootstrap_file = lxdream_get_global_config_path_value(CONFIG_BOOTSTRAP);1.143 + if( bootstrap_file == NULL || bootstrap_file[0] == '\0' ) {1.144 + g_free(data);1.145 + SET_ERROR( err, ENOENT, "Unable to create CD image: bootstrap file is not configured" );1.146 + return NULL;1.147 + }1.148 +1.149 + FILE *f = fopen( bootstrap_file, "ro" );1.150 + if( f == NULL ) {1.151 + g_free(data);1.152 + SET_ERROR( err, errno, "Unable to create CD image: bootstrap file '%s' could not be opened", bootstrap_file );1.153 + return FALSE;1.154 + }1.155 + size_t len = fread( bootstrap, 1, 32768, f );1.156 + fclose(f);1.157 + if( len != 32768 ) {1.158 + g_free(data);1.159 + SET_ERROR( err, EINVAL, "Unable to create CD image: bootstrap file '%s' is invalid", bootstrap_file );1.160 + return FALSE;1.161 + }1.162 +1.163 + /* 2. Scramble the binary if necessary (and set type settings) */1.164 + if( type != CDROM_DISC_GDROM ) {1.165 + /* scramble the binary if we're going the MIL-CD route */1.166 + unsigned char *scramblebin = g_malloc(bin_size);1.167 + bootprogram_scramble( scramblebin, bin, bin_size );1.168 + data = scramblebin;1.169 + start_lba = 0x2DB6; /* CDROM_START (does it matter?) */1.170 + g_free(bin);1.171 + }1.172 +1.173 + /* 3. Frob the bootstrap data */1.174 + dc_bootstrap_head_t boot_header = (dc_bootstrap_head_t)bootstrap;1.175 + memcpy( boot_header->boot_file, "1ST_READ.BIN ", 16 );1.176 + char tmp[129];1.177 + int name_len = snprintf( tmp, 129, "lxdream wrapped image: %s", filename );1.178 + if( name_len < 128 )1.179 + memset( tmp+name_len, ' ', 128-name_len );1.180 + memcpy( boot_header->product_name, tmp, 128 );1.181 +// bootstrap_update_crc(bootstrap);1.182 +1.183 +1.184 + /* 4. Build the ISO image */1.185 + int status = iso_image_new("autocd", &iso);1.186 + if( status != 1 ) {1.187 + g_free(data);1.188 + return NULL;1.189 + }1.190 +1.191 + IsoStream *stream;1.192 + if( iso_memory_stream_new(data, bin_size, &stream) != 1 ) {1.193 + g_free(data);1.194 + iso_image_unref(iso);1.195 + return NULL;1.196 + }1.197 + iso_tree_add_new_file(iso_image_get_root(iso), "1ST_READ.BIN", stream, NULL);1.198 + sector_source_t track = iso_sector_source_new( iso, SECTOR_MODE2_FORM1, start_lba,1.199 + bootstrap, err );1.200 + if( track == NULL ) {1.201 + iso_image_unref(iso);1.202 + return NULL;1.203 + }1.204 +1.205 + cdrom_disc_t disc = cdrom_disc_new_from_track( type, track, start_lba );1.206 + iso_image_unref(iso);1.207 + if( disc != NULL ) {1.208 + disc->name = g_strdup(filename);1.209 + }1.210 + return disc;1.211 +}1.212 +1.213 +cdrom_disc_t cdrom_wrap_elf_fd( cdrom_disc_type_t type, const gchar *filename, int fd, ERROR *err )1.214 +{1.215 + Elf32_Ehdr head;1.216 + int i;1.217 +1.218 + /* Check the file header is actually an SH4 binary */1.219 + if( read( fd, &head, sizeof(head) ) != sizeof(head) )1.220 + return FALSE;1.221 + if( !is_sh4_elf(&head) ) {1.222 + SET_ERROR( err, EINVAL, "File is not an SH4 ELF executable file" );1.223 + return FALSE;1.224 + }1.225 + if( head.e_entry != BINARY_LOAD_ADDR ) {1.226 + SET_ERROR( err, EINVAL, "SH4 Binary has incorrect entry point (should be %08X but is %08X)", BINARY_LOAD_ADDR, head.e_entry );1.227 + return FALSE;1.228 + }1.229 +1.230 + /* Load the program headers */1.231 + Elf32_Phdr phdr[head.e_phnum];1.232 + lseek( fd, head.e_phoff, SEEK_SET );1.233 + if( read( fd, phdr, sizeof(phdr) ) != sizeof(phdr) ) {1.234 + SET_ERROR( err, EINVAL, "File is not a valid executable file" );1.235 + return FALSE;1.236 + }1.237 +1.238 + sh4addr_t start = (sh4addr_t)-1, end=0;1.239 + /* Scan program headers for memory range in use */1.240 + for( i=0; i<head.e_phnum; i++ ) {1.241 + if( phdr[i].p_type == PT_LOAD ) {1.242 + if( phdr[i].p_vaddr < start )1.243 + start = phdr[i].p_vaddr;1.244 + if( phdr[i].p_vaddr + phdr[i].p_memsz > end )1.245 + end = phdr[i].p_vaddr + phdr[i].p_memsz;1.246 + }1.247 + }1.248 +1.249 + if( start != BINARY_LOAD_ADDR ) {1.250 + SET_ERROR( err, EINVAL, "SH4 Binary has incorrect load address (should be %08X but is %08X)", BINARY_LOAD_ADDR, start );1.251 + return FALSE;1.252 + }1.253 + if( end >= 0x8D000000 ) {1.254 + SET_ERROR( err, EINVAL, "SH4 binary is too large to fit in memory (end address is %08X)", end );1.255 + return FALSE;1.256 + }1.257 +1.258 + /* Load the program into memory */1.259 + char *program = g_malloc0( end-start );1.260 + for( i=0; i<head.e_phnum; i++ ) {1.261 + if( phdr[i].p_type == PT_LOAD ) {1.262 + lseek( fd, phdr[i].p_offset, SEEK_SET );1.263 + uint32_t size = MIN( phdr[i].p_filesz, phdr[i].p_memsz);1.264 + read( fd, program + phdr[i].p_vaddr, size );1.265 + }1.266 + }1.267 +1.268 + /* And finally pass it over to the disc wrapper */1.269 + return cdrom_disc_new_wrapped_binary(type, filename, program, end-start, err );1.270 +}1.271 +1.272 +cdrom_disc_t cdrom_wrap_magic( cdrom_disc_type_t type, const gchar *filename, ERROR *err )1.273 +{1.274 + cdrom_disc_t disc;1.275 + char *data;1.276 + int len;1.277 + struct stat st;1.278 + int fd = open( filename, O_RDONLY );1.279 + if( fd == -1 ) {1.280 + SET_ERROR( err, errno, "Unable to open file '%s'", filename );1.281 + return NULL;1.282 + }1.283 +1.284 +1.285 + lxdream_file_type_t filetype = file_magic( filename, fd, err );1.286 + switch( filetype ) {1.287 + case FILE_BINARY:1.288 + fstat( fd, &st );1.289 + data = g_malloc(st.st_size);1.290 + len = read( fd, data, st.st_size );1.291 + close(fd);1.292 + if( len != st.st_size ) {1.293 + SET_ERROR( err, errno, "Error reading binary file '%s'", filename );1.294 + return NULL;1.295 + }1.296 + return cdrom_disc_new_wrapped_binary( type, filename, data, st.st_size, err );1.297 + case FILE_ELF:1.298 + disc = cdrom_wrap_elf_fd(type, filename, fd, err);1.299 + close(fd);1.300 + return disc;1.301 + default:1.302 + close(fd);1.303 + SET_ERROR( err, EINVAL, "File '%s' cannot be wrapped (not a binary)", filename );1.304 + return NULL;1.305 + }1.306 +1.307 +}
.