/[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 6 - (hide annotations)
Mon Oct 8 16:18:11 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 44299 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.772 2005/06/04 12:02:16 debug Exp $
20050428	Disabling the "-fmove-all-movables" option in the configure
		script, because it causes the compile to fail on OpenBSD/sgi.
20050502	Minor updates.
20050503	Removing the WRT54G mode (it was bogus anyway), and adding a
		comment about Windows NT for MIPS in doc/experiments.html.
		Minor updates to the x86 instruction decoding.
20050504	Adding some more x86 instructions.
		Adding support for reading files from ISO9660 CDROMs (including
		gzipped files). It's an ugly hack, but it seems to work.
		Various other minor updates (dev_vga.c, pc_bios.c etc).
20050505	Some more x86-related updates.
		Beginning (what I hope will be) a major code cleanup phase.
		"bootris" (an x86 bootsector) runs :-)
20050506	Adding some more x86 instructions.
20050507	tmpnam => mkstemp.
		Working on a hack to allow VGA charcells to be shown even when
		not running with X11.
		Adding more x86 instructions.
20050508	x86 32-bit SIB addressing fix, and more instructions.
20050509	Adding more x86 instructions.
20050510	Minor documentation updates, and other updates (x86 stuff etc.)
20050511	More x86-related updates.
20050513	Various updates, mostly x86-related. (Trying to fix flag 
		calculation, factoring out the ugly shift/rotate code, and
		some other things.)
20050514	Adding support for loading some old i386 a.out executables.
		Finally beginning the cleanup of machine/PROM/bios dependant
		info.
		Some minor documentation updates.
		Trying to clean up ARCBIOS stuff a little.
20050515	Trying to make it possible to actually use more than one disk
		type per machine (floppy, ide, scsi).
		Trying to clean up the kbd vs PROM console stuff. (For PC and
		ARC emulation modes, mostly.)
		Beginning to add an 8259 interrupt controller, and connecting
		it to the x86 emulation.
20050516	The first x86 interrupts seem to work (keyboard stuff).
		Adding a 8253/8254 programmable interval timer skeleton.
		FreeDOS now reaches a command prompt and can be interacted
		with.
20050517	After some bugfixes, MS-DOS also (sometimes) reaches a
		command prompt now.
		Trying to fix the pckbc to work with MS-DOS' keyb.com, but no
		success yet.
20050518	Adding a simple 32-bit x86 MMU skeleton.
20050519	Some more work on the x86 stuff. (Beginning the work on paging,
		and various other fixes).
20050520	More updates. Working on dev_vga (4-bit graphics modes), adding
		40 columns support to the PC bios emulation.
		Trying to add support for resizing windows when switching
		between graphics modes.
20050521	Many more x86-related updates.
20050522	Correcting the initial stack pointer's sign-extension for
		ARCBIOS emulation (thanks to Alec Voropay for noticing the
		error).
		Continuing on the cleanup (ARCBIOS etc).
		dev_vga updates.
20050523	More x86 updates: trying to add some support for protected mode
		interrupts (via gate descriptors) and many other fixes.
		More ARCBIOS cleanup.
		Adding a device flag which indicates that reads cause no
		side-effects. (Useful for the "dump" command in the debugger,
		and other things.)
		Adding support for directly starting up x86 ELFs, skipping the
		bootloader stage. (Most ELFs, however, are not suitable for
		this.)
20050524	Adding simple 32-bit x86 TSS task switching, but no privilege
		level support yet.
		More work on dev_vga. A small "Copper bars" demo works. :-)
		Adding support for Trap Flag (single-step exceptions), at least
		in real mode, and various other x86-related fixes.
20050525	Adding a new disk image prefix (gH;S;) which can be used to
		override the default nr of heads and sectors per track.
20050527	Various bug fixes, more work on the x86 mode (stack change on
		interrupts between different priv.levels), and some minor
		documentation updates.
