bootblock.cc Source File

Back to the index.

bootblock.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2003-2011 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  * Bootblock handling:
29  *
30  * o) For some machines (e.g. DECstation or the Dreamcast), it is
31  * possible to load a bootblock from a fixed location on disk, and
32  * simply execute it in memory.
33  *
34  * o) For booting from generic CDROM ISO9660 images, a filename of
35  * a file to load must be supplied (the kernel filename). It is
36  * loaded, possibly gunzipped, and then executed as if it was a
37  * separate file.
38  *
39  * TODO: This module needs some cleanup.
40  */
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 
46 #include "cpu.h"
47 #include "diskimage.h"
48 #include "emul.h"
49 #include "machine.h"
50 #include "memory.h"
51 
52 static const char *diskimage_types[] = DISKIMAGE_TYPES;
53 
54 
55 /*
56  * load_bootblock():
57  *
58  * For some emulation modes, it is possible to boot from a harddisk image by
59  * loading a bootblock from a specific disk offset into memory, and executing
60  * that, instead of requiring a separate kernel file. It is then up to the
61  * bootblock to load a kernel.
62  *
63  * Returns 1 on success, 0 on failure.
64  */
65 int load_bootblock(struct machine *m, struct cpu *cpu,
66  int *n_loadp, char ***load_namesp)
67 {
68  int boot_disk_id, boot_disk_type = 0, n_blocks, res, readofs,
69  iso_type, retval = 0;
70  unsigned char minibuf[0x20];
71  unsigned char *bootblock_buf;
72  uint64_t bootblock_offset, base_offset;
73  uint64_t bootblock_loadaddr, bootblock_pc;
74 
75  boot_disk_id = diskimage_bootdev(m, &boot_disk_type);
76  if (boot_disk_id < 0)
77  return 0;
78 
79  base_offset = diskimage_get_baseoffset(m, boot_disk_id, boot_disk_type);
80 
81  switch (m->machine_type) {
82 
83  case MACHINE_DREAMCAST:
84  if (!diskimage_is_a_cdrom(cpu->machine, boot_disk_id,
85  boot_disk_type)) {
86  fatal("The Dreamcast emulation mode can only boot"
87  " from CD images, not from other disk types.\n");
88  exit(1);
89  }
90 
91  CHECK_ALLOCATION(bootblock_buf = (unsigned char *) malloc(32768));
92 
93  debug("loading Dreamcast IP.BIN from %s id %i to 0x8c008000\n",
94  diskimage_types[boot_disk_type], boot_disk_id);
95 
96  res = diskimage_access(m, boot_disk_id, boot_disk_type,
97  0, base_offset, bootblock_buf, 0x8000);
98  if (!res) {
99  fatal("Couldn't read the first 32 KB from the disk image. Aborting.\n");
100  return 0;
101  }
102 
103  if (strncmp((char *)bootblock_buf, "SEGA ", 5) != 0) {
104  fatal("This is not a Dreamcast IP.BIN header.\n");
105  free(bootblock_buf);
106  return 0;
107  }
108 
109  /*
110  * Store IP.BIN at 0x8c008000, and set entry point.
111  *
112  * Note: The boot block contains several executable parts:
113  * Offset 0x0300-36ff: SEGA logo code
114  * Offset 0x3800-5FFF: Bootstrap code 1
115  * Offset 0x6000-7FFF: Bootstrap code 2
116  *
117  * (See http://mc.pp.se/dc/ip.bin.html for details.)
118  *
119  * So one way to boot could be to set initial PC to 0x8c008300,
120  * but that would only run the SEGA logo code and then return
121  * to... well, to nothing.
122  *
123  * Instead, initial PC is set to 0x8c000140, which triggers
124  * software PROM emulation, which in turn:
125  * a) calls 0x8c008300 to show the logo, and
126  * b) calls 0x8c00b800 to set up registers etc and
127  * this code will hopefully jump to 0x8c010000.
128  *
129  * This mimics the behavior of the real Dreamcast.
130  */
131  store_buf(cpu, 0x8c008000, (char *)bootblock_buf, 32768);
132  cpu->pc = 0x8c000140; // see src/promemul/dreamcast.cc for details
133 
134  /* Remember the name of the file to boot (1ST_READ.BIN): */
135  if (cpu->machine->boot_kernel_filename == NULL ||
136  cpu->machine->boot_kernel_filename[0] == '\0') {
137  int i = 0x60;
138  while (i < 0x70) {
139  if (bootblock_buf[i] == ' ')
140  bootblock_buf[i] = 0;
141  i ++;
142  }
144  strdup((char *)bootblock_buf + 0x60));
145  }
146 
147  debug("Dreamcast boot filename: %s (to be loaded to 0x8c010000)\n",
149 
150  free(bootblock_buf);
151 
152  break;
153 
154  case MACHINE_PMAX:
155  /*
156  * The first few bytes of a disk contains information about
157  * where the bootblock(s) are located. (These are all 32-bit
158  * little-endian words.)
159  *
160  * Offset 0x10 = load address
161  * 0x14 = initial PC value
162  * 0x18 = nr of 512-byte blocks to read
163  * 0x1c = offset on disk to where the bootblocks
164  * are (in 512-byte units)
165  * 0x20 = nr of blocks to read...
166  * 0x24 = offset...
167  *
168  * nr of blocks to read and offset are repeated until nr of
169  * blocks to read is zero.
170  */
171  res = diskimage_access(m, boot_disk_id, boot_disk_type, 0, 0,
172  minibuf, sizeof(minibuf));
173 
174  bootblock_loadaddr = minibuf[0x10] + (minibuf[0x11] << 8)
175  + (minibuf[0x12] << 16) + ((uint64_t)minibuf[0x13] << 24);
176 
177  /* Convert loadaddr to uncached: */
178  if ((bootblock_loadaddr & 0xf0000000ULL) != 0x80000000 &&
179  (bootblock_loadaddr & 0xf0000000ULL) != 0xa0000000) {
180  fatal("\nWARNING! Weird load address 0x%08" PRIx32
181  " for SCSI id %i.\n\n",
182  (uint32_t)bootblock_loadaddr, boot_disk_id);
183  if (bootblock_loadaddr == 0) {
184  fatal("I'm assuming that this is _not_ a "
185  "DEC bootblock.\nAre you sure you are"
186  " booting from the correct disk?\n");
187  exit(1);
188  }
189  }
190 
191  bootblock_loadaddr &= 0x0fffffffULL;
192  bootblock_loadaddr |= 0xffffffffa0000000ULL;
193 
194  bootblock_pc = minibuf[0x14] + (minibuf[0x15] << 8)
195  + (minibuf[0x16] << 16) + ((uint64_t)minibuf[0x17] << 24);
196 
197  bootblock_pc &= 0x0fffffffULL;
198  bootblock_pc |= 0xffffffffa0000000ULL;
199  cpu->pc = bootblock_pc;
200 
201  debug("DEC boot: loadaddr=0x%08" PRIx32", pc=0x%08" PRIx32,
202  (uint32_t) bootblock_loadaddr, (uint32_t) bootblock_pc);
203 
204  readofs = 0x18;
205 
206  for (;;) {
207  res = diskimage_access(m, boot_disk_id, boot_disk_type,
208  0, readofs, minibuf, sizeof(minibuf));
209  if (!res) {
210  fatal("Couldn't read the disk image. "
211  "Aborting.\n");
212  return 0;
213  }
214 
215  n_blocks = minibuf[0] + (minibuf[1] << 8)
216  + (minibuf[2] << 16) + ((uint64_t)minibuf[3] << 24);
217 
218  bootblock_offset = (minibuf[4] + (minibuf[5] << 8) +
219  (minibuf[6]<<16) + ((uint64_t)minibuf[7]<<24)) * 512;
220 
221  if (n_blocks < 1)
222  break;
223 
224  debug(readofs == 0x18? ": %i" : " + %i", n_blocks);
225 
226  if (n_blocks * 512 > 65536)
227  fatal("\nWARNING! Unusually large bootblock "
228  "(%i bytes)\n\n", n_blocks * 512);
229 
230  CHECK_ALLOCATION(bootblock_buf = (unsigned char *) malloc(n_blocks*512));
231 
232  res = diskimage_access(m, boot_disk_id, boot_disk_type,
233  0, bootblock_offset, bootblock_buf, n_blocks * 512);
234  if (!res) {
235  fatal("WARNING: could not load bootblocks from"
236  " disk offset 0x%llx\n",
237  (long long)bootblock_offset);
238  }
239 
240  store_buf(cpu, bootblock_loadaddr,
241  (char *)bootblock_buf, n_blocks * 512);
242 
243  bootblock_loadaddr += 512*n_blocks;
244  free(bootblock_buf);
245  readofs += 8;
246  }
247 
248  debug(readofs == 0x18? ": no blocks?\n" : " blocks\n");
249  return 1;
250  }
251 
252 
253  /*
254  * Try reading a kernel manually from the disk. The code here
255  * does not rely on machine-dependent boot blocks etc.
256  */
257  /* ISO9660: (0x800 bytes at 0x8000 + base_offset) */
258  CHECK_ALLOCATION(bootblock_buf = (unsigned char *) malloc(0x800));
259  res = diskimage_access(m, boot_disk_id, boot_disk_type,
260  0, base_offset + 0x8000, bootblock_buf, 0x800);
261  if (!res) {
262  fatal("Couldn't read the ISO header from the disk image. Aborting.\n");
263  return 0;
264  }
265 
266  iso_type = 0;
267  if (strncmp((char *)bootblock_buf+1, "CD001", 5) == 0)
268  iso_type = 1;
269  if (strncmp((char *)bootblock_buf+1, "CDW01", 5) == 0)
270  iso_type = 2;
271  if (strncmp((char *)bootblock_buf+1, "CDROM", 5) == 0)
272  iso_type = 3;
273 
274  if (iso_type != 0) {
275  /*
276  * If the user specified a kernel name, then load it from
277  * disk.
278  */
279  if (cpu->machine->boot_kernel_filename == NULL ||
280  cpu->machine->boot_kernel_filename[0] == '\0')
281  fatal("\nISO9660 filesystem, but no kernel "
282  "specified? (Use the -j option.)\n");
283  else
284  retval = iso_load_bootblock(m, cpu, boot_disk_id,
285  boot_disk_type, iso_type, bootblock_buf,
286  n_loadp, load_namesp);
287  }
288 
289  if (retval != 0)
290  goto ret_ok;
291 
292  /* Apple parition table: */
293  res = diskimage_access(m, boot_disk_id, boot_disk_type,
294  0, 0x0, bootblock_buf, 0x800);
295  if (!res) {
296  fatal("Couldn't read the disk image. Aborting.\n");
297  return 0;
298  }
299  if (bootblock_buf[0x000] == 'E' && bootblock_buf[0x001] == 'R' &&
300  bootblock_buf[0x200] == 'P' && bootblock_buf[0x201] == 'M') {
301  if (cpu->machine->boot_kernel_filename == NULL ||
302  cpu->machine->boot_kernel_filename[0] == '\0')
303  fatal("\nApple partition table, but no kernel "
304  "specified? (Use the -j option.)\n");
305  else
306  retval = apple_load_bootblock(m, cpu, boot_disk_id,
307  boot_disk_type, n_loadp, load_namesp);
308  }
309 
310 ret_ok:
311  free(bootblock_buf);
312  return retval;
313 }
314 
315 
diskimage_bootdev
int diskimage_bootdev(struct machine *machine, int *typep)
Definition: diskimage.cc:1001
store_buf
void store_buf(struct cpu *cpu, uint64_t addr, const char *s, size_t len)
Definition: memory.cc:826
diskimage.h
debug
#define debug
Definition: dev_adb.cc:57
DISKIMAGE_TYPES
#define DISKIMAGE_TYPES
Definition: diskimage.h:44
iso_load_bootblock
int iso_load_bootblock(struct machine *m, struct cpu *cpu, int disk_id, int disk_type, int iso_type, unsigned char *buf, int *n_loadp, char ***load_namesp)
Definition: bootblock_iso9660.cc:99
diskimage_access
int diskimage_access(struct machine *machine, int id, int type, int writeflag, off_t offset, unsigned char *buf, size_t len)
Definition: diskimage.cc:617
diskimage_is_a_cdrom
int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
Definition: diskimage.cc:1060
MACHINE_DREAMCAST
#define MACHINE_DREAMCAST
Definition: machine.h:251
load_bootblock
int load_bootblock(struct machine *m, struct cpu *cpu, int *n_loadp, char ***load_namesp)
Definition: bootblock.cc:65
fatal
void fatal(const char *fmt,...)
Definition: main.cc:152
diskimage_get_baseoffset
int64_t diskimage_get_baseoffset(struct machine *machine, int id, int type)
Definition: diskimage.cc:222
machine.h
machine
Definition: machine.h:97
emul.h
cpu.h
apple_load_bootblock
int apple_load_bootblock(struct machine *m, struct cpu *cpu, int disk_id, int disk_type, int *n_loadp, char ***load_namesp)
Definition: bootblock_apple.cc:53
cpu::machine
struct machine * machine
Definition: cpu.h:328
machine::boot_kernel_filename
char * boot_kernel_filename
Definition: machine.h:170
MACHINE_PMAX
#define MACHINE_PMAX
Definition: machine.h:213
machine::machine_type
int machine_type
Definition: machine.h:111
cpu
Definition: cpu.h:326
cpu::pc
uint64_t pc
Definition: cpu.h:386
memory.h
CHECK_ALLOCATION
#define CHECK_ALLOCATION(ptr)
Definition: misc.h:239

Generated on Tue Aug 25 2020 19:25:06 for GXemul by doxygen 1.8.18