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: diskimage.c,v 1.5 2007/03/26 03:01:09 debug Exp $ |
29 |
* |
30 |
* Disk image support. |
31 |
* |
32 |
* TODO: diskimage_remove()? This would be useful for floppies in PC-style |
33 |
* machines, where disks may need to be swapped during boot etc. |
34 |
*/ |
35 |
|
36 |
#include <stdio.h> |
37 |
#include <stdlib.h> |
38 |
#include <string.h> |
39 |
#include <unistd.h> |
40 |
#include <sys/types.h> |
41 |
#include <sys/stat.h> |
42 |
|
43 |
#include "cpu.h" |
44 |
#include "diskimage.h" |
45 |
#include "machine.h" |
46 |
#include "misc.h" |
47 |
|
48 |
|
49 |
/* #define debug fatal */ |
50 |
|
51 |
extern int single_step; |
52 |
|
53 |
static char *diskimage_types[] = DISKIMAGE_TYPES; |
54 |
|
55 |
|
56 |
/**************************************************************************/ |
57 |
|
58 |
/* |
59 |
* my_fseek(): |
60 |
* |
61 |
* A helper function, like fseek() but takes off_t. If the system has |
62 |
* fseeko, then that is used. Otherwise I try to fake off_t offsets here. |
63 |
* |
64 |
* The correct position is reached by seeking 2 billion bytes at a time |
65 |
* (or less). Note: This method is only used for SEEK_SET, for SEEK_CUR |
66 |
* and SEEK_END, normal fseek() is used! |
67 |
* |
68 |
* TODO: It seemed to work on Linux/i386, but not on Solaris/sparc (?). |
69 |
* Anyway, most modern systems have fseeko(), so it shouldn't be a problem. |
70 |
*/ |
71 |
static int my_fseek(FILE *f, off_t offset, int whence) |
72 |
{ |
73 |
#ifdef HACK_FSEEKO |
74 |
if (whence == SEEK_SET) { |
75 |
int res = 0; |
76 |
off_t curoff = 0; |
77 |
off_t cur_step; |
78 |
|
79 |
fseek(f, 0, SEEK_SET); |
80 |
while (curoff < offset) { |
81 |
/* How far to seek? */ |
82 |
cur_step = offset - curoff; |
83 |
if (cur_step > 2000000000) |
84 |
cur_step = 2000000000; |
85 |
res = fseek(f, cur_step, SEEK_CUR); |
86 |
if (res) |
87 |
return res; |
88 |
curoff += cur_step; |
89 |
} |
90 |
return 0; |
91 |
} else |
92 |
return fseek(f, offset, whence); |
93 |
#else |
94 |
return fseeko(f, offset, whence); |
95 |
#endif |
96 |
} |
97 |
|
98 |
|
99 |
/**************************************************************************/ |
100 |
|
101 |
|
102 |
/* |
103 |
* diskimage_exist(): |
104 |
* |
105 |
* Returns 1 if the specified disk id (for a specific type) exists, 0 |
106 |
* otherwise. |
107 |
*/ |
108 |
int diskimage_exist(struct machine *machine, int id, int type) |
109 |
{ |
110 |
struct diskimage *d = machine->first_diskimage; |
111 |
|
112 |
while (d != NULL) { |
113 |
if (d->type == type && d->id == id) |
114 |
return 1; |
115 |
d = d->next; |
116 |
} |
117 |
return 0; |
118 |
} |
119 |
|
120 |
|
121 |
/* |
122 |
* diskimage_add_overlay(): |
123 |
* |
124 |
* Opens an overlay data file and its corresponding bitmap file, and adds |
125 |
* the overlay to a disk image. |
126 |
*/ |
127 |
void diskimage_add_overlay(struct diskimage *d, char *overlay_basename) |
128 |
{ |
129 |
struct diskimage_overlay overlay; |
130 |
size_t bitmap_name_len = strlen(overlay_basename) + 20; |
131 |
char *bitmap_name = malloc(bitmap_name_len); |
132 |
|
133 |
if (bitmap_name == NULL) { |
134 |
fprintf(stderr, "out of memory\n"); |
135 |
exit(1); |
136 |
} |
137 |
snprintf(bitmap_name, bitmap_name_len, "%s.map", overlay_basename); |
138 |
|
139 |
overlay.overlay_basename = strdup(overlay_basename); |
140 |
overlay.f_data = fopen(overlay_basename, d->writable? "r+" : "r"); |
141 |
if (overlay.f_data == NULL) { |
142 |
perror(overlay_basename); |
143 |
exit(1); |
144 |
} |
145 |
|
146 |
overlay.f_bitmap = fopen(bitmap_name, d->writable? "r+" : "r"); |
147 |
if (overlay.f_bitmap == NULL) { |
148 |
perror(bitmap_name); |
149 |
fprintf(stderr, "Please create the map file first.\n"); |
150 |
exit(1); |
151 |
} |
152 |
|
153 |
d->nr_of_overlays ++; |
154 |
d->overlays = realloc(d->overlays, sizeof(struct diskimage_overlay) |
155 |
* d->nr_of_overlays); |
156 |
if (d->overlays == NULL) { |
157 |
fprintf(stderr, "out of memory\n"); |
158 |
exit(1); |
159 |
} |
160 |
|
161 |
d->overlays[d->nr_of_overlays - 1] = overlay; |
162 |
|
163 |
free(bitmap_name); |
164 |
} |
165 |
|
166 |
|
167 |
/* |
168 |
* diskimage_recalc_size(): |
169 |
* |
170 |
* Recalculate a disk's size by stat()-ing it. |
171 |
* d is assumed to be non-NULL. |
172 |
*/ |
173 |
void diskimage_recalc_size(struct diskimage *d) |
174 |
{ |
175 |
struct stat st; |
176 |
int res; |
177 |
off_t size = 0; |
178 |
|
179 |
res = stat(d->fname, &st); |
180 |
if (res) { |
181 |
fprintf(stderr, "[ diskimage_recalc_size(): could not stat " |
182 |
"'%s' ]\n", d->fname); |
183 |
return; |
184 |
} |
185 |
|
186 |
size = st.st_size; |
187 |
|
188 |
/* |
189 |
* TODO: CD-ROM devices, such as /dev/cd0c, how can one |
190 |
* check how much data is on that cd-rom without reading it? |
191 |
* For now, assume some large number, hopefully it will be |
192 |
* enough to hold any cd-rom image. |
193 |
*/ |
194 |
if (d->is_a_cdrom && size == 0) |
195 |
size = 762048000; |
196 |
|
197 |
d->total_size = size; |
198 |
d->ncyls = d->total_size / 1048576; |
199 |
|
200 |
/* TODO: There is a mismatch between d->ncyls and d->cylinders, |
201 |
SCSI-based stuff usually doesn't care. TODO: Fix this. */ |
202 |
} |
203 |
|
204 |
|
205 |
/* |
206 |
* diskimage_getsize(): |
207 |
* |
208 |
* Returns -1 if the specified disk id/type does not exists, otherwise |
209 |
* the size of the disk image is returned. |
210 |
*/ |
211 |
int64_t diskimage_getsize(struct machine *machine, int id, int type) |
212 |
{ |
213 |
struct diskimage *d = machine->first_diskimage; |
214 |
|
215 |
while (d != NULL) { |
216 |
if (d->type == type && d->id == id) |
217 |
return d->total_size; |
218 |
d = d->next; |
219 |
} |
220 |
return -1; |
221 |
} |
222 |
|
223 |
|
224 |
/* |
225 |
* diskimage_get_baseoffset(): |
226 |
* |
227 |
* Returns -1 if the specified disk id/type does not exists, otherwise |
228 |
* the base offset of the disk image is returned. |
229 |
*/ |
230 |
int64_t diskimage_get_baseoffset(struct machine *machine, int id, int type) |
231 |
{ |
232 |
struct diskimage *d = machine->first_diskimage; |
233 |
|
234 |
while (d != NULL) { |
235 |
if (d->type == type && d->id == id) |
236 |
return d->override_base_offset; |
237 |
d = d->next; |
238 |
} |
239 |
return -1; |
240 |
} |
241 |
|
242 |
|
243 |
/* |
244 |
* diskimage_getchs(): |
245 |
* |
246 |
* Returns the current CHS values of a disk image. |
247 |
*/ |
248 |
void diskimage_getchs(struct machine *machine, int id, int type, |
249 |
int *c, int *h, int *s) |
250 |
{ |
251 |
struct diskimage *d = machine->first_diskimage; |
252 |
|
253 |
while (d != NULL) { |
254 |
if (d->type == type && d->id == id) { |
255 |
*c = d->cylinders; |
256 |
*h = d->heads; |
257 |
*s = d->sectors_per_track; |
258 |
return; |
259 |
} |
260 |
d = d->next; |
261 |
} |
262 |
fatal("diskimage_getchs(): disk id %i (type %i) not found?\n", |
263 |
id, diskimage_types[type]); |
264 |
exit(1); |
265 |
} |
266 |
|
267 |
|
268 |
/* |
269 |
* diskimage_access__cdrom(): |
270 |
* |
271 |
* This is a special-case function, called from diskimage__internal_access(). |
272 |
* On my FreeBSD 4.9 system, the cdrom device /dev/cd0c seems to not be able |
273 |
* to handle something like "fseek(512); fread(512);" but it handles |
274 |
* "fseek(2048); fread(512);" just fine. So, if diskimage__internal_access() |
275 |
* fails in reading a block of data, this function is called as an attempt to |
276 |
* align reads at 2048-byte sectors instead. |
277 |
* |
278 |
* (Ugly hack. TODO: how to solve this cleanly?) |
279 |
* |
280 |
* NOTE: Returns the number of bytes read, 0 if nothing was successfully |
281 |
* read. (These are not the same as diskimage_access()). |
282 |
*/ |
283 |
#define CDROM_SECTOR_SIZE 2048 |
284 |
static size_t diskimage_access__cdrom(struct diskimage *d, off_t offset, |
285 |
unsigned char *buf, size_t len) |
286 |
{ |
287 |
off_t aligned_offset; |
288 |
size_t bytes_read, total_copied = 0; |
289 |
unsigned char cdrom_buf[CDROM_SECTOR_SIZE]; |
290 |
off_t buf_ofs, i = 0; |
291 |
|
292 |
/* printf("diskimage_access__cdrom(): offset=0x%llx size=%lli\n", |
293 |
(long long)offset, (long long)len); */ |
294 |
|
295 |
aligned_offset = (offset / CDROM_SECTOR_SIZE) * CDROM_SECTOR_SIZE; |
296 |
my_fseek(d->f, aligned_offset, SEEK_SET); |
297 |
|
298 |
while (len != 0) { |
299 |
bytes_read = fread(cdrom_buf, 1, CDROM_SECTOR_SIZE, d->f); |
300 |
if (bytes_read != CDROM_SECTOR_SIZE) |
301 |
return 0; |
302 |
|
303 |
/* Copy (part of) cdrom_buf into buf: */ |
304 |
buf_ofs = offset - aligned_offset; |
305 |
while (buf_ofs < CDROM_SECTOR_SIZE && len != 0) { |
306 |
buf[i ++] = cdrom_buf[buf_ofs ++]; |
307 |
total_copied ++; |
308 |
len --; |
309 |
} |
310 |
|
311 |
aligned_offset += CDROM_SECTOR_SIZE; |
312 |
offset = aligned_offset; |
313 |
} |
314 |
|
315 |
return total_copied; |
316 |
} |
317 |
|
318 |
|
319 |
/* Helper function. */ |
320 |
static void overlay_set_block_in_use(struct diskimage *d, |
321 |
int overlay_nr, off_t ofs) |
322 |
{ |
323 |
off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE; |
324 |
off_t bitmap_file_offset = bit_nr / 8; |
325 |
int res; |
326 |
unsigned char data; |
327 |
|
328 |
res = my_fseek(d->overlays[overlay_nr].f_bitmap, |
329 |
bitmap_file_offset, SEEK_SET); |
330 |
if (res) { |
331 |
perror("my_fseek"); |
332 |
fprintf(stderr, "Could not seek in bitmap file?" |
333 |
" offset = %lli, read\n", (long long)bitmap_file_offset); |
334 |
exit(1); |
335 |
} |
336 |
|
337 |
/* Read the original bitmap data, and OR in the new bit: */ |
338 |
res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap); |
339 |
if (res != 1) |
340 |
data = 0x00; |
341 |
|
342 |
data |= (1 << (bit_nr & 7)); |
343 |
|
344 |
/* Write it back: */ |
345 |
res = my_fseek(d->overlays[overlay_nr].f_bitmap, |
346 |
bitmap_file_offset, SEEK_SET); |
347 |
if (res) { |
348 |
perror("my_fseek"); |
349 |
fprintf(stderr, "Could not seek in bitmap file?" |
350 |
" offset = %lli, write\n", (long long)bitmap_file_offset); |
351 |
exit(1); |
352 |
} |
353 |
res = fwrite(&data, 1, 1, d->overlays[overlay_nr].f_bitmap); |
354 |
if (res != 1) { |
355 |
fprintf(stderr, "Could not write to bitmap file. Aborting.\n"); |
356 |
exit(1); |
357 |
} |
358 |
} |
359 |
|
360 |
|
361 |
/* Helper function. */ |
362 |
static int overlay_has_block(struct diskimage *d, int overlay_nr, off_t ofs) |
363 |
{ |
364 |
off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE; |
365 |
off_t bitmap_file_offset = bit_nr / 8; |
366 |
int res; |
367 |
unsigned char data; |
368 |
|
369 |
res = my_fseek(d->overlays[overlay_nr].f_bitmap, |
370 |
bitmap_file_offset, SEEK_SET); |
371 |
if (res != 0) |
372 |
return 0; |
373 |
|
374 |
/* The seek succeeded, now read the bit: */ |
375 |
res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap); |
376 |
if (res != 1) |
377 |
return 0; |
378 |
|
379 |
if (data & (1 << (bit_nr & 7))) |
380 |
return 1; |
381 |
|
382 |
return 0; |
383 |
} |
384 |
|
385 |
|
386 |
/* |
387 |
* fwrite_helper(): |
388 |
* |
389 |
* Internal helper function. Writes to a disk image file, or if the |
390 |
* disk image has overlays, to the last overlay. |
391 |
*/ |
392 |
static size_t fwrite_helper(off_t offset, unsigned char *buf, |
393 |
size_t len, struct diskimage *d) |
394 |
{ |
395 |
off_t curofs; |
396 |
|
397 |
/* Fast return-path for the case when no overlays are used: */ |
398 |
if (d->nr_of_overlays == 0) { |
399 |
int res = my_fseek(d->f, offset, SEEK_SET); |
400 |
if (res != 0) { |
401 |
fatal("[ diskimage__internal_access(): fseek() failed" |
402 |
" on disk id %i \n", d->id); |
403 |
return 0; |
404 |
} |
405 |
|
406 |
return fwrite(buf, 1, len, d->f); |
407 |
} |
408 |
|
409 |
if ((len & (OVERLAY_BLOCK_SIZE-1)) != 0) { |
410 |
fatal("TODO: overlay access (write), len not multiple of " |
411 |
"overlay block size. not yet implemented.\n"); |
412 |
fatal("len = %lli\n", (long long) len); |
413 |
abort(); |
414 |
} |
415 |
if ((offset & (OVERLAY_BLOCK_SIZE-1)) != 0) { |
416 |
fatal("TODO: unaligned overlay access\n"); |
417 |
fatal("offset = %lli\n", (long long) offset); |
418 |
abort(); |
419 |
} |
420 |
|
421 |
/* Split the write into OVERLAY_BLOCK_SIZE writes: */ |
422 |
for (curofs=offset; curofs<offset+len; curofs+=OVERLAY_BLOCK_SIZE) { |
423 |
/* Always write to the last overlay: */ |
424 |
int overlay_nr = d->nr_of_overlays-1; |
425 |
off_t lenwritten; |
426 |
int res = my_fseek(d->overlays[overlay_nr].f_data, |
427 |
curofs, SEEK_SET); |
428 |
if (res != 0) { |
429 |
fatal("[ diskimage__internal_access(): fseek()" |
430 |
" failed on disk id %i \n", d->id); |
431 |
return 0; |
432 |
} |
433 |
|
434 |
lenwritten = fwrite(buf, 1, OVERLAY_BLOCK_SIZE, |
435 |
d->overlays[overlay_nr].f_data); |
436 |
buf += OVERLAY_BLOCK_SIZE; |
437 |
|
438 |
/* Mark this block in the last overlay as in use: */ |
439 |
overlay_set_block_in_use(d, overlay_nr, curofs); |
440 |
} |
441 |
|
442 |
return len; |
443 |
} |
444 |
|
445 |
|
446 |
/* |
447 |
* fread_helper(): |
448 |
* |
449 |
* Internal helper function. Reads from a disk image file, or if the |
450 |
* disk image has overlays, from the last overlay that has the specific |
451 |
* data (or the disk image file itself). |
452 |
*/ |
453 |
static size_t fread_helper(off_t offset, unsigned char *buf, |
454 |
size_t len, struct diskimage *d) |
455 |
{ |
456 |
off_t curofs; |
457 |
size_t totallenread = 0; |
458 |
|
459 |
/* Fast return-path for the case when no overlays are used: */ |
460 |
if (d->nr_of_overlays == 0) { |
461 |
int res = my_fseek(d->f, offset, SEEK_SET); |
462 |
if (res != 0) { |
463 |
fatal("[ diskimage__internal_access(): fseek() failed" |
464 |
" on disk id %i \n", d->id); |
465 |
return 0; |
466 |
} |
467 |
|
468 |
return fread(buf, 1, len, d->f); |
469 |
} |
470 |
|
471 |
/* Split the read into OVERLAY_BLOCK_SIZE reads: */ |
472 |
for (curofs=offset; len != 0; |
473 |
curofs = (curofs | (OVERLAY_BLOCK_SIZE-1)) + 1) { |
474 |
/* Find the overlay, if any, that has this block: */ |
475 |
off_t lenread, lentoread; |
476 |
int overlay_nr; |
477 |
for (overlay_nr = d->nr_of_overlays-1; |
478 |
overlay_nr >= 0; overlay_nr --) { |
479 |
if (overlay_has_block(d, overlay_nr, curofs)) |
480 |
break; |
481 |
} |
482 |
|
483 |
lentoread = len > OVERLAY_BLOCK_SIZE? OVERLAY_BLOCK_SIZE : len; |
484 |
|
485 |
if (overlay_nr >= 0) { |
486 |
/* Read from overlay: */ |
487 |
int res = my_fseek(d->overlays[overlay_nr].f_data, |
488 |
curofs, SEEK_SET); |
489 |
if (res != 0) { |
490 |
fatal("[ diskimage__internal_access(): fseek()" |
491 |
" failed on disk id %i \n", d->id); |
492 |
return 0; |
493 |
} |
494 |
lenread = fread(buf, 1, lentoread, |
495 |
d->overlays[overlay_nr].f_data); |
496 |
} else { |
497 |
/* Read from the base disk image: */ |
498 |
int res = my_fseek(d->f, curofs, SEEK_SET); |
499 |
if (res != 0) { |
500 |
fatal("[ diskimage__internal_access(): fseek()" |
501 |
" failed on disk id %i \n", d->id); |
502 |
return 0; |
503 |
} |
504 |
lenread = fread(buf, 1, lentoread, d->f); |
505 |
} |
506 |
|
507 |
if (lenread != lentoread) { |
508 |
fatal("[ INCOMPLETE READ from disk id %i, offset" |
509 |
" %lli ]\n", d->id, (long long)curofs); |
510 |
} |
511 |
|
512 |
len -= lentoread; |
513 |
totallenread += lenread; |
514 |
buf += OVERLAY_BLOCK_SIZE; |
515 |
} |
516 |
|
517 |
return totallenread; |
518 |
} |
519 |
|
520 |
|
521 |
/* |
522 |
* diskimage__internal_access(): |
523 |
* |
524 |
* Read from or write to a struct diskimage. |
525 |
* |
526 |
* Returns 1 if the access completed successfully, 0 otherwise. |
527 |
*/ |
528 |
int diskimage__internal_access(struct diskimage *d, int writeflag, |
529 |
off_t offset, unsigned char *buf, size_t len) |
530 |
{ |
531 |
ssize_t lendone; |
532 |
|
533 |
if (buf == NULL) { |
534 |
fprintf(stderr, "diskimage__internal_access(): buf = NULL\n"); |
535 |
exit(1); |
536 |
} |
537 |
if (len == 0) |
538 |
return 1; |
539 |
if (d->f == NULL) |
540 |
return 0; |
541 |
|
542 |
if (writeflag) { |
543 |
if (!d->writable) |
544 |
return 0; |
545 |
|
546 |
lendone = fwrite_helper(offset, buf, len, d); |
547 |
} else { |
548 |
/* |
549 |
* Special case for CD-ROMs. Actually, this is not needed |
550 |
* for .iso images, only for physical CDROMS on some OSes, |
551 |
* such as FreeBSD. |
552 |
*/ |
553 |
if (d->is_a_cdrom) |
554 |
lendone = diskimage_access__cdrom(d, offset, buf, len); |
555 |
else |
556 |
lendone = fread_helper(offset, buf, len, d); |
557 |
|
558 |
if (lendone < (ssize_t)len) |
559 |
memset(buf + lendone, 0, len - lendone); |
560 |
} |
561 |
|
562 |
/* Incomplete data transfer? Then return failure: */ |
563 |
if (lendone != (ssize_t)len) { |
564 |
#ifdef UNSTABLE_DEVEL |
565 |
fatal |
566 |
#else |
567 |
debug |
568 |
#endif |
569 |
("[ diskimage__internal_access(): disk_id %i, offset %lli" |
570 |
", transfer not completed. len=%i, len_done=%i ]\n", |
571 |
d->id, (long long)offset, (int)len, (int)lendone); |
572 |
return 0; |
573 |
} |
574 |
|
575 |
return 1; |
576 |
} |
577 |
|
578 |
|
579 |
/* |
580 |
* diskimage_access(): |
581 |
* |
582 |
* Read from or write to a disk image on a machine. |
583 |
* |
584 |
* Returns 1 if the access completed successfully, 0 otherwise. |
585 |
*/ |
586 |
int diskimage_access(struct machine *machine, int id, int type, int writeflag, |
587 |
off_t offset, unsigned char *buf, size_t len) |
588 |
{ |
589 |
struct diskimage *d = machine->first_diskimage; |
590 |
|
591 |
while (d != NULL) { |
592 |
if (d->type == type && d->id == id) |
593 |
break; |
594 |
d = d->next; |
595 |
} |
596 |
|
597 |
if (d == NULL) { |
598 |
fatal("[ diskimage_access(): ERROR: trying to access a " |
599 |
"non-existant %s disk image (id %i)\n", |
600 |
diskimage_types[type], id); |
601 |
return 0; |
602 |
} |
603 |
|
604 |
offset -= d->override_base_offset; |
605 |
if (offset < 0 && offset + d->override_base_offset >= 0) { |
606 |
debug("[ reading before start of disk image ]\n"); |
607 |
/* Returning zeros. */ |
608 |
memset(buf, 0, len); |
609 |
return 1; |
610 |
} |
611 |
|
612 |
return diskimage__internal_access(d, writeflag, offset, buf, len); |
613 |
} |
614 |
|
615 |
|
616 |
/* |
617 |
* diskimage_add(): |
618 |
* |
619 |
* Add a disk image. fname is the filename of the disk image. |
620 |
* The filename may be prefixed with one or more modifiers, followed |
621 |
* by a colon. |
622 |
* |
623 |
* b specifies that this is a bootable device |
624 |
* c CD-ROM (instead of a normal DISK) |
625 |
* d DISK (this is the default) |
626 |
* f FLOPPY (instead of SCSI) |
627 |
* gH;S; set geometry (H=heads, S=sectors per track, cylinders are |
628 |
* automatically calculated). (This is ignored for floppies.) |
629 |
* i IDE (instead of SCSI) |
630 |
* oOFS; set base offset in bytes, when booting from an ISO9660 fs |
631 |
* r read-only (don't allow changes to the file) |
632 |
* s SCSI (this is the default) |
633 |
* t tape |
634 |
* V add an overlay to a disk image |
635 |
* 0-7 force a specific SCSI ID number |
636 |
* |
637 |
* machine is assumed to be non-NULL. |
638 |
* Returns an integer >= 0 identifying the disk image. |
639 |
*/ |
640 |
int diskimage_add(struct machine *machine, char *fname) |
641 |
{ |
642 |
struct diskimage *d, *d2; |
643 |
int id = 0, override_heads=0, override_spt=0; |
644 |
int64_t bytespercyl, override_base_offset=0; |
645 |
char *cp; |
646 |
int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0; |
647 |
int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id=-1; |
648 |
int prefix_o=0, prefix_V=0; |
649 |
|
650 |
if (fname == NULL) { |
651 |
fprintf(stderr, "diskimage_add(): NULL ptr\n"); |
652 |
return 0; |
653 |
} |
654 |
|
655 |
/* Get prefix from fname: */ |
656 |
cp = strchr(fname, ':'); |
657 |
if (cp != NULL) { |
658 |
while (fname <= cp) { |
659 |
char c = *fname++; |
660 |
switch (c) { |
661 |
case '0': |
662 |
case '1': |
663 |
case '2': |
664 |
case '3': |
665 |
case '4': |
666 |
case '5': |
667 |
case '6': |
668 |
case '7': |
669 |
prefix_id = c - '0'; |
670 |
break; |
671 |
case 'b': |
672 |
prefix_b = 1; |
673 |
break; |
674 |
case 'c': |
675 |
prefix_c = 1; |
676 |
break; |
677 |
case 'd': |
678 |
prefix_d = 1; |
679 |
break; |
680 |
case 'f': |
681 |
prefix_f = 1; |
682 |
break; |
683 |
case 'g': |
684 |
prefix_g = 1; |
685 |
override_heads = atoi(fname); |
686 |
while (*fname != '\0' && *fname != ';') |
687 |
fname ++; |
688 |
if (*fname == ';') |
689 |
fname ++; |
690 |
override_spt = atoi(fname); |
691 |
while (*fname != '\0' && *fname != ';' && |
692 |
*fname != ':') |
693 |
fname ++; |
694 |
if (*fname == ';') |
695 |
fname ++; |
696 |
if (override_heads < 1 || |
697 |
override_spt < 1) { |
698 |
fatal("Bad geometry: heads=%i " |
699 |
"spt=%i\n", override_heads, |
700 |
override_spt); |
701 |
exit(1); |
702 |
} |
703 |
break; |
704 |
case 'i': |
705 |
prefix_i = 1; |
706 |
break; |
707 |
case 'o': |
708 |
prefix_o = 1; |
709 |
override_base_offset = atoi(fname); |
710 |
while (*fname != '\0' && *fname != ':' |
711 |
&& *fname != ';') |
712 |
fname ++; |
713 |
if (*fname == ':' || *fname == ';') |
714 |
fname ++; |
715 |
if (override_base_offset < 0) { |
716 |
fatal("Bad base offset: %"PRIi64 |
717 |
"\n", override_base_offset); |
718 |
exit(1); |
719 |
} |
720 |
break; |
721 |
case 'r': |
722 |
prefix_r = 1; |
723 |
break; |
724 |
case 's': |
725 |
prefix_s = 1; |
726 |
break; |
727 |
case 't': |
728 |
prefix_t = 1; |
729 |
break; |
730 |
case 'V': |
731 |
prefix_V = 1; |
732 |
break; |
733 |
case ':': |
734 |
break; |
735 |
default: |
736 |
fprintf(stderr, "diskimage_add(): invalid " |
737 |
"prefix char '%c'\n", c); |
738 |
exit(1); |
739 |
} |
740 |
} |
741 |
} |
742 |
|
743 |
/* Allocate a new diskimage struct: */ |
744 |
d = malloc(sizeof(struct diskimage)); |
745 |
if (d == NULL) { |
746 |
fprintf(stderr, "out of memory in diskimage_add()\n"); |
747 |
exit(1); |
748 |
} |
749 |
memset(d, 0, sizeof(struct diskimage)); |
750 |
|
751 |
/* Default to IDE disks... */ |
752 |
d->type = DISKIMAGE_IDE; |
753 |
|
754 |
/* ... but some machines use SCSI by default: */ |
755 |
if (machine->machine_type == MACHINE_PMAX || |
756 |
machine->machine_type == MACHINE_ARC) |
757 |
d->type = DISKIMAGE_SCSI; |
758 |
|
759 |
if (prefix_i + prefix_f + prefix_s > 1) { |
760 |
fprintf(stderr, "Invalid disk image prefix(es). You can" |
761 |
"only use one of i, f, and s\nfor each disk image.\n"); |
762 |
exit(1); |
763 |
} |
764 |
|
765 |
if (prefix_i) |
766 |
d->type = DISKIMAGE_IDE; |
767 |
if (prefix_f) |
768 |
d->type = DISKIMAGE_FLOPPY; |
769 |
if (prefix_s) |
770 |
d->type = DISKIMAGE_SCSI; |
771 |
|
772 |
/* Special case: Add an overlay for an already added disk image: */ |
773 |
if (prefix_V) { |
774 |
struct diskimage *dx = machine->first_diskimage; |
775 |
|
776 |
if (prefix_id < 0) { |
777 |
fprintf(stderr, "The 'V' disk image prefix requires" |
778 |
" a disk ID to also be supplied.\n"); |
779 |
exit(1); |
780 |
} |
781 |
|
782 |
while (dx != NULL) { |
783 |
if (d->type == dx->type && prefix_id == dx->id) |
784 |
break; |
785 |
dx = dx->next; |
786 |
} |
787 |
|
788 |
if (dx == NULL) { |
789 |
fprintf(stderr, "Bad ID supplied for overlay?\n"); |
790 |
exit(1); |
791 |
} |
792 |
|
793 |
diskimage_add_overlay(dx, fname); |
794 |
|
795 |
/* Free the preliminary d struct: */ |
796 |
free(d); |
797 |
|
798 |
/* Don't add any disk image. This is an overlay! */ |
799 |
return -1; |
800 |
} |
801 |
|
802 |
/* Add the new disk image in the disk image chain: */ |
803 |
d2 = machine->first_diskimage; |
804 |
if (d2 == NULL) { |
805 |
machine->first_diskimage = d; |
806 |
} else { |
807 |
while (d2->next != NULL) |
808 |
d2 = d2->next; |
809 |
d2->next = d; |
810 |
} |
811 |
|
812 |
if (prefix_o) |
813 |
d->override_base_offset = override_base_offset; |
814 |
|
815 |
d->fname = strdup(fname); |
816 |
if (d->fname == NULL) { |
817 |
fprintf(stderr, "out of memory\n"); |
818 |
exit(1); |
819 |
} |
820 |
|
821 |
d->logical_block_size = 512; |
822 |
|
823 |
/* |
824 |
* Is this a tape, CD-ROM or a normal disk? |
825 |
* |
826 |
* An intelligent guess, if no prefixes are used, would be that |
827 |
* filenames ending with .iso or .cdr are CD-ROM images. |
828 |
*/ |
829 |
if (prefix_t) { |
830 |
d->is_a_tape = 1; |
831 |
} else { |
832 |
if (prefix_c || |
833 |
((strlen(d->fname) > 4 && |
834 |
(strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 || |
835 |
strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0)) |
836 |
&& !prefix_d) |
837 |
) { |
838 |
d->is_a_cdrom = 1; |
839 |
|
840 |
/* |
841 |
* This is tricky. Should I use 512 or 2048 here? |
842 |
* NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes |
843 |
* per sector, but NetBSD 2.0_BETA suddenly ignores |
844 |
* this value and uses 2048 instead. |
845 |
* |
846 |
* OpenBSD/arc doesn't like 2048, it requires 512 |
847 |
* to work correctly. |
848 |
* |
849 |
* TODO |
850 |
*/ |
851 |
|
852 |
#if 0 |
853 |
if (machine->machine_type == MACHINE_PMAX) |
854 |
d->logical_block_size = 512; |
855 |
else |
856 |
d->logical_block_size = 2048; |
857 |
#endif |
858 |
d->logical_block_size = 512; |
859 |
} |
860 |
} |
861 |
|
862 |
diskimage_recalc_size(d); |
863 |
|
864 |
if ((d->total_size == 720*1024 || d->total_size == 1474560 |
865 |
|| d->total_size == 2949120 || d->total_size == 1228800) |
866 |
&& !prefix_i && !prefix_s) |
867 |
d->type = DISKIMAGE_FLOPPY; |
868 |
|
869 |
switch (d->type) { |
870 |
case DISKIMAGE_FLOPPY: |
871 |
if (d->total_size < 737280) { |
872 |
fatal("\nTODO: small (non-80-cylinder) floppies?\n\n"); |
873 |
exit(1); |
874 |
} |
875 |
d->cylinders = 80; |
876 |
d->heads = 2; |
877 |
d->sectors_per_track = d->total_size / (d->cylinders * |
878 |
d->heads * 512); |
879 |
break; |
880 |
default:/* Non-floppies: */ |
881 |
d->heads = 16; |
882 |
d->sectors_per_track = 63; |
883 |
if (prefix_g) { |
884 |
d->chs_override = 1; |
885 |
d->heads = override_heads; |
886 |
d->sectors_per_track = override_spt; |
887 |
} |
888 |
bytespercyl = d->heads * d->sectors_per_track * 512; |
889 |
d->cylinders = d->total_size / bytespercyl; |
890 |
if (d->cylinders * bytespercyl < d->total_size) |
891 |
d->cylinders ++; |
892 |
} |
893 |
|
894 |
d->rpms = 3600; |
895 |
|
896 |
if (prefix_b) |
897 |
d->is_boot_device = 1; |
898 |
|
899 |
d->writable = access(fname, W_OK) == 0? 1 : 0; |
900 |
|
901 |
if (d->is_a_cdrom || prefix_r) |
902 |
d->writable = 0; |
903 |
|
904 |
d->f = fopen(fname, d->writable? "r+" : "r"); |
905 |
if (d->f == NULL) { |
906 |
perror(fname); |
907 |
exit(1); |
908 |
} |
909 |
|
910 |
/* Calculate which ID to use: */ |
911 |
if (prefix_id == -1) { |
912 |
int free = 0, collision = 1; |
913 |
|
914 |
while (collision) { |
915 |
collision = 0; |
916 |
d2 = machine->first_diskimage; |
917 |
while (d2 != NULL) { |
918 |
/* (don't compare against ourselves :) */ |
919 |
if (d2 == d) { |
920 |
d2 = d2->next; |
921 |
continue; |
922 |
} |
923 |
if (d2->id == free && d2->type == d->type) { |
924 |
collision = 1; |
925 |
break; |
926 |
} |
927 |
d2 = d2->next; |
928 |
} |
929 |
if (!collision) |
930 |
id = free; |
931 |
else |
932 |
free ++; |
933 |
} |
934 |
} else { |
935 |
id = prefix_id; |
936 |
d2 = machine->first_diskimage; |
937 |
while (d2 != NULL) { |
938 |
/* (don't compare against ourselves :) */ |
939 |
if (d2 == d) { |
940 |
d2 = d2->next; |
941 |
continue; |
942 |
} |
943 |
if (d2->id == id && d2->type == d->type) { |
944 |
fprintf(stderr, "disk image id %i " |
945 |
"already in use\n", id); |
946 |
exit(1); |
947 |
} |
948 |
d2 = d2->next; |
949 |
} |
950 |
} |
951 |
|
952 |
d->id = id; |
953 |
|
954 |
return id; |
955 |
} |
956 |
|
957 |
|
958 |
/* |
959 |
* diskimage_bootdev(): |
960 |
* |
961 |
* Returns the disk id of the device which we're booting from. If typep is |
962 |
* non-NULL, the type is returned as well. |
963 |
* |
964 |
* If no disk was used as boot device, then -1 is returned. (In practice, |
965 |
* this is used to fake network (tftp) boot.) |
966 |
*/ |
967 |
int diskimage_bootdev(struct machine *machine, int *typep) |
968 |
{ |
969 |
struct diskimage *d; |
970 |
|
971 |
d = machine->first_diskimage; |
972 |
while (d != NULL) { |
973 |
if (d->is_boot_device) { |
974 |
if (typep != NULL) |
975 |
*typep = d->type; |
976 |
return d->id; |
977 |
} |
978 |
d = d->next; |
979 |
} |
980 |
|
981 |
d = machine->first_diskimage; |
982 |
if (d != NULL) { |
983 |
if (typep != NULL) |
984 |
*typep = d->type; |
985 |
return d->id; |
986 |
} |
987 |
|
988 |
return -1; |
989 |
} |
990 |
|
991 |
|
992 |
/* |
993 |
* diskimage_getname(): |
994 |
* |
995 |
* Returns 1 if a valid disk image name was returned, 0 otherwise. |
996 |
*/ |
997 |
int diskimage_getname(struct machine *machine, int id, int type, |
998 |
char *buf, size_t bufsize) |
999 |
{ |
1000 |
struct diskimage *d = machine->first_diskimage; |
1001 |
|
1002 |
if (buf == NULL) |
1003 |
return 0; |
1004 |
|
1005 |
while (d != NULL) { |
1006 |
if (d->type == type && d->id == id) { |
1007 |
char *p = strrchr(d->fname, '/'); |
1008 |
if (p == NULL) |
1009 |
p = d->fname; |
1010 |
else |
1011 |
p ++; |
1012 |
snprintf(buf, bufsize, "%s", p); |
1013 |
return 1; |
1014 |
} |
1015 |
d = d->next; |
1016 |
} |
1017 |
return 0; |
1018 |
} |
1019 |
|
1020 |
|
1021 |
/* |
1022 |
* diskimage_is_a_cdrom(): |
1023 |
* |
1024 |
* Returns 1 if a disk image is a CDROM, 0 otherwise. |
1025 |
*/ |
1026 |
int diskimage_is_a_cdrom(struct machine *machine, int id, int type) |
1027 |
{ |
1028 |
struct diskimage *d = machine->first_diskimage; |
1029 |
|
1030 |
while (d != NULL) { |
1031 |
if (d->type == type && d->id == id) |
1032 |
return d->is_a_cdrom; |
1033 |
d = d->next; |
1034 |
} |
1035 |
return 0; |
1036 |
} |
1037 |
|
1038 |
|
1039 |
/* |
1040 |
* diskimage_is_a_tape(): |
1041 |
* |
1042 |
* Returns 1 if a disk image is a tape, 0 otherwise. |
1043 |
* |
1044 |
* (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation |
1045 |
* boot strings.) |
1046 |
*/ |
1047 |
int diskimage_is_a_tape(struct machine *machine, int id, int type) |
1048 |
{ |
1049 |
struct diskimage *d = machine->first_diskimage; |
1050 |
|
1051 |
while (d != NULL) { |
1052 |
if (d->type == type && d->id == id) |
1053 |
return d->is_a_tape; |
1054 |
d = d->next; |
1055 |
} |
1056 |
return 0; |
1057 |
} |
1058 |
|
1059 |
|
1060 |
/* |
1061 |
* diskimage_dump_info(): |
1062 |
* |
1063 |
* Debug dump of all diskimages that are loaded for a specific machine. |
1064 |
*/ |
1065 |
void diskimage_dump_info(struct machine *machine) |
1066 |
{ |
1067 |
int i, iadd = DEBUG_INDENTATION; |
1068 |
struct diskimage *d = machine->first_diskimage; |
1069 |
|
1070 |
while (d != NULL) { |
1071 |
debug("diskimage: %s\n", d->fname); |
1072 |
debug_indentation(iadd); |
1073 |
|
1074 |
switch (d->type) { |
1075 |
case DISKIMAGE_SCSI: |
1076 |
debug("SCSI"); |
1077 |
break; |
1078 |
case DISKIMAGE_IDE: |
1079 |
debug("IDE"); |
1080 |
break; |
1081 |
case DISKIMAGE_FLOPPY: |
1082 |
debug("FLOPPY"); |
1083 |
break; |
1084 |
default: |
1085 |
debug("UNKNOWN type %i", d->type); |
1086 |
} |
1087 |
|
1088 |
debug(" %s", d->is_a_tape? "TAPE" : |
1089 |
(d->is_a_cdrom? "CD-ROM" : "DISK")); |
1090 |
debug(" id %i, ", d->id); |
1091 |
debug("%s, ", d->writable? "read/write" : "read-only"); |
1092 |
|
1093 |
if (d->type == DISKIMAGE_FLOPPY) |
1094 |
debug("%lli KB", (long long) (d->total_size / 1024)); |
1095 |
else |
1096 |
debug("%lli MB", (long long) (d->total_size / 1048576)); |
1097 |
|
1098 |
if (d->type == DISKIMAGE_FLOPPY || d->chs_override) |
1099 |
debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads, |
1100 |
d->sectors_per_track); |
1101 |
else |
1102 |
debug(" (%lli sectors)", (long long) |
1103 |
(d->total_size / 512)); |
1104 |
|
1105 |
if (d->is_boot_device) |
1106 |
debug(" (BOOT)"); |
1107 |
debug("\n"); |
1108 |
|
1109 |
for (i=0; i<d->nr_of_overlays; i++) { |
1110 |
debug("overlay %i: %s\n", |
1111 |
i, d->overlays[i].overlay_basename); |
1112 |
} |
1113 |
|
1114 |
debug_indentation(-iadd); |
1115 |
|
1116 |
d = d->next; |
1117 |
} |
1118 |
} |
1119 |
|