]> git.proxmox.com Git - qemu.git/blame - hw/esp.c
PCI SCSI HBA emulation.
[qemu.git] / hw / esp.c
CommitLineData
6f7e9aec
FB
1/*
2 * QEMU ESP emulation
3 *
4e9aec74 4 * Copyright (c) 2005-2006 Fabrice Bellard
6f7e9aec
FB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24#include "vl.h"
25
26/* debug ESP card */
2f275b8f 27//#define DEBUG_ESP
6f7e9aec
FB
28
29#ifdef DEBUG_ESP
30#define DPRINTF(fmt, args...) \
31do { printf("ESP: " fmt , ##args); } while (0)
9e61bde5
FB
32#define pic_set_irq(irq, level) \
33do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0)
6f7e9aec
FB
34#else
35#define DPRINTF(fmt, args...)
36#endif
37
38#define ESPDMA_REGS 4
39#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
40#define ESP_MAXREG 0x3f
2e5d83bb 41#define TI_BUFSZ 32
4f6200f0 42#define DMA_VER 0xa0000000
9e61bde5
FB
43#define DMA_INTR 1
44#define DMA_INTREN 0x10
2e5d83bb 45#define DMA_WRITE_MEM 0x100
4f6200f0 46#define DMA_LOADED 0x04000000
4e9aec74 47typedef struct ESPState ESPState;
6f7e9aec 48
4e9aec74 49struct ESPState {
6f7e9aec 50 BlockDriverState **bd;
2f275b8f
FB
51 uint8_t rregs[ESP_MAXREG];
52 uint8_t wregs[ESP_MAXREG];
6f7e9aec
FB
53 int irq;
54 uint32_t espdmaregs[ESPDMA_REGS];
2f275b8f 55 uint32_t ti_size;
4f6200f0 56 uint32_t ti_rptr, ti_wptr;
4f6200f0 57 uint8_t ti_buf[TI_BUFSZ];
0fc5c15a 58 int sense;
4f6200f0 59 int dma;
2e5d83bb
PB
60 SCSIDevice *scsi_dev[MAX_DISKS];
61 SCSIDevice *current_dev;
4e9aec74 62};
6f7e9aec 63
2f275b8f
FB
64#define STAT_DO 0x00
65#define STAT_DI 0x01
66#define STAT_CD 0x02
67#define STAT_ST 0x03
68#define STAT_MI 0x06
69#define STAT_MO 0x07
70
71#define STAT_TC 0x10
72#define STAT_IN 0x80
73
74#define INTR_FC 0x08
75#define INTR_BS 0x10
76#define INTR_DC 0x20
9e61bde5 77#define INTR_RST 0x80
2f275b8f
FB
78
79#define SEQ_0 0x0
80#define SEQ_CD 0x4
81
82static void handle_satn(ESPState *s)
83{
84 uint8_t buf[32];
85 uint32_t dmaptr, dmalen;
2f275b8f 86 int target;
2e5d83bb 87 int32_t datalen;
0fc5c15a 88 int lun;
2f275b8f 89
2f275b8f 90 dmalen = s->wregs[0] | (s->wregs[1] << 8);
4f6200f0
FB
91 target = s->wregs[4] & 7;
92 DPRINTF("Select with ATN len %d target %d\n", dmalen, target);
93 if (s->dma) {
94 dmaptr = iommu_translate(s->espdmaregs[1]);
2e5d83bb
PB
95 DPRINTF("DMA Direction: %c, addr 0x%8.8x\n",
96 s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', dmaptr);
4f6200f0
FB
97 cpu_physical_memory_read(dmaptr, buf, dmalen);
98 } else {
99 buf[0] = 0;
100 memcpy(&buf[1], s->ti_buf, dmalen);
101 dmalen++;
102 }
0fc5c15a
PB
103 DPRINTF("busid 0x%x\n", buf[0]);
104 lun = buf[0] & 7;
2e5d83bb 105
2f275b8f 106 s->ti_size = 0;
4f6200f0
FB
107 s->ti_rptr = 0;
108 s->ti_wptr = 0;
2f275b8f 109
2e5d83bb
PB
110 if (target >= 4 || !s->scsi_dev[target]) {
111 // No such drive
2f275b8f
FB
112 s->rregs[4] = STAT_IN;
113 s->rregs[5] = INTR_DC;
114 s->rregs[6] = SEQ_0;
9e61bde5 115 s->espdmaregs[0] |= DMA_INTR;
2f275b8f
FB
116 pic_set_irq(s->irq, 1);
117 return;
118 }
2e5d83bb 119 s->current_dev = s->scsi_dev[target];
0fc5c15a 120 datalen = scsi_send_command(s->current_dev, 0, &buf[1], lun);
2e5d83bb
PB
121 if (datalen == 0) {
122 s->ti_size = 0;
123 } else {
124 s->rregs[4] = STAT_IN | STAT_TC;
125 if (datalen > 0) {
126 s->rregs[4] |= STAT_DI;
127 s->ti_size = datalen;
128 } else {
129 s->rregs[4] |= STAT_DO;
130 s->ti_size = -datalen;
b9788fc4 131 }
2f275b8f 132 }
2f275b8f
FB
133 s->rregs[5] = INTR_BS | INTR_FC;
134 s->rregs[6] = SEQ_CD;
9e61bde5 135 s->espdmaregs[0] |= DMA_INTR;
2f275b8f
FB
136 pic_set_irq(s->irq, 1);
137}
138
0fc5c15a 139static void write_response(ESPState *s)
2f275b8f 140{
db59203d 141 uint32_t dmaptr;
2f275b8f 142
0fc5c15a
PB
143 DPRINTF("Transfer status (sense=%d)\n", s->sense);
144 s->ti_buf[0] = s->sense;
145 s->ti_buf[1] = 0;
4f6200f0
FB
146 if (s->dma) {
147 dmaptr = iommu_translate(s->espdmaregs[1]);
2e5d83bb
PB
148 DPRINTF("DMA Direction: %c\n",
149 s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r');
0fc5c15a 150 cpu_physical_memory_write(dmaptr, s->ti_buf, 2);
4f6200f0
FB
151 s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
152 s->rregs[5] = INTR_BS | INTR_FC;
153 s->rregs[6] = SEQ_CD;
4f6200f0 154 } else {
0fc5c15a 155 s->ti_size = 2;
4f6200f0
FB
156 s->ti_rptr = 0;
157 s->ti_wptr = 0;
0fc5c15a 158 s->rregs[7] = 2;
4f6200f0 159 }
9e61bde5 160 s->espdmaregs[0] |= DMA_INTR;
2f275b8f
FB
161 pic_set_irq(s->irq, 1);
162
163}
4f6200f0 164
0fc5c15a 165static void esp_command_complete(void *opaque, uint32_t tag, int sense)
2e5d83bb
PB
166{
167 ESPState *s = (ESPState *)opaque;
168
169 DPRINTF("SCSI Command complete\n");
170 if (s->ti_size != 0)
171 DPRINTF("SCSI command completed unexpectedly\n");
172 s->ti_size = 0;
0fc5c15a 173 if (sense)
2e5d83bb 174 DPRINTF("Command failed\n");
0fc5c15a 175 s->sense = sense;
2e5d83bb
PB
176 s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
177}
178
2f275b8f
FB
179static void handle_ti(ESPState *s)
180{
db59203d 181 uint32_t dmaptr, dmalen, minlen, len, from, to;
2f275b8f 182 unsigned int i;
2e5d83bb
PB
183 int to_device;
184 uint8_t buf[TARGET_PAGE_SIZE];
2f275b8f 185
2f275b8f 186 dmalen = s->wregs[0] | (s->wregs[1] << 8);
db59203d
PB
187 if (dmalen==0) {
188 dmalen=0x10000;
189 }
190
191 minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
192 DPRINTF("Transfer Information len %d\n", minlen);
4f6200f0
FB
193 if (s->dma) {
194 dmaptr = iommu_translate(s->espdmaregs[1]);
2e5d83bb
PB
195 /* Check if the transfer writes to to reads from the device. */
196 to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
197 DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x\n",
198 to_device ? 'r': 'w', dmaptr, s->ti_size);
db59203d
PB
199 from = s->espdmaregs[1];
200 to = from + minlen;
201 for (i = 0; i < minlen; i += len, from += len) {
4f6200f0 202 dmaptr = iommu_translate(s->espdmaregs[1] + i);
db59203d
PB
203 if ((from & TARGET_PAGE_MASK) != (to & TARGET_PAGE_MASK)) {
204 len = TARGET_PAGE_SIZE - (from & ~TARGET_PAGE_MASK);
205 } else {
206 len = to - from;
207 }
208 DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
2e5d83bb
PB
209 s->ti_size -= len;
210 if (to_device) {
211 cpu_physical_memory_read(dmaptr, buf, len);
212 scsi_write_data(s->current_dev, buf, len);
213 } else {
214 scsi_read_data(s->current_dev, buf, len);
215 cpu_physical_memory_write(dmaptr, buf, len);
216 }
4f6200f0 217 }
2e5d83bb
PB
218 if (s->ti_size) {
219 s->rregs[4] = STAT_IN | STAT_TC | (to_device ? STAT_DO : STAT_DI);
4e9aec74 220 }
db59203d 221 s->rregs[5] = INTR_BS;
4f6200f0 222 s->rregs[6] = 0;
db59203d 223 s->rregs[7] = 0;
9e61bde5 224 s->espdmaregs[0] |= DMA_INTR;
4f6200f0 225 }
2f275b8f
FB
226 pic_set_irq(s->irq, 1);
227}
228
6f7e9aec
FB
229static void esp_reset(void *opaque)
230{
231 ESPState *s = opaque;
2f275b8f 232 memset(s->rregs, 0, ESP_MAXREG);
4e9aec74 233 memset(s->wregs, 0, ESP_MAXREG);
2f275b8f 234 s->rregs[0x0e] = 0x4; // Indicate fas100a
6f7e9aec 235 memset(s->espdmaregs, 0, ESPDMA_REGS * 4);
4e9aec74
PB
236 s->ti_size = 0;
237 s->ti_rptr = 0;
238 s->ti_wptr = 0;
4e9aec74 239 s->dma = 0;
6f7e9aec
FB
240}
241
242static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
243{
244 ESPState *s = opaque;
245 uint32_t saddr;
246
247 saddr = (addr & ESP_MAXREG) >> 2;
9e61bde5 248 DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
6f7e9aec 249 switch (saddr) {
4f6200f0
FB
250 case 2:
251 // FIFO
252 if (s->ti_size > 0) {
253 s->ti_size--;
2e5d83bb
PB
254 if ((s->rregs[4] & 6) == 0) {
255 /* Data in/out. */
256 scsi_read_data(s->current_dev, &s->rregs[2], 0);
257 } else {
258 s->rregs[2] = s->ti_buf[s->ti_rptr++];
259 }
4f6200f0
FB
260 pic_set_irq(s->irq, 1);
261 }
262 if (s->ti_size == 0) {
263 s->ti_rptr = 0;
264 s->ti_wptr = 0;
265 }
266 break;
9e61bde5
FB
267 case 5:
268 // interrupt
269 // Clear status bits except TC
270 s->rregs[4] &= STAT_TC;
271 pic_set_irq(s->irq, 0);
272 s->espdmaregs[0] &= ~DMA_INTR;
273 break;
6f7e9aec
FB
274 default:
275 break;
276 }
2f275b8f 277 return s->rregs[saddr];
6f7e9aec
FB
278}
279
280static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
281{
282 ESPState *s = opaque;
283 uint32_t saddr;
284
285 saddr = (addr & ESP_MAXREG) >> 2;
2f275b8f 286 DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val);
6f7e9aec 287 switch (saddr) {
4f6200f0
FB
288 case 0:
289 case 1:
290 s->rregs[saddr] = val;
291 break;
292 case 2:
293 // FIFO
2e5d83bb
PB
294 if ((s->rregs[4] & 6) == 0) {
295 uint8_t buf;
296 buf = val & 0xff;
297 s->ti_size--;
298 scsi_write_data(s->current_dev, &buf, 0);
299 } else {
300 s->ti_size++;
301 s->ti_buf[s->ti_wptr++] = val & 0xff;
302 }
4f6200f0 303 break;
6f7e9aec 304 case 3:
4f6200f0 305 s->rregs[saddr] = val;
6f7e9aec 306 // Command
4f6200f0
FB
307 if (val & 0x80) {
308 s->dma = 1;
309 } else {
310 s->dma = 0;
311 }
6f7e9aec
FB
312 switch(val & 0x7f) {
313 case 0:
2f275b8f
FB
314 DPRINTF("NOP (%2.2x)\n", val);
315 break;
316 case 1:
317 DPRINTF("Flush FIFO (%2.2x)\n", val);
9e61bde5 318 //s->ti_size = 0;
2f275b8f 319 s->rregs[5] = INTR_FC;
9e61bde5 320 s->rregs[6] = 0;
6f7e9aec
FB
321 break;
322 case 2:
2f275b8f 323 DPRINTF("Chip reset (%2.2x)\n", val);
6f7e9aec
FB
324 esp_reset(s);
325 break;
326 case 3:
2f275b8f 327 DPRINTF("Bus reset (%2.2x)\n", val);
9e61bde5
FB
328 s->rregs[5] = INTR_RST;
329 if (!(s->wregs[8] & 0x40)) {
330 s->espdmaregs[0] |= DMA_INTR;
331 pic_set_irq(s->irq, 1);
332 }
2f275b8f
FB
333 break;
334 case 0x10:
335 handle_ti(s);
336 break;
337 case 0x11:
338 DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val);
0fc5c15a 339 write_response(s);
2f275b8f
FB
340 break;
341 case 0x12:
342 DPRINTF("Message Accepted (%2.2x)\n", val);
0fc5c15a 343 write_response(s);
2f275b8f
FB
344 s->rregs[5] = INTR_DC;
345 s->rregs[6] = 0;
6f7e9aec
FB
346 break;
347 case 0x1a:
2f275b8f 348 DPRINTF("Set ATN (%2.2x)\n", val);
6f7e9aec
FB
349 break;
350 case 0x42:
2f275b8f
FB
351 handle_satn(s);
352 break;
353 case 0x43:
354 DPRINTF("Set ATN & stop (%2.2x)\n", val);
355 handle_satn(s);
356 break;
357 default:
4f6200f0 358 DPRINTF("Unhandled ESP command (%2.2x)\n", val);
6f7e9aec
FB
359 break;
360 }
361 break;
362 case 4 ... 7:
6f7e9aec 363 break;
4f6200f0
FB
364 case 8:
365 s->rregs[saddr] = val;
366 break;
367 case 9 ... 10:
368 break;
9e61bde5
FB
369 case 11:
370 s->rregs[saddr] = val & 0x15;
371 break;
372 case 12 ... 15:
4f6200f0
FB
373 s->rregs[saddr] = val;
374 break;
6f7e9aec 375 default:
6f7e9aec
FB
376 break;
377 }
2f275b8f 378 s->wregs[saddr] = val;
6f7e9aec
FB
379}
380
381static CPUReadMemoryFunc *esp_mem_read[3] = {
382 esp_mem_readb,
383 esp_mem_readb,
384 esp_mem_readb,
385};
386
387static CPUWriteMemoryFunc *esp_mem_write[3] = {
388 esp_mem_writeb,
389 esp_mem_writeb,
390 esp_mem_writeb,
391};
392
393static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr)
394{
395 ESPState *s = opaque;
396 uint32_t saddr;
397
398 saddr = (addr & ESPDMA_MAXADDR) >> 2;
4f6200f0
FB
399 DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]);
400
6f7e9aec
FB
401 return s->espdmaregs[saddr];
402}
403
404static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
405{
406 ESPState *s = opaque;
407 uint32_t saddr;
408
409 saddr = (addr & ESPDMA_MAXADDR) >> 2;
4f6200f0 410 DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val);
2f275b8f
FB
411 switch (saddr) {
412 case 0:
9e61bde5 413 if (!(val & DMA_INTREN))
2f275b8f 414 pic_set_irq(s->irq, 0);
4f6200f0
FB
415 if (val & 0x80) {
416 esp_reset(s);
417 } else if (val & 0x40) {
418 val &= ~0x40;
419 } else if (val == 0)
420 val = 0x40;
421 val &= 0x0fffffff;
422 val |= DMA_VER;
2f275b8f 423 break;
4f6200f0 424 case 1:
e4d165c2 425 s->espdmaregs[0] |= DMA_LOADED;
4f6200f0 426 break;
2f275b8f
FB
427 default:
428 break;
429 }
6f7e9aec
FB
430 s->espdmaregs[saddr] = val;
431}
432
433static CPUReadMemoryFunc *espdma_mem_read[3] = {
434 espdma_mem_readl,
435 espdma_mem_readl,
436 espdma_mem_readl,
437};
438
439static CPUWriteMemoryFunc *espdma_mem_write[3] = {
440 espdma_mem_writel,
441 espdma_mem_writel,
442 espdma_mem_writel,
443};
444
445static void esp_save(QEMUFile *f, void *opaque)
446{
447 ESPState *s = opaque;
2f275b8f
FB
448 unsigned int i;
449
450 qemu_put_buffer(f, s->rregs, ESP_MAXREG);
451 qemu_put_buffer(f, s->wregs, ESP_MAXREG);
452 qemu_put_be32s(f, &s->irq);
453 for (i = 0; i < ESPDMA_REGS; i++)
454 qemu_put_be32s(f, &s->espdmaregs[i]);
4f6200f0
FB
455 qemu_put_be32s(f, &s->ti_size);
456 qemu_put_be32s(f, &s->ti_rptr);
457 qemu_put_be32s(f, &s->ti_wptr);
4f6200f0
FB
458 qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
459 qemu_put_be32s(f, &s->dma);
6f7e9aec
FB
460}
461
462static int esp_load(QEMUFile *f, void *opaque, int version_id)
463{
464 ESPState *s = opaque;
2f275b8f 465 unsigned int i;
6f7e9aec
FB
466
467 if (version_id != 1)
468 return -EINVAL;
469
2f275b8f
FB
470 qemu_get_buffer(f, s->rregs, ESP_MAXREG);
471 qemu_get_buffer(f, s->wregs, ESP_MAXREG);
472 qemu_get_be32s(f, &s->irq);
473 for (i = 0; i < ESPDMA_REGS; i++)
474 qemu_get_be32s(f, &s->espdmaregs[i]);
4f6200f0
FB
475 qemu_get_be32s(f, &s->ti_size);
476 qemu_get_be32s(f, &s->ti_rptr);
477 qemu_get_be32s(f, &s->ti_wptr);
4f6200f0
FB
478 qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
479 qemu_get_be32s(f, &s->dma);
2f275b8f 480
6f7e9aec
FB
481 return 0;
482}
483
484void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr)
485{
486 ESPState *s;
487 int esp_io_memory, espdma_io_memory;
2e5d83bb 488 int i;
6f7e9aec
FB
489
490 s = qemu_mallocz(sizeof(ESPState));
491 if (!s)
492 return;
493
494 s->bd = bd;
495 s->irq = irq;
496
497 esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s);
498 cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory);
499
500 espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s);
501 cpu_register_physical_memory(espdaddr, 16, espdma_io_memory);
502
503 esp_reset(s);
504
505 register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
506 qemu_register_reset(esp_reset, s);
2e5d83bb
PB
507 for (i = 0; i < MAX_DISKS; i++) {
508 if (bs_table[i]) {
509 s->scsi_dev[i] =
510 scsi_disk_init(bs_table[i], esp_command_complete, s);
511 }
512 }
6f7e9aec
FB
513}
514