1 |
/* |
2 |
* Cisco C7200 (Predator) Bootflash. |
3 |
* Copyright (c) 2006 Christophe Fillot. All rights reserved. |
4 |
* |
5 |
* Intel Flash SIMM emulation (28F008SA/28F016SA) |
6 |
* |
7 |
* Intelligent ID Codes: |
8 |
* 28F008SA: 0x89A2 (1 Mb) |
9 |
* 28F016SA: 0x89A0 (2 Mb) |
10 |
* |
11 |
* Manuals: |
12 |
* http://www.ortodoxism.ro/datasheets/Intel/mXvsysv.pdf |
13 |
* |
14 |
* This code is working but is far from perfect. The four assembled circuits |
15 |
* should be managed independently. |
16 |
* |
17 |
* Here, we emulate a group of four 28F016SA, for a total size of 8 Mb. |
18 |
* If you need to change this, the Flash SIMM register must also be changed. |
19 |
* (TODO: a CLI option + generic code). |
20 |
*/ |
21 |
|
22 |
#include <stdio.h> |
23 |
#include <stdlib.h> |
24 |
#include <string.h> |
25 |
#include <time.h> |
26 |
#include <errno.h> |
27 |
|
28 |
#include "mips64.h" |
29 |
#include "dynamips.h" |
30 |
#include "memory.h" |
31 |
#include "device.h" |
32 |
|
33 |
#define DEBUG_ACCESS 0 |
34 |
#define DEBUG_WRITE 0 |
35 |
|
36 |
/* Bootflash private data */ |
37 |
struct bootflash_data { |
38 |
vm_obj_t vm_obj; |
39 |
struct vdevice dev; |
40 |
m_uint32_t cui_cmd,blk_cmd; |
41 |
m_uint32_t status; |
42 |
char *filename; |
43 |
}; |
44 |
|
45 |
#define BPTR(d,offset) (((char *)d->dev.host_addr) + offset) |
46 |
|
47 |
/* |
48 |
* dev_bootflash_access() |
49 |
*/ |
50 |
void *dev_bootflash_access(cpu_mips_t *cpu,struct vdevice *dev, |
51 |
m_uint32_t offset,u_int op_size,u_int op_type, |
52 |
m_uint64_t *data) |
53 |
{ |
54 |
struct bootflash_data *d = dev->priv_data; |
55 |
|
56 |
#if DEBUG_ACCESS |
57 |
if (op_type == MTS_READ) |
58 |
cpu_log(cpu,dev->name,"read access to offset = 0x%x, pc = 0x%llx " |
59 |
"(stat=%u,cui_cmd=0x%x)\n", |
60 |
offset,cpu->pc,d->status,d->cui_cmd); |
61 |
else |
62 |
cpu_log(cpu,dev->name,"write access to vaddr = 0x%x, pc = 0x%llx, " |
63 |
"val = 0x%llx\n",offset,cpu->pc,*data); |
64 |
#endif |
65 |
|
66 |
if (op_type == MTS_READ) { |
67 |
/* Read Array mode */ |
68 |
if (d->status == 0) |
69 |
return(BPTR(d,offset)); |
70 |
|
71 |
switch(d->cui_cmd) { |
72 |
/* Intelligent identifier */ |
73 |
case 0x90909090: |
74 |
switch(offset) { |
75 |
case 0x00: |
76 |
*data = 0x89898989; /* manufacturer code */ |
77 |
return NULL; |
78 |
case 0x04: |
79 |
*data = 0xA0A0A0A0; /* device code */ |
80 |
return NULL; |
81 |
default: |
82 |
cpu_log(cpu,dev->name, |
83 |
"Reading Intelligent ID Code at offset = 0x%x ?\n", |
84 |
offset); |
85 |
*data = 0x00000000; |
86 |
return NULL; |
87 |
} |
88 |
break; |
89 |
|
90 |
/* Read Status Register */ |
91 |
case 0x70707070: |
92 |
*data = 0x80808080; |
93 |
return NULL; |
94 |
} |
95 |
|
96 |
/* Default: status register */ |
97 |
*data = 0x80808080; |
98 |
return NULL; |
99 |
} |
100 |
|
101 |
/* write mode */ |
102 |
if (d->blk_cmd == 0x40404040) { |
103 |
#if DEBUG_WRITE |
104 |
cpu_log(cpu,dev->name,"Writing 0x%llx at offset=0x%x\n",*data,offset); |
105 |
#endif |
106 |
d->blk_cmd = 0; |
107 |
d->cui_cmd = 0; |
108 |
d->status = 1; |
109 |
return(BPTR(d,offset)); |
110 |
} |
111 |
|
112 |
switch(*data) { |
113 |
/* Erase Setup */ |
114 |
case 0x20202020: |
115 |
d->blk_cmd = *data; |
116 |
break; |
117 |
|
118 |
/* Erase Confirm */ |
119 |
case 0xd0d0d0d0: |
120 |
if ((d->blk_cmd == 0x20202020) && !(offset & 0x3FFFF)) { |
121 |
memset(BPTR(d,offset),0xFF,0x40000); |
122 |
d->blk_cmd = 0; |
123 |
d->cui_cmd = 0; |
124 |
d->status = 1; |
125 |
} |
126 |
break; |
127 |
|
128 |
/* Byte Write Setup (XXX ugly hack) */ |
129 |
case 0x40404040: |
130 |
case 0x40ffffff: |
131 |
case 0x4040ffff: |
132 |
case 0x404040ff: |
133 |
case 0xff404040: |
134 |
case 0xffff4040: |
135 |
case 0xffffff40: |
136 |
d->blk_cmd = 0x40404040; |
137 |
break; |
138 |
|
139 |
/* Reset */ |
140 |
case 0xffffffff: |
141 |
d->status = 0; |
142 |
break; |
143 |
|
144 |
/* Intelligent Identifier and Read Status register */ |
145 |
case 0x90909090: |
146 |
case 0x70707070: |
147 |
d->status = 1; |
148 |
d->cui_cmd = *data; |
149 |
break; |
150 |
|
151 |
default: |
152 |
cpu_log(cpu,dev->name, |
153 |
"default write case at offset=0x%7.7x, val=0x%llx\n", |
154 |
offset,*data); |
155 |
} |
156 |
|
157 |
return NULL; |
158 |
} |
159 |
|
160 |
/* Shutdown a bootflash device */ |
161 |
void dev_bootflash_shutdown(vm_instance_t *vm,struct bootflash_data *d) |
162 |
{ |
163 |
if (d != NULL) { |
164 |
/* Remove the device */ |
165 |
dev_remove(vm,&d->dev); |
166 |
|
167 |
/* We don't remove the file, since it used as permanent storage */ |
168 |
if (d->filename) |
169 |
free(d->filename); |
170 |
|
171 |
/* Free the structure itself */ |
172 |
free(d); |
173 |
} |
174 |
} |
175 |
|
176 |
/* Create a 8 Mb bootflash */ |
177 |
int dev_bootflash_init(vm_instance_t *vm,char *name, |
178 |
m_uint64_t paddr,m_uint32_t len) |
179 |
{ |
180 |
struct bootflash_data *d; |
181 |
u_char *ptr; |
182 |
|
183 |
/* Allocate the private data structure */ |
184 |
if (!(d = malloc(sizeof(*d)))) { |
185 |
fprintf(stderr,"Bootflash: unable to create device.\n"); |
186 |
return(-1); |
187 |
} |
188 |
|
189 |
memset(d,0,sizeof(*d)); |
190 |
|
191 |
vm_object_init(&d->vm_obj); |
192 |
d->vm_obj.name = name; |
193 |
d->vm_obj.data = d; |
194 |
d->vm_obj.shutdown = (vm_shutdown_t)dev_bootflash_shutdown; |
195 |
|
196 |
if (!(d->filename = vm_build_filename(vm,name))) { |
197 |
fprintf(stderr,"Bootflash: unable to create filename.\n"); |
198 |
goto err_filename; |
199 |
} |
200 |
|
201 |
dev_init(&d->dev); |
202 |
d->dev.name = name; |
203 |
d->dev.priv_data = d; |
204 |
d->dev.phys_addr = paddr; |
205 |
d->dev.phys_len = len; |
206 |
d->dev.handler = dev_bootflash_access; |
207 |
d->dev.fd = memzone_create_file(d->filename,d->dev.phys_len,&ptr); |
208 |
d->dev.host_addr = (m_iptr_t)ptr; |
209 |
d->dev.flags = VDEVICE_FLAG_NO_MTS_MMAP; |
210 |
|
211 |
if (d->dev.fd == -1) { |
212 |
fprintf(stderr,"Bootflash: unable to map file '%s'\n",d->filename); |
213 |
goto err_fd_create; |
214 |
} |
215 |
|
216 |
/* Map this device to the VM */ |
217 |
vm_bind_device(vm,&d->dev); |
218 |
vm_object_add(vm,&d->vm_obj); |
219 |
return(0); |
220 |
|
221 |
err_fd_create: |
222 |
free(d->filename); |
223 |
err_filename: |
224 |
free(d); |
225 |
return(-1); |
226 |
} |