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 <js...@redhat.com> Reviewed-by: Paolo Bonzini <pbonz...@redhat.com> --- tests/ahci-test.c | 29 ++++------------------------ tests/libqos/ahci.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ tests/libqos/ahci.h | 54 ++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 88 insertions(+), 42 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 4cc7e21..b67d935 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -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); } /******************************************************************************/ diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index db3055f..36a7c93 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -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) diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index eaad076..f17aa23 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -283,25 +283,44 @@ 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. */ typedef struct RegH2DFIS { @@ -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, -- 1.9.3