X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/34475239b8f1fff0b715cb20f8b534b9d07a897e..e822e81e350825dd94f41ee2538ff1432b812eb9:/tests/ahci-test.c diff --git a/tests/ahci-test.c b/tests/ahci-test.c index ee1dc20a8f..cb84edc8fb 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -22,11 +22,8 @@ * THE SOFTWARE. */ -#include -#include -#include +#include "qemu/osdep.h" #include -#include #include "libqtest.h" #include "libqos/libqos-pc.h" @@ -39,15 +36,17 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" -/* Test-specific defines -- in MiB */ -#define TEST_IMAGE_SIZE_MB (200 * 1024) -#define TEST_IMAGE_SECTORS ((TEST_IMAGE_SIZE_MB / AHCI_SECTOR_SIZE) \ - * 1024 * 1024) +/* Test images sizes in MB */ +#define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024) +#define TEST_IMAGE_SIZE_MB_SMALL 64 /*** Globals ***/ static char tmp_path[] = "/tmp/qtest.XXXXXX"; static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; +static char mig_socket[] = "/tmp/qtest-migration.XXXXXX"; static bool ahci_pedantic; +static const char *imgfmt; +static unsigned test_image_size_mb; /*** Function Declarations ***/ static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port); @@ -60,6 +59,11 @@ static void ahci_test_pmcap(AHCIQState *ahci, uint8_t offset); /*** Utilities ***/ +static uint64_t mb_to_sectors(uint64_t image_size_mb) +{ + return (image_size_mb * 1024 * 1024) / AHCI_SECTOR_SIZE; +} + static void string_bswap16(uint16_t *s, size_t bytes) { g_assert_cmphex((bytes & 1), ==, 0); @@ -71,54 +75,26 @@ static void string_bswap16(uint16_t *s, size_t bytes) } } -static void generate_pattern(void *buffer, size_t len, size_t cycle_len) -{ - int i, j; - unsigned char *tx = (unsigned char *)buffer; - unsigned char p; - size_t *sx; - - /* Write an indicative pattern that varies and is unique per-cycle */ - p = rand() % 256; - for (i = j = 0; i < len; i++, j++) { - tx[i] = p; - if (j % cycle_len == 0) { - p = rand() % 256; - } - } - - /* force uniqueness by writing an id per-cycle */ - for (i = 0; i < len / cycle_len; i++) { - j = i * cycle_len; - if (j + sizeof(*sx) <= len) { - sx = (size_t *)&tx[j]; - *sx = i; - } - } -} - /** * Verify that the transfer did not corrupt our state at all. */ -static void verify_state(AHCIQState *ahci) +static void verify_state(AHCIQState *ahci, uint64_t hba_old) { int i, j; uint32_t ahci_fingerprint; uint64_t hba_base; - uint64_t hba_stored; AHCICommandHeader cmd; ahci_fingerprint = qpci_config_readl(ahci->dev, PCI_VENDOR_ID); g_assert_cmphex(ahci_fingerprint, ==, ahci->fingerprint); /* If we haven't initialized, this is as much as can be validated. */ - if (!ahci->hba_base) { + if (!ahci->enabled) { return; } hba_base = (uint64_t)qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); - hba_stored = (uint64_t)(uintptr_t)ahci->hba_base; - g_assert_cmphex(hba_base, ==, hba_stored); + g_assert_cmphex(hba_base, ==, hba_old); g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP), ==, ahci->cap); g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP2), ==, ahci->cap2); @@ -140,10 +116,16 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) { QOSState *tmp = to->parent; QPCIDevice *dev = to->dev; + char *uri_local = NULL; + uint64_t hba_old; + if (uri == NULL) { - uri = "tcp:127.0.0.1:1234"; + uri_local = g_strdup_printf("%s%s", "unix:", mig_socket); + uri = uri_local; } + hba_old = (uint64_t)qpci_config_readl(from->dev, PCI_BASE_ADDRESS_5); + /* context will be 'to' after completion. */ migrate(from->parent, to->parent, uri); @@ -160,7 +142,8 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) from->parent = tmp; from->dev = dev; - verify_state(to); + verify_state(to, hba_old); + g_free(uri_local); } /*** Test Setup & Teardown ***/ @@ -172,7 +155,7 @@ static AHCIQState *ahci_vboot(const char *cli, va_list ap) { AHCIQState *s; - s = g_malloc0(sizeof(AHCIQState)); + s = g_new0(AHCIQState, 1); s->parent = qtest_pc_vboot(cli, ap); alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT); @@ -196,11 +179,11 @@ static AHCIQState *ahci_boot(const char *cli, ...) va_end(ap); } else { cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" - ",format=qcow2" + ",format=%s" " -M q35 " "-device ide-hd,drive=drive0 " "-global ide-hd.ver=%s"; - s = ahci_boot(cli, tmp_path, "testdisk", "version"); + s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version"); } return s; @@ -230,6 +213,7 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...) va_list ap; uint16_t buff[256]; uint8_t port; + uint8_t hello; if (cli) { va_start(ap, cli); @@ -244,7 +228,12 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...) /* Initialize test device */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - ahci_io(ahci, port, CMD_IDENTIFY, &buff, sizeof(buff), 0); + if (is_atapi(ahci, port)) { + hello = CMD_PACKET_ID; + } else { + hello = CMD_IDENTIFY; + } + ahci_io(ahci, port, hello, &buff, sizeof(buff), 0); return ahci; } @@ -899,18 +888,12 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd) { uint8_t port; - AHCICommand *cmd; /* Sanitize */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - /* Issue Command */ - cmd = ahci_command_create(ide_cmd); - ahci_command_commit(ahci, cmd, port); - ahci_command_issue(ahci, cmd); - ahci_command_verify(ahci, cmd); - ahci_command_free(cmd); + ahci_io(ahci, port, ide_cmd, NULL, 0, 0); return port; } @@ -926,7 +909,7 @@ static void ahci_test_max(AHCIQState *ahci) uint64_t nsect; uint8_t port; uint8_t cmd; - uint64_t config_sect = TEST_IMAGE_SECTORS - 1; + uint64_t config_sect = mb_to_sectors(test_image_size_mb) - 1; if (config_sect > 0xFFFFFF) { cmd = CMD_READ_MAX_EXT; @@ -1060,14 +1043,14 @@ static void test_dma_fragmented(void) ahci_command_commit(ahci, cmd, px); ahci_command_issue(ahci, cmd); ahci_command_verify(ahci, cmd); - g_free(cmd); + ahci_command_free(cmd); cmd = ahci_command_create(CMD_READ_DMA); ahci_command_adjust(cmd, 0, ptr, bufsize, 32); ahci_command_commit(ahci, cmd, px); ahci_command_issue(ahci, cmd); ahci_command_verify(ahci, cmd); - g_free(cmd); + ahci_command_free(cmd); /* Read back the guest's receive buffer into local memory */ bufread(ptr, rx, bufsize); @@ -1081,11 +1064,34 @@ static void test_dma_fragmented(void) g_free(tx); } +/* + * Write sector 1 with random data to make AHCI storage dirty + * Needed for flush tests so that flushes actually go though the block layer + */ +static void make_dirty(AHCIQState* ahci, uint8_t port) +{ + uint64_t ptr; + unsigned bufsize = 512; + + ptr = ahci_alloc(ahci, bufsize); + g_assert(ptr); + + ahci_guest_io(ahci, port, CMD_WRITE_DMA, ptr, bufsize, 1); + ahci_free(ahci, ptr); +} + static void test_flush(void) { AHCIQState *ahci; + uint8_t port; ahci = ahci_boot_and_enable(NULL); + + port = ahci_port_select(ahci); + ahci_port_clear(ahci, port); + + make_dirty(ahci, port); + ahci_test_flush(ahci); ahci_shutdown(ahci); } @@ -1095,33 +1101,26 @@ static void test_flush_retry(void) AHCIQState *ahci; AHCICommand *cmd; uint8_t port; - const char *s; prepare_blkdebug_script(debug_path, "flush_to_disk"); ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 ", debug_path, - tmp_path); + tmp_path, imgfmt); - /* Issue Flush Command and wait for error */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - cmd = ahci_command_create(CMD_FLUSH_CACHE); - ahci_command_commit(ahci, cmd, port); - ahci_command_issue_async(ahci, cmd); - qmp_eventwait("STOP"); - /* Complete the command */ - s = "{'execute':'cont' }"; - qmp_async(s); - qmp_eventwait("RESUME"); - ahci_command_wait(ahci, cmd); - ahci_command_verify(ahci, cmd); + /* Issue write so that flush actually goes to disk */ + make_dirty(ahci, port); + + /* Issue Flush Command and wait for error */ + cmd = ahci_guest_io_halt(ahci, port, CMD_FLUSH_CACHE, 0, 0, 0); + ahci_guest_io_resume(ahci, cmd); - ahci_command_free(cmd); ahci_shutdown(ahci); } @@ -1131,38 +1130,39 @@ static void test_flush_retry(void) static void test_migrate_sanity(void) { AHCIQState *src, *dst; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); - src = ahci_boot("-m 1024 -M q35 " - "-hda %s ", tmp_path); - dst = ahci_boot("-m 1024 -M q35 " - "-hda %s " - "-incoming %s", tmp_path, uri); + src = ahci_boot("-m 384 -M q35 " + "-drive if=ide,file=%s,format=%s ", tmp_path, imgfmt); + dst = ahci_boot("-m 384 -M q35 " + "-drive if=ide,file=%s,format=%s " + "-incoming %s", tmp_path, imgfmt, uri); ahci_migrate(src, dst, uri); ahci_shutdown(src); ahci_shutdown(dst); + g_free(uri); } /** - * 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; size_t bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); - unsigned i; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); - src = ahci_boot_and_enable("-m 1024 -M q35 " - "-hda %s ", tmp_path); - dst = ahci_boot("-m 1024 -M q35 " - "-hda %s " - "-incoming %s", tmp_path, uri); + src = ahci_boot_and_enable("-m 384 -M q35 " + "-drive if=ide,format=%s,file=%s ", + imgfmt, tmp_path); + dst = ahci_boot("-m 384 -M q35 " + "-drive if=ide,format=%s,file=%s " + "-incoming %s", imgfmt, tmp_path, uri); set_context(src->parent); @@ -1171,14 +1171,12 @@ static void test_migrate_dma(void) ahci_port_clear(src, px); /* create pattern */ - for (i = 0; i < bufsize; i++) { - tx[i] = (bufsize - i); - } + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); /* 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); @@ -1187,51 +1185,58 @@ static void test_migrate_dma(void) ahci_shutdown(dst); g_free(rx); g_free(tx); + g_free(uri); +} + +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; size_t bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); - unsigned i; uint64_t ptr; AHCICommand *cmd; prepare_blkdebug_script(debug_path, "write_aio"); ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 ", debug_path, - tmp_path); + tmp_path, imgfmt); /* Initialize and prepare */ port = ahci_port_select(ahci); ahci_port_clear(ahci, port); - for (i = 0; i < bufsize; i++) { - tx[i] = (bufsize - i); - } - /* create DMA source buffer and write pattern */ + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); ptr = ahci_alloc(ahci, bufsize); g_assert(ptr); 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 */ @@ -1239,7 +1244,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 */ @@ -1248,52 +1253,58 @@ 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; size_t bufsize = 4096; unsigned char *tx = g_malloc(bufsize); unsigned char *rx = g_malloc0(bufsize); - unsigned i; uint64_t ptr; AHCICommand *cmd; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); prepare_blkdebug_script(debug_path, "write_aio"); src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 ", debug_path, - tmp_path); + tmp_path, imgfmt); dst = ahci_boot("-drive file=%s,if=none,id=drive0," - "format=qcow2,cache=writeback," + "format=%s,cache=writeback," "rerror=stop,werror=stop " "-M q35 " "-device ide-hd,drive=drive0 " "-incoming %s", - tmp_path, uri); + tmp_path, imgfmt, uri); set_context(src->parent); /* Initialize and prepare */ port = ahci_port_select(src); ahci_port_clear(src, port); - - for (i = 0; i < bufsize; i++) { - tx[i] = (bufsize - i); - } + generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); /* create DMA source buffer and write pattern */ ptr = ahci_alloc(src, bufsize); @@ -1301,14 +1312,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); @@ -1318,6 +1329,17 @@ static void test_migrate_halted_dma(void) ahci_shutdown(dst); g_free(rx); g_free(tx); + g_free(uri); +} + +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); } /** @@ -1329,26 +1351,32 @@ static void test_flush_migrate(void) AHCICommand *cmd; uint8_t px; const char *s; - const char *uri = "tcp:127.0.0.1:1234"; + char *uri = g_strdup_printf("unix:%s", mig_socket); prepare_blkdebug_script(debug_path, "flush_to_disk"); src = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0," - "cache=writeback,rerror=stop,werror=stop " + "cache=writeback,rerror=stop,werror=stop," + "format=%s " "-M q35 " "-device ide-hd,drive=drive0 ", - debug_path, tmp_path); + debug_path, tmp_path, imgfmt); dst = ahci_boot("-drive file=%s,if=none,id=drive0," - "cache=writeback,rerror=stop,werror=stop " + "cache=writeback,rerror=stop,werror=stop," + "format=%s " "-M q35 " "-device ide-hd,drive=drive0 " - "-incoming %s", tmp_path, uri); + "-incoming %s", tmp_path, imgfmt, uri); set_context(src->parent); - /* Issue Flush Command */ px = ahci_port_select(src); ahci_port_clear(src, px); + + /* Dirty device so that flush reaches disk */ + make_dirty(src, px); + + /* Issue Flush Command */ cmd = ahci_command_create(CMD_FLUSH_CACHE); ahci_command_commit(src, cmd, px); ahci_command_issue_async(src, cmd); @@ -1367,6 +1395,7 @@ static void test_flush_migrate(void) ahci_command_free(cmd); ahci_shutdown(src); ahci_shutdown(dst); + g_free(uri); } static void test_max(void) @@ -1401,6 +1430,227 @@ static void test_reset(void) 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); +} + +static int prepare_iso(size_t size, unsigned char **buf, char **name) +{ + char cdrom_path[] = "/tmp/qtest.iso.XXXXXX"; + unsigned char *patt; + ssize_t ret; + int fd = mkstemp(cdrom_path); + + g_assert(buf); + g_assert(name); + patt = g_malloc(size); + + /* Generate a pattern and build a CDROM image to read from */ + generate_pattern(patt, size, ATAPI_SECTOR_SIZE); + ret = write(fd, patt, size); + g_assert(ret == size); + + *name = g_strdup(cdrom_path); + *buf = patt; + return fd; +} + +static void remove_iso(int fd, char *name) +{ + unlink(name); + g_free(name); + close(fd); +} + +static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd, + const AHCIOpts *opts) +{ + unsigned char *tx = opts->opaque; + unsigned char *rx; + + if (!opts->size) { + return 0; + } + + rx = g_malloc0(opts->size); + bufread(opts->buffer, rx, opts->size); + g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0); + g_free(rx); + + return 0; +} + +static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd, + bool override_bcl, uint16_t bcl) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + AHCIOpts opts = { + .size = (ATAPI_SECTOR_SIZE * nsectors), + .atapi = true, + .atapi_dma = dma, + .post_cb = ahci_cb_cmp_buff, + .set_bcl = override_bcl, + .bcl = bcl, + }; + uint64_t iso_size = ATAPI_SECTOR_SIZE * (nsectors + 1); + + /* Prepare ISO and fill 'tx' buffer */ + fd = prepare_iso(iso_size, &tx, &iso); + opts.opaque = tx; + + /* Standard startup wonkery, but use ide-cd and our special iso file */ + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + + /* Build & Send AHCI command */ + ahci_exec(ahci, ahci_port_select(ahci), cmd, &opts); + + /* Cleanup */ + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + +static void ahci_test_cdrom_read10(int nsectors, bool dma) +{ + ahci_test_cdrom(nsectors, dma, CMD_ATAPI_READ_10, false, 0); +} + +static void test_cdrom_dma(void) +{ + ahci_test_cdrom_read10(1, true); +} + +static void test_cdrom_dma_multi(void) +{ + ahci_test_cdrom_read10(3, true); +} + +static void test_cdrom_pio(void) +{ + ahci_test_cdrom_read10(1, false); +} + +static void test_cdrom_pio_multi(void) +{ + ahci_test_cdrom_read10(3, false); +} + +/* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0 + * completes as a NOP instead of erroring out. */ +static void test_atapi_bcl(void) +{ + ahci_test_cdrom(0, false, CMD_ATAPI_READ_CD, true, 0); +} + + +static void atapi_wait_tray(bool open) +{ + QDict *rsp = qmp_eventwait_ref("DEVICE_TRAY_MOVED"); + QDict *data = qdict_get_qdict(rsp, "data"); + if (open) { + g_assert(qdict_get_bool(data, "tray-open")); + } else { + g_assert(!qdict_get_bool(data, "tray-open")); + } + QDECREF(rsp); +} + +static void test_atapi_tray(void) +{ + AHCIQState *ahci; + unsigned char *tx; + char *iso; + int fd; + uint8_t port, sense, asc; + uint64_t iso_size = ATAPI_SECTOR_SIZE; + QDict *rsp; + + fd = prepare_iso(iso_size, &tx, &iso); + ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw " + "-M q35 " + "-device ide-cd,drive=drive0 ", iso); + port = ahci_port_select(ahci); + + ahci_atapi_eject(ahci, port); + atapi_wait_tray(true); + + ahci_atapi_load(ahci, port); + atapi_wait_tray(false); + + /* Remove media */ + qmp_async("{'execute': 'blockdev-open-tray', " + "'arguments': {'device': 'drive0'}}"); + atapi_wait_tray(true); + rsp = qmp_receive(); + QDECREF(rsp); + + qmp_discard_response("{'execute': 'x-blockdev-remove-medium', " + "'arguments': {'device': 'drive0'}}"); + + /* Test the tray without a medium */ + ahci_atapi_load(ahci, port); + atapi_wait_tray(false); + + ahci_atapi_eject(ahci, port); + atapi_wait_tray(true); + + /* Re-insert media */ + qmp_discard_response("{'execute': 'blockdev-add', " + "'arguments': {'node-name': 'node0', " + "'driver': 'raw', " + "'file': { 'driver': 'file', " + "'filename': %s }}}", iso); + qmp_discard_response("{'execute': 'x-blockdev-insert-medium'," + "'arguments': { 'device': 'drive0', " + "'node-name': 'node0' }}"); + + /* Again, the event shows up first */ + qmp_async("{'execute': 'blockdev-close-tray', " + "'arguments': {'device': 'drive0'}}"); + atapi_wait_tray(false); + rsp = qmp_receive(); + QDECREF(rsp); + + /* Now, to convince ATAPI we understand the media has changed... */ + ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY); + ahci_atapi_get_sense(ahci, port, &sense, &asc); + g_assert_cmpuint(sense, ==, SENSE_NOT_READY); + g_assert_cmpuint(asc, ==, ASC_MEDIUM_NOT_PRESENT); + + ahci_atapi_test_ready(ahci, port, false, SENSE_UNIT_ATTENTION); + ahci_atapi_get_sense(ahci, port, &sense, &asc); + g_assert_cmpuint(sense, ==, SENSE_UNIT_ATTENTION); + g_assert_cmpuint(asc, ==, ASC_MEDIUM_MAY_HAVE_CHANGED); + + ahci_atapi_test_ready(ahci, port, true, SENSE_NO_SENSE); + ahci_atapi_get_sense(ahci, port, &sense, &asc); + g_assert_cmpuint(sense, ==, SENSE_NO_SENSE); + + /* Final tray test. */ + ahci_atapi_eject(ahci, port); + atapi_wait_tray(true); + + ahci_atapi_load(ahci, port); + atapi_wait_tray(false); + + /* Cleanup */ + g_free(tx); + ahci_shutdown(ahci); + remove_iso(fd, iso); +} + /******************************************************************************/ /* AHCI I/O Test Matrix Definitions */ @@ -1472,7 +1722,7 @@ static uint64_t offset_sector(enum OffsetType ofst, return 1; case OFFSET_HIGH: ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff; - ceil = MIN(ceil, TEST_IMAGE_SECTORS - 1); + ceil = MIN(ceil, mb_to_sectors(test_image_size_mb) - 1); nsectors = buffsize / AHCI_SECTOR_SIZE; return ceil - nsectors + 1; default: @@ -1554,8 +1804,9 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, enum BuffLen len, enum OffsetType offset) { char *name; - AHCIIOTestOptions *opts = g_malloc(sizeof(AHCIIOTestOptions)); + AHCIIOTestOptions *opts; + opts = g_new(AHCIIOTestOptions, 1); opts->length = len; opts->address_type = addr; opts->io_type = type; @@ -1567,6 +1818,13 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, buff_len_str[len], offset_str[offset]); + if ((addr == ADDR_MODE_LBA48) && (offset == OFFSET_HIGH) && + (mb_to_sectors(test_image_size_mb) <= 0xFFFFFFF)) { + g_test_message("%s: skipped; test image too small", name); + g_free(name); + return; + } + qtest_add_data_func(name, opts, test_io_interface); g_free(name); } @@ -1613,15 +1871,33 @@ int main(int argc, char **argv) return 0; } - /* Create a temporary qcow2 image */ - close(mkstemp(tmp_path)); - mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB); + /* Create a temporary image */ + fd = mkstemp(tmp_path); + g_assert(fd >= 0); + if (have_qemu_img()) { + imgfmt = "qcow2"; + test_image_size_mb = TEST_IMAGE_SIZE_MB_LARGE; + mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB_LARGE); + } else { + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " + "skipping LBA48 high-sector tests"); + imgfmt = "raw"; + test_image_size_mb = TEST_IMAGE_SIZE_MB_SMALL; + ret = ftruncate(fd, test_image_size_mb * 1024 * 1024); + g_assert(ret == 0); + } + close(fd); /* Create temporary blkdebug instructions */ fd = mkstemp(debug_path); g_assert(fd >= 0); close(fd); + /* Reserve a hollow file to use as a socket for migration tests */ + fd = mkstemp(mig_socket); + g_assert(fd >= 0); + close(fd); + /* Run the tests */ qtest_add_func("/ahci/sanity", test_sanity); qtest_add_func("/ahci/pci_spec", test_pci_spec); @@ -1654,11 +1930,25 @@ int main(int argc, char **argv) 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); + + qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma); + qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi); + qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio); + qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi); + + qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl); + qtest_add_func("/ahci/cdrom/eject", test_atapi_tray); + ret = g_test_run(); /* Cleanup */ unlink(tmp_path); unlink(debug_path); + unlink(mig_socket); return ret; }