link_cmd_slot creates the command header, by setting a pointer to the command table as well as adjusting other metadata, like DMA direction and PRDT length.
It effectively links a command, via its table, to the indicated command slot. Signed-off-by: John Snow <js...@redhat.com> --- tests/ahci-test.c | 72 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index f07495f..3e65435 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -252,11 +252,26 @@ #define AHCI_VERSION_1_2 (0x00010200) #define AHCI_VERSION_1_3 (0x00010300) +/* AHCI COMMAND HEADER FLAGS */ + +#define CMDH_CFL (0x1F) +#define CMDH_ATAPI (0x20) +#define CMDH_READ (0x00) /* Fake. */ +#define CMDH_WRITE (0x40) +#define CMDH_PREFETCH (0x80) +#define CMDH_RESET (0x100) +#define CMDH_BIST (0x200) +#define CMDH_CLR_BSY (0x400) +#define CMDH_RES (0x800) +#define CMDH_PMP (0xF000) + /*** Structures ***/ typedef struct AHCIPortState { uint64_t fb; uint64_t clb; + uint64_t _ctba; + uint16_t _prdtl; uint64_t ctba[32]; uint16_t prdtl[32]; uint8_t next; /** Next Command Slot to Use **/ @@ -341,8 +356,7 @@ typedef struct RegH2DFIS { * The command list contains between 1-32 of these structures. */ typedef struct AHCICommand { - uint8_t b1; - uint8_t b2; + 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 */ uint64_t ctba; /* Command Table Descriptor Base Address */ @@ -1286,6 +1300,7 @@ static void get_command_header(AHCIState *ahci, uint8_t px, ba += cx * sizeof(AHCICommand); memread(ba, cmd, sizeof(AHCICommand)); + 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); @@ -1298,6 +1313,7 @@ static void set_command_header(AHCIState *ahci, uint8_t px, uint64_t ba = ahci->port[px].clb; ba += cx * sizeof(AHCICommand); + cmd->flags = cpu_to_le16(cmd->flags); cmd->prdtl = cpu_to_le16(cmd->prdtl); cmd->prdbc = cpu_to_le32(cmd->prdbc); cmd->ctba = cpu_to_le64(cmd->ctba); @@ -1408,10 +1424,47 @@ static uint64_t build_cmd_table(AHCIState *ahci, uint8_t px, size_t sz, /* Commit the Command FIS to the Command Table */ memwrite(table_ptr, &fis, sizeof(fis)); + /* Bookmark these values. */ + ahci->port[px]._prdtl = prdtl; + ahci->port[px]._ctba = table_ptr; + return table_ptr; } /** + * Given a pointer to a Command Table, create a Command Header for it and write + * it to a free slot in the Command List buffer. + * @return The command slot number we picked. + */ +static unsigned link_cmd_slot(AHCIState *ahci, uint8_t px, uint64_t table_ptr, + uint8_t cmdh_flags) +{ + AHCICommand cmd = { .flags = 0 }; + uint8_t cx; + + cx = pick_cmd(ahci, px); + + /* Construct our Command Header */ + cmd.flags = 5; + cmd.flags |= CMDH_CLR_BSY; + cmd.flags |= cmdh_flags; + cmd.prdtl = (table_ptr == ahci->port[px]._ctba) ? ahci->port[px]._prdtl : 1; + cmd.prdbc = 0; + cmd.ctba = table_ptr; + + /* Commit Command #cx, pointing to the Table, to the Command List Buffer. */ + set_command_header(ahci, px, cx, &cmd); + + /* For convenience only. */ + ahci->port[px].ctba[cx] = table_ptr; + ahci->port[px].prdtl[cx] = cmd.prdtl; + ahci->port[px]._ctba = 0; + ahci->port[px]._prdtl = 0; + + return cx; +} + +/** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. */ @@ -1458,19 +1511,8 @@ static void ahci_test_identify(AHCIState *ahci) /* 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); - - /* 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 = 1; /* One PRD table entry. */ - cmd.prdbc = 0; - cmd.ctba = table_ptr; - - /* Commit Command #cx, pointing to the Table, to the Command List Buffer. */ - set_command_header(ahci, i, cx, &cmd); + /* Pick a command slot and link it to the command table we've built */ + cx = link_cmd_slot(ahci, i, table_ptr, CMDH_READ); /* Everything is in place, but we haven't given the go-ahead yet, * so we should find that there are no pending interrupts yet. */ -- 1.9.3