/[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 32 - (hide annotations)
Mon Oct 8 16:20:58 2007 UTC (16 years, 6 months ago) by dpavlin
File MIME type: text/plain
File size: 46422 byte(s)
++ trunk/HISTORY	(local)
$Id: HISTORY,v 1.1421 2006/11/06 05:32:37 debug Exp $
20060816	Adding a framework for emulated/virtual timers (src/timer.c),
		using only setitimer().
		Rewriting the mc146818 to use the new timer framework.
20060817	Adding a call to gettimeofday() every now and then (once every
		second, at the moment) to resynch the timer if it drifts.
		Beginning to convert the ISA timer interrupt mechanism (8253
		and 8259) to use the new timer framework.
		Removing the -I command line option.
20060819	Adding the -I command line option again, with new semantics.
		Working on Footbridge timer interrupts; NetBSD/NetWinder and
		NetBSD/CATS now run at correct speed, but unfortunately with
		HUGE delays during bootup.
20060821	Some minor m68k updates. Adding the first instruction: nop. :)
		Minor Alpha emulation updates.
20060822	Adding a FreeBSD development specific YAMON environment
		variable ("khz") (as suggested by Bruce M. Simpson).
		Moving YAMON environment variable initialization from
		machine_evbmips.c into promemul/yamon.c, and adding some more
		variables.
		Continuing on the LCA PCI bus controller (for Alpha machines).
20060823	Continuing on the timer stuff: experimenting with MIPS count/
		compare interrupts connected to the timer framework.
20060825	Adding bogus SCSI commands 0x51 (SCSICDROM_READ_DISCINFO) and
		0x52 (SCSICDROM_READ_TRACKINFO) to the SCSI emulation layer,
		to allow NetBSD/pmax 4.0_BETA to be installed from CDROM.
		Minor updates to the LCA PCI controller.
20060827	Implementing a CHIP8 cpu mode, and a corresponding CHIP8
		machine, for fun. Disassembly support for all instructions,
		and most of the common instructions have been implemented: mvi,
		mov_imm, add_imm, jmp, rand, cls, sprite, skeq_imm, jsr,
		skne_imm, bcd, rts, ldr, str, mov, or, and, xor, add, sub,
		font, ssound, sdelay, gdelay, bogus skup/skpr, skeq, skne.
20060828	Beginning to convert the CHIP8 cpu in the CHIP8 machine to a
		(more correct) RCA 180x cpu. (Disassembly for all 1802
		instructions has been implemented, but no execution yet, and
		no 1805 extended instructions.)
20060829	Minor Alpha emulation updates.
20060830	Beginning to experiment a little with PCI IDE for SGI O2.
		Fixing the cursor key mappings for MobilePro 770 emulation.
		Fixing the LK201 warning caused by recent NetBSD/pmax.
		The MIPS R41xx standby, suspend, and hibernate instructions now
		behave like the RM52xx/MIPS32/MIPS64 wait instruction.
		Fixing dev_wdc so it calculates correct (64-bit) offsets before
		giving them to diskimage_access().
20060831	Continuing on Alpha emulation (OSF1 PALcode).
20060901	Minor Alpha updates; beginning on virtual memory pagetables.
		Removed the limit for max nr of devices (in preparation for
		allowing devices' base addresses to be changed during runtime).
		Adding a hack for MIPS [d]mfc0 select 0 (except the count
		register), so that the coproc register is simply copied.
		The MIPS suspend instruction now exits the emulator, instead
		of being treated as a wait instruction (this causes NetBSD/
		hpcmips to get correct 'halt' behavior).
		The VR41xx RTC now returns correct time.
		Connecting the VR41xx timer to the timer framework (fixed at
		128 Hz, for now).
		Continuing on SPARC emulation, adding more instructions:
		restore, ba_xcc, ble. The rectangle drawing demo works :)
		Removing the last traces of the old ENABLE_CACHE_EMULATION
		MIPS stuff (not usable with dyntrans anyway).
20060902	Splitting up src/net.c into several smaller files in its own
		subdirectory (src/net/).
20060903	Cleanup of the files in src/net/, to make them less ugly.
20060904	Continuing on the 'settings' subsystem.
		Minor progress on the SPARC emulation mode.
20060905	Cleanup of various things, and connecting the settings
		infrastructure to various subsystems (emul, machine, cpu, etc).
		Changing the lk201 mouse update routine to not rely on any
		emulated hardware framebuffer cursor coordinates, but instead
		always do (semi-usable) relative movements.
20060906	Continuing on the lk201 mouse stuff. Mouse behaviour with
		multiple framebuffers (which was working in Ultrix) is now
		semi-broken (but it still works, in a way).
		Moving the documentation about networking into its own file
		(networking.html), and refreshing it a bit. Adding an example
		of how to use ethernet frame direct-access (udp_snoop).
20060907	Continuing on the settings infrastructure.
20060908	Minor updates to SH emulation: for 32-bit emulation: delay
		slots and the 'jsr @Rn' instruction. I'm putting 64-bit SH5 on
		ice, for now.
20060909-10	Implementing some more 32-bit SH instructions. Removing the
		64-bit mode completely. Enough has now been implemented to run
		the rectangle drawing demo. :-)
20060912	Adding more SH instructions.
20060916	Continuing on SH emulation (some more instructions: div0u,
		div1, rotcl/rotcr, more mov instructions, dt, braf, sets, sett,
		tst_imm, dmuls.l, subc, ldc_rm_vbr, movt, clrt, clrs, clrmac).
		Continuing on the settings subsystem (beginning on reading/
		writing settings, removing bugs, and connecting more cpus to
		the framework).
20060919	More work on SH emulation; adding an ldc banked instruction,
		and attaching a 640x480 framebuffer to the Dreamcast machine
		mode (NetBSD/dreamcast prints the NetBSD copyright banner :-),
		and then panics).
20060920	Continuing on the settings subsystem.
20060921	Fixing the Footbridge timer stuff so that NetBSD/cats and
		NetBSD/netwinder boot up without the delays.
20060922	Temporarily hardcoding MIPS timer interrupt to 100 Hz. With
		'wait' support disabled, NetBSD/malta and Linux/malta run at
		correct speed.
20060923	Connecting dev_gt to the timer framework, so that NetBSD/cobalt
		runs at correct speed.
		Moving SH4-specific memory mapped registers into its own
		device (dev_sh4.c).
		Running with -N now prints "idling" instead of bogus nr of
		instrs/second (which isn't valid anyway) while idling.
20060924	Algor emulation should now run at correct speed.
		Adding disassembly support for some MIPS64 revision 2
		instructions: ext, dext, dextm, dextu.
20060926	The timer framework now works also when the MIPS wait
		instruction is used.
20060928	Re-implementing checks for coprocessor availability for MIPS
		cop0 instructions. (Thanks to Carl van Schaik for noticing the
		lack of cop0 availability checks.)
20060929	Implementing an instruction combination hack which treats
		NetBSD/pmax' idle loop as a wait-like instruction.
20060930	The ENTRYHI_R_MASK was missing in (at least) memory_mips_v2p.c,
		causing TLB lookups to sometimes succeed when they should have
		failed. (A big thank you to Juli Mallett for noticing the
		problem.)
		Adding disassembly support for more MIPS64 revision 2 opcodes
		(seb, seh, wsbh, jalr.hb, jr.hb, synci, ins, dins, dinsu,
		dinsm, dsbh, dshd, ror, dror, rorv, drorv, dror32). Also
		implementing seb, seh, dsbh, dshd, and wsbh.
		Implementing an instruction combination hack for Linux/pmax'
		idle loop, similar to the NetBSD/pmax case.
20061001	Changing the NetBSD/sgimips install instructions to extract
		files from an iso image, instead of downloading them via ftp.
20061002	More-than-31-bit userland addresses in memory_mips_v2p.c were
		not actually working; applying a fix from Carl van Schaik to
		enable them to work + making some other updates (adding kuseg
		support).
		Fixing hpcmips (vr41xx) timer initialization.
		Experimenting with O(n)->O(1) reduction in the MIPS TLB lookup
		loop. Seems to work both for R3000 and non-R3000.
20061003	Continuing a little on SH emulation (adding more control
		registers; mini-cleanup of memory_sh.c).
20061004	Beginning on a dev_rtc, a clock/timer device for the test
		machines; also adding a demo, and some documentation.
		Fixing a bug in SH "mov.w @(disp,pc),Rn" (the result wasn't
		sign-extended), and adding the addc and ldtlb instructions.
20061005	Contining on SH emulation: virtual to physical address
		translation, and a skeleton exception mechanism.
20061006	Adding more SH instructions (various loads and stores, rte,
		negc, muls.w, various privileged register-move instructions).
20061007	More SH instructions: various move instructions, trapa, div0s,
		float, fdiv, ftrc.
		Continuing on dev_rtc; removing the rtc demo.
20061008	Adding a dummy Dreamcast PROM module. (Homebrew Dreamcast
		programs using KOS libs need this.)
		Adding more SH instructions: "stc vbr,rn", rotl, rotr, fsca,
		fmul, fadd, various floating-point moves, etc. A 256-byte
		demo for Dreamcast runs :-)
