/[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 24 - (hide annotations)
Mon Oct 8 16:19:56 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 45788 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1256 2006/06/23 20:43:44 debug Exp $
20060219	Various minor updates. Removing the old MIPS16 skeleton code,
		because it will need to be rewritten for dyntrans anyway.
20060220-22	Removing the non-working dyntrans backend support.
		Continuing on the 64-bit dyntrans virtual memory generalization.
20060223	More work on the 64-bit vm generalization.
20060225	Beginning on MIPS dyntrans load/store instructions.
		Minor PPC updates (64-bit load/store, etc).
		Fixes for the variable-instruction-length framework, some
		minor AVR updates (a simple Hello World program works!).
		Beginning on a skeleton for automatically generating documen-
		tation (for devices etc.).
20060226	PPC updates (adding some more 64-bit instructions, etc).
		AVR updates (more instructions).
		FINALLY found and fixed the zs bug, making NetBSD/macppc
		accept the serial console.
20060301	Adding more AVR instructions.
20060304	Continuing on AVR-related stuff. Beginning on a framework for
		cycle-accurate device emulation. Adding an experimental "PAL
		TV" device (just a dummy so far).
20060305	Adding more AVR instructions.
		Adding a dummy epcom serial controller (for TS7200 emulation).
20060310	Removing the emul() command from configuration files, so only
		net() and machine() are supported.
		Minor progress on the MIPS dyntrans rewrite.
20060311	Continuing on the MIPS dyntrans rewrite (adding more
		instructions, etc).
20060315	Adding more instructions (sllv, srav, srlv, bgtz[l], blez[l],
		beql, bnel, slti[u], various loads and stores).
20060316	Removing the ALWAYS_SIGNEXTEND_32 option, since it was rarely
		used.
		Adding more MIPS dyntrans instructions, and fixing bugs.
20060318	Implementing fast loads/stores for MIPS dyntrans (big/little
		endian, 32-bit and 64-bit modes).
20060320	Making MIPS dyntrans the default configure option; use
		"--enable-oldmips" to use the old bintrans system.
		Adding MIPS dyntrans dmult[u]; minor updates.
20060322	Continuing... adding some more instructions.
		Adding a simple skeleton for demangling C++ "_ZN" symbols.
20060323	Moving src/debugger.c into a new directory (src/debugger/).
20060324	Fixing the hack used to load PPC ELFs (useful for relocated
		Linux/ppc kernels), and adding a dummy G3 machine mode.
20060325-26	Beginning to experiment with GDB remote serial protocol
		connections; adding a -G command line option for selecting
		which TCP port to listen to.
20060330	Beginning a major cleanup to replace things like "0x%016llx"
		with more correct "0x%016"PRIx64, etc.
		Continuing on the GDB remote serial protocol support.
20060331	More cleanup, and some minor GDB remote progress.
20060402	Adding a hack to the configure script, to allow compilation
		on systems that lack PRIx64 etc.
20060406	Removing the temporary FreeBSD/arm hack in dev_ns16550.c and
		replacing it with a better fix from Olivier Houchard.
20060407	A remote debugger (gdb or ddd) can now start and stop the
		emulator using the GDB remote serial protocol, and registers
		and memory can be read. MIPS only for now.
20060408	More GDB progress: single-stepping also works, and also adding
		support for ARM, PowerPC, and Alpha targets.
		Continuing on the delay-slot-across-page-boundary issue.
20060412	Minor update: beginning to add support for the SPARC target
		to the remote GDB functionality.
20060414	Various MIPS updates: adding more instructions for dyntrans
		(eret, add), and making some exceptions work. Fixing a bug
		in dmult[u].
		Implementing the first SPARC instructions (sethi, or).
20060415	Adding "magic trap" instructions so that PROM calls can be
		software emulated in MIPS dyntrans.
		Adding more MIPS dyntrans instructions (ddiv, dadd) and
		fixing another bug in dmult.
20060416	More MIPS dyntrans progress: adding [d]addi, movn, movz, dsllv,
		rfi, an ugly hack for supporting R2000/R3000 style faked caches,
		preliminary interrupt support, and various other updates and
		bugfixes.
20060417	Adding more SPARC instructions (add, sub, sll[x], sra[x],
		srl[x]), and useful SPARC header definitions.
		Adding the first (trivial) x86/AMD64 dyntrans instructions (nop,
		cli/sti, stc/clc, std/cld, simple mov, inc ax). Various other
		x86 updates related to variable instruction length stuff.
		Adding unaligned loads/stores to the MIPS dyntrans mode (but
		still using the pre-dyntrans (slow) imlementation).
20060419	Fixing a MIPS dyntrans exception-in-delay-slot bug.
		Removing the old "show opcode statistics" functionality, since
		it wasn't really useful and isn't implemented for dyntrans.
		Single-stepping (or running with instruction trace) now looks
		ok with dyntrans with delay-slot architectures.
20060420	Minor hacks (removing the -B command line option when compiled
		for non-bintrans, and some other very minor updates).
		Adding (slow) MIPS dyntrans load-linked/store-conditional.
20060422	Applying fixes for bugs discovered by Nils Weller's nwcc
		(static DEC memmap => now per machine, and adding an extern
		keyword in cpu_arm_instr.c).
		Finally found one of the MIPS dyntrans bugs that I've been
		looking for (copy/paste spelling error BIG vs LITTLE endian in
		cpu_mips_instr_loadstore.c for 16-bit fast stores).
		FINALLY found the major MIPS dyntrans bug: slti vs sltiu
		signed/unsigned code in cpu_mips_instr.c. :-)
		Adding more MIPS dyntrans instructions (lwc1, swc1, bgezal[l],
		ctc1, tlt[u], tge[u], tne, beginning on rdhwr).
		NetBSD/hpcmips can now reach userland when using dyntrans :-)
		Adding some more x86 dyntrans instructions.
		Finally removed the old Alpha-specific virtual memory code,
		and replaced it with the generic 64-bit version.
		Beginning to add disassembly support for SPECIAL3 MIPS opcodes.
