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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26