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