From 24a5c62cfe3cbe3fb4722f79661b9900a2579316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20Poussineau?= Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 01/10] fdc: remove sparc sun4m mutations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They were introduced in 6f7e9aec5eb5bdfa57a9e458e391b785c283a007 and 82407d1a4035e5bfefb53ffdcb270872f813b34c and lots of bug fixes were done after that. This fixes (at least) the detection of the floppy controller on Debian 4.0r9/SPARC, and SS-5's OBP initialization routine still works. Signed-off-by: Hervé Poussineau Tested-by: Mark Cave-Ayland Message-id: 1426351846-6497-1-git-send-email-hpoussin@reactos.org Signed-off-by: John Snow --- hw/block/fdc.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 2bf87c9eea..f72a392163 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -535,8 +535,6 @@ struct FDCtrl { uint8_t pwrd; /* Floppy drives */ uint8_t num_floppies; - /* Sun4m quirks? */ - int sun4m; FDrive drives[MAX_FD]; int reset_sensei; uint32_t check_media_rate; @@ -885,13 +883,6 @@ static void fdctrl_reset_irq(FDCtrl *fdctrl) static void fdctrl_raise_irq(FDCtrl *fdctrl) { - /* Sparc mutation */ - if (fdctrl->sun4m && (fdctrl->msr & FD_MSR_CMDBUSY)) { - /* XXX: not sure */ - fdctrl->msr &= ~FD_MSR_CMDBUSY; - fdctrl->msr |= FD_MSR_RQM | FD_MSR_DIO; - return; - } if (!(fdctrl->sra & FD_SRA_INTPEND)) { qemu_set_irq(fdctrl->irq, 1); fdctrl->sra |= FD_SRA_INTPEND; @@ -1080,12 +1071,6 @@ static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl) fdctrl->dsr &= ~FD_DSR_PWRDOWN; fdctrl->dor |= FD_DOR_nRESET; - /* Sparc mutation */ - if (fdctrl->sun4m) { - retval |= FD_MSR_DIO; - fdctrl_reset_irq(fdctrl); - }; - FLOPPY_DPRINTF("main status register: 0x%02x\n", retval); return retval; @@ -2241,8 +2226,6 @@ static void sun4m_fdc_initfn(Object *obj) FDCtrlSysBus *sys = SYSBUS_FDC(obj); FDCtrl *fdctrl = &sys->state; - fdctrl->sun4m = 1; - memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops, fdctrl, "fdctrl", 0x08); sysbus_init_mmio(sbd, &fdctrl->iomem); From 122fdf2d8822699482723e6f50f34c9c3933360b Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 02/10] qtest/ahci: add qcow2 support to ahci-test This will enable the testing of high offsets without wasting a lot of disk space, and does not impact the previous tests. mkimg and mkqcow2 are added to libqos for other tests. Signed-off-by: John Snow Acked-by: Stefan Hajnoczi Message-id: 1426274523-22661-2-git-send-email-jsnow@redhat.com --- tests/Makefile | 1 + tests/ahci-test.c | 16 ++++++---------- tests/libqos/libqos.c | 44 +++++++++++++++++++++++++++++++++++++++++++ tests/libqos/libqos.h | 2 ++ 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/tests/Makefile b/tests/Makefile index 55aa7452b4..309e8697fd 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -415,6 +415,7 @@ GCOV_OPTIONS = -n $(if $(V),-f,) $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y) $(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,) $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ + QTEST_QEMU_IMG=qemu-img$(EXESUF) \ MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@") $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \ diff --git a/tests/ahci-test.c b/tests/ahci-test.c index ea62e249f5..cdfa7b93d0 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -39,8 +39,8 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" -/* Test-specific defines. */ -#define TEST_IMAGE_SIZE (64 * 1024 * 1024) +/* Test-specific defines -- in MiB */ +#define TEST_IMAGE_SIZE_MB (200 * 1024) /*** Globals ***/ static char tmp_path[] = "/tmp/qtest.XXXXXX"; @@ -107,7 +107,7 @@ static AHCIQState *ahci_boot(void) s = g_malloc0(sizeof(AHCIQState)); cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" - ",format=raw" + ",format=qcow2" " -M q35 " "-device ide-hd,drive=drive0 " "-global ide-hd.ver=%s"; @@ -1071,7 +1071,6 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, int main(int argc, char **argv) { const char *arch; - int fd; int ret; int c; int i, j, k; @@ -1108,12 +1107,9 @@ int main(int argc, char **argv) return 0; } - /* Create a temporary raw image */ - fd = mkstemp(tmp_path); - g_assert(fd >= 0); - ret = ftruncate(fd, TEST_IMAGE_SIZE); - g_assert(ret == 0); - close(fd); + /* Create a temporary qcow2 image */ + close(mkstemp(tmp_path)); + mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB); /* Run the tests */ qtest_add_func("/ahci/sanity", test_sanity); diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index bc8beb281f..097091c61e 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -61,3 +61,47 @@ void qtest_shutdown(QOSState *qs) qtest_quit(qs->qts); g_free(qs); } + +void mkimg(const char *file, const char *fmt, unsigned size_mb) +{ + gchar *cli; + bool ret; + int rc; + GError *err = NULL; + char *qemu_img_path; + gchar *out, *out2; + char *abs_path; + + qemu_img_path = getenv("QTEST_QEMU_IMG"); + abs_path = realpath(qemu_img_path, NULL); + assert(qemu_img_path); + + cli = g_strdup_printf("%s create -f %s %s %uM", abs_path, + fmt, file, size_mb); + ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err); + if (err) { + fprintf(stderr, "%s\n", err->message); + g_error_free(err); + } + g_assert(ret && !err); + + /* In glib 2.34, we have g_spawn_check_exit_status. in 2.12, we don't. + * glib 2.43.91 implementation assumes that any non-zero is an error for + * windows, but uses extra precautions for Linux. However, + * 0 is only possible if the program exited normally, so that should be + * sufficient for our purposes on all platforms, here. */ + if (rc) { + fprintf(stderr, "qemu-img returned status code %d\n", rc); + } + g_assert(!rc); + + g_free(out); + g_free(out2); + g_free(cli); + free(abs_path); +} + +void mkqcow2(const char *file, unsigned size_mb) +{ + return mkimg(file, "qcow2", size_mb); +} diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h index 612d41e5e9..8cb298729d 100644 --- a/tests/libqos/libqos.h +++ b/tests/libqos/libqos.h @@ -19,6 +19,8 @@ typedef struct QOSState { QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap); QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...); void qtest_shutdown(QOSState *qs); +void mkimg(const char *file, const char *fmt, unsigned size_mb); +void mkqcow2(const char *file, unsigned size_mb); static inline uint64_t qmalloc(QOSState *q, size_t bytes) { From 727be1a7550b5caad0b94098a41de8033ad43f85 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 03/10] qtest/ahci: test different disk sectors Test sector offset 0, 1, and the last sector(s) in LBA28 and LBA48 modes. Signed-off-by: John Snow Acked-by: Stefan Hajnoczi Message-id: 1426274523-22661-3-git-send-email-jsnow@redhat.com --- tests/ahci-test.c | 68 ++++++++++++++++++++++++++++++++++++--------- tests/libqos/ahci.c | 10 ++++--- tests/libqos/ahci.h | 4 +-- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index cdfa7b93d0..6686242981 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -41,6 +41,8 @@ /* 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) /*** Globals ***/ static char tmp_path[] = "/tmp/qtest.XXXXXX"; @@ -738,7 +740,7 @@ static void ahci_test_identify(AHCIQState *ahci) ahci_port_clear(ahci, px); /* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */ - ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize); + ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize, 0); /* Check serial number/version in the buffer */ /* NB: IDENTIFY strings are packed in 16bit little endian chunks. @@ -754,11 +756,12 @@ static void ahci_test_identify(AHCIQState *ahci) g_assert_cmphex(rc, ==, 0); sect_size = le16_to_cpu(*((uint16_t *)(&buff[5]))); - g_assert_cmphex(sect_size, ==, 0x200); + g_assert_cmphex(sect_size, ==, AHCI_SECTOR_SIZE); } static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, - uint8_t read_cmd, uint8_t write_cmd) + uint64_t sector, uint8_t read_cmd, + uint8_t write_cmd) { uint64_t ptr; uint8_t port; @@ -781,9 +784,9 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, memwrite(ptr, tx, bufsize); /* Write this buffer to disk, then read it back to the DMA buffer. */ - ahci_guest_io(ahci, port, write_cmd, ptr, bufsize); + ahci_guest_io(ahci, port, write_cmd, ptr, bufsize, sector); qmemset(ptr, 0x00, bufsize); - ahci_guest_io(ahci, port, read_cmd, ptr, bufsize); + ahci_guest_io(ahci, port, read_cmd, ptr, bufsize, sector); /*** Read back the Data ***/ memread(ptr, rx, bufsize); @@ -968,12 +971,45 @@ enum IOOps { NUM_IO_OPS }; +enum OffsetType { + OFFSET_BEGIN = 0, + OFFSET_ZERO = OFFSET_BEGIN, + OFFSET_LOW, + OFFSET_HIGH, + NUM_OFFSETS +}; + +static const char *offset_str[NUM_OFFSETS] = { "zero", "low", "high" }; + typedef struct AHCIIOTestOptions { enum BuffLen length; enum AddrMode address_type; enum IOMode io_type; + enum OffsetType offset; } AHCIIOTestOptions; +static uint64_t offset_sector(enum OffsetType ofst, + enum AddrMode addr_type, + uint64_t buffsize) +{ + uint64_t ceil; + uint64_t nsectors; + + switch (ofst) { + case OFFSET_ZERO: + return 0; + case OFFSET_LOW: + return 1; + case OFFSET_HIGH: + ceil = (addr_type == ADDR_MODE_LBA28) ? 0xfffffff : 0xffffffffffff; + ceil = MIN(ceil, TEST_IMAGE_SECTORS - 1); + nsectors = buffsize / AHCI_SECTOR_SIZE; + return ceil - nsectors + 1; + default: + g_assert_not_reached(); + } +} + /** * Table of possible I/O ATA commands given a set of enumerations. */ @@ -1001,12 +1037,12 @@ static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = { * transfer modes, and buffer sizes. */ static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma, - unsigned bufsize) + unsigned bufsize, uint64_t sector) { AHCIQState *ahci; ahci = ahci_boot_and_enable(); - ahci_test_io_rw_simple(ahci, bufsize, + ahci_test_io_rw_simple(ahci, bufsize, sector, io_cmds[dma][lba48][IO_READ], io_cmds[dma][lba48][IO_WRITE]); ahci_shutdown(ahci); @@ -1019,6 +1055,7 @@ static void test_io_interface(gconstpointer opaque) { AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque; unsigned bufsize; + uint64_t sector; switch (opts->length) { case LEN_SIMPLE: @@ -1037,13 +1074,14 @@ static void test_io_interface(gconstpointer opaque) g_assert_not_reached(); } - test_io_rw_interface(opts->address_type, opts->io_type, bufsize); + sector = offset_sector(opts->offset, opts->address_type, bufsize); + test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector); g_free(opts); return; } static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, - enum BuffLen len) + enum BuffLen len, enum OffsetType offset) { static const char *arch; char *name; @@ -1052,15 +1090,17 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, opts->length = len; opts->address_type = addr; opts->io_type = type; + opts->offset = offset; if (!arch) { arch = qtest_get_arch(); } - name = g_strdup_printf("/%s/ahci/io/%s/%s/%s", arch, + name = g_strdup_printf("/%s/ahci/io/%s/%s/%s/%s", arch, io_mode_str[type], addr_mode_str[addr], - buff_len_str[len]); + buff_len_str[len], + offset_str[offset]); g_test_add_data_func(name, opts, test_io_interface); g_free(name); @@ -1073,7 +1113,7 @@ int main(int argc, char **argv) const char *arch; int ret; int c; - int i, j, k; + int i, j, k, m; static struct option long_options[] = { {"pedantic", no_argument, 0, 'p' }, @@ -1122,7 +1162,9 @@ int main(int argc, char **argv) for (i = MODE_BEGIN; i < NUM_MODES; i++) { for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) { for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) { - create_ahci_io_test(i, j, k); + for (m = OFFSET_BEGIN; m < NUM_OFFSETS; m++) { + create_ahci_io_test(i, j, k, m); + } } } } diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index b0f39a5e32..a18c12bcc1 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -568,13 +568,15 @@ inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd) /* Given a guest buffer address, perform an IO operation */ void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, - uint64_t buffer, size_t bufsize) + uint64_t buffer, size_t bufsize, uint64_t sector) { AHCICommand *cmd; - cmd = ahci_command_create(ide_cmd); ahci_command_set_buffer(cmd, buffer); ahci_command_set_size(cmd, bufsize); + if (sector) { + ahci_command_set_offset(cmd, sector); + } ahci_command_commit(ahci, cmd, port); ahci_command_issue(ahci, cmd); ahci_command_verify(ahci, cmd); @@ -612,7 +614,7 @@ static AHCICommandProp *ahci_command_find(uint8_t command_name) /* Given a HOST buffer, create a buffer address and perform an IO operation. */ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, - void *buffer, size_t bufsize) + void *buffer, size_t bufsize, uint64_t sector) { uint64_t ptr; AHCICommandProp *props; @@ -626,7 +628,7 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, memwrite(ptr, buffer, bufsize); } - ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize); + ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector); if (props->read) { memread(ptr, buffer, bufsize); diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index 888545d5a2..40e8ca48ba 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -523,9 +523,9 @@ void ahci_write_fis(AHCIQState *ahci, RegH2DFIS *fis, uint64_t addr); unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port); unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd); void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, - uint64_t gbuffer, size_t size); + uint64_t gbuffer, size_t size, uint64_t sector); void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, - void *buffer, size_t bufsize); + void *buffer, size_t bufsize, uint64_t sector); /* Command Lifecycle */ AHCICommand *ahci_command_create(uint8_t command_name); From 4e217074ca3f704d9a1c3bd1ebb03eb7621ab882 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 04/10] qtest/ahci: Add simple flush test Signed-off-by: John Snow Message-id: 1426018503-821-2-git-send-email-jsnow@redhat.com --- tests/ahci-test.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 6686242981..4a5c788e59 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -797,6 +797,29 @@ 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) +{ + uint8_t px; + AHCICommand *cmd; + + /* Sanitize */ + px = ahci_port_select(ahci); + ahci_port_clear(ahci, px); + + /* Issue Command */ + cmd = ahci_command_create(ide_cmd); + ahci_command_commit(ahci, cmd, px); + ahci_command_issue(ahci, cmd); + ahci_command_verify(ahci, cmd); + ahci_command_free(cmd); +} + +static void ahci_test_flush(AHCIQState *ahci) +{ + ahci_test_nondata(ahci, CMD_FLUSH_CACHE); +} + + /******************************************************************************/ /* Test Interfaces */ /******************************************************************************/ @@ -931,6 +954,15 @@ static void test_dma_fragmented(void) g_free(tx); } +static void test_flush(void) +{ + AHCIQState *ahci; + + ahci = ahci_boot_and_enable(); + ahci_test_flush(ahci); + ahci_shutdown(ahci); +} + /******************************************************************************/ /* AHCI I/O Test Matrix Definitions */ @@ -1171,6 +1203,8 @@ int main(int argc, char **argv) qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented); + qtest_add_func("/ahci/flush/simple", test_flush); + ret = g_test_run(); /* Cleanup */ From debaaa114a8877a939533ba846e64168fb287b7b Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 05/10] qtest/ahci: Allow override of default CLI options Signed-off-by: John Snow Message-id: 1426018503-821-3-git-send-email-jsnow@redhat.com --- tests/ahci-test.c | 67 +++++++++++++++++++++++++++------------- tests/libqos/libqos-pc.c | 5 +++ tests/libqos/libqos-pc.h | 1 + 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 4a5c788e59..cd7d2ceb42 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -101,19 +101,12 @@ static void generate_pattern(void *buffer, size_t len, size_t cycle_len) /** * Start a Q35 machine and bookmark a handle to the AHCI device. */ -static AHCIQState *ahci_boot(void) +static AHCIQState *ahci_vboot(const char *cli, va_list ap) { AHCIQState *s; - const char *cli; s = g_malloc0(sizeof(AHCIQState)); - - cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" - ",format=qcow2" - " -M q35 " - "-device ide-hd,drive=drive0 " - "-global ide-hd.ver=%s"; - s->parent = qtest_pc_boot(cli, tmp_path, "testdisk", "version"); + s->parent = qtest_pc_vboot(cli, ap); alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT); /* Verify that we have an AHCI device present. */ @@ -122,13 +115,36 @@ static AHCIQState *ahci_boot(void) return s; } +/** + * Start a Q35 machine and bookmark a handle to the AHCI device. + */ +static AHCIQState *ahci_boot(const char *cli, ...) +{ + AHCIQState *s; + va_list ap; + + if (cli) { + va_start(ap, cli); + s = ahci_vboot(cli, ap); + va_end(ap); + } else { + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" + ",format=qcow2" + " -M q35 " + "-device ide-hd,drive=drive0 " + "-global ide-hd.ver=%s"; + s = ahci_boot(cli, tmp_path, "testdisk", "version"); + } + + return s; +} + /** * Clean up the PCI device, then terminate the QEMU instance. */ static void ahci_shutdown(AHCIQState *ahci) { QOSState *qs = ahci->parent; - ahci_clean_mem(ahci); free_ahci_device(ahci->dev); g_free(ahci); @@ -139,10 +155,18 @@ static void ahci_shutdown(AHCIQState *ahci) * Boot and fully enable the HBA device. * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. */ -static AHCIQState *ahci_boot_and_enable(void) +static AHCIQState *ahci_boot_and_enable(const char *cli, ...) { AHCIQState *ahci; - ahci = ahci_boot(); + va_list ap; + + if (cli) { + va_start(ap, cli); + ahci = ahci_vboot(cli, ap); + va_end(ap); + } else { + ahci = ahci_boot(NULL); + } ahci_pci_enable(ahci); ahci_hba_enable(ahci); @@ -830,7 +854,7 @@ static void ahci_test_flush(AHCIQState *ahci) static void test_sanity(void) { AHCIQState *ahci; - ahci = ahci_boot(); + ahci = ahci_boot(NULL); ahci_shutdown(ahci); } @@ -841,7 +865,7 @@ static void test_sanity(void) static void test_pci_spec(void) { AHCIQState *ahci; - ahci = ahci_boot(); + ahci = ahci_boot(NULL); ahci_test_pci_spec(ahci); ahci_shutdown(ahci); } @@ -853,8 +877,7 @@ static void test_pci_spec(void) static void test_pci_enable(void) { AHCIQState *ahci; - - ahci = ahci_boot(); + ahci = ahci_boot(NULL); ahci_pci_enable(ahci); ahci_shutdown(ahci); } @@ -867,7 +890,7 @@ static void test_hba_spec(void) { AHCIQState *ahci; - ahci = ahci_boot(); + ahci = ahci_boot(NULL); ahci_pci_enable(ahci); ahci_test_hba_spec(ahci); ahci_shutdown(ahci); @@ -881,7 +904,7 @@ static void test_hba_enable(void) { AHCIQState *ahci; - ahci = ahci_boot(); + ahci = ahci_boot(NULL); ahci_pci_enable(ahci); ahci_hba_enable(ahci); ahci_shutdown(ahci); @@ -895,7 +918,7 @@ static void test_identify(void) { AHCIQState *ahci; - ahci = ahci_boot_and_enable(); + ahci = ahci_boot_and_enable(NULL); ahci_test_identify(ahci); ahci_shutdown(ahci); } @@ -916,7 +939,7 @@ static void test_dma_fragmented(void) unsigned char *rx = g_malloc0(bufsize); uint64_t ptr; - ahci = ahci_boot_and_enable(); + ahci = ahci_boot_and_enable(NULL); px = ahci_port_select(ahci); ahci_port_clear(ahci, px); @@ -958,7 +981,7 @@ static void test_flush(void) { AHCIQState *ahci; - ahci = ahci_boot_and_enable(); + ahci = ahci_boot_and_enable(NULL); ahci_test_flush(ahci); ahci_shutdown(ahci); } @@ -1073,7 +1096,7 @@ static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma, { AHCIQState *ahci; - ahci = ahci_boot_and_enable(); + ahci = ahci_boot_and_enable(NULL); ahci_test_io_rw_simple(ahci, bufsize, sector, io_cmds[dma][lba48][IO_READ], io_cmds[dma][lba48][IO_WRITE]); diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c index bbace893fb..1403699377 100644 --- a/tests/libqos/libqos-pc.c +++ b/tests/libqos/libqos-pc.c @@ -6,6 +6,11 @@ static QOSOps qos_ops = { .uninit_allocator = pc_alloc_uninit }; +QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap) +{ + return qtest_vboot(&qos_ops, cmdline_fmt, ap); +} + QOSState *qtest_pc_boot(const char *cmdline_fmt, ...) { QOSState *qs; diff --git a/tests/libqos/libqos-pc.h b/tests/libqos/libqos-pc.h index 316857d32f..b1820c5739 100644 --- a/tests/libqos/libqos-pc.h +++ b/tests/libqos/libqos-pc.h @@ -3,6 +3,7 @@ #include "libqos/libqos.h" +QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap); QOSState *qtest_pc_boot(const char *cmdline_fmt, ...); void qtest_pc_shutdown(QOSState *qs); From 8fe941f749b2db3735abade1c298552de4eab496 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 06/10] libqtest: add qmp_eventwait Allow the user to poll until a desired interrupt occurs. Signed-off-by: John Snow Message-id: 1426018503-821-4-git-send-email-jsnow@redhat.com --- tests/ide-test.c | 11 +---------- tests/libqtest.c | 16 ++++++++++++++++ tests/libqtest.h | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/tests/ide-test.c b/tests/ide-test.c index b28a3023c2..1dae84fc94 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -520,7 +520,6 @@ static void test_retry_flush(const char *machine) { uint8_t data; const char *s; - QDict *response; prepare_blkdebug_script(debug_path, "flush_to_disk"); @@ -539,15 +538,7 @@ static void test_retry_flush(const char *machine) assert_bit_set(data, BSY | DRDY); assert_bit_clear(data, DF | ERR | DRQ); - for (;; response = NULL) { - response = qmp_receive(); - if ((qdict_haskey(response, "event")) && - (strcmp(qdict_get_str(response, "event"), "STOP") == 0)) { - QDECREF(response); - break; - } - QDECREF(response); - } + qmp_eventwait("STOP"); /* Complete the command */ s = "{'execute':'cont' }"; diff --git a/tests/libqtest.c b/tests/libqtest.c index 12d65bd1e6..e2d01b47a2 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -450,6 +450,22 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) QDECREF(response); } +void qtest_qmp_eventwait(QTestState *s, const char *event) +{ + QDict *response; + + for (;;) { + response = qtest_qmp_receive(s); + if ((qdict_haskey(response, "event")) && + (strcmp(qdict_get_str(response, "event"), event) == 0)) { + QDECREF(response); + break; + } + QDECREF(response); + } +} + + const char *qtest_get_arch(void) { const char *qemu = getenv("QTEST_QEMU_BINARY"); diff --git a/tests/libqtest.h b/tests/libqtest.h index 03469b8781..30009ca5c9 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -91,6 +91,15 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap); */ QDict *qtest_qmp_receive(QTestState *s); +/** + * qtest_qmp_eventwait: + * @s: #QTestState instance to operate on. + * @s: #event event to wait for. + * + * Continuosly polls for QMP responses until it receives the desired event. + */ +void qtest_qmp_eventwait(QTestState *s, const char *event); + /** * qtest_get_irq: * @s: #QTestState instance to operate on. @@ -428,6 +437,17 @@ static inline QDict *qmp_receive(void) return qtest_qmp_receive(global_qtest); } +/** + * qmp_eventwait: + * @s: #event event to wait for. + * + * Continuosly polls for QMP responses until it receives the desired event. + */ +static inline void qmp_eventwait(const char *event) +{ + return qtest_qmp_eventwait(global_qtest, event); +} + /** * get_irq: * @num: Interrupt to observe. From ba4ed39346c1bdbfefd1d781b39009f90822b956 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 07/10] libqtest: add qmp_async Add qmp_async, which lets us send QMP commands asynchronously. This is useful when we want to send commands that will trigger event responses, but we don't know in what order to expect them. Sometimes the event responses may arrive even before the command confirmation will show up, so it is convenient to leave the responses in the stream. Signed-off-by: John Snow Message-id: 1426018503-821-5-git-send-email-jsnow@redhat.com --- tests/libqtest.c | 30 +++++++++++++++++++++++++++++- tests/libqtest.h | 27 +++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/tests/libqtest.c b/tests/libqtest.c index e2d01b47a2..659a3c7c63 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -388,7 +388,12 @@ QDict *qtest_qmp_receive(QTestState *s) return qmp.response; } -QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) +/** + * Allow users to send a message without waiting for the reply, + * in the case that they choose to discard all replies up until + * a particular EVENT is received. + */ +void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) { va_list ap_copy; QObject *qobj; @@ -417,6 +422,11 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) QDECREF(qstr); qobject_decref(qobj); } +} + +QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) +{ + qtest_async_qmpv(s, fmt, ap); /* Receive reply */ return qtest_qmp_receive(s); @@ -433,6 +443,15 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...) return response; } +void qtest_async_qmp(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qtest_async_qmpv(s, fmt, ap); + va_end(ap); +} + void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap) { QDict *response = qtest_qmpv(s, fmt, ap); @@ -711,6 +730,15 @@ QDict *qmp(const char *fmt, ...) return response; } +void qmp_async(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qtest_async_qmpv(global_qtest, fmt, ap); + va_end(ap); +} + void qmp_discard_response(const char *fmt, ...) { va_list ap; diff --git a/tests/libqtest.h b/tests/libqtest.h index 30009ca5c9..4b54b5da9e 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -63,6 +63,15 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...); */ QDict *qtest_qmp(QTestState *s, const char *fmt, ...); +/** + * qtest_async_qmp: + * @s: #QTestState instance to operate on. + * @fmt...: QMP message to send to qemu + * + * Sends a QMP message to QEMU and leaves the response in the stream. + */ +void qtest_async_qmp(QTestState *s, const char *fmt, ...); + /** * qtest_qmpv_discard_response: * @s: #QTestState instance to operate on. @@ -83,6 +92,16 @@ void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap); */ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap); +/** + * qtest_async_qmpv: + * @s: #QTestState instance to operate on. + * @fmt: QMP message to send to QEMU + * @ap: QMP message arguments + * + * Sends a QMP message to QEMU and leaves the response in the stream. + */ +void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap); + /** * qtest_receive: * @s: #QTestState instance to operate on. @@ -419,6 +438,14 @@ static inline void qtest_end(void) */ QDict *qmp(const char *fmt, ...); +/** + * qmp_async: + * @fmt...: QMP message to send to qemu + * + * Sends a QMP message to QEMU and leaves the response in the stream. + */ +void qmp_async(const char *fmt, ...); + /** * qmp_discard_response: * @fmt...: QMP message to send to qemu From 72c85e949fd162b039614d588d94393ff3e2dae3 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 08/10] libqos: add blkdebug_prepare_script Pull this helper out of ide-test and into libqos, to be shared with ahci-test. Signed-off-by: John Snow Message-id: 1426018503-821-6-git-send-email-jsnow@redhat.com --- tests/ide-test.c | 23 +---------------------- tests/libqos/libqos.c | 22 ++++++++++++++++++++++ tests/libqos/libqos.h | 1 + 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/ide-test.c b/tests/ide-test.c index 1dae84fc94..78382e9c75 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -29,6 +29,7 @@ #include #include "libqtest.h" +#include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/malloc-pc.h" @@ -494,28 +495,6 @@ static void test_flush(void) ide_test_quit(); } -static void prepare_blkdebug_script(const char *debug_fn, const char *event) -{ - FILE *debug_file = fopen(debug_fn, "w"); - int ret; - - fprintf(debug_file, "[inject-error]\n"); - fprintf(debug_file, "event = \"%s\"\n", event); - fprintf(debug_file, "errno = \"5\"\n"); - fprintf(debug_file, "state = \"1\"\n"); - fprintf(debug_file, "immediately = \"off\"\n"); - fprintf(debug_file, "once = \"on\"\n"); - - fprintf(debug_file, "[set-state]\n"); - fprintf(debug_file, "event = \"%s\"\n", event); - fprintf(debug_file, "new_state = \"2\"\n"); - fflush(debug_file); - g_assert(!ferror(debug_file)); - - ret = fclose(debug_file); - g_assert(ret == 0); -} - static void test_retry_flush(const char *machine) { uint8_t data; diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 097091c61e..7e7207856e 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -105,3 +105,25 @@ void mkqcow2(const char *file, unsigned size_mb) { return mkimg(file, "qcow2", size_mb); } + +void prepare_blkdebug_script(const char *debug_fn, const char *event) +{ + FILE *debug_file = fopen(debug_fn, "w"); + int ret; + + fprintf(debug_file, "[inject-error]\n"); + fprintf(debug_file, "event = \"%s\"\n", event); + fprintf(debug_file, "errno = \"5\"\n"); + fprintf(debug_file, "state = \"1\"\n"); + fprintf(debug_file, "immediately = \"off\"\n"); + fprintf(debug_file, "once = \"on\"\n"); + + fprintf(debug_file, "[set-state]\n"); + fprintf(debug_file, "event = \"%s\"\n", event); + fprintf(debug_file, "new_state = \"2\"\n"); + fflush(debug_file); + g_assert(!ferror(debug_file)); + + ret = fclose(debug_file); + g_assert(ret == 0); +} diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h index 8cb298729d..f57362b688 100644 --- a/tests/libqos/libqos.h +++ b/tests/libqos/libqos.h @@ -21,6 +21,7 @@ QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...); void qtest_shutdown(QOSState *qs); void mkimg(const char *file, const char *fmt, unsigned size_mb); void mkqcow2(const char *file, unsigned size_mb); +void prepare_blkdebug_script(const char *debug_fn, const char *event); static inline uint64_t qmalloc(QOSState *q, size_t bytes) { From cf5aa89e9d32ae39bd9df27c9b2aec03c0d240b2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 09/10] qtest/ahci: add flush retry test Signed-off-by: John Snow Message-id: 1426018503-821-7-git-send-email-jsnow@redhat.com --- tests/ahci-test.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index cd7d2ceb42..7c23bb2180 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -46,6 +46,7 @@ /*** Globals ***/ static char tmp_path[] = "/tmp/qtest.XXXXXX"; +static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; static bool ahci_pedantic; /*** Function Declarations ***/ @@ -986,6 +987,41 @@ static void test_flush(void) ahci_shutdown(ahci); } +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," + "rerror=stop,werror=stop " + "-M q35 " + "-device ide-hd,drive=drive0 ", + debug_path, + tmp_path); + + /* Issue Flush Command */ + 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); + + ahci_command_free(cmd); + ahci_shutdown(ahci); +} + /******************************************************************************/ /* AHCI I/O Test Matrix Definitions */ @@ -1167,6 +1203,7 @@ int main(int argc, char **argv) { const char *arch; int ret; + int fd; int c; int i, j, k, m; @@ -1206,6 +1243,11 @@ int main(int argc, char **argv) close(mkstemp(tmp_path)); mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB); + /* Create temporary blkdebug instructions */ + fd = mkstemp(debug_path); + 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); @@ -1227,11 +1269,13 @@ int main(int argc, char **argv) qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented); qtest_add_func("/ahci/flush/simple", test_flush); + qtest_add_func("/ahci/flush/retry", test_flush_retry); ret = g_test_run(); /* Cleanup */ unlink(tmp_path); + unlink(debug_path); return ret; } From c8368674980b299604e3cfe9215c4105acefa516 Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Tue, 28 Apr 2015 15:27:51 -0400 Subject: [PATCH 10/10] qtest: Add assertion that required environment variable is set Signed-off-by: Ed Maste Reviewed-by: John Snow Message-id: 1427911244-22565-1-git-send-email-emaste@freebsd.org Signed-off-by: John Snow --- tests/libqtest.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/libqtest.c b/tests/libqtest.c index 659a3c7c63..a525dc532c 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -488,6 +488,7 @@ void qtest_qmp_eventwait(QTestState *s, const char *event) const char *qtest_get_arch(void) { const char *qemu = getenv("QTEST_QEMU_BINARY"); + g_assert(qemu != NULL); const char *end = strrchr(qemu, '/'); return end + strlen("/qemu-system-");