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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 36 - (hide annotations)
Mon Oct 8 16:21:34 2007 UTC (13 years, 3 months ago) by dpavlin
File MIME type: text/plain
File size: 47581 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1497 2007/03/18 03:41:36 debug Exp $
20070224	Minor update to the initialization of the ns16550 in
		machine_walnut.c, to allow that machine type to boot with the
		new interrupt system (although it is still a dummy machine).
		Adding a wdc at 0x14000000 to machine_landisk.c, and fixing
		the SCIF serial interrupts of the SH4 cpu enough to get
		NetBSD/landisk booting from a disk image :-)  Adding a
		preliminary install instruction skeleton to guestoses.html.
20070306	Adding SH-IPL+G PROM emulation, and also passing the "end"
		symbol in r5 on bootup, for Landisk emulation. This is enough
		to get OpenBSD/landisk to install :)  Adding a preliminary
		install instruction skeleton to the documentation. SuperH
		emulation is still shaky, though :-/
20070307	Fixed a strangeness in memory_sh.c (read/write was never
		returned for any page). (Unknown whether this fixes any actual
		problems, though.)
20070308	dev_ram.c fix: invalidate code translations on writes to
		RAM, emulated as separate devices. Linux/dreamcast gets
		further in the boot process than before, but still bugs out
		in userland.
		Fixing bugs in the "stc.l gbr,@-rN" and "ldc.l @rN+,gbr" SuperH 
		instructions (they should NOT check the MD bit), allowing the
		Linux/dreamcast Live CD to reach userland correctly :-)
20070310	Changing the cpu name "Alpha" in src/useremul.c to "21364" to
		unbreak userland syscall emulation of FreeBSD/Alpha binaries.
20070314	Applying a patch from Michael Yaroslavtsev which fixes the
		previous Linux lib64 patch to the configure script.
20070315	Adding a (dummy) sun4v machine type, and SPARC T1 cpu type.
20070316	Creating a new directory, src/disk, and moving diskimage.c
		to it. Separating out bootblock loading stuff from emul.c into
		new files in src/disk.
		Adding some more SPARC registers.
20070318	Preparing/testing for a minirelease, 0.4.4.1.

==============  RELEASE 0.4.4.1  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26