tests/qtest/ide-test: cover the migrated PIO transfer window
/ide/migration/pio_state_rejected leaves a drive in DRQ so the source streams ide_drive/pio_state, rewrites cur_io_buffer_offset to the end of the io_buffer, and expects the destination to refuse the load. It asserts the window the source wrote before overwriting it, so a wrong guess at the stream layout fails the test rather than passing it for the wrong reason. Cc: John Snow <jsnow@redhat.com> Cc: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Signed-off-by: Denis V. Lunev <den@openvz.org>
Denis V. Lunev committed
Aug 18, 2026 at 01:05 UTC
e46245cd3ba0b7a558fd33dc24dd630a0a9d59d0
1 file changed
+79
tests/qtest/ide-test.c
+79
@@ -1571,6 +1571,83 @@ static void test_migrate_chs_rejected(void)
1571
unlink(path);
1572
}
1573
1574
+/* A PIO transfer window reaching past the io_buffer has to be refused */
1575
+static void test_migrate_pio_state_rejected(void)
1576
+{
1577
+ const char *name = "ide_drive/pio_state";
1578
+ /* IDE_DMA_BUF_SECTORS * 512 + 4, the length of the streamed io_buffer */
1579
+ const gsize io_buffer_len = 256 * 512 + 4;
1580
+ /* cur_io_buffer_offset and cur_io_buffer_len, big endian */
1581
+ const uint8_t in_bounds[8] = { 0, 0, 0, 0, 0, 0, 0x02, 0 };
1582
+ const uint8_t past_the_end[8] = { 0, 0x02, 0, 0x04, 0, 0, 0x10, 0 };
1583
+ QTestState *src, *dst;
1584
+ QPCIDevice *dev;
1585
+ QPCIBar bmdma_bar, ide_bar;
1586
+ g_autofree char *path = NULL;
1587
+ g_autofree char *uri = NULL;
1588
+ g_autofree char *dst_args = NULL;
1589
+ g_autofree char *stream = NULL;
1590
+ char *window;
1591
+ gsize len;
1592
+ int fd;
1593
+
1594
+ fd = g_file_open_tmp("qtest-ide-stream.XXXXXX", &path, NULL);
1595
+ g_assert(fd >= 0);
1596
+ close(fd);
1597
+ uri = g_strdup_printf("file:%s", path);
1598
+
1599
+ src = ide_test_start(
1600
+ "-blockdev driver=file,node-name=hda,filename=%s "
1601
+ "-device ide-hd,drive=hda,bus=ide.0,unit=0 ",
1602
+ tmp_path[0]);
1603
+ dev = get_pci_device(src, &bmdma_bar, &ide_bar);
1604
+
1605
+ /* WRITE SECTOR(S) waits in DRQ for the data, so pio_state is streamed */
1606
+ qpci_io_writeb(dev, ide_bar, reg_nsectors, 1);
1607
+ qpci_io_writeb(dev, ide_bar, reg_lba_low, 0);
1608
+ qpci_io_writeb(dev, ide_bar, reg_lba_middle, 0);
1609
+ qpci_io_writeb(dev, ide_bar, reg_lba_high, 0);
1610
+ qpci_io_writeb(dev, ide_bar, reg_device, LBA);
1611
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_WRITE);
1612
+ assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRQ);
1613
+
1614
+ qtest_qmp_assert_success(src, "{ 'execute': 'migrate',"
1615
+ " 'arguments': { 'uri': %s } }", uri);
1616
+ qtest_qmp_eventwait(src, "STOP");
1617
+ ide_migration_wait(src, "completed");
1618
+ free_pci_device(dev);
1619
+ ide_test_quit(src);
1620
+
1621
+ /*
1622
+ * Behind the name come the version and req_nb_sectors as big endian 32
1623
+ * bit, then the io_buffer array, then the transfer window this rewrites.
1624
+ * Asserting the window the source streamed keeps that arithmetic honest.
1625
+ */
1626
+ g_assert(g_file_get_contents(path, &stream, &len, NULL));
1627
+ window = ide_stream_find(stream, len, name);
1628
+ g_assert(window);
1629
+ window += strlen(name) + 8 + io_buffer_len;
1630
+ g_assert_cmpint(window - stream + sizeof(past_the_end), <=, len);
1631
+ g_assert_cmpint(memcmp(window, in_bounds, sizeof(in_bounds)), ==, 0);
1632
+ memcpy(window, past_the_end, sizeof(past_the_end));
1633
+ g_assert(g_file_set_contents(path, stream, len, NULL));
1634
+
1635
+ dst_args = g_strdup_printf(
1636
+ "-machine pc "
1637
+ "-blockdev driver=file,node-name=hda,filename=%s "
1638
+ "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming defer",
1639
+ tmp_path[0]);
1640
+ dst = qtest_init(dst_args);
1641
+
1642
+ qtest_qmp_assert_success(dst, "{ 'execute': 'migrate-incoming',"
1643
+ " 'arguments': { 'uri': %s,"
1644
+ " 'exit-on-error': false } }", uri);
1645
+ ide_migration_wait(dst, "failed");
1646
+
1647
+ qtest_quit(dst);
1648
+ unlink(path);
1649
+}
1650
+
1651
/* Words 54 to 58 follow the translation even when the data was cached first */
1652
static void test_specify_identify(void)
1653
{
@@ -1764,6 +1841,8 @@ int main(int argc, char **argv)
1841
test_migrate_chs_translation);
1842
qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot);
1843
qtest_add_func("/ide/migration/chs_rejected", test_migrate_chs_rejected);
1844
+ qtest_add_func("/ide/migration/pio_state_rejected",
1845
+ test_migrate_pio_state_rejected);
1846
1847
qtest_add_func("/ide/identify", test_identify);
1848