From dd8edf0122a605631420547f251fe636932dc4aa Mon Sep 17 00:00:00 2001 From: aliguori Date: Wed, 10 Dec 2008 15:38:47 +0000 Subject: [PATCH] Add 64-bit Block Move support (Direct & Table Indirect) (Ryan Harper) This patch adds support for 64-bit Block Move instructions. There are multiple modes for 64-bit Block moves, direct, indirect, and table indirect. This patch implements Direct and Table indirect moves which are needed by 64-bit windows and SYM_CONF_DMA_ADDRESSING_MODE=2 for the Linux sym53c8xx_2 driver respectively. Two helper functions are included to check which mode the guest is using. For 64-bit direct moves, we fetch a 3rd DWORD and store the value in the DBMS register. For Table Indirect moves, we look into the table for which register contains the upper 32-bits of the 64-bit address. This selector value indicates which register to pull the value from and into dnad64 register. Finally, lsi_do_dma is updated to use the approriate register to build a 64-bit DMA address if required. With this patch, Windows XP x64, 2003 SP2 x64, can now install to scsi devices. Linux SYM_CONF_DMA_ADDRESSING_MODE=2 need a quirk fixup in Patch 4 to function properly. Signed-off-by: Ryan Harper Signed-off-by: Anthony Liguori git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5969 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/lsi53c895a.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index ca6e34188..78a23a5f9 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -338,6 +338,20 @@ static int lsi_dma_40bit(LSIState *s) return 0; } +static int lsi_dma_ti64bit(LSIState *s) +{ + if ((s->ccntl1 & LSI_CCNTL1_EN64TIBMV) == LSI_CCNTL1_EN64TIBMV) + return 1; + return 0; +} + +static int lsi_dma_64bit(LSIState *s) +{ + if ((s->ccntl1 & LSI_CCNTL1_EN64DBMV) == LSI_CCNTL1_EN64DBMV) + return 1; + return 0; +} + static uint8_t lsi_reg_readb(LSIState *s, int offset); static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val); static void lsi_execute_script(LSIState *s); @@ -477,8 +491,11 @@ static void lsi_do_dma(LSIState *s, int out) count = s->current_dma_len; addr = s->dnad; - if (lsi_dma_40bit(s)) + /* both 40 and Table Indirect 64-bit DMAs store upper bits in dnad64 */ + if (lsi_dma_40bit(s) || lsi_dma_ti64bit(s)) addr |= ((uint64_t)s->dnad64 << 32); + else if (s->dbms) + addr |= ((uint64_t)s->dbms << 32); else if (s->sbms) addr |= ((uint64_t)s->sbms << 32); @@ -888,6 +905,8 @@ again: } s->dbc = insn & 0xffffff; s->rbc = s->dbc; + /* ??? Set ESA. */ + s->ia = s->dsp - 8; if (insn & (1 << 29)) { /* Indirect addressing. */ addr = read_dword(s, addr); @@ -895,6 +914,8 @@ again: uint32_t buf[2]; int32_t offset; /* Table indirect addressing. */ + + /* 32-bit Table indirect */ offset = sxt24(addr); cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8); /* byte count is stored in bits 0:23 only */ @@ -906,6 +927,44 @@ again: * table, bits [31:24] */ if (lsi_dma_40bit(s)) addr_high = cpu_to_le32(buf[0]) >> 24; + else if (lsi_dma_ti64bit(s)) { + int selector = (cpu_to_le32(buf[0]) >> 24) & 0x1f; + switch (selector) { + case 0 ... 0x0f: + /* offset index into scratch registers since + * TI64 mode can use registers C to R */ + addr_high = s->scratch[2 + selector]; + break; + case 0x10: + addr_high = s->mmrs; + break; + case 0x11: + addr_high = s->mmws; + break; + case 0x12: + addr_high = s->sfs; + break; + case 0x13: + addr_high = s->drs; + break; + case 0x14: + addr_high = s->sbms; + break; + case 0x15: + addr_high = s->dbms; + break; + default: + BADF("Illegal selector specified (0x%x > 0x15)" + " for 64-bit DMA block move", selector); + break; + } + } + } else if (lsi_dma_64bit(s)) { + /* fetch a 3rd dword if 64-bit direct move is enabled and + only if we're not doing table indirect or indirect addressing */ + s->dbms = read_dword(s, s->dsp); + s->dsp += 4; + s->ia = s->dsp - 12; } if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) { DPRINTF("Wrong phase got %d expected %d\n", @@ -915,8 +974,6 @@ again: } s->dnad = addr; s->dnad64 = addr_high; - /* ??? Set ESA. */ - s->ia = s->dsp - 8; switch (s->sstat1 & 0x7) { case PHASE_DO: s->waiting = 2;