/[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 22 - (hide annotations)
Mon Oct 8 16:19:37 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 45703 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1121 2006/02/18 21:03:08 debug Exp $
20051126	Cobalt and PReP now work with the 21143 NIC.
		Continuing on Alpha dyntrans things.
		Fixing some more left-shift-by-24 to unsigned.
20051127	Working on OpenFirmware emulation; major cleanup/redesign.
		Progress on MacPPC emulation: NetBSD detects two CPUs (when
		running with -n 2), framebuffer output (for text) works.
		Adding quick-hack Bandit PCI controller and "gc" interrupt
		controller for MacPPC.
20051128	Changing from a Bandit to a Uni-North controller for macppc.
		Continuing on OpenFirmware and MacPPC emulation in general
		(obio controller, and wdc attached to the obio seems to work).
20051129	More work on MacPPC emulation (adding a dummy ADB controller).
		Continuing the PCI bus cleanup (endianness and tag composition)
		and rewriting all PCI controllers' access functions.
20051130	Various minor PPC dyntrans optimizations.
		Manually inlining some parts of the framebuffer redraw routine.
		Slowly beginning the conversion of the old MIPS emulation into
		dyntrans (but this will take quite some time to get right).
		Generalizing quick_pc_to_pointers.
20051201	Documentation update (David Muse has made available a kernel
		which simplifies Debian/DECstation installation).
		Continuing on the ADB bus controller.
20051202	Beginning a rewrite of the Zilog serial controller (dev_zs).
20051203	Continuing on the zs rewrite (now called dev_z8530); conversion
		to devinit style.
		Reworking some of the input-only vs output-only vs input-output
		details of src/console.c, better warning messages, and adding
		a debug dump.
		Removing the concept of "device state"; it wasn't really used.
		Changing some debug output (-vv should now be used to show all
		details about devices and busses; not shown during normal
		startup anymore).
		Beginning on some SPARC instruction disassembly support.
20051204	Minor PPC updates (WALNUT skeleton stuff).
		Continuing on the MIPS dyntrans rewrite.
		More progress on the ADB controller (a keyboard is "detected"
		by NetBSD and OpenBSD).
		Downgrading OpenBSD/arc as a guest OS from "working" to
		"almost working" in the documentation.
		Progress on Algor emulation ("v3" PCI controller).
20051205	Minor updates.
20051207	Sorting devices according to address; this reduces complexity
		of device lookups from O(n) to O(log n) in memory_rw (but no
		real performance increase (yet) in experiments).
20051210	Beginning the work on native dyntrans backends (by making a
		simple skeleton; so far only for Alpha hosts).
20051211	Some very minor SPARC updates.
20051215	Fixing a bug in the MIPS mul (note: not mult) instruction,
		so it also works with non-64-bit emulation. (Thanks to Alec
		Voropay for noticing the problem.)
20051216	More work on the fake/empty/simple/skeleton/whatever backend;
		performance doesn't increase, so this isn't really worth it,
		but it was probably worth it to prepare for a real backend
		later.
20051219	More instr call statistics gathering and analysis stuff.
20051220	Another fix for MIPS 'mul'. Also converting mul and {d,}cl{o,z}
		to dyntrans.
		memory_ppc.c syntax error fix (noticed by Peter Valchev).
		Beginning to move out machines from src/machine.c into
		individual files in src/machines (in a way similar to the
		autodev system for devices).
20051222	Updating the documentation regarding NetBSD/pmax 3.0.
20051223	- " - NetBSD/cats 3.0.
20051225	- " - NetBSD/hpcmips 3.0.
20051226	Continuing on the machine registry redesign.
		Adding support for ARM rrx (33-bit rotate).
		Fixing some signed/unsigned issues (exposed by gcc -W).
20051227	Fixing the bug which prevented a NetBSD/prep 3.0 install kernel
		from starting (triggered when an mtmsr was the last instruction
		on a page). Unfortunately not enough to get the kernel to run
		as well as the 2.1 kernels did.
20051230	Some dyntrans refactoring.
20051231	Continuing on the machine registry redesign.
20060101-10	Continuing... moving more machines. Moving MD interrupt stuff
		from machine.c into a new src/machines/interrupts.c.
20060114	Adding various mvmeppc machine skeletons.
20060115	Continuing on mvme* stuff. NetBSD/mvmeppc prints boot messages
		(for MVME1600) and reaches the root device prompt, but no
		specific hardware devices are emulated yet.
20060116	Minor updates to the mvme1600 emulation mode; the Eagle PCI bus
		seems to work without much modification, and a 21143 can be
		detected, interrupts might work (but untested so far).
		Adding a fake MK48Txx (mkclock) device, for NetBSD/mvmeppc.
20060121	Adding an aux control register for ARM. (A BIG thank you to
		Olivier Houchard for tracking down this bug.)
20060122	Adding more ARM instructions (smulXY), and dev_iq80321_7seg.
20060124	Adding disassembly of more ARM instructions (mia*, mra/mar),
		and some semi-bogus XScale and i80321 registers.
20060201-02	Various minor updates. Moving the last machines out of
		machine.c.
20060204	Adding a -c command line option, for running debugger commands
		before the simulation starts, but after all files have been
		loaded.
		Minor iq80321-related updates.
20060209	Minor hacks (DEVINIT macro, etc).
		Preparing for the generalization of the 64-bit dyntrans address
		translation subsystem.
20060216	Adding ARM ldrd (double-register load).
20060217	Continuing on various ARM-related stuff.
20060218	More progress on the ATA/wdc emulation for NetBSD/iq80321.
		NetBSD/evbarm can now be installed :-)  Updating the docs, etc.
		Continuing on Algor emulation.

