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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 15 - (hide annotations)
Mon Oct 8 16:18:56 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 45344 byte(s)
0.3.6
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 14 * $Id: diskimage.c,v 1.98 2005/09/27 23:55:43 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     extern int quiet_mode;
57     extern int single_step;
58    
59 dpavlin 6 static char *diskimage_types[] = DISKIMAGE_TYPES;
60 dpavlin 2
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 dpavlin 6 * Returns 1 if the specified disk id (for a specific type) exists, 0
212     * otherwise.
213 dpavlin 2 */
214 dpavlin 6 int diskimage_exist(struct machine *machine, int id, int type)
215 dpavlin 2 {
216     struct diskimage *d = machine->first_diskimage;
217    
218     while (d != NULL) {
219 dpavlin 6 if (d->type == type && d->id == id)
220 dpavlin 2 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 dpavlin 6
260     /* TODO: There is a mismatch between d->ncyls and d->cylinders,
261     SCSI-based stuff usually doesn't care. TODO: Fix this. */
262 dpavlin 2 }
263    
264    
265     /*
266     * diskimage_getsize():
267     *
268 dpavlin 6 * Returns -1 if the specified disk id/type does not exists, otherwise
269 dpavlin 2 * the size of the disk image is returned.
270     */
271 dpavlin 6 int64_t diskimage_getsize(struct machine *machine, int id, int type)
272 dpavlin 2 {
273     struct diskimage *d = machine->first_diskimage;
274    
275     while (d != NULL) {
276 dpavlin 6 if (d->type == type && d->id == id)
277 dpavlin 2 return d->total_size;
278     d = d->next;
279     }
280     return -1;
281     }
282    
283    
284     /*
285 dpavlin 6 * 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 dpavlin 2 * 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 dpavlin 12 ssize_t lendone;
413 dpavlin 2 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 dpavlin 6 int diskimage_scsicommand(struct cpu *cpu, int id, int type,
479 dpavlin 2 struct scsi_transfer *xferp)
480     {
481 dpavlin 14 char namebuf[16];
482 dpavlin 2 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 dpavlin 6 if (d->type == type && d->id == id)
497 dpavlin 2 break;
498     d = d->next;
499     }
500     if (d == NULL) {
501 dpavlin 6 fprintf(stderr, "[ diskimage_scsicommand(): %s "
502     " id %i not connected? ]\n", diskimage_types[type], id);
503 dpavlin 2 }
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 dpavlin 6 id, xferp->cmd[0]);
518 dpavlin 2
519     #if 0
520     fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",
521 dpavlin 6 id, xferp->cmd[0], xferp->cmd_len);
522 dpavlin 2 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 dpavlin 6 fprintf(f, "id=%i cmd =", id);
537 dpavlin 2 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 dpavlin 14 /* These are padded with spaces: */
593 dpavlin 2
594 dpavlin 14 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 dpavlin 2 /*
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 dpavlin 6 debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode);
739 dpavlin 2
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 dpavlin 6 xferp->data_in[12 + 11] = d->sectors_per_track;
780 dpavlin 2
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 dpavlin 6 xferp->data_in[12 + 5] = d->heads;
793 dpavlin 2
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 dpavlin 6 xferp->data_in[12 + 4] = d->heads;
806     xferp->data_in[12 + 5] = d->sectors_per_track;
807 dpavlin 2
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 dpavlin 6 ", ofs=%lli ]\n", id, d->tape_filenr,
864 dpavlin 2 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 dpavlin 6 debug(" feof id=%i\n", id);
927 dpavlin 2 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 dpavlin 10 d->tape_offset = ftello(d->f);
936 dpavlin 2
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 dpavlin 12 for (i=0; i<xferp->cmd_len; i++)
1023 dpavlin 2 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 dpavlin 10
1320 dpavlin 2 break;
1321    
1322     default:
1323     fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1324 dpavlin 6 xferp->cmd[0], id);
1325 dpavlin 2 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 dpavlin 6 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
1341 dpavlin 2 off_t offset, unsigned char *buf, size_t len)
1342     {
1343     struct diskimage *d = machine->first_diskimage;
1344    
1345     while (d != NULL) {
1346 dpavlin 6 if (d->type == type && d->id == id)
1347 dpavlin 2 break;
1348     d = d->next;
1349     }
1350    
1351     if (d == NULL) {
1352     fatal("[ diskimage_access(): ERROR: trying to access a "
1353 dpavlin 6 "non-existant %s disk image (id %i)\n",
1354     diskimage_types[type], id);
1355 dpavlin 2 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 dpavlin 6 * b specifies that this is a bootable device
1370     * c CD-ROM (instead of a normal DISK)
1371 dpavlin 4 * d DISK (this is the default)
1372     * f FLOPPY (instead of SCSI)
1373 dpavlin 6 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
1374     * automatically calculated). (This is ignored for floppies.)
1375 dpavlin 2 * i IDE (instead of SCSI)
1376     * r read-only (don't allow changes to the file)
1377 dpavlin 4 * s SCSI (this is the default)
1378     * t tape
1379 dpavlin 2 * 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 dpavlin 6 int id = 0, override_heads=0, override_spt=0;
1388     int64_t bytespercyl;
1389 dpavlin 2 char *cp;
1390 dpavlin 6 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 dpavlin 2
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 dpavlin 4 case 'f':
1424     prefix_f = 1;
1425     break;
1426 dpavlin 6 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 dpavlin 2 case 'i':
1448     prefix_i = 1;
1449     break;
1450 dpavlin 4 case 'r':
1451     prefix_r = 1;
1452     break;
1453     case 's':
1454     prefix_s = 1;
1455     break;
1456 dpavlin 2 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 dpavlin 4 /* Special cases: some machines usually have FLOPPY/IDE, not SCSI: */
1489     if (machine->arch == ARCH_X86 ||
1490     machine->machine_type == MACHINE_COBALT ||
1491 dpavlin 10 machine->machine_type == MACHINE_EVBMIPS ||
1492 dpavlin 4 machine->machine_type == MACHINE_HPCMIPS ||
1493 dpavlin 14 machine->machine_type == MACHINE_CATS ||
1494     machine->machine_type == MACHINE_NETWINDER ||
1495 dpavlin 4 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 dpavlin 2 if (prefix_i)
1505     d->type = DISKIMAGE_IDE;
1506 dpavlin 4 if (prefix_f)
1507     d->type = DISKIMAGE_FLOPPY;
1508     if (prefix_s)
1509     d->type = DISKIMAGE_SCSI;
1510 dpavlin 2
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 dpavlin 14 * filenames ending with .iso or .cdr are CD-ROM images.
1524 dpavlin 2 */
1525     if (prefix_t) {
1526     d->is_a_tape = 1;
1527     } else {
1528     if (prefix_c ||
1529     ((strlen(d->fname) > 4 &&
1530 dpavlin 14 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
1531     strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
1532 dpavlin 2 && !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 dpavlin 6 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 dpavlin 4 d->type = DISKIMAGE_FLOPPY;
1564    
1565 dpavlin 6 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 dpavlin 2 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 dpavlin 6 /* 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 dpavlin 2 return id;
1651     }
1652    
1653    
1654     /*
1655     * diskimage_bootdev():
1656     *
1657 dpavlin 6 * 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 dpavlin 2 *
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 dpavlin 6 int diskimage_bootdev(struct machine *machine, int *typep)
1664 dpavlin 2 {
1665 dpavlin 6 struct diskimage *d;
1666    
1667     d = machine->first_diskimage;
1668 dpavlin 2 while (d != NULL) {
1669 dpavlin 6 if (d->is_boot_device) {
1670     if (typep != NULL)
1671     *typep = d->type;
1672 dpavlin 2 return d->id;
1673 dpavlin 6 }
1674 dpavlin 2 d = d->next;
1675     }
1676    
1677     d = machine->first_diskimage;
1678 dpavlin 6 if (d != NULL) {
1679     if (typep != NULL)
1680     *typep = d->type;
1681 dpavlin 2 return d->id;
1682 dpavlin 6 }
1683 dpavlin 2
1684     return -1;
1685     }
1686    
1687    
1688     /*
1689 dpavlin 14 * 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 dpavlin 2 * diskimage_is_a_cdrom():
1719     *
1720 dpavlin 6 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1721 dpavlin 2 */
1722 dpavlin 6 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1723 dpavlin 2 {
1724     struct diskimage *d = machine->first_diskimage;
1725    
1726     while (d != NULL) {
1727 dpavlin 6 if (d->type == type && d->id == id)
1728 dpavlin 2 return d->is_a_cdrom;
1729     d = d->next;
1730     }
1731     return 0;
1732     }
1733    
1734    
1735     /*
1736     * diskimage_is_a_tape():
1737     *
1738 dpavlin 6 * Returns 1 if a disk image is a tape, 0 otherwise.
1739     *
1740 dpavlin 2 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1741     * boot strings.)
1742     */
1743 dpavlin 6 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1744 dpavlin 2 {
1745     struct diskimage *d = machine->first_diskimage;
1746    
1747     while (d != NULL) {
1748 dpavlin 6 if (d->type == type && d->id == id)
1749 dpavlin 2 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 dpavlin 4 case DISKIMAGE_FLOPPY:
1778     debug("FLOPPY");
1779     break;
1780 dpavlin 2 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 dpavlin 4 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 dpavlin 6 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 dpavlin 2
1801 dpavlin 6 if (d->is_boot_device)
1802     debug(" (BOOT)");
1803     debug("\n");
1804    
1805 dpavlin 2 debug_indentation(-iadd);
1806    
1807     d = d->next;
1808     }
1809     }
1810    

  ViewVC Help
Powered by ViewVC 1.1.26