/[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

Annotation of /upstream/0.3.7/src/diskimage.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 21 - (hide annotations)
Mon Oct 8 16:19:28 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 46014 byte(s)
0.3.7
1 dpavlin 2 /*
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 dpavlin 20 * $Id: diskimage.c,v 1.102 2005/11/21 09:17:25 debug Exp $
29 dpavlin 2 *
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 dpavlin 6 * 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 dpavlin 2 */
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 dpavlin 20 /* #define debug fatal */
57    
58 dpavlin 2 extern int single_step;
59    
60 dpavlin 6 static char *diskimage_types[] = DISKIMAGE_TYPES;
61 dpavlin 2
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 dpavlin 6 * Returns 1 if the specified disk id (for a specific type) exists, 0
213     * otherwise.
214 dpavlin 2 */
215 dpavlin 6 int diskimage_exist(struct machine *machine, int id, int type)
216 dpavlin 2 {
217     struct diskimage *d = machine->first_diskimage;
218    
219     while (d != NULL) {
220 dpavlin 6 if (d->type == type && d->id == id)
221 dpavlin 2 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 dpavlin 6
261     /* TODO: There is a mismatch between d->ncyls and d->cylinders,
262     SCSI-based stuff usually doesn't care. TODO: Fix this. */
263 dpavlin 2 }
264    
265    
266     /*
267     * diskimage_getsize():
268     *
269 dpavlin 6 * Returns -1 if the specified disk id/type does not exists, otherwise
270 dpavlin 2 * the size of the disk image is returned.
271     */
272 dpavlin 6 int64_t diskimage_getsize(struct machine *machine, int id, int type)
273 dpavlin 2 {
274     struct diskimage *d = machine->first_diskimage;
275    
276     while (d != NULL) {
277 dpavlin 6 if (d->type == type && d->id == id)
278 dpavlin 2 return d->total_size;
279     d = d->next;
280     }
281     return -1;
282     }
283    
284    
285     /*
286 dpavlin 6 * 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 dpavlin 2 * 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 dpavlin 12 ssize_t lendone;
414 dpavlin 2 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 dpavlin 6 int diskimage_scsicommand(struct cpu *cpu, int id, int type,
480 dpavlin 2 struct scsi_transfer *xferp)
481     {
482 dpavlin 14 char namebuf[16];
483 dpavlin 20 int retlen, i, q;
484 dpavlin 2 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 dpavlin 6 if (d->type == type && d->id == id)
498 dpavlin 2 break;
499     d = d->next;
500     }
501     if (d == NULL) {
502 dpavlin 6 fprintf(stderr, "[ diskimage_scsicommand(): %s "
503     " id %i not connected? ]\n", diskimage_types[type], id);
504 dpavlin 2 }
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 dpavlin 6 id, xferp->cmd[0]);
519 dpavlin 2
520     #if 0
521     fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",
522 dpavlin 6 id, xferp->cmd[0], xferp->cmd_len);
523 dpavlin 2 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 dpavlin 6 fprintf(f, "id=%i cmd =", id);
538 dpavlin 2 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 dpavlin 14 /* These are padded with spaces: */
594 dpavlin 2
595 dpavlin 20 memcpy(xferp->data_in+8, "GXemul ", 8);
596 dpavlin 14 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 dpavlin 20 memcpy(xferp->data_in+32, "0 ", 4);
609 dpavlin 14
610 dpavlin 2 /*
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 dpavlin 20 /* memcpy(xferp->data_in+16, "CD-ROM ", 16);*/
628 dpavlin 2
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 dpavlin 20 } else if (machine->machine_type == MACHINE_ARC) {
641 dpavlin 2 /* 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 dpavlin 20 case SCSICMD_MODE_SENSE10:
711 dpavlin 2 debug("MODE_SENSE");
712 dpavlin 20 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 dpavlin 2 xferp->cmd_len);
720 dpavlin 20 }
721 dpavlin 2
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 dpavlin 6 debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode);
744 dpavlin 2
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 dpavlin 20 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 dpavlin 2
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 dpavlin 20 xferp->data_in[q + 0] = pagecode;
776     xferp->data_in[q + 1] = 10;
777 dpavlin 2 break;
778     case 3: /* format device page */
779 dpavlin 20 xferp->data_in[q + 0] = pagecode;
780     xferp->data_in[q + 1] = 22;
781 dpavlin 2
782     /* 10,11 = sectors per track */
783 dpavlin 20 xferp->data_in[q + 10] = 0;
784     xferp->data_in[q + 11] = d->sectors_per_track;
785 dpavlin 2
786     /* 12,13 = physical sector size */
787 dpavlin 20 xferp->data_in[q + 12] =
788 dpavlin 2 (d->logical_block_size >> 8) & 255;
789 dpavlin 20 xferp->data_in[q + 13] = d->logical_block_size & 255;
790 dpavlin 2 break;
791     case 4: /* rigid disk geometry page */
792 dpavlin 20 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 dpavlin 2
799 dpavlin 20 xferp->data_in[q + 20] = (d->rpms >> 8) & 255;
800     xferp->data_in[q + 21] = d->rpms & 255;
801 dpavlin 2 break;
802     case 5: /* flexible disk page */
803 dpavlin 20 xferp->data_in[q + 0] = pagecode;
804     xferp->data_in[q + 1] = 0x1e;
805 dpavlin 2
806     /* 2,3 = transfer rate */
807 dpavlin 20 xferp->data_in[q + 2] = ((5000) >> 8) & 255;
808     xferp->data_in[q + 3] = (5000) & 255;
809 dpavlin 2
810 dpavlin 20 xferp->data_in[q + 4] = d->heads;
811     xferp->data_in[q + 5] = d->sectors_per_track;
812 dpavlin 2
813     /* 6,7 = data bytes per sector */
814 dpavlin 20 xferp->data_in[q + 6] = (d->logical_block_size >> 8)
815 dpavlin 2 & 255;
816 dpavlin 20 xferp->data_in[q + 7] = d->logical_block_size & 255;
817 dpavlin 2
818 dpavlin 20 xferp->data_in[q + 8] = (d->ncyls >> 8) & 255;
819     xferp->data_in[q + 9] = d->ncyls & 255;
820 dpavlin 2
821 dpavlin 20 xferp->data_in[q + 28] = (d->rpms >> 8) & 255;
822     xferp->data_in[q + 29] = d->rpms & 255;
823 dpavlin 2 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 dpavlin 6 ", ofs=%lli ]\n", id, d->tape_filenr,
869 dpavlin 2 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 dpavlin 6 debug(" feof id=%i\n", id);
932 dpavlin 2 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 dpavlin 10 d->tape_offset = ftello(d->f);
941 dpavlin 2
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 dpavlin 12 for (i=0; i<xferp->cmd_len; i++)
1028 dpavlin 2 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 dpavlin 20 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 dpavlin 2
1241 dpavlin 20 /* 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 dpavlin 2 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 dpavlin 20 case SCSICMD_PREVENT_ALLOW_REMOVE:
1301     debug("[ SCSI 0x%02x Prevent/allow medium removal: "
1302     "TODO ]\n", xferp->cmd[0]);
1303 dpavlin 2
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 dpavlin 10
1335 dpavlin 2 break;
1336    
1337     default:
1338     fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1339 dpavlin 6 xferp->cmd[0], id);
1340 dpavlin 2 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 dpavlin 6 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
1356 dpavlin 2 off_t offset, unsigned char *buf, size_t len)
1357     {
1358     struct diskimage *d = machine->first_diskimage;
1359    
1360     while (d != NULL) {
1361 dpavlin 6 if (d->type == type && d->id == id)
1362 dpavlin 2 break;
1363     d = d->next;
1364     }
1365    
1366     if (d == NULL) {
1367     fatal("[ diskimage_access(): ERROR: trying to access a "
1368 dpavlin 6 "non-existant %s disk image (id %i)\n",
1369     diskimage_types[type], id);
1370 dpavlin 2 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 dpavlin 6 * b specifies that this is a bootable device
1385     * c CD-ROM (instead of a normal DISK)
1386 dpavlin 4 * d DISK (this is the default)
1387     * f FLOPPY (instead of SCSI)
1388 dpavlin 6 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
1389     * automatically calculated). (This is ignored for floppies.)
1390 dpavlin 2 * i IDE (instead of SCSI)
1391     * r read-only (don't allow changes to the file)
1392 dpavlin 4 * s SCSI (this is the default)
1393     * t tape
1394 dpavlin 2 * 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 dpavlin 6 int id = 0, override_heads=0, override_spt=0;
1403     int64_t bytespercyl;
1404 dpavlin 2 char *cp;
1405 dpavlin 6 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 dpavlin 2
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 dpavlin 4 case 'f':
1439     prefix_f = 1;
1440     break;
1441 dpavlin 6 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 dpavlin 2 case 'i':
1463     prefix_i = 1;
1464     break;
1465 dpavlin 4 case 'r':
1466     prefix_r = 1;
1467     break;
1468     case 's':
1469     prefix_s = 1;
1470     break;
1471 dpavlin 2 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 dpavlin 4 /* Special cases: some machines usually have FLOPPY/IDE, not SCSI: */
1504     if (machine->arch == ARCH_X86 ||
1505     machine->machine_type == MACHINE_COBALT ||
1506 dpavlin 10 machine->machine_type == MACHINE_EVBMIPS ||
1507 dpavlin 4 machine->machine_type == MACHINE_HPCMIPS ||
1508 dpavlin 20 machine->machine_type == MACHINE_BEBOX ||
1509     machine->machine_type == MACHINE_PREP ||
1510 dpavlin 14 machine->machine_type == MACHINE_CATS ||
1511     machine->machine_type == MACHINE_NETWINDER ||
1512 dpavlin 4 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 dpavlin 2 if (prefix_i)
1522     d->type = DISKIMAGE_IDE;
1523 dpavlin 4 if (prefix_f)
1524     d->type = DISKIMAGE_FLOPPY;
1525     if (prefix_s)
1526     d->type = DISKIMAGE_SCSI;
1527 dpavlin 2
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 dpavlin 14 * filenames ending with .iso or .cdr are CD-ROM images.
1541 dpavlin 2 */
1542     if (prefix_t) {
1543     d->is_a_tape = 1;
1544     } else {
1545     if (prefix_c ||
1546     ((strlen(d->fname) > 4 &&
1547 dpavlin 14 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
1548     strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
1549 dpavlin 2 && !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 dpavlin 6 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 dpavlin 4 d->type = DISKIMAGE_FLOPPY;
1581    
1582 dpavlin 6 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 dpavlin 2 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 dpavlin 6 /* 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 dpavlin 2 return id;
1668     }
1669    
1670    
1671     /*
1672     * diskimage_bootdev():
1673     *
1674 dpavlin 6 * 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 dpavlin 2 *
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 dpavlin 6 int diskimage_bootdev(struct machine *machine, int *typep)
1681 dpavlin 2 {
1682 dpavlin 6 struct diskimage *d;
1683    
1684     d = machine->first_diskimage;
1685 dpavlin 2 while (d != NULL) {
1686 dpavlin 6 if (d->is_boot_device) {
1687     if (typep != NULL)
1688     *typep = d->type;
1689 dpavlin 2 return d->id;
1690 dpavlin 6 }
1691 dpavlin 2 d = d->next;
1692     }
1693    
1694     d = machine->first_diskimage;
1695 dpavlin 6 if (d != NULL) {
1696     if (typep != NULL)
1697     *typep = d->type;
1698 dpavlin 2 return d->id;
1699 dpavlin 6 }
1700 dpavlin 2
1701     return -1;
1702     }
1703    
1704    
1705     /*
1706 dpavlin 14 * 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 dpavlin 2 * diskimage_is_a_cdrom():
1736     *
1737 dpavlin 6 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1738 dpavlin 2 */
1739 dpavlin 6 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1740 dpavlin 2 {
1741     struct diskimage *d = machine->first_diskimage;
1742    
1743     while (d != NULL) {
1744 dpavlin 6 if (d->type == type && d->id == id)
1745 dpavlin 2 return d->is_a_cdrom;
1746     d = d->next;
1747     }
1748     return 0;
1749     }
1750    
1751    
1752     /*
1753     * diskimage_is_a_tape():
1754     *
1755 dpavlin 6 * Returns 1 if a disk image is a tape, 0 otherwise.
1756     *
1757 dpavlin 2 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1758     * boot strings.)
1759     */
1760 dpavlin 6 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1761 dpavlin 2 {
1762     struct diskimage *d = machine->first_diskimage;
1763    
1764     while (d != NULL) {
1765 dpavlin 6 if (d->type == type && d->id == id)
1766 dpavlin 2 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 dpavlin 4 case DISKIMAGE_FLOPPY:
1795     debug("FLOPPY");
1796     break;
1797 dpavlin 2 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 dpavlin 4 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 dpavlin 6 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 dpavlin 2
1818 dpavlin 6 if (d->is_boot_device)
1819     debug(" (BOOT)");
1820     debug("\n");
1821    
1822 dpavlin 2 debug_indentation(-iadd);
1823    
1824     d = d->next;
1825     }
1826     }
1827    

  ViewVC Help
Powered by ViewVC 1.1.26