From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:44254) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XUlNC-00065p-Jk for qemu-devel@nongnu.org; Thu, 18 Sep 2014 19:44:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XUlN4-0003N8-MT for qemu-devel@nongnu.org; Thu, 18 Sep 2014 19:44:26 -0400 Received: from mx1.redhat.com ([209.132.183.28]:61946) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XUlN4-0003Js-F3 for qemu-devel@nongnu.org; Thu, 18 Sep 2014 19:44:18 -0400 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id s8INiC2X014475 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Thu, 18 Sep 2014 19:44:13 -0400 From: John Snow Date: Thu, 18 Sep 2014 19:43:28 -0400 Message-Id: <1411083819-9284-5-git-send-email-jsnow@redhat.com> In-Reply-To: <1411083819-9284-1-git-send-email-jsnow@redhat.com> References: <1411083819-9284-1-git-send-email-jsnow@redhat.com> Subject: [Qemu-devel] [PATCH 04/15] qtest/ahci: Add command header helpers List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org Cc: kwolf@redhat.com, mst@redhat.com, armbru@redhat.com, stefanha@redhat.com, pbonzini@redhat.com, John Snow Add command_header_set, command_header_get, command_destroy and cmd_pick to help quickly manage the command header information in the AHCI device. command_header_set and get will store or retrieve an AHCI command header, respectively. cmd_pick chooses the first available but least recently used command slot to allow us to cycle through the available command slots. command_destroy obliterates all information contained within a given slot's command header, and frees its associated command table, but not its DMA buffer! Signed-off-by: John Snow --- tests/ahci-test.c | 132 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 104 insertions(+), 28 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 77e3963..7a62eef 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -257,6 +257,9 @@ typedef struct AHCIPortState { uint64_t fb; uint64_t clb; + uint64_t ctba[32]; + uint16_t prdtl[32]; + uint8_t next; /** Next Command Slot to Use **/ } AHCIPortState; typedef struct AHCIState { @@ -342,8 +345,7 @@ typedef struct AHCICommand { uint8_t b2; uint16_t prdtl; /* Phys Region Desc. Table Length */ uint32_t prdbc; /* Phys Region Desc. Byte Count */ - uint32_t ctba; /* Command Table Descriptor Base Address */ - uint32_t ctbau; /* '' Upper */ + uint64_t ctba; /* Command Table Descriptor Base Address */ uint32_t res[4]; } __attribute__((__packed__)) AHCICommand; @@ -352,11 +354,10 @@ typedef struct AHCICommand { * struct ahci_command. */ typedef struct PRD { - uint32_t dba; /* Data Base Address */ - uint32_t dbau; /* Data Base Address Upper */ + uint64_t dba; /* Data Base Address */ uint32_t res; /* Reserved */ uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */ -} PRD; +} __attribute__((__packed__)) PRD; /*** Globals ***/ static QGuestAllocator *guest_malloc; @@ -1277,6 +1278,78 @@ static void port_clear(AHCIState *ahci, uint8_t px) qmemset(ahci->port[px].fb, 0x00, 0x100); } +/* Get the #cx'th command of port #px. */ +static void get_command_header(AHCIState *ahci, uint8_t px, + uint8_t cx, AHCICommand *cmd) +{ + uint64_t ba = ahci->port[px].clb; + ba += cx * sizeof(AHCICommand); + memread(ba, cmd, sizeof(AHCICommand)); + + cmd->prdtl = le16_to_cpu(cmd->prdtl); + cmd->prdbc = le32_to_cpu(cmd->prdbc); + cmd->ctba = le64_to_cpu(cmd->ctba); +} + +/* Set the #cx'th command of port #px. */ +static void set_command_header(AHCIState *ahci, uint8_t px, + uint8_t cx, AHCICommand *cmd) +{ + uint64_t ba = ahci->port[px].clb; + ba += cx * sizeof(AHCICommand); + + cmd->prdtl = cpu_to_le16(cmd->prdtl); + cmd->prdbc = cpu_to_le32(cmd->prdbc); + cmd->ctba = cpu_to_le64(cmd->ctba); + + memwrite(ba, cmd, sizeof(AHCICommand)); +} + +static void destroy_command(AHCIState *ahci, uint8_t px, uint8_t cx) +{ + AHCICommand cmd; + + /* Obtain the Nth Command Header */ + get_command_header(ahci, px, cx, &cmd); + if (cmd.ctba == 0) { + /* No address in it, so just return -- it's empty. */ + goto tidy; + } + + /* Free the Table */ + guest_free(guest_malloc, cmd.ctba); + + tidy: + /* NULL the header. */ + memset(&cmd, 0x00, sizeof(cmd)); + set_command_header(ahci, px, cx, &cmd); + ahci->port[px].ctba[cx] = 0; + ahci->port[px].prdtl[cx] = 0; +} + +static unsigned pick_cmd(AHCIState *ahci, uint8_t px) +{ + unsigned i; + unsigned j; + uint32_t reg; + + reg = PX_RREG(px, AHCI_PX_CI); + + /* Pick the least recently used command slot that's available */ + for (i = 0; i < 32; ++i) { + j = ((ahci->port[px].next + i) % 32); + if (reg & (1 << j)) { + continue; + } + destroy_command(ahci, px, i); + ahci->port[px].next = (j + 1) % 32; + return j; + } + + g_test_message("All command slots were busy."); + g_assert_not_reached(); +} + /** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. @@ -1288,10 +1361,12 @@ static void ahci_test_identify(AHCIState *ahci) RegH2DFIS fis; AHCICommand cmd; PRD prd; - uint32_t reg, table, data_ptr; + uint32_t reg, data_ptr; uint16_t buff[256]; unsigned i; int rc; + uint8_t cx; + uint64_t table_ptr; g_assert(ahci != NULL); @@ -1318,27 +1393,27 @@ static void ahci_test_identify(AHCIState *ahci) /* 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 = guest_alloc(guest_malloc, CMD_TBL_SIZ(1)); - g_assert(table); - ASSERT_BIT_CLEAR(table, 0x7F); + 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. */ data_ptr = guest_alloc(guest_malloc, 512); g_assert(data_ptr); - /* Copy the existing Command #0 structure from the CLB into local memory, - * and build a new command #0. */ - memread(ahci->port[i].clb, &cmd, sizeof(cmd)); + /* pick a command slot (should be 0!) */ + cx = pick_cmd(ahci, i); + + /* Construct our Command Header (set_command_header handles endianness.) */ + memset(&cmd, 0x00, sizeof(cmd)); cmd.b1 = 5; /* reg_h2d_fis is 5 double-words long */ cmd.b2 = 0x04; /* clear PxTFD.STS.BSY when done */ - cmd.prdtl = cpu_to_le16(1); /* One PRD table entry. */ + cmd.prdtl = 1; /* One PRD table entry. */ cmd.prdbc = 0; - cmd.ctba = cpu_to_le32(table); - cmd.ctbau = 0; + cmd.ctba = table_ptr; /* Construct our PRD, noting that DBC is 0-indexed. */ - prd.dba = cpu_to_le32(data_ptr); - prd.dbau = 0; + prd.dba = cpu_to_le64(data_ptr); prd.res = 0; /* 511+1 bytes, request DPS interrupt */ prd.dbc = cpu_to_le32(511 | 0x80000000); @@ -1354,19 +1429,20 @@ static void ahci_test_identify(AHCIState *ahci) g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); /* Commit the Command FIS to the Command Table */ - memwrite(table, &fis, sizeof(fis)); + memwrite(table_ptr, &fis, sizeof(fis)); /* Commit the PRD entry to the Command Table */ - memwrite(table + 0x80, &prd, sizeof(prd)); + memwrite(table_ptr + 0x80, &prd, sizeof(prd)); - /* Commit Command #0, pointing to the Table, to the Command List Buffer. */ - memwrite(ahci->port[i].clb, &cmd, sizeof(cmd)); + /* Commit Command #cx, pointing to the Table, to the Command List Buffer. */ + set_command_header(ahci, i, cx, &cmd); - /* Everything is in place, but we haven't given the go-ahead yet. */ + /* 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(PX_RREG(i, AHCI_PX_IS), ==, 0); - /* Issue Command #0 via PxCI */ - PX_WREG(i, AHCI_PX_CI, (1 << 0)); + /* Issue Command #cx via PxCI */ + PX_WREG(i, AHCI_PX_CI, (1 << cx)); while (BITSET(PX_RREG(i, AHCI_PX_TFD), AHCI_PX_TFD_STS_BSY)) { usleep(50); } @@ -1389,9 +1465,9 @@ static void ahci_test_identify(AHCIState *ahci) ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); - /* Investigate CMD #0, assert that we read 512 bytes */ - memread(ahci->port[i].clb, &cmd, sizeof(cmd)); - g_assert_cmphex(512, ==, le32_to_cpu(cmd.prdbc)); + /* Investigate the CMD, assert that we read 512 bytes */ + get_command_header(ahci, i, cx, &cmd); + g_assert_cmphex(512, ==, cmd.prdbc); /* Investigate FIS responses */ memread(ahci->port[i].fb + 0x20, pio, 0x20); @@ -1408,7 +1484,7 @@ static void ahci_test_identify(AHCIState *ahci) /* 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), ==, le32_to_cpu(cmd.prdbc)); + g_assert_cmphex(le16_to_cpu(pio->res4), ==, cmd.prdbc); /* Last, but not least: Investigate the IDENTIFY response data. */ memread(data_ptr, &buff, 512); -- 1.9.3