source: trunk/packages/xen-3.1/xen-3.1/tools/ioemu/hw/esp.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 14.5 KB
Line 
1/*
2 * QEMU ESP emulation
3 *
4 * Copyright (c) 2005-2006 Fabrice Bellard
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 */
27//#define DEBUG_ESP
28
29#ifdef DEBUG_ESP
30#define DPRINTF(fmt, args...) \
31do { printf("ESP: " fmt , ##args); } while (0)
32#define pic_set_irq(irq, level) \
33do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level));} while (0)
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
41#define TI_BUFSZ 32
42#define DMA_VER 0xa0000000
43#define DMA_INTR 1
44#define DMA_INTREN 0x10
45#define DMA_WRITE_MEM 0x100
46#define DMA_LOADED 0x04000000
47typedef struct ESPState ESPState;
48
49struct ESPState {
50    BlockDriverState **bd;
51    uint8_t rregs[ESP_MAXREG];
52    uint8_t wregs[ESP_MAXREG];
53    int irq;
54    uint32_t espdmaregs[ESPDMA_REGS];
55    uint32_t ti_size;
56    uint32_t ti_rptr, ti_wptr;
57    uint8_t ti_buf[TI_BUFSZ];
58    int sense;
59    int dma;
60    SCSIDevice *scsi_dev[MAX_DISKS];
61    SCSIDevice *current_dev;
62    uint8_t cmdbuf[TI_BUFSZ];
63    int cmdlen;
64    int do_cmd;
65};
66
67#define STAT_DO 0x00
68#define STAT_DI 0x01
69#define STAT_CD 0x02
70#define STAT_ST 0x03
71#define STAT_MI 0x06
72#define STAT_MO 0x07
73
74#define STAT_TC 0x10
75#define STAT_IN 0x80
76
77#define INTR_FC 0x08
78#define INTR_BS 0x10
79#define INTR_DC 0x20
80#define INTR_RST 0x80
81
82#define SEQ_0 0x0
83#define SEQ_CD 0x4
84
85static int get_cmd(ESPState *s, uint8_t *buf)
86{
87    uint32_t dmaptr, dmalen;
88    int target;
89
90    dmalen = s->wregs[0] | (s->wregs[1] << 8);
91    target = s->wregs[4] & 7;
92    DPRINTF("get_cmd: len %d target %d\n", dmalen, target);
93    if (s->dma) {
94        dmaptr = iommu_translate(s->espdmaregs[1]);
95        DPRINTF("DMA Direction: %c, addr 0x%8.8x\n",
96                s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', dmaptr);
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    }
103
104    s->ti_size = 0;
105    s->ti_rptr = 0;
106    s->ti_wptr = 0;
107
108    if (target >= 4 || !s->scsi_dev[target]) {
109        // No such drive
110        s->rregs[4] = STAT_IN;
111        s->rregs[5] = INTR_DC;
112        s->rregs[6] = SEQ_0;
113        s->espdmaregs[0] |= DMA_INTR;
114        pic_set_irq(s->irq, 1);
115        return 0;
116    }
117    s->current_dev = s->scsi_dev[target];
118    return dmalen;
119}
120
121static void do_cmd(ESPState *s, uint8_t *buf)
122{
123    int32_t datalen;
124    int lun;
125
126    DPRINTF("do_cmd: busid 0x%x\n", buf[0]);
127    lun = buf[0] & 7;
128    datalen = scsi_send_command(s->current_dev, 0, &buf[1], lun);
129    if (datalen == 0) {
130        s->ti_size = 0;
131    } else {
132        s->rregs[4] = STAT_IN | STAT_TC;
133        if (datalen > 0) {
134            s->rregs[4] |= STAT_DI;
135            s->ti_size = datalen;
136        } else {
137            s->rregs[4] |= STAT_DO;
138            s->ti_size = -datalen;
139        }
140    }
141    s->rregs[5] = INTR_BS | INTR_FC;
142    s->rregs[6] = SEQ_CD;
143    s->espdmaregs[0] |= DMA_INTR;
144    pic_set_irq(s->irq, 1);
145}
146
147static void handle_satn(ESPState *s)
148{
149    uint8_t buf[32];
150    int len;
151
152    len = get_cmd(s, buf);
153    if (len)
154        do_cmd(s, buf);
155}
156
157static void handle_satn_stop(ESPState *s)
158{
159    s->cmdlen = get_cmd(s, s->cmdbuf);
160    if (s->cmdlen) {
161        DPRINTF("Set ATN & Stop: cmdlen %d\n", s->cmdlen);
162        s->do_cmd = 1;
163        s->espdmaregs[1] += s->cmdlen;
164        s->rregs[4] = STAT_IN | STAT_TC | STAT_CD;
165        s->rregs[5] = INTR_BS | INTR_FC;
166        s->rregs[6] = SEQ_CD;
167        s->espdmaregs[0] |= DMA_INTR;
168        pic_set_irq(s->irq, 1);
169    }
170}
171
172static void write_response(ESPState *s)
173{
174    uint32_t dmaptr;
175
176    DPRINTF("Transfer status (sense=%d)\n", s->sense);
177    s->ti_buf[0] = s->sense;
178    s->ti_buf[1] = 0;
179    if (s->dma) {
180        dmaptr = iommu_translate(s->espdmaregs[1]);
181        DPRINTF("DMA Direction: %c\n",
182                s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r');
183        cpu_physical_memory_write(dmaptr, s->ti_buf, 2);
184        s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
185        s->rregs[5] = INTR_BS | INTR_FC;
186        s->rregs[6] = SEQ_CD;
187    } else {
188        s->ti_size = 2;
189        s->ti_rptr = 0;
190        s->ti_wptr = 0;
191        s->rregs[7] = 2;
192    }
193    s->espdmaregs[0] |= DMA_INTR;
194    pic_set_irq(s->irq, 1);
195
196}
197
198static void esp_command_complete(void *opaque, uint32_t tag, int sense)
199{
200    ESPState *s = (ESPState *)opaque;
201
202    DPRINTF("SCSI Command complete\n");
203    if (s->ti_size != 0)
204        DPRINTF("SCSI command completed unexpectedly\n");
205    s->ti_size = 0;
206    if (sense)
207        DPRINTF("Command failed\n");
208    s->sense = sense;
209    s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
210}
211
212static void handle_ti(ESPState *s)
213{
214    uint32_t dmaptr, dmalen, minlen, len, from, to;
215    unsigned int i;
216    int to_device;
217    uint8_t buf[TARGET_PAGE_SIZE];
218
219    dmalen = s->wregs[0] | (s->wregs[1] << 8);
220    if (dmalen==0) {
221      dmalen=0x10000;
222    }
223
224    if (s->do_cmd)
225        minlen = (dmalen < 32) ? dmalen : 32;
226    else
227        minlen = (dmalen < s->ti_size) ? dmalen : s->ti_size;
228    DPRINTF("Transfer Information len %d\n", minlen);
229    if (s->dma) {
230        dmaptr = iommu_translate(s->espdmaregs[1]);
231        /* Check if the transfer writes to to reads from the device.  */
232        to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
233        DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x\n",
234                to_device ? 'r': 'w', dmaptr, s->ti_size);
235        from = s->espdmaregs[1];
236        to = from + minlen;
237        for (i = 0; i < minlen; i += len, from += len) {
238            dmaptr = iommu_translate(s->espdmaregs[1] + i);
239            if ((from & TARGET_PAGE_MASK) != (to & TARGET_PAGE_MASK)) {
240               len = TARGET_PAGE_SIZE - (from & ~TARGET_PAGE_MASK);
241            } else {
242               len = to - from;
243            }
244            DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
245            s->ti_size -= len;
246            if (s->do_cmd) {
247                DPRINTF("command len %d + %d\n", s->cmdlen, len);
248                cpu_physical_memory_read(dmaptr, &s->cmdbuf[s->cmdlen], len);
249                s->ti_size = 0;
250                s->cmdlen = 0;
251                s->do_cmd = 0;
252                do_cmd(s, s->cmdbuf);
253                return;
254            } else {
255                if (to_device) {
256                    cpu_physical_memory_read(dmaptr, buf, len);
257                    scsi_write_data(s->current_dev, buf, len);
258                } else {
259                    scsi_read_data(s->current_dev, buf, len);
260                    cpu_physical_memory_write(dmaptr, buf, len);
261                }
262            }
263        }
264        if (s->ti_size) {
265            s->rregs[4] = STAT_IN | STAT_TC | (to_device ? STAT_DO : STAT_DI);
266        }
267        s->rregs[5] = INTR_BS;
268        s->rregs[6] = 0;
269        s->rregs[7] = 0;
270        s->espdmaregs[0] |= DMA_INTR;
271    } else if (s->do_cmd) {
272        DPRINTF("command len %d\n", s->cmdlen);
273        s->ti_size = 0;
274        s->cmdlen = 0;
275        s->do_cmd = 0;
276        do_cmd(s, s->cmdbuf);
277        return;
278    }
279    pic_set_irq(s->irq, 1);
280}
281
282static void esp_reset(void *opaque)
283{
284    ESPState *s = opaque;
285    memset(s->rregs, 0, ESP_MAXREG);
286    memset(s->wregs, 0, ESP_MAXREG);
287    s->rregs[0x0e] = 0x4; // Indicate fas100a
288    memset(s->espdmaregs, 0, ESPDMA_REGS * 4);
289    s->ti_size = 0;
290    s->ti_rptr = 0;
291    s->ti_wptr = 0;
292    s->dma = 0;
293    s->do_cmd = 0;
294}
295
296static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
297{
298    ESPState *s = opaque;
299    uint32_t saddr;
300
301    saddr = (addr & ESP_MAXREG) >> 2;
302    DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]);
303    switch (saddr) {
304    case 2:
305        // FIFO
306        if (s->ti_size > 0) {
307            s->ti_size--;
308            if ((s->rregs[4] & 6) == 0) {
309                /* Data in/out.  */
310                scsi_read_data(s->current_dev, &s->rregs[2], 0);
311            } else {
312                s->rregs[2] = s->ti_buf[s->ti_rptr++];
313            }
314            pic_set_irq(s->irq, 1);
315        }
316        if (s->ti_size == 0) {
317            s->ti_rptr = 0;
318            s->ti_wptr = 0;
319        }
320        break;
321    case 5:
322        // interrupt
323        // Clear status bits except TC
324        s->rregs[4] &= STAT_TC;
325        pic_set_irq(s->irq, 0);
326        s->espdmaregs[0] &= ~DMA_INTR;
327        break;
328    default:
329        break;
330    }
331    return s->rregs[saddr];
332}
333
334static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
335{
336    ESPState *s = opaque;
337    uint32_t saddr;
338
339    saddr = (addr & ESP_MAXREG) >> 2;
340    DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val);
341    switch (saddr) {
342    case 0:
343    case 1:
344        s->rregs[saddr] = val;
345        break;
346    case 2:
347        // FIFO
348        if (s->do_cmd) {
349            s->cmdbuf[s->cmdlen++] = val & 0xff;
350        } else if ((s->rregs[4] & 6) == 0) {
351            uint8_t buf;
352            buf = val & 0xff;
353            s->ti_size--;
354            scsi_write_data(s->current_dev, &buf, 0);
355        } else {
356            s->ti_size++;
357            s->ti_buf[s->ti_wptr++] = val & 0xff;
358        }
359        break;
360    case 3:
361        s->rregs[saddr] = val;
362        // Command
363        if (val & 0x80) {
364            s->dma = 1;
365        } else {
366            s->dma = 0;
367        }
368        switch(val & 0x7f) {
369        case 0:
370            DPRINTF("NOP (%2.2x)\n", val);
371            break;
372        case 1:
373            DPRINTF("Flush FIFO (%2.2x)\n", val);
374            //s->ti_size = 0;
375            s->rregs[5] = INTR_FC;
376            s->rregs[6] = 0;
377            break;
378        case 2:
379            DPRINTF("Chip reset (%2.2x)\n", val);
380            esp_reset(s);
381            break;
382        case 3:
383            DPRINTF("Bus reset (%2.2x)\n", val);
384            s->rregs[5] = INTR_RST;
385            if (!(s->wregs[8] & 0x40)) {
386                s->espdmaregs[0] |= DMA_INTR;
387                pic_set_irq(s->irq, 1);
388            }
389            break;
390        case 0x10:
391            handle_ti(s);
392            break;
393        case 0x11:
394            DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val);
395            write_response(s);
396            break;
397        case 0x12:
398            DPRINTF("Message Accepted (%2.2x)\n", val);
399            write_response(s);
400            s->rregs[5] = INTR_DC;
401            s->rregs[6] = 0;
402            break;
403        case 0x1a:
404            DPRINTF("Set ATN (%2.2x)\n", val);
405            break;
406        case 0x42:
407            DPRINTF("Set ATN (%2.2x)\n", val);
408            handle_satn(s);
409            break;
410        case 0x43:
411            DPRINTF("Set ATN & stop (%2.2x)\n", val);
412            handle_satn_stop(s);
413            break;
414        default:
415            DPRINTF("Unhandled ESP command (%2.2x)\n", val);
416            break;
417        }
418        break;
419    case 4 ... 7:
420        break;
421    case 8:
422        s->rregs[saddr] = val;
423        break;
424    case 9 ... 10:
425        break;
426    case 11:
427        s->rregs[saddr] = val & 0x15;
428        break;
429    case 12 ... 15:
430        s->rregs[saddr] = val;
431        break;
432    default:
433        break;
434    }
435    s->wregs[saddr] = val;
436}
437
438static CPUReadMemoryFunc *esp_mem_read[3] = {
439    esp_mem_readb,
440    esp_mem_readb,
441    esp_mem_readb,
442};
443
444static CPUWriteMemoryFunc *esp_mem_write[3] = {
445    esp_mem_writeb,
446    esp_mem_writeb,
447    esp_mem_writeb,
448};
449
450static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr)
451{
452    ESPState *s = opaque;
453    uint32_t saddr;
454
455    saddr = (addr & ESPDMA_MAXADDR) >> 2;
456    DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]);
457
458    return s->espdmaregs[saddr];
459}
460
461static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
462{
463    ESPState *s = opaque;
464    uint32_t saddr;
465
466    saddr = (addr & ESPDMA_MAXADDR) >> 2;
467    DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val);
468    switch (saddr) {
469    case 0:
470        if (!(val & DMA_INTREN))
471            pic_set_irq(s->irq, 0);
472        if (val & 0x80) {
473            esp_reset(s);
474        } else if (val & 0x40) {
475            val &= ~0x40;
476        } else if (val == 0)
477            val = 0x40;
478        val &= 0x0fffffff;
479        val |= DMA_VER;
480        break;
481    case 1:
482        s->espdmaregs[0] |= DMA_LOADED;
483        break;
484    default:
485        break;
486    }
487    s->espdmaregs[saddr] = val;
488}
489
490static CPUReadMemoryFunc *espdma_mem_read[3] = {
491    espdma_mem_readl,
492    espdma_mem_readl,
493    espdma_mem_readl,
494};
495
496static CPUWriteMemoryFunc *espdma_mem_write[3] = {
497    espdma_mem_writel,
498    espdma_mem_writel,
499    espdma_mem_writel,
500};
501
502static void esp_save(QEMUFile *f, void *opaque)
503{
504    ESPState *s = opaque;
505    unsigned int i;
506
507    qemu_put_buffer(f, s->rregs, ESP_MAXREG);
508    qemu_put_buffer(f, s->wregs, ESP_MAXREG);
509    qemu_put_be32s(f, &s->irq);
510    for (i = 0; i < ESPDMA_REGS; i++)
511        qemu_put_be32s(f, &s->espdmaregs[i]);
512    qemu_put_be32s(f, &s->ti_size);
513    qemu_put_be32s(f, &s->ti_rptr);
514    qemu_put_be32s(f, &s->ti_wptr);
515    qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
516    qemu_put_be32s(f, &s->dma);
517}
518
519static int esp_load(QEMUFile *f, void *opaque, int version_id)
520{
521    ESPState *s = opaque;
522    unsigned int i;
523   
524    if (version_id != 1)
525        return -EINVAL;
526
527    qemu_get_buffer(f, s->rregs, ESP_MAXREG);
528    qemu_get_buffer(f, s->wregs, ESP_MAXREG);
529    qemu_get_be32s(f, &s->irq);
530    for (i = 0; i < ESPDMA_REGS; i++)
531        qemu_get_be32s(f, &s->espdmaregs[i]);
532    qemu_get_be32s(f, &s->ti_size);
533    qemu_get_be32s(f, &s->ti_rptr);
534    qemu_get_be32s(f, &s->ti_wptr);
535    qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
536    qemu_get_be32s(f, &s->dma);
537
538    return 0;
539}
540
541void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdaddr)
542{
543    ESPState *s;
544    int esp_io_memory, espdma_io_memory;
545    int i;
546
547    s = qemu_mallocz(sizeof(ESPState));
548    if (!s)
549        return;
550
551    s->bd = bd;
552    s->irq = irq;
553
554    esp_io_memory = cpu_register_io_memory(0, esp_mem_read, esp_mem_write, s);
555    cpu_register_physical_memory(espaddr, ESP_MAXREG*4, esp_io_memory);
556
557    espdma_io_memory = cpu_register_io_memory(0, espdma_mem_read, espdma_mem_write, s);
558    cpu_register_physical_memory(espdaddr, 16, espdma_io_memory);
559
560    esp_reset(s);
561
562    register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
563    qemu_register_reset(esp_reset, s);
564    for (i = 0; i < MAX_DISKS; i++) {
565        if (bs_table[i]) {
566            s->scsi_dev[i] =
567                scsi_disk_init(bs_table[i], esp_command_complete, s);
568        }
569    }
570}
571
Note: See TracBrowser for help on using the repository browser.