All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 1.5 0/5] ide: Test cases and a corner case fix
@ 2013-05-08  9:43 Kevin Wolf
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 1/5] ide: Reset BMIDEA bit when the bus master is stopped Kevin Wolf
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Kevin Wolf @ 2013-05-08  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, stefanha

Kevin Wolf (5):
  ide: Reset BMIDEA bit when the bus master is stopped
  libqos/pci: Enable bus mastering
  qtest: Add IDE test case
  qtest/ide-test: Add simple DMA read/write test case
  qtest/ide-test: Test short and long PRDTs

 hw/ide/pci.c       |   1 +
 tests/Makefile     |   2 +
 tests/ide-test.c   | 440 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 tests/libqos/pci.c |   2 +-
 4 files changed, 444 insertions(+), 1 deletion(-)
 create mode 100644 tests/ide-test.c

-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 1/5] ide: Reset BMIDEA bit when the bus master is stopped
  2013-05-08  9:43 [Qemu-devel] [PATCH 1.5 0/5] ide: Test cases and a corner case fix Kevin Wolf
@ 2013-05-08  9:43 ` Kevin Wolf
  2013-05-08 14:35   ` Stefan Hajnoczi
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 2/5] libqos/pci: Enable bus mastering Kevin Wolf
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 8+ messages in thread
From: Kevin Wolf @ 2013-05-08  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, stefanha

The device is supposed to reset the Bus Master IDE Active bit in the
status register when 0 is written to the Start/Stop Bus Master bit in
the command register.

In the common cases this happens automatically because bdrv_drain_all()
flushes the requests, but with a large PRDT it could remain set.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 hw/ide/pci.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/hw/ide/pci.c b/hw/ide/pci.c
index a310975..635a364 100644
--- a/hw/ide/pci.c
+++ b/hw/ide/pci.c
@@ -312,6 +312,7 @@ void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val)
                 bdrv_drain_all();
                 assert(bm->bus->dma->aiocb == NULL);
             }
+            bm->status &= ~BM_STATUS_DMAING;
         } else {
             bm->cur_addr = bm->addr;
             if (!(bm->status & BM_STATUS_DMAING)) {
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 2/5] libqos/pci: Enable bus mastering
  2013-05-08  9:43 [Qemu-devel] [PATCH 1.5 0/5] ide: Test cases and a corner case fix Kevin Wolf
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 1/5] ide: Reset BMIDEA bit when the bus master is stopped Kevin Wolf
@ 2013-05-08  9:43 ` Kevin Wolf
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 3/5] qtest: Add IDE test case Kevin Wolf
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Kevin Wolf @ 2013-05-08  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, stefanha

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/libqos/pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c
index 95e287b..7e0907b 100644
--- a/tests/libqos/pci.c
+++ b/tests/libqos/pci.c
@@ -71,7 +71,7 @@ void qpci_device_enable(QPCIDevice *dev)
 
     /* FIXME -- does this need to be a bus callout? */
     cmd = qpci_config_readw(dev, PCI_COMMAND);
-    cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
+    cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
     qpci_config_writew(dev, PCI_COMMAND, cmd);
 }
 
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 3/5] qtest: Add IDE test case
  2013-05-08  9:43 [Qemu-devel] [PATCH 1.5 0/5] ide: Test cases and a corner case fix Kevin Wolf
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 1/5] ide: Reset BMIDEA bit when the bus master is stopped Kevin Wolf
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 2/5] libqos/pci: Enable bus mastering Kevin Wolf
@ 2013-05-08  9:43 ` Kevin Wolf
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 4/5] qtest/ide-test: Add simple DMA read/write " Kevin Wolf
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 5/5] qtest/ide-test: Test short and long PRDTs Kevin Wolf
  4 siblings, 0 replies; 8+ messages in thread
From: Kevin Wolf @ 2013-05-08  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, stefanha

This adds a simple IDE test case and starts by verifying that IDENTIFY
can be successfully used and return the correct serial number, version
and the WCE flag is set for cache=writeback.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/Makefile   |   2 +
 tests/ide-test.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 167 insertions(+)
 create mode 100644 tests/ide-test.c

