1 |
dpavlin |
1 |
/* |
2 |
dpavlin |
7 |
* Cisco router simulation platform. |
3 |
dpavlin |
1 |
* Copyright (c) 2006 Christophe Fillot. All rights reserved. |
4 |
|
|
* |
5 |
|
|
* PCMCIA ATA Flash emulation. |
6 |
|
|
*/ |
7 |
|
|
|
8 |
|
|
#include <stdio.h> |
9 |
|
|
#include <stdlib.h> |
10 |
|
|
#include <string.h> |
11 |
|
|
#include <unistd.h> |
12 |
|
|
#include <time.h> |
13 |
|
|
#include <errno.h> |
14 |
|
|
#include <sys/types.h> |
15 |
|
|
#include <sys/stat.h> |
16 |
|
|
#include <fcntl.h> |
17 |
|
|
|
18 |
dpavlin |
7 |
#include "cpu.h" |
19 |
|
|
#include "vm.h" |
20 |
dpavlin |
1 |
#include "dynamips.h" |
21 |
|
|
#include "memory.h" |
22 |
|
|
#include "device.h" |
23 |
|
|
|
24 |
|
|
#define DEBUG_ACCESS 0 |
25 |
|
|
#define DEBUG_ATA 0 |
26 |
dpavlin |
4 |
#define DEBUG_READ 0 |
27 |
|
|
#define DEBUG_WRITE 0 |
28 |
dpavlin |
1 |
|
29 |
|
|
/* Default disk parameters: 4 heads, 32 sectors per track */ |
30 |
|
|
#define DISK_NR_HEADS 4 |
31 |
|
|
#define DISK_SECTS_PER_TRACK 32 |
32 |
|
|
|
33 |
|
|
/* Size (in bytes) of a sector */ |
34 |
|
|
#define SECTOR_SIZE 512 |
35 |
|
|
|
36 |
|
|
/* ATA commands */ |
37 |
|
|
#define ATA_CMD_NOP 0x00 |
38 |
|
|
#define ATA_CMD_READ_SECTOR 0x20 |
39 |
|
|
#define ATA_CMD_WRITE_SECTOR 0x30 |
40 |
|
|
#define ATA_CMD_IDENT_DEVICE 0xEC |
41 |
|
|
|
42 |
|
|
/* ATA status */ |
43 |
|
|
#define ATA_STATUS_BUSY 0x80 /* Controller busy */ |
44 |
|
|
#define ATA_STATUS_RDY 0x40 /* Device ready */ |
45 |
|
|
#define ATA_STATUS_DWF 0x20 /* Write fault */ |
46 |
|
|
#define ATA_STATUS_DSC 0x10 /* Device ready */ |
47 |
|
|
#define ATA_STATUS_DRQ 0x08 /* Data Request */ |
48 |
|
|
#define ATA_STATUS_CORR 0x04 /* Correctable error */ |
49 |
|
|
#define ATA_STATUS_IDX 0x02 /* Always 0 */ |
50 |
|
|
#define ATA_STATUS_ERR 0x01 /* Error */ |
51 |
|
|
|
52 |
|
|
/* ATA Drive/Head register */ |
53 |
|
|
#define ATA_DH_LBA 0x40 /* LBA Mode */ |
54 |
|
|
|
55 |
|
|
/* Card Information Structure */ |
56 |
|
|
static m_uint8_t cis_table[] = { |
57 |
|
|
0x01, 0x03, 0xd9, 0x01, 0xff, 0x1c, 0x04, 0x03, |
58 |
|
|
0xd9, 0x01, 0xff, 0x18, 0x02, 0xdf, 0x01, 0x20, |
59 |
|
|
0x04, 0x34, 0x12, 0x00, 0x02, 0x15, 0x2b, 0x04, |
60 |
|
|
0x01, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x70, |
61 |
|
|
0x73, 0x20, 0x41, 0x54, 0x41, 0x20, 0x46, 0x6c, |
62 |
|
|
0x61, 0x73, 0x68, 0x20, 0x43, 0x61, 0x72, 0x64, |
63 |
|
|
0x20, 0x20, 0x00, 0x44, 0x59, 0x4e, 0x41, 0x30, |
64 |
|
|
0x20, 0x20, 0x00, 0x44, 0x59, 0x4e, 0x41, 0x30, |
65 |
|
|
0x00, 0xff, 0x21, 0x02, 0x04, 0x01, 0x22, 0x02, |
66 |
|
|
0x01, 0x01, 0x22, 0x03, 0x02, 0x04, 0x5f, 0x1a, |
67 |
|
|
0x05, 0x01, 0x03, 0x00, 0x02, 0x0f, 0x1b, 0x0b, |
68 |
|
|
0xc0, 0x40, 0xa1, 0x27, 0x55, 0x4d, 0x5d, 0x75, |
69 |
|
|
0x08, 0x00, 0x21, 0x1b, 0x06, 0x00, 0x01, 0x21, |
70 |
|
|
0xb5, 0x1e, 0x4d, 0x1b, 0x0d, 0xc1, 0x41, 0x99, |
71 |
|
|
0x27, 0x55, 0x4d, 0x5d, 0x75, 0x64, 0xf0, 0xff, |
72 |
|
|
0xff, 0x21, 0x1b, 0x06, 0x01, 0x01, 0x21, 0xb5, |
73 |
|
|
0x1e, 0x4d, 0x1b, 0x12, 0xc2, 0x41, 0x99, 0x27, |
74 |
|
|
0x55, 0x4d, 0x5d, 0x75, 0xea, 0x61, 0xf0, 0x01, |
75 |
|
|
0x07, 0xf6, 0x03, 0x01, 0xee, 0x21, 0x1b, 0x06, |
76 |
|
|
0x02, 0x01, 0x21, 0xb5, 0x1e, 0x4d, 0x1b, 0x12, |
77 |
|
|
0xc3, 0x41, 0x99, 0x27, 0x55, 0x4d, 0x5d, 0x75, |
78 |
|
|
0xea, 0x61, 0x70, 0x01, 0x07, 0x76, 0x03, 0x01, |
79 |
|
|
0xee, 0x21, 0x1b, 0x06, 0x03, 0x01, 0x21, 0xb5, |
80 |
|
|
0x1e, 0x4d, 0x14, 0x00, |
81 |
|
|
}; |
82 |
|
|
|
83 |
|
|
/* PCMCIA private data */ |
84 |
|
|
struct pcmcia_disk_data { |
85 |
|
|
vm_instance_t *vm; |
86 |
|
|
vm_obj_t vm_obj; |
87 |
|
|
struct vdevice dev; |
88 |
|
|
char *filename; |
89 |
|
|
int fd; |
90 |
|
|
|
91 |
|
|
/* Disk parameters (C/H/S) */ |
92 |
|
|
u_int nr_heads; |
93 |
|
|
u_int nr_cylinders; |
94 |
|
|
u_int sects_per_track; |
95 |
|
|
|
96 |
|
|
/* Current ATA command and CHS info */ |
97 |
|
|
m_uint8_t ata_cmd,ata_cmd_in_progress; |
98 |
|
|
m_uint8_t ata_status; |
99 |
|
|
|
100 |
|
|
m_uint8_t cyl_low,cyl_high; |
101 |
|
|
m_uint8_t head,sect_no; |
102 |
|
|
m_uint8_t sect_count; |
103 |
|
|
|
104 |
|
|
/* Current sector */ |
105 |
|
|
m_uint32_t sect_pos; |
106 |
|
|
|
107 |
|
|
/* Remaining sectors to read or write */ |
108 |
|
|
u_int sect_remaining; |
109 |
|
|
|
110 |
|
|
/* Callback function when data buffer is validated */ |
111 |
|
|
void (*ata_cmd_callback)(struct pcmcia_disk_data *); |
112 |
|
|
|
113 |
|
|
/* Data buffer */ |
114 |
|
|
m_uint32_t data_offset; |
115 |
|
|
u_int data_pos; |
116 |
|
|
m_uint8_t data_buffer[SECTOR_SIZE]; |
117 |
|
|
}; |
118 |
|
|
|
119 |
|
|
/* Convert a CHS reference to an LBA reference */ |
120 |
|
|
static inline m_uint32_t chs_to_lba(struct pcmcia_disk_data *d, |
121 |
|
|
u_int cyl,u_int head,u_int sect) |
122 |
|
|
{ |
123 |
|
|
return((((cyl * d->nr_heads) + head) * d->sects_per_track) + sect - 1); |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
/* Create the virtual disk */ |
127 |
|
|
static int disk_create(struct pcmcia_disk_data *d) |
128 |
|
|
{ |
129 |
|
|
off_t disk_len; |
130 |
|
|
|
131 |
|
|
if ((d->fd = open(d->filename,O_CREAT|O_RDWR,0600)) < 0) { |
132 |
|
|
perror("disk_create: open"); |
133 |
|
|
return(-1); |
134 |
|
|
} |
135 |
|
|
|
136 |
|
|
disk_len = d->nr_heads * d->nr_cylinders * d->sects_per_track * SECTOR_SIZE; |
137 |
|
|
ftruncate(d->fd,disk_len); |
138 |
|
|
return(0); |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
/* Read a sector from disk file */ |
142 |
|
|
static int disk_read_sector(struct pcmcia_disk_data *d,m_uint32_t sect, |
143 |
|
|
m_uint8_t *buffer) |
144 |
|
|
{ |
145 |
|
|
off_t disk_offset = (off_t)sect * SECTOR_SIZE; |
146 |
|
|
|
147 |
dpavlin |
4 |
#if DEBUG_READ |
148 |
|
|
vm_log(d->vm,d->dev.name,"reading sector 0x%8.8x\n",sect); |
149 |
|
|
#endif |
150 |
|
|
|
151 |
dpavlin |
1 |
if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { |
152 |
|
|
perror("read_sector: lseek"); |
153 |
|
|
return(-1); |
154 |
|
|
} |
155 |
|
|
|
156 |
|
|
if (read(d->fd,buffer,SECTOR_SIZE) != SECTOR_SIZE) { |
157 |
|
|
perror("read_sector: read"); |
158 |
|
|
return(-1); |
159 |
|
|
} |
160 |
|
|
|
161 |
|
|
return(0); |
162 |
|
|
} |
163 |
|
|
|
164 |
|
|
/* Write a sector to disk file */ |
165 |
|
|
static int disk_write_sector(struct pcmcia_disk_data *d,m_uint32_t sect, |
166 |
|
|
m_uint8_t *buffer) |
167 |
|
|
{ |
168 |
|
|
off_t disk_offset = (off_t)sect * SECTOR_SIZE; |
169 |
|
|
|
170 |
dpavlin |
4 |
#if DEBUG_WRITE |
171 |
|
|
vm_log(d->vm,d->dev.name,"writing sector 0x%8.8x\n",sect); |
172 |
|
|
#endif |
173 |
|
|
|
174 |
dpavlin |
1 |
if (lseek(d->fd,disk_offset,SEEK_SET) == -1) { |
175 |
|
|
perror("write_sector: lseek"); |
176 |
|
|
return(-1); |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
if (write(d->fd,buffer,SECTOR_SIZE) != SECTOR_SIZE) { |
180 |
|
|
perror("write_sector: write"); |
181 |
|
|
return(-1); |
182 |
|
|
} |
183 |
|
|
|
184 |
|
|
return(0); |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
/* Identify PCMCIA device (ATA command 0xEC) */ |
188 |
|
|
static void ata_identify_device(struct pcmcia_disk_data *d) |
189 |
|
|
{ |
190 |
|
|
m_uint8_t *p = d->data_buffer; |
191 |
|
|
m_uint32_t sect_count; |
192 |
|
|
|
193 |
|
|
sect_count = d->nr_heads * d->nr_cylinders * d->sects_per_track; |
194 |
|
|
|
195 |
|
|
/* Clear all fields (for safety) */ |
196 |
dpavlin |
4 |
memset(p,0x00,SECTOR_SIZE); |
197 |
dpavlin |
1 |
|
198 |
|
|
/* Word 0: General Configuration */ |
199 |
|
|
p[0] = 0x8a; |
200 |
|
|
p[1] = 0x84; |
201 |
|
|
|
202 |
|
|
/* Word 1: Default number of cylinders */ |
203 |
|
|
p[2] = d->nr_cylinders & 0xFF; |
204 |
|
|
p[3] = (d->nr_cylinders >> 8) & 0xFF; |
205 |
|
|
|
206 |
|
|
/* Word 3: Default number of heads */ |
207 |
|
|
p[6] = d->nr_heads; |
208 |
|
|
|
209 |
|
|
/* Word 6: Default number of sectors per track */ |
210 |
|
|
p[12] = d->sects_per_track; |
211 |
|
|
|
212 |
|
|
/* Word 7: Number of sectors per card (MSW) */ |
213 |
|
|
p[14] = (sect_count >> 16) & 0xFF; |
214 |
|
|
p[15] = (sect_count >> 24); |
215 |
|
|
|
216 |
|
|
/* Word 8: Number of sectors per card (LSW) */ |
217 |
|
|
p[16] = sect_count & 0xFF; |
218 |
|
|
p[17] = (sect_count >> 8) & 0xFF; |
219 |
|
|
|
220 |
|
|
/* Word 22: ECC count */ |
221 |
|
|
p[44] = 0x04; |
222 |
|
|
|
223 |
|
|
/* Word 53: Translation parameters valid */ |
224 |
|
|
p[106] = 0x3; |
225 |
|
|
|
226 |
|
|
/* Word 54: Current number of cylinders */ |
227 |
|
|
p[108] = d->nr_cylinders & 0xFF; |
228 |
|
|
p[109] = (d->nr_cylinders >> 8) & 0xFF; |
229 |
|
|
|
230 |
|
|
/* Word 55: Current number of heads */ |
231 |
|
|
p[110] = d->nr_heads; |
232 |
|
|
|
233 |
|
|
/* Word 56: Current number of sectors per track */ |
234 |
|
|
p[112] = d->sects_per_track; |
235 |
|
|
|
236 |
dpavlin |
4 |
/* Word 57/58: Current of sectors per card (LSW/MSW) */ |
237 |
|
|
p[114] = sect_count & 0xFF; |
238 |
|
|
p[115] = (sect_count >> 8) & 0xFF; |
239 |
dpavlin |
1 |
|
240 |
dpavlin |
4 |
p[116] = (sect_count >> 16) & 0xFF; |
241 |
|
|
p[117] = (sect_count >> 24); |
242 |
|
|
|
243 |
|
|
#if 0 |
244 |
dpavlin |
1 |
/* Word 60/61: Total sectors addressable in LBA mode (MSW/LSW) */ |
245 |
|
|
p[120] = (sect_count >> 16) & 0xFF; |
246 |
|
|
p[121] = (sect_count >> 24); |
247 |
|
|
p[122] = sect_count & 0xFF; |
248 |
|
|
p[123] = (sect_count >> 8) & 0xFF; |
249 |
dpavlin |
4 |
#endif |
250 |
dpavlin |
1 |
} |
251 |
|
|
|
252 |
|
|
/* Set sector position */ |
253 |
|
|
static void ata_set_sect_pos(struct pcmcia_disk_data *d) |
254 |
|
|
{ |
255 |
|
|
u_int cyl; |
256 |
|
|
|
257 |
|
|
if (d->head & ATA_DH_LBA) { |
258 |
dpavlin |
4 |
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 |
|
|
#if DEBUG_ATA |
264 |
|
|
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 |
dpavlin |
1 |
} |
278 |
|
|
} |
279 |
|
|
|
280 |
|
|
/* ATA device identifier callback */ |
281 |
|
|
static void ata_cmd_ident_device_callback(struct pcmcia_disk_data *d) |
282 |
|
|
{ |
283 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; |
284 |
|
|
} |
285 |
|
|
|
286 |
|
|
/* ATA read sector callback */ |
287 |
|
|
static void ata_cmd_read_callback(struct pcmcia_disk_data *d) |
288 |
|
|
{ |
289 |
|
|
d->sect_remaining--; |
290 |
|
|
|
291 |
|
|
if (!d->sect_remaining) { |
292 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; |
293 |
|
|
return; |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
/* Read the next sector */ |
297 |
|
|
d->sect_pos++; |
298 |
|
|
disk_read_sector(d,d->sect_pos,d->data_buffer); |
299 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; |
300 |
|
|
} |
301 |
|
|
|
302 |
|
|
/* ATA write sector callback */ |
303 |
|
|
static void ata_cmd_write_callback(struct pcmcia_disk_data *d) |
304 |
|
|
{ |
305 |
|
|
/* Write the sector */ |
306 |
|
|
disk_write_sector(d,d->sect_pos,d->data_buffer); |
307 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; |
308 |
|
|
d->sect_pos++; |
309 |
|
|
|
310 |
|
|
d->sect_remaining--; |
311 |
|
|
|
312 |
|
|
if (!d->sect_remaining) { |
313 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; |
314 |
|
|
} |
315 |
|
|
} |
316 |
|
|
|
317 |
|
|
/* Handle an ATA command */ |
318 |
|
|
static void ata_handle_cmd(struct pcmcia_disk_data *d) |
319 |
|
|
{ |
320 |
|
|
#if DEBUG_ATA |
321 |
|
|
vm_log(d->vm,d->dev.name,"ATA command 0x%2.2x\n",(u_int)d->ata_cmd); |
322 |
|
|
#endif |
323 |
|
|
|
324 |
dpavlin |
4 |
d->data_pos = 0; |
325 |
|
|
|
326 |
dpavlin |
1 |
switch(d->ata_cmd) { |
327 |
|
|
case ATA_CMD_IDENT_DEVICE: |
328 |
|
|
ata_identify_device(d); |
329 |
|
|
d->ata_cmd_callback = ata_cmd_ident_device_callback; |
330 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; |
331 |
|
|
break; |
332 |
|
|
|
333 |
|
|
case ATA_CMD_READ_SECTOR: |
334 |
|
|
d->sect_remaining = d->sect_count; |
335 |
|
|
|
336 |
|
|
if (!d->sect_remaining) |
337 |
|
|
d->sect_remaining = 256; |
338 |
|
|
|
339 |
|
|
ata_set_sect_pos(d); |
340 |
|
|
disk_read_sector(d,d->sect_pos,d->data_buffer); |
341 |
|
|
d->ata_cmd_callback = ata_cmd_read_callback; |
342 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; |
343 |
|
|
break; |
344 |
|
|
|
345 |
|
|
case ATA_CMD_WRITE_SECTOR: |
346 |
|
|
d->sect_remaining = d->sect_count; |
347 |
|
|
|
348 |
|
|
if (!d->sect_remaining) |
349 |
|
|
d->sect_remaining = 256; |
350 |
|
|
|
351 |
|
|
ata_set_sect_pos(d); |
352 |
|
|
d->ata_cmd_callback = ata_cmd_write_callback; |
353 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC|ATA_STATUS_DRQ; |
354 |
|
|
break; |
355 |
|
|
|
356 |
|
|
default: |
357 |
|
|
vm_log(d->vm,d->dev.name,"unhandled ATA command 0x%2.2x\n", |
358 |
|
|
(u_int)d->ata_cmd); |
359 |
|
|
} |
360 |
|
|
} |
361 |
|
|
|
362 |
|
|
/* |
363 |
dpavlin |
4 |
* dev_pcmcia_disk_access_0() |
364 |
dpavlin |
1 |
*/ |
365 |
dpavlin |
7 |
void *dev_pcmcia_disk_access_0(cpu_gen_t *cpu,struct vdevice *dev, |
366 |
dpavlin |
4 |
m_uint32_t offset,u_int op_size,u_int op_type, |
367 |
|
|
m_uint64_t *data) |
368 |
dpavlin |
1 |
{ |
369 |
|
|
struct pcmcia_disk_data *d = dev->priv_data; |
370 |
|
|
|
371 |
|
|
/* Compute the good internal offset */ |
372 |
|
|
offset = (offset >> 1) ^ 1; |
373 |
|
|
|
374 |
|
|
#if DEBUG_ACCESS |
375 |
|
|
if (op_type == MTS_READ) { |
376 |
|
|
cpu_log(cpu,d->dev.name, |
377 |
|
|
"reading offset 0x%5.5x at pc=0x%llx (size=%u)\n", |
378 |
dpavlin |
7 |
offset,cpu_get_pc(cpu),op_size); |
379 |
dpavlin |
1 |
} else { |
380 |
|
|
cpu_log(cpu,d->dev.name, |
381 |
|
|
"writing offset 0x%5.5x, data=0x%llx at pc=0x%llx (size=%u)\n", |
382 |
dpavlin |
7 |
offset,*data,cpu_get_pc(cpu),op_size); |
383 |
dpavlin |
1 |
} |
384 |
|
|
#endif |
385 |
|
|
|
386 |
|
|
/* Card Information Structure */ |
387 |
|
|
if (offset < sizeof(cis_table)) { |
388 |
|
|
if (op_type == MTS_READ) |
389 |
|
|
*data = cis_table[offset]; |
390 |
|
|
|
391 |
|
|
return NULL; |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
switch(offset) { |
395 |
|
|
case 0x102: /* Pin Replacement Register */ |
396 |
|
|
if (op_type == MTS_READ) |
397 |
|
|
*data = 0x22; |
398 |
|
|
break; |
399 |
|
|
|
400 |
|
|
case 0x80001: /* Sector Count + Sector no */ |
401 |
|
|
if (op_type == MTS_READ) { |
402 |
|
|
*data = (d->sect_no << 8) + d->sect_count; |
403 |
|
|
} else { |
404 |
|
|
d->sect_no = *data >> 8; |
405 |
|
|
d->sect_count = *data & 0xFF; |
406 |
|
|
} |
407 |
|
|
break; |
408 |
|
|
|
409 |
|
|
case 0x80002: /* Cylinder Low + Cylinder High */ |
410 |
|
|
if (op_type == MTS_READ) { |
411 |
|
|
*data = (d->cyl_high << 8) + d->cyl_low; |
412 |
|
|
} else { |
413 |
|
|
d->cyl_high = *data >> 8; |
414 |
|
|
d->cyl_low = *data & 0xFF; |
415 |
|
|
} |
416 |
|
|
break; |
417 |
|
|
|
418 |
|
|
case 0x80003: /* Select Card/Head + Status/Command register */ |
419 |
|
|
if (op_type == MTS_READ) |
420 |
|
|
*data = (d->ata_status << 8) + d->head; |
421 |
|
|
else { |
422 |
|
|
d->ata_cmd = *data >> 8; |
423 |
dpavlin |
4 |
d->head = *data; |
424 |
dpavlin |
1 |
ata_handle_cmd(d); |
425 |
|
|
} |
426 |
|
|
break; |
427 |
|
|
|
428 |
|
|
default: |
429 |
|
|
/* Data buffer access ? */ |
430 |
|
|
if ((offset >= d->data_offset) && |
431 |
|
|
(offset < d->data_offset + (SECTOR_SIZE/2))) |
432 |
|
|
{ |
433 |
|
|
if (op_type == MTS_READ) { |
434 |
dpavlin |
4 |
*data = d->data_buffer[(d->data_pos << 1)]; |
435 |
|
|
*data += d->data_buffer[(d->data_pos << 1)+1] << 8; |
436 |
dpavlin |
1 |
} else { |
437 |
dpavlin |
4 |
d->data_buffer[(d->data_pos << 1)] = *data & 0xFF; |
438 |
|
|
d->data_buffer[(d->data_pos << 1)+1] = *data >> 8; |
439 |
dpavlin |
1 |
} |
440 |
dpavlin |
4 |
|
441 |
|
|
d->data_pos++; |
442 |
dpavlin |
1 |
|
443 |
dpavlin |
4 |
/* Buffer complete: call the callback function */ |
444 |
|
|
if (d->data_pos == (SECTOR_SIZE/2)) { |
445 |
|
|
d->data_pos = 0; |
446 |
|
|
|
447 |
|
|
if (d->ata_cmd_callback) |
448 |
|
|
d->ata_cmd_callback(d); |
449 |
|
|
} |
450 |
|
|
} |
451 |
|
|
} |
452 |
dpavlin |
1 |
|
453 |
dpavlin |
4 |
return NULL; |
454 |
|
|
} |
455 |
dpavlin |
1 |
|
456 |
dpavlin |
4 |
/* |
457 |
|
|
* dev_pcmcia_disk_access_1() |
458 |
|
|
*/ |
459 |
dpavlin |
7 |
void *dev_pcmcia_disk_access_1(cpu_gen_t *cpu,struct vdevice *dev, |
460 |
dpavlin |
4 |
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 |
dpavlin |
1 |
} |
488 |
dpavlin |
4 |
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 |
dpavlin |
1 |
} |
532 |
|
|
|
533 |
|
|
return NULL; |
534 |
|
|
} |
535 |
|
|
|
536 |
|
|
/* Shutdown a PCMCIA disk device */ |
537 |
|
|
void dev_pcmcia_disk_shutdown(vm_instance_t *vm,struct pcmcia_disk_data *d) |
538 |
|
|
{ |
539 |
|
|
if (d != NULL) { |
540 |
|
|
/* Remove the device */ |
541 |
|
|
dev_remove(vm,&d->dev); |
542 |
|
|
|
543 |
|
|
/* Close disk file */ |
544 |
|
|
if (d->fd != -1) close(d->fd); |
545 |
|
|
|
546 |
|
|
/* Free filename */ |
547 |
|
|
free(d->filename); |
548 |
|
|
|
549 |
|
|
/* Free the structure itself */ |
550 |
|
|
free(d); |
551 |
|
|
} |
552 |
|
|
} |
553 |
|
|
|
554 |
|
|
/* Initialize a PCMCIA disk */ |
555 |
|
|
vm_obj_t *dev_pcmcia_disk_init(vm_instance_t *vm,char *name, |
556 |
|
|
m_uint64_t paddr,m_uint32_t len, |
557 |
dpavlin |
4 |
u_int disk_size,int mode) |
558 |
dpavlin |
1 |
{ |
559 |
|
|
struct pcmcia_disk_data *d; |
560 |
|
|
m_uint32_t tot_sect; |
561 |
|
|
|
562 |
|
|
/* allocate the private data structure */ |
563 |
|
|
if (!(d = malloc(sizeof(*d)))) { |
564 |
|
|
fprintf(stderr,"PCMCIA: unable to create disk device '%s'.\n",name); |
565 |
|
|
return NULL; |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
memset(d,0,sizeof(*d)); |
569 |
|
|
vm_object_init(&d->vm_obj); |
570 |
|
|
d->vm = vm; |
571 |
|
|
d->vm_obj.name = name; |
572 |
|
|
d->vm_obj.data = d; |
573 |
|
|
d->vm_obj.shutdown = (vm_shutdown_t)dev_pcmcia_disk_shutdown; |
574 |
|
|
d->fd = -1; |
575 |
|
|
|
576 |
|
|
if (!(d->filename = vm_build_filename(vm,name))) { |
577 |
|
|
fprintf(stderr,"PCMCIA: unable to create filename.\n"); |
578 |
|
|
goto err_filename; |
579 |
|
|
} |
580 |
|
|
|
581 |
|
|
/* Data buffer offset in mapped memory */ |
582 |
|
|
d->data_offset = 0x80200; |
583 |
|
|
d->ata_status = ATA_STATUS_RDY|ATA_STATUS_DSC; |
584 |
|
|
|
585 |
|
|
/* Compute the number of cylinders given a disk size in Mb */ |
586 |
|
|
tot_sect = ((m_uint64_t)disk_size * 1048576) / SECTOR_SIZE; |
587 |
|
|
|
588 |
|
|
d->nr_heads = DISK_NR_HEADS; |
589 |
|
|
d->sects_per_track = DISK_SECTS_PER_TRACK; |
590 |
|
|
d->nr_cylinders = tot_sect / (d->nr_heads * d->sects_per_track); |
591 |
|
|
|
592 |
|
|
vm_log(vm,name,"C/H/S settings = %u/%u/%u\n", |
593 |
|
|
d->nr_cylinders,d->nr_heads,d->sects_per_track); |
594 |
|
|
|
595 |
|
|
/* Create the disk file */ |
596 |
|
|
if (disk_create(d) == -1) |
597 |
|
|
goto err_disk_create; |
598 |
|
|
|
599 |
|
|
dev_init(&d->dev); |
600 |
|
|
d->dev.name = name; |
601 |
|
|
d->dev.priv_data = d; |
602 |
|
|
d->dev.phys_addr = paddr; |
603 |
|
|
d->dev.phys_len = len; |
604 |
|
|
d->dev.flags = VDEVICE_FLAG_CACHING; |
605 |
|
|
|
606 |
dpavlin |
4 |
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 |
dpavlin |
1 |
/* Map this device to the VM */ |
612 |
|
|
vm_bind_device(vm,&d->dev); |
613 |
|
|
vm_object_add(vm,&d->vm_obj); |
614 |
|
|
return(&d->vm_obj); |
615 |
|
|
|
616 |
|
|
err_disk_create: |
617 |
|
|
free(d->filename); |
618 |
|
|
err_filename: |
619 |
|
|
free(d); |
620 |
|
|
return NULL; |
621 |
|
|
} |
622 |
dpavlin |
7 |
|
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 |
|
|
} |