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>
This commit is contained in:
John Snow 2015-02-05 12:41:15 -05:00 committed by Stefan Hajnoczi
parent c7f9c570b9
commit 6cae27a6af
3 changed files with 110 additions and 25 deletions

View file

@ -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();
}