20061012	Adding the SH "lds Rm,pr" and bsr instructions.
20061013	More SH instructions: "sts fpscr,rn", tas.b, and some more
		floating point instructions, cmp/str, and more moves.
		Adding a dummy dev_pvr (Dreamcast graphics controller).
20061014	Generalizing the expression evaluator (used in the built-in
		debugger) to support parentheses and +-*/%^&|.
20061015	Removing the experimental tlb index hint code in
		mips_memory_v2p.c, since it didn't really have any effect.
20061017	Minor SH updates; adding the "sts pr,Rn", fcmp/gt, fneg,
		frchg, and some other instructions. Fixing missing sign-
		extension in an 8-bit load instruction.
20061019	Adding a simple dev_dreamcast_rtc.
		Implementing memory-mapped access to the SH ITLB/UTLB arrays.
20061021	Continuing on various SH and Dreamcast things: sh4 timers,
		debug messages for dev_pvr, fixing some virtual address
		translation bugs, adding the bsrf instruction.
		The NetBSD/dreamcast GENERIC_MD kernel now reaches userland :)
		Adding a dummy dev_dreamcast_asic.c (not really useful yet).
		Implementing simple support for Store Queues.
		Beginning on the PVR Tile Accelerator.
20061022	Generalizing the PVR framebuffer to support off-screen drawing,
		multiple bit-depths, etc. (A small speed penalty, but most
		likely worth it.)
		Adding more SH instructions (mulu.w, fcmp/eq, fsub, fmac,
		fschg, and some more); correcting bugs in "fsca" and "float".
20061024	Adding the SH ftrv (matrix * vector) instruction. Marcus
		Comstedt's "tatest" example runs :) (wireframe only).
		Correcting disassembly for SH floating point instructions that
		use the xd* registers.
		Adding the SH fsts instruction.
		In memory_device_dyntrans_access(), only the currently used
		range is now invalidated, and not the entire device range.
20061025	Adding a dummy AVR32 cpu mode skeleton.
20061026	Various Dreamcast updates; beginning on a Maple bus controller.
20061027	Continuing on the Maple bus. A bogus Controller, Keyboard, and
		Mouse can now be detected by NetBSD and KOS homebrew programs.
		Cleaning up the SH4 Timer Management Unit, and beginning on
		SH4 interrupts.
		Implementing the Dreamcast SYSASIC.
20061028	Continuing on the SYSASIC.
		Adding the SH fsqrt instruction.
		memory_sh.c now actually scans the ITLB.
		Fixing a bug in dev_sh4.c, related to associative writes into
		the memory-mapped UTLB array. NetBSD/dreamcast now reaches
		userland stably, and prints the "Terminal type?" message :-]
		Implementing enough of the Dreamcast keyboard to make NetBSD
		accept it for input.
		Enabling SuperH for stable (non-development) builds.
		Adding NetBSD/dreamcast to the documentation, although it
		doesn't support root-on-nfs yet.
20061029	Changing usleep(1) calls in the debugger to to usleep(10000)
		(according to Brian Foley, this makes GXemul run better on
		MacOS X).
		Making the Maple "Controller" do something (enough to barely
		interact with dcircus.elf).
20061030-31	Some progress on the PVR. More test programs start running (but
		with strange output).
		Various other SH4-related updates.
