1 |
/* |
2 |
* Copyright (C) 2003-2007 Anders Gavare. All rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions are met: |
6 |
* |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* 3. The name of the author may not be used to endorse or promote products |
13 |
* derived from this software without specific prior written permission. |
14 |
* |
15 |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
16 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
19 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
20 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
21 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
22 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
23 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
24 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
25 |
* SUCH DAMAGE. |
26 |
* |
27 |
* |
28 |
* $Id: bootblock.c,v 1.1 2007/03/16 14:45:30 debug Exp $ |
29 |
* |
30 |
* Bootblock handling: |
31 |
* |
32 |
* o) For some machines (e.g. DECstation or the Dreamcast), it is |
33 |
* possible to load a bootblock from a fixed location on disk, and |
34 |
* simply execute it in memory. |
35 |
* |
36 |
* o) For booting from generic CDROM ISO9660 images, a filename of |
37 |
* a file to load must be supplied (the kernel filename). It is |
38 |
* loaded, possibly gunzipped, and then executed as if it was a |
39 |
* separate file. |
40 |
* |
41 |
* TODO: This module needs some cleanup. |
42 |
*/ |
43 |
|
44 |
#include <stdio.h> |
45 |
#include <stdlib.h> |
46 |
#include <string.h> |
47 |
|
48 |
#include "cpu.h" |
49 |
#include "diskimage.h" |
50 |
#include "emul.h" |
51 |
#include "machine.h" |
52 |
#include "misc.h" |
53 |
|
54 |
static char *diskimage_types[] = DISKIMAGE_TYPES; |
55 |
|
56 |
|
57 |
/* |
58 |
* load_bootblock(): |
59 |
* |
60 |
* For some emulation modes, it is possible to boot from a harddisk image by |
61 |
* loading a bootblock from a specific disk offset into memory, and executing |
62 |
* that, instead of requiring a separate kernel file. It is then up to the |
63 |
* bootblock to load a kernel. |
64 |
* |
65 |
* Returns 1 on success, 0 on failure. |
66 |
*/ |
67 |
int load_bootblock(struct machine *m, struct cpu *cpu, |
68 |
int *n_loadp, char ***load_namesp) |
69 |
{ |
70 |
int boot_disk_id, boot_disk_type = 0, n_blocks, res, readofs, |
71 |
iso_type, retval = 0; |
72 |
unsigned char minibuf[0x20]; |
73 |
unsigned char *bootblock_buf; |
74 |
uint64_t bootblock_offset, base_offset; |
75 |
uint64_t bootblock_loadaddr, bootblock_pc; |
76 |
|
77 |
boot_disk_id = diskimage_bootdev(m, &boot_disk_type); |
78 |
if (boot_disk_id < 0) |
79 |
return 0; |
80 |
|
81 |
base_offset = diskimage_get_baseoffset(m, boot_disk_id, boot_disk_type); |
82 |
|
83 |
switch (m->machine_type) { |
84 |
|
85 |
case MACHINE_DREAMCAST: |
86 |
if (!diskimage_is_a_cdrom(cpu->machine, boot_disk_id, |
87 |
boot_disk_type)) { |
88 |
fatal("The Dreamcast emulation mode can only boot" |
89 |
" from CD images, not from other disk types.\n"); |
90 |
exit(1); |
91 |
} |
92 |
|
93 |
bootblock_buf = malloc(32768); |
94 |
if (bootblock_buf == NULL) { |
95 |
fprintf(stderr, "Out of memory.\n"); |
96 |
exit(1); |
97 |
} |
98 |
|
99 |
debug("loading Dreamcast IP.BIN from %s id %i\n", |
100 |
diskimage_types[boot_disk_type], boot_disk_id); |
101 |
|
102 |
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
103 |
0, base_offset, bootblock_buf, 0x8000); |
104 |
if (!res) { |
105 |
fatal("Couldn't read the disk image. Aborting.\n"); |
106 |
return 0; |
107 |
} |
108 |
|
109 |
if (strncmp((char *)bootblock_buf, "SEGA ", 5) != 0) { |
110 |
fatal("This is not a Dreamcast IP.BIN header.\n"); |
111 |
free(bootblock_buf); |
112 |
return 0; |
113 |
} |
114 |
|
115 |
/* Store IP.BIN at 0x8c008000, and set entry point. */ |
116 |
store_buf(cpu, 0x8c008000, (char *)bootblock_buf, 32768); |
117 |
cpu->pc = 0x8c008300; |
118 |
|
119 |
/* Remember the name of the file to boot (1ST_READ.BIN): */ |
120 |
if (cpu->machine->boot_kernel_filename == NULL || |
121 |
cpu->machine->boot_kernel_filename[0] == '\0') { |
122 |
int i = 0x60; |
123 |
while (i < 0x70) { |
124 |
if (bootblock_buf[i] == ' ') |
125 |
bootblock_buf[i] = 0; |
126 |
i ++; |
127 |
} |
128 |
cpu->machine->boot_kernel_filename = strdup( |
129 |
(char *)bootblock_buf + 0x60); |
130 |
} |
131 |
|
132 |
debug("boot filename: %s\n", |
133 |
cpu->machine->boot_kernel_filename); |
134 |
|
135 |
free(bootblock_buf); |
136 |
|
137 |
break; |
138 |
|
139 |
case MACHINE_PMAX: |
140 |
/* |
141 |
* The first few bytes of a disk contains information about |
142 |
* where the bootblock(s) are located. (These are all 32-bit |
143 |
* little-endian words.) |
144 |
* |
145 |
* Offset 0x10 = load address |
146 |
* 0x14 = initial PC value |
147 |
* 0x18 = nr of 512-byte blocks to read |
148 |
* 0x1c = offset on disk to where the bootblocks |
149 |
* are (in 512-byte units) |
150 |
* 0x20 = nr of blocks to read... |
151 |
* 0x24 = offset... |
152 |
* |
153 |
* nr of blocks to read and offset are repeated until nr of |
154 |
* blocks to read is zero. |
155 |
*/ |
156 |
res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0, |
157 |
minibuf, sizeof(minibuf)); |
158 |
|
159 |
bootblock_loadaddr = minibuf[0x10] + (minibuf[0x11] << 8) |
160 |
+ (minibuf[0x12] << 16) + ((uint64_t)minibuf[0x13] << 24); |
161 |
|
162 |
/* Convert loadaddr to uncached: */ |
163 |
if ((bootblock_loadaddr & 0xf0000000ULL) != 0x80000000 && |
164 |
(bootblock_loadaddr & 0xf0000000ULL) != 0xa0000000) { |
165 |
fatal("\nWARNING! Weird load address 0x%08"PRIx32 |
166 |
" for SCSI id %i.\n\n", |
167 |
(uint32_t)bootblock_loadaddr, boot_disk_id); |
168 |
if (bootblock_loadaddr == 0) { |
169 |
fatal("I'm assuming that this is _not_ a " |
170 |
"DEC bootblock.\nAre you sure you are" |
171 |
" booting from the correct disk?\n"); |
172 |
exit(1); |
173 |
} |
174 |
} |
175 |
|
176 |
bootblock_loadaddr &= 0x0fffffffULL; |
177 |
bootblock_loadaddr |= 0xffffffffa0000000ULL; |
178 |
|
179 |
bootblock_pc = minibuf[0x14] + (minibuf[0x15] << 8) |
180 |
+ (minibuf[0x16] << 16) + ((uint64_t)minibuf[0x17] << 24); |
181 |
|
182 |
bootblock_pc &= 0x0fffffffULL; |
183 |
bootblock_pc |= 0xffffffffa0000000ULL; |
184 |
cpu->pc = bootblock_pc; |
185 |
|
186 |
debug("DEC boot: loadaddr=0x%08x, pc=0x%08x", |
187 |
(int)bootblock_loadaddr, (int)bootblock_pc); |
188 |
|
189 |
readofs = 0x18; |
190 |
|
191 |
for (;;) { |
192 |
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
193 |
0, readofs, minibuf, sizeof(minibuf)); |
194 |
if (!res) { |
195 |
fatal("Couldn't read the disk image. " |
196 |
"Aborting.\n"); |
197 |
return 0; |
198 |
} |
199 |
|
200 |
n_blocks = minibuf[0] + (minibuf[1] << 8) |
201 |
+ (minibuf[2] << 16) + ((uint64_t)minibuf[3] << 24); |
202 |
|
203 |
bootblock_offset = (minibuf[4] + (minibuf[5] << 8) + |
204 |
(minibuf[6]<<16) + ((uint64_t)minibuf[7]<<24)) * 512; |
205 |
|
206 |
if (n_blocks < 1) |
207 |
break; |
208 |
|
209 |
debug(readofs == 0x18? ": %i" : " + %i", n_blocks); |
210 |
|
211 |
if (n_blocks * 512 > 65536) |
212 |
fatal("\nWARNING! Unusually large bootblock " |
213 |
"(%i bytes)\n\n", n_blocks * 512); |
214 |
|
215 |
bootblock_buf = malloc(n_blocks * 512); |
216 |
if (bootblock_buf == NULL) { |
217 |
fprintf(stderr, "out of memory in " |
218 |
"load_bootblock()\n"); |
219 |
exit(1); |
220 |
} |
221 |
|
222 |
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
223 |
0, bootblock_offset, bootblock_buf, n_blocks * 512); |
224 |
if (!res) { |
225 |
fatal("WARNING: could not load bootblocks from" |
226 |
" disk offset 0x%llx\n", |
227 |
(long long)bootblock_offset); |
228 |
} |
229 |
|
230 |
store_buf(cpu, bootblock_loadaddr, |
231 |
(char *)bootblock_buf, n_blocks * 512); |
232 |
|
233 |
bootblock_loadaddr += 512*n_blocks; |
234 |
free(bootblock_buf); |
235 |
readofs += 8; |
236 |
} |
237 |
|
238 |
debug(readofs == 0x18? ": no blocks?\n" : " blocks\n"); |
239 |
return 1; |
240 |
} |
241 |
|
242 |
|
243 |
/* |
244 |
* Try reading a kernel manually from the disk. The code here |
245 |
* does not rely on machine-dependent boot blocks etc. |
246 |
*/ |
247 |
/* ISO9660: (0x800 bytes at 0x8000 + base_offset) */ |
248 |
bootblock_buf = malloc(0x800); |
249 |
if (bootblock_buf == NULL) { |
250 |
fprintf(stderr, "Out of memory.\n"); |
251 |
exit(1); |
252 |
} |
253 |
|
254 |
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
255 |
0, base_offset + 0x8000, bootblock_buf, 0x800); |
256 |
if (!res) { |
257 |
fatal("Couldn't read the disk image. Aborting.\n"); |
258 |
return 0; |
259 |
} |
260 |
|
261 |
iso_type = 0; |
262 |
if (strncmp((char *)bootblock_buf+1, "CD001", 5) == 0) |
263 |
iso_type = 1; |
264 |
if (strncmp((char *)bootblock_buf+1, "CDW01", 5) == 0) |
265 |
iso_type = 2; |
266 |
if (strncmp((char *)bootblock_buf+1, "CDROM", 5) == 0) |
267 |
iso_type = 3; |
268 |
|
269 |
if (iso_type != 0) { |
270 |
/* |
271 |
* If the user specified a kernel name, then load it from |
272 |
* disk. |
273 |
*/ |
274 |
if (cpu->machine->boot_kernel_filename == NULL || |
275 |
cpu->machine->boot_kernel_filename[0] == '\0') |
276 |
fatal("\nISO9660 filesystem, but no kernel " |
277 |
"specified? (Use the -j option.)\n"); |
278 |
else |
279 |
retval = iso_load_bootblock(m, cpu, boot_disk_id, |
280 |
boot_disk_type, iso_type, bootblock_buf, |
281 |
n_loadp, load_namesp); |
282 |
} |
283 |
|
284 |
if (retval != 0) |
285 |
goto ret_ok; |
286 |
|
287 |
/* Apple parition table: */ |
288 |
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
289 |
0, 0x0, bootblock_buf, 0x800); |
290 |
if (!res) { |
291 |
fatal("Couldn't read the disk image. Aborting.\n"); |
292 |
return 0; |
293 |
} |
294 |
if (bootblock_buf[0x000] == 'E' && bootblock_buf[0x001] == 'R' && |
295 |
bootblock_buf[0x200] == 'P' && bootblock_buf[0x201] == 'M') { |
296 |
if (cpu->machine->boot_kernel_filename == NULL || |
297 |
cpu->machine->boot_kernel_filename[0] == '\0') |
298 |
fatal("\nApple partition table, but no kernel " |
299 |
"specified? (Use the -j option.)\n"); |
300 |
else |
301 |
retval = apple_load_bootblock(m, cpu, boot_disk_id, |
302 |
boot_disk_type, n_loadp, load_namesp); |
303 |
} |
304 |
|
305 |
ret_ok: |
306 |
free(bootblock_buf); |
307 |
return retval; |
308 |
} |
309 |
|
310 |
|