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