20061102	Various Dreamcast and SH4 updates; more KOS demos run now.
20061104	Adding a skeleton dev_mb8696x.c (the Dreamcast's LAN adapter).
20061105	Continuing on the MB8696x; NetBSD/dreamcast detects it as mbe0.
		Testing for the release.

==============  RELEASE 0.4.3  ==============


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 32 * $Id: diskimage.c,v 1.114 2006/09/07 11:44:01 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 dpavlin 30 #ifdef UNSTABLE_DEVEL
455 dpavlin 2 fatal("[ diskimage__internal_access(): disk_id %i, offset %lli"
456     ", transfer not completed. len=%i, len_done=%i ]\n",
457     d->id, (long long)offset, (int)len, (int)lendone);
458 dpavlin 30 #endif
459 dpavlin 2 return 0;
460     }
461    
462     return 1;
463     }
464    
465    
466     /*
467     * diskimage_scsicommand():
468     *
469     * Perform a SCSI command on a disk image.
470     *
471     * The xferp points to a scsi_transfer struct, containing msg_out, command,
472     * and data_out coming from the SCSI controller device. This function
473     * interprets the command, and (if necessary) creates responses in
474     * data_in, msg_in, and status.
475     *
476     * Returns:
477     * 2 if the command expects data from the DATA_OUT phase,
478     * 1 if otherwise ok,
479     * 0 on error.
480     */
481 dpavlin 6 int diskimage_scsicommand(struct cpu *cpu, int id, int type,
482 dpavlin 2 struct scsi_transfer *xferp)
483     {
484 dpavlin 14 char namebuf[16];
485 dpavlin 20 int retlen, i, q;
486 dpavlin 2 uint64_t size;
487     int64_t ofs;
488     int pagecode;
489     struct machine *machine = cpu->machine;
490     struct diskimage *d;
491    
492     if (machine == NULL) {
493     fatal("[ diskimage_scsicommand(): machine == NULL ]\n");
494     return 0;
495     }
496    
497     d = machine->first_diskimage;
498     while (d != NULL) {
499 dpavlin 6 if (d->type == type && d->id == id)
500 dpavlin 2 break;
501     d = d->next;
502     }
503     if (d == NULL) {
504 dpavlin 6 fprintf(stderr, "[ diskimage_scsicommand(): %s "
505     " id %i not connected? ]\n", diskimage_types[type], id);
506 dpavlin 2 }
507    
508     if (xferp->cmd == NULL) {
509     fatal("[ diskimage_scsicommand(): cmd == NULL ]\n");
510     return 0;
511     }
512    
513     if (xferp->cmd_len < 1) {
514     fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n",
515     xferp->cmd_len);
516     return 0;
517     }
518    
519     debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ",
520 dpavlin 6 id, xferp->cmd[0]);
521 dpavlin 2
522     #if 0
523     fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:",
524 dpavlin 6 id, xferp->cmd[0], xferp->cmd_len);
525 dpavlin 2 for (i=0; i<xferp->cmd_len; i++)
526     fatal(" %02x", xferp->cmd[i]);
527     fatal("\n");
528     if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11)
529 dpavlin 26 single_step = ENTER_SINGLE_STEPPING;
530 dpavlin 2 #endif
531    
532     #if 0
533     {
534     static FILE *f = NULL;
535     if (f == NULL)
536     f = fopen("scsi_log.txt", "w");
537     if (f != NULL) {
538     int i;
539 dpavlin 6 fprintf(f, "id=%i cmd =", id);
540 dpavlin 2 for (i=0; i<xferp->cmd_len; i++)
541     fprintf(f, " %02x", xferp->cmd[i]);
542     fprintf(f, "\n");
543     fflush(f);
544     }
545     }
546     #endif
547    
548     switch (xferp->cmd[0]) {
549    
550     case SCSICMD_TEST_UNIT_READY:
551     debug("TEST_UNIT_READY");
552     if (xferp->cmd_len != 6)
553     debug(" (weird len=%i)", xferp->cmd_len);
554    
555     /* TODO: bits 765 of buf[1] contains the LUN */
556     if (xferp->cmd[1] != 0x00)
557     fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x"
558     " not yet implemented\n", (int)xferp->cmd[1]);
559    
560     diskimage__return_default_status_and_message(xferp);
561     break;
562    
563     case SCSICMD_INQUIRY:
564     debug("INQUIRY");
565     if (xferp->cmd_len != 6)
566     debug(" (weird len=%i)", xferp->cmd_len);
567     if (xferp->cmd[1] != 0x00) {
568     debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet "
569     "implemented\n", (int)xferp->cmd[1]);
570    
571     break;
572     }
573    
574     /* Return values: */
575     retlen = xferp->cmd[4];
576     if (retlen < 36) {
577     fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen);
578     retlen = 36;
579     }
580    
581     /* Return data: */
582     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
583     retlen, 1);
584     xferp->data_in[0] = 0x00; /* 0x00 = Direct-access disk */
585     xferp->data_in[1] = 0x00; /* 0x00 = non-removable */
586     xferp->data_in[2] = 0x02; /* SCSI-2 */
587     #if 0
588     xferp->data_in[3] = 0x02; /* Response data format = SCSI-2 */
589     #endif
590     xferp->data_in[4] = retlen - 4; /* Additional length */
591     xferp->data_in[4] = 0x2c - 4; /* Additional length */
592     xferp->data_in[6] = 0x04; /* ACKREQQ */
593     xferp->data_in[7] = 0x60; /* WBus32, WBus16 */
594    
595 dpavlin 14 /* These are padded with spaces: */
596 dpavlin 2
597 dpavlin 20 memcpy(xferp->data_in+8, "GXemul ", 8);
598 dpavlin 14 if (diskimage_getname(cpu->machine, id,
599     type, namebuf, sizeof(namebuf))) {
600 dpavlin 22 size_t i;
601 dpavlin 14 for (i=0; i<sizeof(namebuf); i++)
602     if (namebuf[i] == 0) {
603     for (; i<sizeof(namebuf); i++)
604     namebuf[i] = ' ';
605     break;
606     }
607     memcpy(xferp->data_in+16, namebuf, 16);
608     } else
609     memcpy(xferp->data_in+16, "DISK ", 16);
610 dpavlin 20 memcpy(xferp->data_in+32, "0 ", 4);
611 dpavlin 14
612 dpavlin 2 /*
613     * Some Ultrix kernels want specific responses from
614     * the drives.
615     */
616    
617 dpavlin 22 if (machine->machine_type == MACHINE_PMAX) {
618 dpavlin 2 /* DEC, RZ25 (rev 0900) = 832527 sectors */
619     /* DEC, RZ58 (rev 2000) = 2698061 sectors */
620     memcpy(xferp->data_in+8, "DEC ", 8);
621     memcpy(xferp->data_in+16, "RZ58 (C) DEC", 16);
622     memcpy(xferp->data_in+32, "2000", 4);
623     }
624    
625     /* Some data is different for CD-ROM drives: */
626     if (d->is_a_cdrom) {
627     xferp->data_in[0] = 0x05; /* 0x05 = CD-ROM */
628     xferp->data_in[1] = 0x80; /* 0x80 = removable */
629 dpavlin 20 /* memcpy(xferp->data_in+16, "CD-ROM ", 16);*/
630 dpavlin 2
631 dpavlin 22 if (machine->machine_type == MACHINE_PMAX) {
632 dpavlin 2 /* SONY, CD-ROM: */
633     memcpy(xferp->data_in+8, "SONY ", 8);
634     memcpy(xferp->data_in+16,
635     "CD-ROM ", 16);
636    
637     /* ... or perhaps this: */
638     memcpy(xferp->data_in+8, "DEC ", 8);
639     memcpy(xferp->data_in+16,
640     "RRD42 (C) DEC ", 16);
641     memcpy(xferp->data_in+32, "4.5d", 4);
642 dpavlin 20 } else if (machine->machine_type == MACHINE_ARC) {
643 dpavlin 2 /* NEC, CD-ROM: */
644     memcpy(xferp->data_in+8, "NEC ", 8);
645     memcpy(xferp->data_in+16,
646     "CD-ROM CDR-210P ", 16);
647     memcpy(xferp->data_in+32, "1.0 ", 4);
648     }
649     }
650    
651     /* Data for tape devices: */
652     if (d->is_a_tape) {
653     xferp->data_in[0] = 0x01; /* 0x01 = tape */
654     xferp->data_in[1] = 0x80; /* 0x80 = removable */
655     memcpy(xferp->data_in+16, "TAPE ", 16);
656    
657 dpavlin 22 if (machine->machine_type == MACHINE_PMAX) {
658 dpavlin 2 /*
659     * TODO: find out if these are correct.
660     *
661     * The name might be TZK10, TSZ07, or TLZ04,
662     * or something completely different.
663     */
664     memcpy(xferp->data_in+8, "DEC ", 8);
665     memcpy(xferp->data_in+16,
666     "TK50 (C) DEC", 16);
667     memcpy(xferp->data_in+32, "2000", 4);
668     }
669     }
670    
671     diskimage__return_default_status_and_message(xferp);
672     break;
673    
674     case SCSIBLOCKCMD_READ_CAPACITY:
675     debug("READ_CAPACITY");
676    
677     if (xferp->cmd_len != 10)
678     fatal(" [ weird READ_CAPACITY len=%i, should be 10 ] ",
679     xferp->cmd_len);
680     else {
681     if (xferp->cmd[8] & 1) {
682     /* Partial Medium Indicator bit... TODO */
683     fatal("WARNING: READ_CAPACITY with PMI bit"
684     " set not yet implemented\n");
685     }
686     }
687    
688     /* Return data: */
689     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
690     8, 1);
691    
692     diskimage_recalc_size(d);
693    
694     size = d->total_size / d->logical_block_size;
695     if (d->total_size & (d->logical_block_size-1))
696     size ++;
697    
698     xferp->data_in[0] = (size >> 24) & 255;
699     xferp->data_in[1] = (size >> 16) & 255;
700     xferp->data_in[2] = (size >> 8) & 255;
701     xferp->data_in[3] = size & 255;
702    
703     xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
704     xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
705     xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
706     xferp->data_in[7] = d->logical_block_size & 255;
707    
708     diskimage__return_default_status_and_message(xferp);
709     break;
710    
711     case SCSICMD_MODE_SENSE:
712 dpavlin 20 case SCSICMD_MODE_SENSE10:
713 dpavlin 2 debug("MODE_SENSE");
714 dpavlin 20 q = 4; retlen = xferp->cmd[4];
715     switch (xferp->cmd_len) {
716     case 6: break;
717     case 10:q = 8;
718     retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
719     break;
720     default:fatal(" (unimplemented mode_sense len=%i)",
721 dpavlin 2 xferp->cmd_len);
722 dpavlin 20 }
723 dpavlin 2
724     /*
725     * NOTE/TODO: This code doesn't handle too short retlens
726     * very well. A quick hack around this is that I allocate
727     * a bit too much memory, so that nothing is actually
728     * written outside of xferp->data_in[].
729     */
730    
731     retlen += 100; /* Should be enough. (Ugly.) */
732    
733     if ((xferp->cmd[2] & 0xc0) != 0)
734     fatal("WARNING: mode sense, cmd[2] = 0x%02x\n",
735     xferp->cmd[2]);
736    
737     /* Return data: */
738     scsi_transfer_allocbuf(&xferp->data_in_len,
739     &xferp->data_in, retlen, 1);
740    
741     xferp->data_in_len -= 100; /* Restore size. */
742    
743     pagecode = xferp->cmd[2] & 0x3f;
744    
745 dpavlin 6 debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode);
746 dpavlin 2
747     /* 4 bytes of header for 6-byte command,
748     8 bytes of header for 10-byte command. */
749     xferp->data_in[0] = retlen; /* 0: mode data length */
750     xferp->data_in[1] = d->is_a_cdrom? 0x05 : 0x00;
751     /* 1: medium type */
752     xferp->data_in[2] = 0x00; /* device specific
753     parameter */
754     xferp->data_in[3] = 8 * 1; /* block descriptor
755     length: 1 page (?) */
756    
757 dpavlin 20 xferp->data_in[q+0] = 0x00; /* density code */
758     xferp->data_in[q+1] = 0; /* nr of blocks, high */
759     xferp->data_in[q+2] = 0; /* nr of blocks, mid */
760     xferp->data_in[q+3] = 0; /* nr of blocks, low */
761     xferp->data_in[q+4] = 0x00; /* reserved */
762     xferp->data_in[q+5] = (d->logical_block_size >> 16) & 255;
763     xferp->data_in[q+6] = (d->logical_block_size >> 8) & 255;
764     xferp->data_in[q+7] = d->logical_block_size & 255;
765     q += 8;
766 dpavlin 2
767     diskimage__return_default_status_and_message(xferp);
768    
769     /* descriptors, 8 bytes (each) */
770    
771     /* page, n bytes (each) */
772     switch (pagecode) {
773     case 0:
774     /* TODO: Nothing here? */
775     break;
776     case 1: /* read-write error recovery page */
777 dpavlin 20 xferp->data_in[q + 0] = pagecode;
778     xferp->data_in[q + 1] = 10;
779 dpavlin 2 break;
780     case 3: /* format device page */
781 dpavlin 20 xferp->data_in[q + 0] = pagecode;
782     xferp->data_in[q + 1] = 22;
783 dpavlin 2
784     /* 10,11 = sectors per track */
785 dpavlin 20 xferp->data_in[q + 10] = 0;
786     xferp->data_in[q + 11] = d->sectors_per_track;
787 dpavlin 2
788     /* 12,13 = physical sector size */
789 dpavlin 20 xferp->data_in[q + 12] =
790 dpavlin 2 (d->logical_block_size >> 8) & 255;
791 dpavlin 20 xferp->data_in[q + 13] = d->logical_block_size & 255;
792 dpavlin 2 break;
793     case 4: /* rigid disk geometry page */
794 dpavlin 20 xferp->data_in[q + 0] = pagecode;
795     xferp->data_in[q + 1] = 22;
796     xferp->data_in[q + 2] = (d->ncyls >> 16) & 255;
797     xferp->data_in[q + 3] = (d->ncyls >> 8) & 255;
798     xferp->data_in[q + 4] = d->ncyls & 255;
799     xferp->data_in[q + 5] = d->heads;
800 dpavlin 2
801 dpavlin 20 xferp->data_in[q + 20] = (d->rpms >> 8) & 255;
802     xferp->data_in[q + 21] = d->rpms & 255;
803 dpavlin 2 break;
804     case 5: /* flexible disk page */
805 dpavlin 20 xferp->data_in[q + 0] = pagecode;
806     xferp->data_in[q + 1] = 0x1e;
807 dpavlin 2
808     /* 2,3 = transfer rate */
809 dpavlin 20 xferp->data_in[q + 2] = ((5000) >> 8) & 255;
810     xferp->data_in[q + 3] = (5000) & 255;
811 dpavlin 2
812 dpavlin 20 xferp->data_in[q + 4] = d->heads;
813     xferp->data_in[q + 5] = d->sectors_per_track;
814 dpavlin 2
815     /* 6,7 = data bytes per sector */
816 dpavlin 20 xferp->data_in[q + 6] = (d->logical_block_size >> 8)
817 dpavlin 2 & 255;
818 dpavlin 20 xferp->data_in[q + 7] = d->logical_block_size & 255;
819 dpavlin 2
820 dpavlin 20 xferp->data_in[q + 8] = (d->ncyls >> 8) & 255;
821     xferp->data_in[q + 9] = d->ncyls & 255;
822 dpavlin 2
823 dpavlin 20 xferp->data_in[q + 28] = (d->rpms >> 8) & 255;
824     xferp->data_in[q + 29] = d->rpms & 255;
825 dpavlin 2 break;
826     default:
827     fatal("[ MODE_SENSE for page %i is not yet "
828     "implemented! ]\n", pagecode);
829     }
830    
831     break;
832    
833     case SCSICMD_READ:
834     case SCSICMD_READ_10:
835     debug("READ");
836    
837     /*
838     * For tape devices, read data at the current position.
839     * For disk and CDROM devices, the command bytes contain
840     * an offset telling us where to read from the device.
841     */
842    
843     if (d->is_a_tape) {
844     /* bits 7..5 of cmd[1] are the LUN bits... TODO */
845    
846     size = (xferp->cmd[2] << 16) +
847     (xferp->cmd[3] << 8) +
848     xferp->cmd[4];
849    
850     /* Bit 1 of cmd[1] is the SILI bit (TODO), and
851     bit 0 is the "use fixed length" bit. */
852    
853     if (xferp->cmd[1] & 0x01) {
854     /* Fixed block length: */
855     size *= d->logical_block_size;
856     }
857    
858     if (d->filemark) {
859     /* At end of file, switch to the next
860     automagically: */
861     d->tape_filenr ++;
862     diskimage__switch_tape(d);
863    
864     d->filemark = 0;
865     }
866    
867     ofs = d->tape_offset;
868    
869     fatal("[ READ tape, id=%i file=%i, cmd[1]=%02x size=%i"
870 dpavlin 6 ", ofs=%lli ]\n", id, d->tape_filenr,
871 dpavlin 2 xferp->cmd[1], (int)size, (long long)ofs);
872     } else {
873     if (xferp->cmd[0] == SCSICMD_READ) {
874     if (xferp->cmd_len != 6)
875     debug(" (weird len=%i)",
876     xferp->cmd_len);
877    
878     /*
879     * bits 4..0 of cmd[1], and cmd[2] and cmd[3]
880     * hold the logical block address.
881     *
882     * cmd[4] holds the number of logical blocks
883     * to transfer. (Special case if the value is
884     * 0, actually means 256.)
885     */
886     ofs = ((xferp->cmd[1] & 0x1f) << 16) +
887     (xferp->cmd[2] << 8) + xferp->cmd[3];
888     retlen = xferp->cmd[4];
889     if (retlen == 0)
890     retlen = 256;
891     } else {
892     if (xferp->cmd_len != 10)
893     debug(" (weird len=%i)",
894     xferp->cmd_len);
895    
896     /*
897     * cmd[2..5] hold the logical block address.
898     * cmd[7..8] holds the number of logical
899     * blocks to transfer. (NOTE: If the value is
900     * 0, this means 0, not 65536. :-)
901     */
902 dpavlin 22 ofs = ((uint64_t)xferp->cmd[2] << 24) +
903     (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8)
904     + xferp->cmd[5];
905 dpavlin 2 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
906     }
907    
908     size = retlen * d->logical_block_size;
909     ofs *= d->logical_block_size;
910     }
911    
912     /* Return data: */
913     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
914     size, 0);
915    
916     debug(" READ ofs=%lli size=%i\n", (long long)ofs, (int)size);
917    
918     diskimage__return_default_status_and_message(xferp);
919    
920     d->filemark = 0;
921    
922     /*
923     * Failure? Then set check condition.
924     * For tapes, error should only occur at the end of a file.
925     *
926     * "If the logical unit encounters a filemark during
927     * a READ command, CHECK CONDITION status shall be
928     * returned and the filemark and valid bits shall be
929     * set to one in the sense data. The sense key shall
930     * be set to NO SENSE"..
931     */
932     if (d->is_a_tape && d->f != NULL && feof(d->f)) {
933 dpavlin 6 debug(" feof id=%i\n", id);
934 dpavlin 2 xferp->status[0] = 0x02; /* CHECK CONDITION */
935    
936     d->filemark = 1;
937     } else
938     diskimage__internal_access(d, 0, ofs,
939     xferp->data_in, size);
940    
941     if (d->is_a_tape && d->f != NULL)
942 dpavlin 10 d->tape_offset = ftello(d->f);
943 dpavlin 2
944     /* TODO: other errors? */
945     break;
946    
947     case SCSICMD_WRITE:
948     case SCSICMD_WRITE_10:
949     debug("WRITE");
950    
951     /* TODO: tape */
952    
953     if (xferp->cmd[0] == SCSICMD_WRITE) {
954     if (xferp->cmd_len != 6)
955     debug(" (weird len=%i)", xferp->cmd_len);
956    
957     /*
958     * bits 4..0 of cmd[1], and cmd[2] and cmd[3] hold the
959     * logical block address.
960     *
961     * cmd[4] holds the number of logical blocks to
962     * transfer. (Special case if the value is 0, actually
963     * means 256.)
964     */
965     ofs = ((xferp->cmd[1] & 0x1f) << 16) +
966     (xferp->cmd[2] << 8) + xferp->cmd[3];
967     retlen = xferp->cmd[4];
968     if (retlen == 0)
969     retlen = 256;
970     } else {
971     if (xferp->cmd_len != 10)
972     debug(" (weird len=%i)", xferp->cmd_len);
973    
974     /*
975     * cmd[2..5] hold the logical block address.
976     * cmd[7..8] holds the number of logical blocks to
977     * transfer. (NOTE: If the value is 0 this means 0,
978     * not 65536.)
979     */
980 dpavlin 22 ofs = ((uint64_t)xferp->cmd[2] << 24) +
981     (xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) +
982     xferp->cmd[5];
983 dpavlin 2 retlen = (xferp->cmd[7] << 8) + xferp->cmd[8];
984     }
985    
986     size = retlen * d->logical_block_size;
987     ofs *= d->logical_block_size;
988    
989     if (xferp->data_out_offset != size) {
990     debug(", data_out == NULL, wanting %i bytes, \n\n",
991     (int)size);
992     xferp->data_out_len = size;
993     return 2;
994     }
995    
996     debug(", data_out != NULL, OK :-)");
997    
998     debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs,
999     (int)size, (int)xferp->data_out_offset);
1000    
1001     diskimage__internal_access(d, 1, ofs,
1002     xferp->data_out, size);
1003    
1004     /* TODO: how about return code? */
1005    
1006     /* Is this really necessary? */
1007     /* fsync(fileno(d->f)); */
1008    
1009     diskimage__return_default_status_and_message(xferp);
1010     break;
1011    
1012     case SCSICMD_SYNCHRONIZE_CACHE:
1013     debug("SYNCHRONIZE_CACHE");
1014    
1015     if (xferp->cmd_len != 10)
1016     debug(" (weird len=%i)", xferp->cmd_len);
1017    
1018     /* TODO: actualy care about cmd[] */
1019     fsync(fileno(d->f));
1020    
1021     diskimage__return_default_status_and_message(xferp);
1022     break;
1023    
1024     case SCSICMD_START_STOP_UNIT:
1025     debug("START_STOP_UNIT");
1026    
1027     if (xferp->cmd_len != 6)
1028     debug(" (weird len=%i)", xferp->cmd_len);
1029    
1030 dpavlin 22 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1031 dpavlin 2 debug(" %02x", xferp->cmd[i]);
1032    
1033     /* TODO: actualy care about cmd[] */
1034    
1035     diskimage__return_default_status_and_message(xferp);
1036     break;
1037    
1038     case SCSICMD_REQUEST_SENSE:
1039     debug("REQUEST_SENSE");
1040    
1041     retlen = xferp->cmd[4];
1042    
1043     /* TODO: bits 765 of buf[1] contains the LUN */
1044     if (xferp->cmd[1] != 0x00)
1045     fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not"
1046     " yet implemented\n", (int)xferp->cmd[1]);
1047    
1048     if (retlen < 18) {
1049     fatal("WARNING: SCSI request sense len=%i, <18!\n",
1050     (int)retlen);
1051     retlen = 18;
1052     }
1053    
1054     /* Return data: */
1055     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1056     retlen, 1);
1057    
1058     xferp->data_in[0] = 0x80 + 0x70;/* 0x80 = valid,
1059     0x70 = "current errors" */
1060     xferp->data_in[2] = 0x00; /* SENSE KEY! */
1061    
1062     if (d->filemark) {
1063     xferp->data_in[2] = 0x80;
1064     }
1065     debug(": [2]=0x%02x ", xferp->data_in[2]);
1066    
1067     printf(" XXX(!) \n");
1068    
1069     /* TODO */
1070     xferp->data_in[7] = retlen - 7; /* additional sense length */
1071     /* TODO */
1072    
1073     diskimage__return_default_status_and_message(xferp);
1074     break;
1075    
1076     case SCSICMD_READ_BLOCK_LIMITS:
1077     debug("READ_BLOCK_LIMITS");
1078    
1079     retlen = 6;
1080    
1081     /* TODO: bits 765 of buf[1] contains the LUN */
1082     if (xferp->cmd[1] != 0x00)
1083     fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]="
1084     "0x%02x not yet implemented\n", (int)xferp->cmd[1]);
1085    
1086     /* Return data: */
1087     scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in,
1088     retlen, 1);
1089    
1090     /*
1091     * data[0] is reserved, data[1..3] contain the maximum block
1092     * length limit, data[4..5] contain the minimum limit.
1093     */
1094    
1095     {
1096     int max_limit = 32768;
1097     int min_limit = 128;
1098    
1099     xferp->data_in[1] = (max_limit >> 16) & 255;
1100     xferp->data_in[2] = (max_limit >> 8) & 255;
1101     xferp->data_in[3] = max_limit & 255;
1102     xferp->data_in[4] = (min_limit >> 8) & 255;
1103     xferp->data_in[5] = min_limit & 255;
1104     }
1105    
1106     diskimage__return_default_status_and_message(xferp);
1107     break;
1108    
1109     case SCSICMD_REWIND:
1110     debug("REWIND");
1111    
1112     /* TODO: bits 765 of buf[1] contains the LUN */
1113     if ((xferp->cmd[1] & 0xe0) != 0x00)
1114     fatal("WARNING: REWIND with cmd[1]=0x%02x not yet "
1115     "implemented\n", (int)xferp->cmd[1]);
1116    
1117     /* Close and reopen. */
1118    
1119     if (d->f != NULL)
1120     fclose(d->f);
1121    
1122     d->f = fopen(d->fname, d->writable? "r+" : "r");
1123     if (d->f == NULL) {
1124     fprintf(stderr, "[ diskimage: could not (re)open "
1125     "'%s' ]\n", d->fname);
1126     /* TODO: return error */
1127     }
1128    
1129     d->tape_offset = 0;
1130     d->tape_filenr = 0;
1131     d->filemark = 0;
1132    
1133     diskimage__return_default_status_and_message(xferp);
1134     break;
1135    
1136     case SCSICMD_SPACE:
1137     debug("SPACE");
1138    
1139     /* TODO: bits 765 of buf[1] contains the LUN */
1140     if ((xferp->cmd[1] & 0xe0) != 0x00)
1141     fatal("WARNING: SPACE with cmd[1]=0x%02x not yet "
1142     "implemented\n", (int)xferp->cmd[1]);
1143    
1144     /*
1145     * Bits 2..0 of buf[1] contain the 'code' which describes how
1146 dpavlin 32 * spacing should be done, and buf[2..4] contain the number of
1147 dpavlin 2 * operations.
1148     */
1149     debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n",
1150     xferp->cmd[0],
1151     xferp->cmd[1],
1152     xferp->cmd[2],
1153     xferp->cmd[3],
1154     xferp->cmd[4],
1155     xferp->cmd[5]);
1156    
1157     switch (xferp->cmd[1] & 7) {
1158     case 1: /* Seek to a different file nr: */
1159     {
1160     int diff = (xferp->cmd[2] << 16) +
1161     (xferp->cmd[3] << 8) + xferp->cmd[4];
1162    
1163     /* Negative seek offset: */
1164     if (diff & (1 << 23))
1165     diff = - (16777216 - diff);
1166    
1167     d->tape_filenr += diff;
1168     }
1169    
1170     /* At end of file, switch to the next tape file: */
1171     if (d->filemark) {
1172     d->tape_filenr ++;
1173     d->filemark = 0;
1174     }
1175    
1176     debug("{ switching to tape file %i }", d->tape_filenr);
1177     diskimage__switch_tape(d);
1178     d->filemark = 0;
1179     break;
1180     default:
1181     fatal("[ diskimage.c: unimplemented SPACE type %i ]\n",
1182     xferp->cmd[1] & 7);
1183     }
1184    
1185     diskimage__return_default_status_and_message(xferp);
1186     break;
1187    
1188     case SCSICDROM_READ_SUBCHANNEL:
1189     /*
1190     * According to
1191     * http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html:
1192     *
1193     * "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT
1194     * commands have the same opcode in SCSI or ATAPI, but don't
1195     * have the same command structure"...
1196     *
1197     * TODO: This still doesn't work. Hm.
1198     */
1199     retlen = 48;
1200    
1201     debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x",
1202     xferp->cmd[1]);
1203    
1204     /* Return data: */
1205     scsi_transfer_allocbuf(&xferp->data_in_len,
1206     &xferp->data_in, retlen, 1);
1207    
1208     diskimage_recalc_size(d);
1209    
1210     size = d->total_size / d->logical_block_size;
1211     if (d->total_size & (d->logical_block_size-1))
1212     size ++;
1213    
1214     xferp->data_in[0] = (size >> 24) & 255;
1215     xferp->data_in[1] = (size >> 16) & 255;
1216     xferp->data_in[2] = (size >> 8) & 255;
1217     xferp->data_in[3] = size & 255;
1218    
1219     xferp->data_in[4] = (d->logical_block_size >> 24) & 255;
1220     xferp->data_in[5] = (d->logical_block_size >> 16) & 255;
1221     xferp->data_in[6] = (d->logical_block_size >> 8) & 255;
1222     xferp->data_in[7] = d->logical_block_size & 255;
1223    
1224     diskimage__return_default_status_and_message(xferp);
1225     break;
1226    
1227     case SCSICDROM_READ_TOC:
1228     debug("(CDROM_READ_TOC: ");
1229     debug("lun=%i msf=%i ",
1230     xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1);
1231     debug("starting_track=%i ", xferp->cmd[6]);
1232     retlen = xferp->cmd[7] * 256 + xferp->cmd[8];
1233     debug("allocation_len=%i)\n", retlen);
1234    
1235     /* Return data: */
1236     scsi_transfer_allocbuf(&xferp->data_in_len,
1237     &xferp->data_in, retlen, 1);
1238    
1239 dpavlin 20 xferp->data_in[0] = 0;
1240     xferp->data_in[1] = 10;
1241     xferp->data_in[2] = 0; /* First track. */
1242     xferp->data_in[3] = 0; /* Last track. */
1243 dpavlin 2
1244 dpavlin 20 /* Track 0 data: */
1245     xferp->data_in[4] = 0x00; /* Reserved. */
1246     xferp->data_in[5] = 0x04; /* ADR + CTRL:
1247     Data, not audio */
1248     xferp->data_in[6] = 0x00; /* Track nr */
1249     xferp->data_in[7] = 0x00; /* Reserved */
1250     /* 8..11 = absolute CDROM address */
1251    
1252 dpavlin 2 diskimage__return_default_status_and_message(xferp);
1253     break;
1254    
1255 dpavlin 32 case SCSICDROM_READ_DISCINFO:
1256     debug("(SCSICDROM_READ_DISCINFO: ");
1257     debug("TODO");
1258     retlen = 0;
1259    
1260     /* Return data: */
1261     scsi_transfer_allocbuf(&xferp->data_in_len,
1262     &xferp->data_in, retlen, 1);
1263    
1264     /* TODO */
1265    
1266     diskimage__return_default_status_and_message(xferp);
1267     break;
1268    
1269     case SCSICDROM_READ_TRACKINFO:
1270     debug("(SCSICDROM_READ_TRACKINFO: ");
1271     debug("TODO");
1272     retlen = 0;
1273    
1274     /* Return data: */
1275     scsi_transfer_allocbuf(&xferp->data_in_len,
1276     &xferp->data_in, retlen, 1);
1277    
1278     /* TODO */
1279    
1280     diskimage__return_default_status_and_message(xferp);
1281     break;
1282    
1283 dpavlin 2 case SCSICMD_MODE_SELECT:
1284     debug("[ SCSI MODE_SELECT: ");
1285    
1286     /*
1287     * TODO:
1288     *
1289     * This is super-hardcoded for NetBSD's usage of mode_select
1290     * to set the size of CDROM sectors to 2048.
1291     */
1292    
1293     if (xferp->data_out_offset == 0) {
1294     xferp->data_out_len = 12; /* TODO */
1295     debug("data_out == NULL, wanting %i bytes ]\n",
1296     (int)xferp->data_out_len);
1297     return 2;
1298     }
1299    
1300     debug("data_out!=NULL (OK), ");
1301    
1302     /* TODO: Care about cmd? */
1303    
1304     /* Set sector size to 2048: */
1305     /* 00 05 00 08 00 03 ca 40 00 00 08 00 */
1306     if (xferp->data_out[0] == 0x00 &&
1307     xferp->data_out[1] == 0x05 &&
1308     xferp->data_out[2] == 0x00 &&
1309     xferp->data_out[3] == 0x08) {
1310     d->logical_block_size =
1311     (xferp->data_out[9] << 16) +
1312     (xferp->data_out[10] << 8) +
1313     xferp->data_out[11];
1314     debug("[ setting logical_block_size to %i ]\n",
1315     d->logical_block_size);
1316     } else {
1317     int i;
1318     fatal("[ unknown MODE_SELECT: cmd =");
1319 dpavlin 22 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1320 dpavlin 2 fatal(" %02x", xferp->cmd[i]);
1321     fatal(", data_out =");
1322 dpavlin 22 for (i=0; i<(ssize_t)xferp->data_out_len; i++)
1323 dpavlin 2 fatal(" %02x", xferp->data_out[i]);
1324     fatal(" ]");
1325     }
1326    
1327     debug(" ]\n");
1328     diskimage__return_default_status_and_message(xferp);
1329     break;
1330    
1331 dpavlin 20 case SCSICMD_PREVENT_ALLOW_REMOVE:
1332     debug("[ SCSI 0x%02x Prevent/allow medium removal: "
1333     "TODO ]\n", xferp->cmd[0]);
1334 dpavlin 2
1335     diskimage__return_default_status_and_message(xferp);
1336     break;
1337    
1338     case 0xbd:
1339     fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0],
1340     xferp->cmd_len);
1341 dpavlin 22 for (i=0; i<(ssize_t)xferp->cmd_len; i++)
1342 dpavlin 2 fatal(" %02x", xferp->cmd[i]);
1343     fatal(" ]\n");
1344    
1345     /*
1346     * Used by Windows NT?
1347     *
1348     * Not documented in http://www.danbbs.dk/~dino/
1349     * SCSI/SCSI2-D.html.
1350     * Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm.
1351     */
1352    
1353     if (xferp->cmd_len < 12) {
1354     fatal("WEIRD LEN?\n");
1355     retlen = 8;
1356     } else {
1357     retlen = xferp->cmd[8] * 256 + xferp->cmd[9];
1358     }
1359    
1360     /* Return data: */
1361     scsi_transfer_allocbuf(&xferp->data_in_len,
1362     &xferp->data_in, retlen, 1);
1363    
1364     diskimage__return_default_status_and_message(xferp);
1365 dpavlin 10
1366 dpavlin 2 break;
1367    
1368     default:
1369     fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n",
1370 dpavlin 6 xferp->cmd[0], id);
1371 dpavlin 2 exit(1);
1372     }
1373     debug(" ]\n");
1374    
1375     return 1;
1376     }
1377    
1378    
1379     /*
1380     * diskimage_access():
1381     *
1382     * Read from or write to a disk image on a machine.
1383     *
1384     * Returns 1 if the access completed successfully, 0 otherwise.
1385     */
1386 dpavlin 6 int diskimage_access(struct machine *machine, int id, int type, int writeflag,
1387 dpavlin 2 off_t offset, unsigned char *buf, size_t len)
1388     {
1389     struct diskimage *d = machine->first_diskimage;
1390    
1391     while (d != NULL) {
1392 dpavlin 6 if (d->type == type && d->id == id)
1393 dpavlin 2 break;
1394     d = d->next;
1395     }
1396    
1397     if (d == NULL) {
1398     fatal("[ diskimage_access(): ERROR: trying to access a "
1399 dpavlin 6 "non-existant %s disk image (id %i)\n",
1400     diskimage_types[type], id);
1401 dpavlin 2 return 0;
1402     }
1403    
1404     return diskimage__internal_access(d, writeflag, offset, buf, len);
1405     }
1406    
1407    
1408     /*
1409     * diskimage_add():
1410     *
1411     * Add a disk image. fname is the filename of the disk image.
1412     * The filename may be prefixed with one or more modifiers, followed
1413     * by a colon.
1414     *
1415 dpavlin 6 * b specifies that this is a bootable device
1416     * c CD-ROM (instead of a normal DISK)
1417 dpavlin 4 * d DISK (this is the default)
1418     * f FLOPPY (instead of SCSI)
1419 dpavlin 6 * gH;S; set geometry (H=heads, S=sectors per track, cylinders are
1420     * automatically calculated). (This is ignored for floppies.)
1421 dpavlin 2 * i IDE (instead of SCSI)
1422     * r read-only (don't allow changes to the file)
1423 dpavlin 4 * s SCSI (this is the default)
1424     * t tape
1425 dpavlin 2 * 0-7 force a specific SCSI ID number
1426     *
1427     * machine is assumed to be non-NULL.
1428     * Returns an integer >= 0 identifying the disk image.
1429     */
1430     int diskimage_add(struct machine *machine, char *fname)
1431     {
1432     struct diskimage *d, *d2;
1433 dpavlin 6 int id = 0, override_heads=0, override_spt=0;
1434     int64_t bytespercyl;
1435 dpavlin 2 char *cp;
1436 dpavlin 6 int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0;
1437     int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id = -1;
1438 dpavlin 2
1439     if (fname == NULL) {
1440     fprintf(stderr, "diskimage_add(): NULL ptr\n");
1441     return 0;
1442     }
1443    
1444     /* Get prefix from fname: */
1445     cp = strchr(fname, ':');
1446     if (cp != NULL) {
1447     while (fname <= cp) {
1448     char c = *fname++;
1449     switch (c) {
1450     case '0':
1451     case '1':
1452     case '2':
1453     case '3':
1454     case '4':
1455     case '5':
1456     case '6':
1457     case '7':
1458     prefix_id = c - '0';
1459     break;
1460     case 'b':
1461     prefix_b = 1;
1462     break;
1463     case 'c':
1464     prefix_c = 1;
1465     break;
1466     case 'd':
1467     prefix_d = 1;
1468     break;
1469 dpavlin 4 case 'f':
1470     prefix_f = 1;
1471     break;
1472 dpavlin 6 case 'g':
1473     prefix_g = 1;
1474     override_heads = atoi(fname);
1475     while (*fname != '\0' && *fname != ';')
1476     fname ++;
1477     if (*fname == ';')
1478     fname ++;
1479     override_spt = atoi(fname);
1480     while (*fname != '\0' && *fname != ';' &&
1481     *fname != ':')
1482     fname ++;
1483     if (*fname == ';')
1484     fname ++;
1485     if (override_heads < 1 ||
1486     override_spt < 1) {
1487     fatal("Bad geometry: heads=%i "
1488     "spt=%i\n", override_heads,
1489     override_spt);
1490     exit(1);
1491     }
1492     break;
1493 dpavlin 2 case 'i':
1494     prefix_i = 1;
1495     break;
1496 dpavlin 4 case 'r':
1497     prefix_r = 1;
1498     break;
1499     case 's':
1500     prefix_s = 1;
1501     break;
1502 dpavlin 2 case 't':
1503     prefix_t = 1;
1504     break;
1505     case ':':
1506     break;
1507     default:
1508     fprintf(stderr, "diskimage_add(): invalid "
1509     "prefix char '%c'\n", c);
1510     exit(1);
1511     }
1512     }
1513     }
1514    
1515     /* Allocate a new diskimage struct: */
1516     d = malloc(sizeof(struct diskimage));
1517     if (d == NULL) {
1518     fprintf(stderr, "out of memory in diskimage_add()\n");
1519     exit(1);
1520     }
1521     memset(d, 0, sizeof(struct diskimage));
1522    
1523     d2 = machine->first_diskimage;
1524     if (d2 == NULL) {
1525     machine->first_diskimage = d;
1526     } else {
1527     while (d2->next != NULL)
1528     d2 = d2->next;
1529     d2->next = d;
1530     }
1531    
1532 dpavlin 24 /* Default to IDE disks... */
1533 dpavlin 22 d->type = DISKIMAGE_IDE;
1534 dpavlin 2
1535 dpavlin 24 /* ... but some machines use SCSI by default: */
1536 dpavlin 22 if (machine->machine_type == MACHINE_PMAX ||
1537     machine->machine_type == MACHINE_ARC)
1538     d->type = DISKIMAGE_SCSI;
1539 dpavlin 4
1540     if (prefix_i + prefix_f + prefix_s > 1) {
1541     fprintf(stderr, "Invalid disk image prefix(es). You can"
1542     "only use one of i, f, and s\nfor each disk image.\n");
1543     exit(1);
1544     }
1545    
1546 dpavlin 2 if (prefix_i)
1547     d->type = DISKIMAGE_IDE;
1548 dpavlin 4 if (prefix_f)
1549     d->type = DISKIMAGE_FLOPPY;
1550     if (prefix_s)
1551     d->type = DISKIMAGE_SCSI;
1552 dpavlin 2
1553     d->fname = strdup(fname);
1554     if (d->fname == NULL) {
1555     fprintf(stderr, "out of memory\n");
1556     exit(1);
1557     }
1558    
1559     d->logical_block_size = 512;
1560    
1561     /*
1562     * Is this a tape, CD-ROM or a normal disk?
1563     *
1564     * An intelligent guess, if no prefixes are used, would be that
1565 dpavlin 14 * filenames ending with .iso or .cdr are CD-ROM images.
1566 dpavlin 2 */
1567     if (prefix_t) {
1568     d->is_a_tape = 1;
1569     } else {
1570     if (prefix_c ||
1571     ((strlen(d->fname) > 4 &&
1572 dpavlin 14 (strcasecmp(d->fname + strlen(d->fname) - 4, ".cdr") == 0 ||
1573     strcasecmp(d->fname + strlen(d->fname) - 4, ".iso") == 0))
1574 dpavlin 2 && !prefix_d)
1575     ) {
1576     d->is_a_cdrom = 1;
1577    
1578     /*
1579     * This is tricky. Should I use 512 or 2048 here?
1580     * NetBSD/pmax 1.6.2 and Ultrix likes 512 bytes
1581     * per sector, but NetBSD 2.0_BETA suddenly ignores
1582     * this value and uses 2048 instead.
1583     *
1584     * OpenBSD/arc doesn't like 2048, it requires 512
1585     * to work correctly.
1586     *
1587     * TODO
1588     */
1589    
1590     #if 0
1591 dpavlin 22 if (machine->machine_type == MACHINE_PMAX)
1592 dpavlin 2 d->logical_block_size = 512;
1593     else
1594     d->logical_block_size = 2048;
1595     #endif
1596     d->logical_block_size = 512;
1597     }
1598     }
1599    
1600     diskimage_recalc_size(d);
1601    
1602 dpavlin 6 if ((d->total_size == 720*1024 || d->total_size == 1474560
1603     || d->total_size == 2949120 || d->total_size == 1228800)
1604     && !prefix_i && !prefix_s)
1605 dpavlin 4 d->type = DISKIMAGE_FLOPPY;
1606    
1607 dpavlin 6 switch (d->type) {
1608     case DISKIMAGE_FLOPPY:
1609     if (d->total_size < 737280) {
1610     fatal("\nTODO: small (non-80-cylinder) floppies?\n\n");
1611     exit(1);
1612     }
1613     d->cylinders = 80;
1614     d->heads = 2;
1615     d->sectors_per_track = d->total_size / (d->cylinders *
1616     d->heads * 512);
1617     break;
1618     default:/* Non-floppies: */
1619     d->heads = 16;
1620     d->sectors_per_track = 63;
1621     if (prefix_g) {
1622     d->chs_override = 1;
1623     d->heads = override_heads;
1624     d->sectors_per_track = override_spt;
1625     }
1626     bytespercyl = d->heads * d->sectors_per_track * 512;
1627     d->cylinders = d->total_size / bytespercyl;
1628     if (d->cylinders * bytespercyl < d->total_size)
1629     d->cylinders ++;
1630     }
1631    
1632 dpavlin 2 d->rpms = 3600;
1633    
1634     if (prefix_b)
1635     d->is_boot_device = 1;
1636    
1637     d->writable = access(fname, W_OK) == 0? 1 : 0;
1638    
1639     if (d->is_a_cdrom || prefix_r)
1640     d->writable = 0;
1641    
1642     d->f = fopen(fname, d->writable? "r+" : "r");
1643     if (d->f == NULL) {
1644     perror(fname);
1645     exit(1);
1646     }
1647    
1648 dpavlin 6 /* Calculate which ID to use: */
1649     if (prefix_id == -1) {
1650     int free = 0, collision = 1;
1651    
1652     while (collision) {
1653     collision = 0;
1654     d2 = machine->first_diskimage;
1655     while (d2 != NULL) {
1656     /* (don't compare against ourselves :) */
1657     if (d2 == d) {
1658     d2 = d2->next;
1659     continue;
1660     }
1661     if (d2->id == free && d2->type == d->type) {
1662     collision = 1;
1663     break;
1664     }
1665     d2 = d2->next;
1666     }
1667     if (!collision)
1668     id = free;
1669     else
1670     free ++;
1671     }
1672     } else {
1673     id = prefix_id;
1674     d2 = machine->first_diskimage;
1675     while (d2 != NULL) {
1676     /* (don't compare against ourselves :) */
1677     if (d2 == d) {
1678     d2 = d2->next;
1679     continue;
1680     }
1681     if (d2->id == id && d2->type == d->type) {
1682     fprintf(stderr, "disk image id %i "
1683     "already in use\n", id);
1684     exit(1);
1685     }
1686     d2 = d2->next;
1687     }
1688     }
1689    
1690     d->id = id;
1691    
1692 dpavlin 2 return id;
1693     }
1694    
1695    
1696     /*
1697     * diskimage_bootdev():
1698     *
1699 dpavlin 6 * Returns the disk id of the device which we're booting from. If typep is
1700     * non-NULL, the type is returned as well.
1701 dpavlin 2 *
1702     * If no disk was used as boot device, then -1 is returned. (In practice,
1703     * this is used to fake network (tftp) boot.)
1704     */
1705 dpavlin 6 int diskimage_bootdev(struct machine *machine, int *typep)
1706 dpavlin 2 {
1707 dpavlin 6 struct diskimage *d;
1708    
1709     d = machine->first_diskimage;
1710 dpavlin 2 while (d != NULL) {
1711 dpavlin 6 if (d->is_boot_device) {
1712     if (typep != NULL)
1713     *typep = d->type;
1714 dpavlin 2 return d->id;
1715 dpavlin 6 }
1716 dpavlin 2 d = d->next;
1717     }
1718    
1719     d = machine->first_diskimage;
1720 dpavlin 6 if (d != NULL) {
1721     if (typep != NULL)
1722     *typep = d->type;
1723 dpavlin 2 return d->id;
1724 dpavlin 6 }
1725 dpavlin 2
1726     return -1;
1727     }
1728    
1729    
1730     /*
1731 dpavlin 14 * diskimage_getname():
1732     *
1733     * Returns 1 if a valid disk image name was returned, 0 otherwise.
1734     */
1735     int diskimage_getname(struct machine *machine, int id, int type,
1736     char *buf, size_t bufsize)
1737     {
1738     struct diskimage *d = machine->first_diskimage;
1739    
1740     if (buf == NULL)
1741     return 0;
1742    
1743     while (d != NULL) {
1744     if (d->type == type && d->id == id) {
1745     char *p = strrchr(d->fname, '/');
1746     if (p == NULL)
1747     p = d->fname;
1748     else
1749     p ++;
1750     snprintf(buf, bufsize, "%s", p);
1751     return 1;
1752     }
1753     d = d->next;
1754     }
1755     return 0;
1756     }
1757    
1758    
1759     /*
1760 dpavlin 2 * diskimage_is_a_cdrom():
1761     *
1762 dpavlin 6 * Returns 1 if a disk image is a CDROM, 0 otherwise.
1763 dpavlin 2 */
1764 dpavlin 6 int diskimage_is_a_cdrom(struct machine *machine, int id, int type)
1765 dpavlin 2 {
1766     struct diskimage *d = machine->first_diskimage;
1767    
1768     while (d != NULL) {
1769 dpavlin 6 if (d->type == type && d->id == id)
1770 dpavlin 2 return d->is_a_cdrom;
1771     d = d->next;
1772     }
1773     return 0;
1774     }
1775    
1776    
1777     /*
1778     * diskimage_is_a_tape():
1779     *
1780 dpavlin 6 * Returns 1 if a disk image is a tape, 0 otherwise.
1781     *
1782 dpavlin 2 * (Used in src/machine.c, to select 'rz' vs 'tz' for DECstation
1783     * boot strings.)
1784     */
1785 dpavlin 6 int diskimage_is_a_tape(struct machine *machine, int id, int type)
1786 dpavlin 2 {
1787     struct diskimage *d = machine->first_diskimage;
1788    
1789     while (d != NULL) {
1790 dpavlin 6 if (d->type == type && d->id == id)
1791 dpavlin 2 return d->is_a_tape;
1792     d = d->next;
1793     }
1794     return 0;
1795     }
1796    
1797    
1798     /*
1799     * diskimage_dump_info():
1800     *
1801     * Debug dump of all diskimages that are loaded for a specific machine.
1802     */
1803     void diskimage_dump_info(struct machine *machine)
1804     {
1805 dpavlin 22 int iadd = DEBUG_INDENTATION;
1806 dpavlin 2 struct diskimage *d = machine->first_diskimage;
1807    
1808     while (d != NULL) {
1809     debug("diskimage: %s\n", d->fname);
1810     debug_indentation(iadd);
1811    
1812     switch (d->type) {
1813     case DISKIMAGE_SCSI:
1814     debug("SCSI");
1815     break;
1816     case DISKIMAGE_IDE:
1817     debug("IDE");
1818     break;
1819 dpavlin 4 case DISKIMAGE_FLOPPY:
1820     debug("FLOPPY");
1821     break;
1822 dpavlin 2 default:
1823     debug("UNKNOWN type %i", d->type);
1824     }
1825    
1826     debug(" %s", d->is_a_tape? "TAPE" :
1827     (d->is_a_cdrom? "CD-ROM" : "DISK"));
1828     debug(" id %i, ", d->id);
1829     debug("%s, ", d->writable? "read/write" : "read-only");
1830    
1831 dpavlin 4 if (d->type == DISKIMAGE_FLOPPY)
1832     debug("%lli KB", (long long) (d->total_size / 1024));
1833     else
1834     debug("%lli MB", (long long) (d->total_size / 1048576));
1835    
1836 dpavlin 6 if (d->type == DISKIMAGE_FLOPPY || d->chs_override)
1837     debug(" (CHS=%i,%i,%i)", d->cylinders, d->heads,
1838     d->sectors_per_track);
1839     else
1840     debug(" (%lli sectors)", (long long)
1841     (d->total_size / 512));
1842 dpavlin 2
1843 dpavlin 6 if (d->is_boot_device)
1844     debug(" (BOOT)");
1845     debug("\n");
1846    
1847 dpavlin 2 debug_indentation(-iadd);
1848    
1849     d = d->next;
1850     }
1851     }
1852    

  ViewVC Help
Powered by ViewVC 1.1.26