/[gxemul]/upstream/0.4.1/src/diskimage.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /upstream/0.4.1/src/diskimage.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 29 - (show annotations)
Mon Oct 8 16:20:32 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 45808 byte(s)
0.4.1
1 /*
2 * Copyright (C) 2003-2006 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.110 2006/06/24 19:52:27 debug Exp $
29 *
30 * Disk image support.
31 *
32 * TODO: There's probably a bug in the tape support:
33 * Let's say there are 10240 bytes left in a file, and 10240
34 * bytes are read. Then feof() is not true yet (?), so the next
35 * read will also return 10240 bytes (but all zeroes), and then after
36 * that return feof (which results in a filemark). This is probably
37 * trivial to fix, but I don't feel like it right now.
38 *
39 * TODO: diskimage_remove()? This would be useful for floppies in PC-style
40 * machines, where disks may need to be swapped during boot etc.
41 */
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49
50 #include "cpu.h"
51 #include "diskimage.h"
52 #include "machine.h"
53 #include "misc.h"
54
55
56 /* #define debug fatal */
57
58 extern int single_step;
59
60 static char *diskimage_types[] = DISKIMAGE_TYPES;
61
62 static struct scsi_transfer *first_free_scsi_transfer_alloc = NULL;
63
64
65 /**************************************************************************/
66
67 /*
68 * my_fseek():
69 *
70 * A helper function, like fseek() but takes off_t. If the system has
71 * fseeko, then that is used. Otherwise I try to fake off_t offsets here.
72 *
73 * The correct position is reached by seeking 2 billion bytes at a time
74 * (or less). Note: This method is only used for SEEK_SET, for SEEK_CUR
75 * and SEEK_END, normal fseek() is used!
76 *
77 * TODO: It seemed to work on Linux/i386, but not on Solaris/sparc (?).
78 * Anyway, most modern systems have fseeko(), so it shouldn't be a problem.
79 */
80 static int my_fseek(FILE *f, off_t offset, int whence)
81 {
82 #ifdef HACK_FSEEKO
83 if (whence == SEEK_SET) {
84 int res = 0;
85 off_t curoff = 0;
86 off_t cur_step;
87
88 fseek(f, 0, SEEK_SET);
89 while (curoff < offset) {
90 /* How far to seek? */
91 cur_step = offset - curoff;
92 if (cur_step > 2000000000)
93 cur_step = 2000000000;
94 res = fseek(f, cur_step, SEEK_CUR);
95 if (res)
96 return res;
97 curoff += cur_step;
98 }
99 return 0;
100 } else
101 return fseek(f, offset, whence);
102 #else
103 return fseeko(f, offset, whence);
104 #endif
105 }
106
107
108 /**************************************************************************/
109
110
111 /*
112 * scsi_transfer_alloc():
113 *
114 * Allocates memory for a new scsi_transfer struct, and fills it with
115 * sane data (NULL pointers).
116 * The return value is a pointer to the new struct. If allocation
117 * failed, the program exits.
118 */
119 struct scsi_transfer *scsi_transfer_alloc(void)
120 {
121 struct scsi_transfer *p;
122
123 if (first_free_scsi_transfer_alloc != NULL) {
124 p = first_free_scsi_transfer_alloc;
125 first_free_scsi_transfer_alloc = p->next_free;
126 } else {
127 p = malloc(sizeof(struct scsi_transfer));
128 if (p == NULL) {
129 fprintf(stderr, "scsi_transfer_alloc(): out "
130 "of memory\n");
131 exit(1);
132 }
133 }
134
135 memset(p, 0, sizeof(struct scsi_transfer));
136
137 return p;
138 }
139
140
141 /*
142 * scsi_transfer_free():
143 *
144 * Frees the space used by a scsi_transfer struct. All buffers refered
145 * to by the scsi_transfer struct are freed.
146 */
147 void scsi_transfer_free(struct scsi_transfer *p)
148 {
149 if (p == NULL) {
150 fprintf(stderr, "scsi_transfer_free(): p == NULL\n");
151 exit(1);
152 }
153
154 if (p->msg_out != NULL)
155 free(p->msg_out);
156 if (p->cmd != NULL)
157 free(p->cmd);
158 if (p->data_out != NULL)
159 free(p->data_out);
160
161 if (p->data_in != NULL)
162 free(p->data_in);
163 if (p->msg_in != NULL)
164 free(p->msg_in);
165 if (p->status != NULL)
166 free(p->status);
167
168 p->next_free = first_free_scsi_transfer_alloc;
169 first_free_scsi_transfer_alloc = p;
170 }
171
172
173 /*
174 * scsi_transfer_allocbuf():
175 *
176 * Helper function, used by diskimage_scsicommand(), and SCSI controller
177 * devices. Example of usage:
178 *
179 * scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1);
180 */
181 void scsi_transfer_allocbuf(size_t *lenp, unsigned char **pp, size_t want_len,
182 int clearflag)
183 {
184 unsigned char *p = (*pp);
185
186 if (p != NULL) {
187 printf("WARNING! scsi_transfer_allocbuf(): old pointer "
188 "was not NULL, freeing it now\n");
189 free(p);
190 }
191
192 (*lenp) = want_len;
193 if ((p = malloc(want_len)) == NULL) {
194 fprintf(stderr, "scsi_transfer_allocbuf(): out of "
195 "memory trying to allocate %li bytes\n", (long)want_len);
196 exit(1);
197 }
198
199 if (clearflag)
200 memset(p, 0, want_len);
201
202 (*pp) = p;
203 }
204
205
206 /**************************************************************************/
207
208
209 /*
210 * diskimage_exist():
211 *
212 * Returns 1 if the specified disk id (for a specific type) exists, 0
213 * otherwise.
214 */
215 int diskimage_exist(struct machine *machine, int id, int type)
216 {
217 struct diskimage *d = machine->first_diskimage;
218
219 while (d != NULL) {
220 if (d->type == type && d->id == id)
221 return 1;
222 d = d->next;
223 }
224 return 0;
225 }
226
227
228 /*
229 * diskimage_recalc_size():
230 *
231 * Recalculate a disk's size by stat()-ing it.
232 * d is assumed to be non-NULL.
233 */
234 static void diskimage_recalc_size(struct diskimage *d)
235 {
236 struct stat st;
237 int res;
238 off_t size = 0;
239
240 res = stat(d->fname, &st);
241 if (res) {
242 fprintf(stderr, "[ diskimage_recalc_size(): could not stat "
243 "'%s' ]\n", d->fname);
244 return;
245 }
246
247 size = st.st_size;
248
249 /*
250 * TODO: CD-ROM devices, such as /dev/cd0c, how can one
251 * check how much data is on that cd-rom without reading it?
252 * For now, assume some large number, hopefully it will be
253 * enough to hold any cd-rom image.
254 */
255 if (d->is_a_cdrom && size == 0)
256 size = 762048000;
257
258 d->total_size = size;
259 d->ncyls = d->total_size / 1048576;
260
261 /* TODO: There is a mismatch between d->ncyls and d->cylinders,
262 SCSI-based stuff usually doesn't care. TODO: Fix this. */
263 }
264
265
266 /*
267 * diskimage_getsize():
268 *
269 * Returns -1 if the specified disk id/type does not exists, otherwise
270 * the size of the disk image is returned.
271 */
272 int64_t diskimage_getsize(struct machine *machine, int id, int type)
273 {
274 struct diskimage *d = machine->first_diskimage;
275
276 while (d != NULL) {
277 if (d->type == type && d->id == id)
278 return d->total_size;
279 d = d->next;
280 }
281 return -1;
282 }
283
284
285 /*
286 * diskimage_getchs():
287 *
288 * Returns the current CHS values of a disk image.
289 */
290 void diskimage_getchs(struct machine *machine, int id, int type,
291 int *c, int *h, int *s)
292 {
293 struct diskimage *d = machine->first_diskimage;
294
295 while (d != NULL) {
296 if (d->type == type && d->id == id) {
297 *c = d->cylinders;
298 *h = d->heads;
299 *s = d->sectors_per_track;
300 return;
301 }
302 d = d->next;
303 }
304 fatal("diskimage_getchs(): disk id %i (type %i) not found?\n",
305 id, diskimage_types[type]);
306 exit(1);
307 }
308
309
310 /*
311 * diskimage__return_default_status_and_message():
312 *
313 * Set the status and msg_in parts of a scsi_transfer struct
314 * to default values (msg_in = 0x00, status = 0x00).
315 */
316 static void diskimage__return_default_status_and_message(
317 struct scsi_transfer *xferp)
318 {
319 scsi_transfer_allocbuf(&xferp->status_len, &xferp->status, 1, 0);
320 xferp->status[0] = 0x00;
321 scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1, 0);
322 xferp->msg_in[0] = 0x00;
323 }
324
325
326 /*
327 * diskimage__switch_tape():
328 *
329 * Used by the SPACE command. (d is assumed to be non-NULL.)
330 */
331 static void diskimage__switch_tape(struct diskimage *d)
332 {
333 char tmpfname[1000];
334
335 snprintf(tmpfname, sizeof(tmpfname), "%s.%i",
336 d->fname, d->tape_filenr);
337 tmpfname[sizeof(tmpfname)-1] = '\0';
338
339 if (d->f != NULL)
340 fclose(d->f);
341
342 d->f = fopen(tmpfname, d->writable? "r+" : "r");
343 if (d->f == NULL) {
344 fprintf(stderr, "[ diskimage__switch_tape(): could not "
345 "(re)open '%s' ]\n", tmpfname);
346 /* TODO: return error */
347 }
348 d->tape_offset = 0;
349 }
350
351
352 /*
353 * diskimage_access__cdrom():
354 *
355 * This is a special-case function, called from diskimage__internal_access().
356 * On my FreeBSD 4.9 system, the cdrom device /dev/cd0c seems to not be able
357 * to handle something like "fseek(512); fread(512);" but it handles
358 * "fseek(2048); fread(512);" just fine. So, if diskimage__internal_access()
359 * fails in reading a block of data, this function is called as an attempt to
360 * align reads at 2048-byte sectors instead.
361 *
362 * (Ugly hack. TODO: how to solve this cleanly?)
363 *
364 * NOTE: Returns the number of bytes read, 0 if nothing was successfully
365 * read. (These are not the same as diskimage_access()).
366 */
367 #define CDROM_SECTOR_SIZE 2048
368 static size_t diskimage_access__cdrom(struct diskimage *d, off_t offset,
369 unsigned char *buf, size_t len)
370 {
371 off_t aligned_offset;
372 size_t bytes_read, total_copied = 0;
373 unsigned char cdrom_buf[CDROM_SECTOR_SIZE];
374 off_t buf_ofs, i = 0;
375
376 /* printf("diskimage_access__cdrom(): offset=0x%llx size=%lli\n",
377 (long long)offset, (long long)len); */
378
379 aligned_offset = (offset / CDROM_SECTOR_SIZE) * CDROM_SECTOR_SIZE;
380 my_fseek(d->f, aligned_offset, SEEK_SET);
381
382 while (len != 0) {
383 bytes_read = fread(cdrom_buf, 1, CDROM_SECTOR_SIZE, d->f);
384 if (bytes_read != CDROM_SECTOR_SIZE)
385 return 0;
386
387 /* Copy (part of) cdrom_buf into buf: */
388 buf_ofs = offset - aligned_offset;
389 while (buf_ofs < CDROM_SECTOR_SIZE && len != 0) {
390 buf[i ++] = cdrom_buf[buf_ofs ++];
391 total_copied ++;
392 len --;
393 }
394
395 aligned_offset += CDROM_SECTOR_SIZE;
396 offset = aligned_offset;
397 }
398
399 return total_copied;
400 }
401
402
403 /*
404 * diskimage__internal_access():
405 *
406 * Read from or write to a struct diskimage.
407 *
408 * Returns 1 if the access completed successfully, 0 otherwise.
409 */
410 static int diskimage__internal_access(struct diskimage *d, int writeflag,
411 off_t offset, unsigned char *buf, size_t len)
412 {
413 ssize_t lendone;
414 int res;
415
416 if (buf == NULL) {
417 fprintf(stderr, "diskimage__internal_access(): buf = NULL\n");
418 exit(1);
419 }
420 if (len == 0)
421 return 1;
422 if (d->f == NULL)
423 return 0;
424
425 res = my_fseek(d->f, offset, SEEK_SET);
426 if (res != 0) {
427 fatal("[ diskimage__internal_access(): fseek() failed on "
428 "disk id %i \n", d->id);
429 return 0;
430 }
431
432 if (writeflag) {
433 if (!d->writable)
434 return 0;
435
436 lendone = fwrite(buf, 1, len, d->f);
437 } else {
438 /*
439 * Special case for CD-ROMs. Actually, this is not needed
440 * for .iso images, only for physical CDROMS on some OSes,
441 * such as FreeBSD.
442 */
443 if (d->is_a_cdrom)
444 lendone = diskimage_access__cdrom(d, offset, buf, len);
445 else
446 lendone = fread(buf, 1, len, d->f);
447
448 if (lendone < (ssize_t)len)
449 memset(buf + lendone, 0, len - lendone);
450 }
451
452 /* Warn about non-complete data transfers: */
453 if (lendone != (ssize_t)len) {
454 fatal("[ diskimage__internal_access(): disk_id %i, offset %lli"
455 ", transfer not completed. len=%i, len_done=%i ]\n",
456 d->id, (long long)offset, (int)len, (int)lendone);
457 return 0;
458 }
459
460 return 1;
461 }
462
463
464 /*
465 * diskimage_scsicommand():
466 *
467 * Perform a SCSI command on a disk image.
468 *
469 * The xferp points to a scsi_transfer struct, containing msg_out, command,
470 * and data_out coming from the SCSI controller device. This function
471 * interprets the command, and (if necessary) creates responses in
472 * data_in, msg_in, and status.
473 *
474 * Returns:
475 * 2 if the command expects data from the DATA_OUT phase,
476 * 1 if otherwise ok,
477 * 0 on error.
478 */
479 int diskimage_scsicommand(struct cpu *cpu, int id, int type,
480 struct scsi_transfer *xferp)
481 {
482 char namebuf[16];
483 int retlen, i, q;
484 uint64_t size;
485 int64_t ofs;
486 int pagecode;
487 struct machine *machine = cpu->machine;
488 struct diskimage *d;
489
490 if (machine == NULL) {
491 fatal("[ diskimage_scsicommand(): machine == NULL ]\n");
492 return 0;
493 }
494
495 d = machine->first_diskimage;
496 while (d != NULL) {
497 if (d->type == type && d->id == id)
498 break;
499 d = d->next;
500 }
501 if (d == NULL) {
502 fprintf(stderr, "[ diskimage_scsicommand(): %s "
503 " id %i not connected? ]\n", diskimage_types[type], id);
504 }
505
506 if (xferp->cmd == NULL) {
507 fatal("[ diskimage_scsicommand(): cmd == NULL ]\n");
508 return 0;
509 }
510
511 if (xferp->cmd_len < 1) {
512 fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n",
513 xferp->cmd_len);
514 return 0;
515 }
516
517 debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ",
518 id, xferp->cmd[0]);
519
520 #if 0
521 fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",
522 id, xferp->cmd[0], xferp->cmd_len);
523 for (i=0; i<xferp->cmd_len; i++)
524 fatal(" %02x", xferp->cmd[i]);
525 fatal("\n");
526 if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11)
527 single_step = ENTER_SINGLE_STEPPING;
528 #endif
529
530 #if 0
531 {
532 static FILE *f = NULL;
533 if (f == NULL)
534 f = fopen("scsi_log.txt", "w");
535 if (f != NULL) {
536 int i;
537 fprintf(f, "id=%i cmd =", id);
538 for (i=0; i<xferp->cmd_len; i++)
539 fprintf(f, " %02x", xferp->cmd[i]);
540 fprintf(f, "\n");
541 fflush(f);
542 }
543 }
544 #endif
545
546 switch (xferp->cmd[0]) {
547
548 case SCSICMD_TEST_UNIT_READY:
549 debug("TEST_UNIT_READY");
550 if (xferp->cmd_len != 6)
551 debug(" (weird len=%i)", xferp->cmd_len);
552
553 /* TODO: bits 765 of buf[1] contains the LUN */
554 if (xferp->cmd[1] != 0x00)
555 fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x"
556 " not yet implemented\n", (int)xferp->cmd[1]);
557
558 diskimage__return_default_status_and_message(xferp);
559 break;
560
561 case SCSICMD_INQUIRY:
562 debug("INQUIRY");
563 if (xferp->cmd_len != 6)
564 debug(" (weird len=%i)", xferp->cmd_len);
565 if (xferp->cmd[1] != 0x00) {
566 debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet "
567 "implemented\n", (int)xferp->cmd[1]);
568
569 break;
570 }
571
572 /* Return values: */
573 retlen = xferp->cmd[4];
574 if (retlen < 36) {
575 fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen);
576 retlen = 36;
577 }
578
579 /* Return data: */
580 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
581 retlen, 1);
582 xferp->data_in[0] = 0x00; /* 0x00 = Direct-access disk */
583 xferp->data_in[1] = 0x00; /* 0x00 = non-removable */
584 xferp->data_in[2] = 0x02; /* SCSI-2 */
585 #if 0
586 xferp->data_in[3] = 0x02; /* Response data format = SCSI-2 */
587 #endif
588 xferp->data_in[4] = retlen - 4; /* Additional length */
589 xferp->data_in[4] = 0x2c - 4; /* Additional length */
590 xferp->data_in[6] = 0x04; /* ACKREQQ */
591 xferp->data_in[7] = 0x60; /* WBus32, WBus16 */
592
593 /* These are padded with spaces: */
594
595 memcpy(xferp->data_in+8, "GXemul ", 8);
596 if (diskimage_getname(cpu->machine, id,
597 type, namebuf, sizeof(namebuf))) {
598 size_t i;
599 for (i=0; i<sizeof(namebuf); i++)
600 if (namebuf[i] == 0) {
601 for (; i<sizeof(namebuf); i++)
602 namebuf[i] = ' ';
603 break;
604 }
605 memcpy(xferp->data_in+16, namebuf, 16);
606 } else
607 memcpy(xferp->data_in+16, "DISK ", 16);
608 memcpy(xferp->data_in+32, "0 ", 4);
609
610 /*
611 * Some Ultrix kernels want specific responses from
612 * the drives.
613 */
614
615 if (machine->machine_type == MACHINE_PMAX) {
616 /* DEC, RZ25 (rev 0900) = 832527 sectors */
617 /* DEC, RZ58 (rev 2000) = 2698061 sectors */
618 memcpy(xferp->data_in+8, "DEC ", 8);
619 memcpy(xferp->data_in+16, "RZ58 (C) DEC", 16);
620 memcpy(xferp->data_in+32, "2000", 4);
621 }
622
623 /* Some data is different for CD-ROM drives: */
624 if (d->is_a_cdrom) {
625 xferp->data_in[0] = 0x05; /* 0x05 = CD-ROM */
626 xferp->data_in[1] = 0x80; /* 0x80 = removable */
627 /* memcpy(xferp->data_in+16, "CD-ROM ", 16);*/
628
629 if (machine->machine_type == MACHINE_PMAX) {
630 /* SONY, CD-ROM: */
631 memcpy(xferp->data_in+8, "SONY ", 8);
632 memcpy(xferp->data_in+16,
633 "CD-ROM ", 16);
634
635 /* ... or perhaps this: */
636 memcpy(xferp->data_in+8, "DEC ", 8);
637 memcpy(xferp->data_in+16,
638 "RRD42 (C) DEC ", 16);
639 memcpy(xferp->data_in+32, "4.5d", 4);
640 } else if (machine->machine_type == MACHINE_ARC) {
641 /* NEC, CD-ROM: */
642 memcpy(xferp->data_in+8, "NEC ", 8);
643 memcpy(xferp->data_in+16,
644 "CD-ROM CDR-210P ", 16);
645 memcpy(xferp->data_in+32, "1.0 ", 4);
646 }
647 }
648
649 /* Data for tape devices: */
650 if (d->is_a_tape) {
651 xferp->data_in[0] = 0x01; /* 0x01 = tape */
652 xferp->data_in[1] = 0x80; /* 0x80 = removable */
653 memcpy(xferp->data_in+16, "TAPE ", 16);
654
655 if (machine->machine_type == MACHINE_PMAX) {
656 /*
657 * TODO: find out if these are correct.
658 *
659 * The name might be TZK10, TSZ07, or TLZ04,
660 * or something completely different.
661 */
662 memcpy(xferp->data_in+8, "DEC ", 8);
663 memcpy(xferp->data_in+16,
664 "TK50 (C) DEC", 16);
665 memcpy(xferp->data_in+32, "2000", 4);
666 }
667 }
668
669 diskimage__return_default_status_and_message(xferp);
670 break;
671
672 case SCSIBLOCKCMD_READ_CAPACITY:
673 debug("READ_CAPACITY");
674
675 if (xferp->cmd_len != 10)
676 fatal(" [ weird READ_CAPACITY len=%i, should be 10 ] ",
677 xferp->cmd_len);
678 else {
679 if (xferp->cmd[8] & 1) {
680 /* Partial Medium Indicator bit... TODO */
681 fatal("WARNING: READ_CAPACITY with PMI bit"
682 " set not yet implemented\n");
683 }
684 }
685
686 /* Return data: */
687 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
688 8, 1);
689
690 diskimage_recalc_size(d);
691
692 size = d->total_size / d->logical_block_size;
693 if (d->total_size & (d->logical_block_size-1))
694 size ++;
695
696 xferp->data_in[0] = (size >> 24) & 255;
697 xferp->data_in[1] = (size >> 16) & 255;
698 xferp->data_in[2] = (size >> 8) & 255;
699 xferp->data_in[3] = size & 255;
700
701 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
702 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
703 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
704 xferp->data_in[7] = d->logical_block_size & 255;
705
706 diskimage__return_default_status_and_message(xferp);
707 break;
708
709 case SCSICMD_MODE_SENSE:
710 case SCSICMD_MODE_SENSE10:
711 debug("MODE_SENSE");
712 q = 4; retlen = xferp->cmd[4];
713 switch (xferp->cmd_len) {
714 case 6: break;
715 case 10:q = 8;
716 retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
717 break;
718 default:fatal(" (unimplemented mode_sense len=%i)",
719 xferp->cmd_len);
720 }
721
722 /*
723 * NOTE/TODO: This code doesn't handle too short retlens
724 * very well. A quick hack around this is that I allocate
725 * a bit too much memory, so that nothing is actually
726 * written outside of xferp->data_in[].
727 */
728
729 retlen += 100; /* Should be enough. (Ugly.) */
730
731 if ((xferp->cmd[2] & 0xc0) != 0)
732 fatal("WARNING: mode sense, cmd[2] = 0x%02x\n",
733 xferp->cmd[2]);
734
735 /* Return data: */
736 scsi_transfer_allocbuf(&xferp->data_in_len,
737 &xferp->data_in, retlen, 1);
738
739 xferp->data_in_len -= 100; /* Restore size. */
740
741 pagecode = xferp->cmd[2] & 0x3f;
742
743 debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode);
744
745 /* 4 bytes of header for 6-byte command,
746 8 bytes of header for 10-byte command. */
747 xferp->data_in[0] = retlen; /* 0: mode data length */
748 xferp->data_in[1] = d->is_a_cdrom? 0x05 : 0x00;
749 /* 1: medium type */
750 xferp->data_in[2] = 0x00; /* device specific
751 parameter */
752 xferp->data_in[3] = 8 * 1; /* block descriptor
753 length: 1 page (?) */
754
755 xferp->data_in[q+0] = 0x00; /* density code */
756 xferp->data_in[q+1] = 0; /* nr of blocks, high */
757 xferp->data_in[q+2] = 0; /* nr of blocks, mid */
758 xferp->data_in[q+3] = 0; /* nr of blocks, low */
759 xferp->data_in[q+4] = 0x00; /* reserved */
760 xferp->data_in[q+5] = (d->logical_block_size >> 16) & 255;
761 xferp->data_in[q+6] = (d->logical_block_size >> 8) & 255;
762 xferp->data_in[q+7] = d->logical_block_size & 255;
763 q += 8;
764
765 diskimage__return_default_status_and_message(xferp);
766
767 /* descriptors, 8 bytes (each) */
768
769 /* page, n bytes (each) */
770 switch (pagecode) {
771 case 0:
772 /* TODO: Nothing here? */
773 break;
774 case 1: /* read-write error recovery page */
775 xferp->data_in[q + 0] = pagecode;
776 xferp->data_in[q + 1] = 10;
777 break;
778 case 3: /* format device page */
779 xferp->data_in[q + 0] = pagecode;
780 xferp->data_in[q + 1] = 22;
781
782 /* 10,11 = sectors per track */
783 xferp->data_in[q + 10] = 0;
784 xferp->data_in[q + 11] = d->sectors_per_track;
785
786 /* 12,13 = physical sector size */
787 xferp->data_in[q + 12] =
788 (d->logical_block_size >> 8) & 255;
789 xferp->data_in[q + 13] = d->logical_block_size & 255;
790 break;
791 case 4: /* rigid disk geometry page */
792 xferp->data_in[q + 0] = pagecode;
793 xferp->data_in[q + 1] = 22;
794 xferp->data_in[q + 2] = (d->ncyls >> 16) & 255;
795 xferp->data_in[q + 3] = (d->ncyls >> 8) & 255;
796 xferp->data_in[q + 4] = d->ncyls & 255;
797 xferp->data_in[q + 5] = d->heads;
798
799 xferp->data_in[q + 20] = (d->rpms >> 8) & 255;
800 xferp->data_in[q + 21] = d->rpms & 255;
801 break;
802 case 5: /* flexible disk page */
803 xferp->data_in[q + 0] = pagecode;
804 xferp->data_in[q + 1] = 0x1e;
805
806 /* 2,3 = transfer rate */
807 xferp->data_in[q + 2] = ((5000) >> 8) & 255;
808 xferp->data_in[q + 3] = (5000) & 255;
809
810 xferp->data_in[q + 4] = d->heads;
811 xferp->data_in[q + 5] = d->sectors_per_track;
812
813 /* 6,7 = data bytes per sector */
814 xferp->data_in[q + 6] = (d->logical_block_size >> 8)
815 & 255;
816 xferp->data_in[q + 7] = d->logical_block_size & 255;
817
818 xferp->data_in[q + 8] = (d->ncyls >> 8) & 255;
819 xferp->data_in[q + 9] = d->ncyls & 255;
820
821 xferp->data_in[q + 28] = (d->rpms >> 8) & 255;
822 xferp->data_in[q + 29] = d->rpms & 255;
823 break;
824 default:
825 fatal("[ MODE_SENSE for page %i is not yet "
826 "implemented! ]\n", pagecode);
827 }
828
829 break;
830
831 case SCSICMD_READ:
832 case SCSICMD_READ_10:
833 debug("READ");
834
835 /*
836 * For tape devices, read data at the current position.
837 * For disk and CDROM devices, the command bytes contain
838 * an offset telling us where to read from the device.
839 */
840
841 if (d->is_a_tape) {
842 /* bits 7..5 of cmd[1] are the LUN bits... TODO */
843
844 size = (xferp->cmd[2] << 16) +
845 (xferp->cmd[3] << 8) +
846 xferp->cmd[4];
847
848 /* Bit 1 of cmd[1] is the SILI bit (TODO), and
849 bit 0 is the "use fixed length" bit. */
850
851 if (xferp->cmd[1] & 0x01) {
852 /* Fixed block length: */
853 size *= d->logical_block_size;
854 }
855
856 if (d->filemark) {
857 /* At end of file, switch to the next
858 automagically: */
859 d->tape_filenr ++;
860 diskimage__switch_tape(d);
861
862 d->filemark = 0;
863 }
864
865 ofs = d->tape_offset;
866
867 fatal("[ READ tape, id=%i file=%i, cmd[1]=%02x size=%i"
868 ", ofs=%lli ]\n", id, d->tape_filenr,
869 xferp->cmd[1], (int)size, (long long)ofs);
870 } else {
871 if (xferp->cmd[0] == SCSICMD_READ) {
872 if (xferp->cmd_len != 6)
873 debug(" (weird len=%i)",
874 xferp->cmd_len);
875
876 /*
877 * bits 4..0 of cmd[1], and cmd[2] and cmd[3]
878 * hold the logical block address.
879 *
880 * cmd[4] holds the number of logical blocks
881 * to transfer. (Special case if the value is
882 * 0, actually means 256.)
883 */
884 ofs = ((xferp->cmd[1] & 0x1f) << 16) +
885 (xferp->cmd[2] << 8) + xferp->cmd[3];
886 retlen = xferp->cmd[4];
887 if (retlen == 0)
888 retlen = 256;
889 } else {
890 if (xferp->cmd_len != 10)
891 debug(" (weird len=%i)",
892 xferp->cmd_len);
893
894 /*
895 * cmd[2..5] hold the logical block address.
896 * cmd[7..8] holds the number of logical
897 * blocks to transfer. (NOTE: If the value is
898 * 0, this means 0, not 65536. :-)
899 */
900 ofs = ((uint64_t)xferp->cmd[2] << 24) +
901 (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8)
902 + xferp->cmd[5];
903 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
904 }
905
906 size = retlen * d->logical_block_size;
907 ofs *= d->logical_block_size;
908 }
909
910 /* Return data: */
911 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
912 size, 0);
913
914 debug(" READ ofs=%lli size=%i\n", (long long)ofs, (int)size);
915
916 diskimage__return_default_status_and_message(xferp);
917
918 d->filemark = 0;
919
920 /*
921 * Failure? Then set check condition.
922 * For tapes, error should only occur at the end of a file.
923 *
924 * "If the logical unit encounters a filemark during
925 * a READ command, CHECK CONDITION status shall be
926 * returned and the filemark and valid bits shall be
927 * set to one in the sense data. The sense key shall
928 * be set to NO SENSE"..
929 */
930 if (d->is_a_tape && d->f != NULL && feof(d->f)) {
931 debug(" feof id=%i\n", id);
932 xferp->status[0] = 0x02; /* CHECK CONDITION */
933
934 d->filemark = 1;
935 } else
936 diskimage__internal_access(d, 0, ofs,
937 xferp->data_in, size);
938
939 if (d->is_a_tape && d->f != NULL)
940 d->tape_offset = ftello(d->f);
941
942 /* TODO: other errors? */
943 break;
944
945 case SCSICMD_WRITE:
946 case SCSICMD_WRITE_10:
947 debug("WRITE");
948
949 /* TODO: tape */
950
951 if (xferp->cmd[0] == SCSICMD_WRITE) {
952 if (xferp->cmd_len != 6)
953 debug(" (weird len=%i)", xferp->cmd_len);
954
955 /*
956 * bits 4..0 of cmd[1], and cmd[2] and cmd[3] hold the
957 * logical block address.
958 *
959 * cmd[4] holds the number of logical blocks to
960 * transfer. (Special case if the value is 0, actually
961 * means 256.)
962 */
963 ofs = ((xferp->cmd[1] & 0x1f) << 16) +
964 (xferp->cmd[2] << 8) + xferp->cmd[3];
965 retlen = xferp->cmd[4];
966 if (retlen == 0)
967 retlen = 256;
968 } else {
969 if (xferp->cmd_len != 10)
970 debug(" (weird len=%i)", xferp->cmd_len);
971
972 /*
973 * cmd[2..5] hold the logical block address.
974 * cmd[7..8] holds the number of logical blocks to
975 * transfer. (NOTE: If the value is 0 this means 0,
976 * not 65536.)
977 */
978 ofs = ((uint64_t)xferp->cmd[2] << 24) +
979 (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) +
980 xferp->cmd[5];
981 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
982 }
983
984 size = retlen * d->logical_block_size;
985 ofs *= d->logical_block_size;
986
987 if (xferp->data_out_offset != size) {
988 debug(", data_out == NULL, wanting %i bytes, \n\n",
989 (int)size);
990 xferp->data_out_len = size;
991 return 2;
992 }
993
994 debug(", data_out != NULL, OK :-)");
995
996 debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs,
997 (int)size, (int)xferp->data_out_offset);
998
999 diskimage__internal_access(d, 1, ofs,
1000 xferp->data_out, size);
1001
1002 /* TODO: how about return code? */
1003
1004 /* Is this really necessary? */
1005 /* fsync(fileno(d->f)); */
1006
1007 diskimage__return_default_status_and_message(xferp);
1008 break;
1009
1010 case SCSICMD_SYNCHRONIZE_CACHE:
1011 debug("SYNCHRONIZE_CACHE");
1012
1013 if (xferp->cmd_len != 10)
1014 debug(" (weird len=%i)", xferp->cmd_len);
1015
1016 /* TODO: actualy care about cmd[] */
1017 fsync(fileno(d->f));
1018
1019 diskimage__return_default_status_and_message(xferp);
1020 break;
1021
1022 case SCSICMD_START_STOP_UNIT:
1023 debug("START_STOP_UNIT");
1024
1025 if (xferp->cmd_len != 6)
1026 debug(" (weird len=%i)", xferp->cmd_len);
1027
1028 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1029 debug(" %02x", xferp->cmd[i]);
1030
1031 /* TODO: actualy care about cmd[] */
1032
1033 diskimage__return_default_status_and_message(xferp);
1034 break;
1035
1036 case SCSICMD_REQUEST_SENSE:
1037 debug("REQUEST_SENSE");
1038
1039 retlen = xferp->cmd[4];
1040
1041 /* TODO: bits 765 of buf[1] contains the LUN */
1042 if (xferp->cmd[1] != 0x00)
1043 fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not"
1044 " yet implemented\n", (int)xferp->cmd[1]);
1045
1046 if (retlen < 18) {
1047 fatal("WARNING: SCSI request sense len=%i, <18!\n",
1048 (int)retlen);
1049 retlen = 18;
1050 }
1051
1052 /* Return data: */
1053 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1054 retlen, 1);
1055
1056 xferp->data_in[0] = 0x80 + 0x70;/* 0x80 = valid,
1057 0x70 = "current errors" */
1058 xferp->data_in[2] = 0x00; /* SENSE KEY! */
1059
1060 if (d->filemark) {
1061 xferp->data_in[2] = 0x80;
1062 }
1063 debug(": [2]=0x%02x ", xferp->data_in[2]);
1064
1065 printf(" XXX(!) \n");
1066
1067 /* TODO */
1068 xferp->data_in[7] = retlen - 7; /* additional sense length */
1069 /* TODO */
1070
1071 diskimage__return_default_status_and_message(xferp);
1072 break;
1073
1074 case SCSICMD_READ_BLOCK_LIMITS:
1075 debug("READ_BLOCK_LIMITS");
1076
1077 retlen = 6;
1078
1079 /* TODO: bits 765 of buf[1] contains the LUN */
1080 if (xferp->cmd[1] != 0x00)
1081 fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]="
1082 "0x%02x not yet implemented\n", (int)xferp->cmd[1]);
1083
1084 /* Return data: */
1085 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1086 retlen, 1);
1087
1088 /*
1089 * data[0] is reserved, data[1..3] contain the maximum block
1090 * length limit, data[4..5] contain the minimum limit.
1091 */
1092
1093 {
1094 int max_limit = 32768;
1095 int min_limit = 128;
1096
1097 xferp->data_in[1] = (max_limit >> 16) & 255;
1098 xferp->data_in[2] = (max_limit >> 8) & 255;
1099 xferp->data_in[3] = max_limit & 255;
1100 xferp->data_in[4] = (min_limit >> 8) & 255;
1101 xferp->data_in[5] = min_limit & 255;
1102 }
1103
1104 diskimage__return_default_status_and_message(xferp);
1105 break;
1106
1107 case SCSICMD_REWIND:
1108 debug("REWIND");
1109
1110 /* TODO: bits 765 of buf[1] contains the LUN */
1111 if ((xferp->cmd[1] & 0xe0) != 0x00)
1112 fatal("WARNING: REWIND with cmd[1]=0x%02x not yet "
1113 "implemented\n", (int)xferp->cmd[1]);
1114
1115 /* Close and reopen. */
1116
1117 if (d->f != NULL)
1118 fclose(d->f);
1119
1120 d->f = fopen(d->fname, d->writable? "r+" : "r");
1121 if (d->f == NULL) {
1122 fprintf(stderr, "[ diskimage: could not (re)open "
1123 "'%s' ]\n", d->fname);
1124 /* TODO: return error */
1125 }
1126
1127 d->tape_offset = 0;
1128 d->tape_filenr = 0;
1129 d->filemark = 0;
1130
1131 diskimage__return_default_status_and_message(xferp);
1132 break;
1133
1134 case SCSICMD_SPACE:
1135 debug("SPACE");
1136
1137 /* TODO: bits 765 of buf[1] contains the LUN */
1138 if ((xferp->cmd[1] & 0xe0) != 0x00)
1139 fatal("WARNING: SPACE with cmd[1]=0x%02x not yet "
1140 "implemented\n", (int)xferp->cmd[1]);
1141
1142 /*
1143 * Bits 2..0 of buf[1] contain the 'code' which describes how
1144 * we should space, and buf[2..4] contain the number of
1145 * operations.
1146 */
1147 debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n",
1148 xferp->cmd[0],
1149 xferp->cmd[1],
1150 xferp->cmd[2],
1151 xferp->cmd[3],
1152 xferp->cmd[4],
1153 xferp->cmd[5]);
1154
1155 switch (xferp->cmd[1] & 7) {
1156 case 1: /* Seek to a different file nr: */
1157 {
1158 int diff = (xferp->cmd[2] << 16) +
1159 (xferp->cmd[3] << 8) + xferp->cmd[4];
1160
1161 /* Negative seek offset: */
1162 if (diff & (1 << 23))
1163 diff = - (16777216 - diff);
1164
1165 d->tape_filenr += diff;
1166 }
1167
1168 /* At end of file, switch to the next tape file: */
1169 if (d->filemark) {
1170 d->tape_filenr ++;
1171 d->filemark = 0;
1172 }
1173
1174 debug("{ switching to tape file %i }", d->tape_filenr);
1175 diskimage__switch_tape(d);
1176 d->filemark = 0;
1177 break;
1178 default:
1179 fatal("[ diskimage.c: unimplemented SPACE type %i ]\n",
1180 xferp->cmd[1] & 7);
1181 }
1182
1183 diskimage__return_default_status_and_message(xferp);
1184 break;
1185
1186 case SCSICDROM_READ_SUBCHANNEL:
1187 /*
1188 * According to
1189 * http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html:
1190 *
1191 * "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT
1192 * commands have the same opcode in SCSI or ATAPI, but don't
1193 * have the same command structure"...
1194 *
1195 * TODO: This still doesn't work. Hm.
1196 */
1197 retlen = 48;
1198
1199 debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x",
1200 xferp->cmd[1]);
1201
1202 /* Return data: */
1203 scsi_transfer_allocbuf(&xferp->data_in_len,
1204 &xferp->data_in, retlen, 1);
1205
1206 diskimage_recalc_size(d);
1207
1208 size = d->total_size / d->logical_block_size;
1209 if (d->total_size & (d->logical_block_size-1))
1210 size ++;
1211
1212 xferp->data_in[0] = (size >> 24) & 255;
1213 xferp->data_in[1] = (size >> 16) & 255;
1214 xferp->data_in[2] = (size >> 8) & 255;
1215 xferp->data_in[3] = size & 255;
1216
1217 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
1218 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
1219 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
1220 xferp->data_in[7] = d->logical_block_size & 255;
1221
1222 diskimage__return_default_status_and_message(xferp);
1223 break;
1224
1225 case SCSICDROM_READ_TOC:
1226 debug("(CDROM_READ_TOC: ");
1227 debug("lun=%i msf=%i ",
1228 xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1);
1229 debug("starting_track=%i ", xferp->cmd[6]);
1230 retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
1231 debug("allocation_len=%i)\n", retlen);
1232
1233 /* Return data: */
1234 scsi_transfer_allocbuf(&xferp->data_in_len,
1235 &xferp->data_in, retlen, 1);
1236
1237 xferp->data_in[0] = 0;
1238 xferp->data_in[1] = 10;
1239 xferp->data_in[2] = 0; /* First track. */
1240 xferp->data_in[3] = 0; /* Last track. */
1241
1242 /* Track 0 data: */
1243 xferp->data_in[4] = 0x00; /* Reserved. */
1244 xferp->data_in[5] = 0x04; /* ADR + CTRL:
1245 Data, not audio */
1246 xferp->data_in[6] = 0x00; /* Track nr */
1247 xferp->data_in[7] = 0x00; /* Reserved */
1248 /* 8..11 = absolute CDROM address */
1249
1250 diskimage__return_default_status_and_message(xferp);
1251 break;
1252
1253 case SCSICMD_MODE_SELECT:
1254 debug("[ SCSI MODE_SELECT: ");
1255
1256 /*
1257 * TODO:
1258 *
1259 * This is super-hardcoded for NetBSD's usage of mode_select
1260 * to set the size of CDROM sectors to 2048.
1261 */
1262
1263 if (xferp->data_out_offset == 0) {
1264 xferp->data_out_len = 12; /* TODO */
1265 debug("data_out == NULL, wanting %i bytes ]\n",
1266 (int)xferp->data_out_len);
1267 return 2;
1268 }
1269
1270 debug("data_out!=NULL (OK), ");
1271
1272 /* TODO: Care about cmd? */
1273
1274 /* Set sector size to 2048: */
1275 /* 00 05 00 08 00 03 ca 40 00 00 08 00 */
1276 if (xferp->data_out[0] == 0x00 &&
1277 xferp->data_out[1] == 0x05 &&
1278 xferp->data_out[2] == 0x00 &&
1279 xferp->data_out[3] == 0x08) {
1280 d->logical_block_size =
1281 (xferp->data_out[9] << 16) +
1282 (xferp->data_out[10] << 8) +
1283 xferp->data_out[11];
1284 debug("[ setting logical_block_size to %i ]\n",
1285 d->logical_block_size);
1286 } else {
1287 int i;
1288 fatal("[ unknown MODE_SELECT: cmd =");
1289 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1290 fatal(" %02x", xferp->cmd[i]);
1291 fatal(", data_out =");
1292 for (i=0; i<(ssize_t)xferp->data_out_len; i++)
1293 fatal(" %02x", xferp->data_out[i]);
1294 fatal(" ]");
1295 }
1296
1297 debug(" ]\n");
1298 diskimage__return_default_status_and_message(xferp);
1299 break;
1300
1301 case SCSICMD_PREVENT_ALLOW_REMOVE:
1302 debug("[ SCSI 0x%02x Prevent/allow medium removal: "
1303 "TODO ]\n", xferp->cmd[0]);
1304
1305 diskimage__return_default_status_and_message(xferp);
1306 break;
1307
1308 case 0xbd:
1309 fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0],
1310 xferp->cmd_len);
1311 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1312 fatal(" %02x", xferp->cmd[i]);
1313 fatal(" ]\n");
1314
1315 /*
1316 * Used by Windows NT?
1317 *
1318 * Not documented in http://www.danbbs.dk/~dino/
1319 * SCSI/SCSI2-D.html.
1320 * Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm.
1321 */
1322
1323 if (xferp->cmd_len < 12) {
1324 fatal("WEIRD LEN?\n");
1325 retlen = 8;
1326 } else {
1327 retlen = xferp->cmd[8] * 256 + xferp->cmd[9];
1328 }
1329
1330 /* Return data: */
1331 scsi_transfer_allocbuf(&xferp->data_in_len,
1332 &xferp->data_in, retlen, 1);
1333
1334 diskimage__return_default_status_and_message(xferp);
1335
1336 break;
1337
1338 default:
1339 fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1340 xferp->cmd[0], id);
1341 exit(1);
1342 }
1343 debug(" ]\n");
1344
1345 return 1;
1346 }
1347
1348
1349 /*
1350 * diskimage_access():
1351 *
1352 * Read from or write to a disk image on a machine.
1353 *
1354 * Returns 1 if the access completed successfully, 0 otherwise.
1355 */
1356 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
1357 off_t offset, unsigned char *buf, size_t len)
1358 {
1359 struct diskimage *d = machine->first_diskimage;
1360
1361 while (d != NULL) {
1362 if (d->type == type && d->id == id)
1363 break;
1364 d = d->next;
1365 }
1366
1367 if (d == NULL) {
1368 fatal("[ diskimage_access(): ERROR: trying to access a "
1369 "non-existant %s disk image (id %i)\n",
1370 diskimage_types[type], id);
1371 return 0;
1372 }
1373
1374 return diskimage__internal_access(d, writeflag, offset, buf, len);
1375 }
1376
1377
1378 /*
1379 * diskimage_add():
1380 *
1381 * Add a disk image. fname is the filename of the disk image.
1382 * The filename may be prefixed with one or more modifiers, followed
1383 * by a colon.
1384 *
1385 * b specifies that this is a bootable device
1386 * c CD-ROM (instead of a normal DISK)
1387 * d DISK (this is the default)
1388 * f FLOPPY (instead of SCSI)
1389 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
1390 * automatically calculated). (This is ignored for floppies.)
1391 * i IDE (instead of SCSI)
1392 * r read-only (don't allow changes to the file)
1393 * s SCSI (this is the default)
1394 * t tape
1395 * 0-7 force a specific SCSI ID number
1396 *
1397 * machine is assumed to be non-NULL.
1398 * Returns an integer >= 0 identifying the disk image.
1399 */
1400 int diskimage_add(struct machine *machine, char *fname)
1401 {
1402 struct diskimage *d, *d2;
1403 int id = 0, override_heads=0, override_spt=0;
1404 int64_t bytespercyl;
1405 char *cp;
1406 int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;
1407 int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id = -1;
1408
1409 if (fname == NULL) {
1410 fprintf(stderr, "diskimage_add(): NULL ptr\n");
1411 return 0;
1412 }
1413
1414 /* Get prefix from fname: */
1415 cp = strchr(fname, ':');
1416 if (cp != NULL) {
1417 while (fname <= cp) {
1418 char c = *fname++;
1419 switch (c) {
1420 case '0':
1421 case '1':
1422 case '2':
1423 case '3':
1424 case '4':
1425 case '5':
1426 case '6':
1427 case '7':
1428 prefix_id = c - '0';
1429 break;
1430 case 'b':
1431 prefix_b = 1;
1432 break;
1433 case 'c':
1434 prefix_c = 1;
1435 break;
1436 case 'd':
1437 prefix_d = 1;
1438 break;
1439 case 'f':
1440 prefix_f = 1;
1441 break;
1442 case 'g':
1443 prefix_g = 1;
1444 override_heads = atoi(fname);
1445 while (*fname != '\0' && *fname != ';')
1446 fname ++;
1447 if (*fname == ';')
1448 fname ++;
1449 override_spt = atoi(fname);
1450 while (*fname != '\0' && *fname != ';' &&
1451 *fname != ':')
1452 fname ++;
1453 if (*fname == ';')
1454 fname ++;
1455 if (override_heads < 1 ||
1456 override_spt < 1) {
1457 fatal("Bad geometry: heads=%i "
1458 "spt=%i\n", override_heads,
1459 override_spt);
1460 exit(1);
1461 }
1462 break;
1463 case 'i':
1464 prefix_i = 1;
1465 break;
1466 case 'r':
1467 prefix_r = 1;
1468 break;
1469 case 's':
1470 prefix_s = 1;
1471 break;
1472 case 't':
1473 prefix_t = 1;
1474 break;
1475 case ':':
1476 break;
1477 default:
1478 fprintf(stderr, "diskimage_add(): invalid "
1479 "prefix char '%c'\n", c);
1480 exit(1);
1481 }
1482 }
1483 }
1484
1485 /* Allocate a new diskimage struct: */
1486 d = malloc(sizeof(struct diskimage));
1487 if (d == NULL) {
1488 fprintf(stderr, "out of memory in diskimage_add()\n");
1489 exit(1);
1490 }
1491 memset(d, 0, sizeof(struct diskimage));
1492
1493 d2 = machine->first_diskimage;
1494 if (d2 == NULL) {
1495 machine->first_diskimage = d;
1496 } else {
1497 while (d2->next != NULL)
1498 d2 = d2->next;
1499 d2->next = d;
1500 }
1501
1502 /* Default to IDE disks... */
1503 d->type = DISKIMAGE_IDE;
1504
1505 /* ... but some machines use SCSI by default: */
1506 if (machine->machine_type == MACHINE_PMAX ||
1507 machine->machine_type == MACHINE_ARC)
1508 d->type = DISKIMAGE_SCSI;
1509
1510 if (prefix_i + prefix_f + prefix_s > 1) {
1511 fprintf(stderr, "Invalid disk image prefix(es). You can"
1512 "only use one of i, f, and s\nfor each disk image.\n");
1513 exit(1);
1514 }
1515
1516 if (prefix_i)
1517 d->type = DISKIMAGE_IDE;
1518 if (prefix_f)
1519 d->type = DISKIMAGE_FLOPPY;
1520 if (prefix_s)
1521 d->type = DISKIMAGE_SCSI;
1522
1523 d->fname = strdup(fname);
1524 if (d->fname == NULL) {
1525 fprintf(stderr, "out of memory\n");
1526 exit(1);
1527 }
1528
1529 d->logical_block_size = 512;
1530
1531 /*
1532 * Is this a tape, CD-ROM or a normal disk?
1533 *
1534 * An intelligent guess, if no prefixes are used, would be that
1535 * filenames ending with .iso or .cdr are CD-ROM images.
1536 */
1537 if (prefix_t) {
1538 d->is_a_tape = 1;
1539 } else {
1540 if (prefix_c ||
1541 ((strlen(d->fname) > 4 &&
1542 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
1543 strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
1544 && !prefix_d)
1545 ) {
1546 d->is_a_cdrom = 1;
1547
1548 /*
1549 * This is tricky. Should I use 512 or 2048 here?
1550 * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
1551 * per sector, but NetBSD 2.0_BETA suddenly ignores
1552 * this value and uses 2048 instead.
1553 *
1554 * OpenBSD/arc doesn't like 2048, it requires 512
1555 * to work correctly.
1556 *
1557 * TODO
1558 */
1559
1560 #if 0
1561 if (machine->machine_type == MACHINE_PMAX)
1562 d->logical_block_size = 512;
1563 else
1564 d->logical_block_size = 2048;
1565 #endif
1566 d->logical_block_size = 512;
1567 }
1568 }
1569
1570 diskimage_recalc_size(d);
1571
1572 if ((d->total_size == 720*1024 || d->total_size == 1474560
1573 || d->total_size == 2949120 || d->total_size == 1228800)
1574 && !prefix_i && !prefix_s)
1575 d->type = DISKIMAGE_FLOPPY;
1576
1577 switch (d->type) {
1578 case DISKIMAGE_FLOPPY:
1579 if (d->total_size < 737280) {
1580 fatal("\nTODO: small (non-80-cylinder) floppies?\n\n");
1581 exit(1);
1582 }
1583 d->cylinders = 80;
1584 d->heads = 2;
1585 d->sectors_per_track = d->total_size / (d->cylinders *
1586 d->heads * 512);
1587 break;
1588 default:/* Non-floppies: */
1589 d->heads = 16;
1590 d->sectors_per_track = 63;
1591 if (prefix_g) {
1592 d->chs_override = 1;
1593 d->heads = override_heads;
1594 d->sectors_per_track = override_spt;
1595 }
1596 bytespercyl = d->heads * d->sectors_per_track * 512;
1597 d->cylinders = d->total_size / bytespercyl;
1598 if (d->cylinders * bytespercyl < d->total_size)
1599 d->cylinders ++;
1600 }
1601
1602 d->rpms = 3600;
1603
1604 if (prefix_b)
1605 d->is_boot_device = 1;
1606
1607 d->writable = access(fname, W_OK) == 0? 1 : 0;
1608
1609 if (d->is_a_cdrom || prefix_r)
1610 d->writable = 0;
1611
1612 d->f = fopen(fname, d->writable? "r+" : "r");
1613 if (d->f == NULL) {
1614 perror(fname);
1615 exit(1);
1616 }
1617
1618 /* Calculate which ID to use: */
1619 if (prefix_id == -1) {
1620 int free = 0, collision = 1;
1621
1622 while (collision) {
1623 collision = 0;
1624 d2 = machine->first_diskimage;
1625 while (d2 != NULL) {
1626 /* (don't compare against ourselves :) */
1627 if (d2 == d) {
1628 d2 = d2->next;
1629 continue;
1630 }
1631 if (d2->id == free && d2->type == d->type) {
1632 collision = 1;
1633 break;
1634 }
1635 d2 = d2->next;
1636 }
1637 if (!collision)
1638 id = free;
1639 else
1640 free ++;
1641 }
1642 } else {
1643 id = prefix_id;
1644 d2 = machine->first_diskimage;
1645 while (d2 != NULL) {
1646 /* (don't compare against ourselves :) */
1647 if (d2 == d) {
1648 d2 = d2->next;
1649 continue;
1650 }
1651 if (d2->id == id && d2->type == d->type) {
1652 fprintf(stderr, "disk image id %i "
1653 "already in use\n", id);
1654 exit(1);
1655 }
1656 d2 = d2->next;
1657 }
1658 }
1659
1660 d->id = id;
1661
1662 return id;
1663 }
1664
1665
1666 /*
1667 * diskimage_bootdev():
1668 *
1669 * Returns the disk id of the device which we're booting from. If typep is
1670 * non-NULL, the type is returned as well.
1671 *
1672 * If no disk was used as boot device, then -1 is returned. (In practice,
1673 * this is used to fake network (tftp) boot.)
1674 */
1675 int diskimage_bootdev(struct machine *machine, int *typep)
1676 {
1677 struct diskimage *d;
1678
1679 d = machine->first_diskimage;
1680 while (d != NULL) {
1681 if (d->is_boot_device) {
1682 if (typep != NULL)
1683 *typep = d->type;
1684 return d->id;
1685 }
1686 d = d->next;
1687 }
1688
1689 d = machine->first_diskimage;
1690 if (d != NULL) {
1691 if (typep != NULL)
1692 *typep = d->type;
1693 return d->id;
1694 }
1695
1696 return -1;
1697 }
1698
1699
1700 /*
1701 * diskimage_getname():
1702 *
1703 * Returns 1 if a valid disk image name was returned, 0 otherwise.
1704 */
1705 int diskimage_getname(struct machine *machine, int id, int type,
1706 char *buf, size_t bufsize)
1707 {
1708 struct diskimage *d = machine->first_diskimage;
1709
1710 if (buf == NULL)
1711 return 0;
1712
1713 while (d != NULL) {
1714 if (d->type == type && d->id == id) {
1715 char *p = strrchr(d->fname, '/');
1716 if (p == NULL)
1717 p = d->fname;
1718 else
1719 p ++;
1720 snprintf(buf, bufsize, "%s", p);
1721 return 1;
1722 }
1723 d = d->next;
1724 }
1725 return 0;
1726 }
1727
1728
1729 /*
1730 * diskimage_is_a_cdrom():
1731 *
1732 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1733 */
1734 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1735 {
1736 struct diskimage *d = machine->first_diskimage;
1737
1738 while (d != NULL) {
1739 if (d->type == type && d->id == id)
1740 return d->is_a_cdrom;
1741 d = d->next;
1742 }
1743 return 0;
1744 }
1745
1746
1747 /*
1748 * diskimage_is_a_tape():
1749 *
1750 * Returns 1 if a disk image is a tape, 0 otherwise.
1751 *
1752 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1753 * boot strings.)
1754 */
1755 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1756 {
1757 struct diskimage *d = machine->first_diskimage;
1758
1759 while (d != NULL) {
1760 if (d->type == type && d->id == id)
1761 return d->is_a_tape;
1762 d = d->next;
1763 }
1764 return 0;
1765 }
1766
1767
1768 /*
1769 * diskimage_dump_info():
1770 *
1771 * Debug dump of all diskimages that are loaded for a specific machine.
1772 */
1773 void diskimage_dump_info(struct machine *machine)
1774 {
1775 int iadd = DEBUG_INDENTATION;
1776 struct diskimage *d = machine->first_diskimage;
1777
1778 while (d != NULL) {
1779 debug("diskimage: %s\n", d->fname);
1780 debug_indentation(iadd);
1781
1782 switch (d->type) {
1783 case DISKIMAGE_SCSI:
1784 debug("SCSI");
1785 break;
1786 case DISKIMAGE_IDE:
1787 debug("IDE");
1788 break;
1789 case DISKIMAGE_FLOPPY:
1790 debug("FLOPPY");
1791 break;
1792 default:
1793 debug("UNKNOWN type %i", d->type);
1794 }
1795
1796 debug(" %s", d->is_a_tape? "TAPE" :
1797 (d->is_a_cdrom? "CD-ROM" : "DISK"));
1798 debug(" id %i, ", d->id);
1799 debug("%s, ", d->writable? "read/write" : "read-only");
1800
1801 if (d->type == DISKIMAGE_FLOPPY)
1802 debug("%lli KB", (long long) (d->total_size / 1024));
1803 else
1804 debug("%lli MB", (long long) (d->total_size / 1048576));
1805
1806 if (d->type == DISKIMAGE_FLOPPY || d->chs_override)
1807 debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads,
1808 d->sectors_per_track);
1809 else
1810 debug(" (%lli sectors)", (long long)
1811 (d->total_size / 512));
1812
1813 if (d->is_boot_device)
1814 debug(" (BOOT)");
1815 debug("\n");
1816
1817 debug_indentation(-iadd);
1818
1819 d = d->next;
1820 }
1821 }
1822

  ViewVC Help
Powered by ViewVC 1.1.26