From 0ee20e665840d8a887c145b368ee121cb86a028e Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 6 May 2013 15:58:04 +0200 Subject: [PATCH 01/10] ahci: Don't allow creating slave drives An IDE bus provided by AHCI can only take a single IDE drive. If you add a drive as slave, qemu used to accept the command line but the device wouldn't be actually usable. Catch the situation instead and error out. Signed-off-by: Kevin Wolf --- hw/ide/ahci.c | 2 +- hw/ide/cmd646.c | 2 +- hw/ide/internal.h | 3 ++- hw/ide/isa.c | 2 +- hw/ide/macio.c | 2 +- hw/ide/mmio.c | 2 +- hw/ide/piix.c | 2 +- hw/ide/qdev.c | 10 +++++++++- hw/ide/via.c | 2 +- 9 files changed, 18 insertions(+), 9 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 3405583f5..eab60961b 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1163,7 +1163,7 @@ void ahci_init(AHCIState *s, DeviceState *qdev, DMAContext *dma, int ports) for (i = 0; i < s->ports; i++) { AHCIDevice *ad = &s->dev[i]; - ide_bus_new(&ad->port, qdev, i); + ide_bus_new(&ad->port, qdev, i, 1); ide_init2(&ad->port, irqs[i]); ad->hba = s; diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 541d4ef33..a73eb9a5f 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -281,7 +281,7 @@ static int pci_cmd646_ide_initfn(PCIDevice *dev) irq = qemu_allocate_irqs(cmd646_set_irq, d, 2); for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], &d->dev.qdev, i); + ide_bus_new(&d->bus[i], &d->dev.qdev, i, 2); ide_init2(&d->bus[i], irq[i]); bmdma_init(&d->bus[i], &d->bmdma[i], d); diff --git a/hw/ide/internal.h b/hw/ide/internal.h index 0efb2da19..03f14896d 100644 --- a/hw/ide/internal.h +++ b/hw/ide/internal.h @@ -450,6 +450,7 @@ struct IDEBus { IDEDevice *slave; IDEState ifs[2]; int bus_id; + int max_units; IDEDMA *dma; uint8_t unit; uint8_t cmd; @@ -574,7 +575,7 @@ void ide_atapi_cmd(IDEState *s); void ide_atapi_cmd_reply_end(IDEState *s); /* hw/ide/qdev.c */ -void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id); +void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id, int max_units); IDEDevice *ide_create_drive(IDEBus *bus, int unit, DriveInfo *drive); #endif /* HW_IDE_INTERNAL_H */ diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 5e7422fc2..369a7fa58 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -69,7 +69,7 @@ static int isa_ide_initfn(ISADevice *dev) { ISAIDEState *s = ISA_IDE(dev); - ide_bus_new(&s->bus, DEVICE(dev), 0); + ide_bus_new(&s->bus, DEVICE(dev), 0, 2); ide_init_ioport(&s->bus, dev, s->iobase, s->iobase2); isa_init_irq(dev, &s->irq, s->isairq); ide_init2(&s->bus, s->irq); diff --git a/hw/ide/macio.c b/hw/ide/macio.c index 64b2406ab..bf12a100d 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -334,7 +334,7 @@ static void macio_ide_initfn(Object *obj) SysBusDevice *d = SYS_BUS_DEVICE(obj); MACIOIDEState *s = MACIO_IDE(obj); - ide_bus_new(&s->bus, DEVICE(obj), 0); + ide_bus_new(&s->bus, DEVICE(obj), 0, 2); memory_region_init_io(&s->mem, &pmac_ide_ops, s, "pmac-ide", 0x1000); sysbus_init_mmio(d, &s->mem); sysbus_init_irq(d, &s->irq); diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index ce88c3a65..e80e7e5af 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -137,7 +137,7 @@ static void mmio_ide_initfn(Object *obj) SysBusDevice *d = SYS_BUS_DEVICE(obj); MMIOState *s = MMIO_IDE(obj); - ide_bus_new(&s->bus, DEVICE(obj), 0); + ide_bus_new(&s->bus, DEVICE(obj), 0, 2); sysbus_init_irq(d, &s->irq); } diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 1de284d0e..bf2856fb6 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -135,7 +135,7 @@ static void pci_piix_init_ports(PCIIDEState *d) { int i; for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], &d->dev.qdev, i); + ide_bus_new(&d->bus[i], &d->dev.qdev, i, 2); ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, port_info[i].iobase2); ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 8a9a89176..6a272b046 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -47,10 +47,11 @@ static const TypeInfo ide_bus_info = { .class_init = ide_bus_class_init, }; -void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id) +void ide_bus_new(IDEBus *idebus, DeviceState *dev, int bus_id, int max_units) { qbus_create_inplace(&idebus->qbus, TYPE_IDE_BUS, dev, NULL); idebus->bus_id = bus_id; + idebus->max_units = max_units; } static char *idebus_get_fw_dev_path(DeviceState *dev) @@ -76,6 +77,13 @@ static int ide_qdev_init(DeviceState *qdev) if (dev->unit == -1) { dev->unit = bus->master ? 1 : 0; } + + if (dev->unit >= bus->max_units) { + error_report("Can't create IDE unit %d, bus supports only %d units", + dev->unit, bus->max_units); + goto err; + } + switch (dev->unit) { case 0: if (bus->master) { diff --git a/hw/ide/via.c b/hw/ide/via.c index 9d6a64439..5fe053cf9 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -158,7 +158,7 @@ static void vt82c686b_init_ports(PCIIDEState *d) { int i; for (i = 0; i < 2; i++) { - ide_bus_new(&d->bus[i], &d->dev.qdev, i); + ide_bus_new(&d->bus[i], &d->dev.qdev, i, 2); ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, port_info[i].iobase2); ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); From 3bac85d33716d9024afb8a262a39032bc0bba3ed Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 7 May 2013 15:23:41 +0200 Subject: [PATCH 02/10] de_DE.po: Add missing leading spaces Signed-off-by: Kevin Wolf --- po/de_DE.po | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/po/de_DE.po b/po/de_DE.po index 92c5df530..e35aaf487 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -18,11 +18,11 @@ msgstr "" #: ../ui/gtk.c:213 msgid " - Press Ctrl+Alt+G to release grab" -msgstr "- Strg+Alt+G drücken, um Eingabegeräte freizugeben" +msgstr " - Strg+Alt+G drücken, um Eingabegeräte freizugeben" #: ../ui/gtk.c:217 msgid " [Paused]" -msgstr "[Angehalten]" +msgstr " [Angehalten]" #: ../ui/gtk.c:1250 msgid "_Machine" From b39f96126549e2834152211a99919917423d2212 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Mar 2013 16:53:06 +0100 Subject: [PATCH 03/10] ide: Reset BMIDEA bit when the bus master is stopped 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 --- hw/ide/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index a31097539..635a364dd 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)) { From 9f0332b8cf27020d70be92f5e664466f0bcb84ec Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Mar 2013 17:00:40 +0100 Subject: [PATCH 04/10] libqos/pci: Enable bus mastering Signed-off-by: Kevin Wolf --- 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 95e287be6..7e0907b51 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); } From acbe48013bf1eb9c9a435e76959f409584b35509 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 8 May 2013 11:18:41 +0200 Subject: [PATCH 05/10] qtest: Add IDE test case 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 --- 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 bf41d1005..a307d5a95 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 000000000..45036e3bd --- /dev/null +++ b/tests/ide-test.c @@ -0,0 +1,165 @@ +/* + * IDE test cases + * + * Copyright (c) 2013 Kevin Wolf + * + * 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 +#include +#include + +#include + +#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; +} From b95739dcf5ae1dfa8cd5b2a5be27d427835e693b Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 8 May 2013 11:34:20 +0200 Subject: [PATCH 06/10] qtest/ide-test: Add simple DMA read/write test case This tests that single sectors can be successfully written and correctly read back. Signed-off-by: Kevin Wolf --- tests/ide-test.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/tests/ide-test.c b/tests/ide-test.c index 45036e3bd..5fc496fd0 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -29,8 +29,12 @@ #include #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 */ From 948eaed171e0dac541a59b42867f80881399bbd0 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 13 Mar 2013 13:30:24 +0100 Subject: [PATCH 07/10] qtest/ide-test: Test short and long PRDTs 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 --- tests/ide-test.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/ide-test.c b/tests/ide-test.c index 5fc496fd0..bdc1da7df 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(); From 1afe2726632a3344b92757bacdff57df49ef43e1 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 7 May 2013 19:13:03 +0800 Subject: [PATCH 08/10] qemu-iotests: exclude vmdk for test 042 Zero sized disk is not supported by qemu vmdk driver, exclude vmdk from the test script. As tested on vmware-vdiskmanager and vmware workstation, zero sized disk is not supported by vmware, either. Signed-off-by: Fam Zheng Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- tests/qemu-iotests/042 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/042 b/tests/qemu-iotests/042 index c3c3ca8d0..16b2fdbd5 100755 --- a/tests/qemu-iotests/042 +++ b/tests/qemu-iotests/042 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt qcow2 qcow qed vmdk +_supported_fmt qcow2 qcow qed _supported_proto file _supported_os Linux From dbcdd7cb04595cc0015f2dba6be751faa63fab00 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 7 May 2013 19:13:04 +0800 Subject: [PATCH 09/10] qemu-iotests: exclude vmdk and qcow from 043 043 tests recursive backing file by changing backing file. VMDK has not implemented this yet, and qcow1 probably never will. Signed-off-by: Fam Zheng Signed-off-by: Kevin Wolf --- tests/qemu-iotests/043 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043 index 3ba08dc94..478773d10 100755 --- a/tests/qemu-iotests/043 +++ b/tests/qemu-iotests/043 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter # Any format supporting backing files -_supported_fmt qcow qcow2 vmdk qed +_supported_fmt qcow2 qed _supported_proto generic _supported_os Linux From 794d00f71d39e3d18eafade579b2ecb665d6d481 Mon Sep 17 00:00:00 2001 From: Fam Zheng Date: Tue, 7 May 2013 19:13:05 +0800 Subject: [PATCH 10/10] qemu-iotests: fix 017 018 for vmdk 017 and 018 use /bin/mv to move base img from t.IMGFMG to t.IMGFMT.base after filling data, this is not enough for vmdk, when t.IMGFMT is only a description text file who points to t-{flat,s001,f001,...}.IMGFMT as data extent, so testing such subformats alway fails on them. This patch use the trick of temprorily changing TEST_IMG to avoid using /bin/mv. Signed-off-by: Fam Zheng Reviewed-by: Jeff Cody Signed-off-by: Kevin Wolf --- tests/qemu-iotests/017 | 4 +++- tests/qemu-iotests/017.out | 2 +- tests/qemu-iotests/018 | 4 +++- tests/qemu-iotests/018.out | 2 +- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017 index 66951eb1a..45f2c0b05 100755 --- a/tests/qemu-iotests/017 +++ b/tests/qemu-iotests/017 @@ -46,6 +46,8 @@ _supported_os Linux TEST_OFFSETS="0 4294967296" +TEST_IMG_SAVE=$TEST_IMG +TEST_IMG=$TEST_IMG.base _make_test_img 6G echo "Filling base image" @@ -63,7 +65,7 @@ _check_test_img echo "Creating test image with backing file" echo -mv $TEST_IMG $TEST_IMG.base +TEST_IMG=$TEST_IMG_SAVE _make_test_img -b $TEST_IMG.base 6G echo "Filling test image" diff --git a/tests/qemu-iotests/017.out b/tests/qemu-iotests/017.out index a861e5833..df34ee78c 100644 --- a/tests/qemu-iotests/017.out +++ b/tests/qemu-iotests/017.out @@ -1,5 +1,5 @@ QA output created by 017 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 0 diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018 index bd2db7871..453ce61e7 100755 --- a/tests/qemu-iotests/018 +++ b/tests/qemu-iotests/018 @@ -46,6 +46,8 @@ _supported_os Linux TEST_OFFSETS="0 4294967296" +TEST_IMG_SAVE=$TEST_IMG +TEST_IMG=$TEST_IMG.base _make_test_img 6G echo "Filling base image" @@ -63,7 +65,7 @@ _check_test_img echo "Creating test image with backing file" echo -mv $TEST_IMG $TEST_IMG.base +TEST_IMG=$TEST_IMG_SAVE _make_test_img -b $TEST_IMG.base 6G echo "Filling test image" diff --git a/tests/qemu-iotests/018.out b/tests/qemu-iotests/018.out index 6bbd815ce..3ddb8d8f6 100644 --- a/tests/qemu-iotests/018.out +++ b/tests/qemu-iotests/018.out @@ -1,5 +1,5 @@ QA output created by 018 -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=6442450944 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=6442450944 Filling base image === IO: pattern 0