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