瀏覽代碼

Merge remote-tracking branch 'remotes/jnsnow/tags/ide-pull-request' into staging

# gpg: Signature made Wed Apr 29 00:03:44 2015 BST using RSA key ID AAFC390E
# gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>"
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg:          It is not certain that the signature belongs to the owner.
# Primary key fingerprint: FAEB 9711 A12C F475 812F  18F2 88A9 064D 1835 61EB
#      Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76  CBD0 7DEF 8106 AAFC 390E

* remotes/jnsnow/tags/ide-pull-request:
  qtest: Add assertion that required environment variable is set
  qtest/ahci: add flush retry test
  libqos: add blkdebug_prepare_script
  libqtest: add qmp_async
  libqtest: add qmp_eventwait
  qtest/ahci: Allow override of default CLI options
  qtest/ahci: Add simple flush test
  qtest/ahci: test different disk sectors
  qtest/ahci: add qcow2 support to ahci-test
  fdc: remove sparc sun4m mutations

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Peter Maydell 10 年之前
父節點
當前提交
a1fe58f6ad
共有 12 個文件被更改,包括 359 次插入97 次删除
  1. 0 17
      hw/block/fdc.c
  2. 1 0
      tests/Makefile
  3. 180 41
      tests/ahci-test.c
  4. 2 32
      tests/ide-test.c
  5. 6 4
      tests/libqos/ahci.c
  6. 2 2
      tests/libqos/ahci.h
  7. 5 0
      tests/libqos/libqos-pc.c
  8. 1 0
      tests/libqos/libqos-pc.h
  9. 66 0
      tests/libqos/libqos.c
  10. 3 0
      tests/libqos/libqos.h
  11. 46 1
      tests/libqtest.c
  12. 47 0
      tests/libqtest.h

+ 0 - 17
hw/block/fdc.c

@@ -535,8 +535,6 @@ struct FDCtrl {
     uint8_t pwrd;
     uint8_t pwrd;
     /* Floppy drives */
     /* Floppy drives */
     uint8_t num_floppies;
     uint8_t num_floppies;
-    /* Sun4m quirks? */
-    int sun4m;
     FDrive drives[MAX_FD];
     FDrive drives[MAX_FD];
     int reset_sensei;
     int reset_sensei;
     uint32_t check_media_rate;
     uint32_t check_media_rate;
@@ -885,13 +883,6 @@ static void fdctrl_reset_irq(FDCtrl *fdctrl)
 
 
 static void fdctrl_raise_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)) {
     if (!(fdctrl->sra & FD_SRA_INTPEND)) {
         qemu_set_irq(fdctrl->irq, 1);
         qemu_set_irq(fdctrl->irq, 1);
         fdctrl->sra |= FD_SRA_INTPEND;
         fdctrl->sra |= FD_SRA_INTPEND;
@@ -1080,12 +1071,6 @@ static uint32_t fdctrl_read_main_status(FDCtrl *fdctrl)
     fdctrl->dsr &= ~FD_DSR_PWRDOWN;
     fdctrl->dsr &= ~FD_DSR_PWRDOWN;
     fdctrl->dor |= FD_DOR_nRESET;
     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);
     FLOPPY_DPRINTF("main status register: 0x%02x\n", retval);
 
 
     return retval;
     return retval;
@@ -2241,8 +2226,6 @@ static void sun4m_fdc_initfn(Object *obj)
     FDCtrlSysBus *sys = SYSBUS_FDC(obj);
     FDCtrlSysBus *sys = SYSBUS_FDC(obj);
     FDCtrl *fdctrl = &sys->state;
     FDCtrl *fdctrl = &sys->state;
 
 
-    fdctrl->sun4m = 1;
-
     memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops,
     memory_region_init_io(&fdctrl->iomem, obj, &fdctrl_mem_strict_ops,
                           fdctrl, "fdctrl", 0x08);
                           fdctrl, "fdctrl", 0x08);
     sysbus_init_mmio(sbd, &fdctrl->iomem);
     sysbus_init_mmio(sbd, &fdctrl->iomem);