20050528	Various fixes (x86 stuff).
20050529	More x86 fixes. An OpenBSD/i386 bootfloppy reaches userland
		and can be interacted with (although there are problems with
		key repetition). NetBSD/i386 triggers a serious CISC-related
		problem: instruction fetches across page boundaries, where
		the later part isn't actually part of the instruction.
20050530	Various minor updates. (Documentation updates, etc.)
20050531	Adding some experimental code (experiments/new_test_*) which
		could be useful for dynamic (but not binary) translation in
		the future.
20050602	Adding a dummy ARM skeleton.
		Fixing the pckbc key repetition problem (by adding release
		scancodes for all keypresses).
20050603	Minor updates for the next release.
20050604	Release testing. Minor updates.

==============  RELEASE 0.3.3  ==============

20050604	There'll probably be a 0.3.3.1 release soon, with some very
		very tiny updates.


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 6 * $Id: diskimage.c,v 1.91 2005/05/27 07:29:24 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     d->tape_offset = ftell(d->f);
923    
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     break;
1307    
1308     default:
1309     fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1310 dpavlin 6 xferp->cmd[0], id);
1311 dpavlin 2 exit(1);
1312     }
1313     debug(" ]\n");
1314    
1315     return 1;
1316     }
1317    
1318    
1319     /*
1320     * diskimage_access():
1321     *
1322     * Read from or write to a disk image on a machine.
1323     *
1324     * Returns 1 if the access completed successfully, 0 otherwise.
1325     */
1326 dpavlin 6 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
1327 dpavlin 2 off_t offset, unsigned char *buf, size_t len)
1328     {
1329     struct diskimage *d = machine->first_diskimage;
1330    
1331     while (d != NULL) {
1332 dpavlin 6 if (d->type == type && d->id == id)
1333 dpavlin 2 break;
1334     d = d->next;
1335     }
1336    
1337     if (d == NULL) {
1338     fatal("[ diskimage_access(): ERROR: trying to access a "
1339 dpavlin 6 "non-existant %s disk image (id %i)\n",
1340     diskimage_types[type], id);
1341 dpavlin 2 return 0;
1342     }
1343    
1344     return diskimage__internal_access(d, writeflag, offset, buf, len);
1345     }
1346    
1347    
1348     /*
1349     * diskimage_add():
1350     *
1351     * Add a disk image. fname is the filename of the disk image.
1352     * The filename may be prefixed with one or more modifiers, followed
1353     * by a colon.
1354     *
1355 dpavlin 6 * b specifies that this is a bootable device
1356     * c CD-ROM (instead of a normal DISK)
1357 dpavlin 4 * d DISK (this is the default)
1358     * f FLOPPY (instead of SCSI)
1359 dpavlin 6 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
1360     * automatically calculated). (This is ignored for floppies.)
1361 dpavlin 2 * i IDE (instead of SCSI)
1362     * r read-only (don't allow changes to the file)
1363 dpavlin 4 * s SCSI (this is the default)
1364     * t tape
1365 dpavlin 2 * 0-7 force a specific SCSI ID number
1366     *
1367     * machine is assumed to be non-NULL.
1368     * Returns an integer >= 0 identifying the disk image.
1369     */
1370     int diskimage_add(struct machine *machine, char *fname)
1371     {
1372     struct diskimage *d, *d2;
1373 dpavlin 6 int id = 0, override_heads=0, override_spt=0;
1374     int64_t bytespercyl;
1375 dpavlin 2 char *cp;
1376 dpavlin 6 int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;
1377     int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id = -1;
1378 dpavlin 2
1379     if (fname == NULL) {
1380     fprintf(stderr, "diskimage_add(): NULL ptr\n");
1381     return 0;
1382     }
1383    
1384     /* Get prefix from fname: */
1385     cp = strchr(fname, ':');
1386     if (cp != NULL) {
1387     while (fname <= cp) {
1388     char c = *fname++;
1389     switch (c) {
1390     case '0':
1391     case '1':
1392     case '2':
1393     case '3':
1394     case '4':
1395     case '5':
1396     case '6':
1397     case '7':
1398     prefix_id = c - '0';
1399     break;
1400     case 'b':
1401     prefix_b = 1;
1402     break;
1403     case 'c':
1404     prefix_c = 1;
1405     break;
1406     case 'd':
1407     prefix_d = 1;
1408     break;
1409 dpavlin 4 case 'f':
1410     prefix_f = 1;
1411     break;
1412 dpavlin 6 case 'g':
1413     prefix_g = 1;
1414     override_heads = atoi(fname);
1415     while (*fname != '\0' && *fname != ';')
1416     fname ++;
1417     if (*fname == ';')
1418     fname ++;
1419     override_spt = atoi(fname);
1420     while (*fname != '\0' && *fname != ';' &&
1421     *fname != ':')
1422     fname ++;
1423     if (*fname == ';')
1424     fname ++;
1425     if (override_heads < 1 ||
1426     override_spt < 1) {
1427     fatal("Bad geometry: heads=%i "
1428     "spt=%i\n", override_heads,
1429     override_spt);
1430     exit(1);
1431     }
1432     break;
1433 dpavlin 2 case 'i':
1434     prefix_i = 1;
1435     break;
1436 dpavlin 4 case 'r':
1437     prefix_r = 1;
1438     break;
1439     case 's':
1440     prefix_s = 1;
1441     break;
1442 dpavlin 2 case 't':
1443     prefix_t = 1;
1444     break;
1445     case ':':
1446     break;
1447     default:
1448     fprintf(stderr, "diskimage_add(): invalid "
1449     "prefix char '%c'\n", c);
1450     exit(1);
1451     }
1452     }
1453     }
1454    
1455     /* Allocate a new diskimage struct: */
1456     d = malloc(sizeof(struct diskimage));
1457     if (d == NULL) {
1458     fprintf(stderr, "out of memory in diskimage_add()\n");
1459     exit(1);
1460     }
1461     memset(d, 0, sizeof(struct diskimage));
1462    
1463     d2 = machine->first_diskimage;
1464     if (d2 == NULL) {
1465     machine->first_diskimage = d;
1466     } else {
1467     while (d2->next != NULL)
1468     d2 = d2->next;
1469     d2->next = d;
1470     }
1471    
1472     d->type = DISKIMAGE_SCSI;
1473    
1474 dpavlin 4 /* Special cases: some machines usually have FLOPPY/IDE, not SCSI: */
1475     if (machine->arch == ARCH_X86 ||
1476     machine->machine_type == MACHINE_COBALT ||
1477     machine->machine_type == MACHINE_HPCMIPS ||
1478     machine->machine_type == MACHINE_PS2)
1479     d->type = DISKIMAGE_IDE;
1480    
1481     if (prefix_i + prefix_f + prefix_s > 1) {
1482     fprintf(stderr, "Invalid disk image prefix(es). You can"
1483     "only use one of i, f, and s\nfor each disk image.\n");
1484     exit(1);
1485     }
1486    
1487 dpavlin 2 if (prefix_i)
1488     d->type = DISKIMAGE_IDE;
1489 dpavlin 4 if (prefix_f)
1490     d->type = DISKIMAGE_FLOPPY;
1491     if (prefix_s)
1492     d->type = DISKIMAGE_SCSI;
1493 dpavlin 2
1494     d->fname = strdup(fname);
1495     if (d->fname == NULL) {
1496     fprintf(stderr, "out of memory\n");
1497     exit(1);
1498     }
1499    
1500     d->logical_block_size = 512;
1501    
1502     /*
1503     * Is this a tape, CD-ROM or a normal disk?
1504     *
1505     * An intelligent guess, if no prefixes are used, would be that
1506     * filenames ending with .iso are CD-ROM images.
1507     */
1508     if (prefix_t) {
1509     d->is_a_tape = 1;
1510     } else {
1511     if (prefix_c ||
1512     ((strlen(d->fname) > 4 &&
1513     strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0)
1514     && !prefix_d)
1515     ) {
1516     d->is_a_cdrom = 1;
1517    
1518     /*
1519     * This is tricky. Should I use 512 or 2048 here?
1520     * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
1521     * per sector, but NetBSD 2.0_BETA suddenly ignores
1522     * this value and uses 2048 instead.
1523     *
1524     * OpenBSD/arc doesn't like 2048, it requires 512
1525     * to work correctly.
1526     *
1527     * TODO
1528     */
1529    
1530     #if 0
1531     if (machine->machine_type == MACHINE_DEC)
1532     d->logical_block_size = 512;
1533     else
1534     d->logical_block_size = 2048;
1535     #endif
1536     d->logical_block_size = 512;
1537     }
1538     }
1539    
1540     diskimage_recalc_size(d);
1541    
1542 dpavlin 6 if ((d->total_size == 720*1024 || d->total_size == 1474560
1543     || d->total_size == 2949120 || d->total_size == 1228800)
1544     && !prefix_i && !prefix_s)
1545 dpavlin 4 d->type = DISKIMAGE_FLOPPY;
1546    
1547 dpavlin 6 switch (d->type) {
1548     case DISKIMAGE_FLOPPY:
1549     if (d->total_size < 737280) {
1550     fatal("\nTODO: small (non-80-cylinder) floppies?\n\n");
1551     exit(1);
1552     }
1553     d->cylinders = 80;
1554     d->heads = 2;
1555     d->sectors_per_track = d->total_size / (d->cylinders *
1556     d->heads * 512);
1557     break;
1558     default:/* Non-floppies: */
1559     d->heads = 16;
1560     d->sectors_per_track = 63;
1561     if (prefix_g) {
1562     d->chs_override = 1;
1563     d->heads = override_heads;
1564     d->sectors_per_track = override_spt;
1565     }
1566     bytespercyl = d->heads * d->sectors_per_track * 512;
1567     d->cylinders = d->total_size / bytespercyl;
1568     if (d->cylinders * bytespercyl < d->total_size)
1569     d->cylinders ++;
1570     }
1571    
1572 dpavlin 2 d->rpms = 3600;
1573    
1574     if (prefix_b)
1575     d->is_boot_device = 1;
1576    
1577     d->writable = access(fname, W_OK) == 0? 1 : 0;
1578    
1579     if (d->is_a_cdrom || prefix_r)
1580     d->writable = 0;
1581    
1582     d->f = fopen(fname, d->writable? "r+" : "r");
1583     if (d->f == NULL) {
1584     perror(fname);
1585     exit(1);
1586     }
1587    
1588 dpavlin 6 /* Calculate which ID to use: */
1589     if (prefix_id == -1) {
1590     int free = 0, collision = 1;
1591    
1592     while (collision) {
1593     collision = 0;
1594     d2 = machine->first_diskimage;
1595     while (d2 != NULL) {
1596     /* (don't compare against ourselves :) */
1597     if (d2 == d) {
1598     d2 = d2->next;
1599     continue;
1600     }
1601     if (d2->id == free && d2->type == d->type) {
1602     collision = 1;
1603     break;
1604     }
1605     d2 = d2->next;
1606     }
1607     if (!collision)
1608     id = free;
1609     else
1610     free ++;
1611     }
1612     } else {
1613     id = prefix_id;
1614     d2 = machine->first_diskimage;
1615     while (d2 != NULL) {
1616     /* (don't compare against ourselves :) */
1617     if (d2 == d) {
1618     d2 = d2->next;
1619     continue;
1620     }
1621     if (d2->id == id && d2->type == d->type) {
1622     fprintf(stderr, "disk image id %i "
1623     "already in use\n", id);
1624     exit(1);
1625     }
1626     d2 = d2->next;
1627     }
1628     }
1629    
1630     d->id = id;
1631    
1632 dpavlin 2 return id;
1633     }
1634    
1635    
1636     /*
1637     * diskimage_bootdev():
1638     *
1639 dpavlin 6 * Returns the disk id of the device which we're booting from. If typep is
1640     * non-NULL, the type is returned as well.
1641 dpavlin 2 *
1642     * If no disk was used as boot device, then -1 is returned. (In practice,
1643     * this is used to fake network (tftp) boot.)
1644     */
1645 dpavlin 6 int diskimage_bootdev(struct machine *machine, int *typep)
1646 dpavlin 2 {
1647 dpavlin 6 struct diskimage *d;
1648    
1649     d = machine->first_diskimage;
1650 dpavlin 2 while (d != NULL) {
1651 dpavlin 6 if (d->is_boot_device) {
1652     if (typep != NULL)
1653     *typep = d->type;
1654 dpavlin 2 return d->id;
1655 dpavlin 6 }
1656 dpavlin 2 d = d->next;
1657     }
1658    
1659     d = machine->first_diskimage;
1660 dpavlin 6 if (d != NULL) {
1661     if (typep != NULL)
1662     *typep = d->type;
1663 dpavlin 2 return d->id;
1664 dpavlin 6 }
1665 dpavlin 2
1666     return -1;
1667     }
1668    
1669    
1670     /*
1671     * diskimage_is_a_cdrom():
1672     *
1673 dpavlin 6 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1674 dpavlin 2 */
1675 dpavlin 6 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1676 dpavlin 2 {
1677     struct diskimage *d = machine->first_diskimage;
1678    
1679     while (d != NULL) {
1680 dpavlin 6 if (d->type == type && d->id == id)
1681 dpavlin 2 return d->is_a_cdrom;
1682     d = d->next;
1683     }
1684     return 0;
1685     }
1686    
1687    
1688     /*
1689     * diskimage_is_a_tape():
1690     *
1691 dpavlin 6 * Returns 1 if a disk image is a tape, 0 otherwise.
1692     *
1693 dpavlin 2 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1694     * boot strings.)
1695     */
1696 dpavlin 6 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1697 dpavlin 2 {
1698     struct diskimage *d = machine->first_diskimage;
1699    
1700     while (d != NULL) {
1701 dpavlin 6 if (d->type == type && d->id == id)
1702 dpavlin 2 return d->is_a_tape;
1703     d = d->next;
1704     }
1705     return 0;
1706     }
1707    
1708    
1709     /*
1710     * diskimage_dump_info():
1711     *
1712     * Debug dump of all diskimages that are loaded for a specific machine.
1713     */
1714     void diskimage_dump_info(struct machine *machine)
1715     {
1716     int iadd=4;
1717     struct diskimage *d = machine->first_diskimage;
1718    
1719     while (d != NULL) {
1720     debug("diskimage: %s\n", d->fname);
1721     debug_indentation(iadd);
1722    
1723     switch (d->type) {
1724     case DISKIMAGE_SCSI:
1725     debug("SCSI");
1726     break;
1727     case DISKIMAGE_IDE:
1728     debug("IDE");
1729     break;
1730 dpavlin 4 case DISKIMAGE_FLOPPY:
1731     debug("FLOPPY");
1732     break;
1733 dpavlin 2 default:
1734     debug("UNKNOWN type %i", d->type);
1735     }
1736    
1737     debug(" %s", d->is_a_tape? "TAPE" :
1738     (d->is_a_cdrom? "CD-ROM" : "DISK"));
1739     debug(" id %i, ", d->id);
1740     debug("%s, ", d->writable? "read/write" : "read-only");
1741    
1742 dpavlin 4 if (d->type == DISKIMAGE_FLOPPY)
1743     debug("%lli KB", (long long) (d->total_size / 1024));
1744     else
1745     debug("%lli MB", (long long) (d->total_size / 1048576));
1746    
1747 dpavlin 6 if (d->type == DISKIMAGE_FLOPPY || d->chs_override)
1748     debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads,
1749     d->sectors_per_track);
1750     else
1751     debug(" (%lli sectors)", (long long)
1752     (d->total_size / 512));
1753 dpavlin 2
1754 dpavlin 6 if (d->is_boot_device)
1755     debug(" (BOOT)");
1756     debug("\n");
1757    
1758 dpavlin 2 debug_indentation(-iadd);
1759    
1760     d = d->next;
1761     }
1762     }
1763    

  ViewVC Help
Powered by ViewVC 1.1.26