diff options
author | John Snow <jsnow@redhat.com> | 2015-02-05 12:41:15 -0500 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2015-02-16 15:07:16 +0000 |
commit | 6cae27a6af159ab44f7c265d7f22d9e95880db25 (patch) | |
tree | 3235ddb2967e6ebf936fcb05b9925c236f069aa1 | |
parent | c7f9c570b908a844aee393d93d01c332aea2a5a5 (diff) | |
download | qemu-6cae27a6af159ab44f7c265d7f22d9e95880db25.zip qemu-6cae27a6af159ab44f7c265d7f22d9e95880db25.tar.gz qemu-6cae27a6af159ab44f7c265d7f22d9e95880db25.tar.bz2 |
libqos/ahci: Add command header helpers
Adds command header helper functions:
-ahci_command_header_set
-ahci_command_header_get,
-ahci_command_destroy, and
-ahci_cmd_pick
These helpers help to quickly manage the command header information in
the AHCI device.
ahci_command_header_set and get will store or retrieve an AHCI command
header, respectively.
ahci_cmd_pick chooses the first available but least recently used
command slot to allow us to cycle through the available command slots.
ahci_command_destroy obliterates all information contained within a
given slot's command header, and frees its associated command table,
but not its DMA buffer!
Lastly, the command table pointer fields (dba and dbau) are merged into
a single 64bit value to make managing 64bit tests simpler.
Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 1423158090-25580-5-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r-- | tests/ahci-test.c | 43 | ||||
-rw-r--r-- | tests/libqos/ahci.c | 75 | ||||
-rw-r--r-- | tests/libqos/ahci.h | 17 |
3 files changed, 110 insertions, 25 deletions
diff --git a/tests/ahci-test.c b/tests/ahci-test.c index d420e5f..fbf329e 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -662,10 +662,12 @@ static void ahci_test_identify(AHCIQState *ahci) RegH2DFIS fis; AHCICommandHeader 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; g_assert(ahci != NULL); @@ -700,19 +702,19 @@ static void ahci_test_identify(AHCIQState *ahci) data_ptr = ahci_alloc(ahci, 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)); - cmd.flags = cpu_to_le16(5); /* reg_h2d_fis is 5 double-words long */ - cmd.flags |= cpu_to_le16(0x400); /* clear PxTFD.STS.BSY when done */ - cmd.prdtl = cpu_to_le16(1); /* One PRD table entry. */ + /* pick a command slot (should be 0!) */ + cx = ahci_pick_cmd(ahci, i); + + /* Construct our Command Header (set_command_header handles endianness.) */ + memset(&cmd, 0x00, sizeof(cmd)); + cmd.flags = 5; /* reg_h2d_fis is 5 double-words long */ + cmd.flags |= 0x400; /* clear PxTFD.STS.BSY when done */ + cmd.prdtl = 1; /* One PRD table entry. */ cmd.prdbc = 0; - cmd.ctba = cpu_to_le32(table); - cmd.ctbau = 0; + cmd.ctba = table; /* 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); @@ -733,14 +735,15 @@ static void ahci_test_identify(AHCIQState *ahci) /* Commit the PRD entry to the Command Table */ memwrite(table + 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. */ + ahci_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(ahci_px_rreg(ahci, i, AHCI_PX_IS), ==, 0); - /* Issue Command #0 via PxCI */ - ahci_px_wreg(ahci, i, AHCI_PX_CI, (1 << 0)); + /* Issue Command #cx via PxCI */ + ahci_px_wreg(ahci, i, AHCI_PX_CI, (1 << cx)); while (BITSET(ahci_px_rreg(ahci, i, AHCI_PX_TFD), AHCI_PX_TFD_STS_BSY)) { usleep(50); } @@ -764,9 +767,9 @@ static void ahci_test_identify(AHCIQState *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 */ + ahci_get_command_header(ahci, i, cx, &cmd); + g_assert_cmphex(512, ==, cmd.prdbc); /* Investigate FIS responses */ memread(ahci->port[i].fb + 0x20, pio, 0x20); @@ -783,7 +786,7 @@ static void ahci_test_identify(AHCIQState *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); diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 3b1b9ba..8c3f664 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -310,3 +310,78 @@ void ahci_port_clear(AHCIQState *ahci, uint8_t port) /* Wipe the FIS-Recieve Buffer */ qmemset(ahci->port[port].fb, 0x00, 0x100); } + +/* Get the command in #slot of port #port. */ +void ahci_get_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd) +{ + uint64_t ba = ahci->port[port].clb; + ba += slot * sizeof(AHCICommandHeader); + memread(ba, cmd, sizeof(AHCICommandHeader)); + + cmd->flags = le16_to_cpu(cmd->flags); + cmd->prdtl = le16_to_cpu(cmd->prdtl); + cmd->prdbc = le32_to_cpu(cmd->prdbc); + cmd->ctba = le64_to_cpu(cmd->ctba); +} + +/* Set the command in #slot of port #port. */ +void ahci_set_command_header(AHCIQState *ahci, uint8_t port, + uint8_t slot, AHCICommandHeader *cmd) +{ + AHCICommandHeader tmp; + uint64_t ba = ahci->port[port].clb; + ba += slot * sizeof(AHCICommandHeader); + + tmp.flags = cpu_to_le16(cmd->flags); + tmp.prdtl = cpu_to_le16(cmd->prdtl); + tmp.prdbc = cpu_to_le32(cmd->prdbc); + tmp.ctba = cpu_to_le64(cmd->ctba); + + memwrite(ba, &tmp, sizeof(AHCICommandHeader)); +} + +void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot) +{ + AHCICommandHeader cmd; + + /* Obtain the Nth Command Header */ + ahci_get_command_header(ahci, port, slot, &cmd); + if (cmd.ctba == 0) { + /* No address in it, so just return -- it's empty. */ + goto tidy; + } + + /* Free the Table */ + ahci_free(ahci, cmd.ctba); + + tidy: + /* NULL the header. */ + memset(&cmd, 0x00, sizeof(cmd)); + ahci_set_command_header(ahci, port, slot, &cmd); + ahci->port[port].ctba[slot] = 0; + ahci->port[port].prdtl[slot] = 0; +} + +unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port) +{ + unsigned i; + unsigned j; + uint32_t reg; + + reg = ahci_px_rreg(ahci, port, AHCI_PX_CI); + + /* Pick the least recently used command slot that's available */ + for (i = 0; i < 32; ++i) { + j = ((ahci->port[port].next + i) % 32); + if (reg & (1 << j)) { + continue; + } + ahci_destroy_command(ahci, port, i); + ahci->port[port].next = (j + 1) % 32; + return j; + } + + g_test_message("All command slots were busy."); + g_assert_not_reached(); +} diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 1fddf33..0835be4 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -248,6 +248,9 @@ typedef struct AHCIPortQState { uint64_t fb; uint64_t clb; + uint64_t ctba[32]; + uint16_t prdtl[32]; + uint8_t next; /** Next Command Slot to Use **/ } AHCIPortQState; typedef struct AHCIQState { @@ -333,8 +336,7 @@ typedef struct AHCICommandHeader { uint16_t flags; /* Cmd-Fis-Len, PMP#, and flags. */ 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__)) AHCICommandHeader; @@ -343,11 +345,10 @@ typedef struct AHCICommandHeader { * 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; /*** Macro Utilities ***/ #define BITANY(data, mask) (((data) & (mask)) != 0) @@ -432,5 +433,11 @@ void start_ahci_device(AHCIQState *ahci); void ahci_hba_enable(AHCIQState *ahci); unsigned ahci_port_select(AHCIQState *ahci); void ahci_port_clear(AHCIQState *ahci, uint8_t port); +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, + uint8_t slot, AHCICommandHeader *cmd); +void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot); +unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port); #endif |