+ 1 - 0
tests/Makefile

@@ -415,6 +415,7 @@ GCOV_OPTIONS = -n $(if $(V),-f,)
 $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y)
 $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y)
 	$(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,)
 	$(if $(CONFIG_GCOV),@rm -f *.gcda */*.gcda */*/*.gcda */*/*/*.gcda,)
 	$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
 	$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
+		QTEST_QEMU_IMG=qemu-img$(EXESUF) \
 		MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
 		MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
 		gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
 		gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
 	$(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \
 	$(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \

+ 180 - 41
tests/ahci-test.c

@@ -39,11 +39,14 @@
 #include "hw/pci/pci_ids.h"
 #include "hw/pci/pci_ids.h"
 #include "hw/pci/pci_regs.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)
+#define TEST_IMAGE_SECTORS ((TEST_IMAGE_SIZE_MB / AHCI_SECTOR_SIZE)     \
+                            * 1024 * 1024)
 
 
 /*** Globals ***/
 /*** Globals ***/
 static char tmp_path[] = "/tmp/qtest.XXXXXX";
 static char tmp_path[] = "/tmp/qtest.XXXXXX";
+static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX";
 static bool ahci_pedantic;
 static bool ahci_pedantic;
 
 
 /*** Function Declarations ***/
 /*** Function Declarations ***/
@@ -99,19 +102,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.
  * 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;
     AHCIQState *s;
-    const char *cli;
 
 
     s = g_malloc0(sizeof(AHCIQState));
     s = g_malloc0(sizeof(AHCIQState));
-
-    cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s"
-        ",format=raw"
-        " -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);
     alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT);
 
 
     /* Verify that we have an AHCI device present. */
     /* Verify that we have an AHCI device present. */
