diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/ahci-test.c | 150 | ||||
-rw-r--r-- | tests/libqos/ahci.c | 168 | ||||
-rw-r--r-- | tests/libqos/ahci.h | 59 |
3 files changed, 285 insertions, 92 deletions
diff --git a/tests/ahci-test.c b/tests/ahci-test.c index ae9415d74c..87d7691861 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -228,6 +228,8 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...) { AHCIQState *ahci; va_list ap; + uint16_t buff[256]; + uint8_t port; if (cli) { va_start(ap, cli); @@ -239,6 +241,10 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...) ahci_pci_enable(ahci); ahci_hba_enable(ahci); + /* Initialize test device */ + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + ahci_io(ahci, port, CMD_IDENTIFY, &buff, sizeof(buff), 0); return ahci; } @@ -890,21 +896,23 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, g_free(rx); } -static void ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd) +static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd) { - uint8_t px; + uint8_t port; AHCICommand *cmd; /* Sanitize */ - px = ahci_port_select(ahci); - ahci_port_clear(ahci, px); + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); /* Issue Command */ cmd = ahci_command_create(ide_cmd); - ahci_command_commit(ahci, cmd, px); + ahci_command_commit(ahci, cmd, port); ahci_command_issue(ahci, cmd); ahci_command_verify(ahci, cmd); ahci_command_free(cmd); + + return port; } static void ahci_test_flush(AHCIQState *ahci) @@ -912,6 +920,33 @@ static void ahci_test_flush(AHCIQState *ahci) ahci_test_nondata(ahci, CMD_FLUSH_CACHE); } +static void ahci_test_max(AHCIQState *ahci) +{ + RegD2HFIS *d2h = g_malloc0(0x20); + uint64_t nsect; + uint8_t port; + uint8_t cmd; + uint64_t config_sect = TEST_IMAGE_SECTORS - 1; + + if (config_sect > 0xFFFFFF) { + cmd = CMD_READ_MAX_EXT; + } else { + cmd = CMD_READ_MAX; + } + + port = ahci_test_nondata(ahci, cmd); + memread(ahci->port[port].fb + 0x40, d2h, 0x20); + nsect = (uint64_t)d2h->lba_hi[2] << 40 | + (uint64_t)d2h->lba_hi[1] << 32 | + (uint64_t)d2h->lba_hi[0] << 24 | + (uint64_t)d2h->lba_lo[2] << 16 | + (uint64_t)d2h->lba_lo[1] << 8 | + (uint64_t)d2h->lba_lo[0]; + + g_assert_cmphex(nsect, ==, config_sect); + g_free(d2h); +} + /******************************************************************************/ /* Test Interfaces */ @@ -1111,9 +1146,9 @@ static void test_migrate_sanity(void) } /** - * DMA Migration test: Write a pattern, migrate, then read. + * Simple migration test: Write a pattern, migrate, then read. */ -static void test_migrate_dma(void) +static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write) { AHCIQState *src, *dst; uint8_t px; @@ -1141,9 +1176,9 @@ static void test_migrate_dma(void) } /* Write, migrate, then read. */ - ahci_io(src, px, CMD_WRITE_DMA, tx, bufsize, 0); + ahci_io(src, px, cmd_write, tx, bufsize, 0); ahci_migrate(src, dst, uri); - ahci_io(dst, px, CMD_READ_DMA, rx, bufsize, 0); + ahci_io(dst, px, cmd_read, rx, bufsize, 0); /* Verify pattern */ g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); @@ -1154,14 +1189,24 @@ static void test_migrate_dma(void) g_free(tx); } +static void test_migrate_dma(void) +{ + ahci_migrate_simple(CMD_READ_DMA, CMD_WRITE_DMA); +} + +static void test_migrate_ncq(void) +{ + ahci_migrate_simple(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); +} + /** - * DMA Error Test + * Halted IO Error Test * * Simulate an error on first write, Try to write a pattern, * Confirm the VM has stopped, resume the VM, verify command * has completed, then read back the data and verify. */ -static void test_halted_dma(void) +static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write) { AHCIQState *ahci; uint8_t port; @@ -1196,7 +1241,7 @@ static void test_halted_dma(void) memwrite(ptr, tx, bufsize); /* Attempt to write (and fail) */ - cmd = ahci_guest_io_halt(ahci, port, CMD_WRITE_DMA, + cmd = ahci_guest_io_halt(ahci, port, cmd_write, ptr, bufsize, 0); /* Attempt to resume the command */ @@ -1204,7 +1249,7 @@ static void test_halted_dma(void) ahci_free(ahci, ptr); /* Read back and verify */ - ahci_io(ahci, port, CMD_READ_DMA, rx, bufsize, 0); + ahci_io(ahci, port, cmd_read, rx, bufsize, 0); g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); /* Cleanup and go home */ @@ -1213,14 +1258,24 @@ static void test_halted_dma(void) g_free(tx); } +static void test_halted_dma(void) +{ + ahci_halted_io_test(CMD_READ_DMA, CMD_WRITE_DMA); +} + +static void test_halted_ncq(void) +{ + ahci_halted_io_test(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); +} + /** - * DMA Error Migration Test + * IO Error Migration Test * * Simulate an error on first write, Try to write a pattern, * Confirm the VM has stopped, migrate, resume the VM, * verify command has completed, then read back the data and verify. */ -static void test_migrate_halted_dma(void) +static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write) { AHCIQState *src, *dst; uint8_t port; @@ -1266,14 +1321,14 @@ static void test_migrate_halted_dma(void) memwrite(ptr, tx, bufsize); /* Write, trigger the VM to stop, migrate, then resume. */ - cmd = ahci_guest_io_halt(src, port, CMD_WRITE_DMA, + cmd = ahci_guest_io_halt(src, port, cmd_write, ptr, bufsize, 0); ahci_migrate(src, dst, uri); ahci_guest_io_resume(dst, cmd); ahci_free(dst, ptr); /* Read back */ - ahci_io(dst, port, CMD_READ_DMA, rx, bufsize, 0); + ahci_io(dst, port, cmd_read, rx, bufsize, 0); /* Verify TX and RX are identical */ g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); @@ -1285,6 +1340,16 @@ static void test_migrate_halted_dma(void) g_free(tx); } +static void test_migrate_halted_dma(void) +{ + ahci_migrate_halted_io(CMD_READ_DMA, CMD_WRITE_DMA); +} + +static void test_migrate_halted_ncq(void) +{ + ahci_migrate_halted_io(READ_FPDMA_QUEUED, WRITE_FPDMA_QUEUED); +} + /** * Migration test: Try to flush, migrate, then resume. */ @@ -1334,6 +1399,49 @@ static void test_flush_migrate(void) ahci_shutdown(dst); } +static void test_max(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + ahci_test_max(ahci); + ahci_shutdown(ahci); +} + +static void test_reset(void) +{ + AHCIQState *ahci; + int i; + + ahci = ahci_boot(NULL); + ahci_test_pci_spec(ahci); + ahci_pci_enable(ahci); + + for (i = 0; i < 2; i++) { + ahci_test_hba_spec(ahci); + ahci_hba_enable(ahci); + ahci_test_identify(ahci); + ahci_test_io_rw_simple(ahci, 4096, 0, + CMD_READ_DMA_EXT, + CMD_WRITE_DMA_EXT); + ahci_set(ahci, AHCI_GHC, AHCI_GHC_HR); + ahci_clean_mem(ahci); + } + + ahci_shutdown(ahci); +} + +static void test_ncq_simple(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(NULL); + ahci_test_io_rw_simple(ahci, 4096, 0, + READ_FPDMA_QUEUED, + WRITE_FPDMA_QUEUED); + ahci_shutdown(ahci); +} + /******************************************************************************/ /* AHCI I/O Test Matrix Definitions */ @@ -1584,6 +1692,14 @@ int main(int argc, char **argv) qtest_add_func("/ahci/io/dma/lba28/retry", test_halted_dma); qtest_add_func("/ahci/migrate/dma/halted", test_migrate_halted_dma); + qtest_add_func("/ahci/max", test_max); + qtest_add_func("/ahci/reset", test_reset); + + qtest_add_func("/ahci/io/ncq/simple", test_ncq_simple); + qtest_add_func("/ahci/migrate/ncq/simple", test_migrate_ncq); + qtest_add_func("/ahci/io/ncq/retry", test_halted_ncq); + qtest_add_func("/ahci/migrate/ncq/halted", test_migrate_halted_ncq); + ret = g_test_run(); /* Cleanup */ diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 7e17bb691e..33ecd2abfb 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -50,27 +50,47 @@ typedef struct AHCICommandProp { } AHCICommandProp; AHCICommandProp ahci_command_properties[] = { - { .cmd = CMD_READ_PIO, .data = true, .pio = true, - .lba28 = true, .read = true }, - { .cmd = CMD_WRITE_PIO, .data = true, .pio = true, - .lba28 = true, .write = true }, - { .cmd = CMD_READ_PIO_EXT, .data = true, .pio = true, - .lba48 = true, .read = true }, - { .cmd = CMD_WRITE_PIO_EXT, .data = true, .pio = true, - .lba48 = true, .write = true }, - { .cmd = CMD_READ_DMA, .data = true, .dma = true, - .lba28 = true, .read = true }, - { .cmd = CMD_WRITE_DMA, .data = true, .dma = true, - .lba28 = true, .write = true }, - { .cmd = CMD_READ_DMA_EXT, .data = true, .dma = true, - .lba48 = true, .read = true }, - { .cmd = CMD_WRITE_DMA_EXT, .data = true, .dma = true, - .lba48 = true, .write = true }, - { .cmd = CMD_IDENTIFY, .data = true, .pio = true, - .size = 512, .read = true }, - { .cmd = CMD_READ_MAX, .lba28 = true }, - { .cmd = CMD_READ_MAX_EXT, .lba48 = true }, - { .cmd = CMD_FLUSH_CACHE, .data = false } + { .cmd = CMD_READ_PIO, .data = true, .pio = true, + .lba28 = true, .read = true }, + { .cmd = CMD_WRITE_PIO, .data = true, .pio = true, + .lba28 = true, .write = true }, + { .cmd = CMD_READ_PIO_EXT, .data = true, .pio = true, + .lba48 = true, .read = true }, + { .cmd = CMD_WRITE_PIO_EXT, .data = true, .pio = true, + .lba48 = true, .write = true }, + { .cmd = CMD_READ_DMA, .data = true, .dma = true, + .lba28 = true, .read = true }, + { .cmd = CMD_WRITE_DMA, .data = true, .dma = true, + .lba28 = true, .write = true }, + { .cmd = CMD_READ_DMA_EXT, .data = true, .dma = true, + .lba48 = true, .read = true }, + { .cmd = CMD_WRITE_DMA_EXT, .data = true, .dma = true, + .lba48 = true, .write = true }, + { .cmd = CMD_IDENTIFY, .data = true, .pio = true, + .size = 512, .read = true }, + { .cmd = READ_FPDMA_QUEUED, .data = true, .dma = true, + .lba48 = true, .read = true, .ncq = true }, + { .cmd = WRITE_FPDMA_QUEUED, .data = true, .dma = true, + .lba48 = true, .write = true, .ncq = true }, + { .cmd = CMD_READ_MAX, .lba28 = true }, + { .cmd = CMD_READ_MAX_EXT, .lba48 = true }, + { .cmd = CMD_FLUSH_CACHE, .data = false } +}; + +struct AHCICommand { + /* Test Management Data */ + uint8_t name; + uint8_t port; + uint8_t slot; + uint32_t interrupts; + uint64_t xbytes; + uint32_t prd_size; + uint64_t buffer; + AHCICommandProp *props; + /* Data to be transferred to the guest */ + AHCICommandHeader header; + RegH2DFIS fis; + void *atapi_cmd; }; /** @@ -138,12 +158,14 @@ void ahci_clean_mem(AHCIQState *ahci) for (port = 0; port < 32; ++port) { if (ahci->port[port].fb) { ahci_free(ahci, ahci->port[port].fb); + ahci->port[port].fb = 0; } if (ahci->port[port].clb) { for (slot = 0; slot < 32; slot++) { ahci_destroy_command(ahci, port, slot); } ahci_free(ahci, ahci->port[port].clb); + ahci->port[port].clb = 0; } } } @@ -252,7 +274,7 @@ void ahci_hba_enable(AHCIQState *ahci) /* Allocate Memory for the Command List Buffer & FIS Buffer */ /* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */ ahci->port[i].clb = ahci_alloc(ahci, num_cmd_slots * 0x20); - qmemset(ahci->port[i].clb, 0x00, 0x100); + qmemset(ahci->port[i].clb, 0x00, num_cmd_slots * 0x20); g_test_message("CLB: 0x%08" PRIx64, ahci->port[i].clb); ahci_px_wreg(ahci, i, AHCI_PX_CLB, ahci->port[i].clb); g_assert_cmphex(ahci->port[i].clb, ==, @@ -460,13 +482,15 @@ void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, g_free(pio); } -void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port, - uint8_t slot, size_t buffsize) +void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd) { - AHCICommandHeader cmd; + AHCICommandHeader cmdh; - ahci_get_command_header(ahci, port, slot, &cmd); - g_assert_cmphex(buffsize, ==, cmd.prdbc); + ahci_get_command_header(ahci, cmd->port, cmd->slot, &cmdh); + /* Physical Region Descriptor Byte Count is not required to work for NCQ. */ + if (!cmd->props->ncq) { + g_assert_cmphex(cmd->xbytes, ==, cmdh.prdbc); + } } /* Get the command in #slot of port #port. */ @@ -549,7 +573,7 @@ unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port) if (reg & (1 << j)) { continue; } - ahci_destroy_command(ahci, port, i); + ahci_destroy_command(ahci, port, j); ahci->port[port].next = (j + 1) % 32; return j; } @@ -610,22 +634,6 @@ void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, ahci_command_free(cmd); } -struct AHCICommand { - /* Test Management Data */ - uint8_t name; - uint8_t port; - uint8_t slot; - uint32_t interrupts; - uint64_t xbytes; - uint32_t prd_size; - uint64_t buffer; - AHCICommandProp *props; - /* Data to be transferred to the guest */ - AHCICommandHeader header; - RegH2DFIS fis; - void *atapi_cmd; -}; - static AHCICommandProp *ahci_command_find(uint8_t command_name) { int i; @@ -691,19 +699,34 @@ static void command_header_init(AHCICommand *cmd) static void command_table_init(AHCICommand *cmd) { RegH2DFIS *fis = &(cmd->fis); + uint16_t sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE); fis->fis_type = REG_H2D_FIS; fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */ fis->command = cmd->name; - cmd->fis.feature_low = 0x00; - cmd->fis.feature_high = 0x00; - if (cmd->props->lba28 || cmd->props->lba48) { - cmd->fis.device = ATA_DEVICE_LBA; + + if (cmd->props->ncq) { + NCQFIS *ncqfis = (NCQFIS *)fis; + /* NCQ is weird and re-uses FIS frames for unrelated data. + * See SATA 3.2, 13.6.4.1 READ FPDMA QUEUED for an example. */ + ncqfis->sector_low = sect_count & 0xFF; + ncqfis->sector_hi = (sect_count >> 8) & 0xFF; + ncqfis->device = NCQ_DEVICE_MAGIC; + /* Force Unit Access is bit 7 in the device register */ + ncqfis->tag = 0; /* bits 3-7 are the NCQ tag */ + ncqfis->prio = 0; /* bits 6,7 are a prio tag */ + /* RARC bit is bit 0 of TAG field */ + } else { + fis->feature_low = 0x00; + fis->feature_high = 0x00; + if (cmd->props->lba28 || cmd->props->lba48) { + fis->device = ATA_DEVICE_LBA; + } + fis->count = (cmd->xbytes / AHCI_SECTOR_SIZE); } - cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE); - cmd->fis.icc = 0x00; - cmd->fis.control = 0x00; - memset(cmd->fis.aux, 0x00, ARRAY_SIZE(cmd->fis.aux)); + fis->icc = 0x00; + fis->control = 0x00; + memset(fis->aux, 0x00, ARRAY_SIZE(fis->aux)); } AHCICommand *ahci_command_create(uint8_t command_name) @@ -717,6 +740,7 @@ AHCICommand *ahci_command_create(uint8_t command_name) g_assert(!(props->lba28 && props->lba48)); g_assert(!(props->read && props->write)); g_assert(!props->size || props->data); + g_assert(!props->ncq || (props->ncq && props->lba48)); /* Defaults and book-keeping */ cmd->props = props; @@ -725,12 +749,15 @@ AHCICommand *ahci_command_create(uint8_t command_name) cmd->prd_size = 4096; cmd->buffer = 0xabad1dea; - cmd->interrupts = AHCI_PX_IS_DHRS; + if (!cmd->props->ncq) { + cmd->interrupts = AHCI_PX_IS_DHRS; + } /* BUG: We expect the DPS interrupt for data commands */ /* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */ /* BUG: We expect the DMA Setup interrupt for DMA commands */ /* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */ cmd->interrupts |= props->pio ? AHCI_PX_IS_PSS : 0; + cmd->interrupts |= props->ncq ? AHCI_PX_IS_SDBS : 0; command_header_init(cmd); command_table_init(cmd); @@ -758,7 +785,7 @@ void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect) RegH2DFIS *fis = &(cmd->fis); if (cmd->props->lba28) { g_assert_cmphex(lba_sect, <=, 0xFFFFFFF); - } else if (cmd->props->lba48) { + } else if (cmd->props->lba48 || cmd->props->ncq) { g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF); } else { /* Can't set offset if we don't know the format. */ @@ -785,6 +812,8 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer) void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, unsigned prd_size) { + uint16_t sect_count; + /* Each PRD can describe up to 4MiB, and must not be odd. */ g_assert_cmphex(prd_size, <=, 4096 * 1024); g_assert_cmphex(prd_size & 0x01, ==, 0x00); @@ -792,7 +821,15 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes, cmd->prd_size = prd_size; } cmd->xbytes = xbytes; - cmd->fis.count = (cmd->xbytes / AHCI_SECTOR_SIZE); + sect_count = (cmd->xbytes / AHCI_SECTOR_SIZE); + + if (cmd->props->ncq) { + NCQFIS *nfis = (NCQFIS *)&(cmd->fis); + nfis->sector_low = sect_count & 0xFF; + nfis->sector_hi = (sect_count >> 8) & 0xFF; + } else { + cmd->fis.count = sect_count; + } cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size); } @@ -824,6 +861,11 @@ void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port) cmd->port = port; cmd->slot = ahci_pick_cmd(ahci, port); + if (cmd->props->ncq) { + NCQFIS *nfis = (NCQFIS *)&cmd->fis; + nfis->tag = (cmd->slot << 3) & 0xFC; + } + /* Create a buffer for the command table */ prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size); table_size = CMD_TBL_SIZ(prdtl); @@ -878,11 +920,15 @@ void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd) /* We can't rely on STS_BSY until the command has started processing. * Therefore, we also use the Command Issue bit as indication of * a command in-flight. */ - while (BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_TFD), - AHCI_PX_TFD_STS_BSY) || - BITSET(ahci_px_rreg(ahci, cmd->port, AHCI_PX_CI), (1 << cmd->slot))) { + +#define RSET(REG, MASK) (BITSET(ahci_px_rreg(ahci, cmd->port, (REG)), (MASK))) + + while (RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) || + RSET(AHCI_PX_CI, 1 << cmd->slot) || + (cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot))) { usleep(50); } + } void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd) @@ -899,8 +945,10 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd) ahci_port_check_error(ahci, port); ahci_port_check_interrupts(ahci, port, cmd->interrupts); ahci_port_check_nonbusy(ahci, port, slot); - ahci_port_check_cmd_sanity(ahci, port, slot, cmd->xbytes); - ahci_port_check_d2h_sanity(ahci, port, slot); + ahci_port_check_cmd_sanity(ahci, cmd); + if (cmd->interrupts & AHCI_PX_IS_DHRS) { + ahci_port_check_d2h_sanity(ahci, port, slot); + } if (cmd->props->pio) { ahci_port_check_pio_sanity(ahci, port, slot, cmd->xbytes); } diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 779e812400..a08a9ddac1 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -263,20 +263,23 @@ enum { /* ATA Commands */ enum { /* DMA */ - CMD_READ_DMA = 0xC8, - CMD_READ_DMA_EXT = 0x25, - CMD_WRITE_DMA = 0xCA, - CMD_WRITE_DMA_EXT = 0x35, + CMD_READ_DMA = 0xC8, + CMD_READ_DMA_EXT = 0x25, + CMD_WRITE_DMA = 0xCA, + CMD_WRITE_DMA_EXT = 0x35, /* PIO */ - CMD_READ_PIO = 0x20, - CMD_READ_PIO_EXT = 0x24, - CMD_WRITE_PIO = 0x30, - CMD_WRITE_PIO_EXT = 0x34, + CMD_READ_PIO = 0x20, + CMD_READ_PIO_EXT = 0x24, + CMD_WRITE_PIO = 0x30, + CMD_WRITE_PIO_EXT = 0x34, /* Misc */ - CMD_READ_MAX = 0xF8, - CMD_READ_MAX_EXT = 0x27, - CMD_FLUSH_CACHE = 0xE7, - CMD_IDENTIFY = 0xEC + CMD_READ_MAX = 0xF8, + CMD_READ_MAX_EXT = 0x27, + CMD_FLUSH_CACHE = 0xE7, + CMD_IDENTIFY = 0xEC, + /* NCQ */ + READ_FPDMA_QUEUED = 0x60, + WRITE_FPDMA_QUEUED = 0x61, }; /* AHCI Command Header Flags & Masks*/ @@ -291,8 +294,9 @@ enum { #define CMDH_PMP (0xF000) /* ATA device register masks */ -#define ATA_DEVICE_MAGIC 0xA0 +#define ATA_DEVICE_MAGIC 0xA0 /* used in ata1-3 */ #define ATA_DEVICE_LBA 0x40 +#define NCQ_DEVICE_MAGIC 0x40 /* for ncq device registers */ #define ATA_DEVICE_DRIVE 0x10 #define ATA_DEVICE_HEAD 0x0F @@ -397,6 +401,32 @@ typedef struct RegH2DFIS { } __attribute__((__packed__)) RegH2DFIS; /** + * Register host-to-device FIS structure, for NCQ commands. + * Actually just a RegH2DFIS, but with fields repurposed. + * Repurposed fields are annotated below. + */ +typedef struct NCQFIS { + /* DW0 */ + uint8_t fis_type; + uint8_t flags; + uint8_t command; + uint8_t sector_low; /* H2D: Feature 7:0 */ + /* DW1 */ + uint8_t lba_lo[3]; + uint8_t device; + /* DW2 */ + uint8_t lba_hi[3]; + uint8_t sector_hi; /* H2D: Feature 15:8 */ + /* DW3 */ + uint8_t tag; /* H2D: Count 0:7 */ + uint8_t prio; /* H2D: Count 15:8 */ + uint8_t icc; + uint8_t control; + /* DW4 */ + uint8_t aux[4]; +} __attribute__((__packed__)) NCQFIS; + +/** * Command List entry structure. * The command list contains between 1-32 of these structures. */ @@ -512,8 +542,7 @@ void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot); void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot); void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot, size_t buffsize); -void ahci_port_check_cmd_sanity(AHCIQState *ahci, uint8_t port, - uint8_t slot, size_t buffsize); +void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd); 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, |