/[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

Contents of /trunk/src/diskimage.c

Parent Directory Parent Directory | Revision Log Revision Log


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

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


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

  ViewVC Help
Powered by ViewVC 1.1.26