[Qemu-devel] [PATCH] ahci: fix FIS I bit and PIO Setup FIS interrupt

Paolo Bonzini posted 1 patch 5 years, 10 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20180620132546.27128-1-pbonzini@redhat.com
Test checkpatch passed
Test docker-mingw@fedora passed
Test docker-quick@centos7 passed
Test s390x passed
There is a newer version of this series
hw/ide/ahci.c       | 28 +++++++++++++++++++++-------
tests/libqos/ahci.c | 20 +++++++++++---------
tests/libqos/ahci.h |  2 +-
3 files changed, 33 insertions(+), 17 deletions(-)
[Qemu-devel] [PATCH] ahci: fix FIS I bit and PIO Setup FIS interrupt
Posted by Paolo Bonzini 5 years, 10 months ago
The "I" bit in PIO Setup and D2H FISes is exclusively a device concept
and the irqstatus register in the controller does not matter.  The SATA
spec says when it should be one; for D2H FISes in practice it is always
set, while the PIO Setup FIS has several subcases that are documented in
the patch.

Also, the PIO Setup FIS interrupt is actually generated _after_ data
has been received.

Someone should probably spend some time reading the SATA specification and
figuring out the more obscure fields in the PIO Setup FIS, but this is enough
to fix SeaBIOS booting from ATAPI CD-ROMs over an AHCI controller.

Fixes: 956556e131e35f387ac482ad7b41151576fef057
Reported-by: Gerd Hoffmann <kraxel@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 hw/ide/ahci.c       | 28 +++++++++++++++++++++-------
 tests/libqos/ahci.c | 20 +++++++++++---------
 tests/libqos/ahci.h |  2 +-
 3 files changed, 33 insertions(+), 17 deletions(-)

diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index f7852be842..acb90f41f3 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -801,7 +801,7 @@ static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs)
     }
 }
 
-static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
+static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len, bool pio_fis_i)
 {
     AHCIPortRegs *pr = &ad->port_regs;
     uint8_t *pio_fis;
@@ -814,7 +814,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
     pio_fis = &ad->res_fis[RES_FIS_PSFIS];
 
     pio_fis[0] = SATA_FIS_TYPE_PIO_SETUP;
-    pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+    pio_fis[1] = (pio_fis_i ? (1 << 6) : 0);
     pio_fis[2] = s->status;
     pio_fis[3] = s->error;
 
@@ -842,8 +842,6 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len)
     if (pio_fis[2] & ERR_STAT) {
         ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES);
     }
-
-    ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS);
 }
 
 static bool ahci_write_fis_d2h(AHCIDevice *ad)
@@ -860,7 +858,7 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad)
     d2h_fis = &ad->res_fis[RES_FIS_RFIS];
 
     d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H;
-    d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0);
+    d2h_fis[1] = (1 << 6); /* interrupt bit */
     d2h_fis[2] = s->status;
     d2h_fis[3] = s->error;
 
@@ -1351,9 +1349,20 @@ static void ahci_pio_transfer(IDEDMA *dma)
     int is_write = opts & AHCI_CMD_WRITE;
     int is_atapi = opts & AHCI_CMD_ATAPI;
     int has_sglist = 0;
+    bool pio_fis_i;
 
-    /* PIO FIS gets written prior to transfer */
-    ahci_write_fis_pio(ad, size);
+    /* The PIO Setup FIS is received prior to transfer, but the interrupt
+     * is only triggered after data is received.
+     *
+     * The device only sets the 'I' bit in the PIO Setup FIS for device->host
+     * requests (see "DPIOI1" in the SATA spec), or for host->device PIO requests
+     * after the first (see "DPIOO1").  The latter is consistent with the spec's
+     * description of the PACKET protocol, where the command part of ATAPI requests
+     * ("DPKT0") has the 'I' bit clear, while the data part of PIO ATAPI requests
+     * ("DPKT4a" and "DPKT7") has the 'I' bit set for both directions.
+     */
+    pio_fis_i = is_atapi ? ad->done_atapi_packet : !is_write;
+    ahci_write_fis_pio(ad, size, pio_fis_i);
 
     if (is_atapi && !ad->done_atapi_packet) {
         /* already prepopulated iobuffer */
@@ -1379,9 +1388,14 @@ static void ahci_pio_transfer(IDEDMA *dma)
 
     /* Update number of transferred bytes, destroy sglist */
     dma_buf_commit(s, size);
+
 out:
     /* declare that we processed everything */
     s->data_ptr = s->data_end;
+
+    if (pio_fis_i) {
+        ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_PSS);
+    }
 }
 
 static void ahci_start_dma(IDEDMA *dma, IDEState *s,
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
index 7264e085d0..0e295ce31a 100644
--- a/tests/libqos/ahci.c
+++ b/tests/libqos/ahci.c
@@ -651,10 +651,7 @@ void ahci_exec(AHCIQState *ahci, uint8_t port,
     /* Command creation */
     if (opts->atapi) {
         uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE;
-        cmd = ahci_atapi_command_create(op, bcl);
-        if (opts->atapi_dma) {
-            ahci_command_enable_atapi_dma(cmd);
-        }
+        cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma);
     } else {
         cmd = ahci_command_create(op);
     }
@@ -874,7 +871,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
     /* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */
     /* BUG: We expect the DMA Setup interrupt for DMA commands */
     /* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */
-    cmd->interrupts |= props->pio ? AHCI_PX_IS_PSS : 0;
+    cmd->interrupts |= (props->pio && props->read) ? AHCI_PX_IS_PSS : 0;
     cmd->interrupts |= props->ncq ? AHCI_PX_IS_SDBS : 0;
 
     command_header_init(cmd);
@@ -883,19 +880,24 @@ AHCICommand *ahci_command_create(uint8_t command_name)
     return cmd;
 }
 
-AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl)
+AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma)
 {
     AHCICommand *cmd = ahci_command_create(CMD_PACKET);
     cmd->atapi_cmd = g_malloc0(16);
     cmd->atapi_cmd[0] = scsi_cmd;
     stw_le_p(&cmd->fis.lba_lo[1], bcl);
+    if (dma) {
+        ahci_command_enable_atapi_dma(cmd);
+    } else {
+        cmd->interrupts |= bcl ? AHCI_PX_IS_PSS : 0;
+    }
     return cmd;
 }
 
 void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port,
                            bool ready, uint8_t expected_sense)
 {
-    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0);
+    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0, false);
     ahci_command_set_size(cmd, 0);
     if (!ready) {
         cmd->interrupts |= AHCI_PX_IS_TFES;
@@ -937,7 +939,7 @@ void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
 
 void ahci_atapi_eject(AHCIQState *ahci, uint8_t port)
 {
-    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0);
+    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false);
     ahci_command_set_size(cmd, 0);
 
     cmd->atapi_cmd[4] = 0x02; /* loej = true */
@@ -949,7 +951,7 @@ void ahci_atapi_eject(AHCIQState *ahci, uint8_t port)
 
 void ahci_atapi_load(AHCIQState *ahci, uint8_t port)
 {
-    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0);
+    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false);
     ahci_command_set_size(cmd, 0);
 
     cmd->atapi_cmd[4] = 0x03; /* loej,start = true */
diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h
index 13f6d87b75..f05b3e5fce 100644
--- a/tests/libqos/ahci.h
+++ b/tests/libqos/ahci.h
@@ -622,7 +622,7 @@ void ahci_atapi_load(AHCIQState *ahci, uint8_t port);
 
 /* Command: Fine-grained lifecycle */
 AHCICommand *ahci_command_create(uint8_t command_name);
-AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl);
+AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma);
 void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
 void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
 void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
-- 
2.17.0


Re: [Qemu-devel] [PATCH] ahci: fix FIS I bit and PIO Setup FIS interrupt
Posted by John Snow 5 years, 10 months ago

On 06/20/2018 09:25 AM, Paolo Bonzini wrote:
> +    pio_fis_i = is_atapi ? ad->done_atapi_packet : !is_write;

Per DPIOO1, does this go to false for the first DRQ block, or did I
misunderstand? Currently my understanding:

- device->host
	DPIOI1
	Interrupt bit shall be set.
- host->device:
	DPIOO1:
	0 for first block, 1 otherwise
- ATAPI:
	0 for packet itself
	1 for all data otherwise.

Yes?



Re: [Qemu-devel] [PATCH] ahci: fix FIS I bit and PIO Setup FIS interrupt
Posted by Paolo Bonzini 5 years, 10 months ago
On 21/06/2018 22:06, John Snow wrote:
> 
> On 06/20/2018 09:25 AM, Paolo Bonzini wrote:
>> +    pio_fis_i = is_atapi ? ad->done_atapi_packet : !is_write;
> Per DPIOO1, does this go to false for the first DRQ block, or did I
> misunderstand? Currently my understanding:

DPIOO1 is the !is_atapi && is_write case, where I is currently always 0.
 When do we have more than one DRQ block, is it for multi-sector PIO
reads?  Then perhaps we need something like ad->command->done_first_pio.

Paolo

> - device->host
> 	DPIOI1
> 	Interrupt bit shall be set.
> - host->device:
> 	DPIOO1:
> 	0 for first block, 1 otherwise
> - ATAPI:
> 	0 for packet itself
> 	1 for all data otherwise.


Re: [Qemu-devel] [PATCH] ahci: fix FIS I bit and PIO Setup FIS interrupt
Posted by John Snow 5 years, 10 months ago

On 06/22/2018 04:58 AM, Paolo Bonzini wrote:
> On 21/06/2018 22:06, John Snow wrote:
>>
>> On 06/20/2018 09:25 AM, Paolo Bonzini wrote:
>>> +    pio_fis_i = is_atapi ? ad->done_atapi_packet : !is_write;
>> Per DPIOO1, does this go to false for the first DRQ block, or did I
>> misunderstand? Currently my understanding:
> 
> DPIOO1 is the !is_atapi && is_write case, where I is currently always 0.
>  When do we have more than one DRQ block, is it for multi-sector PIO
> reads?  Then perhaps we need something like ad->command->done_first_pio.
> 
> Paolo
> 

cmd_read_pio: req_nb_sectors = 1
ide_sector_read:
	sector_num = ide_get_sector(s) (LBA offset)
	n = s->nsector (1 or more sectors)
	but then we clamp it to s->req_nb_sectors, which is 1 here,
	then we build an SGlist pointed to s->io_buffer;

s->io_buffer_total_len = IDE_DMA_BUF_SECTORS*512 + 4;

Oh, actually our buffer here is quite big, 256 sectors plus four extra
bytes that Fabrice never explained.

Max request size for lba28 is going to be 256 sectors on the button, but
64K for lba48. I don't remember immediately if there is some
spec-mandated limit on how large a single DRQ block can be for PATA or SATA.

IDENTIFY Word 47 specifies how many for READ/WRITE Multiple, so I'm
intuiting here that READ/WRITE implicitly mandate one sector per DRQ block.

>> - device->host
>> 	DPIOI1
>> 	Interrupt bit shall be set.
>> - host->device:
>> 	DPIOO1:
>> 	0 for first block, 1 otherwise
>> - ATAPI:
>> 	0 for packet itself
>> 	1 for all data otherwise.
> 

-- 
—js