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:
parent
c7f9c570b9
commit
6cae27a6af
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user