From patchwork Fri Aug 3 19:57:06 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jason Baron X-Patchwork-Id: 175042 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E5E4F2C0079 for ; Sat, 4 Aug 2012 05:57:30 +1000 (EST) Received: from localhost ([::1]:39037 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SxO01-0003I8-3g for incoming@patchwork.ozlabs.org; Fri, 03 Aug 2012 15:57:29 -0400 Received: from eggs.gnu.org ([208.118.235.92]:52278) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SxNzk-0002tA-0z for qemu-devel@nongnu.org; Fri, 03 Aug 2012 15:57:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SxNzi-0003gD-Kj for qemu-devel@nongnu.org; Fri, 03 Aug 2012 15:57:11 -0400 Received: from mx1.redhat.com ([209.132.183.28]:10364) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SxNzf-0003fQ-Nj; Fri, 03 Aug 2012 15:57:07 -0400 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q73Jv6eb030751 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Fri, 3 Aug 2012 15:57:07 -0400 Received: from redhat.com (dhcp-185-114.bos.redhat.com [10.16.185.114]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q73Jv6oO031965; Fri, 3 Aug 2012 15:57:06 -0400 Date: Fri, 3 Aug 2012 15:57:06 -0400 From: Jason Baron To: agraf@suse.de Message-Id: In-Reply-To: References: X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: kwolf@redhat.com, aliguori@us.ibm.com, qemu-devel@nongnu.org, armbru@redhat.com, qemu-stable@nongnu.org, alex.williamson@redhat.com, avi@redhat.com, pbonzini@redhat.com, afaerber@suse.de Subject: [Qemu-devel] [PATCH 1/2 v2] ahci: Fix ahci cdrom read corruptions for reads > 128k X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org While testing q35, which has its cdrom attached to the ahci controller, I found that the Fedora 17 install would panic on boot. The panic occurs while squashfs is trying to read from the cdrom. The errors are: [ 8.622711] SQUASHFS error: xz_dec_run error, data probably corrupt [ 8.625180] SQUASHFS error: squashfs_read_data failed to read block 0x20be48a I was also able to produce corrupt data reads using an installed piix based qemu machine, using 'dd'. I found that the corruptions were only occuring when then read size was greater than 128k. For example, the following command results in corrupted reads: dd if=/dev/sr0 of=/tmp/blah bs=256k iflag=direct The > 128k size reads exercise a different code path than 128k and below. In ide_atapi_cmd_read_dma_cb() s->io_buffer_size is capped at 128k. Thus, ide_atapi_cmd_read_dma_cb() is called a second time when the read is > 128k. However, ahci_dma_rw_buf() restart the read from offset 0, instead of at 128k. Thus, resulting in a corrupted read. To fix this, I've introduced 'io_buffer_offset' field in IDEState to keep track of the offset. I've also modified ahci_populate_sglist() to take a new 3rd offset argument, so that the sglist is property initialized. I've tested this patch using 'dd' testing, and Fedora 17 now correctly boots and installs on q35 with the cdrom ahci controller. Signed-off-by: Jason Baron Tested-by: Andreas Färber --- hw/ide/ahci.c | 41 ++++++++++++++++++++++++++++++++++------- hw/ide/internal.h | 1 + 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index efea93f..de580a6 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -636,7 +636,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) } } -static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) +static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist, int offset) { AHCICmdHdr *cmd = ad->cur_cmd; uint32_t opts = le32_to_cpu(cmd->opts); @@ -647,6 +647,10 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) uint8_t *prdt; int i; int r = 0; + int sum = 0; + int off_idx = -1; + int off_pos = -1; + int tbl_entry_size; if (!sglist_alloc_hint) { DPRINTF(ad->port_no, "no sg list given by guest: 0x%08x\n", opts); @@ -669,10 +673,31 @@ static int ahci_populate_sglist(AHCIDevice *ad, QEMUSGList *sglist) /* Get entries in the PRDT, init a qemu sglist accordingly */ if (sglist_alloc_hint > 0) { AHCI_SG *tbl = (AHCI_SG *)prdt; - - qemu_sglist_init(sglist, sglist_alloc_hint, ad->hba->dma); + sum = 0; for (i = 0; i < sglist_alloc_hint; i++) { /* flags_size is zero-based */ + tbl_entry_size = (le32_to_cpu(tbl[i].flags_size) + 1); + if (offset <= (sum + tbl_entry_size)) { + off_idx = i; + off_pos = offset - sum; + break; + } + sum += tbl_entry_size; + } + if ((off_idx == -1) || (off_pos < 0) || (off_pos > tbl_entry_size)) { + DPRINTF(ad->port_no, "%s: Incorrect offset! " + "off_idx: %d, off_pos: %d\n", + __func__, off_idx, off_pos); + r = -1; + goto out; + } + + qemu_sglist_init(sglist, (sglist_alloc_hint - off_idx), ad->hba->dma); + qemu_sglist_add(sglist, le64_to_cpu(tbl[off_idx].addr + off_pos), + le32_to_cpu(tbl[off_idx].flags_size) + 1 - off_pos); + + for (i = off_idx + 1; i < sglist_alloc_hint; i++) { + /* flags_size is zero-based */ qemu_sglist_add(sglist, le64_to_cpu(tbl[i].addr), le32_to_cpu(tbl[i].flags_size) + 1); } @@ -745,7 +770,7 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis, ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2, s->dev[port].port.ifs[0].nb_sectors - 1); - ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist); + ahci_populate_sglist(&s->dev[port], &ncq_tfs->sglist, 0); ncq_tfs->tag = tag; switch(ncq_fis->command) { @@ -970,7 +995,7 @@ static int ahci_start_transfer(IDEDMA *dma) goto out; } - if (!ahci_populate_sglist(ad, &s->sg)) { + if (!ahci_populate_sglist(ad, &s->sg, 0)) { has_sglist = 1; } @@ -1015,6 +1040,7 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, DPRINTF(ad->port_no, "\n"); ad->dma_cb = dma_cb; ad->dma_status |= BM_STATUS_DMAING; + s->io_buffer_offset = 0; dma_cb(s, 0); } @@ -1023,7 +1049,7 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; - ahci_populate_sglist(ad, &s->sg); + ahci_populate_sglist(ad, &s->sg, 0); s->io_buffer_size = s->sg.size; DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); @@ -1037,7 +1063,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) uint8_t *p = s->io_buffer + s->io_buffer_index; int l = s->io_buffer_size - s->io_buffer_index; - if (ahci_populate_sglist(ad, &s->sg)) { + if (ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) { return 0; } @@ -1050,6 +1076,7 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) /* update number of transferred bytes */ ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); s->io_buffer_index += l; + s->io_buffer_offset += l; DPRINTF(ad->port_no, "len=%#x\n", l); diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 7170bd9..bf7d313 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -393,6 +393,7 @@ struct IDEState { struct iovec iov; QEMUIOVector qiov; /* ATA DMA state */ + int io_buffer_offset; int io_buffer_size; QEMUSGList sg; /* PIO transfer handling */