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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 33 - (show annotations)
Mon Oct 8 16:21:06 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 46422 byte(s)
0.4.3
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.114 2006/09/07 11:44:01 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 #ifdef UNSTABLE_DEVEL
455 fatal("[ diskimage__internal_access(): disk_id %i, offset %lli"
456 ", transfer not completed. len=%i, len_done=%i ]\n",
457 d->id, (long long)offset, (int)len, (int)lendone);
458 #endif
459 return 0;
460 }
461
462 return 1;
463 }
464
465
466 /*
467 * diskimage_scsicommand():
468 *
469 * Perform a SCSI command on a disk image.
470 *
471 * The xferp points to a scsi_transfer struct, containing msg_out, command,
472 * and data_out coming from the SCSI controller device. This function
473 * interprets the command, and (if necessary) creates responses in
474 * data_in, msg_in, and status.
475 *
476 * Returns:
477 * 2 if the command expects data from the DATA_OUT phase,
478 * 1 if otherwise ok,
479 * 0 on error.
480 */
481 int diskimage_scsicommand(struct cpu *cpu, int id, int type,
482 struct scsi_transfer *xferp)
483 {
484 char namebuf[16];
485 int retlen, i, q;
486 uint64_t size;
487 int64_t ofs;
488 int pagecode;
489 struct machine *machine = cpu->machine;
490 struct diskimage *d;
491
492 if (machine == NULL) {
493 fatal("[ diskimage_scsicommand(): machine == NULL ]\n");
494 return 0;
495 }
496
497 d = machine->first_diskimage;
498 while (d != NULL) {
499 if (d->type == type && d->id == id)
500 break;
501 d = d->next;
502 }
503 if (d == NULL) {
504 fprintf(stderr, "[ diskimage_scsicommand(): %s "
505 " id %i not connected? ]\n", diskimage_types[type], id);
506 }
507
508 if (xferp->cmd == NULL) {
509 fatal("[ diskimage_scsicommand(): cmd == NULL ]\n");
510 return 0;
511 }
512
513 if (xferp->cmd_len < 1) {
514 fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n",
515 xferp->cmd_len);
516 return 0;
517 }
518
519 debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ",
520 id, xferp->cmd[0]);
521
522 #if 0
523 fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",
524 id, xferp->cmd[0], xferp->cmd_len);
525 for (i=0; i<xferp->cmd_len; i++)
526 fatal(" %02x", xferp->cmd[i]);
527 fatal("\n");
528 if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11)
529 single_step = ENTER_SINGLE_STEPPING;
530 #endif
531
532 #if 0
533 {
534 static FILE *f = NULL;
535 if (f == NULL)
536 f = fopen("scsi_log.txt", "w");
537 if (f != NULL) {
538 int i;
539 fprintf(f, "id=%i cmd =", id);
540 for (i=0; i<xferp->cmd_len; i++)
541 fprintf(f, " %02x", xferp->cmd[i]);
542 fprintf(f, "\n");
543 fflush(f);
544 }
545 }
546 #endif
547
548 switch (xferp->cmd[0]) {
549
550 case SCSICMD_TEST_UNIT_READY:
551 debug("TEST_UNIT_READY");
552 if (xferp->cmd_len != 6)
553 debug(" (weird len=%i)", xferp->cmd_len);
554
555 /* TODO: bits 765 of buf[1] contains the LUN */
556 if (xferp->cmd[1] != 0x00)
557 fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x"
558 " not yet implemented\n", (int)xferp->cmd[1]);
559
560 diskimage__return_default_status_and_message(xferp);
561 break;
562
563 case SCSICMD_INQUIRY:
564 debug("INQUIRY");
565 if (xferp->cmd_len != 6)
566 debug(" (weird len=%i)", xferp->cmd_len);
567 if (xferp->cmd[1] != 0x00) {
568 debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet "
569 "implemented\n", (int)xferp->cmd[1]);
570
571 break;
572 }
573
574 /* Return values: */
575 retlen = xferp->cmd[4];
576 if (retlen < 36) {
577 fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen);
578 retlen = 36;
579 }
580
581 /* Return data: */
582 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
583 retlen, 1);
584 xferp->data_in[0] = 0x00; /* 0x00 = Direct-access disk */
585 xferp->data_in[1] = 0x00; /* 0x00 = non-removable */
586 xferp->data_in[2] = 0x02; /* SCSI-2 */
587 #if 0
588 xferp->data_in[3] = 0x02; /* Response data format = SCSI-2 */
589 #endif
590 xferp->data_in[4] = retlen - 4; /* Additional length */
591 xferp->data_in[4] = 0x2c - 4; /* Additional length */
592 xferp->data_in[6] = 0x04; /* ACKREQQ */
593 xferp->data_in[7] = 0x60; /* WBus32, WBus16 */
594
595 /* These are padded with spaces: */
596
597 memcpy(xferp->data_in+8, "GXemul ", 8);
598 if (diskimage_getname(cpu->machine, id,
599 type, namebuf, sizeof(namebuf))) {
600 size_t i;
601 for (i=0; i<sizeof(namebuf); i++)
602 if (namebuf[i] == 0) {
603 for (; i<sizeof(namebuf); i++)
604 namebuf[i] = ' ';
605 break;
606 }
607 memcpy(xferp->data_in+16, namebuf, 16);
608 } else
609 memcpy(xferp->data_in+16, "DISK ", 16);
610 memcpy(xferp->data_in+32, "0 ", 4);
611
612 /*
613 * Some Ultrix kernels want specific responses from
614 * the drives.
615 */
616
617 if (machine->machine_type == MACHINE_PMAX) {
618 /* DEC, RZ25 (rev 0900) = 832527 sectors */
619 /* DEC, RZ58 (rev 2000) = 2698061 sectors */
620 memcpy(xferp->data_in+8, "DEC ", 8);
621 memcpy(xferp->data_in+16, "RZ58 (C) DEC", 16);
622 memcpy(xferp->data_in+32, "2000", 4);
623 }
624
625 /* Some data is different for CD-ROM drives: */
626 if (d->is_a_cdrom) {
627 xferp->data_in[0] = 0x05; /* 0x05 = CD-ROM */
628 xferp->data_in[1] = 0x80; /* 0x80 = removable */
629 /* memcpy(xferp->data_in+16, "CD-ROM ", 16);*/
630
631 if (machine->machine_type == MACHINE_PMAX) {
632 /* SONY, CD-ROM: */
633 memcpy(xferp->data_in+8, "SONY ", 8);
634 memcpy(xferp->data_in+16,
635 "CD-ROM ", 16);
636
637 /* ... or perhaps this: */
638 memcpy(xferp->data_in+8, "DEC ", 8);
639 memcpy(xferp->data_in+16,
640 "RRD42 (C) DEC ", 16);
641 memcpy(xferp->data_in+32, "4.5d", 4);
642 } else if (machine->machine_type == MACHINE_ARC) {
643 /* NEC, CD-ROM: */
644 memcpy(xferp->data_in+8, "NEC ", 8);
645 memcpy(xferp->data_in+16,
646 "CD-ROM CDR-210P ", 16);
647 memcpy(xferp->data_in+32, "1.0 ", 4);
648 }
649 }
650
651 /* Data for tape devices: */
652 if (d->is_a_tape) {
653 xferp->data_in[0] = 0x01; /* 0x01 = tape */
654 xferp->data_in[1] = 0x80; /* 0x80 = removable */
655 memcpy(xferp->data_in+16, "TAPE ", 16);
656
657 if (machine->machine_type == MACHINE_PMAX) {
658 /*
659 * TODO: find out if these are correct.
660 *
661 * The name might be TZK10, TSZ07, or TLZ04,
662 * or something completely different.
663 */
664 memcpy(xferp->data_in+8, "DEC ", 8);
665 memcpy(xferp->data_in+16,
666 "TK50 (C) DEC", 16);
667 memcpy(xferp->data_in+32, "2000", 4);
668 }
669 }
670
671 diskimage__return_default_status_and_message(xferp);
672 break;
673
674 case SCSIBLOCKCMD_READ_CAPACITY:
675 debug("READ_CAPACITY");
676
677 if (xferp->cmd_len != 10)
678 fatal(" [ weird READ_CAPACITY len=%i, should be 10 ] ",
679 xferp->cmd_len);
680 else {
681 if (xferp->cmd[8] & 1) {
682 /* Partial Medium Indicator bit... TODO */
683 fatal("WARNING: READ_CAPACITY with PMI bit"
684 " set not yet implemented\n");
685 }
686 }
687
688 /* Return data: */
689 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
690 8, 1);
691
692 diskimage_recalc_size(d);
693
694 size = d->total_size / d->logical_block_size;
695 if (d->total_size & (d->logical_block_size-1))
696 size ++;
697
698 xferp->data_in[0] = (size >> 24) & 255;
699 xferp->data_in[1] = (size >> 16) & 255;
700 xferp->data_in[2] = (size >> 8) & 255;
701 xferp->data_in[3] = size & 255;
702
703 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
704 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
705 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
706 xferp->data_in[7] = d->logical_block_size & 255;
707
708 diskimage__return_default_status_and_message(xferp);
709 break;
710
711 case SCSICMD_MODE_SENSE:
712 case SCSICMD_MODE_SENSE10:
713 debug("MODE_SENSE");
714 q = 4; retlen = xferp->cmd[4];
715 switch (xferp->cmd_len) {
716 case 6: break;
717 case 10:q = 8;
718 retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
719 break;
720 default:fatal(" (unimplemented mode_sense len=%i)",
721 xferp->cmd_len);
722 }
723
724 /*
725 * NOTE/TODO: This code doesn't handle too short retlens
726 * very well. A quick hack around this is that I allocate
727 * a bit too much memory, so that nothing is actually
728 * written outside of xferp->data_in[].
729 */
730
731 retlen += 100; /* Should be enough. (Ugly.) */
732
733 if ((xferp->cmd[2] & 0xc0) != 0)
734 fatal("WARNING: mode sense, cmd[2] = 0x%02x\n",
735 xferp->cmd[2]);
736
737 /* Return data: */
738 scsi_transfer_allocbuf(&xferp->data_in_len,
739 &xferp->data_in, retlen, 1);
740
741 xferp->data_in_len -= 100; /* Restore size. */
742
743 pagecode = xferp->cmd[2] & 0x3f;
744
745 debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode);
746
747 /* 4 bytes of header for 6-byte command,
748 8 bytes of header for 10-byte command. */
749 xferp->data_in[0] = retlen; /* 0: mode data length */
750 xferp->data_in[1] = d->is_a_cdrom? 0x05 : 0x00;
751 /* 1: medium type */
752 xferp->data_in[2] = 0x00; /* device specific
753 parameter */
754 xferp->data_in[3] = 8 * 1; /* block descriptor
755 length: 1 page (?) */
756
757 xferp->data_in[q+0] = 0x00; /* density code */
758 xferp->data_in[q+1] = 0; /* nr of blocks, high */
759 xferp->data_in[q+2] = 0; /* nr of blocks, mid */
760 xferp->data_in[q+3] = 0; /* nr of blocks, low */
761 xferp->data_in[q+4] = 0x00; /* reserved */
762 xferp->data_in[q+5] = (d->logical_block_size >> 16) & 255;
763 xferp->data_in[q+6] = (d->logical_block_size >> 8) & 255;
764 xferp->data_in[q+7] = d->logical_block_size & 255;
765 q += 8;
766
767 diskimage__return_default_status_and_message(xferp);
768
769 /* descriptors, 8 bytes (each) */
770
771 /* page, n bytes (each) */
772 switch (pagecode) {
773 case 0:
774 /* TODO: Nothing here? */
775 break;
776 case 1: /* read-write error recovery page */
777 xferp->data_in[q + 0] = pagecode;
778 xferp->data_in[q + 1] = 10;
779 break;
780 case 3: /* format device page */
781 xferp->data_in[q + 0] = pagecode;
782 xferp->data_in[q + 1] = 22;
783
784 /* 10,11 = sectors per track */
785 xferp->data_in[q + 10] = 0;
786 xferp->data_in[q + 11] = d->sectors_per_track;
787
788 /* 12,13 = physical sector size */
789 xferp->data_in[q + 12] =
790 (d->logical_block_size >> 8) & 255;
791 xferp->data_in[q + 13] = d->logical_block_size & 255;
792 break;
793 case 4: /* rigid disk geometry page */
794 xferp->data_in[q + 0] = pagecode;
795 xferp->data_in[q + 1] = 22;
796 xferp->data_in[q + 2] = (d->ncyls >> 16) & 255;
797 xferp->data_in[q + 3] = (d->ncyls >> 8) & 255;
798 xferp->data_in[q + 4] = d->ncyls & 255;
799 xferp->data_in[q + 5] = d->heads;
800
801 xferp->data_in[q + 20] = (d->rpms >> 8) & 255;
802 xferp->data_in[q + 21] = d->rpms & 255;
803 break;
804 case 5: /* flexible disk page */
805 xferp->data_in[q + 0] = pagecode;
806 xferp->data_in[q + 1] = 0x1e;
807
808 /* 2,3 = transfer rate */
809 xferp->data_in[q + 2] = ((5000) >> 8) & 255;
810 xferp->data_in[q + 3] = (5000) & 255;
811
812 xferp->data_in[q + 4] = d->heads;
813 xferp->data_in[q + 5] = d->sectors_per_track;
814
815 /* 6,7 = data bytes per sector */
816 xferp->data_in[q + 6] = (d->logical_block_size >> 8)
817 & 255;
818 xferp->data_in[q + 7] = d->logical_block_size & 255;
819
820 xferp->data_in[q + 8] = (d->ncyls >> 8) & 255;
821 xferp->data_in[q + 9] = d->ncyls & 255;
822
823 xferp->data_in[q + 28] = (d->rpms >> 8) & 255;
824 xferp->data_in[q + 29] = d->rpms & 255;
825 break;
826 default:
827 fatal("[ MODE_SENSE for page %i is not yet "
828 "implemented! ]\n", pagecode);
829 }
830
831 break;
832
833 case SCSICMD_READ:
834 case SCSICMD_READ_10:
835 debug("READ");
836
837 /*
838 * For tape devices, read data at the current position.
839 * For disk and CDROM devices, the command bytes contain
840 * an offset telling us where to read from the device.
841 */
842
843 if (d->is_a_tape) {
844 /* bits 7..5 of cmd[1] are the LUN bits... TODO */
845
846 size = (xferp->cmd[2] << 16) +
847 (xferp->cmd[3] << 8) +
848 xferp->cmd[4];
849
850 /* Bit 1 of cmd[1] is the SILI bit (TODO), and
851 bit 0 is the "use fixed length" bit. */
852
853 if (xferp->cmd[1] & 0x01) {
854 /* Fixed block length: */
855 size *= d->logical_block_size;
856 }
857
858 if (d->filemark) {
859 /* At end of file, switch to the next
860 automagically: */
861 d->tape_filenr ++;
862 diskimage__switch_tape(d);
863
864 d->filemark = 0;
865 }
866
867 ofs = d->tape_offset;
868
869 fatal("[ READ tape, id=%i file=%i, cmd[1]=%02x size=%i"
870 ", ofs=%lli ]\n", id, d->tape_filenr,
871 xferp->cmd[1], (int)size, (long long)ofs);
872 } else {
873 if (xferp->cmd[0] == SCSICMD_READ) {
874 if (xferp->cmd_len != 6)
875 debug(" (weird len=%i)",
876 xferp->cmd_len);
877
878 /*
879 * bits 4..0 of cmd[1], and cmd[2] and cmd[3]
880 * hold the logical block address.
881 *
882 * cmd[4] holds the number of logical blocks
883 * to transfer. (Special case if the value is
884 * 0, actually means 256.)
885 */
886 ofs = ((xferp->cmd[1] & 0x1f) << 16) +
887 (xferp->cmd[2] << 8) + xferp->cmd[3];
888 retlen = xferp->cmd[4];
889 if (retlen == 0)
890 retlen = 256;
891 } else {
892 if (xferp->cmd_len != 10)
893 debug(" (weird len=%i)",
894 xferp->cmd_len);
895
896 /*
897 * cmd[2..5] hold the logical block address.
898 * cmd[7..8] holds the number of logical
899 * blocks to transfer. (NOTE: If the value is
900 * 0, this means 0, not 65536. :-)
901 */
902 ofs = ((uint64_t)xferp->cmd[2] << 24) +
903 (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8)
904 + xferp->cmd[5];
905 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
906 }
907
908 size = retlen * d->logical_block_size;
909 ofs *= d->logical_block_size;
910 }
911
912 /* Return data: */
913 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
914 size, 0);
915
916 debug(" READ ofs=%lli size=%i\n", (long long)ofs, (int)size);
917
918 diskimage__return_default_status_and_message(xferp);
919
920 d->filemark = 0;
921
922 /*
923 * Failure? Then set check condition.
924 * For tapes, error should only occur at the end of a file.
925 *
926 * "If the logical unit encounters a filemark during
927 * a READ command, CHECK CONDITION status shall be
928 * returned and the filemark and valid bits shall be
929 * set to one in the sense data. The sense key shall
930 * be set to NO SENSE"..
931 */
932 if (d->is_a_tape && d->f != NULL && feof(d->f)) {
933 debug(" feof id=%i\n", id);
934 xferp->status[0] = 0x02; /* CHECK CONDITION */
935
936 d->filemark = 1;
937 } else
938 diskimage__internal_access(d, 0, ofs,
939 xferp->data_in, size);
940
941 if (d->is_a_tape && d->f != NULL)
942 d->tape_offset = ftello(d->f);
943
944 /* TODO: other errors? */
945 break;
946
947 case SCSICMD_WRITE:
948 case SCSICMD_WRITE_10:
949 debug("WRITE");
950
951 /* TODO: tape */
952
953 if (xferp->cmd[0] == SCSICMD_WRITE) {
954 if (xferp->cmd_len != 6)
955 debug(" (weird len=%i)", xferp->cmd_len);
956
957 /*
958 * bits 4..0 of cmd[1], and cmd[2] and cmd[3] hold the
959 * logical block address.
960 *
961 * cmd[4] holds the number of logical blocks to
962 * transfer. (Special case if the value is 0, actually
963 * means 256.)
964 */
965 ofs = ((xferp->cmd[1] & 0x1f) << 16) +
966 (xferp->cmd[2] << 8) + xferp->cmd[3];
967 retlen = xferp->cmd[4];
968 if (retlen == 0)
969 retlen = 256;
970 } else {
971 if (xferp->cmd_len != 10)
972 debug(" (weird len=%i)", xferp->cmd_len);
973
974 /*
975 * cmd[2..5] hold the logical block address.
976 * cmd[7..8] holds the number of logical blocks to
977 * transfer. (NOTE: If the value is 0 this means 0,
978 * not 65536.)
979 */
980 ofs = ((uint64_t)xferp->cmd[2] << 24) +
981 (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) +
982 xferp->cmd[5];
983 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
984 }
985
986 size = retlen * d->logical_block_size;
987 ofs *= d->logical_block_size;
988
989 if (xferp->data_out_offset != size) {
990 debug(", data_out == NULL, wanting %i bytes, \n\n",
991 (int)size);
992 xferp->data_out_len = size;
993 return 2;
994 }
995
996 debug(", data_out != NULL, OK :-)");
997
998 debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs,
999 (int)size, (int)xferp->data_out_offset);
1000
1001 diskimage__internal_access(d, 1, ofs,
1002 xferp->data_out, size);
1003
1004 /* TODO: how about return code? */
1005
1006 /* Is this really necessary? */
1007 /* fsync(fileno(d->f)); */
1008
1009 diskimage__return_default_status_and_message(xferp);
1010 break;
1011
1012 case SCSICMD_SYNCHRONIZE_CACHE:
1013 debug("SYNCHRONIZE_CACHE");
1014
1015 if (xferp->cmd_len != 10)
1016 debug(" (weird len=%i)", xferp->cmd_len);
1017
1018 /* TODO: actualy care about cmd[] */
1019 fsync(fileno(d->f));
1020
1021 diskimage__return_default_status_and_message(xferp);
1022 break;
1023
1024 case SCSICMD_START_STOP_UNIT:
1025 debug("START_STOP_UNIT");
1026
1027 if (xferp->cmd_len != 6)
1028 debug(" (weird len=%i)", xferp->cmd_len);
1029
1030 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1031 debug(" %02x", xferp->cmd[i]);
1032
1033 /* TODO: actualy care about cmd[] */
1034
1035 diskimage__return_default_status_and_message(xferp);
1036 break;
1037
1038 case SCSICMD_REQUEST_SENSE:
1039 debug("REQUEST_SENSE");
1040
1041 retlen = xferp->cmd[4];
1042
1043 /* TODO: bits 765 of buf[1] contains the LUN */
1044 if (xferp->cmd[1] != 0x00)
1045 fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not"
1046 " yet implemented\n", (int)xferp->cmd[1]);
1047
1048 if (retlen < 18) {
1049 fatal("WARNING: SCSI request sense len=%i, <18!\n",
1050 (int)retlen);
1051 retlen = 18;
1052 }
1053
1054 /* Return data: */
1055 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1056 retlen, 1);
1057
1058 xferp->data_in[0] = 0x80 + 0x70;/* 0x80 = valid,
1059 0x70 = "current errors" */
1060 xferp->data_in[2] = 0x00; /* SENSE KEY! */
1061
1062 if (d->filemark) {
1063 xferp->data_in[2] = 0x80;
1064 }
1065 debug(": [2]=0x%02x ", xferp->data_in[2]);
1066
1067 printf(" XXX(!) \n");
1068
1069 /* TODO */
1070 xferp->data_in[7] = retlen - 7; /* additional sense length */
1071 /* TODO */
1072
1073 diskimage__return_default_status_and_message(xferp);
1074 break;
1075
1076 case SCSICMD_READ_BLOCK_LIMITS:
1077 debug("READ_BLOCK_LIMITS");
1078
1079 retlen = 6;
1080
1081 /* TODO: bits 765 of buf[1] contains the LUN */
1082 if (xferp->cmd[1] != 0x00)
1083 fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]="
1084 "0x%02x not yet implemented\n", (int)xferp->cmd[1]);
1085
1086 /* Return data: */
1087 scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1088 retlen, 1);
1089
1090 /*
1091 * data[0] is reserved, data[1..3] contain the maximum block
1092 * length limit, data[4..5] contain the minimum limit.
1093 */
1094
1095 {
1096 int max_limit = 32768;
1097 int min_limit = 128;
1098
1099 xferp->data_in[1] = (max_limit >> 16) & 255;
1100 xferp->data_in[2] = (max_limit >> 8) & 255;
1101 xferp->data_in[3] = max_limit & 255;
1102 xferp->data_in[4] = (min_limit >> 8) & 255;
1103 xferp->data_in[5] = min_limit & 255;
1104 }
1105
1106 diskimage__return_default_status_and_message(xferp);
1107 break;
1108
1109 case SCSICMD_REWIND:
1110 debug("REWIND");
1111
1112 /* TODO: bits 765 of buf[1] contains the LUN */
1113 if ((xferp->cmd[1] & 0xe0) != 0x00)
1114 fatal("WARNING: REWIND with cmd[1]=0x%02x not yet "
1115 "implemented\n", (int)xferp->cmd[1]);
1116
1117 /* Close and reopen. */
1118
1119 if (d->f != NULL)
1120 fclose(d->f);
1121
1122 d->f = fopen(d->fname, d->writable? "r+" : "r");
1123 if (d->f == NULL) {
1124 fprintf(stderr, "[ diskimage: could not (re)open "
1125 "'%s' ]\n", d->fname);
1126 /* TODO: return error */
1127 }
1128
1129 d->tape_offset = 0;
1130 d->tape_filenr = 0;
1131 d->filemark = 0;
1132
1133 diskimage__return_default_status_and_message(xferp);
1134 break;
1135
1136 case SCSICMD_SPACE:
1137 debug("SPACE");
1138
1139 /* TODO: bits 765 of buf[1] contains the LUN */
1140 if ((xferp->cmd[1] & 0xe0) != 0x00)
1141 fatal("WARNING: SPACE with cmd[1]=0x%02x not yet "
1142 "implemented\n", (int)xferp->cmd[1]);
1143
1144 /*
1145 * Bits 2..0 of buf[1] contain the 'code' which describes how
1146 * spacing should be done, and buf[2..4] contain the number of
1147 * operations.
1148 */
1149 debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n",
1150 xferp->cmd[0],
1151 xferp->cmd[1],
1152 xferp->cmd[2],
1153 xferp->cmd[3],
1154 xferp->cmd[4],
1155 xferp->cmd[5]);
1156
1157 switch (xferp->cmd[1] & 7) {
1158 case 1: /* Seek to a different file nr: */
1159 {
1160 int diff = (xferp->cmd[2] << 16) +
1161 (xferp->cmd[3] << 8) + xferp->cmd[4];
1162
1163 /* Negative seek offset: */
1164 if (diff & (1 << 23))
1165 diff = - (16777216 - diff);
1166
1167 d->tape_filenr += diff;
1168 }
1169
1170 /* At end of file, switch to the next tape file: */
1171 if (d->filemark) {
1172 d->tape_filenr ++;
1173 d->filemark = 0;
1174 }
1175
1176 debug("{ switching to tape file %i }", d->tape_filenr);
1177 diskimage__switch_tape(d);
1178 d->filemark = 0;
1179 break;
1180 default:
1181 fatal("[ diskimage.c: unimplemented SPACE type %i ]\n",
1182 xferp->cmd[1] & 7);
1183 }
1184
1185 diskimage__return_default_status_and_message(xferp);
1186 break;
1187
1188 case SCSICDROM_READ_SUBCHANNEL:
1189 /*
1190 * According to
1191 * http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html:
1192 *
1193 * "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT
1194 * commands have the same opcode in SCSI or ATAPI, but don't
1195 * have the same command structure"...
1196 *
1197 * TODO: This still doesn't work. Hm.
1198 */
1199 retlen = 48;
1200
1201 debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x",
1202 xferp->cmd[1]);
1203
1204 /* Return data: */
1205 scsi_transfer_allocbuf(&xferp->data_in_len,
1206 &xferp->data_in, retlen, 1);
1207
1208 diskimage_recalc_size(d);
1209
1210 size = d->total_size / d->logical_block_size;
1211 if (d->total_size & (d->logical_block_size-1))
1212 size ++;
1213
1214 xferp->data_in[0] = (size >> 24) & 255;
1215 xferp->data_in[1] = (size >> 16) & 255;
1216 xferp->data_in[2] = (size >> 8) & 255;
1217 xferp->data_in[3] = size & 255;
1218
1219 xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
1220 xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
1221 xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
1222 xferp->data_in[7] = d->logical_block_size & 255;
1223
1224 diskimage__return_default_status_and_message(xferp);
1225 break;
1226
1227 case SCSICDROM_READ_TOC:
1228 debug("(CDROM_READ_TOC: ");
1229 debug("lun=%i msf=%i ",
1230 xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1);
1231 debug("starting_track=%i ", xferp->cmd[6]);
1232 retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
1233 debug("allocation_len=%i)\n", retlen);
1234
1235 /* Return data: */
1236 scsi_transfer_allocbuf(&xferp->data_in_len,
1237 &xferp->data_in, retlen, 1);
1238
1239 xferp->data_in[0] = 0;
1240 xferp->data_in[1] = 10;
1241 xferp->data_in[2] = 0; /* First track. */
1242 xferp->data_in[3] = 0; /* Last track. */
1243
1244 /* Track 0 data: */
1245 xferp->data_in[4] = 0x00; /* Reserved. */
1246 xferp->data_in[5] = 0x04; /* ADR + CTRL:
1247 Data, not audio */
1248 xferp->data_in[6] = 0x00; /* Track nr */
1249 xferp->data_in[7] = 0x00; /* Reserved */
1250 /* 8..11 = absolute CDROM address */
1251
1252 diskimage__return_default_status_and_message(xferp);
1253 break;
1254
1255 case SCSICDROM_READ_DISCINFO:
1256 debug("(SCSICDROM_READ_DISCINFO: ");
1257 debug("TODO");
1258 retlen = 0;
1259
1260 /* Return data: */
1261 scsi_transfer_allocbuf(&xferp->data_in_len,
1262 &xferp->data_in, retlen, 1);
1263
1264 /* TODO */
1265
1266 diskimage__return_default_status_and_message(xferp);
1267 break;
1268
1269 case SCSICDROM_READ_TRACKINFO:
1270 debug("(SCSICDROM_READ_TRACKINFO: ");
1271 debug("TODO");
1272 retlen = 0;
1273
1274 /* Return data: */
1275 scsi_transfer_allocbuf(&xferp->data_in_len,
1276 &xferp->data_in, retlen, 1);
1277
1278 /* TODO */
1279
1280 diskimage__return_default_status_and_message(xferp);
1281 break;
1282
1283 case SCSICMD_MODE_SELECT:
1284 debug("[ SCSI MODE_SELECT: ");
1285
1286 /*
1287 * TODO:
1288 *
1289 * This is super-hardcoded for NetBSD's usage of mode_select
1290 * to set the size of CDROM sectors to 2048.
1291 */
1292
1293 if (xferp->data_out_offset == 0) {
1294 xferp->data_out_len = 12; /* TODO */
1295 debug("data_out == NULL, wanting %i bytes ]\n",
1296 (int)xferp->data_out_len);
1297 return 2;
1298 }
1299
1300 debug("data_out!=NULL (OK), ");
1301
1302 /* TODO: Care about cmd? */
1303
1304 /* Set sector size to 2048: */
1305 /* 00 05 00 08 00 03 ca 40 00 00 08 00 */
1306 if (xferp->data_out[0] == 0x00 &&
1307 xferp->data_out[1] == 0x05 &&
1308 xferp->data_out[2] == 0x00 &&
1309 xferp->data_out[3] == 0x08) {
1310 d->logical_block_size =
1311 (xferp->data_out[9] << 16) +
1312 (xferp->data_out[10] << 8) +
1313 xferp->data_out[11];
1314 debug("[ setting logical_block_size to %i ]\n",
1315 d->logical_block_size);
1316 } else {
1317 int i;
1318 fatal("[ unknown MODE_SELECT: cmd =");
1319 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1320 fatal(" %02x", xferp->cmd[i]);
1321 fatal(", data_out =");
1322 for (i=0; i<(ssize_t)xferp->data_out_len; i++)
1323 fatal(" %02x", xferp->data_out[i]);
1324 fatal(" ]");
1325 }
1326
1327 debug(" ]\n");
1328 diskimage__return_default_status_and_message(xferp);
1329 break;
1330
1331 case SCSICMD_PREVENT_ALLOW_REMOVE:
1332 debug("[ SCSI 0x%02x Prevent/allow medium removal: "
1333 "TODO ]\n", xferp->cmd[0]);
1334
1335 diskimage__return_default_status_and_message(xferp);
1336 break;
1337
1338 case 0xbd:
1339 fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0],
1340 xferp->cmd_len);
1341 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1342 fatal(" %02x", xferp->cmd[i]);
1343 fatal(" ]\n");
1344
1345 /*
1346 * Used by Windows NT?
1347 *
1348 * Not documented in http://www.danbbs.dk/~dino/
1349 * SCSI/SCSI2-D.html.
1350 * Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm.
1351 */
1352
1353 if (xferp->cmd_len < 12) {
1354 fatal("WEIRD LEN?\n");
1355 retlen = 8;
1356 } else {
1357 retlen = xferp->cmd[8] * 256 + xferp->cmd[9];
1358 }
1359
1360 /* Return data: */
1361 scsi_transfer_allocbuf(&xferp->data_in_len,
1362 &xferp->data_in, retlen, 1);
1363
1364 diskimage__return_default_status_and_message(xferp);
1365
1366 break;
1367
1368 default:
1369 fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1370 xferp->cmd[0], id);
1371 exit(1);
1372 }
1373 debug(" ]\n");
1374
1375 return 1;
1376 }
1377
1378
1379 /*
1380 * diskimage_access():
1381 *
1382 * Read from or write to a disk image on a machine.
1383 *
1384 * Returns 1 if the access completed successfully, 0 otherwise.
1385 */
1386 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
1387 off_t offset, unsigned char *buf, size_t len)
1388 {
1389 struct diskimage *d = machine->first_diskimage;
1390
1391 while (d != NULL) {
1392 if (d->type == type && d->id == id)
1393 break;
1394 d = d->next;
1395 }
1396
1397 if (d == NULL) {
1398 fatal("[ diskimage_access(): ERROR: trying to access a "
1399 "non-existant %s disk image (id %i)\n",
1400 diskimage_types[type], id);
1401 return 0;
1402 }
1403
1404 return diskimage__internal_access(d, writeflag, offset, buf, len);
1405 }
1406
1407
1408 /*
1409 * diskimage_add():
1410 *
1411 * Add a disk image. fname is the filename of the disk image.
1412 * The filename may be prefixed with one or more modifiers, followed
1413 * by a colon.
1414 *
1415 * b specifies that this is a bootable device
1416 * c CD-ROM (instead of a normal DISK)
1417 * d DISK (this is the default)
1418 * f FLOPPY (instead of SCSI)
1419 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
1420 * automatically calculated). (This is ignored for floppies.)
1421 * i IDE (instead of SCSI)
1422 * r read-only (don't allow changes to the file)
1423 * s SCSI (this is the default)
1424 * t tape
1425 * 0-7 force a specific SCSI ID number
1426 *
1427 * machine is assumed to be non-NULL.
1428 * Returns an integer >= 0 identifying the disk image.
1429 */
1430 int diskimage_add(struct machine *machine, char *fname)
1431 {
1432 struct diskimage *d, *d2;
1433 int id = 0, override_heads=0, override_spt=0;
1434 int64_t bytespercyl;
1435 char *cp;
1436 int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;
1437 int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id = -1;
1438
1439 if (fname == NULL) {
1440 fprintf(stderr, "diskimage_add(): NULL ptr\n");
1441 return 0;
1442 }
1443
1444 /* Get prefix from fname: */
1445 cp = strchr(fname, ':');
1446 if (cp != NULL) {
1447 while (fname <= cp) {
1448 char c = *fname++;
1449 switch (c) {
1450 case '0':
1451 case '1':
1452 case '2':
1453 case '3':
1454 case '4':
1455 case '5':
1456 case '6':
1457 case '7':
1458 prefix_id = c - '0';
1459 break;
1460 case 'b':
1461 prefix_b = 1;
1462 break;
1463 case 'c':
1464 prefix_c = 1;
1465 break;
1466 case 'd':
1467 prefix_d = 1;
1468 break;
1469 case 'f':
1470 prefix_f = 1;
1471 break;
1472 case 'g':
1473 prefix_g = 1;
1474 override_heads = atoi(fname);
1475 while (*fname != '\0' && *fname != ';')
1476 fname ++;
1477 if (*fname == ';')
1478 fname ++;
1479 override_spt = atoi(fname);
1480 while (*fname != '\0' && *fname != ';' &&
1481 *fname != ':')
1482 fname ++;
1483 if (*fname == ';')
1484 fname ++;
1485 if (override_heads < 1 ||
1486 override_spt < 1) {
1487 fatal("Bad geometry: heads=%i "
1488 "spt=%i\n", override_heads,
1489 override_spt);
1490 exit(1);
1491 }
1492 break;
1493 case 'i':
1494 prefix_i = 1;
1495 break;
1496 case 'r':
1497 prefix_r = 1;
1498 break;
1499 case 's':
1500 prefix_s = 1;
1501 break;
1502 case 't':
1503 prefix_t = 1;
1504 break;
1505 case ':':
1506 break;
1507 default:
1508 fprintf(stderr, "diskimage_add(): invalid "
1509 "prefix char '%c'\n", c);
1510 exit(1);
1511 }
1512 }
1513 }
1514
1515 /* Allocate a new diskimage struct: */
1516 d = malloc(sizeof(struct diskimage));
1517 if (d == NULL) {
1518 fprintf(stderr, "out of memory in diskimage_add()\n");
1519 exit(1);
1520 }
1521 memset(d, 0, sizeof(struct diskimage));
1522
1523 d2 = machine->first_diskimage;
1524 if (d2 == NULL) {
1525 machine->first_diskimage = d;
1526 } else {
1527 while (d2->next != NULL)
1528 d2 = d2->next;
1529 d2->next = d;
1530 }
1531
1532 /* Default to IDE disks... */
1533 d->type = DISKIMAGE_IDE;
1534
1535 /* ... but some machines use SCSI by default: */
1536 if (machine->machine_type == MACHINE_PMAX ||
1537 machine->machine_type == MACHINE_ARC)
1538 d->type = DISKIMAGE_SCSI;
1539
1540 if (prefix_i + prefix_f + prefix_s > 1) {
1541 fprintf(stderr, "Invalid disk image prefix(es). You can"
1542 "only use one of i, f, and s\nfor each disk image.\n");
1543 exit(1);
1544 }
1545
1546 if (prefix_i)
1547 d->type = DISKIMAGE_IDE;
1548 if (prefix_f)
1549 d->type = DISKIMAGE_FLOPPY;
1550 if (prefix_s)
1551 d->type = DISKIMAGE_SCSI;
1552
1553 d->fname = strdup(fname);
1554 if (d->fname == NULL) {
1555 fprintf(stderr, "out of memory\n");
1556 exit(1);
1557 }
1558
1559 d->logical_block_size = 512;
1560
1561 /*
1562 * Is this a tape, CD-ROM or a normal disk?
1563 *
1564 * An intelligent guess, if no prefixes are used, would be that
1565 * filenames ending with .iso or .cdr are CD-ROM images.
1566 */
1567 if (prefix_t) {
1568 d->is_a_tape = 1;
1569 } else {
1570 if (prefix_c ||
1571 ((strlen(d->fname) > 4 &&
1572 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
1573 strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
1574 && !prefix_d)
1575 ) {
1576 d->is_a_cdrom = 1;
1577
1578 /*
1579 * This is tricky. Should I use 512 or 2048 here?
1580 * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
1581 * per sector, but NetBSD 2.0_BETA suddenly ignores
1582 * this value and uses 2048 instead.
1583 *
1584 * OpenBSD/arc doesn't like 2048, it requires 512
1585 * to work correctly.
1586 *
1587 * TODO
1588 */
1589
1590 #if 0
1591 if (machine->machine_type == MACHINE_PMAX)
1592 d->logical_block_size = 512;
1593 else
1594 d->logical_block_size = 2048;
1595 #endif
1596 d->logical_block_size = 512;
1597 }
1598 }
1599
1600 diskimage_recalc_size(d);
1601
1602 if ((d->total_size == 720*1024 || d->total_size == 1474560
1603 || d->total_size == 2949120 || d->total_size == 1228800)
1604 && !prefix_i && !prefix_s)
1605 d->type = DISKIMAGE_FLOPPY;
1606
1607 switch (d->type) {
1608 case DISKIMAGE_FLOPPY:
1609 if (d->total_size < 737280) {
1610 fatal("\nTODO: small (non-80-cylinder) floppies?\n\n");
1611 exit(1);
1612 }
1613 d->cylinders = 80;
1614 d->heads = 2;
1615 d->sectors_per_track = d->total_size / (d->cylinders *
1616 d->heads * 512);
1617 break;
1618 default:/* Non-floppies: */
1619 d->heads = 16;
1620 d->sectors_per_track = 63;
1621 if (prefix_g) {
1622 d->chs_override = 1;
1623 d->heads = override_heads;
1624 d->sectors_per_track = override_spt;
1625 }
1626 bytespercyl = d->heads * d->sectors_per_track * 512;
1627 d->cylinders = d->total_size / bytespercyl;
1628 if (d->cylinders * bytespercyl < d->total_size)
1629 d->cylinders ++;
1630 }
1631
1632 d->rpms = 3600;
1633
1634 if (prefix_b)
1635 d->is_boot_device = 1;
1636
1637 d->writable = access(fname, W_OK) == 0? 1 : 0;
1638
1639 if (d->is_a_cdrom || prefix_r)
1640 d->writable = 0;
1641
1642 d->f = fopen(fname, d->writable? "r+" : "r");
1643 if (d->f == NULL) {
1644 perror(fname);
1645 exit(1);
1646 }
1647
1648 /* Calculate which ID to use: */
1649 if (prefix_id == -1) {
1650 int free = 0, collision = 1;
1651
1652 while (collision) {
1653 collision = 0;
1654 d2 = machine->first_diskimage;
1655 while (d2 != NULL) {
1656 /* (don't compare against ourselves :) */
1657 if (d2 == d) {
1658 d2 = d2->next;
1659 continue;
1660 }
1661 if (d2->id == free && d2->type == d->type) {
1662 collision = 1;
1663 break;
1664 }
1665 d2 = d2->next;
1666 }
1667 if (!collision)
1668 id = free;
1669 else
1670 free ++;
1671 }
1672 } else {
1673 id = prefix_id;
1674 d2 = machine->first_diskimage;
1675 while (d2 != NULL) {
1676 /* (don't compare against ourselves :) */
1677 if (d2 == d) {
1678 d2 = d2->next;
1679 continue;
1680 }
1681 if (d2->id == id && d2->type == d->type) {
1682 fprintf(stderr, "disk image id %i "
1683 "already in use\n", id);
1684 exit(1);
1685 }
1686 d2 = d2->next;
1687 }
1688 }
1689
1690 d->id = id;
1691
1692 return id;
1693 }
1694
1695
1696 /*
1697 * diskimage_bootdev():
1698 *
1699 * Returns the disk id of the device which we're booting from. If typep is
1700 * non-NULL, the type is returned as well.
1701 *
1702 * If no disk was used as boot device, then -1 is returned. (In practice,
1703 * this is used to fake network (tftp) boot.)
1704 */
1705 int diskimage_bootdev(struct machine *machine, int *typep)
1706 {
1707 struct diskimage *d;
1708
1709 d = machine->first_diskimage;
1710 while (d != NULL) {
1711 if (d->is_boot_device) {
1712 if (typep != NULL)
1713 *typep = d->type;
1714 return d->id;
1715 }
1716 d = d->next;
1717 }
1718
1719 d = machine->first_diskimage;
1720 if (d != NULL) {
1721 if (typep != NULL)
1722 *typep = d->type;
1723 return d->id;
1724 }
1725
1726 return -1;
1727 }
1728
1729
1730 /*
1731 * diskimage_getname():
1732 *
1733 * Returns 1 if a valid disk image name was returned, 0 otherwise.
1734 */
1735 int diskimage_getname(struct machine *machine, int id, int type,
1736 char *buf, size_t bufsize)
1737 {
1738 struct diskimage *d = machine->first_diskimage;
1739
1740 if (buf == NULL)
1741 return 0;
1742
1743 while (d != NULL) {
1744 if (d->type == type && d->id == id) {
1745 char *p = strrchr(d->fname, '/');
1746 if (p == NULL)
1747 p = d->fname;
1748 else
1749 p ++;
1750 snprintf(buf, bufsize, "%s", p);
1751 return 1;
1752 }
1753 d = d->next;
1754 }
1755 return 0;
1756 }
1757
1758
1759 /*
1760 * diskimage_is_a_cdrom():
1761 *
1762 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1763 */
1764 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1765 {
1766 struct diskimage *d = machine->first_diskimage;
1767
1768 while (d != NULL) {
1769 if (d->type == type && d->id == id)
1770 return d->is_a_cdrom;
1771 d = d->next;
1772 }
1773 return 0;
1774 }
1775
1776
1777 /*
1778 * diskimage_is_a_tape():
1779 *
1780 * Returns 1 if a disk image is a tape, 0 otherwise.
1781 *
1782 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1783 * boot strings.)
1784 */
1785 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1786 {
1787 struct diskimage *d = machine->first_diskimage;
1788
1789 while (d != NULL) {
1790 if (d->type == type && d->id == id)
1791 return d->is_a_tape;
1792 d = d->next;
1793 }
1794 return 0;
1795 }
1796
1797
1798 /*
1799 * diskimage_dump_info():
1800 *
1801 * Debug dump of all diskimages that are loaded for a specific machine.
1802 */
1803 void diskimage_dump_info(struct machine *machine)
1804 {
1805 int iadd = DEBUG_INDENTATION;
1806 struct diskimage *d = machine->first_diskimage;
1807
1808 while (d != NULL) {
1809 debug("diskimage: %s\n", d->fname);
1810 debug_indentation(iadd);
1811
1812 switch (d->type) {
1813 case DISKIMAGE_SCSI:
1814 debug("SCSI");
1815 break;
1816 case DISKIMAGE_IDE:
1817 debug("IDE");
1818 break;
1819 case DISKIMAGE_FLOPPY:
1820 debug("FLOPPY");
1821 break;
1822 default:
1823 debug("UNKNOWN type %i", d->type);
1824 }
1825
1826 debug(" %s", d->is_a_tape? "TAPE" :
1827 (d->is_a_cdrom? "CD-ROM" : "DISK"));
1828 debug(" id %i, ", d->id);
1829 debug("%s, ", d->writable? "read/write" : "read-only");
1830
1831 if (d->type == DISKIMAGE_FLOPPY)
1832 debug("%lli KB", (long long) (d->total_size / 1024));
1833 else
1834 debug("%lli MB", (long long) (d->total_size / 1048576));
1835
1836 if (d->type == DISKIMAGE_FLOPPY || d->chs_override)
1837 debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads,
1838 d->sectors_per_track);
1839 else
1840 debug(" (%lli sectors)", (long long)
1841 (d->total_size / 512));
1842
1843 if (d->is_boot_device)
1844 debug(" (BOOT)");
1845 debug("\n");
1846
1847 debug_indentation(-iadd);
1848
1849 d = d->next;
1850 }
1851 }
1852

  ViewVC Help
Powered by ViewVC 1.1.26