ahci: check for ncq prdtl overflow
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> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Message-id: 1435016308-6150-5-git-send-email-jsnow@redhat.com
This commit is contained in:
		
							parent
							
								
									a55c8231d0
								
							
						
					
					
						commit
						3bcbe4aa80
					
				@ -983,6 +983,7 @@ static void process_ncq_command(AHCIState *s, int port, uint8_t *cmd_fis,
 | 
				
			|||||||
    NCQFrame *ncq_fis = (NCQFrame*)cmd_fis;
 | 
					    NCQFrame *ncq_fis = (NCQFrame*)cmd_fis;
 | 
				
			||||||
    uint8_t tag = ncq_fis->tag >> 3;
 | 
					    uint8_t tag = ncq_fis->tag >> 3;
 | 
				
			||||||
    NCQTransferState *ncq_tfs = &ad->ncq_tfs[tag];
 | 
					    NCQTransferState *ncq_tfs = &ad->ncq_tfs[tag];
 | 
				
			||||||
 | 
					    size_t size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (ncq_tfs->used) {
 | 
					    if (ncq_tfs->used) {
 | 
				
			||||||
        /* error - already in use */
 | 
					        /* 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->lba2 << 16) |
 | 
				
			||||||
                   ((uint64_t)ncq_fis->lba1 << 8) |
 | 
					                   ((uint64_t)ncq_fis->lba1 << 8) |
 | 
				
			||||||
                   (uint64_t)ncq_fis->lba0;
 | 
					                   (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_tfs->sector_count = ((uint16_t)ncq_fis->sector_count_high << 8) |
 | 
				
			||||||
                                ncq_fis->sector_count_low;
 | 
					                                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", "
 | 
					    DPRINTF(port, "NCQ transfer LBA from %"PRId64" to %"PRId64", "
 | 
				
			||||||
            "drive max %"PRId64"\n",
 | 
					            "drive max %"PRId64"\n",
 | 
				
			||||||
            ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
 | 
					            ncq_tfs->lba, ncq_tfs->lba + ncq_tfs->sector_count - 2,
 | 
				
			||||||
            ide_state->nb_sectors - 1);
 | 
					            ide_state->nb_sectors - 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ahci_populate_sglist(ad, &ncq_tfs->sglist, 0);
 | 
					 | 
				
			||||||
    ncq_tfs->tag = tag;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    switch(ncq_fis->command) {
 | 
					    switch(ncq_fis->command) {
 | 
				
			||||||
        case READ_FPDMA_QUEUED:
 | 
					        case READ_FPDMA_QUEUED:
 | 
				
			||||||
            DPRINTF(port, "NCQ reading %d sectors from LBA %"PRId64", "
 | 
					            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");
 | 
					                        "error: tried to process non-NCQ command as NCQ\n");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            qemu_sglist_destroy(&ncq_tfs->sglist);
 | 
					            qemu_sglist_destroy(&ncq_tfs->sglist);
 | 
				
			||||||
 | 
					            ncq_err(ncq_tfs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user