diff --git a/hw/esp.c b/hw/esp.c index e04539a2ab..6890bd144c 100644 --- a/hw/esp.c +++ b/hw/esp.c @@ -36,6 +36,9 @@ do { printf("ESP: " fmt , ##args); } while (0) #define ESPDMA_REGS 4 #define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1) #define ESP_MAXREG 0x3f +#define TI_BUFSZ 65536 +#define DMA_VER 0xa0000000 +#define DMA_LOADED 0x04000000 typedef struct ESPState { BlockDriverState **bd; @@ -44,8 +47,10 @@ typedef struct ESPState { int irq; uint32_t espdmaregs[ESPDMA_REGS]; uint32_t ti_size; + uint32_t ti_rptr, ti_wptr; int ti_dir; - uint8_t ti_buf[65536]; + uint8_t ti_buf[TI_BUFSZ]; + int dma; } ESPState; #define STAT_DO 0x00 @@ -73,17 +78,25 @@ static void handle_satn(ESPState *s) int64_t nb_sectors; int target; - dmaptr = iommu_translate(s->espdmaregs[1]); dmalen = s->wregs[0] | (s->wregs[1] << 8); - DPRINTF("Select with ATN at %8.8x len %d\n", dmaptr, dmalen); - DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); - cpu_physical_memory_read(dmaptr, buf, dmalen); + target = s->wregs[4] & 7; + DPRINTF("Select with ATN len %d target %d\n", dmalen, target); + if (s->dma) { + dmaptr = iommu_translate(s->espdmaregs[1]); + DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr); + cpu_physical_memory_read(dmaptr, buf, dmalen); + } else { + buf[0] = 0; + memcpy(&buf[1], s->ti_buf, dmalen); + dmalen++; + } for (i = 0; i < dmalen; i++) { DPRINTF("Command %2.2x\n", buf[i]); } s->ti_dir = 0; s->ti_size = 0; - target = s->wregs[4] & 7; + s->ti_rptr = 0; + s->ti_wptr = 0; if (target > 4 || !s->bd[target]) { // No such drive s->rregs[4] = STAT_IN; @@ -126,7 +139,10 @@ static void handle_satn(ESPState *s) s->ti_buf[3] = nb_sectors & 0xff; s->ti_buf[4] = 0; s->ti_buf[5] = 0; - s->ti_buf[6] = 2; + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) + s->ti_buf[6] = 8; // sector size 2048 + else + s->ti_buf[6] = 2; // sector size 512 s->ti_buf[7] = 0; s->ti_dir = 1; s->ti_size = 8; @@ -135,28 +151,42 @@ static void handle_satn(ESPState *s) { int64_t offset, len; - offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; - len = (buf[8] << 8) | buf[9]; + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { + offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4; + len = ((buf[8] << 8) | buf[9]) * 4; + s->ti_size = len * 2048; + } else { + offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; + len = (buf[8] << 8) | buf[9]; + s->ti_size = len * 512; + } DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len); bdrv_read(s->bd[target], offset, s->ti_buf, len); + // XXX error handling s->ti_dir = 1; - s->ti_size = len * 512; break; } case 0x2a: { int64_t offset, len; - offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; - len = (buf[8] << 8) | buf[9]; + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { + offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4; + len = ((buf[8] << 8) | buf[9]) * 4; + s->ti_size = len * 2048; + } else { + offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; + len = (buf[8] << 8) | buf[9]; + s->ti_size = len * 512; + } DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len); bdrv_write(s->bd[target], offset, s->ti_buf, len); + // XXX error handling s->ti_dir = 0; - s->ti_size = len * 512; break; } default: - DPRINTF("Unknown command (%2.2x)\n", buf[1]); + DPRINTF("Unknown SCSI command (%2.2x)\n", buf[1]); break; } s->rregs[4] = STAT_IN | STAT_TC | STAT_DI; @@ -170,17 +200,26 @@ static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len) { uint32_t dmaptr, dmalen; - dmaptr = iommu_translate(s->espdmaregs[1]); dmalen = s->wregs[0] | (s->wregs[1] << 8); - DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); - cpu_physical_memory_write(dmaptr, buf, len); - s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; - s->rregs[5] = INTR_BS | INTR_FC; - s->rregs[6] = SEQ_CD; - s->espdmaregs[0] |= 1; + DPRINTF("Transfer status len %d\n", dmalen); + if (s->dma) { + dmaptr = iommu_translate(s->espdmaregs[1]); + DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); + cpu_physical_memory_write(dmaptr, buf, len); + s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; + s->rregs[5] = INTR_BS | INTR_FC; + s->rregs[6] = SEQ_CD; + s->espdmaregs[0] |= 1; + } else { + memcpy(s->ti_buf, buf, len); + s->ti_size = dmalen; + s->ti_rptr = 0; + s->ti_wptr = 0; + } pic_set_irq(s->irq, 1); } + static const uint8_t okbuf[] = {0, 0}; static void handle_ti(ESPState *s) @@ -188,21 +227,27 @@ static void handle_ti(ESPState *s) uint32_t dmaptr, dmalen; unsigned int i; - dmaptr = iommu_translate(s->espdmaregs[1]); dmalen = s->wregs[0] | (s->wregs[1] << 8); - DPRINTF("Transfer Information at %8.8x len %d\n", dmaptr, dmalen); - DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); - for (i = 0; i < s->ti_size; i++) { - dmaptr = iommu_translate(s->espdmaregs[1] + i); - if (s->ti_dir) - cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1); - else - cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1); - } - s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; - s->rregs[5] = INTR_BS; - s->rregs[6] = 0; - s->espdmaregs[0] |= 1; + DPRINTF("Transfer Information len %d\n", dmalen); + if (s->dma) { + dmaptr = iommu_translate(s->espdmaregs[1]); + DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr); + for (i = 0; i < s->ti_size; i++) { + dmaptr = iommu_translate(s->espdmaregs[1] + i); + if (s->ti_dir) + cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1); + else + cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1); + } + s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; + s->rregs[5] = INTR_BS; + s->rregs[6] = 0; + s->espdmaregs[0] |= 1; + } else { + s->ti_size = dmalen; + s->ti_rptr = 0; + s->ti_wptr = 0; + } pic_set_irq(s->irq, 1); } @@ -221,10 +266,23 @@ static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr) saddr = (addr & ESP_MAXREG) >> 2; switch (saddr) { + case 2: + // FIFO + if (s->ti_size > 0) { + s->ti_size--; + s->rregs[saddr] = s->ti_buf[s->ti_rptr++]; + pic_set_irq(s->irq, 1); + } + if (s->ti_size == 0) { + s->ti_rptr = 0; + s->ti_wptr = 0; + } + break; default: break; } DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); + return s->rregs[saddr]; } @@ -236,8 +294,23 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) saddr = (addr & ESP_MAXREG) >> 2; DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val); switch (saddr) { + case 0: + case 1: + s->rregs[saddr] = val; + break; + case 2: + // FIFO + s->ti_size++; + s->ti_buf[s->ti_wptr++] = val & 0xff; + break; case 3: + s->rregs[saddr] = val; // Command + if (val & 0x80) { + s->dma = 1; + } else { + s->dma = 0; + } switch(val & 0x7f) { case 0: DPRINTF("NOP (%2.2x)\n", val); @@ -278,13 +351,20 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) handle_satn(s); break; default: - DPRINTF("Unhandled command (%2.2x)\n", val); + DPRINTF("Unhandled ESP command (%2.2x)\n", val); break; } break; case 4 ... 7: - case 9 ... 0xf: break; + case 8: + s->rregs[saddr] = val; + break; + case 9 ... 10: + break; + case 11 ... 15: + s->rregs[saddr] = val; + break; default: break; } @@ -309,7 +389,8 @@ static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr) uint32_t saddr; saddr = (addr & ESPDMA_MAXADDR) >> 2; - DPRINTF("read dmareg[%d]: 0x%2.2x\n", saddr, s->espdmaregs[saddr]); + DPRINTF("read dmareg[%d]: 0x%8.8x\n", saddr, s->espdmaregs[saddr]); + return s->espdmaregs[saddr]; } @@ -319,12 +400,23 @@ static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t va uint32_t saddr; saddr = (addr & ESPDMA_MAXADDR) >> 2; - DPRINTF("write dmareg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->espdmaregs[saddr], val); + DPRINTF("write dmareg[%d]: 0x%8.8x -> 0x%8.8x\n", saddr, s->espdmaregs[saddr], val); switch (saddr) { case 0: if (!(val & 0x10)) pic_set_irq(s->irq, 0); + if (val & 0x80) { + esp_reset(s); + } else if (val & 0x40) { + val &= ~0x40; + } else if (val == 0) + val = 0x40; + val &= 0x0fffffff; + val |= DMA_VER; break; + case 1: + s->espdmaregs[0] = DMA_LOADED; + break; default: break; } @@ -353,6 +445,12 @@ static void esp_save(QEMUFile *f, void *opaque) qemu_put_be32s(f, &s->irq); for (i = 0; i < ESPDMA_REGS; i++) qemu_put_be32s(f, &s->espdmaregs[i]); + qemu_put_be32s(f, &s->ti_size); + qemu_put_be32s(f, &s->ti_rptr); + qemu_put_be32s(f, &s->ti_wptr); + qemu_put_be32s(f, &s->ti_dir); + qemu_put_buffer(f, s->ti_buf, TI_BUFSZ); + qemu_put_be32s(f, &s->dma); } static int esp_load(QEMUFile *f, void *opaque, int version_id) @@ -368,6 +466,12 @@ static int esp_load(QEMUFile *f, void *opaque, int version_id) qemu_get_be32s(f, &s->irq); for (i = 0; i < ESPDMA_REGS; i++) qemu_get_be32s(f, &s->espdmaregs[i]); + qemu_get_be32s(f, &s->ti_size); + qemu_get_be32s(f, &s->ti_rptr); + qemu_get_be32s(f, &s->ti_wptr); + qemu_get_be32s(f, &s->ti_dir); + qemu_get_buffer(f, s->ti_buf, TI_BUFSZ); + qemu_get_be32s(f, &s->dma); return 0; }