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) |
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 }
.