"-device ide-hd,drive=drive0 "
"-global ide-hd.ver=%s";
s->parent = qtest_pc_boot(cli, tmp_path, "testdisk", "version");
+ alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT);
/* Verify that we have an AHCI device present. */
s->dev = get_ahci_device(&s->fingerprint);
static void ahci_shutdown(AHCIQState *ahci)
{
QOSState *qs = ahci->parent;
+
+ ahci_clean_mem(ahci);
free_ahci_device(ahci->dev);
g_free(ahci);
qtest_shutdown(qs);
*/
static void ahci_test_identify(AHCIQState *ahci)
{
- uint32_t data_ptr;
uint16_t buff[256];
- unsigned i;
+ unsigned px;
int rc;
- AHCICommand *cmd;
- uint8_t cx;
+ uint16_t sect_size;
+ const size_t buffsize = 512;
g_assert(ahci != NULL);
- /* We need to:
- * (1) Create a data buffer for the IDENTIFY response to be sent to,
- * (2) Create a Command Table Buffer
+ /**
+ * This serves as a bit of a tutorial on AHCI device programming:
+ *
+ * (1) Create a data buffer for the IDENTIFY response to be sent to
+ * (2) Create a Command Table buffer, where we will store the
+ * command and PRDT (Physical Region Descriptor Table)
* (3) Construct an FIS host-to-device command structure, and write it to
- * the top of the command table buffer.
- * (4) Create a Physical Region Descriptor that points to the data buffer,
- * and write it to the bottom (offset 0x80) of the command table.
- * (5) Obtain a Command List slot, and update this header to point to
- * the Command Table we built above.
- * (6) Now, PxCLB points to the command list, command 0 points to
- * our table, and our table contains an FIS instruction and a
- * PRD that points to our rx buffer.
- * (7) We inform the HBA via PxCI that there is a command ready in slot #0.
+ * the top of the Command Table buffer.
+ * (4) Create one or more Physical Region Descriptors (PRDs) that describe
+ * a location in memory where data may be stored/retrieved.
+ * (5) Write these PRDTs to the bottom (offset 0x80) of the Command Table.
+ * (6) Each AHCI port has up to 32 command slots. Each slot contains a
+ * header that points to a Command Table buffer. Pick an unused slot
+ * and update it to point to the Command Table we have built.
+ * (7) Now: Command #n points to our Command Table, and our Command Table
+ * contains the FIS (that describes our command) and the PRDTL, which
+ * describes our buffer.
+ * (8) We inform the HBA via PxCI (Command Issue) that the command in slot
+ * #n is ready for processing.
*/
/* Pick the first implemented and running port */
- i = ahci_port_select(ahci);
- g_test_message("Selected port %u for test", i);
+ px = ahci_port_select(ahci);
+ g_test_message("Selected port %u for test", px);
/* Clear out the FIS Receive area and any pending interrupts. */
- ahci_port_clear(ahci, i);
-
- /* Create a data buffer where we will dump the IDENTIFY data to. */
- data_ptr = ahci_alloc(ahci, 512);
- g_assert(data_ptr);
-
- /* Construct the Command Table (FIS and PRDT) and Command Header */
- cmd = ahci_command_create(CMD_IDENTIFY);
- ahci_command_set_buffer(cmd, data_ptr);
- /* Write the command header and PRDT to guest memory */
- ahci_command_commit(ahci, cmd, i);
-
- /* Everything is in place, but we haven't given the go-ahead yet,
- * so we should find that there are no pending interrupts yet. */
- g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_IS), ==, 0);
-
- /* Issue Command #cx via PxCI */
- ahci_command_issue(ahci, cmd);
- cx = ahci_command_slot(cmd);
-
- /* Check registers for post-command consistency */
- ahci_port_check_error(ahci, i);
- /* 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_port_check_cmd_sanity(ahci, i, cx, 512);
- /* Investigate FIS responses */
- 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);
+ ahci_port_clear(ahci, px);
+
+ /* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */
+ ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize);
/* Check serial number/version in the buffer */
/* NB: IDENTIFY strings are packed in 16bit little endian chunks.
string_bswap16(&buff[23], 8);
rc = memcmp(&buff[23], "version ", 8);
g_assert_cmphex(rc, ==, 0);
+
+ sect_size = le16_to_cpu(*((uint16_t *)(&buff[5])));
+ g_assert_cmphex(sect_size, ==, 0x200);
+}
+
+static void ahci_test_dma_rw_simple(AHCIQState *ahci)
+{
+ uint64_t ptr;
+ uint8_t port;
+ unsigned i;
+ const unsigned bufsize = 4096;
+ unsigned char *tx = g_malloc(bufsize);
+ unsigned char *rx = g_malloc0(bufsize);
+
+ g_assert(ahci != NULL);
+
+ /* Pick the first running port and clear it. */
+ port = ahci_port_select(ahci);
+ ahci_port_clear(ahci, port);
+
+ /*** Create pattern and transfer to guest ***/
+ /* Data buffer in the guest */
+ ptr = ahci_alloc(ahci, bufsize);
+ g_assert(ptr);
+
+ /* Write some indicative pattern to our 4K buffer. */
+ for (i = 0; i < bufsize; i++) {
+ tx[i] = (bufsize - i);
+ }
+ memwrite(ptr, tx, bufsize);
+
+ /* Write this buffer to disk, then read it back to the DMA buffer. */
+ ahci_guest_io(ahci, port, CMD_WRITE_DMA, ptr, bufsize);
+ qmemset(ptr, 0x00, bufsize);
+ ahci_guest_io(ahci, port, CMD_READ_DMA, ptr, bufsize);
+
+ /*** Read back the Data ***/
+ memread(ptr, rx, bufsize);
+ g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0);
+
+ ahci_free(ahci, ptr);
+ g_free(tx);
+ g_free(rx);
}
/******************************************************************************/
ahci_shutdown(ahci);
}
+/**
+ * Perform a simple DMA R/W test, using a single PRD and non-NCQ commands.
+ */
+static void test_dma_rw_simple(void)
+{
+ AHCIQState *ahci;
+
+ ahci = ahci_boot();
+ ahci_pci_enable(ahci);
+ ahci_hba_enable(ahci);
+ ahci_test_dma_rw_simple(ahci);
+ ahci_shutdown(ahci);
+}
+
/******************************************************************************/
int main(int argc, char **argv)
qtest_add_func("/ahci/hba_spec", test_hba_spec);
qtest_add_func("/ahci/hba_enable", test_hba_enable);
qtest_add_func("/ahci/identify", test_identify);
+ qtest_add_func("/ahci/dma/simple", test_dma_rw_simple);
ret = g_test_run();