diff --git a/tests/Makefile b/tests/Makefile
index bf41d10..a307d5a 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -51,6 +51,7 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 # really in libqtest, not in the testcases themselves.
 check-qtest-i386-y = tests/fdc-test$(EXESUF)
 gcov-files-i386-y = hw/fdc.c
+check-qtest-i386-y += tests/ide-test$(EXESUF)
 check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
 gcov-files-i386-y += hw/hd-geometry.c
 check-qtest-i386-y += tests/rtc-test$(EXESUF)
@@ -127,6 +128,7 @@ libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
 tests/rtc-test$(EXESUF): tests/rtc-test.o
 tests/m48t59-test$(EXESUF): tests/m48t59-test.o
 tests/fdc-test$(EXESUF): tests/fdc-test.o
+tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
 tests/tmp105-test$(EXESUF): tests/tmp105-test.o $(libqos-omap-obj-y)
 tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y)
diff --git a/tests/ide-test.c b/tests/ide-test.c
new file mode 100644
index 0000000..45036e3
--- /dev/null
+++ b/tests/ide-test.c
@@ -0,0 +1,165 @@
+/*
+ * IDE test cases
+ *
+ * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include "libqtest.h"
+
+#include "qemu-common.h"
+
+#define TEST_IMAGE_SIZE 64 * 1024 * 1024
+
+#define IDE_PCI_DEV     1
+#define IDE_PCI_FUNC    1
+
+#define IDE_BASE 0x1f0
+#define IDE_PRIMARY_IRQ 14
+
+enum {
+    reg_data        = 0x0,
+    reg_nsectors    = 0x2,
+    reg_lba_low     = 0x3,
+    reg_lba_middle  = 0x4,
+    reg_lba_high    = 0x5,
+    reg_device      = 0x6,
+    reg_status      = 0x7,
+    reg_command     = 0x7,
+};
+
+enum {
+    BSY     = 0x80,
+    DRDY    = 0x40,
+    DF      = 0x20,
+    DRQ     = 0x08,
+    ERR     = 0x01,
+};
+
+enum {
+    CMD_IDENTIFY    = 0xec,
+};
+
+#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
+#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
+
+static char tmp_path[] = "/tmp/qtest.XXXXXX";
+
+static void ide_test_start(const char *cmdline_fmt, ...)
+{
+    va_list ap;
+    char *cmdline;
+
+    va_start(ap, cmdline_fmt);
+    cmdline = g_strdup_vprintf(cmdline_fmt, ap);
+    va_end(ap);
+
+    qtest_start(cmdline);
+    qtest_irq_intercept_in(global_qtest, "ioapic");
+}
+
+static void ide_test_quit(void)
+{
+    qtest_quit(global_qtest);
+}
+
+static void test_identify(void)
+{
+    uint8_t data;
+    uint16_t buf[256];
+    int i;
+    int ret;
+
+    ide_test_start(
+        "-vnc none "
+        "-drive file=%s,if=ide,serial=%s,cache=writeback "
+        "-global ide-hd.ver=%s",
+        tmp_path, "testdisk", "version");
+
+    /* IDENTIFY command on device 0*/
+    outb(IDE_BASE + reg_device, 0);
+    outb(IDE_BASE + reg_command, CMD_IDENTIFY);
+
+    /* Read in the IDENTIFY buffer and check registers */
+    data = inb(IDE_BASE + reg_device);
+    g_assert_cmpint(data & 0x10, ==, 0);
+
+    for (i = 0; i < 256; i++) {
+        data = inb(IDE_BASE + reg_status);
+        assert_bit_set(data, DRDY | DRQ);
+        assert_bit_clear(data, BSY | DF | ERR);
+
+        ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data);
+    }
+
+    data = inb(IDE_BASE + reg_status);
+    assert_bit_set(data, DRDY);
+    assert_bit_clear(data, BSY | DF | ERR | DRQ);
+
+    /* Check serial number/version in the buffer */
+    ret = memcmp(&buf[10], "ettsidks            ", 20);
+    g_assert(ret == 0);
+
+    ret = memcmp(&buf[23], "evsroi n", 8);
+    g_assert(ret == 0);
+
+    /* Write cache enabled bit */
+    assert_bit_set(buf[85], 0x20);
+
+    ide_test_quit();
+}
+
+int main(int argc, char **argv)
+{
+    const char *arch = qtest_get_arch();
+    int fd;
+    int ret;
+
+    /* Check architecture */
+    if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) {
+        g_test_message("Skipping test for non-x86\n");
+        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);
+
+    /* Run the tests */
+    g_test_init(&argc, &argv, NULL);
+
+    qtest_add_func("/ide/identify", test_identify);
+
+    ret = g_test_run();
+
+    /* Cleanup */
+    unlink(tmp_path);
+
+    return ret;
+}
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 4/5] qtest/ide-test: Add simple DMA read/write test case
  2013-05-08  9:43 [Qemu-devel] [PATCH 1.5 0/5] ide: Test cases and a corner case fix Kevin Wolf
                   ` (2 preceding siblings ...)
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 3/5] qtest: Add IDE test case Kevin Wolf
@ 2013-05-08  9:43 ` Kevin Wolf
  2013-05-08 16:28   ` Paolo Bonzini
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 5/5] qtest/ide-test: Test short and long PRDTs Kevin Wolf
  4 siblings, 1 reply; 8+ messages in thread
From: Kevin Wolf @ 2013-05-08  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, stefanha

This tests that single sectors can be successfully written and correctly
read back.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/ide-test.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 221 insertions(+)

diff --git a/tests/ide-test.c b/tests/ide-test.c
index 45036e3..5fc496f 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -29,8 +29,12 @@
 #include <glib.h>
 
 #include "libqtest.h"
+#include "libqos/pci-pc.h"
+#include "libqos/malloc-pc.h"
 
 #include "qemu-common.h"
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_regs.h"
 
 #define TEST_IMAGE_SIZE 64 * 1024 * 1024
 
@@ -60,12 +64,42 @@ enum {
 };
 
 enum {
+    LBA     = 0x40,
+};
+
+enum {
+    bmreg_cmd       = 0x0,
+    bmreg_status    = 0x2,
+    bmreg_prdt      = 0x4,
+};
+
+enum {
+    CMD_READ_DMA    = 0xc8,
+    CMD_WRITE_DMA   = 0xca,
     CMD_IDENTIFY    = 0xec,
 };
 
+enum {
+    BM_CMD_START    =  0x1,
+    BM_CMD_WRITE    =  0x8, /* write = from device to memory */
+};
+
+enum {
+    BM_STS_ACTIVE   =  0x1,
+    BM_STS_ERROR    =  0x2,
+    BM_STS_INTR     =  0x4,
+};
+
+enum {
+    PRDT_EOT        = 0x80000000,
+};
+
 #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
 #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
 
+static QPCIBus *pcibus = NULL;
+static QGuestAllocator *guest_malloc;
+
 static char tmp_path[] = "/tmp/qtest.XXXXXX";
 
 static void ide_test_start(const char *cmdline_fmt, ...)
@@ -79,6 +113,7 @@ static void ide_test_start(const char *cmdline_fmt, ...)
 
     qtest_start(cmdline);
     qtest_irq_intercept_in(global_qtest, "ioapic");
+    guest_malloc = pc_alloc_init();
 }
 
 static void ide_test_quit(void)
@@ -86,6 +121,188 @@ static void ide_test_quit(void)
     qtest_quit(global_qtest);
 }
 
+static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
+{
+    QPCIDevice *dev;
+    uint16_t vendor_id, device_id;
+
+    if (!pcibus) {
+        pcibus = qpci_init_pc();
+    }
+
+    /* Find PCI device and verify it's the right one */
+    dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC));
+    g_assert(dev != NULL);
+
+    vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
+    device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
+    g_assert(vendor_id == PCI_VENDOR_ID_INTEL);
+    g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
+
+    /* Map bmdma BAR */
+    *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4);
+
+    qpci_device_enable(dev);
+
+    return dev;
+}
+
+static void free_pci_device(QPCIDevice *dev)
+{
+    /* libqos doesn't have a function for this, so free it manually */
+    g_free(dev);
+}
+
+typedef struct PrdtEntry {
+    uint32_t addr;
+    uint32_t size;
+} QEMU_PACKED PrdtEntry;
+
+#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
+#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
+
+static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
+                            PrdtEntry *prdt, int prdt_entries)
+{
+    QPCIDevice *dev;
+    uint16_t bmdma_base;
+    uintptr_t guest_prdt;
+    size_t len;
+    bool from_dev;
+    uint8_t status;
+
+    dev = get_pci_device(&bmdma_base);
+
+    switch (cmd) {
+    case CMD_READ_DMA:
+        from_dev = true;
+        break;
+    case CMD_WRITE_DMA:
+        from_dev = false;
+        break;
+    default:
+        g_assert_not_reached();
+    }
+
+    /* Select device 0 */
+    outb(IDE_BASE + reg_device, 0 | LBA);
+
+    /* Stop any running transfer, clear any pending interrupt */
+    outb(bmdma_base + bmreg_cmd, 0);
+    outb(bmdma_base + bmreg_status, BM_STS_INTR);
+
+    /* Setup PRDT */
+    len = sizeof(*prdt) * prdt_entries;
+    guest_prdt = guest_alloc(guest_malloc, len);
+    memwrite(guest_prdt, prdt, len);
+    outl(bmdma_base + bmreg_prdt, guest_prdt);
+
+    /* ATA DMA command */
+    outb(IDE_BASE + reg_nsectors, nb_sectors);
+
+    outb(IDE_BASE + reg_lba_low,    sector & 0xff);
+    outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff);
+    outb(IDE_BASE + reg_lba_high,   (sector >> 16) & 0xff);
+
+    outb(IDE_BASE + reg_command, cmd);
+
+    /* Start DMA transfer */
+    outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
+
+    /* Wait for the DMA transfer to complete */
+    do {
+        status = inb(bmdma_base + bmreg_status);
+    } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE);
+
+    g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR));
+
+    /* Check IDE status code */
+    assert_bit_set(inb(IDE_BASE + reg_status), DRDY);
+    assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ);
+
+    /* Reading the status register clears the IRQ */
+    g_assert(!get_irq(IDE_PRIMARY_IRQ));
+
+    /* Stop DMA transfer if still active */
+    if (status & BM_STS_ACTIVE) {
+        outb(bmdma_base + bmreg_cmd, 0);
+    }
+
+    free_pci_device(dev);
+
+    return status;
+}
+
+static void test_bmdma_simple_rw(void)
+{
+    uint8_t status;
+    uint8_t *buf;
+    uint8_t *cmpbuf;
+    size_t len = 512;
+    uintptr_t guest_buf = guest_alloc(guest_malloc, len);
+
+    PrdtEntry prdt[] = {
+        { .addr = guest_buf, .size = len | PRDT_EOT },
+    };
+
+    buf = g_malloc(len);
+    cmpbuf = g_malloc(len);
+
+    /* Write 0x55 pattern to sector 0 */
+    memset(buf, 0x55, len);
+    memwrite(guest_buf, buf, len);
+
+    status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, BM_STS_INTR);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+
+    /* Write 0xaa pattern to sector 1 */
+    memset(buf, 0xaa, len);
+    memwrite(guest_buf, buf, len);
+
+    status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, BM_STS_INTR);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+
+    /* Read and verify 0x55 pattern in sector 0 */
+    memset(cmpbuf, 0x55, len);
+
+    status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, BM_STS_INTR);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+
+    memread(guest_buf, buf, len);
+    g_assert(memcmp(buf, cmpbuf, len) == 0);
+
+    /* Read and verify 0xaa pattern in sector 1 */
+    memset(cmpbuf, 0xaa, len);
+
+    status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, BM_STS_INTR);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+
+    memread(guest_buf, buf, len);
+    g_assert(memcmp(buf, cmpbuf, len) == 0);
+
+
+    g_free(buf);
+    g_free(cmpbuf);
+}
+
+static void test_bmdma_setup(void)
+{
+    ide_test_start(
+        "-vnc none "
+        "-drive file=%s,if=ide,serial=%s,cache=writeback "
+        "-global ide-hd.ver=%s",
+        tmp_path, "testdisk", "version");
+}
+
+static void test_bmdma_teardown(void)
+{
+    ide_test_quit();
+}
+
 static void test_identify(void)
 {
     uint8_t data;
@@ -156,6 +373,10 @@ int main(int argc, char **argv)
 
     qtest_add_func("/ide/identify", test_identify);
 
+    qtest_add_func("/ide/bmdma/setup", test_bmdma_setup);
+    qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
+    qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
+
     ret = g_test_run();
 
     /* Cleanup */
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [Qemu-devel] [PATCH 5/5] qtest/ide-test: Test short and long PRDTs
  2013-05-08  9:43 [Qemu-devel] [PATCH 1.5 0/5] ide: Test cases and a corner case fix Kevin Wolf
                   ` (3 preceding siblings ...)
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 4/5] qtest/ide-test: Add simple DMA read/write " Kevin Wolf
@ 2013-05-08  9:43 ` Kevin Wolf
  4 siblings, 0 replies; 8+ messages in thread
From: Kevin Wolf @ 2013-05-08  9:43 UTC (permalink / raw)
  To: qemu-devel; +Cc: kwolf, stefanha

This tests the behaviour of the DMA engine when the given PRDT contains
physical region descriptors for either more or less bytes than the
IDE request is for.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
 tests/ide-test.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/tests/ide-test.c b/tests/ide-test.c
index 5fc496f..bdc1da7 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -77,6 +77,8 @@ enum {
     CMD_READ_DMA    = 0xc8,
     CMD_WRITE_DMA   = 0xca,
     CMD_IDENTIFY    = 0xec,
+
+    CMDF_ABORT      = 0x100,
 };
 
 enum {
@@ -170,9 +172,13 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
     size_t len;
     bool from_dev;
     uint8_t status;
+    int flags;
 
     dev = get_pci_device(&bmdma_base);
 
+    flags = cmd & ~0xff;
+    cmd &= 0xff;
+
     switch (cmd) {
     case CMD_READ_DMA:
         from_dev = true;
@@ -209,6 +215,10 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
     /* Start DMA transfer */
     outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0));
 
+    if (flags & CMDF_ABORT) {
+        outb(bmdma_base + bmreg_cmd, 0);
+    }
+
     /* Wait for the DMA transfer to complete */
     do {
         status = inb(bmdma_base + bmreg_status);
@@ -289,6 +299,48 @@ static void test_bmdma_simple_rw(void)
     g_free(cmpbuf);
 }
 
+static void test_bmdma_short_prdt(void)
+{
+    uint8_t status;
+
+    PrdtEntry prdt[] = {
+        { .addr = 0, .size = 0x10 | PRDT_EOT },
+    };
+
+    /* Normal request */
+    status = send_dma_request(CMD_READ_DMA, 0, 1,
+                              prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, 0);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+
+    /* Abort the request before it completes */
+    status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
+                              prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, 0);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+}
+
+static void test_bmdma_long_prdt(void)
+{
+    uint8_t status;
+
+    PrdtEntry prdt[] = {
+        { .addr = 0, .size = 0x1000 | PRDT_EOT },
+    };
+
+    /* Normal request */
+    status = send_dma_request(CMD_READ_DMA, 0, 1,
+                              prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+
+    /* Abort the request before it completes */
+    status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1,
+                              prdt, ARRAY_SIZE(prdt));
+    g_assert_cmphex(status, ==, BM_STS_INTR);
+    assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR);
+}
+
 static void test_bmdma_setup(void)
 {
     ide_test_start(
@@ -375,6 +427,8 @@ int main(int argc, char **argv)
 
     qtest_add_func("/ide/bmdma/setup", test_bmdma_setup);
     qtest_add_func("/ide/bmdma/simple_rw", test_bmdma_simple_rw);
+    qtest_add_func("/ide/bmdma/short_prdt", test_bmdma_short_prdt);
+    qtest_add_func("/ide/bmdma/long_prdt", test_bmdma_long_prdt);
     qtest_add_func("/ide/bmdma/teardown", test_bmdma_teardown);
 
     ret = g_test_run();
-- 
1.8.1.4

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 1/5] ide: Reset BMIDEA bit when the bus master is stopped
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 1/5] ide: Reset BMIDEA bit when the bus master is stopped Kevin Wolf
@ 2013-05-08 14:35   ` Stefan Hajnoczi
  0 siblings, 0 replies; 8+ messages in thread
