diff mbox

[04/16] ahci: check for ncq prdtl overflow

Message ID 1434765047-29333-5-git-send-email-jsnow@redhat.com
State New
Headers show

Commit Message

John Snow June 20, 2015, 1:50 a.m. UTC
Don't attempt the NCQ transfer if the PRDT we were given is not big
enough to perform the entire transfer.

Signed-off-by: John Snow <jsnow@redhat.com>
---
 hw/ide/ahci.c | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

Comments

Stefan Hajnoczi June 22, 2015, 2:06 p.m. UTC | #1
On Fri, Jun 19, 2015 at 09:50:35PM -0400, John Snow wrote:
> @@ -999,20 +1000,28 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
>                     ((uint64_t)ncq_fis->lba2 << 16) |
>                     ((uint64_t)ncq_fis->lba1 << 8) |
>                     (uint64_t)ncq_fis->lba0;
> +    ncq_tfs->tag = tag;
>  
> -    /* Note: We calculate the sector count, but don't currently rely on it.
> -     * The total size of the DMA buffer tells us the transfer size instead. */
>      ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
>                                  ncq_fis->sector_count_low;
> +    ahci_populate_sglist(ad, &ncq_tfs->sglist, 0);
> +    size = ncq_tfs->sector_count * 512;

ncq_tfs->sector_count is used with - 2 and - 1 below.  What is the
semantics of this field and why is it okay to use it without subtracting
here?
John Snow June 22, 2015, 2:08 p.m. UTC | #2
On 06/22/2015 10:06 AM, Stefan Hajnoczi wrote:
> On Fri, Jun 19, 2015 at 09:50:35PM -0400, John Snow wrote:
>> @@ -999,20 +1000,28 @@ static void process_ncq_command(AHCIState
>> *s, int port, uint8_t *cmd_fis, ((uint64_t)ncq_fis->lba2 << 16)
>> | ((uint64_t)ncq_fis->lba1 << 8) | (uint64_t)ncq_fis->lba0; +
>> ncq_tfs->tag = tag;
>> 
>> -    /* Note: We calculate the sector count, but don't currently
>> rely on it. -     * The total size of the DMA buffer tells us the
>> transfer size instead. */ ncq_tfs->sector_count =
>> ((uint16_t)ncq_fis->sector_count_high << 8) | 
>> ncq_fis->sector_count_low; +    ahci_populate_sglist(ad,
>> &ncq_tfs->sglist, 0); +    size = ncq_tfs->sector_count * 512;
> 
> ncq_tfs->sector_count is used with - 2 and - 1 below.  What is the 
> semantics of this field and why is it okay to use it without
> subtracting here?
> 

Just a bonkers patch order. Patch #7 fixes the debug prints, which are
wrong.

The field is not "off by one" in the spec, sector_count == 1 means 1
sector.

(sector_count == 0 means 64K sectors, though, like it does in regular
ATA.)
Stefan Hajnoczi June 22, 2015, 2:16 p.m. UTC | #3
On Fri, Jun 19, 2015 at 09:50:35PM -0400, John Snow wrote:
> -    /* Note: We calculate the sector count, but don't currently rely on it.
> -     * The total size of the DMA buffer tells us the transfer size instead. */
>      ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
>                                  ncq_fis->sector_count_low;
> +    ahci_populate_sglist(ad, &ncq_tfs->sglist, 0);
> +    size = ncq_tfs->sector_count * 512;

I just saw that a later patch addresses the sector_count inconsistency,
so I'm happy with this now.
Stefan Hajnoczi June 23, 2015, 1:46 p.m. UTC | #4
On Mon, Jun 22, 2015 at 10:08:22AM -0400, John Snow wrote:
> (sector_count == 0 means 64K sectors, though, like it does in regular
> ATA.)

I don't see a case to handle this.
John Snow June 23, 2015, 3:55 p.m. UTC | #5
On 06/23/2015 09:46 AM, Stefan Hajnoczi wrote:
> On Mon, Jun 22, 2015 at 10:08:22AM -0400, John Snow wrote:
>> (sector_count == 0 means 64K sectors, though, like it does in regular
>> ATA.)
> 
> I don't see a case to handle this.
> 

I apologize:

Corrected in [PATCH 09/16] ahci: correct ncq sector count, in the "part
2" series.

Sorry for the confusing order, I played a lot of patch-reshuffling and
due to the refactors to get NCQ migrating, sometimes it was literally
just a sin of convenience to leave certain patches after the shuffle.

I tried to leave each patch fixing just one very small issue at a time
so they were easier to digest, instead of batching them together like
"Fix (everything wrong about) sector count." Maybe that wasn't the right
approach, you can let me know.

Thank you,
--John
diff mbox

Patch

diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index 375aa44..24c4832 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -983,6 +983,7 @@  static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
     NCQFrame *ncq_fis = (NCQFrame*)cmd_fis;
     uint8_t tag = ncq_fis->tag >> 3;
     NCQTransferState *ncq_tfs = &ad->ncq_tfs[tag];
+    size_t size;
 
     if (ncq_tfs->used) {
         /* error - already in use */
@@ -999,20 +1000,28 @@  static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
                    ((uint64_t)ncq_fis->lba2 << 16) |
                    ((uint64_t)ncq_fis->lba1 << 8) |
                    (uint64_t)ncq_fis->lba0;
+    ncq_tfs->tag = tag;
 
-    /* Note: We calculate the sector count, but don't currently rely on it.
-     * The total size of the DMA buffer tells us the transfer size instead. */
     ncq_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
                                 ncq_fis->sector_count_low;
+    ahci_populate_sglist(ad, &ncq_tfs->sglist, 0);
+    size = ncq_tfs->sector_count * 512;
+
+    if (ncq_tfs->sglist.size < size) {
+        error_report("ahci: PRDT length for NCQ command (0x%zx) "
+                     "is smaller than the requested size (0x%zx)",
+                     ncq_tfs->sglist.size, size);
+        qemu_sglist_destroy(&ncq_tfs->sglist);
+        ncq_err(ncq_tfs);
+        ahci_trigger_irq(ad->hba, ad, PORT_IRQ_OVERFLOW);
+        return;
+    }
 
     DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", "
             "drive max %"PRId64"\n",
             ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
             ide_state->nb_sectors - 1);
 
-    ahci_populate_sglist(ad, &ncq_tfs->sglist, 0);
-    ncq_tfs->tag = tag;
-
     switch(ncq_fis->command) {
         case READ_FPDMA_QUEUED:
             DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", "
@@ -1051,6 +1060,7 @@  static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
                         "error: tried to process non-NCQ command as NCQ\n");
             }
             qemu_sglist_destroy(&ncq_tfs->sglist);
+            ncq_err(ncq_tfs);
     }
 }