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