From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1L0JNQ-0000vA-So for qemu-devel@nongnu.org; Wed, 12 Nov 2008 12:19:37 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1L0JNP-0000ur-J7 for qemu-devel@nongnu.org; Wed, 12 Nov 2008 12:19:36 -0500 Received: from [199.232.76.173] (port=41804 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1L0JNP-0000um-EG for qemu-devel@nongnu.org; Wed, 12 Nov 2008 12:19:35 -0500 Received: from e34.co.us.ibm.com ([32.97.110.152]:40133) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1L0JNP-00031B-0w for qemu-devel@nongnu.org; Wed, 12 Nov 2008 12:19:35 -0500 Received: from d03relay02.boulder.ibm.com (d03relay02.boulder.ibm.com [9.17.195.227]) by e34.co.us.ibm.com (8.13.1/8.13.1) with ESMTP id mACHJ53r017511 for ; Wed, 12 Nov 2008 10:19:05 -0700 Received: from d03av03.boulder.ibm.com (d03av03.boulder.ibm.com [9.17.195.169]) by d03relay02.boulder.ibm.com (8.13.8/8.13.8/NCO v9.1) with ESMTP id mACHJUve123450 for ; Wed, 12 Nov 2008 10:19:30 -0700 Received: from d03av03.boulder.ibm.com (loopback [127.0.0.1]) by d03av03.boulder.ibm.com (8.12.11.20060308/8.13.3) with ESMTP id mACHJTdQ031283 for ; Wed, 12 Nov 2008 10:19:29 -0700 Date: Wed, 12 Nov 2008 11:19:28 -0600 From: Ryan Harper Message-ID: <20081112171928.GO31893@us.ibm.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Subject: [Qemu-devel] [PATCH] Add 40-bit DMA support to LSI scsi emulation Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: kvm@vger.kernel.org This patch fixes Linux machines configured with > 4G of ram and using a SCSI device. I'm working on 64-bit DMA support as well which should hopefully fix 64-bit Windows using SCSI devices as well. -- Ryan Harper Software Engineer; Linux Technology Center IBM Corp., Austin, Tx (512) 838-9253 T/L: 678-9253 ryanh@us.ibm.com diffstat output: lsi53c895a.c | 36 ++++++++++++++++++++++++++++++++---- 1 files changed, 32 insertions(+), 4 deletions(-) Signed-off-by: Ryan Harper --- Background: The LSI device we emulate in QEMU is capable of 32, 40, and 64-bit DMA for block moves. The Linux device driver for the LSI device by default compiles support for 40-bit DMA and configures the LSI devices in register ccntl1 to use 40-bit DMA. For systems with more than 4GB of ram, the device driver in Linux may issue a DMA with an address > than 32-bit. The current LSI code ignores the upper 8-bits of the destination and then DMAs into some random location. This can be observed by booting a Linux guest with a scsi device attached and configured with more than 4GB of ram. Linux stalls while probing the scsi bus and dumps messages like this in dmesg: [ 5.640225] scsi scan: INQUIRY result too short (5), using 36 This happens since the DMA destination for the result of the INQUIRY scsi command does not contain the result, rather it was DMAed to some other location because the emulation dropped the upper 8-bits of the target address and doesn't mask off the lower 23-bits for the proper byte count to transfer. The Linux device driver assumes that the result is bogus and marshals on. The Fix: This patch introduces the flags for the ccntl1 register which are used to determine how the guest device driver is configuring the LSI device w.r.t to DMA address size. lsi_do_dma now uses target_phys_addr_t instead of a uint32_t to hold the DMA address, and in the block move code, we decode and store the upper 8-bits of the 40-bit DMA in the dnad64 register which is combined with dnad to form the 40-bit DMA. Details: When using 40-bit DMA mode, the OS driver will issue a block move instruction and use Table Indirect Adressing mode (TIA - bit 28). Table indirect address implies that the actual DMA address and byte count are stored somewhere else in memory rather than in the instruction encoding. For all table indirect moves (32, 40, and 64), the lower 24-bits of the 2nd dword of the original instruction are used as an offset combined with the DSA register to point to the table. Depending on the DMA mode, the table format differers, though all tables are 2 dwords long. We detect 40-bit Block Move by inspecting ccntl1 register and see if both LSI_CCNTL1_EN64TIBMV and LSI_CCNTL1_64TIMOD bits are on. If so, then table layout of a 40 bit DMA move has the byte count (amount of data to move) in the lower 23-bits of the first dword of the indirect table. The upper 8-bits of the first dword of the indirect table are to be the top 8-bits of the 40-bit DMA address and are stored in the dnad64 register. The lower 32-bits are pulled from the second dword of the indirect table. In lsi_do_dma, we combine the dnad64 register with dnad to form the 40-bit DMA address. The specification indicates that if smbs register is non-zero, then the upper 32-bits of the DMA will be pulled from there. 1. http://www.lsi.com/DistributionSystem/AssetDocument/files/docs/techdocs/storage_stand_prod/SCSIControllers/lsi53c895a_tech_manual.pdf Signed-off-by: Ryan Harper diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index 3f67837..d7608e4 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -143,6 +143,14 @@ do { fprintf(stderr, "lsi_scsi: error: " fmt , ##args);} while (0) #define LSI_CCNTL0_PMJCTL 0x40 #define LSI_CCNTL0_ENPMJ 0x80 +#define LSI_CCNTL1_EN64DBMV 0x01 +#define LSI_CCNTL1_EN64TIBMV 0x02 +#define LSI_CCNTL1_64TIMOD 0x04 +#define LSI_CCNTL1_DDAC 0x08 +#define LSI_CCNTL1_ZMOD 0x80 + +#define LSI_CCNTL1_40BIT (LSI_CCNTL1_EN64TIBMV|LSI_CCNTL1_64TIMOD) + #define PHASE_DO 0 #define PHASE_DI 1 #define PHASE_CMD 2 @@ -323,6 +331,13 @@ static void lsi_soft_reset(LSIState *s) s->csbc = 0; } +static int lsi_dma_40bit(LSIState *s) +{ + if ((s->ccntl1 & LSI_CCNTL1_40BIT) == LSI_CCNTL1_40BIT) + 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); @@ -449,7 +464,7 @@ static void lsi_resume_script(LSIState *s) static void lsi_do_dma(LSIState *s, int out) { uint32_t count; - uint32_t addr; + target_phys_addr_t addr; if (!s->current_dma_len) { /* Wait until data is available. */ @@ -460,9 +475,14 @@ static void lsi_do_dma(LSIState *s, int out) count = s->dbc; if (count > s->current_dma_len) count = s->current_dma_len; - DPRINTF("DMA addr=0x%08x len=%d\n", s->dnad, count); addr = s->dnad; + if (lsi_dma_40bit(s)) + addr |= ((uint64_t)s->dnad64 << 32UL); + else if (s->sbms) + addr |= ((uint64_t)s->sbms << 32UL); + + DPRINTF("DMA addr=0x%" PRIx64 " len=%d\n", addr, count); s->csbc += count; s->dnad += count; s->dbc -= count; @@ -839,7 +859,7 @@ static void lsi_wait_reselect(LSIState *s) static void lsi_execute_script(LSIState *s) { uint32_t insn; - uint32_t addr; + uint32_t addr, addr64; int opcode; int insn_processed = 0; @@ -848,6 +868,7 @@ again: insn_processed++; insn = read_dword(s, s->dsp); addr = read_dword(s, s->dsp + 4); + addr64 = 0; DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr); s->dsps = addr; s->dcmd = insn >> 24; @@ -870,9 +891,15 @@ again: /* Table indirect addressing. */ offset = sxt24(addr); cpu_physical_memory_read(s->dsa + offset, (uint8_t *)buf, 8); - s->dbc = cpu_to_le32(buf[0]); + /* byte count is stored in bits 0:23 only */ + s->dbc = cpu_to_le32(buf[0]) & 0xffffff; s->rbc = s->dbc; addr = cpu_to_le32(buf[1]); + + /* 40-bit DMA, upper addr bits [39:32] stored in first DWORD of + * table, bits [31:24] */ + if (lsi_dma_40bit(s)) + addr64 = cpu_to_le32(buf[0]) >> 24; } if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) { DPRINTF("Wrong phase got %d expected %d\n", @@ -881,6 +908,7 @@ again: break; } s->dnad = addr; + s->dnad64 = addr64; /* ??? Set ESA. */ s->ia = s->dsp - 8; switch (s->sstat1 & 0x7) {