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