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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26