build_cmd_table is a helper routine to build the command table, which consists of the Command FIS and the Physical Region Descriptor Table.
Signed-off-by: John Snow <js...@redhat.com> --- tests/ahci-test.c | 113 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 7a62eef..f07495f 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1350,6 +1350,67 @@ static unsigned pick_cmd(AHCIState *ahci, uint8_t px) g_assert_not_reached(); } +static inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd) +{ + /* Each PRD can describe up to 4MiB */ + g_assert_cmphex(bytes_per_prd, <=, 4096 * 1024); + g_assert_cmphex(bytes_per_prd & 0x01, ==, 0x00); + return (bytes + bytes_per_prd - 1) / bytes_per_prd; +} + +static uint64_t build_cmd_table(AHCIState *ahci, uint8_t px, size_t sz, + uint8_t cmd, uint64_t buffer) +{ + PRD prd; + RegH2DFIS fis; + + const unsigned bytes_per_prd = 4096; + unsigned prdtl = size_to_prdtl(sz, bytes_per_prd); + unsigned table_size = CMD_TBL_SIZ(prdtl); + uint64_t table_ptr; + unsigned remaining, i; + + /* Create a Command Table buffer. 0x80 for the bulk, + * plus enough room for `prdtl` entries. */ + table_ptr = guest_alloc(guest_malloc, table_size); + g_assert(table_ptr); + /* AHCI 1.3 spec states that it must be 0x80 aligned. */ + ASSERT_BIT_CLEAR(table_ptr, 0x7F); + + /* Construct our Command FIS */ + memset(&fis, 0x00, sizeof(fis)); + fis.fis_type = 0x27; /* Register Host-to-Device FIS */ + fis.flags = 0x80; /* Command FIS. ('C bit') */ + fis.command = cmd; /* e.g, 0xEC : Identify */ + fis.count = cpu_to_le16(sz / 512); + + /* Construct the PRDs. */ + remaining = sz; + for (i = 0; i < prdtl; ++i) { + prd.dba = cpu_to_le64(buffer + (bytes_per_prd * i)); + prd.res = 0; + if (remaining > bytes_per_prd) { + /* Note that byte count is 0-based. */ + prd.dbc = cpu_to_le32(bytes_per_prd - 1); + remaining -= bytes_per_prd; + } else { + /* Again, dbc is 0-based. */ + prd.dbc = cpu_to_le32(remaining - 1); + remaining = 0; + } + prd.dbc |= cpu_to_le32(0x80000000); /* Request DPS Interrupt */ + + /* Commit the PRD entry to the Command Table */ + memwrite(table_ptr + 0x80 + (i * sizeof(PRD)), + &prd, sizeof(PRD)); + } + + /* Commit the Command FIS to the Command Table */ + memwrite(table_ptr, &fis, sizeof(fis)); + + return table_ptr; +} + /** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. @@ -1358,9 +1419,7 @@ static void ahci_test_identify(AHCIState *ahci) { RegD2HFIS *d2h = g_malloc0(0x20); RegD2HFIS *pio = g_malloc0(0x20); - RegH2DFIS fis; AHCICommand cmd; - PRD prd; uint32_t reg, data_ptr; uint16_t buff[256]; unsigned i; @@ -1371,17 +1430,18 @@ static void ahci_test_identify(AHCIState *ahci) g_assert(ahci != NULL); /* We need to: - * (1) Create a Command Table Buffer and update the Command List Slot #0 - * to point to this buffer. - * (2) Construct an FIS host-to-device command structure, and write it to - * the top of the command table buffer. - * (3) Create a data buffer for the IDENTIFY response to be sent to + * (1) Create a data buffer for the IDENTIFY response to be sent to, + * (2) Create a Command Table Buffer + * (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) Now, PxCLB points to the command list, command 0 points to + * 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. - * (6) We inform the HBA via PxCI that there is a command ready in slot #0. + * (7) We inform the HBA via PxCI that there is a command ready in slot #0. */ /* Pick the first implemented and running port */ @@ -1391,16 +1451,13 @@ static void ahci_test_identify(AHCIState *ahci) /* Clear out the FIS Receive area and any pending interrupts. */ port_clear(ahci, i); - /* Create a Command Table buffer. 0x80 is the smallest with a PRDTL of 0. */ - /* We need at least one PRD, so round up to the nearest 0x80 multiple. */ - table_ptr = guest_alloc(guest_malloc, CMD_TBL_SIZ(1)); - g_assert(table_ptr); - ASSERT_BIT_CLEAR(table_ptr, 0x7F); - - /* Create a data buffer ... where we will dump the IDENTIFY data to. */ + /* Create a data buffer where we will dump the IDENTIFY data to. */ data_ptr = guest_alloc(guest_malloc, 512); g_assert(data_ptr); + /* Construct the Command Table (FIS and PRDT) */ + table_ptr = build_cmd_table(ahci, i, 512, 0xEC, data_ptr); + /* pick a command slot (should be 0!) */ cx = pick_cmd(ahci, i); @@ -1412,28 +1469,6 @@ static void ahci_test_identify(AHCIState *ahci) cmd.prdbc = 0; cmd.ctba = table_ptr; - /* Construct our PRD, noting that DBC is 0-indexed. */ - prd.dba = cpu_to_le64(data_ptr); - prd.res = 0; - /* 511+1 bytes, request DPS interrupt */ - prd.dbc = cpu_to_le32(511 | 0x80000000); - - /* Construct our Command FIS, Based on http://wiki.osdev.org/AHCI */ - memset(&fis, 0x00, sizeof(fis)); - fis.fis_type = 0x27; /* Register Host-to-Device FIS */ - fis.command = 0xEC; /* IDENTIFY */ - fis.device = 0; - fis.flags = 0x80; /* Indicate this is a command FIS */ - - /* We've committed nothing yet, no interrupts should be posted yet. */ - g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); - - /* Commit the Command FIS to the Command Table */ - memwrite(table_ptr, &fis, sizeof(fis)); - - /* Commit the PRD entry to the Command Table */ - memwrite(table_ptr + 0x80, &prd, sizeof(prd)); - /* Commit Command #cx, pointing to the Table, to the Command List Buffer. */ set_command_header(ahci, i, cx, &cmd); -- 1.9.3