From: "Denis V. Lunev" <den@openvz.org>
To: qemu-devel@nongnu.org
Cc: qemu-block@nongnu.org, "Denis V. Lunev" <den@openvz.org>,
"John Snow" <jsnow@redhat.com>,
"Philippe Mathieu-Daudé" <philmd@oss.qualcomm.com>
Subject: [PATCH 02/11] tests/qtest/ide-test: cover the migrated PIO transfer window
Date: Thu, 20 Aug 2026 16:43:00 +0200 [thread overview]
Message-ID: <20260820144309.835173-3-den@openvz.org> (raw)
In-Reply-To: <20260820144309.835173-1-den@openvz.org>
From: Denis V. Lunev <den@openvz.org>
/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>
---
tests/qtest/ide-test.c | 79 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c
index f14a0851f0..a3109da908 100644
--- a/tests/qtest/ide-test.c
+++ b/tests/qtest/ide-test.c
@@ -1571,6 +1571,83 @@ static void test_migrate_chs_rejected(void)
unlink(path);
}
+/* A PIO transfer window reaching past the io_buffer has to be refused */
+static void test_migrate_pio_state_rejected(void)
+{
+ const char *name = "ide_drive/pio_state";
+ /* IDE_DMA_BUF_SECTORS * 512 + 4, the length of the streamed io_buffer */
+ const gsize io_buffer_len = 256 * 512 + 4;
+ /* cur_io_buffer_offset and cur_io_buffer_len, big endian */
+ const uint8_t in_bounds[8] = { 0, 0, 0, 0, 0, 0, 0x02, 0 };
+ const uint8_t past_the_end[8] = { 0, 0x02, 0, 0x04, 0, 0, 0x10, 0 };
+ QTestState *src, *dst;
+ QPCIDevice *dev;
+ QPCIBar bmdma_bar, ide_bar;
+ g_autofree char *path = NULL;
+ g_autofree char *uri = NULL;
+ g_autofree char *dst_args = NULL;
+ g_autofree char *stream = NULL;
+ char *window;
+ gsize len;
+ int fd;
+
+ fd = g_file_open_tmp("qtest-ide-stream.XXXXXX", &path, NULL);
+ g_assert(fd >= 0);
+ close(fd);
+ uri = g_strdup_printf("file:%s", path);
+
+ src = ide_test_start(
+ "-blockdev driver=file,node-name=hda,filename=%s "
+ "-device ide-hd,drive=hda,bus=ide.0,unit=0 ",
+ tmp_path[0]);
+ dev = get_pci_device(src, &bmdma_bar, &ide_bar);
+
+ /* WRITE SECTOR(S) waits in DRQ for the data, so pio_state is streamed */
+ qpci_io_writeb(dev, ide_bar, reg_nsectors, 1);
+ qpci_io_writeb(dev, ide_bar, reg_lba_low, 0);
+ qpci_io_writeb(dev, ide_bar, reg_lba_middle, 0);
+ qpci_io_writeb(dev, ide_bar, reg_lba_high, 0);
+ qpci_io_writeb(dev, ide_bar, reg_device, LBA);
+ qpci_io_writeb(dev, ide_bar, reg_command, CMD_WRITE);
+ assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRQ);
+
+ qtest_qmp_assert_success(src, "{ 'execute': 'migrate',"
+ " 'arguments': { 'uri': %s } }", uri);
+ qtest_qmp_eventwait(src, "STOP");
+ ide_migration_wait(src, "completed");
+ free_pci_device(dev);
+ ide_test_quit(src);
+
+ /*
+ * Behind the name come the version and req_nb_sectors as big endian 32
+ * bit, then the io_buffer array, then the transfer window this rewrites.
+ * Asserting the window the source streamed keeps that arithmetic honest.
+ */
+ g_assert(g_file_get_contents(path, &stream, &len, NULL));
+ window = ide_stream_find(stream, len, name);
+ g_assert(window);
+ window += strlen(name) + 8 + io_buffer_len;
+ g_assert_cmpint(window - stream + sizeof(past_the_end), <=, len);
+ g_assert_cmpint(memcmp(window, in_bounds, sizeof(in_bounds)), ==, 0);
+ memcpy(window, past_the_end, sizeof(past_the_end));
+ g_assert(g_file_set_contents(path, stream, len, NULL));
+
+ dst_args = g_strdup_printf(
+ "-machine pc "
+ "-blockdev driver=file,node-name=hda,filename=%s "
+ "-device ide-hd,drive=hda,bus=ide.0,unit=0 -incoming defer",
+ tmp_path[0]);
+ dst = qtest_init(dst_args);
+
+ qtest_qmp_assert_success(dst, "{ 'execute': 'migrate-incoming',"
+ " 'arguments': { 'uri': %s,"
+ " 'exit-on-error': false } }", uri);
+ ide_migration_wait(dst, "failed");
+
+ qtest_quit(dst);
+ unlink(path);
+}
+
/* Words 54 to 58 follow the translation even when the data was cached first */
static void test_specify_identify(void)
{
@@ -1764,6 +1841,8 @@ int main(int argc, char **argv)
test_migrate_chs_translation);
qtest_add_func("/ide/migration/chs_snapshot", test_migrate_chs_snapshot);
qtest_add_func("/ide/migration/chs_rejected", test_migrate_chs_rejected);
+ qtest_add_func("/ide/migration/pio_state_rejected",
+ test_migrate_pio_state_rejected);
qtest_add_func("/ide/identify", test_identify);
--
2.53.0
next prev parent reply other threads:[~2026-08-20 14:43 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-20 14:42 [PATCH 00/11] hw/ide: pending IDE fixes Denis V. Lunev
2026-08-20 14:42 ` [PATCH 01/11] hw/ide: reject an out-of-range PIO transfer window on load Denis V. Lunev
2026-08-20 14:43 ` Denis V. Lunev [this message]
2026-08-20 14:43 ` [PATCH 03/11] hw/ide/ahci: refuse a PIO transfer with no command header Denis V. Lunev
2026-08-20 14:43 ` [PATCH 04/11] hw/ide/ahci: clear cur_cmd when the command list is unmapped Denis V. Lunev
2026-08-20 14:43 ` [PATCH 05/11] tests/qtest/ahci: regression test for a PIO write vs. engine stop Denis V. Lunev
2026-08-20 14:43 ` [PATCH 06/11] hw/ide/ahci: treat a failed PRDT walk as a PIO transfer failure Denis V. Lunev
2026-08-20 14:43 ` [PATCH 07/11] hw/ide/ahci: reject a command header with an invalid FIS length Denis V. Lunev
2026-08-20 14:43 ` [PATCH 08/11] hw/ide/ahci: drain the ports on teardown Denis V. Lunev
2026-08-20 14:43 ` [PATCH 09/11] tests/qtest/ahci: regression test for a request outliving an unplug Denis V. Lunev
2026-08-20 14:43 ` [PATCH 10/11] hw/ide: report ATAPI UDMA5 with a matching standard and cable Denis V. Lunev
2026-08-20 14:43 ` [PATCH 11/11] tests/qtest/ide-test: cover the UDMA5 identify words Denis V. Lunev
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260820144309.835173-3-den@openvz.org \
--to=den@openvz.org \
--cc=jsnow@redhat.com \
--cc=philmd@oss.qualcomm.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.