1 |
/* |
/* |
2 |
* Cisco C7200 (Predator) simulation platform. |
* Cisco router simulation platform. |
3 |
* Copyright (c) 2006 Christophe Fillot. All rights reserved. |
* Copyright (c) 2006 Christophe Fillot. All rights reserved. |
4 |
* |
* |
5 |
* PCMCIA ATA Flash emulation. |
* PCMCIA ATA Flash emulation. |
15 |
#include <sys/stat.h> |
#include <sys/stat.h> |
16 |
#include <fcntl.h> |
#include <fcntl.h> |
17 |
|
|
18 |
#include "mips64.h" |
#include "cpu.h" |
19 |
|
#include "vm.h" |
20 |
#include "dynamips.h" |
#include "dynamips.h" |
21 |
#include "memory.h" |
#include "memory.h" |
22 |
#include "device.h" |
#include "device.h" |
23 |
|
|
24 |
#define DEBUG_ACCESS 0 |
#define DEBUG_ACCESS 0 |
25 |
#define DEBUG_ATA 0 |
#define DEBUG_ATA 0 |
26 |
|
#define DEBUG_READ 0 |
27 |
|
#define DEBUG_WRITE 0 |
28 |
|
|
29 |
/* Default disk parameters: 4 heads, 32 sectors per track */ |
/* Default disk parameters: 4 heads, 32 sectors per track */ |
30 |
#define DISK_NR_HEADS 4 |
#define DISK_NR_HEADS 4 |
144 |
{ |
{ |
145 |
off_t disk_offset = (off_t)sect * SECTOR_SIZE; |
off_t disk_offset = (off_t)sect * SECTOR_SIZE; |
146 |
|
|
147 |
|
#if DEBUG_READ |
148 |
|
vm_log(d->vm,d->dev.name,"reading sector 0x%8.8x\n",sect); |
149 |
|
#endif |
150 |
|
|
151 |
if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { |
if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { |
152 |
perror("read_sector: lseek"); |
perror("read_sector: lseek"); |
153 |
return(-1); |
return(-1); |
167 |
{ |
{ |
168 |
off_t disk_offset = (off_t)sect * SECTOR_SIZE; |
off_t disk_offset = (off_t)sect * SECTOR_SIZE; |
169 |
|
|
170 |
|
#if DEBUG_WRITE |
171 |
|
vm_log(d->vm,d->dev.name,"writing sector 0x%8.8x\n",sect); |
172 |
|
#endif |
173 |
|
|
174 |
if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { |
if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { |
175 |
perror("write_sector: lseek"); |
perror("write_sector: lseek"); |
176 |
return(-1); |
return(-1); |
193 |
sect_count = d->nr_heads * d->nr_cylinders * d->sects_per_track; |
sect_count = d->nr_heads * d->nr_cylinders * d->sects_per_track; |
194 |
|
|
195 |
/* Clear all fields (for safety) */ |
/* Clear all fields (for safety) */ |
196 |
memset(p,0,SECTOR_SIZE); |
memset(p,0x00,SECTOR_SIZE); |
197 |
|
|
198 |
/* Word 0: General Configuration */ |
/* Word 0: General Configuration */ |
199 |
p[0] = 0x8a; |
p[0] = 0x8a; |
233 |
/* Word 56: Current number of sectors per track */ |
/* Word 56: Current number of sectors per track */ |
234 |
p[112] = d->sects_per_track; |
p[112] = d->sects_per_track; |
235 |
|
|
236 |
/* Word 57/58: Current of sectors per card (MSW/LSW) */ |
/* Word 57/58: Current of sectors per card (LSW/MSW) */ |
237 |
p[114] = (sect_count >> 16) & 0xFF; |
p[114] = sect_count & 0xFF; |
238 |
p[115] = (sect_count >> 24); |
p[115] = (sect_count >> 8) & 0xFF; |
239 |
p[116] = sect_count & 0xFF; |
|
240 |
p[117] = (sect_count >> 8) & 0xFF; |
p[116] = (sect_count >> 16) & 0xFF; |
241 |
|
p[117] = (sect_count >> 24); |
242 |
|
|
243 |
|
#if 0 |
244 |
/* Word 60/61: Total sectors addressable in LBA mode (MSW/LSW) */ |
/* Word 60/61: Total sectors addressable in LBA mode (MSW/LSW) */ |
245 |
p[120] = (sect_count >> 16) & 0xFF; |
p[120] = (sect_count >> 16) & 0xFF; |
246 |
p[121] = (sect_count >> 24); |
p[121] = (sect_count >> 24); |
247 |
p[122] = sect_count & 0xFF; |
p[122] = sect_count & 0xFF; |
248 |
p[123] = (sect_count >> 8) & 0xFF; |
p[123] = (sect_count >> 8) & 0xFF; |
249 |
|
#endif |
250 |
} |
} |
251 |
|
|
252 |
/* Set sector position */ |
/* Set sector position */ |
255 |
u_int cyl; |
u_int cyl; |
256 |
|
|
257 |
if (d->head & ATA_DH_LBA) { |
if (d->head & ATA_DH_LBA) { |
258 |
/* TODO */ |
d->sect_pos = (u_int)(d->head & 0x0F) << 24; |
259 |
} |
d->sect_pos |= (u_int)d->cyl_high << 16; |
260 |
|
d->sect_pos |= (u_int)d->cyl_low << 8; |
261 |
|
d->sect_pos |= (u_int)d->sect_no; |
262 |
|
|
263 |
cyl = (((u_int)d->cyl_high) << 8) + d->cyl_low; |
#if DEBUG_ATA |
264 |
d->sect_pos = chs_to_lba(d,cyl,d->head & 0x0F,d->sect_no); |
vm_log(d->vm,d->dev.name,"ata_set_sect_pos: LBA sect=0x%x\n", |
265 |
|
d->sect_pos); |
266 |
|
#endif |
267 |
|
} else { |
268 |
|
cyl = (((u_int)d->cyl_high) << 8) + d->cyl_low; |
269 |
|
d->sect_pos = chs_to_lba(d,cyl,d->head & 0x0F,d->sect_no); |
270 |
|
|
271 |
|
#if DEBUG_ATA |
272 |
|
vm_log(d->vm,d->dev.name, |
273 |
|
"ata_set_sect_pos: cyl=0x%x,head=0x%x,sect=0x%x => " |
274 |
|
"sect_pos=0x%x\n", |
275 |
|
cyl,d->head & 0x0F,d->sect_no,d->sect_pos); |
276 |
|
#endif |
277 |
|
} |
278 |
} |
} |
279 |
|
|
280 |
/* ATA device identifier callback */ |
/* ATA device identifier callback */ |
321 |
vm_log(d->vm,d->dev.name,"ATA command 0x%2.2x\n",(u_int)d->ata_cmd); |
vm_log(d->vm,d->dev.name,"ATA command 0x%2.2x\n",(u_int)d->ata_cmd); |
322 |
#endif |
#endif |
323 |
|
|
324 |
|
d->data_pos = 0; |
325 |
|
|
326 |
switch(d->ata_cmd) { |
switch(d->ata_cmd) { |
327 |
case ATA_CMD_IDENT_DEVICE: |
case ATA_CMD_IDENT_DEVICE: |
328 |
ata_identify_device(d); |
ata_identify_device(d); |
360 |
} |
} |
361 |
|
|
362 |
/* |
/* |
363 |
* dev_pcmcia_disk_access() |
* dev_pcmcia_disk_access_0() |
364 |
*/ |
*/ |
365 |
void *dev_pcmcia_disk_access(cpu_mips_t *cpu,struct vdevice *dev, |
void *dev_pcmcia_disk_access_0(cpu_gen_t *cpu,struct vdevice *dev, |
366 |
m_uint32_t offset,u_int op_size,u_int op_type, |
m_uint32_t offset,u_int op_size,u_int op_type, |
367 |
m_uint64_t *data) |
m_uint64_t *data) |
368 |
{ |
{ |
369 |
struct pcmcia_disk_data *d = dev->priv_data; |
struct pcmcia_disk_data *d = dev->priv_data; |
|
m_uint32_t d_offset; |
|
370 |
|
|
371 |
/* Compute the good internal offset */ |
/* Compute the good internal offset */ |
372 |
offset = (offset >> 1) ^ 1; |
offset = (offset >> 1) ^ 1; |
375 |
if (op_type == MTS_READ) { |
if (op_type == MTS_READ) { |
376 |
cpu_log(cpu,d->dev.name, |
cpu_log(cpu,d->dev.name, |
377 |
"reading offset 0x%5.5x at pc=0x%llx (size=%u)\n", |
"reading offset 0x%5.5x at pc=0x%llx (size=%u)\n", |
378 |
offset,cpu->pc,op_size); |
offset,cpu_get_pc(cpu),op_size); |
379 |
} else { |
} else { |
380 |
cpu_log(cpu,d->dev.name, |
cpu_log(cpu,d->dev.name, |
381 |
"writing offset 0x%5.5x, data=0x%llx at pc=0x%llx (size=%u)\n", |
"writing offset 0x%5.5x, data=0x%llx at pc=0x%llx (size=%u)\n", |
382 |
offset,*data,cpu->pc,op_size); |
offset,*data,cpu_get_pc(cpu),op_size); |
383 |
} |
} |
384 |
#endif |
#endif |
385 |
|
|
420 |
*data = (d->ata_status << 8) + d->head; |
*data = (d->ata_status << 8) + d->head; |
421 |
else { |
else { |
422 |
d->ata_cmd = *data >> 8; |
d->ata_cmd = *data >> 8; |
423 |
d->head = *data & 0xFF; |
d->head = *data; |
424 |
ata_handle_cmd(d); |
ata_handle_cmd(d); |
425 |
} |
} |
426 |
break; |
break; |
430 |
if ((offset >= d->data_offset) && |
if ((offset >= d->data_offset) && |
431 |
(offset < d->data_offset + (SECTOR_SIZE/2))) |
(offset < d->data_offset + (SECTOR_SIZE/2))) |
432 |
{ |
{ |
|
d_offset = offset - d->data_offset; |
|
|
|
|
433 |
if (op_type == MTS_READ) { |
if (op_type == MTS_READ) { |
434 |
*data = d->data_buffer[(d_offset << 1)]; |
*data = d->data_buffer[(d->data_pos << 1)]; |
435 |
*data += d->data_buffer[(d_offset << 1)+1] << 8; |
*data += d->data_buffer[(d->data_pos << 1)+1] << 8; |
436 |
} else { |
} else { |
437 |
d->data_buffer[(d_offset << 1)] = *data & 0xFF; |
d->data_buffer[(d->data_pos << 1)] = *data & 0xFF; |
438 |
d->data_buffer[(d_offset << 1)+1] = *data >> 8; |
d->data_buffer[(d->data_pos << 1)+1] = *data >> 8; |
439 |
} |
} |
440 |
|
|
441 |
|
d->data_pos++; |
442 |
|
|
443 |
/* Validate data transfer */ |
/* Buffer complete: call the callback function */ |
444 |
if (d_offset == d->data_pos) { |
if (d->data_pos == (SECTOR_SIZE/2)) { |
445 |
d->data_pos++; |
d->data_pos = 0; |
446 |
|
|
447 |
/* Buffer validated: call the callback function */ |
if (d->ata_cmd_callback) |
448 |
if (d->data_pos == (SECTOR_SIZE/2)) { |
d->ata_cmd_callback(d); |
|
d->data_pos = 0; |
|
|
|
|
|
if (d->ata_cmd_callback) |
|
|
d->ata_cmd_callback(d); |
|
|
} |
|
449 |
} |
} |
450 |
} |
} |
451 |
} |
} |
453 |
return NULL; |
return NULL; |
454 |
} |
} |
455 |
|
|
456 |
|
/* |
457 |
|
* dev_pcmcia_disk_access_1() |
458 |
|
*/ |
459 |
|
void *dev_pcmcia_disk_access_1(cpu_gen_t *cpu,struct vdevice *dev, |
460 |
|
m_uint32_t offset,u_int op_size,u_int op_type, |
461 |
|
m_uint64_t *data) |
462 |
|
{ |
463 |
|
struct pcmcia_disk_data *d = dev->priv_data; |
464 |
|
|
465 |
|
/* Compute the good internal offset */ |
466 |
|
offset = (offset >> 1) ^ 1; |
467 |
|
|
468 |
|
#if DEBUG_ACCESS |
469 |
|
if (op_type == MTS_READ) { |
470 |
|
cpu_log(cpu,d->dev.name, |
471 |
|
"reading offset 0x%5.5x at pc=0x%llx (size=%u)\n", |
472 |
|
offset,cpu->pc,op_size); |
473 |
|
} else { |
474 |
|
cpu_log(cpu,d->dev.name, |
475 |
|
"writing offset 0x%5.5x, data=0x%llx at pc=0x%llx (size=%u)\n", |
476 |
|
offset,*data,cpu->pc,op_size); |
477 |
|
} |
478 |
|
#endif |
479 |
|
|
480 |
|
switch(offset) { |
481 |
|
case 0x02: /* Sector Count + Sector no */ |
482 |
|
if (op_type == MTS_READ) { |
483 |
|
*data = (d->sect_no << 8) + d->sect_count; |
484 |
|
} else { |
485 |
|
d->sect_no = *data >> 8; |
486 |
|
d->sect_count = *data & 0xFF; |
487 |
|
} |
488 |
|
break; |
489 |
|
|
490 |
|
case 0x04: /* Cylinder Low + Cylinder High */ |
491 |
|
if (op_type == MTS_READ) { |
492 |
|
*data = (d->cyl_high << 8) + d->cyl_low; |
493 |
|
} else { |
494 |
|
d->cyl_high = *data >> 8; |
495 |
|
d->cyl_low = *data & 0xFF; |
496 |
|
} |
497 |
|
break; |
498 |
|
|
499 |
|
case 0x06: /* Select Card/Head + Status/Command register */ |
500 |
|
if (op_type == MTS_READ) |
501 |
|
*data = (d->ata_status << 8) + d->head; |
502 |
|
else { |
503 |
|
d->ata_cmd = *data >> 8; |
504 |
|
d->head = *data & 0xFF; |
505 |
|
ata_handle_cmd(d); |
506 |
|
} |
507 |
|
break; |
508 |
|
|
509 |
|
case 0x08: |
510 |
|
if (op_type == MTS_READ) { |
511 |
|
*data = d->data_buffer[(d->data_pos << 1)]; |
512 |
|
*data += d->data_buffer[(d->data_pos << 1)+1] << 8; |
513 |
|
} else { |
514 |
|
d->data_buffer[(d->data_pos << 1)] = *data & 0xFF; |
515 |
|
d->data_buffer[(d->data_pos << 1)+1] = *data >> 8; |
516 |
|
} |
517 |
|
|
518 |
|
d->data_pos++; |
519 |
|
|
520 |
|
/* Buffer complete: call the callback function */ |
521 |
|
if (d->data_pos == (SECTOR_SIZE/2)) { |
522 |
|
d->data_pos = 0; |
523 |
|
|
524 |
|
if (d->ata_cmd_callback) |
525 |
|
d->ata_cmd_callback(d); |
526 |
|
} |
527 |
|
break; |
528 |
|
|
529 |
|
case 0x0E: |
530 |
|
break; |
531 |
|
} |
532 |
|
|
533 |
|
return NULL; |
534 |
|
} |
535 |
|
|
536 |
/* Shutdown a PCMCIA disk device */ |
/* Shutdown a PCMCIA disk device */ |
537 |
void dev_pcmcia_disk_shutdown(vm_instance_t *vm,struct pcmcia_disk_data *d) |
void dev_pcmcia_disk_shutdown(vm_instance_t *vm,struct pcmcia_disk_data *d) |
538 |
{ |
{ |
554 |
/* Initialize a PCMCIA disk */ |
/* Initialize a PCMCIA disk */ |
555 |
vm_obj_t *dev_pcmcia_disk_init(vm_instance_t *vm,char *name, |
vm_obj_t *dev_pcmcia_disk_init(vm_instance_t *vm,char *name, |
556 |
m_uint64_t paddr,m_uint32_t len, |
m_uint64_t paddr,m_uint32_t len, |
557 |
u_int disk_size) |
u_int disk_size,int mode) |
558 |
{ |
{ |
559 |
struct pcmcia_disk_data *d; |
struct pcmcia_disk_data *d; |
560 |
m_uint32_t tot_sect; |
m_uint32_t tot_sect; |
602 |
d->dev.phys_addr = paddr; |
d->dev.phys_addr = paddr; |
603 |
d->dev.phys_len = len; |
d->dev.phys_len = len; |
604 |
d->dev.flags = VDEVICE_FLAG_CACHING; |
d->dev.flags = VDEVICE_FLAG_CACHING; |
605 |
d->dev.handler = dev_pcmcia_disk_access; |
|
606 |
|
if (mode == 0) |
607 |
|
d->dev.handler = dev_pcmcia_disk_access_0; |
608 |
|
else |
609 |
|
d->dev.handler = dev_pcmcia_disk_access_1; |
610 |
|
|
611 |
/* Map this device to the VM */ |
/* Map this device to the VM */ |
612 |
vm_bind_device(vm,&d->dev); |
vm_bind_device(vm,&d->dev); |
619 |
free(d); |
free(d); |
620 |
return NULL; |
return NULL; |
621 |
} |
} |
622 |
|
|
623 |
|
/* Get the device associated with a PCMCIA disk object */ |
624 |
|
struct vdevice *dev_pcmcia_disk_get_device(vm_obj_t *obj) |
625 |
|
{ |
626 |
|
struct pcmcia_disk_data *d; |
627 |
|
|
628 |
|
if (!obj || !(d = obj->data)) |
629 |
|
return NULL; |
630 |
|
|
631 |
|
return(&d->dev); |
632 |
|
} |