/[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 10 - (hide annotations)
Mon Oct 8 16:18:27 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 44350 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.815 2005/06/27 23:04:35 debug Exp $
20050617	Experimenting some more with netbooting OpenBSD/sgi. Adding
		a hack which allows emulated ethernet networks to be
		distributed across multiple emulator processes.
20050618	Minor updates (documentation, dummy YAMON emulation, etc).
20050620	strcpy/strcat -> strlcpy/strlcat updates.
		Some more progress on evbmips (Malta).
20050621	Adding a section to doc/configfiles.html about ethernet
		emulation across multiple hosts.
		Beginning the work on the ARM translation engine (using the
		dynamic-but-not-binary translation method).
		Fixing a bintrans bug: 0x9fc00000 should always be treated as
		PROM area, just as 0xbfc00000 is.
		Minor progress on Malta emulation (the PCI-ISA bus).
20050622	NetBSD/evbmips can now be installed (using another emulated
		machine) and run (including userland and so on). :-)
		Spliting up the bintrans haddr_entry field into two (one for
		read, one for write). Probably not much of a speed increase,
		though.
		Updating some NetBSD 2.0 -> 2.0.2 in the documentation.
20050623	Minor updates (documentation, the TODO file, etc).
		gzipped kernels are now always automagically gunzipped when
		loaded.
20050624	Adding a dummy Playstation Portable (PSP) mode, just barely
		enough to run Hello World (in weird colors :-).
		Removing the -b command line option; old bintrans is enabled
		by default instead. It makes more sense.
		Trying to finally fix the non-working performance measurement
		thing (instr/second etc).
20050625	Continuing on the essential basics for ARM emulation. Two
		instructions seem to work, a branch and a simple "mov". (The
		mov arguments are not correct yet.) Performance is definitely
		reasonable.
		Various other minor updates.
		Adding the ARM "bl" instruction.
		Adding support for combining multiple ARM instructions into one
		function call. ("mov" + "mov" is the only one implemented so
		far, but it seems to work.)
		Cleaning up some IP32 interrupt things (crime/mace); disabling
		the PS/2 keyboard controller on IP32, so that NetBSD/sgimips
		boots into userland again.
20050626	Finally! NetBSD/sgimips netboots. Adding instructions to
		doc/guestoses.html on how to set up an nfs server etc.
		Various other minor fixes.
		Playstation Portable ".pbp" files can now be used directly.
		(The ELF part of the .pbp is extracted transparently.)
		Converting some sprintf -> snprintf.
		Adding some more instructions to the ARM disassembler.
20050627	More ARM updates. Adding some simple ldr(b), str(b),
		cmps, and conditional branch instructions, enough to run
		a simple Hello World program.
		All ARM instructions are now inlined/generated for all possible
		condition codes.
		Adding add and sub, and more load/store instructions.
		Removing dummy files: cpu_alpha.c, cpu_hppa.c, and cpu_sparc.c.
		Some minor documentation updates; preparing for a 0.3.4
		release. Updating some URLs.

==============  RELEASE 0.3.4  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26