From: Stefan Hajnoczi @ 2013-05-08 14:35 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, stefanha

On Wed, May 08, 2013 at 11:43:22AM +0200, Kevin Wolf wrote:
> The device is supposed to reset the Bus Master IDE Active bit in the
> status register when 0 is written to the Start/Stop Bus Master bit in
> the command register.
> 
> In the common cases this happens automatically because bdrv_drain_all()
> flushes the requests, but with a large PRDT it could remain set.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  hw/ide/pci.c | 1 +
>  1 file changed, 1 insertion(+)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [Qemu-devel] [PATCH 4/5] qtest/ide-test: Add simple DMA read/write test case
  2013-05-08  9:43 ` [Qemu-devel] [PATCH 4/5] qtest/ide-test: Add simple DMA read/write " Kevin Wolf
@ 2013-05-08 16:28   ` Paolo Bonzini
  0 siblings, 0 replies; 8+ messages in thread
From: Paolo Bonzini @ 2013-05-08 16:28 UTC (permalink / raw)
  To: Kevin Wolf; +Cc: qemu-devel, stefanha

Il 08/05/2013 11:43, Kevin Wolf ha scritto:
> This tests that single sectors can be successfully written and correctly
> read back.
> 
> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
> ---
>  tests/ide-test.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 221 insertions(+)
> 
> diff --git a/tests/ide-test.c b/tests/ide-test.c
> index 45036e3..5fc496f 100644
> --- a/tests/ide-test.c
> +++ b/tests/ide-test.c
> @@ -29,8 +29,12 @@
>  #include <glib.h>
>  
>  #include "libqtest.h"
> +#include "libqos/pci-pc.h"
> +#include "libqos/malloc-pc.h"
>  
>  #include "qemu-common.h"
> +#include "hw/pci/pci_ids.h"
> +#include "hw/pci/pci_regs.h"
>  
>  #define TEST_IMAGE_SIZE 64 * 1024 * 1024
>  
> @@ -60,12 +64,42 @@ enum {
>  };
>  
>  enum {
> +    LBA     = 0x40,
> +};
> +
> +enum {
> +    bmreg_cmd       = 0x0,
> +    bmreg_status    = 0x2,
> +    bmreg_prdt      = 0x4,
> +};
> +
> +enum {
> +    CMD_READ_DMA    = 0xc8,
> +    CMD_WRITE_DMA   = 0xca,
>      CMD_IDENTIFY    = 0xec,
>  };
>  
> +enum {
> +    BM_CMD_START    =  0x1,
> +    BM_CMD_WRITE    =  0x8, /* write = from device to memory */
> +};
> +
> +enum {
> +    BM_STS_ACTIVE   =  0x1,
> +    BM_STS_ERROR    =  0x2,
> +    BM_STS_INTR     =  0x4,
> +};
> +
> +enum {
> +    PRDT_EOT        = 0x80000000,
> +};
> +
>  #define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
>  #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
>  
> +static QPCIBus *pcibus = NULL;
> +static QGuestAllocator *guest_malloc;
> +
>  static char tmp_path[] = "/tmp/qtest.XXXXXX";
>  
>  static void ide_test_start(const char *cmdline_fmt, ...)
> @@ -79,6 +113,7 @@ static void ide_test_start(const char *cmdline_fmt, ...)
>  
>      qtest_start(cmdline);
>      qtest_irq_intercept_in(global_qtest, "ioapic");
> +    guest_malloc = pc_alloc_init();
>  }
>  
>  static void ide_test_quit(void)
> @@ -86,6 +121,188 @@ static void ide_test_quit(void)
>      qtest_quit(global_qtest);
>  }
>  
> +static QPCIDevice *get_pci_device(uint16_t *bmdma_base)
> +{
> +    QPCIDevice *dev;
> +    uint16_t vendor_id, device_id;
> +
> +    if (!pcibus) {
> +        pcibus = qpci_init_pc();
> +    }
> +
> +    /* Find PCI device and verify it's the right one */
> +    dev = qpci_device_find(pcibus, QPCI_DEVFN(IDE_PCI_DEV, IDE_PCI_FUNC));
> +    g_assert(dev != NULL);
> +
> +    vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
> +    device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
> +    g_assert(vendor_id == PCI_VENDOR_ID_INTEL);
> +    g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1);
> +
> +    /* Map bmdma BAR */
> +    *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4);
> +
> +    qpci_device_enable(dev);
> +
> +    return dev;
> +}
> +
> +static void free_pci_device(QPCIDevice *dev)
> +{
> +    /* libqos doesn't have a function for this, so free it manually */
> +    g_free(dev);
> +}
> +
> +typedef struct PrdtEntry {
> +    uint32_t addr;
> +    uint32_t size;
> +} QEMU_PACKED PrdtEntry;
> +
> +#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
> +#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
> +
> +static int send_dma_request(int cmd, uint64_t sector, int nb_sectors,
> +                            PrdtEntry *prdt, int prdt_entries)
> +{
> +    QPCIDevice *dev;
> +    uint16_t bmdma_base;
> +    uintptr_t guest_prdt;
> +    size_t len;
> +    bool from_dev;
> +    uint8_t status;
> +
> +    dev = get_pci_device(&bmdma_base);
> +
> +    switch (cmd) {
> +    case CMD_READ_DMA:
> +        from_dev = true;
> +        break;
> +    case CMD_WRITE_DMA:
> +        from_dev = false;
> +        break;
> +    default:
> +        g_assert_not_reached();
> +    }
> +
> +    /* Select device 0 */
> +    outb(IDE_BASE + reg_device, 0 | LBA);
> +
> +    /* Stop any running transfer, clear any pending interrupt */
> +    outb(bmdma_base + bmreg_cmd, 0);
> +    outb(bmdma_base + bmreg_status, BM_STS_INTR);
> +
> +    /* Setup PRDT */
> +    len = sizeof(*prdt) * prdt_entries;
> +    guest_prdt = guest_alloc(guest_malloc, len);
> +    memwrite(guest_prdt, prdt, len);

What endianness?

Paolo

> +    outl(bmdma_base + bmreg_prdt, guest_prdt);

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2013-05-08 16:28 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-05-08  9:43 [Qemu-devel] [PATCH 1.5 0/5] ide: Test cases and a corner case fix Kevin Wolf
2013-05-08  9:43 ` [Qemu-devel] [PATCH 1/5] ide: Reset BMIDEA bit when the bus master is stopped Kevin Wolf
2013-05-08 14:35   ` Stefan Hajnoczi
2013-05-08  9:43 ` [Qemu-devel] [PATCH 2/5] libqos/pci: Enable bus mastering Kevin Wolf
2013-05-08  9:43 ` [Qemu-devel] [PATCH 3/5] qtest: Add IDE test case Kevin Wolf
2013-05-08  9:43 ` [Qemu-devel] [PATCH 4/5] qtest/ide-test: Add simple DMA read/write " Kevin Wolf
2013-05-08 16:28   ` Paolo Bonzini
2013-05-08  9:43 ` [Qemu-devel] [PATCH 5/5] qtest/ide-test: Test short and long PRDTs Kevin Wolf

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.