Skip to content

Commit

Permalink
ahci: move PIO Setup FIS before transfer, fix it for ATAPI commands
Browse files Browse the repository at this point in the history
The PIO Setup FIS is written in the PIO:Entry state, which comes before
the ATA and ATAPI data transfer states.  As a result, the PIO Setup FIS
interrupt is now raised before DMA ends for ATAPI commands, and tests have
to be adjusted.

This is also hinted by the description of the command header in the AHCI
specification, where the "A" bit is described as

    When ‘1’, indicates that a PIO setup FIS shall be sent by the device
    indicating a transfer for the ATAPI command.

and also by the description of the ACMD (ATAPI command region):

    The ATAPI command must be either 12 or 16 bytes in length. The length
    transmitted by the HBA is determined by the PIO setup FIS that is sent
    by the device requesting the ATAPI command.

QEMU, which conflates the "generator" and the "receiver" of the FIS into
one device, always uses ATAPI_PACKET_SIZE, aka 12, for the length.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
  • Loading branch information
jnsnow committed Jun 4, 2018
1 parent 99ca77c commit 0657390
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 27 deletions.
16 changes: 5 additions & 11 deletions hw/ide/ahci.c
Expand Up @@ -1259,7 +1259,6 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
g_free(pretty_fis);
}
s->dev[port].done_atapi_packet = false;
/* XXX send PIO setup FIS */
}

ide_state->error = 0;
Expand Down Expand Up @@ -1353,10 +1352,12 @@ static void ahci_start_transfer(IDEDMA *dma)
int is_atapi = opts & AHCI_CMD_ATAPI;
int has_sglist = 0;

/* PIO FIS gets written prior to transfer */
ahci_write_fis_pio(ad, size);

if (is_atapi && !ad->done_atapi_packet) {
/* already prepopulated iobuffer */
ad->done_atapi_packet = true;
size = 0;
goto out;
}

Expand All @@ -1376,19 +1377,12 @@ static void ahci_start_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;

/* Update number of transferred bytes, destroy sglist */
dma_buf_commit(s, size);

s->end_transfer_func(s);

if (!(s->status & DRQ_STAT)) {
/* done with PIO send/receive */
ahci_write_fis_pio(ad, le32_to_cpu(ad->cur_cmd->status));
}
}

static void ahci_start_dma(IDEDMA *dma, IDEState *s,
Expand Down
35 changes: 21 additions & 14 deletions tests/libqos/ahci.c
Expand Up @@ -478,26 +478,33 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot)
g_free(d2h);
}

void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
uint8_t slot, size_t buffsize)
void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd)
{
PIOSetupFIS *pio = g_malloc0(0x20);
uint8_t port = cmd->port;

/* We cannot check the Status or E_Status registers, because
* the status may have again changed between the PIO Setup FIS
* and the conclusion of the command with the D2H Register FIS. */
qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20);
g_assert_cmphex(pio->fis_type, ==, 0x5f);

/* BUG: PIO Setup FIS as utilized by QEMU tries to fit the entire
* transfer size in a uint16_t field. The maximum transfer size can
* eclipse this; the field is meant to convey the size of data per
* each Data FIS, not the entire operation as a whole. For now,
* we will sanity check the broken case where applicable. */
if (buffsize <= UINT16_MAX) {
g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize);
/* Data transferred by PIO will either be:
* (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or
* (2) Actual data from the drive.
* If we do both, (2) winds up erasing any evidence of (1).
*/
if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) {
g_assert(le16_to_cpu(pio->tx_count) == 12 ||
le16_to_cpu(pio->tx_count) == 16);
} else {
/* The AHCI test suite here does not test any PIO command that specifies
* a DRQ block larger than one sector (like 0xC4), so this should always
* be one sector or less. */
size_t pio_len = ((cmd->xbytes % cmd->sector_size) ?
(cmd->xbytes % cmd->sector_size) : cmd->sector_size);
g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, pio_len);
}

g_free(pio);
}

Expand Down Expand Up @@ -832,9 +839,9 @@ void ahci_command_enable_atapi_dma(AHCICommand *cmd)
RegH2DFIS *fis = &(cmd->fis);
g_assert(cmd->props->atapi);
fis->feature_low |= 0x01;
cmd->interrupts &= ~AHCI_PX_IS_PSS;
/* PIO is still used to transfer the ATAPI command */
g_assert(cmd->props->pio);
cmd->props->dma = true;
cmd->props->pio = false;
/* BUG: We expect the DMA Setup interrupt for DMA commands */
/* cmd->interrupts |= AHCI_PX_IS_DSS; */
}
Expand All @@ -846,7 +853,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)

g_assert(props);
cmd = g_new0(AHCICommand, 1);
g_assert(!(props->dma && props->pio));
g_assert(!(props->dma && props->pio) || props->atapi);
g_assert(!(props->lba28 && props->lba48));
g_assert(!(props->read && props->write));
g_assert(!props->size || props->data);
Expand Down Expand Up @@ -1218,7 +1225,7 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
ahci_port_check_d2h_sanity(ahci, port, slot);
}
if (cmd->props->pio) {
ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes);
ahci_port_check_pio_sanity(ahci, cmd);
}
}

Expand Down
3 changes: 1 addition & 2 deletions tests/libqos/ahci.h
Expand Up @@ -596,8 +596,7 @@ void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
uint32_t intr_mask);
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
uint8_t slot, size_t buffsize);
void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);

/* Misc */
Expand Down

0 comments on commit 0657390

Please sign in to comment.