20060423	Continuing on the delay-slot-across-page-boundary issue;
		adding an end_of_page2 ic slot (like I had planned before, but
		had removed for some reason).
		Adding a quick-and-dirty fallback to legacy coprocessor 1
		code (i.e. skipping dyntrans implementation for now).
		NetBSD/hpcmips and NetBSD/pmax (when running on an emulated
		R4400) can now be installed and run. :-)  (Many bugs left
		to fix, though.)
		Adding more MIPS dyntrans instructions: madd[u], msub[u].
		Cleaning up the SPECIAL2 vs R5900/TX79/C790 "MMI" opcode
		maps somewhat (disassembly and dyntrans instruction decoding).
20060424	Adding an isa_revision field to mips_cpu_types.h, and making
		sure that SPECIAL3 opcodes cause Reserved Instruction
		exceptions on MIPS32/64 revisions lower than 2.
		Adding the SPARC 'ba', 'call', 'jmpl/retl', 'and', and 'xor'
		instructions.
20060425	Removing the -m command line option ("run at most x 
		instructions") and -T ("single_step_on_bad_addr"), because
		they never worked correctly with dyntrans anyway.
		Freshening up the man page.
20060428	Adding more MIPS dyntrans instructions: bltzal[l], idle.
		Enabling MIPS dyntrans compare interrupts.
20060429	FINALLY found the weird dyntrans bug, causing NetBSD etc. to
		behave strangely: some floating point code (conditional
		coprocessor branches) could not be reused from the old
		non-dyntrans code. The "quick-and-dirty fallback" only appeared
		to work. Fixing by implementing bc1* for MIPS dyntrans.
		More MIPS instructions: [d]sub, sdc1, ldc1, dmtc1, dmfc1, cfc0.
		Freshening up MIPS floating point disassembly appearance.
20060430	Continuing on C790/R5900/TX79 disassembly; implementing 128-bit
		"por" and "pextlw".
20060504	Disabling -u (userland emulation) unless compiled as unstable
		development version.
		Beginning on freshening up the testmachine include files,
		to make it easier to reuse those files (placing them in
		src/include/testmachine/), and beginning on a set of "demos"
		or "tutorials" for the testmachine functionality.
		Minor updates to the MIPS GDB remote protocol stub.
		Refreshing doc/experiments.html and gdb_remote.html.
		Enabling Alpha emulation in the stable release configuration,
		even though no guest OSes for Alpha can run yet.
20060505	Adding a generic 'settings' object, which will contain
		references to settable variables (which will later be possible
		to access using the debugger).
20060506	Updating dev_disk and corresponding demo/documentation (and
		switching from SCSI to IDE disk types, so it actually works
		with current test machines :-).
20060510	Adding a -D_LARGEFILE_SOURCE hack for 64-bit Linux hosts,
		so that fseeko() doesn't give a warning.
		Updating the section about how dyntrans works (the "runnable
		IR") in doc/intro.html.
		Instruction updates (some x64=1 checks, some more R5900
		dyntrans stuff: better mul/mult separation from MIPS32/64,
		adding ei and di).
		Updating MIPS cpuregs.h to a newer one (from NetBSD).
		Adding more MIPS dyntrans instructions: deret, ehb.
20060514	Adding disassembly and beginning implementation of SPARC wr
		and wrpr instructions.
20060515	Adding a SUN SPARC machine mode, with dummy SS20 and Ultra1
		machines. Adding the 32-bit "rd psr" instruction.
20060517	Disassembly support for the general SPARC rd instruction.
		Partial implementation of the cmp (subcc) instruction.
		Some other minor updates (making sure that R5900 processors
		start up with the EIE bit enabled, otherwise Linux/playstation2
		receives no interrupts).
20060519	Minor MIPS updates/cleanups.
20060521	Moving the MeshCube machine into evbmips; this seems to work
		reasonably well with a snapshot of a NetBSD MeshCube kernel.
		Cleanup/fix of MIPS config0 register initialization.
20060529	Minor MIPS fixes, including a sign-extension fix to the
		unaligned load/store code, which makes NetBSD/pmax on R3000
		work better with dyntrans. (Ultrix and Linux/DECstation still
		don't work, though.)
20060530	Minor updates to the Alpha machine mode: adding an AlphaBook
		mode, an LCA bus (forwarding accesses to an ISA bus), etc.
20060531	Applying a bugfix for the MIPS dyntrans sc[d] instruction from
		Ondrej Palkovsky. (Many thanks.)
20060601	Minifix to allow ARM immediate msr instruction to not give
		an error for some valid values.
		More Alpha updates.
20060602	Some minor Alpha updates.
20060603	Adding the Alpha cmpbge instruction. NetBSD/alpha prints its
		first boot messages :-) on an emulated Alphabook 1.
20060612	Minor updates; adding a dev_ether.h include file for the
		testmachine ether device. Continuing the hunt for the dyntrans
		bug which makes Linux and Ultrix on DECstation behave
		strangely... FINALLY found it! It seems to be related to
		invalidation of the translation cache, on tlbw{r,i}. There
		also seems to be some remaining interrupt-related problems.
20060614	Correcting the implementation of ldc1/sdc1 for MIPS dyntrans
		(so that it uses 16 32-bit registers if the FR bit in the
		status register is not set).
20060616	REMOVING BINTRANS COMPLETELY!
		Removing the old MIPS interpretation mode.
		Removing the MFHILO_DELAY and instruction delay stuff, because
		they wouldn't work with dyntrans anyway.
20060617	Some documentation updates (adding "NetBSD-archive" to some
		URLs, and new Debian/DECstation installation screenshots).
		Removing the "tracenull" and "enable-caches" configure options.
		Improving MIPS dyntrans performance somewhat (only invalidate
		translations if necessary, on writes to the entryhi register,
		instead of doing it for all cop0 writes).
20060618	More cleanup after the removal of the old MIPS emulation.
		Trying to fix the MIPS dyntrans performance bugs/bottlenecks;
		only semi-successful so far (for R3000).
20060620	Minor update to allow clean compilation again on Tru64/Alpha.
20060622	MIPS cleanup and fixes (removing the pc_last stuff, which
		doesn't make sense with dyntrans anyway, and fixing a cross-
		page-delay-slot-with-exception case in end_of_page).
		Removing the old max_random_cycles_per_chunk stuff, and the
		concept of cycles vs instructions for MIPS emulation.
		FINALLY found and fixed the bug which caused NetBSD/pmax
		clocks to behave strangely (it was a load to the zero register,
		which was treated as a NOP; now it is treated as a load to a
		dummy scratch register).
20060623	Increasing the dyntrans chunk size back to
		N_SAFE_DYNTRANS_LIMIT, instead of N_SAFE_DYNTRANS_LIMIT/2.
		Preparing for a quick release, even though there are known
		bugs, and performance for non-R3000 MIPS emulation is very
		poor. :-/
		Reverting to half the dyntrans chunk size again, because
		NetBSD/cats seemed less stable with full size chunks. :(
		NetBSD/sgimips 3.0 can now run :-)  (With release 0.3.8, only
		NetBSD/sgimips 2.1 worked, not 3.0.)

==============  RELEASE 0.4.0  ==============


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

  ViewVC Help
Powered by ViewVC 1.1.26