/* * Copyright (C) yajin 2008 * * This file is part of the virtualmips distribution. * See LICENSE file for terms of the license. */ /* 4M *byte* FLASH device simulation (device id=22F9h). Most important part of flash simulation is CFI interface. See flash datasheet for details. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include "device.h" #include "mips_memory.h" #define ROM_INIT_STATE 0 /* flash private data */ struct flash_data { struct vdevice *dev; m_uint8_t *flash_ptr; m_uint32_t flash_size; m_uint8_t *flash_file_name; m_uint32_t state; int flash_fd; }; typedef struct flash_data flash_data_t; #define BPTR(d,offset) (((char *)d->flash_ptr) + offset) m_uint16_t vendorID = 0x01; // target is little end 0x0001 m_uint16_t deviceID = 0x22f9; // target is little end 0x22F9 m_uint16_t earse_ready = 0x80; // target is little end 0X0080 m_uint32_t last_offset = 0; m_uint32_t dump_data; m_uint32_t cfi_data[] = { 0x51, 0x52, 0x59, 0x2, 0x0, 0x40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x27, 0x36, 0x0, 0x0, 0x4, 0x0, 0xa, 0x0, 0x5, 0x0, 0x4, 0x0, 0x16, 0x2, 0x0, 0x0, 0x0, 0x2, 0x7, 0x0, 0x20, 0x0, 0x3e, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x50, 0x52, 0x49, 0x31, 0x31, 0x0, 0x2, 0x4, 0x1, 0x4, 0x0, 0x0, 0x0, 0xb5, 0xc5, 0x2, //02 BOTTOM }; static void secotor_info (m_uint32_t offset, m_uint32_t * sector_start, m_uint32_t * sector_size) { if (offset <= 0x00FFF) { *sector_start = offset & 0xFFFFFE00; *sector_size = 0x2000; return; } *sector_start = offset & (0xffff0000); *sector_size = 0x10000; } void *dev_flash_access (cpu_mips_t * cpu, struct vdevice *dev, m_uint32_t offset, u_int op_size, u_int op_type, m_uint32_t * data, m_uint8_t * has_set_value) { flash_data_t *d = dev->priv_data; m_uint32_t r_offset, sector_size, sector_start; m_uint32_t last_sector_start, last_sector_size; if (offset >= d->flash_size) { *data = 0; return NULL; } if (op_type == MTS_READ) { switch (d->state) { case 0: return (BPTR (d, offset)); case 99: /*cfi query */ if ((offset >= 0x20) && (offset <= 0x9e)) { *data = (cfi_data[(offset - 0x20) / 2]); //always littleend *has_set_value = TRUE; } else { d->state = 0; return (BPTR (d, offset)); } break; case 0x6: d->state = 0; if (offset == 0X0) return &vendorID; if (offset == 0X2) return &deviceID; break; case 10: //last cycle is chip erase or sector erase secotor_info (offset, §or_start, §or_size); secotor_info (last_offset, &last_sector_start, &last_sector_size); d->state = 0; if (last_sector_start == sector_start) return &earse_ready; else return (BPTR (d, offset)); break; default: cpu_log (cpu, dev->name, "read: unhandled state %d\n", d->state); } return NULL; } if (op_type == MTS_WRITE) { r_offset = offset; if ((op_size == MTS_HALF_WORD) && (offset == 0X554)) offset = 0X555; switch (d->state) { case ROM_INIT_STATE: switch (offset) { case 0xAAA: if (((*data) & 0xff) == 0xAA) d->state = 1; break; case 0XAA: if (((*data) & 0xff) == 0x98) { d->state = 99; //CFI QUERY } break; default: switch ((*data) & 0xff) { case 0xB0: /* Erase/Program Suspend */ d->state = 0; break; case 0x30: /* Erase/Program Resume */ d->state = 0; break; case 0xF0: case 0xFF: /*Read/Reset */ d->state = 0; break; default: return ((void *) (d->flash_ptr + r_offset)); } } break; case 99: if (((*data & 0xff) == 0xff) || ((*data & 0xff) == 0xf0)) d->state = 0; else return ((void *) (d->flash_ptr + r_offset)); break; case 1: if ((offset != 0x555) && ((*data & 0xff) != 0x55)) d->state = 0; else d->state = 2; break; case 2: d->state = 0; if (offset == 0xAAA) { switch ((*data) & 0xff) { case 0x80: d->state = 3; break; case 0xA0: /* Byte/Word program */ d->state = 4; break; case 0x90: /* Product ID Entry / Status of Block B protection */ d->state = 6; break; } } break; case 3: if ((offset != 0xAAA) && (*data != 0xAA)) d->state = 0; else d->state = 8; break; case 8: if ((offset != 0x555) && (*data != 0x55)) d->state = 0; else d->state = 9; break; case 9: d->state = 10; last_offset = r_offset; switch ((*data) & 0xff) { case 0x10: /* Chip Erase */ memset (BPTR (d, 0), 0, d->dev->phys_len); break; case 0x30: /* Sector Erase */ secotor_info (r_offset, §or_start, §or_size); break; } break; /* Byte/Word Program */ case 4: d->state = 0; return ((void *) (d->flash_ptr + r_offset)); break; default: cpu_log (cpu, dev->name, "write: unhandled state %d\n", d->state); } return &dump_data; } assert (0); } static int dev_flash_load (char *flash_file_name, m_uint32_t flash_len, unsigned char **flash_data_hp, u_int create) { int fd; struct stat sb; unsigned char *temp; fd = open (flash_file_name, O_RDWR); if ((fd < 0) && (create == 1)) { fprintf (stderr, "Can not open flash file. name %s\n", flash_file_name); fprintf (stderr, "creating flash file. name %s\n", flash_file_name); fd = open (flash_file_name, O_RDWR | O_CREAT, S_IREAD | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd < 0) { fprintf (stderr, "Can not create flash file. name %s\n", flash_file_name); return (-1); } temp = malloc (flash_len); assert (temp != NULL); memset (temp, 0xff, flash_len); lseek (fd, 0, SEEK_SET); write (fd, (void *) temp, flash_len); free (temp); fprintf (stderr, "create flash file success. name %s\n", flash_file_name); lseek (fd, 0, SEEK_SET); } else if (fd < 0) { fprintf (stderr, "%s does not exist and not allowed to create.\n", flash_file_name); return (-1); } assert (fd >= 0); fstat (fd, &sb); if (flash_len < sb.st_size) { fprintf (stderr, "Too large flash file. flash len:%d M, flash file name %s," "flash file legth: %d bytes.\n", flash_len, flash_file_name, (int) sb.st_size); return (-1); } *flash_data_hp = mmap (NULL, sb.st_size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); if (*flash_data_hp == MAP_FAILED) { fprintf (stderr, "errno %d\n", errno); fprintf (stderr, "failed\n"); return (-1); } return 0; } /* Initialize a NOR Flash zone */ int dev_nor_flash_4m_init (vm_instance_t * vm, char *name) { flash_data_t *d; unsigned char *flash_data_hp; /*load rom data */ if (dev_flash_load (vm->flash_filename, vm->flash_size * 1048576, &flash_data_hp, 1) < 0) return (-1); /* allocate the private data structure */ if (!(d = malloc (sizeof (*d)))) { fprintf (stderr, "FLASH: unable to create device.\n"); return (-1); } memset (d, 0, sizeof (*d)); d->flash_ptr = flash_data_hp; d->flash_size = vm->flash_size * 1048576; d->state = ROM_INIT_STATE; if (!(d->dev = dev_create (name))) goto err_dev_create; d->dev->priv_data = d; d->dev->phys_addr = vm->flash_address; d->dev->phys_len = vm->flash_size * 1048576; d->dev->handler = dev_flash_access; d->dev->flags = VDEVICE_FLAG_NO_MTS_MMAP; /* Map this device to the VM */ vm_bind_device (vm, d->dev); return (0); err_dev_create: free (d); return (-1); }