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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 26 - (hide annotations)
Mon Oct 8 16:20:10 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 45808 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1264 2006/06/25 11:08:04 debug Exp $
20060624	Replacing the error-prone machine type initialization stuff
		with something more reasonable.
		Finally removing the old "cpu_run" kludge; moving around stuff
		in machine.c and emul.c to better suit the dyntrans system.
		Various minor dyntrans cleanups (renaming translate_address to
		translate_v2p, and experimenting with template physpages).
20060625	Removing the speed hack which separated the vph entries into
		two halves (code vs data); things seem a lot more stable now.
		Minor performance hack: R2000/R3000 cache isolation now only
		clears address translations when going into isolation, not
		when going out of it.
		Fixing the MIPS interrupt problems by letting mtc0 immediately
		cause interrupts.

==============  RELEASE 0.4.0.1  ==============


1 dpavlin 2 /*
2 dpavlin 22 * Copyright (C) 2003-2006 Anders Gavare. All rights reserved.
3 dpavlin 2 *
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 26 * $Id: diskimage.c,v 1.110 2006/06/24 19:52:27 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 dpavlin 26 single_step = ENTER_SINGLE_STEPPING;
528 dpavlin 2 #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 dpavlin 22 size_t i;
599 dpavlin 14 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 dpavlin 22 if (machine->machine_type == MACHINE_PMAX) {
616 dpavlin 2 /* 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 dpavlin 22 if (machine->machine_type == MACHINE_PMAX) {
630 dpavlin 2 /* 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 dpavlin 22 if (machine->machine_type == MACHINE_PMAX) {
656 dpavlin 2 /*
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 dpavlin 22 ofs = ((uint64_t)xferp->cmd[2] << 24) +
901     (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8)
902     + xferp->cmd[5];
903 dpavlin 2 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 dpavlin 22 ofs = ((uint64_t)xferp->cmd[2] << 24) +
979     (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) +
980     xferp->cmd[5];
981 dpavlin 2 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
982     }
983    
984     size = retlen * d->logical_block_size;
985     ofs *= d->logical_block_size;
986    
987     if (xferp->data_out_offset != size) {
988     debug(", data_out == NULL, wanting %i bytes, \n\n",
989     (int)size);
990     xferp->data_out_len = size;
991     return 2;
992     }
993    
994     debug(", data_out != NULL, OK :-)");
995    
996     debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs,
997     (int)size, (int)xferp->data_out_offset);
998    
999     diskimage__internal_access(d, 1, ofs,
1000     xferp->data_out, size);
1001    
1002     /* TODO: how about return code? */
1003    
1004     /* Is this really necessary? */
1005     /* fsync(fileno(d->f)); */
1006    
1007     diskimage__return_default_status_and_message(xferp);
1008     break;
1009    
1010     case SCSICMD_SYNCHRONIZE_CACHE:
1011     debug("SYNCHRONIZE_CACHE");
1012    
1013     if (xferp->cmd_len != 10)
1014     debug(" (weird len=%i)", xferp->cmd_len);
1015    
1016     /* TODO: actualy care about cmd[] */
1017     fsync(fileno(d->f));
1018    
1019     diskimage__return_default_status_and_message(xferp);
1020     break;
1021    
1022     case SCSICMD_START_STOP_UNIT:
1023     debug("START_STOP_UNIT");
1024    
1025     if (xferp->cmd_len != 6)
1026     debug(" (weird len=%i)", xferp->cmd_len);
1027    
1028 dpavlin 22 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1029 dpavlin 2 debug(" %02x", xferp->cmd[i]);
1030    
1031     /* TODO: actualy care about cmd[] */
1032    
1033     diskimage__return_default_status_and_message(xferp);
1034     break;
1035    
1036     case SCSICMD_REQUEST_SENSE:
1037     debug("REQUEST_SENSE");
1038    
1039     retlen = xferp->cmd[4];
1040    
1041     /* TODO: bits 765 of buf[1] contains the LUN */
1042     if (xferp->cmd[1] != 0x00)
1043     fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not"
1044     " yet implemented\n", (int)xferp->cmd[1]);
1045    
1046     if (retlen < 18) {
1047     fatal("WARNING: SCSI request sense len=%i, <18!\n",
1048     (int)retlen);
1049     retlen = 18;
1050     }
1051    
1052     /* Return data: */
1053     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1054     retlen, 1);
1055    
1056     xferp->data_in[0] = 0x80 + 0x70;/* 0x80 = valid,
1057     0x70 = "current errors" */
1058     xferp->data_in[2] = 0x00; /* SENSE KEY! */
1059    
1060     if (d->filemark) {
1061     xferp->data_in[2] = 0x80;
1062     }
1063     debug(": [2]=0x%02x ", xferp->data_in[2]);
1064    
1065     printf(" XXX(!) \n");
1066    
1067     /* TODO */
1068     xferp->data_in[7] = retlen - 7; /* additional sense length */
1069     /* TODO */
1070    
1071     diskimage__return_default_status_and_message(xferp);
1072     break;
1073    
1074     case SCSICMD_READ_BLOCK_LIMITS:
1075     debug("READ_BLOCK_LIMITS");
1076    
1077     retlen = 6;
1078    
1079     /* TODO: bits 765 of buf[1] contains the LUN */
1080     if (xferp->cmd[1] != 0x00)
1081     fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]="
1082     "0x%02x not yet implemented\n", (int)xferp->cmd[1]);
1083    
1084     /* Return data: */
1085     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1086     retlen, 1);
1087    
1088     /*
1089     * data[0] is reserved, data[1..3] contain the maximum block
1090     * length limit, data[4..5] contain the minimum limit.
1091     */
1092    
1093     {
1094     int max_limit = 32768;
1095     int min_limit = 128;
1096    
1097     xferp->data_in[1] = (max_limit >> 16) & 255;
1098     xferp->data_in[2] = (max_limit >> 8) & 255;
1099     xferp->data_in[3] = max_limit & 255;
1100     xferp->data_in[4] = (min_limit >> 8) & 255;
1101     xferp->data_in[5] = min_limit & 255;
1102     }
1103    
1104     diskimage__return_default_status_and_message(xferp);
1105     break;
1106    
1107     case SCSICMD_REWIND:
1108     debug("REWIND");
1109    
1110     /* TODO: bits 765 of buf[1] contains the LUN */
1111     if ((xferp->cmd[1] & 0xe0) != 0x00)
1112     fatal("WARNING: REWIND with cmd[1]=0x%02x not yet "
1113     "implemented\n", (int)xferp->cmd[1]);
1114    
1115     /* Close and reopen. */
1116    
1117     if (d->f != NULL)
1118     fclose(d->f);
1119    
1120     d->f = fopen(d->fname, d->writable? "r+" : "r");
1121     if (d->f == NULL) {
1122     fprintf(stderr, "[ diskimage: could not (re)open "
1123     "'%s' ]\n", d->fname);
1124     /* TODO: return error */
1125     }
1126    
1127     d->tape_offset = 0;
1128     d->tape_filenr = 0;
1129     d->filemark = 0;
1130    
1131     diskimage__return_default_status_and_message(xferp);
1132     break;
1133    
1134     case SCSICMD_SPACE:
1135     debug("SPACE");
1136    
1137     /* TODO: bits 765 of buf[1] contains the LUN */
1138     if ((xferp->cmd[1] & 0xe0) != 0x00)
1139     fatal("WARNING: SPACE with cmd[1]=0x%02x not yet "
1140     "implemented\n", (int)xferp->cmd[1]);
1141    
1142     /*
1143     * Bits 2..0 of buf[1] contain the 'code' which describes how
1144     * we should space, and buf[2..4] contain the number of
1145     * operations.
1146     */
1147     debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n",
1148     xferp->cmd[0],
1149     xferp->cmd[1],
1150     xferp->cmd[2],
1151     xferp->cmd[3],
1152     xferp->cmd[4],
1153     xferp->cmd[5]);
1154    
1155     switch (xferp->cmd[1] & 7) {
1156     case 1: /* Seek to a different file nr: */
1157     {
1158     int diff = (xferp->cmd[2] << 16) +
1159     (xferp->cmd[3] << 8) + xferp->cmd[4];
1160    
1161     /* Negative seek offset: */
1162     if (diff & (1 << 23))
1163     diff = - (16777216 - diff);
1164    
1165     d->tape_filenr += diff;
1166     }
1167    
1168     /* At end of file, switch to the next tape file: */
1169     if (d->filemark) {
1170     d->tape_filenr ++;
1171     d->filemark = 0;
1172     }
1173    
1174     debug("{ switching to tape file %i }", d->tape_filenr);
1175     diskimage__switch_tape(d);
1176     d->filemark = 0;
1177     break;
1178     default:
1179     fatal("[ diskimage.c: unimplemented SPACE type %i ]\n",
1180     xferp->cmd[1] & 7);
1181     }
1182    
1183     diskimage__return_default_status_and_message(xferp);
1184     break;
1185    
1186     case SCSICDROM_READ_SUBCHANNEL:
1187     /*
1188     * According to
1189     * http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html:
1190     *
1191     * "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT
1192     * commands have the same opcode in SCSI or ATAPI, but don't
1193     * have the same command structure"...
1194     *
1195     * TODO: This still doesn't work. Hm.
1196     */
1197     retlen = 48;
1198    
1199     debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x",
1200     xferp->cmd[1]);
1201    
1202     /* Return data: */
1203     scsi_transfer_allocbuf(&xferp->data_in_len,
1204     &xferp->data_in, retlen, 1);
1205    
1206     diskimage_recalc_size(d);
1207    
1208     size = d->total_size / d->logical_block_size;
1209     if (d->total_size & (d->logical_block_size-1))
1210     size ++;
1211    
1212     xferp->data_in[0] = (size >> 24) & 255;
1213     xferp->data_in[1] = (size >> 16) & 255;
1214     xferp->data_in[2] = (size >> 8) & 255;
1215     xferp->data_in[3] = size & 255;
1216    
1217     xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
1218     xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
1219     xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
1220     xferp->data_in[7] = d->logical_block_size & 255;
1221    
1222     diskimage__return_default_status_and_message(xferp);
1223     break;
1224    
1225     case SCSICDROM_READ_TOC:
1226     debug("(CDROM_READ_TOC: ");
1227     debug("lun=%i msf=%i ",
1228     xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1);
1229     debug("starting_track=%i ", xferp->cmd[6]);
1230     retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
1231     debug("allocation_len=%i)\n", retlen);
1232    
1233     /* Return data: */
1234     scsi_transfer_allocbuf(&xferp->data_in_len,
1235     &xferp->data_in, retlen, 1);
1236    
1237 dpavlin 20 xferp->data_in[0] = 0;
1238     xferp->data_in[1] = 10;
1239     xferp->data_in[2] = 0; /* First track. */
1240     xferp->data_in[3] = 0; /* Last track. */
1241 dpavlin 2
1242 dpavlin 20 /* Track 0 data: */
1243     xferp->data_in[4] = 0x00; /* Reserved. */
1244     xferp->data_in[5] = 0x04; /* ADR + CTRL:
1245     Data, not audio */
1246     xferp->data_in[6] = 0x00; /* Track nr */
1247     xferp->data_in[7] = 0x00; /* Reserved */
1248     /* 8..11 = absolute CDROM address */
1249    
1250 dpavlin 2 diskimage__return_default_status_and_message(xferp);
1251     break;
1252    
1253     case SCSICMD_MODE_SELECT:
1254     debug("[ SCSI MODE_SELECT: ");
1255    
1256     /*
1257     * TODO:
1258     *
1259     * This is super-hardcoded for NetBSD's usage of mode_select
1260     * to set the size of CDROM sectors to 2048.
1261     */
1262    
1263     if (xferp->data_out_offset == 0) {
1264     xferp->data_out_len = 12; /* TODO */
1265     debug("data_out == NULL, wanting %i bytes ]\n",
1266     (int)xferp->data_out_len);
1267     return 2;
1268     }
1269    
1270     debug("data_out!=NULL (OK), ");
1271    
1272     /* TODO: Care about cmd? */
1273    
1274     /* Set sector size to 2048: */
1275     /* 00 05 00 08 00 03 ca 40 00 00 08 00 */
1276     if (xferp->data_out[0] == 0x00 &&
1277     xferp->data_out[1] == 0x05 &&
1278     xferp->data_out[2] == 0x00 &&
1279     xferp->data_out[3] == 0x08) {
1280     d->logical_block_size =
1281     (xferp->data_out[9] << 16) +
1282     (xferp->data_out[10] << 8) +
1283     xferp->data_out[11];
1284     debug("[ setting logical_block_size to %i ]\n",
1285     d->logical_block_size);
1286     } else {
1287     int i;
1288     fatal("[ unknown MODE_SELECT: cmd =");
1289 dpavlin 22 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1290 dpavlin 2 fatal(" %02x", xferp->cmd[i]);
1291     fatal(", data_out =");
1292 dpavlin 22 for (i=0; i<(ssize_t)xferp->data_out_len; i++)
1293 dpavlin 2 fatal(" %02x", xferp->data_out[i]);
1294     fatal(" ]");
1295     }
1296    
1297     debug(" ]\n");
1298     diskimage__return_default_status_and_message(xferp);
1299     break;
1300    
1301 dpavlin 20 case SCSICMD_PREVENT_ALLOW_REMOVE:
1302     debug("[ SCSI 0x%02x Prevent/allow medium removal: "
1303     "TODO ]\n", xferp->cmd[0]);
1304 dpavlin 2
1305     diskimage__return_default_status_and_message(xferp);
1306     break;
1307    
1308     case 0xbd:
1309     fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0],
1310     xferp->cmd_len);
1311 dpavlin 22 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1312 dpavlin 2 fatal(" %02x", xferp->cmd[i]);
1313     fatal(" ]\n");
1314    
1315     /*
1316     * Used by Windows NT?
1317     *
1318     * Not documented in http://www.danbbs.dk/~dino/
1319     * SCSI/SCSI2-D.html.
1320     * Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm.
1321     */
1322    
1323     if (xferp->cmd_len < 12) {
1324     fatal("WEIRD LEN?\n");
1325     retlen = 8;
1326     } else {
1327     retlen = xferp->cmd[8] * 256 + xferp->cmd[9];
1328     }
1329    
1330     /* Return data: */
1331     scsi_transfer_allocbuf(&xferp->data_in_len,
1332     &xferp->data_in, retlen, 1);
1333    
1334     diskimage__return_default_status_and_message(xferp);
1335 dpavlin 10
1336 dpavlin 2 break;
1337    
1338     default:
1339     fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1340 dpavlin 6 xferp->cmd[0], id);
1341 dpavlin 2 exit(1);
1342     }
1343     debug(" ]\n");
1344    
1345     return 1;
1346     }
1347    
1348    
1349     /*
1350     * diskimage_access():
1351     *
1352     * Read from or write to a disk image on a machine.
1353     *
1354     * Returns 1 if the access completed successfully, 0 otherwise.
1355     */
1356 dpavlin 6 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
1357 dpavlin 2 off_t offset, unsigned char *buf, size_t len)
1358     {
1359     struct diskimage *d = machine->first_diskimage;
1360    
1361     while (d != NULL) {
1362 dpavlin 6 if (d->type == type && d->id == id)
1363 dpavlin 2 break;
1364     d = d->next;
1365     }
1366    
1367     if (d == NULL) {
1368     fatal("[ diskimage_access(): ERROR: trying to access a "
1369 dpavlin 6 "non-existant %s disk image (id %i)\n",
1370     diskimage_types[type], id);
1371 dpavlin 2 return 0;
1372     }
1373    
1374     return diskimage__internal_access(d, writeflag, offset, buf, len);
1375     }
1376    
1377    
1378     /*
1379     * diskimage_add():
1380     *
1381     * Add a disk image. fname is the filename of the disk image.
1382     * The filename may be prefixed with one or more modifiers, followed
1383     * by a colon.
1384     *
1385 dpavlin 6 * b specifies that this is a bootable device
1386     * c CD-ROM (instead of a normal DISK)
1387 dpavlin 4 * d DISK (this is the default)
1388     * f FLOPPY (instead of SCSI)
1389 dpavlin 6 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
1390     * automatically calculated). (This is ignored for floppies.)
1391 dpavlin 2 * i IDE (instead of SCSI)
1392     * r read-only (don't allow changes to the file)
1393 dpavlin 4 * s SCSI (this is the default)
1394     * t tape
1395 dpavlin 2 * 0-7 force a specific SCSI ID number
1396     *
1397     * machine is assumed to be non-NULL.
1398     * Returns an integer >= 0 identifying the disk image.
1399     */
1400     int diskimage_add(struct machine *machine, char *fname)
1401     {
1402     struct diskimage *d, *d2;
1403 dpavlin 6 int id = 0, override_heads=0, override_spt=0;
1404     int64_t bytespercyl;
1405 dpavlin 2 char *cp;
1406 dpavlin 6 int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;
1407     int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id = -1;
1408 dpavlin 2
1409     if (fname == NULL) {
1410     fprintf(stderr, "diskimage_add(): NULL ptr\n");
1411     return 0;
1412     }
1413    
1414     /* Get prefix from fname: */
1415     cp = strchr(fname, ':');
1416     if (cp != NULL) {
1417     while (fname <= cp) {
1418     char c = *fname++;
1419     switch (c) {
1420     case '0':
1421     case '1':
1422     case '2':
1423     case '3':
1424     case '4':
1425     case '5':
1426     case '6':
1427     case '7':
1428     prefix_id = c - '0';
1429     break;
1430     case 'b':
1431     prefix_b = 1;
1432     break;
1433     case 'c':
1434     prefix_c = 1;
1435     break;
1436     case 'd':
1437     prefix_d = 1;
1438     break;
1439 dpavlin 4 case 'f':
1440     prefix_f = 1;
1441     break;
1442 dpavlin 6 case 'g':
1443     prefix_g = 1;
1444     override_heads = atoi(fname);
1445     while (*fname != '\0' && *fname != ';')
1446     fname ++;
1447     if (*fname == ';')
1448     fname ++;
1449     override_spt = atoi(fname);
1450     while (*fname != '\0' && *fname != ';' &&
1451     *fname != ':')
1452     fname ++;
1453     if (*fname == ';')
1454     fname ++;
1455     if (override_heads < 1 ||
1456     override_spt < 1) {
1457     fatal("Bad geometry: heads=%i "
1458     "spt=%i\n", override_heads,
1459     override_spt);
1460     exit(1);
1461     }
1462     break;
1463 dpavlin 2 case 'i':
1464     prefix_i = 1;
1465     break;
1466 dpavlin 4 case 'r':
1467     prefix_r = 1;
1468     break;
1469     case 's':
1470     prefix_s = 1;
1471     break;
1472 dpavlin 2 case 't':
1473     prefix_t = 1;
1474     break;
1475     case ':':
1476     break;
1477     default:
1478     fprintf(stderr, "diskimage_add(): invalid "
1479     "prefix char '%c'\n", c);
1480     exit(1);
1481     }
1482     }
1483     }
1484    
1485     /* Allocate a new diskimage struct: */
1486     d = malloc(sizeof(struct diskimage));
1487     if (d == NULL) {
1488     fprintf(stderr, "out of memory in diskimage_add()\n");
1489     exit(1);
1490     }
1491     memset(d, 0, sizeof(struct diskimage));
1492    
1493     d2 = machine->first_diskimage;
1494     if (d2 == NULL) {
1495     machine->first_diskimage = d;
1496     } else {
1497     while (d2->next != NULL)
1498     d2 = d2->next;
1499     d2->next = d;
1500     }
1501    
1502 dpavlin 24 /* Default to IDE disks... */
1503 dpavlin 22 d->type = DISKIMAGE_IDE;
1504 dpavlin 2
1505 dpavlin 24 /* ... but some machines use SCSI by default: */
1506 dpavlin 22 if (machine->machine_type == MACHINE_PMAX ||
1507     machine->machine_type == MACHINE_ARC)
1508     d->type = DISKIMAGE_SCSI;
1509 dpavlin 4
1510     if (prefix_i + prefix_f + prefix_s > 1) {
1511     fprintf(stderr, "Invalid disk image prefix(es). You can"
1512     "only use one of i, f, and s\nfor each disk image.\n");
1513     exit(1);
1514     }
1515    
1516 dpavlin 2 if (prefix_i)
1517     d->type = DISKIMAGE_IDE;
1518 dpavlin 4 if (prefix_f)
1519     d->type = DISKIMAGE_FLOPPY;
1520     if (prefix_s)
1521     d->type = DISKIMAGE_SCSI;
1522 dpavlin 2
1523     d->fname = strdup(fname);
1524     if (d->fname == NULL) {
1525     fprintf(stderr, "out of memory\n");
1526     exit(1);
1527     }
1528    
1529     d->logical_block_size = 512;
1530    
1531     /*
1532     * Is this a tape, CD-ROM or a normal disk?
1533     *
1534     * An intelligent guess, if no prefixes are used, would be that
1535 dpavlin 14 * filenames ending with .iso or .cdr are CD-ROM images.
1536 dpavlin 2 */
1537     if (prefix_t) {
1538     d->is_a_tape = 1;
1539     } else {
1540     if (prefix_c ||
1541     ((strlen(d->fname) > 4 &&
1542 dpavlin 14 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
1543     strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
1544 dpavlin 2 && !prefix_d)
1545     ) {
1546     d->is_a_cdrom = 1;
1547    
1548     /*
1549     * This is tricky. Should I use 512 or 2048 here?
1550     * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
1551     * per sector, but NetBSD 2.0_BETA suddenly ignores
1552     * this value and uses 2048 instead.
1553     *
1554     * OpenBSD/arc doesn't like 2048, it requires 512
1555     * to work correctly.
1556     *
1557     * TODO
1558     */
1559    
1560     #if 0
1561 dpavlin 22 if (machine->machine_type == MACHINE_PMAX)
1562 dpavlin 2 d->logical_block_size = 512;
1563     else
1564     d->logical_block_size = 2048;
1565     #endif
1566     d->logical_block_size = 512;
1567     }
1568     }
1569    
1570     diskimage_recalc_size(d);
1571    
1572 dpavlin 6 if ((d->total_size == 720*1024 || d->total_size == 1474560
1573     || d->total_size == 2949120 || d->total_size == 1228800)
1574     && !prefix_i && !prefix_s)
1575 dpavlin 4 d->type = DISKIMAGE_FLOPPY;
1576    
1577 dpavlin 6 switch (d->type) {
1578     case DISKIMAGE_FLOPPY:
1579     if (d->total_size < 737280) {
1580     fatal("\nTODO: small (non-80-cylinder) floppies?\n\n");
1581     exit(1);
1582     }
1583     d->cylinders = 80;
1584     d->heads = 2;
1585     d->sectors_per_track = d->total_size / (d->cylinders *
1586     d->heads * 512);
1587     break;
1588     default:/* Non-floppies: */
1589     d->heads = 16;
1590     d->sectors_per_track = 63;
1591     if (prefix_g) {
1592     d->chs_override = 1;
1593     d->heads = override_heads;
1594     d->sectors_per_track = override_spt;
1595     }
1596     bytespercyl = d->heads * d->sectors_per_track * 512;
1597     d->cylinders = d->total_size / bytespercyl;
1598     if (d->cylinders * bytespercyl < d->total_size)
1599     d->cylinders ++;
1600     }
1601    
1602 dpavlin 2 d->rpms = 3600;
1603    
1604     if (prefix_b)
1605     d->is_boot_device = 1;
1606    
1607     d->writable = access(fname, W_OK) == 0? 1 : 0;
1608    
1609     if (d->is_a_cdrom || prefix_r)
1610     d->writable = 0;
1611    
1612     d->f = fopen(fname, d->writable? "r+" : "r");
1613     if (d->f == NULL) {
1614     perror(fname);
1615     exit(1);
1616     }
1617    
1618 dpavlin 6 /* Calculate which ID to use: */
1619     if (prefix_id == -1) {
1620     int free = 0, collision = 1;
1621    
1622     while (collision) {
1623     collision = 0;
1624     d2 = machine->first_diskimage;
1625     while (d2 != NULL) {
1626     /* (don't compare against ourselves :) */
1627     if (d2 == d) {
1628     d2 = d2->next;
1629     continue;
1630     }
1631     if (d2->id == free && d2->type == d->type) {
1632     collision = 1;
1633     break;
1634     }
1635     d2 = d2->next;
1636     }
1637     if (!collision)
1638     id = free;
1639     else
1640     free ++;
1641     }
1642     } else {
1643     id = prefix_id;
1644     d2 = machine->first_diskimage;
1645     while (d2 != NULL) {
1646     /* (don't compare against ourselves :) */
1647     if (d2 == d) {
1648     d2 = d2->next;
1649     continue;
1650     }
1651     if (d2->id == id && d2->type == d->type) {
1652     fprintf(stderr, "disk image id %i "
1653     "already in use\n", id);
1654     exit(1);
1655     }
1656     d2 = d2->next;
1657     }
1658     }
1659    
1660     d->id = id;
1661    
1662 dpavlin 2 return id;
1663     }
1664    
1665    
1666     /*
1667     * diskimage_bootdev():
1668     *
1669 dpavlin 6 * Returns the disk id of the device which we're booting from. If typep is
1670     * non-NULL, the type is returned as well.
1671 dpavlin 2 *
1672     * If no disk was used as boot device, then -1 is returned. (In practice,
1673     * this is used to fake network (tftp) boot.)
1674     */
1675 dpavlin 6 int diskimage_bootdev(struct machine *machine, int *typep)
1676 dpavlin 2 {
1677 dpavlin 6 struct diskimage *d;
1678    
1679     d = machine->first_diskimage;
1680 dpavlin 2 while (d != NULL) {
1681 dpavlin 6 if (d->is_boot_device) {
1682     if (typep != NULL)
1683     *typep = d->type;
1684 dpavlin 2 return d->id;
1685 dpavlin 6 }
1686 dpavlin 2 d = d->next;
1687     }
1688    
1689     d = machine->first_diskimage;
1690 dpavlin 6 if (d != NULL) {
1691     if (typep != NULL)
1692     *typep = d->type;
1693 dpavlin 2 return d->id;
1694 dpavlin 6 }
1695 dpavlin 2
1696     return -1;
1697     }
1698    
1699    
1700     /*
1701 dpavlin 14 * diskimage_getname():
1702     *
1703     * Returns 1 if a valid disk image name was returned, 0 otherwise.
1704     */
1705     int diskimage_getname(struct machine *machine, int id, int type,
1706     char *buf, size_t bufsize)
1707     {
1708     struct diskimage *d = machine->first_diskimage;
1709    
1710     if (buf == NULL)
1711     return 0;
1712    
1713     while (d != NULL) {
1714     if (d->type == type && d->id == id) {
1715     char *p = strrchr(d->fname, '/');
1716     if (p == NULL)
1717     p = d->fname;
1718     else
1719     p ++;
1720     snprintf(buf, bufsize, "%s", p);
1721     return 1;
1722     }
1723     d = d->next;
1724     }
1725     return 0;
1726     }
1727    
1728    
1729     /*
1730 dpavlin 2 * diskimage_is_a_cdrom():
1731     *
1732 dpavlin 6 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1733 dpavlin 2 */
1734 dpavlin 6 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1735 dpavlin 2 {
1736     struct diskimage *d = machine->first_diskimage;
1737    
1738     while (d != NULL) {
1739 dpavlin 6 if (d->type == type && d->id == id)
1740 dpavlin 2 return d->is_a_cdrom;
1741     d = d->next;
1742     }
1743     return 0;
1744     }
1745    
1746    
1747     /*
1748     * diskimage_is_a_tape():
1749     *
1750 dpavlin 6 * Returns 1 if a disk image is a tape, 0 otherwise.
1751     *
1752 dpavlin 2 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1753     * boot strings.)
1754     */
1755 dpavlin 6 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1756 dpavlin 2 {
1757     struct diskimage *d = machine->first_diskimage;
1758    
1759     while (d != NULL) {
1760 dpavlin 6 if (d->type == type && d->id == id)
1761 dpavlin 2 return d->is_a_tape;
1762     d = d->next;
1763     }
1764     return 0;
1765     }
1766    
1767    
1768     /*
1769     * diskimage_dump_info():
1770     *
1771     * Debug dump of all diskimages that are loaded for a specific machine.
1772     */
1773     void diskimage_dump_info(struct machine *machine)
1774     {
1775 dpavlin 22 int iadd = DEBUG_INDENTATION;
1776 dpavlin 2 struct diskimage *d = machine->first_diskimage;
1777    
1778     while (d != NULL) {
1779     debug("diskimage: %s\n", d->fname);
1780     debug_indentation(iadd);
1781    
1782     switch (d->type) {
1783     case DISKIMAGE_SCSI:
1784     debug("SCSI");
1785     break;
1786     case DISKIMAGE_IDE:
1787     debug("IDE");
1788     break;
1789 dpavlin 4 case DISKIMAGE_FLOPPY:
1790     debug("FLOPPY");
1791     break;
1792 dpavlin 2 default:
1793     debug("UNKNOWN type %i", d->type);
1794     }
1795    
1796     debug(" %s", d->is_a_tape? "TAPE" :
1797     (d->is_a_cdrom? "CD-ROM" : "DISK"));
1798     debug(" id %i, ", d->id);
1799     debug("%s, ", d->writable? "read/write" : "read-only");
1800    
1801 dpavlin 4 if (d->type == DISKIMAGE_FLOPPY)
1802     debug("%lli KB", (long long) (d->total_size / 1024));
1803     else
1804     debug("%lli MB", (long long) (d->total_size / 1048576));
1805    
1806 dpavlin 6 if (d->type == DISKIMAGE_FLOPPY || d->chs_override)
1807     debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads,
1808     d->sectors_per_track);
1809     else
1810     debug(" (%lli sectors)", (long long)
1811     (d->total_size / 512));
1812 dpavlin 2
1813 dpavlin 6 if (d->is_boot_device)
1814     debug(" (BOOT)");
1815     debug("\n");
1816    
1817 dpavlin 2 debug_indentation(-iadd);
1818    
1819     d = d->next;
1820     }
1821     }
1822    

  ViewVC Help
Powered by ViewVC 1.1.26