Search
lxdream.org :: lxdream/src/loader.c :: diff
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 (10 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 +1000
1.2 +++ b/src/loader.c Fri Jun 04 09:13:40 2010 +1000
1.3 @@ -2,7 +2,7 @@
1.4 * $Id$
1.5 *
1.6 * File loading routines, mostly for loading demos without going through the
1.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.25
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.34
1.35 -#define CDI_V2 0x80000004
1.36 -#define CDI_V3 0x80000005
1.37 +gboolean file_load_elf_fd( const gchar *filename, int fd );
1.38
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.78
1.79
1.80 gboolean file_load_magic( const gchar *filename )
1.81 @@ -128,6 +167,26 @@
1.82 }
1.83 }
1.84
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.109
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_XA
1.130 + * @param bin The binary data (takes ownership)
1.131 + * @param bin_size
1.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 +}
.