25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: diskimage.c,v 1.1 2007/03/16 14:13:59 debug Exp $ |
* $Id: diskimage.c,v 1.5 2007/03/26 03:01:09 debug Exp $ |
29 |
* |
* |
30 |
* Disk image support. |
* Disk image support. |
31 |
* |
* |
|
* TODO: There's probably a bug in the tape support: |
|
|
* Let's say there are 10240 bytes left in a file, and 10240 |
|
|
* bytes are read. Then feof() is not true yet (?), so the next |
|
|
* read will also return 10240 bytes (but all zeroes), and then after |
|
|
* that return feof (which results in a filemark). This is probably |
|
|
* trivial to fix, but I don't feel like it right now. |
|
|
* |
|
32 |
* TODO: diskimage_remove()? This would be useful for floppies in PC-style |
* TODO: diskimage_remove()? This would be useful for floppies in PC-style |
33 |
* machines, where disks may need to be swapped during boot etc. |
* machines, where disks may need to be swapped during boot etc. |
34 |
*/ |
*/ |
52 |
|
|
53 |
static char *diskimage_types[] = DISKIMAGE_TYPES; |
static char *diskimage_types[] = DISKIMAGE_TYPES; |
54 |
|
|
|
static struct scsi_transfer *first_free_scsi_transfer_alloc = NULL; |
|
|
|
|
55 |
|
|
56 |
/**************************************************************************/ |
/**************************************************************************/ |
57 |
|
|
100 |
|
|
101 |
|
|
102 |
/* |
/* |
103 |
* scsi_transfer_alloc(): |
* diskimage_exist(): |
104 |
* |
* |
105 |
* Allocates memory for a new scsi_transfer struct, and fills it with |
* Returns 1 if the specified disk id (for a specific type) exists, 0 |
106 |
* sane data (NULL pointers). |
* otherwise. |
|
* The return value is a pointer to the new struct. If allocation |
|
|
* failed, the program exits. |
|
107 |
*/ |
*/ |
108 |
struct scsi_transfer *scsi_transfer_alloc(void) |
int diskimage_exist(struct machine *machine, int id, int type) |
109 |
{ |
{ |
110 |
struct scsi_transfer *p; |
struct diskimage *d = machine->first_diskimage; |
111 |
|
|
112 |
if (first_free_scsi_transfer_alloc != NULL) { |
while (d != NULL) { |
113 |
p = first_free_scsi_transfer_alloc; |
if (d->type == type && d->id == id) |
114 |
first_free_scsi_transfer_alloc = p->next_free; |
return 1; |
115 |
} else { |
d = d->next; |
|
p = malloc(sizeof(struct scsi_transfer)); |
|
|
if (p == NULL) { |
|
|
fprintf(stderr, "scsi_transfer_alloc(): out " |
|
|
"of memory\n"); |
|
|
exit(1); |
|
|
} |
|
116 |
} |
} |
117 |
|
return 0; |
|
memset(p, 0, sizeof(struct scsi_transfer)); |
|
|
|
|
|
return p; |
|
118 |
} |
} |
119 |
|
|
120 |
|
|
121 |
/* |
/* |
122 |
* scsi_transfer_free(): |
* diskimage_add_overlay(): |
123 |
* |
* |
124 |
* Frees the space used by a scsi_transfer struct. All buffers refered |
* Opens an overlay data file and its corresponding bitmap file, and adds |
125 |
* to by the scsi_transfer struct are freed. |
* the overlay to a disk image. |
126 |
*/ |
*/ |
127 |
void scsi_transfer_free(struct scsi_transfer *p) |
void diskimage_add_overlay(struct diskimage *d, char *overlay_basename) |
128 |
{ |
{ |
129 |
if (p == NULL) { |
struct diskimage_overlay overlay; |
130 |
fprintf(stderr, "scsi_transfer_free(): p == NULL\n"); |
size_t bitmap_name_len = strlen(overlay_basename) + 20; |
131 |
|
char *bitmap_name = malloc(bitmap_name_len); |
132 |
|
|
133 |
|
if (bitmap_name == NULL) { |
134 |
|
fprintf(stderr, "out of memory\n"); |
135 |
exit(1); |
exit(1); |
136 |
} |
} |
137 |
|
snprintf(bitmap_name, bitmap_name_len, "%s.map", overlay_basename); |
138 |
|
|
139 |
if (p->msg_out != NULL) |
overlay.overlay_basename = strdup(overlay_basename); |
140 |
free(p->msg_out); |
overlay.f_data = fopen(overlay_basename, d->writable? "r+" : "r"); |
141 |
if (p->cmd != NULL) |
if (overlay.f_data == NULL) { |
142 |
free(p->cmd); |
perror(overlay_basename); |
143 |
if (p->data_out != NULL) |
exit(1); |
|
free(p->data_out); |
|
|
|
|
|
if (p->data_in != NULL) |
|
|
free(p->data_in); |
|
|
if (p->msg_in != NULL) |
|
|
free(p->msg_in); |
|
|
if (p->status != NULL) |
|
|
free(p->status); |
|
|
|
|
|
p->next_free = first_free_scsi_transfer_alloc; |
|
|
first_free_scsi_transfer_alloc = p; |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
* scsi_transfer_allocbuf(): |
|
|
* |
|
|
* Helper function, used by diskimage_scsicommand(), and SCSI controller |
|
|
* devices. Example of usage: |
|
|
* |
|
|
* scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1); |
|
|
*/ |
|
|
void scsi_transfer_allocbuf(size_t *lenp, unsigned char **pp, size_t want_len, |
|
|
int clearflag) |
|
|
{ |
|
|
unsigned char *p = (*pp); |
|
|
|
|
|
if (p != NULL) { |
|
|
printf("WARNING! scsi_transfer_allocbuf(): old pointer " |
|
|
"was not NULL, freeing it now\n"); |
|
|
free(p); |
|
144 |
} |
} |
145 |
|
|
146 |
(*lenp) = want_len; |
overlay.f_bitmap = fopen(bitmap_name, d->writable? "r+" : "r"); |
147 |
if ((p = malloc(want_len)) == NULL) { |
if (overlay.f_bitmap == NULL) { |
148 |
fprintf(stderr, "scsi_transfer_allocbuf(): out of " |
perror(bitmap_name); |
149 |
"memory trying to allocate %li bytes\n", (long)want_len); |
fprintf(stderr, "Please create the map file first.\n"); |
150 |
exit(1); |
exit(1); |
151 |
} |
} |
152 |
|
|
153 |
if (clearflag) |
d->nr_of_overlays ++; |
154 |
memset(p, 0, want_len); |
d->overlays = realloc(d->overlays, sizeof(struct diskimage_overlay) |
155 |
|
* d->nr_of_overlays); |
156 |
(*pp) = p; |
if (d->overlays == NULL) { |
157 |
} |
fprintf(stderr, "out of memory\n"); |
158 |
|
exit(1); |
159 |
|
} |
|
/**************************************************************************/ |
|
160 |
|
|
161 |
|
d->overlays[d->nr_of_overlays - 1] = overlay; |
162 |
|
|
163 |
/* |
free(bitmap_name); |
|
* diskimage_exist(): |
|
|
* |
|
|
* Returns 1 if the specified disk id (for a specific type) exists, 0 |
|
|
* otherwise. |
|
|
*/ |
|
|
int diskimage_exist(struct machine *machine, int id, int type) |
|
|
{ |
|
|
struct diskimage *d = machine->first_diskimage; |
|
|
|
|
|
while (d != NULL) { |
|
|
if (d->type == type && d->id == id) |
|
|
return 1; |
|
|
d = d->next; |
|
|
} |
|
|
return 0; |
|
164 |
} |
} |
165 |
|
|
166 |
|
|
170 |
* Recalculate a disk's size by stat()-ing it. |
* Recalculate a disk's size by stat()-ing it. |
171 |
* d is assumed to be non-NULL. |
* d is assumed to be non-NULL. |
172 |
*/ |
*/ |
173 |
static void diskimage_recalc_size(struct diskimage *d) |
void diskimage_recalc_size(struct diskimage *d) |
174 |
{ |
{ |
175 |
struct stat st; |
struct stat st; |
176 |
int res; |
int res; |
266 |
|
|
267 |
|
|
268 |
/* |
/* |
|
* diskimage__return_default_status_and_message(): |
|
|
* |
|
|
* Set the status and msg_in parts of a scsi_transfer struct |
|
|
* to default values (msg_in = 0x00, status = 0x00). |
|
|
*/ |
|
|
static void diskimage__return_default_status_and_message( |
|
|
struct scsi_transfer *xferp) |
|
|
{ |
|
|
scsi_transfer_allocbuf(&xferp->status_len, &xferp->status, 1, 0); |
|
|
xferp->status[0] = 0x00; |
|
|
scsi_transfer_allocbuf(&xferp->msg_in_len, &xferp->msg_in, 1, 0); |
|
|
xferp->msg_in[0] = 0x00; |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
|
* diskimage__switch_tape(): |
|
|
* |
|
|
* Used by the SPACE command. (d is assumed to be non-NULL.) |
|
|
*/ |
|
|
static void diskimage__switch_tape(struct diskimage *d) |
|
|
{ |
|
|
char tmpfname[1000]; |
|
|
|
|
|
snprintf(tmpfname, sizeof(tmpfname), "%s.%i", |
|
|
d->fname, d->tape_filenr); |
|
|
tmpfname[sizeof(tmpfname)-1] = '\0'; |
|
|
|
|
|
if (d->f != NULL) |
|
|
fclose(d->f); |
|
|
|
|
|
d->f = fopen(tmpfname, d->writable? "r+" : "r"); |
|
|
if (d->f == NULL) { |
|
|
fprintf(stderr, "[ diskimage__switch_tape(): could not " |
|
|
"(re)open '%s' ]\n", tmpfname); |
|
|
/* TODO: return error */ |
|
|
} |
|
|
d->tape_offset = 0; |
|
|
} |
|
|
|
|
|
|
|
|
/* |
|
269 |
* diskimage_access__cdrom(): |
* diskimage_access__cdrom(): |
270 |
* |
* |
271 |
* This is a special-case function, called from diskimage__internal_access(). |
* This is a special-case function, called from diskimage__internal_access(). |
316 |
} |
} |
317 |
|
|
318 |
|
|
319 |
/* |
/* Helper function. */ |
320 |
* diskimage__internal_access(): |
static void overlay_set_block_in_use(struct diskimage *d, |
321 |
* |
int overlay_nr, off_t ofs) |
|
* Read from or write to a struct diskimage. |
|
|
* |
|
|
* Returns 1 if the access completed successfully, 0 otherwise. |
|
|
*/ |
|
|
static int diskimage__internal_access(struct diskimage *d, int writeflag, |
|
|
off_t offset, unsigned char *buf, size_t len) |
|
322 |
{ |
{ |
323 |
ssize_t lendone; |
off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE; |
324 |
|
off_t bitmap_file_offset = bit_nr / 8; |
325 |
int res; |
int res; |
326 |
|
unsigned char data; |
327 |
|
|
328 |
if (buf == NULL) { |
res = my_fseek(d->overlays[overlay_nr].f_bitmap, |
329 |
fprintf(stderr, "diskimage__internal_access(): buf = NULL\n"); |
bitmap_file_offset, SEEK_SET); |
330 |
|
if (res) { |
331 |
|
perror("my_fseek"); |
332 |
|
fprintf(stderr, "Could not seek in bitmap file?" |
333 |
|
" offset = %lli, read\n", (long long)bitmap_file_offset); |
334 |
exit(1); |
exit(1); |
335 |
} |
} |
|
if (len == 0) |
|
|
return 1; |
|
|
if (d->f == NULL) |
|
|
return 0; |
|
|
|
|
|
res = my_fseek(d->f, offset, SEEK_SET); |
|
|
if (res != 0) { |
|
|
fatal("[ diskimage__internal_access(): fseek() failed on " |
|
|
"disk id %i \n", d->id); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (writeflag) { |
|
|
if (!d->writable) |
|
|
return 0; |
|
|
|
|
|
lendone = fwrite(buf, 1, len, d->f); |
|
|
} else { |
|
|
/* |
|
|
* Special case for CD-ROMs. Actually, this is not needed |
|
|
* for .iso images, only for physical CDROMS on some OSes, |
|
|
* such as FreeBSD. |
|
|
*/ |
|
|
if (d->is_a_cdrom) |
|
|
lendone = diskimage_access__cdrom(d, offset, buf, len); |
|
|
else |
|
|
lendone = fread(buf, 1, len, d->f); |
|
336 |
|
|
337 |
if (lendone < (ssize_t)len) |
/* Read the original bitmap data, and OR in the new bit: */ |
338 |
memset(buf + lendone, 0, len - lendone); |
res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap); |
339 |
|
if (res != 1) |
340 |
|
data = 0x00; |
341 |
|
|
342 |
|
data |= (1 << (bit_nr & 7)); |
343 |
|
|
344 |
|
/* Write it back: */ |
345 |
|
res = my_fseek(d->overlays[overlay_nr].f_bitmap, |
346 |
|
bitmap_file_offset, SEEK_SET); |
347 |
|
if (res) { |
348 |
|
perror("my_fseek"); |
349 |
|
fprintf(stderr, "Could not seek in bitmap file?" |
350 |
|
" offset = %lli, write\n", (long long)bitmap_file_offset); |
351 |
|
exit(1); |
352 |
} |
} |
353 |
|
res = fwrite(&data, 1, 1, d->overlays[overlay_nr].f_bitmap); |
354 |
/* Warn about non-complete data transfers: */ |
if (res != 1) { |
355 |
if (lendone != (ssize_t)len) { |
fprintf(stderr, "Could not write to bitmap file. Aborting.\n"); |
356 |
#ifdef UNSTABLE_DEVEL |
exit(1); |
|
fatal("[ diskimage__internal_access(): disk_id %i, offset %lli" |
|
|
", transfer not completed. len=%i, len_done=%i ]\n", |
|
|
d->id, (long long)offset, (int)len, (int)lendone); |
|
|
#endif |
|
|
return 0; |
|
357 |
} |
} |
|
|
|
|
return 1; |
|
358 |
} |
} |
359 |
|
|
360 |
|
|
361 |
/* |
/* Helper function. */ |
362 |
* diskimage_scsicommand(): |
static int overlay_has_block(struct diskimage *d, int overlay_nr, off_t ofs) |
|
* |
|
|
* Perform a SCSI command on a disk image. |
|
|
* |
|
|
* The xferp points to a scsi_transfer struct, containing msg_out, command, |
|
|
* and data_out coming from the SCSI controller device. This function |
|
|
* interprets the command, and (if necessary) creates responses in |
|
|
* data_in, msg_in, and status. |
|
|
* |
|
|
* Returns: |
|
|
* 2 if the command expects data from the DATA_OUT phase, |
|
|
* 1 if otherwise ok, |
|
|
* 0 on error. |
|
|
*/ |
|
|
int diskimage_scsicommand(struct cpu *cpu, int id, int type, |
|
|
struct scsi_transfer *xferp) |
|
363 |
{ |
{ |
364 |
char namebuf[16]; |
off_t bit_nr = ofs / OVERLAY_BLOCK_SIZE; |
365 |
int retlen, i, q; |
off_t bitmap_file_offset = bit_nr / 8; |
366 |
uint64_t size; |
int res; |
367 |
int64_t ofs; |
unsigned char data; |
|
int pagecode; |
|
|
struct machine *machine = cpu->machine; |
|
|
struct diskimage *d; |
|
|
|
|
|
if (machine == NULL) { |
|
|
fatal("[ diskimage_scsicommand(): machine == NULL ]\n"); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
d = machine->first_diskimage; |
|
|
while (d != NULL) { |
|
|
if (d->type == type && d->id == id) |
|
|
break; |
|
|
d = d->next; |
|
|
} |
|
|
if (d == NULL) { |
|
|
fprintf(stderr, "[ diskimage_scsicommand(): %s " |
|
|
" id %i not connected? ]\n", diskimage_types[type], id); |
|
|
} |
|
368 |
|
|
369 |
if (xferp->cmd == NULL) { |
res = my_fseek(d->overlays[overlay_nr].f_bitmap, |
370 |
fatal("[ diskimage_scsicommand(): cmd == NULL ]\n"); |
bitmap_file_offset, SEEK_SET); |
371 |
|
if (res != 0) |
372 |
return 0; |
return 0; |
|
} |
|
373 |
|
|
374 |
if (xferp->cmd_len < 1) { |
/* The seek succeeded, now read the bit: */ |
375 |
fatal("[ diskimage_scsicommand(): cmd_len == %i ]\n", |
res = fread(&data, 1, 1, d->overlays[overlay_nr].f_bitmap); |
376 |
xferp->cmd_len); |
if (res != 1) |
377 |
return 0; |
return 0; |
|
} |
|
|
|
|
|
debug("[ diskimage_scsicommand(id=%i) cmd=0x%02x: ", |
|
|
id, xferp->cmd[0]); |
|
378 |
|
|
379 |
#if 0 |
if (data & (1 << (bit_nr & 7))) |
380 |
fatal("[ diskimage_scsicommand(id=%i) cmd=0x%02x len=%i:", |
return 1; |
|
id, xferp->cmd[0], xferp->cmd_len); |
|
|
for (i=0; i<xferp->cmd_len; i++) |
|
|
fatal(" %02x", xferp->cmd[i]); |
|
|
fatal("\n"); |
|
|
if (xferp->cmd_len > 7 && xferp->cmd[5] == 0x11) |
|
|
single_step = ENTER_SINGLE_STEPPING; |
|
|
#endif |
|
381 |
|
|
382 |
#if 0 |
return 0; |
|
{ |
|
|
static FILE *f = NULL; |
|
|
if (f == NULL) |
|
|
f = fopen("scsi_log.txt", "w"); |
|
|
if (f != NULL) { |
|
|
int i; |
|
|
fprintf(f, "id=%i cmd =", id); |
|
|
for (i=0; i<xferp->cmd_len; i++) |
|
|
fprintf(f, " %02x", xferp->cmd[i]); |
|
|
fprintf(f, "\n"); |
|
|
fflush(f); |
|
|
} |
|
383 |
} |
} |
|
#endif |
|
|
|
|
|
switch (xferp->cmd[0]) { |
|
|
|
|
|
case SCSICMD_TEST_UNIT_READY: |
|
|
debug("TEST_UNIT_READY"); |
|
|
if (xferp->cmd_len != 6) |
|
|
debug(" (weird len=%i)", xferp->cmd_len); |
|
|
|
|
|
/* TODO: bits 765 of buf[1] contains the LUN */ |
|
|
if (xferp->cmd[1] != 0x00) |
|
|
fatal("WARNING: TEST_UNIT_READY with cmd[1]=0x%02x" |
|
|
" not yet implemented\n", (int)xferp->cmd[1]); |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
384 |
|
|
|
case SCSICMD_INQUIRY: |
|
|
debug("INQUIRY"); |
|
|
if (xferp->cmd_len != 6) |
|
|
debug(" (weird len=%i)", xferp->cmd_len); |
|
|
if (xferp->cmd[1] != 0x00) { |
|
|
debug("WARNING: INQUIRY with cmd[1]=0x%02x not yet " |
|
|
"implemented\n", (int)xferp->cmd[1]); |
|
385 |
|
|
386 |
break; |
/* |
387 |
} |
* fwrite_helper(): |
388 |
|
* |
389 |
/* Return values: */ |
* Internal helper function. Writes to a disk image file, or if the |
390 |
retlen = xferp->cmd[4]; |
* disk image has overlays, to the last overlay. |
391 |
if (retlen < 36) { |
*/ |
392 |
fatal("WARNING: SCSI inquiry len=%i, <36!\n", retlen); |
static size_t fwrite_helper(off_t offset, unsigned char *buf, |
393 |
retlen = 36; |
size_t len, struct diskimage *d) |
394 |
|
{ |
395 |
|
off_t curofs; |
396 |
|
|
397 |
|
/* Fast return-path for the case when no overlays are used: */ |
398 |
|
if (d->nr_of_overlays == 0) { |
399 |
|
int res = my_fseek(d->f, offset, SEEK_SET); |
400 |
|
if (res != 0) { |
401 |
|
fatal("[ diskimage__internal_access(): fseek() failed" |
402 |
|
" on disk id %i \n", d->id); |
403 |
|
return 0; |
404 |
} |
} |
405 |
|
|
406 |
/* Return data: */ |
return fwrite(buf, 1, len, d->f); |
407 |
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
} |
|
retlen, 1); |
|
|
xferp->data_in[0] = 0x00; /* 0x00 = Direct-access disk */ |
|
|
xferp->data_in[1] = 0x00; /* 0x00 = non-removable */ |
|
|
xferp->data_in[2] = 0x02; /* SCSI-2 */ |
|
|
#if 0 |
|
|
xferp->data_in[3] = 0x02; /* Response data format = SCSI-2 */ |
|
|
#endif |
|
|
xferp->data_in[4] = retlen - 4; /* Additional length */ |
|
|
xferp->data_in[4] = 0x2c - 4; /* Additional length */ |
|
|
xferp->data_in[6] = 0x04; /* ACKREQQ */ |
|
|
xferp->data_in[7] = 0x60; /* WBus32, WBus16 */ |
|
|
|
|
|
/* These are padded with spaces: */ |
|
|
|
|
|
memcpy(xferp->data_in+8, "GXemul ", 8); |
|
|
if (diskimage_getname(cpu->machine, id, |
|
|
type, namebuf, sizeof(namebuf))) { |
|
|
size_t i; |
|
|
for (i=0; i<sizeof(namebuf); i++) |
|
|
if (namebuf[i] == 0) { |
|
|
for (; i<sizeof(namebuf); i++) |
|
|
namebuf[i] = ' '; |
|
|
break; |
|
|
} |
|
|
memcpy(xferp->data_in+16, namebuf, 16); |
|
|
} else |
|
|
memcpy(xferp->data_in+16, "DISK ", 16); |
|
|
memcpy(xferp->data_in+32, "0 ", 4); |
|
|
|
|
|
/* |
|
|
* Some Ultrix kernels want specific responses from |
|
|
* the drives. |
|
|
*/ |
|
|
|
|
|
if (machine->machine_type == MACHINE_PMAX) { |
|
|
/* DEC, RZ25 (rev 0900) = 832527 sectors */ |
|
|
/* DEC, RZ58 (rev 2000) = 2698061 sectors */ |
|
|
memcpy(xferp->data_in+8, "DEC ", 8); |
|
|
memcpy(xferp->data_in+16, "RZ58 (C) DEC", 16); |
|
|
memcpy(xferp->data_in+32, "2000", 4); |
|
|
} |
|
|
|
|
|
/* Some data is different for CD-ROM drives: */ |
|
|
if (d->is_a_cdrom) { |
|
|
xferp->data_in[0] = 0x05; /* 0x05 = CD-ROM */ |
|
|
xferp->data_in[1] = 0x80; /* 0x80 = removable */ |
|
|
/* memcpy(xferp->data_in+16, "CD-ROM ", 16);*/ |
|
|
|
|
|
if (machine->machine_type == MACHINE_PMAX) { |
|
|
/* SONY, CD-ROM: */ |
|
|
memcpy(xferp->data_in+8, "SONY ", 8); |
|
|
memcpy(xferp->data_in+16, |
|
|
"CD-ROM ", 16); |
|
|
|
|
|
/* ... or perhaps this: */ |
|
|
memcpy(xferp->data_in+8, "DEC ", 8); |
|
|
memcpy(xferp->data_in+16, |
|
|
"RRD42 (C) DEC ", 16); |
|
|
memcpy(xferp->data_in+32, "4.5d", 4); |
|
|
} else if (machine->machine_type == MACHINE_ARC) { |
|
|
/* NEC, CD-ROM: */ |
|
|
memcpy(xferp->data_in+8, "NEC ", 8); |
|
|
memcpy(xferp->data_in+16, |
|
|
"CD-ROM CDR-210P ", 16); |
|
|
memcpy(xferp->data_in+32, "1.0 ", 4); |
|
|
} |
|
|
} |
|
408 |
|
|
409 |
/* Data for tape devices: */ |
if ((len & (OVERLAY_BLOCK_SIZE-1)) != 0) { |
410 |
if (d->is_a_tape) { |
fatal("TODO: overlay access (write), len not multiple of " |
411 |
xferp->data_in[0] = 0x01; /* 0x01 = tape */ |
"overlay block size. not yet implemented.\n"); |
412 |
xferp->data_in[1] = 0x80; /* 0x80 = removable */ |
fatal("len = %lli\n", (long long) len); |
413 |
memcpy(xferp->data_in+16, "TAPE ", 16); |
abort(); |
414 |
|
} |
415 |
if (machine->machine_type == MACHINE_PMAX) { |
if ((offset & (OVERLAY_BLOCK_SIZE-1)) != 0) { |
416 |
/* |
fatal("TODO: unaligned overlay access\n"); |
417 |
* TODO: find out if these are correct. |
fatal("offset = %lli\n", (long long) offset); |
418 |
* |
abort(); |
419 |
* The name might be TZK10, TSZ07, or TLZ04, |
} |
420 |
* or something completely different. |
|
421 |
*/ |
/* Split the write into OVERLAY_BLOCK_SIZE writes: */ |
422 |
memcpy(xferp->data_in+8, "DEC ", 8); |
for (curofs=offset; curofs<offset+len; curofs+=OVERLAY_BLOCK_SIZE) { |
423 |
memcpy(xferp->data_in+16, |
/* Always write to the last overlay: */ |
424 |
"TK50 (C) DEC", 16); |
int overlay_nr = d->nr_of_overlays-1; |
425 |
memcpy(xferp->data_in+32, "2000", 4); |
off_t lenwritten; |
426 |
} |
int res = my_fseek(d->overlays[overlay_nr].f_data, |
427 |
|
curofs, SEEK_SET); |
428 |
|
if (res != 0) { |
429 |
|
fatal("[ diskimage__internal_access(): fseek()" |
430 |
|
" failed on disk id %i \n", d->id); |
431 |
|
return 0; |
432 |
} |
} |
433 |
|
|
434 |
diskimage__return_default_status_and_message(xferp); |
lenwritten = fwrite(buf, 1, OVERLAY_BLOCK_SIZE, |
435 |
break; |
d->overlays[overlay_nr].f_data); |
436 |
|
buf += OVERLAY_BLOCK_SIZE; |
437 |
|
|
438 |
case SCSIBLOCKCMD_READ_CAPACITY: |
/* Mark this block in the last overlay as in use: */ |
439 |
debug("READ_CAPACITY"); |
overlay_set_block_in_use(d, overlay_nr, curofs); |
440 |
|
} |
441 |
|
|
442 |
if (xferp->cmd_len != 10) |
return len; |
443 |
fatal(" [ weird READ_CAPACITY len=%i, should be 10 ] ", |
} |
|
xferp->cmd_len); |
|
|
else { |
|
|
if (xferp->cmd[8] & 1) { |
|
|
/* Partial Medium Indicator bit... TODO */ |
|
|
fatal("WARNING: READ_CAPACITY with PMI bit" |
|
|
" set not yet implemented\n"); |
|
|
} |
|
|
} |
|
444 |
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
|
|
8, 1); |
|
|
|
|
|
diskimage_recalc_size(d); |
|
|
|
|
|
size = d->total_size / d->logical_block_size; |
|
|
if (d->total_size & (d->logical_block_size-1)) |
|
|
size ++; |
|
|
|
|
|
xferp->data_in[0] = (size >> 24) & 255; |
|
|
xferp->data_in[1] = (size >> 16) & 255; |
|
|
xferp->data_in[2] = (size >> 8) & 255; |
|
|
xferp->data_in[3] = size & 255; |
|
|
|
|
|
xferp->data_in[4] = (d->logical_block_size >> 24) & 255; |
|
|
xferp->data_in[5] = (d->logical_block_size >> 16) & 255; |
|
|
xferp->data_in[6] = (d->logical_block_size >> 8) & 255; |
|
|
xferp->data_in[7] = d->logical_block_size & 255; |
|
445 |
|
|
446 |
diskimage__return_default_status_and_message(xferp); |
/* |
447 |
break; |
* fread_helper(): |
448 |
|
* |
449 |
|
* Internal helper function. Reads from a disk image file, or if the |
450 |
|
* disk image has overlays, from the last overlay that has the specific |
451 |
|
* data (or the disk image file itself). |
452 |
|
*/ |
453 |
|
static size_t fread_helper(off_t offset, unsigned char *buf, |
454 |
|
size_t len, struct diskimage *d) |
455 |
|
{ |
456 |
|
off_t curofs; |
457 |
|
size_t totallenread = 0; |
458 |
|
|
459 |
case SCSICMD_MODE_SENSE: |
/* Fast return-path for the case when no overlays are used: */ |
460 |
case SCSICMD_MODE_SENSE10: |
if (d->nr_of_overlays == 0) { |
461 |
debug("MODE_SENSE"); |
int res = my_fseek(d->f, offset, SEEK_SET); |
462 |
q = 4; retlen = xferp->cmd[4]; |
if (res != 0) { |
463 |
switch (xferp->cmd_len) { |
fatal("[ diskimage__internal_access(): fseek() failed" |
464 |
case 6: break; |
" on disk id %i \n", d->id); |
465 |
case 10:q = 8; |
return 0; |
|
retlen = xferp->cmd[7] * 256 + xferp->cmd[8]; |
|
|
break; |
|
|
default:fatal(" (unimplemented mode_sense len=%i)", |
|
|
xferp->cmd_len); |
|
466 |
} |
} |
467 |
|
|
468 |
/* |
return fread(buf, 1, len, d->f); |
469 |
* NOTE/TODO: This code doesn't handle too short retlens |
} |
|
* very well. A quick hack around this is that I allocate |
|
|
* a bit too much memory, so that nothing is actually |
|
|
* written outside of xferp->data_in[]. |
|
|
*/ |
|
|
|
|
|
retlen += 100; /* Should be enough. (Ugly.) */ |
|
|
|
|
|
if ((xferp->cmd[2] & 0xc0) != 0) |
|
|
fatal("WARNING: mode sense, cmd[2] = 0x%02x\n", |
|
|
xferp->cmd[2]); |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, |
|
|
&xferp->data_in, retlen, 1); |
|
|
|
|
|
xferp->data_in_len -= 100; /* Restore size. */ |
|
|
|
|
|
pagecode = xferp->cmd[2] & 0x3f; |
|
|
|
|
|
debug("[ MODE SENSE id %i, pagecode=%i ]\n", id, pagecode); |
|
|
|
|
|
/* 4 bytes of header for 6-byte command, |
|
|
8 bytes of header for 10-byte command. */ |
|
|
xferp->data_in[0] = retlen; /* 0: mode data length */ |
|
|
xferp->data_in[1] = d->is_a_cdrom? 0x05 : 0x00; |
|
|
/* 1: medium type */ |
|
|
xferp->data_in[2] = 0x00; /* device specific |
|
|
parameter */ |
|
|
xferp->data_in[3] = 8 * 1; /* block descriptor |
|
|
length: 1 page (?) */ |
|
|
|
|
|
xferp->data_in[q+0] = 0x00; /* density code */ |
|
|
xferp->data_in[q+1] = 0; /* nr of blocks, high */ |
|
|
xferp->data_in[q+2] = 0; /* nr of blocks, mid */ |
|
|
xferp->data_in[q+3] = 0; /* nr of blocks, low */ |
|
|
xferp->data_in[q+4] = 0x00; /* reserved */ |
|
|
xferp->data_in[q+5] = (d->logical_block_size >> 16) & 255; |
|
|
xferp->data_in[q+6] = (d->logical_block_size >> 8) & 255; |
|
|
xferp->data_in[q+7] = d->logical_block_size & 255; |
|
|
q += 8; |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
|
|
|
/* descriptors, 8 bytes (each) */ |
|
|
|
|
|
/* page, n bytes (each) */ |
|
|
switch (pagecode) { |
|
|
case 0: |
|
|
/* TODO: Nothing here? */ |
|
|
break; |
|
|
case 1: /* read-write error recovery page */ |
|
|
xferp->data_in[q + 0] = pagecode; |
|
|
xferp->data_in[q + 1] = 10; |
|
|
break; |
|
|
case 3: /* format device page */ |
|
|
xferp->data_in[q + 0] = pagecode; |
|
|
xferp->data_in[q + 1] = 22; |
|
|
|
|
|
/* 10,11 = sectors per track */ |
|
|
xferp->data_in[q + 10] = 0; |
|
|
xferp->data_in[q + 11] = d->sectors_per_track; |
|
|
|
|
|
/* 12,13 = physical sector size */ |
|
|
xferp->data_in[q + 12] = |
|
|
(d->logical_block_size >> 8) & 255; |
|
|
xferp->data_in[q + 13] = d->logical_block_size & 255; |
|
|
break; |
|
|
case 4: /* rigid disk geometry page */ |
|
|
xferp->data_in[q + 0] = pagecode; |
|
|
xferp->data_in[q + 1] = 22; |
|
|
xferp->data_in[q + 2] = (d->ncyls >> 16) & 255; |
|
|
xferp->data_in[q + 3] = (d->ncyls >> 8) & 255; |
|
|
xferp->data_in[q + 4] = d->ncyls & 255; |
|
|
xferp->data_in[q + 5] = d->heads; |
|
|
|
|
|
xferp->data_in[q + 20] = (d->rpms >> 8) & 255; |
|
|
xferp->data_in[q + 21] = d->rpms & 255; |
|
|
break; |
|
|
case 5: /* flexible disk page */ |
|
|
xferp->data_in[q + 0] = pagecode; |
|
|
xferp->data_in[q + 1] = 0x1e; |
|
|
|
|
|
/* 2,3 = transfer rate */ |
|
|
xferp->data_in[q + 2] = ((5000) >> 8) & 255; |
|
|
xferp->data_in[q + 3] = (5000) & 255; |
|
|
|
|
|
xferp->data_in[q + 4] = d->heads; |
|
|
xferp->data_in[q + 5] = d->sectors_per_track; |
|
|
|
|
|
/* 6,7 = data bytes per sector */ |
|
|
xferp->data_in[q + 6] = (d->logical_block_size >> 8) |
|
|
& 255; |
|
|
xferp->data_in[q + 7] = d->logical_block_size & 255; |
|
|
|
|
|
xferp->data_in[q + 8] = (d->ncyls >> 8) & 255; |
|
|
xferp->data_in[q + 9] = d->ncyls & 255; |
|
470 |
|
|
471 |
xferp->data_in[q + 28] = (d->rpms >> 8) & 255; |
/* Split the read into OVERLAY_BLOCK_SIZE reads: */ |
472 |
xferp->data_in[q + 29] = d->rpms & 255; |
for (curofs=offset; len != 0; |
473 |
break; |
curofs = (curofs | (OVERLAY_BLOCK_SIZE-1)) + 1) { |
474 |
default: |
/* Find the overlay, if any, that has this block: */ |
475 |
fatal("[ MODE_SENSE for page %i is not yet " |
off_t lenread, lentoread; |
476 |
"implemented! ]\n", pagecode); |
int overlay_nr; |
477 |
|
for (overlay_nr = d->nr_of_overlays-1; |
478 |
|
overlay_nr >= 0; overlay_nr --) { |
479 |
|
if (overlay_has_block(d, overlay_nr, curofs)) |
480 |
|
break; |
481 |
} |
} |
482 |
|
|
483 |
break; |
lentoread = len > OVERLAY_BLOCK_SIZE? OVERLAY_BLOCK_SIZE : len; |
|
|
|
|
case SCSICMD_READ: |
|
|
case SCSICMD_READ_10: |
|
|
debug("READ"); |
|
|
|
|
|
/* |
|
|
* For tape devices, read data at the current position. |
|
|
* For disk and CDROM devices, the command bytes contain |
|
|
* an offset telling us where to read from the device. |
|
|
*/ |
|
|
|
|
|
if (d->is_a_tape) { |
|
|
/* bits 7..5 of cmd[1] are the LUN bits... TODO */ |
|
484 |
|
|
485 |
size = (xferp->cmd[2] << 16) + |
if (overlay_nr >= 0) { |
486 |
(xferp->cmd[3] << 8) + |
/* Read from overlay: */ |
487 |
xferp->cmd[4]; |
int res = my_fseek(d->overlays[overlay_nr].f_data, |
488 |
|
curofs, SEEK_SET); |
489 |
/* Bit 1 of cmd[1] is the SILI bit (TODO), and |
if (res != 0) { |
490 |
bit 0 is the "use fixed length" bit. */ |
fatal("[ diskimage__internal_access(): fseek()" |
491 |
|
" failed on disk id %i \n", d->id); |
492 |
if (xferp->cmd[1] & 0x01) { |
return 0; |
|
/* Fixed block length: */ |
|
|
size *= d->logical_block_size; |
|
493 |
} |
} |
494 |
|
lenread = fread(buf, 1, lentoread, |
495 |
if (d->filemark) { |
d->overlays[overlay_nr].f_data); |
|
/* At end of file, switch to the next |
|
|
automagically: */ |
|
|
d->tape_filenr ++; |
|
|
diskimage__switch_tape(d); |
|
|
|
|
|
d->filemark = 0; |
|
|
} |
|
|
|
|
|
ofs = d->tape_offset; |
|
|
|
|
|
fatal("[ READ tape, id=%i file=%i, cmd[1]=%02x size=%i" |
|
|
", ofs=%lli ]\n", id, d->tape_filenr, |
|
|
xferp->cmd[1], (int)size, (long long)ofs); |
|
496 |
} else { |
} else { |
497 |
if (xferp->cmd[0] == SCSICMD_READ) { |
/* Read from the base disk image: */ |
498 |
if (xferp->cmd_len != 6) |
int res = my_fseek(d->f, curofs, SEEK_SET); |
499 |
debug(" (weird len=%i)", |
if (res != 0) { |
500 |
xferp->cmd_len); |
fatal("[ diskimage__internal_access(): fseek()" |
501 |
|
" failed on disk id %i \n", d->id); |
502 |
/* |
return 0; |
|
* bits 4..0 of cmd[1], and cmd[2] and cmd[3] |
|
|
* hold the logical block address. |
|
|
* |
|
|
* cmd[4] holds the number of logical blocks |
|
|
* to transfer. (Special case if the value is |
|
|
* 0, actually means 256.) |
|
|
*/ |
|
|
ofs = ((xferp->cmd[1] & 0x1f) << 16) + |
|
|
(xferp->cmd[2] << 8) + xferp->cmd[3]; |
|
|
retlen = xferp->cmd[4]; |
|
|
if (retlen == 0) |
|
|
retlen = 256; |
|
|
} else { |
|
|
if (xferp->cmd_len != 10) |
|
|
debug(" (weird len=%i)", |
|
|
xferp->cmd_len); |
|
|
|
|
|
/* |
|
|
* cmd[2..5] hold the logical block address. |
|
|
* cmd[7..8] holds the number of logical |
|
|
* blocks to transfer. (NOTE: If the value is |
|
|
* 0, this means 0, not 65536. :-) |
|
|
*/ |
|
|
ofs = ((uint64_t)xferp->cmd[2] << 24) + |
|
|
(xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) |
|
|
+ xferp->cmd[5]; |
|
|
retlen = (xferp->cmd[7] << 8) + xferp->cmd[8]; |
|
503 |
} |
} |
504 |
|
lenread = fread(buf, 1, lentoread, d->f); |
|
size = retlen * d->logical_block_size; |
|
|
ofs *= d->logical_block_size; |
|
|
} |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
|
|
size, 0); |
|
|
|
|
|
debug(" READ ofs=%lli size=%i\n", (long long)ofs, (int)size); |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
|
|
|
d->filemark = 0; |
|
|
|
|
|
/* |
|
|
* Failure? Then set check condition. |
|
|
* For tapes, error should only occur at the end of a file. |
|
|
* |
|
|
* "If the logical unit encounters a filemark during |
|
|
* a READ command, CHECK CONDITION status shall be |
|
|
* returned and the filemark and valid bits shall be |
|
|
* set to one in the sense data. The sense key shall |
|
|
* be set to NO SENSE".. |
|
|
*/ |
|
|
if (d->is_a_tape && d->f != NULL && feof(d->f)) { |
|
|
debug(" feof id=%i\n", id); |
|
|
xferp->status[0] = 0x02; /* CHECK CONDITION */ |
|
|
|
|
|
d->filemark = 1; |
|
|
} else |
|
|
diskimage__internal_access(d, 0, ofs, |
|
|
xferp->data_in, size); |
|
|
|
|
|
if (d->is_a_tape && d->f != NULL) |
|
|
d->tape_offset = ftello(d->f); |
|
|
|
|
|
/* TODO: other errors? */ |
|
|
break; |
|
|
|
|
|
case SCSICMD_WRITE: |
|
|
case SCSICMD_WRITE_10: |
|
|
debug("WRITE"); |
|
|
|
|
|
/* TODO: tape */ |
|
|
|
|
|
if (xferp->cmd[0] == SCSICMD_WRITE) { |
|
|
if (xferp->cmd_len != 6) |
|
|
debug(" (weird len=%i)", xferp->cmd_len); |
|
|
|
|
|
/* |
|
|
* bits 4..0 of cmd[1], and cmd[2] and cmd[3] hold the |
|
|
* logical block address. |
|
|
* |
|
|
* cmd[4] holds the number of logical blocks to |
|
|
* transfer. (Special case if the value is 0, actually |
|
|
* means 256.) |
|
|
*/ |
|
|
ofs = ((xferp->cmd[1] & 0x1f) << 16) + |
|
|
(xferp->cmd[2] << 8) + xferp->cmd[3]; |
|
|
retlen = xferp->cmd[4]; |
|
|
if (retlen == 0) |
|
|
retlen = 256; |
|
|
} else { |
|
|
if (xferp->cmd_len != 10) |
|
|
debug(" (weird len=%i)", xferp->cmd_len); |
|
|
|
|
|
/* |
|
|
* cmd[2..5] hold the logical block address. |
|
|
* cmd[7..8] holds the number of logical blocks to |
|
|
* transfer. (NOTE: If the value is 0 this means 0, |
|
|
* not 65536.) |
|
|
*/ |
|
|
ofs = ((uint64_t)xferp->cmd[2] << 24) + |
|
|
(xferp->cmd[3] << 16) + (xferp->cmd[4] << 8) + |
|
|
xferp->cmd[5]; |
|
|
retlen = (xferp->cmd[7] << 8) + xferp->cmd[8]; |
|
|
} |
|
|
|
|
|
size = retlen * d->logical_block_size; |
|
|
ofs *= d->logical_block_size; |
|
|
|
|
|
if (xferp->data_out_offset != size) { |
|
|
debug(", data_out == NULL, wanting %i bytes, \n\n", |
|
|
(int)size); |
|
|
xferp->data_out_len = size; |
|
|
return 2; |
|
|
} |
|
|
|
|
|
debug(", data_out != NULL, OK :-)"); |
|
|
|
|
|
debug("WRITE ofs=%i size=%i offset=%i\n", (int)ofs, |
|
|
(int)size, (int)xferp->data_out_offset); |
|
|
|
|
|
diskimage__internal_access(d, 1, ofs, |
|
|
xferp->data_out, size); |
|
|
|
|
|
/* TODO: how about return code? */ |
|
|
|
|
|
/* Is this really necessary? */ |
|
|
/* fsync(fileno(d->f)); */ |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICMD_SYNCHRONIZE_CACHE: |
|
|
debug("SYNCHRONIZE_CACHE"); |
|
|
|
|
|
if (xferp->cmd_len != 10) |
|
|
debug(" (weird len=%i)", xferp->cmd_len); |
|
|
|
|
|
/* TODO: actualy care about cmd[] */ |
|
|
fsync(fileno(d->f)); |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICMD_START_STOP_UNIT: |
|
|
debug("START_STOP_UNIT"); |
|
|
|
|
|
if (xferp->cmd_len != 6) |
|
|
debug(" (weird len=%i)", xferp->cmd_len); |
|
|
|
|
|
for (i=0; i<(ssize_t)xferp->cmd_len; i++) |
|
|
debug(" %02x", xferp->cmd[i]); |
|
|
|
|
|
/* TODO: actualy care about cmd[] */ |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICMD_REQUEST_SENSE: |
|
|
debug("REQUEST_SENSE"); |
|
|
|
|
|
retlen = xferp->cmd[4]; |
|
|
|
|
|
/* TODO: bits 765 of buf[1] contains the LUN */ |
|
|
if (xferp->cmd[1] != 0x00) |
|
|
fatal("WARNING: REQUEST_SENSE with cmd[1]=0x%02x not" |
|
|
" yet implemented\n", (int)xferp->cmd[1]); |
|
|
|
|
|
if (retlen < 18) { |
|
|
fatal("WARNING: SCSI request sense len=%i, <18!\n", |
|
|
(int)retlen); |
|
|
retlen = 18; |
|
|
} |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
|
|
retlen, 1); |
|
|
|
|
|
xferp->data_in[0] = 0x80 + 0x70;/* 0x80 = valid, |
|
|
0x70 = "current errors" */ |
|
|
xferp->data_in[2] = 0x00; /* SENSE KEY! */ |
|
|
|
|
|
if (d->filemark) { |
|
|
xferp->data_in[2] = 0x80; |
|
|
} |
|
|
debug(": [2]=0x%02x ", xferp->data_in[2]); |
|
|
|
|
|
printf(" XXX(!) \n"); |
|
|
|
|
|
/* TODO */ |
|
|
xferp->data_in[7] = retlen - 7; /* additional sense length */ |
|
|
/* TODO */ |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICMD_READ_BLOCK_LIMITS: |
|
|
debug("READ_BLOCK_LIMITS"); |
|
|
|
|
|
retlen = 6; |
|
|
|
|
|
/* TODO: bits 765 of buf[1] contains the LUN */ |
|
|
if (xferp->cmd[1] != 0x00) |
|
|
fatal("WARNING: READ_BLOCK_LIMITS with cmd[1]=" |
|
|
"0x%02x not yet implemented\n", (int)xferp->cmd[1]); |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, &xferp->data_in, |
|
|
retlen, 1); |
|
|
|
|
|
/* |
|
|
* data[0] is reserved, data[1..3] contain the maximum block |
|
|
* length limit, data[4..5] contain the minimum limit. |
|
|
*/ |
|
|
|
|
|
{ |
|
|
int max_limit = 32768; |
|
|
int min_limit = 128; |
|
|
|
|
|
xferp->data_in[1] = (max_limit >> 16) & 255; |
|
|
xferp->data_in[2] = (max_limit >> 8) & 255; |
|
|
xferp->data_in[3] = max_limit & 255; |
|
|
xferp->data_in[4] = (min_limit >> 8) & 255; |
|
|
xferp->data_in[5] = min_limit & 255; |
|
505 |
} |
} |
506 |
|
|
507 |
diskimage__return_default_status_and_message(xferp); |
if (lenread != lentoread) { |
508 |
break; |
fatal("[ INCOMPLETE READ from disk id %i, offset" |
509 |
|
" %lli ]\n", d->id, (long long)curofs); |
|
case SCSICMD_REWIND: |
|
|
debug("REWIND"); |
|
|
|
|
|
/* TODO: bits 765 of buf[1] contains the LUN */ |
|
|
if ((xferp->cmd[1] & 0xe0) != 0x00) |
|
|
fatal("WARNING: REWIND with cmd[1]=0x%02x not yet " |
|
|
"implemented\n", (int)xferp->cmd[1]); |
|
|
|
|
|
/* Close and reopen. */ |
|
|
|
|
|
if (d->f != NULL) |
|
|
fclose(d->f); |
|
|
|
|
|
d->f = fopen(d->fname, d->writable? "r+" : "r"); |
|
|
if (d->f == NULL) { |
|
|
fprintf(stderr, "[ diskimage: could not (re)open " |
|
|
"'%s' ]\n", d->fname); |
|
|
/* TODO: return error */ |
|
510 |
} |
} |
511 |
|
|
512 |
d->tape_offset = 0; |
len -= lentoread; |
513 |
d->tape_filenr = 0; |
totallenread += lenread; |
514 |
d->filemark = 0; |
buf += OVERLAY_BLOCK_SIZE; |
515 |
|
} |
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICMD_SPACE: |
|
|
debug("SPACE"); |
|
|
|
|
|
/* TODO: bits 765 of buf[1] contains the LUN */ |
|
|
if ((xferp->cmd[1] & 0xe0) != 0x00) |
|
|
fatal("WARNING: SPACE with cmd[1]=0x%02x not yet " |
|
|
"implemented\n", (int)xferp->cmd[1]); |
|
|
|
|
|
/* |
|
|
* Bits 2..0 of buf[1] contain the 'code' which describes how |
|
|
* spacing should be done, and buf[2..4] contain the number of |
|
|
* operations. |
|
|
*/ |
|
|
debug("[ SPACE: buf[] = %02x %02x %02x %02x %02x %02x ]\n", |
|
|
xferp->cmd[0], |
|
|
xferp->cmd[1], |
|
|
xferp->cmd[2], |
|
|
xferp->cmd[3], |
|
|
xferp->cmd[4], |
|
|
xferp->cmd[5]); |
|
|
|
|
|
switch (xferp->cmd[1] & 7) { |
|
|
case 1: /* Seek to a different file nr: */ |
|
|
{ |
|
|
int diff = (xferp->cmd[2] << 16) + |
|
|
(xferp->cmd[3] << 8) + xferp->cmd[4]; |
|
|
|
|
|
/* Negative seek offset: */ |
|
|
if (diff & (1 << 23)) |
|
|
diff = - (16777216 - diff); |
|
|
|
|
|
d->tape_filenr += diff; |
|
|
} |
|
|
|
|
|
/* At end of file, switch to the next tape file: */ |
|
|
if (d->filemark) { |
|
|
d->tape_filenr ++; |
|
|
d->filemark = 0; |
|
|
} |
|
|
|
|
|
debug("{ switching to tape file %i }", d->tape_filenr); |
|
|
diskimage__switch_tape(d); |
|
|
d->filemark = 0; |
|
|
break; |
|
|
default: |
|
|
fatal("[ diskimage.c: unimplemented SPACE type %i ]\n", |
|
|
xferp->cmd[1] & 7); |
|
|
} |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICDROM_READ_SUBCHANNEL: |
|
|
/* |
|
|
* According to |
|
|
* http://mail-index.netbsd.org/port-i386/1997/03/03/0010.html: |
|
|
* |
|
|
* "The READ_CD_CAPACITY, READ_SUBCHANNEL, and MODE_SELECT |
|
|
* commands have the same opcode in SCSI or ATAPI, but don't |
|
|
* have the same command structure"... |
|
|
* |
|
|
* TODO: This still doesn't work. Hm. |
|
|
*/ |
|
|
retlen = 48; |
|
|
|
|
|
debug("CDROM_READ_SUBCHANNEL/READ_CD_CAPACITY, cmd[1]=0x%02x", |
|
|
xferp->cmd[1]); |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, |
|
|
&xferp->data_in, retlen, 1); |
|
|
|
|
|
diskimage_recalc_size(d); |
|
|
|
|
|
size = d->total_size / d->logical_block_size; |
|
|
if (d->total_size & (d->logical_block_size-1)) |
|
|
size ++; |
|
|
|
|
|
xferp->data_in[0] = (size >> 24) & 255; |
|
|
xferp->data_in[1] = (size >> 16) & 255; |
|
|
xferp->data_in[2] = (size >> 8) & 255; |
|
|
xferp->data_in[3] = size & 255; |
|
|
|
|
|
xferp->data_in[4] = (d->logical_block_size >> 24) & 255; |
|
|
xferp->data_in[5] = (d->logical_block_size >> 16) & 255; |
|
|
xferp->data_in[6] = (d->logical_block_size >> 8) & 255; |
|
|
xferp->data_in[7] = d->logical_block_size & 255; |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICDROM_READ_TOC: |
|
|
debug("(CDROM_READ_TOC: "); |
|
|
debug("lun=%i msf=%i ", |
|
|
xferp->cmd[1] >> 5, (xferp->cmd[1] >> 1) & 1); |
|
|
debug("starting_track=%i ", xferp->cmd[6]); |
|
|
retlen = xferp->cmd[7] * 256 + xferp->cmd[8]; |
|
|
debug("allocation_len=%i)\n", retlen); |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, |
|
|
&xferp->data_in, retlen, 1); |
|
|
|
|
|
xferp->data_in[0] = 0; |
|
|
xferp->data_in[1] = 10; |
|
|
xferp->data_in[2] = 0; /* First track. */ |
|
|
xferp->data_in[3] = 0; /* Last track. */ |
|
|
|
|
|
/* Track 0 data: */ |
|
|
xferp->data_in[4] = 0x00; /* Reserved. */ |
|
|
xferp->data_in[5] = 0x04; /* ADR + CTRL: |
|
|
Data, not audio */ |
|
|
xferp->data_in[6] = 0x00; /* Track nr */ |
|
|
xferp->data_in[7] = 0x00; /* Reserved */ |
|
|
/* 8..11 = absolute CDROM address */ |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICDROM_READ_DISCINFO: |
|
|
debug("(SCSICDROM_READ_DISCINFO: "); |
|
|
debug("TODO"); |
|
|
retlen = 0; |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, |
|
|
&xferp->data_in, retlen, 1); |
|
|
|
|
|
/* TODO */ |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICDROM_READ_TRACKINFO: |
|
|
debug("(SCSICDROM_READ_TRACKINFO: "); |
|
|
debug("TODO"); |
|
|
retlen = 0; |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, |
|
|
&xferp->data_in, retlen, 1); |
|
|
|
|
|
/* TODO */ |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
|
|
|
|
case SCSICMD_MODE_SELECT: |
|
|
debug("[ SCSI MODE_SELECT: "); |
|
|
|
|
|
/* |
|
|
* TODO: |
|
|
* |
|
|
* This is super-hardcoded for NetBSD's usage of mode_select |
|
|
* to set the size of CDROM sectors to 2048. |
|
|
*/ |
|
516 |
|
|
517 |
if (xferp->data_out_offset == 0) { |
return totallenread; |
518 |
xferp->data_out_len = 12; /* TODO */ |
} |
|
debug("data_out == NULL, wanting %i bytes ]\n", |
|
|
(int)xferp->data_out_len); |
|
|
return 2; |
|
|
} |
|
|
|
|
|
debug("data_out!=NULL (OK), "); |
|
|
|
|
|
/* TODO: Care about cmd? */ |
|
|
|
|
|
/* Set sector size to 2048: */ |
|
|
/* 00 05 00 08 00 03 ca 40 00 00 08 00 */ |
|
|
if (xferp->data_out[0] == 0x00 && |
|
|
xferp->data_out[1] == 0x05 && |
|
|
xferp->data_out[2] == 0x00 && |
|
|
xferp->data_out[3] == 0x08) { |
|
|
d->logical_block_size = |
|
|
(xferp->data_out[9] << 16) + |
|
|
(xferp->data_out[10] << 8) + |
|
|
xferp->data_out[11]; |
|
|
debug("[ setting logical_block_size to %i ]\n", |
|
|
d->logical_block_size); |
|
|
} else { |
|
|
int i; |
|
|
fatal("[ unknown MODE_SELECT: cmd ="); |
|
|
for (i=0; i<(ssize_t)xferp->cmd_len; i++) |
|
|
fatal(" %02x", xferp->cmd[i]); |
|
|
fatal(", data_out ="); |
|
|
for (i=0; i<(ssize_t)xferp->data_out_len; i++) |
|
|
fatal(" %02x", xferp->data_out[i]); |
|
|
fatal(" ]"); |
|
|
} |
|
519 |
|
|
|
debug(" ]\n"); |
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
break; |
|
520 |
|
|
521 |
case SCSICMD_PREVENT_ALLOW_REMOVE: |
/* |
522 |
debug("[ SCSI 0x%02x Prevent/allow medium removal: " |
* diskimage__internal_access(): |
523 |
"TODO ]\n", xferp->cmd[0]); |
* |
524 |
|
* Read from or write to a struct diskimage. |
525 |
|
* |
526 |
|
* Returns 1 if the access completed successfully, 0 otherwise. |
527 |
|
*/ |
528 |
|
int diskimage__internal_access(struct diskimage *d, int writeflag, |
529 |
|
off_t offset, unsigned char *buf, size_t len) |
530 |
|
{ |
531 |
|
ssize_t lendone; |
532 |
|
|
533 |
diskimage__return_default_status_and_message(xferp); |
if (buf == NULL) { |
534 |
break; |
fprintf(stderr, "diskimage__internal_access(): buf = NULL\n"); |
535 |
|
exit(1); |
536 |
|
} |
537 |
|
if (len == 0) |
538 |
|
return 1; |
539 |
|
if (d->f == NULL) |
540 |
|
return 0; |
541 |
|
|
542 |
case 0xbd: |
if (writeflag) { |
543 |
fatal("[ SCSI 0x%02x (len %i), TODO: ", xferp->cmd[0], |
if (!d->writable) |
544 |
xferp->cmd_len); |
return 0; |
|
for (i=0; i<(ssize_t)xferp->cmd_len; i++) |
|
|
fatal(" %02x", xferp->cmd[i]); |
|
|
fatal(" ]\n"); |
|
545 |
|
|
546 |
|
lendone = fwrite_helper(offset, buf, len, d); |
547 |
|
} else { |
548 |
/* |
/* |
549 |
* Used by Windows NT? |
* Special case for CD-ROMs. Actually, this is not needed |
550 |
* |
* for .iso images, only for physical CDROMS on some OSes, |
551 |
* Not documented in http://www.danbbs.dk/~dino/ |
* such as FreeBSD. |
|
* SCSI/SCSI2-D.html. |
|
|
* Google gave the answer "MECHANISM_STATUS" for ATAPI. Hm. |
|
552 |
*/ |
*/ |
553 |
|
if (d->is_a_cdrom) |
554 |
|
lendone = diskimage_access__cdrom(d, offset, buf, len); |
555 |
|
else |
556 |
|
lendone = fread_helper(offset, buf, len, d); |
557 |
|
|
558 |
if (xferp->cmd_len < 12) { |
if (lendone < (ssize_t)len) |
559 |
fatal("WEIRD LEN?\n"); |
memset(buf + lendone, 0, len - lendone); |
560 |
retlen = 8; |
} |
|
} else { |
|
|
retlen = xferp->cmd[8] * 256 + xferp->cmd[9]; |
|
|
} |
|
|
|
|
|
/* Return data: */ |
|
|
scsi_transfer_allocbuf(&xferp->data_in_len, |
|
|
&xferp->data_in, retlen, 1); |
|
|
|
|
|
diskimage__return_default_status_and_message(xferp); |
|
|
|
|
|
break; |
|
561 |
|
|
562 |
default: |
/* Incomplete data transfer? Then return failure: */ |
563 |
fatal("[ UNIMPLEMENTED SCSI command 0x%02x, disk id=%i ]\n", |
if (lendone != (ssize_t)len) { |
564 |
xferp->cmd[0], id); |
#ifdef UNSTABLE_DEVEL |
565 |
exit(1); |
fatal |
566 |
|
#else |
567 |
|
debug |
568 |
|
#endif |
569 |
|
("[ diskimage__internal_access(): disk_id %i, offset %lli" |
570 |
|
", transfer not completed. len=%i, len_done=%i ]\n", |
571 |
|
d->id, (long long)offset, (int)len, (int)lendone); |
572 |
|
return 0; |
573 |
} |
} |
|
debug(" ]\n"); |
|
574 |
|
|
575 |
return 1; |
return 1; |
576 |
} |
} |
631 |
* r read-only (don't allow changes to the file) |
* r read-only (don't allow changes to the file) |
632 |
* s SCSI (this is the default) |
* s SCSI (this is the default) |
633 |
* t tape |
* t tape |
634 |
|
* V add an overlay to a disk image |
635 |
* 0-7 force a specific SCSI ID number |
* 0-7 force a specific SCSI ID number |
636 |
* |
* |
637 |
* machine is assumed to be non-NULL. |
* machine is assumed to be non-NULL. |
645 |
char *cp; |
char *cp; |
646 |
int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0; |
int prefix_b=0, prefix_c=0, prefix_d=0, prefix_f=0, prefix_g=0; |
647 |
int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id=-1; |
int prefix_i=0, prefix_r=0, prefix_s=0, prefix_t=0, prefix_id=-1; |
648 |
int prefix_o=0; |
int prefix_o=0, prefix_V=0; |
649 |
|
|
650 |
if (fname == NULL) { |
if (fname == NULL) { |
651 |
fprintf(stderr, "diskimage_add(): NULL ptr\n"); |
fprintf(stderr, "diskimage_add(): NULL ptr\n"); |
727 |
case 't': |
case 't': |
728 |
prefix_t = 1; |
prefix_t = 1; |
729 |
break; |
break; |
730 |
|
case 'V': |
731 |
|
prefix_V = 1; |
732 |
|
break; |
733 |
case ':': |
case ':': |
734 |
break; |
break; |
735 |
default: |
default: |
748 |
} |
} |
749 |
memset(d, 0, sizeof(struct diskimage)); |
memset(d, 0, sizeof(struct diskimage)); |
750 |
|
|
|
d2 = machine->first_diskimage; |
|
|
if (d2 == NULL) { |
|
|
machine->first_diskimage = d; |
|
|
} else { |
|
|
while (d2->next != NULL) |
|
|
d2 = d2->next; |
|
|
d2->next = d; |
|
|
} |
|
|
|
|
751 |
/* Default to IDE disks... */ |
/* Default to IDE disks... */ |
752 |
d->type = DISKIMAGE_IDE; |
d->type = DISKIMAGE_IDE; |
753 |
|
|
769 |
if (prefix_s) |
if (prefix_s) |
770 |
d->type = DISKIMAGE_SCSI; |
d->type = DISKIMAGE_SCSI; |
771 |
|
|
772 |
|
/* Special case: Add an overlay for an already added disk image: */ |
773 |
|
if (prefix_V) { |
774 |
|
struct diskimage *dx = machine->first_diskimage; |
775 |
|
|
776 |
|
if (prefix_id < 0) { |
777 |
|
fprintf(stderr, "The 'V' disk image prefix requires" |
778 |
|
" a disk ID to also be supplied.\n"); |
779 |
|
exit(1); |
780 |
|
} |
781 |
|
|
782 |
|
while (dx != NULL) { |
783 |
|
if (d->type == dx->type && prefix_id == dx->id) |
784 |
|
break; |
785 |
|
dx = dx->next; |
786 |
|
} |
787 |
|
|
788 |
|
if (dx == NULL) { |
789 |
|
fprintf(stderr, "Bad ID supplied for overlay?\n"); |
790 |
|
exit(1); |
791 |
|
} |
792 |
|
|
793 |
|
diskimage_add_overlay(dx, fname); |
794 |
|
|
795 |
|
/* Free the preliminary d struct: */ |
796 |
|
free(d); |
797 |
|
|
798 |
|
/* Don't add any disk image. This is an overlay! */ |
799 |
|
return -1; |
800 |
|
} |
801 |
|
|
802 |
|
/* Add the new disk image in the disk image chain: */ |
803 |
|
d2 = machine->first_diskimage; |
804 |
|
if (d2 == NULL) { |
805 |
|
machine->first_diskimage = d; |
806 |
|
} else { |
807 |
|
while (d2->next != NULL) |
808 |
|
d2 = d2->next; |
809 |
|
d2->next = d; |
810 |
|
} |
811 |
|
|
812 |
if (prefix_o) |
if (prefix_o) |
813 |
d->override_base_offset = override_base_offset; |
d->override_base_offset = override_base_offset; |
814 |
|
|
1064 |
*/ |
*/ |
1065 |
void diskimage_dump_info(struct machine *machine) |
void diskimage_dump_info(struct machine *machine) |
1066 |
{ |
{ |
1067 |
int iadd = DEBUG_INDENTATION; |
int i, iadd = DEBUG_INDENTATION; |
1068 |
struct diskimage *d = machine->first_diskimage; |
struct diskimage *d = machine->first_diskimage; |
1069 |
|
|
1070 |
while (d != NULL) { |
while (d != NULL) { |
1106 |
debug(" (BOOT)"); |
debug(" (BOOT)"); |
1107 |
debug("\n"); |
debug("\n"); |
1108 |
|
|
1109 |
|
for (i=0; i<d->nr_of_overlays; i++) { |
1110 |
|
debug("overlay %i: %s\n", |
1111 |
|
i, d->overlays[i].overlay_basename); |
1112 |
|
} |
1113 |
|
|
1114 |
debug_indentation(-iadd); |
debug_indentation(-iadd); |
1115 |
|
|
1116 |
d = d->next; |
d = d->next; |