@@ -120,13 +116,36 @@ static AHCIQState *ahci_boot(void)
     return s;
     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.
  * Clean up the PCI device, then terminate the QEMU instance.
  */
  */
 static void ahci_shutdown(AHCIQState *ahci)
 static void ahci_shutdown(AHCIQState *ahci)
 {
 {
     QOSState *qs = ahci->parent;
     QOSState *qs = ahci->parent;
-
     ahci_clean_mem(ahci);
     ahci_clean_mem(ahci);
     free_ahci_device(ahci->dev);
     free_ahci_device(ahci->dev);
     g_free(ahci);
     g_free(ahci);
@@ -137,10 +156,18 @@ static void ahci_shutdown(AHCIQState *ahci)
  * Boot and fully enable the HBA device.
  * Boot and fully enable the HBA device.
  * @see ahci_boot, ahci_pci_enable and ahci_hba_enable.
  * @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;
     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_pci_enable(ahci);
     ahci_hba_enable(ahci);
     ahci_hba_enable(ahci);
@@ -738,7 +765,7 @@ static void ahci_test_identify(AHCIQState *ahci)
     ahci_port_clear(ahci, px);
     ahci_port_clear(ahci, px);
 
 
     /* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */
     /* "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 */
     /* Check serial number/version in the buffer */
     /* NB: IDENTIFY strings are packed in 16bit little endian chunks.
     /* NB: IDENTIFY strings are packed in 16bit little endian chunks.
@@ -754,11 +781,12 @@ static void ahci_test_identify(AHCIQState *ahci)
     g_assert_cmphex(rc, ==, 0);
     g_assert_cmphex(rc, ==, 0);
 
 
     sect_size = le16_to_cpu(*((uint16_t *)(&buff[5])));
     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,
 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;
     uint64_t ptr;
     uint8_t port;
     uint8_t port;
@@ -781,9 +809,9 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
     memwrite(ptr, tx, bufsize);
     memwrite(ptr, tx, bufsize);
 
 
     /* Write this buffer to disk, then read it back to the DMA buffer. */
     /* 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);
     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 ***/
     /*** Read back the Data ***/
     memread(ptr, rx, bufsize);
     memread(ptr, rx, bufsize);
@@ -794,6 +822,29 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
     g_free(rx);
     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                                                            */
 /* Test Interfaces                                                            */
 /******************************************************************************/
 /******************************************************************************/
@@ -804,7 +855,7 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
 static void test_sanity(void)
 static void test_sanity(void)
 {
 {
     AHCIQState *ahci;
     AHCIQState *ahci;
-    ahci = ahci_boot();
+    ahci = ahci_boot(NULL);
     ahci_shutdown(ahci);
     ahci_shutdown(ahci);
 }
 }
 
 
@@ -815,7 +866,7 @@ static void test_sanity(void)
 static void test_pci_spec(void)
 static void test_pci_spec(void)
 {
 {
     AHCIQState *ahci;
     AHCIQState *ahci;
-    ahci = ahci_boot();
+    ahci = ahci_boot(NULL);
     ahci_test_pci_spec(ahci);
     ahci_test_pci_spec(ahci);
     ahci_shutdown(ahci);
     ahci_shutdown(ahci);
 }
 }
@@ -827,8 +878,7 @@ static void test_pci_spec(void)
 static void test_pci_enable(void)
 static void test_pci_enable(void)
 {
 {
     AHCIQState *ahci;
     AHCIQState *ahci;
-
-    ahci = ahci_boot();
+    ahci = ahci_boot(NULL);
     ahci_pci_enable(ahci);
     ahci_pci_enable(ahci);
     ahci_shutdown(ahci);
     ahci_shutdown(ahci);
 }
 }
@@ -841,7 +891,7 @@ static void test_hba_spec(void)
 {
 {
     AHCIQState *ahci;
     AHCIQState *ahci;
 
 
-    ahci = ahci_boot();
+    ahci = ahci_boot(NULL);
     ahci_pci_enable(ahci);
     ahci_pci_enable(ahci);
     ahci_test_hba_spec(ahci);
     ahci_test_hba_spec(ahci);
     ahci_shutdown(ahci);
     ahci_shutdown(ahci);
@@ -855,7 +905,7 @@ static void test_hba_enable(void)
 {
 {
     AHCIQState *ahci;
     AHCIQState *ahci;
 
 
-    ahci = ahci_boot();
+    ahci = ahci_boot(NULL);
     ahci_pci_enable(ahci);
     ahci_pci_enable(ahci);
     ahci_hba_enable(ahci);
     ahci_hba_enable(ahci);
     ahci_shutdown(ahci);
     ahci_shutdown(ahci);
@@ -869,7 +919,7 @@ static void test_identify(void)
 {
 {
     AHCIQState *ahci;
     AHCIQState *ahci;
 
 
-    ahci = ahci_boot_and_enable();
+    ahci = ahci_boot_and_enable(NULL);
     ahci_test_identify(ahci);
     ahci_test_identify(ahci);
     ahci_shutdown(ahci);
     ahci_shutdown(ahci);
 }
 }
@@ -890,7 +940,7 @@ static void test_dma_fragmented(void)
     unsigned char *rx = g_malloc0(bufsize);
     unsigned char *rx = g_malloc0(bufsize);
     uint64_t ptr;
     uint64_t ptr;
 
 
-    ahci = ahci_boot_and_enable();
+    ahci = ahci_boot_and_enable(NULL);
     px = ahci_port_select(ahci);
     px = ahci_port_select(ahci);
     ahci_port_clear(ahci, px);
     ahci_port_clear(ahci, px);
 
 
@@ -928,6 +978,50 @@ static void test_dma_fragmented(void)
     g_free(tx);
     g_free(tx);
 }
 }
 
 
+static void test_flush(void)
+{
+    AHCIQState *ahci;
+
+    ahci = ahci_boot_and_enable(NULL);
+    ahci_test_flush(ahci);
+    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                                           */
 /* AHCI I/O Test Matrix Definitions                                           */
 
 
@@ -968,12 +1062,45 @@ enum IOOps {
     NUM_IO_OPS
     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 {
 typedef struct AHCIIOTestOptions {
     enum BuffLen length;
     enum BuffLen length;
     enum AddrMode address_type;
     enum AddrMode address_type;
     enum IOMode io_type;
     enum IOMode io_type;
+    enum OffsetType offset;
 } AHCIIOTestOptions;
 } 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.
  * Table of possible I/O ATA commands given a set of enumerations.
  */
  */
@@ -1001,12 +1128,12 @@ static const uint8_t io_cmds[NUM_MODES][NUM_ADDR_MODES][NUM_IO_OPS] = {
  * transfer modes, and buffer sizes.
  * transfer modes, and buffer sizes.
  */
  */
 static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma,
 static void test_io_rw_interface(enum AddrMode lba48, enum IOMode dma,
-                                 unsigned bufsize)
+                                 unsigned bufsize, uint64_t sector)
 {
 {
     AHCIQState *ahci;
     AHCIQState *ahci;
 
 
-    ahci = ahci_boot_and_enable();
-    ahci_test_io_rw_simple(ahci, bufsize,
+    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_READ],
                            io_cmds[dma][lba48][IO_WRITE]);
                            io_cmds[dma][lba48][IO_WRITE]);
     ahci_shutdown(ahci);
     ahci_shutdown(ahci);
@@ -1019,6 +1146,7 @@ static void test_io_interface(gconstpointer opaque)
 {
 {
     AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque;
     AHCIIOTestOptions *opts = (AHCIIOTestOptions *)opaque;
     unsigned bufsize;
     unsigned bufsize;
+    uint64_t sector;
 
 
     switch (opts->length) {
     switch (opts->length) {
     case LEN_SIMPLE:
     case LEN_SIMPLE:
@@ -1037,13 +1165,14 @@ static void test_io_interface(gconstpointer opaque)
         g_assert_not_reached();
         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);
     g_free(opts);
     return;
     return;
 }
 }
 
 
 static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
 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;
     static const char *arch;
     char *name;
     char *name;
@@ -1052,15 +1181,17 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
     opts->length = len;
     opts->length = len;
     opts->address_type = addr;
     opts->address_type = addr;
     opts->io_type = type;
     opts->io_type = type;
+    opts->offset = offset;
 
 
     if (!arch) {
     if (!arch) {
         arch = qtest_get_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],
                            io_mode_str[type],
                            addr_mode_str[addr],
                            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_test_add_data_func(name, opts, test_io_interface);
     g_free(name);
     g_free(name);
@@ -1071,10 +1202,10 @@ static void create_ahci_io_test(enum IOMode type, enum AddrMode addr,
 int main(int argc, char **argv)
 int main(int argc, char **argv)
 {
 {
     const char *arch;
     const char *arch;
-    int fd;
     int ret;
     int ret;
+    int fd;
     int c;
     int c;
-    int i, j, k;
+    int i, j, k, m;
 
 
     static struct option long_options[] = {
     static struct option long_options[] = {
         {"pedantic", no_argument, 0, 'p' },
         {"pedantic", no_argument, 0, 'p' },
@@ -1108,11 +1239,13 @@ int main(int argc, char **argv)
         return 0;
         return 0;
     }
     }
 
 
-    /* Create a temporary raw image */
-    fd = mkstemp(tmp_path);
+    /* Create a temporary qcow2 image */
+    close(mkstemp(tmp_path));
+    mkqcow2(tmp_path, TEST_IMAGE_SIZE_MB);
+
+    /* Create temporary blkdebug instructions */
+    fd = mkstemp(debug_path);
     g_assert(fd >= 0);
     g_assert(fd >= 0);
-    ret = ftruncate(fd, TEST_IMAGE_SIZE);
-    g_assert(ret == 0);
     close(fd);
     close(fd);
 
 
     /* Run the tests */
     /* Run the tests */
@@ -1126,17 +1259,23 @@ int main(int argc, char **argv)
     for (i = MODE_BEGIN; i < NUM_MODES; i++) {
     for (i = MODE_BEGIN; i < NUM_MODES; i++) {
         for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) {
         for (j = ADDR_MODE_BEGIN; j < NUM_ADDR_MODES; j++) {
             for (k = LEN_BEGIN; k < NUM_LENGTHS; k++) {
             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);
+                }
             }
             }
         }
         }
     }
     }
 
 
     qtest_add_func("/ahci/io/dma/lba28/fragmented", test_dma_fragmented);
     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();
     ret = g_test_run();
 
 
     /* Cleanup */
     /* Cleanup */
     unlink(tmp_path);
     unlink(tmp_path);
+    unlink(debug_path);
 
 
     return ret;
     return ret;
 }
 }

+ 2 - 32
tests/ide-test.c

@@ -29,6 +29,7 @@
 #include <glib.h>
 #include <glib.h>
 
 
 #include "libqtest.h"
 #include "libqtest.h"
+#include "libqos/libqos.h"
 #include "libqos/pci-pc.h"
 #include "libqos/pci-pc.h"
 #include "libqos/malloc-pc.h"
 #include "libqos/malloc-pc.h"
 
 
@@ -494,33 +495,10 @@ static void test_flush(void)
     ide_test_quit();
     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)
 static void test_retry_flush(const char *machine)
 {
 {
     uint8_t data;
     uint8_t data;
     const char *s;
     const char *s;
-    QDict *response;
 
 
     prepare_blkdebug_script(debug_path, "flush_to_disk");
     prepare_blkdebug_script(debug_path, "flush_to_disk");
 
 
@@ -539,15 +517,7 @@ static void test_retry_flush(const char *machine)
     assert_bit_set(data, BSY | DRDY);
     assert_bit_set(data, BSY | DRDY);
     assert_bit_clear(data, DF | ERR | DRQ);
     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 */
     /* Complete the command */
     s = "{'execute':'cont' }";
     s = "{'execute':'cont' }";

+ 6 - 4
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 */
 /* Given a guest buffer address, perform an IO operation */
 void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
 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;
     AHCICommand *cmd;
-
     cmd = ahci_command_create(ide_cmd);
     cmd = ahci_command_create(ide_cmd);
     ahci_command_set_buffer(cmd, buffer);
     ahci_command_set_buffer(cmd, buffer);
     ahci_command_set_size(cmd, bufsize);
     ahci_command_set_size(cmd, bufsize);
+    if (sector) {
+        ahci_command_set_offset(cmd, sector);
+    }
     ahci_command_commit(ahci, cmd, port);
     ahci_command_commit(ahci, cmd, port);
     ahci_command_issue(ahci, cmd);
     ahci_command_issue(ahci, cmd);
     ahci_command_verify(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. */
 /* 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 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;
     uint64_t ptr;
     AHCICommandProp *props;
     AHCICommandProp *props;
@@ -626,7 +628,7 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
         memwrite(ptr, buffer, bufsize);
         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) {
     if (props->read) {
         memread(ptr, buffer, bufsize);
         memread(ptr, buffer, bufsize);

+ 2 - 2
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 ahci_pick_cmd(AHCIQState *ahci, uint8_t port);
 unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd);
 unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd);
 void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
 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 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 */
 /* Command Lifecycle */
 AHCICommand *ahci_command_create(uint8_t command_name);
 AHCICommand *ahci_command_create(uint8_t command_name);

+ 5 - 0
tests/libqos/libqos-pc.c

@@ -6,6 +6,11 @@ static QOSOps qos_ops = {
     .uninit_allocator = pc_alloc_uninit
     .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 *qtest_pc_boot(const char *cmdline_fmt, ...)
 {
 {
     QOSState *qs;
     QOSState *qs;

+ 1 - 0
tests/libqos/libqos-pc.h

@@ -3,6 +3,7 @@
 
 
 #include "libqos/libqos.h"
 #include "libqos/libqos.h"
 
 
+QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap);
 QOSState *qtest_pc_boot(const char *cmdline_fmt, ...);
 QOSState *qtest_pc_boot(const char *cmdline_fmt, ...);
 void qtest_pc_shutdown(QOSState *qs);
 void qtest_pc_shutdown(QOSState *qs);
 
 

+ 66 - 0
tests/libqos/libqos.c

@@ -61,3 +61,69 @@ void qtest_shutdown(QOSState *qs)
     qtest_quit(qs->qts);
     qtest_quit(qs->qts);
     g_free(qs);
     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);
+}
+
+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);
+}

+ 3 - 0
tests/libqos/libqos.h

@@ -19,6 +19,9 @@ typedef struct QOSState {
 QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap);
 QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap);
 QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...);
 QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...);
 void qtest_shutdown(QOSState *qs);
 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)
 static inline uint64_t qmalloc(QOSState *q, size_t bytes)
 {
 {

+ 46 - 1
tests/libqtest.c

@@ -388,7 +388,12 @@ QDict *qtest_qmp_receive(QTestState *s)
     return qmp.response;
     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;
     va_list ap_copy;
     QObject *qobj;
     QObject *qobj;
@@ -417,6 +422,11 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
         QDECREF(qstr);
         QDECREF(qstr);
         qobject_decref(qobj);
         qobject_decref(qobj);
     }
     }
+}
+
+QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap)
+{
+    qtest_async_qmpv(s, fmt, ap);
 
 
     /* Receive reply */
     /* Receive reply */
     return qtest_qmp_receive(s);
     return qtest_qmp_receive(s);
@@ -433,6 +443,15 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...)
     return response;
     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)
 void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap)
 {
 {
     QDict *response = qtest_qmpv(s, fmt, ap);
     QDict *response = qtest_qmpv(s, fmt, ap);
@@ -450,9 +469,26 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...)
     QDECREF(response);
     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 *qtest_get_arch(void)
 {
 {
     const char *qemu = getenv("QTEST_QEMU_BINARY");
     const char *qemu = getenv("QTEST_QEMU_BINARY");
+    g_assert(qemu != NULL);
     const char *end = strrchr(qemu, '/');
     const char *end = strrchr(qemu, '/');
 
 
     return end + strlen("/qemu-system-");
     return end + strlen("/qemu-system-");
@@ -695,6 +731,15 @@ QDict *qmp(const char *fmt, ...)
     return response;
     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, ...)
 void qmp_discard_response(const char *fmt, ...)
 {
 {
     va_list ap;
     va_list ap;

+ 47 - 0
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, ...);
 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:
  * qtest_qmpv_discard_response:
  * @s: #QTestState instance to operate on.
  * @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);
 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:
  * qtest_receive:
  * @s: #QTestState instance to operate on.
  * @s: #QTestState instance to operate on.
@@ -91,6 +110,15 @@ QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap);
  */
  */
 QDict *qtest_qmp_receive(QTestState *s);
 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:
  * qtest_get_irq:
  * @s: #QTestState instance to operate on.
  * @s: #QTestState instance to operate on.
@@ -410,6 +438,14 @@ static inline void qtest_end(void)
  */
  */
 QDict *qmp(const char *fmt, ...);
 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:
  * qmp_discard_response:
  * @fmt...: QMP message to send to qemu
  * @fmt...: QMP message to send to qemu
@@ -428,6 +464,17 @@ static inline QDict *qmp_receive(void)
     return qtest_qmp_receive(global_qtest);
     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:
  * get_irq:
  * @num: Interrupt to observe.
  * @num: Interrupt to observe.