/[gxemul]/upstream/0.3.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.3.1/src/diskimage.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3 - (show annotations)
Mon Oct 8 16:17:52 2007 UTC (16 years, 8 months ago) by dpavlin
File MIME type: text/plain
File size: 40731 byte(s)
0.3.1
1 /*
2 * Copyright (C) 2003-2005 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.81 2005/04/04 21:50:05 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: Non-SCSI disk images?
40 *
41 * TODO: diskimage_remove() ?
42 * Actually test diskimage_access() to see that it works.
43 */
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51
52 #include "cpu.h"
53 #include "diskimage.h"
54 #include "machine.h"
55 #include "misc.h"
56
57
58 extern int quiet_mode;
59 extern int single_step;
60
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 SCSI id exists, 0 otherwise.
213 */
214 int diskimage_exist(struct machine *machine, int scsi_id)
215 {
216 struct diskimage *d = machine->first_diskimage;
217
218 while (d != NULL) {
219 if (d->type == DISKIMAGE_SCSI && d->id == scsi_id)
220 return 1;
221 d = d->next;
222 }
223 return 0;
224 }
225
226
227 /*
228 * diskimage_recalc_size():
229 *
230 * Recalculate a disk's size by stat()-ing it.
231 * d is assumed to be non-NULL.
232 */
233 static void diskimage_recalc_size(struct diskimage *d)
234 {
235 struct stat st;
236 int res;
237 off_t size = 0;
238
239 res = stat(d->fname, &st);
240 if (res) {
241 fprintf(stderr, "[ diskimage_recalc_size(): could not stat "
242 "'%s' ]\n", d->fname);
243 return;
244 }
245
246 size = st.st_size;
247
248 /*
249 * TODO: CD-ROM devices, such as /dev/cd0c, how can one
250 * check how much data is on that cd-rom without reading it?
251 * For now, assume some large number, hopefully it will be
252 * enough to hold any cd-rom image.
253 */
254 if (d->is_a_cdrom && size == 0)
255 size = 762048000;
256
257 d->total_size = size;
258 d->ncyls = d->total_size / 1048576;
259 }
260
261
262 /*
263 * diskimage_getsize():
264 *
265 * Returns -1 if the specified SCSI id does not exists, otherwise
266 * the size of the disk image is returned.
267 */
268 int64_t diskimage_getsize(struct machine *machine, int scsi_id)
269 {
270 struct diskimage *d = machine->first_diskimage;
271
272 while (d != NULL) {
273 if (d->type == DISKIMAGE_SCSI && d->id == scsi_id)
274 return d->total_size;
275 d = d->next;
276 }
277 return -1;
278 }
279
280
281 /*
282 * diskimage__return_default_status_and_message():
283 *
284 * Set the status and msg_in parts of a scsi_transfer struct
285 * to default values (msg_in = 0x00, status = 0x00).
286 */
287 static void diskimage__return_default_status_and_message(
288 struct scsi_transfer *xferp)
289 {
290 scsi_transfer_allocbuf(&xferp->status_len, &xferp->status, 1, 0);
291 xferp->status[0] = 0x00;
292 scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1, 0);
293 xferp->msg_in[0] = 0x00;
294 }
295
296
297 /*
298 * diskimage__switch_tape():
299 *
300 * Used by the SPACE command. (d is assumed to be non-NULL.)
301 */
302 static void diskimage__switch_tape(struct diskimage *d)
303 {
304 char tmpfname[1000];
305
306 snprintf(tmpfname, sizeof(tmpfname), "%s.%i",
307 d->fname, d->tape_filenr);
308 tmpfname[sizeof(tmpfname)-1] = '\0';
309
310 if (d->f != NULL)
311 fclose(d->f);
312
313 d->f = fopen(tmpfname, d->writable? "r+" : "r");
314 if (d->f == NULL) {
315 fprintf(stderr, "[ diskimage__switch_tape(): could not "
316 "(re)open '%s' ]\n", tmpfname);
317 /* TODO: return error */
318 }
319 d->tape_offset = 0;
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
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 /*
375 * diskimage__internal_access():
376 *
377 * Read from or write to a struct diskimage.
378 *
379 * Returns 1 if the access completed successfully, 0 otherwise.
380 */
381 static int diskimage__internal_access(struct diskimage *d, int writeflag,
382 off_t offset, unsigned char *buf, size_t len)
383 {
384 size_t lendone;
385 int res;
386
387 if (buf == NULL) {
388 fprintf(stderr, "diskimage__internal_access(): buf = NULL\n");
389 exit(1);
390 }
391 if (len == 0)
392 return 1;
393 if (d->f == NULL)
394 return 0;
395
396 res = my_fseek(d->f, offset, SEEK_SET);
397 if (res != 0) {
398 fatal("[ diskimage__internal_access(): fseek() failed on "
399 "disk id %i \n", d->id);
400 return 0;
401 }
402
403 if (writeflag) {
404 if (!d->writable)
405 return 0;
406
407 lendone = fwrite(buf, 1, len, d->f);
408 } else {
409 /*
410 * Special case for CD-ROMs. Actually, this is not needed
411 * for .iso images, only for physical CDROMS on some OSes,
412 * such as FreeBSD.
413 */
414 if (d->is_a_cdrom)
415 lendone = diskimage_access__cdrom(d, offset, buf, len);
416 else
417 lendone = fread(buf, 1, len, d->f);
418
419 if (lendone < (ssize_t)len)
420 memset(buf + lendone, 0, len - lendone);
421 }
422
423 /* Warn about non-complete data transfers: */
424 if (lendone != (ssize_t)len) {
425 fatal("[ diskimage__internal_access(): disk_id %i, offset %lli"
426 ", transfer not completed. len=%i, len_done=%i ]\n",
427 d->id, (long long)offset, (int)len, (int)lendone);
428 return 0;
429 }
430
431 return 1;
432 }
433
434
435 /*
436 * diskimage_scsicommand():
437 *
438 * Perform a SCSI command on a disk image.
439 *
440 * The xferp points to a scsi_transfer struct, containing msg_out, command,
441 * and data_out coming from the SCSI controller device. This function
442 * interprets the command, and (if necessary) creates responses in
443 * data_in, msg_in, and status.
444 *
445 * Returns:
446 * 2 if the command expects data from the DATA_OUT phase,
447 * 1 if otherwise ok,
448 * 0 on error.
449 */
450 int diskimage_scsicommand(struct cpu *cpu, int scsi_id,
451 struct scsi_transfer *xferp)
452 {
453 int retlen, i;
454 uint64_t size;
455 int64_t ofs;
456 int pagecode;
457 struct machine *machine = cpu->machine;
458 struct diskimage *d;
459
460 if (machine == NULL) {
461 fatal("[ diskimage_scsicommand(): machine == NULL ]\n");
462 return 0;
463 }
464
465 d = machine->first_diskimage;
466 while (d != NULL) {
467 if (d->type == DISKIMAGE_SCSI && d->id == scsi_id)
468 break;
469 d = d->next;
470 }
471 if (d == NULL) {
472 fprintf(stderr, "[ diskimage_scsicommand(): SCSI disk"
473 " with id %i not connected? ]\n", scsi_id);
474 }
475
476 if (xferp->cmd == NULL) {
477 fatal("[ diskimage_scsicommand(): cmd == NULL ]\n");
478 return 0;
479 }
480
481 if (xferp->cmd_len < 1) {
482 fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n",
483 xferp->cmd_len);
484 return 0;
485 }
486
487 debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ",
488 scsi_id, xferp->cmd[0]);
489
490 #if 0
491 fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",
492 scsi_id, xferp->cmd[0], xferp->cmd_len);
493 for (i=0; i<xferp->cmd_len; i++)
494 fatal(" %02x", xferp->cmd[i]);
495 fatal("\n");
496 if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11)
497 single_step = 1;
498 #endif
499
500 #if 0
501 {
502 static FILE *f = NULL;
503 if (f == NULL)
504 f = fopen("scsi_log.txt", "w");
505 if (f != NULL) {
506 int i;
507 fprintf(f, "id=%i cmd =", scsi_id);
508 for (i=0; i<xferp->cmd_len; i++)
509 fprintf(f, " %02x", xferp->cmd[i]);
510 fprintf(f, "\n");
511 fflush(f);
512 }
513 }
514 #endif
515
516 switch (xferp->cmd[0]) {
517
518 case SCSICMD_TEST_UNIT_READY:
519 debug("TEST_UNIT_READY");
520 if (xferp->cmd_len != 6)
521 debug(" (weird len=%i)", xferp->cmd_len);
522
523 /* TODO: bits 765 of buf[1] contains the LUN */
524 if (xferp->cmd[1] != 0x00)
525 fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x"
526 " not yet implemented\n", (int)xferp->cmd[1]);
527
528 diskimage__return_default_status_and_message(xferp);
529 break;
530
531 case SCSICMD_INQUIRY:
532 debug("INQUIRY");
533 if (xferp->cmd_len != 6)
534 debug(" (weird len=%i)", xferp->cmd_len);
535 if (xferp->cmd[1] != 0x00) {
536 debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet "
537 "implemented\n", (int)xferp->cmd[1]);
538
539 break;
540 }
541
542 /* Return values: */
543 retlen = xferp->cmd[4];
544 if (retlen < 36) {
545 fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen);
546 retlen = 36;
547 }
548
549 /* Return data: */
550 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
551 retlen, 1);
552 xferp->data_in[0] = 0x00; /* 0x00 = Direct-access disk */
553 xferp->data_in[1] = 0x00; /* 0x00 = non-removable */
554 xferp->data_in[2] = 0x02; /* SCSI-2 */
555 #if 0
556 xferp->data_in[3] = 0x02; /* Response data format = SCSI-2 */
557 #endif
558 xferp->data_in[4] = retlen - 4; /* Additional length */
559 xferp->data_in[4] = 0x2c - 4; /* Additional length */
560 xferp->data_in[6] = 0x04; /* ACKREQQ */
561 xferp->data_in[7] = 0x60; /* WBus32, WBus16 */
562
563 /* These must be padded with spaces: */
564 memcpy(xferp->data_in+8, "FAKE ", 8);
565 memcpy(xferp->data_in+16, "DISK ", 16);
566 memcpy(xferp->data_in+32, "V0.0", 4);
567
568 /*
569 * Some Ultrix kernels want specific responses from
570 * the drives.
571 */
572
573 if (machine->machine_type == MACHINE_DEC) {
574 /* DEC, RZ25 (rev 0900) = 832527 sectors */
575 /* DEC, RZ58 (rev 2000) = 2698061 sectors */
576 memcpy(xferp->data_in+8, "DEC ", 8);
577 memcpy(xferp->data_in+16, "RZ58 (C) DEC", 16);
578 memcpy(xferp->data_in+32, "2000", 4);
579 }
580
581 /* Some data is different for CD-ROM drives: */
582 if (d->is_a_cdrom) {
583 xferp->data_in[0] = 0x05; /* 0x05 = CD-ROM */
584 xferp->data_in[1] = 0x80; /* 0x80 = removable */
585 memcpy(xferp->data_in+16, "CD-ROM ", 16);
586
587 if (machine->machine_type == MACHINE_DEC) {
588 /* SONY, CD-ROM: */
589 memcpy(xferp->data_in+8, "SONY ", 8);
590 memcpy(xferp->data_in+16,
591 "CD-ROM ", 16);
592
593 /* ... or perhaps this: */
594 memcpy(xferp->data_in+8, "DEC ", 8);
595 memcpy(xferp->data_in+16,
596 "RRD42 (C) DEC ", 16);
597 memcpy(xferp->data_in+32, "4.5d", 4);
598 } else {
599 /* NEC, CD-ROM: */
600 memcpy(xferp->data_in+8, "NEC ", 8);
601 memcpy(xferp->data_in+16,
602 "CD-ROM CDR-210P ", 16);
603 memcpy(xferp->data_in+32, "1.0 ", 4);
604 }
605 }
606
607 /* Data for tape devices: */
608 if (d->is_a_tape) {
609 xferp->data_in[0] = 0x01; /* 0x01 = tape */
610 xferp->data_in[1] = 0x80; /* 0x80 = removable */
611 memcpy(xferp->data_in+16, "TAPE ", 16);
612
613 if (machine->machine_type == MACHINE_DEC) {
614 /*
615 * TODO: find out if these are correct.
616 *
617 * The name might be TZK10, TSZ07, or TLZ04,
618 * or something completely different.
619 */
620 memcpy(xferp->data_in+8, "DEC ", 8);
621 memcpy(xferp->data_in+16,
622 "TK50 (C) DEC", 16);
623 memcpy(xferp->data_in+32, "2000", 4);
624 }
625 }
626
627 diskimage__return_default_status_and_message(xferp);
628 break;
629
630 case SCSIBLOCKCMD_READ_CAPACITY:
631 debug("READ_CAPACITY");
632
633 if (xferp->cmd_len != 10)
634 fatal(" [ weird READ_CAPACITY len=%i, should be 10 ] ",
635 xferp->cmd_len);
636 else {
637 if (xferp->cmd[8] & 1) {
638 /* Partial Medium Indicator bit... TODO */
639 fatal("WARNING: READ_CAPACITY with PMI bit"
640 " set not yet implemented\n");
641 }
642 }
643
644 /* Return data: */
645 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
646 8, 1);
647
648 diskimage_recalc_size(d);
649
650 size = d->total_size / d->logical_block_size;
651 if (d->total_size & (d->logical_block_size-1))
652 size ++;
653
654 xferp->data_in[0] = (size >> 24) & 255;
655 xferp->data_in[1] = (size >> 16) & 255;
656 xferp->data_in[2] = (size >> 8) & 255;
657 xferp->data_in[3] = size & 255;
658
659 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
660 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
661 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
662 xferp->data_in[7] = d->logical_block_size & 255;
663
664 diskimage__return_default_status_and_message(xferp);
665 break;
666
667 case SCSICMD_MODE_SENSE:
668 debug("MODE_SENSE");
669
670 if (xferp->cmd_len != 6)
671 fatal(" (unimplemented mode_sense len=%i)",
672 xferp->cmd_len);
673
674 retlen = xferp->cmd[4];
675
676 /*
677 * NOTE/TODO: This code doesn't handle too short retlens
678 * very well. A quick hack around this is that I allocate
679 * a bit too much memory, so that nothing is actually
680 * written outside of xferp->data_in[].
681 */
682
683 retlen += 100; /* Should be enough. (Ugly.) */
684
685 if ((xferp->cmd[2] & 0xc0) != 0)
686 fatal("WARNING: mode sense, cmd[2] = 0x%02x\n",
687 xferp->cmd[2]);
688
689 /* Return data: */
690 scsi_transfer_allocbuf(&xferp->data_in_len,
691 &xferp->data_in, retlen, 1);
692
693 xferp->data_in_len -= 100; /* Restore size. */
694
695 pagecode = xferp->cmd[2] & 0x3f;
696
697 debug("[ MODE SENSE id %i, pagecode=%i ]\n", scsi_id, pagecode);
698
699 /* 4 bytes of header for 6-byte command,
700 8 bytes of header for 10-byte command. */
701 xferp->data_in[0] = retlen; /* 0: mode data length */
702 xferp->data_in[1] = d->is_a_cdrom? 0x05 : 0x00;
703 /* 1: medium type */
704 xferp->data_in[2] = 0x00; /* device specific
705 parameter */
706 xferp->data_in[3] = 8 * 1; /* block descriptor
707 length: 1 page (?) */
708
709 /* TODO: update this when implementing 10-byte commands: */
710 xferp->data_in[4] = 0x00; /* density code */
711 xferp->data_in[5] = 0; /* nr of blocks, high */
712 xferp->data_in[6] = 0; /* nr of blocks, mid */
713 xferp->data_in[7] = 0; /* nr of blocks, low */
714 xferp->data_in[8] = 0x00; /* reserved */
715 xferp->data_in[9] = (d->logical_block_size >> 16) & 255;
716 xferp->data_in[10] = (d->logical_block_size >> 8) & 255;
717 xferp->data_in[11] = d->logical_block_size & 255;
718
719 diskimage__return_default_status_and_message(xferp);
720
721 /* descriptors, 8 bytes (each) */
722
723 /* page, n bytes (each) */
724 switch (pagecode) {
725 case 0:
726 /* TODO: Nothing here? */
727 break;
728 case 1: /* read-write error recovery page */
729 xferp->data_in[12 + 0] = pagecode;
730 xferp->data_in[12 + 1] = 10;
731 break;
732 case 3: /* format device page */
733 xferp->data_in[12 + 0] = pagecode;
734 xferp->data_in[12 + 1] = 22;
735
736 /* 10,11 = sectors per track */
737 xferp->data_in[12 + 10] = 0;
738 xferp->data_in[12 + 11] = 1; /* TODO */
739
740 /* 12,13 = physical sector size */
741 xferp->data_in[12 + 12] =
742 (d->logical_block_size >> 8) & 255;
743 xferp->data_in[12 + 13] = d->logical_block_size & 255;
744 break;
745 case 4: /* rigid disk geometry page */
746 xferp->data_in[12 + 0] = pagecode;
747 xferp->data_in[12 + 1] = 22;
748 xferp->data_in[12 + 2] = (d->ncyls >> 16) & 255;
749 xferp->data_in[12 + 3] = (d->ncyls >> 8) & 255;
750 xferp->data_in[12 + 4] = d->ncyls & 255;
751 xferp->data_in[12 + 5] = 15; /* nr of heads */
752
753 xferp->data_in[12 + 20] = (d->rpms >> 8) & 255;
754 xferp->data_in[12 + 21] = d->rpms & 255;
755 break;
756 case 5: /* flexible disk page */
757 xferp->data_in[12 + 0] = pagecode;
758 xferp->data_in[12 + 1] = 0x1e;
759
760 /* 2,3 = transfer rate */
761 xferp->data_in[12 + 2] = ((5000) >> 8) & 255;
762 xferp->data_in[12 + 3] = (5000) & 255;
763
764 xferp->data_in[12 + 4] = 2; /* nr of heads */
765 xferp->data_in[12 + 5] = 18; /* sectors per track */
766
767 /* 6,7 = data bytes per sector */
768 xferp->data_in[12 + 6] = (d->logical_block_size >> 8)
769 & 255;
770 xferp->data_in[12 + 7] = d->logical_block_size & 255;
771
772 xferp->data_in[12 + 8] = (d->ncyls >> 8) & 255;
773 xferp->data_in[12 + 9] = d->ncyls & 255;
774
775 xferp->data_in[12 + 28] = (d->rpms >> 8) & 255;
776 xferp->data_in[12 + 29] = d->rpms & 255;
777 break;
778 default:
779 fatal("[ MODE_SENSE for page %i is not yet "
780 "implemented! ]\n", pagecode);
781 }
782
783 break;
784
785 case SCSICMD_READ:
786 case SCSICMD_READ_10:
787 debug("READ");
788
789 /*
790 * For tape devices, read data at the current position.
791 * For disk and CDROM devices, the command bytes contain
792 * an offset telling us where to read from the device.
793 */
794
795 if (d->is_a_tape) {
796 /* bits 7..5 of cmd[1] are the LUN bits... TODO */
797
798 size = (xferp->cmd[2] << 16) +
799 (xferp->cmd[3] << 8) +
800 xferp->cmd[4];
801
802 /* Bit 1 of cmd[1] is the SILI bit (TODO), and
803 bit 0 is the "use fixed length" bit. */
804
805 if (xferp->cmd[1] & 0x01) {
806 /* Fixed block length: */
807 size *= d->logical_block_size;
808 }
809
810 if (d->filemark) {
811 /* At end of file, switch to the next
812 automagically: */
813 d->tape_filenr ++;
814 diskimage__switch_tape(d);
815
816 d->filemark = 0;
817 }
818
819 ofs = d->tape_offset;
820
821 fatal("[ READ tape, id=%i file=%i, cmd[1]=%02x size=%i"
822 ", ofs=%lli ]\n", scsi_id, d->tape_filenr,
823 xferp->cmd[1], (int)size, (long long)ofs);
824 } else {
825 if (xferp->cmd[0] == SCSICMD_READ) {
826 if (xferp->cmd_len != 6)
827 debug(" (weird len=%i)",
828 xferp->cmd_len);
829
830 /*
831 * bits 4..0 of cmd[1], and cmd[2] and cmd[3]
832 * hold the logical block address.
833 *
834 * cmd[4] holds the number of logical blocks
835 * to transfer. (Special case if the value is
836 * 0, actually means 256.)
837 */
838 ofs = ((xferp->cmd[1] & 0x1f) << 16) +
839 (xferp->cmd[2] << 8) + xferp->cmd[3];
840 retlen = xferp->cmd[4];
841 if (retlen == 0)
842 retlen = 256;
843 } else {
844 if (xferp->cmd_len != 10)
845 debug(" (weird len=%i)",
846 xferp->cmd_len);
847
848 /*
849 * cmd[2..5] hold the logical block address.
850 * cmd[7..8] holds the number of logical
851 * blocks to transfer. (NOTE: If the value is
852 * 0, this means 0, not 65536. :-)
853 */
854 ofs = (xferp->cmd[2] << 24) + (xferp->cmd[3]
855 << 16) + (xferp->cmd[4] << 8) +
856 xferp->cmd[5];
857 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
858 }
859
860 size = retlen * d->logical_block_size;
861 ofs *= d->logical_block_size;
862 }
863
864 /* Return data: */
865 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
866 size, 0);
867
868 debug(" READ ofs=%lli size=%i\n", (long long)ofs, (int)size);
869
870 diskimage__return_default_status_and_message(xferp);
871
872 d->filemark = 0;
873
874 /*
875 * Failure? Then set check condition.
876 * For tapes, error should only occur at the end of a file.
877 *
878 * "If the logical unit encounters a filemark during
879 * a READ command, CHECK CONDITION status shall be
880 * returned and the filemark and valid bits shall be
881 * set to one in the sense data. The sense key shall
882 * be set to NO SENSE"..
883 */
884 if (d->is_a_tape && d->f != NULL && feof(d->f)) {
885 debug(" feof id=%i\n", scsi_id);
886 xferp->status[0] = 0x02; /* CHECK CONDITION */
887
888 d->filemark = 1;
889 } else
890 diskimage__internal_access(d, 0, ofs,
891 xferp->data_in, size);
892
893 if (d->is_a_tape && d->f != NULL)
894 d->tape_offset = ftell(d->f);
895
896 /* TODO: other errors? */
897 break;
898
899 case SCSICMD_WRITE:
900 case SCSICMD_WRITE_10:
901 debug("WRITE");
902
903 /* TODO: tape */
904
905 if (xferp->cmd[0] == SCSICMD_WRITE) {
906 if (xferp->cmd_len != 6)
907 debug(" (weird len=%i)", xferp->cmd_len);
908
909 /*
910 * bits 4..0 of cmd[1], and cmd[2] and cmd[3] hold the
911 * logical block address.
912 *
913 * cmd[4] holds the number of logical blocks to
914 * transfer. (Special case if the value is 0, actually
915 * means 256.)
916 */
917 ofs = ((xferp->cmd[1] & 0x1f) << 16) +
918 (xferp->cmd[2] << 8) + xferp->cmd[3];
919 retlen = xferp->cmd[4];
920 if (retlen == 0)
921 retlen = 256;
922 } else {
923 if (xferp->cmd_len != 10)
924 debug(" (weird len=%i)", xferp->cmd_len);
925
926 /*
927 * cmd[2..5] hold the logical block address.
928 * cmd[7..8] holds the number of logical blocks to
929 * transfer. (NOTE: If the value is 0 this means 0,
930 * not 65536.)
931 */
932 ofs = (xferp->cmd[2] << 24) + (xferp->cmd[3] << 16) +
933 (xferp->cmd[4] << 8) + xferp->cmd[5];
934 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
935 }
936
937 size = retlen * d->logical_block_size;
938 ofs *= d->logical_block_size;
939
940 if (xferp->data_out_offset != size) {
941 debug(", data_out == NULL, wanting %i bytes, \n\n",
942 (int)size);
943 xferp->data_out_len = size;
944 return 2;
945 }
946
947 debug(", data_out != NULL, OK :-)");
948
949 debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs,
950 (int)size, (int)xferp->data_out_offset);
951
952 diskimage__internal_access(d, 1, ofs,
953 xferp->data_out, size);
954
955 /* TODO: how about return code? */
956
957 /* Is this really necessary? */
958 /* fsync(fileno(d->f)); */
959
960 diskimage__return_default_status_and_message(xferp);
961 break;
962
963 case SCSICMD_SYNCHRONIZE_CACHE:
964 debug("SYNCHRONIZE_CACHE");
965
966 if (xferp->cmd_len != 10)
967 debug(" (weird len=%i)", xferp->cmd_len);
968
969 /* TODO: actualy care about cmd[] */
970 fsync(fileno(d->f));
971
972 diskimage__return_default_status_and_message(xferp);
973 break;
974
975 case SCSICMD_START_STOP_UNIT:
976 debug("START_STOP_UNIT");
977
978 if (xferp->cmd_len != 6)
979 debug(" (weird len=%i)", xferp->cmd_len);
980
981 for (i=0; i<xferp->cmd_len ; i++)
982 debug(" %02x", xferp->cmd[i]);
983
984 /* TODO: actualy care about cmd[] */
985
986 diskimage__return_default_status_and_message(xferp);
987 break;
988
989 case SCSICMD_REQUEST_SENSE:
990 debug("REQUEST_SENSE");
991
992 retlen = xferp->cmd[4];
993
994 /* TODO: bits 765 of buf[1] contains the LUN */
995 if (xferp->cmd[1] != 0x00)
996 fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not"
997 " yet implemented\n", (int)xferp->cmd[1]);
998
999 if (retlen < 18) {
1000 fatal("WARNING: SCSI request sense len=%i, <18!\n",
1001 (int)retlen);
1002 retlen = 18;
1003 }
1004
1005 /* Return data: */
1006 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1007 retlen, 1);
1008
1009 xferp->data_in[0] = 0x80 + 0x70;/* 0x80 = valid,
1010 0x70 = "current errors" */
1011 xferp->data_in[2] = 0x00; /* SENSE KEY! */
1012
1013 if (d->filemark) {
1014 xferp->data_in[2] = 0x80;
1015 }
1016 debug(": [2]=0x%02x ", xferp->data_in[2]);
1017
1018 printf(" XXX(!) \n");
1019
1020 /* TODO */
1021 xferp->data_in[7] = retlen - 7; /* additional sense length */
1022 /* TODO */
1023
1024 diskimage__return_default_status_and_message(xferp);
1025 break;
1026
1027 case SCSICMD_READ_BLOCK_LIMITS:
1028 debug("READ_BLOCK_LIMITS");
1029
1030 retlen = 6;
1031
1032 /* TODO: bits 765 of buf[1] contains the LUN */
1033 if (xferp->cmd[1] != 0x00)
1034 fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]="
1035 "0x%02x not yet implemented\n", (int)xferp->cmd[1]);
1036
1037 /* Return data: */
1038 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1039 retlen, 1);
1040
1041 /*
1042 * data[0] is reserved, data[1..3] contain the maximum block
1043 * length limit, data[4..5] contain the minimum limit.
1044 */
1045
1046 {
1047 int max_limit = 32768;
1048 int min_limit = 128;
1049
1050 xferp->data_in[1] = (max_limit >> 16) & 255;
1051 xferp->data_in[2] = (max_limit >> 8) & 255;
1052 xferp->data_in[3] = max_limit & 255;
1053 xferp->data_in[4] = (min_limit >> 8) & 255;
1054 xferp->data_in[5] = min_limit & 255;
1055 }
1056
1057 diskimage__return_default_status_and_message(xferp);
1058 break;
1059
1060 case SCSICMD_REWIND:
1061 debug("REWIND");
1062
1063 /* TODO: bits 765 of buf[1] contains the LUN */
1064 if ((xferp->cmd[1] & 0xe0) != 0x00)
1065 fatal("WARNING: REWIND with cmd[1]=0x%02x not yet "
1066 "implemented\n", (int)xferp->cmd[1]);
1067
1068 /* Close and reopen. */
1069
1070 if (d->f != NULL)
1071 fclose(d->f);
1072
1073 d->f = fopen(d->fname, d->writable? "r+" : "r");
1074 if (d->f == NULL) {
1075 fprintf(stderr, "[ diskimage: could not (re)open "
1076 "'%s' ]\n", d->fname);
1077 /* TODO: return error */
1078 }
1079
1080 d->tape_offset = 0;
1081 d->tape_filenr = 0;
1082 d->filemark = 0;
1083
1084 diskimage__return_default_status_and_message(xferp);
1085 break;
1086
1087 case SCSICMD_SPACE:
1088 debug("SPACE");
1089
1090 /* TODO: bits 765 of buf[1] contains the LUN */
1091 if ((xferp->cmd[1] & 0xe0) != 0x00)
1092 fatal("WARNING: SPACE with cmd[1]=0x%02x not yet "
1093 "implemented\n", (int)xferp->cmd[1]);
1094
1095 /*
1096 * Bits 2..0 of buf[1] contain the 'code' which describes how
1097 * we should space, and buf[2..4] contain the number of
1098 * operations.
1099 */
1100 debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n",
1101 xferp->cmd[0],
1102 xferp->cmd[1],
1103 xferp->cmd[2],
1104 xferp->cmd[3],
1105 xferp->cmd[4],
1106 xferp->cmd[5]);
1107
1108 switch (xferp->cmd[1] & 7) {
1109 case 1: /* Seek to a different file nr: */
1110 {
1111 int diff = (xferp->cmd[2] << 16) +
1112 (xferp->cmd[3] << 8) + xferp->cmd[4];
1113
1114 /* Negative seek offset: */
1115 if (diff & (1 << 23))
1116 diff = - (16777216 - diff);
1117
1118 d->tape_filenr += diff;
1119 }
1120
1121 /* At end of file, switch to the next tape file: */
1122 if (d->filemark) {
1123 d->tape_filenr ++;
1124 d->filemark = 0;
1125 }
1126
1127 debug("{ switching to tape file %i }", d->tape_filenr);
1128 diskimage__switch_tape(d);
1129 d->filemark = 0;
1130 break;
1131 default:
1132 fatal("[ diskimage.c: unimplemented SPACE type %i ]\n",
1133 xferp->cmd[1] & 7);
1134 }
1135
1136 diskimage__return_default_status_and_message(xferp);
1137 break;
1138
1139 case SCSICDROM_READ_SUBCHANNEL:
1140 /*
1141 * According to
1142 * http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html:
1143 *
1144 * "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT
1145 * commands have the same opcode in SCSI or ATAPI, but don't
1146 * have the same command structure"...
1147 *
1148 * TODO: This still doesn't work. Hm.
1149 */
1150 retlen = 48;
1151
1152 debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x",
1153 xferp->cmd[1]);
1154
1155 /* Return data: */
1156 scsi_transfer_allocbuf(&xferp->data_in_len,
1157 &xferp->data_in, retlen, 1);
1158
1159 diskimage_recalc_size(d);
1160
1161 size = d->total_size / d->logical_block_size;
1162 if (d->total_size & (d->logical_block_size-1))
1163 size ++;
1164
1165 xferp->data_in[0] = (size >> 24) & 255;
1166 xferp->data_in[1] = (size >> 16) & 255;
1167 xferp->data_in[2] = (size >> 8) & 255;
1168 xferp->data_in[3] = size & 255;
1169
1170 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
1171 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
1172 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
1173 xferp->data_in[7] = d->logical_block_size & 255;
1174
1175 diskimage__return_default_status_and_message(xferp);
1176 break;
1177
1178 case SCSICDROM_READ_TOC:
1179 debug("(CDROM_READ_TOC: ");
1180 debug("lun=%i msf=%i ",
1181 xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1);
1182 debug("starting_track=%i ", xferp->cmd[6]);
1183 retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
1184 debug("allocation_len=%i)\n", retlen);
1185
1186 /* Return data: */
1187 scsi_transfer_allocbuf(&xferp->data_in_len,
1188 &xferp->data_in, retlen, 1);
1189
1190 /* TODO */
1191
1192 diskimage__return_default_status_and_message(xferp);
1193 break;
1194
1195 case SCSICMD_MODE_SELECT:
1196 debug("[ SCSI MODE_SELECT: ");
1197
1198 /*
1199 * TODO:
1200 *
1201 * This is super-hardcoded for NetBSD's usage of mode_select
1202 * to set the size of CDROM sectors to 2048.
1203 */
1204
1205 if (xferp->data_out_offset == 0) {
1206 xferp->data_out_len = 12; /* TODO */
1207 debug("data_out == NULL, wanting %i bytes ]\n",
1208 (int)xferp->data_out_len);
1209 return 2;
1210 }
1211
1212 debug("data_out!=NULL (OK), ");
1213
1214 /* TODO: Care about cmd? */
1215
1216 /* Set sector size to 2048: */
1217 /* 00 05 00 08 00 03 ca 40 00 00 08 00 */
1218 if (xferp->data_out[0] == 0x00 &&
1219 xferp->data_out[1] == 0x05 &&
1220 xferp->data_out[2] == 0x00 &&
1221 xferp->data_out[3] == 0x08) {
1222 d->logical_block_size =
1223 (xferp->data_out[9] << 16) +
1224 (xferp->data_out[10] << 8) +
1225 xferp->data_out[11];
1226 debug("[ setting logical_block_size to %i ]\n",
1227 d->logical_block_size);
1228 } else {
1229 int i;
1230 fatal("[ unknown MODE_SELECT: cmd =");
1231 for (i=0; i<xferp->cmd_len; i++)
1232 fatal(" %02x", xferp->cmd[i]);
1233 fatal(", data_out =");
1234 for (i=0; i<xferp->data_out_len; i++)
1235 fatal(" %02x", xferp->data_out[i]);
1236 fatal(" ]");
1237 }
1238
1239 debug(" ]\n");
1240 diskimage__return_default_status_and_message(xferp);
1241 break;
1242
1243 case 0x1e:
1244 debug("[ SCSI 0x%02x: TODO ]\n", xferp->cmd[0]);
1245
1246 /* TODO */
1247
1248 diskimage__return_default_status_and_message(xferp);
1249 break;
1250
1251 case 0xbd:
1252 fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0],
1253 xferp->cmd_len);
1254 for (i=0; i<xferp->cmd_len; i++)
1255 fatal(" %02x", xferp->cmd[i]);
1256 fatal(" ]\n");
1257
1258 /*
1259 * Used by Windows NT?
1260 *
1261 * Not documented in http://www.danbbs.dk/~dino/
1262 * SCSI/SCSI2-D.html.
1263 * Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm.
1264 */
1265
1266 if (xferp->cmd_len < 12) {
1267 fatal("WEIRD LEN?\n");
1268 retlen = 8;
1269 } else {
1270 retlen = xferp->cmd[8] * 256 + xferp->cmd[9];
1271 }
1272
1273 /* Return data: */
1274 scsi_transfer_allocbuf(&xferp->data_in_len,
1275 &xferp->data_in, retlen, 1);
1276
1277 diskimage__return_default_status_and_message(xferp);
1278 break;
1279
1280 default:
1281 fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1282 xferp->cmd[0], scsi_id);
1283 exit(1);
1284 }
1285 debug(" ]\n");
1286
1287 return 1;
1288 }
1289
1290
1291 /*
1292 * diskimage_access():
1293 *
1294 * Read from or write to a disk image on a machine.
1295 *
1296 * Returns 1 if the access completed successfully, 0 otherwise.
1297 */
1298 int diskimage_access(struct machine *machine, int scsi_id, int writeflag,
1299 off_t offset, unsigned char *buf, size_t len)
1300 {
1301 struct diskimage *d = machine->first_diskimage;
1302
1303 while (d != NULL) {
1304 if (d->type == DISKIMAGE_SCSI && d->id == scsi_id)
1305 break;
1306 d = d->next;
1307 }
1308
1309 if (d == NULL) {
1310 fatal("[ diskimage_access(): ERROR: trying to access a "
1311 "non-existant SCSI disk image (%i)\n", scsi_id);
1312 return 0;
1313 }
1314
1315 return diskimage__internal_access(d, writeflag, offset, buf, len);
1316 }
1317
1318
1319 /*
1320 * diskimage_add():
1321 *
1322 * Add a disk image. fname is the filename of the disk image.
1323 * The filename may be prefixed with one or more modifiers, followed
1324 * by a colon.
1325 *
1326 * b specifies that this is the boot device
1327 * c CD-ROM (instead of normal SCSI DISK)
1328 * d SCSI DISK (this is the default)
1329 * i IDE (instead of SCSI)
1330 * r read-only (don't allow changes to the file)
1331 * t SCSI tape
1332 * 0-7 force a specific SCSI ID number
1333 *
1334 * machine is assumed to be non-NULL.
1335 * Returns an integer >= 0 identifying the disk image.
1336 */
1337 int diskimage_add(struct machine *machine, char *fname)
1338 {
1339 struct diskimage *d, *d2;
1340 int id = 0;
1341 char *cp;
1342 int prefix_b = 0;
1343 int prefix_c = 0;
1344 int prefix_d = 0;
1345 int prefix_i = 0;
1346 int prefix_t = 0;
1347 int prefix_id = -1;
1348 int prefix_r = 0;
1349
1350 if (fname == NULL) {
1351 fprintf(stderr, "diskimage_add(): NULL ptr\n");
1352 return 0;
1353 }
1354
1355 /* Get prefix from fname: */
1356 cp = strchr(fname, ':');
1357 if (cp != NULL) {
1358 while (fname <= cp) {
1359 char c = *fname++;
1360 switch (c) {
1361 case '0':
1362 case '1':
1363 case '2':
1364 case '3':
1365 case '4':
1366 case '5':
1367 case '6':
1368 case '7':
1369 prefix_id = c - '0';
1370 break;
1371 case 'b':
1372 prefix_b = 1;
1373 break;
1374 case 'c':
1375 prefix_c = 1;
1376 break;
1377 case 'd':
1378 prefix_d = 1;
1379 break;
1380 case 'i':
1381 prefix_i = 1;
1382 break;
1383 case 't':
1384 prefix_t = 1;
1385 break;
1386 case 'r':
1387 prefix_r = 1;
1388 break;
1389 case ':':
1390 break;
1391 default:
1392 fprintf(stderr, "diskimage_add(): invalid "
1393 "prefix char '%c'\n", c);
1394 exit(1);
1395 }
1396 }
1397 }
1398
1399 /* Calculate which ID to use: */
1400 if (prefix_id == -1) {
1401 int free = 0, collision = 1;
1402
1403 while (collision) {
1404 collision = 0;
1405 d = machine->first_diskimage;
1406 while (d != NULL) {
1407 if (d->id == free) {
1408 collision = 1;
1409 break;
1410 }
1411 d = d->next;
1412 }
1413 if (!collision)
1414 id = free;
1415 else
1416 free ++;
1417 }
1418 } else {
1419 id = prefix_id;
1420
1421 d = machine->first_diskimage;
1422 while (d != NULL) {
1423 if (d->id == id) {
1424 fprintf(stderr, "disk image SCSI id %i "
1425 "already in use\n", id);
1426 exit(1);
1427 }
1428 d = d->next;
1429 }
1430 }
1431
1432 /* Allocate a new diskimage struct: */
1433 d = malloc(sizeof(struct diskimage));
1434 if (d == NULL) {
1435 fprintf(stderr, "out of memory in diskimage_add()\n");
1436 exit(1);
1437 }
1438 memset(d, 0, sizeof(struct diskimage));
1439
1440 d2 = machine->first_diskimage;
1441 if (d2 == NULL) {
1442 machine->first_diskimage = d;
1443 } else {
1444 while (d2->next != NULL)
1445 d2 = d2->next;
1446 d2->next = d;
1447 }
1448
1449 d->type = DISKIMAGE_SCSI;
1450 d->id = id;
1451
1452 if (prefix_i)
1453 d->type = DISKIMAGE_IDE;
1454
1455 d->fname = strdup(fname);
1456 if (d->fname == NULL) {
1457 fprintf(stderr, "out of memory\n");
1458 exit(1);
1459 }
1460
1461 d->logical_block_size = 512;
1462
1463 /*
1464 * Is this a tape, CD-ROM or a normal disk?
1465 *
1466 * An intelligent guess, if no prefixes are used, would be that
1467 * filenames ending with .iso are CD-ROM images.
1468 */
1469 if (prefix_t) {
1470 d->is_a_tape = 1;
1471 } else {
1472 if (prefix_c ||
1473 ((strlen(d->fname) > 4 &&
1474 strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0)
1475 && !prefix_d)
1476 ) {
1477 d->is_a_cdrom = 1;
1478
1479 /*
1480 * This is tricky. Should I use 512 or 2048 here?
1481 * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
1482 * per sector, but NetBSD 2.0_BETA suddenly ignores
1483 * this value and uses 2048 instead.
1484 *
1485 * OpenBSD/arc doesn't like 2048, it requires 512
1486 * to work correctly.
1487 *
1488 * TODO
1489 */
1490
1491 #if 0
1492 if (machine->machine_type == MACHINE_DEC)
1493 d->logical_block_size = 512;
1494 else
1495 d->logical_block_size = 2048;
1496 #endif
1497 d->logical_block_size = 512;
1498 }
1499 }
1500
1501 diskimage_recalc_size(d);
1502
1503 d->rpms = 3600;
1504
1505 if (prefix_b)
1506 d->is_boot_device = 1;
1507
1508 d->writable = access(fname, W_OK) == 0? 1 : 0;
1509
1510 if (d->is_a_cdrom || prefix_r)
1511 d->writable = 0;
1512
1513 d->f = fopen(fname, d->writable? "r+" : "r");
1514 if (d->f == NULL) {
1515 perror(fname);
1516 exit(1);
1517 }
1518
1519 return id;
1520 }
1521
1522
1523 /*
1524 * diskimage_bootdev():
1525 *
1526 * Returns the disk id (0..7) of the device which we're booting from.
1527 *
1528 * If no disk was used as boot device, then -1 is returned. (In practice,
1529 * this is used to fake network (tftp) boot.)
1530 */
1531 int diskimage_bootdev(struct machine *machine)
1532 {
1533 struct diskimage *d = machine->first_diskimage;
1534 while (d != NULL) {
1535 if (d->is_boot_device)
1536 return d->id;
1537 d = d->next;
1538 }
1539
1540 d = machine->first_diskimage;
1541 if (d != NULL)
1542 return d->id;
1543
1544 return -1;
1545 }
1546
1547
1548 /*
1549 * diskimage_is_a_cdrom():
1550 *
1551 * Returns 1 if a disk image is a SCSI CDROM, 0 otherwise.
1552 */
1553 int diskimage_is_a_cdrom(struct machine *machine, int scsi_id)
1554 {
1555 struct diskimage *d = machine->first_diskimage;
1556
1557 while (d != NULL) {
1558 if (d->type == DISKIMAGE_SCSI && d->id == scsi_id)
1559 return d->is_a_cdrom;
1560 d = d->next;
1561 }
1562 return 0;
1563 }
1564
1565
1566 /*
1567 * diskimage_is_a_tape():
1568 *
1569 * Returns 1 if a disk image is a SCSI tape, 0 otherwise.
1570 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1571 * boot strings.)
1572 */
1573 int diskimage_is_a_tape(struct machine *machine, int scsi_id)
1574 {
1575 struct diskimage *d = machine->first_diskimage;
1576
1577 while (d != NULL) {
1578 if (d->type == DISKIMAGE_SCSI && d->id == scsi_id)
1579 return d->is_a_tape;
1580 d = d->next;
1581 }
1582 return 0;
1583 }
1584
1585
1586 /*
1587 * diskimage_dump_info():
1588 *
1589 * Debug dump of all diskimages that are loaded for a specific machine.
1590 */
1591 void diskimage_dump_info(struct machine *machine)
1592 {
1593 int iadd=4;
1594 struct diskimage *d = machine->first_diskimage;
1595
1596 while (d != NULL) {
1597 debug("diskimage: %s\n", d->fname);
1598 debug_indentation(iadd);
1599
1600 switch (d->type) {
1601 case DISKIMAGE_SCSI:
1602 debug("SCSI");
1603 break;
1604 case DISKIMAGE_IDE:
1605 debug("IDE");
1606 break;
1607 default:
1608 debug("UNKNOWN type %i", d->type);
1609 }
1610
1611 debug(" %s", d->is_a_tape? "TAPE" :
1612 (d->is_a_cdrom? "CD-ROM" : "DISK"));
1613 debug(" id %i, ", d->id);
1614 debug("%s, ", d->writable? "read/write" : "read-only");
1615
1616 debug("%lli MB (%lli sectors)%s\n",
1617 (long long) (d->total_size / 1048576),
1618 (long long) (d->total_size / 512),
1619 d->is_boot_device? " (BOOT)" : "");
1620
1621 debug_indentation(-iadd);
1622
1623 d = d->next;
1624 }
1625 }
1626

  ViewVC Help
Powered by ViewVC 1.1.26