==============  RELEASE 0.3.8  ==============


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 22 * $Id: diskimage.c,v 1.108 2006/01/11 20:14:41 debug Exp $
29 dpavlin 2 *
30     * Disk image support.
31     *
32     * TODO: There's probably a bug in the tape support:
33     * Let's say there are 10240 bytes left in a file, and 10240
34     * bytes are read. Then feof() is not true yet (?), so the next
35     * read will also return 10240 bytes (but all zeroes), and then after
36     * that return feof (which results in a filemark). This is probably
37     * trivial to fix, but I don't feel like it right now.
38     *
39 dpavlin 6 * TODO: diskimage_remove()? This would be useful for floppies in PC-style
40     * machines, where disks may need to be swapped during boot etc.
41 dpavlin 2 */
42    
43     #include <stdio.h>
44     #include <stdlib.h>
45     #include <string.h>
46     #include <unistd.h>
47     #include <sys/types.h>
48     #include <sys/stat.h>
49    
50     #include "cpu.h"
51     #include "diskimage.h"
52     #include "machine.h"
53     #include "misc.h"
54    
55    
56 dpavlin 20 /* #define debug fatal */
57    
58 dpavlin 2 extern int single_step;
59    
60 dpavlin 6 static char *diskimage_types[] = DISKIMAGE_TYPES;
61 dpavlin 2
62     static struct scsi_transfer *first_free_scsi_transfer_alloc = NULL;
63    
64    
65     /**************************************************************************/
66    
67     /*
68     * my_fseek():
69     *
70     * A helper function, like fseek() but takes off_t. If the system has
71     * fseeko, then that is used. Otherwise I try to fake off_t offsets here.
72     *
73     * The correct position is reached by seeking 2 billion bytes at a time
74     * (or less). Note: This method is only used for SEEK_SET, for SEEK_CUR
75     * and SEEK_END, normal fseek() is used!
76     *
77     * TODO: It seemed to work on Linux/i386, but not on Solaris/sparc (?).
78     * Anyway, most modern systems have fseeko(), so it shouldn't be a problem.
79     */
80     static int my_fseek(FILE *f, off_t offset, int whence)
81     {
82     #ifdef HACK_FSEEKO
83     if (whence == SEEK_SET) {
84     int res = 0;
85     off_t curoff = 0;
86     off_t cur_step;
87    
88     fseek(f, 0, SEEK_SET);
89     while (curoff < offset) {
90     /* How far to seek? */
91     cur_step = offset - curoff;
92     if (cur_step > 2000000000)
93     cur_step = 2000000000;
94     res = fseek(f, cur_step, SEEK_CUR);
95     if (res)
96     return res;
97     curoff += cur_step;
98     }
99     return 0;
100     } else
101     return fseek(f, offset, whence);
102     #else
103     return fseeko(f, offset, whence);
104     #endif
105     }
106    
107    
108     /**************************************************************************/
109    
110    
111     /*
112     * scsi_transfer_alloc():
113     *
114     * Allocates memory for a new scsi_transfer struct, and fills it with
115     * sane data (NULL pointers).
116     * The return value is a pointer to the new struct. If allocation
117     * failed, the program exits.
118     */
119     struct scsi_transfer *scsi_transfer_alloc(void)
120     {
121     struct scsi_transfer *p;
122    
123     if (first_free_scsi_transfer_alloc != NULL) {
124     p = first_free_scsi_transfer_alloc;
125     first_free_scsi_transfer_alloc = p->next_free;
126     } else {
127     p = malloc(sizeof(struct scsi_transfer));
128     if (p == NULL) {
129     fprintf(stderr, "scsi_transfer_alloc(): out "
130     "of memory\n");
131     exit(1);
132     }
133     }
134    
135     memset(p, 0, sizeof(struct scsi_transfer));
136    
137     return p;
138     }
139    
140    
141     /*
142     * scsi_transfer_free():
143     *
144     * Frees the space used by a scsi_transfer struct. All buffers refered
145     * to by the scsi_transfer struct are freed.
146     */
147     void scsi_transfer_free(struct scsi_transfer *p)
148     {
149     if (p == NULL) {
150     fprintf(stderr, "scsi_transfer_free(): p == NULL\n");
151     exit(1);
152     }
153    
154     if (p->msg_out != NULL)
155     free(p->msg_out);
156     if (p->cmd != NULL)
157     free(p->cmd);
158     if (p->data_out != NULL)
159     free(p->data_out);
160    
161     if (p->data_in != NULL)
162     free(p->data_in);
163     if (p->msg_in != NULL)
164     free(p->msg_in);
165     if (p->status != NULL)
166     free(p->status);
167    
168     p->next_free = first_free_scsi_transfer_alloc;
169     first_free_scsi_transfer_alloc = p;
170     }
171    
172    
173     /*
174     * scsi_transfer_allocbuf():
175     *
176     * Helper function, used by diskimage_scsicommand(), and SCSI controller
177     * devices. Example of usage:
178     *
179     * scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1);
180     */
181     void scsi_transfer_allocbuf(size_t *lenp, unsigned char **pp, size_t want_len,
182     int clearflag)
183     {
184     unsigned char *p = (*pp);
185    
186     if (p != NULL) {
187     printf("WARNING! scsi_transfer_allocbuf(): old pointer "
188     "was not NULL, freeing it now\n");
189     free(p);
190     }
191    
192     (*lenp) = want_len;
193     if ((p = malloc(want_len)) == NULL) {
194     fprintf(stderr, "scsi_transfer_allocbuf(): out of "
195     "memory trying to allocate %li bytes\n", (long)want_len);
196     exit(1);
197     }
198    
199     if (clearflag)
200     memset(p, 0, want_len);
201    
202     (*pp) = p;
203     }
204    
205    
206     /**************************************************************************/
207    
208    
209     /*
210     * diskimage_exist():
211     *
212 dpavlin 6 * Returns 1 if the specified disk id (for a specific type) exists, 0
213     * otherwise.
214 dpavlin 2 */
215 dpavlin 6 int diskimage_exist(struct machine *machine, int id, int type)
216 dpavlin 2 {
217     struct diskimage *d = machine->first_diskimage;
218    
219     while (d != NULL) {
220 dpavlin 6 if (d->type == type && d->id == id)
221 dpavlin 2 return 1;
222     d = d->next;
223     }
224     return 0;
225     }
226    
227    
228     /*
229     * diskimage_recalc_size():
230     *
231     * Recalculate a disk's size by stat()-ing it.
232     * d is assumed to be non-NULL.
233     */
234     static void diskimage_recalc_size(struct diskimage *d)
235     {
236     struct stat st;
237     int res;
238     off_t size = 0;
239    
240     res = stat(d->fname, &st);
241     if (res) {
242     fprintf(stderr, "[ diskimage_recalc_size(): could not stat "
243     "'%s' ]\n", d->fname);
244     return;
245     }
246    
247     size = st.st_size;
248    
249     /*
250     * TODO: CD-ROM devices, such as /dev/cd0c, how can one
251     * check how much data is on that cd-rom without reading it?
252     * For now, assume some large number, hopefully it will be
253     * enough to hold any cd-rom image.
254     */
255     if (d->is_a_cdrom && size == 0)
256     size = 762048000;
257    
258     d->total_size = size;
259     d->ncyls = d->total_size / 1048576;
260 dpavlin 6
261     /* TODO: There is a mismatch between d->ncyls and d->cylinders,
262     SCSI-based stuff usually doesn't care. TODO: Fix this. */
263 dpavlin 2 }
264    
265    
266     /*
267     * diskimage_getsize():
268     *
269 dpavlin 6 * Returns -1 if the specified disk id/type does not exists, otherwise
270 dpavlin 2 * the size of the disk image is returned.
271     */
272 dpavlin 6 int64_t diskimage_getsize(struct machine *machine, int id, int type)
273 dpavlin 2 {
274     struct diskimage *d = machine->first_diskimage;
275    
276     while (d != NULL) {
277 dpavlin 6 if (d->type == type && d->id == id)
278 dpavlin 2 return d->total_size;
279     d = d->next;
280     }
281     return -1;
282     }
283    
284    
285     /*
286 dpavlin 6 * diskimage_getchs():
287     *
288     * Returns the current CHS values of a disk image.
289     */
290     void diskimage_getchs(struct machine *machine, int id, int type,
291     int *c, int *h, int *s)
292     {
293     struct diskimage *d = machine->first_diskimage;
294    
295     while (d != NULL) {
296     if (d->type == type && d->id == id) {
297     *c = d->cylinders;
298     *h = d->heads;
299     *s = d->sectors_per_track;
300     return;
301     }
302     d = d->next;
303     }
304     fatal("diskimage_getchs(): disk id %i (type %i) not found?\n",
305     id, diskimage_types[type]);
306     exit(1);
307     }
308    
309    
310     /*
311 dpavlin 2 * diskimage__return_default_status_and_message():
312     *
313     * Set the status and msg_in parts of a scsi_transfer struct
314     * to default values (msg_in = 0x00, status = 0x00).
315     */
316     static void diskimage__return_default_status_and_message(
317     struct scsi_transfer *xferp)
318     {
319     scsi_transfer_allocbuf(&xferp->status_len, &xferp->status, 1, 0);
320     xferp->status[0] = 0x00;
321     scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1, 0);
322     xferp->msg_in[0] = 0x00;
323     }
324    
325    
326     /*
327     * diskimage__switch_tape():
328     *
329     * Used by the SPACE command. (d is assumed to be non-NULL.)
330     */
331     static void diskimage__switch_tape(struct diskimage *d)
332     {
333     char tmpfname[1000];
334    
335     snprintf(tmpfname, sizeof(tmpfname), "%s.%i",
336     d->fname, d->tape_filenr);
337     tmpfname[sizeof(tmpfname)-1] = '\0';
338    
339     if (d->f != NULL)
340     fclose(d->f);
341    
342     d->f = fopen(tmpfname, d->writable? "r+" : "r");
343     if (d->f == NULL) {
344     fprintf(stderr, "[ diskimage__switch_tape(): could not "
345     "(re)open '%s' ]\n", tmpfname);
346     /* TODO: return error */
347     }
348     d->tape_offset = 0;
349     }
350    
351    
352     /*
353     * diskimage_access__cdrom():
354     *
355     * This is a special-case function, called from diskimage__internal_access().
356     * On my FreeBSD 4.9 system, the cdrom device /dev/cd0c seems to not be able
357     * to handle something like "fseek(512); fread(512);" but it handles
358     * "fseek(2048); fread(512);" just fine. So, if diskimage__internal_access()
359     * fails in reading a block of data, this function is called as an attempt to
360     * align reads at 2048-byte sectors instead.
361     *
362     * (Ugly hack. TODO: how to solve this cleanly?)
363     *
364     * NOTE: Returns the number of bytes read, 0 if nothing was successfully
365     * read. (These are not the same as diskimage_access()).
366     */
367     #define CDROM_SECTOR_SIZE 2048
368     static size_t diskimage_access__cdrom(struct diskimage *d, off_t offset,
369     unsigned char *buf, size_t len)
370     {
371     off_t aligned_offset;
372     size_t bytes_read, total_copied = 0;
373     unsigned char cdrom_buf[CDROM_SECTOR_SIZE];
374     off_t buf_ofs, i = 0;
375    
376     /* printf("diskimage_access__cdrom(): offset=0x%llx size=%lli\n",
377     (long long)offset, (long long)len); */
378    
379     aligned_offset = (offset / CDROM_SECTOR_SIZE) * CDROM_SECTOR_SIZE;
380     my_fseek(d->f, aligned_offset, SEEK_SET);
381    
382     while (len != 0) {
383     bytes_read = fread(cdrom_buf, 1, CDROM_SECTOR_SIZE, d->f);
384     if (bytes_read != CDROM_SECTOR_SIZE)
385     return 0;
386    
387     /* Copy (part of) cdrom_buf into buf: */
388     buf_ofs = offset - aligned_offset;
389     while (buf_ofs < CDROM_SECTOR_SIZE && len != 0) {
390     buf[i ++] = cdrom_buf[buf_ofs ++];
391     total_copied ++;
392     len --;
393     }
394    
395     aligned_offset += CDROM_SECTOR_SIZE;
396     offset = aligned_offset;
397     }
398    
399     return total_copied;
400     }
401    
402    
403     /*
404     * diskimage__internal_access():
405     *
406     * Read from or write to a struct diskimage.
407     *
408     * Returns 1 if the access completed successfully, 0 otherwise.
409     */
410     static int diskimage__internal_access(struct diskimage *d, int writeflag,
411     off_t offset, unsigned char *buf, size_t len)
412     {
413 dpavlin 12 ssize_t lendone;
414 dpavlin 2 int res;
415    
416     if (buf == NULL) {
417     fprintf(stderr, "diskimage__internal_access(): buf = NULL\n");
418     exit(1);
419     }
420     if (len == 0)
421     return 1;
422     if (d->f == NULL)
423     return 0;
424    
425     res = my_fseek(d->f, offset, SEEK_SET);
426     if (res != 0) {
427     fatal("[ diskimage__internal_access(): fseek() failed on "
428     "disk id %i \n", d->id);
429     return 0;
430     }
431    
432     if (writeflag) {
433     if (!d->writable)
434     return 0;
435    
436     lendone = fwrite(buf, 1, len, d->f);
437     } else {
438     /*
439     * Special case for CD-ROMs. Actually, this is not needed
440     * for .iso images, only for physical CDROMS on some OSes,
441     * such as FreeBSD.
442     */
443     if (d->is_a_cdrom)
444     lendone = diskimage_access__cdrom(d, offset, buf, len);
445     else
446     lendone = fread(buf, 1, len, d->f);
447    
448     if (lendone < (ssize_t)len)
449     memset(buf + lendone, 0, len - lendone);
450     }
451    
452     /* Warn about non-complete data transfers: */
453     if (lendone != (ssize_t)len) {
454     fatal("[ diskimage__internal_access(): disk_id %i, offset %lli"
455     ", transfer not completed. len=%i, len_done=%i ]\n",
456     d->id, (long long)offset, (int)len, (int)lendone);
457     return 0;
458     }
459    
460     return 1;
461     }
462    
463    
464     /*
465     * diskimage_scsicommand():
466     *
467     * Perform a SCSI command on a disk image.
468     *
469     * The xferp points to a scsi_transfer struct, containing msg_out, command,
470     * and data_out coming from the SCSI controller device. This function
471     * interprets the command, and (if necessary) creates responses in
472     * data_in, msg_in, and status.
473     *
474     * Returns:
475     * 2 if the command expects data from the DATA_OUT phase,
476     * 1 if otherwise ok,
477     * 0 on error.
478     */
479 dpavlin 6 int diskimage_scsicommand(struct cpu *cpu, int id, int type,
480 dpavlin 2 struct scsi_transfer *xferp)
481     {
482 dpavlin 14 char namebuf[16];
483 dpavlin 20 int retlen, i, q;
484 dpavlin 2 uint64_t size;
485     int64_t ofs;
486     int pagecode;
487     struct machine *machine = cpu->machine;
488     struct diskimage *d;
489    
490     if (machine == NULL) {
491     fatal("[ diskimage_scsicommand(): machine == NULL ]\n");
492     return 0;
493     }
494    
495     d = machine->first_diskimage;
496     while (d != NULL) {
497 dpavlin 6 if (d->type == type && d->id == id)
498 dpavlin 2 break;
499     d = d->next;
500     }
501     if (d == NULL) {
502 dpavlin 6 fprintf(stderr, "[ diskimage_scsicommand(): %s "
503     " id %i not connected? ]\n", diskimage_types[type], id);
504 dpavlin 2 }
505    
506     if (xferp->cmd == NULL) {
507     fatal("[ diskimage_scsicommand(): cmd == NULL ]\n");
508     return 0;
509     }
510    
511     if (xferp->cmd_len < 1) {
512     fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n",
513     xferp->cmd_len);
514     return 0;
515     }
516    
517     debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ",
518 dpavlin 6 id, xferp->cmd[0]);
519 dpavlin 2
520     #if 0
521     fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",
522 dpavlin 6 id, xferp->cmd[0], xferp->cmd_len);
523 dpavlin 2 for (i=0; i<xferp->cmd_len; i++)
524     fatal(" %02x", xferp->cmd[i]);
525     fatal("\n");
526     if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11)
527     single_step = 1;
528     #endif
529    
530     #if 0
531     {
532     static FILE *f = NULL;
533     if (f == NULL)
534     f = fopen("scsi_log.txt", "w");
535     if (f != NULL) {
536     int i;
537 dpavlin 6 fprintf(f, "id=%i cmd =", id);
538 dpavlin 2 for (i=0; i<xferp->cmd_len; i++)
539     fprintf(f, " %02x", xferp->cmd[i]);
540     fprintf(f, "\n");
541     fflush(f);
542     }
543     }
544     #endif
545    
546     switch (xferp->cmd[0]) {
547    
548     case SCSICMD_TEST_UNIT_READY:
549     debug("TEST_UNIT_READY");
550     if (xferp->cmd_len != 6)
551     debug(" (weird len=%i)", xferp->cmd_len);
552    
553     /* TODO: bits 765 of buf[1] contains the LUN */
554     if (xferp->cmd[1] != 0x00)
555     fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x"
556     " not yet implemented\n", (int)xferp->cmd[1]);
557    
558     diskimage__return_default_status_and_message(xferp);
559     break;
560    
561     case SCSICMD_INQUIRY:
562     debug("INQUIRY");
563     if (xferp->cmd_len != 6)
564     debug(" (weird len=%i)", xferp->cmd_len);
565     if (xferp->cmd[1] != 0x00) {
566     debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet "
567     "implemented\n", (int)xferp->cmd[1]);
568    
569     break;
570     }
571    
572     /* Return values: */
573     retlen = xferp->cmd[4];
574     if (retlen < 36) {
575     fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen);
576     retlen = 36;
577     }
578    
579     /* Return data: */
580     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
581     retlen, 1);
582     xferp->data_in[0] = 0x00; /* 0x00 = Direct-access disk */
583     xferp->data_in[1] = 0x00; /* 0x00 = non-removable */
584     xferp->data_in[2] = 0x02; /* SCSI-2 */
585     #if 0
586     xferp->data_in[3] = 0x02; /* Response data format = SCSI-2 */
587     #endif
588     xferp->data_in[4] = retlen - 4; /* Additional length */
589     xferp->data_in[4] = 0x2c - 4; /* Additional length */
590     xferp->data_in[6] = 0x04; /* ACKREQQ */
591     xferp->data_in[7] = 0x60; /* WBus32, WBus16 */
592    
593 dpavlin 14 /* These are padded with spaces: */
594 dpavlin 2
595 dpavlin 20 memcpy(xferp->data_in+8, "GXemul ", 8);
596 dpavlin 14 if (diskimage_getname(cpu->machine, id,
597     type, namebuf, sizeof(namebuf))) {
598 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 22 d->type = DISKIMAGE_IDE;
1503 dpavlin 2
1504 dpavlin 22 if (machine->machine_type == MACHINE_PMAX ||
1505     machine->machine_type == MACHINE_ARC)
1506     d->type = DISKIMAGE_SCSI;
1507 dpavlin 4
1508     if (prefix_i + prefix_f + prefix_s > 1) {
1509     fprintf(stderr, "Invalid disk image prefix(es). You can"
1510     "only use one of i, f, and s\nfor each disk image.\n");
1511     exit(1);
1512     }
1513    
1514 dpavlin 2 if (prefix_i)
1515     d->type = DISKIMAGE_IDE;
1516 dpavlin 4 if (prefix_f)
1517     d->type = DISKIMAGE_FLOPPY;
1518     if (prefix_s)
1519     d->type = DISKIMAGE_SCSI;
1520 dpavlin 2
1521     d->fname = strdup(fname);
1522     if (d->fname == NULL) {
1523     fprintf(stderr, "out of memory\n");
1524     exit(1);
1525     }
1526    
1527     d->logical_block_size = 512;
1528    
1529     /*
1530     * Is this a tape, CD-ROM or a normal disk?
1531     *
1532     * An intelligent guess, if no prefixes are used, would be that
1533 dpavlin 14 * filenames ending with .iso or .cdr are CD-ROM images.
1534 dpavlin 2 */
1535     if (prefix_t) {
1536     d->is_a_tape = 1;
1537     } else {
1538     if (prefix_c ||
1539     ((strlen(d->fname) > 4 &&
1540 dpavlin 14 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
1541     strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
1542 dpavlin 2 && !prefix_d)
1543     ) {
1544     d->is_a_cdrom = 1;
1545    
1546     /*
1547     * This is tricky. Should I use 512 or 2048 here?
1548     * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
1549     * per sector, but NetBSD 2.0_BETA suddenly ignores
1550     * this value and uses 2048 instead.
1551     *
1552     * OpenBSD/arc doesn't like 2048, it requires 512
1553     * to work correctly.
1554     *
1555     * TODO
1556     */
1557    
1558     #if 0
1559 dpavlin 22 if (machine->machine_type == MACHINE_PMAX)
1560 dpavlin 2 d->logical_block_size = 512;
1561     else
1562     d->logical_block_size = 2048;
1563     #endif
1564     d->logical_block_size = 512;
1565     }
1566     }
1567    
1568     diskimage_recalc_size(d);
1569    
1570 dpavlin 6 if ((d->total_size == 720*1024 || d->total_size == 1474560
1571     || d->total_size == 2949120 || d->total_size == 1228800)
1572     && !prefix_i && !prefix_s)
1573 dpavlin 4 d->type = DISKIMAGE_FLOPPY;
1574    
1575 dpavlin 6 switch (d->type) {
1576     case DISKIMAGE_FLOPPY:
1577     if (d->total_size < 737280) {
1578     fatal("\nTODO: small (non-80-cylinder) floppies?\n\n");
1579     exit(1);
1580     }
1581     d->cylinders = 80;
1582     d->heads = 2;
1583     d->sectors_per_track = d->total_size / (d->cylinders *
1584     d->heads * 512);
1585     break;
1586     default:/* Non-floppies: */
1587     d->heads = 16;
1588     d->sectors_per_track = 63;
1589     if (prefix_g) {
1590     d->chs_override = 1;
1591     d->heads = override_heads;
1592     d->sectors_per_track = override_spt;
1593     }
1594     bytespercyl = d->heads * d->sectors_per_track * 512;
1595     d->cylinders = d->total_size / bytespercyl;
1596     if (d->cylinders * bytespercyl < d->total_size)
1597     d->cylinders ++;
1598     }
1599    
1600 dpavlin 2 d->rpms = 3600;
1601    
1602     if (prefix_b)
1603     d->is_boot_device = 1;
1604    
1605     d->writable = access(fname, W_OK) == 0? 1 : 0;
1606    
1607     if (d->is_a_cdrom || prefix_r)
1608     d->writable = 0;
1609    
1610     d->f = fopen(fname, d->writable? "r+" : "r");
1611     if (d->f == NULL) {
1612     perror(fname);
1613     exit(1);
1614     }
1615    
1616 dpavlin 6 /* Calculate which ID to use: */
1617     if (prefix_id == -1) {
1618     int free = 0, collision = 1;
1619    
1620     while (collision) {
1621     collision = 0;
1622     d2 = machine->first_diskimage;
1623     while (d2 != NULL) {
1624     /* (don't compare against ourselves :) */
1625     if (d2 == d) {
1626     d2 = d2->next;
1627     continue;
1628     }
1629     if (d2->id == free && d2->type == d->type) {
1630     collision = 1;
1631     break;
1632     }
1633     d2 = d2->next;
1634     }
1635     if (!collision)
1636     id = free;
1637     else
1638     free ++;
1639     }
1640     } else {
1641     id = prefix_id;
1642     d2 = machine->first_diskimage;
1643     while (d2 != NULL) {
1644     /* (don't compare against ourselves :) */
1645     if (d2 == d) {
1646     d2 = d2->next;
1647     continue;
1648     }
1649     if (d2->id == id && d2->type == d->type) {
1650     fprintf(stderr, "disk image id %i "
1651     "already in use\n", id);
1652     exit(1);
1653     }
1654     d2 = d2->next;
1655     }
1656     }
1657    
1658     d->id = id;
1659    
1660 dpavlin 2 return id;
1661     }
1662    
1663    
1664     /*
1665     * diskimage_bootdev():
1666     *
1667 dpavlin 6 * Returns the disk id of the device which we're booting from. If typep is
1668     * non-NULL, the type is returned as well.
1669 dpavlin 2 *
1670     * If no disk was used as boot device, then -1 is returned. (In practice,
1671     * this is used to fake network (tftp) boot.)
1672     */
1673 dpavlin 6 int diskimage_bootdev(struct machine *machine, int *typep)
1674 dpavlin 2 {
1675 dpavlin 6 struct diskimage *d;
1676    
1677     d = machine->first_diskimage;
1678 dpavlin 2 while (d != NULL) {
1679 dpavlin 6 if (d->is_boot_device) {
1680     if (typep != NULL)
1681     *typep = d->type;
1682 dpavlin 2 return d->id;
1683 dpavlin 6 }
1684 dpavlin 2 d = d->next;
1685     }
1686    
1687     d = machine->first_diskimage;
1688 dpavlin 6 if (d != NULL) {
1689     if (typep != NULL)
1690     *typep = d->type;
1691 dpavlin 2 return d->id;
1692 dpavlin 6 }
1693 dpavlin 2
1694     return -1;
1695     }
1696    
1697    
1698     /*
1699 dpavlin 14 * diskimage_getname():
1700     *
1701     * Returns 1 if a valid disk image name was returned, 0 otherwise.
1702     */
1703     int diskimage_getname(struct machine *machine, int id, int type,
1704     char *buf, size_t bufsize)
1705     {
1706     struct diskimage *d = machine->first_diskimage;
1707    
1708     if (buf == NULL)
1709     return 0;
1710    
1711     while (d != NULL) {
1712     if (d->type == type && d->id == id) {
1713     char *p = strrchr(d->fname, '/');
1714     if (p == NULL)
1715     p = d->fname;
1716     else
1717     p ++;
1718     snprintf(buf, bufsize, "%s", p);
1719     return 1;
1720     }
1721     d = d->next;
1722     }
1723     return 0;
1724     }
1725    
1726    
1727     /*
1728 dpavlin 2 * diskimage_is_a_cdrom():
1729     *
1730 dpavlin 6 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1731 dpavlin 2 */
1732 dpavlin 6 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1733 dpavlin 2 {
1734     struct diskimage *d = machine->first_diskimage;
1735    
1736     while (d != NULL) {
1737 dpavlin 6 if (d->type == type && d->id == id)
1738 dpavlin 2 return d->is_a_cdrom;
1739     d = d->next;
1740     }
1741     return 0;
1742     }
1743    
1744    
1745     /*
1746     * diskimage_is_a_tape():
1747     *
1748 dpavlin 6 * Returns 1 if a disk image is a tape, 0 otherwise.
1749     *
1750 dpavlin 2 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1751     * boot strings.)
1752     */
1753 dpavlin 6 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1754 dpavlin 2 {
1755     struct diskimage *d = machine->first_diskimage;
1756    
1757     while (d != NULL) {
1758 dpavlin 6 if (d->type == type && d->id == id)
1759 dpavlin 2 return d->is_a_tape;
1760     d = d->next;
1761     }
1762     return 0;
1763     }
1764    
1765    
1766     /*
1767     * diskimage_dump_info():
1768     *
1769     * Debug dump of all diskimages that are loaded for a specific machine.
1770     */
1771     void diskimage_dump_info(struct machine *machine)
1772     {
1773 dpavlin 22 int iadd = DEBUG_INDENTATION;
1774 dpavlin 2 struct diskimage *d = machine->first_diskimage;
1775    
1776     while (d != NULL) {
1777     debug("diskimage: %s\n", d->fname);
1778     debug_indentation(iadd);
1779    
1780     switch (d->type) {
1781     case DISKIMAGE_SCSI:
1782     debug("SCSI");
1783     break;
1784     case DISKIMAGE_IDE:
1785     debug("IDE");
1786     break;
1787 dpavlin 4 case DISKIMAGE_FLOPPY:
1788     debug("FLOPPY");
1789     break;
1790 dpavlin 2 default:
1791     debug("UNKNOWN type %i", d->type);
1792     }
1793    
1794     debug(" %s", d->is_a_tape? "TAPE" :
1795     (d->is_a_cdrom? "CD-ROM" : "DISK"));
1796     debug(" id %i, ", d->id);
1797     debug("%s, ", d->writable? "read/write" : "read-only");
1798    
1799 dpavlin 4 if (d->type == DISKIMAGE_FLOPPY)
1800     debug("%lli KB", (long long) (d->total_size / 1024));
1801     else
1802     debug("%lli MB", (long long) (d->total_size / 1048576));
1803    
1804 dpavlin 6 if (d->type == DISKIMAGE_FLOPPY || d->chs_override)
1805     debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads,
1806     d->sectors_per_track);
1807     else
1808     debug(" (%lli sectors)", (long long)
1809     (d->total_size / 512));
1810 dpavlin 2
1811 dpavlin 6 if (d->is_boot_device)
1812     debug(" (BOOT)");
1813     debug("\n");
1814    
1815 dpavlin 2 debug_indentation(-iadd);
1816    
1817     d = d->next;
1818     }
1819     }
1820    

  ViewVC Help
Powered by ViewVC 1.1.26