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.6 2007/04/28 09:19:52 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 < (off_t) (offset+len); |
423 |
curofs += OVERLAY_BLOCK_SIZE) { |
424 |
/* Always write to the last overlay: */ |
425 |
int overlay_nr = d->nr_of_overlays-1; |
426 |
off_t lenwritten; |
427 |
int res = my_fseek(d->overlays[overlay_nr].f_data, |
428 |
curofs, SEEK_SET); |
429 |
if (res != 0) { |
430 |
fatal("[ diskimage__internal_access(): fseek()" |
431 |
" failed on disk id %i \n", d->id); |
432 |
return 0; |
433 |
} |
434 |
|
435 |
lenwritten = fwrite(buf, 1, OVERLAY_BLOCK_SIZE, |
436 |
d->overlays[overlay_nr].f_data); |
437 |
buf += OVERLAY_BLOCK_SIZE; |
438 |
|
439 |
/* Mark this block in the last overlay as in use: */ |
440 |
overlay_set_block_in_use(d, overlay_nr, curofs); |
441 |
} |
442 |
|
443 |
return len; |
444 |
} |
445 |
|
446 |
|
447 |
/* |
448 |
* fread_helper(): |
449 |
* |
450 |
* Internal helper function. Reads from a disk image file, or if the |
451 |
* disk image has overlays, from the last overlay that has the specific |
452 |
* data (or the disk image file itself). |
453 |
*/ |
454 |
static size_t fread_helper(off_t offset, unsigned char *buf, |
455 |
size_t len, struct diskimage *d) |
456 |
{ |
457 |
off_t curofs; |
458 |
size_t totallenread = 0; |
459 |
|
460 |
/* Fast return-path for the case when no overlays are used: */ |
461 |
if (d->nr_of_overlays == 0) { |
462 |
int res = my_fseek(d->f, offset, SEEK_SET); |
463 |
if (res != 0) { |
464 |
fatal("[ diskimage__internal_access(): fseek() failed" |
465 |
" on disk id %i \n", d->id); |
466 |
return 0; |
467 |
} |
468 |
|
469 |
return fread(buf, 1, len, d->f); |
470 |
} |
471 |
|
472 |
/* Split the read into OVERLAY_BLOCK_SIZE reads: */ |
473 |
for (curofs=offset; len != 0; |
474 |
curofs = (curofs | (OVERLAY_BLOCK_SIZE-1)) + 1) { |
475 |
/* Find the overlay, if any, that has this block: */ |
476 |
off_t lenread, lentoread; |
477 |
int overlay_nr; |
478 |
for (overlay_nr = d->nr_of_overlays-1; |
479 |
overlay_nr >= 0; overlay_nr --) { |
480 |
if (overlay_has_block(d, overlay_nr, curofs)) |
481 |
break; |
482 |
} |
483 |
|
484 |
lentoread = len > OVERLAY_BLOCK_SIZE? OVERLAY_BLOCK_SIZE : len; |
485 |
|
486 |
if (overlay_nr >= 0) { |
487 |
/* Read from overlay: */ |
488 |
int res = my_fseek(d->overlays[overlay_nr].f_data, |
489 |
curofs, SEEK_SET); |
490 |
if (res != 0) { |
491 |
fatal("[ diskimage__internal_access(): fseek()" |
492 |
" failed on disk id %i \n", d->id); |
493 |
return 0; |
494 |
} |
495 |
lenread = fread(buf, 1, lentoread, |
496 |
d->overlays[overlay_nr].f_data); |
497 |
} else { |
498 |
/* Read from the base disk image: */ |
499 |
int res = my_fseek(d->f, curofs, SEEK_SET); |
500 |
if (res != 0) { |
501 |
fatal("[ diskimage__internal_access(): fseek()" |
502 |
" failed on disk id %i \n", d->id); |
503 |
return 0; |
504 |
} |
505 |
lenread = fread(buf, 1, lentoread, d->f); |
506 |
} |
507 |
|
508 |
if (lenread != lentoread) { |
509 |
fatal("[ INCOMPLETE READ from disk id %i, offset" |
510 |
" %lli ]\n", d->id, (long long)curofs); |
511 |
} |
512 |
|
513 |
len -= lentoread; |
514 |
totallenread += lenread; |
515 |
buf += OVERLAY_BLOCK_SIZE; |
516 |
} |
517 |
|
518 |
return totallenread; |
519 |
} |
520 |
|
521 |
|
522 |
/* |
523 |
* diskimage__internal_access(): |
524 |
* |
525 |
* Read from or write to a struct diskimage. |
526 |
* |
527 |
* Returns 1 if the access completed successfully, 0 otherwise. |
528 |
*/ |
529 |
int diskimage__internal_access(struct diskimage *d, int writeflag, |
530 |
off_t offset, unsigned char *buf, size_t len) |
531 |
{ |
532 |
ssize_t lendone; |
533 |
|
534 |
if (buf == NULL) { |
535 |
fprintf(stderr, "diskimage__internal_access(): buf = NULL\n"); |
536 |
exit(1); |
537 |
} |
538 |
if (len == 0) |
539 |
return 1; |
540 |
if (d->f == NULL) |
541 |
return 0; |
542 |
|
543 |
if (writeflag) { |
544 |
if (!d->writable) |
545 |
return 0; |
546 |
|
547 |
lendone = fwrite_helper(offset, buf, len, d); |
548 |
} else { |
549 |
/* |
550 |
* Special case for CD-ROMs. Actually, this is not needed |
551 |
* for .iso images, only for physical CDROMS on some OSes, |
552 |
* such as FreeBSD. |
553 |
*/ |
554 |
if (d->is_a_cdrom) |
555 |
lendone = diskimage_access__cdrom(d, offset, buf, len); |
556 |
else |
557 |
lendone = fread_helper(offset, buf, len, d); |
558 |
|
559 |
if (lendone < (ssize_t)len) |
560 |
memset(buf + lendone, 0, len - lendone); |
561 |
} |
562 |
|
563 |
/* Incomplete data transfer? Then return failure: */ |
564 |
if (lendone != (ssize_t)len) { |
565 |
#ifdef UNSTABLE_DEVEL |
566 |
fatal |
567 |
#else |
568 |
debug |
569 |
#endif |
570 |
("[ diskimage__internal_access(): disk_id %i, offset %lli" |
571 |
", transfer not completed. len=%i, len_done=%i ]\n", |
572 |
d->id, (long long)offset, (int)len, (int)lendone); |
573 |
return 0; |
574 |
} |
575 |
|
576 |
return 1; |
577 |
} |
578 |
|
579 |
|
580 |
/* |
581 |
* diskimage_access(): |
582 |
* |
583 |
* Read from or write to a disk image on a machine. |
584 |
* |
585 |
* Returns 1 if the access completed successfully, 0 otherwise. |
586 |
*/ |
587 |
int diskimage_access(struct machine *machine, int id, int type, int writeflag, |
588 |
off_t offset, unsigned char *buf, size_t len) |
589 |
{ |
590 |
struct diskimage *d = machine->first_diskimage; |
591 |
|
592 |
while (d != NULL) { |
593 |
if (d->type == type && d->id == id) |
594 |
break; |
595 |
d = d->next; |
596 |
} |
597 |
|
598 |
if (d == NULL) { |
599 |
fatal("[ diskimage_access(): ERROR: trying to access a " |
600 |
"non-existant %s disk image (id %i)\n", |
601 |
diskimage_types[type], id); |
602 |
return 0; |
603 |
} |
604 |
|
605 |
offset -= d->override_base_offset; |
606 |
if (offset < 0 && offset + d->override_base_offset >= 0) { |
607 |
debug("[ reading before start of disk image ]\n"); |
608 |
/* Returning zeros. */ |
609 |
memset(buf, 0, len); |
610 |
return 1; |
611 |
} |
612 |
|
613 |
return diskimage__internal_access(d, writeflag, offset, buf, len); |
614 |
} |
615 |
|
616 |
|
617 |
/* |
618 |
* diskimage_add(): |
619 |
* |
620 |
* Add a disk image. fname is the filename of the disk image. |
621 |
* The filename may be prefixed with one or more modifiers, followed |
622 |
* by a colon. |
623 |
* |
624 |
* b specifies that this is a bootable device |
625 |
* c CD-ROM (instead of a normal DISK) |
626 |
* d DISK (this is the default) |
627 |
* f FLOPPY (instead of SCSI) |
628 |
* gH;S; set geometry (H=heads, S=sectors per track, cylinders are |
629 |
* automatically calculated). (This is ignored for floppies.) |
630 |
* i IDE (instead of SCSI) |
631 |
* oOFS; set base offset in bytes, when booting from an ISO9660 fs |
632 |
* r read-only (don't allow changes to the file) |
633 |
* s SCSI (this is the default) |
634 |
* t tape |
635 |
* V add an overlay to a disk image |
636 |
* 0-7 force a specific SCSI ID number |
637 |
* |
638 |
* machine is assumed to be non-NULL. |
639 |
* Returns an integer >= 0 identifying the disk image. |
640 |
*/ |
641 |
int diskimage_add(struct machine *machine, char *fname) |
642 |
{ |
643 |
struct diskimage *d, *d2; |
644 |
int id = 0, override_heads=0, override_spt=0; |
645 |
int64_t bytespercyl, override_base_offset=0; |
646 |
char *cp; |
647 |
int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0; |
648 |
int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id=-1; |
649 |
int prefix_o=0, prefix_V=0; |
650 |
|
651 |
if (fname == NULL) { |
652 |
fprintf(stderr, "diskimage_add(): NULL ptr\n"); |
653 |
return 0; |
654 |
} |
655 |
|
656 |
/* Get prefix from fname: */ |
657 |
cp = strchr(fname, ':'); |
658 |
if (cp != NULL) { |
659 |
while (fname <= cp) { |
660 |
char c = *fname++; |
661 |
switch (c) { |
662 |
case '0': |
663 |
case '1': |
664 |
case '2': |
665 |
case '3': |
666 |
case '4': |
667 |
case '5': |
668 |
case '6': |
669 |
case '7': |
670 |
prefix_id = c - '0'; |
671 |
break; |
672 |
case 'b': |
673 |
prefix_b = 1; |
674 |
break; |
675 |
case 'c': |
676 |
prefix_c = 1; |
677 |
break; |
678 |
case 'd': |
679 |
prefix_d = 1; |
680 |
break; |
681 |
case 'f': |
682 |
prefix_f = 1; |
683 |
break; |
684 |
case 'g': |
685 |
prefix_g = 1; |
686 |
override_heads = atoi(fname); |
687 |
while (*fname != '\0' && *fname != ';') |
688 |
fname ++; |
689 |
if (*fname == ';') |
690 |
fname ++; |
691 |
override_spt = atoi(fname); |
692 |
while (*fname != '\0' && *fname != ';' && |
693 |
*fname != ':') |
694 |
fname ++; |
695 |
if (*fname == ';') |
696 |
fname ++; |
697 |
if (override_heads < 1 || |
698 |
override_spt < 1) { |
699 |
fatal("Bad geometry: heads=%i " |
700 |
"spt=%i\n", override_heads, |
701 |
override_spt); |
702 |
exit(1); |
703 |
} |
704 |
break; |
705 |
case 'i': |
706 |
prefix_i = 1; |
707 |
break; |
708 |
case 'o': |
709 |
prefix_o = 1; |
710 |
override_base_offset = atoi(fname); |
711 |
while (*fname != '\0' && *fname != ':' |
712 |
&& *fname != ';') |
713 |
fname ++; |
714 |
if (*fname == ':' || *fname == ';') |
715 |
fname ++; |
716 |
if (override_base_offset < 0) { |
717 |
fatal("Bad base offset: %"PRIi64 |
718 |
"\n", override_base_offset); |
719 |
exit(1); |
720 |
} |
721 |
break; |
722 |
case 'r': |
723 |
prefix_r = 1; |
724 |
break; |
725 |
case 's': |
726 |
prefix_s = 1; |
727 |
break; |
728 |
case 't': |
729 |
prefix_t = 1; |
730 |
break; |
731 |
case 'V': |
732 |
prefix_V = 1; |
733 |
break; |
734 |
case ':': |
735 |
break; |
736 |
default: |
737 |
fprintf(stderr, "diskimage_add(): invalid " |
738 |
"prefix char '%c'\n", c); |
739 |
exit(1); |
740 |
} |
741 |
} |
742 |
} |
743 |
|
744 |
/* Allocate a new diskimage struct: */ |
745 |
d = malloc(sizeof(struct diskimage)); |
746 |
if (d == NULL) { |
747 |
fprintf(stderr, "out of memory in diskimage_add()\n"); |
748 |
exit(1); |
749 |
} |
750 |
memset(d, 0, sizeof(struct diskimage)); |
751 |
|
752 |
/* Default to IDE disks... */ |
753 |
d->type = DISKIMAGE_IDE; |
754 |
|
755 |
/* ... but some machines use SCSI by default: */ |
756 |
if (machine->machine_type == MACHINE_PMAX || |
757 |
machine->machine_type == MACHINE_ARC) |
758 |
d->type = DISKIMAGE_SCSI; |
759 |
|
760 |
if (prefix_i + prefix_f + prefix_s > 1) { |
761 |
fprintf(stderr, "Invalid disk image prefix(es). You can" |
762 |
"only use one of i, f, and s\nfor each disk image.\n"); |
763 |
exit(1); |
764 |
} |
765 |
|
766 |
if (prefix_i) |
767 |
d->type = DISKIMAGE_IDE; |
768 |
if (prefix_f) |
769 |
d->type = DISKIMAGE_FLOPPY; |
770 |
if (prefix_s) |
771 |
d->type = DISKIMAGE_SCSI; |
772 |
|
773 |
/* Special case: Add an overlay for an already added disk image: */ |
774 |
if (prefix_V) { |
775 |
struct diskimage *dx = machine->first_diskimage; |
776 |
|
777 |
if (prefix_id < 0) { |
778 |
fprintf(stderr, "The 'V' disk image prefix requires" |
779 |
" a disk ID to also be supplied.\n"); |
780 |
exit(1); |
781 |
} |
782 |
|
783 |
while (dx != NULL) { |
784 |
if (d->type == dx->type && prefix_id == dx->id) |
785 |
break; |
786 |
dx = dx->next; |
787 |
} |
788 |
|
789 |
if (dx == NULL) { |
790 |
fprintf(stderr, "Bad ID supplied for overlay?\n"); |
791 |
exit(1); |
792 |
} |
793 |
|
794 |
diskimage_add_overlay(dx, fname); |
795 |
|
796 |
/* Free the preliminary d struct: */ |
797 |
free(d); |
798 |
|
799 |
/* Don't add any disk image. This is an overlay! */ |
800 |
return -1; |
801 |
} |
802 |
|
803 |
/* Add the new disk image in the disk image chain: */ |
804 |
d2 = machine->first_diskimage; |
805 |
if (d2 == NULL) { |
806 |
machine->first_diskimage = d; |
807 |
} else { |
808 |
while (d2->next != NULL) |
809 |
d2 = d2->next; |
810 |
d2->next = d; |
811 |
} |
812 |
|
813 |
if (prefix_o) |
814 |
d->override_base_offset = override_base_offset; |
815 |
|
816 |
d->fname = strdup(fname); |
817 |
if (d->fname == NULL) { |
818 |
fprintf(stderr, "out of memory\n"); |
819 |
exit(1); |
820 |
} |
821 |
|
822 |
d->logical_block_size = 512; |
823 |
|
824 |
/* |
825 |
* Is this a tape, CD-ROM or a normal disk? |
826 |
* |
827 |
* An intelligent guess, if no prefixes are used, would be that |
828 |
* filenames ending with .iso or .cdr are CD-ROM images. |
829 |
*/ |
830 |
if (prefix_t) { |
831 |
d->is_a_tape = 1; |
832 |
} else { |
833 |
if (prefix_c || |
834 |
((strlen(d->fname) > 4 && |
835 |
(strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 || |
836 |
strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0)) |
837 |
&& !prefix_d) |
838 |
) { |
839 |
d->is_a_cdrom = 1; |
840 |
|
841 |
/* |
842 |
* This is tricky. Should I use 512 or 2048 here? |
843 |
* NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes |
844 |
* per sector, but NetBSD 2.0_BETA suddenly ignores |
845 |
* this value and uses 2048 instead. |
846 |
* |
847 |
* OpenBSD/arc doesn't like 2048, it requires 512 |
848 |
* to work correctly. |
849 |
* |
850 |
* TODO |
851 |
*/ |
852 |
|
853 |
#if 0 |
854 |
if (machine->machine_type == MACHINE_PMAX) |
855 |
d->logical_block_size = 512; |
856 |
else |
857 |
d->logical_block_size = 2048; |
858 |
#endif |
859 |
d->logical_block_size = 512; |
860 |
} |
861 |
} |
862 |
|
863 |
diskimage_recalc_size(d); |
864 |
|
865 |
if ((d->total_size == 720*1024 || d->total_size == 1474560 |
866 |
|| d->total_size == 2949120 || d->total_size == 1228800) |
867 |
&& !prefix_i && !prefix_s) |
868 |
d->type = DISKIMAGE_FLOPPY; |
869 |
|
870 |
switch (d->type) { |
871 |
case DISKIMAGE_FLOPPY: |
872 |
if (d->total_size < 737280) { |
873 |
fatal("\nTODO: small (non-80-cylinder) floppies?\n\n"); |
874 |
exit(1); |
875 |
} |
876 |
d->cylinders = 80; |
877 |
d->heads = 2; |
878 |
d->sectors_per_track = d->total_size / (d->cylinders * |
879 |
d->heads * 512); |
880 |
break; |
881 |
default:/* Non-floppies: */ |
882 |
d->heads = 16; |
883 |
d->sectors_per_track = 63; |
884 |
if (prefix_g) { |
885 |
d->chs_override = 1; |
886 |
d->heads = override_heads; |
887 |
d->sectors_per_track = override_spt; |
888 |
} |
889 |
bytespercyl = d->heads * d->sectors_per_track * 512; |
890 |
d->cylinders = d->total_size / bytespercyl; |
891 |
if (d->cylinders * bytespercyl < d->total_size) |
892 |
d->cylinders ++; |
893 |
} |
894 |
|
895 |
d->rpms = 3600; |
896 |
|
897 |
if (prefix_b) |
898 |
d->is_boot_device = 1; |
899 |
|
900 |
d->writable = access(fname, W_OK) == 0? 1 : 0; |
901 |
|
902 |
if (d->is_a_cdrom || prefix_r) |
903 |
d->writable = 0; |
904 |
|
905 |
d->f = fopen(fname, d->writable? "r+" : "r"); |
906 |
if (d->f == NULL) { |
907 |
perror(fname); |
908 |
exit(1); |
909 |
} |
910 |
|
911 |
/* Calculate which ID to use: */ |
912 |
if (prefix_id == -1) { |
913 |
int free = 0, collision = 1; |
914 |
|
915 |
while (collision) { |
916 |
collision = 0; |
917 |
d2 = machine->first_diskimage; |
918 |
while (d2 != NULL) { |
919 |
/* (don't compare against ourselves :) */ |
920 |
if (d2 == d) { |
921 |
d2 = d2->next; |
922 |
continue; |
923 |
} |
924 |
if (d2->id == free && d2->type == d->type) { |
925 |
collision = 1; |
926 |
break; |
927 |
} |
928 |
d2 = d2->next; |
929 |
} |
930 |
if (!collision) |
931 |
id = free; |
932 |
else |
933 |
free ++; |
934 |
} |
935 |
} else { |
936 |
id = prefix_id; |
937 |
d2 = machine->first_diskimage; |
938 |
while (d2 != NULL) { |
939 |
/* (don't compare against ourselves :) */ |
940 |
if (d2 == d) { |
941 |
d2 = d2->next; |
942 |
continue; |
943 |
} |
944 |
if (d2->id == id && d2->type == d->type) { |
945 |
fprintf(stderr, "disk image id %i " |
946 |
"already in use\n", id); |
947 |
exit(1); |
948 |
} |
949 |
d2 = d2->next; |
950 |
} |
951 |
} |
952 |
|
953 |
d->id = id; |
954 |
|
955 |
return id; |
956 |
} |
957 |
|
958 |
|
959 |
/* |
960 |
* diskimage_bootdev(): |
961 |
* |
962 |
* Returns the disk id of the device which we're booting from. If typep is |
963 |
* non-NULL, the type is returned as well. |
964 |
* |
965 |
* If no disk was used as boot device, then -1 is returned. (In practice, |
966 |
* this is used to fake network (tftp) boot.) |
967 |
*/ |
968 |
int diskimage_bootdev(struct machine *machine, int *typep) |
969 |
{ |
970 |
struct diskimage *d; |
971 |
|
972 |
d = machine->first_diskimage; |
973 |
while (d != NULL) { |
974 |
if (d->is_boot_device) { |
975 |
if (typep != NULL) |
976 |
*typep = d->type; |
977 |
return d->id; |
978 |
} |
979 |
d = d->next; |
980 |
} |
981 |
|
982 |
d = machine->first_diskimage; |
983 |
if (d != NULL) { |
984 |
if (typep != NULL) |
985 |
*typep = d->type; |
986 |
return d->id; |
987 |
} |
988 |
|
989 |
return -1; |
990 |
} |
991 |
|
992 |
|
993 |
/* |
994 |
* diskimage_getname(): |
995 |
* |
996 |
* Returns 1 if a valid disk image name was returned, 0 otherwise. |
997 |
*/ |
998 |
int diskimage_getname(struct machine *machine, int id, int type, |
999 |
char *buf, size_t bufsize) |
1000 |
{ |
1001 |
struct diskimage *d = machine->first_diskimage; |
1002 |
|
1003 |
if (buf == NULL) |
1004 |
return 0; |
1005 |
|
1006 |
while (d != NULL) { |
1007 |
if (d->type == type && d->id == id) { |
1008 |
char *p = strrchr(d->fname, '/'); |
1009 |
if (p == NULL) |
1010 |
p = d->fname; |
1011 |
else |
1012 |
p ++; |
1013 |
snprintf(buf, bufsize, "%s", p); |
1014 |
return 1; |
1015 |
} |
1016 |
d = d->next; |
1017 |
} |
1018 |
return 0; |
1019 |
} |
1020 |
|
1021 |
|
1022 |
/* |
1023 |
* diskimage_is_a_cdrom(): |
1024 |
* |
1025 |
* Returns 1 if a disk image is a CDROM, 0 otherwise. |
1026 |
*/ |
1027 |
int diskimage_is_a_cdrom(struct machine *machine, int id, int type) |
1028 |
{ |
1029 |
struct diskimage *d = machine->first_diskimage; |
1030 |
|
1031 |
while (d != NULL) { |
1032 |
if (d->type == type && d->id == id) |
1033 |
return d->is_a_cdrom; |
1034 |
d = d->next; |
1035 |
} |
1036 |
return 0; |
1037 |
} |
1038 |
|
1039 |
|
1040 |
/* |
1041 |
* diskimage_is_a_tape(): |
1042 |
* |
1043 |
* Returns 1 if a disk image is a tape, 0 otherwise. |
1044 |
* |
1045 |
* (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation |
1046 |
* boot strings.) |
1047 |
*/ |
1048 |
int diskimage_is_a_tape(struct machine *machine, int id, int type) |
1049 |
{ |
1050 |
struct diskimage *d = machine->first_diskimage; |
1051 |
|
1052 |
while (d != NULL) { |
1053 |
if (d->type == type && d->id == id) |
1054 |
return d->is_a_tape; |
1055 |
d = d->next; |
1056 |
} |
1057 |
return 0; |
1058 |
} |
1059 |
|
1060 |
|
1061 |
/* |
1062 |
* diskimage_dump_info(): |
1063 |
* |
1064 |
* Debug dump of all diskimages that are loaded for a specific machine. |
1065 |
*/ |
1066 |
void diskimage_dump_info(struct machine *machine) |
1067 |
{ |
1068 |
int i, iadd = DEBUG_INDENTATION; |
1069 |
struct diskimage *d = machine->first_diskimage; |
1070 |
|
1071 |
while (d != NULL) { |
1072 |
debug("diskimage: %s\n", d->fname); |
1073 |
debug_indentation(iadd); |
1074 |
|
1075 |
switch (d->type) { |
1076 |
case DISKIMAGE_SCSI: |
1077 |
debug("SCSI"); |
1078 |
break; |
1079 |
case DISKIMAGE_IDE: |
1080 |
debug("IDE"); |
1081 |
break; |
1082 |
case DISKIMAGE_FLOPPY: |
1083 |
debug("FLOPPY"); |
1084 |
break; |
1085 |
default: |
1086 |
debug("UNKNOWN type %i", d->type); |
1087 |
} |
1088 |
|
1089 |
debug(" %s", d->is_a_tape? "TAPE" : |
1090 |
(d->is_a_cdrom? "CD-ROM" : "DISK")); |
1091 |
debug(" id %i, ", d->id); |
1092 |
debug("%s, ", d->writable? "read/write" : "read-only"); |
1093 |
|
1094 |
if (d->type == DISKIMAGE_FLOPPY) |
1095 |
debug("%lli KB", (long long) (d->total_size / 1024)); |
1096 |
else |
1097 |
debug("%lli MB", (long long) (d->total_size / 1048576)); |
1098 |
|
1099 |
if (d->type == DISKIMAGE_FLOPPY || d->chs_override) |
1100 |
debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads, |
1101 |
d->sectors_per_track); |
1102 |
else |
1103 |
debug(" (%lli sectors)", (long long) |
1104 |
(d->total_size / 512)); |
1105 |
|
1106 |
if (d->is_boot_device) |
1107 |
debug(" (BOOT)"); |
1108 |
debug("\n"); |
1109 |
|
1110 |
for (i=0; i<d->nr_of_overlays; i++) { |
1111 |
debug("overlay %i: %s\n", |
1112 |
i, d->overlays[i].overlay_basename); |
1113 |
} |
1114 |
|
1115 |
debug_indentation(-iadd); |
1116 |
|
1117 |
d = d->next; |
1118 |
} |
1119 |
} |
1120 |
|