libqos/ahci: Add cmd response sanity check helpers
This patch adds a few helpers to help sanity-check the response of the AHCI device after a command. ahci_d2h_check_sanity inspects the D2H Register FIS, ahci_pio_check_sanity inspects the PIO Setup FIS, and ahci_cmd_check_sanity inspects the command header. To support the PIO sanity check, a new structure is added for the PIO Setup FIS type. Existing FIS types (H2D and D2H) have had their members renamed slightly to condense reserved members into fewer fields; and LBA fields are now represented by arrays of 8 byte chunks instead of independent variables. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Message-id: 1423158090-25580-9-git-send-email-jsnow@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
89a4672366
commit
d1ef883894
@ -657,12 +657,10 @@ static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port)
|
||||
*/
|
||||
static void ahci_test_identify(AHCIQState *ahci)
|
||||
{
|
||||
RegD2HFIS *d2h = g_malloc0(0x20);
|
||||
RegD2HFIS *pio = g_malloc0(0x20);
|
||||
RegH2DFIS fis;
|
||||
AHCICommandHeader cmd;
|
||||
PRD prd;
|
||||
uint32_t reg, data_ptr;
|
||||
uint32_t data_ptr;
|
||||
uint16_t buff[256];
|
||||
unsigned i;
|
||||
int rc;
|
||||
@ -752,27 +750,11 @@ static void ahci_test_identify(AHCIQState *ahci)
|
||||
/* BUG: we expect AHCI_PX_IS_DPS to be set. */
|
||||
ahci_port_check_interrupts(ahci, i, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS);
|
||||
ahci_port_check_nonbusy(ahci, i, cx);
|
||||
|
||||
/* Investigate the CMD, assert that we read 512 bytes */
|
||||
ahci_get_command_header(ahci, i, cx, &cmd);
|
||||
g_assert_cmphex(512, ==, cmd.prdbc);
|
||||
|
||||
ahci_port_check_cmd_sanity(ahci, i, cx, 512);
|
||||
/* Investigate FIS responses */
|
||||
memread(ahci->port[i].fb + 0x20, pio, 0x20);
|
||||
memread(ahci->port[i].fb + 0x40, d2h, 0x20);
|
||||
g_assert_cmphex(pio->fis_type, ==, 0x5f);
|
||||
g_assert_cmphex(d2h->fis_type, ==, 0x34);
|
||||
g_assert_cmphex(pio->flags, ==, d2h->flags);
|
||||
g_assert_cmphex(pio->status, ==, d2h->status);
|
||||
g_assert_cmphex(pio->error, ==, d2h->error);
|
||||
|
||||
reg = ahci_px_rreg(ahci, i, AHCI_PX_TFD);
|
||||
g_assert_cmphex((reg & AHCI_PX_TFD_ERR), ==, pio->error);
|
||||
g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, pio->status);
|
||||
/* The PIO Setup FIS contains a "bytes read" field, which is a
|
||||
* 16-bit value. The Physical Region Descriptor Byte Count is
|
||||
* 32-bit, but for small transfers using one PRD, it should match. */
|
||||
g_assert_cmphex(le16_to_cpu(pio->res4), ==, cmd.prdbc);
|
||||
ahci_port_check_d2h_sanity(ahci, i, cx);
|
||||
ahci_port_check_pio_sanity(ahci, i, cx, 512);
|
||||
|
||||
/* Last, but not least: Investigate the IDENTIFY response data. */
|
||||
memread(data_ptr, &buff, 512);
|
||||
@ -789,9 +771,6 @@ static void ahci_test_identify(AHCIQState *ahci)
|
||||
string_bswap16(&buff[23], 8);
|
||||
rc = memcmp(&buff[23], "version ", 8);
|
||||
g_assert_cmphex(rc, ==, 0);
|
||||
|
||||
g_free(d2h);
|
||||
g_free(pio);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -365,6 +365,53 @@ void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot)
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_DRQ);
|
||||
}
|
||||
|
||||
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot)
|
||||
{
|
||||
RegD2HFIS *d2h = g_malloc0(0x20);
|
||||
uint32_t reg;
|
||||
|
||||
memread(ahci->port[port].fb + 0x40, d2h, 0x20);
|
||||
g_assert_cmphex(d2h->fis_type, ==, 0x34);
|
||||
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||
g_assert_cmphex((reg & AHCI_PX_TFD_ERR) >> 8, ==, d2h->error);
|
||||
g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, d2h->status);
|
||||
|
||||
g_free(d2h);
|
||||
}
|
||||
|
||||
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, size_t buffsize)
|
||||
{
|
||||
PIOSetupFIS *pio = g_malloc0(0x20);
|
||||
|
||||
/* We cannot check the Status or E_Status registers, becuase
|
||||
* the status may have again changed between the PIO Setup FIS
|
||||
* and the conclusion of the command with the D2H Register FIS. */
|
||||
memread(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);
|
||||
}
|
||||
|
||||
g_free(pio);
|
||||
}
|
||||
|
||||
void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, size_t buffsize)
|
||||
{
|
||||
AHCICommandHeader cmd;
|
||||
|
||||
ahci_get_command_header(ahci, port, slot, &cmd);
|
||||
g_assert_cmphex(buffsize, ==, cmd.prdbc);
|
||||
}
|
||||
|
||||
/* Get the command in #slot of port #port. */
|
||||
void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, AHCICommandHeader *cmd)
|
||||
|
@ -283,24 +283,43 @@ typedef struct RegD2HFIS {
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
/* DW1 */
|
||||
uint8_t lba_low;
|
||||
uint8_t lba_mid;
|
||||
uint8_t lba_high;
|
||||
uint8_t lba_lo[3];
|
||||
uint8_t device;
|
||||
/* DW2 */
|
||||
uint8_t lba3;
|
||||
uint8_t lba4;
|
||||
uint8_t lba5;
|
||||
uint8_t res1;
|
||||
uint8_t lba_hi[3];
|
||||
uint8_t res0;
|
||||
/* DW3 */
|
||||
uint16_t count;
|
||||
uint8_t res2;
|
||||
uint8_t res3;
|
||||
uint16_t res1;
|
||||
/* DW4 */
|
||||
uint16_t res4;
|
||||
uint16_t res5;
|
||||
uint32_t res2;
|
||||
} __attribute__((__packed__)) RegD2HFIS;
|
||||
|
||||
/**
|
||||
* Register device-to-host FIS structure;
|
||||
* PIO Setup variety.
|
||||
*/
|
||||
typedef struct PIOSetupFIS {
|
||||
/* DW0 */
|
||||
uint8_t fis_type;
|
||||
uint8_t flags;
|
||||
uint8_t status;
|
||||
uint8_t error;
|
||||
/* DW1 */
|
||||
uint8_t lba_lo[3];
|
||||
uint8_t device;
|
||||
/* DW2 */
|
||||
uint8_t lba_hi[3];
|
||||
uint8_t res0;
|
||||
/* DW3 */
|
||||
uint16_t count;
|
||||
uint8_t res1;
|
||||
uint8_t e_status;
|
||||
/* DW4 */
|
||||
uint16_t tx_count;
|
||||
uint16_t res2;
|
||||
} __attribute__((__packed__)) PIOSetupFIS;
|
||||
|
||||
/**
|
||||
* Register host-to-device FIS structure.
|
||||
*/
|
||||
@ -311,14 +330,10 @@ typedef struct RegH2DFIS {
|
||||
uint8_t command;
|
||||
uint8_t feature_low;
|
||||
/* DW1 */
|
||||
uint8_t lba_low;
|
||||
uint8_t lba_mid;
|
||||
uint8_t lba_high;
|
||||
uint8_t lba_lo[3];
|
||||
uint8_t device;
|
||||
/* DW2 */
|
||||
uint8_t lba3;
|
||||
uint8_t lba4;
|
||||
uint8_t lba5;
|
||||
uint8_t lba_hi[3];
|
||||
uint8_t feature_high;
|
||||
/* DW3 */
|
||||
uint16_t count;
|
||||
@ -437,6 +452,11 @@ void ahci_port_check_error(AHCIQState *ahci, uint8_t port);
|
||||
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_cmd_sanity(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, size_t buffsize);
|
||||
void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t slot, AHCICommandHeader *cmd);
|
||||
void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
|
||||
|
Loading…
x
Reference in New Issue
Block a user