1 |
dpavlin |
36 |
/* |
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 |
dpavlin |
42 |
* $Id: bootblock.c,v 1.4 2007/06/15 17:02:39 debug Exp $ |
29 |
dpavlin |
36 |
* |
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 |
dpavlin |
42 |
#include "memory.h" |
53 |
dpavlin |
36 |
|
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 |
dpavlin |
42 |
CHECK_ALLOCATION(bootblock_buf = malloc(32768)); |
94 |
dpavlin |
36 |
|
95 |
|
|
debug("loading Dreamcast IP.BIN from %s id %i\n", |
96 |
|
|
diskimage_types[boot_disk_type], boot_disk_id); |
97 |
|
|
|
98 |
|
|
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
99 |
|
|
0, base_offset, bootblock_buf, 0x8000); |
100 |
|
|
if (!res) { |
101 |
|
|
fatal("Couldn't read the disk image. Aborting.\n"); |
102 |
|
|
return 0; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
if (strncmp((char *)bootblock_buf, "SEGA ", 5) != 0) { |
106 |
|
|
fatal("This is not a Dreamcast IP.BIN header.\n"); |
107 |
|
|
free(bootblock_buf); |
108 |
|
|
return 0; |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
/* Store IP.BIN at 0x8c008000, and set entry point. */ |
112 |
|
|
store_buf(cpu, 0x8c008000, (char *)bootblock_buf, 32768); |
113 |
|
|
cpu->pc = 0x8c008300; |
114 |
|
|
|
115 |
|
|
/* Remember the name of the file to boot (1ST_READ.BIN): */ |
116 |
|
|
if (cpu->machine->boot_kernel_filename == NULL || |
117 |
|
|
cpu->machine->boot_kernel_filename[0] == '\0') { |
118 |
|
|
int i = 0x60; |
119 |
|
|
while (i < 0x70) { |
120 |
|
|
if (bootblock_buf[i] == ' ') |
121 |
|
|
bootblock_buf[i] = 0; |
122 |
|
|
i ++; |
123 |
|
|
} |
124 |
dpavlin |
42 |
CHECK_ALLOCATION(cpu->machine->boot_kernel_filename = |
125 |
|
|
strdup((char *)bootblock_buf + 0x60)); |
126 |
dpavlin |
36 |
} |
127 |
|
|
|
128 |
|
|
debug("boot filename: %s\n", |
129 |
|
|
cpu->machine->boot_kernel_filename); |
130 |
|
|
|
131 |
|
|
free(bootblock_buf); |
132 |
|
|
|
133 |
|
|
break; |
134 |
|
|
|
135 |
|
|
case MACHINE_PMAX: |
136 |
|
|
/* |
137 |
|
|
* The first few bytes of a disk contains information about |
138 |
|
|
* where the bootblock(s) are located. (These are all 32-bit |
139 |
|
|
* little-endian words.) |
140 |
|
|
* |
141 |
|
|
* Offset 0x10 = load address |
142 |
|
|
* 0x14 = initial PC value |
143 |
|
|
* 0x18 = nr of 512-byte blocks to read |
144 |
|
|
* 0x1c = offset on disk to where the bootblocks |
145 |
|
|
* are (in 512-byte units) |
146 |
|
|
* 0x20 = nr of blocks to read... |
147 |
|
|
* 0x24 = offset... |
148 |
|
|
* |
149 |
|
|
* nr of blocks to read and offset are repeated until nr of |
150 |
|
|
* blocks to read is zero. |
151 |
|
|
*/ |
152 |
|
|
res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0, |
153 |
|
|
minibuf, sizeof(minibuf)); |
154 |
|
|
|
155 |
|
|
bootblock_loadaddr = minibuf[0x10] + (minibuf[0x11] << 8) |
156 |
|
|
+ (minibuf[0x12] << 16) + ((uint64_t)minibuf[0x13] << 24); |
157 |
|
|
|
158 |
|
|
/* Convert loadaddr to uncached: */ |
159 |
|
|
if ((bootblock_loadaddr & 0xf0000000ULL) != 0x80000000 && |
160 |
|
|
(bootblock_loadaddr & 0xf0000000ULL) != 0xa0000000) { |
161 |
|
|
fatal("\nWARNING! Weird load address 0x%08"PRIx32 |
162 |
|
|
" for SCSI id %i.\n\n", |
163 |
|
|
(uint32_t)bootblock_loadaddr, boot_disk_id); |
164 |
|
|
if (bootblock_loadaddr == 0) { |
165 |
|
|
fatal("I'm assuming that this is _not_ a " |
166 |
|
|
"DEC bootblock.\nAre you sure you are" |
167 |
|
|
" booting from the correct disk?\n"); |
168 |
|
|
exit(1); |
169 |
|
|
} |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
bootblock_loadaddr &= 0x0fffffffULL; |
173 |
|
|
bootblock_loadaddr |= 0xffffffffa0000000ULL; |
174 |
|
|
|
175 |
|
|
bootblock_pc = minibuf[0x14] + (minibuf[0x15] << 8) |
176 |
|
|
+ (minibuf[0x16] << 16) + ((uint64_t)minibuf[0x17] << 24); |
177 |
|
|
|
178 |
|
|
bootblock_pc &= 0x0fffffffULL; |
179 |
|
|
bootblock_pc |= 0xffffffffa0000000ULL; |
180 |
|
|
cpu->pc = bootblock_pc; |
181 |
|
|
|
182 |
dpavlin |
42 |
debug("DEC boot: loadaddr=0x%08"PRIx32", pc=0x%08"PRIx32, |
183 |
|
|
(uint32_t) bootblock_loadaddr, (uint32_t) bootblock_pc); |
184 |
dpavlin |
36 |
|
185 |
|
|
readofs = 0x18; |
186 |
|
|
|
187 |
|
|
for (;;) { |
188 |
|
|
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
189 |
|
|
0, readofs, minibuf, sizeof(minibuf)); |
190 |
|
|
if (!res) { |
191 |
|
|
fatal("Couldn't read the disk image. " |
192 |
|
|
"Aborting.\n"); |
193 |
|
|
return 0; |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
n_blocks = minibuf[0] + (minibuf[1] << 8) |
197 |
|
|
+ (minibuf[2] << 16) + ((uint64_t)minibuf[3] << 24); |
198 |
|
|
|
199 |
|
|
bootblock_offset = (minibuf[4] + (minibuf[5] << 8) + |
200 |
|
|
(minibuf[6]<<16) + ((uint64_t)minibuf[7]<<24)) * 512; |
201 |
|
|
|
202 |
|
|
if (n_blocks < 1) |
203 |
|
|
break; |
204 |
|
|
|
205 |
|
|
debug(readofs == 0x18? ": %i" : " + %i", n_blocks); |
206 |
|
|
|
207 |
|
|
if (n_blocks * 512 > 65536) |
208 |
|
|
fatal("\nWARNING! Unusually large bootblock " |
209 |
|
|
"(%i bytes)\n\n", n_blocks * 512); |
210 |
|
|
|
211 |
dpavlin |
42 |
CHECK_ALLOCATION(bootblock_buf = malloc(n_blocks*512)); |
212 |
dpavlin |
36 |
|
213 |
|
|
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
214 |
|
|
0, bootblock_offset, bootblock_buf, n_blocks * 512); |
215 |
|
|
if (!res) { |
216 |
|
|
fatal("WARNING: could not load bootblocks from" |
217 |
|
|
" disk offset 0x%llx\n", |
218 |
|
|
(long long)bootblock_offset); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
store_buf(cpu, bootblock_loadaddr, |
222 |
|
|
(char *)bootblock_buf, n_blocks * 512); |
223 |
|
|
|
224 |
|
|
bootblock_loadaddr += 512*n_blocks; |
225 |
|
|
free(bootblock_buf); |
226 |
|
|
readofs += 8; |
227 |
|
|
} |
228 |
|
|
|
229 |
|
|
debug(readofs == 0x18? ": no blocks?\n" : " blocks\n"); |
230 |
|
|
return 1; |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
|
234 |
|
|
/* |
235 |
|
|
* Try reading a kernel manually from the disk. The code here |
236 |
|
|
* does not rely on machine-dependent boot blocks etc. |
237 |
|
|
*/ |
238 |
|
|
/* ISO9660: (0x800 bytes at 0x8000 + base_offset) */ |
239 |
dpavlin |
42 |
CHECK_ALLOCATION(bootblock_buf = malloc(0x800)); |
240 |
dpavlin |
36 |
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
241 |
|
|
0, base_offset + 0x8000, bootblock_buf, 0x800); |
242 |
|
|
if (!res) { |
243 |
|
|
fatal("Couldn't read the disk image. Aborting.\n"); |
244 |
|
|
return 0; |
245 |
|
|
} |
246 |
|
|
|
247 |
|
|
iso_type = 0; |
248 |
|
|
if (strncmp((char *)bootblock_buf+1, "CD001", 5) == 0) |
249 |
|
|
iso_type = 1; |
250 |
|
|
if (strncmp((char *)bootblock_buf+1, "CDW01", 5) == 0) |
251 |
|
|
iso_type = 2; |
252 |
|
|
if (strncmp((char *)bootblock_buf+1, "CDROM", 5) == 0) |
253 |
|
|
iso_type = 3; |
254 |
|
|
|
255 |
|
|
if (iso_type != 0) { |
256 |
|
|
/* |
257 |
|
|
* If the user specified a kernel name, then load it from |
258 |
|
|
* disk. |
259 |
|
|
*/ |
260 |
|
|
if (cpu->machine->boot_kernel_filename == NULL || |
261 |
|
|
cpu->machine->boot_kernel_filename[0] == '\0') |
262 |
|
|
fatal("\nISO9660 filesystem, but no kernel " |
263 |
|
|
"specified? (Use the -j option.)\n"); |
264 |
|
|
else |
265 |
|
|
retval = iso_load_bootblock(m, cpu, boot_disk_id, |
266 |
|
|
boot_disk_type, iso_type, bootblock_buf, |
267 |
|
|
n_loadp, load_namesp); |
268 |
|
|
} |
269 |
|
|
|
270 |
|
|
if (retval != 0) |
271 |
|
|
goto ret_ok; |
272 |
|
|
|
273 |
|
|
/* Apple parition table: */ |
274 |
|
|
res = diskimage_access(m, boot_disk_id, boot_disk_type, |
275 |
|
|
0, 0x0, bootblock_buf, 0x800); |
276 |
|
|
if (!res) { |
277 |
|
|
fatal("Couldn't read the disk image. Aborting.\n"); |
278 |
|
|
return 0; |
279 |
|
|
} |
280 |
|
|
if (bootblock_buf[0x000] == 'E' && bootblock_buf[0x001] == 'R' && |
281 |
|
|
bootblock_buf[0x200] == 'P' && bootblock_buf[0x201] == 'M') { |
282 |
|
|
if (cpu->machine->boot_kernel_filename == NULL || |
283 |
|
|
cpu->machine->boot_kernel_filename[0] == '\0') |
284 |
|
|
fatal("\nApple partition table, but no kernel " |
285 |
|
|
"specified? (Use the -j option.)\n"); |
286 |
|
|
else |
287 |
|
|
retval = apple_load_bootblock(m, cpu, boot_disk_id, |
288 |
|
|
boot_disk_type, n_loadp, load_namesp); |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
ret_ok: |
292 |
|
|
free(bootblock_buf); |
293 |
|
|
return retval; |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
|