tests/libqos: Move the libqos files under tests/qtest/
authorThomas Huth <thuth@redhat.com>
Tue, 10 Sep 2019 14:41:20 +0000 (16:41 +0200)
committerThomas Huth <thuth@redhat.com>
Sun, 12 Jan 2020 10:42:41 +0000 (11:42 +0100)
The qos stuff belongs to qtest, so move it into that directory, too.

Message-Id: <20191218103059.11729-8-thuth@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
144 files changed:
MAINTAINERS
configure
tests/Makefile.include
tests/libqos/aarch64-xlnx-zcu102-machine.c [deleted file]
tests/libqos/ahci.c [deleted file]
tests/libqos/ahci.h [deleted file]
tests/libqos/arm-imx25-pdk-machine.c [deleted file]
tests/libqos/arm-n800-machine.c [deleted file]
tests/libqos/arm-raspi2-machine.c [deleted file]
tests/libqos/arm-sabrelite-machine.c [deleted file]
tests/libqos/arm-smdkc210-machine.c [deleted file]
tests/libqos/arm-virt-machine.c [deleted file]
tests/libqos/arm-xilinx-zynq-a9-machine.c [deleted file]
tests/libqos/e1000e.c [deleted file]
tests/libqos/e1000e.h [deleted file]
tests/libqos/fw_cfg.c [deleted file]
tests/libqos/fw_cfg.h [deleted file]
tests/libqos/i2c-imx.c [deleted file]
tests/libqos/i2c-omap.c [deleted file]
tests/libqos/i2c.c [deleted file]
tests/libqos/i2c.h [deleted file]
tests/libqos/libqos-pc.c [deleted file]
tests/libqos/libqos-pc.h [deleted file]
tests/libqos/libqos-spapr.c [deleted file]
tests/libqos/libqos-spapr.h [deleted file]
tests/libqos/libqos.c [deleted file]
tests/libqos/libqos.h [deleted file]
tests/libqos/malloc-pc.c [deleted file]
tests/libqos/malloc-pc.h [deleted file]
tests/libqos/malloc-spapr.c [deleted file]
tests/libqos/malloc-spapr.h [deleted file]
tests/libqos/malloc.c [deleted file]
tests/libqos/malloc.h [deleted file]
tests/libqos/pci-pc.c [deleted file]
tests/libqos/pci-pc.h [deleted file]
tests/libqos/pci-spapr.c [deleted file]
tests/libqos/pci-spapr.h [deleted file]
tests/libqos/pci.c [deleted file]
tests/libqos/pci.h [deleted file]
tests/libqos/ppc64_pseries-machine.c [deleted file]
tests/libqos/qgraph.c [deleted file]
tests/libqos/qgraph.h [deleted file]
tests/libqos/qgraph_internal.h [deleted file]
tests/libqos/rtas.c [deleted file]
tests/libqos/rtas.h [deleted file]
tests/libqos/sdhci.c [deleted file]
tests/libqos/sdhci.h [deleted file]
tests/libqos/tpci200.c [deleted file]
tests/libqos/usb.c [deleted file]
tests/libqos/usb.h [deleted file]
tests/libqos/virtio-9p.c [deleted file]
tests/libqos/virtio-9p.h [deleted file]
tests/libqos/virtio-balloon.c [deleted file]
tests/libqos/virtio-balloon.h [deleted file]
tests/libqos/virtio-blk.c [deleted file]
tests/libqos/virtio-blk.h [deleted file]
tests/libqos/virtio-mmio.c [deleted file]
tests/libqos/virtio-mmio.h [deleted file]
tests/libqos/virtio-net.c [deleted file]
tests/libqos/virtio-net.h [deleted file]
tests/libqos/virtio-pci-modern.c [deleted file]
tests/libqos/virtio-pci-modern.h [deleted file]
tests/libqos/virtio-pci.c [deleted file]
tests/libqos/virtio-pci.h [deleted file]
tests/libqos/virtio-rng.c [deleted file]
tests/libqos/virtio-rng.h [deleted file]
tests/libqos/virtio-scsi.c [deleted file]
tests/libqos/virtio-scsi.h [deleted file]
tests/libqos/virtio-serial.c [deleted file]
tests/libqos/virtio-serial.h [deleted file]
tests/libqos/virtio.c [deleted file]
tests/libqos/virtio.h [deleted file]
tests/libqos/x86_64_pc-machine.c [deleted file]
tests/qtest/Makefile.include
tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c [new file with mode: 0644]
tests/qtest/libqos/ahci.c [new file with mode: 0644]
tests/qtest/libqos/ahci.h [new file with mode: 0644]
tests/qtest/libqos/arm-imx25-pdk-machine.c [new file with mode: 0644]
tests/qtest/libqos/arm-n800-machine.c [new file with mode: 0644]
tests/qtest/libqos/arm-raspi2-machine.c [new file with mode: 0644]
tests/qtest/libqos/arm-sabrelite-machine.c [new file with mode: 0644]
tests/qtest/libqos/arm-smdkc210-machine.c [new file with mode: 0644]
tests/qtest/libqos/arm-virt-machine.c [new file with mode: 0644]
tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c [new file with mode: 0644]
tests/qtest/libqos/e1000e.c [new file with mode: 0644]
tests/qtest/libqos/e1000e.h [new file with mode: 0644]
tests/qtest/libqos/fw_cfg.c [new file with mode: 0644]
tests/qtest/libqos/fw_cfg.h [new file with mode: 0644]
tests/qtest/libqos/i2c-imx.c [new file with mode: 0644]
tests/qtest/libqos/i2c-omap.c [new file with mode: 0644]
tests/qtest/libqos/i2c.c [new file with mode: 0644]
tests/qtest/libqos/i2c.h [new file with mode: 0644]
tests/qtest/libqos/libqos-pc.c [new file with mode: 0644]
tests/qtest/libqos/libqos-pc.h [new file with mode: 0644]
tests/qtest/libqos/libqos-spapr.c [new file with mode: 0644]
tests/qtest/libqos/libqos-spapr.h [new file with mode: 0644]
tests/qtest/libqos/libqos.c [new file with mode: 0644]
tests/qtest/libqos/libqos.h [new file with mode: 0644]
tests/qtest/libqos/malloc-pc.c [new file with mode: 0644]
tests/qtest/libqos/malloc-pc.h [new file with mode: 0644]
tests/qtest/libqos/malloc-spapr.c [new file with mode: 0644]
tests/qtest/libqos/malloc-spapr.h [new file with mode: 0644]
tests/qtest/libqos/malloc.c [new file with mode: 0644]
tests/qtest/libqos/malloc.h [new file with mode: 0644]
tests/qtest/libqos/pci-pc.c [new file with mode: 0644]
tests/qtest/libqos/pci-pc.h [new file with mode: 0644]
tests/qtest/libqos/pci-spapr.c [new file with mode: 0644]
tests/qtest/libqos/pci-spapr.h [new file with mode: 0644]
tests/qtest/libqos/pci.c [new file with mode: 0644]
tests/qtest/libqos/pci.h [new file with mode: 0644]
tests/qtest/libqos/ppc64_pseries-machine.c [new file with mode: 0644]
tests/qtest/libqos/qgraph.c [new file with mode: 0644]
tests/qtest/libqos/qgraph.h [new file with mode: 0644]
tests/qtest/libqos/qgraph_internal.h [new file with mode: 0644]
tests/qtest/libqos/rtas.c [new file with mode: 0644]
tests/qtest/libqos/rtas.h [new file with mode: 0644]
tests/qtest/libqos/sdhci.c [new file with mode: 0644]
tests/qtest/libqos/sdhci.h [new file with mode: 0644]
tests/qtest/libqos/tpci200.c [new file with mode: 0644]
tests/qtest/libqos/usb.c [new file with mode: 0644]
tests/qtest/libqos/usb.h [new file with mode: 0644]
tests/qtest/libqos/virtio-9p.c [new file with mode: 0644]
tests/qtest/libqos/virtio-9p.h [new file with mode: 0644]
tests/qtest/libqos/virtio-balloon.c [new file with mode: 0644]
tests/qtest/libqos/virtio-balloon.h [new file with mode: 0644]
tests/qtest/libqos/virtio-blk.c [new file with mode: 0644]
tests/qtest/libqos/virtio-blk.h [new file with mode: 0644]
tests/qtest/libqos/virtio-mmio.c [new file with mode: 0644]
tests/qtest/libqos/virtio-mmio.h [new file with mode: 0644]
tests/qtest/libqos/virtio-net.c [new file with mode: 0644]
tests/qtest/libqos/virtio-net.h [new file with mode: 0644]
tests/qtest/libqos/virtio-pci-modern.c [new file with mode: 0644]
tests/qtest/libqos/virtio-pci-modern.h [new file with mode: 0644]
tests/qtest/libqos/virtio-pci.c [new file with mode: 0644]
tests/qtest/libqos/virtio-pci.h [new file with mode: 0644]
tests/qtest/libqos/virtio-rng.c [new file with mode: 0644]
tests/qtest/libqos/virtio-rng.h [new file with mode: 0644]
tests/qtest/libqos/virtio-scsi.c [new file with mode: 0644]
tests/qtest/libqos/virtio-scsi.h [new file with mode: 0644]
tests/qtest/libqos/virtio-serial.c [new file with mode: 0644]
tests/qtest/libqos/virtio-serial.h [new file with mode: 0644]
tests/qtest/libqos/virtio.c [new file with mode: 0644]
tests/qtest/libqos/virtio.h [new file with mode: 0644]
tests/qtest/libqos/x86_64_pc-machine.c [new file with mode: 0644]

index 02eab66b02c062c005355c67e0403dba43f9a566..ceb5c45c0ff360ca06d06666f72a6eeb5803813e 100644 (file)
@@ -1102,9 +1102,9 @@ F: pc-bios/slof.bin
 F: docs/specs/ppc-spapr-hcalls.txt
 F: docs/specs/ppc-spapr-hotplug.txt
 F: tests/qtest/spapr*
-F: tests/libqos/*spapr*
+F: tests/qtest/libqos/*spapr*
 F: tests/qtest/rtas*
-F: tests/libqos/rtas*
+F: tests/qtest/libqos/rtas*
 
 PowerNV (Non-Virtualized)
 M: CĂ©dric Le Goater <clg@kaod.org>
@@ -1363,7 +1363,7 @@ F: hw/block/hd-geometry.c
 F: tests/qtest/ide-test.c
 F: tests/qtest/ahci-test.c
 F: tests/qtest/cdrom-test.c
-F: tests/libqos/ahci*
+F: tests/qtest/libqos/ahci*
 T: git https://github.com/jnsnow/qemu.git ide
 
 IPMI
@@ -1776,7 +1776,7 @@ F: hw/nvram/fw_cfg.c
 F: stubs/fw_cfg.c
 F: include/hw/nvram/fw_cfg.h
 F: include/standard-headers/linux/qemu_fw_cfg.h
-F: tests/libqos/fw_cfg.c
+F: tests/qtest/libqos/fw_cfg.c
 F: tests/qtest/fw_cfg-test.c
 T: git https://github.com/philmd/qemu.git fw_cfg-next
 
@@ -2137,7 +2137,6 @@ R: Paolo Bonzini <pbonzini@redhat.com>
 S: Maintained
 F: qtest.c
 F: accel/qtest.c
-F: tests/libqos/
 F: tests/qtest/
 
 Register API
index 1b8796fc21dbe7cd27a86b2edd121952457ceb0d..08c3a1c1f08a9e4f13caad807ef63f14903c4183 100755 (executable)
--- a/configure
+++ b/configure
@@ -7963,8 +7963,8 @@ fi
 # so the build tree will be missing the link back to the new file, and
 # tests might fail. Prefer to keep the relevant files in their own
 # directory and symlink the directory instead.
-DIRS="tests tests/tcg tests/tcg/lm32 tests/libqos tests/qapi-schema tests/qtest"
-DIRS="$DIRS tests/qemu-iotests tests/vm tests/fp tests/qgraph"
+DIRS="tests tests/tcg tests/tcg/lm32 tests/qapi-schema tests/qtest/libqos"
+DIRS="$DIRS tests/qtest tests/qemu-iotests tests/vm tests/fp tests/qgraph"
 DIRS="$DIRS docs docs/interop fsdev scsi"
 DIRS="$DIRS pc-bios/optionrom pc-bios/s390-ccw"
 DIRS="$DIRS roms/seabios roms/vgabios"
index 065fd09964307c2d670df603cdc6e5c64e1b8224..1ae14a8b158276a67d00d78c65a650acfd8fc731 100644 (file)
@@ -912,6 +912,6 @@ all: $(QEMU_IOTESTS_HELPERS-y)
 
 -include $(wildcard tests/*.d)
 -include $(wildcard tests/qtest/*.d)
--include $(wildcard tests/libqos/*.d)
+-include $(wildcard tests/qtest/qos/*.d)
 
 endif
diff --git a/tests/libqos/aarch64-xlnx-zcu102-machine.c b/tests/libqos/aarch64-xlnx-zcu102-machine.c
deleted file mode 100644 (file)
index 1d5de5a..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "sdhci.h"
-
-typedef struct QXlnxZCU102Machine QXlnxZCU102Machine;
-
-struct QXlnxZCU102Machine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    QSDHCI_MemoryMapped sdhci;
-};
-
-#define ARM_PAGE_SIZE          4096
-#define XLNX_ZCU102_RAM_ADDR   0
-#define XLNX_ZCU102_RAM_SIZE   0x20000000
-
-static void *xlnx_zcu102_get_driver(void *object, const char *interface)
-{
-    QXlnxZCU102Machine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *xlnx_zcu102_get_device(void *obj, const char *device)
-{
-    QXlnxZCU102Machine *machine = obj;
-    if (!g_strcmp0(device, "generic-sdhci")) {
-        return &machine->sdhci.obj;
-    }
-
-    fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", device);
-    g_assert_not_reached();
-}
-
-static void xlnx_zcu102_destructor(QOSGraphObject *obj)
-{
-    QXlnxZCU102Machine *machine = (QXlnxZCU102Machine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_aarch64_xlnx_zcu102(QTestState *qts)
-{
-    QXlnxZCU102Machine *machine = g_new0(QXlnxZCU102Machine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               XLNX_ZCU102_RAM_ADDR + (1 << 20),
-               XLNX_ZCU102_RAM_ADDR + XLNX_ZCU102_RAM_SIZE,
-               ARM_PAGE_SIZE);
-
-    machine->obj.get_device = xlnx_zcu102_get_device;
-    machine->obj.get_driver = xlnx_zcu102_get_driver;
-    machine->obj.destructor = xlnx_zcu102_destructor;
-    /* Datasheet: UG1085 (v1.7) */
-    qos_init_sdhci_mm(&machine->sdhci, qts, 0xff160000, &(QSDHCIProperties) {
-        .version = 3,
-        .baseclock = 0,
-        .capab.sdma = true,
-        .capab.reg = 0x280737ec6481
-    });
-    return &machine->obj;
-}
-
-static void xlnx_zcu102_register_nodes(void)
-{
-    qos_node_create_machine("aarch64/xlnx-zcu102",
-                            qos_create_machine_aarch64_xlnx_zcu102);
-    qos_node_contains("aarch64/xlnx-zcu102", "generic-sdhci", NULL);
-}
-
-libqos_init(xlnx_zcu102_register_nodes);
diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c
deleted file mode 100644 (file)
index cc1b08e..0000000
+++ /dev/null
@@ -1,1242 +0,0 @@
-/*
- * libqos AHCI functions
- *
- * Copyright (c) 2014 John Snow <jsnow@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 "qemu/osdep.h"
-
-#include "libqtest.h"
-#include "libqos/ahci.h"
-#include "libqos/pci-pc.h"
-
-#include "qemu-common.h"
-#include "qemu/host-utils.h"
-
-#include "hw/pci/pci_ids.h"
-#include "hw/pci/pci_regs.h"
-
-typedef struct AHCICommandProp {
-    uint8_t  cmd;        /* Command Code */
-    bool     data;       /* Data transfer command? */
-    bool     pio;
-    bool     dma;
-    bool     lba28;
-    bool     lba48;
-    bool     read;
-    bool     write;
-    bool     atapi;
-    bool     ncq;
-    uint64_t size;       /* Static transfer size, for commands like IDENTIFY. */
-    uint32_t interrupts; /* Expected interrupts for this command. */
-} AHCICommandProp;
-
-AHCICommandProp ahci_command_properties[] = {
-    { .cmd = CMD_READ_PIO,       .data = true,  .pio = true,
-                                 .lba28 = true, .read = true },
-    { .cmd = CMD_WRITE_PIO,      .data = true,  .pio = true,
-                                 .lba28 = true, .write = true },
-    { .cmd = CMD_READ_PIO_EXT,   .data = true,  .pio = true,
-                                 .lba48 = true, .read = true },
-    { .cmd = CMD_WRITE_PIO_EXT,  .data = true,  .pio = true,
-                                 .lba48 = true, .write = true },
-    { .cmd = CMD_READ_DMA,       .data = true,  .dma = true,
-                                 .lba28 = true, .read = true },
-    { .cmd = CMD_WRITE_DMA,      .data = true,  .dma = true,
-                                 .lba28 = true, .write = true },
-    { .cmd = CMD_READ_DMA_EXT,   .data = true,  .dma = true,
-                                 .lba48 = true, .read = true },
-    { .cmd = CMD_WRITE_DMA_EXT,  .data = true,  .dma = true,
-                                 .lba48 = true, .write = true },
-    { .cmd = CMD_IDENTIFY,       .data = true,  .pio = true,
-                                 .size = 512,   .read = true },
-    { .cmd = READ_FPDMA_QUEUED,  .data = true,  .dma = true,
-                                 .lba48 = true, .read = true, .ncq = true },
-    { .cmd = WRITE_FPDMA_QUEUED, .data = true,  .dma = true,
-                                 .lba48 = true, .write = true, .ncq = true },
-    { .cmd = CMD_READ_MAX,       .lba28 = true },
-    { .cmd = CMD_READ_MAX_EXT,   .lba48 = true },
-    { .cmd = CMD_FLUSH_CACHE,    .data = false },
-    { .cmd = CMD_PACKET,         .data = true,  .size = 16,
-                                 .atapi = true, .pio = true },
-    { .cmd = CMD_PACKET_ID,      .data = true,  .pio = true,
-                                 .size = 512,   .read = true }
-};
-
-struct AHCICommand {
-    /* Test Management Data */
-    uint8_t name;
-    uint8_t port;
-    uint8_t slot;
-    uint8_t errors;
-    uint32_t interrupts;
-    uint64_t xbytes;
-    uint32_t prd_size;
-    uint32_t sector_size;
-    uint64_t buffer;
-    AHCICommandProp *props;
-    /* Data to be transferred to the guest */
-    AHCICommandHeader header;
-    RegH2DFIS fis;
-    unsigned char *atapi_cmd;
-};
-
-/**
- * Allocate space in the guest using information in the AHCIQState object.
- */
-uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes)
-{
-    g_assert(ahci);
-    g_assert(ahci->parent);
-    return qmalloc(ahci->parent, bytes);
-}
-
-void ahci_free(AHCIQState *ahci, uint64_t addr)
-{
-    g_assert(ahci);
-    g_assert(ahci->parent);
-    qfree(ahci->parent, addr);
-}
-
-bool is_atapi(AHCIQState *ahci, uint8_t port)
-{
-    return ahci_px_rreg(ahci, port, AHCI_PX_SIG) == AHCI_SIGNATURE_CDROM;
-}
-
-/**
- * Locate, verify, and return a handle to the AHCI device.
- */
-QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint)
-{
-    QPCIDevice *ahci;
-    uint32_t ahci_fingerprint;
-    QPCIBus *pcibus;
-
-    pcibus = qpci_new_pc(qts, NULL);
-
-    /* Find the AHCI PCI device and verify it's the right one. */
-    ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02));
-    g_assert(ahci != NULL);
-
-    ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID);
-
-    switch (ahci_fingerprint) {
-    case AHCI_INTEL_ICH9:
-        break;
-    default:
-        /* Unknown device. */
-        g_assert_not_reached();
-    }
-
-    if (fingerprint) {
-        *fingerprint = ahci_fingerprint;
-    }
-    return ahci;
-}
-
-void free_ahci_device(QPCIDevice *dev)
-{
-    QPCIBus *pcibus = dev ? dev->bus : NULL;
-
-    /* libqos doesn't have a function for this, so free it manually */
-    g_free(dev);
-    qpci_free_pc(pcibus);
-}
-
-/* Free all memory in-use by the AHCI device. */
-void ahci_clean_mem(AHCIQState *ahci)
-{
-    uint8_t port, slot;
-
-    for (port = 0; port < 32; ++port) {
-        if (ahci->port[port].fb) {
-            ahci_free(ahci, ahci->port[port].fb);
-            ahci->port[port].fb = 0;
-        }
-        if (ahci->port[port].clb) {
-            for (slot = 0; slot < 32; slot++) {
-                ahci_destroy_command(ahci, port, slot);
-            }
-            ahci_free(ahci, ahci->port[port].clb);
-            ahci->port[port].clb = 0;
-        }
-    }
-}
-
-/*** Logical Device Initialization ***/
-
-/**
- * Start the PCI device and sanity-check default operation.
- */
-void ahci_pci_enable(AHCIQState *ahci)
-{
-    uint8_t reg;
-
-    start_ahci_device(ahci);
-
-    switch (ahci->fingerprint) {
-    case AHCI_INTEL_ICH9:
-        /* ICH9 has a register at PCI 0x92 that
-         * acts as a master port enabler mask. */
-        reg = qpci_config_readb(ahci->dev, 0x92);
-        reg |= 0x3F;
-        qpci_config_writeb(ahci->dev, 0x92, reg);
-        /* 0...0111111b -- bit significant, ports 0-5 enabled. */
-        ASSERT_BIT_SET(qpci_config_readb(ahci->dev, 0x92), 0x3F);
-        break;
-    }
-
-}
-
-/**
- * Map BAR5/ABAR, and engage the PCI device.
- */
-void start_ahci_device(AHCIQState *ahci)
-{
-    /* Map AHCI's ABAR (BAR5) */
-    ahci->hba_bar = qpci_iomap(ahci->dev, 5, &ahci->barsize);
-
-    /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */
-    qpci_device_enable(ahci->dev);
-}
-
-/**
- * Test and initialize the AHCI's HBA memory areas.
- * Initialize and start any ports with devices attached.
- * Bring the HBA into the idle state.
- */
-void ahci_hba_enable(AHCIQState *ahci)
-{
-    /* Bits of interest in this section:
-     * GHC.AE     Global Host Control / AHCI Enable
-     * PxCMD.ST   Port Command: Start
-     * PxCMD.SUD  "Spin Up Device"
-     * PxCMD.POD  "Power On Device"
-     * PxCMD.FRE  "FIS Receive Enable"
-     * PxCMD.FR   "FIS Receive Running"
-     * PxCMD.CR   "Command List Running"
-     */
-    uint32_t reg, ports_impl;
-    uint16_t i;
-    uint8_t num_cmd_slots;
-
-    g_assert(ahci != NULL);
-
-    /* Set GHC.AE to 1 */
-    ahci_set(ahci, AHCI_GHC, AHCI_GHC_AE);
-    reg = ahci_rreg(ahci, AHCI_GHC);
-    ASSERT_BIT_SET(reg, AHCI_GHC_AE);
-
-    /* Cache CAP and CAP2. */
-    ahci->cap = ahci_rreg(ahci, AHCI_CAP);
-    ahci->cap2 = ahci_rreg(ahci, AHCI_CAP2);
-
-    /* Read CAP.NCS, how many command slots do we have? */
-    num_cmd_slots = ((ahci->cap & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1;
-    g_test_message("Number of Command Slots: %u", num_cmd_slots);
-
-    /* Determine which ports are implemented. */
-    ports_impl = ahci_rreg(ahci, AHCI_PI);
-
-    for (i = 0; ports_impl; ports_impl >>= 1, ++i) {
-        if (!(ports_impl & 0x01)) {
-            continue;
-        }
-
-        g_test_message("Initializing port %u", i);
-
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
-        if (BITCLR(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR |
-                   AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) {
-            g_test_message("port is idle");
-        } else {
-            g_test_message("port needs to be idled");
-            ahci_px_clr(ahci, i, AHCI_PX_CMD,
-                        (AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE));
-            /* The port has 500ms to disengage. */
-            usleep(500000);
-            reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
-            ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR);
-            ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR);
-            g_test_message("port is now idle");
-            /* The spec does allow for possibly needing a PORT RESET
-             * or HBA reset if we fail to idle the port. */
-        }
-
-        /* Allocate Memory for the Command List Buffer & FIS Buffer */
-        /* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */
-        ahci->port[i].clb = ahci_alloc(ahci, num_cmd_slots * 0x20);
-        qtest_memset(ahci->parent->qts, ahci->port[i].clb, 0x00,
-                     num_cmd_slots * 0x20);
-        g_test_message("CLB: 0x%08" PRIx64, ahci->port[i].clb);
-        ahci_px_wreg(ahci, i, AHCI_PX_CLB, ahci->port[i].clb);
-        g_assert_cmphex(ahci->port[i].clb, ==,
-                        ahci_px_rreg(ahci, i, AHCI_PX_CLB));
-
-        /* PxFB space ... 0x100, as in 4.2.1 p 35 */
-        ahci->port[i].fb = ahci_alloc(ahci, 0x100);
-        qtest_memset(ahci->parent->qts, ahci->port[i].fb, 0x00, 0x100);
-        g_test_message("FB: 0x%08" PRIx64, ahci->port[i].fb);
-        ahci_px_wreg(ahci, i, AHCI_PX_FB, ahci->port[i].fb);
-        g_assert_cmphex(ahci->port[i].fb, ==,
-                        ahci_px_rreg(ahci, i, AHCI_PX_FB));
-
-        /* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */
-        ahci_px_wreg(ahci, i, AHCI_PX_SERR, 0xFFFFFFFF);
-        ahci_px_wreg(ahci, i, AHCI_PX_IS, 0xFFFFFFFF);
-        ahci_wreg(ahci, AHCI_IS, (1 << i));
-
-        /* Verify Interrupts Cleared */
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR);
-        g_assert_cmphex(reg, ==, 0);
-
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_IS);
-        g_assert_cmphex(reg, ==, 0);
-
-        reg = ahci_rreg(ahci, AHCI_IS);
-        ASSERT_BIT_CLEAR(reg, (1 << i));
-
-        /* Enable All Interrupts: */
-        ahci_px_wreg(ahci, i, AHCI_PX_IE, 0xFFFFFFFF);
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_IE);
-        g_assert_cmphex(reg, ==, ~((uint32_t)AHCI_PX_IE_RESERVED));
-
-        /* Enable the FIS Receive Engine. */
-        ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_FRE);
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
-        ASSERT_BIT_SET(reg, AHCI_PX_CMD_FR);
-
-        /* AHCI 1.3 spec: if !STS.BSY, !STS.DRQ and PxSSTS.DET indicates
-         * physical presence, a device is present and may be started. However,
-         * PxSERR.DIAG.X /may/ need to be cleared a priori. */
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR);
-        if (BITSET(reg, AHCI_PX_SERR_DIAG_X)) {
-            ahci_px_set(ahci, i, AHCI_PX_SERR, AHCI_PX_SERR_DIAG_X);
-        }
-
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_TFD);
-        if (BITCLR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ)) {
-            reg = ahci_px_rreg(ahci, i, AHCI_PX_SSTS);
-            if ((reg & AHCI_PX_SSTS_DET) == SSTS_DET_ESTABLISHED) {
-                /* Device Found: set PxCMD.ST := 1 */
-                ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_ST);
-                ASSERT_BIT_SET(ahci_px_rreg(ahci, i, AHCI_PX_CMD),
-                               AHCI_PX_CMD_CR);
-                g_test_message("Started Device %u", i);
-            } else if ((reg & AHCI_PX_SSTS_DET)) {
-                /* Device present, but in some unknown state. */
-                g_assert_not_reached();
-            }
-        }
-    }
-
-    /* Enable GHC.IE */
-    ahci_set(ahci, AHCI_GHC, AHCI_GHC_IE);
-    reg = ahci_rreg(ahci, AHCI_GHC);
-    ASSERT_BIT_SET(reg, AHCI_GHC_IE);
-
-    ahci->enabled = true;
-    /* TODO: The device should now be idling and waiting for commands.
-     * In the future, a small test-case to inspect the Register D2H FIS
-     * and clear the initial interrupts might be good. */
-}
-
-/**
- * Pick the first implemented and running port
- */
-unsigned ahci_port_select(AHCIQState *ahci)
-{
-    uint32_t ports, reg;
-    unsigned i;
-
-    ports = ahci_rreg(ahci, AHCI_PI);
-    for (i = 0; i < 32; ports >>= 1, ++i) {
-        if (ports == 0) {
-            i = 32;
-        }
-
-        if (!(ports & 0x01)) {
-            continue;
-        }
-
-        reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
-        if (BITSET(reg, AHCI_PX_CMD_ST)) {
-            break;
-        }
-    }
-    g_assert(i < 32);
-    return i;
-}
-
-/**
- * Clear a port's interrupts and status information prior to a test.
- */
-void ahci_port_clear(AHCIQState *ahci, uint8_t port)
-{
-    uint32_t reg;
-
-    /* Clear out this port's interrupts (ignore the init register d2h fis) */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
-    ahci_px_wreg(ahci, port, AHCI_PX_IS, reg);
-    g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
-
-    /* Wipe the FIS-Receive Buffer */
-    qtest_memset(ahci->parent->qts, ahci->port[port].fb, 0x00, 0x100);
-}
-
-/**
- * Check a port for errors.
- */
-void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
-                           uint32_t imask, uint8_t emask)
-{
-    uint32_t reg;
-
-    /* The upper 9 bits of the IS register all indicate errors. */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
-    reg &= ~imask;
-    reg >>= 23;
-    g_assert_cmphex(reg, ==, 0);
-
-    /* The Sata Error Register should be empty. */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR);
-    g_assert_cmphex(reg, ==, 0);
-
-    /* The TFD also has two error sections. */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
-    if (!emask) {
-        ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
-    } else {
-        ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR);
-    }
-    ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~emask << 8));
-    ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (emask << 8));
-}
-
-void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
-                                uint32_t intr_mask)
-{
-    uint32_t reg;
-
-    /* Check for expected interrupts */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
-    ASSERT_BIT_SET(reg, intr_mask);
-
-    /* Clear expected interrupts and assert all interrupts now cleared. */
-    ahci_px_wreg(ahci, port, AHCI_PX_IS, intr_mask);
-    g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
-}
-
-void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot)
-{
-    uint32_t reg;
-
-    /* Assert that the command slot is no longer busy (NCQ) */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT);
-    ASSERT_BIT_CLEAR(reg, (1 << slot));
-
-    /* Non-NCQ */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
-    ASSERT_BIT_CLEAR(reg, (1 << slot));
-
-    /* And assert that we are generally not busy. */
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
-    ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY);
-    ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_DRQ);
-}
-
-void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot)
-{
-    RegD2HFIS *d2h = g_malloc0(0x20);
-    uint32_t reg;
-
-    qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x40, d2h, 0x20);
-    g_assert_cmphex(d2h->fis_type, ==, 0x34);
-
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
-    g_assert_cmphex((reg & AHCI_PX_TFD_ERR) >> 8, ==, d2h->error);
-    g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, d2h->status);
-
-    g_free(d2h);
-}
-
-void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd)
-{
-    PIOSetupFIS *pio = g_malloc0(0x20);
-    uint8_t port = cmd->port;
-
-    /* We cannot check the Status or E_Status registers, because
-     * the status may have again changed between the PIO Setup FIS
-     * and the conclusion of the command with the D2H Register FIS. */
-    qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20);
-    g_assert_cmphex(pio->fis_type, ==, 0x5f);
-
-    /* Data transferred by PIO will either be:
-     * (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or
-     * (2) Actual data from the drive.
-     * If we do both, (2) winds up erasing any evidence of (1).
-     */
-    if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) {
-        g_assert(le16_to_cpu(pio->tx_count) == 12 ||
-                 le16_to_cpu(pio->tx_count) == 16);
-    } else {
-        /* The AHCI test suite here does not test any PIO command that specifies
-         * a DRQ block larger than one sector (like 0xC4), so this should always
-         * be one sector or less. */
-        size_t pio_len = ((cmd->xbytes % cmd->sector_size) ?
-                          (cmd->xbytes % cmd->sector_size) : cmd->sector_size);
-        g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, pio_len);
-    }
-    g_free(pio);
-}
-
-void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd)
-{
-    AHCICommandHeader cmdh;
-
-    ahci_get_command_header(ahci, cmd->port, cmd->slot, &cmdh);
-    /* Physical Region Descriptor Byte Count is not required to work for NCQ. */
-    if (!cmd->props->ncq) {
-        g_assert_cmphex(cmd->xbytes, ==, cmdh.prdbc);
-    }
-}
-
-/* Get the command in #slot of port #port. */
-void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
-                             uint8_t slot, AHCICommandHeader *cmd)
-{
-    uint64_t ba = ahci->port[port].clb;
-    ba += slot * sizeof(AHCICommandHeader);
-    qtest_memread(ahci->parent->qts, ba, cmd, sizeof(AHCICommandHeader));
-
-    cmd->flags = le16_to_cpu(cmd->flags);
-    cmd->prdtl = le16_to_cpu(cmd->prdtl);
-    cmd->prdbc = le32_to_cpu(cmd->prdbc);
-    cmd->ctba = le64_to_cpu(cmd->ctba);
-}
-
-/* Set the command in #slot of port #port. */
-void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
-                             uint8_t slot, AHCICommandHeader *cmd)
-{
-    AHCICommandHeader tmp = { .flags = 0 };
-    uint64_t ba = ahci->port[port].clb;
-    ba += slot * sizeof(AHCICommandHeader);
-
-    tmp.flags = cpu_to_le16(cmd->flags);
-    tmp.prdtl = cpu_to_le16(cmd->prdtl);
-    tmp.prdbc = cpu_to_le32(cmd->prdbc);
-    tmp.ctba = cpu_to_le64(cmd->ctba);
-
-    qtest_memwrite(ahci->parent->qts, ba, &tmp, sizeof(AHCICommandHeader));
-}
-
-void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot)
-{
-    AHCICommandHeader cmd;
-
-    /* Obtain the Nth Command Header */
-    ahci_get_command_header(ahci, port, slot, &cmd);
-    if (cmd.ctba == 0) {
-        /* No address in it, so just return -- it's empty. */
-        goto tidy;
-    }
-
-    /* Free the Table */
-    ahci_free(ahci, cmd.ctba);
-
- tidy:
-    /* NULL the header. */
-    memset(&cmd, 0x00, sizeof(cmd));
-    ahci_set_command_header(ahci, port, slot, &cmd);
-    ahci->port[port].ctba[slot] = 0;
-    ahci->port[port].prdtl[slot] = 0;
-}
-
-void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd)
-{
-    RegH2DFIS tmp = cmd->fis;
-    uint64_t addr = cmd->header.ctba;
-
-    /* NCQ commands use exclusively 8 bit fields and needs no adjustment.
-     * Only the count field needs to be adjusted for non-NCQ commands.
-     * The auxiliary FIS fields are defined per-command and are not currently
-     * implemented in libqos/ahci.o, but may or may not need to be flipped. */
-    if (!cmd->props->ncq) {
-        tmp.count = cpu_to_le16(tmp.count);
-    }
-
-    qtest_memwrite(ahci->parent->qts, addr, &tmp, sizeof(tmp));
-}
-
-unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port)
-{
-    unsigned i;
-    unsigned j;
-    uint32_t reg;
-
-    reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
-
-    /* Pick the least recently used command slot that's available */
-    for (i = 0; i < 32; ++i) {
-        j = ((ahci->port[port].next + i) % 32);
-        if (reg & (1 << j)) {
-            continue;
-        }
-        ahci_destroy_command(ahci, port, j);
-        ahci->port[port].next = (j + 1) % 32;
-        return j;
-    }
-
-    g_test_message("All command slots were busy.");
-    g_assert_not_reached();
-}
-
-inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd)
-{
-    /* Each PRD can describe up to 4MiB */
-    g_assert_cmphex(bytes_per_prd, <=, 4096 * 1024);
-    g_assert_cmphex(bytes_per_prd & 0x01, ==, 0x00);
-    return (bytes + bytes_per_prd - 1) / bytes_per_prd;
-}
-
-const AHCIOpts default_opts = { .size = 0 };
-
-/**
- * ahci_exec: execute a given command on a specific
- * AHCI port.
- *
- * @ahci: The device to send the command to
- * @port: The port number of the SATA device we wish
- *        to have execute this command
- * @op:   The S/ATA command to execute, or if opts.atapi
- *        is true, the SCSI command code.
- * @opts: Optional arguments to modify execution behavior.
- */
-void ahci_exec(AHCIQState *ahci, uint8_t port,
-               uint8_t op, const AHCIOpts *opts_in)
-{
-    AHCICommand *cmd;
-    int rc;
-    AHCIOpts *opts;
-
-    opts = g_memdup((opts_in == NULL ? &default_opts : opts_in),
-                    sizeof(AHCIOpts));
-
-    /* No guest buffer provided, create one. */
-    if (opts->size && !opts->buffer) {
-        opts->buffer = ahci_alloc(ahci, opts->size);
-        g_assert(opts->buffer);
-        qtest_memset(ahci->parent->qts, opts->buffer, 0x00, opts->size);
-    }
-
-    /* Command creation */
-    if (opts->atapi) {
-        uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE;
-        cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma);
-    } else {
-        cmd = ahci_command_create(op);
-    }
-    ahci_command_adjust(cmd, opts->lba, opts->buffer,
-                        opts->size, opts->prd_size);
-
-    if (opts->pre_cb) {
-        rc = opts->pre_cb(ahci, cmd, opts);
-        g_assert_cmpint(rc, ==, 0);
-    }
-
-    /* Write command to memory and issue it */
-    ahci_command_commit(ahci, cmd, port);
-    ahci_command_issue_async(ahci, cmd);
-    if (opts->error) {
-        qtest_qmp_eventwait(ahci->parent->qts, "STOP");
-    }
-    if (opts->mid_cb) {
-        rc = opts->mid_cb(ahci, cmd, opts);
-        g_assert_cmpint(rc, ==, 0);
-    }
-    if (opts->error) {
-        qtest_qmp_send(ahci->parent->qts, "{'execute':'cont' }");
-        qtest_qmp_eventwait(ahci->parent->qts, "RESUME");
-    }
-
-    /* Wait for command to complete and verify sanity */
-    ahci_command_wait(ahci, cmd);
-    ahci_command_verify(ahci, cmd);
-    if (opts->post_cb) {
-        rc = opts->post_cb(ahci, cmd, opts);
-        g_assert_cmpint(rc, ==, 0);
-    }
-    ahci_command_free(cmd);
-    if (opts->buffer != opts_in->buffer) {
-        ahci_free(ahci, opts->buffer);
-    }
-    g_free(opts);
-}
-
-/* Issue a command, expecting it to fail and STOP the VM */
-AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port,
-                                uint8_t ide_cmd, uint64_t buffer,
-                                size_t bufsize, uint64_t sector)
-{
-    AHCICommand *cmd;
-
-    cmd = ahci_command_create(ide_cmd);
-    ahci_command_adjust(cmd, sector, buffer, bufsize, 0);
-    ahci_command_commit(ahci, cmd, port);
-    ahci_command_issue_async(ahci, cmd);
-    qtest_qmp_eventwait(ahci->parent->qts, "STOP");
-
-    return cmd;
-}
-
-/* Resume a previously failed command and verify/finalize */
-void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd)
-{
-    /* Complete the command */
-    qtest_qmp_send(ahci->parent->qts, "{'execute':'cont' }");
-    qtest_qmp_eventwait(ahci->parent->qts, "RESUME");
-    ahci_command_wait(ahci, cmd);
-    ahci_command_verify(ahci, cmd);
-    ahci_command_free(cmd);
-}
-
-/* Given a guest buffer address, perform an IO operation */
-void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
-                   uint64_t buffer, size_t bufsize, uint64_t sector)
-{
-    AHCICommand *cmd;
-    cmd = ahci_command_create(ide_cmd);
-    ahci_command_set_buffer(cmd, buffer);
-    ahci_command_set_size(cmd, bufsize);
-    if (sector) {
-        ahci_command_set_offset(cmd, sector);
-    }
-    ahci_command_commit(ahci, cmd, port);
-    ahci_command_issue(ahci, cmd);
-    ahci_command_verify(ahci, cmd);
-    ahci_command_free(cmd);
-}
-
-static AHCICommandProp *ahci_command_find(uint8_t command_name)
-{
-    int i;
-
-    for (i = 0; i < ARRAY_SIZE(ahci_command_properties); i++) {
-        if (ahci_command_properties[i].cmd == command_name) {
-            return &ahci_command_properties[i];
-        }
-    }
-
-    return NULL;
-}
-
-/* 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 *buffer, size_t bufsize, uint64_t sector)
-{
-    uint64_t ptr;
-    AHCICommandProp *props;
-
-    props = ahci_command_find(ide_cmd);
-    g_assert(props);
-    ptr = ahci_alloc(ahci, bufsize);
-    g_assert(!bufsize || ptr);
-    qtest_memset(ahci->parent->qts, ptr, 0x00, bufsize);
-
-    if (bufsize && props->write) {
-        qtest_bufwrite(ahci->parent->qts, ptr, buffer, bufsize);
-    }
-
-    ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector);
-
-    if (bufsize && props->read) {
-        qtest_bufread(ahci->parent->qts, ptr, buffer, bufsize);
-    }
-
-    ahci_free(ahci, ptr);
-}
-
-/**
- * Initializes a basic command header in memory.
- * We assume that this is for an ATA command using RegH2DFIS.
- */
-static void command_header_init(AHCICommand *cmd)
-{
-    AHCICommandHeader *hdr = &cmd->header;
-    AHCICommandProp *props = cmd->props;
-
-    hdr->flags = 5;             /* RegH2DFIS is 5 DW long. Must be < 32 */
-    hdr->flags |= CMDH_CLR_BSY; /* Clear the BSY bit when done */
-    if (props->write) {
-        hdr->flags |= CMDH_WRITE;
-    }
-    if (props->atapi) {
-        hdr->flags |= CMDH_ATAPI;
-    }
-    /* Other flags: PREFETCH, RESET, and BIST */
-    hdr->prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
-    hdr->prdbc = 0;
-    hdr->ctba = 0;
-}
-
-static void command_table_init(AHCICommand *cmd)
-{
-    RegH2DFIS *fis = &(cmd->fis);
-    uint16_t sect_count = (cmd->xbytes / cmd->sector_size);
-
-    fis->fis_type = REG_H2D_FIS;
-    fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */
-    fis->command = cmd->name;
-
-    if (cmd->props->ncq) {
-        NCQFIS *ncqfis = (NCQFIS *)fis;
-        /* NCQ is weird and re-uses FIS frames for unrelated data.
-         * See SATA 3.2, 13.6.4.1 READ FPDMA QUEUED for an example. */
-        ncqfis->sector_low = sect_count & 0xFF;
-        ncqfis->sector_hi = (sect_count >> 8) & 0xFF;
-        ncqfis->device = NCQ_DEVICE_MAGIC;
-        /* Force Unit Access is bit 7 in the device register */
-        ncqfis->tag = 0;  /* bits 3-7 are the NCQ tag */
-        ncqfis->prio = 0; /* bits 6,7 are a prio tag */
-        /* RARC bit is bit 0 of TAG field */
-    } else {
-        fis->feature_low = 0x00;
-        fis->feature_high = 0x00;
-        if (cmd->props->lba28 || cmd->props->lba48) {
-            fis->device = ATA_DEVICE_LBA;
-        }
-        fis->count = (cmd->xbytes / cmd->sector_size);
-    }
-    fis->icc = 0x00;
-    fis->control = 0x00;
-    memset(fis->aux, 0x00, ARRAY_SIZE(fis->aux));
-}
-
-void ahci_command_enable_atapi_dma(AHCICommand *cmd)
-{
-    RegH2DFIS *fis = &(cmd->fis);
-    g_assert(cmd->props->atapi);
-    fis->feature_low |= 0x01;
-    /* PIO is still used to transfer the ATAPI command */
-    g_assert(cmd->props->pio);
-    cmd->props->dma = true;
-    /* BUG: We expect the DMA Setup interrupt for DMA commands */
-    /* cmd->interrupts |= AHCI_PX_IS_DSS; */
-}
-
-AHCICommand *ahci_command_create(uint8_t command_name)
-{
-    AHCICommandProp *props = ahci_command_find(command_name);
-    AHCICommand *cmd;
-
-    g_assert(props);
-    cmd = g_new0(AHCICommand, 1);
-    g_assert(!(props->dma && props->pio) || props->atapi);
-    g_assert(!(props->lba28 && props->lba48));
-    g_assert(!(props->read && props->write));
-    g_assert(!props->size || props->data);
-    g_assert(!props->ncq || props->lba48);
-
-    /* Defaults and book-keeping */
-    cmd->props = g_memdup(props, sizeof(AHCICommandProp));
-    cmd->name = command_name;
-    cmd->xbytes = props->size;
-    cmd->prd_size = 4096;
-    cmd->buffer = 0xabad1dea;
-    cmd->sector_size = props->atapi ? ATAPI_SECTOR_SIZE : AHCI_SECTOR_SIZE;
-
-    if (!cmd->props->ncq) {
-        cmd->interrupts = AHCI_PX_IS_DHRS;
-    }
-    /* BUG: We expect the DPS interrupt for data commands */
-    /* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */
-    /* BUG: We expect the DMA Setup interrupt for DMA commands */
-    /* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */
-    cmd->interrupts |= props->ncq ? AHCI_PX_IS_SDBS : 0;
-
-    command_header_init(cmd);
-    command_table_init(cmd);
-
-    return cmd;
-}
-
-AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma)
-{
-    AHCICommand *cmd = ahci_command_create(CMD_PACKET);
-    cmd->atapi_cmd = g_malloc0(16);
-    cmd->atapi_cmd[0] = scsi_cmd;
-    stw_le_p(&cmd->fis.lba_lo[1], bcl);
-    if (dma) {
-        ahci_command_enable_atapi_dma(cmd);
-    } else {
-        cmd->interrupts |= bcl ? AHCI_PX_IS_PSS : 0;
-    }
-    return cmd;
-}
-
-void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port,
-                           bool ready, uint8_t expected_sense)
-{
-    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0, false);
-    ahci_command_set_size(cmd, 0);
-    if (!ready) {
-        cmd->interrupts |= AHCI_PX_IS_TFES;
-        cmd->errors |= expected_sense << 4;
-    }
-    ahci_command_commit(ahci, cmd, port);
-    ahci_command_issue(ahci, cmd);
-    ahci_command_verify(ahci, cmd);
-    ahci_command_free(cmd);
-}
-
-static int copy_buffer(AHCIQState *ahci, AHCICommand *cmd,
-                        const AHCIOpts *opts)
-{
-    unsigned char *rx = opts->opaque;
-    qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size);
-    return 0;
-}
-
-void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
-                          uint8_t *sense, uint8_t *asc)
-{
-    unsigned char *rx;
-    AHCIOpts opts = {
-        .size = 18,
-        .atapi = true,
-        .post_cb = copy_buffer,
-    };
-    rx = g_malloc(18);
-    opts.opaque = rx;
-
-    ahci_exec(ahci, port, CMD_ATAPI_REQUEST_SENSE, &opts);
-
-    *sense = rx[2];
-    *asc = rx[12];
-
-    g_free(rx);
-}
-
-void ahci_atapi_eject(AHCIQState *ahci, uint8_t port)
-{
-    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false);
-    ahci_command_set_size(cmd, 0);
-
-    cmd->atapi_cmd[4] = 0x02; /* loej = true */
-    ahci_command_commit(ahci, cmd, port);
-    ahci_command_issue(ahci, cmd);
-    ahci_command_verify(ahci, cmd);
-    ahci_command_free(cmd);
-}
-
-void ahci_atapi_load(AHCIQState *ahci, uint8_t port)
-{
-    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false);
-    ahci_command_set_size(cmd, 0);
-
-    cmd->atapi_cmd[4] = 0x03; /* loej,start = true */
-    ahci_command_commit(ahci, cmd, port);
-    ahci_command_issue(ahci, cmd);
-    ahci_command_verify(ahci, cmd);
-    ahci_command_free(cmd);
-}
-
-void ahci_command_free(AHCICommand *cmd)
-{
-    g_free(cmd->atapi_cmd);
-    g_free(cmd->props);
-    g_free(cmd);
-}
-
-void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags)
-{
-    cmd->header.flags |= cmdh_flags;
-}
-
-void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags)
-{
-    cmd->header.flags &= ~cmdh_flags;
-}
-
-static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba)
-{
-    unsigned char *cbd = cmd->atapi_cmd;
-    g_assert(cbd);
-
-    switch (cbd[0]) {
-    case CMD_ATAPI_READ_10:
-    case CMD_ATAPI_READ_CD:
-        g_assert_cmpuint(lba, <=, UINT32_MAX);
-        stl_be_p(&cbd[2], lba);
-        break;
-    case CMD_ATAPI_REQUEST_SENSE:
-    case CMD_ATAPI_TEST_UNIT_READY:
-    case CMD_ATAPI_START_STOP_UNIT:
-        g_assert_cmpuint(lba, ==, 0x00);
-        break;
-    default:
-        /* SCSI doesn't have uniform packet formats,
-         * so you have to add support for it manually. Sorry! */
-        fprintf(stderr, "The Libqos AHCI driver does not support the "
-                "set_offset operation for ATAPI command 0x%02x, "
-                "please add support.\n",
-                cbd[0]);
-        g_assert_not_reached();
-    }
-}
-
-void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect)
-{
-    RegH2DFIS *fis = &(cmd->fis);
-
-    if (cmd->props->atapi) {
-        ahci_atapi_command_set_offset(cmd, lba_sect);
-        return;
-    } else if (!cmd->props->data && !lba_sect) {
-        /* Not meaningful, ignore. */
-        return;
-    } else if (cmd->props->lba28) {
-        g_assert_cmphex(lba_sect, <=, 0xFFFFFFF);
-    } else if (cmd->props->lba48 || cmd->props->ncq) {
-        g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF);
-    } else {
-        /* Can't set offset if we don't know the format. */
-        g_assert_not_reached();
-    }
-
-    /* LBA28 uses the low nibble of the device/control register for LBA24:27 */
-    fis->lba_lo[0] = (lba_sect & 0xFF);
-    fis->lba_lo[1] = (lba_sect >> 8) & 0xFF;
-    fis->lba_lo[2] = (lba_sect >> 16) & 0xFF;
-    if (cmd->props->lba28) {
-        fis->device = (fis->device & 0xF0) | ((lba_sect >> 24) & 0x0F);
-    }
-    fis->lba_hi[0] = (lba_sect >> 24) & 0xFF;
-    fis->lba_hi[1] = (lba_sect >> 32) & 0xFF;
-    fis->lba_hi[2] = (lba_sect >> 40) & 0xFF;
-}
-
-void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
-{
-    cmd->buffer = buffer;
-}
-
-static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
-{
-    unsigned char *cbd = cmd->atapi_cmd;
-    uint64_t nsectors = xbytes / ATAPI_SECTOR_SIZE;
-    uint32_t tmp;
-    g_assert(cbd);
-
-    switch (cbd[0]) {
-    case CMD_ATAPI_READ_10:
-        g_assert_cmpuint(nsectors, <=, UINT16_MAX);
-        stw_be_p(&cbd[7], nsectors);
-        break;
-    case CMD_ATAPI_READ_CD:
-        /* 24bit BE store */
-        g_assert_cmpuint(nsectors, <, 1ULL << 24);
-        tmp = nsectors;
-        cbd[6] = (tmp & 0xFF0000) >> 16;
-        cbd[7] = (tmp & 0xFF00) >> 8;
-        cbd[8] = (tmp & 0xFF);
-        break;
-    case CMD_ATAPI_REQUEST_SENSE:
-        g_assert_cmpuint(xbytes, <=, UINT8_MAX);
-        cbd[4] = (uint8_t)xbytes;
-        break;
-    case CMD_ATAPI_TEST_UNIT_READY:
-    case CMD_ATAPI_START_STOP_UNIT:
-        g_assert_cmpuint(xbytes, ==, 0);
-        break;
-    default:
-        /* SCSI doesn't have uniform packet formats,
-         * so you have to add support for it manually. Sorry! */
-        fprintf(stderr, "The Libqos AHCI driver does not support the set_size "
-                "operation for ATAPI command 0x%02x, please add support.\n",
-                cbd[0]);
-        g_assert_not_reached();
-    }
-}
-
-void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
-                            unsigned prd_size)
-{
-    uint16_t sect_count;
-
-    /* Each PRD can describe up to 4MiB, and must not be odd. */
-    g_assert_cmphex(prd_size, <=, 4096 * 1024);
-    g_assert_cmphex(prd_size & 0x01, ==, 0x00);
-    if (prd_size) {
-        cmd->prd_size = prd_size;
-    }
-    cmd->xbytes = xbytes;
-    sect_count = (cmd->xbytes / cmd->sector_size);
-
-    if (cmd->props->ncq) {
-        NCQFIS *nfis = (NCQFIS *)&(cmd->fis);
-        nfis->sector_low = sect_count & 0xFF;
-        nfis->sector_hi = (sect_count >> 8) & 0xFF;
-    } else if (cmd->props->atapi) {
-        ahci_atapi_set_size(cmd, xbytes);
-    } else {
-        /* For writes, the PIO Setup FIS interrupt only comes from DRQs
-         * after the first.
-         */
-        if (cmd->props->pio && sect_count > (cmd->props->read ? 0 : 1)) {
-            cmd->interrupts |= AHCI_PX_IS_PSS;
-        }
-        cmd->fis.count = sect_count;
-    }
-    cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
-}
-
-void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes)
-{
-    ahci_command_set_sizes(cmd, xbytes, cmd->prd_size);
-}
-
-void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size)
-{
-    ahci_command_set_sizes(cmd, cmd->xbytes, prd_size);
-}
-
-void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer,
-                         uint64_t xbytes, unsigned prd_size)
-{
-    ahci_command_set_sizes(cmd, xbytes, prd_size);
-    ahci_command_set_buffer(cmd, buffer);
-    ahci_command_set_offset(cmd, offset);
-}
-
-void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port)
-{
-    uint16_t i, prdtl;
-    uint64_t table_size, table_ptr, remaining;
-    PRD prd;
-
-    /* This command is now tied to this port/command slot */
-    cmd->port = port;
-    cmd->slot = ahci_pick_cmd(ahci, port);
-
-    if (cmd->props->ncq) {
-        NCQFIS *nfis = (NCQFIS *)&cmd->fis;
-        nfis->tag = (cmd->slot << 3) & 0xFC;
-    }
-
-    /* Create a buffer for the command table */
-    prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
-    table_size = CMD_TBL_SIZ(prdtl);
-    table_ptr = ahci_alloc(ahci, table_size);
-    g_assert(table_ptr);
-    /* AHCI 1.3: Must be aligned to 0x80 */
-    g_assert((table_ptr & 0x7F) == 0x00);
-    cmd->header.ctba = table_ptr;
-
-    /* Commit the command header (part of the Command List Buffer) */
-    ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header));
-    /* Now, write the command table (FIS, ACMD, and PRDT) -- FIS first, */
-    ahci_write_fis(ahci, cmd);
-    /* Then ATAPI CMD, if needed */
-    if (cmd->props->atapi) {
-        qtest_memwrite(ahci->parent->qts, table_ptr + 0x40, cmd->atapi_cmd, 16);
-    }
-
-    /* Construct and write the PRDs to the command table */
-    g_assert_cmphex(prdtl, ==, cmd->header.prdtl);
-    remaining = cmd->xbytes;
-    for (i = 0; i < prdtl; ++i) {
-        prd.dba = cpu_to_le64(cmd->buffer + (cmd->prd_size * i));
-        prd.res = 0;
-        if (remaining > cmd->prd_size) {
-            /* Note that byte count is 0-based. */
-            prd.dbc = cpu_to_le32(cmd->prd_size - 1);
-            remaining -= cmd->prd_size;
-        } else {
-            /* Again, dbc is 0-based. */
-            prd.dbc = cpu_to_le32(remaining - 1);
-            remaining = 0;
-        }
-        prd.dbc |= cpu_to_le32(0x80000000); /* Request DPS Interrupt */
-
-        /* Commit the PRD entry to the Command Table */
-        qtest_memwrite(ahci->parent->qts, table_ptr + 0x80 + (i * sizeof(PRD)),
-                       &prd, sizeof(PRD));
-    }
-
-    /* Bookmark the PRDTL and CTBA values */
-    ahci->port[port].ctba[cmd->slot] = table_ptr;
-    ahci->port[port].prdtl[cmd->slot] = prdtl;
-}
-
-void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd)
-{
-    if (cmd->props->ncq) {
-        ahci_px_wreg(ahci, cmd->port, AHCI_PX_SACT, (1 << cmd->slot));
-    }
-
-    ahci_px_wreg(ahci, cmd->port, AHCI_PX_CI, (1 << cmd->slot));
-}
-
-void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd)
-{
-    /* We can't rely on STS_BSY until the command has started processing.
-     * Therefore, we also use the Command Issue bit as indication of
-     * a command in-flight. */
-
-#define RSET(REG, MASK) (BITSET(ahci_px_rreg(ahci, cmd->port, (REG)), (MASK)))
-
-    while (RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) ||
-           RSET(AHCI_PX_CI, 1 << cmd->slot) ||
-           (cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot))) {
-        usleep(50);
-    }
-
-}
-
-void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd)
-{
-    ahci_command_issue_async(ahci, cmd);
-    ahci_command_wait(ahci, cmd);
-}
-
-void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
-{
-    uint8_t slot = cmd->slot;
-    uint8_t port = cmd->port;
-
-    ahci_port_check_error(ahci, port, cmd->interrupts, cmd->errors);
-    ahci_port_check_interrupts(ahci, port, cmd->interrupts);
-    ahci_port_check_nonbusy(ahci, port, slot);
-    ahci_port_check_cmd_sanity(ahci, cmd);
-    if (cmd->interrupts & AHCI_PX_IS_DHRS) {
-        ahci_port_check_d2h_sanity(ahci, port, slot);
-    }
-    if (cmd->props->pio) {
-        ahci_port_check_pio_sanity(ahci, cmd);
-    }
-}
-
-uint8_t ahci_command_slot(AHCICommand *cmd)
-{
-    return cmd->slot;
-}
diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h
deleted file mode 100644 (file)
index f05b3e5..0000000
+++ /dev/null
@@ -1,651 +0,0 @@
-#ifndef LIBQOS_AHCI_H
-#define LIBQOS_AHCI_H
-
-/*
- * AHCI qtest library functions and definitions
- *
- * Copyright (c) 2014 John Snow <jsnow@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 "libqos/libqos.h"
-#include "libqos/pci.h"
-#include "libqos/malloc-pc.h"
-
-/*** Supplementary PCI Config Space IDs & Masks ***/
-#define PCI_DEVICE_ID_INTEL_Q35_AHCI   (0x2922)
-#define PCI_MSI_FLAGS_RESERVED         (0xFF00)
-#define PCI_PM_CTRL_RESERVED             (0xFC)
-#define PCI_BCC(REG32)          ((REG32) >> 24)
-#define PCI_PI(REG32)   (((REG32) >> 8) & 0xFF)
-#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF)
-
-/*** Recognized AHCI Device Types ***/
-#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \
-                         PCI_VENDOR_ID_INTEL)
-
-/*** AHCI/HBA Register Offsets and Bitmasks ***/
-#define AHCI_CAP                          (0)
-#define AHCI_CAP_NP                    (0x1F)
-#define AHCI_CAP_SXS                   (0x20)
-#define AHCI_CAP_EMS                   (0x40)
-#define AHCI_CAP_CCCS                  (0x80)
-#define AHCI_CAP_NCS                 (0x1F00)
-#define AHCI_CAP_PSC                 (0x2000)
-#define AHCI_CAP_SSC                 (0x4000)
-#define AHCI_CAP_PMD                 (0x8000)
-#define AHCI_CAP_FBSS               (0x10000)
-#define AHCI_CAP_SPM                (0x20000)
-#define AHCI_CAP_SAM                (0x40000)
-#define AHCI_CAP_RESERVED           (0x80000)
-#define AHCI_CAP_ISS               (0xF00000)
-#define AHCI_CAP_SCLO             (0x1000000)
-#define AHCI_CAP_SAL              (0x2000000)
-#define AHCI_CAP_SALP             (0x4000000)
-#define AHCI_CAP_SSS              (0x8000000)
-#define AHCI_CAP_SMPS            (0x10000000)
-#define AHCI_CAP_SSNTF           (0x20000000)
-#define AHCI_CAP_SNCQ            (0x40000000)
-#define AHCI_CAP_S64A            (0x80000000)
-
-#define AHCI_GHC                          (1)
-#define AHCI_GHC_HR                    (0x01)
-#define AHCI_GHC_IE                    (0x02)
-#define AHCI_GHC_MRSM                  (0x04)
-#define AHCI_GHC_RESERVED        (0x7FFFFFF8)
-#define AHCI_GHC_AE              (0x80000000)
-
-#define AHCI_IS                           (2)
-#define AHCI_PI                           (3)
-#define AHCI_VS                           (4)
-
-#define AHCI_CCCCTL                       (5)
-#define AHCI_CCCCTL_EN                 (0x01)
-#define AHCI_CCCCTL_RESERVED           (0x06)
-#define AHCI_CCCCTL_CC               (0xFF00)
-#define AHCI_CCCCTL_TV           (0xFFFF0000)
-
-#define AHCI_CCCPORTS                     (6)
-#define AHCI_EMLOC                        (7)
-
-#define AHCI_EMCTL                        (8)
-#define AHCI_EMCTL_STSMR               (0x01)
-#define AHCI_EMCTL_CTLTM              (0x100)
-#define AHCI_EMCTL_CTLRST             (0x200)
-#define AHCI_EMCTL_RESERVED      (0xF0F0FCFE)
-
-#define AHCI_CAP2                         (9)
-#define AHCI_CAP2_BOH                  (0x01)
-#define AHCI_CAP2_NVMP                 (0x02)
-#define AHCI_CAP2_APST                 (0x04)
-#define AHCI_CAP2_RESERVED       (0xFFFFFFF8)
-
-#define AHCI_BOHC                        (10)
-#define AHCI_RESERVED                    (11)
-#define AHCI_NVMHCI                      (24)
-#define AHCI_VENDOR                      (40)
-#define AHCI_PORTS                       (64)
-
-/*** Port Memory Offsets & Bitmasks ***/
-#define AHCI_PX_CLB                       (0)
-#define AHCI_PX_CLB_RESERVED          (0x1FF)
-
-#define AHCI_PX_CLBU                      (1)
-
-#define AHCI_PX_FB                        (2)
-#define AHCI_PX_FB_RESERVED            (0xFF)
-
-#define AHCI_PX_FBU                       (3)
-
-#define AHCI_PX_IS                        (4)
-#define AHCI_PX_IS_DHRS                 (0x1)
-#define AHCI_PX_IS_PSS                  (0x2)
-#define AHCI_PX_IS_DSS                  (0x4)
-#define AHCI_PX_IS_SDBS                 (0x8)
-#define AHCI_PX_IS_UFS                 (0x10)
-#define AHCI_PX_IS_DPS                 (0x20)
-#define AHCI_PX_IS_PCS                 (0x40)
-#define AHCI_PX_IS_DMPS                (0x80)
-#define AHCI_PX_IS_RESERVED       (0x23FFF00)
-#define AHCI_PX_IS_PRCS            (0x400000)
-#define AHCI_PX_IS_IPMS            (0x800000)
-#define AHCI_PX_IS_OFS            (0x1000000)
-#define AHCI_PX_IS_INFS           (0x4000000)
-#define AHCI_PX_IS_IFS            (0x8000000)
-#define AHCI_PX_IS_HBDS          (0x10000000)
-#define AHCI_PX_IS_HBFS          (0x20000000)
-#define AHCI_PX_IS_TFES          (0x40000000)
-#define AHCI_PX_IS_CPDS          (0x80000000)
-
-#define AHCI_PX_IE                        (5)
-#define AHCI_PX_IE_DHRE                 (0x1)
-#define AHCI_PX_IE_PSE                  (0x2)
-#define AHCI_PX_IE_DSE                  (0x4)
-#define AHCI_PX_IE_SDBE                 (0x8)
-#define AHCI_PX_IE_UFE                 (0x10)
-#define AHCI_PX_IE_DPE                 (0x20)
-#define AHCI_PX_IE_PCE                 (0x40)
-#define AHCI_PX_IE_DMPE                (0x80)
-#define AHCI_PX_IE_RESERVED       (0x23FFF00)
-#define AHCI_PX_IE_PRCE            (0x400000)
-#define AHCI_PX_IE_IPME            (0x800000)
-#define AHCI_PX_IE_OFE            (0x1000000)
-#define AHCI_PX_IE_INFE           (0x4000000)
-#define AHCI_PX_IE_IFE            (0x8000000)
-#define AHCI_PX_IE_HBDE          (0x10000000)
-#define AHCI_PX_IE_HBFE          (0x20000000)
-#define AHCI_PX_IE_TFEE          (0x40000000)
-#define AHCI_PX_IE_CPDE          (0x80000000)
-
-#define AHCI_PX_CMD                       (6)
-#define AHCI_PX_CMD_ST                  (0x1)
-#define AHCI_PX_CMD_SUD                 (0x2)
-#define AHCI_PX_CMD_POD                 (0x4)
-#define AHCI_PX_CMD_CLO                 (0x8)
-#define AHCI_PX_CMD_FRE                (0x10)
-#define AHCI_PX_CMD_RESERVED           (0xE0)
-#define AHCI_PX_CMD_CCS              (0x1F00)
-#define AHCI_PX_CMD_MPSS             (0x2000)
-#define AHCI_PX_CMD_FR               (0x4000)
-#define AHCI_PX_CMD_CR               (0x8000)
-#define AHCI_PX_CMD_CPS             (0x10000)
-#define AHCI_PX_CMD_PMA             (0x20000)
-#define AHCI_PX_CMD_HPCP            (0x40000)
-#define AHCI_PX_CMD_MPSP            (0x80000)
-#define AHCI_PX_CMD_CPD            (0x100000)
-#define AHCI_PX_CMD_ESP            (0x200000)
-#define AHCI_PX_CMD_FBSCP          (0x400000)
-#define AHCI_PX_CMD_APSTE          (0x800000)
-#define AHCI_PX_CMD_ATAPI         (0x1000000)
-#define AHCI_PX_CMD_DLAE          (0x2000000)
-#define AHCI_PX_CMD_ALPE          (0x4000000)
-#define AHCI_PX_CMD_ASP           (0x8000000)
-#define AHCI_PX_CMD_ICC          (0xF0000000)
-
-#define AHCI_PX_RES1                      (7)
-
-#define AHCI_PX_TFD                       (8)
-#define AHCI_PX_TFD_STS                (0xFF)
-#define AHCI_PX_TFD_STS_ERR            (0x01)
-#define AHCI_PX_TFD_STS_CS1            (0x06)
-#define AHCI_PX_TFD_STS_DRQ            (0x08)
-#define AHCI_PX_TFD_STS_CS2            (0x70)
-#define AHCI_PX_TFD_STS_BSY            (0x80)
-#define AHCI_PX_TFD_ERR              (0xFF00)
-#define AHCI_PX_TFD_RESERVED     (0xFFFF0000)
-
-#define AHCI_PX_SIG                       (9)
-#define AHCI_PX_SIG_SECTOR_COUNT       (0xFF)
-#define AHCI_PX_SIG_LBA_LOW          (0xFF00)
-#define AHCI_PX_SIG_LBA_MID        (0xFF0000)
-#define AHCI_PX_SIG_LBA_HIGH     (0xFF000000)
-
-#define AHCI_PX_SSTS                     (10)
-#define AHCI_PX_SSTS_DET               (0x0F)
-#define AHCI_PX_SSTS_SPD               (0xF0)
-#define AHCI_PX_SSTS_IPM              (0xF00)
-#define AHCI_PX_SSTS_RESERVED    (0xFFFFF000)
-#define SSTS_DET_NO_DEVICE             (0x00)
-#define SSTS_DET_PRESENT               (0x01)
-#define SSTS_DET_ESTABLISHED           (0x03)
-#define SSTS_DET_OFFLINE               (0x04)
-
-#define AHCI_PX_SCTL                     (11)
-
-#define AHCI_PX_SERR                     (12)
-#define AHCI_PX_SERR_ERR             (0xFFFF)
-#define AHCI_PX_SERR_DIAG        (0xFFFF0000)
-#define AHCI_PX_SERR_DIAG_X      (0x04000000)
-
-#define AHCI_PX_SACT                     (13)
-#define AHCI_PX_CI                       (14)
-#define AHCI_PX_SNTF                     (15)
-
-#define AHCI_PX_FBS                      (16)
-#define AHCI_PX_FBS_EN                  (0x1)
-#define AHCI_PX_FBS_DEC                 (0x2)
-#define AHCI_PX_FBS_SDE                 (0x4)
-#define AHCI_PX_FBS_DEV               (0xF00)
-#define AHCI_PX_FBS_ADO              (0xF000)
-#define AHCI_PX_FBS_DWE             (0xF0000)
-#define AHCI_PX_FBS_RESERVED     (0xFFF000F8)
-
-#define AHCI_PX_RES2                     (17)
-#define AHCI_PX_VS                       (28)
-
-#define HBA_DATA_REGION_SIZE            (256)
-#define HBA_PORT_DATA_SIZE              (128)
-#define HBA_PORT_NUM_REG (HBA_PORT_DATA_SIZE/4)
-
-#define AHCI_VERSION_0_95        (0x00000905)
-#define AHCI_VERSION_1_0         (0x00010000)
-#define AHCI_VERSION_1_1         (0x00010100)
-#define AHCI_VERSION_1_2         (0x00010200)
-#define AHCI_VERSION_1_3         (0x00010300)
-
-#define AHCI_SECTOR_SIZE                (512)
-#define ATAPI_SECTOR_SIZE              (2048)
-
-#define AHCI_SIGNATURE_CDROM     (0xeb140101)
-#define AHCI_SIGNATURE_DISK      (0x00000101)
-
-/* FIS types */
-enum {
-    REG_H2D_FIS = 0x27,
-    REG_D2H_FIS = 0x34,
-    DMA_ACTIVATE_FIS = 0x39,
-    DMA_SETUP_FIS = 0x41,
-    DATA_FIS = 0x46,
-    BIST_ACTIVATE_FIS = 0x58,
-    PIO_SETUP_FIS = 0x5F,
-    SDB_FIS = 0xA1
-};
-
-/* FIS flags */
-#define REG_H2D_FIS_CMD  0x80
-
-/* ATA Commands */
-enum {
-    /* DMA */
-    CMD_READ_DMA       = 0xC8,
-    CMD_READ_DMA_EXT   = 0x25,
-    CMD_WRITE_DMA      = 0xCA,
-    CMD_WRITE_DMA_EXT  = 0x35,
-    /* PIO */
-    CMD_READ_PIO       = 0x20,
-    CMD_READ_PIO_EXT   = 0x24,
-    CMD_WRITE_PIO      = 0x30,
-    CMD_WRITE_PIO_EXT  = 0x34,
-    /* Misc */
-    CMD_READ_MAX       = 0xF8,
-    CMD_READ_MAX_EXT   = 0x27,
-    CMD_FLUSH_CACHE    = 0xE7,
-    CMD_IDENTIFY       = 0xEC,
-    CMD_PACKET         = 0xA0,
-    CMD_PACKET_ID      = 0xA1,
-    /* NCQ */
-    READ_FPDMA_QUEUED  = 0x60,
-    WRITE_FPDMA_QUEUED = 0x61,
-};
-
-/* ATAPI Commands */
-enum {
-    CMD_ATAPI_TEST_UNIT_READY = 0x00,
-    CMD_ATAPI_REQUEST_SENSE   = 0x03,
-    CMD_ATAPI_START_STOP_UNIT = 0x1b,
-    CMD_ATAPI_READ_10         = 0x28,
-    CMD_ATAPI_READ_CD         = 0xbe,
-};
-
-enum {
-    SENSE_NO_SENSE       = 0x00,
-    SENSE_NOT_READY      = 0x02,
-    SENSE_UNIT_ATTENTION = 0x06,
-};
-
-enum {
-    ASC_MEDIUM_MAY_HAVE_CHANGED = 0x28,
-    ASC_MEDIUM_NOT_PRESENT      = 0x3a,
-};
-
-/* AHCI Command Header Flags & Masks*/
-#define CMDH_CFL        (0x1F)
-#define CMDH_ATAPI      (0x20)
-#define CMDH_WRITE      (0x40)
-#define CMDH_PREFETCH   (0x80)
-#define CMDH_RESET     (0x100)
-#define CMDH_BIST      (0x200)
-#define CMDH_CLR_BSY   (0x400)
-#define CMDH_RES       (0x800)
-#define CMDH_PMP      (0xF000)
-
-/* ATA device register masks */
-#define ATA_DEVICE_MAGIC 0xA0 /* used in ata1-3 */
-#define ATA_DEVICE_LBA   0x40
-#define NCQ_DEVICE_MAGIC 0x40 /* for ncq device registers */
-#define ATA_DEVICE_DRIVE 0x10
-#define ATA_DEVICE_HEAD  0x0F
-
-/*** Structures ***/
-
-typedef struct AHCIPortQState {
-    uint64_t fb;
-    uint64_t clb;
-    uint64_t ctba[32];
-    uint16_t prdtl[32];
-    uint8_t next; /** Next Command Slot to Use **/
-} AHCIPortQState;
-
-typedef struct AHCIQState {
-    QOSState *parent;
-    QPCIDevice *dev;
-    QPCIBar hba_bar;
-    uint64_t barsize;
-    uint32_t fingerprint;
-    uint32_t cap;
-    uint32_t cap2;
-    AHCIPortQState port[32];
-    bool enabled;
-} AHCIQState;
-
-/**
- * Generic FIS structure.
- */
-typedef struct FIS {
-    uint8_t fis_type;
-    uint8_t flags;
-    char data[0];
-} __attribute__((__packed__)) FIS;
-
-/**
- * Register device-to-host FIS structure.
- */
-typedef struct RegD2HFIS {
-    /* DW0 */
-    uint8_t fis_type;
-    uint8_t flags;
-    uint8_t status;
-    uint8_t error;
-    /* DW1 */
-    uint8_t lba_lo[3];
-    uint8_t device;
-    /* DW2 */
-    uint8_t lba_hi[3];
-    uint8_t res0;
-    /* DW3 */
-    uint16_t count;
-    uint16_t res1;
-    /* DW4 */
-    uint32_t res2;
-} __attribute__((__packed__)) RegD2HFIS;
-
-/**
- * Register device-to-host FIS structure;
- * PIO Setup variety.
- */
-typedef struct PIOSetupFIS {
-    /* DW0 */
-    uint8_t fis_type;
-    uint8_t flags;
-    uint8_t status;
-    uint8_t error;
-    /* DW1 */
-    uint8_t lba_lo[3];
-    uint8_t device;
-    /* DW2 */
-    uint8_t lba_hi[3];
-    uint8_t res0;
-    /* DW3 */
-    uint16_t count;
-    uint8_t res1;
-    uint8_t e_status;
-    /* DW4 */
-    uint16_t tx_count;
-    uint16_t res2;
-} __attribute__((__packed__)) PIOSetupFIS;
-
-/**
- * Register host-to-device FIS structure.
- */
-typedef struct RegH2DFIS {
-    /* DW0 */
-    uint8_t fis_type;
-    uint8_t flags;
-    uint8_t command;
-    uint8_t feature_low;
-    /* DW1 */
-    uint8_t lba_lo[3];
-    uint8_t device;
-    /* DW2 */
-    uint8_t lba_hi[3];
-    uint8_t feature_high;
-    /* DW3 */
-    uint16_t count;
-    uint8_t icc;
-    uint8_t control;
-    /* DW4 */
-    uint8_t aux[4];
-} __attribute__((__packed__)) RegH2DFIS;
-
-/**
- * Register host-to-device FIS structure, for NCQ commands.
- * Actually just a RegH2DFIS, but with fields repurposed.
- * Repurposed fields are annotated below.
- */
-typedef struct NCQFIS {
-    /* DW0 */
-    uint8_t fis_type;
-    uint8_t flags;
-    uint8_t command;
-    uint8_t sector_low; /* H2D: Feature 7:0 */
-    /* DW1 */
-    uint8_t lba_lo[3];
-    uint8_t device;
-    /* DW2 */
-    uint8_t lba_hi[3];
-    uint8_t sector_hi; /* H2D: Feature 15:8 */
-    /* DW3 */
-    uint8_t tag;       /* H2D: Count 0:7 */
-    uint8_t prio;      /* H2D: Count 15:8 */
-    uint8_t icc;
-    uint8_t control;
-    /* DW4 */
-    uint8_t aux[4];
-} __attribute__((__packed__)) NCQFIS;
-
-/**
- * Command List entry structure.
- * The command list contains between 1-32 of these structures.
- */
-typedef struct AHCICommandHeader {
-    uint16_t flags; /* Cmd-Fis-Len, PMP#, and flags. */
-    uint16_t prdtl; /* Phys Region Desc. Table Length */
-    uint32_t prdbc; /* Phys Region Desc. Byte Count */
-    uint64_t ctba;  /* Command Table Descriptor Base Address */
-    uint32_t res[4];
-} __attribute__((__packed__)) AHCICommandHeader;
-
-/**
- * Physical Region Descriptor; pointed to by the Command List Header,
- * struct ahci_command.
- */
-typedef struct PRD {
-    uint64_t dba;  /* Data Base Address */
-    uint32_t res;  /* Reserved */
-    uint32_t dbc;  /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */
-} __attribute__((__packed__)) PRD;
-
-/* Opaque, defined within ahci.c */
-typedef struct AHCICommand AHCICommand;
-
-/* Options to ahci_exec */
-typedef struct AHCIOpts {
-    size_t size;        /* Size of transfer */
-    unsigned prd_size;  /* Size per-each PRD */
-    bool set_bcl;       /* Override the default BCL of ATAPI_SECTOR_SIZE */
-    unsigned bcl;       /* Byte Count Limit, for ATAPI PIO */
-    uint64_t lba;       /* Starting LBA offset */
-    uint64_t buffer;    /* Pointer to source or destination guest buffer */
-    bool atapi;         /* ATAPI command? */
-    bool atapi_dma;     /* Use DMA for ATAPI? */
-    bool error;
-    int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
-    int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
-    int (*post_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
-    void *opaque;
-} AHCIOpts;
-
-/*** Macro Utilities ***/
-#define BITANY(data, mask) (((data) & (mask)) != 0)
-#define BITSET(data, mask) (((data) & (mask)) == (mask))
-#define BITCLR(data, mask) (((data) & (mask)) == 0)
-#define ASSERT_BIT_SET(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
-#define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
-
-/* For calculating how big the PRD table needs to be: */
-#define CMD_TBL_SIZ(n) ((0x80 + ((n) * sizeof(PRD)) + 0x7F) & ~0x7F)
-
-/* Helpers for reading/writing AHCI HBA register values */
-
-static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset)
-{
-    return qpci_io_readl(ahci->dev, ahci->hba_bar, offset);
-}
-
-static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value)
-{
-    qpci_io_writel(ahci->dev, ahci->hba_bar, offset, value);
-}
-
-static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num)
-{
-    return ahci_mread(ahci, 4 * reg_num);
-}
-
-static inline void ahci_wreg(AHCIQState *ahci, uint32_t reg_num, uint32_t value)
-{
-    ahci_mwrite(ahci, 4 * reg_num, value);
-}
-
-static inline void ahci_set(AHCIQState *ahci, uint32_t reg_num, uint32_t mask)
-{
-    ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) | mask);
-}
-
-static inline void ahci_clr(AHCIQState *ahci, uint32_t reg_num, uint32_t mask)
-{
-    ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) & ~mask);
-}
-
-static inline size_t ahci_px_offset(uint8_t port, uint32_t reg_num)
-{
-    return AHCI_PORTS + (HBA_PORT_NUM_REG * port) + reg_num;
-}
-
-static inline uint32_t ahci_px_rreg(AHCIQState *ahci, uint8_t port,
-                                    uint32_t reg_num)
-{
-    return ahci_rreg(ahci, ahci_px_offset(port, reg_num));
-}
-
-static inline void ahci_px_wreg(AHCIQState *ahci, uint8_t port,
-                                uint32_t reg_num, uint32_t value)
-{
-    ahci_wreg(ahci, ahci_px_offset(port, reg_num), value);
-}
-
-static inline void ahci_px_set(AHCIQState *ahci, uint8_t port,
-                               uint32_t reg_num, uint32_t mask)
-{
-    ahci_px_wreg(ahci, port, reg_num,
-                 ahci_px_rreg(ahci, port, reg_num) | mask);
-}
-
-static inline void ahci_px_clr(AHCIQState *ahci, uint8_t port,
-                               uint32_t reg_num, uint32_t mask)
-{
-    ahci_px_wreg(ahci, port, reg_num,
-                 ahci_px_rreg(ahci, port, reg_num) & ~mask);
-}
-
-/*** Prototypes ***/
-uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes);
-void ahci_free(AHCIQState *ahci, uint64_t addr);
-void ahci_clean_mem(AHCIQState *ahci);
-
-/* Device management */
-QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint);
-void free_ahci_device(QPCIDevice *dev);
-void ahci_pci_enable(AHCIQState *ahci);
-void start_ahci_device(AHCIQState *ahci);
-void ahci_hba_enable(AHCIQState *ahci);
-
-/* Port Management */
-unsigned ahci_port_select(AHCIQState *ahci);
-void ahci_port_clear(AHCIQState *ahci, uint8_t port);
-
-/* Command header / table management */
-unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port);
-void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
-                             uint8_t slot, AHCICommandHeader *cmd);
-void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
-                             uint8_t slot, AHCICommandHeader *cmd);
-void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
-
-/* AHCI sanity check routines */
-void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
-                           uint32_t imask, uint8_t emask);
-void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
-                                uint32_t intr_mask);
-void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
-void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
-void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
-void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
-
-/* Misc */
-bool is_atapi(AHCIQState *ahci, uint8_t port);
-unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd);
-
-/* Command: Macro level execution */
-void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
-                   uint64_t gbuffer, size_t size, uint64_t sector);
-AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
-                                uint64_t gbuffer, size_t size, uint64_t sector);
-void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd);
-void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
-             void *buffer, size_t bufsize, uint64_t sector);
-void ahci_exec(AHCIQState *ahci, uint8_t port,
-               uint8_t op, const AHCIOpts *opts);
-void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, bool ready,
-                           uint8_t expected_sense);
-void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
-                          uint8_t *sense, uint8_t *asc);
-void ahci_atapi_eject(AHCIQState *ahci, uint8_t port);
-void ahci_atapi_load(AHCIQState *ahci, uint8_t port);
-
-/* Command: Fine-grained lifecycle */
-AHCICommand *ahci_command_create(uint8_t command_name);
-AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma);
-void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
-void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
-void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
-void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd);
-void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd);
-void ahci_command_free(AHCICommand *cmd);
-
-/* Command: adjustments */
-void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags);
-void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags);
-void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect);
-void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer);
-void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes);
-void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size);
-void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
-                            unsigned prd_size);
-void ahci_command_set_acmd(AHCICommand *cmd, void *acmd);
-void ahci_command_enable_atapi_dma(AHCICommand *cmd);
-void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer,
-                         uint64_t xbytes, unsigned prd_size);
-
-/* Command: Misc */
-uint8_t ahci_command_slot(AHCICommand *cmd);
-void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd);
-
-#endif
diff --git a/tests/libqos/arm-imx25-pdk-machine.c b/tests/libqos/arm-imx25-pdk-machine.c
deleted file mode 100644 (file)
index 25066fb..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2019 Red Hat, Inc.
- *
- * Author: Paolo Bonzini <pbonzini@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "libqos/i2c.h"
-
-#define ARM_PAGE_SIZE            4096
-#define IMX25_PDK_RAM_START      0x80000000
-#define IMX25_PDK_RAM_END        0x88000000
-
-typedef struct QIMX25PDKMachine QIMX25PDKMachine;
-
-struct QIMX25PDKMachine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    IMXI2C i2c_1;
-};
-
-static void *imx25_pdk_get_driver(void *object, const char *interface)
-{
-    QIMX25PDKMachine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in arm/imx25_pdk\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *imx25_pdk_get_device(void *obj, const char *device)
-{
-    QIMX25PDKMachine *machine = obj;
-    if (!g_strcmp0(device, "imx.i2c")) {
-        return &machine->i2c_1.obj;
-    }
-
-    fprintf(stderr, "%s not present in arm/imx25_pdk\n", device);
-    g_assert_not_reached();
-}
-
-static void imx25_pdk_destructor(QOSGraphObject *obj)
-{
-    QIMX25PDKMachine *machine = (QIMX25PDKMachine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_arm_imx25_pdk(QTestState *qts)
-{
-    QIMX25PDKMachine *machine = g_new0(QIMX25PDKMachine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               IMX25_PDK_RAM_START,
-               IMX25_PDK_RAM_END,
-               ARM_PAGE_SIZE);
-    machine->obj.get_device = imx25_pdk_get_device;
-    machine->obj.get_driver = imx25_pdk_get_driver;
-    machine->obj.destructor = imx25_pdk_destructor;
-
-    imx_i2c_init(&machine->i2c_1, qts, 0x43f80000);
-    return &machine->obj;
-}
-
-static void imx25_pdk_register_nodes(void)
-{
-    QOSGraphEdgeOptions edge = {
-        .extra_device_opts = "bus=i2c-bus.0"
-    };
-    qos_node_create_machine("arm/imx25-pdk", qos_create_machine_arm_imx25_pdk);
-    qos_node_contains("arm/imx25-pdk", "imx.i2c", &edge, NULL);
-}
-
-libqos_init(imx25_pdk_register_nodes);
diff --git a/tests/libqos/arm-n800-machine.c b/tests/libqos/arm-n800-machine.c
deleted file mode 100644 (file)
index 87279bd..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2019 Red Hat, Inc.
- *
- * Author: Paolo Bonzini <pbonzini@redhat.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "libqos/i2c.h"
-
-#define ARM_PAGE_SIZE            4096
-#define N800_RAM_START      0x80000000
-#define N800_RAM_END        0x88000000
-
-typedef struct QN800Machine QN800Machine;
-
-struct QN800Machine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    OMAPI2C i2c_1;
-};
-
-static void *n800_get_driver(void *object, const char *interface)
-{
-    QN800Machine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in arm/n800\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *n800_get_device(void *obj, const char *device)
-{
-    QN800Machine *machine = obj;
-    if (!g_strcmp0(device, "omap_i2c")) {
-        return &machine->i2c_1.obj;
-    }
-
-    fprintf(stderr, "%s not present in arm/n800\n", device);
-    g_assert_not_reached();
-}
-
-static void n800_destructor(QOSGraphObject *obj)
-{
-    QN800Machine *machine = (QN800Machine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_arm_n800(QTestState *qts)
-{
-    QN800Machine *machine = g_new0(QN800Machine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               N800_RAM_START,
-               N800_RAM_END,
-               ARM_PAGE_SIZE);
-    machine->obj.get_device = n800_get_device;
-    machine->obj.get_driver = n800_get_driver;
-    machine->obj.destructor = n800_destructor;
-
-    omap_i2c_init(&machine->i2c_1, qts, 0x48070000);
-    return &machine->obj;
-}
-
-static void n800_register_nodes(void)
-{
-    QOSGraphEdgeOptions edge = {
-        .extra_device_opts = "bus=i2c-bus.0"
-    };
-    qos_node_create_machine("arm/n800", qos_create_machine_arm_n800);
-    qos_node_contains("arm/n800", "omap_i2c", &edge, NULL);
-}
-
-libqos_init(n800_register_nodes);
diff --git a/tests/libqos/arm-raspi2-machine.c b/tests/libqos/arm-raspi2-machine.c
deleted file mode 100644 (file)
index 12a7cb7..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "sdhci.h"
-
-#define ARM_PAGE_SIZE             4096
-#define RASPI2_RAM_ADDR           0
-#define RASPI2_RAM_SIZE           0x20000000
-
-typedef struct QRaspi2Machine QRaspi2Machine;
-
-struct QRaspi2Machine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    QSDHCI_MemoryMapped sdhci;
-};
-
-static void *raspi2_get_driver(void *object, const char *interface)
-{
-    QRaspi2Machine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in arm/raspi2\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *raspi2_get_device(void *obj, const char *device)
-{
-    QRaspi2Machine *machine = obj;
-    if (!g_strcmp0(device, "generic-sdhci")) {
-        return &machine->sdhci.obj;
-    }
-
-    fprintf(stderr, "%s not present in arm/raspi2\n", device);
-    g_assert_not_reached();
-}
-
-static void raspi2_destructor(QOSGraphObject *obj)
-{
-    QRaspi2Machine *machine = (QRaspi2Machine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_arm_raspi2(QTestState *qts)
-{
-    QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               RASPI2_RAM_ADDR + (1 << 20),
-               RASPI2_RAM_ADDR + RASPI2_RAM_SIZE,
-               ARM_PAGE_SIZE);
-    machine->obj.get_device = raspi2_get_device;
-    machine->obj.get_driver = raspi2_get_driver;
-    machine->obj.destructor = raspi2_destructor;
-    qos_init_sdhci_mm(&machine->sdhci, qts, 0x3f300000, &(QSDHCIProperties) {
-        .version = 3,
-        .baseclock = 52,
-        .capab.sdma = false,
-        .capab.reg = 0x052134b4
-    });
-    return &machine->obj;
-}
-
-static void raspi2_register_nodes(void)
-{
-    qos_node_create_machine("arm/raspi2", qos_create_machine_arm_raspi2);
-    qos_node_contains("arm/raspi2", "generic-sdhci", NULL);
-}
-
-libqos_init(raspi2_register_nodes);
diff --git a/tests/libqos/arm-sabrelite-machine.c b/tests/libqos/arm-sabrelite-machine.c
deleted file mode 100644 (file)
index e6df437..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "sdhci.h"
-
-#define ARM_PAGE_SIZE            4096
-#define SABRELITE_RAM_START      0x10000000
-#define SABRELITE_RAM_END        0x30000000
-
-typedef struct QSabreliteMachine QSabreliteMachine;
-
-struct QSabreliteMachine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    QSDHCI_MemoryMapped sdhci;
-};
-
-static void *sabrelite_get_driver(void *object, const char *interface)
-{
-    QSabreliteMachine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in arm/sabrelite\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *sabrelite_get_device(void *obj, const char *device)
-{
-    QSabreliteMachine *machine = obj;
-    if (!g_strcmp0(device, "generic-sdhci")) {
-        return &machine->sdhci.obj;
-    }
-
-    fprintf(stderr, "%s not present in arm/sabrelite\n", device);
-    g_assert_not_reached();
-}
-
-static void sabrelite_destructor(QOSGraphObject *obj)
-{
-    QSabreliteMachine *machine = (QSabreliteMachine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_arm_sabrelite(QTestState *qts)
-{
-    QSabreliteMachine *machine = g_new0(QSabreliteMachine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               SABRELITE_RAM_START,
-               SABRELITE_RAM_END,
-               ARM_PAGE_SIZE);
-    machine->obj.get_device = sabrelite_get_device;
-    machine->obj.get_driver = sabrelite_get_driver;
-    machine->obj.destructor = sabrelite_destructor;
-    qos_init_sdhci_mm(&machine->sdhci, qts, 0x02190000, &(QSDHCIProperties) {
-        .version = 3,
-        .baseclock = 0,
-        .capab.sdma = true,
-        .capab.reg = 0x057834b4,
-    });
-    return &machine->obj;
-}
-
-static void sabrelite_register_nodes(void)
-{
-    qos_node_create_machine("arm/sabrelite", qos_create_machine_arm_sabrelite);
-    qos_node_contains("arm/sabrelite", "generic-sdhci", NULL);
-}
-
-libqos_init(sabrelite_register_nodes);
diff --git a/tests/libqos/arm-smdkc210-machine.c b/tests/libqos/arm-smdkc210-machine.c
deleted file mode 100644 (file)
index 215b628..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "sdhci.h"
-
-#define ARM_PAGE_SIZE             4096
-#define SMDKC210_RAM_ADDR         0x40000000ull
-#define SMDKC210_RAM_SIZE         0x40000000ull
-
-typedef struct QSmdkc210Machine QSmdkc210Machine;
-
-struct QSmdkc210Machine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    QSDHCI_MemoryMapped sdhci;
-};
-
-static void *smdkc210_get_driver(void *object, const char *interface)
-{
-    QSmdkc210Machine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in arm/smdkc210\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *smdkc210_get_device(void *obj, const char *device)
-{
-    QSmdkc210Machine *machine = obj;
-    if (!g_strcmp0(device, "generic-sdhci")) {
-        return &machine->sdhci.obj;
-    }
-
-    fprintf(stderr, "%s not present in arm/smdkc210\n", device);
-    g_assert_not_reached();
-}
-
-static void smdkc210_destructor(QOSGraphObject *obj)
-{
-    QSmdkc210Machine *machine = (QSmdkc210Machine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_arm_smdkc210(QTestState *qts)
-{
-    QSmdkc210Machine *machine = g_new0(QSmdkc210Machine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               SMDKC210_RAM_ADDR,
-               SMDKC210_RAM_ADDR + SMDKC210_RAM_SIZE,
-               ARM_PAGE_SIZE);
-    machine->obj.get_device = smdkc210_get_device;
-    machine->obj.get_driver = smdkc210_get_driver;
-    machine->obj.destructor = smdkc210_destructor;
-    qos_init_sdhci_mm(&machine->sdhci, qts, 0x12510000, &(QSDHCIProperties) {
-        .version = 2,
-        .baseclock = 0,
-        .capab.sdma = true,
-        .capab.reg = 0x5e80080,
-    });
-    return &machine->obj;
-}
-
-static void smdkc210_register_nodes(void)
-{
-    qos_node_create_machine("arm/smdkc210", qos_create_machine_arm_smdkc210);
-    qos_node_contains("arm/smdkc210", "generic-sdhci", NULL);
-}
-
-libqos_init(smdkc210_register_nodes);
diff --git a/tests/libqos/arm-virt-machine.c b/tests/libqos/arm-virt-machine.c
deleted file mode 100644 (file)
index 96ffe3e..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "libqos/virtio-mmio.h"
-
-#define ARM_PAGE_SIZE               4096
-#define VIRTIO_MMIO_BASE_ADDR       0x0A003E00
-#define ARM_VIRT_RAM_ADDR           0x40000000
-#define ARM_VIRT_RAM_SIZE           0x20000000
-#define VIRTIO_MMIO_SIZE            0x00000200
-
-typedef struct QVirtMachine QVirtMachine;
-
-struct QVirtMachine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    QVirtioMMIODevice virtio_mmio;
-};
-
-static void virt_destructor(QOSGraphObject *obj)
-{
-    QVirtMachine *machine = (QVirtMachine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *virt_get_driver(void *object, const char *interface)
-{
-    QVirtMachine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in arm/virtio\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *virt_get_device(void *obj, const char *device)
-{
-    QVirtMachine *machine = obj;
-    if (!g_strcmp0(device, "virtio-mmio")) {
-        return &machine->virtio_mmio.obj;
-    }
-
-    fprintf(stderr, "%s not present in arm/virtio\n", device);
-    g_assert_not_reached();
-}
-
-static void *qos_create_machine_arm_virt(QTestState *qts)
-{
-    QVirtMachine *machine = g_new0(QVirtMachine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               ARM_VIRT_RAM_ADDR,
-               ARM_VIRT_RAM_ADDR + ARM_VIRT_RAM_SIZE,
-               ARM_PAGE_SIZE);
-    qvirtio_mmio_init_device(&machine->virtio_mmio, qts, VIRTIO_MMIO_BASE_ADDR,
-                             VIRTIO_MMIO_SIZE);
-
-    machine->obj.get_device = virt_get_device;
-    machine->obj.get_driver = virt_get_driver;
-    machine->obj.destructor = virt_destructor;
-    return machine;
-}
-
-static void virtio_mmio_register_nodes(void)
-{
-    qos_node_create_machine("arm/virt", qos_create_machine_arm_virt);
-    qos_node_contains("arm/virt", "virtio-mmio", NULL);
-}
-
-libqos_init(virtio_mmio_register_nodes);
diff --git a/tests/libqos/arm-xilinx-zynq-a9-machine.c b/tests/libqos/arm-xilinx-zynq-a9-machine.c
deleted file mode 100644 (file)
index 5bc95f2..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "sdhci.h"
-
-typedef struct QXilinxZynqA9Machine QXilinxZynqA9Machine;
-
-struct QXilinxZynqA9Machine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    QSDHCI_MemoryMapped sdhci;
-};
-
-#define ARM_PAGE_SIZE             4096
-#define XILINX_ZYNQ_A9_RAM_ADDR   0
-#define XILINX_ZYNQ_A9_RAM_SIZE   0x20000000
-
-static void *xilinx_zynq_a9_get_driver(void *object, const char *interface)
-{
-    QXilinxZynqA9Machine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *xilinx_zynq_a9_get_device(void *obj, const char *device)
-{
-    QXilinxZynqA9Machine *machine = obj;
-    if (!g_strcmp0(device, "generic-sdhci")) {
-        return &machine->sdhci.obj;
-    }
-
-    fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", device);
-    g_assert_not_reached();
-}
-
-static void xilinx_zynq_a9_destructor(QOSGraphObject *obj)
-{
-    QXilinxZynqA9Machine *machine = (QXilinxZynqA9Machine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *qos_create_machine_arm_xilinx_zynq_a9(QTestState *qts)
-{
-    QXilinxZynqA9Machine *machine = g_new0(QXilinxZynqA9Machine, 1);
-
-    alloc_init(&machine->alloc, 0,
-               XILINX_ZYNQ_A9_RAM_ADDR + (1 << 20),
-               XILINX_ZYNQ_A9_RAM_ADDR + XILINX_ZYNQ_A9_RAM_SIZE,
-               ARM_PAGE_SIZE);
-
-    machine->obj.get_device = xilinx_zynq_a9_get_device;
-    machine->obj.get_driver = xilinx_zynq_a9_get_driver;
-    machine->obj.destructor = xilinx_zynq_a9_destructor;
-    /* Datasheet: UG585 (v1.12.1) */
-    qos_init_sdhci_mm(&machine->sdhci, qts, 0xe0100000, &(QSDHCIProperties) {
-        .version = 2,
-        .baseclock = 0,
-        .capab.sdma = true,
-        .capab.reg = 0x69ec0080,
-    });
-    return &machine->obj;
-}
-
-static void xilinx_zynq_a9_register_nodes(void)
-{
-    qos_node_create_machine("arm/xilinx-zynq-a9",
-                            qos_create_machine_arm_xilinx_zynq_a9);
-    qos_node_contains("arm/xilinx-zynq-a9", "generic-sdhci", NULL);
-}
-
-libqos_init(xilinx_zynq_a9_register_nodes);
diff --git a/tests/libqos/e1000e.c b/tests/libqos/e1000e.c
deleted file mode 100644 (file)
index 560e7a2..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/pci-pc.h"
-#include "qemu/sockets.h"
-#include "qemu/iov.h"
-#include "qemu/module.h"
-#include "qemu/bitops.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "e1000e.h"
-
-#define E1000E_IMS      (0x00d0)
-
-#define E1000E_STATUS   (0x0008)
-#define E1000E_STATUS_LU BIT(1)
-#define E1000E_STATUS_ASDV1000 BIT(9)
-
-#define E1000E_CTRL     (0x0000)
-#define E1000E_CTRL_RESET BIT(26)
-
-#define E1000E_RCTL     (0x0100)
-#define E1000E_RCTL_EN  BIT(1)
-#define E1000E_RCTL_UPE BIT(3)
-#define E1000E_RCTL_MPE BIT(4)
-
-#define E1000E_RFCTL     (0x5008)
-#define E1000E_RFCTL_EXTEN  BIT(15)
-
-#define E1000E_TCTL     (0x0400)
-#define E1000E_TCTL_EN  BIT(1)
-
-#define E1000E_CTRL_EXT             (0x0018)
-#define E1000E_CTRL_EXT_DRV_LOAD    BIT(28)
-#define E1000E_CTRL_EXT_TXLSFLOW    BIT(22)
-
-#define E1000E_IVAR                 (0x00E4)
-#define E1000E_IVAR_TEST_CFG        ((E1000E_RX0_MSG_ID << 0)    | BIT(3)  | \
-                                     (E1000E_TX0_MSG_ID << 8)    | BIT(11) | \
-                                     (E1000E_OTHER_MSG_ID << 16) | BIT(19) | \
-                                     BIT(31))
-
-#define E1000E_RING_LEN             (0x1000)
-
-#define E1000E_TDBAL    (0x3800)
-
-#define E1000E_TDBAH    (0x3804)
-#define E1000E_TDH      (0x3810)
-
-#define E1000E_RDBAL    (0x2800)
-#define E1000E_RDBAH    (0x2804)
-#define E1000E_RDH      (0x2810)
-
-#define E1000E_TXD_LEN              (16)
-#define E1000E_RXD_LEN              (16)
-
-static void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val)
-{
-    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
-    qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val);
-}
-
-static uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg)
-{
-    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
-    return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg);
-}
-
-void e1000e_tx_ring_push(QE1000E *d, void *descr)
-{
-    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
-    uint32_t tail = e1000e_macreg_read(d, E1000E_TDT);
-    uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN;
-
-    qtest_memwrite(d_pci->pci_dev.bus->qts, d->tx_ring + tail * E1000E_TXD_LEN,
-                   descr, E1000E_TXD_LEN);
-    e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len);
-
-    /* Read WB data for the packet transmitted */
-    qtest_memread(d_pci->pci_dev.bus->qts, d->tx_ring + tail * E1000E_TXD_LEN,
-                  descr, E1000E_TXD_LEN);
-}
-
-void e1000e_rx_ring_push(QE1000E *d, void *descr)
-{
-    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
-    uint32_t tail = e1000e_macreg_read(d, E1000E_RDT);
-    uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN;
-
-    qtest_memwrite(d_pci->pci_dev.bus->qts, d->rx_ring + tail * E1000E_RXD_LEN,
-                   descr, E1000E_RXD_LEN);
-    e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len);
-
-    /* Read WB data for the packet received */
-    qtest_memread(d_pci->pci_dev.bus->qts, d->rx_ring + tail * E1000E_RXD_LEN,
-                  descr, E1000E_RXD_LEN);
-}
-
-static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data)
-{
-    QPCIDevice *res = data;
-    memcpy(res, dev, sizeof(QPCIDevice));
-    g_free(dev);
-}
-
-void e1000e_wait_isr(QE1000E *d, uint16_t msg_id)
-{
-    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
-    guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
-
-    do {
-        if (qpci_msix_pending(&d_pci->pci_dev, msg_id)) {
-            return;
-        }
-        qtest_clock_step(d_pci->pci_dev.bus->qts, 10000);
-    } while (g_get_monotonic_time() < end_time);
-
-    g_error("Timeout expired");
-}
-
-static void e1000e_pci_destructor(QOSGraphObject *obj)
-{
-    QE1000E_PCI *epci = (QE1000E_PCI *) obj;
-    qpci_iounmap(&epci->pci_dev, epci->mac_regs);
-    qpci_msix_disable(&epci->pci_dev);
-}
-
-static void e1000e_pci_start_hw(QOSGraphObject *obj)
-{
-    QE1000E_PCI *d = (QE1000E_PCI *) obj;
-    uint32_t val;
-
-    /* Enable the device */
-    qpci_device_enable(&d->pci_dev);
-
-    /* Reset the device */
-    val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL);
-    e1000e_macreg_write(&d->e1000e, E1000E_CTRL, val | E1000E_CTRL_RESET);
-
-    /* Enable and configure MSI-X */
-    qpci_msix_enable(&d->pci_dev);
-    e1000e_macreg_write(&d->e1000e, E1000E_IVAR, E1000E_IVAR_TEST_CFG);
-
-    /* Check the device status - link and speed */
-    val = e1000e_macreg_read(&d->e1000e, E1000E_STATUS);
-    g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000),
-        ==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000);
-
-    /* Initialize TX/RX logic */
-    e1000e_macreg_write(&d->e1000e, E1000E_RCTL, 0);
-    e1000e_macreg_write(&d->e1000e, E1000E_TCTL, 0);
-
-    /* Notify the device that the driver is ready */
-    val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL_EXT);
-    e1000e_macreg_write(&d->e1000e, E1000E_CTRL_EXT,
-        val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW);
-
-    e1000e_macreg_write(&d->e1000e, E1000E_TDBAL,
-                           (uint32_t) d->e1000e.tx_ring);
-    e1000e_macreg_write(&d->e1000e, E1000E_TDBAH,
-                           (uint32_t) (d->e1000e.tx_ring >> 32));
-    e1000e_macreg_write(&d->e1000e, E1000E_TDLEN, E1000E_RING_LEN);
-    e1000e_macreg_write(&d->e1000e, E1000E_TDT, 0);
-    e1000e_macreg_write(&d->e1000e, E1000E_TDH, 0);
-
-    /* Enable transmit */
-    e1000e_macreg_write(&d->e1000e, E1000E_TCTL, E1000E_TCTL_EN);
-    e1000e_macreg_write(&d->e1000e, E1000E_RDBAL,
-                           (uint32_t)d->e1000e.rx_ring);
-    e1000e_macreg_write(&d->e1000e, E1000E_RDBAH,
-                           (uint32_t)(d->e1000e.rx_ring >> 32));
-    e1000e_macreg_write(&d->e1000e, E1000E_RDLEN, E1000E_RING_LEN);
-    e1000e_macreg_write(&d->e1000e, E1000E_RDT, 0);
-    e1000e_macreg_write(&d->e1000e, E1000E_RDH, 0);
-
-    /* Enable receive */
-    e1000e_macreg_write(&d->e1000e, E1000E_RFCTL, E1000E_RFCTL_EXTEN);
-    e1000e_macreg_write(&d->e1000e, E1000E_RCTL, E1000E_RCTL_EN  |
-                                        E1000E_RCTL_UPE |
-                                        E1000E_RCTL_MPE);
-
-    /* Enable all interrupts */
-    e1000e_macreg_write(&d->e1000e, E1000E_IMS, 0xFFFFFFFF);
-
-}
-
-static void *e1000e_pci_get_driver(void *obj, const char *interface)
-{
-    QE1000E_PCI *epci = obj;
-    if (!g_strcmp0(interface, "e1000e-if")) {
-        return &epci->e1000e;
-    }
-
-    /* implicit contains */
-    if (!g_strcmp0(interface, "pci-device")) {
-        return &epci->pci_dev;
-    }
-
-    fprintf(stderr, "%s not present in e1000e\n", interface);
-    g_assert_not_reached();
-}
-
-static void *e1000e_pci_create(void *pci_bus, QGuestAllocator *alloc,
-                               void *addr)
-{
-    QE1000E_PCI *d = g_new0(QE1000E_PCI, 1);
-    QPCIBus *bus = pci_bus;
-    QPCIAddress *address = addr;
-
-    qpci_device_foreach(bus, address->vendor_id, address->device_id,
-                        e1000e_foreach_callback, &d->pci_dev);
-
-    /* Map BAR0 (mac registers) */
-    d->mac_regs = qpci_iomap(&d->pci_dev, 0, NULL);
-
-    /* Allocate and setup TX ring */
-    d->e1000e.tx_ring = guest_alloc(alloc, E1000E_RING_LEN);
-    g_assert(d->e1000e.tx_ring != 0);
-
-    /* Allocate and setup RX ring */
-    d->e1000e.rx_ring = guest_alloc(alloc, E1000E_RING_LEN);
-    g_assert(d->e1000e.rx_ring != 0);
-
-    d->obj.get_driver = e1000e_pci_get_driver;
-    d->obj.start_hw = e1000e_pci_start_hw;
-    d->obj.destructor = e1000e_pci_destructor;
-
-    return &d->obj;
-}
-
-static void e1000e_register_nodes(void)
-{
-    QPCIAddress addr = {
-        .vendor_id = 0x8086,
-        .device_id = 0x10D3,
-    };
-
-    /* FIXME: every test using this node needs to setup a -netdev socket,id=hs0
-     * otherwise QEMU is not going to start */
-    QOSGraphEdgeOptions opts = {
-        .extra_device_opts = "netdev=hs0",
-    };
-    add_qpci_address(&opts, &addr);
-
-    qos_node_create_driver("e1000e", e1000e_pci_create);
-    qos_node_consumes("e1000e", "pci-bus", &opts);
-}
-
-libqos_init(e1000e_register_nodes);
diff --git a/tests/libqos/e1000e.h b/tests/libqos/e1000e.h
deleted file mode 100644 (file)
index dc4ab10..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef QGRAPH_E1000E_H
-#define QGRAPH_E1000E_H
-
-#include "libqos/qgraph.h"
-#include "pci.h"
-
-#define E1000E_RX0_MSG_ID           (0)
-#define E1000E_TX0_MSG_ID           (1)
-#define E1000E_OTHER_MSG_ID         (2)
-
-#define E1000E_TDLEN    (0x3808)
-#define E1000E_TDT      (0x3818)
-#define E1000E_RDLEN    (0x2808)
-#define E1000E_RDT      (0x2818)
-
-typedef struct QE1000E QE1000E;
-typedef struct QE1000E_PCI QE1000E_PCI;
-
-struct QE1000E {
-    uint64_t tx_ring;
-    uint64_t rx_ring;
-};
-
-struct QE1000E_PCI {
-    QOSGraphObject obj;
-    QPCIDevice pci_dev;
-    QPCIBar mac_regs;
-    QE1000E e1000e;
-};
-
-void e1000e_wait_isr(QE1000E *d, uint16_t msg_id);
-void e1000e_tx_ring_push(QE1000E *d, void *descr);
-void e1000e_rx_ring_push(QE1000E *d, void *descr);
-
-#endif
diff --git a/tests/libqos/fw_cfg.c b/tests/libqos/fw_cfg.c
deleted file mode 100644 (file)
index 1f46258..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * libqos fw_cfg support
- *
- * Copyright IBM, Corp. 2012-2013
- * Copyright (C) 2013 Red Hat Inc.
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *  Markus Armbruster <armbru@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqos/fw_cfg.h"
-#include "libqtest.h"
-#include "qemu/bswap.h"
-#include "hw/nvram/fw_cfg.h"
-
-void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
-{
-    fw_cfg->select(fw_cfg, key);
-}
-
-void qfw_cfg_read_data(QFWCFG *fw_cfg, void *data, size_t len)
-{
-    fw_cfg->read(fw_cfg, data, len);
-}
-
-void qfw_cfg_get(QFWCFG *fw_cfg, uint16_t key, void *data, size_t len)
-{
-    qfw_cfg_select(fw_cfg, key);
-    qfw_cfg_read_data(fw_cfg, data, len);
-}
-
-uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key)
-{
-    uint16_t value;
-    qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
-    return le16_to_cpu(value);
-}
-
-uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key)
-{
-    uint32_t value;
-    qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
-    return le32_to_cpu(value);
-}
-
-uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key)
-{
-    uint64_t value;
-    qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
-    return le64_to_cpu(value);
-}
-
-static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
-{
-    qtest_writew(fw_cfg->qts, fw_cfg->base, key);
-}
-
-/*
- * The caller need check the return value. When the return value is
- * nonzero, it means that some bytes have been transferred.
- *
- * If the fw_cfg file in question is smaller than the allocated & passed-in
- * buffer, then the buffer has been populated only in part.
- *
- * If the fw_cfg file in question is larger than the passed-in
- * buffer, then the return value explains how much room would have been
- * necessary in total. And, while the caller's buffer has been fully
- * populated, it has received only a starting slice of the fw_cfg file.
- */
-size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
-                      void *data, size_t buflen)
-{
-    uint32_t count;
-    uint32_t i;
-    unsigned char *filesbuf = NULL;
-    size_t dsize;
-    FWCfgFile *pdir_entry;
-    size_t filesize = 0;
-
-    qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count));
-    count = be32_to_cpu(count);
-    dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file);
-    filesbuf = g_malloc(dsize);
-    qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize);
-    pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t));
-    for (i = 0; i < count; ++i, ++pdir_entry) {
-        if (!strcmp(pdir_entry->name, filename)) {
-            uint32_t len = be32_to_cpu(pdir_entry->size);
-            uint16_t sel = be16_to_cpu(pdir_entry->select);
-            filesize = len;
-            if (len > buflen) {
-                len = buflen;
-            }
-            qfw_cfg_get(fw_cfg, sel, data, len);
-            break;
-        }
-    }
-    g_free(filesbuf);
-    return filesize;
-}
-
-static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
-{
-    uint8_t *ptr = data;
-    int i;
-
-    for (i = 0; i < len; i++) {
-        ptr[i] = qtest_readb(fw_cfg->qts, fw_cfg->base + 2);
-    }
-}
-
-QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base)
-{
-    QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
-
-    fw_cfg->base = base;
-    fw_cfg->qts = qts;
-    fw_cfg->select = mm_fw_cfg_select;
-    fw_cfg->read = mm_fw_cfg_read;
-
-    return fw_cfg;
-}
-
-void mm_fw_cfg_uninit(QFWCFG *fw_cfg)
-{
-    g_free(fw_cfg);
-}
-
-static void io_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
-{
-    qtest_outw(fw_cfg->qts, fw_cfg->base, key);
-}
-
-static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
-{
-    uint8_t *ptr = data;
-    int i;
-
-    for (i = 0; i < len; i++) {
-        ptr[i] = qtest_inb(fw_cfg->qts, fw_cfg->base + 1);
-    }
-}
-
-QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base)
-{
-    QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
-
-    fw_cfg->base = base;
-    fw_cfg->qts = qts;
-    fw_cfg->select = io_fw_cfg_select;
-    fw_cfg->read = io_fw_cfg_read;
-
-    return fw_cfg;
-}
-
-void io_fw_cfg_uninit(QFWCFG *fw_cfg)
-{
-    g_free(fw_cfg);
-}
diff --git a/tests/libqos/fw_cfg.h b/tests/libqos/fw_cfg.h
deleted file mode 100644 (file)
index 13325cc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * libqos fw_cfg support
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_FW_CFG_H
-#define LIBQOS_FW_CFG_H
-
-#include "libqtest.h"
-
-typedef struct QFWCFG QFWCFG;
-
-struct QFWCFG
-{
-    uint64_t base;
-    QTestState *qts;
-    void (*select)(QFWCFG *fw_cfg, uint16_t key);
-    void (*read)(QFWCFG *fw_cfg, void *data, size_t len);
-};
-
-void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key);
-void qfw_cfg_read_data(QFWCFG *fw_cfg, void *data, size_t len);
-void qfw_cfg_get(QFWCFG *fw_cfg, uint16_t key, void *data, size_t len);
-uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key);
-uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key);
-uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key);
-size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
-                        void *data, size_t buflen);
-
-QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base);
-void mm_fw_cfg_uninit(QFWCFG *fw_cfg);
-QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base);
-void io_fw_cfg_uninit(QFWCFG *fw_cfg);
-
-static inline QFWCFG *pc_fw_cfg_init(QTestState *qts)
-{
-    return io_fw_cfg_init(qts, 0x510);
-}
-
-static inline void pc_fw_cfg_uninit(QFWCFG *fw_cfg)
-{
-    io_fw_cfg_uninit(fw_cfg);
-}
-
-#endif
diff --git a/tests/libqos/i2c-imx.c b/tests/libqos/i2c-imx.c
deleted file mode 100644 (file)
index f33ece5..0000000
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * QTest i.MX I2C driver
- *
- * Copyright (c) 2013 Jean-Christophe Dubois
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License as published by the
- *  Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful, but WITHOUT
- *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- *  for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "qemu/osdep.h"
-#include "libqos/i2c.h"
-
-
-#include "libqtest.h"
-
-#include "hw/i2c/imx_i2c.h"
-
-enum IMXI2CDirection {
-    IMX_I2C_READ,
-    IMX_I2C_WRITE,
-};
-
-static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr,
-                                   enum IMXI2CDirection direction)
-{
-    qtest_writeb(s->parent.qts, s->addr + I2DR_ADDR,
-                 (addr << 1) | (direction == IMX_I2C_READ ? 1 : 0));
-}
-
-static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr,
-                         const uint8_t *buf, uint16_t len)
-{
-    IMXI2C *s = container_of(i2c, IMXI2C, parent);
-    uint8_t data;
-    uint8_t status;
-    uint16_t size = 0;
-
-    if (!len) {
-        return;
-    }
-
-    /* set the bus for write */
-    data = I2CR_IEN |
-           I2CR_IIEN |
-           I2CR_MSTA |
-           I2CR_MTX |
-           I2CR_TXAK;
-
-    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IBB) != 0);
-
-    /* set the slave address */
-    imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IIF) != 0);
-    g_assert((status & I2SR_RXAK) == 0);
-
-    /* ack the interrupt */
-    qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IIF) == 0);
-
-    while (size < len) {
-        /* check we are still busy */
-        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-        g_assert((status & I2SR_IBB) != 0);
-
-        /* write the data */
-        qtest_writeb(i2c->qts, s->addr + I2DR_ADDR, buf[size]);
-        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-        g_assert((status & I2SR_IIF) != 0);
-        g_assert((status & I2SR_RXAK) == 0);
-
-        /* ack the interrupt */
-        qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
-        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-        g_assert((status & I2SR_IIF) == 0);
-
-        size++;
-    }
-
-    /* release the bus */
-    data &= ~(I2CR_MSTA | I2CR_MTX);
-    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IBB) == 0);
-}
-
-static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr,
-                         uint8_t *buf, uint16_t len)
-{
-    IMXI2C *s = container_of(i2c, IMXI2C, parent);
-    uint8_t data;
-    uint8_t status;
-    uint16_t size = 0;
-
-    if (!len) {
-        return;
-    }
-
-    /* set the bus for write */
-    data = I2CR_IEN |
-           I2CR_IIEN |
-           I2CR_MSTA |
-           I2CR_MTX |
-           I2CR_TXAK;
-
-    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IBB) != 0);
-
-    /* set the slave address */
-    imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IIF) != 0);
-    g_assert((status & I2SR_RXAK) == 0);
-
-    /* ack the interrupt */
-    qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IIF) == 0);
-
-    /* set the bus for read */
-    data &= ~I2CR_MTX;
-    /* if only one byte don't ack */
-    if (len != 1) {
-        data &= ~I2CR_TXAK;
-    }
-    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IBB) != 0);
-
-    /* dummy read */
-    qtest_readb(i2c->qts, s->addr + I2DR_ADDR);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IIF) != 0);
-
-    /* ack the interrupt */
-    qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IIF) == 0);
-
-    while (size < len) {
-        /* check we are still busy */
-        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-        g_assert((status & I2SR_IBB) != 0);
-
-        if (size == (len - 1)) {
-            /* stop the read transaction */
-            data &= ~(I2CR_MSTA | I2CR_MTX);
-        } else {
-            /* ack the data read */
-            data |= I2CR_TXAK;
-        }
-        qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
-
-        /* read the data */
-        buf[size] = qtest_readb(i2c->qts, s->addr + I2DR_ADDR);
-
-        if (size != (len - 1)) {
-            status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-            g_assert((status & I2SR_IIF) != 0);
-
-            /* ack the interrupt */
-            qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
-        }
-
-        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-        g_assert((status & I2SR_IIF) == 0);
-
-        size++;
-    }
-
-    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
-    g_assert((status & I2SR_IBB) == 0);
-}
-
-static void *imx_i2c_get_driver(void *obj, const char *interface)
-{
-    IMXI2C *s = obj;
-    if (!g_strcmp0(interface, "i2c-bus")) {
-        return &s->parent;
-    }
-    fprintf(stderr, "%s not present in imx-i2c\n", interface);
-    g_assert_not_reached();
-}
-
-void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr)
-{
-    s->addr = addr;
-
-    s->obj.get_driver = imx_i2c_get_driver;
-
-    s->parent.send = imx_i2c_send;
-    s->parent.recv = imx_i2c_recv;
-    s->parent.qts = qts;
-}
-
-static void imx_i2c_register_nodes(void)
-{
-    qos_node_create_driver("imx.i2c", NULL);
-    qos_node_produces("imx.i2c", "i2c-bus");
-}
-
-libqos_init(imx_i2c_register_nodes);
diff --git a/tests/libqos/i2c-omap.c b/tests/libqos/i2c-omap.c
deleted file mode 100644 (file)
index 9ae8214..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * QTest I2C driver
- *
- * Copyright (c) 2012 Andreas Färber
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-#include "qemu/osdep.h"
-#include "libqos/i2c.h"
-
-
-#include "qemu/bswap.h"
-#include "libqtest.h"
-
-enum OMAPI2CRegisters {
-    OMAP_I2C_REV  = 0x00,
-    OMAP_I2C_STAT = 0x08,
-    OMAP_I2C_CNT  = 0x18,
-    OMAP_I2C_DATA = 0x1c,
-    OMAP_I2C_CON  = 0x24,
-    OMAP_I2C_SA   = 0x2c,
-};
-
-enum OMAPI2CSTATBits {
-    OMAP_I2C_STAT_NACK = 1 << 1,
-    OMAP_I2C_STAT_ARDY = 1 << 2,
-    OMAP_I2C_STAT_RRDY = 1 << 3,
-    OMAP_I2C_STAT_XRDY = 1 << 4,
-    OMAP_I2C_STAT_ROVR = 1 << 11,
-    OMAP_I2C_STAT_SBD  = 1 << 15,
-};
-
-enum OMAPI2CCONBits {
-    OMAP_I2C_CON_STT    = 1 << 0,
-    OMAP_I2C_CON_STP    = 1 << 1,
-    OMAP_I2C_CON_TRX    = 1 << 9,
-    OMAP_I2C_CON_MST    = 1 << 10,
-    OMAP_I2C_CON_BE     = 1 << 14,
-    OMAP_I2C_CON_I2C_EN = 1 << 15,
-};
-
-
-static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr)
-{
-    uint16_t data = addr;
-
-    qtest_writew(s->parent.qts, s->addr + OMAP_I2C_SA, data);
-    data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_SA);
-    g_assert_cmphex(data, ==, addr);
-}
-
-static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr,
-                          const uint8_t *buf, uint16_t len)
-{
-    OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
-    uint16_t data;
-
-    omap_i2c_set_slave_addr(s, addr);
-
-    data = len;
-    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data);
-
-    data = OMAP_I2C_CON_I2C_EN |
-           OMAP_I2C_CON_TRX |
-           OMAP_I2C_CON_MST |
-           OMAP_I2C_CON_STT |
-           OMAP_I2C_CON_STP;
-    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data);
-    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
-    g_assert((data & OMAP_I2C_CON_STP) != 0);
-
-    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
-    g_assert((data & OMAP_I2C_STAT_NACK) == 0);
-
-    while (len > 1) {
-        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
-        g_assert((data & OMAP_I2C_STAT_XRDY) != 0);
-
-        data = buf[0] | ((uint16_t)buf[1] << 8);
-        qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data);
-        buf = (uint8_t *)buf + 2;
-        len -= 2;
-    }
-    if (len == 1) {
-        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
-        g_assert((data & OMAP_I2C_STAT_XRDY) != 0);
-
-        data = buf[0];
-        qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data);
-    }
-
-    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
-    g_assert((data & OMAP_I2C_CON_STP) == 0);
-}
-
-static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr,
-                          uint8_t *buf, uint16_t len)
-{
-    OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
-    uint16_t data, stat;
-    uint16_t orig_len = len;
-
-    omap_i2c_set_slave_addr(s, addr);
-
-    data = len;
-    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data);
-
-    data = OMAP_I2C_CON_I2C_EN |
-           OMAP_I2C_CON_MST |
-           OMAP_I2C_CON_STT |
-           OMAP_I2C_CON_STP;
-    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data);
-
-    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
-    g_assert((data & OMAP_I2C_STAT_NACK) == 0);
-
-    while (len > 0) {
-        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
-        if (len <= 4) {
-            g_assert((data & OMAP_I2C_CON_STP) == 0);
-
-            data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
-            g_assert_cmpuint(data, ==, orig_len);
-        } else {
-            g_assert((data & OMAP_I2C_CON_STP) != 0);
-
-            data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
-            g_assert_cmpuint(data, ==, len - 4);
-        }
-
-        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
-        g_assert((data & OMAP_I2C_STAT_RRDY) != 0);
-        g_assert((data & OMAP_I2C_STAT_ROVR) == 0);
-
-        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_DATA);
-
-        stat = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
-
-        if (unlikely(len == 1)) {
-            g_assert((stat & OMAP_I2C_STAT_SBD) != 0);
-
-            buf[0] = data & 0xff;
-            buf++;
-            len--;
-        } else {
-            buf[0] = data & 0xff;
-            buf[1] = data >> 8;
-            buf += 2;
-            len -= 2;
-        }
-    }
-
-    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
-    g_assert((data & OMAP_I2C_CON_STP) == 0);
-}
-
-static void *omap_i2c_get_driver(void *obj, const char *interface)
-{
-    OMAPI2C *s = obj;
-    if (!g_strcmp0(interface, "i2c-bus")) {
-        return &s->parent;
-    }
-    fprintf(stderr, "%s not present in omap_i2c\n", interface);
-    g_assert_not_reached();
-}
-
-static void omap_i2c_start_hw(QOSGraphObject *object)
-{
-    OMAPI2C *s = (OMAPI2C *) object;
-    uint16_t data;
-
-    /* verify the mmio address by looking for a known signature */
-    data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_REV);
-    g_assert_cmphex(data, ==, 0x34);
-}
-
-void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr)
-{
-    s->addr = addr;
-
-    s->obj.get_driver = omap_i2c_get_driver;
-    s->obj.start_hw = omap_i2c_start_hw;
-
-    s->parent.send = omap_i2c_send;
-    s->parent.recv = omap_i2c_recv;
-    s->parent.qts = qts;
-}
-
-static void omap_i2c_register_nodes(void)
-{
-    qos_node_create_driver("omap_i2c", NULL);
-    qos_node_produces("omap_i2c", "i2c-bus");
-}
-
-libqos_init(omap_i2c_register_nodes);
diff --git a/tests/libqos/i2c.c b/tests/libqos/i2c.c
deleted file mode 100644 (file)
index 156114e..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * QTest I2C driver
- *
- * Copyright (c) 2012 Andreas Färber
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-#include "qemu/osdep.h"
-#include "libqos/i2c.h"
-#include "libqtest.h"
-
-void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
-{
-    i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len);
-}
-
-void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
-{
-    i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len);
-}
-
-void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg,
-                    uint8_t *buf, uint16_t len)
-{
-    i2c_send(i2cdev, &reg, 1);
-    i2c_recv(i2cdev, buf, len);
-}
-
-void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
-                     const uint8_t *buf, uint16_t len)
-{
-    uint8_t *cmd = g_malloc(len + 1);
-    cmd[0] = reg;
-    memcpy(&cmd[1], buf, len);
-    i2c_send(i2cdev, cmd, len + 1);
-    g_free(cmd);
-}
-
-uint8_t i2c_get8(QI2CDevice *i2cdev, uint8_t reg)
-{
-    uint8_t resp[1];
-    i2c_read_block(i2cdev, reg, resp, sizeof(resp));
-    return resp[0];
-}
-
-uint16_t i2c_get16(QI2CDevice *i2cdev, uint8_t reg)
-{
-    uint8_t resp[2];
-    i2c_read_block(i2cdev, reg, resp, sizeof(resp));
-    return (resp[0] << 8) | resp[1];
-}
-
-void i2c_set8(QI2CDevice *i2cdev, uint8_t reg, uint8_t value)
-{
-    i2c_write_block(i2cdev, reg, &value, 1);
-}
-
-void i2c_set16(QI2CDevice *i2cdev, uint8_t reg, uint16_t value)
-{
-    uint8_t data[2];
-
-    data[0] = value >> 8;
-    data[1] = value & 255;
-    i2c_write_block(i2cdev, reg, data, sizeof(data));
-}
-
-void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr)
-{
-    QI2CDevice *i2cdev = g_new0(QI2CDevice, 1);
-
-    i2cdev->bus = i2c_bus;
-    if (addr) {
-        i2cdev->addr = ((QI2CAddress *)addr)->addr;
-    }
-    return &i2cdev->obj;
-}
-
-void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr)
-{
-    g_assert(addr);
-
-    opts->arg = addr;
-    opts->size_arg = sizeof(QI2CAddress);
-}
diff --git a/tests/libqos/i2c.h b/tests/libqos/i2c.h
deleted file mode 100644 (file)
index 945b65b..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * I2C libqos
- *
- * Copyright (c) 2012 Andreas Färber
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-#ifndef LIBQOS_I2C_H
-#define LIBQOS_I2C_H
-
-#include "libqtest.h"
-#include "libqos/qgraph.h"
-
-typedef struct I2CAdapter I2CAdapter;
-struct I2CAdapter {
-    void (*send)(I2CAdapter *adapter, uint8_t addr,
-                 const uint8_t *buf, uint16_t len);
-    void (*recv)(I2CAdapter *adapter, uint8_t addr,
-                 uint8_t *buf, uint16_t len);
-
-    QTestState *qts;
-};
-
-typedef struct QI2CAddress QI2CAddress;
-struct QI2CAddress {
-    uint8_t addr;
-};
-
-typedef struct QI2CDevice QI2CDevice;
-struct QI2CDevice {
-    /*
-     * For now, all devices are simple enough that there is no need for
-     * them to define their own constructor and get_driver functions.
-     * Therefore, QOSGraphObject is included directly in QI2CDevice;
-     * the tests expect to get a QI2CDevice rather than doing something
-     * like obj->get_driver("i2c-device").
-     *
-     * In fact there is no i2c-device interface even, because there are
-     * no generic I2C tests).
-     */
-    QOSGraphObject obj;
-    I2CAdapter *bus;
-    uint8_t addr;
-};
-
-void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr);
-void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr);
-
-void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
-void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
-
-void i2c_read_block(QI2CDevice *dev, uint8_t reg,
-                    uint8_t *buf, uint16_t len);
-void i2c_write_block(QI2CDevice *dev, uint8_t reg,
-                     const uint8_t *buf, uint16_t len);
-uint8_t i2c_get8(QI2CDevice *dev, uint8_t reg);
-uint16_t i2c_get16(QI2CDevice *dev, uint8_t reg);
-void i2c_set8(QI2CDevice *dev, uint8_t reg, uint8_t value);
-void i2c_set16(QI2CDevice *dev, uint8_t reg, uint16_t value);
-
-/* i2c-omap.c */
-typedef struct OMAPI2C {
-    QOSGraphObject obj;
-    I2CAdapter parent;
-
-    uint64_t addr;
-} OMAPI2C;
-
-void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr);
-
-/* i2c-imx.c */
-typedef struct IMXI2C {
-    QOSGraphObject obj;
-    I2CAdapter parent;
-
-    uint64_t addr;
-} IMXI2C;
-
-void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr);
-
-#endif
diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c
deleted file mode 100644 (file)
index d04abc5..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#include "qemu/osdep.h"
-#include "libqos/libqos-pc.h"
-#include "libqos/malloc-pc.h"
-#include "libqos/pci-pc.h"
-
-static QOSOps qos_ops = {
-    .alloc_init = pc_alloc_init,
-    .qpci_new = qpci_new_pc,
-    .qpci_free = qpci_free_pc,
-    .shutdown = qtest_pc_shutdown,
-};
-
-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 *qs;
-    va_list ap;
-
-    va_start(ap, cmdline_fmt);
-    qs = qtest_vboot(&qos_ops, cmdline_fmt, ap);
-    va_end(ap);
-
-    qtest_irq_intercept_in(qs->qts, "ioapic");
-
-    return qs;
-}
-
-void qtest_pc_shutdown(QOSState *qs)
-{
-    return qtest_common_shutdown(qs);
-}
diff --git a/tests/libqos/libqos-pc.h b/tests/libqos/libqos-pc.h
deleted file mode 100644 (file)
index a0e4c45..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef LIBQOS_PC_H
-#define LIBQOS_PC_H
-
-#include "libqos/libqos.h"
-
-QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap);
-QOSState *qtest_pc_boot(const char *cmdline_fmt, ...);
-void qtest_pc_shutdown(QOSState *qs);
-
-#endif
diff --git a/tests/libqos/libqos-spapr.c b/tests/libqos/libqos-spapr.c
deleted file mode 100644 (file)
index 8766d54..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "qemu/osdep.h"
-#include "libqos/libqos-spapr.h"
-#include "libqos/malloc-spapr.h"
-#include "libqos/pci-spapr.h"
-
-static QOSOps qos_ops = {
-    .alloc_init = spapr_alloc_init,
-    .qpci_new = qpci_new_spapr,
-    .qpci_free = qpci_free_spapr,
-    .shutdown = qtest_spapr_shutdown,
-};
-
-QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap)
-{
-    return qtest_vboot(&qos_ops, cmdline_fmt, ap);
-}
-
-QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...)
-{
-    QOSState *qs;
-    va_list ap;
-
-    va_start(ap, cmdline_fmt);
-    qs = qtest_vboot(&qos_ops, cmdline_fmt, ap);
-    va_end(ap);
-
-    return qs;
-}
-
-void qtest_spapr_shutdown(QOSState *qs)
-{
-    return qtest_common_shutdown(qs);
-}
diff --git a/tests/libqos/libqos-spapr.h b/tests/libqos/libqos-spapr.h
deleted file mode 100644 (file)
index dcb5c43..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef LIBQOS_SPAPR_H
-#define LIBQOS_SPAPR_H
-
-#include "libqos/libqos.h"
-
-QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap);
-QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...);
-void qtest_spapr_shutdown(QOSState *qs);
-
-#endif
diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c
deleted file mode 100644 (file)
index f229eb2..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-#include "qemu/osdep.h"
-#include <sys/wait.h>
-
-#include "libqtest.h"
-#include "libqos/libqos.h"
-#include "libqos/pci.h"
-#include "qapi/qmp/qdict.h"
-
-/*** Test Setup & Teardown ***/
-
-/**
- * Launch QEMU with the given command line,
- * and then set up interrupts and our guest malloc interface.
- * Never returns NULL:
- * Terminates the application in case an error is encountered.
- */
-QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap)
-{
-    char *cmdline;
-
-    QOSState *qs = g_new0(QOSState, 1);
-
-    cmdline = g_strdup_vprintf(cmdline_fmt, ap);
-    qs->qts = qtest_init(cmdline);
-    qs->ops = ops;
-    if (ops) {
-        ops->alloc_init(&qs->alloc, qs->qts, ALLOC_NO_FLAGS);
-        qs->pcibus = ops->qpci_new(qs->qts, &qs->alloc);
-    }
-
-    g_free(cmdline);
-    return qs;
-}
-
-/**
- * Launch QEMU with the given command line,
- * and then set up interrupts and our guest malloc interface.
- */
-QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...)
-{
-    QOSState *qs;
-    va_list ap;
-
-    va_start(ap, cmdline_fmt);
-    qs = qtest_vboot(ops, cmdline_fmt, ap);
-    va_end(ap);
-
-    return qs;
-}
-
-/**
- * Tear down the QEMU instance.
- */
-void qtest_common_shutdown(QOSState *qs)
-{
-    if (qs->ops) {
-        if (qs->pcibus && qs->ops->qpci_free) {
-            qs->ops->qpci_free(qs->pcibus);
-            qs->pcibus = NULL;
-        }
-    }
-    alloc_destroy(&qs->alloc);
-    qtest_quit(qs->qts);
-    g_free(qs);
-}
-
-void qtest_shutdown(QOSState *qs)
-{
-    if (qs->ops && qs->ops->shutdown) {
-        qs->ops->shutdown(qs);
-    } else {
-        qtest_common_shutdown(qs);
-    }
-}
-
-static QDict *qmp_execute(QTestState *qts, const char *command)
-{
-    return qtest_qmp(qts, "{ 'execute': %s }", command);
-}
-
-void migrate(QOSState *from, QOSState *to, const char *uri)
-{
-    const char *st;
-    QDict *rsp, *sub;
-    bool running;
-
-    /* Is the machine currently running? */
-    rsp = qmp_execute(from->qts, "query-status");
-    g_assert(qdict_haskey(rsp, "return"));
-    sub = qdict_get_qdict(rsp, "return");
-    g_assert(qdict_haskey(sub, "running"));
-    running = qdict_get_bool(sub, "running");
-    qobject_unref(rsp);
-
-    /* Issue the migrate command. */
-    rsp = qtest_qmp(from->qts,
-                    "{ 'execute': 'migrate', 'arguments': { 'uri': %s }}",
-                    uri);
-    g_assert(qdict_haskey(rsp, "return"));
-    qobject_unref(rsp);
-
-    /* Wait for STOP event, but only if we were running: */
-    if (running) {
-        qtest_qmp_eventwait(from->qts, "STOP");
-    }
-
-    /* If we were running, we can wait for an event. */
-    if (running) {
-        migrate_allocator(&from->alloc, &to->alloc);
-        qtest_qmp_eventwait(to->qts, "RESUME");
-        return;
-    }
-
-    /* Otherwise, we need to wait: poll until migration is completed. */
-    while (1) {
-        rsp = qmp_execute(from->qts, "query-migrate");
-        g_assert(qdict_haskey(rsp, "return"));
-        sub = qdict_get_qdict(rsp, "return");
-        g_assert(qdict_haskey(sub, "status"));
-        st = qdict_get_str(sub, "status");
-
-        /* "setup", "active", "completed", "failed", "cancelled" */
-        if (strcmp(st, "completed") == 0) {
-            qobject_unref(rsp);
-            break;
-        }
-
-        if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)
-            || (strcmp(st, "wait-unplug") == 0)) {
-            qobject_unref(rsp);
-            g_usleep(5000);
-            continue;
-        }
-
-        fprintf(stderr, "Migration did not complete, status: %s\n", st);
-        g_assert_not_reached();
-    }
-
-    migrate_allocator(&from->alloc, &to->alloc);
-}
-
-bool have_qemu_img(void)
-{
-    char *rpath;
-    const char *path = getenv("QTEST_QEMU_IMG");
-    if (!path) {
-        return false;
-    }
-
-    rpath = realpath(path, NULL);
-    if (!rpath) {
-        return false;
-    } else {
-        free(rpath);
-        return true;
-    }
-}
-
-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 *qemu_img_abs_path;
-
-    qemu_img_path = getenv("QTEST_QEMU_IMG");
-    g_assert(qemu_img_path);
-    qemu_img_abs_path = realpath(qemu_img_path, NULL);
-    g_assert(qemu_img_abs_path);
-
-    cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
-                          fmt, file, size_mb);
-    ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
-    if (err || !g_spawn_check_exit_status(rc, &err)) {
-        fprintf(stderr, "%s\n", err->message);
-        g_error_free(err);
-    }
-    g_assert(ret && !err);
-
-    g_free(out);
-    g_free(out2);
-    g_free(cli);
-    free(qemu_img_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);
-}
-
-void generate_pattern(void *buffer, size_t len, size_t cycle_len)
-{
-    int i, j;
-    unsigned char *tx = (unsigned char *)buffer;
-    unsigned char p;
-    size_t *sx;
-
-    /* Write an indicative pattern that varies and is unique per-cycle */
-    p = rand() % 256;
-    for (i = 0; i < len; i++) {
-        tx[i] = p++ % 256;
-        if (i % cycle_len == 0) {
-            p = rand() % 256;
-        }
-    }
-
-    /* force uniqueness by writing an id per-cycle */
-    for (i = 0; i < len / cycle_len; i++) {
-        j = i * cycle_len;
-        if (j + sizeof(*sx) <= len) {
-            sx = (size_t *)&tx[j];
-            *sx = i;
-        }
-    }
-}
diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h
deleted file mode 100644 (file)
index 8e971c2..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-#ifndef LIBQOS_H
-#define LIBQOS_H
-
-#include "libqtest.h"
-#include "libqos/pci.h"
-#include "libqos/malloc.h"
-
-typedef struct QOSState QOSState;
-
-typedef struct QOSOps {
-    void (*alloc_init)(QGuestAllocator *, QTestState *, QAllocOpts);
-    QPCIBus *(*qpci_new)(QTestState *qts, QGuestAllocator *alloc);
-    void (*qpci_free)(QPCIBus *bus);
-    void (*shutdown)(QOSState *);
-} QOSOps;
-
-struct QOSState {
-    QTestState *qts;
-    QGuestAllocator alloc;
-    QPCIBus *pcibus;
-    QOSOps *ops;
-};
-
-QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap);
-QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...);
-void qtest_common_shutdown(QOSState *qs);
-void qtest_shutdown(QOSState *qs);
-bool have_qemu_img(void);
-void mkimg(const char *file, const char *fmt, unsigned size_mb);
-void mkqcow2(const char *file, unsigned size_mb);
-void migrate(QOSState *from, QOSState *to, const char *uri);
-void prepare_blkdebug_script(const char *debug_fn, const char *event);
-void generate_pattern(void *buffer, size_t len, size_t cycle_len);
-
-static inline uint64_t qmalloc(QOSState *q, size_t bytes)
-{
-    return guest_alloc(&q->alloc, bytes);
-}
-
-static inline void qfree(QOSState *q, uint64_t addr)
-{
-    guest_free(&q->alloc, addr);
-}
-
-#endif
diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c
deleted file mode 100644 (file)
index 6f92ce4..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * libqos malloc support for PC
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqos/malloc-pc.h"
-#include "libqos/fw_cfg.h"
-
-#include "standard-headers/linux/qemu_fw_cfg.h"
-
-#include "qemu-common.h"
-
-#define PAGE_SIZE (4096)
-
-void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags)
-{
-    uint64_t ram_size;
-    QFWCFG *fw_cfg = pc_fw_cfg_init(qts);
-
-    ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE);
-    alloc_init(s, flags, 1 << 20, MIN(ram_size, 0xE0000000), PAGE_SIZE);
-
-    /* clean-up */
-    pc_fw_cfg_uninit(fw_cfg);
-}
diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h
deleted file mode 100644 (file)
index 21e75ae..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * libqos malloc support for PC
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_MALLOC_PC_H
-#define LIBQOS_MALLOC_PC_H
-
-#include "libqos/malloc.h"
-
-void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags);
-
-#endif
diff --git a/tests/libqos/malloc-spapr.c b/tests/libqos/malloc-spapr.c
deleted file mode 100644 (file)
index 2a6b7e3..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * libqos malloc support for SPAPR
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqos/malloc-spapr.h"
-
-#include "qemu-common.h"
-
-#define PAGE_SIZE 4096
-
-/* Memory must be a multiple of 256 MB,
- * so we have at least 256MB
- */
-#define SPAPR_MIN_SIZE 0x10000000
-
-void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags)
-{
-    alloc_init(s, flags, 1 << 20, SPAPR_MIN_SIZE, PAGE_SIZE);
-}
diff --git a/tests/libqos/malloc-spapr.h b/tests/libqos/malloc-spapr.h
deleted file mode 100644 (file)
index e5fe9bf..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * libqos malloc support for SPAPR
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_MALLOC_SPAPR_H
-#define LIBQOS_MALLOC_SPAPR_H
-
-#include "libqos/malloc.h"
-
-void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags);
-
-#endif
diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c
deleted file mode 100644 (file)
index 615422a..0000000
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * libqos malloc support
- *
- * Copyright (c) 2014
- *
- * Author:
- *  John Snow <jsnow@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqos/malloc.h"
-#include "qemu-common.h"
-#include "qemu/host-utils.h"
-
-typedef struct MemBlock {
-    QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME;
-    uint64_t size;
-    uint64_t addr;
-} MemBlock;
-
-#define DEFAULT_PAGE_SIZE 4096
-
-static void mlist_delete(MemList *list, MemBlock *node)
-{
-    g_assert(list && node);
-    QTAILQ_REMOVE(list, node, MLIST_ENTNAME);
-    g_free(node);
-}
-
-static MemBlock *mlist_find_key(MemList *head, uint64_t addr)
-{
-    MemBlock *node;
-    QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
-        if (node->addr == addr) {
-            return node;
-        }
-    }
-    return NULL;
-}
-
-static MemBlock *mlist_find_space(MemList *head, uint64_t size)
-{
-    MemBlock *node;
-
-    QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
-        if (node->size >= size) {
-            return node;
-        }
-    }
-    return NULL;
-}
-
-static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr)
-{
-    MemBlock *node;
-    g_assert(head && insr);
-
-    QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
-        if (insr->addr < node->addr) {
-            QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME);
-            return insr;
-        }
-    }
-
-    QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME);
-    return insr;
-}
-
-static inline uint64_t mlist_boundary(MemBlock *node)
-{
-    return node->size + node->addr;
-}
-
-static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right)
-{
-    g_assert(head && left && right);
-
-    left->size += right->size;
-    mlist_delete(head, right);
-    return left;
-}
-
-static void mlist_coalesce(MemList *head, MemBlock *node)
-{
-    g_assert(node);
-    MemBlock *left;
-    MemBlock *right;
-    char merge;
-
-    do {
-        merge = 0;
-        left = QTAILQ_PREV(node, MLIST_ENTNAME);
-        right = QTAILQ_NEXT(node, MLIST_ENTNAME);
-
-        /* clowns to the left of me */
-        if (left && mlist_boundary(left) == node->addr) {
-            node = mlist_join(head, left, node);
-            merge = 1;
-        }
-
-        /* jokers to the right */
-        if (right && mlist_boundary(node) == right->addr) {
-            node = mlist_join(head, node, right);
-            merge = 1;
-        }
-
-    } while (merge);
-}
-
-static MemBlock *mlist_new(uint64_t addr, uint64_t size)
-{
-    MemBlock *block;
-
-    if (!size) {
-        return NULL;
-    }
-    block = g_new0(MemBlock, 1);
-
-    block->addr = addr;
-    block->size = size;
-
-    return block;
-}
-
-static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode,
-                                                                uint64_t size)
-{
-    uint64_t addr;
-    MemBlock *usednode;
-
-    g_assert(freenode);
-    g_assert_cmpint(freenode->size, >=, size);
-
-    addr = freenode->addr;
-    if (freenode->size == size) {
-        /* re-use this freenode as our used node */
-        QTAILQ_REMOVE(s->free, freenode, MLIST_ENTNAME);
-        usednode = freenode;
-    } else {
-        /* adjust the free node and create a new used node */
-        freenode->addr += size;
-        freenode->size -= size;
-        usednode = mlist_new(addr, size);
-    }
-
-    mlist_sort_insert(s->used, usednode);
-    return addr;
-}
-
-/* To assert the correctness of the list.
- * Used only if ALLOC_PARANOID is set. */
-static void mlist_check(QGuestAllocator *s)
-{
-    MemBlock *node;
-    uint64_t addr = s->start > 0 ? s->start - 1 : 0;
-    uint64_t next = s->start;
-
-    QTAILQ_FOREACH(node, s->free, MLIST_ENTNAME) {
-        g_assert_cmpint(node->addr, >, addr);
-        g_assert_cmpint(node->addr, >=, next);
-        addr = node->addr;
-        next = node->addr + node->size;
-    }
-
-    addr = s->start > 0 ? s->start - 1 : 0;
-    next = s->start;
-    QTAILQ_FOREACH(node, s->used, MLIST_ENTNAME) {
-        g_assert_cmpint(node->addr, >, addr);
-        g_assert_cmpint(node->addr, >=, next);
-        addr = node->addr;
-        next = node->addr + node->size;
-    }
-}
-
-static uint64_t mlist_alloc(QGuestAllocator *s, uint64_t size)
-{
-    MemBlock *node;
-
-    node = mlist_find_space(s->free, size);
-    if (!node) {
-        fprintf(stderr, "Out of guest memory.\n");
-        g_assert_not_reached();
-    }
-    return mlist_fulfill(s, node, size);
-}
-
-static void mlist_free(QGuestAllocator *s, uint64_t addr)
-{
-    MemBlock *node;
-
-    if (addr == 0) {
-        return;
-    }
-
-    node = mlist_find_key(s->used, addr);
-    if (!node) {
-        fprintf(stderr, "Error: no record found for an allocation at "
-                "0x%016" PRIx64 ".\n",
-                addr);
-        g_assert_not_reached();
-    }
-
-    /* Rip it out of the used list and re-insert back into the free list. */
-    QTAILQ_REMOVE(s->used, node, MLIST_ENTNAME);
-    mlist_sort_insert(s->free, node);
-    mlist_coalesce(s->free, node);
-}
-
-/*
- * Mostly for valgrind happiness, but it does offer
- * a chokepoint for debugging guest memory leaks, too.
- */
-void alloc_destroy(QGuestAllocator *allocator)
-{
-    MemBlock *node;
-    MemBlock *tmp;
-    QAllocOpts mask;
-
-    /* Check for guest leaks, and destroy the list. */
-    QTAILQ_FOREACH_SAFE(node, allocator->used, MLIST_ENTNAME, tmp) {
-        if (allocator->opts & (ALLOC_LEAK_WARN | ALLOC_LEAK_ASSERT)) {
-            fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; "
-                    "size 0x%016" PRIx64 ".\n",
-                    node->addr, node->size);
-        }
-        if (allocator->opts & (ALLOC_LEAK_ASSERT)) {
-            g_assert_not_reached();
-        }
-        g_free(node);
-    }
-
-    /* If we have previously asserted that there are no leaks, then there
-     * should be only one node here with a specific address and size. */
-    mask = ALLOC_LEAK_ASSERT | ALLOC_PARANOID;
-    QTAILQ_FOREACH_SAFE(node, allocator->free, MLIST_ENTNAME, tmp) {
-        if ((allocator->opts & mask) == mask) {
-            if ((node->addr != allocator->start) ||
-                (node->size != allocator->end - allocator->start)) {
-                fprintf(stderr, "Free list is corrupted.\n");
-                g_assert_not_reached();
-            }
-        }
-
-        g_free(node);
-    }
-
-    g_free(allocator->used);
-    g_free(allocator->free);
-}
-
-uint64_t guest_alloc(QGuestAllocator *allocator, size_t size)
-{
-    uint64_t rsize = size;
-    uint64_t naddr;
-
-    if (!size) {
-        return 0;
-    }
-
-    rsize += (allocator->page_size - 1);
-    rsize &= -allocator->page_size;
-    g_assert_cmpint((allocator->start + rsize), <=, allocator->end);
-    g_assert_cmpint(rsize, >=, size);
-
-    naddr = mlist_alloc(allocator, rsize);
-    if (allocator->opts & ALLOC_PARANOID) {
-        mlist_check(allocator);
-    }
-
-    return naddr;
-}
-
-void guest_free(QGuestAllocator *allocator, uint64_t addr)
-{
-    if (!addr) {
-        return;
-    }
-    mlist_free(allocator, addr);
-    if (allocator->opts & ALLOC_PARANOID) {
-        mlist_check(allocator);
-    }
-}
-
-void alloc_init(QGuestAllocator *s, QAllocOpts opts,
-                uint64_t start, uint64_t end,
-                size_t page_size)
-{
-    MemBlock *node;
-
-    s->opts = opts;
-    s->start = start;
-    s->end = end;
-
-    s->used = g_new(MemList, 1);
-    s->free = g_new(MemList, 1);
-    QTAILQ_INIT(s->used);
-    QTAILQ_INIT(s->free);
-
-    node = mlist_new(s->start, s->end - s->start);
-    QTAILQ_INSERT_HEAD(s->free, node, MLIST_ENTNAME);
-
-    s->page_size = page_size;
-}
-
-void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts)
-{
-    allocator->opts |= opts;
-}
-
-void migrate_allocator(QGuestAllocator *src,
-                       QGuestAllocator *dst)
-{
-    MemBlock *node, *tmp;
-    MemList *tmpused, *tmpfree;
-
-    /* The general memory layout should be equivalent,
-     * though opts can differ. */
-    g_assert_cmphex(src->start, ==, dst->start);
-    g_assert_cmphex(src->end, ==, dst->end);
-
-    /* Destroy (silently, regardless of options) the dest-list: */
-    QTAILQ_FOREACH_SAFE(node, dst->used, MLIST_ENTNAME, tmp) {
-        g_free(node);
-    }
-    QTAILQ_FOREACH_SAFE(node, dst->free, MLIST_ENTNAME, tmp) {
-        g_free(node);
-    }
-
-    tmpused = dst->used;
-    tmpfree = dst->free;
-
-    /* Inherit the lists of the source allocator: */
-    dst->used = src->used;
-    dst->free = src->free;
-
-    /* Source is now re-initialized, the source memory is 'invalid' now: */
-    src->used = tmpused;
-    src->free = tmpfree;
-    QTAILQ_INIT(src->used);
-    QTAILQ_INIT(src->free);
-    node = mlist_new(src->start, src->end - src->start);
-    QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME);
-    return;
-}
diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h
deleted file mode 100644 (file)
index 4d1a2e2..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * libqos malloc support
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_MALLOC_H
-#define LIBQOS_MALLOC_H
-
-#include "qemu/queue.h"
-#include "libqtest.h"
-
-typedef enum {
-    ALLOC_NO_FLAGS    = 0x00,
-    ALLOC_LEAK_WARN   = 0x01,
-    ALLOC_LEAK_ASSERT = 0x02,
-    ALLOC_PARANOID    = 0x04
-} QAllocOpts;
-
-typedef QTAILQ_HEAD(MemList, MemBlock) MemList;
-
-typedef struct QGuestAllocator {
-    QAllocOpts opts;
-    uint64_t start;
-    uint64_t end;
-    uint32_t page_size;
-
-    MemList *used;
-    MemList *free;
-} QGuestAllocator;
-
-/* Always returns page aligned values */
-uint64_t guest_alloc(QGuestAllocator *allocator, size_t size);
-void guest_free(QGuestAllocator *allocator, uint64_t addr);
-void migrate_allocator(QGuestAllocator *src, QGuestAllocator *dst);
-
-void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts);
-
-void alloc_init(QGuestAllocator *alloc, QAllocOpts flags,
-                uint64_t start, uint64_t end,
-                size_t page_size);
-void alloc_destroy(QGuestAllocator *allocator);
-
-#endif
diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c
deleted file mode 100644 (file)
index 0bc591d..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * libqos PCI bindings for PC
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/pci-pc.h"
-#include "qapi/qmp/qdict.h"
-#include "hw/pci/pci_regs.h"
-
-#include "qemu/module.h"
-
-#define ACPI_PCIHP_ADDR         0xae00
-#define PCI_EJ_BASE             0x0008
-
-static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr)
-{
-    return qtest_inb(bus->qts, addr);
-}
-
-static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val)
-{
-    qtest_outb(bus->qts, addr, val);
-}
-
-static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr)
-{
-    return qtest_inw(bus->qts, addr);
-}
-
-static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val)
-{
-    qtest_outw(bus->qts, addr, val);
-}
-
-static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr)
-{
-    return qtest_inl(bus->qts, addr);
-}
-
-static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val)
-{
-    qtest_outl(bus->qts, addr, val);
-}
-
-static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr)
-{
-    return (uint64_t)qtest_inl(bus->qts, addr) +
-        ((uint64_t)qtest_inl(bus->qts, addr + 4) << 32);
-}
-
-static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val)
-{
-    qtest_outl(bus->qts, addr, val & 0xffffffff);
-    qtest_outl(bus->qts, addr + 4, val >> 32);
-}
-
-static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len)
-{
-    qtest_memread(bus->qts, addr, buf, len);
-}
-
-static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr,
-                             const void *buf, size_t len)
-{
-    qtest_memwrite(bus->qts, addr, buf, len);
-}
-
-static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
-{
-    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
-    return qtest_inb(bus->qts, 0xcfc);
-}
-
-static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
-{
-    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
-    return qtest_inw(bus->qts, 0xcfc);
-}
-
-static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
-{
-    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
-    return qtest_inl(bus->qts, 0xcfc);
-}
-
-static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value)
-{
-    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
-    qtest_outb(bus->qts, 0xcfc, value);
-}
-
-static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value)
-{
-    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
-    qtest_outw(bus->qts, 0xcfc, value);
-}
-
-static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value)
-{
-    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
-    qtest_outl(bus->qts, 0xcfc, value);
-}
-
-static void *qpci_pc_get_driver(void *obj, const char *interface)
-{
-    QPCIBusPC *qpci = obj;
-    if (!g_strcmp0(interface, "pci-bus")) {
-        return &qpci->bus;
-    }
-    fprintf(stderr, "%s not present in pci-bus-pc\n", interface);
-    g_assert_not_reached();
-}
-
-void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc)
-{
-    assert(qts);
-
-    /* tests can use pci-bus */
-    qpci->bus.has_buggy_msi = false;
-
-    qpci->bus.pio_readb = qpci_pc_pio_readb;
-    qpci->bus.pio_readw = qpci_pc_pio_readw;
-    qpci->bus.pio_readl = qpci_pc_pio_readl;
-    qpci->bus.pio_readq = qpci_pc_pio_readq;
-
-    qpci->bus.pio_writeb = qpci_pc_pio_writeb;
-    qpci->bus.pio_writew = qpci_pc_pio_writew;
-    qpci->bus.pio_writel = qpci_pc_pio_writel;
-    qpci->bus.pio_writeq = qpci_pc_pio_writeq;
-
-    qpci->bus.memread = qpci_pc_memread;
-    qpci->bus.memwrite = qpci_pc_memwrite;
-
-    qpci->bus.config_readb = qpci_pc_config_readb;
-    qpci->bus.config_readw = qpci_pc_config_readw;
-    qpci->bus.config_readl = qpci_pc_config_readl;
-
-    qpci->bus.config_writeb = qpci_pc_config_writeb;
-    qpci->bus.config_writew = qpci_pc_config_writew;
-    qpci->bus.config_writel = qpci_pc_config_writel;
-
-    qpci->bus.qts = qts;
-    qpci->bus.pio_alloc_ptr = 0xc000;
-    qpci->bus.mmio_alloc_ptr = 0xE0000000;
-    qpci->bus.mmio_limit = 0x100000000ULL;
-
-    qpci->obj.get_driver = qpci_pc_get_driver;
-}
-
-QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc)
-{
-    QPCIBusPC *qpci = g_new0(QPCIBusPC, 1);
-    qpci_init_pc(qpci, qts, alloc);
-
-    return &qpci->bus;
-}
-
-void qpci_free_pc(QPCIBus *bus)
-{
-    QPCIBusPC *s;
-
-    if (!bus) {
-        return;
-    }
-    s = container_of(bus, QPCIBusPC, bus);
-
-    g_free(s);
-}
-
-void qpci_unplug_acpi_device_test(QTestState *qts, const char *id, uint8_t slot)
-{
-    QDict *response;
-
-    response = qtest_qmp(qts, "{'execute': 'device_del',"
-                              " 'arguments': {'id': %s}}", id);
-    g_assert(response);
-    g_assert(!qdict_haskey(response, "error"));
-    qobject_unref(response);
-
-    qtest_outb(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot);
-
-    qtest_qmp_eventwait(qts, "DEVICE_DELETED");
-}
-
-static void qpci_pc_register_nodes(void)
-{
-    qos_node_create_driver("pci-bus-pc", NULL);
-    qos_node_produces("pci-bus-pc", "pci-bus");
-}
-
-libqos_init(qpci_pc_register_nodes);
diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h
deleted file mode 100644 (file)
index 4690005..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * libqos PCI bindings for PC
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_PCI_PC_H
-#define LIBQOS_PCI_PC_H
-
-#include "libqos/pci.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-
-typedef struct QPCIBusPC {
-    QOSGraphObject obj;
-    QPCIBus bus;
-} QPCIBusPC;
-
-/* qpci_init_pc():
- * @ret: A valid QPCIBusPC * pointer
- * @qts: The %QTestState for this PC machine
- * @alloc: A previously initialized @alloc providing memory for @qts
- *
- * This function initializes an already allocated
- * QPCIBusPC object.
- */
-void qpci_init_pc(QPCIBusPC *ret, QTestState *qts, QGuestAllocator *alloc);
-
-/* qpci_pc_new():
- * @qts: The %QTestState for this PC machine
- * @alloc: A previously initialized @alloc providing memory for @qts
- *
- * This function creates a new QPCIBusPC object,
- * and properly initialize its fields.
- *
- * Returns the QPCIBus *bus field of a newly
- * allocated QPCIBusPC.
- */
-QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc);
-
-void     qpci_free_pc(QPCIBus *bus);
-
-#endif
diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c
deleted file mode 100644 (file)
index d6f8c01..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * libqos PCI bindings for SPAPR
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/pci-spapr.h"
-#include "libqos/rtas.h"
-#include "libqos/qgraph.h"
-
-#include "hw/pci/pci_regs.h"
-
-#include "qemu/host-utils.h"
-#include "qemu/module.h"
-
-/*
- * PCI devices are always little-endian
- * SPAPR by default is big-endian
- * so PCI accessors need to swap data endianness
- */
-
-static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    return qtest_readb(bus->qts, s->pio_cpu_base + addr);
-}
-
-static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    qtest_writeb(bus->qts, s->pio_cpu_base + addr, val);
-}
-
-static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    return bswap16(qtest_readw(bus->qts, s->pio_cpu_base + addr));
-}
-
-static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    qtest_writew(bus->qts, s->pio_cpu_base + addr, bswap16(val));
-}
-
-static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    return bswap32(qtest_readl(bus->qts, s->pio_cpu_base + addr));
-}
-
-static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    qtest_writel(bus->qts, s->pio_cpu_base + addr, bswap32(val));
-}
-
-static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    return bswap64(qtest_readq(bus->qts, s->pio_cpu_base + addr));
-}
-
-static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    qtest_writeq(bus->qts, s->pio_cpu_base + addr, bswap64(val));
-}
-
-static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr,
-                               void *buf, size_t len)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    qtest_memread(bus->qts, s->mmio32_cpu_base + addr, buf, len);
-}
-
-static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr,
-                                const void *buf, size_t len)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    qtest_memwrite(bus->qts, s->mmio32_cpu_base + addr, buf, len);
-}
-
-static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    uint32_t config_addr = (devfn << 8) | offset;
-    return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid,
-                                     config_addr, 1);
-}
-
-static uint16_t qpci_spapr_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    uint32_t config_addr = (devfn << 8) | offset;
-    return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid,
-                                     config_addr, 2);
-}
-
-static uint32_t qpci_spapr_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    uint32_t config_addr = (devfn << 8) | offset;
-    return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid,
-                                     config_addr, 4);
-}
-
-static void qpci_spapr_config_writeb(QPCIBus *bus, int devfn, uint8_t offset,
-                                     uint8_t value)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    uint32_t config_addr = (devfn << 8) | offset;
-    qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid,
-                               config_addr, 1, value);
-}
-
-static void qpci_spapr_config_writew(QPCIBus *bus, int devfn, uint8_t offset,
-                                     uint16_t value)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    uint32_t config_addr = (devfn << 8) | offset;
-    qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid,
-                               config_addr, 2, value);
-}
-
-static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset,
-                                     uint32_t value)
-{
-    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
-    uint32_t config_addr = (devfn << 8) | offset;
-    qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid,
-                               config_addr, 4, value);
-}
-
-#define SPAPR_PCI_BASE               (1ULL << 45)
-
-#define SPAPR_PCI_MMIO32_WIN_SIZE    0x80000000 /* 2 GiB */
-#define SPAPR_PCI_IO_WIN_SIZE        0x10000
-
-static void *qpci_spapr_get_driver(void *obj, const char *interface)
-{
-    QPCIBusSPAPR *qpci = obj;
-    if (!g_strcmp0(interface, "pci-bus")) {
-        return &qpci->bus;
-    }
-    fprintf(stderr, "%s not present in pci-bus-spapr", interface);
-    g_assert_not_reached();
-}
-
-void qpci_init_spapr(QPCIBusSPAPR *qpci, QTestState *qts,
-                     QGuestAllocator *alloc)
-{
-    assert(qts);
-
-    /* tests cannot use spapr, needs to be fixed first */
-    qpci->bus.has_buggy_msi = true;
-
-    qpci->alloc = alloc;
-
-    qpci->bus.pio_readb = qpci_spapr_pio_readb;
-    qpci->bus.pio_readw = qpci_spapr_pio_readw;
-    qpci->bus.pio_readl = qpci_spapr_pio_readl;
-    qpci->bus.pio_readq = qpci_spapr_pio_readq;
-
-    qpci->bus.pio_writeb = qpci_spapr_pio_writeb;
-    qpci->bus.pio_writew = qpci_spapr_pio_writew;
-    qpci->bus.pio_writel = qpci_spapr_pio_writel;
-    qpci->bus.pio_writeq = qpci_spapr_pio_writeq;
-
-    qpci->bus.memread = qpci_spapr_memread;
-    qpci->bus.memwrite = qpci_spapr_memwrite;
-
-    qpci->bus.config_readb = qpci_spapr_config_readb;
-    qpci->bus.config_readw = qpci_spapr_config_readw;
-    qpci->bus.config_readl = qpci_spapr_config_readl;
-
-    qpci->bus.config_writeb = qpci_spapr_config_writeb;
-    qpci->bus.config_writew = qpci_spapr_config_writew;
-    qpci->bus.config_writel = qpci_spapr_config_writel;
-
-    /* FIXME: We assume the default location of the PHB for now.
-     * Ideally we'd parse the device tree deposited in the guest to
-     * get the window locations */
-    qpci->buid = 0x800000020000000ULL;
-
-    qpci->pio_cpu_base = SPAPR_PCI_BASE;
-    qpci->pio.pci_base = 0;
-    qpci->pio.size = SPAPR_PCI_IO_WIN_SIZE;
-
-    /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */
-    qpci->mmio32_cpu_base = SPAPR_PCI_BASE;
-    qpci->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE;
-    qpci->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
-
-    qpci->bus.qts = qts;
-    qpci->bus.pio_alloc_ptr = 0xc000;
-    qpci->bus.mmio_alloc_ptr = qpci->mmio32.pci_base;
-    qpci->bus.mmio_limit = qpci->mmio32.pci_base + qpci->mmio32.size;
-
-    qpci->obj.get_driver = qpci_spapr_get_driver;
-}
-
-QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc)
-{
-    QPCIBusSPAPR *qpci = g_new0(QPCIBusSPAPR, 1);
-    qpci_init_spapr(qpci, qts, alloc);
-
-    return &qpci->bus;
-}
-
-void qpci_free_spapr(QPCIBus *bus)
-{
-    QPCIBusSPAPR *s;
-
-    if (!bus) {
-        return;
-    }
-    s = container_of(bus, QPCIBusSPAPR, bus);
-
-    g_free(s);
-}
-
-static void qpci_spapr_register_nodes(void)
-{
-    qos_node_create_driver("pci-bus-spapr", NULL);
-    qos_node_produces("pci-bus-spapr", "pci-bus");
-}
-
-libqos_init(qpci_spapr_register_nodes);
diff --git a/tests/libqos/pci-spapr.h b/tests/libqos/pci-spapr.h
deleted file mode 100644 (file)
index d9e2563..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * libqos PCI bindings for SPAPR
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_PCI_SPAPR_H
-#define LIBQOS_PCI_SPAPR_H
-
-#include "libqos/malloc.h"
-#include "libqos/pci.h"
-#include "libqos/qgraph.h"
-
-/* From include/hw/pci-host/spapr.h */
-
-typedef struct QPCIWindow {
-    uint64_t pci_base;    /* window address in PCI space */
-    uint64_t size;        /* window size */
-} QPCIWindow;
-
-typedef struct QPCIBusSPAPR {
-    QOSGraphObject obj;
-    QPCIBus bus;
-    QGuestAllocator *alloc;
-
-    uint64_t buid;
-
-    uint64_t pio_cpu_base;
-    QPCIWindow pio;
-
-    uint64_t mmio32_cpu_base;
-    QPCIWindow mmio32;
-} QPCIBusSPAPR;
-
-void qpci_init_spapr(QPCIBusSPAPR *ret, QTestState *qts,
-                     QGuestAllocator *alloc);
-QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc);
-void     qpci_free_spapr(QPCIBus *bus);
-
-#endif
diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c
deleted file mode 100644 (file)
index 2309a72..0000000
+++ /dev/null
@@ -1,457 +0,0 @@
-/*
- * libqos PCI bindings
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqos/pci.h"
-
-#include "hw/pci/pci_regs.h"
-#include "qemu/host-utils.h"
-#include "libqos/qgraph.h"
-
-void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
-                         void (*func)(QPCIDevice *dev, int devfn, void *data),
-                         void *data)
-{
-    int slot;
-
-    for (slot = 0; slot < 32; slot++) {
-        int fn;
-
-        for (fn = 0; fn < 8; fn++) {
-            QPCIDevice *dev;
-
-            dev = qpci_device_find(bus, QPCI_DEVFN(slot, fn));
-            if (!dev) {
-                continue;
-            }
-
-            if (vendor_id != -1 &&
-                qpci_config_readw(dev, PCI_VENDOR_ID) != vendor_id) {
-                g_free(dev);
-                continue;
-            }
-
-            if (device_id != -1 &&
-                qpci_config_readw(dev, PCI_DEVICE_ID) != device_id) {
-                g_free(dev);
-                continue;
-            }
-
-            func(dev, QPCI_DEVFN(slot, fn), data);
-        }
-    }
-}
-
-bool qpci_has_buggy_msi(QPCIDevice *dev)
-{
-    return dev->bus->has_buggy_msi;
-}
-
-bool qpci_check_buggy_msi(QPCIDevice *dev)
-{
-    if (qpci_has_buggy_msi(dev)) {
-        g_test_skip("Skipping due to incomplete support for MSI");
-        return true;
-    }
-    return false;
-}
-
-static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn)
-{
-    g_assert(dev);
-
-    dev->bus = bus;
-    dev->devfn = devfn;
-}
-
-QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
-{
-    QPCIDevice *dev;
-
-    dev = g_malloc0(sizeof(*dev));
-    qpci_device_set(dev, bus, devfn);
-
-    if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
-        g_free(dev);
-        return NULL;
-    }
-
-    return dev;
-}
-
-void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr)
-{
-    uint16_t vendor_id, device_id;
-
-    qpci_device_set(dev, bus, addr->devfn);
-    vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
-    device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
-    g_assert(!addr->vendor_id || vendor_id == addr->vendor_id);
-    g_assert(!addr->device_id || device_id == addr->device_id);
-}
-
-void qpci_device_enable(QPCIDevice *dev)
-{
-    uint16_t cmd;
-
-    /* FIXME -- does this need to be a bus callout? */
-    cmd = qpci_config_readw(dev, PCI_COMMAND);
-    cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
-    qpci_config_writew(dev, PCI_COMMAND, cmd);
-
-    /* Verify the bits are now set. */
-    cmd = qpci_config_readw(dev, PCI_COMMAND);
-    g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO);
-    g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY);
-    g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER);
-}
-
-/**
- * qpci_find_capability:
- * @dev: the PCI device
- * @id: the PCI Capability ID (PCI_CAP_ID_*)
- * @start_addr: 0 to begin iteration or the last return value to continue
- *              iteration
- *
- * Iterate over the PCI Capabilities List.
- *
- * Returns: PCI Configuration Space offset of the capabililty structure or
- *          0 if no further matching capability is found
- */
-uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id, uint8_t start_addr)
-{
-    uint8_t cap;
-    uint8_t addr;
-
-    if (start_addr) {
-        addr = qpci_config_readb(dev, start_addr + PCI_CAP_LIST_NEXT);
-    } else {
-        addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST);
-    }
-
-    do {
-        cap = qpci_config_readb(dev, addr);
-        if (cap != id) {
-            addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT);
-        }
-    } while (cap != id && addr != 0);
-
-    return addr;
-}
-
-void qpci_msix_enable(QPCIDevice *dev)
-{
-    uint8_t addr;
-    uint16_t val;
-    uint32_t table;
-    uint8_t bir_table;
-    uint8_t bir_pba;
-
-    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
-    g_assert_cmphex(addr, !=, 0);
-
-    val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
-    qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE);
-
-    table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE);
-    bir_table = table & PCI_MSIX_FLAGS_BIRMASK;
-    dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL);
-    dev->msix_table_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
-
-    table = qpci_config_readl(dev, addr + PCI_MSIX_PBA);
-    bir_pba = table & PCI_MSIX_FLAGS_BIRMASK;
-    if (bir_pba != bir_table) {
-        dev->msix_pba_bar = qpci_iomap(dev, bir_pba, NULL);
-    } else {
-        dev->msix_pba_bar = dev->msix_table_bar;
-    }
-    dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
-
-    dev->msix_enabled = true;
-}
-
-void qpci_msix_disable(QPCIDevice *dev)
-{
-    uint8_t addr;
-    uint16_t val;
-
-    g_assert(dev->msix_enabled);
-    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
-    g_assert_cmphex(addr, !=, 0);
-    val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
-    qpci_config_writew(dev, addr + PCI_MSIX_FLAGS,
-                                                val & ~PCI_MSIX_FLAGS_ENABLE);
-
-    if (dev->msix_pba_bar.addr != dev->msix_table_bar.addr) {
-        qpci_iounmap(dev, dev->msix_pba_bar);
-    }
-    qpci_iounmap(dev, dev->msix_table_bar);
-
-    dev->msix_enabled = 0;
-    dev->msix_table_off = 0;
-    dev->msix_pba_off = 0;
-}
-
-bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
-{
-    uint32_t pba_entry;
-    uint8_t bit_n = entry % 32;
-    uint64_t  off = (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4;
-
-    g_assert(dev->msix_enabled);
-    pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off);
-    qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off,
-                   pba_entry & ~(1 << bit_n));
-    return (pba_entry & (1 << bit_n)) != 0;
-}
-
-bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry)
-{
-    uint8_t addr;
-    uint16_t val;
-    uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE;
-
-    g_assert(dev->msix_enabled);
-    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
-    g_assert_cmphex(addr, !=, 0);
-    val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
-
-    if (val & PCI_MSIX_FLAGS_MASKALL) {
-        return true;
-    } else {
-        return (qpci_io_readl(dev, dev->msix_table_bar,
-                              vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL)
-                & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0;
-    }
-}
-
-uint16_t qpci_msix_table_size(QPCIDevice *dev)
-{
-    uint8_t addr;
-    uint16_t control;
-
-    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
-    g_assert_cmphex(addr, !=, 0);
-
-    control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
-    return (control & PCI_MSIX_FLAGS_QSIZE) + 1;
-}
-
-uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset)
-{
-    return dev->bus->config_readb(dev->bus, dev->devfn, offset);
-}
-
-uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset)
-{
-    return dev->bus->config_readw(dev->bus, dev->devfn, offset);
-}
-
-uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset)
-{
-    return dev->bus->config_readl(dev->bus, dev->devfn, offset);
-}
-
-
-void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value)
-{
-    dev->bus->config_writeb(dev->bus, dev->devfn, offset, value);
-}
-
-void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value)
-{
-    dev->bus->config_writew(dev->bus, dev->devfn, offset, value);
-}
-
-void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value)
-{
-    dev->bus->config_writel(dev->bus, dev->devfn, offset, value);
-}
-
-uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        return dev->bus->pio_readb(dev->bus, token.addr + off);
-    } else {
-        uint8_t val;
-        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
-        return val;
-    }
-}
-
-uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        return dev->bus->pio_readw(dev->bus, token.addr + off);
-    } else {
-        uint16_t val;
-        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
-        return le16_to_cpu(val);
-    }
-}
-
-uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        return dev->bus->pio_readl(dev->bus, token.addr + off);
-    } else {
-        uint32_t val;
-        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
-        return le32_to_cpu(val);
-    }
-}
-
-uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        return dev->bus->pio_readq(dev->bus, token.addr + off);
-    } else {
-        uint64_t val;
-        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
-        return le64_to_cpu(val);
-    }
-}
-
-void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint8_t value)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        dev->bus->pio_writeb(dev->bus, token.addr + off, value);
-    } else {
-        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
-    }
-}
-
-void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint16_t value)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        dev->bus->pio_writew(dev->bus, token.addr + off, value);
-    } else {
-        value = cpu_to_le16(value);
-        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
-    }
-}
-
-void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint32_t value)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        dev->bus->pio_writel(dev->bus, token.addr + off, value);
-    } else {
-        value = cpu_to_le32(value);
-        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
-    }
-}
-
-void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint64_t value)
-{
-    if (token.addr < QPCI_PIO_LIMIT) {
-        dev->bus->pio_writeq(dev->bus, token.addr + off, value);
-    } else {
-        value = cpu_to_le64(value);
-        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
-    }
-}
-
-void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                  void *buf, size_t len)
-{
-    g_assert(token.addr >= QPCI_PIO_LIMIT);
-    dev->bus->memread(dev->bus, token.addr + off, buf, len);
-}
-
-void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                   const void *buf, size_t len)
-{
-    g_assert(token.addr >= QPCI_PIO_LIMIT);
-    dev->bus->memwrite(dev->bus, token.addr + off, buf, len);
-}
-
-QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr)
-{
-    QPCIBus *bus = dev->bus;
-    static const int bar_reg_map[] = {
-        PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
-        PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
-    };
-    QPCIBar bar;
-    int bar_reg;
-    uint32_t addr, size;
-    uint32_t io_type;
-    uint64_t loc;
-
-    g_assert(barno >= 0 && barno <= 5);
-    bar_reg = bar_reg_map[barno];
-
-    qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
-    addr = qpci_config_readl(dev, bar_reg);
-
-    io_type = addr & PCI_BASE_ADDRESS_SPACE;
-    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
-        addr &= PCI_BASE_ADDRESS_IO_MASK;
-    } else {
-        addr &= PCI_BASE_ADDRESS_MEM_MASK;
-    }
-
-    g_assert(addr); /* Must have *some* size bits */
-
-    size = 1U << ctz32(addr);
-    if (sizeptr) {
-        *sizeptr = size;
-    }
-
-    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
-        loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size);
-
-        g_assert(loc >= bus->pio_alloc_ptr);
-        g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */
-
-        bus->pio_alloc_ptr = loc + size;
-
-        qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
-    } else {
-        loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size);
-
-        /* Check for space */
-        g_assert(loc >= bus->mmio_alloc_ptr);
-        g_assert(loc + size <= bus->mmio_limit);
-
-        bus->mmio_alloc_ptr = loc + size;
-
-        qpci_config_writel(dev, bar_reg, loc);
-    }
-
-    bar.addr = loc;
-    return bar;
-}
-
-void qpci_iounmap(QPCIDevice *dev, QPCIBar bar)
-{
-    /* FIXME */
-}
-
-QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr)
-{
-    QPCIBar bar = { .addr = addr };
-    return bar;
-}
-
-void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr)
-{
-    g_assert(addr);
-    g_assert(opts);
-
-    opts->arg = addr;
-    opts->size_arg = sizeof(QPCIAddress);
-}
diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h
deleted file mode 100644 (file)
index 590c175..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * libqos PCI bindings
- *
- * Copyright IBM, Corp. 2012-2013
- *
- * Authors:
- *  Anthony Liguori   <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_PCI_H
-#define LIBQOS_PCI_H
-
-#include "libqtest.h"
-#include "libqos/qgraph.h"
-
-#define QPCI_PIO_LIMIT    0x10000
-
-#define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn))
-
-typedef struct QPCIDevice QPCIDevice;
-typedef struct QPCIBus QPCIBus;
-typedef struct QPCIBar QPCIBar;
-typedef struct QPCIAddress QPCIAddress;
-
-struct QPCIBus {
-    uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr);
-    uint16_t (*pio_readw)(QPCIBus *bus, uint32_t addr);
-    uint32_t (*pio_readl)(QPCIBus *bus, uint32_t addr);
-    uint64_t (*pio_readq)(QPCIBus *bus, uint32_t addr);
-
-    void (*pio_writeb)(QPCIBus *bus, uint32_t addr, uint8_t value);
-    void (*pio_writew)(QPCIBus *bus, uint32_t addr, uint16_t value);
-    void (*pio_writel)(QPCIBus *bus, uint32_t addr, uint32_t value);
-    void (*pio_writeq)(QPCIBus *bus, uint32_t addr, uint64_t value);
-
-    void (*memread)(QPCIBus *bus, uint32_t addr, void *buf, size_t len);
-    void (*memwrite)(QPCIBus *bus, uint32_t addr, const void *buf, size_t len);
-
-    uint8_t (*config_readb)(QPCIBus *bus, int devfn, uint8_t offset);
-    uint16_t (*config_readw)(QPCIBus *bus, int devfn, uint8_t offset);
-    uint32_t (*config_readl)(QPCIBus *bus, int devfn, uint8_t offset);
-
-    void (*config_writeb)(QPCIBus *bus, int devfn,
-                          uint8_t offset, uint8_t value);
-    void (*config_writew)(QPCIBus *bus, int devfn,
-                          uint8_t offset, uint16_t value);
-    void (*config_writel)(QPCIBus *bus, int devfn,
-                          uint8_t offset, uint32_t value);
-
-    QTestState *qts;
-    uint16_t pio_alloc_ptr;
-    uint64_t mmio_alloc_ptr, mmio_limit;
-    bool has_buggy_msi; /* TRUE for spapr, FALSE for pci */
-
-};
-
-struct QPCIBar {
-    uint64_t addr;
-};
-
-struct QPCIDevice
-{
-    QPCIBus *bus;
-    int devfn;
-    bool msix_enabled;
-    QPCIBar msix_table_bar, msix_pba_bar;
-    uint64_t msix_table_off, msix_pba_off;
-};
-
-struct QPCIAddress {
-    uint32_t devfn;
-    uint16_t vendor_id;
-    uint16_t device_id;
-};
-
-void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
-                         void (*func)(QPCIDevice *dev, int devfn, void *data),
-                         void *data);
-QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
-void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr);
-
-bool qpci_has_buggy_msi(QPCIDevice *dev);
-bool qpci_check_buggy_msi(QPCIDevice *dev);
-
-void qpci_device_enable(QPCIDevice *dev);
-uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id, uint8_t start_addr);
-void qpci_msix_enable(QPCIDevice *dev);
-void qpci_msix_disable(QPCIDevice *dev);
-bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry);
-bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry);
-uint16_t qpci_msix_table_size(QPCIDevice *dev);
-
-uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset);
-uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset);
-uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset);
-
-void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value);
-void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value);
-void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value);
-
-uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off);
-uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off);
-uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off);
-uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off);
-
-void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint8_t value);
-void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint16_t value);
-void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint32_t value);
-void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off,
-                    uint64_t value);
-
-void qpci_memread(QPCIDevice *bus, QPCIBar token, uint64_t off,
-                  void *buf, size_t len);
-void qpci_memwrite(QPCIDevice *bus, QPCIBar token, uint64_t off,
-                   const void *buf, size_t len);
-QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr);
-void qpci_iounmap(QPCIDevice *dev, QPCIBar addr);
-QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr);
-
-void qpci_unplug_acpi_device_test(QTestState *qs, const char *id, uint8_t slot);
-
-void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr);
-#endif
diff --git a/tests/libqos/ppc64_pseries-machine.c b/tests/libqos/ppc64_pseries-machine.c
deleted file mode 100644 (file)
index 867f27a..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/qgraph.h"
-#include "pci-spapr.h"
-#include "qemu/module.h"
-#include "libqos/malloc-spapr.h"
-
-typedef struct QSPAPR_pci_host QSPAPR_pci_host;
-typedef struct Qppc64_pseriesMachine Qppc64_pseriesMachine;
-
-struct QSPAPR_pci_host {
-    QOSGraphObject obj;
-    QPCIBusSPAPR pci;
-};
-
-struct Qppc64_pseriesMachine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    QSPAPR_pci_host bridge;
-};
-
-/* QSPAPR_pci_host */
-
-static QOSGraphObject *QSPAPR_host_get_device(void *obj, const char *device)
-{
-    QSPAPR_pci_host *host = obj;
-    if (!g_strcmp0(device, "pci-bus-spapr")) {
-        return &host->pci.obj;
-    }
-    fprintf(stderr, "%s not present in QSPAPR_pci_host\n", device);
-    g_assert_not_reached();
-}
-
-static void qos_create_QSPAPR_host(QSPAPR_pci_host *host,
-                                   QTestState *qts,
-                                   QGuestAllocator *alloc)
-{
-    host->obj.get_device = QSPAPR_host_get_device;
-    qpci_init_spapr(&host->pci, qts, alloc);
-}
-
-/* ppc64/pseries machine */
-
-static void spapr_destructor(QOSGraphObject *obj)
-{
-    Qppc64_pseriesMachine *machine = (Qppc64_pseriesMachine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *spapr_get_driver(void *object, const char *interface)
-{
-    Qppc64_pseriesMachine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in ppc64/pseries\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *spapr_get_device(void *obj, const char *device)
-{
-    Qppc64_pseriesMachine *machine = obj;
-    if (!g_strcmp0(device, "spapr-pci-host-bridge")) {
-        return &machine->bridge.obj;
-    }
-
-    fprintf(stderr, "%s not present in ppc64/pseries\n", device);
-    g_assert_not_reached();
-}
-
-static void *qos_create_machine_spapr(QTestState *qts)
-{
-    Qppc64_pseriesMachine *machine = g_new0(Qppc64_pseriesMachine, 1);
-    machine->obj.get_device = spapr_get_device;
-    machine->obj.get_driver = spapr_get_driver;
-    machine->obj.destructor = spapr_destructor;
-    spapr_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS);
-
-    qos_create_QSPAPR_host(&machine->bridge, qts, &machine->alloc);
-
-    return &machine->obj;
-}
-
-static void spapr_machine_register_nodes(void)
-{
-    qos_node_create_machine("ppc64/pseries", qos_create_machine_spapr);
-    qos_node_create_driver("spapr-pci-host-bridge", NULL);
-    qos_node_contains("ppc64/pseries", "spapr-pci-host-bridge", NULL);
-    qos_node_contains("spapr-pci-host-bridge", "pci-bus-spapr", NULL);
-}
-
-libqos_init(spapr_machine_register_nodes);
-
diff --git a/tests/libqos/qgraph.c b/tests/libqos/qgraph.c
deleted file mode 100644 (file)
index 7a7ae2a..0000000
+++ /dev/null
@@ -1,759 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/queue.h"
-#include "libqos/qgraph_internal.h"
-#include "libqos/qgraph.h"
-
-#define QGRAPH_PRINT_DEBUG 0
-#define QOS_ROOT ""
-typedef struct QOSStackElement QOSStackElement;
-
-/* Graph Edge.*/
-struct QOSGraphEdge {
-    QOSEdgeType type;
-    char *dest;
-    void *arg;                /* just for QEDGE_CONTAINS
-                               * and QEDGE_CONSUMED_BY */
-    char *extra_device_opts;  /* added to -device option, "," is
-                               * automatically added
-                               */
-    char *before_cmd_line;    /* added before node cmd_line */
-    char *after_cmd_line;     /* added after -device options */
-    char *edge_name;          /* used by QEDGE_CONTAINS */
-    QSLIST_ENTRY(QOSGraphEdge) edge_list;
-};
-
-typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList;
-
-/**
- * Stack used to keep track of the discovered path when using
- * the DFS algorithm
- */
-struct QOSStackElement {
-    QOSGraphNode *node;
-    QOSStackElement *parent;
-    QOSGraphEdge *parent_edge;
-    int length;
-};
-
-/* Each enty in these hash table will consist of <string, node/edge> pair. */
-static GHashTable *edge_table;
-static GHashTable *node_table;
-
-/* stack used by the DFS algorithm to store the path from machine to test */
-static QOSStackElement qos_node_stack[QOS_PATH_MAX_ELEMENT_SIZE];
-static int qos_node_tos;
-
-/**
- * add_edge(): creates an edge of type @type
- * from @source to @dest node, and inserts it in the
- * edges hash table
- *
- * Nodes @source and @dest do not necessarily need to exist.
- * Possibility to add also options (see #QOSGraphEdgeOptions)
- * edge->edge_name is used as identifier for get_device relationships,
- * so by default is equal to @dest.
- */
-static void add_edge(const char *source, const char *dest,
-                     QOSEdgeType type, QOSGraphEdgeOptions *opts)
-{
-    char *key;
-    QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source);
-    QOSGraphEdgeOptions def_opts = { };
-
-    if (!list) {
-        list = g_new0(QOSGraphEdgeList, 1);
-        key = g_strdup(source);
-        g_hash_table_insert(edge_table, key, list);
-    }
-
-    if (!opts) {
-        opts = &def_opts;
-    }
-
-    QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1);
-    edge->type = type;
-    edge->dest = g_strdup(dest);
-    edge->edge_name = g_strdup(opts->edge_name ?: dest);
-    edge->arg = g_memdup(opts->arg, opts->size_arg);
-
-    edge->before_cmd_line =
-        opts->before_cmd_line ? g_strconcat(" ", opts->before_cmd_line, NULL) : NULL;
-    edge->extra_device_opts =
-        opts->extra_device_opts ? g_strconcat(",", opts->extra_device_opts, NULL) : NULL;
-    edge->after_cmd_line =
-        opts->after_cmd_line ? g_strconcat(" ", opts->after_cmd_line, NULL) : NULL;
-
-    QSLIST_INSERT_HEAD(list, edge, edge_list);
-}
-
-/* destroy_edges(): frees all edges inside a given @list */
-static void destroy_edges(void *list)
-{
-    QOSGraphEdge *temp;
-    QOSGraphEdgeList *elist = list;
-
-    while (!QSLIST_EMPTY(elist)) {
-        temp = QSLIST_FIRST(elist);
-        QSLIST_REMOVE_HEAD(elist, edge_list);
-        g_free(temp->dest);
-        g_free(temp->before_cmd_line);
-        g_free(temp->after_cmd_line);
-        g_free(temp->extra_device_opts);
-        g_free(temp->edge_name);
-        g_free(temp->arg);
-        g_free(temp);
-    }
-    g_free(elist);
-}
-
-/**
- * create_node(): creates a node @name of type @type
- * and inserts it to the nodes hash table.
- * By default, node is not available.
- */
-static QOSGraphNode *create_node(const char *name, QOSNodeType type)
-{
-    if (g_hash_table_lookup(node_table, name)) {
-        g_printerr("Node %s already created\n", name);
-        abort();
-    }
-
-    QOSGraphNode *node = g_new0(QOSGraphNode, 1);
-    node->type = type;
-    node->available = false;
-    node->name = g_strdup(name);
-    g_hash_table_insert(node_table, node->name, node);
-    return node;
-}
-
-/**
- * destroy_node(): frees a node @val from the nodes hash table.
- * Note that node->name is not free'd since it will represent the
- * hash table key
- */
-static void destroy_node(void *val)
-{
-    QOSGraphNode *node = val;
-    g_free(node->command_line);
-    g_free(node);
-}
-
-/**
- * destroy_string(): frees @key from the nodes hash table.
- * Actually frees the node->name
- */
-static void destroy_string(void *key)
-{
-    g_free(key);
-}
-
-/**
- * search_node(): search for a node @key in the nodes hash table
- * Returns the QOSGraphNode if found, #NULL otherwise
- */
-static QOSGraphNode *search_node(const char *key)
-{
-    return g_hash_table_lookup(node_table, key);
-}
-
-/**
- * get_edgelist(): returns the edge list (value) assigned to
- * the @key in the edge hash table.
- * This list will contain all edges with source equal to @key
- *
- * Returns: on success: the %QOSGraphEdgeList
- *          otherwise: abort()
- */
-static QOSGraphEdgeList *get_edgelist(const char *key)
-{
-    return g_hash_table_lookup(edge_table, key);
-}
-
-/**
- * search_list_edges(): search for an edge with destination @dest
- * in the given @edgelist.
- *
- * Returns: on success: the %QOSGraphEdge
- *          otherwise: #NULL
- */
-static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist,
-                                       const char *dest)
-{
-    QOSGraphEdge *tmp, *next;
-    if (!edgelist) {
-        return NULL;
-    }
-    QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) {
-        if (g_strcmp0(tmp->dest, dest) == 0) {
-            break;
-        }
-    }
-    return tmp;
-}
-
-/**
- * search_machine(): search for a machine @name in the node hash
- * table. A machine is the child of the root node.
- * This function forces the research in the childs of the root,
- * to check the node is a proper machine
- *
- * Returns: on success: the %QOSGraphNode
- *          otherwise: #NULL
- */
-static QOSGraphNode *search_machine(const char *name)
-{
-    QOSGraphNode *n;
-    QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT);
-    QOSGraphEdge *e = search_list_edges(root_list, name);
-    if (!e) {
-        return NULL;
-    }
-    n = search_node(e->dest);
-    if (n->type == QNODE_MACHINE) {
-        return n;
-    }
-    return NULL;
-}
-
-/**
- * create_interface(): checks if there is already
- * a node @node in the node hash table, if not
- * creates a node @node of type #QNODE_INTERFACE
- * and inserts it. If there is one, check it's
- * a #QNODE_INTERFACE and abort() if it's not.
- */
-static void create_interface(const char *node)
-{
-    QOSGraphNode *interface;
-    interface = search_node(node);
-    if (!interface) {
-        create_node(node, QNODE_INTERFACE);
-    } else if (interface->type != QNODE_INTERFACE) {
-        fprintf(stderr, "Error: Node %s is not an interface\n", node);
-        abort();
-    }
-}
-
-/**
- * build_machine_cmd_line(): builds the command line for the machine
- * @node. The node name must be a valid qemu identifier, since it
- * will be used to build the command line.
- *
- * It is also possible to pass an optional @args that will be
- * concatenated to the command line.
- *
- * For machines, prepend -M to the machine name. ", @rgs" is added
- * after the -M <machine> command.
- */
-static void build_machine_cmd_line(QOSGraphNode *node, const char *args)
-{
-    char *machine = qos_get_machine_type(node->name);
-    if (args) {
-        node->command_line = g_strconcat("-M ", machine, ",", args, NULL);
-    } else {
-        node->command_line = g_strconcat("-M ", machine, " ", NULL);
-    }
-}
-
-/**
- * build_driver_cmd_line(): builds the command line for the driver
- * @node. The node name must be a valid qemu identifier, since it
- * will be used to build the command line.
- *
- * Driver do not need additional command line, since it will be
- * provided by the edge options.
- *
- * For drivers, prepend -device to the node name.
- */
-static void build_driver_cmd_line(QOSGraphNode *node)
-{
-    node->command_line = g_strconcat(" -device ", node->name, NULL);
-}
-
-/* qos_print_cb(): callback prints all path found by the DFS algorithm. */
-static void qos_print_cb(QOSGraphNode *path, int length)
-{
-    #if QGRAPH_PRINT_DEBUG
-        printf("%d elements\n", length);
-
-        if (!path) {
-            return;
-        }
-
-        while (path->path_edge) {
-            printf("%s ", path->name);
-            switch (path->path_edge->type) {
-            case QEDGE_PRODUCES:
-                printf("--PRODUCES--> ");
-                break;
-            case QEDGE_CONSUMED_BY:
-                printf("--CONSUMED_BY--> ");
-                break;
-            case QEDGE_CONTAINS:
-                printf("--CONTAINS--> ");
-                break;
-            }
-            path = search_node(path->path_edge->dest);
-        }
-
-        printf("%s\n\n", path->name);
-    #endif
-}
-
-/* qos_push(): push a node @el and edge @e in the qos_node_stack */
-static void qos_push(QOSGraphNode *el, QOSStackElement *parent,
-                     QOSGraphEdge *e)
-{
-    int len = 0; /* root is not counted */
-    if (qos_node_tos == QOS_PATH_MAX_ELEMENT_SIZE) {
-        g_printerr("QOSStack: full stack, cannot push");
-        abort();
-    }
-
-    if (parent) {
-        len = parent->length + 1;
-    }
-    qos_node_stack[qos_node_tos++] = (QOSStackElement) {
-        .node = el,
-        .parent = parent,
-        .parent_edge = e,
-        .length = len,
-    };
-}
-
-/* qos_tos(): returns the top of stack, without popping */
-static QOSStackElement *qos_tos(void)
-{
-    return &qos_node_stack[qos_node_tos - 1];
-}
-
-/* qos_pop(): pops an element from the tos, setting it unvisited*/
-static QOSStackElement *qos_pop(void)
-{
-    if (qos_node_tos == 0) {
-        g_printerr("QOSStack: empty stack, cannot pop");
-        abort();
-    }
-    QOSStackElement *e = qos_tos();
-    e->node->visited = false;
-    qos_node_tos--;
-    return e;
-}
-
-/**
- * qos_reverse_path(): reverses the found path, going from
- * test-to-machine to machine-to-test
- */
-static QOSGraphNode *qos_reverse_path(QOSStackElement *el)
-{
-    if (!el) {
-        return NULL;
-    }
-
-    el->node->path_edge = NULL;
-
-    while (el->parent) {
-        el->parent->node->path_edge = el->parent_edge;
-        el = el->parent;
-    }
-
-    return el->node;
-}
-
-/**
- * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it
- * starts from the root @machine and walks all possible path until it
- * reaches a test node.
- * At that point, it reverses the path found and invokes the @callback.
- *
- * Being Depth First Search, time complexity is O(|V| + |E|), while
- * space is O(|V|). In this case, the maximum stack size is set by
- * QOS_PATH_MAX_ELEMENT_SIZE.
- */
-static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback)
-{
-    QOSGraphNode *v, *dest_node, *path;
-    QOSStackElement *s_el;
-    QOSGraphEdge *e, *next;
-    QOSGraphEdgeList *list;
-
-    qos_push(root, NULL, NULL);
-
-    while (qos_node_tos > 0) {
-        s_el = qos_tos();
-        v = s_el->node;
-        if (v->visited) {
-            qos_pop();
-            continue;
-        }
-        v->visited = true;
-        list = get_edgelist(v->name);
-        if (!list) {
-            qos_pop();
-            if (v->type == QNODE_TEST) {
-                v->visited = false;
-                path = qos_reverse_path(s_el);
-                callback(path, s_el->length);
-            }
-        } else {
-            QSLIST_FOREACH_SAFE(e, list, edge_list, next) {
-                dest_node = search_node(e->dest);
-
-                if (!dest_node) {
-                    fprintf(stderr, "node %s in %s -> %s does not exist\n",
-                            e->dest, v->name, e->dest);
-                    abort();
-                }
-
-                if (!dest_node->visited && dest_node->available) {
-                    qos_push(dest_node, s_el, e);
-                }
-            }
-        }
-    }
-}
-
-/* QGRAPH API*/
-
-QOSGraphNode *qos_graph_get_node(const char *key)
-{
-    return search_node(key);
-}
-
-bool qos_graph_has_node(const char *node)
-{
-    QOSGraphNode *n = search_node(node);
-    return n != NULL;
-}
-
-QOSNodeType qos_graph_get_node_type(const char *node)
-{
-    QOSGraphNode *n = search_node(node);
-    if (n) {
-        return n->type;
-    }
-    return -1;
-}
-
-bool qos_graph_get_node_availability(const char *node)
-{
-    QOSGraphNode *n = search_node(node);
-    if (n) {
-        return n->available;
-    }
-    return false;
-}
-
-QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest)
-{
-    QOSGraphEdgeList *list = get_edgelist(node);
-    return search_list_edges(list, dest);
-}
-
-QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge)
-{
-    if (!edge) {
-        return -1;
-    }
-    return edge->type;;
-}
-
-char *qos_graph_edge_get_dest(QOSGraphEdge *edge)
-{
-    if (!edge) {
-        return NULL;
-    }
-    return edge->dest;
-}
-
-void *qos_graph_edge_get_arg(QOSGraphEdge *edge)
-{
-    if (!edge) {
-        return NULL;
-    }
-    return edge->arg;
-}
-
-char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge)
-{
-    if (!edge) {
-        return NULL;
-    }
-    return edge->after_cmd_line;
-}
-
-char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge)
-{
-    if (!edge) {
-        return NULL;
-    }
-    return edge->before_cmd_line;
-}
-
-char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge)
-{
-    if (!edge) {
-        return NULL;
-    }
-    return edge->extra_device_opts;
-}
-
-char *qos_graph_edge_get_name(QOSGraphEdge *edge)
-{
-    if (!edge) {
-        return NULL;
-    }
-    return edge->edge_name;
-}
-
-bool qos_graph_has_edge(const char *start, const char *dest)
-{
-    QOSGraphEdgeList *list = get_edgelist(start);
-    QOSGraphEdge *e = search_list_edges(list, dest);
-    return e != NULL;
-}
-
-QOSGraphNode *qos_graph_get_machine(const char *node)
-{
-    return search_machine(node);
-}
-
-bool qos_graph_has_machine(const char *node)
-{
-    QOSGraphNode *m = search_machine(node);
-    return m != NULL;
-}
-
-void qos_print_graph(void)
-{
-    qos_graph_foreach_test_path(qos_print_cb);
-}
-
-void qos_graph_init(void)
-{
-    if (!node_table) {
-        node_table = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                           destroy_string, destroy_node);
-        create_node(QOS_ROOT, QNODE_DRIVER);
-    }
-
-    if (!edge_table) {
-        edge_table = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                           destroy_string, destroy_edges);
-    }
-}
-
-void qos_graph_destroy(void)
-{
-    if (node_table) {
-        g_hash_table_destroy(node_table);
-    }
-
-    if (edge_table) {
-        g_hash_table_destroy(edge_table);
-    }
-
-    node_table = NULL;
-    edge_table = NULL;
-}
-
-void qos_node_destroy(void *key)
-{
-    g_hash_table_remove(node_table, key);
-}
-
-void qos_edge_destroy(void *key)
-{
-    g_hash_table_remove(edge_table, key);
-}
-
-void qos_add_test(const char *name, const char *interface,
-                  QOSTestFunc test_func, QOSGraphTestOptions *opts)
-{
-    QOSGraphNode *node;
-    char *test_name = g_strdup_printf("%s-tests/%s", interface, name);;
-    QOSGraphTestOptions def_opts = { };
-
-    if (!opts) {
-        opts = &def_opts;
-    }
-    node = create_node(test_name, QNODE_TEST);
-    node->u.test.function = test_func;
-    node->u.test.arg = opts->arg;
-    assert(!opts->edge.arg);
-    assert(!opts->edge.size_arg);
-
-    node->u.test.before = opts->before;
-    node->u.test.subprocess = opts->subprocess;
-    node->available = true;
-    add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge);
-    g_free(test_name);
-}
-
-void qos_node_create_machine(const char *name, QOSCreateMachineFunc function)
-{
-    qos_node_create_machine_args(name, function, NULL);
-}
-
-void qos_node_create_machine_args(const char *name,
-                                  QOSCreateMachineFunc function,
-                                  const char *opts)
-{
-    QOSGraphNode *node = create_node(name, QNODE_MACHINE);
-    build_machine_cmd_line(node, opts);
-    node->u.machine.constructor = function;
-    add_edge(QOS_ROOT, name, QEDGE_CONTAINS, NULL);
-}
-
-void qos_node_create_driver(const char *name, QOSCreateDriverFunc function)
-{
-    QOSGraphNode *node = create_node(name, QNODE_DRIVER);
-    build_driver_cmd_line(node);
-    node->u.driver.constructor = function;
-}
-
-void qos_node_contains(const char *container, const char *contained,
-                       QOSGraphEdgeOptions *opts, ...)
-{
-    va_list va;
-
-    if (opts == NULL) {
-        add_edge(container, contained, QEDGE_CONTAINS, NULL);
-        return;
-    }
-
-    va_start(va, opts);
-    do {
-        add_edge(container, contained, QEDGE_CONTAINS, opts);
-        opts = va_arg(va, QOSGraphEdgeOptions *);
-    } while (opts != NULL);
-
-    va_end(va);
-}
-
-void qos_node_produces(const char *producer, const char *interface)
-{
-    create_interface(interface);
-    add_edge(producer, interface, QEDGE_PRODUCES, NULL);
-}
-
-void qos_node_consumes(const char *consumer, const char *interface,
-                       QOSGraphEdgeOptions *opts)
-{
-    create_interface(interface);
-    add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts);
-}
-
-void qos_graph_node_set_availability(const char *node, bool av)
-{
-    QOSGraphEdgeList *elist;
-    QOSGraphNode *n = search_node(node);
-    QOSGraphEdge *e, *next;
-    if (!n) {
-        return;
-    }
-    n->available = av;
-    elist = get_edgelist(node);
-    if (!elist) {
-        return;
-    }
-    QSLIST_FOREACH_SAFE(e, elist, edge_list, next) {
-        if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) {
-            qos_graph_node_set_availability(e->dest, av);
-        }
-    }
-}
-
-void qos_graph_foreach_test_path(QOSTestCallback fn)
-{
-    QOSGraphNode *root = qos_graph_get_node(QOS_ROOT);
-    qos_traverse_graph(root, fn);
-}
-
-QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts)
-{
-    QOSGraphObject *obj;
-
-    g_assert(node->type == QNODE_MACHINE);
-    obj = node->u.machine.constructor(qts);
-    obj->free = g_free;
-    return obj;
-}
-
-QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
-                               QGuestAllocator *alloc, void *arg)
-{
-    QOSGraphObject *obj;
-
-    g_assert(node->type == QNODE_DRIVER);
-    obj = node->u.driver.constructor(parent, alloc, arg);
-    obj->free = g_free;
-    return obj;
-}
-
-void qos_object_destroy(QOSGraphObject *obj)
-{
-    if (!obj) {
-        return;
-    }
-    if (obj->destructor) {
-        obj->destructor(obj);
-    }
-    if (obj->free) {
-        obj->free(obj);
-    }
-}
-
-void qos_object_queue_destroy(QOSGraphObject *obj)
-{
-    g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj);
-}
-
-void qos_object_start_hw(QOSGraphObject *obj)
-{
-    if (obj->start_hw) {
-        obj->start_hw(obj);
-    }
-}
-
-char *qos_get_machine_type(char *name)
-{
-    while (*name != '\0' && *name != '/') {
-        name++;
-    }
-
-    if (!*name || !name[1]) {
-        fprintf(stderr, "Machine name has to be of the form <arch>/<machine>\n");
-        abort();
-    }
-
-    return name + 1;
-}
-
-void qos_delete_cmd_line(const char *name)
-{
-    QOSGraphNode *node = search_node(name);
-    if (node) {
-        g_free(node->command_line);
-        node->command_line = NULL;
-    }
-}
diff --git a/tests/libqos/qgraph.h b/tests/libqos/qgraph.h
deleted file mode 100644 (file)
index 3a25dda..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef QGRAPH_H
-#define QGRAPH_H
-
-#include <gmodule.h>
-#include "qemu/module.h"
-#include "malloc.h"
-
-/* maximum path length */
-#define QOS_PATH_MAX_ELEMENT_SIZE 50
-
-typedef struct QOSGraphObject QOSGraphObject;
-typedef struct QOSGraphNode QOSGraphNode;
-typedef struct QOSGraphEdge QOSGraphEdge;
-typedef struct QOSGraphNodeOptions QOSGraphNodeOptions;
-typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions;
-typedef struct QOSGraphTestOptions QOSGraphTestOptions;
-
-/* Constructor for drivers, machines and test */
-typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc,
-                                      void *addr);
-typedef void *(*QOSCreateMachineFunc) (QTestState *qts);
-typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc);
-
-/* QOSGraphObject functions */
-typedef void *(*QOSGetDriver) (void *object, const char *interface);
-typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name);
-typedef void (*QOSDestructorFunc) (QOSGraphObject *object);
-typedef void (*QOSStartFunct) (QOSGraphObject *object);
-
-/* Test options functions */
-typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg);
-
-/**
- * SECTION: qgraph.h
- * @title: Qtest Driver Framework
- * @short_description: interfaces to organize drivers and tests
- *                     as nodes in a graph
- *
- * This Qgraph API provides all basic functions to create a graph
- * and instantiate nodes representing machines, drivers and tests
- * representing their relations with CONSUMES, PRODUCES, and CONTAINS
- * edges.
- *
- * The idea is to have a framework where each test asks for a specific
- * driver, and the framework takes care of allocating the proper devices
- * required and passing the correct command line arguments to QEMU.
- *
- * A node can be of four types:
- * - QNODE_MACHINE:   for example "arm/raspi2"
- * - QNODE_DRIVER:    for example "generic-sdhci"
- * - QNODE_INTERFACE: for example "sdhci" (interface for all "-sdhci" drivers)
- *                     an interface is not explicitly created, it will be auto-
- *                     matically instantiated when a node consumes or produces
- *                     it.
- * - QNODE_TEST:      for example "sdhci-test", consumes an interface and tests
- *                    the functions provided
- *
- * Notes for the nodes:
- * - QNODE_MACHINE: each machine struct must have a QGuestAllocator and
- *                  implement get_driver to return the allocator passing
- *                  "memory". The function can also return NULL if the
- *                  allocator is not set.
- * - QNODE_DRIVER:  driver names must be unique, and machines and nodes
- *                  planned to be "consumed" by other nodes must match QEMU
- *                  drivers name, otherwise they won't be discovered
- *
- * An edge relation between two nodes (drivers or machines) X and Y can be:
- * - X CONSUMES Y: Y can be plugged into X
- * - X PRODUCES Y: X provides the interface Y
- * - X CONTAINS Y: Y is part of X component
- *
- * Basic framework steps are the following:
- * - All nodes and edges are created in their respective
- *   machine/driver/test files
- * - The framework starts QEMU and asks for a list of available devices
- *   and machines (note that only machines and "consumed" nodes are mapped
- *   1:1 with QEMU devices)
- * - The framework walks the graph starting from the available machines and
- *   performs a Depth First Search for tests
- * - Once a test is found, the path is walked again and all drivers are
- *   allocated accordingly and the final interface is passed to the test
- * - The test is executed
- * - Unused objects are cleaned and the path discovery is continued
- *
- * Depending on the QEMU binary used, only some drivers/machines will be
- * available and only test that are reached by them will be executed.
- *
- * <example>
- *   <title>Creating new driver an its interface</title>
- *   <programlisting>
- #include "libqos/qgraph.h"
-
- struct My_driver {
-     QOSGraphObject obj;
-     Node_produced prod;
-     Node_contained cont;
- }
-
- static void my_destructor(QOSGraphObject *obj)
- {
-    g_free(obj);
- }
-
- static void my_get_driver(void *object, const char *interface) {
-    My_driver *dev = object;
-    if (!g_strcmp0(interface, "my_interface")) {
-        return &dev->prod;
-    }
-    abort();
- }
-
- static void my_get_device(void *object, const char *device) {
-    My_driver *dev = object;
-    if (!g_strcmp0(device, "my_driver_contained")) {
-        return &dev->cont;
-    }
-    abort();
- }
-
- static void *my_driver_constructor(void *node_consumed,
-                                    QOSGraphObject *alloc)
- {
-    My_driver dev = g_new(My_driver, 1);
-    // get the node pointed by the produce edge
-    dev->obj.get_driver = my_get_driver;
-    // get the node pointed by the contains
-    dev->obj.get_device = my_get_device;
-    // free the object
-    dev->obj.destructor = my_destructor;
-    do_something_with_node_consumed(node_consumed);
-    // set all fields of contained device
-    init_contained_device(&dev->cont);
-    return &dev->obj;
- }
-
- static void register_my_driver(void)
- {
-     qos_node_create_driver("my_driver", my_driver_constructor);
-     // contained drivers don't need a constructor,
-     // they will be init by the parent.
-     qos_node_create_driver("my_driver_contained", NULL);
-
-    // For the sake of this example, assume machine x86_64/pc contains
-    // "other_node".
-    // This relation, along with the machine and "other_node" creation,
-    // should be defined in the x86_64_pc-machine.c file.
-    // "my_driver" will then consume "other_node"
-    qos_node_contains("my_driver", "my_driver_contained");
-    qos_node_produces("my_driver", "my_interface");
-    qos_node_consumes("my_driver", "other_node");
- }
- *   </programlisting>
- * </example>
- *
- * In the above example, all possible types of relations are created:
- * node "my_driver" consumes, contains and produces other nodes.
- * more specifically:
- * x86_64/pc -->contains--> other_node <--consumes-- my_driver
- *                                                       |
- *                      my_driver_contained <--contains--+
- *                                                       |
- *                             my_interface <--produces--+
- *
- * or inverting the consumes edge in consumed_by:
- *
- * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
- *                                                           |
- *                          my_driver_contained <--contains--+
- *                                                           |
- *                                 my_interface <--produces--+
- *
- * <example>
- *   <title>Creating new test</title>
- *   <programlisting>
- * #include "libqos/qgraph.h"
- *
- * static void my_test_function(void *obj, void *data)
- * {
- *    Node_produced *interface_to_test = obj;
- *    // test interface_to_test
- * }
- *
- * static void register_my_test(void)
- * {
- *    qos_add_test("my_interface", "my_test", my_test_function);
- * }
- *
- * libqos_init(register_my_test);
- *
- *   </programlisting>
- * </example>
- *
- * Here a new test is created, consuming "my_interface" node
- * and creating a valid path from a machine to a test.
- * Final graph will be like this:
- * x86_64/pc -->contains--> other_node <--consumes-- my_driver
- *                                                        |
- *                       my_driver_contained <--contains--+
- *                                                        |
- *        my_test --consumes--> my_interface <--produces--+
- *
- * or inverting the consumes edge in consumed_by:
- *
- * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
- *                                                           |
- *                          my_driver_contained <--contains--+
- *                                                           |
- *        my_test <--consumed_by-- my_interface <--produces--+
- *
- * Assuming there the binary is
- * QTEST_QEMU_BINARY=x86_64-softmmu/qemu-system-x86_64
- * a valid test path will be:
- * "/x86_64/pc/other_node/my_driver/my_interface/my_test".
- *
- * Additional examples are also in libqos/test-qgraph.c
- *
- * Command line:
- * Command line is built by using node names and optional arguments
- * passed by the user when building the edges.
- *
- * There are three types of command line arguments:
- * - in node      : created from the node name. For example, machines will
- *                  have "-M <machine>" to its command line, while devices
- *                  "-device <device>". It is automatically done by the
- *                   framework.
- * - after node   : added as additional argument to the node name.
- *                  This argument is added optionally when creating edges,
- *                  by setting the parameter @after_cmd_line and
- *                  @extra_edge_opts in #QOSGraphEdgeOptions.
- *                  The framework automatically adds
- *                  a comma before @extra_edge_opts,
- *                  because it is going to add attributes
- *                  after the destination node pointed by
- *                  the edge containing these options, and automatically
- *                  adds a space before @after_cmd_line, because it
- *                  adds an additional device, not an attribute.
- * - before node  : added as additional argument to the node name.
- *                  This argument is added optionally when creating edges,
- *                  by setting the parameter @before_cmd_line in
- *                  #QOSGraphEdgeOptions. This attribute
- *                  is going to add attributes before the destination node
- *                  pointed by the edge containing these options. It is
- *                  helpful to commands that are not node-representable,
- *                  such as "-fdsev" or "-netdev".
- *
- * While adding command line in edges is always used, not all nodes names are
- * used in every path walk: this is because the contained or produced ones
- * are already added by QEMU, so only nodes that "consumes" will be used to
- * build the command line. Also, nodes that will have { "abstract" : true }
- * as QMP attribute will loose their command line, since they are not proper
- * devices to be added in QEMU.
- *
- * Example:
- *
- QOSGraphEdgeOptions opts = {
-     .arg = NULL,
-     .size_arg = 0,
-     .after_cmd_line = "-device other",
-     .before_cmd_line = "-netdev something",
-     .extra_edge_opts = "addr=04.0",
- };
- QOSGraphNode * node = qos_node_create_driver("my_node", constructor);
- qos_node_consumes_args("my_node", "interface", &opts);
- *
- * Will produce the following command line:
- * "-netdev something -device my_node,addr=04.0 -device other"
- */
-
-/**
- * Edge options to be passed to the contains/consumes *_args function.
- */
-struct QOSGraphEdgeOptions {
-    void *arg;                    /*
-                                   * optional arg that will be used by
-                                   * dest edge
-                                   */
-    uint32_t size_arg;            /*
-                                   * optional arg size that will be used by
-                                   * dest edge
-                                   */
-    const char *extra_device_opts;/*
-                                   *optional additional command line for dest
-                                   * edge, used to add additional attributes
-                                   * *after* the node command line, the
-                                   * framework automatically prepends ","
-                                   * to this argument.
-                                   */
-    const char *before_cmd_line;  /*
-                                   * optional additional command line for dest
-                                   * edge, used to add additional attributes
-                                   * *before* the node command line, usually
-                                   * other non-node represented commands,
-                                   * like "-fdsev synt"
-                                   */
-    const char *after_cmd_line;   /*
-                                   * optional extra command line to be added
-                                   * after the device command. This option
-                                   * is used to add other devices
-                                   * command line that depend on current node.
-                                   * Automatically prepends " " to this
-                                   * argument
-                                   */
-    const char *edge_name;        /*
-                                   * optional edge to differentiate multiple
-                                   * devices with same node name
-                                   */
-};
-
-/**
- * Test options to be passed to the test functions.
- */
-struct QOSGraphTestOptions {
-    QOSGraphEdgeOptions edge;   /* edge arguments that will be used by test.
-                                 * Note that test *does not* use edge_name,
-                                 * and uses instead arg and size_arg as
-                                 * data arg for its test function.
-                                 */
-    void *arg;                  /* passed to the .before function, or to the
-                                 * test function if there is no .before
-                                 * function
-                                 */
-    QOSBeforeTest before;       /* executed before the test. Can add
-                                 * additional parameters to the command line
-                                 * and modify the argument to the test function.
-                                 */
-    bool subprocess;            /* run the test in a subprocess */
-};
-
-/**
- * Each driver, test or machine of this framework will have a
- * QOSGraphObject as first field.
- *
- * This set of functions offered by QOSGraphObject are executed
- * in different stages of the framework:
- * - get_driver / get_device : Once a machine-to-test path has been
- * found, the framework traverses it again and allocates all the
- * nodes, using the provided constructor. To satisfy their relations,
- * i.e. for produces or contains, where a struct constructor needs
- * an external parameter represented by the previous node,
- * the framework will call get_device (for contains) or
- * get_driver (for produces), depending on the edge type, passing
- * them the name of the next node to be taken and getting from them
- * the corresponding pointer to the actual structure of the next node to
- * be used in the path.
- *
- * - start_hw: This function is executed after all the path objects
- * have been allocated, but before the test is run. It starts the hw, setting
- * the initial configurations (*_device_enable) and making it ready for the
- * test.
- *
- * - destructor: Opposite to the node constructor, destroys the object.
- * This function is called after the test has been executed, and performs
- * a complete cleanup of each node allocated field. In case no constructor
- * is provided, no destructor will be called.
- *
- */
-struct QOSGraphObject {
-    /* for produces edges, returns void * */
-    QOSGetDriver get_driver;
-    /* for contains edges, returns a QOSGraphObject * */
-    QOSGetDevice get_device;
-    /* start the hw, get ready for the test */
-    QOSStartFunct start_hw;
-    /* destroy this QOSGraphObject */
-    QOSDestructorFunc destructor;
-    /* free the memory associated to the QOSGraphObject and its contained
-     * children */
-    GDestroyNotify free;
-};
-
-/**
- * qos_graph_init(): initialize the framework, creates two hash
- * tables: one for the nodes and another for the edges.
- */
-void qos_graph_init(void);
-
-/**
- * qos_graph_destroy(): deallocates all the hash tables,
- * freeing all nodes and edges.
- */
-void qos_graph_destroy(void);
-
-/**
- * qos_node_destroy(): removes and frees a node from the,
- * nodes hash table.
- */
-void qos_node_destroy(void *key);
-
-/**
- * qos_edge_destroy(): removes and frees an edge from the,
- * edges hash table.
- */
-void qos_edge_destroy(void *key);
-
-/**
- * qos_add_test(): adds a test node @name to the nodes hash table.
- *
- * The test will consume a @interface node, and once the
- * graph walking algorithm has found it, the @test_func will be
- * executed. It also has the possibility to
- * add an optional @opts (see %QOSGraphNodeOptions).
- *
- * For tests, opts->edge.arg and size_arg represent the arg to pass
- * to @test_func
- */
-void qos_add_test(const char *name, const char *interface,
-                  QOSTestFunc test_func,
-                  QOSGraphTestOptions *opts);
-
-/**
- * qos_node_create_machine(): creates the machine @name and
- * adds it to the node hash table.
- *
- * This node will be of type QNODE_MACHINE and have @function
- * as constructor
- */
-void qos_node_create_machine(const char *name, QOSCreateMachineFunc function);
-
-/**
- * qos_node_create_machine_args(): same as qos_node_create_machine,
- * but with the possibility to add an optional ", @opts" after -M machine
- * command line.
- */
-void qos_node_create_machine_args(const char *name,
-                                  QOSCreateMachineFunc function,
-                                  const char *opts);
-
-/**
- * qos_node_create_driver(): creates the driver @name and
- * adds it to the node hash table.
- *
- * This node will be of type QNODE_DRIVER and have @function
- * as constructor
- */
-void qos_node_create_driver(const char *name, QOSCreateDriverFunc function);
-
-/**
- * qos_node_contains(): creates one or more edges of type QEDGE_CONTAINS
- * and adds them to the edge list mapped to @container in the
- * edge hash table.
- *
- * The edges will have @container as source and @contained as destination.
- *
- * If @opts is NULL, a single edge will be added with no options.
- * If @opts is non-NULL, the arguments after @contained represent a
- * NULL-terminated list of %QOSGraphEdgeOptions structs, and an
- * edge will be added for each of them.
- *
- * This function can be useful when there are multiple devices
- * with the same node name contained in a machine/other node
- *
- * For example, if "arm/raspi2" contains 2 "generic-sdhci"
- * devices, the right commands will be:
- * qos_node_create_machine("arm/raspi2");
- * qos_node_create_driver("generic-sdhci", constructor);
- * //assume rest of the fields are set NULL
- * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" };
- * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" };
- * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL);
- *
- * Of course this also requires that the @container's get_device function
- * should implement a case for "emmc" and "sdcard".
- *
- * For contains, op1.arg and op1.size_arg represent the arg to pass
- * to @contained constructor to properly initialize it.
- */
-void qos_node_contains(const char *container, const char *contained,
-                       QOSGraphEdgeOptions *opts, ...);
-
-/**
- * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and
- * adds it to the edge list mapped to @producer in the
- * edge hash table.
- *
- * This edge will have @producer as source and @interface as destination.
- */
-void qos_node_produces(const char *producer, const char *interface);
-
-/**
- * qos_node_consumes():  creates an edge of type QEDGE_CONSUMED_BY and
- * adds it to the edge list mapped to @interface in the
- * edge hash table.
- *
- * This edge will have @interface as source and @consumer as destination.
- * It also has the possibility to add an optional @opts
- * (see %QOSGraphEdgeOptions)
- */
-void qos_node_consumes(const char *consumer, const char *interface,
-                       QOSGraphEdgeOptions *opts);
-
-/**
- * qos_invalidate_command_line(): invalidates current command line, so that
- * qgraph framework cannot try to cache the current command line and
- * forces QEMU to restart.
- */
-void qos_invalidate_command_line(void);
-
-/**
- * qos_get_current_command_line(): return the command line required by the
- * machine and driver objects.  This is the same string that was passed to
- * the test's "before" callback, if any.
- */
-const char *qos_get_current_command_line(void);
-
-/**
- * qos_allocate_objects():
- * @qts: The #QTestState that will be referred to by the machine object.
- * @alloc: Where to store the allocator for the machine object, or %NULL.
- *
- * Allocate driver objects for the current test
- * path, but relative to the QTestState @qts.
- *
- * Returns a test object just like the one that was passed to
- * the test function, but relative to @qts.
- */
-void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc);
-
-/**
- * qos_object_destroy(): calls the destructor for @obj
- */
-void qos_object_destroy(QOSGraphObject *obj);
-
-/**
- * qos_object_queue_destroy(): queue the destructor for @obj so that it is
- * called at the end of the test
- */
-void qos_object_queue_destroy(QOSGraphObject *obj);
-
-/**
- * qos_object_start_hw(): calls the start_hw function for @obj
- */
-void qos_object_start_hw(QOSGraphObject *obj);
-
-/**
- * qos_machine_new(): instantiate a new machine node
- * @node: A machine node to be instantiated
- * @qts: The #QTestState that will be referred to by the machine object.
- *
- * Returns a machine object.
- */
-QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts);
-
-/**
- * qos_machine_new(): instantiate a new driver node
- * @node: A driver node to be instantiated
- * @parent: A #QOSGraphObject to be consumed by the new driver node
- * @alloc: An allocator to be used by the new driver node.
- * @arg: The argument for the consumed-by edge to @node.
- *
- * Calls the constructor for the driver object.
- */
-QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
-                               QGuestAllocator *alloc, void *arg);
-
-
-#endif
diff --git a/tests/libqos/qgraph_internal.h b/tests/libqos/qgraph_internal.h
deleted file mode 100644 (file)
index f4734c8..0000000
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef QGRAPH_INTERNAL_H
-#define QGRAPH_INTERNAL_H
-
-/* This header is declaring additional helper functions defined in
- * libqos/qgraph.c
- * It should not be included in tests
- */
-
-#include "libqos/qgraph.h"
-
-typedef struct QOSGraphMachine QOSGraphMachine;
-typedef enum QOSEdgeType QOSEdgeType;
-typedef enum QOSNodeType QOSNodeType;
-
-/* callback called when the walk path algorithm found a
- * valid path
- */
-typedef void (*QOSTestCallback) (QOSGraphNode *path, int len);
-
-/* edge types*/
-enum QOSEdgeType {
-    QEDGE_CONTAINS,
-    QEDGE_PRODUCES,
-    QEDGE_CONSUMED_BY
-};
-
-/* node types*/
-enum QOSNodeType {
-    QNODE_MACHINE,
-    QNODE_DRIVER,
-    QNODE_INTERFACE,
-    QNODE_TEST
-};
-
-/* Graph Node */
-struct QOSGraphNode {
-    QOSNodeType type;
-    bool available;     /* set by QEMU via QMP, used during graph walk */
-    bool visited;       /* used during graph walk */
-    char *name;         /* used to identify the node */
-    char *command_line; /* used to start QEMU at test execution */
-    union {
-        struct {
-            QOSCreateDriverFunc constructor;
-        } driver;
-        struct {
-            QOSCreateMachineFunc constructor;
-        } machine;
-        struct {
-            QOSTestFunc function;
-            void *arg;
-            QOSBeforeTest before;
-            bool subprocess;
-        } test;
-    } u;
-
-    /**
-     * only used when traversing the path, never rely on that except in the
-     * qos_traverse_graph callback function
-     */
-    QOSGraphEdge *path_edge;
-};
-
-/**
- * qos_graph_get_node(): returns the node mapped to that @key.
- * It performs an hash map search O(1)
- *
- * Returns: on success: the %QOSGraphNode
- *          otherwise: #NULL
- */
-QOSGraphNode *qos_graph_get_node(const char *key);
-
-/**
- * qos_graph_has_node(): returns #TRUE if the node
- * has map has a node mapped to that @key.
- */
-bool qos_graph_has_node(const char *node);
-
-/**
- * qos_graph_get_node_type(): returns the %QOSNodeType
- * of the node @node.
- * It performs an hash map search O(1)
- * Returns: on success: the %QOSNodeType
- *          otherwise: #-1
- */
-QOSNodeType qos_graph_get_node_type(const char *node);
-
-/**
- * qos_graph_get_node_availability(): returns the availability (boolean)
- * of the node @node.
- */
-bool qos_graph_get_node_availability(const char *node);
-
-/**
- * qos_graph_get_edge(): returns the edge
- * linking of the node @node with @dest.
- *
- * Returns: on success: the %QOSGraphEdge
- *          otherwise: #NULL
- */
-QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest);
-
-/**
- * qos_graph_edge_get_type(): returns the edge type
- * of the edge @edge.
- *
- * Returns: on success: the %QOSEdgeType
- *          otherwise: #-1
- */
-QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge);
-
-/**
- * qos_graph_edge_get_dest(): returns the name of the node
- * pointed as destination of edge @edge.
- *
- * Returns: on success: the destination
- *          otherwise: #NULL
- */
-char *qos_graph_edge_get_dest(QOSGraphEdge *edge);
-
-/**
- * qos_graph_has_edge(): returns #TRUE if there
- * exists an edge from @start to @dest.
- */
-bool qos_graph_has_edge(const char *start, const char *dest);
-
-/**
- * qos_graph_edge_get_arg(): returns the args assigned
- * to that @edge.
- *
- * Returns: on success: the arg
- *          otherwise: #NULL
- */
-void *qos_graph_edge_get_arg(QOSGraphEdge *edge);
-
-/**
- * qos_graph_edge_get_after_cmd_line(): returns the edge
- * command line that will be added after all the node arguments
- * and all the before_cmd_line arguments.
- *
- * Returns: on success: the char* arg
- *          otherwise: #NULL
- */
-char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge);
-
-/**
- * qos_graph_edge_get_before_cmd_line(): returns the edge
- * command line that will be added before the node command
- * line argument.
- *
- * Returns: on success: the char* arg
- *          otherwise: #NULL
- */
-char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge);
-
-/**
- * qos_graph_edge_get_extra_device_opts(): returns the arg
- * command line that will be added to the node command
- * line argument.
- *
- * Returns: on success: the char* arg
- *          otherwise: #NULL
- */
-char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge);
-
-/**
- * qos_graph_edge_get_name(): returns the name
- * assigned to the destination node (different only)
- * if there are multiple devices with the same node name
- * e.g. a node has two "generic-sdhci", "emmc" and "sdcard"
- * there will be two edges with edge_name ="emmc" and "sdcard"
- *
- * Returns always the char* edge_name
- */
-char *qos_graph_edge_get_name(QOSGraphEdge *edge);
-
-/**
- * qos_graph_get_machine(): returns the machine assigned
- * to that @node name.
- *
- * It performs a search only trough the list of machines
- * (i.e. the QOS_ROOT child).
- *
- * Returns: on success: the %QOSGraphNode
- *          otherwise: #NULL
- */
-QOSGraphNode *qos_graph_get_machine(const char *node);
-
-/**
- * qos_graph_has_machine(): returns #TRUE if the node
- * has map has a node mapped to that @node.
- */
-bool qos_graph_has_machine(const char *node);
-
-
-/**
- * qos_print_graph(): walks the graph and prints
- * all machine-to-test paths.
- */
-void qos_print_graph(void);
-
-/**
- * qos_graph_foreach_test_path(): executes the Depth First search
- * algorithm and applies @fn to all discovered paths.
- *
- * See qos_traverse_graph() in qgraph.c for more info on
- * how it works.
- */
-void qos_graph_foreach_test_path(QOSTestCallback fn);
-
-/**
- * qos_get_machine_type(): return QEMU machine type for a machine node.
- * This function requires every machine @name to be in the form
- * <arch>/<machine_name>, like "arm/raspi2" or "x86_64/pc".
- *
- * The function will validate the format and return a pointer to
- * @machine to <machine_name>.  For example, when passed "x86_64/pc"
- * it will return "pc".
- *
- * Note that this function *does not* allocate any new string.
- */
-char *qos_get_machine_type(char *name);
-
-/**
- * qos_delete_cmd_line(): delete the
- * command line present in node mapped with key @name.
- *
- * This function is called when the QMP query returns a node with
- * { "abstract" : true } attribute.
- */
-void qos_delete_cmd_line(const char *name);
-
-/**
- * qos_graph_node_set_availability(): sets the node identified
- * by @node with availability @av.
- */
-void qos_graph_node_set_availability(const char *node, bool av);
-
-#endif
diff --git a/tests/libqos/rtas.c b/tests/libqos/rtas.c
deleted file mode 100644 (file)
index d81ff42..0000000
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/rtas.h"
-
-static void qrtas_copy_args(QTestState *qts, uint64_t target_args,
-                            uint32_t nargs, uint32_t *args)
-{
-    int i;
-
-    for (i = 0; i < nargs; i++) {
-        qtest_writel(qts, target_args + i * sizeof(uint32_t), args[i]);
-    }
-}
-
-static void qrtas_copy_ret(QTestState *qts, uint64_t target_ret,
-                           uint32_t nret, uint32_t *ret)
-{
-    int i;
-
-    for (i = 0; i < nret; i++) {
-        ret[i] = qtest_readl(qts, target_ret + i * sizeof(uint32_t));
-    }
-}
-
-static uint64_t qrtas_call(QTestState *qts, QGuestAllocator *alloc,
-                           const char *name,
-                           uint32_t nargs, uint32_t *args,
-                           uint32_t nret, uint32_t *ret)
-{
-    uint64_t res;
-    uint64_t target_args, target_ret;
-
-    target_args = guest_alloc(alloc, nargs * sizeof(uint32_t));
-    target_ret = guest_alloc(alloc, nret * sizeof(uint32_t));
-
-    qrtas_copy_args(qts, target_args, nargs, args);
-    res = qtest_rtas_call(qts, name, nargs, target_args, nret, target_ret);
-    qrtas_copy_ret(qts, target_ret, nret, ret);
-
-    guest_free(alloc, target_ret);
-    guest_free(alloc, target_args);
-
-    return res;
-}
-
-int qrtas_get_time_of_day(QTestState *qts, QGuestAllocator *alloc,
-                          struct tm *tm, uint32_t *ns)
-{
-    int res;
-    uint32_t ret[8];
-
-    res = qrtas_call(qts, alloc, "get-time-of-day", 0, NULL, 8, ret);
-    if (res != 0) {
-        return res;
-    }
-
-    res = ret[0];
-    memset(tm, 0, sizeof(*tm));
-    tm->tm_year = ret[1] - 1900;
-    tm->tm_mon = ret[2] - 1;
-    tm->tm_mday = ret[3];
-    tm->tm_hour = ret[4];
-    tm->tm_min = ret[5];
-    tm->tm_sec = ret[6];
-    *ns = ret[7];
-
-    return res;
-}
-
-uint32_t qrtas_ibm_read_pci_config(QTestState *qts, QGuestAllocator *alloc,
-                                   uint64_t buid,
-                                   uint32_t addr, uint32_t size)
-{
-    int res;
-    uint32_t args[4], ret[2];
-
-    args[0] = addr;
-    args[1] = buid >> 32;
-    args[2] = buid & 0xffffffff;
-    args[3] = size;
-    res = qrtas_call(qts, alloc, "ibm,read-pci-config", 4, args, 2, ret);
-    if (res != 0) {
-        return -1;
-    }
-
-    if (ret[0] != 0) {
-        return -1;
-    }
-
-    return ret[1];
-}
-
-int qrtas_ibm_write_pci_config(QTestState *qts, QGuestAllocator *alloc,
-                               uint64_t buid,
-                               uint32_t addr, uint32_t size, uint32_t val)
-{
-    int res;
-    uint32_t args[5], ret[1];
-
-    args[0] = addr;
-    args[1] = buid >> 32;
-    args[2] = buid & 0xffffffff;
-    args[3] = size;
-    args[4] = val;
-    res = qrtas_call(qts, alloc, "ibm,write-pci-config", 5, args, 1, ret);
-    if (res != 0) {
-        return -1;
-    }
-
-    if (ret[0] != 0) {
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/tests/libqos/rtas.h b/tests/libqos/rtas.h
deleted file mode 100644 (file)
index 459e23a..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_RTAS_H
-#define LIBQOS_RTAS_H
-#include "libqos/malloc.h"
-
-int qrtas_get_time_of_day(QTestState *qts, QGuestAllocator *alloc,
-                          struct tm *tm, uint32_t *ns);
-uint32_t qrtas_ibm_read_pci_config(QTestState *qts, QGuestAllocator *alloc,
-                                   uint64_t buid, uint32_t addr, uint32_t size);
-int qrtas_ibm_write_pci_config(QTestState *qts, QGuestAllocator *alloc,
-                               uint64_t buid, uint32_t addr, uint32_t size,
-                               uint32_t val);
-#endif /* LIBQOS_RTAS_H */
diff --git a/tests/libqos/sdhci.c b/tests/libqos/sdhci.c
deleted file mode 100644 (file)
index 309794b..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/qgraph.h"
-#include "pci.h"
-#include "qemu/module.h"
-#include "sdhci.h"
-#include "hw/pci/pci.h"
-
-static void set_qsdhci_fields(QSDHCI *s, uint8_t version, uint8_t baseclock,
-                              bool sdma, uint64_t reg)
-{
-    s->props.version = version;
-    s->props.baseclock = baseclock;
-    s->props.capab.sdma = sdma;
-    s->props.capab.reg = reg;
-}
-
-/* Memory mapped implementation of QSDHCI */
-
-static uint16_t sdhci_mm_readw(QSDHCI *s, uint32_t reg)
-{
-    QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
-    return qtest_readw(smm->qts, smm->addr + reg);
-}
-
-static uint64_t sdhci_mm_readq(QSDHCI *s, uint32_t reg)
-{
-    QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
-    return qtest_readq(smm->qts, smm->addr + reg);
-}
-
-static void sdhci_mm_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
-{
-    QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
-    qtest_writeq(smm->qts, smm->addr + reg, val);
-}
-
-static void *sdhci_mm_get_driver(void *obj, const char *interface)
-{
-    QSDHCI_MemoryMapped *smm = obj;
-    if (!g_strcmp0(interface, "sdhci")) {
-        return &smm->sdhci;
-    }
-    fprintf(stderr, "%s not present in generic-sdhci\n", interface);
-    g_assert_not_reached();
-}
-
-void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts,
-                       uint32_t addr, QSDHCIProperties *common)
-{
-    sdhci->obj.get_driver = sdhci_mm_get_driver;
-    sdhci->sdhci.readw = sdhci_mm_readw;
-    sdhci->sdhci.readq = sdhci_mm_readq;
-    sdhci->sdhci.writeq = sdhci_mm_writeq;
-    memcpy(&sdhci->sdhci.props, common, sizeof(QSDHCIProperties));
-    sdhci->addr = addr;
-    sdhci->qts = qts;
-}
-
-/* PCI implementation of QSDHCI */
-
-static uint16_t sdhci_pci_readw(QSDHCI *s, uint32_t reg)
-{
-    QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
-    return qpci_io_readw(&spci->dev, spci->mem_bar, reg);
-}
-
-static uint64_t sdhci_pci_readq(QSDHCI *s, uint32_t reg)
-{
-    QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
-    return qpci_io_readq(&spci->dev, spci->mem_bar, reg);
-}
-
-static void sdhci_pci_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
-{
-    QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
-    return qpci_io_writeq(&spci->dev, spci->mem_bar, reg, val);
-}
-
-static void *sdhci_pci_get_driver(void *object, const char *interface)
-{
-    QSDHCI_PCI *spci = object;
-    if (!g_strcmp0(interface, "sdhci")) {
-        return &spci->sdhci;
-    }
-
-    fprintf(stderr, "%s not present in sdhci-pci\n", interface);
-    g_assert_not_reached();
-}
-
-static void sdhci_pci_start_hw(QOSGraphObject *obj)
-{
-    QSDHCI_PCI *spci = (QSDHCI_PCI *)obj;
-    qpci_device_enable(&spci->dev);
-}
-
-static void sdhci_destructor(QOSGraphObject *obj)
-{
-    QSDHCI_PCI *spci = (QSDHCI_PCI *)obj;
-    qpci_iounmap(&spci->dev, spci->mem_bar);
-}
-
-static void *sdhci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
-{
-    QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1);
-    QPCIBus *bus = pci_bus;
-    uint64_t barsize;
-
-    qpci_device_init(&spci->dev, bus, addr);
-    spci->mem_bar = qpci_iomap(&spci->dev, 0, &barsize);
-    spci->sdhci.readw = sdhci_pci_readw;
-    spci->sdhci.readq = sdhci_pci_readq;
-    spci->sdhci.writeq = sdhci_pci_writeq;
-    set_qsdhci_fields(&spci->sdhci, 2, 0, 1, 0x057834b4);
-
-    spci->obj.get_driver = sdhci_pci_get_driver;
-    spci->obj.start_hw = sdhci_pci_start_hw;
-    spci->obj.destructor = sdhci_destructor;
-    return &spci->obj;
-}
-
-static void qsdhci_register_nodes(void)
-{
-    QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(4, 0),
-        .vendor_id = PCI_VENDOR_ID_REDHAT,
-        .device_id = PCI_DEVICE_ID_REDHAT_SDHCI,
-    };
-
-    QOSGraphEdgeOptions opts = {
-        .extra_device_opts = "addr=04.0",
-    };
-
-    /* generic-sdhci */
-    qos_node_create_driver("generic-sdhci", NULL);
-    qos_node_produces("generic-sdhci", "sdhci");
-
-    /* sdhci-pci */
-    add_qpci_address(&opts, &addr);
-    qos_node_create_driver("sdhci-pci", sdhci_pci_create);
-    qos_node_produces("sdhci-pci", "sdhci");
-    qos_node_consumes("sdhci-pci", "pci-bus", &opts);
-
-}
-
-libqos_init(qsdhci_register_nodes);
diff --git a/tests/libqos/sdhci.h b/tests/libqos/sdhci.h
deleted file mode 100644 (file)
index a88b45a..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef QGRAPH_QSDHCI_H
-#define QGRAPH_QSDHCI_H
-
-#include "libqos/qgraph.h"
-#include "pci.h"
-
-typedef struct QSDHCI QSDHCI;
-typedef struct QSDHCI_MemoryMapped QSDHCI_MemoryMapped;
-typedef struct QSDHCI_PCI  QSDHCI_PCI;
-typedef struct QSDHCIProperties QSDHCIProperties;
-
-/* Properties common to all QSDHCI devices */
-struct QSDHCIProperties {
-    uint8_t version;
-    uint8_t baseclock;
-    struct {
-        bool sdma;
-        uint64_t reg;
-    } capab;
-};
-
-struct QSDHCI {
-    uint16_t (*readw)(QSDHCI *s, uint32_t reg);
-    uint64_t (*readq)(QSDHCI *s, uint32_t reg);
-    void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val);
-    QSDHCIProperties props;
-};
-
-/* Memory Mapped implementation of QSDHCI */
-struct QSDHCI_MemoryMapped {
-    QOSGraphObject obj;
-    QTestState *qts;
-    QSDHCI sdhci;
-    uint64_t addr;
-};
-
-/* PCI implementation of QSDHCI */
-struct QSDHCI_PCI {
-    QOSGraphObject obj;
-    QPCIDevice dev;
-    QSDHCI sdhci;
-    QPCIBar mem_bar;
-};
-
-/**
- * qos_init_sdhci_mm(): external constructor used by all drivers/machines
- * that "contain" a #QSDHCI_MemoryMapped driver
- */
-void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts,
-                       uint32_t addr, QSDHCIProperties *common);
-
-#endif
diff --git a/tests/libqos/tpci200.c b/tests/libqos/tpci200.c
deleted file mode 100644 (file)
index ae590a4..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * QTest testcase for tpci200 PCI-IndustryPack bridge
- *
- * Copyright (c) 2014 SUSE LINUX Products GmbH
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/qgraph.h"
-#include "libqos/pci.h"
-
-typedef struct QTpci200 QTpci200;
-typedef struct QIpack QIpack;
-
-struct QIpack {
-
-};
-struct QTpci200 {
-    QOSGraphObject obj;
-    QPCIDevice dev;
-    QIpack ipack;
-};
-
-/* tpci200 */
-static void *tpci200_get_driver(void *obj, const char *interface)
-{
-    QTpci200 *tpci200 = obj;
-    if (!g_strcmp0(interface, "ipack")) {
-        return &tpci200->ipack;
-    }
-    if (!g_strcmp0(interface, "pci-device")) {
-        return &tpci200->dev;
-    }
-
-    fprintf(stderr, "%s not present in tpci200\n", interface);
-    g_assert_not_reached();
-}
-
-static void *tpci200_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
-{
-    QTpci200 *tpci200 = g_new0(QTpci200, 1);
-    QPCIBus *bus = pci_bus;
-
-    qpci_device_init(&tpci200->dev, bus, addr);
-    tpci200->obj.get_driver = tpci200_get_driver;
-    return &tpci200->obj;
-}
-
-static void tpci200_register_nodes(void)
-{
-    QOSGraphEdgeOptions opts = {
-        .extra_device_opts = "addr=04.0,id=ipack0",
-    };
-    add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
-
-    qos_node_create_driver("tpci200", tpci200_create);
-    qos_node_consumes("tpci200", "pci-bus", &opts);
-    qos_node_produces("tpci200", "ipack");
-    qos_node_produces("tpci200", "pci-device");
-}
-
-libqos_init(tpci200_register_nodes);
diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c
deleted file mode 100644 (file)
index d7a9cb3..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * common code shared by usb tests
- *
- * Copyright (c) 2014 Red Hat, Inc
- *
- * Authors:
- *     Gerd Hoffmann <kraxel@redhat.com>
- *     John Snow <jsnow@redhat.com>
- *     Igor Mammedov <imammedo@redhat.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "hw/usb/uhci-regs.h"
-#include "libqos/usb.h"
-
-void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar)
-{
-    hc->dev = qpci_device_find(pcibus, devfn);
-    g_assert(hc->dev != NULL);
-    qpci_device_enable(hc->dev);
-    hc->bar = qpci_iomap(hc->dev, bar, NULL);
-}
-
-void uhci_deinit(struct qhc *hc)
-{
-    g_free(hc->dev);
-}
-
-void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
-{
-    uint16_t value = qpci_io_readw(hc->dev, hc->bar, 0x10 + 2 * port);
-    uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
-
-    g_assert((value & mask) == (expect & mask));
-}
-
-void usb_test_hotplug(QTestState *qts, const char *hcd_id, const char *port,
-                      void (*port_check)(void))
-{
-    char *id = g_strdup_printf("usbdev%s", port);
-    char *bus = g_strdup_printf("%s.0", hcd_id);
-
-    qtest_qmp_device_add(qts, "usb-tablet", id, "{'port': %s, 'bus': %s}",
-                         port, bus);
-
-    if (port_check) {
-        port_check();
-    }
-
-    qtest_qmp_device_del(qts, id);
-
-    g_free(bus);
-    g_free(id);
-}
diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h
deleted file mode 100644 (file)
index eeced39..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef LIBQOS_USB_H
-#define LIBQOS_USB_H
-
-#include "libqos/pci-pc.h"
-
-struct qhc {
-    QPCIDevice *dev;
-    QPCIBar bar;
-};
-
-void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc,
-                       uint32_t devfn, int bar);
-void uhci_port_test(struct qhc *hc, int port, uint16_t expect);
-void uhci_deinit(struct qhc *hc);
-
-void usb_test_hotplug(QTestState *qts, const char *bus_name, const char *port,
-                      void (*port_check)(void));
-#endif
diff --git a/tests/libqos/virtio-9p.c b/tests/libqos/virtio-9p.c
deleted file mode 100644 (file)
index 77dbfb6..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "standard-headers/linux/virtio_ids.h"
-#include "libqos/virtio-9p.h"
-#include "libqos/qgraph.h"
-
-static QGuestAllocator *alloc;
-
-static void virtio_9p_cleanup(QVirtio9P *interface)
-{
-    qvirtqueue_cleanup(interface->vdev->bus, interface->vq, alloc);
-}
-
-static void virtio_9p_setup(QVirtio9P *interface)
-{
-    uint64_t features;
-
-    features = qvirtio_get_features(interface->vdev);
-    features &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
-    qvirtio_set_features(interface->vdev, features);
-
-    interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0);
-    qvirtio_set_driver_ok(interface->vdev);
-}
-
-/* virtio-9p-device */
-static void virtio_9p_device_destructor(QOSGraphObject *obj)
-{
-    QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj;
-    QVirtio9P *v9p = &v_9p->v9p;
-
-    virtio_9p_cleanup(v9p);
-}
-
-static void virtio_9p_device_start_hw(QOSGraphObject *obj)
-{
-    QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj;
-    QVirtio9P *v9p = &v_9p->v9p;
-
-    virtio_9p_setup(v9p);
-}
-
-static void *virtio_9p_get_driver(QVirtio9P *v_9p,
-                                         const char *interface)
-{
-    if (!g_strcmp0(interface, "virtio-9p")) {
-        return v_9p;
-    }
-    if (!g_strcmp0(interface, "virtio")) {
-        return v_9p->vdev;
-    }
-
-    fprintf(stderr, "%s not present in virtio-9p-device\n", interface);
-    g_assert_not_reached();
-}
-
-static void *virtio_9p_device_get_driver(void *object, const char *interface)
-{
-    QVirtio9PDevice *v_9p = object;
-    return virtio_9p_get_driver(&v_9p->v9p, interface);
-}
-
-static void *virtio_9p_device_create(void *virtio_dev,
-                                     QGuestAllocator *t_alloc,
-                                     void *addr)
-{
-    QVirtio9PDevice *virtio_device = g_new0(QVirtio9PDevice, 1);
-    QVirtio9P *interface = &virtio_device->v9p;
-
-    interface->vdev = virtio_dev;
-    alloc = t_alloc;
-
-    virtio_device->obj.destructor = virtio_9p_device_destructor;
-    virtio_device->obj.get_driver = virtio_9p_device_get_driver;
-    virtio_device->obj.start_hw = virtio_9p_device_start_hw;
-
-    return &virtio_device->obj;
-}
-
-/* virtio-9p-pci */
-static void virtio_9p_pci_destructor(QOSGraphObject *obj)
-{
-    QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj;
-    QVirtio9P *interface = &v9_pci->v9p;
-    QOSGraphObject *pci_vobj =  &v9_pci->pci_vdev.obj;
-
-    virtio_9p_cleanup(interface);
-    qvirtio_pci_destructor(pci_vobj);
-}
-
-static void virtio_9p_pci_start_hw(QOSGraphObject *obj)
-{
-    QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj;
-    QVirtio9P *interface = &v9_pci->v9p;
-    QOSGraphObject *pci_vobj =  &v9_pci->pci_vdev.obj;
-
-    qvirtio_pci_start_hw(pci_vobj);
-    virtio_9p_setup(interface);
-}
-
-static void *virtio_9p_pci_get_driver(void *object, const char *interface)
-{
-    QVirtio9PPCI *v_9p = object;
-    if (!g_strcmp0(interface, "pci-device")) {
-        return v_9p->pci_vdev.pdev;
-    }
-    return virtio_9p_get_driver(&v_9p->v9p, interface);
-}
-
-static void *virtio_9p_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
-                                  void *addr)
-{
-    QVirtio9PPCI *v9_pci = g_new0(QVirtio9PPCI, 1);
-    QVirtio9P *interface = &v9_pci->v9p;
-    QOSGraphObject *obj = &v9_pci->pci_vdev.obj;
-
-    virtio_pci_init(&v9_pci->pci_vdev, pci_bus, addr);
-    interface->vdev = &v9_pci->pci_vdev.vdev;
-    alloc = t_alloc;
-
-    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_9P);
-
-    obj->destructor = virtio_9p_pci_destructor;
-    obj->start_hw = virtio_9p_pci_start_hw;
-    obj->get_driver = virtio_9p_pci_get_driver;
-
-    return obj;
-}
-
-static void virtio_9p_register_nodes(void)
-{
-    const char *str_simple = "fsdev=fsdev0,mount_tag=" MOUNT_TAG;
-    const char *str_addr = "fsdev=fsdev0,addr=04.0,mount_tag=" MOUNT_TAG;
-
-    QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(4, 0),
-    };
-
-    QOSGraphEdgeOptions opts = {
-        .before_cmd_line = "-fsdev synth,id=fsdev0",
-    };
-
-    /* virtio-9p-device */
-    opts.extra_device_opts = str_simple,
-    qos_node_create_driver("virtio-9p-device", virtio_9p_device_create);
-    qos_node_consumes("virtio-9p-device", "virtio-bus", &opts);
-    qos_node_produces("virtio-9p-device", "virtio");
-    qos_node_produces("virtio-9p-device", "virtio-9p");
-
-    /* virtio-9p-pci */
-    opts.extra_device_opts = str_addr;
-    add_qpci_address(&opts, &addr);
-    qos_node_create_driver("virtio-9p-pci", virtio_9p_pci_create);
-    qos_node_consumes("virtio-9p-pci", "pci-bus", &opts);
-    qos_node_produces("virtio-9p-pci", "pci-device");
-    qos_node_produces("virtio-9p-pci", "virtio");
-    qos_node_produces("virtio-9p-pci", "virtio-9p");
-
-}
-
-libqos_init(virtio_9p_register_nodes);
diff --git a/tests/libqos/virtio-9p.h b/tests/libqos/virtio-9p.h
deleted file mode 100644 (file)
index b54e89b..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TESTS_LIBQOS_VIRTIO_9P_H
-#define TESTS_LIBQOS_VIRTIO_9P_H
-
-#include "libqos/qgraph.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-
-typedef struct QVirtio9P QVirtio9P;
-typedef struct QVirtio9PPCI QVirtio9PPCI;
-typedef struct QVirtio9PDevice QVirtio9PDevice;
-
-#define MOUNT_TAG "qtest"
-
-struct QVirtio9P {
-    QVirtioDevice *vdev;
-    QVirtQueue *vq;
-};
-
-struct QVirtio9PPCI {
-    QVirtioPCIDevice pci_vdev;
-    QVirtio9P v9p;
-};
-
-struct QVirtio9PDevice {
-    QOSGraphObject obj;
-    QVirtio9P v9p;
-};
-
-#endif
diff --git a/tests/libqos/virtio-balloon.c b/tests/libqos/virtio-balloon.c
deleted file mode 100644 (file)
index 42a4c58..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/qgraph.h"
-#include "libqos/virtio-balloon.h"
-
-/* virtio-balloon-device */
-static void *qvirtio_balloon_get_driver(QVirtioBalloon *v_balloon,
-                                        const char *interface)
-{
-    if (!g_strcmp0(interface, "virtio-balloon")) {
-        return v_balloon;
-    }
-    if (!g_strcmp0(interface, "virtio")) {
-        return v_balloon->vdev;
-    }
-
-    fprintf(stderr, "%s not present in virtio-balloon-device\n", interface);
-    g_assert_not_reached();
-}
-
-static void *qvirtio_balloon_device_get_driver(void *object,
-                                               const char *interface)
-{
-    QVirtioBalloonDevice *v_balloon = object;
-    return qvirtio_balloon_get_driver(&v_balloon->balloon, interface);
-}
-
-static void *virtio_balloon_device_create(void *virtio_dev,
-                                          QGuestAllocator *t_alloc,
-                                          void *addr)
-{
-    QVirtioBalloonDevice *virtio_bdevice = g_new0(QVirtioBalloonDevice, 1);
-    QVirtioBalloon *interface = &virtio_bdevice->balloon;
-
-    interface->vdev = virtio_dev;
-
-    virtio_bdevice->obj.get_driver = qvirtio_balloon_device_get_driver;
-
-    return &virtio_bdevice->obj;
-}
-
-/* virtio-balloon-pci */
-static void *qvirtio_balloon_pci_get_driver(void *object,
-                                            const char *interface)
-{
-    QVirtioBalloonPCI *v_balloon = object;
-    if (!g_strcmp0(interface, "pci-device")) {
-        return v_balloon->pci_vdev.pdev;
-    }
-    return qvirtio_balloon_get_driver(&v_balloon->balloon, interface);
-}
-
-static void *virtio_balloon_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
-                                  void *addr)
-{
-    QVirtioBalloonPCI *virtio_bpci = g_new0(QVirtioBalloonPCI, 1);
-    QVirtioBalloon *interface = &virtio_bpci->balloon;
-    QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj;
-
-
-    virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr);
-    interface->vdev = &virtio_bpci->pci_vdev.vdev;
-
-    obj->get_driver = qvirtio_balloon_pci_get_driver;
-
-    return obj;
-}
-
-static void virtio_balloon_register_nodes(void)
-{
-    QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(4, 0),
-    };
-
-    QOSGraphEdgeOptions opts = {
-        .extra_device_opts = "addr=04.0",
-    };
-
-    /* virtio-balloon-device */
-    qos_node_create_driver("virtio-balloon-device",
-                            virtio_balloon_device_create);
-    qos_node_consumes("virtio-balloon-device", "virtio-bus", NULL);
-    qos_node_produces("virtio-balloon-device", "virtio");
-    qos_node_produces("virtio-balloon-device", "virtio-balloon");
-
-    /* virtio-balloon-pci */
-    add_qpci_address(&opts, &addr);
-    qos_node_create_driver("virtio-balloon-pci", virtio_balloon_pci_create);
-    qos_node_consumes("virtio-balloon-pci", "pci-bus", &opts);
-    qos_node_produces("virtio-balloon-pci", "pci-device");
-    qos_node_produces("virtio-balloon-pci", "virtio");
-    qos_node_produces("virtio-balloon-pci", "virtio-balloon");
-}
-
-libqos_init(virtio_balloon_register_nodes);
diff --git a/tests/libqos/virtio-balloon.h b/tests/libqos/virtio-balloon.h
deleted file mode 100644 (file)
index 52661cc..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TESTS_LIBQOS_VIRTIO_BALLOON_H
-#define TESTS_LIBQOS_VIRTIO_BALLOON_H
-
-#include "libqos/qgraph.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-
-typedef struct QVirtioBalloon QVirtioBalloon;
-typedef struct QVirtioBalloonPCI QVirtioBalloonPCI;
-typedef struct QVirtioBalloonDevice QVirtioBalloonDevice;
-
-struct QVirtioBalloon {
-    QVirtioDevice *vdev;
-};
-
-struct QVirtioBalloonPCI {
-    QVirtioPCIDevice pci_vdev;
-    QVirtioBalloon balloon;
-};
-
-struct QVirtioBalloonDevice {
-    QOSGraphObject obj;
-    QVirtioBalloon balloon;
-};
-
-#endif
diff --git a/tests/libqos/virtio-blk.c b/tests/libqos/virtio-blk.c
deleted file mode 100644 (file)
index 726e93c..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "standard-headers/linux/virtio_blk.h"
-#include "libqos/qgraph.h"
-#include "libqos/virtio-blk.h"
-
-#define PCI_SLOT                0x04
-#define PCI_FN                  0x00
-
-/* virtio-blk-device */
-static void *qvirtio_blk_get_driver(QVirtioBlk *v_blk,
-                                    const char *interface)
-{
-    if (!g_strcmp0(interface, "virtio-blk")) {
-        return v_blk;
-    }
-    if (!g_strcmp0(interface, "virtio")) {
-        return v_blk->vdev;
-    }
-
-    fprintf(stderr, "%s not present in virtio-blk-device\n", interface);
-    g_assert_not_reached();
-}
-
-static void *qvirtio_blk_device_get_driver(void *object,
-                                           const char *interface)
-{
-    QVirtioBlkDevice *v_blk = object;
-    return qvirtio_blk_get_driver(&v_blk->blk, interface);
-}
-
-static void *virtio_blk_device_create(void *virtio_dev,
-                                      QGuestAllocator *t_alloc,
-                                      void *addr)
-{
-    QVirtioBlkDevice *virtio_blk = g_new0(QVirtioBlkDevice, 1);
-    QVirtioBlk *interface = &virtio_blk->blk;
-
-    interface->vdev = virtio_dev;
-
-    virtio_blk->obj.get_driver = qvirtio_blk_device_get_driver;
-
-    return &virtio_blk->obj;
-}
-
-/* virtio-blk-pci */
-static void *qvirtio_blk_pci_get_driver(void *object, const char *interface)
-{
-    QVirtioBlkPCI *v_blk = object;
-    if (!g_strcmp0(interface, "pci-device")) {
-        return v_blk->pci_vdev.pdev;
-    }
-    return qvirtio_blk_get_driver(&v_blk->blk, interface);
-}
-
-static void *virtio_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
-                                      void *addr)
-{
-    QVirtioBlkPCI *virtio_blk = g_new0(QVirtioBlkPCI, 1);
-    QVirtioBlk *interface = &virtio_blk->blk;
-    QOSGraphObject *obj = &virtio_blk->pci_vdev.obj;
-
-    virtio_pci_init(&virtio_blk->pci_vdev, pci_bus, addr);
-    interface->vdev = &virtio_blk->pci_vdev.vdev;
-
-    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK);
-
-    obj->get_driver = qvirtio_blk_pci_get_driver;
-
-    return obj;
-}
-
-static void virtio_blk_register_nodes(void)
-{
-    /* FIXME: every test using these two nodes needs to setup a
-     * -drive,id=drive0 otherwise QEMU is not going to start.
-     * Therefore, we do not include "produces" edge for virtio
-     * and pci-device yet.
-    */
-
-    char *arg = g_strdup_printf("id=drv0,drive=drive0,addr=%x.%x",
-                                PCI_SLOT, PCI_FN);
-
-    QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN),
-    };
-
-    QOSGraphEdgeOptions opts = { };
-
-    /* virtio-blk-device */
-    opts.extra_device_opts = "drive=drive0";
-    qos_node_create_driver("virtio-blk-device", virtio_blk_device_create);
-    qos_node_consumes("virtio-blk-device", "virtio-bus", &opts);
-    qos_node_produces("virtio-blk-device", "virtio-blk");
-
-    /* virtio-blk-pci */
-    opts.extra_device_opts = arg;
-    add_qpci_address(&opts, &addr);
-    qos_node_create_driver("virtio-blk-pci", virtio_blk_pci_create);
-    qos_node_consumes("virtio-blk-pci", "pci-bus", &opts);
-    qos_node_produces("virtio-blk-pci", "virtio-blk");
-
-    g_free(arg);
-}
-
-libqos_init(virtio_blk_register_nodes);
diff --git a/tests/libqos/virtio-blk.h b/tests/libqos/virtio-blk.h
deleted file mode 100644 (file)
index c05adc6..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TESTS_LIBQOS_VIRTIO_BLK_H
-#define TESTS_LIBQOS_VIRTIO_BLK_H
-
-#include "libqos/qgraph.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-
-typedef struct QVirtioBlk QVirtioBlk;
-typedef struct QVirtioBlkPCI QVirtioBlkPCI;
-typedef struct QVirtioBlkDevice QVirtioBlkDevice;
-
-/* virtqueue is created in each test */
-struct QVirtioBlk {
-    QVirtioDevice *vdev;
-};
-
-struct QVirtioBlkPCI {
-    QVirtioPCIDevice pci_vdev;
-    QVirtioBlk blk;
-};
-
-struct QVirtioBlkDevice {
-    QOSGraphObject obj;
-    QVirtioBlk blk;
-};
-
-#endif
diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c
deleted file mode 100644 (file)
index e0a2bd7..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * libqos virtio MMIO driver
- *
- * Copyright (c) 2014 Marc MarĂ­
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-mmio.h"
-#include "libqos/malloc.h"
-#include "libqos/qgraph.h"
-#include "standard-headers/linux/virtio_ring.h"
-
-static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    return qtest_readb(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
-}
-
-static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    return qtest_readw(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
-}
-
-static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
-}
-
-static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    return qtest_readq(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
-}
-
-static uint64_t qvirtio_mmio_get_features(QVirtioDevice *d)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    uint64_t lo;
-    uint64_t hi = 0;
-
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0);
-    lo = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
-
-    if (dev->version >= 2) {
-        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 1);
-        hi = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
-    }
-
-    return (hi << 32) | lo;
-}
-
-static void qvirtio_mmio_set_features(QVirtioDevice *d, uint64_t features)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    dev->features = features;
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features);
-
-    if (dev->version >= 2) {
-        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 1);
-        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES,
-                     features >> 32);
-    }
-}
-
-static uint64_t qvirtio_mmio_get_guest_features(QVirtioDevice *d)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    return dev->features;
-}
-
-static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    return (uint8_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS);
-}
-
-static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status);
-}
-
-static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    uint32_t isr;
-
-    isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1;
-    if (isr != 0) {
-        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1);
-        return true;
-    }
-
-    return false;
-}
-
-static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    uint32_t isr;
-
-    isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2;
-    if (isr != 0) {
-        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2);
-        return true;
-    }
-
-    return false;
-}
-
-static void qvirtio_mmio_wait_config_isr_status(QVirtioDevice *d,
-                                                gint64 timeout_us)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    gint64 start_time = g_get_monotonic_time();
-
-    do {
-        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
-        qtest_clock_step(dev->qts, 100);
-    } while (!qvirtio_mmio_get_config_isr_status(d));
-}
-
-static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index);
-
-    g_assert_cmphex(qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0);
-}
-
-static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    return (uint16_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX);
-}
-
-static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    uint64_t pfn = vq->desc / dev->page_size;
-
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn);
-}
-
-static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
-                                        QGuestAllocator *alloc, uint16_t index)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    QVirtQueue *vq;
-    uint64_t addr;
-
-    vq = g_malloc0(sizeof(*vq));
-    vq->vdev = d;
-    qvirtio_mmio_queue_select(d, index);
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size);
-
-    vq->index = index;
-    vq->size = qvirtio_mmio_get_queue_size(d);
-    vq->free_head = 0;
-    vq->num_free = vq->size;
-    vq->align = dev->page_size;
-    vq->indirect = dev->features & (1ull << VIRTIO_RING_F_INDIRECT_DESC);
-    vq->event = dev->features & (1ull << VIRTIO_RING_F_EVENT_IDX);
-
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
-
-    /* Check different than 0 */
-    g_assert_cmpint(vq->size, !=, 0);
-
-    /* Check power of 2 */
-    g_assert_cmpint(vq->size & (vq->size - 1), ==, 0);
-
-    addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size));
-    qvring_init(dev->qts, alloc, vq, addr);
-    qvirtio_mmio_set_queue_address(d, vq);
-
-    return vq;
-}
-
-static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue *vq,
-                                           QGuestAllocator *alloc)
-{
-    guest_free(alloc, vq->desc);
-    g_free(vq);
-}
-
-static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
-    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index);
-}
-
-const QVirtioBus qvirtio_mmio = {
-    .config_readb = qvirtio_mmio_config_readb,
-    .config_readw = qvirtio_mmio_config_readw,
-    .config_readl = qvirtio_mmio_config_readl,
-    .config_readq = qvirtio_mmio_config_readq,
-    .get_features = qvirtio_mmio_get_features,
-    .set_features = qvirtio_mmio_set_features,
-    .get_guest_features = qvirtio_mmio_get_guest_features,
-    .get_status = qvirtio_mmio_get_status,
-    .set_status = qvirtio_mmio_set_status,
-    .get_queue_isr_status = qvirtio_mmio_get_queue_isr_status,
-    .wait_config_isr_status = qvirtio_mmio_wait_config_isr_status,
-    .queue_select = qvirtio_mmio_queue_select,
-    .get_queue_size = qvirtio_mmio_get_queue_size,
-    .set_queue_address = qvirtio_mmio_set_queue_address,
-    .virtqueue_setup = qvirtio_mmio_virtqueue_setup,
-    .virtqueue_cleanup = qvirtio_mmio_virtqueue_cleanup,
-    .virtqueue_kick = qvirtio_mmio_virtqueue_kick,
-};
-
-static void *qvirtio_mmio_get_driver(void *obj, const char *interface)
-{
-    QVirtioMMIODevice *virtio_mmio = obj;
-    if (!g_strcmp0(interface, "virtio-bus")) {
-        return &virtio_mmio->vdev;
-    }
-    fprintf(stderr, "%s not present in virtio-mmio\n", interface);
-    g_assert_not_reached();
-}
-
-static void qvirtio_mmio_start_hw(QOSGraphObject *obj)
-{
-    QVirtioMMIODevice *dev = (QVirtioMMIODevice *) obj;
-    qvirtio_start_device(&dev->vdev);
-}
-
-void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts,
-                              uint64_t addr, uint32_t page_size)
-{
-    uint32_t magic;
-    magic = qtest_readl(qts, addr + QVIRTIO_MMIO_MAGIC_VALUE);
-    g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
-
-    dev->version = qtest_readl(qts, addr + QVIRTIO_MMIO_VERSION);
-    g_assert(dev->version == 1 || dev->version == 2);
-
-    dev->qts = qts;
-    dev->addr = addr;
-    dev->page_size = page_size;
-    dev->vdev.device_type = qtest_readl(qts, addr + QVIRTIO_MMIO_DEVICE_ID);
-    dev->vdev.bus = &qvirtio_mmio;
-
-    qtest_writel(qts, addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size);
-
-    dev->obj.get_driver = qvirtio_mmio_get_driver;
-    dev->obj.start_hw = qvirtio_mmio_start_hw;
-}
-
-static void virtio_mmio_register_nodes(void)
-{
-    qos_node_create_driver("virtio-mmio", NULL);
-    qos_node_produces("virtio-mmio", "virtio-bus");
-}
-
-libqos_init(virtio_mmio_register_nodes);
diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h
deleted file mode 100644 (file)
index 0e45778..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * libqos virtio MMIO definitions
- *
- * Copyright (c) 2014 Marc MarĂ­
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_VIRTIO_MMIO_H
-#define LIBQOS_VIRTIO_MMIO_H
-
-#include "libqos/virtio.h"
-#include "libqos/qgraph.h"
-
-#define QVIRTIO_MMIO_MAGIC_VALUE        0x000
-#define QVIRTIO_MMIO_VERSION            0x004
-#define QVIRTIO_MMIO_DEVICE_ID          0x008
-#define QVIRTIO_MMIO_VENDOR_ID          0x00C
-#define QVIRTIO_MMIO_HOST_FEATURES      0x010
-#define QVIRTIO_MMIO_HOST_FEATURES_SEL  0x014
-#define QVIRTIO_MMIO_GUEST_FEATURES     0x020
-#define QVIRTIO_MMIO_GUEST_FEATURES_SEL 0x024
-#define QVIRTIO_MMIO_GUEST_PAGE_SIZE    0x028
-#define QVIRTIO_MMIO_QUEUE_SEL          0x030
-#define QVIRTIO_MMIO_QUEUE_NUM_MAX      0x034
-#define QVIRTIO_MMIO_QUEUE_NUM          0x038
-#define QVIRTIO_MMIO_QUEUE_ALIGN        0x03C
-#define QVIRTIO_MMIO_QUEUE_PFN          0x040
-#define QVIRTIO_MMIO_QUEUE_NOTIFY       0x050
-#define QVIRTIO_MMIO_INTERRUPT_STATUS   0x060
-#define QVIRTIO_MMIO_INTERRUPT_ACK      0x064
-#define QVIRTIO_MMIO_DEVICE_STATUS      0x070
-#define QVIRTIO_MMIO_DEVICE_SPECIFIC    0x100
-
-typedef struct QVirtioMMIODevice {
-    QOSGraphObject obj;
-    QVirtioDevice vdev;
-    QTestState *qts;
-    uint64_t addr;
-    uint32_t page_size;
-    uint32_t features; /* As it cannot be read later, save it */
-    uint32_t version;
-} QVirtioMMIODevice;
-
-extern const QVirtioBus qvirtio_mmio;
-
-void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts,
-                              uint64_t addr, uint32_t page_size);
-
-#endif
diff --git a/tests/libqos/virtio-net.c b/tests/libqos/virtio-net.c
deleted file mode 100644 (file)
index 710d440..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/qgraph.h"
-#include "libqos/virtio-net.h"
-#include "hw/virtio/virtio-net.h"
-
-
-static QGuestAllocator *alloc;
-
-static void virtio_net_cleanup(QVirtioNet *interface)
-{
-    int i;
-
-    for (i = 0; i < interface->n_queues; i++) {
-        qvirtqueue_cleanup(interface->vdev->bus, interface->queues[i], alloc);
-    }
-    g_free(interface->queues);
-}
-
-static void virtio_net_setup(QVirtioNet *interface)
-{
-    QVirtioDevice *vdev = interface->vdev;
-    uint64_t features;
-    int i;
-
-    features = qvirtio_get_features(vdev);
-    features &= ~(QVIRTIO_F_BAD_FEATURE |
-                  (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
-                  (1ull << VIRTIO_RING_F_EVENT_IDX));
-    qvirtio_set_features(vdev, features);
-
-    if (features & (1ull << VIRTIO_NET_F_MQ)) {
-        interface->n_queues = qvirtio_config_readw(vdev, 8) * 2;
-    } else {
-        interface->n_queues = 2;
-    }
-    interface->n_queues++; /* Account for the ctrl queue */
-
-    interface->queues = g_new(QVirtQueue *, interface->n_queues);
-    for (i = 0; i < interface->n_queues; i++) {
-        interface->queues[i] = qvirtqueue_setup(vdev, alloc, i);
-    }
-    qvirtio_set_driver_ok(vdev);
-}
-
-/* virtio-net-device */
-static void qvirtio_net_device_destructor(QOSGraphObject *obj)
-{
-    QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj;
-    virtio_net_cleanup(&v_net->net);
-}
-
-static void qvirtio_net_device_start_hw(QOSGraphObject *obj)
-{
-    QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj;
-    QVirtioNet *interface = &v_net->net;
-
-    virtio_net_setup(interface);
-}
-
-static void *qvirtio_net_get_driver(QVirtioNet *v_net,
-                                    const char *interface)
-{
-    if (!g_strcmp0(interface, "virtio-net")) {
-        return v_net;
-    }
-    if (!g_strcmp0(interface, "virtio")) {
-        return v_net->vdev;
-    }
-
-    fprintf(stderr, "%s not present in virtio-net-device\n", interface);
-    g_assert_not_reached();
-}
-
-static void *qvirtio_net_device_get_driver(void *object,
-                                           const char *interface)
-{
-    QVirtioNetDevice *v_net = object;
-    return qvirtio_net_get_driver(&v_net->net, interface);
-}
-
-static void *virtio_net_device_create(void *virtio_dev,
-                                          QGuestAllocator *t_alloc,
-                                          void *addr)
-{
-    QVirtioNetDevice *virtio_ndevice = g_new0(QVirtioNetDevice, 1);
-    QVirtioNet *interface = &virtio_ndevice->net;
-
-    interface->vdev = virtio_dev;
-    alloc = t_alloc;
-
-    virtio_ndevice->obj.destructor = qvirtio_net_device_destructor;
-    virtio_ndevice->obj.get_driver = qvirtio_net_device_get_driver;
-    virtio_ndevice->obj.start_hw = qvirtio_net_device_start_hw;
-
-    return &virtio_ndevice->obj;
-}
-
-/* virtio-net-pci */
-static void qvirtio_net_pci_destructor(QOSGraphObject *obj)
-{
-    QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj;
-    QVirtioNet *interface = &v_net->net;
-    QOSGraphObject *pci_vobj =  &v_net->pci_vdev.obj;
-
-    virtio_net_cleanup(interface);
-    qvirtio_pci_destructor(pci_vobj);
-}
-
-static void qvirtio_net_pci_start_hw(QOSGraphObject *obj)
-{
-    QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj;
-    QVirtioNet *interface = &v_net->net;
-    QOSGraphObject *pci_vobj =  &v_net->pci_vdev.obj;
-
-    qvirtio_pci_start_hw(pci_vobj);
-    virtio_net_setup(interface);
-}
-
-static void *qvirtio_net_pci_get_driver(void *object,
-                                            const char *interface)
-{
-    QVirtioNetPCI *v_net = object;
-    if (!g_strcmp0(interface, "pci-device")) {
-        return v_net->pci_vdev.pdev;
-    }
-    return qvirtio_net_get_driver(&v_net->net, interface);
-}
-
-static void *virtio_net_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
-                                  void *addr)
-{
-    QVirtioNetPCI *virtio_bpci = g_new0(QVirtioNetPCI, 1);
-    QVirtioNet *interface = &virtio_bpci->net;
-    QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj;
-
-    virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr);
-    interface->vdev = &virtio_bpci->pci_vdev.vdev;
-    alloc = t_alloc;
-
-    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_NET);
-
-    obj->destructor = qvirtio_net_pci_destructor;
-    obj->start_hw = qvirtio_net_pci_start_hw;
-    obj->get_driver = qvirtio_net_pci_get_driver;
-
-    return obj;
-}
-
-static void virtio_net_register_nodes(void)
-{
-    /* FIXME: every test using these nodes needs to setup a
-     * -netdev socket,id=hs0 otherwise QEMU is not going to start.
-     * Therefore, we do not include "produces" edge for virtio
-     * and pci-device yet.
-     */
-    QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(4, 0),
-    };
-
-    QOSGraphEdgeOptions opts = { };
-
-    /* virtio-net-device */
-    opts.extra_device_opts = "netdev=hs0";
-    qos_node_create_driver("virtio-net-device",
-                            virtio_net_device_create);
-    qos_node_consumes("virtio-net-device", "virtio-bus", &opts);
-    qos_node_produces("virtio-net-device", "virtio-net");
-
-    /* virtio-net-pci */
-    opts.extra_device_opts = "netdev=hs0,addr=04.0";
-    add_qpci_address(&opts, &addr);
-    qos_node_create_driver("virtio-net-pci", virtio_net_pci_create);
-    qos_node_consumes("virtio-net-pci", "pci-bus", &opts);
-    qos_node_produces("virtio-net-pci", "virtio-net");
-}
-
-libqos_init(virtio_net_register_nodes);
diff --git a/tests/libqos/virtio-net.h b/tests/libqos/virtio-net.h
deleted file mode 100644 (file)
index 855c67d..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TESTS_LIBQOS_VIRTIO_NET_H
-#define TESTS_LIBQOS_VIRTIO_NET_H
-
-#include "libqos/qgraph.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-
-typedef struct QVirtioNet QVirtioNet;
-typedef struct QVirtioNetPCI QVirtioNetPCI;
-typedef struct QVirtioNetDevice QVirtioNetDevice;
-
-struct QVirtioNet {
-    QVirtioDevice *vdev;
-    int n_queues; /* total number of virtqueues (rx, tx, ctrl) */
-    QVirtQueue **queues;
-};
-
-struct QVirtioNetPCI {
-    QVirtioPCIDevice pci_vdev;
-    QVirtioNet net;
-};
-
-struct QVirtioNetDevice {
-    QOSGraphObject obj;
-    QVirtioNet net;
-};
-
-#endif
diff --git a/tests/libqos/virtio-pci-modern.c b/tests/libqos/virtio-pci-modern.c
deleted file mode 100644 (file)
index 18d1188..0000000
+++ /dev/null
@@ -1,443 +0,0 @@
-/*
- * libqos VIRTIO 1.0 PCI driver
- *
- * Copyright (c) 2019 Red Hat, Inc
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "standard-headers/linux/pci_regs.h"
-#include "standard-headers/linux/virtio_pci.h"
-#include "standard-headers/linux/virtio_config.h"
-#include "virtio-pci-modern.h"
-
-static uint8_t config_readb(QVirtioDevice *d, uint64_t addr)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readb(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
-}
-
-static uint16_t config_readw(QVirtioDevice *d, uint64_t addr)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readw(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
-}
-
-static uint32_t config_readl(QVirtioDevice *d, uint64_t addr)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readl(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
-}
-
-static uint64_t config_readq(QVirtioDevice *d, uint64_t addr)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readq(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
-}
-
-static uint64_t get_features(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    uint64_t lo, hi;
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            device_feature_select),
-                   0);
-    lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
-                       offsetof(struct virtio_pci_common_cfg, device_feature));
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            device_feature_select),
-                   1);
-    hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
-                       offsetof(struct virtio_pci_common_cfg, device_feature));
-
-    return (hi << 32) | lo;
-}
-
-static void set_features(QVirtioDevice *d, uint64_t features)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    /* Drivers must enable VIRTIO 1.0 or else use the Legacy interface */
-    g_assert_cmphex(features & (1ull << VIRTIO_F_VERSION_1), !=, 0);
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            guest_feature_select),
-                   0);
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            guest_feature),
-                   features);
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            guest_feature_select),
-                   1);
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            guest_feature),
-                   features >> 32);
-}
-
-static uint64_t get_guest_features(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    uint64_t lo, hi;
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            guest_feature_select),
-                   0);
-    lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
-                       offsetof(struct virtio_pci_common_cfg, guest_feature));
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg,
-                            guest_feature_select),
-                   1);
-    hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
-                       offsetof(struct virtio_pci_common_cfg, guest_feature));
-
-    return (hi << 32) | lo;
-}
-
-static uint8_t get_status(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    return qpci_io_readb(dev->pdev, dev->bar, dev->common_cfg_offset +
-                         offsetof(struct virtio_pci_common_cfg,
-                                  device_status));
-}
-
-static void set_status(QVirtioDevice *d, uint8_t status)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    return qpci_io_writeb(dev->pdev, dev->bar, dev->common_cfg_offset +
-                          offsetof(struct virtio_pci_common_cfg,
-                                   device_status),
-                          status);
-}
-
-static bool get_msix_status(QVirtioPCIDevice *dev, uint32_t msix_entry,
-                            uint32_t msix_addr, uint32_t msix_data)
-{
-    uint32_t data;
-
-    g_assert_cmpint(msix_entry, !=, -1);
-    if (qpci_msix_masked(dev->pdev, msix_entry)) {
-        /* No ISR checking should be done if masked, but read anyway */
-        return qpci_msix_pending(dev->pdev, msix_entry);
-    }
-
-    data = qtest_readl(dev->pdev->bus->qts, msix_addr);
-    if (data == msix_data) {
-        qtest_writel(dev->pdev->bus->qts, msix_addr, 0);
-        return true;
-    } else {
-        return false;
-    }
-}
-
-static bool get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    if (dev->pdev->msix_enabled) {
-        QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
-
-        return get_msix_status(dev, vqpci->msix_entry, vqpci->msix_addr,
-                               vqpci->msix_data);
-    }
-
-    return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 1;
-}
-
-static bool get_config_isr_status(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    if (dev->pdev->msix_enabled) {
-        return get_msix_status(dev, dev->config_msix_entry,
-                               dev->config_msix_addr, dev->config_msix_data);
-    }
-
-    return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 2;
-}
-
-static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    gint64 start_time = g_get_monotonic_time();
-
-    do {
-        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
-        qtest_clock_step(dev->pdev->bus->qts, 100);
-    } while (!get_config_isr_status(d));
-}
-
-static void queue_select(QVirtioDevice *d, uint16_t index)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_select),
-                   index);
-}
-
-static uint16_t get_queue_size(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    return qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset +
-                         offsetof(struct virtio_pci_common_cfg, queue_size));
-}
-
-static void set_queue_address(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_desc_lo),
-                   vq->desc);
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_desc_hi),
-                   vq->desc >> 32);
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_avail_lo),
-                   vq->avail);
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_avail_hi),
-                   vq->avail >> 32);
-
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_used_lo),
-                   vq->used);
-    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_used_hi),
-                   vq->used >> 32);
-}
-
-static QVirtQueue *virtqueue_setup(QVirtioDevice *d, QGuestAllocator *alloc,
-                                   uint16_t index)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    QVirtQueue *vq;
-    QVirtQueuePCI *vqpci;
-    uint16_t notify_off;
-
-    vq = qvirtio_pci_virtqueue_setup_common(d, alloc, index);
-    vqpci = container_of(vq, QVirtQueuePCI, vq);
-
-    notify_off = qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset +
-                               offsetof(struct virtio_pci_common_cfg,
-                                        queue_notify_off));
-
-    vqpci->notify_offset = dev->notify_cfg_offset +
-                           notify_off * dev->notify_off_multiplier;
-
-    qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_enable), 1);
-
-    return vq;
-}
-
-static void virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
-
-    qpci_io_writew(dev->pdev, dev->bar, vqpci->notify_offset, vq->index);
-}
-
-static const QVirtioBus qvirtio_pci_virtio_1 = {
-    .config_readb = config_readb,
-    .config_readw = config_readw,
-    .config_readl = config_readl,
-    .config_readq = config_readq,
-    .get_features = get_features,
-    .set_features = set_features,
-    .get_guest_features = get_guest_features,
-    .get_status = get_status,
-    .set_status = set_status,
-    .get_queue_isr_status = get_queue_isr_status,
-    .wait_config_isr_status = wait_config_isr_status,
-    .queue_select = queue_select,
-    .get_queue_size = get_queue_size,
-    .set_queue_address = set_queue_address,
-    .virtqueue_setup = virtqueue_setup,
-    .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common,
-    .virtqueue_kick = virtqueue_kick,
-};
-
-static void set_config_vector(QVirtioPCIDevice *d, uint16_t entry)
-{
-    uint16_t vector;
-
-    qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, msix_config), entry);
-    vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset +
-                           offsetof(struct virtio_pci_common_cfg,
-                                    msix_config));
-    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
-}
-
-static void set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx,
-                             uint16_t entry)
-{
-    uint16_t vector;
-
-    queue_select(&d->vdev, vq_idx);
-    qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset +
-                   offsetof(struct virtio_pci_common_cfg, queue_msix_vector),
-                   entry);
-    vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset +
-                           offsetof(struct virtio_pci_common_cfg,
-                                    queue_msix_vector));
-    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
-}
-
-static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_virtio_1 = {
-    .set_config_vector = set_config_vector,
-    .set_queue_vector = set_queue_vector,
-};
-
-static bool probe_device_type(QVirtioPCIDevice *dev)
-{
-    uint16_t vendor_id;
-    uint16_t device_id;
-
-    /* "Drivers MUST match devices with the PCI Vendor ID 0x1AF4" */
-    vendor_id = qpci_config_readw(dev->pdev, PCI_VENDOR_ID);
-    if (vendor_id != 0x1af4) {
-        return false;
-    }
-
-    /*
-     * "Any PCI device with ... PCI Device ID 0x1000 through 0x107F inclusive
-     * is a virtio device"
-     */
-    device_id = qpci_config_readw(dev->pdev, PCI_DEVICE_ID);
-    if (device_id < 0x1000 || device_id > 0x107f) {
-        return false;
-    }
-
-    /*
-     * "Devices MAY utilize a Transitional PCI Device ID range, 0x1000 to
-     * 0x103F depending on the device type"
-     */
-    if (device_id < 0x1040) {
-        /*
-         * "Transitional devices MUST have the PCI Subsystem Device ID matching
-         * the Virtio Device ID"
-         */
-        dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID);
-    } else {
-        /*
-         * "The PCI Device ID is calculated by adding 0x1040 to the Virtio
-         * Device ID"
-         */
-        dev->vdev.device_type = device_id - 0x1040;
-    }
-
-    return true;
-}
-
-/* Find the first VIRTIO 1.0 PCI structure for a given type */
-static bool find_structure(QVirtioPCIDevice *dev, uint8_t cfg_type,
-                           uint8_t *bar, uint32_t *offset, uint32_t *length,
-                           uint8_t *cfg_addr)
-{
-    uint8_t addr = 0;
-
-    while ((addr = qpci_find_capability(dev->pdev, PCI_CAP_ID_VNDR,
-                                        addr)) != 0) {
-        uint8_t type;
-
-        type = qpci_config_readb(dev->pdev,
-                addr + offsetof(struct virtio_pci_cap, cfg_type));
-        if (type != cfg_type) {
-            continue;
-        }
-
-        *bar = qpci_config_readb(dev->pdev,
-                addr + offsetof(struct virtio_pci_cap, bar));
-        *offset = qpci_config_readl(dev->pdev,
-                addr + offsetof(struct virtio_pci_cap, offset));
-        *length = qpci_config_readl(dev->pdev,
-                addr + offsetof(struct virtio_pci_cap, length));
-        if (cfg_addr) {
-            *cfg_addr = addr;
-        }
-
-        return true;
-    }
-
-    return false;
-}
-
-static bool probe_device_layout(QVirtioPCIDevice *dev)
-{
-    uint8_t bar;
-    uint8_t cfg_addr;
-    uint32_t length;
-
-    /*
-     * Due to the qpci_iomap() API we only support devices that put all
-     * structures in the same PCI BAR.  Luckily this is true with QEMU.
-     */
-
-    if (!find_structure(dev, VIRTIO_PCI_CAP_COMMON_CFG, &bar,
-                        &dev->common_cfg_offset, &length, NULL)) {
-        return false;
-    }
-    dev->bar_idx = bar;
-
-    if (!find_structure(dev, VIRTIO_PCI_CAP_NOTIFY_CFG, &bar,
-                        &dev->notify_cfg_offset, &length, &cfg_addr)) {
-        return false;
-    }
-    g_assert_cmphex(bar, ==, dev->bar_idx);
-
-    dev->notify_off_multiplier = qpci_config_readl(dev->pdev,
-            cfg_addr + offsetof(struct virtio_pci_notify_cap,
-                                notify_off_multiplier));
-
-    if (!find_structure(dev, VIRTIO_PCI_CAP_ISR_CFG, &bar,
-                        &dev->isr_cfg_offset, &length, NULL)) {
-        return false;
-    }
-    g_assert_cmphex(bar, ==, dev->bar_idx);
-
-    if (!find_structure(dev, VIRTIO_PCI_CAP_DEVICE_CFG, &bar,
-                        &dev->device_cfg_offset, &length, NULL)) {
-        return false;
-    }
-    g_assert_cmphex(bar, ==, dev->bar_idx);
-
-    return true;
-}
-
-/* Probe a VIRTIO 1.0 device */
-bool qvirtio_pci_init_virtio_1(QVirtioPCIDevice *dev)
-{
-    if (!probe_device_type(dev)) {
-        return false;
-    }
-
-    if (!probe_device_layout(dev)) {
-        return false;
-    }
-
-    dev->vdev.bus = &qvirtio_pci_virtio_1;
-    dev->msix_ops = &qvirtio_pci_msix_ops_virtio_1;
-    dev->vdev.big_endian = false;
-    return true;
-}
diff --git a/tests/libqos/virtio-pci-modern.h b/tests/libqos/virtio-pci-modern.h
deleted file mode 100644 (file)
index 6bf2b20..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * libqos virtio PCI VIRTIO 1.0 definitions
- *
- * Copyright (c) 2019 Red Hat, Inc
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_VIRTIO_PCI_MODERN_H
-#define LIBQOS_VIRTIO_PCI_MODERN_H
-
-#include "virtio-pci.h"
-
-bool qvirtio_pci_init_virtio_1(QVirtioPCIDevice *dev);
-
-#endif /* LIBQOS_VIRTIO_PCI_MODERN_H */
diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c
deleted file mode 100644 (file)
index 62851c2..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * libqos virtio PCI driver
- *
- * Copyright (c) 2014 Marc MarĂ­
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-#include "libqos/pci.h"
-#include "libqos/pci-pc.h"
-#include "libqos/malloc.h"
-#include "libqos/malloc-pc.h"
-#include "libqos/qgraph.h"
-#include "standard-headers/linux/virtio_ring.h"
-#include "standard-headers/linux/virtio_pci.h"
-
-#include "hw/pci/pci.h"
-#include "hw/pci/pci_regs.h"
-
-#include "virtio-pci-modern.h"
-
-/* virtio-pci is a superclass of all virtio-xxx-pci devices;
- * the relation between virtio-pci and virtio-xxx-pci is implicit,
- * and therefore virtio-pci does not produce virtio and is not
- * reached by any edge, not even as a "contains" edge.
- * In facts, every device is a QVirtioPCIDevice with
- * additional fields, since every one has its own
- * number of queues and various attributes.
- * Virtio-pci provides default functions to start the
- * hw and destroy the object, and nodes that want to
- * override them should always remember to call the
- * original qvirtio_pci_destructor and qvirtio_pci_start_hw.
- */
-
-#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled))
-
-static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
-}
-
-/* PCI is always read in little-endian order
- * but virtio ( < 1.0) is in guest order
- * so with a big-endian guest the order has been reversed,
- * reverse it again
- * virtio-1.0 is always little-endian, like PCI
- */
-
-static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    uint16_t value;
-
-    value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
-    if (qvirtio_is_big_endian(d)) {
-        value = bswap16(value);
-    }
-    return value;
-}
-
-static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    uint32_t value;
-
-    value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
-    if (qvirtio_is_big_endian(d)) {
-        value = bswap32(value);
-    }
-    return value;
-}
-
-static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    uint64_t val;
-
-    val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
-    if (qvirtio_is_big_endian(d)) {
-        val = bswap64(val);
-    }
-
-    return val;
-}
-
-static uint64_t qvirtio_pci_get_features(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES);
-}
-
-static void qvirtio_pci_set_features(QVirtioDevice *d, uint64_t features)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features);
-}
-
-static uint64_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES);
-}
-
-static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS);
-}
-
-static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status);
-}
-
-static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq;
-    uint32_t data;
-
-    if (dev->pdev->msix_enabled) {
-        g_assert_cmpint(vqpci->msix_entry, !=, -1);
-        if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) {
-            /* No ISR checking should be done if masked, but read anyway */
-            return qpci_msix_pending(dev->pdev, vqpci->msix_entry);
-        } else {
-            data = qtest_readl(dev->pdev->bus->qts, vqpci->msix_addr);
-            if (data == vqpci->msix_data) {
-                qtest_writel(dev->pdev->bus->qts, vqpci->msix_addr, 0);
-                return true;
-            } else {
-                return false;
-            }
-        }
-    } else {
-        return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1;
-    }
-}
-
-static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    uint32_t data;
-
-    if (dev->pdev->msix_enabled) {
-        g_assert_cmpint(dev->config_msix_entry, !=, -1);
-        if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) {
-            /* No ISR checking should be done if masked, but read anyway */
-            return qpci_msix_pending(dev->pdev, dev->config_msix_entry);
-        } else {
-            data = qtest_readl(dev->pdev->bus->qts, dev->config_msix_addr);
-            if (data == dev->config_msix_data) {
-                qtest_writel(dev->pdev->bus->qts, dev->config_msix_addr, 0);
-                return true;
-            } else {
-                return false;
-            }
-        }
-    } else {
-        return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2;
-    }
-}
-
-static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d,
-                                               gint64 timeout_us)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    gint64 start_time = g_get_monotonic_time();
-
-    do {
-        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
-        qtest_clock_step(dev->pdev->bus->qts, 100);
-    } while (!qvirtio_pci_get_config_isr_status(d));
-}
-
-static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index);
-}
-
-static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM);
-}
-
-static void qvirtio_pci_set_queue_address(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    uint64_t pfn = vq->desc / VIRTIO_PCI_VRING_ALIGN;
-
-    qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn);
-}
-
-QVirtQueue *qvirtio_pci_virtqueue_setup_common(QVirtioDevice *d,
-                                               QGuestAllocator *alloc,
-                                               uint16_t index)
-{
-    uint64_t feat;
-    uint64_t addr;
-    QVirtQueuePCI *vqpci;
-    QVirtioPCIDevice *qvpcidev = container_of(d, QVirtioPCIDevice, vdev);
-
-    vqpci = g_malloc0(sizeof(*vqpci));
-    feat = d->bus->get_guest_features(d);
-
-    d->bus->queue_select(d, index);
-    vqpci->vq.vdev = d;
-    vqpci->vq.index = index;
-    vqpci->vq.size = d->bus->get_queue_size(d);
-    vqpci->vq.free_head = 0;
-    vqpci->vq.num_free = vqpci->vq.size;
-    vqpci->vq.align = VIRTIO_PCI_VRING_ALIGN;
-    vqpci->vq.indirect = feat & (1ull << VIRTIO_RING_F_INDIRECT_DESC);
-    vqpci->vq.event = feat & (1ull << VIRTIO_RING_F_EVENT_IDX);
-
-    vqpci->msix_entry = -1;
-    vqpci->msix_addr = 0;
-    vqpci->msix_data = 0x12345678;
-
-    /* Check different than 0 */
-    g_assert_cmpint(vqpci->vq.size, !=, 0);
-
-    /* Check power of 2 */
-    g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0);
-
-    addr = guest_alloc(alloc, qvring_size(vqpci->vq.size,
-                                          VIRTIO_PCI_VRING_ALIGN));
-    qvring_init(qvpcidev->pdev->bus->qts, alloc, &vqpci->vq, addr);
-    d->bus->set_queue_address(d, &vqpci->vq);
-
-    return &vqpci->vq;
-}
-
-void qvirtio_pci_virtqueue_cleanup_common(QVirtQueue *vq,
-                                          QGuestAllocator *alloc)
-{
-    QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
-
-    guest_free(alloc, vq->desc);
-    g_free(vqpci);
-}
-
-static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
-{
-    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
-    qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index);
-}
-
-static const QVirtioBus qvirtio_pci_legacy = {
-    .config_readb = qvirtio_pci_config_readb,
-    .config_readw = qvirtio_pci_config_readw,
-    .config_readl = qvirtio_pci_config_readl,
-    .config_readq = qvirtio_pci_config_readq,
-    .get_features = qvirtio_pci_get_features,
-    .set_features = qvirtio_pci_set_features,
-    .get_guest_features = qvirtio_pci_get_guest_features,
-    .get_status = qvirtio_pci_get_status,
-    .set_status = qvirtio_pci_set_status,
-    .get_queue_isr_status = qvirtio_pci_get_queue_isr_status,
-    .wait_config_isr_status = qvirtio_pci_wait_config_isr_status,
-    .queue_select = qvirtio_pci_queue_select,
-    .get_queue_size = qvirtio_pci_get_queue_size,
-    .set_queue_address = qvirtio_pci_set_queue_address,
-    .virtqueue_setup = qvirtio_pci_virtqueue_setup_common,
-    .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common,
-    .virtqueue_kick = qvirtio_pci_virtqueue_kick,
-};
-
-static void qvirtio_pci_set_config_vector(QVirtioPCIDevice *d, uint16_t entry)
-{
-    uint16_t vector;
-
-    qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry);
-    vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR);
-    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
-}
-
-static void qvirtio_pci_set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx,
-                                         uint16_t entry)
-{
-    uint16_t vector;
-
-    qvirtio_pci_queue_select(&d->vdev, vq_idx);
-    qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry);
-    vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR);
-    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
-}
-
-static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_legacy = {
-    .set_config_vector = qvirtio_pci_set_config_vector,
-    .set_queue_vector = qvirtio_pci_set_queue_vector,
-};
-
-void qvirtio_pci_device_enable(QVirtioPCIDevice *d)
-{
-    qpci_device_enable(d->pdev);
-    d->bar = qpci_iomap(d->pdev, d->bar_idx, NULL);
-}
-
-void qvirtio_pci_device_disable(QVirtioPCIDevice *d)
-{
-    qpci_iounmap(d->pdev, d->bar);
-}
-
-void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
-                                        QGuestAllocator *alloc, uint16_t entry)
-{
-    uint32_t control;
-    uint64_t off;
-
-    g_assert(d->pdev->msix_enabled);
-    off = d->pdev->msix_table_off + (entry * 16);
-
-    g_assert_cmpint(entry, >=, 0);
-    g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
-    vqpci->msix_entry = entry;
-
-    vqpci->msix_addr = guest_alloc(alloc, 4);
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL);
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_UPPER_ADDR,
-                   (vqpci->msix_addr >> 32) & ~0UL);
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data);
-
-    control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
-                            off + PCI_MSIX_ENTRY_VECTOR_CTRL);
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_VECTOR_CTRL,
-                   control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
-
-    d->msix_ops->set_queue_vector(d, vqpci->vq.index, entry);
-}
-
-void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
-                                        QGuestAllocator *alloc, uint16_t entry)
-{
-    uint32_t control;
-    uint64_t off;
-
-    g_assert(d->pdev->msix_enabled);
-    off = d->pdev->msix_table_off + (entry * 16);
-
-    g_assert_cmpint(entry, >=, 0);
-    g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
-    d->config_msix_entry = entry;
-
-    d->config_msix_data = 0x12345678;
-    d->config_msix_addr = guest_alloc(alloc, 4);
-
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL);
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_UPPER_ADDR,
-                   (d->config_msix_addr >> 32) & ~0UL);
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_DATA, d->config_msix_data);
-
-    control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
-                            off + PCI_MSIX_ENTRY_VECTOR_CTRL);
-    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
-                   off + PCI_MSIX_ENTRY_VECTOR_CTRL,
-                   control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
-
-    d->msix_ops->set_config_vector(d, entry);
-}
-
-void qvirtio_pci_destructor(QOSGraphObject *obj)
-{
-    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj;
-    qvirtio_pci_device_disable(dev);
-    g_free(dev->pdev);
-}
-
-void qvirtio_pci_start_hw(QOSGraphObject *obj)
-{
-    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj;
-    qvirtio_pci_device_enable(dev);
-    qvirtio_start_device(&dev->vdev);
-}
-
-static void qvirtio_pci_init_legacy(QVirtioPCIDevice *dev)
-{
-    dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID);
-    dev->bar_idx = 0;
-    dev->vdev.bus = &qvirtio_pci_legacy;
-    dev->msix_ops = &qvirtio_pci_msix_ops_legacy;
-    dev->vdev.big_endian = qtest_big_endian(dev->pdev->bus->qts);
-}
-
-static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev)
-{
-    dev->pdev = pci_dev;
-    dev->config_msix_entry = -1;
-
-    if (!qvirtio_pci_init_virtio_1(dev)) {
-        qvirtio_pci_init_legacy(dev);
-    }
-
-    /* each virtio-xxx-pci device should override at least this function */
-    dev->obj.get_driver = NULL;
-    dev->obj.start_hw = qvirtio_pci_start_hw;
-    dev->obj.destructor = qvirtio_pci_destructor;
-}
-
-void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr)
-{
-    QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn);
-    g_assert_nonnull(pci_dev);
-    qvirtio_pci_init_from_pcidev(dev, pci_dev);
-}
-
-QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr)
-{
-    QVirtioPCIDevice *dev;
-    QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn);
-    if (!pci_dev) {
-        return NULL;
-    }
-
-    dev = g_new0(QVirtioPCIDevice, 1);
-    qvirtio_pci_init_from_pcidev(dev, pci_dev);
-    dev->obj.free = g_free;
-    return dev;
-}
diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h
deleted file mode 100644 (file)
index 294d556..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * libqos virtio PCI definitions
- *
- * Copyright (c) 2014 Marc MarĂ­
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_VIRTIO_PCI_H
-#define LIBQOS_VIRTIO_PCI_H
-
-#include "libqos/virtio.h"
-#include "libqos/pci.h"
-#include "libqos/qgraph.h"
-
-typedef struct QVirtioPCIMSIXOps QVirtioPCIMSIXOps;
-
-typedef struct QVirtioPCIDevice {
-    QOSGraphObject obj;
-    QVirtioDevice vdev;
-    QPCIDevice *pdev;
-    QPCIBar bar;
-    const QVirtioPCIMSIXOps *msix_ops;
-    uint16_t config_msix_entry;
-    uint64_t config_msix_addr;
-    uint32_t config_msix_data;
-
-    int bar_idx;
-
-    /* VIRTIO 1.0 */
-    uint32_t common_cfg_offset;
-    uint32_t notify_cfg_offset;
-    uint32_t notify_off_multiplier;
-    uint32_t isr_cfg_offset;
-    uint32_t device_cfg_offset;
-} QVirtioPCIDevice;
-
-struct QVirtioPCIMSIXOps {
-    /* Set the Configuration Vector for MSI-X */
-    void (*set_config_vector)(QVirtioPCIDevice *d, uint16_t entry);
-
-    /* Set the Queue Vector for MSI-X */
-    void (*set_queue_vector)(QVirtioPCIDevice *d, uint16_t vq_idx,
-                             uint16_t entry);
-};
-
-typedef struct QVirtQueuePCI {
-    QVirtQueue vq;
-    uint16_t msix_entry;
-    uint64_t msix_addr;
-    uint32_t msix_data;
-
-    /* VIRTIO 1.0 */
-    uint64_t notify_offset;
-} QVirtQueuePCI;
-
-void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr);
-QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr);
-
-/* virtio-pci object functions available for subclasses that
- * override the original start_hw and destroy
- * function. All virtio-xxx-pci subclass that override must
- * take care of calling these two functions in the respective
- * places
- */
-void qvirtio_pci_destructor(QOSGraphObject *obj);
-void qvirtio_pci_start_hw(QOSGraphObject *obj);
-
-
-void qvirtio_pci_device_enable(QVirtioPCIDevice *d);
-void qvirtio_pci_device_disable(QVirtioPCIDevice *d);
-
-void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
-                                        QGuestAllocator *alloc, uint16_t entry);
-void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
-                                        QGuestAllocator *alloc, uint16_t entry);
-
-/* Used by Legacy and Modern virtio-pci code */
-QVirtQueue *qvirtio_pci_virtqueue_setup_common(QVirtioDevice *d,
-                                               QGuestAllocator *alloc,
-                                               uint16_t index);
-void qvirtio_pci_virtqueue_cleanup_common(QVirtQueue *vq,
-                                          QGuestAllocator *alloc);
-
-#endif
diff --git a/tests/libqos/virtio-rng.c b/tests/libqos/virtio-rng.c
deleted file mode 100644 (file)
index b86349e..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/qgraph.h"
-#include "libqos/virtio-rng.h"
-
-/* virtio-rng-device */
-static void *qvirtio_rng_get_driver(QVirtioRng *v_rng,
-                                    const char *interface)
-{
-    if (!g_strcmp0(interface, "virtio-rng")) {
-        return v_rng;
-    }
-    if (!g_strcmp0(interface, "virtio")) {
-        return v_rng->vdev;
-    }
-
-    fprintf(stderr, "%s not present in virtio-rng-device\n", interface);
-    g_assert_not_reached();
-}
-
-static void *qvirtio_rng_device_get_driver(void *object,
-                                           const char *interface)
-{
-    QVirtioRngDevice *v_rng = object;
-    return qvirtio_rng_get_driver(&v_rng->rng, interface);
-}
-
-static void *virtio_rng_device_create(void *virtio_dev,
-                                      QGuestAllocator *t_alloc,
-                                      void *addr)
-{
-    QVirtioRngDevice *virtio_rdevice = g_new0(QVirtioRngDevice, 1);
-    QVirtioRng *interface = &virtio_rdevice->rng;
-
-    interface->vdev = virtio_dev;
-
-    virtio_rdevice->obj.get_driver = qvirtio_rng_device_get_driver;
-
-    return &virtio_rdevice->obj;
-}
-
-/* virtio-rng-pci */
-static void *qvirtio_rng_pci_get_driver(void *object, const char *interface)
-{
-    QVirtioRngPCI *v_rng = object;
-    if (!g_strcmp0(interface, "pci-device")) {
-        return v_rng->pci_vdev.pdev;
-    }
-    return qvirtio_rng_get_driver(&v_rng->rng, interface);
-}
-
-static void *virtio_rng_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
-                                   void *addr)
-{
-    QVirtioRngPCI *virtio_rpci = g_new0(QVirtioRngPCI, 1);
-    QVirtioRng *interface = &virtio_rpci->rng;
-    QOSGraphObject *obj = &virtio_rpci->pci_vdev.obj;
-
-    virtio_pci_init(&virtio_rpci->pci_vdev, pci_bus, addr);
-    interface->vdev = &virtio_rpci->pci_vdev.vdev;
-
-    obj->get_driver = qvirtio_rng_pci_get_driver;
-
-    return obj;
-}
-
-static void virtio_rng_register_nodes(void)
-{
-    QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(4, 0),
-    };
-
-    QOSGraphEdgeOptions opts = {
-        .extra_device_opts = "addr=04.0",
-    };
-
-    /* virtio-rng-device */
-    qos_node_create_driver("virtio-rng-device", virtio_rng_device_create);
-    qos_node_consumes("virtio-rng-device", "virtio-bus", NULL);
-    qos_node_produces("virtio-rng-device", "virtio");
-    qos_node_produces("virtio-rng-device", "virtio-rng");
-
-    /* virtio-rng-pci */
-    add_qpci_address(&opts, &addr);
-    qos_node_create_driver("virtio-rng-pci", virtio_rng_pci_create);
-    qos_node_consumes("virtio-rng-pci", "pci-bus", &opts);
-    qos_node_produces("virtio-rng-pci", "pci-device");
-    qos_node_produces("virtio-rng-pci", "virtio");
-    qos_node_produces("virtio-rng-pci", "virtio-rng");
-}
-
-libqos_init(virtio_rng_register_nodes);
diff --git a/tests/libqos/virtio-rng.h b/tests/libqos/virtio-rng.h
deleted file mode 100644 (file)
index 9e192f1..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TESTS_LIBQOS_VIRTIO_RNG_H
-#define TESTS_LIBQOS_VIRTIO_RNG_H
-
-#include "libqos/qgraph.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-
-typedef struct QVirtioRng QVirtioRng;
-typedef struct QVirtioRngPCI QVirtioRngPCI;
-typedef struct QVirtioRngDevice QVirtioRngDevice;
-
-struct QVirtioRng {
-    QVirtioDevice *vdev;
-};
-
-struct QVirtioRngPCI {
-    QVirtioPCIDevice pci_vdev;
-    QVirtioRng rng;
-};
-
-struct QVirtioRngDevice {
-    QOSGraphObject obj;
-    QVirtioRng rng;
-};
-
-#endif
diff --git a/tests/libqos/virtio-scsi.c b/tests/libqos/virtio-scsi.c
deleted file mode 100644 (file)
index de739be..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "standard-headers/linux/virtio_ids.h"
-#include "libqos/qgraph.h"
-#include "libqos/virtio-scsi.h"
-
-/* virtio-scsi-device */
-static void *qvirtio_scsi_get_driver(QVirtioSCSI *v_scsi,
-                                     const char *interface)
-{
-    if (!g_strcmp0(interface, "virtio-scsi")) {
-        return v_scsi;
-    }
-    if (!g_strcmp0(interface, "virtio")) {
-        return v_scsi->vdev;
-    }
-
-    fprintf(stderr, "%s not present in virtio-scsi-device\n", interface);
-    g_assert_not_reached();
-}
-
-static void *qvirtio_scsi_device_get_driver(void *object,
-                                            const char *interface)
-{
-    QVirtioSCSIDevice *v_scsi = object;
-    return qvirtio_scsi_get_driver(&v_scsi->scsi, interface);
-}
-
-static void *virtio_scsi_device_create(void *virtio_dev,
-                                          QGuestAllocator *t_alloc,
-                                          void *addr)
-{
-    QVirtioSCSIDevice *virtio_bdevice = g_new0(QVirtioSCSIDevice, 1);
-    QVirtioSCSI *interface = &virtio_bdevice->scsi;
-
-    interface->vdev = virtio_dev;
-
-    virtio_bdevice->obj.get_driver = qvirtio_scsi_device_get_driver;
-
-    return &virtio_bdevice->obj;
-}
-
-/* virtio-scsi-pci */
-static void *qvirtio_scsi_pci_get_driver(void *object,
-                                         const char *interface)
-{
-    QVirtioSCSIPCI *v_scsi = object;
-    if (!g_strcmp0(interface, "pci-device")) {
-        return v_scsi->pci_vdev.pdev;
-    }
-    return qvirtio_scsi_get_driver(&v_scsi->scsi, interface);
-}
-
-static void *virtio_scsi_pci_create(void *pci_bus,
-                                    QGuestAllocator *t_alloc,
-                                    void *addr)
-{
-    QVirtioSCSIPCI *virtio_spci = g_new0(QVirtioSCSIPCI, 1);
-    QVirtioSCSI *interface = &virtio_spci->scsi;
-    QOSGraphObject *obj = &virtio_spci->pci_vdev.obj;
-
-    virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr);
-    interface->vdev = &virtio_spci->pci_vdev.vdev;
-
-    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_SCSI);
-
-    obj->get_driver = qvirtio_scsi_pci_get_driver;
-
-    return obj;
-}
-
-static void virtio_scsi_register_nodes(void)
-{
-    QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(4, 0),
-    };
-
-    QOSGraphEdgeOptions opts = {
-        .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,"
-                           "file.read-zeroes=on,format=raw",
-        .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0",
-    };
-
-    /* virtio-scsi-device */
-    opts.extra_device_opts = "id=vs0";
-    qos_node_create_driver("virtio-scsi-device",
-                            virtio_scsi_device_create);
-    qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts);
-    qos_node_produces("virtio-scsi-device", "virtio-scsi");
-
-    /* virtio-scsi-pci */
-    opts.extra_device_opts = "id=vs0,addr=04.0";
-    add_qpci_address(&opts, &addr);
-    qos_node_create_driver("virtio-scsi-pci", virtio_scsi_pci_create);
-    qos_node_consumes("virtio-scsi-pci", "pci-bus", &opts);
-    qos_node_produces("virtio-scsi-pci", "pci-device");
-    qos_node_produces("virtio-scsi-pci", "virtio-scsi");
-}
-
-libqos_init(virtio_scsi_register_nodes);
diff --git a/tests/libqos/virtio-scsi.h b/tests/libqos/virtio-scsi.h
deleted file mode 100644 (file)
index 4ca19a6..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TESTS_LIBQOS_VIRTIO_SCSI_H
-#define TESTS_LIBQOS_VIRTIO_SCSI_H
-
-#include "libqos/qgraph.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-
-typedef struct QVirtioSCSI QVirtioSCSI;
-typedef struct QVirtioSCSIPCI QVirtioSCSIPCI;
-typedef struct QVirtioSCSIDevice QVirtioSCSIDevice;
-
-struct QVirtioSCSI {
-    QVirtioDevice *vdev;
-};
-
-struct QVirtioSCSIPCI {
-    QVirtioPCIDevice pci_vdev;
-    QVirtioSCSI scsi;
-};
-
-struct QVirtioSCSIDevice {
-    QOSGraphObject obj;
-    QVirtioSCSI scsi;
-};
-
-#endif
diff --git a/tests/libqos/virtio-serial.c b/tests/libqos/virtio-serial.c
deleted file mode 100644 (file)
index 3e5b8b8..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "qemu/module.h"
-#include "libqos/qgraph.h"
-#include "libqos/virtio-serial.h"
-
-static void *qvirtio_serial_get_driver(QVirtioSerial *v_serial,
-                                       const char *interface)
-{
-    if (!g_strcmp0(interface, "virtio-serial")) {
-        return v_serial;
-    }
-    if (!g_strcmp0(interface, "virtio")) {
-        return v_serial->vdev;
-    }
-
-    fprintf(stderr, "%s not present in virtio-serial-device\n", interface);
-    g_assert_not_reached();
-}
-
-static void *qvirtio_serial_device_get_driver(void *object,
-                                              const char *interface)
-{
-    QVirtioSerialDevice *v_serial = object;
-    return qvirtio_serial_get_driver(&v_serial->serial, interface);
-}
-
-static void *virtio_serial_device_create(void *virtio_dev,
-                                         QGuestAllocator *t_alloc,
-                                         void *addr)
-{
-    QVirtioSerialDevice *virtio_device = g_new0(QVirtioSerialDevice, 1);
-    QVirtioSerial *interface = &virtio_device->serial;
-
-    interface->vdev = virtio_dev;
-
-    virtio_device->obj.get_driver = qvirtio_serial_device_get_driver;
-
-    return &virtio_device->obj;
-}
-
-/* virtio-serial-pci */
-static void *qvirtio_serial_pci_get_driver(void *object, const char *interface)
-{
-    QVirtioSerialPCI *v_serial = object;
-    if (!g_strcmp0(interface, "pci-device")) {
-        return v_serial->pci_vdev.pdev;
-    }
-    return qvirtio_serial_get_driver(&v_serial->serial, interface);
-}
-
-static void *virtio_serial_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
-                                      void *addr)
-{
-    QVirtioSerialPCI *virtio_spci = g_new0(QVirtioSerialPCI, 1);
-    QVirtioSerial *interface = &virtio_spci->serial;
-    QOSGraphObject *obj = &virtio_spci->pci_vdev.obj;
-
-    virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr);
-    interface->vdev = &virtio_spci->pci_vdev.vdev;
-
-    obj->get_driver = qvirtio_serial_pci_get_driver;
-
-    return obj;
-}
-
-static void virtio_serial_register_nodes(void)
-{
-   QPCIAddress addr = {
-        .devfn = QPCI_DEVFN(4, 0),
-    };
-
-    QOSGraphEdgeOptions edge_opts = { };
-
-    /* virtio-serial-device */
-    edge_opts.extra_device_opts = "id=vser0";
-    qos_node_create_driver("virtio-serial-device",
-                            virtio_serial_device_create);
-    qos_node_consumes("virtio-serial-device", "virtio-bus", &edge_opts);
-    qos_node_produces("virtio-serial-device", "virtio");
-    qos_node_produces("virtio-serial-device", "virtio-serial");
-
-    /* virtio-serial-pci */
-    edge_opts.extra_device_opts = "id=vser0,addr=04.0";
-    add_qpci_address(&edge_opts, &addr);
-    qos_node_create_driver("virtio-serial-pci", virtio_serial_pci_create);
-    qos_node_consumes("virtio-serial-pci", "pci-bus", &edge_opts);
-    qos_node_produces("virtio-serial-pci", "pci-device");
-    qos_node_produces("virtio-serial-pci", "virtio");
-    qos_node_produces("virtio-serial-pci", "virtio-serial");
-}
-
-libqos_init(virtio_serial_register_nodes);
diff --git a/tests/libqos/virtio-serial.h b/tests/libqos/virtio-serial.h
deleted file mode 100644 (file)
index 080fa84..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#ifndef TESTS_LIBQOS_VIRTIO_SERIAL_H
-#define TESTS_LIBQOS_VIRTIO_SERIAL_H
-
-#include "libqos/qgraph.h"
-#include "libqos/virtio.h"
-#include "libqos/virtio-pci.h"
-
-typedef struct QVirtioSerial QVirtioSerial;
-typedef struct QVirtioSerialPCI QVirtioSerialPCI;
-typedef struct QVirtioSerialDevice QVirtioSerialDevice;
-
-struct QVirtioSerial {
-    QVirtioDevice *vdev;
-};
-
-struct QVirtioSerialPCI {
-    QVirtioPCIDevice pci_vdev;
-    QVirtioSerial serial;
-};
-
-struct QVirtioSerialDevice {
-    QOSGraphObject obj;
-    QVirtioSerial serial;
-};
-
-#endif
diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c
deleted file mode 100644 (file)
index 9aa3606..0000000
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * libqos virtio driver
- *
- * Copyright (c) 2014 Marc MarĂ­
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/bswap.h"
-#include "libqtest.h"
-#include "libqos/virtio.h"
-#include "standard-headers/linux/virtio_config.h"
-#include "standard-headers/linux/virtio_ring.h"
-
-/*
- * qtest_readX/writeX() functions transfer host endian from/to guest endian.
- * This works great for Legacy VIRTIO devices where we need guest endian
- * accesses.  For VIRTIO 1.0 the vring is little-endian so the automatic guest
- * endianness conversion is not wanted.
- *
- * The following qvirtio_readX/writeX() functions handle Legacy and VIRTIO 1.0
- * accesses seamlessly.
- */
-static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr)
-{
-    uint16_t val = qtest_readw(qts, addr);
-
-    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
-        val = bswap16(val);
-    }
-    return val;
-}
-
-static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr)
-{
-    uint32_t val = qtest_readl(qts, addr);
-
-    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
-        val = bswap32(val);
-    }
-    return val;
-}
-
-static void qvirtio_writew(QVirtioDevice *d, QTestState *qts,
-                           uint64_t addr, uint16_t val)
-{
-    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
-        val = bswap16(val);
-    }
-    qtest_writew(qts, addr, val);
-}
-
-static void qvirtio_writel(QVirtioDevice *d, QTestState *qts,
-                           uint64_t addr, uint32_t val)
-{
-    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
-        val = bswap32(val);
-    }
-    qtest_writel(qts, addr, val);
-}
-
-static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts,
-                           uint64_t addr, uint64_t val)
-{
-    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
-        val = bswap64(val);
-    }
-    qtest_writeq(qts, addr, val);
-}
-
-uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
-{
-    g_assert_true(d->features_negotiated);
-    return d->bus->config_readb(d, addr);
-}
-
-uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr)
-{
-    g_assert_true(d->features_negotiated);
-    return d->bus->config_readw(d, addr);
-}
-
-uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr)
-{
-    g_assert_true(d->features_negotiated);
-    return d->bus->config_readl(d, addr);
-}
-
-uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr)
-{
-    g_assert_true(d->features_negotiated);
-    return d->bus->config_readq(d, addr);
-}
-
-uint64_t qvirtio_get_features(QVirtioDevice *d)
-{
-    return d->bus->get_features(d);
-}
-
-void qvirtio_set_features(QVirtioDevice *d, uint64_t features)
-{
-    d->features = features;
-    d->bus->set_features(d, features);
-
-    /*
-     * This could be a separate function for drivers that want to access
-     * configuration space before setting FEATURES_OK, but no existing users
-     * need that and it's less code for callers if this is done implicitly.
-    */
-    if (features & (1ull << VIRTIO_F_VERSION_1)) {
-        uint8_t status = d->bus->get_status(d) |
-                         VIRTIO_CONFIG_S_FEATURES_OK;
-
-        d->bus->set_status(d, status);
-        g_assert_cmphex(d->bus->get_status(d), ==, status);
-    }
-
-    d->features_negotiated = true;
-}
-
-QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
-                             QGuestAllocator *alloc, uint16_t index)
-{
-    g_assert_true(d->features_negotiated);
-    return d->bus->virtqueue_setup(d, alloc, index);
-}
-
-void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
-                        QGuestAllocator *alloc)
-{
-    return bus->virtqueue_cleanup(vq, alloc);
-}
-
-void qvirtio_reset(QVirtioDevice *d)
-{
-    d->bus->set_status(d, 0);
-    g_assert_cmphex(d->bus->get_status(d), ==, 0);
-    d->features_negotiated = false;
-}
-
-void qvirtio_set_acknowledge(QVirtioDevice *d)
-{
-    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
-    g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
-}
-
-void qvirtio_set_driver(QVirtioDevice *d)
-{
-    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
-    g_assert_cmphex(d->bus->get_status(d), ==,
-                    VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
-}
-
-void qvirtio_set_driver_ok(QVirtioDevice *d)
-{
-    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
-    g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
-                    VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE |
-                    (d->features & (1ull << VIRTIO_F_VERSION_1) ?
-                     VIRTIO_CONFIG_S_FEATURES_OK : 0));
-}
-
-void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d,
-                            QVirtQueue *vq, gint64 timeout_us)
-{
-    gint64 start_time = g_get_monotonic_time();
-
-    for (;;) {
-        qtest_clock_step(qts, 100);
-        if (d->bus->get_queue_isr_status(d, vq)) {
-            return;
-        }
-        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
-    }
-}
-
-/* Wait for the status byte at given guest memory address to be set
- *
- * The virtqueue interrupt must not be raised, making this useful for testing
- * event_index functionality.
- */
-uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d,
-                                        QVirtQueue *vq,
-                                        uint64_t addr,
-                                        gint64 timeout_us)
-{
-    gint64 start_time = g_get_monotonic_time();
-    uint8_t val;
-
-    while ((val = qtest_readb(qts, addr)) == 0xff) {
-        qtest_clock_step(qts, 100);
-        g_assert(!d->bus->get_queue_isr_status(d, vq));
-        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
-    }
-    return val;
-}
-
-/*
- * qvirtio_wait_used_elem:
- * @desc_idx: The next expected vq->desc[] index in the used ring
- * @len: A pointer that is filled with the length written into the buffer, may
- *       be NULL
- * @timeout_us: How many microseconds to wait before failing
- *
- * This function waits for the next completed request on the used ring.
- */
-void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d,
-                            QVirtQueue *vq,
-                            uint32_t desc_idx,
-                            uint32_t *len,
-                            gint64 timeout_us)
-{
-    gint64 start_time = g_get_monotonic_time();
-
-    for (;;) {
-        uint32_t got_desc_idx;
-
-        qtest_clock_step(qts, 100);
-
-        if (d->bus->get_queue_isr_status(d, vq) &&
-            qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) {
-            g_assert_cmpint(got_desc_idx, ==, desc_idx);
-            return;
-        }
-
-        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
-    }
-}
-
-void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us)
-{
-    d->bus->wait_config_isr_status(d, timeout_us);
-}
-
-void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
-                 uint64_t addr)
-{
-    int i;
-
-    vq->desc = addr;
-    vq->avail = vq->desc + vq->size * sizeof(struct vring_desc);
-    vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
-        + vq->align - 1) & ~(vq->align - 1));
-
-    for (i = 0; i < vq->size - 1; i++) {
-        /* vq->desc[i].addr */
-        qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0);
-        /* vq->desc[i].next */
-        qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1);
-    }
-
-    /* vq->avail->flags */
-    qvirtio_writew(vq->vdev, qts, vq->avail, 0);
-    /* vq->avail->idx */
-    qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0);
-    /* vq->avail->used_event */
-    qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0);
-
-    /* vq->used->flags */
-    qvirtio_writew(vq->vdev, qts, vq->used, 0);
-    /* vq->used->avail_event */
-    qvirtio_writew(vq->vdev, qts, vq->used + 2 +
-                   sizeof(struct vring_used_elem) * vq->size, 0);
-}
-
-QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
-                                               QGuestAllocator *alloc,
-                                               uint16_t elem)
-{
-    int i;
-    QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
-
-    indirect->index = 0;
-    indirect->elem = elem;
-    indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem);
-
-    for (i = 0; i < elem - 1; ++i) {
-        /* indirect->desc[i].addr */
-        qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0);
-        /* indirect->desc[i].flags */
-        qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12,
-                       VRING_DESC_F_NEXT);
-        /* indirect->desc[i].next */
-        qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1);
-    }
-
-    return indirect;
-}
-
-void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
-                              QVRingIndirectDesc *indirect,
-                              uint64_t data, uint32_t len, bool write)
-{
-    uint16_t flags;
-
-    g_assert_cmpint(indirect->index, <, indirect->elem);
-
-    flags = qvirtio_readw(d, qts, indirect->desc +
-                                  (16 * indirect->index) + 12);
-
-    if (write) {
-        flags |= VRING_DESC_F_WRITE;
-    }
-
-    /* indirect->desc[indirect->index].addr */
-    qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data);
-    /* indirect->desc[indirect->index].len */
-    qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len);
-    /* indirect->desc[indirect->index].flags */
-    qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12,
-                   flags);
-
-    indirect->index++;
-}
-
-uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
-                        uint32_t len, bool write, bool next)
-{
-    uint16_t flags = 0;
-    vq->num_free--;
-
-    if (write) {
-        flags |= VRING_DESC_F_WRITE;
-    }
-
-    if (next) {
-        flags |= VRING_DESC_F_NEXT;
-    }
-
-    /* vq->desc[vq->free_head].addr */
-    qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data);
-    /* vq->desc[vq->free_head].len */
-    qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len);
-    /* vq->desc[vq->free_head].flags */
-    qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags);
-
-    return vq->free_head++; /* Return and increase, in this order */
-}
-
-uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq,
-                                 QVRingIndirectDesc *indirect)
-{
-    g_assert(vq->indirect);
-    g_assert_cmpint(vq->size, >=, indirect->elem);
-    g_assert_cmpint(indirect->index, ==, indirect->elem);
-
-    vq->num_free--;
-
-    /* vq->desc[vq->free_head].addr */
-    qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head),
-                   indirect->desc);
-    /* vq->desc[vq->free_head].len */
-    qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8,
-                   sizeof(struct vring_desc) * indirect->elem);
-    /* vq->desc[vq->free_head].flags */
-    qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12,
-                   VRING_DESC_F_INDIRECT);
-
-    return vq->free_head++; /* Return and increase, in this order */
-}
-
-void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
-                     uint32_t free_head)
-{
-    /* vq->avail->idx */
-    uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2);
-    /* vq->used->flags */
-    uint16_t flags;
-    /* vq->used->avail_event */
-    uint16_t avail_event;
-
-    /* vq->avail->ring[idx % vq->size] */
-    qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head);
-    /* vq->avail->idx */
-    qvirtio_writew(d, qts, vq->avail + 2, idx + 1);
-
-    /* Must read after idx is updated */
-    flags = qvirtio_readw(d, qts, vq->avail);
-    avail_event = qvirtio_readw(d, qts, vq->used + 4 +
-                                sizeof(struct vring_used_elem) * vq->size);
-
-    /* < 1 because we add elements to avail queue one by one */
-    if ((flags & VRING_USED_F_NO_NOTIFY) == 0 &&
-                            (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
-        d->bus->virtqueue_kick(d, vq);
-    }
-}
-
-/*
- * qvirtqueue_get_buf:
- * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL
- * @len: A pointer that is filled with the length written into the buffer, may
- *       be NULL
- *
- * This function gets the next used element if there is one ready.
- *
- * Returns: true if an element was ready, false otherwise
- */
-bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
-                        uint32_t *len)
-{
-    uint16_t idx;
-    uint64_t elem_addr, addr;
-
-    idx = qvirtio_readw(vq->vdev, qts,
-                        vq->used + offsetof(struct vring_used, idx));
-    if (idx == vq->last_used_idx) {
-        return false;
-    }
-
-    elem_addr = vq->used +
-        offsetof(struct vring_used, ring) +
-        (vq->last_used_idx % vq->size) *
-        sizeof(struct vring_used_elem);
-
-    if (desc_idx) {
-        addr = elem_addr + offsetof(struct vring_used_elem, id);
-        *desc_idx = qvirtio_readl(vq->vdev, qts, addr);
-    }
-
-    if (len) {
-        addr = elem_addr + offsetof(struct vring_used_elem, len);
-        *len = qvirtio_readw(vq->vdev, qts, addr);
-    }
-
-    vq->last_used_idx++;
-    return true;
-}
-
-void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx)
-{
-    g_assert(vq->event);
-
-    /* vq->avail->used_event */
-    qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx);
-}
-
-void qvirtio_start_device(QVirtioDevice *vdev)
-{
-    qvirtio_reset(vdev);
-    qvirtio_set_acknowledge(vdev);
-    qvirtio_set_driver(vdev);
-}
-
-bool qvirtio_is_big_endian(QVirtioDevice *d)
-{
-    return d->big_endian;
-}
diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h
deleted file mode 100644 (file)
index 529ef75..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * libqos virtio definitions
- *
- * Copyright (c) 2014 Marc MarĂ­
- *
- * This work is licensed under the terms of the GNU GPL, version 2 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LIBQOS_VIRTIO_H
-#define LIBQOS_VIRTIO_H
-
-#include "libqos/malloc.h"
-#include "standard-headers/linux/virtio_ring.h"
-
-#define QVIRTIO_F_BAD_FEATURE           0x40000000ull
-
-typedef struct QVirtioBus QVirtioBus;
-
-typedef struct QVirtioDevice {
-    const QVirtioBus *bus;
-    /* Device type */
-    uint16_t device_type;
-    uint64_t features;
-    bool big_endian;
-    bool features_negotiated;
-} QVirtioDevice;
-
-typedef struct QVirtQueue {
-    QVirtioDevice *vdev;
-    uint64_t desc; /* This points to an array of struct vring_desc */
-    uint64_t avail; /* This points to a struct vring_avail */
-    uint64_t used; /* This points to a struct vring_used */
-    uint16_t index;
-    uint32_t size;
-    uint32_t free_head;
-    uint32_t num_free;
-    uint32_t align;
-    uint16_t last_used_idx;
-    bool indirect;
-    bool event;
-} QVirtQueue;
-
-typedef struct QVRingIndirectDesc {
-    uint64_t desc; /* This points to an array fo struct vring_desc */
-    uint16_t index;
-    uint16_t elem;
-} QVRingIndirectDesc;
-
-struct QVirtioBus {
-    uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr);
-    uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr);
-    uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr);
-    uint64_t (*config_readq)(QVirtioDevice *d, uint64_t addr);
-
-    /* Get features of the device */
-    uint64_t (*get_features)(QVirtioDevice *d);
-
-    /* Set features of the device */
-    void (*set_features)(QVirtioDevice *d, uint64_t features);
-
-    /* Get features of the guest */
-    uint64_t (*get_guest_features)(QVirtioDevice *d);
-
-    /* Get status of the device */
-    uint8_t (*get_status)(QVirtioDevice *d);
-
-    /* Set status of the device  */
-    void (*set_status)(QVirtioDevice *d, uint8_t status);
-
-    /* Get the queue ISR status of the device */
-    bool (*get_queue_isr_status)(QVirtioDevice *d, QVirtQueue *vq);
-
-    /* Wait for the configuration ISR status of the device */
-    void (*wait_config_isr_status)(QVirtioDevice *d, gint64 timeout_us);
-
-    /* Select a queue to work on */
-    void (*queue_select)(QVirtioDevice *d, uint16_t index);
-
-    /* Get the size of the selected queue */
-    uint16_t (*get_queue_size)(QVirtioDevice *d);
-
-    /* Set the address of the selected queue */
-    void (*set_queue_address)(QVirtioDevice *d, QVirtQueue *vq);
-
-    /* Setup the virtqueue specified by index */
-    QVirtQueue *(*virtqueue_setup)(QVirtioDevice *d, QGuestAllocator *alloc,
-                                                                uint16_t index);
-
-    /* Free virtqueue resources */
-    void (*virtqueue_cleanup)(QVirtQueue *vq, QGuestAllocator *alloc);
-
-    /* Notify changes in virtqueue */
-    void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq);
-};
-
-static inline uint32_t qvring_size(uint32_t num, uint32_t align)
-{
-    return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num)
-        + align - 1) & ~(align - 1))
-        + sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num;
-}
-
-uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr);
-uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr);
-uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr);
-uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr);
-uint64_t qvirtio_get_features(QVirtioDevice *d);
-void qvirtio_set_features(QVirtioDevice *d, uint64_t features);
-bool qvirtio_is_big_endian(QVirtioDevice *d);
-
-void qvirtio_reset(QVirtioDevice *d);
-void qvirtio_set_acknowledge(QVirtioDevice *d);
-void qvirtio_set_driver(QVirtioDevice *d);
-void qvirtio_set_driver_ok(QVirtioDevice *d);
-
-void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d,
-                            QVirtQueue *vq, gint64 timeout_us);
-uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d,
-                                        QVirtQueue *vq,
-                                        uint64_t addr,
-                                        gint64 timeout_us);
-void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d,
-                            QVirtQueue *vq,
-                            uint32_t desc_idx,
-                            uint32_t *len,
-                            gint64 timeout_us);
-void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us);
-QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
-                             QGuestAllocator *alloc, uint16_t index);
-void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
-                        QGuestAllocator *alloc);
-
-void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
-                 uint64_t addr);
-QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
-                                               QGuestAllocator *alloc,
-                                               uint16_t elem);
-void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
-                              QVRingIndirectDesc *indirect,
-                              uint64_t data, uint32_t len, bool write);
-uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
-                        uint32_t len, bool write, bool next);
-uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq,
-                                 QVRingIndirectDesc *indirect);
-void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
-                     uint32_t free_head);
-bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
-                        uint32_t *len);
-
-void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx);
-
-void qvirtio_start_device(QVirtioDevice *vdev);
-
-#endif
diff --git a/tests/libqos/x86_64_pc-machine.c b/tests/libqos/x86_64_pc-machine.c
deleted file mode 100644 (file)
index 6dfa705..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * libqos driver framework
- *
- * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2 as published by the Free Software Foundation.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>
- */
-
-#include "qemu/osdep.h"
-#include "libqtest.h"
-#include "libqos/qgraph.h"
-#include "pci-pc.h"
-#include "qemu/module.h"
-#include "malloc-pc.h"
-
-typedef struct QX86PCMachine QX86PCMachine;
-typedef struct i440FX_pcihost i440FX_pcihost;
-typedef struct QSDHCI_PCI  QSDHCI_PCI;
-
-struct i440FX_pcihost {
-    QOSGraphObject obj;
-    QPCIBusPC pci;
-};
-
-struct QX86PCMachine {
-    QOSGraphObject obj;
-    QGuestAllocator alloc;
-    i440FX_pcihost bridge;
-};
-
-/* i440FX_pcihost */
-
-static QOSGraphObject *i440FX_host_get_device(void *obj, const char *device)
-{
-    i440FX_pcihost *host = obj;
-    if (!g_strcmp0(device, "pci-bus-pc")) {
-        return &host->pci.obj;
-    }
-    fprintf(stderr, "%s not present in i440FX-pcihost\n", device);
-    g_assert_not_reached();
-}
-
-static void qos_create_i440FX_host(i440FX_pcihost *host,
-                                   QTestState *qts,
-                                   QGuestAllocator *alloc)
-{
-    host->obj.get_device = i440FX_host_get_device;
-    qpci_init_pc(&host->pci, qts, alloc);
-}
-
-/* x86_64/pc machine */
-
-static void pc_destructor(QOSGraphObject *obj)
-{
-    QX86PCMachine *machine = (QX86PCMachine *) obj;
-    alloc_destroy(&machine->alloc);
-}
-
-static void *pc_get_driver(void *object, const char *interface)
-{
-    QX86PCMachine *machine = object;
-    if (!g_strcmp0(interface, "memory")) {
-        return &machine->alloc;
-    }
-
-    fprintf(stderr, "%s not present in x86_64/pc\n", interface);
-    g_assert_not_reached();
-}
-
-static QOSGraphObject *pc_get_device(void *obj, const char *device)
-{
-    QX86PCMachine *machine = obj;
-    if (!g_strcmp0(device, "i440FX-pcihost")) {
-        return &machine->bridge.obj;
-    }
-
-    fprintf(stderr, "%s not present in x86_64/pc\n", device);
-    g_assert_not_reached();
-}
-
-static void *qos_create_machine_pc(QTestState *qts)
-{
-    QX86PCMachine *machine = g_new0(QX86PCMachine, 1);
-    machine->obj.get_device = pc_get_device;
-    machine->obj.get_driver = pc_get_driver;
-    machine->obj.destructor = pc_destructor;
-    pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS);
-    qos_create_i440FX_host(&machine->bridge, qts, &machine->alloc);
-
-    return &machine->obj;
-}
-
-static void pc_machine_register_nodes(void)
-{
-    qos_node_create_machine("i386/pc", qos_create_machine_pc);
-    qos_node_contains("i386/pc", "i440FX-pcihost", NULL);
-
-    qos_node_create_machine("x86_64/pc", qos_create_machine_pc);
-    qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL);
-
-    qos_node_create_driver("i440FX-pcihost", NULL);
-    qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL);
-}
-
-libqos_init(pc_machine_register_nodes);
index 816d0d90eaa341dabdddec7459c9bb3106c332da..e6bb4ab28c0ed31cbad092bf633f41788f7e6486 100644 (file)
@@ -155,52 +155,52 @@ check-qtest-s390x-y += cpu-plug-test
 check-qtest-s390x-y += migration-test
 
 # libqos / qgraph :
-libqgraph-obj-y = tests/libqos/qgraph.o
-
-libqos-obj-y = $(libqgraph-obj-y) tests/libqos/pci.o tests/libqos/fw_cfg.o
-libqos-obj-y += tests/libqos/malloc.o
-libqos-obj-y += tests/libqos/libqos.o
-libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o
-libqos-spapr-obj-y += tests/libqos/libqos-spapr.o
-libqos-spapr-obj-y += tests/libqos/rtas.o
-libqos-spapr-obj-y += tests/libqos/pci-spapr.o
-libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
-libqos-pc-obj-y += tests/libqos/malloc-pc.o tests/libqos/libqos-pc.o
-libqos-pc-obj-y += tests/libqos/ahci.o
-libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
+libqgraph-obj-y = tests/qtest/libqos/qgraph.o
+
+libqos-obj-y = $(libqgraph-obj-y) tests/qtest/libqos/pci.o tests/qtest/libqos/fw_cfg.o
+libqos-obj-y += tests/qtest/libqos/malloc.o
+libqos-obj-y += tests/qtest/libqos/libqos.o
+libqos-spapr-obj-y = $(libqos-obj-y) tests/qtest/libqos/malloc-spapr.o
+libqos-spapr-obj-y += tests/qtest/libqos/libqos-spapr.o
+libqos-spapr-obj-y += tests/qtest/libqos/rtas.o
+libqos-spapr-obj-y += tests/qtest/libqos/pci-spapr.o
+libqos-pc-obj-y = $(libqos-obj-y) tests/qtest/libqos/pci-pc.o
+libqos-pc-obj-y += tests/qtest/libqos/malloc-pc.o tests/qtest/libqos/libqos-pc.o
+libqos-pc-obj-y += tests/qtest/libqos/ahci.o
+libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/qtest/libqos/usb.o
 
 # qos devices:
 qos-test-obj-y = tests/qtest/qos-test.o $(libqgraph-obj-y)
 qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y)
-qos-test-obj-y += tests/libqos/e1000e.o
-qos-test-obj-y += tests/libqos/i2c.o
-qos-test-obj-y += tests/libqos/i2c-imx.o
-qos-test-obj-y += tests/libqos/i2c-omap.o
-qos-test-obj-y += tests/libqos/sdhci.o
-qos-test-obj-y += tests/libqos/tpci200.o
-qos-test-obj-y += tests/libqos/virtio.o
-qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o
-qos-test-obj-y += tests/libqos/virtio-balloon.o
-qos-test-obj-y += tests/libqos/virtio-blk.o
-qos-test-obj-y += tests/libqos/virtio-mmio.o
-qos-test-obj-y += tests/libqos/virtio-net.o
-qos-test-obj-y += tests/libqos/virtio-pci.o
-qos-test-obj-y += tests/libqos/virtio-pci-modern.o
-qos-test-obj-y += tests/libqos/virtio-rng.o
-qos-test-obj-y += tests/libqos/virtio-scsi.o
-qos-test-obj-y += tests/libqos/virtio-serial.o
+qos-test-obj-y += tests/qtest/libqos/e1000e.o
+qos-test-obj-y += tests/qtest/libqos/i2c.o
+qos-test-obj-y += tests/qtest/libqos/i2c-imx.o
+qos-test-obj-y += tests/qtest/libqos/i2c-omap.o
+qos-test-obj-y += tests/qtest/libqos/sdhci.o
+qos-test-obj-y += tests/qtest/libqos/tpci200.o
+qos-test-obj-y += tests/qtest/libqos/virtio.o
+qos-test-obj-$(CONFIG_VIRTFS) += tests/qtest/libqos/virtio-9p.o
+qos-test-obj-y += tests/qtest/libqos/virtio-balloon.o
+qos-test-obj-y += tests/qtest/libqos/virtio-blk.o
+qos-test-obj-y += tests/qtest/libqos/virtio-mmio.o
+qos-test-obj-y += tests/qtest/libqos/virtio-net.o
+qos-test-obj-y += tests/qtest/libqos/virtio-pci.o
+qos-test-obj-y += tests/qtest/libqos/virtio-pci-modern.o
+qos-test-obj-y += tests/qtest/libqos/virtio-rng.o
+qos-test-obj-y += tests/qtest/libqos/virtio-scsi.o
+qos-test-obj-y += tests/qtest/libqos/virtio-serial.o
 
 # qos machines:
-qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o
-qos-test-obj-y += tests/libqos/arm-imx25-pdk-machine.o
-qos-test-obj-y += tests/libqos/arm-n800-machine.o
-qos-test-obj-y += tests/libqos/arm-raspi2-machine.o
-qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o
-qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o
-qos-test-obj-y += tests/libqos/arm-virt-machine.o
-qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o
-qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o
-qos-test-obj-y += tests/libqos/x86_64_pc-machine.o
+qos-test-obj-y += tests/qtest/libqos/aarch64-xlnx-zcu102-machine.o
+qos-test-obj-y += tests/qtest/libqos/arm-imx25-pdk-machine.o
+qos-test-obj-y += tests/qtest/libqos/arm-n800-machine.o
+qos-test-obj-y += tests/qtest/libqos/arm-raspi2-machine.o
+qos-test-obj-y += tests/qtest/libqos/arm-sabrelite-machine.o
+qos-test-obj-y += tests/qtest/libqos/arm-smdkc210-machine.o
+qos-test-obj-y += tests/qtest/libqos/arm-virt-machine.o
+qos-test-obj-y += tests/qtest/libqos/arm-xilinx-zynq-a9-machine.o
+qos-test-obj-y += tests/qtest/libqos/ppc64_pseries-machine.o
+qos-test-obj-y += tests/qtest/libqos/x86_64_pc-machine.o
 
 # qos tests:
 qos-test-obj-y += tests/qtest/ac97-test.o
diff --git a/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c b/tests/qtest/libqos/aarch64-xlnx-zcu102-machine.c
new file mode 100644 (file)
index 0000000..1d5de5a
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "sdhci.h"
+
+typedef struct QXlnxZCU102Machine QXlnxZCU102Machine;
+
+struct QXlnxZCU102Machine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    QSDHCI_MemoryMapped sdhci;
+};
+
+#define ARM_PAGE_SIZE          4096
+#define XLNX_ZCU102_RAM_ADDR   0
+#define XLNX_ZCU102_RAM_SIZE   0x20000000
+
+static void *xlnx_zcu102_get_driver(void *object, const char *interface)
+{
+    QXlnxZCU102Machine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *xlnx_zcu102_get_device(void *obj, const char *device)
+{
+    QXlnxZCU102Machine *machine = obj;
+    if (!g_strcmp0(device, "generic-sdhci")) {
+        return &machine->sdhci.obj;
+    }
+
+    fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", device);
+    g_assert_not_reached();
+}
+
+static void xlnx_zcu102_destructor(QOSGraphObject *obj)
+{
+    QXlnxZCU102Machine *machine = (QXlnxZCU102Machine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_aarch64_xlnx_zcu102(QTestState *qts)
+{
+    QXlnxZCU102Machine *machine = g_new0(QXlnxZCU102Machine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               XLNX_ZCU102_RAM_ADDR + (1 << 20),
+               XLNX_ZCU102_RAM_ADDR + XLNX_ZCU102_RAM_SIZE,
+               ARM_PAGE_SIZE);
+
+    machine->obj.get_device = xlnx_zcu102_get_device;
+    machine->obj.get_driver = xlnx_zcu102_get_driver;
+    machine->obj.destructor = xlnx_zcu102_destructor;
+    /* Datasheet: UG1085 (v1.7) */
+    qos_init_sdhci_mm(&machine->sdhci, qts, 0xff160000, &(QSDHCIProperties) {
+        .version = 3,
+        .baseclock = 0,
+        .capab.sdma = true,
+        .capab.reg = 0x280737ec6481
+    });
+    return &machine->obj;
+}
+
+static void xlnx_zcu102_register_nodes(void)
+{
+    qos_node_create_machine("aarch64/xlnx-zcu102",
+                            qos_create_machine_aarch64_xlnx_zcu102);
+    qos_node_contains("aarch64/xlnx-zcu102", "generic-sdhci", NULL);
+}
+
+libqos_init(xlnx_zcu102_register_nodes);
diff --git a/tests/qtest/libqos/ahci.c b/tests/qtest/libqos/ahci.c
new file mode 100644 (file)
index 0000000..cc1b08e
--- /dev/null
@@ -0,0 +1,1242 @@
+/*
+ * libqos AHCI functions
+ *
+ * Copyright (c) 2014 John Snow <jsnow@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 "qemu/osdep.h"
+
+#include "libqtest.h"
+#include "libqos/ahci.h"
+#include "libqos/pci-pc.h"
+
+#include "qemu-common.h"
+#include "qemu/host-utils.h"
+
+#include "hw/pci/pci_ids.h"
+#include "hw/pci/pci_regs.h"
+
+typedef struct AHCICommandProp {
+    uint8_t  cmd;        /* Command Code */
+    bool     data;       /* Data transfer command? */
+    bool     pio;
+    bool     dma;
+    bool     lba28;
+    bool     lba48;
+    bool     read;
+    bool     write;
+    bool     atapi;
+    bool     ncq;
+    uint64_t size;       /* Static transfer size, for commands like IDENTIFY. */
+    uint32_t interrupts; /* Expected interrupts for this command. */
+} AHCICommandProp;
+
+AHCICommandProp ahci_command_properties[] = {
+    { .cmd = CMD_READ_PIO,       .data = true,  .pio = true,
+                                 .lba28 = true, .read = true },
+    { .cmd = CMD_WRITE_PIO,      .data = true,  .pio = true,
+                                 .lba28 = true, .write = true },
+    { .cmd = CMD_READ_PIO_EXT,   .data = true,  .pio = true,
+                                 .lba48 = true, .read = true },
+    { .cmd = CMD_WRITE_PIO_EXT,  .data = true,  .pio = true,
+                                 .lba48 = true, .write = true },
+    { .cmd = CMD_READ_DMA,       .data = true,  .dma = true,
+                                 .lba28 = true, .read = true },
+    { .cmd = CMD_WRITE_DMA,      .data = true,  .dma = true,
+                                 .lba28 = true, .write = true },
+    { .cmd = CMD_READ_DMA_EXT,   .data = true,  .dma = true,
+                                 .lba48 = true, .read = true },
+    { .cmd = CMD_WRITE_DMA_EXT,  .data = true,  .dma = true,
+                                 .lba48 = true, .write = true },
+    { .cmd = CMD_IDENTIFY,       .data = true,  .pio = true,
+                                 .size = 512,   .read = true },
+    { .cmd = READ_FPDMA_QUEUED,  .data = true,  .dma = true,
+                                 .lba48 = true, .read = true, .ncq = true },
+    { .cmd = WRITE_FPDMA_QUEUED, .data = true,  .dma = true,
+                                 .lba48 = true, .write = true, .ncq = true },
+    { .cmd = CMD_READ_MAX,       .lba28 = true },
+    { .cmd = CMD_READ_MAX_EXT,   .lba48 = true },
+    { .cmd = CMD_FLUSH_CACHE,    .data = false },
+    { .cmd = CMD_PACKET,         .data = true,  .size = 16,
+                                 .atapi = true, .pio = true },
+    { .cmd = CMD_PACKET_ID,      .data = true,  .pio = true,
+                                 .size = 512,   .read = true }
+};
+
+struct AHCICommand {
+    /* Test Management Data */
+    uint8_t name;
+    uint8_t port;
+    uint8_t slot;
+    uint8_t errors;
+    uint32_t interrupts;
+    uint64_t xbytes;
+    uint32_t prd_size;
+    uint32_t sector_size;
+    uint64_t buffer;
+    AHCICommandProp *props;
+    /* Data to be transferred to the guest */
+    AHCICommandHeader header;
+    RegH2DFIS fis;
+    unsigned char *atapi_cmd;
+};
+
+/**
+ * Allocate space in the guest using information in the AHCIQState object.
+ */
+uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes)
+{
+    g_assert(ahci);
+    g_assert(ahci->parent);
+    return qmalloc(ahci->parent, bytes);
+}
+
+void ahci_free(AHCIQState *ahci, uint64_t addr)
+{
+    g_assert(ahci);
+    g_assert(ahci->parent);
+    qfree(ahci->parent, addr);
+}
+
+bool is_atapi(AHCIQState *ahci, uint8_t port)
+{
+    return ahci_px_rreg(ahci, port, AHCI_PX_SIG) == AHCI_SIGNATURE_CDROM;
+}
+
+/**
+ * Locate, verify, and return a handle to the AHCI device.
+ */
+QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint)
+{
+    QPCIDevice *ahci;
+    uint32_t ahci_fingerprint;
+    QPCIBus *pcibus;
+
+    pcibus = qpci_new_pc(qts, NULL);
+
+    /* Find the AHCI PCI device and verify it's the right one. */
+    ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02));
+    g_assert(ahci != NULL);
+
+    ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID);
+
+    switch (ahci_fingerprint) {
+    case AHCI_INTEL_ICH9:
+        break;
+    default:
+        /* Unknown device. */
+        g_assert_not_reached();
+    }
+
+    if (fingerprint) {
+        *fingerprint = ahci_fingerprint;
+    }
+    return ahci;
+}
+
+void free_ahci_device(QPCIDevice *dev)
+{
+    QPCIBus *pcibus = dev ? dev->bus : NULL;
+
+    /* libqos doesn't have a function for this, so free it manually */
+    g_free(dev);
+    qpci_free_pc(pcibus);
+}
+
+/* Free all memory in-use by the AHCI device. */
+void ahci_clean_mem(AHCIQState *ahci)
+{
+    uint8_t port, slot;
+
+    for (port = 0; port < 32; ++port) {
+        if (ahci->port[port].fb) {
+            ahci_free(ahci, ahci->port[port].fb);
+            ahci->port[port].fb = 0;
+        }
+        if (ahci->port[port].clb) {
+            for (slot = 0; slot < 32; slot++) {
+                ahci_destroy_command(ahci, port, slot);
+            }
+            ahci_free(ahci, ahci->port[port].clb);
+            ahci->port[port].clb = 0;
+        }
+    }
+}
+
+/*** Logical Device Initialization ***/
+
+/**
+ * Start the PCI device and sanity-check default operation.
+ */
+void ahci_pci_enable(AHCIQState *ahci)
+{
+    uint8_t reg;
+
+    start_ahci_device(ahci);
+
+    switch (ahci->fingerprint) {
+    case AHCI_INTEL_ICH9:
+        /* ICH9 has a register at PCI 0x92 that
+         * acts as a master port enabler mask. */
+        reg = qpci_config_readb(ahci->dev, 0x92);
+        reg |= 0x3F;
+        qpci_config_writeb(ahci->dev, 0x92, reg);
+        /* 0...0111111b -- bit significant, ports 0-5 enabled. */
+        ASSERT_BIT_SET(qpci_config_readb(ahci->dev, 0x92), 0x3F);
+        break;
+    }
+
+}
+
+/**
+ * Map BAR5/ABAR, and engage the PCI device.
+ */
+void start_ahci_device(AHCIQState *ahci)
+{
+    /* Map AHCI's ABAR (BAR5) */
+    ahci->hba_bar = qpci_iomap(ahci->dev, 5, &ahci->barsize);
+
+    /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */
+    qpci_device_enable(ahci->dev);
+}
+
+/**
+ * Test and initialize the AHCI's HBA memory areas.
+ * Initialize and start any ports with devices attached.
+ * Bring the HBA into the idle state.
+ */
+void ahci_hba_enable(AHCIQState *ahci)
+{
+    /* Bits of interest in this section:
+     * GHC.AE     Global Host Control / AHCI Enable
+     * PxCMD.ST   Port Command: Start
+     * PxCMD.SUD  "Spin Up Device"
+     * PxCMD.POD  "Power On Device"
+     * PxCMD.FRE  "FIS Receive Enable"
+     * PxCMD.FR   "FIS Receive Running"
+     * PxCMD.CR   "Command List Running"
+     */
+    uint32_t reg, ports_impl;
+    uint16_t i;
+    uint8_t num_cmd_slots;
+
+    g_assert(ahci != NULL);
+
+    /* Set GHC.AE to 1 */
+    ahci_set(ahci, AHCI_GHC, AHCI_GHC_AE);
+    reg = ahci_rreg(ahci, AHCI_GHC);
+    ASSERT_BIT_SET(reg, AHCI_GHC_AE);
+
+    /* Cache CAP and CAP2. */
+    ahci->cap = ahci_rreg(ahci, AHCI_CAP);
+    ahci->cap2 = ahci_rreg(ahci, AHCI_CAP2);
+
+    /* Read CAP.NCS, how many command slots do we have? */
+    num_cmd_slots = ((ahci->cap & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1;
+    g_test_message("Number of Command Slots: %u", num_cmd_slots);
+
+    /* Determine which ports are implemented. */
+    ports_impl = ahci_rreg(ahci, AHCI_PI);
+
+    for (i = 0; ports_impl; ports_impl >>= 1, ++i) {
+        if (!(ports_impl & 0x01)) {
+            continue;
+        }
+
+        g_test_message("Initializing port %u", i);
+
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
+        if (BITCLR(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR |
+                   AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) {
+            g_test_message("port is idle");
+        } else {
+            g_test_message("port needs to be idled");
+            ahci_px_clr(ahci, i, AHCI_PX_CMD,
+                        (AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE));
+            /* The port has 500ms to disengage. */
+            usleep(500000);
+            reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
+            ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR);
+            ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR);
+            g_test_message("port is now idle");
+            /* The spec does allow for possibly needing a PORT RESET
+             * or HBA reset if we fail to idle the port. */
+        }
+
+        /* Allocate Memory for the Command List Buffer & FIS Buffer */
+        /* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */
+        ahci->port[i].clb = ahci_alloc(ahci, num_cmd_slots * 0x20);
+        qtest_memset(ahci->parent->qts, ahci->port[i].clb, 0x00,
+                     num_cmd_slots * 0x20);
+        g_test_message("CLB: 0x%08" PRIx64, ahci->port[i].clb);
+        ahci_px_wreg(ahci, i, AHCI_PX_CLB, ahci->port[i].clb);
+        g_assert_cmphex(ahci->port[i].clb, ==,
+                        ahci_px_rreg(ahci, i, AHCI_PX_CLB));
+
+        /* PxFB space ... 0x100, as in 4.2.1 p 35 */
+        ahci->port[i].fb = ahci_alloc(ahci, 0x100);
+        qtest_memset(ahci->parent->qts, ahci->port[i].fb, 0x00, 0x100);
+        g_test_message("FB: 0x%08" PRIx64, ahci->port[i].fb);
+        ahci_px_wreg(ahci, i, AHCI_PX_FB, ahci->port[i].fb);
+        g_assert_cmphex(ahci->port[i].fb, ==,
+                        ahci_px_rreg(ahci, i, AHCI_PX_FB));
+
+        /* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */
+        ahci_px_wreg(ahci, i, AHCI_PX_SERR, 0xFFFFFFFF);
+        ahci_px_wreg(ahci, i, AHCI_PX_IS, 0xFFFFFFFF);
+        ahci_wreg(ahci, AHCI_IS, (1 << i));
+
+        /* Verify Interrupts Cleared */
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR);
+        g_assert_cmphex(reg, ==, 0);
+
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_IS);
+        g_assert_cmphex(reg, ==, 0);
+
+        reg = ahci_rreg(ahci, AHCI_IS);
+        ASSERT_BIT_CLEAR(reg, (1 << i));
+
+        /* Enable All Interrupts: */
+        ahci_px_wreg(ahci, i, AHCI_PX_IE, 0xFFFFFFFF);
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_IE);
+        g_assert_cmphex(reg, ==, ~((uint32_t)AHCI_PX_IE_RESERVED));
+
+        /* Enable the FIS Receive Engine. */
+        ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_FRE);
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
+        ASSERT_BIT_SET(reg, AHCI_PX_CMD_FR);
+
+        /* AHCI 1.3 spec: if !STS.BSY, !STS.DRQ and PxSSTS.DET indicates
+         * physical presence, a device is present and may be started. However,
+         * PxSERR.DIAG.X /may/ need to be cleared a priori. */
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_SERR);
+        if (BITSET(reg, AHCI_PX_SERR_DIAG_X)) {
+            ahci_px_set(ahci, i, AHCI_PX_SERR, AHCI_PX_SERR_DIAG_X);
+        }
+
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_TFD);
+        if (BITCLR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ)) {
+            reg = ahci_px_rreg(ahci, i, AHCI_PX_SSTS);
+            if ((reg & AHCI_PX_SSTS_DET) == SSTS_DET_ESTABLISHED) {
+                /* Device Found: set PxCMD.ST := 1 */
+                ahci_px_set(ahci, i, AHCI_PX_CMD, AHCI_PX_CMD_ST);
+                ASSERT_BIT_SET(ahci_px_rreg(ahci, i, AHCI_PX_CMD),
+                               AHCI_PX_CMD_CR);
+                g_test_message("Started Device %u", i);
+            } else if ((reg & AHCI_PX_SSTS_DET)) {
+                /* Device present, but in some unknown state. */
+                g_assert_not_reached();
+            }
+        }
+    }
+
+    /* Enable GHC.IE */
+    ahci_set(ahci, AHCI_GHC, AHCI_GHC_IE);
+    reg = ahci_rreg(ahci, AHCI_GHC);
+    ASSERT_BIT_SET(reg, AHCI_GHC_IE);
+
+    ahci->enabled = true;
+    /* TODO: The device should now be idling and waiting for commands.
+     * In the future, a small test-case to inspect the Register D2H FIS
+     * and clear the initial interrupts might be good. */
+}
+
+/**
+ * Pick the first implemented and running port
+ */
+unsigned ahci_port_select(AHCIQState *ahci)
+{
+    uint32_t ports, reg;
+    unsigned i;
+
+    ports = ahci_rreg(ahci, AHCI_PI);
+    for (i = 0; i < 32; ports >>= 1, ++i) {
+        if (ports == 0) {
+            i = 32;
+        }
+
+        if (!(ports & 0x01)) {
+            continue;
+        }
+
+        reg = ahci_px_rreg(ahci, i, AHCI_PX_CMD);
+        if (BITSET(reg, AHCI_PX_CMD_ST)) {
+            break;
+        }
+    }
+    g_assert(i < 32);
+    return i;
+}
+
+/**
+ * Clear a port's interrupts and status information prior to a test.
+ */
+void ahci_port_clear(AHCIQState *ahci, uint8_t port)
+{
+    uint32_t reg;
+
+    /* Clear out this port's interrupts (ignore the init register d2h fis) */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
+    ahci_px_wreg(ahci, port, AHCI_PX_IS, reg);
+    g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
+
+    /* Wipe the FIS-Receive Buffer */
+    qtest_memset(ahci->parent->qts, ahci->port[port].fb, 0x00, 0x100);
+}
+
+/**
+ * Check a port for errors.
+ */
+void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
+                           uint32_t imask, uint8_t emask)
+{
+    uint32_t reg;
+
+    /* The upper 9 bits of the IS register all indicate errors. */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
+    reg &= ~imask;
+    reg >>= 23;
+    g_assert_cmphex(reg, ==, 0);
+
+    /* The Sata Error Register should be empty. */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR);
+    g_assert_cmphex(reg, ==, 0);
+
+    /* The TFD also has two error sections. */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
+    if (!emask) {
+        ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
+    } else {
+        ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR);
+    }
+    ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~emask << 8));
+    ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (emask << 8));
+}
+
+void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
+                                uint32_t intr_mask)
+{
+    uint32_t reg;
+
+    /* Check for expected interrupts */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
+    ASSERT_BIT_SET(reg, intr_mask);
+
+    /* Clear expected interrupts and assert all interrupts now cleared. */
+    ahci_px_wreg(ahci, port, AHCI_PX_IS, intr_mask);
+    g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
+}
+
+void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot)
+{
+    uint32_t reg;
+
+    /* Assert that the command slot is no longer busy (NCQ) */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT);
+    ASSERT_BIT_CLEAR(reg, (1 << slot));
+
+    /* Non-NCQ */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
+    ASSERT_BIT_CLEAR(reg, (1 << slot));
+
+    /* And assert that we are generally not busy. */
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
+    ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY);
+    ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_DRQ);
+}
+
+void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot)
+{
+    RegD2HFIS *d2h = g_malloc0(0x20);
+    uint32_t reg;
+
+    qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x40, d2h, 0x20);
+    g_assert_cmphex(d2h->fis_type, ==, 0x34);
+
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
+    g_assert_cmphex((reg & AHCI_PX_TFD_ERR) >> 8, ==, d2h->error);
+    g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, d2h->status);
+
+    g_free(d2h);
+}
+
+void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd)
+{
+    PIOSetupFIS *pio = g_malloc0(0x20);
+    uint8_t port = cmd->port;
+
+    /* We cannot check the Status or E_Status registers, because
+     * the status may have again changed between the PIO Setup FIS
+     * and the conclusion of the command with the D2H Register FIS. */
+    qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x20, pio, 0x20);
+    g_assert_cmphex(pio->fis_type, ==, 0x5f);
+
+    /* Data transferred by PIO will either be:
+     * (1) 12 or 16 bytes for an ATAPI command packet (QEMU always uses 12), or
+     * (2) Actual data from the drive.
+     * If we do both, (2) winds up erasing any evidence of (1).
+     */
+    if (cmd->props->atapi && (cmd->xbytes == 0 || cmd->props->dma)) {
+        g_assert(le16_to_cpu(pio->tx_count) == 12 ||
+                 le16_to_cpu(pio->tx_count) == 16);
+    } else {
+        /* The AHCI test suite here does not test any PIO command that specifies
+         * a DRQ block larger than one sector (like 0xC4), so this should always
+         * be one sector or less. */
+        size_t pio_len = ((cmd->xbytes % cmd->sector_size) ?
+                          (cmd->xbytes % cmd->sector_size) : cmd->sector_size);
+        g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, pio_len);
+    }
+    g_free(pio);
+}
+
+void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd)
+{
+    AHCICommandHeader cmdh;
+
+    ahci_get_command_header(ahci, cmd->port, cmd->slot, &cmdh);
+    /* Physical Region Descriptor Byte Count is not required to work for NCQ. */
+    if (!cmd->props->ncq) {
+        g_assert_cmphex(cmd->xbytes, ==, cmdh.prdbc);
+    }
+}
+
+/* Get the command in #slot of port #port. */
+void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
+                             uint8_t slot, AHCICommandHeader *cmd)
+{
+    uint64_t ba = ahci->port[port].clb;
+    ba += slot * sizeof(AHCICommandHeader);
+    qtest_memread(ahci->parent->qts, ba, cmd, sizeof(AHCICommandHeader));
+
+    cmd->flags = le16_to_cpu(cmd->flags);
+    cmd->prdtl = le16_to_cpu(cmd->prdtl);
+    cmd->prdbc = le32_to_cpu(cmd->prdbc);
+    cmd->ctba = le64_to_cpu(cmd->ctba);
+}
+
+/* Set the command in #slot of port #port. */
+void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
+                             uint8_t slot, AHCICommandHeader *cmd)
+{
+    AHCICommandHeader tmp = { .flags = 0 };
+    uint64_t ba = ahci->port[port].clb;
+    ba += slot * sizeof(AHCICommandHeader);
+
+    tmp.flags = cpu_to_le16(cmd->flags);
+    tmp.prdtl = cpu_to_le16(cmd->prdtl);
+    tmp.prdbc = cpu_to_le32(cmd->prdbc);
+    tmp.ctba = cpu_to_le64(cmd->ctba);
+
+    qtest_memwrite(ahci->parent->qts, ba, &tmp, sizeof(AHCICommandHeader));
+}
+
+void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot)
+{
+    AHCICommandHeader cmd;
+
+    /* Obtain the Nth Command Header */
+    ahci_get_command_header(ahci, port, slot, &cmd);
+    if (cmd.ctba == 0) {
+        /* No address in it, so just return -- it's empty. */
+        goto tidy;
+    }
+
+    /* Free the Table */
+    ahci_free(ahci, cmd.ctba);
+
+ tidy:
+    /* NULL the header. */
+    memset(&cmd, 0x00, sizeof(cmd));
+    ahci_set_command_header(ahci, port, slot, &cmd);
+    ahci->port[port].ctba[slot] = 0;
+    ahci->port[port].prdtl[slot] = 0;
+}
+
+void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd)
+{
+    RegH2DFIS tmp = cmd->fis;
+    uint64_t addr = cmd->header.ctba;
+
+    /* NCQ commands use exclusively 8 bit fields and needs no adjustment.
+     * Only the count field needs to be adjusted for non-NCQ commands.
+     * The auxiliary FIS fields are defined per-command and are not currently
+     * implemented in libqos/ahci.o, but may or may not need to be flipped. */
+    if (!cmd->props->ncq) {
+        tmp.count = cpu_to_le16(tmp.count);
+    }
+
+    qtest_memwrite(ahci->parent->qts, addr, &tmp, sizeof(tmp));
+}
+
+unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port)
+{
+    unsigned i;
+    unsigned j;
+    uint32_t reg;
+
+    reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
+
+    /* Pick the least recently used command slot that's available */
+    for (i = 0; i < 32; ++i) {
+        j = ((ahci->port[port].next + i) % 32);
+        if (reg & (1 << j)) {
+            continue;
+        }
+        ahci_destroy_command(ahci, port, j);
+        ahci->port[port].next = (j + 1) % 32;
+        return j;
+    }
+
+    g_test_message("All command slots were busy.");
+    g_assert_not_reached();
+}
+
+inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd)
+{
+    /* Each PRD can describe up to 4MiB */
+    g_assert_cmphex(bytes_per_prd, <=, 4096 * 1024);
+    g_assert_cmphex(bytes_per_prd & 0x01, ==, 0x00);
+    return (bytes + bytes_per_prd - 1) / bytes_per_prd;
+}
+
+const AHCIOpts default_opts = { .size = 0 };
+
+/**
+ * ahci_exec: execute a given command on a specific
+ * AHCI port.
+ *
+ * @ahci: The device to send the command to
+ * @port: The port number of the SATA device we wish
+ *        to have execute this command
+ * @op:   The S/ATA command to execute, or if opts.atapi
+ *        is true, the SCSI command code.
+ * @opts: Optional arguments to modify execution behavior.
+ */
+void ahci_exec(AHCIQState *ahci, uint8_t port,
+               uint8_t op, const AHCIOpts *opts_in)
+{
+    AHCICommand *cmd;
+    int rc;
+    AHCIOpts *opts;
+
+    opts = g_memdup((opts_in == NULL ? &default_opts : opts_in),
+                    sizeof(AHCIOpts));
+
+    /* No guest buffer provided, create one. */
+    if (opts->size && !opts->buffer) {
+        opts->buffer = ahci_alloc(ahci, opts->size);
+        g_assert(opts->buffer);
+        qtest_memset(ahci->parent->qts, opts->buffer, 0x00, opts->size);
+    }
+
+    /* Command creation */
+    if (opts->atapi) {
+        uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE;
+        cmd = ahci_atapi_command_create(op, bcl, opts->atapi_dma);
+    } else {
+        cmd = ahci_command_create(op);
+    }
+    ahci_command_adjust(cmd, opts->lba, opts->buffer,
+                        opts->size, opts->prd_size);
+
+    if (opts->pre_cb) {
+        rc = opts->pre_cb(ahci, cmd, opts);
+        g_assert_cmpint(rc, ==, 0);
+    }
+
+    /* Write command to memory and issue it */
+    ahci_command_commit(ahci, cmd, port);
+    ahci_command_issue_async(ahci, cmd);
+    if (opts->error) {
+        qtest_qmp_eventwait(ahci->parent->qts, "STOP");
+    }
+    if (opts->mid_cb) {
+        rc = opts->mid_cb(ahci, cmd, opts);
+        g_assert_cmpint(rc, ==, 0);
+    }
+    if (opts->error) {
+        qtest_qmp_send(ahci->parent->qts, "{'execute':'cont' }");
+        qtest_qmp_eventwait(ahci->parent->qts, "RESUME");
+    }
+
+    /* Wait for command to complete and verify sanity */
+    ahci_command_wait(ahci, cmd);
+    ahci_command_verify(ahci, cmd);
+    if (opts->post_cb) {
+        rc = opts->post_cb(ahci, cmd, opts);
+        g_assert_cmpint(rc, ==, 0);
+    }
+    ahci_command_free(cmd);
+    if (opts->buffer != opts_in->buffer) {
+        ahci_free(ahci, opts->buffer);
+    }
+    g_free(opts);
+}
+
+/* Issue a command, expecting it to fail and STOP the VM */
+AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port,
+                                uint8_t ide_cmd, uint64_t buffer,
+                                size_t bufsize, uint64_t sector)
+{
+    AHCICommand *cmd;
+
+    cmd = ahci_command_create(ide_cmd);
+    ahci_command_adjust(cmd, sector, buffer, bufsize, 0);
+    ahci_command_commit(ahci, cmd, port);
+    ahci_command_issue_async(ahci, cmd);
+    qtest_qmp_eventwait(ahci->parent->qts, "STOP");
+
+    return cmd;
+}
+
+/* Resume a previously failed command and verify/finalize */
+void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd)
+{
+    /* Complete the command */
+    qtest_qmp_send(ahci->parent->qts, "{'execute':'cont' }");
+    qtest_qmp_eventwait(ahci->parent->qts, "RESUME");
+    ahci_command_wait(ahci, cmd);
+    ahci_command_verify(ahci, cmd);
+    ahci_command_free(cmd);
+}
+
+/* Given a guest buffer address, perform an IO operation */
+void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
+                   uint64_t buffer, size_t bufsize, uint64_t sector)
+{
+    AHCICommand *cmd;
+    cmd = ahci_command_create(ide_cmd);
+    ahci_command_set_buffer(cmd, buffer);
+    ahci_command_set_size(cmd, bufsize);
+    if (sector) {
+        ahci_command_set_offset(cmd, sector);
+    }
+    ahci_command_commit(ahci, cmd, port);
+    ahci_command_issue(ahci, cmd);
+    ahci_command_verify(ahci, cmd);
+    ahci_command_free(cmd);
+}
+
+static AHCICommandProp *ahci_command_find(uint8_t command_name)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(ahci_command_properties); i++) {
+        if (ahci_command_properties[i].cmd == command_name) {
+            return &ahci_command_properties[i];
+        }
+    }
+
+    return NULL;
+}
+
+/* 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 *buffer, size_t bufsize, uint64_t sector)
+{
+    uint64_t ptr;
+    AHCICommandProp *props;
+
+    props = ahci_command_find(ide_cmd);
+    g_assert(props);
+    ptr = ahci_alloc(ahci, bufsize);
+    g_assert(!bufsize || ptr);
+    qtest_memset(ahci->parent->qts, ptr, 0x00, bufsize);
+
+    if (bufsize && props->write) {
+        qtest_bufwrite(ahci->parent->qts, ptr, buffer, bufsize);
+    }
+
+    ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector);
+
+    if (bufsize && props->read) {
+        qtest_bufread(ahci->parent->qts, ptr, buffer, bufsize);
+    }
+
+    ahci_free(ahci, ptr);
+}
+
+/**
+ * Initializes a basic command header in memory.
+ * We assume that this is for an ATA command using RegH2DFIS.
+ */
+static void command_header_init(AHCICommand *cmd)
+{
+    AHCICommandHeader *hdr = &cmd->header;
+    AHCICommandProp *props = cmd->props;
+
+    hdr->flags = 5;             /* RegH2DFIS is 5 DW long. Must be < 32 */
+    hdr->flags |= CMDH_CLR_BSY; /* Clear the BSY bit when done */
+    if (props->write) {
+        hdr->flags |= CMDH_WRITE;
+    }
+    if (props->atapi) {
+        hdr->flags |= CMDH_ATAPI;
+    }
+    /* Other flags: PREFETCH, RESET, and BIST */
+    hdr->prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
+    hdr->prdbc = 0;
+    hdr->ctba = 0;
+}
+
+static void command_table_init(AHCICommand *cmd)
+{
+    RegH2DFIS *fis = &(cmd->fis);
+    uint16_t sect_count = (cmd->xbytes / cmd->sector_size);
+
+    fis->fis_type = REG_H2D_FIS;
+    fis->flags = REG_H2D_FIS_CMD; /* "Command" bit */
+    fis->command = cmd->name;
+
+    if (cmd->props->ncq) {
+        NCQFIS *ncqfis = (NCQFIS *)fis;
+        /* NCQ is weird and re-uses FIS frames for unrelated data.
+         * See SATA 3.2, 13.6.4.1 READ FPDMA QUEUED for an example. */
+        ncqfis->sector_low = sect_count & 0xFF;
+        ncqfis->sector_hi = (sect_count >> 8) & 0xFF;
+        ncqfis->device = NCQ_DEVICE_MAGIC;
+        /* Force Unit Access is bit 7 in the device register */
+        ncqfis->tag = 0;  /* bits 3-7 are the NCQ tag */
+        ncqfis->prio = 0; /* bits 6,7 are a prio tag */
+        /* RARC bit is bit 0 of TAG field */
+    } else {
+        fis->feature_low = 0x00;
+        fis->feature_high = 0x00;
+        if (cmd->props->lba28 || cmd->props->lba48) {
+            fis->device = ATA_DEVICE_LBA;
+        }
+        fis->count = (cmd->xbytes / cmd->sector_size);
+    }
+    fis->icc = 0x00;
+    fis->control = 0x00;
+    memset(fis->aux, 0x00, ARRAY_SIZE(fis->aux));
+}
+
+void ahci_command_enable_atapi_dma(AHCICommand *cmd)
+{
+    RegH2DFIS *fis = &(cmd->fis);
+    g_assert(cmd->props->atapi);
+    fis->feature_low |= 0x01;
+    /* PIO is still used to transfer the ATAPI command */
+    g_assert(cmd->props->pio);
+    cmd->props->dma = true;
+    /* BUG: We expect the DMA Setup interrupt for DMA commands */
+    /* cmd->interrupts |= AHCI_PX_IS_DSS; */
+}
+
+AHCICommand *ahci_command_create(uint8_t command_name)
+{
+    AHCICommandProp *props = ahci_command_find(command_name);
+    AHCICommand *cmd;
+
+    g_assert(props);
+    cmd = g_new0(AHCICommand, 1);
+    g_assert(!(props->dma && props->pio) || props->atapi);
+    g_assert(!(props->lba28 && props->lba48));
+    g_assert(!(props->read && props->write));
+    g_assert(!props->size || props->data);
+    g_assert(!props->ncq || props->lba48);
+
+    /* Defaults and book-keeping */
+    cmd->props = g_memdup(props, sizeof(AHCICommandProp));
+    cmd->name = command_name;
+    cmd->xbytes = props->size;
+    cmd->prd_size = 4096;
+    cmd->buffer = 0xabad1dea;
+    cmd->sector_size = props->atapi ? ATAPI_SECTOR_SIZE : AHCI_SECTOR_SIZE;
+
+    if (!cmd->props->ncq) {
+        cmd->interrupts = AHCI_PX_IS_DHRS;
+    }
+    /* BUG: We expect the DPS interrupt for data commands */
+    /* cmd->interrupts |= props->data ? AHCI_PX_IS_DPS : 0; */
+    /* BUG: We expect the DMA Setup interrupt for DMA commands */
+    /* cmd->interrupts |= props->dma ? AHCI_PX_IS_DSS : 0; */
+    cmd->interrupts |= props->ncq ? AHCI_PX_IS_SDBS : 0;
+
+    command_header_init(cmd);
+    command_table_init(cmd);
+
+    return cmd;
+}
+
+AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma)
+{
+    AHCICommand *cmd = ahci_command_create(CMD_PACKET);
+    cmd->atapi_cmd = g_malloc0(16);
+    cmd->atapi_cmd[0] = scsi_cmd;
+    stw_le_p(&cmd->fis.lba_lo[1], bcl);
+    if (dma) {
+        ahci_command_enable_atapi_dma(cmd);
+    } else {
+        cmd->interrupts |= bcl ? AHCI_PX_IS_PSS : 0;
+    }
+    return cmd;
+}
+
+void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port,
+                           bool ready, uint8_t expected_sense)
+{
+    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0, false);
+    ahci_command_set_size(cmd, 0);
+    if (!ready) {
+        cmd->interrupts |= AHCI_PX_IS_TFES;
+        cmd->errors |= expected_sense << 4;
+    }
+    ahci_command_commit(ahci, cmd, port);
+    ahci_command_issue(ahci, cmd);
+    ahci_command_verify(ahci, cmd);
+    ahci_command_free(cmd);
+}
+
+static int copy_buffer(AHCIQState *ahci, AHCICommand *cmd,
+                        const AHCIOpts *opts)
+{
+    unsigned char *rx = opts->opaque;
+    qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size);
+    return 0;
+}
+
+void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
+                          uint8_t *sense, uint8_t *asc)
+{
+    unsigned char *rx;
+    AHCIOpts opts = {
+        .size = 18,
+        .atapi = true,
+        .post_cb = copy_buffer,
+    };
+    rx = g_malloc(18);
+    opts.opaque = rx;
+
+    ahci_exec(ahci, port, CMD_ATAPI_REQUEST_SENSE, &opts);
+
+    *sense = rx[2];
+    *asc = rx[12];
+
+    g_free(rx);
+}
+
+void ahci_atapi_eject(AHCIQState *ahci, uint8_t port)
+{
+    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false);
+    ahci_command_set_size(cmd, 0);
+
+    cmd->atapi_cmd[4] = 0x02; /* loej = true */
+    ahci_command_commit(ahci, cmd, port);
+    ahci_command_issue(ahci, cmd);
+    ahci_command_verify(ahci, cmd);
+    ahci_command_free(cmd);
+}
+
+void ahci_atapi_load(AHCIQState *ahci, uint8_t port)
+{
+    AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0, false);
+    ahci_command_set_size(cmd, 0);
+
+    cmd->atapi_cmd[4] = 0x03; /* loej,start = true */
+    ahci_command_commit(ahci, cmd, port);
+    ahci_command_issue(ahci, cmd);
+    ahci_command_verify(ahci, cmd);
+    ahci_command_free(cmd);
+}
+
+void ahci_command_free(AHCICommand *cmd)
+{
+    g_free(cmd->atapi_cmd);
+    g_free(cmd->props);
+    g_free(cmd);
+}
+
+void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags)
+{
+    cmd->header.flags |= cmdh_flags;
+}
+
+void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags)
+{
+    cmd->header.flags &= ~cmdh_flags;
+}
+
+static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba)
+{
+    unsigned char *cbd = cmd->atapi_cmd;
+    g_assert(cbd);
+
+    switch (cbd[0]) {
+    case CMD_ATAPI_READ_10:
+    case CMD_ATAPI_READ_CD:
+        g_assert_cmpuint(lba, <=, UINT32_MAX);
+        stl_be_p(&cbd[2], lba);
+        break;
+    case CMD_ATAPI_REQUEST_SENSE:
+    case CMD_ATAPI_TEST_UNIT_READY:
+    case CMD_ATAPI_START_STOP_UNIT:
+        g_assert_cmpuint(lba, ==, 0x00);
+        break;
+    default:
+        /* SCSI doesn't have uniform packet formats,
+         * so you have to add support for it manually. Sorry! */
+        fprintf(stderr, "The Libqos AHCI driver does not support the "
+                "set_offset operation for ATAPI command 0x%02x, "
+                "please add support.\n",
+                cbd[0]);
+        g_assert_not_reached();
+    }
+}
+
+void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect)
+{
+    RegH2DFIS *fis = &(cmd->fis);
+
+    if (cmd->props->atapi) {
+        ahci_atapi_command_set_offset(cmd, lba_sect);
+        return;
+    } else if (!cmd->props->data && !lba_sect) {
+        /* Not meaningful, ignore. */
+        return;
+    } else if (cmd->props->lba28) {
+        g_assert_cmphex(lba_sect, <=, 0xFFFFFFF);
+    } else if (cmd->props->lba48 || cmd->props->ncq) {
+        g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF);
+    } else {
+        /* Can't set offset if we don't know the format. */
+        g_assert_not_reached();
+    }
+
+    /* LBA28 uses the low nibble of the device/control register for LBA24:27 */
+    fis->lba_lo[0] = (lba_sect & 0xFF);
+    fis->lba_lo[1] = (lba_sect >> 8) & 0xFF;
+    fis->lba_lo[2] = (lba_sect >> 16) & 0xFF;
+    if (cmd->props->lba28) {
+        fis->device = (fis->device & 0xF0) | ((lba_sect >> 24) & 0x0F);
+    }
+    fis->lba_hi[0] = (lba_sect >> 24) & 0xFF;
+    fis->lba_hi[1] = (lba_sect >> 32) & 0xFF;
+    fis->lba_hi[2] = (lba_sect >> 40) & 0xFF;
+}
+
+void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
+{
+    cmd->buffer = buffer;
+}
+
+static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
+{
+    unsigned char *cbd = cmd->atapi_cmd;
+    uint64_t nsectors = xbytes / ATAPI_SECTOR_SIZE;
+    uint32_t tmp;
+    g_assert(cbd);
+
+    switch (cbd[0]) {
+    case CMD_ATAPI_READ_10:
+        g_assert_cmpuint(nsectors, <=, UINT16_MAX);
+        stw_be_p(&cbd[7], nsectors);
+        break;
+    case CMD_ATAPI_READ_CD:
+        /* 24bit BE store */
+        g_assert_cmpuint(nsectors, <, 1ULL << 24);
+        tmp = nsectors;
+        cbd[6] = (tmp & 0xFF0000) >> 16;
+        cbd[7] = (tmp & 0xFF00) >> 8;
+        cbd[8] = (tmp & 0xFF);
+        break;
+    case CMD_ATAPI_REQUEST_SENSE:
+        g_assert_cmpuint(xbytes, <=, UINT8_MAX);
+        cbd[4] = (uint8_t)xbytes;
+        break;
+    case CMD_ATAPI_TEST_UNIT_READY:
+    case CMD_ATAPI_START_STOP_UNIT:
+        g_assert_cmpuint(xbytes, ==, 0);
+        break;
+    default:
+        /* SCSI doesn't have uniform packet formats,
+         * so you have to add support for it manually. Sorry! */
+        fprintf(stderr, "The Libqos AHCI driver does not support the set_size "
+                "operation for ATAPI command 0x%02x, please add support.\n",
+                cbd[0]);
+        g_assert_not_reached();
+    }
+}
+
+void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
+                            unsigned prd_size)
+{
+    uint16_t sect_count;
+
+    /* Each PRD can describe up to 4MiB, and must not be odd. */
+    g_assert_cmphex(prd_size, <=, 4096 * 1024);
+    g_assert_cmphex(prd_size & 0x01, ==, 0x00);
+    if (prd_size) {
+        cmd->prd_size = prd_size;
+    }
+    cmd->xbytes = xbytes;
+    sect_count = (cmd->xbytes / cmd->sector_size);
+
+    if (cmd->props->ncq) {
+        NCQFIS *nfis = (NCQFIS *)&(cmd->fis);
+        nfis->sector_low = sect_count & 0xFF;
+        nfis->sector_hi = (sect_count >> 8) & 0xFF;
+    } else if (cmd->props->atapi) {
+        ahci_atapi_set_size(cmd, xbytes);
+    } else {
+        /* For writes, the PIO Setup FIS interrupt only comes from DRQs
+         * after the first.
+         */
+        if (cmd->props->pio && sect_count > (cmd->props->read ? 0 : 1)) {
+            cmd->interrupts |= AHCI_PX_IS_PSS;
+        }
+        cmd->fis.count = sect_count;
+    }
+    cmd->header.prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
+}
+
+void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes)
+{
+    ahci_command_set_sizes(cmd, xbytes, cmd->prd_size);
+}
+
+void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size)
+{
+    ahci_command_set_sizes(cmd, cmd->xbytes, prd_size);
+}
+
+void ahci_command_adjust(AHCICommand *cmd, uint64_t offset, uint64_t buffer,
+                         uint64_t xbytes, unsigned prd_size)
+{
+    ahci_command_set_sizes(cmd, xbytes, prd_size);
+    ahci_command_set_buffer(cmd, buffer);
+    ahci_command_set_offset(cmd, offset);
+}
+
+void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port)
+{
+    uint16_t i, prdtl;
+    uint64_t table_size, table_ptr, remaining;
+    PRD prd;
+
+    /* This command is now tied to this port/command slot */
+    cmd->port = port;
+    cmd->slot = ahci_pick_cmd(ahci, port);
+
+    if (cmd->props->ncq) {
+        NCQFIS *nfis = (NCQFIS *)&cmd->fis;
+        nfis->tag = (cmd->slot << 3) & 0xFC;
+    }
+
+    /* Create a buffer for the command table */
+    prdtl = size_to_prdtl(cmd->xbytes, cmd->prd_size);
+    table_size = CMD_TBL_SIZ(prdtl);
+    table_ptr = ahci_alloc(ahci, table_size);
+    g_assert(table_ptr);
+    /* AHCI 1.3: Must be aligned to 0x80 */
+    g_assert((table_ptr & 0x7F) == 0x00);
+    cmd->header.ctba = table_ptr;
+
+    /* Commit the command header (part of the Command List Buffer) */
+    ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header));
+    /* Now, write the command table (FIS, ACMD, and PRDT) -- FIS first, */
+    ahci_write_fis(ahci, cmd);
+    /* Then ATAPI CMD, if needed */
+    if (cmd->props->atapi) {
+        qtest_memwrite(ahci->parent->qts, table_ptr + 0x40, cmd->atapi_cmd, 16);
+    }
+
+    /* Construct and write the PRDs to the command table */
+    g_assert_cmphex(prdtl, ==, cmd->header.prdtl);
+    remaining = cmd->xbytes;
+    for (i = 0; i < prdtl; ++i) {
+        prd.dba = cpu_to_le64(cmd->buffer + (cmd->prd_size * i));
+        prd.res = 0;
+        if (remaining > cmd->prd_size) {
+            /* Note that byte count is 0-based. */
+            prd.dbc = cpu_to_le32(cmd->prd_size - 1);
+            remaining -= cmd->prd_size;
+        } else {
+            /* Again, dbc is 0-based. */
+            prd.dbc = cpu_to_le32(remaining - 1);
+            remaining = 0;
+        }
+        prd.dbc |= cpu_to_le32(0x80000000); /* Request DPS Interrupt */
+
+        /* Commit the PRD entry to the Command Table */
+        qtest_memwrite(ahci->parent->qts, table_ptr + 0x80 + (i * sizeof(PRD)),
+                       &prd, sizeof(PRD));
+    }
+
+    /* Bookmark the PRDTL and CTBA values */
+    ahci->port[port].ctba[cmd->slot] = table_ptr;
+    ahci->port[port].prdtl[cmd->slot] = prdtl;
+}
+
+void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd)
+{
+    if (cmd->props->ncq) {
+        ahci_px_wreg(ahci, cmd->port, AHCI_PX_SACT, (1 << cmd->slot));
+    }
+
+    ahci_px_wreg(ahci, cmd->port, AHCI_PX_CI, (1 << cmd->slot));
+}
+
+void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd)
+{
+    /* We can't rely on STS_BSY until the command has started processing.
+     * Therefore, we also use the Command Issue bit as indication of
+     * a command in-flight. */
+
+#define RSET(REG, MASK) (BITSET(ahci_px_rreg(ahci, cmd->port, (REG)), (MASK)))
+
+    while (RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) ||
+           RSET(AHCI_PX_CI, 1 << cmd->slot) ||
+           (cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot))) {
+        usleep(50);
+    }
+
+}
+
+void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd)
+{
+    ahci_command_issue_async(ahci, cmd);
+    ahci_command_wait(ahci, cmd);
+}
+
+void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
+{
+    uint8_t slot = cmd->slot;
+    uint8_t port = cmd->port;
+
+    ahci_port_check_error(ahci, port, cmd->interrupts, cmd->errors);
+    ahci_port_check_interrupts(ahci, port, cmd->interrupts);
+    ahci_port_check_nonbusy(ahci, port, slot);
+    ahci_port_check_cmd_sanity(ahci, cmd);
+    if (cmd->interrupts & AHCI_PX_IS_DHRS) {
+        ahci_port_check_d2h_sanity(ahci, port, slot);
+    }
+    if (cmd->props->pio) {
+        ahci_port_check_pio_sanity(ahci, cmd);
+    }
+}
+
+uint8_t ahci_command_slot(AHCICommand *cmd)
+{
+    return cmd->slot;
+}
diff --git a/tests/qtest/libqos/ahci.h b/tests/qtest/libqos/ahci.h
new file mode 100644 (file)
index 0000000..f05b3e5
--- /dev/null
@@ -0,0 +1,651 @@
+#ifndef LIBQOS_AHCI_H
+#define LIBQOS_AHCI_H
+
+/*
+ * AHCI qtest library functions and definitions
+ *
+ * Copyright (c) 2014 John Snow <jsnow@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 "libqos/libqos.h"
+#include "libqos/pci.h"
+#include "libqos/malloc-pc.h"
+
+/*** Supplementary PCI Config Space IDs & Masks ***/
+#define PCI_DEVICE_ID_INTEL_Q35_AHCI   (0x2922)
+#define PCI_MSI_FLAGS_RESERVED         (0xFF00)
+#define PCI_PM_CTRL_RESERVED             (0xFC)
+#define PCI_BCC(REG32)          ((REG32) >> 24)
+#define PCI_PI(REG32)   (((REG32) >> 8) & 0xFF)
+#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF)
+
+/*** Recognized AHCI Device Types ***/
+#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \
+                         PCI_VENDOR_ID_INTEL)
+
+/*** AHCI/HBA Register Offsets and Bitmasks ***/
+#define AHCI_CAP                          (0)
+#define AHCI_CAP_NP                    (0x1F)
+#define AHCI_CAP_SXS                   (0x20)
+#define AHCI_CAP_EMS                   (0x40)
+#define AHCI_CAP_CCCS                  (0x80)
+#define AHCI_CAP_NCS                 (0x1F00)
+#define AHCI_CAP_PSC                 (0x2000)
+#define AHCI_CAP_SSC                 (0x4000)
+#define AHCI_CAP_PMD                 (0x8000)
+#define AHCI_CAP_FBSS               (0x10000)
+#define AHCI_CAP_SPM                (0x20000)
+#define AHCI_CAP_SAM                (0x40000)
+#define AHCI_CAP_RESERVED           (0x80000)
+#define AHCI_CAP_ISS               (0xF00000)
+#define AHCI_CAP_SCLO             (0x1000000)
+#define AHCI_CAP_SAL              (0x2000000)
+#define AHCI_CAP_SALP             (0x4000000)
+#define AHCI_CAP_SSS              (0x8000000)
+#define AHCI_CAP_SMPS            (0x10000000)
+#define AHCI_CAP_SSNTF           (0x20000000)
+#define AHCI_CAP_SNCQ            (0x40000000)
+#define AHCI_CAP_S64A            (0x80000000)
+
+#define AHCI_GHC                          (1)
+#define AHCI_GHC_HR                    (0x01)
+#define AHCI_GHC_IE                    (0x02)
+#define AHCI_GHC_MRSM                  (0x04)
+#define AHCI_GHC_RESERVED        (0x7FFFFFF8)
+#define AHCI_GHC_AE              (0x80000000)
+
+#define AHCI_IS                           (2)
+#define AHCI_PI                           (3)
+#define AHCI_VS                           (4)
+
+#define AHCI_CCCCTL                       (5)
+#define AHCI_CCCCTL_EN                 (0x01)
+#define AHCI_CCCCTL_RESERVED           (0x06)
+#define AHCI_CCCCTL_CC               (0xFF00)
+#define AHCI_CCCCTL_TV           (0xFFFF0000)
+
+#define AHCI_CCCPORTS                     (6)
+#define AHCI_EMLOC                        (7)
+
+#define AHCI_EMCTL                        (8)
+#define AHCI_EMCTL_STSMR               (0x01)
+#define AHCI_EMCTL_CTLTM              (0x100)
+#define AHCI_EMCTL_CTLRST             (0x200)
+#define AHCI_EMCTL_RESERVED      (0xF0F0FCFE)
+
+#define AHCI_CAP2                         (9)
+#define AHCI_CAP2_BOH                  (0x01)
+#define AHCI_CAP2_NVMP                 (0x02)
+#define AHCI_CAP2_APST                 (0x04)
+#define AHCI_CAP2_RESERVED       (0xFFFFFFF8)
+
+#define AHCI_BOHC                        (10)
+#define AHCI_RESERVED                    (11)
+#define AHCI_NVMHCI                      (24)
+#define AHCI_VENDOR                      (40)
+#define AHCI_PORTS                       (64)
+
+/*** Port Memory Offsets & Bitmasks ***/
+#define AHCI_PX_CLB                       (0)
+#define AHCI_PX_CLB_RESERVED          (0x1FF)
+
+#define AHCI_PX_CLBU                      (1)
+
+#define AHCI_PX_FB                        (2)
+#define AHCI_PX_FB_RESERVED            (0xFF)
+
+#define AHCI_PX_FBU                       (3)
+
+#define AHCI_PX_IS                        (4)
+#define AHCI_PX_IS_DHRS                 (0x1)
+#define AHCI_PX_IS_PSS                  (0x2)
+#define AHCI_PX_IS_DSS                  (0x4)
+#define AHCI_PX_IS_SDBS                 (0x8)
+#define AHCI_PX_IS_UFS                 (0x10)
+#define AHCI_PX_IS_DPS                 (0x20)
+#define AHCI_PX_IS_PCS                 (0x40)
+#define AHCI_PX_IS_DMPS                (0x80)
+#define AHCI_PX_IS_RESERVED       (0x23FFF00)
+#define AHCI_PX_IS_PRCS            (0x400000)
+#define AHCI_PX_IS_IPMS            (0x800000)
+#define AHCI_PX_IS_OFS            (0x1000000)
+#define AHCI_PX_IS_INFS           (0x4000000)
+#define AHCI_PX_IS_IFS            (0x8000000)
+#define AHCI_PX_IS_HBDS          (0x10000000)
+#define AHCI_PX_IS_HBFS          (0x20000000)
+#define AHCI_PX_IS_TFES          (0x40000000)
+#define AHCI_PX_IS_CPDS          (0x80000000)
+
+#define AHCI_PX_IE                        (5)
+#define AHCI_PX_IE_DHRE                 (0x1)
+#define AHCI_PX_IE_PSE                  (0x2)
+#define AHCI_PX_IE_DSE                  (0x4)
+#define AHCI_PX_IE_SDBE                 (0x8)
+#define AHCI_PX_IE_UFE                 (0x10)
+#define AHCI_PX_IE_DPE                 (0x20)
+#define AHCI_PX_IE_PCE                 (0x40)
+#define AHCI_PX_IE_DMPE                (0x80)
+#define AHCI_PX_IE_RESERVED       (0x23FFF00)
+#define AHCI_PX_IE_PRCE            (0x400000)
+#define AHCI_PX_IE_IPME            (0x800000)
+#define AHCI_PX_IE_OFE            (0x1000000)
+#define AHCI_PX_IE_INFE           (0x4000000)
+#define AHCI_PX_IE_IFE            (0x8000000)
+#define AHCI_PX_IE_HBDE          (0x10000000)
+#define AHCI_PX_IE_HBFE          (0x20000000)
+#define AHCI_PX_IE_TFEE          (0x40000000)
+#define AHCI_PX_IE_CPDE          (0x80000000)
+
+#define AHCI_PX_CMD                       (6)
+#define AHCI_PX_CMD_ST                  (0x1)
+#define AHCI_PX_CMD_SUD                 (0x2)
+#define AHCI_PX_CMD_POD                 (0x4)
+#define AHCI_PX_CMD_CLO                 (0x8)
+#define AHCI_PX_CMD_FRE                (0x10)
+#define AHCI_PX_CMD_RESERVED           (0xE0)
+#define AHCI_PX_CMD_CCS              (0x1F00)
+#define AHCI_PX_CMD_MPSS             (0x2000)
+#define AHCI_PX_CMD_FR               (0x4000)
+#define AHCI_PX_CMD_CR               (0x8000)
+#define AHCI_PX_CMD_CPS             (0x10000)
+#define AHCI_PX_CMD_PMA             (0x20000)
+#define AHCI_PX_CMD_HPCP            (0x40000)
+#define AHCI_PX_CMD_MPSP            (0x80000)
+#define AHCI_PX_CMD_CPD            (0x100000)
+#define AHCI_PX_CMD_ESP            (0x200000)
+#define AHCI_PX_CMD_FBSCP          (0x400000)
+#define AHCI_PX_CMD_APSTE          (0x800000)
+#define AHCI_PX_CMD_ATAPI         (0x1000000)
+#define AHCI_PX_CMD_DLAE          (0x2000000)
+#define AHCI_PX_CMD_ALPE          (0x4000000)
+#define AHCI_PX_CMD_ASP           (0x8000000)
+#define AHCI_PX_CMD_ICC          (0xF0000000)
+
+#define AHCI_PX_RES1                      (7)
+
+#define AHCI_PX_TFD                       (8)
+#define AHCI_PX_TFD_STS                (0xFF)
+#define AHCI_PX_TFD_STS_ERR            (0x01)
+#define AHCI_PX_TFD_STS_CS1            (0x06)
+#define AHCI_PX_TFD_STS_DRQ            (0x08)
+#define AHCI_PX_TFD_STS_CS2            (0x70)
+#define AHCI_PX_TFD_STS_BSY            (0x80)
+#define AHCI_PX_TFD_ERR              (0xFF00)
+#define AHCI_PX_TFD_RESERVED     (0xFFFF0000)
+
+#define AHCI_PX_SIG                       (9)
+#define AHCI_PX_SIG_SECTOR_COUNT       (0xFF)
+#define AHCI_PX_SIG_LBA_LOW          (0xFF00)
+#define AHCI_PX_SIG_LBA_MID        (0xFF0000)
+#define AHCI_PX_SIG_LBA_HIGH     (0xFF000000)
+
+#define AHCI_PX_SSTS                     (10)
+#define AHCI_PX_SSTS_DET               (0x0F)
+#define AHCI_PX_SSTS_SPD               (0xF0)
+#define AHCI_PX_SSTS_IPM              (0xF00)
+#define AHCI_PX_SSTS_RESERVED    (0xFFFFF000)
+#define SSTS_DET_NO_DEVICE             (0x00)
+#define SSTS_DET_PRESENT               (0x01)
+#define SSTS_DET_ESTABLISHED           (0x03)
+#define SSTS_DET_OFFLINE               (0x04)
+
+#define AHCI_PX_SCTL                     (11)
+
+#define AHCI_PX_SERR                     (12)
+#define AHCI_PX_SERR_ERR             (0xFFFF)
+#define AHCI_PX_SERR_DIAG        (0xFFFF0000)
+#define AHCI_PX_SERR_DIAG_X      (0x04000000)
+
+#define AHCI_PX_SACT                     (13)
+#define AHCI_PX_CI                       (14)
+#define AHCI_PX_SNTF                     (15)
+
+#define AHCI_PX_FBS                      (16)
+#define AHCI_PX_FBS_EN                  (0x1)
+#define AHCI_PX_FBS_DEC                 (0x2)
+#define AHCI_PX_FBS_SDE                 (0x4)
+#define AHCI_PX_FBS_DEV               (0xF00)
+#define AHCI_PX_FBS_ADO              (0xF000)
+#define AHCI_PX_FBS_DWE             (0xF0000)
+#define AHCI_PX_FBS_RESERVED     (0xFFF000F8)
+
+#define AHCI_PX_RES2                     (17)
+#define AHCI_PX_VS                       (28)
+
+#define HBA_DATA_REGION_SIZE            (256)
+#define HBA_PORT_DATA_SIZE              (128)
+#define HBA_PORT_NUM_REG (HBA_PORT_DATA_SIZE/4)
+
+#define AHCI_VERSION_0_95        (0x00000905)
+#define AHCI_VERSION_1_0         (0x00010000)
+#define AHCI_VERSION_1_1         (0x00010100)
+#define AHCI_VERSION_1_2         (0x00010200)
+#define AHCI_VERSION_1_3         (0x00010300)
+
+#define AHCI_SECTOR_SIZE                (512)
+#define ATAPI_SECTOR_SIZE              (2048)
+
+#define AHCI_SIGNATURE_CDROM     (0xeb140101)
+#define AHCI_SIGNATURE_DISK      (0x00000101)
+
+/* FIS types */
+enum {
+    REG_H2D_FIS = 0x27,
+    REG_D2H_FIS = 0x34,
+    DMA_ACTIVATE_FIS = 0x39,
+    DMA_SETUP_FIS = 0x41,
+    DATA_FIS = 0x46,
+    BIST_ACTIVATE_FIS = 0x58,
+    PIO_SETUP_FIS = 0x5F,
+    SDB_FIS = 0xA1
+};
+
+/* FIS flags */
+#define REG_H2D_FIS_CMD  0x80
+
+/* ATA Commands */
+enum {
+    /* DMA */
+    CMD_READ_DMA       = 0xC8,
+    CMD_READ_DMA_EXT   = 0x25,
+    CMD_WRITE_DMA      = 0xCA,
+    CMD_WRITE_DMA_EXT  = 0x35,
+    /* PIO */
+    CMD_READ_PIO       = 0x20,
+    CMD_READ_PIO_EXT   = 0x24,
+    CMD_WRITE_PIO      = 0x30,
+    CMD_WRITE_PIO_EXT  = 0x34,
+    /* Misc */
+    CMD_READ_MAX       = 0xF8,
+    CMD_READ_MAX_EXT   = 0x27,
+    CMD_FLUSH_CACHE    = 0xE7,
+    CMD_IDENTIFY       = 0xEC,
+    CMD_PACKET         = 0xA0,
+    CMD_PACKET_ID      = 0xA1,
+    /* NCQ */
+    READ_FPDMA_QUEUED  = 0x60,
+    WRITE_FPDMA_QUEUED = 0x61,
+};
+
+/* ATAPI Commands */
+enum {
+    CMD_ATAPI_TEST_UNIT_READY = 0x00,
+    CMD_ATAPI_REQUEST_SENSE   = 0x03,
+    CMD_ATAPI_START_STOP_UNIT = 0x1b,
+    CMD_ATAPI_READ_10         = 0x28,
+    CMD_ATAPI_READ_CD         = 0xbe,
+};
+
+enum {
+    SENSE_NO_SENSE       = 0x00,
+    SENSE_NOT_READY      = 0x02,
+    SENSE_UNIT_ATTENTION = 0x06,
+};
+
+enum {
+    ASC_MEDIUM_MAY_HAVE_CHANGED = 0x28,
+    ASC_MEDIUM_NOT_PRESENT      = 0x3a,
+};
+
+/* AHCI Command Header Flags & Masks*/
+#define CMDH_CFL        (0x1F)
+#define CMDH_ATAPI      (0x20)
+#define CMDH_WRITE      (0x40)
+#define CMDH_PREFETCH   (0x80)
+#define CMDH_RESET     (0x100)
+#define CMDH_BIST      (0x200)
+#define CMDH_CLR_BSY   (0x400)
+#define CMDH_RES       (0x800)
+#define CMDH_PMP      (0xF000)
+
+/* ATA device register masks */
+#define ATA_DEVICE_MAGIC 0xA0 /* used in ata1-3 */
+#define ATA_DEVICE_LBA   0x40
+#define NCQ_DEVICE_MAGIC 0x40 /* for ncq device registers */
+#define ATA_DEVICE_DRIVE 0x10
+#define ATA_DEVICE_HEAD  0x0F
+
+/*** Structures ***/
+
+typedef struct AHCIPortQState {
+    uint64_t fb;
+    uint64_t clb;
+    uint64_t ctba[32];
+    uint16_t prdtl[32];
+    uint8_t next; /** Next Command Slot to Use **/
+} AHCIPortQState;
+
+typedef struct AHCIQState {
+    QOSState *parent;
+    QPCIDevice *dev;
+    QPCIBar hba_bar;
+    uint64_t barsize;
+    uint32_t fingerprint;
+    uint32_t cap;
+    uint32_t cap2;
+    AHCIPortQState port[32];
+    bool enabled;
+} AHCIQState;
+
+/**
+ * Generic FIS structure.
+ */
+typedef struct FIS {
+    uint8_t fis_type;
+    uint8_t flags;
+    char data[0];
+} __attribute__((__packed__)) FIS;
+
+/**
+ * Register device-to-host FIS structure.
+ */
+typedef struct RegD2HFIS {
+    /* DW0 */
+    uint8_t fis_type;
+    uint8_t flags;
+    uint8_t status;
+    uint8_t error;
+    /* DW1 */
+    uint8_t lba_lo[3];
+    uint8_t device;
+    /* DW2 */
+    uint8_t lba_hi[3];
+    uint8_t res0;
+    /* DW3 */
+    uint16_t count;
+    uint16_t res1;
+    /* DW4 */
+    uint32_t res2;
+} __attribute__((__packed__)) RegD2HFIS;
+
+/**
+ * Register device-to-host FIS structure;
+ * PIO Setup variety.
+ */
+typedef struct PIOSetupFIS {
+    /* DW0 */
+    uint8_t fis_type;
+    uint8_t flags;
+    uint8_t status;
+    uint8_t error;
+    /* DW1 */
+    uint8_t lba_lo[3];
+    uint8_t device;
+    /* DW2 */
+    uint8_t lba_hi[3];
+    uint8_t res0;
+    /* DW3 */
+    uint16_t count;
+    uint8_t res1;
+    uint8_t e_status;
+    /* DW4 */
+    uint16_t tx_count;
+    uint16_t res2;
+} __attribute__((__packed__)) PIOSetupFIS;
+
+/**
+ * Register host-to-device FIS structure.
+ */
+typedef struct RegH2DFIS {
+    /* DW0 */
+    uint8_t fis_type;
+    uint8_t flags;
+    uint8_t command;
+    uint8_t feature_low;
+    /* DW1 */
+    uint8_t lba_lo[3];
+    uint8_t device;
+    /* DW2 */
+    uint8_t lba_hi[3];
+    uint8_t feature_high;
+    /* DW3 */
+    uint16_t count;
+    uint8_t icc;
+    uint8_t control;
+    /* DW4 */
+    uint8_t aux[4];
+} __attribute__((__packed__)) RegH2DFIS;
+
+/**
+ * Register host-to-device FIS structure, for NCQ commands.
+ * Actually just a RegH2DFIS, but with fields repurposed.
+ * Repurposed fields are annotated below.
+ */
+typedef struct NCQFIS {
+    /* DW0 */
+    uint8_t fis_type;
+    uint8_t flags;
+    uint8_t command;
+    uint8_t sector_low; /* H2D: Feature 7:0 */
+    /* DW1 */
+    uint8_t lba_lo[3];
+    uint8_t device;
+    /* DW2 */
+    uint8_t lba_hi[3];
+    uint8_t sector_hi; /* H2D: Feature 15:8 */
+    /* DW3 */
+    uint8_t tag;       /* H2D: Count 0:7 */
+    uint8_t prio;      /* H2D: Count 15:8 */
+    uint8_t icc;
+    uint8_t control;
+    /* DW4 */
+    uint8_t aux[4];
+} __attribute__((__packed__)) NCQFIS;
+
+/**
+ * Command List entry structure.
+ * The command list contains between 1-32 of these structures.
+ */
+typedef struct AHCICommandHeader {
+    uint16_t flags; /* Cmd-Fis-Len, PMP#, and flags. */
+    uint16_t prdtl; /* Phys Region Desc. Table Length */
+    uint32_t prdbc; /* Phys Region Desc. Byte Count */
+    uint64_t ctba;  /* Command Table Descriptor Base Address */
+    uint32_t res[4];
+} __attribute__((__packed__)) AHCICommandHeader;
+
+/**
+ * Physical Region Descriptor; pointed to by the Command List Header,
+ * struct ahci_command.
+ */
+typedef struct PRD {
+    uint64_t dba;  /* Data Base Address */
+    uint32_t res;  /* Reserved */
+    uint32_t dbc;  /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */
+} __attribute__((__packed__)) PRD;
+
+/* Opaque, defined within ahci.c */
+typedef struct AHCICommand AHCICommand;
+
+/* Options to ahci_exec */
+typedef struct AHCIOpts {
+    size_t size;        /* Size of transfer */
+    unsigned prd_size;  /* Size per-each PRD */
+    bool set_bcl;       /* Override the default BCL of ATAPI_SECTOR_SIZE */
+    unsigned bcl;       /* Byte Count Limit, for ATAPI PIO */
+    uint64_t lba;       /* Starting LBA offset */
+    uint64_t buffer;    /* Pointer to source or destination guest buffer */
+    bool atapi;         /* ATAPI command? */
+    bool atapi_dma;     /* Use DMA for ATAPI? */
+    bool error;
+    int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
+    int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
+    int (*post_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
+    void *opaque;
+} AHCIOpts;
+
+/*** Macro Utilities ***/
+#define BITANY(data, mask) (((data) & (mask)) != 0)
+#define BITSET(data, mask) (((data) & (mask)) == (mask))
+#define BITCLR(data, mask) (((data) & (mask)) == 0)
+#define ASSERT_BIT_SET(data, mask) g_assert_cmphex((data) & (mask), ==, (mask))
+#define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0)
+
+/* For calculating how big the PRD table needs to be: */
+#define CMD_TBL_SIZ(n) ((0x80 + ((n) * sizeof(PRD)) + 0x7F) & ~0x7F)
+
+/* Helpers for reading/writing AHCI HBA register values */
+
+static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset)
+{
+    return qpci_io_readl(ahci->dev, ahci->hba_bar, offset);
+}
+
+static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value)
+{
+    qpci_io_writel(ahci->dev, ahci->hba_bar, offset, value);
+}
+
+static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num)
+{
+    return ahci_mread(ahci, 4 * reg_num);
+}
+
+static inline void ahci_wreg(AHCIQState *ahci, uint32_t reg_num, uint32_t value)
+{
+    ahci_mwrite(ahci, 4 * reg_num, value);
+}
+
+static inline void ahci_set(AHCIQState *ahci, uint32_t reg_num, uint32_t mask)
+{
+    ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) | mask);
+}
+
+static inline void ahci_clr(AHCIQState *ahci, uint32_t reg_num, uint32_t mask)
+{
+    ahci_wreg(ahci, reg_num, ahci_rreg(ahci, reg_num) & ~mask);
+}
+
+static inline size_t ahci_px_offset(uint8_t port, uint32_t reg_num)
+{
+    return AHCI_PORTS + (HBA_PORT_NUM_REG * port) + reg_num;
+}
+
+static inline uint32_t ahci_px_rreg(AHCIQState *ahci, uint8_t port,
+                                    uint32_t reg_num)
+{
+    return ahci_rreg(ahci, ahci_px_offset(port, reg_num));
+}
+
+static inline void ahci_px_wreg(AHCIQState *ahci, uint8_t port,
+                                uint32_t reg_num, uint32_t value)
+{
+    ahci_wreg(ahci, ahci_px_offset(port, reg_num), value);
+}
+
+static inline void ahci_px_set(AHCIQState *ahci, uint8_t port,
+                               uint32_t reg_num, uint32_t mask)
+{
+    ahci_px_wreg(ahci, port, reg_num,
+                 ahci_px_rreg(ahci, port, reg_num) | mask);
+}
+
+static inline void ahci_px_clr(AHCIQState *ahci, uint8_t port,
+                               uint32_t reg_num, uint32_t mask)
+{
+    ahci_px_wreg(ahci, port, reg_num,
+                 ahci_px_rreg(ahci, port, reg_num) & ~mask);
+}
+
+/*** Prototypes ***/
+uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes);
+void ahci_free(AHCIQState *ahci, uint64_t addr);
+void ahci_clean_mem(AHCIQState *ahci);
+
+/* Device management */
+QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint);
+void free_ahci_device(QPCIDevice *dev);
+void ahci_pci_enable(AHCIQState *ahci);
+void start_ahci_device(AHCIQState *ahci);
+void ahci_hba_enable(AHCIQState *ahci);
+
+/* Port Management */
+unsigned ahci_port_select(AHCIQState *ahci);
+void ahci_port_clear(AHCIQState *ahci, uint8_t port);
+
+/* Command header / table management */
+unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port);
+void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
+                             uint8_t slot, AHCICommandHeader *cmd);
+void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
+                             uint8_t slot, AHCICommandHeader *cmd);
+void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
+
+/* AHCI sanity check routines */
+void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
+                           uint32_t imask, uint8_t emask);
+void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
+                                uint32_t intr_mask);
+void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
+void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
+void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
+void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
+
+/* Misc */
+bool is_atapi(AHCIQState *ahci, uint8_t port);
+unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd);
+
+/* Command: Macro level execution */
+void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
+                   uint64_t gbuffer, size_t size, uint64_t sector);
+AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
+                                uint64_t gbuffer, size_t size, uint64_t sector);
+void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd);
+void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
+             void *buffer, size_t bufsize, uint64_t sector);
+void ahci_exec(AHCIQState *ahci, uint8_t port,
+               uint8_t op, const AHCIOpts *opts);
+void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, bool ready,
+                           uint8_t expected_sense);
+void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
+                          uint8_t *sense, uint8_t *asc);
+void ahci_atapi_eject(AHCIQState *ahci, uint8_t port);
+void ahci_atapi_load(AHCIQState *ahci, uint8_t port);
+
+/* Command: Fine-grained lifecycle */
+AHCICommand *ahci_command_create(uint8_t command_name);
+AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl, bool dma);
+void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
+void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
+void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
+void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd);
+void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd);
+void ahci_command_free(AHCICommand *cmd);
+
+/* Command: adjustments */
+void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags);
+void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags);
+void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect);
+void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer);
+void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes);
+void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size);
+void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
+                            unsigned prd_size);
+void ahci_command_set_acmd(AHCICommand *cmd, void *acmd);
+void ahci_command_enable_atapi_dma(AHCICommand *cmd);
+void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer,
+                         uint64_t xbytes, unsigned prd_size);
+
+/* Command: Misc */
+uint8_t ahci_command_slot(AHCICommand *cmd);
+void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd);
+
+#endif
diff --git a/tests/qtest/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c
new file mode 100644 (file)
index 0000000..25066fb
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "libqos/i2c.h"
+
+#define ARM_PAGE_SIZE            4096
+#define IMX25_PDK_RAM_START      0x80000000
+#define IMX25_PDK_RAM_END        0x88000000
+
+typedef struct QIMX25PDKMachine QIMX25PDKMachine;
+
+struct QIMX25PDKMachine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    IMXI2C i2c_1;
+};
+
+static void *imx25_pdk_get_driver(void *object, const char *interface)
+{
+    QIMX25PDKMachine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/imx25_pdk\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *imx25_pdk_get_device(void *obj, const char *device)
+{
+    QIMX25PDKMachine *machine = obj;
+    if (!g_strcmp0(device, "imx.i2c")) {
+        return &machine->i2c_1.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/imx25_pdk\n", device);
+    g_assert_not_reached();
+}
+
+static void imx25_pdk_destructor(QOSGraphObject *obj)
+{
+    QIMX25PDKMachine *machine = (QIMX25PDKMachine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_imx25_pdk(QTestState *qts)
+{
+    QIMX25PDKMachine *machine = g_new0(QIMX25PDKMachine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               IMX25_PDK_RAM_START,
+               IMX25_PDK_RAM_END,
+               ARM_PAGE_SIZE);
+    machine->obj.get_device = imx25_pdk_get_device;
+    machine->obj.get_driver = imx25_pdk_get_driver;
+    machine->obj.destructor = imx25_pdk_destructor;
+
+    imx_i2c_init(&machine->i2c_1, qts, 0x43f80000);
+    return &machine->obj;
+}
+
+static void imx25_pdk_register_nodes(void)
+{
+    QOSGraphEdgeOptions edge = {
+        .extra_device_opts = "bus=i2c-bus.0"
+    };
+    qos_node_create_machine("arm/imx25-pdk", qos_create_machine_arm_imx25_pdk);
+    qos_node_contains("arm/imx25-pdk", "imx.i2c", &edge, NULL);
+}
+
+libqos_init(imx25_pdk_register_nodes);
diff --git a/tests/qtest/libqos/arm-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c
new file mode 100644 (file)
index 0000000..87279bd
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2019 Red Hat, Inc.
+ *
+ * Author: Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "libqos/i2c.h"
+
+#define ARM_PAGE_SIZE            4096
+#define N800_RAM_START      0x80000000
+#define N800_RAM_END        0x88000000
+
+typedef struct QN800Machine QN800Machine;
+
+struct QN800Machine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    OMAPI2C i2c_1;
+};
+
+static void *n800_get_driver(void *object, const char *interface)
+{
+    QN800Machine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/n800\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *n800_get_device(void *obj, const char *device)
+{
+    QN800Machine *machine = obj;
+    if (!g_strcmp0(device, "omap_i2c")) {
+        return &machine->i2c_1.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/n800\n", device);
+    g_assert_not_reached();
+}
+
+static void n800_destructor(QOSGraphObject *obj)
+{
+    QN800Machine *machine = (QN800Machine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_n800(QTestState *qts)
+{
+    QN800Machine *machine = g_new0(QN800Machine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               N800_RAM_START,
+               N800_RAM_END,
+               ARM_PAGE_SIZE);
+    machine->obj.get_device = n800_get_device;
+    machine->obj.get_driver = n800_get_driver;
+    machine->obj.destructor = n800_destructor;
+
+    omap_i2c_init(&machine->i2c_1, qts, 0x48070000);
+    return &machine->obj;
+}
+
+static void n800_register_nodes(void)
+{
+    QOSGraphEdgeOptions edge = {
+        .extra_device_opts = "bus=i2c-bus.0"
+    };
+    qos_node_create_machine("arm/n800", qos_create_machine_arm_n800);
+    qos_node_contains("arm/n800", "omap_i2c", &edge, NULL);
+}
+
+libqos_init(n800_register_nodes);
diff --git a/tests/qtest/libqos/arm-raspi2-machine.c b/tests/qtest/libqos/arm-raspi2-machine.c
new file mode 100644 (file)
index 0000000..12a7cb7
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "sdhci.h"
+
+#define ARM_PAGE_SIZE             4096
+#define RASPI2_RAM_ADDR           0
+#define RASPI2_RAM_SIZE           0x20000000
+
+typedef struct QRaspi2Machine QRaspi2Machine;
+
+struct QRaspi2Machine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    QSDHCI_MemoryMapped sdhci;
+};
+
+static void *raspi2_get_driver(void *object, const char *interface)
+{
+    QRaspi2Machine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/raspi2\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *raspi2_get_device(void *obj, const char *device)
+{
+    QRaspi2Machine *machine = obj;
+    if (!g_strcmp0(device, "generic-sdhci")) {
+        return &machine->sdhci.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/raspi2\n", device);
+    g_assert_not_reached();
+}
+
+static void raspi2_destructor(QOSGraphObject *obj)
+{
+    QRaspi2Machine *machine = (QRaspi2Machine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_raspi2(QTestState *qts)
+{
+    QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               RASPI2_RAM_ADDR + (1 << 20),
+               RASPI2_RAM_ADDR + RASPI2_RAM_SIZE,
+               ARM_PAGE_SIZE);
+    machine->obj.get_device = raspi2_get_device;
+    machine->obj.get_driver = raspi2_get_driver;
+    machine->obj.destructor = raspi2_destructor;
+    qos_init_sdhci_mm(&machine->sdhci, qts, 0x3f300000, &(QSDHCIProperties) {
+        .version = 3,
+        .baseclock = 52,
+        .capab.sdma = false,
+        .capab.reg = 0x052134b4
+    });
+    return &machine->obj;
+}
+
+static void raspi2_register_nodes(void)
+{
+    qos_node_create_machine("arm/raspi2", qos_create_machine_arm_raspi2);
+    qos_node_contains("arm/raspi2", "generic-sdhci", NULL);
+}
+
+libqos_init(raspi2_register_nodes);
diff --git a/tests/qtest/libqos/arm-sabrelite-machine.c b/tests/qtest/libqos/arm-sabrelite-machine.c
new file mode 100644 (file)
index 0000000..e6df437
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "sdhci.h"
+
+#define ARM_PAGE_SIZE            4096
+#define SABRELITE_RAM_START      0x10000000
+#define SABRELITE_RAM_END        0x30000000
+
+typedef struct QSabreliteMachine QSabreliteMachine;
+
+struct QSabreliteMachine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    QSDHCI_MemoryMapped sdhci;
+};
+
+static void *sabrelite_get_driver(void *object, const char *interface)
+{
+    QSabreliteMachine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/sabrelite\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *sabrelite_get_device(void *obj, const char *device)
+{
+    QSabreliteMachine *machine = obj;
+    if (!g_strcmp0(device, "generic-sdhci")) {
+        return &machine->sdhci.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/sabrelite\n", device);
+    g_assert_not_reached();
+}
+
+static void sabrelite_destructor(QOSGraphObject *obj)
+{
+    QSabreliteMachine *machine = (QSabreliteMachine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_sabrelite(QTestState *qts)
+{
+    QSabreliteMachine *machine = g_new0(QSabreliteMachine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               SABRELITE_RAM_START,
+               SABRELITE_RAM_END,
+               ARM_PAGE_SIZE);
+    machine->obj.get_device = sabrelite_get_device;
+    machine->obj.get_driver = sabrelite_get_driver;
+    machine->obj.destructor = sabrelite_destructor;
+    qos_init_sdhci_mm(&machine->sdhci, qts, 0x02190000, &(QSDHCIProperties) {
+        .version = 3,
+        .baseclock = 0,
+        .capab.sdma = true,
+        .capab.reg = 0x057834b4,
+    });
+    return &machine->obj;
+}
+
+static void sabrelite_register_nodes(void)
+{
+    qos_node_create_machine("arm/sabrelite", qos_create_machine_arm_sabrelite);
+    qos_node_contains("arm/sabrelite", "generic-sdhci", NULL);
+}
+
+libqos_init(sabrelite_register_nodes);
diff --git a/tests/qtest/libqos/arm-smdkc210-machine.c b/tests/qtest/libqos/arm-smdkc210-machine.c
new file mode 100644 (file)
index 0000000..215b628
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "sdhci.h"
+
+#define ARM_PAGE_SIZE             4096
+#define SMDKC210_RAM_ADDR         0x40000000ull
+#define SMDKC210_RAM_SIZE         0x40000000ull
+
+typedef struct QSmdkc210Machine QSmdkc210Machine;
+
+struct QSmdkc210Machine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    QSDHCI_MemoryMapped sdhci;
+};
+
+static void *smdkc210_get_driver(void *object, const char *interface)
+{
+    QSmdkc210Machine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/smdkc210\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *smdkc210_get_device(void *obj, const char *device)
+{
+    QSmdkc210Machine *machine = obj;
+    if (!g_strcmp0(device, "generic-sdhci")) {
+        return &machine->sdhci.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/smdkc210\n", device);
+    g_assert_not_reached();
+}
+
+static void smdkc210_destructor(QOSGraphObject *obj)
+{
+    QSmdkc210Machine *machine = (QSmdkc210Machine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_smdkc210(QTestState *qts)
+{
+    QSmdkc210Machine *machine = g_new0(QSmdkc210Machine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               SMDKC210_RAM_ADDR,
+               SMDKC210_RAM_ADDR + SMDKC210_RAM_SIZE,
+               ARM_PAGE_SIZE);
+    machine->obj.get_device = smdkc210_get_device;
+    machine->obj.get_driver = smdkc210_get_driver;
+    machine->obj.destructor = smdkc210_destructor;
+    qos_init_sdhci_mm(&machine->sdhci, qts, 0x12510000, &(QSDHCIProperties) {
+        .version = 2,
+        .baseclock = 0,
+        .capab.sdma = true,
+        .capab.reg = 0x5e80080,
+    });
+    return &machine->obj;
+}
+
+static void smdkc210_register_nodes(void)
+{
+    qos_node_create_machine("arm/smdkc210", qos_create_machine_arm_smdkc210);
+    qos_node_contains("arm/smdkc210", "generic-sdhci", NULL);
+}
+
+libqos_init(smdkc210_register_nodes);
diff --git a/tests/qtest/libqos/arm-virt-machine.c b/tests/qtest/libqos/arm-virt-machine.c
new file mode 100644 (file)
index 0000000..96ffe3e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-mmio.h"
+
+#define ARM_PAGE_SIZE               4096
+#define VIRTIO_MMIO_BASE_ADDR       0x0A003E00
+#define ARM_VIRT_RAM_ADDR           0x40000000
+#define ARM_VIRT_RAM_SIZE           0x20000000
+#define VIRTIO_MMIO_SIZE            0x00000200
+
+typedef struct QVirtMachine QVirtMachine;
+
+struct QVirtMachine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    QVirtioMMIODevice virtio_mmio;
+};
+
+static void virt_destructor(QOSGraphObject *obj)
+{
+    QVirtMachine *machine = (QVirtMachine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *virt_get_driver(void *object, const char *interface)
+{
+    QVirtMachine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/virtio\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *virt_get_device(void *obj, const char *device)
+{
+    QVirtMachine *machine = obj;
+    if (!g_strcmp0(device, "virtio-mmio")) {
+        return &machine->virtio_mmio.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/virtio\n", device);
+    g_assert_not_reached();
+}
+
+static void *qos_create_machine_arm_virt(QTestState *qts)
+{
+    QVirtMachine *machine = g_new0(QVirtMachine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               ARM_VIRT_RAM_ADDR,
+               ARM_VIRT_RAM_ADDR + ARM_VIRT_RAM_SIZE,
+               ARM_PAGE_SIZE);
+    qvirtio_mmio_init_device(&machine->virtio_mmio, qts, VIRTIO_MMIO_BASE_ADDR,
+                             VIRTIO_MMIO_SIZE);
+
+    machine->obj.get_device = virt_get_device;
+    machine->obj.get_driver = virt_get_driver;
+    machine->obj.destructor = virt_destructor;
+    return machine;
+}
+
+static void virtio_mmio_register_nodes(void)
+{
+    qos_node_create_machine("arm/virt", qos_create_machine_arm_virt);
+    qos_node_contains("arm/virt", "virtio-mmio", NULL);
+}
+
+libqos_init(virtio_mmio_register_nodes);
diff --git a/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c b/tests/qtest/libqos/arm-xilinx-zynq-a9-machine.c
new file mode 100644 (file)
index 0000000..5bc95f2
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "sdhci.h"
+
+typedef struct QXilinxZynqA9Machine QXilinxZynqA9Machine;
+
+struct QXilinxZynqA9Machine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    QSDHCI_MemoryMapped sdhci;
+};
+
+#define ARM_PAGE_SIZE             4096
+#define XILINX_ZYNQ_A9_RAM_ADDR   0
+#define XILINX_ZYNQ_A9_RAM_SIZE   0x20000000
+
+static void *xilinx_zynq_a9_get_driver(void *object, const char *interface)
+{
+    QXilinxZynqA9Machine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *xilinx_zynq_a9_get_device(void *obj, const char *device)
+{
+    QXilinxZynqA9Machine *machine = obj;
+    if (!g_strcmp0(device, "generic-sdhci")) {
+        return &machine->sdhci.obj;
+    }
+
+    fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", device);
+    g_assert_not_reached();
+}
+
+static void xilinx_zynq_a9_destructor(QOSGraphObject *obj)
+{
+    QXilinxZynqA9Machine *machine = (QXilinxZynqA9Machine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *qos_create_machine_arm_xilinx_zynq_a9(QTestState *qts)
+{
+    QXilinxZynqA9Machine *machine = g_new0(QXilinxZynqA9Machine, 1);
+
+    alloc_init(&machine->alloc, 0,
+               XILINX_ZYNQ_A9_RAM_ADDR + (1 << 20),
+               XILINX_ZYNQ_A9_RAM_ADDR + XILINX_ZYNQ_A9_RAM_SIZE,
+               ARM_PAGE_SIZE);
+
+    machine->obj.get_device = xilinx_zynq_a9_get_device;
+    machine->obj.get_driver = xilinx_zynq_a9_get_driver;
+    machine->obj.destructor = xilinx_zynq_a9_destructor;
+    /* Datasheet: UG585 (v1.12.1) */
+    qos_init_sdhci_mm(&machine->sdhci, qts, 0xe0100000, &(QSDHCIProperties) {
+        .version = 2,
+        .baseclock = 0,
+        .capab.sdma = true,
+        .capab.reg = 0x69ec0080,
+    });
+    return &machine->obj;
+}
+
+static void xilinx_zynq_a9_register_nodes(void)
+{
+    qos_node_create_machine("arm/xilinx-zynq-a9",
+                            qos_create_machine_arm_xilinx_zynq_a9);
+    qos_node_contains("arm/xilinx-zynq-a9", "generic-sdhci", NULL);
+}
+
+libqos_init(xilinx_zynq_a9_register_nodes);
diff --git a/tests/qtest/libqos/e1000e.c b/tests/qtest/libqos/e1000e.c
new file mode 100644 (file)
index 0000000..560e7a2
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/pci-pc.h"
+#include "qemu/sockets.h"
+#include "qemu/iov.h"
+#include "qemu/module.h"
+#include "qemu/bitops.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "e1000e.h"
+
+#define E1000E_IMS      (0x00d0)
+
+#define E1000E_STATUS   (0x0008)
+#define E1000E_STATUS_LU BIT(1)
+#define E1000E_STATUS_ASDV1000 BIT(9)
+
+#define E1000E_CTRL     (0x0000)
+#define E1000E_CTRL_RESET BIT(26)
+
+#define E1000E_RCTL     (0x0100)
+#define E1000E_RCTL_EN  BIT(1)
+#define E1000E_RCTL_UPE BIT(3)
+#define E1000E_RCTL_MPE BIT(4)
+
+#define E1000E_RFCTL     (0x5008)
+#define E1000E_RFCTL_EXTEN  BIT(15)
+
+#define E1000E_TCTL     (0x0400)
+#define E1000E_TCTL_EN  BIT(1)
+
+#define E1000E_CTRL_EXT             (0x0018)
+#define E1000E_CTRL_EXT_DRV_LOAD    BIT(28)
+#define E1000E_CTRL_EXT_TXLSFLOW    BIT(22)
+
+#define E1000E_IVAR                 (0x00E4)
+#define E1000E_IVAR_TEST_CFG        ((E1000E_RX0_MSG_ID << 0)    | BIT(3)  | \
+                                     (E1000E_TX0_MSG_ID << 8)    | BIT(11) | \
+                                     (E1000E_OTHER_MSG_ID << 16) | BIT(19) | \
+                                     BIT(31))
+
+#define E1000E_RING_LEN             (0x1000)
+
+#define E1000E_TDBAL    (0x3800)
+
+#define E1000E_TDBAH    (0x3804)
+#define E1000E_TDH      (0x3810)
+
+#define E1000E_RDBAL    (0x2800)
+#define E1000E_RDBAH    (0x2804)
+#define E1000E_RDH      (0x2810)
+
+#define E1000E_TXD_LEN              (16)
+#define E1000E_RXD_LEN              (16)
+
+static void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val)
+{
+    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
+    qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val);
+}
+
+static uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg)
+{
+    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
+    return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg);
+}
+
+void e1000e_tx_ring_push(QE1000E *d, void *descr)
+{
+    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
+    uint32_t tail = e1000e_macreg_read(d, E1000E_TDT);
+    uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN;
+
+    qtest_memwrite(d_pci->pci_dev.bus->qts, d->tx_ring + tail * E1000E_TXD_LEN,
+                   descr, E1000E_TXD_LEN);
+    e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len);
+
+    /* Read WB data for the packet transmitted */
+    qtest_memread(d_pci->pci_dev.bus->qts, d->tx_ring + tail * E1000E_TXD_LEN,
+                  descr, E1000E_TXD_LEN);
+}
+
+void e1000e_rx_ring_push(QE1000E *d, void *descr)
+{
+    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
+    uint32_t tail = e1000e_macreg_read(d, E1000E_RDT);
+    uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN;
+
+    qtest_memwrite(d_pci->pci_dev.bus->qts, d->rx_ring + tail * E1000E_RXD_LEN,
+                   descr, E1000E_RXD_LEN);
+    e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len);
+
+    /* Read WB data for the packet received */
+    qtest_memread(d_pci->pci_dev.bus->qts, d->rx_ring + tail * E1000E_RXD_LEN,
+                  descr, E1000E_RXD_LEN);
+}
+
+static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data)
+{
+    QPCIDevice *res = data;
+    memcpy(res, dev, sizeof(QPCIDevice));
+    g_free(dev);
+}
+
+void e1000e_wait_isr(QE1000E *d, uint16_t msg_id)
+{
+    QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e);
+    guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND;
+
+    do {
+        if (qpci_msix_pending(&d_pci->pci_dev, msg_id)) {
+            return;
+        }
+        qtest_clock_step(d_pci->pci_dev.bus->qts, 10000);
+    } while (g_get_monotonic_time() < end_time);
+
+    g_error("Timeout expired");
+}
+
+static void e1000e_pci_destructor(QOSGraphObject *obj)
+{
+    QE1000E_PCI *epci = (QE1000E_PCI *) obj;
+    qpci_iounmap(&epci->pci_dev, epci->mac_regs);
+    qpci_msix_disable(&epci->pci_dev);
+}
+
+static void e1000e_pci_start_hw(QOSGraphObject *obj)
+{
+    QE1000E_PCI *d = (QE1000E_PCI *) obj;
+    uint32_t val;
+
+    /* Enable the device */
+    qpci_device_enable(&d->pci_dev);
+
+    /* Reset the device */
+    val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL);
+    e1000e_macreg_write(&d->e1000e, E1000E_CTRL, val | E1000E_CTRL_RESET);
+
+    /* Enable and configure MSI-X */
+    qpci_msix_enable(&d->pci_dev);
+    e1000e_macreg_write(&d->e1000e, E1000E_IVAR, E1000E_IVAR_TEST_CFG);
+
+    /* Check the device status - link and speed */
+    val = e1000e_macreg_read(&d->e1000e, E1000E_STATUS);
+    g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000),
+        ==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000);
+
+    /* Initialize TX/RX logic */
+    e1000e_macreg_write(&d->e1000e, E1000E_RCTL, 0);
+    e1000e_macreg_write(&d->e1000e, E1000E_TCTL, 0);
+
+    /* Notify the device that the driver is ready */
+    val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL_EXT);
+    e1000e_macreg_write(&d->e1000e, E1000E_CTRL_EXT,
+        val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW);
+
+    e1000e_macreg_write(&d->e1000e, E1000E_TDBAL,
+                           (uint32_t) d->e1000e.tx_ring);
+    e1000e_macreg_write(&d->e1000e, E1000E_TDBAH,
+                           (uint32_t) (d->e1000e.tx_ring >> 32));
+    e1000e_macreg_write(&d->e1000e, E1000E_TDLEN, E1000E_RING_LEN);
+    e1000e_macreg_write(&d->e1000e, E1000E_TDT, 0);
+    e1000e_macreg_write(&d->e1000e, E1000E_TDH, 0);
+
+    /* Enable transmit */
+    e1000e_macreg_write(&d->e1000e, E1000E_TCTL, E1000E_TCTL_EN);
+    e1000e_macreg_write(&d->e1000e, E1000E_RDBAL,
+                           (uint32_t)d->e1000e.rx_ring);
+    e1000e_macreg_write(&d->e1000e, E1000E_RDBAH,
+                           (uint32_t)(d->e1000e.rx_ring >> 32));
+    e1000e_macreg_write(&d->e1000e, E1000E_RDLEN, E1000E_RING_LEN);
+    e1000e_macreg_write(&d->e1000e, E1000E_RDT, 0);
+    e1000e_macreg_write(&d->e1000e, E1000E_RDH, 0);
+
+    /* Enable receive */
+    e1000e_macreg_write(&d->e1000e, E1000E_RFCTL, E1000E_RFCTL_EXTEN);
+    e1000e_macreg_write(&d->e1000e, E1000E_RCTL, E1000E_RCTL_EN  |
+                                        E1000E_RCTL_UPE |
+                                        E1000E_RCTL_MPE);
+
+    /* Enable all interrupts */
+    e1000e_macreg_write(&d->e1000e, E1000E_IMS, 0xFFFFFFFF);
+
+}
+
+static void *e1000e_pci_get_driver(void *obj, const char *interface)
+{
+    QE1000E_PCI *epci = obj;
+    if (!g_strcmp0(interface, "e1000e-if")) {
+        return &epci->e1000e;
+    }
+
+    /* implicit contains */
+    if (!g_strcmp0(interface, "pci-device")) {
+        return &epci->pci_dev;
+    }
+
+    fprintf(stderr, "%s not present in e1000e\n", interface);
+    g_assert_not_reached();
+}
+
+static void *e1000e_pci_create(void *pci_bus, QGuestAllocator *alloc,
+                               void *addr)
+{
+    QE1000E_PCI *d = g_new0(QE1000E_PCI, 1);
+    QPCIBus *bus = pci_bus;
+    QPCIAddress *address = addr;
+
+    qpci_device_foreach(bus, address->vendor_id, address->device_id,
+                        e1000e_foreach_callback, &d->pci_dev);
+
+    /* Map BAR0 (mac registers) */
+    d->mac_regs = qpci_iomap(&d->pci_dev, 0, NULL);
+
+    /* Allocate and setup TX ring */
+    d->e1000e.tx_ring = guest_alloc(alloc, E1000E_RING_LEN);
+    g_assert(d->e1000e.tx_ring != 0);
+
+    /* Allocate and setup RX ring */
+    d->e1000e.rx_ring = guest_alloc(alloc, E1000E_RING_LEN);
+    g_assert(d->e1000e.rx_ring != 0);
+
+    d->obj.get_driver = e1000e_pci_get_driver;
+    d->obj.start_hw = e1000e_pci_start_hw;
+    d->obj.destructor = e1000e_pci_destructor;
+
+    return &d->obj;
+}
+
+static void e1000e_register_nodes(void)
+{
+    QPCIAddress addr = {
+        .vendor_id = 0x8086,
+        .device_id = 0x10D3,
+    };
+
+    /* FIXME: every test using this node needs to setup a -netdev socket,id=hs0
+     * otherwise QEMU is not going to start */
+    QOSGraphEdgeOptions opts = {
+        .extra_device_opts = "netdev=hs0",
+    };
+    add_qpci_address(&opts, &addr);
+
+    qos_node_create_driver("e1000e", e1000e_pci_create);
+    qos_node_consumes("e1000e", "pci-bus", &opts);
+}
+
+libqos_init(e1000e_register_nodes);
diff --git a/tests/qtest/libqos/e1000e.h b/tests/qtest/libqos/e1000e.h
new file mode 100644 (file)
index 0000000..dc4ab10
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef QGRAPH_E1000E_H
+#define QGRAPH_E1000E_H
+
+#include "libqos/qgraph.h"
+#include "pci.h"
+
+#define E1000E_RX0_MSG_ID           (0)
+#define E1000E_TX0_MSG_ID           (1)
+#define E1000E_OTHER_MSG_ID         (2)
+
+#define E1000E_TDLEN    (0x3808)
+#define E1000E_TDT      (0x3818)
+#define E1000E_RDLEN    (0x2808)
+#define E1000E_RDT      (0x2818)
+
+typedef struct QE1000E QE1000E;
+typedef struct QE1000E_PCI QE1000E_PCI;
+
+struct QE1000E {
+    uint64_t tx_ring;
+    uint64_t rx_ring;
+};
+
+struct QE1000E_PCI {
+    QOSGraphObject obj;
+    QPCIDevice pci_dev;
+    QPCIBar mac_regs;
+    QE1000E e1000e;
+};
+
+void e1000e_wait_isr(QE1000E *d, uint16_t msg_id);
+void e1000e_tx_ring_push(QE1000E *d, void *descr);
+void e1000e_rx_ring_push(QE1000E *d, void *descr);
+
+#endif
diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c
new file mode 100644 (file)
index 0000000..1f46258
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * libqos fw_cfg support
+ *
+ * Copyright IBM, Corp. 2012-2013
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *  Markus Armbruster <armbru@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqos/fw_cfg.h"
+#include "libqtest.h"
+#include "qemu/bswap.h"
+#include "hw/nvram/fw_cfg.h"
+
+void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
+{
+    fw_cfg->select(fw_cfg, key);
+}
+
+void qfw_cfg_read_data(QFWCFG *fw_cfg, void *data, size_t len)
+{
+    fw_cfg->read(fw_cfg, data, len);
+}
+
+void qfw_cfg_get(QFWCFG *fw_cfg, uint16_t key, void *data, size_t len)
+{
+    qfw_cfg_select(fw_cfg, key);
+    qfw_cfg_read_data(fw_cfg, data, len);
+}
+
+uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key)
+{
+    uint16_t value;
+    qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
+    return le16_to_cpu(value);
+}
+
+uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key)
+{
+    uint32_t value;
+    qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
+    return le32_to_cpu(value);
+}
+
+uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key)
+{
+    uint64_t value;
+    qfw_cfg_get(fw_cfg, key, &value, sizeof(value));
+    return le64_to_cpu(value);
+}
+
+static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
+{
+    qtest_writew(fw_cfg->qts, fw_cfg->base, key);
+}
+
+/*
+ * The caller need check the return value. When the return value is
+ * nonzero, it means that some bytes have been transferred.
+ *
+ * If the fw_cfg file in question is smaller than the allocated & passed-in
+ * buffer, then the buffer has been populated only in part.
+ *
+ * If the fw_cfg file in question is larger than the passed-in
+ * buffer, then the return value explains how much room would have been
+ * necessary in total. And, while the caller's buffer has been fully
+ * populated, it has received only a starting slice of the fw_cfg file.
+ */
+size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
+                      void *data, size_t buflen)
+{
+    uint32_t count;
+    uint32_t i;
+    unsigned char *filesbuf = NULL;
+    size_t dsize;
+    FWCfgFile *pdir_entry;
+    size_t filesize = 0;
+
+    qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count));
+    count = be32_to_cpu(count);
+    dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file);
+    filesbuf = g_malloc(dsize);
+    qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize);
+    pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t));
+    for (i = 0; i < count; ++i, ++pdir_entry) {
+        if (!strcmp(pdir_entry->name, filename)) {
+            uint32_t len = be32_to_cpu(pdir_entry->size);
+            uint16_t sel = be16_to_cpu(pdir_entry->select);
+            filesize = len;
+            if (len > buflen) {
+                len = buflen;
+            }
+            qfw_cfg_get(fw_cfg, sel, data, len);
+            break;
+        }
+    }
+    g_free(filesbuf);
+    return filesize;
+}
+
+static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
+{
+    uint8_t *ptr = data;
+    int i;
+
+    for (i = 0; i < len; i++) {
+        ptr[i] = qtest_readb(fw_cfg->qts, fw_cfg->base + 2);
+    }
+}
+
+QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base)
+{
+    QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
+
+    fw_cfg->base = base;
+    fw_cfg->qts = qts;
+    fw_cfg->select = mm_fw_cfg_select;
+    fw_cfg->read = mm_fw_cfg_read;
+
+    return fw_cfg;
+}
+
+void mm_fw_cfg_uninit(QFWCFG *fw_cfg)
+{
+    g_free(fw_cfg);
+}
+
+static void io_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key)
+{
+    qtest_outw(fw_cfg->qts, fw_cfg->base, key);
+}
+
+static void io_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len)
+{
+    uint8_t *ptr = data;
+    int i;
+
+    for (i = 0; i < len; i++) {
+        ptr[i] = qtest_inb(fw_cfg->qts, fw_cfg->base + 1);
+    }
+}
+
+QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base)
+{
+    QFWCFG *fw_cfg = g_malloc0(sizeof(*fw_cfg));
+
+    fw_cfg->base = base;
+    fw_cfg->qts = qts;
+    fw_cfg->select = io_fw_cfg_select;
+    fw_cfg->read = io_fw_cfg_read;
+
+    return fw_cfg;
+}
+
+void io_fw_cfg_uninit(QFWCFG *fw_cfg)
+{
+    g_free(fw_cfg);
+}
diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h
new file mode 100644 (file)
index 0000000..13325cc
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * libqos fw_cfg support
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_FW_CFG_H
+#define LIBQOS_FW_CFG_H
+
+#include "libqtest.h"
+
+typedef struct QFWCFG QFWCFG;
+
+struct QFWCFG
+{
+    uint64_t base;
+    QTestState *qts;
+    void (*select)(QFWCFG *fw_cfg, uint16_t key);
+    void (*read)(QFWCFG *fw_cfg, void *data, size_t len);
+};
+
+void qfw_cfg_select(QFWCFG *fw_cfg, uint16_t key);
+void qfw_cfg_read_data(QFWCFG *fw_cfg, void *data, size_t len);
+void qfw_cfg_get(QFWCFG *fw_cfg, uint16_t key, void *data, size_t len);
+uint16_t qfw_cfg_get_u16(QFWCFG *fw_cfg, uint16_t key);
+uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key);
+uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key);
+size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename,
+                        void *data, size_t buflen);
+
+QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base);
+void mm_fw_cfg_uninit(QFWCFG *fw_cfg);
+QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base);
+void io_fw_cfg_uninit(QFWCFG *fw_cfg);
+
+static inline QFWCFG *pc_fw_cfg_init(QTestState *qts)
+{
+    return io_fw_cfg_init(qts, 0x510);
+}
+
+static inline void pc_fw_cfg_uninit(QFWCFG *fw_cfg)
+{
+    io_fw_cfg_uninit(fw_cfg);
+}
+
+#endif
diff --git a/tests/qtest/libqos/i2c-imx.c b/tests/qtest/libqos/i2c-imx.c
new file mode 100644 (file)
index 0000000..f33ece5
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * QTest i.MX I2C driver
+ *
+ * Copyright (c) 2013 Jean-Christophe Dubois
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the
+ *  Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "libqos/i2c.h"
+
+
+#include "libqtest.h"
+
+#include "hw/i2c/imx_i2c.h"
+
+enum IMXI2CDirection {
+    IMX_I2C_READ,
+    IMX_I2C_WRITE,
+};
+
+static void imx_i2c_set_slave_addr(IMXI2C *s, uint8_t addr,
+                                   enum IMXI2CDirection direction)
+{
+    qtest_writeb(s->parent.qts, s->addr + I2DR_ADDR,
+                 (addr << 1) | (direction == IMX_I2C_READ ? 1 : 0));
+}
+
+static void imx_i2c_send(I2CAdapter *i2c, uint8_t addr,
+                         const uint8_t *buf, uint16_t len)
+{
+    IMXI2C *s = container_of(i2c, IMXI2C, parent);
+    uint8_t data;
+    uint8_t status;
+    uint16_t size = 0;
+
+    if (!len) {
+        return;
+    }
+
+    /* set the bus for write */
+    data = I2CR_IEN |
+           I2CR_IIEN |
+           I2CR_MSTA |
+           I2CR_MTX |
+           I2CR_TXAK;
+
+    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IBB) != 0);
+
+    /* set the slave address */
+    imx_i2c_set_slave_addr(s, addr, IMX_I2C_WRITE);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IIF) != 0);
+    g_assert((status & I2SR_RXAK) == 0);
+
+    /* ack the interrupt */
+    qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IIF) == 0);
+
+    while (size < len) {
+        /* check we are still busy */
+        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+        g_assert((status & I2SR_IBB) != 0);
+
+        /* write the data */
+        qtest_writeb(i2c->qts, s->addr + I2DR_ADDR, buf[size]);
+        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+        g_assert((status & I2SR_IIF) != 0);
+        g_assert((status & I2SR_RXAK) == 0);
+
+        /* ack the interrupt */
+        qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
+        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+        g_assert((status & I2SR_IIF) == 0);
+
+        size++;
+    }
+
+    /* release the bus */
+    data &= ~(I2CR_MSTA | I2CR_MTX);
+    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IBB) == 0);
+}
+
+static void imx_i2c_recv(I2CAdapter *i2c, uint8_t addr,
+                         uint8_t *buf, uint16_t len)
+{
+    IMXI2C *s = container_of(i2c, IMXI2C, parent);
+    uint8_t data;
+    uint8_t status;
+    uint16_t size = 0;
+
+    if (!len) {
+        return;
+    }
+
+    /* set the bus for write */
+    data = I2CR_IEN |
+           I2CR_IIEN |
+           I2CR_MSTA |
+           I2CR_MTX |
+           I2CR_TXAK;
+
+    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IBB) != 0);
+
+    /* set the slave address */
+    imx_i2c_set_slave_addr(s, addr, IMX_I2C_READ);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IIF) != 0);
+    g_assert((status & I2SR_RXAK) == 0);
+
+    /* ack the interrupt */
+    qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IIF) == 0);
+
+    /* set the bus for read */
+    data &= ~I2CR_MTX;
+    /* if only one byte don't ack */
+    if (len != 1) {
+        data &= ~I2CR_TXAK;
+    }
+    qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IBB) != 0);
+
+    /* dummy read */
+    qtest_readb(i2c->qts, s->addr + I2DR_ADDR);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IIF) != 0);
+
+    /* ack the interrupt */
+    qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IIF) == 0);
+
+    while (size < len) {
+        /* check we are still busy */
+        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+        g_assert((status & I2SR_IBB) != 0);
+
+        if (size == (len - 1)) {
+            /* stop the read transaction */
+            data &= ~(I2CR_MSTA | I2CR_MTX);
+        } else {
+            /* ack the data read */
+            data |= I2CR_TXAK;
+        }
+        qtest_writeb(i2c->qts, s->addr + I2CR_ADDR, data);
+
+        /* read the data */
+        buf[size] = qtest_readb(i2c->qts, s->addr + I2DR_ADDR);
+
+        if (size != (len - 1)) {
+            status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+            g_assert((status & I2SR_IIF) != 0);
+
+            /* ack the interrupt */
+            qtest_writeb(i2c->qts, s->addr + I2SR_ADDR, 0);
+        }
+
+        status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+        g_assert((status & I2SR_IIF) == 0);
+
+        size++;
+    }
+
+    status = qtest_readb(i2c->qts, s->addr + I2SR_ADDR);
+    g_assert((status & I2SR_IBB) == 0);
+}
+
+static void *imx_i2c_get_driver(void *obj, const char *interface)
+{
+    IMXI2C *s = obj;
+    if (!g_strcmp0(interface, "i2c-bus")) {
+        return &s->parent;
+    }
+    fprintf(stderr, "%s not present in imx-i2c\n", interface);
+    g_assert_not_reached();
+}
+
+void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr)
+{
+    s->addr = addr;
+
+    s->obj.get_driver = imx_i2c_get_driver;
+
+    s->parent.send = imx_i2c_send;
+    s->parent.recv = imx_i2c_recv;
+    s->parent.qts = qts;
+}
+
+static void imx_i2c_register_nodes(void)
+{
+    qos_node_create_driver("imx.i2c", NULL);
+    qos_node_produces("imx.i2c", "i2c-bus");
+}
+
+libqos_init(imx_i2c_register_nodes);
diff --git a/tests/qtest/libqos/i2c-omap.c b/tests/qtest/libqos/i2c-omap.c
new file mode 100644 (file)
index 0000000..9ae8214
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * QTest I2C driver
+ *
+ * Copyright (c) 2012 Andreas Färber
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqos/i2c.h"
+
+
+#include "qemu/bswap.h"
+#include "libqtest.h"
+
+enum OMAPI2CRegisters {
+    OMAP_I2C_REV  = 0x00,
+    OMAP_I2C_STAT = 0x08,
+    OMAP_I2C_CNT  = 0x18,
+    OMAP_I2C_DATA = 0x1c,
+    OMAP_I2C_CON  = 0x24,
+    OMAP_I2C_SA   = 0x2c,
+};
+
+enum OMAPI2CSTATBits {
+    OMAP_I2C_STAT_NACK = 1 << 1,
+    OMAP_I2C_STAT_ARDY = 1 << 2,
+    OMAP_I2C_STAT_RRDY = 1 << 3,
+    OMAP_I2C_STAT_XRDY = 1 << 4,
+    OMAP_I2C_STAT_ROVR = 1 << 11,
+    OMAP_I2C_STAT_SBD  = 1 << 15,
+};
+
+enum OMAPI2CCONBits {
+    OMAP_I2C_CON_STT    = 1 << 0,
+    OMAP_I2C_CON_STP    = 1 << 1,
+    OMAP_I2C_CON_TRX    = 1 << 9,
+    OMAP_I2C_CON_MST    = 1 << 10,
+    OMAP_I2C_CON_BE     = 1 << 14,
+    OMAP_I2C_CON_I2C_EN = 1 << 15,
+};
+
+
+static void omap_i2c_set_slave_addr(OMAPI2C *s, uint8_t addr)
+{
+    uint16_t data = addr;
+
+    qtest_writew(s->parent.qts, s->addr + OMAP_I2C_SA, data);
+    data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_SA);
+    g_assert_cmphex(data, ==, addr);
+}
+
+static void omap_i2c_send(I2CAdapter *i2c, uint8_t addr,
+                          const uint8_t *buf, uint16_t len)
+{
+    OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
+    uint16_t data;
+
+    omap_i2c_set_slave_addr(s, addr);
+
+    data = len;
+    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data);
+
+    data = OMAP_I2C_CON_I2C_EN |
+           OMAP_I2C_CON_TRX |
+           OMAP_I2C_CON_MST |
+           OMAP_I2C_CON_STT |
+           OMAP_I2C_CON_STP;
+    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data);
+    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
+    g_assert((data & OMAP_I2C_CON_STP) != 0);
+
+    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
+    g_assert((data & OMAP_I2C_STAT_NACK) == 0);
+
+    while (len > 1) {
+        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
+        g_assert((data & OMAP_I2C_STAT_XRDY) != 0);
+
+        data = buf[0] | ((uint16_t)buf[1] << 8);
+        qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data);
+        buf = (uint8_t *)buf + 2;
+        len -= 2;
+    }
+    if (len == 1) {
+        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
+        g_assert((data & OMAP_I2C_STAT_XRDY) != 0);
+
+        data = buf[0];
+        qtest_writew(i2c->qts, s->addr + OMAP_I2C_DATA, data);
+    }
+
+    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
+    g_assert((data & OMAP_I2C_CON_STP) == 0);
+}
+
+static void omap_i2c_recv(I2CAdapter *i2c, uint8_t addr,
+                          uint8_t *buf, uint16_t len)
+{
+    OMAPI2C *s = container_of(i2c, OMAPI2C, parent);
+    uint16_t data, stat;
+    uint16_t orig_len = len;
+
+    omap_i2c_set_slave_addr(s, addr);
+
+    data = len;
+    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CNT, data);
+
+    data = OMAP_I2C_CON_I2C_EN |
+           OMAP_I2C_CON_MST |
+           OMAP_I2C_CON_STT |
+           OMAP_I2C_CON_STP;
+    qtest_writew(i2c->qts, s->addr + OMAP_I2C_CON, data);
+
+    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
+    g_assert((data & OMAP_I2C_STAT_NACK) == 0);
+
+    while (len > 0) {
+        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
+        if (len <= 4) {
+            g_assert((data & OMAP_I2C_CON_STP) == 0);
+
+            data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
+            g_assert_cmpuint(data, ==, orig_len);
+        } else {
+            g_assert((data & OMAP_I2C_CON_STP) != 0);
+
+            data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CNT);
+            g_assert_cmpuint(data, ==, len - 4);
+        }
+
+        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
+        g_assert((data & OMAP_I2C_STAT_RRDY) != 0);
+        g_assert((data & OMAP_I2C_STAT_ROVR) == 0);
+
+        data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_DATA);
+
+        stat = qtest_readw(i2c->qts, s->addr + OMAP_I2C_STAT);
+
+        if (unlikely(len == 1)) {
+            g_assert((stat & OMAP_I2C_STAT_SBD) != 0);
+
+            buf[0] = data & 0xff;
+            buf++;
+            len--;
+        } else {
+            buf[0] = data & 0xff;
+            buf[1] = data >> 8;
+            buf += 2;
+            len -= 2;
+        }
+    }
+
+    data = qtest_readw(i2c->qts, s->addr + OMAP_I2C_CON);
+    g_assert((data & OMAP_I2C_CON_STP) == 0);
+}
+
+static void *omap_i2c_get_driver(void *obj, const char *interface)
+{
+    OMAPI2C *s = obj;
+    if (!g_strcmp0(interface, "i2c-bus")) {
+        return &s->parent;
+    }
+    fprintf(stderr, "%s not present in omap_i2c\n", interface);
+    g_assert_not_reached();
+}
+
+static void omap_i2c_start_hw(QOSGraphObject *object)
+{
+    OMAPI2C *s = (OMAPI2C *) object;
+    uint16_t data;
+
+    /* verify the mmio address by looking for a known signature */
+    data = qtest_readw(s->parent.qts, s->addr + OMAP_I2C_REV);
+    g_assert_cmphex(data, ==, 0x34);
+}
+
+void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr)
+{
+    s->addr = addr;
+
+    s->obj.get_driver = omap_i2c_get_driver;
+    s->obj.start_hw = omap_i2c_start_hw;
+
+    s->parent.send = omap_i2c_send;
+    s->parent.recv = omap_i2c_recv;
+    s->parent.qts = qts;
+}
+
+static void omap_i2c_register_nodes(void)
+{
+    qos_node_create_driver("omap_i2c", NULL);
+    qos_node_produces("omap_i2c", "i2c-bus");
+}
+
+libqos_init(omap_i2c_register_nodes);
diff --git a/tests/qtest/libqos/i2c.c b/tests/qtest/libqos/i2c.c
new file mode 100644 (file)
index 0000000..156114e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * QTest I2C driver
+ *
+ * Copyright (c) 2012 Andreas Färber
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqos/i2c.h"
+#include "libqtest.h"
+
+void i2c_send(QI2CDevice *i2cdev, const uint8_t *buf, uint16_t len)
+{
+    i2cdev->bus->send(i2cdev->bus, i2cdev->addr, buf, len);
+}
+
+void i2c_recv(QI2CDevice *i2cdev, uint8_t *buf, uint16_t len)
+{
+    i2cdev->bus->recv(i2cdev->bus, i2cdev->addr, buf, len);
+}
+
+void i2c_read_block(QI2CDevice *i2cdev, uint8_t reg,
+                    uint8_t *buf, uint16_t len)
+{
+    i2c_send(i2cdev, &reg, 1);
+    i2c_recv(i2cdev, buf, len);
+}
+
+void i2c_write_block(QI2CDevice *i2cdev, uint8_t reg,
+                     const uint8_t *buf, uint16_t len)
+{
+    uint8_t *cmd = g_malloc(len + 1);
+    cmd[0] = reg;
+    memcpy(&cmd[1], buf, len);
+    i2c_send(i2cdev, cmd, len + 1);
+    g_free(cmd);
+}
+
+uint8_t i2c_get8(QI2CDevice *i2cdev, uint8_t reg)
+{
+    uint8_t resp[1];
+    i2c_read_block(i2cdev, reg, resp, sizeof(resp));
+    return resp[0];
+}
+
+uint16_t i2c_get16(QI2CDevice *i2cdev, uint8_t reg)
+{
+    uint8_t resp[2];
+    i2c_read_block(i2cdev, reg, resp, sizeof(resp));
+    return (resp[0] << 8) | resp[1];
+}
+
+void i2c_set8(QI2CDevice *i2cdev, uint8_t reg, uint8_t value)
+{
+    i2c_write_block(i2cdev, reg, &value, 1);
+}
+
+void i2c_set16(QI2CDevice *i2cdev, uint8_t reg, uint16_t value)
+{
+    uint8_t data[2];
+
+    data[0] = value >> 8;
+    data[1] = value & 255;
+    i2c_write_block(i2cdev, reg, data, sizeof(data));
+}
+
+void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr)
+{
+    QI2CDevice *i2cdev = g_new0(QI2CDevice, 1);
+
+    i2cdev->bus = i2c_bus;
+    if (addr) {
+        i2cdev->addr = ((QI2CAddress *)addr)->addr;
+    }
+    return &i2cdev->obj;
+}
+
+void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr)
+{
+    g_assert(addr);
+
+    opts->arg = addr;
+    opts->size_arg = sizeof(QI2CAddress);
+}
diff --git a/tests/qtest/libqos/i2c.h b/tests/qtest/libqos/i2c.h
new file mode 100644 (file)
index 0000000..945b65b
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * I2C libqos
+ *
+ * Copyright (c) 2012 Andreas Färber
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#ifndef LIBQOS_I2C_H
+#define LIBQOS_I2C_H
+
+#include "libqtest.h"
+#include "libqos/qgraph.h"
+
+typedef struct I2CAdapter I2CAdapter;
+struct I2CAdapter {
+    void (*send)(I2CAdapter *adapter, uint8_t addr,
+                 const uint8_t *buf, uint16_t len);
+    void (*recv)(I2CAdapter *adapter, uint8_t addr,
+                 uint8_t *buf, uint16_t len);
+
+    QTestState *qts;
+};
+
+typedef struct QI2CAddress QI2CAddress;
+struct QI2CAddress {
+    uint8_t addr;
+};
+
+typedef struct QI2CDevice QI2CDevice;
+struct QI2CDevice {
+    /*
+     * For now, all devices are simple enough that there is no need for
+     * them to define their own constructor and get_driver functions.
+     * Therefore, QOSGraphObject is included directly in QI2CDevice;
+     * the tests expect to get a QI2CDevice rather than doing something
+     * like obj->get_driver("i2c-device").
+     *
+     * In fact there is no i2c-device interface even, because there are
+     * no generic I2C tests).
+     */
+    QOSGraphObject obj;
+    I2CAdapter *bus;
+    uint8_t addr;
+};
+
+void *i2c_device_create(void *i2c_bus, QGuestAllocator *alloc, void *addr);
+void add_qi2c_address(QOSGraphEdgeOptions *opts, QI2CAddress *addr);
+
+void i2c_send(QI2CDevice *dev, const uint8_t *buf, uint16_t len);
+void i2c_recv(QI2CDevice *dev, uint8_t *buf, uint16_t len);
+
+void i2c_read_block(QI2CDevice *dev, uint8_t reg,
+                    uint8_t *buf, uint16_t len);
+void i2c_write_block(QI2CDevice *dev, uint8_t reg,
+                     const uint8_t *buf, uint16_t len);
+uint8_t i2c_get8(QI2CDevice *dev, uint8_t reg);
+uint16_t i2c_get16(QI2CDevice *dev, uint8_t reg);
+void i2c_set8(QI2CDevice *dev, uint8_t reg, uint8_t value);
+void i2c_set16(QI2CDevice *dev, uint8_t reg, uint16_t value);
+
+/* i2c-omap.c */
+typedef struct OMAPI2C {
+    QOSGraphObject obj;
+    I2CAdapter parent;
+
+    uint64_t addr;
+} OMAPI2C;
+
+void omap_i2c_init(OMAPI2C *s, QTestState *qts, uint64_t addr);
+
+/* i2c-imx.c */
+typedef struct IMXI2C {
+    QOSGraphObject obj;
+    I2CAdapter parent;
+
+    uint64_t addr;
+} IMXI2C;
+
+void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr);
+
+#endif
diff --git a/tests/qtest/libqos/libqos-pc.c b/tests/qtest/libqos/libqos-pc.c
new file mode 100644 (file)
index 0000000..d04abc5
--- /dev/null
@@ -0,0 +1,35 @@
+#include "qemu/osdep.h"
+#include "libqos/libqos-pc.h"
+#include "libqos/malloc-pc.h"
+#include "libqos/pci-pc.h"
+
+static QOSOps qos_ops = {
+    .alloc_init = pc_alloc_init,
+    .qpci_new = qpci_new_pc,
+    .qpci_free = qpci_free_pc,
+    .shutdown = qtest_pc_shutdown,
+};
+
+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 *qs;
+    va_list ap;
+
+    va_start(ap, cmdline_fmt);
+    qs = qtest_vboot(&qos_ops, cmdline_fmt, ap);
+    va_end(ap);
+
+    qtest_irq_intercept_in(qs->qts, "ioapic");
+
+    return qs;
+}
+
+void qtest_pc_shutdown(QOSState *qs)
+{
+    return qtest_common_shutdown(qs);
+}
diff --git a/tests/qtest/libqos/libqos-pc.h b/tests/qtest/libqos/libqos-pc.h
new file mode 100644 (file)
index 0000000..a0e4c45
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef LIBQOS_PC_H
+#define LIBQOS_PC_H
+
+#include "libqos/libqos.h"
+
+QOSState *qtest_pc_vboot(const char *cmdline_fmt, va_list ap);
+QOSState *qtest_pc_boot(const char *cmdline_fmt, ...);
+void qtest_pc_shutdown(QOSState *qs);
+
+#endif
diff --git a/tests/qtest/libqos/libqos-spapr.c b/tests/qtest/libqos/libqos-spapr.c
new file mode 100644 (file)
index 0000000..8766d54
--- /dev/null
@@ -0,0 +1,33 @@
+#include "qemu/osdep.h"
+#include "libqos/libqos-spapr.h"
+#include "libqos/malloc-spapr.h"
+#include "libqos/pci-spapr.h"
+
+static QOSOps qos_ops = {
+    .alloc_init = spapr_alloc_init,
+    .qpci_new = qpci_new_spapr,
+    .qpci_free = qpci_free_spapr,
+    .shutdown = qtest_spapr_shutdown,
+};
+
+QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap)
+{
+    return qtest_vboot(&qos_ops, cmdline_fmt, ap);
+}
+
+QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...)
+{
+    QOSState *qs;
+    va_list ap;
+
+    va_start(ap, cmdline_fmt);
+    qs = qtest_vboot(&qos_ops, cmdline_fmt, ap);
+    va_end(ap);
+
+    return qs;
+}
+
+void qtest_spapr_shutdown(QOSState *qs)
+{
+    return qtest_common_shutdown(qs);
+}
diff --git a/tests/qtest/libqos/libqos-spapr.h b/tests/qtest/libqos/libqos-spapr.h
new file mode 100644 (file)
index 0000000..dcb5c43
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef LIBQOS_SPAPR_H
+#define LIBQOS_SPAPR_H
+
+#include "libqos/libqos.h"
+
+QOSState *qtest_spapr_vboot(const char *cmdline_fmt, va_list ap);
+QOSState *qtest_spapr_boot(const char *cmdline_fmt, ...);
+void qtest_spapr_shutdown(QOSState *qs);
+
+#endif
diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c
new file mode 100644 (file)
index 0000000..f229eb2
--- /dev/null
@@ -0,0 +1,240 @@
+#include "qemu/osdep.h"
+#include <sys/wait.h>
+
+#include "libqtest.h"
+#include "libqos/libqos.h"
+#include "libqos/pci.h"
+#include "qapi/qmp/qdict.h"
+
+/*** Test Setup & Teardown ***/
+
+/**
+ * Launch QEMU with the given command line,
+ * and then set up interrupts and our guest malloc interface.
+ * Never returns NULL:
+ * Terminates the application in case an error is encountered.
+ */
+QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap)
+{
+    char *cmdline;
+
+    QOSState *qs = g_new0(QOSState, 1);
+
+    cmdline = g_strdup_vprintf(cmdline_fmt, ap);
+    qs->qts = qtest_init(cmdline);
+    qs->ops = ops;
+    if (ops) {
+        ops->alloc_init(&qs->alloc, qs->qts, ALLOC_NO_FLAGS);
+        qs->pcibus = ops->qpci_new(qs->qts, &qs->alloc);
+    }
+
+    g_free(cmdline);
+    return qs;
+}
+
+/**
+ * Launch QEMU with the given command line,
+ * and then set up interrupts and our guest malloc interface.
+ */
+QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...)
+{
+    QOSState *qs;
+    va_list ap;
+
+    va_start(ap, cmdline_fmt);
+    qs = qtest_vboot(ops, cmdline_fmt, ap);
+    va_end(ap);
+
+    return qs;
+}
+
+/**
+ * Tear down the QEMU instance.
+ */
+void qtest_common_shutdown(QOSState *qs)
+{
+    if (qs->ops) {
+        if (qs->pcibus && qs->ops->qpci_free) {
+            qs->ops->qpci_free(qs->pcibus);
+            qs->pcibus = NULL;
+        }
+    }
+    alloc_destroy(&qs->alloc);
+    qtest_quit(qs->qts);
+    g_free(qs);
+}
+
+void qtest_shutdown(QOSState *qs)
+{
+    if (qs->ops && qs->ops->shutdown) {
+        qs->ops->shutdown(qs);
+    } else {
+        qtest_common_shutdown(qs);
+    }
+}
+
+static QDict *qmp_execute(QTestState *qts, const char *command)
+{
+    return qtest_qmp(qts, "{ 'execute': %s }", command);
+}
+
+void migrate(QOSState *from, QOSState *to, const char *uri)
+{
+    const char *st;
+    QDict *rsp, *sub;
+    bool running;
+
+    /* Is the machine currently running? */
+    rsp = qmp_execute(from->qts, "query-status");
+    g_assert(qdict_haskey(rsp, "return"));
+    sub = qdict_get_qdict(rsp, "return");
+    g_assert(qdict_haskey(sub, "running"));
+    running = qdict_get_bool(sub, "running");
+    qobject_unref(rsp);
+
+    /* Issue the migrate command. */
+    rsp = qtest_qmp(from->qts,
+                    "{ 'execute': 'migrate', 'arguments': { 'uri': %s }}",
+                    uri);
+    g_assert(qdict_haskey(rsp, "return"));
+    qobject_unref(rsp);
+
+    /* Wait for STOP event, but only if we were running: */
+    if (running) {
+        qtest_qmp_eventwait(from->qts, "STOP");
+    }
+
+    /* If we were running, we can wait for an event. */
+    if (running) {
+        migrate_allocator(&from->alloc, &to->alloc);
+        qtest_qmp_eventwait(to->qts, "RESUME");
+        return;
+    }
+
+    /* Otherwise, we need to wait: poll until migration is completed. */
+    while (1) {
+        rsp = qmp_execute(from->qts, "query-migrate");
+        g_assert(qdict_haskey(rsp, "return"));
+        sub = qdict_get_qdict(rsp, "return");
+        g_assert(qdict_haskey(sub, "status"));
+        st = qdict_get_str(sub, "status");
+
+        /* "setup", "active", "completed", "failed", "cancelled" */
+        if (strcmp(st, "completed") == 0) {
+            qobject_unref(rsp);
+            break;
+        }
+
+        if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0)
+            || (strcmp(st, "wait-unplug") == 0)) {
+            qobject_unref(rsp);
+            g_usleep(5000);
+            continue;
+        }
+
+        fprintf(stderr, "Migration did not complete, status: %s\n", st);
+        g_assert_not_reached();
+    }
+
+    migrate_allocator(&from->alloc, &to->alloc);
+}
+
+bool have_qemu_img(void)
+{
+    char *rpath;
+    const char *path = getenv("QTEST_QEMU_IMG");
+    if (!path) {
+        return false;
+    }
+
+    rpath = realpath(path, NULL);
+    if (!rpath) {
+        return false;
+    } else {
+        free(rpath);
+        return true;
+    }
+}
+
+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 *qemu_img_abs_path;
+
+    qemu_img_path = getenv("QTEST_QEMU_IMG");
+    g_assert(qemu_img_path);
+    qemu_img_abs_path = realpath(qemu_img_path, NULL);
+    g_assert(qemu_img_abs_path);
+
+    cli = g_strdup_printf("%s create -f %s %s %uM", qemu_img_abs_path,
+                          fmt, file, size_mb);
+    ret = g_spawn_command_line_sync(cli, &out, &out2, &rc, &err);
+    if (err || !g_spawn_check_exit_status(rc, &err)) {
+        fprintf(stderr, "%s\n", err->message);
+        g_error_free(err);
+    }
+    g_assert(ret && !err);
+
+    g_free(out);
+    g_free(out2);
+    g_free(cli);
+    free(qemu_img_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);
+}
+
+void generate_pattern(void *buffer, size_t len, size_t cycle_len)
+{
+    int i, j;
+    unsigned char *tx = (unsigned char *)buffer;
+    unsigned char p;
+    size_t *sx;
+
+    /* Write an indicative pattern that varies and is unique per-cycle */
+    p = rand() % 256;
+    for (i = 0; i < len; i++) {
+        tx[i] = p++ % 256;
+        if (i % cycle_len == 0) {
+            p = rand() % 256;
+        }
+    }
+
+    /* force uniqueness by writing an id per-cycle */
+    for (i = 0; i < len / cycle_len; i++) {
+        j = i * cycle_len;
+        if (j + sizeof(*sx) <= len) {
+            sx = (size_t *)&tx[j];
+            *sx = i;
+        }
+    }
+}
diff --git a/tests/qtest/libqos/libqos.h b/tests/qtest/libqos/libqos.h
new file mode 100644 (file)
index 0000000..8e971c2
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef LIBQOS_H
+#define LIBQOS_H
+
+#include "libqtest.h"
+#include "libqos/pci.h"
+#include "libqos/malloc.h"
+
+typedef struct QOSState QOSState;
+
+typedef struct QOSOps {
+    void (*alloc_init)(QGuestAllocator *, QTestState *, QAllocOpts);
+    QPCIBus *(*qpci_new)(QTestState *qts, QGuestAllocator *alloc);
+    void (*qpci_free)(QPCIBus *bus);
+    void (*shutdown)(QOSState *);
+} QOSOps;
+
+struct QOSState {
+    QTestState *qts;
+    QGuestAllocator alloc;
+    QPCIBus *pcibus;
+    QOSOps *ops;
+};
+
+QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap);
+QOSState *qtest_boot(QOSOps *ops, const char *cmdline_fmt, ...);
+void qtest_common_shutdown(QOSState *qs);
+void qtest_shutdown(QOSState *qs);
+bool have_qemu_img(void);
+void mkimg(const char *file, const char *fmt, unsigned size_mb);
+void mkqcow2(const char *file, unsigned size_mb);
+void migrate(QOSState *from, QOSState *to, const char *uri);
+void prepare_blkdebug_script(const char *debug_fn, const char *event);
+void generate_pattern(void *buffer, size_t len, size_t cycle_len);
+
+static inline uint64_t qmalloc(QOSState *q, size_t bytes)
+{
+    return guest_alloc(&q->alloc, bytes);
+}
+
+static inline void qfree(QOSState *q, uint64_t addr)
+{
+    guest_free(&q->alloc, addr);
+}
+
+#endif
diff --git a/tests/qtest/libqos/malloc-pc.c b/tests/qtest/libqos/malloc-pc.c
new file mode 100644 (file)
index 0000000..6f92ce4
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * libqos malloc support for PC
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqos/malloc-pc.h"
+#include "libqos/fw_cfg.h"
+
+#include "standard-headers/linux/qemu_fw_cfg.h"
+
+#include "qemu-common.h"
+
+#define PAGE_SIZE (4096)
+
+void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags)
+{
+    uint64_t ram_size;
+    QFWCFG *fw_cfg = pc_fw_cfg_init(qts);
+
+    ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE);
+    alloc_init(s, flags, 1 << 20, MIN(ram_size, 0xE0000000), PAGE_SIZE);
+
+    /* clean-up */
+    pc_fw_cfg_uninit(fw_cfg);
+}
diff --git a/tests/qtest/libqos/malloc-pc.h b/tests/qtest/libqos/malloc-pc.h
new file mode 100644 (file)
index 0000000..21e75ae
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * libqos malloc support for PC
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_MALLOC_PC_H
+#define LIBQOS_MALLOC_PC_H
+
+#include "libqos/malloc.h"
+
+void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags);
+
+#endif
diff --git a/tests/qtest/libqos/malloc-spapr.c b/tests/qtest/libqos/malloc-spapr.c
new file mode 100644 (file)
index 0000000..2a6b7e3
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * libqos malloc support for SPAPR
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqos/malloc-spapr.h"
+
+#include "qemu-common.h"
+
+#define PAGE_SIZE 4096
+
+/* Memory must be a multiple of 256 MB,
+ * so we have at least 256MB
+ */
+#define SPAPR_MIN_SIZE 0x10000000
+
+void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags)
+{
+    alloc_init(s, flags, 1 << 20, SPAPR_MIN_SIZE, PAGE_SIZE);
+}
diff --git a/tests/qtest/libqos/malloc-spapr.h b/tests/qtest/libqos/malloc-spapr.h
new file mode 100644 (file)
index 0000000..e5fe9bf
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * libqos malloc support for SPAPR
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_MALLOC_SPAPR_H
+#define LIBQOS_MALLOC_SPAPR_H
+
+#include "libqos/malloc.h"
+
+void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags);
+
+#endif
diff --git a/tests/qtest/libqos/malloc.c b/tests/qtest/libqos/malloc.c
new file mode 100644 (file)
index 0000000..615422a
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * libqos malloc support
+ *
+ * Copyright (c) 2014
+ *
+ * Author:
+ *  John Snow <jsnow@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqos/malloc.h"
+#include "qemu-common.h"
+#include "qemu/host-utils.h"
+
+typedef struct MemBlock {
+    QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME;
+    uint64_t size;
+    uint64_t addr;
+} MemBlock;
+
+#define DEFAULT_PAGE_SIZE 4096
+
+static void mlist_delete(MemList *list, MemBlock *node)
+{
+    g_assert(list && node);
+    QTAILQ_REMOVE(list, node, MLIST_ENTNAME);
+    g_free(node);
+}
+
+static MemBlock *mlist_find_key(MemList *head, uint64_t addr)
+{
+    MemBlock *node;
+    QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
+        if (node->addr == addr) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static MemBlock *mlist_find_space(MemList *head, uint64_t size)
+{
+    MemBlock *node;
+
+    QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
+        if (node->size >= size) {
+            return node;
+        }
+    }
+    return NULL;
+}
+
+static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr)
+{
+    MemBlock *node;
+    g_assert(head && insr);
+
+    QTAILQ_FOREACH(node, head, MLIST_ENTNAME) {
+        if (insr->addr < node->addr) {
+            QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME);
+            return insr;
+        }
+    }
+
+    QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME);
+    return insr;
+}
+
+static inline uint64_t mlist_boundary(MemBlock *node)
+{
+    return node->size + node->addr;
+}
+
+static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right)
+{
+    g_assert(head && left && right);
+
+    left->size += right->size;
+    mlist_delete(head, right);
+    return left;
+}
+
+static void mlist_coalesce(MemList *head, MemBlock *node)
+{
+    g_assert(node);
+    MemBlock *left;
+    MemBlock *right;
+    char merge;
+
+    do {
+        merge = 0;
+        left = QTAILQ_PREV(node, MLIST_ENTNAME);
+        right = QTAILQ_NEXT(node, MLIST_ENTNAME);
+
+        /* clowns to the left of me */
+        if (left && mlist_boundary(left) == node->addr) {
+            node = mlist_join(head, left, node);
+            merge = 1;
+        }
+
+        /* jokers to the right */
+        if (right && mlist_boundary(node) == right->addr) {
+            node = mlist_join(head, node, right);
+            merge = 1;
+        }
+
+    } while (merge);
+}
+
+static MemBlock *mlist_new(uint64_t addr, uint64_t size)
+{
+    MemBlock *block;
+
+    if (!size) {
+        return NULL;
+    }
+    block = g_new0(MemBlock, 1);
+
+    block->addr = addr;
+    block->size = size;
+
+    return block;
+}
+
+static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode,
+                                                                uint64_t size)
+{
+    uint64_t addr;
+    MemBlock *usednode;
+
+    g_assert(freenode);
+    g_assert_cmpint(freenode->size, >=, size);
+
+    addr = freenode->addr;
+    if (freenode->size == size) {
+        /* re-use this freenode as our used node */
+        QTAILQ_REMOVE(s->free, freenode, MLIST_ENTNAME);
+        usednode = freenode;
+    } else {
+        /* adjust the free node and create a new used node */
+        freenode->addr += size;
+        freenode->size -= size;
+        usednode = mlist_new(addr, size);
+    }
+
+    mlist_sort_insert(s->used, usednode);
+    return addr;
+}
+
+/* To assert the correctness of the list.
+ * Used only if ALLOC_PARANOID is set. */
+static void mlist_check(QGuestAllocator *s)
+{
+    MemBlock *node;
+    uint64_t addr = s->start > 0 ? s->start - 1 : 0;
+    uint64_t next = s->start;
+
+    QTAILQ_FOREACH(node, s->free, MLIST_ENTNAME) {
+        g_assert_cmpint(node->addr, >, addr);
+        g_assert_cmpint(node->addr, >=, next);
+        addr = node->addr;
+        next = node->addr + node->size;
+    }
+
+    addr = s->start > 0 ? s->start - 1 : 0;
+    next = s->start;
+    QTAILQ_FOREACH(node, s->used, MLIST_ENTNAME) {
+        g_assert_cmpint(node->addr, >, addr);
+        g_assert_cmpint(node->addr, >=, next);
+        addr = node->addr;
+        next = node->addr + node->size;
+    }
+}
+
+static uint64_t mlist_alloc(QGuestAllocator *s, uint64_t size)
+{
+    MemBlock *node;
+
+    node = mlist_find_space(s->free, size);
+    if (!node) {
+        fprintf(stderr, "Out of guest memory.\n");
+        g_assert_not_reached();
+    }
+    return mlist_fulfill(s, node, size);
+}
+
+static void mlist_free(QGuestAllocator *s, uint64_t addr)
+{
+    MemBlock *node;
+
+    if (addr == 0) {
+        return;
+    }
+
+    node = mlist_find_key(s->used, addr);
+    if (!node) {
+        fprintf(stderr, "Error: no record found for an allocation at "
+                "0x%016" PRIx64 ".\n",
+                addr);
+        g_assert_not_reached();
+    }
+
+    /* Rip it out of the used list and re-insert back into the free list. */
+    QTAILQ_REMOVE(s->used, node, MLIST_ENTNAME);
+    mlist_sort_insert(s->free, node);
+    mlist_coalesce(s->free, node);
+}
+
+/*
+ * Mostly for valgrind happiness, but it does offer
+ * a chokepoint for debugging guest memory leaks, too.
+ */
+void alloc_destroy(QGuestAllocator *allocator)
+{
+    MemBlock *node;
+    MemBlock *tmp;
+    QAllocOpts mask;
+
+    /* Check for guest leaks, and destroy the list. */
+    QTAILQ_FOREACH_SAFE(node, allocator->used, MLIST_ENTNAME, tmp) {
+        if (allocator->opts & (ALLOC_LEAK_WARN | ALLOC_LEAK_ASSERT)) {
+            fprintf(stderr, "guest malloc leak @ 0x%016" PRIx64 "; "
+                    "size 0x%016" PRIx64 ".\n",
+                    node->addr, node->size);
+        }
+        if (allocator->opts & (ALLOC_LEAK_ASSERT)) {
+            g_assert_not_reached();
+        }
+        g_free(node);
+    }
+
+    /* If we have previously asserted that there are no leaks, then there
+     * should be only one node here with a specific address and size. */
+    mask = ALLOC_LEAK_ASSERT | ALLOC_PARANOID;
+    QTAILQ_FOREACH_SAFE(node, allocator->free, MLIST_ENTNAME, tmp) {
+        if ((allocator->opts & mask) == mask) {
+            if ((node->addr != allocator->start) ||
+                (node->size != allocator->end - allocator->start)) {
+                fprintf(stderr, "Free list is corrupted.\n");
+                g_assert_not_reached();
+            }
+        }
+
+        g_free(node);
+    }
+
+    g_free(allocator->used);
+    g_free(allocator->free);
+}
+
+uint64_t guest_alloc(QGuestAllocator *allocator, size_t size)
+{
+    uint64_t rsize = size;
+    uint64_t naddr;
+
+    if (!size) {
+        return 0;
+    }
+
+    rsize += (allocator->page_size - 1);
+    rsize &= -allocator->page_size;
+    g_assert_cmpint((allocator->start + rsize), <=, allocator->end);
+    g_assert_cmpint(rsize, >=, size);
+
+    naddr = mlist_alloc(allocator, rsize);
+    if (allocator->opts & ALLOC_PARANOID) {
+        mlist_check(allocator);
+    }
+
+    return naddr;
+}
+
+void guest_free(QGuestAllocator *allocator, uint64_t addr)
+{
+    if (!addr) {
+        return;
+    }
+    mlist_free(allocator, addr);
+    if (allocator->opts & ALLOC_PARANOID) {
+        mlist_check(allocator);
+    }
+}
+
+void alloc_init(QGuestAllocator *s, QAllocOpts opts,
+                uint64_t start, uint64_t end,
+                size_t page_size)
+{
+    MemBlock *node;
+
+    s->opts = opts;
+    s->start = start;
+    s->end = end;
+
+    s->used = g_new(MemList, 1);
+    s->free = g_new(MemList, 1);
+    QTAILQ_INIT(s->used);
+    QTAILQ_INIT(s->free);
+
+    node = mlist_new(s->start, s->end - s->start);
+    QTAILQ_INSERT_HEAD(s->free, node, MLIST_ENTNAME);
+
+    s->page_size = page_size;
+}
+
+void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts)
+{
+    allocator->opts |= opts;
+}
+
+void migrate_allocator(QGuestAllocator *src,
+                       QGuestAllocator *dst)
+{
+    MemBlock *node, *tmp;
+    MemList *tmpused, *tmpfree;
+
+    /* The general memory layout should be equivalent,
+     * though opts can differ. */
+    g_assert_cmphex(src->start, ==, dst->start);
+    g_assert_cmphex(src->end, ==, dst->end);
+
+    /* Destroy (silently, regardless of options) the dest-list: */
+    QTAILQ_FOREACH_SAFE(node, dst->used, MLIST_ENTNAME, tmp) {
+        g_free(node);
+    }
+    QTAILQ_FOREACH_SAFE(node, dst->free, MLIST_ENTNAME, tmp) {
+        g_free(node);
+    }
+
+    tmpused = dst->used;
+    tmpfree = dst->free;
+
+    /* Inherit the lists of the source allocator: */
+    dst->used = src->used;
+    dst->free = src->free;
+
+    /* Source is now re-initialized, the source memory is 'invalid' now: */
+    src->used = tmpused;
+    src->free = tmpfree;
+    QTAILQ_INIT(src->used);
+    QTAILQ_INIT(src->free);
+    node = mlist_new(src->start, src->end - src->start);
+    QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME);
+    return;
+}
diff --git a/tests/qtest/libqos/malloc.h b/tests/qtest/libqos/malloc.h
new file mode 100644 (file)
index 0000000..4d1a2e2
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * libqos malloc support
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_MALLOC_H
+#define LIBQOS_MALLOC_H
+
+#include "qemu/queue.h"
+#include "libqtest.h"
+
+typedef enum {
+    ALLOC_NO_FLAGS    = 0x00,
+    ALLOC_LEAK_WARN   = 0x01,
+    ALLOC_LEAK_ASSERT = 0x02,
+    ALLOC_PARANOID    = 0x04
+} QAllocOpts;
+
+typedef QTAILQ_HEAD(MemList, MemBlock) MemList;
+
+typedef struct QGuestAllocator {
+    QAllocOpts opts;
+    uint64_t start;
+    uint64_t end;
+    uint32_t page_size;
+
+    MemList *used;
+    MemList *free;
+} QGuestAllocator;
+
+/* Always returns page aligned values */
+uint64_t guest_alloc(QGuestAllocator *allocator, size_t size);
+void guest_free(QGuestAllocator *allocator, uint64_t addr);
+void migrate_allocator(QGuestAllocator *src, QGuestAllocator *dst);
+
+void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts);
+
+void alloc_init(QGuestAllocator *alloc, QAllocOpts flags,
+                uint64_t start, uint64_t end,
+                size_t page_size);
+void alloc_destroy(QGuestAllocator *allocator);
+
+#endif
diff --git a/tests/qtest/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c
new file mode 100644 (file)
index 0000000..0bc591d
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * libqos PCI bindings for PC
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/pci-pc.h"
+#include "qapi/qmp/qdict.h"
+#include "hw/pci/pci_regs.h"
+
+#include "qemu/module.h"
+
+#define ACPI_PCIHP_ADDR         0xae00
+#define PCI_EJ_BASE             0x0008
+
+static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr)
+{
+    return qtest_inb(bus->qts, addr);
+}
+
+static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val)
+{
+    qtest_outb(bus->qts, addr, val);
+}
+
+static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr)
+{
+    return qtest_inw(bus->qts, addr);
+}
+
+static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val)
+{
+    qtest_outw(bus->qts, addr, val);
+}
+
+static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr)
+{
+    return qtest_inl(bus->qts, addr);
+}
+
+static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val)
+{
+    qtest_outl(bus->qts, addr, val);
+}
+
+static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr)
+{
+    return (uint64_t)qtest_inl(bus->qts, addr) +
+        ((uint64_t)qtest_inl(bus->qts, addr + 4) << 32);
+}
+
+static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val)
+{
+    qtest_outl(bus->qts, addr, val & 0xffffffff);
+    qtest_outl(bus->qts, addr + 4, val >> 32);
+}
+
+static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len)
+{
+    qtest_memread(bus->qts, addr, buf, len);
+}
+
+static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr,
+                             const void *buf, size_t len)
+{
+    qtest_memwrite(bus->qts, addr, buf, len);
+}
+
+static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
+    return qtest_inb(bus->qts, 0xcfc);
+}
+
+static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
+    return qtest_inw(bus->qts, 0xcfc);
+}
+
+static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
+    return qtest_inl(bus->qts, 0xcfc);
+}
+
+static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value)
+{
+    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
+    qtest_outb(bus->qts, 0xcfc, value);
+}
+
+static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value)
+{
+    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
+    qtest_outw(bus->qts, 0xcfc, value);
+}
+
+static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value)
+{
+    qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset);
+    qtest_outl(bus->qts, 0xcfc, value);
+}
+
+static void *qpci_pc_get_driver(void *obj, const char *interface)
+{
+    QPCIBusPC *qpci = obj;
+    if (!g_strcmp0(interface, "pci-bus")) {
+        return &qpci->bus;
+    }
+    fprintf(stderr, "%s not present in pci-bus-pc\n", interface);
+    g_assert_not_reached();
+}
+
+void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc)
+{
+    assert(qts);
+
+    /* tests can use pci-bus */
+    qpci->bus.has_buggy_msi = false;
+
+    qpci->bus.pio_readb = qpci_pc_pio_readb;
+    qpci->bus.pio_readw = qpci_pc_pio_readw;
+    qpci->bus.pio_readl = qpci_pc_pio_readl;
+    qpci->bus.pio_readq = qpci_pc_pio_readq;
+
+    qpci->bus.pio_writeb = qpci_pc_pio_writeb;
+    qpci->bus.pio_writew = qpci_pc_pio_writew;
+    qpci->bus.pio_writel = qpci_pc_pio_writel;
+    qpci->bus.pio_writeq = qpci_pc_pio_writeq;
+
+    qpci->bus.memread = qpci_pc_memread;
+    qpci->bus.memwrite = qpci_pc_memwrite;
+
+    qpci->bus.config_readb = qpci_pc_config_readb;
+    qpci->bus.config_readw = qpci_pc_config_readw;
+    qpci->bus.config_readl = qpci_pc_config_readl;
+
+    qpci->bus.config_writeb = qpci_pc_config_writeb;
+    qpci->bus.config_writew = qpci_pc_config_writew;
+    qpci->bus.config_writel = qpci_pc_config_writel;
+
+    qpci->bus.qts = qts;
+    qpci->bus.pio_alloc_ptr = 0xc000;
+    qpci->bus.mmio_alloc_ptr = 0xE0000000;
+    qpci->bus.mmio_limit = 0x100000000ULL;
+
+    qpci->obj.get_driver = qpci_pc_get_driver;
+}
+
+QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc)
+{
+    QPCIBusPC *qpci = g_new0(QPCIBusPC, 1);
+    qpci_init_pc(qpci, qts, alloc);
+
+    return &qpci->bus;
+}
+
+void qpci_free_pc(QPCIBus *bus)
+{
+    QPCIBusPC *s;
+
+    if (!bus) {
+        return;
+    }
+    s = container_of(bus, QPCIBusPC, bus);
+
+    g_free(s);
+}
+
+void qpci_unplug_acpi_device_test(QTestState *qts, const char *id, uint8_t slot)
+{
+    QDict *response;
+
+    response = qtest_qmp(qts, "{'execute': 'device_del',"
+                              " 'arguments': {'id': %s}}", id);
+    g_assert(response);
+    g_assert(!qdict_haskey(response, "error"));
+    qobject_unref(response);
+
+    qtest_outb(qts, ACPI_PCIHP_ADDR + PCI_EJ_BASE, 1 << slot);
+
+    qtest_qmp_eventwait(qts, "DEVICE_DELETED");
+}
+
+static void qpci_pc_register_nodes(void)
+{
+    qos_node_create_driver("pci-bus-pc", NULL);
+    qos_node_produces("pci-bus-pc", "pci-bus");
+}
+
+libqos_init(qpci_pc_register_nodes);
diff --git a/tests/qtest/libqos/pci-pc.h b/tests/qtest/libqos/pci-pc.h
new file mode 100644 (file)
index 0000000..4690005
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * libqos PCI bindings for PC
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_PCI_PC_H
+#define LIBQOS_PCI_PC_H
+
+#include "libqos/pci.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+
+typedef struct QPCIBusPC {
+    QOSGraphObject obj;
+    QPCIBus bus;
+} QPCIBusPC;
+
+/* qpci_init_pc():
+ * @ret: A valid QPCIBusPC * pointer
+ * @qts: The %QTestState for this PC machine
+ * @alloc: A previously initialized @alloc providing memory for @qts
+ *
+ * This function initializes an already allocated
+ * QPCIBusPC object.
+ */
+void qpci_init_pc(QPCIBusPC *ret, QTestState *qts, QGuestAllocator *alloc);
+
+/* qpci_pc_new():
+ * @qts: The %QTestState for this PC machine
+ * @alloc: A previously initialized @alloc providing memory for @qts
+ *
+ * This function creates a new QPCIBusPC object,
+ * and properly initialize its fields.
+ *
+ * Returns the QPCIBus *bus field of a newly
+ * allocated QPCIBusPC.
+ */
+QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc);
+
+void     qpci_free_pc(QPCIBus *bus);
+
+#endif
diff --git a/tests/qtest/libqos/pci-spapr.c b/tests/qtest/libqos/pci-spapr.c
new file mode 100644 (file)
index 0000000..d6f8c01
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * libqos PCI bindings for SPAPR
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/pci-spapr.h"
+#include "libqos/rtas.h"
+#include "libqos/qgraph.h"
+
+#include "hw/pci/pci_regs.h"
+
+#include "qemu/host-utils.h"
+#include "qemu/module.h"
+
+/*
+ * PCI devices are always little-endian
+ * SPAPR by default is big-endian
+ * so PCI accessors need to swap data endianness
+ */
+
+static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    return qtest_readb(bus->qts, s->pio_cpu_base + addr);
+}
+
+static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    qtest_writeb(bus->qts, s->pio_cpu_base + addr, val);
+}
+
+static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    return bswap16(qtest_readw(bus->qts, s->pio_cpu_base + addr));
+}
+
+static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    qtest_writew(bus->qts, s->pio_cpu_base + addr, bswap16(val));
+}
+
+static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    return bswap32(qtest_readl(bus->qts, s->pio_cpu_base + addr));
+}
+
+static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    qtest_writel(bus->qts, s->pio_cpu_base + addr, bswap32(val));
+}
+
+static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    return bswap64(qtest_readq(bus->qts, s->pio_cpu_base + addr));
+}
+
+static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    qtest_writeq(bus->qts, s->pio_cpu_base + addr, bswap64(val));
+}
+
+static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr,
+                               void *buf, size_t len)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    qtest_memread(bus->qts, s->mmio32_cpu_base + addr, buf, len);
+}
+
+static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr,
+                                const void *buf, size_t len)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    qtest_memwrite(bus->qts, s->mmio32_cpu_base + addr, buf, len);
+}
+
+static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    uint32_t config_addr = (devfn << 8) | offset;
+    return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid,
+                                     config_addr, 1);
+}
+
+static uint16_t qpci_spapr_config_readw(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    uint32_t config_addr = (devfn << 8) | offset;
+    return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid,
+                                     config_addr, 2);
+}
+
+static uint32_t qpci_spapr_config_readl(QPCIBus *bus, int devfn, uint8_t offset)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    uint32_t config_addr = (devfn << 8) | offset;
+    return qrtas_ibm_read_pci_config(bus->qts, s->alloc, s->buid,
+                                     config_addr, 4);
+}
+
+static void qpci_spapr_config_writeb(QPCIBus *bus, int devfn, uint8_t offset,
+                                     uint8_t value)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    uint32_t config_addr = (devfn << 8) | offset;
+    qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid,
+                               config_addr, 1, value);
+}
+
+static void qpci_spapr_config_writew(QPCIBus *bus, int devfn, uint8_t offset,
+                                     uint16_t value)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    uint32_t config_addr = (devfn << 8) | offset;
+    qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid,
+                               config_addr, 2, value);
+}
+
+static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset,
+                                     uint32_t value)
+{
+    QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus);
+    uint32_t config_addr = (devfn << 8) | offset;
+    qrtas_ibm_write_pci_config(bus->qts, s->alloc, s->buid,
+                               config_addr, 4, value);
+}
+
+#define SPAPR_PCI_BASE               (1ULL << 45)
+
+#define SPAPR_PCI_MMIO32_WIN_SIZE    0x80000000 /* 2 GiB */
+#define SPAPR_PCI_IO_WIN_SIZE        0x10000
+
+static void *qpci_spapr_get_driver(void *obj, const char *interface)
+{
+    QPCIBusSPAPR *qpci = obj;
+    if (!g_strcmp0(interface, "pci-bus")) {
+        return &qpci->bus;
+    }
+    fprintf(stderr, "%s not present in pci-bus-spapr", interface);
+    g_assert_not_reached();
+}
+
+void qpci_init_spapr(QPCIBusSPAPR *qpci, QTestState *qts,
+                     QGuestAllocator *alloc)
+{
+    assert(qts);
+
+    /* tests cannot use spapr, needs to be fixed first */
+    qpci->bus.has_buggy_msi = true;
+
+    qpci->alloc = alloc;
+
+    qpci->bus.pio_readb = qpci_spapr_pio_readb;
+    qpci->bus.pio_readw = qpci_spapr_pio_readw;
+    qpci->bus.pio_readl = qpci_spapr_pio_readl;
+    qpci->bus.pio_readq = qpci_spapr_pio_readq;
+
+    qpci->bus.pio_writeb = qpci_spapr_pio_writeb;
+    qpci->bus.pio_writew = qpci_spapr_pio_writew;
+    qpci->bus.pio_writel = qpci_spapr_pio_writel;
+    qpci->bus.pio_writeq = qpci_spapr_pio_writeq;
+
+    qpci->bus.memread = qpci_spapr_memread;
+    qpci->bus.memwrite = qpci_spapr_memwrite;
+
+    qpci->bus.config_readb = qpci_spapr_config_readb;
+    qpci->bus.config_readw = qpci_spapr_config_readw;
+    qpci->bus.config_readl = qpci_spapr_config_readl;
+
+    qpci->bus.config_writeb = qpci_spapr_config_writeb;
+    qpci->bus.config_writew = qpci_spapr_config_writew;
+    qpci->bus.config_writel = qpci_spapr_config_writel;
+
+    /* FIXME: We assume the default location of the PHB for now.
+     * Ideally we'd parse the device tree deposited in the guest to
+     * get the window locations */
+    qpci->buid = 0x800000020000000ULL;
+
+    qpci->pio_cpu_base = SPAPR_PCI_BASE;
+    qpci->pio.pci_base = 0;
+    qpci->pio.size = SPAPR_PCI_IO_WIN_SIZE;
+
+    /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */
+    qpci->mmio32_cpu_base = SPAPR_PCI_BASE;
+    qpci->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE;
+    qpci->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE;
+
+    qpci->bus.qts = qts;
+    qpci->bus.pio_alloc_ptr = 0xc000;
+    qpci->bus.mmio_alloc_ptr = qpci->mmio32.pci_base;
+    qpci->bus.mmio_limit = qpci->mmio32.pci_base + qpci->mmio32.size;
+
+    qpci->obj.get_driver = qpci_spapr_get_driver;
+}
+
+QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc)
+{
+    QPCIBusSPAPR *qpci = g_new0(QPCIBusSPAPR, 1);
+    qpci_init_spapr(qpci, qts, alloc);
+
+    return &qpci->bus;
+}
+
+void qpci_free_spapr(QPCIBus *bus)
+{
+    QPCIBusSPAPR *s;
+
+    if (!bus) {
+        return;
+    }
+    s = container_of(bus, QPCIBusSPAPR, bus);
+
+    g_free(s);
+}
+
+static void qpci_spapr_register_nodes(void)
+{
+    qos_node_create_driver("pci-bus-spapr", NULL);
+    qos_node_produces("pci-bus-spapr", "pci-bus");
+}
+
+libqos_init(qpci_spapr_register_nodes);
diff --git a/tests/qtest/libqos/pci-spapr.h b/tests/qtest/libqos/pci-spapr.h
new file mode 100644 (file)
index 0000000..d9e2563
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * libqos PCI bindings for SPAPR
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_PCI_SPAPR_H
+#define LIBQOS_PCI_SPAPR_H
+
+#include "libqos/malloc.h"
+#include "libqos/pci.h"
+#include "libqos/qgraph.h"
+
+/* From include/hw/pci-host/spapr.h */
+
+typedef struct QPCIWindow {
+    uint64_t pci_base;    /* window address in PCI space */
+    uint64_t size;        /* window size */
+} QPCIWindow;
+
+typedef struct QPCIBusSPAPR {
+    QOSGraphObject obj;
+    QPCIBus bus;
+    QGuestAllocator *alloc;
+
+    uint64_t buid;
+
+    uint64_t pio_cpu_base;
+    QPCIWindow pio;
+
+    uint64_t mmio32_cpu_base;
+    QPCIWindow mmio32;
+} QPCIBusSPAPR;
+
+void qpci_init_spapr(QPCIBusSPAPR *ret, QTestState *qts,
+                     QGuestAllocator *alloc);
+QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc);
+void     qpci_free_spapr(QPCIBus *bus);
+
+#endif
diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c
new file mode 100644 (file)
index 0000000..2309a72
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * libqos PCI bindings
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqos/pci.h"
+
+#include "hw/pci/pci_regs.h"
+#include "qemu/host-utils.h"
+#include "libqos/qgraph.h"
+
+void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
+                         void (*func)(QPCIDevice *dev, int devfn, void *data),
+                         void *data)
+{
+    int slot;
+
+    for (slot = 0; slot < 32; slot++) {
+        int fn;
+
+        for (fn = 0; fn < 8; fn++) {
+            QPCIDevice *dev;
+
+            dev = qpci_device_find(bus, QPCI_DEVFN(slot, fn));
+            if (!dev) {
+                continue;
+            }
+
+            if (vendor_id != -1 &&
+                qpci_config_readw(dev, PCI_VENDOR_ID) != vendor_id) {
+                g_free(dev);
+                continue;
+            }
+
+            if (device_id != -1 &&
+                qpci_config_readw(dev, PCI_DEVICE_ID) != device_id) {
+                g_free(dev);
+                continue;
+            }
+
+            func(dev, QPCI_DEVFN(slot, fn), data);
+        }
+    }
+}
+
+bool qpci_has_buggy_msi(QPCIDevice *dev)
+{
+    return dev->bus->has_buggy_msi;
+}
+
+bool qpci_check_buggy_msi(QPCIDevice *dev)
+{
+    if (qpci_has_buggy_msi(dev)) {
+        g_test_skip("Skipping due to incomplete support for MSI");
+        return true;
+    }
+    return false;
+}
+
+static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn)
+{
+    g_assert(dev);
+
+    dev->bus = bus;
+    dev->devfn = devfn;
+}
+
+QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
+{
+    QPCIDevice *dev;
+
+    dev = g_malloc0(sizeof(*dev));
+    qpci_device_set(dev, bus, devfn);
+
+    if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
+        g_free(dev);
+        return NULL;
+    }
+
+    return dev;
+}
+
+void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr)
+{
+    uint16_t vendor_id, device_id;
+
+    qpci_device_set(dev, bus, addr->devfn);
+    vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
+    device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
+    g_assert(!addr->vendor_id || vendor_id == addr->vendor_id);
+    g_assert(!addr->device_id || device_id == addr->device_id);
+}
+
+void qpci_device_enable(QPCIDevice *dev)
+{
+    uint16_t cmd;
+
+    /* FIXME -- does this need to be a bus callout? */
+    cmd = qpci_config_readw(dev, PCI_COMMAND);
+    cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+    qpci_config_writew(dev, PCI_COMMAND, cmd);
+
+    /* Verify the bits are now set. */
+    cmd = qpci_config_readw(dev, PCI_COMMAND);
+    g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO);
+    g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY);
+    g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER);
+}
+
+/**
+ * qpci_find_capability:
+ * @dev: the PCI device
+ * @id: the PCI Capability ID (PCI_CAP_ID_*)
+ * @start_addr: 0 to begin iteration or the last return value to continue
+ *              iteration
+ *
+ * Iterate over the PCI Capabilities List.
+ *
+ * Returns: PCI Configuration Space offset of the capabililty structure or
+ *          0 if no further matching capability is found
+ */
+uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id, uint8_t start_addr)
+{
+    uint8_t cap;
+    uint8_t addr;
+
+    if (start_addr) {
+        addr = qpci_config_readb(dev, start_addr + PCI_CAP_LIST_NEXT);
+    } else {
+        addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST);
+    }
+
+    do {
+        cap = qpci_config_readb(dev, addr);
+        if (cap != id) {
+            addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT);
+        }
+    } while (cap != id && addr != 0);
+
+    return addr;
+}
+
+void qpci_msix_enable(QPCIDevice *dev)
+{
+    uint8_t addr;
+    uint16_t val;
+    uint32_t table;
+    uint8_t bir_table;
+    uint8_t bir_pba;
+
+    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
+    g_assert_cmphex(addr, !=, 0);
+
+    val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+    qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE);
+
+    table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE);
+    bir_table = table & PCI_MSIX_FLAGS_BIRMASK;
+    dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL);
+    dev->msix_table_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
+
+    table = qpci_config_readl(dev, addr + PCI_MSIX_PBA);
+    bir_pba = table & PCI_MSIX_FLAGS_BIRMASK;
+    if (bir_pba != bir_table) {
+        dev->msix_pba_bar = qpci_iomap(dev, bir_pba, NULL);
+    } else {
+        dev->msix_pba_bar = dev->msix_table_bar;
+    }
+    dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
+
+    dev->msix_enabled = true;
+}
+
+void qpci_msix_disable(QPCIDevice *dev)
+{
+    uint8_t addr;
+    uint16_t val;
+
+    g_assert(dev->msix_enabled);
+    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
+    g_assert_cmphex(addr, !=, 0);
+    val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+    qpci_config_writew(dev, addr + PCI_MSIX_FLAGS,
+                                                val & ~PCI_MSIX_FLAGS_ENABLE);
+
+    if (dev->msix_pba_bar.addr != dev->msix_table_bar.addr) {
+        qpci_iounmap(dev, dev->msix_pba_bar);
+    }
+    qpci_iounmap(dev, dev->msix_table_bar);
+
+    dev->msix_enabled = 0;
+    dev->msix_table_off = 0;
+    dev->msix_pba_off = 0;
+}
+
+bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
+{
+    uint32_t pba_entry;
+    uint8_t bit_n = entry % 32;
+    uint64_t  off = (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4;
+
+    g_assert(dev->msix_enabled);
+    pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off);
+    qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off,
+                   pba_entry & ~(1 << bit_n));
+    return (pba_entry & (1 << bit_n)) != 0;
+}
+
+bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry)
+{
+    uint8_t addr;
+    uint16_t val;
+    uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE;
+
+    g_assert(dev->msix_enabled);
+    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
+    g_assert_cmphex(addr, !=, 0);
+    val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+
+    if (val & PCI_MSIX_FLAGS_MASKALL) {
+        return true;
+    } else {
+        return (qpci_io_readl(dev, dev->msix_table_bar,
+                              vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL)
+                & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0;
+    }
+}
+
+uint16_t qpci_msix_table_size(QPCIDevice *dev)
+{
+    uint8_t addr;
+    uint16_t control;
+
+    addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
+    g_assert_cmphex(addr, !=, 0);
+
+    control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
+    return (control & PCI_MSIX_FLAGS_QSIZE) + 1;
+}
+
+uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset)
+{
+    return dev->bus->config_readb(dev->bus, dev->devfn, offset);
+}
+
+uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset)
+{
+    return dev->bus->config_readw(dev->bus, dev->devfn, offset);
+}
+
+uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset)
+{
+    return dev->bus->config_readl(dev->bus, dev->devfn, offset);
+}
+
+
+void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value)
+{
+    dev->bus->config_writeb(dev->bus, dev->devfn, offset, value);
+}
+
+void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value)
+{
+    dev->bus->config_writew(dev->bus, dev->devfn, offset, value);
+}
+
+void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value)
+{
+    dev->bus->config_writel(dev->bus, dev->devfn, offset, value);
+}
+
+uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        return dev->bus->pio_readb(dev->bus, token.addr + off);
+    } else {
+        uint8_t val;
+        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+        return val;
+    }
+}
+
+uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        return dev->bus->pio_readw(dev->bus, token.addr + off);
+    } else {
+        uint16_t val;
+        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+        return le16_to_cpu(val);
+    }
+}
+
+uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        return dev->bus->pio_readl(dev->bus, token.addr + off);
+    } else {
+        uint32_t val;
+        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+        return le32_to_cpu(val);
+    }
+}
+
+uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        return dev->bus->pio_readq(dev->bus, token.addr + off);
+    } else {
+        uint64_t val;
+        dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
+        return le64_to_cpu(val);
+    }
+}
+
+void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint8_t value)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        dev->bus->pio_writeb(dev->bus, token.addr + off, value);
+    } else {
+        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+    }
+}
+
+void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint16_t value)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        dev->bus->pio_writew(dev->bus, token.addr + off, value);
+    } else {
+        value = cpu_to_le16(value);
+        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+    }
+}
+
+void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint32_t value)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        dev->bus->pio_writel(dev->bus, token.addr + off, value);
+    } else {
+        value = cpu_to_le32(value);
+        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+    }
+}
+
+void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint64_t value)
+{
+    if (token.addr < QPCI_PIO_LIMIT) {
+        dev->bus->pio_writeq(dev->bus, token.addr + off, value);
+    } else {
+        value = cpu_to_le64(value);
+        dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value));
+    }
+}
+
+void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                  void *buf, size_t len)
+{
+    g_assert(token.addr >= QPCI_PIO_LIMIT);
+    dev->bus->memread(dev->bus, token.addr + off, buf, len);
+}
+
+void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                   const void *buf, size_t len)
+{
+    g_assert(token.addr >= QPCI_PIO_LIMIT);
+    dev->bus->memwrite(dev->bus, token.addr + off, buf, len);
+}
+
+QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr)
+{
+    QPCIBus *bus = dev->bus;
+    static const int bar_reg_map[] = {
+        PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
+        PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
+    };
+    QPCIBar bar;
+    int bar_reg;
+    uint32_t addr, size;
+    uint32_t io_type;
+    uint64_t loc;
+
+    g_assert(barno >= 0 && barno <= 5);
+    bar_reg = bar_reg_map[barno];
+
+    qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
+    addr = qpci_config_readl(dev, bar_reg);
+
+    io_type = addr & PCI_BASE_ADDRESS_SPACE;
+    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
+        addr &= PCI_BASE_ADDRESS_IO_MASK;
+    } else {
+        addr &= PCI_BASE_ADDRESS_MEM_MASK;
+    }
+
+    g_assert(addr); /* Must have *some* size bits */
+
+    size = 1U << ctz32(addr);
+    if (sizeptr) {
+        *sizeptr = size;
+    }
+
+    if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
+        loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size);
+
+        g_assert(loc >= bus->pio_alloc_ptr);
+        g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */
+
+        bus->pio_alloc_ptr = loc + size;
+
+        qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
+    } else {
+        loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size);
+
+        /* Check for space */
+        g_assert(loc >= bus->mmio_alloc_ptr);
+        g_assert(loc + size <= bus->mmio_limit);
+
+        bus->mmio_alloc_ptr = loc + size;
+
+        qpci_config_writel(dev, bar_reg, loc);
+    }
+
+    bar.addr = loc;
+    return bar;
+}
+
+void qpci_iounmap(QPCIDevice *dev, QPCIBar bar)
+{
+    /* FIXME */
+}
+
+QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr)
+{
+    QPCIBar bar = { .addr = addr };
+    return bar;
+}
+
+void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr)
+{
+    g_assert(addr);
+    g_assert(opts);
+
+    opts->arg = addr;
+    opts->size_arg = sizeof(QPCIAddress);
+}
diff --git a/tests/qtest/libqos/pci.h b/tests/qtest/libqos/pci.h
new file mode 100644 (file)
index 0000000..590c175
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * libqos PCI bindings
+ *
+ * Copyright IBM, Corp. 2012-2013
+ *
+ * Authors:
+ *  Anthony Liguori   <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_PCI_H
+#define LIBQOS_PCI_H
+
+#include "libqtest.h"
+#include "libqos/qgraph.h"
+
+#define QPCI_PIO_LIMIT    0x10000
+
+#define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn))
+
+typedef struct QPCIDevice QPCIDevice;
+typedef struct QPCIBus QPCIBus;
+typedef struct QPCIBar QPCIBar;
+typedef struct QPCIAddress QPCIAddress;
+
+struct QPCIBus {
+    uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr);
+    uint16_t (*pio_readw)(QPCIBus *bus, uint32_t addr);
+    uint32_t (*pio_readl)(QPCIBus *bus, uint32_t addr);
+    uint64_t (*pio_readq)(QPCIBus *bus, uint32_t addr);
+
+    void (*pio_writeb)(QPCIBus *bus, uint32_t addr, uint8_t value);
+    void (*pio_writew)(QPCIBus *bus, uint32_t addr, uint16_t value);
+    void (*pio_writel)(QPCIBus *bus, uint32_t addr, uint32_t value);
+    void (*pio_writeq)(QPCIBus *bus, uint32_t addr, uint64_t value);
+
+    void (*memread)(QPCIBus *bus, uint32_t addr, void *buf, size_t len);
+    void (*memwrite)(QPCIBus *bus, uint32_t addr, const void *buf, size_t len);
+
+    uint8_t (*config_readb)(QPCIBus *bus, int devfn, uint8_t offset);
+    uint16_t (*config_readw)(QPCIBus *bus, int devfn, uint8_t offset);
+    uint32_t (*config_readl)(QPCIBus *bus, int devfn, uint8_t offset);
+
+    void (*config_writeb)(QPCIBus *bus, int devfn,
+                          uint8_t offset, uint8_t value);
+    void (*config_writew)(QPCIBus *bus, int devfn,
+                          uint8_t offset, uint16_t value);
+    void (*config_writel)(QPCIBus *bus, int devfn,
+                          uint8_t offset, uint32_t value);
+
+    QTestState *qts;
+    uint16_t pio_alloc_ptr;
+    uint64_t mmio_alloc_ptr, mmio_limit;
+    bool has_buggy_msi; /* TRUE for spapr, FALSE for pci */
+
+};
+
+struct QPCIBar {
+    uint64_t addr;
+};
+
+struct QPCIDevice
+{
+    QPCIBus *bus;
+    int devfn;
+    bool msix_enabled;
+    QPCIBar msix_table_bar, msix_pba_bar;
+    uint64_t msix_table_off, msix_pba_off;
+};
+
+struct QPCIAddress {
+    uint32_t devfn;
+    uint16_t vendor_id;
+    uint16_t device_id;
+};
+
+void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
+                         void (*func)(QPCIDevice *dev, int devfn, void *data),
+                         void *data);
+QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn);
+void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr);
+
+bool qpci_has_buggy_msi(QPCIDevice *dev);
+bool qpci_check_buggy_msi(QPCIDevice *dev);
+
+void qpci_device_enable(QPCIDevice *dev);
+uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id, uint8_t start_addr);
+void qpci_msix_enable(QPCIDevice *dev);
+void qpci_msix_disable(QPCIDevice *dev);
+bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry);
+bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry);
+uint16_t qpci_msix_table_size(QPCIDevice *dev);
+
+uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset);
+uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset);
+uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset);
+
+void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value);
+void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value);
+void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value);
+
+uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off);
+uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off);
+uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off);
+uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off);
+
+void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint8_t value);
+void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint16_t value);
+void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint32_t value);
+void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off,
+                    uint64_t value);
+
+void qpci_memread(QPCIDevice *bus, QPCIBar token, uint64_t off,
+                  void *buf, size_t len);
+void qpci_memwrite(QPCIDevice *bus, QPCIBar token, uint64_t off,
+                   const void *buf, size_t len);
+QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr);
+void qpci_iounmap(QPCIDevice *dev, QPCIBar addr);
+QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr);
+
+void qpci_unplug_acpi_device_test(QTestState *qs, const char *id, uint8_t slot);
+
+void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr);
+#endif
diff --git a/tests/qtest/libqos/ppc64_pseries-machine.c b/tests/qtest/libqos/ppc64_pseries-machine.c
new file mode 100644 (file)
index 0000000..867f27a
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/qgraph.h"
+#include "pci-spapr.h"
+#include "qemu/module.h"
+#include "libqos/malloc-spapr.h"
+
+typedef struct QSPAPR_pci_host QSPAPR_pci_host;
+typedef struct Qppc64_pseriesMachine Qppc64_pseriesMachine;
+
+struct QSPAPR_pci_host {
+    QOSGraphObject obj;
+    QPCIBusSPAPR pci;
+};
+
+struct Qppc64_pseriesMachine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    QSPAPR_pci_host bridge;
+};
+
+/* QSPAPR_pci_host */
+
+static QOSGraphObject *QSPAPR_host_get_device(void *obj, const char *device)
+{
+    QSPAPR_pci_host *host = obj;
+    if (!g_strcmp0(device, "pci-bus-spapr")) {
+        return &host->pci.obj;
+    }
+    fprintf(stderr, "%s not present in QSPAPR_pci_host\n", device);
+    g_assert_not_reached();
+}
+
+static void qos_create_QSPAPR_host(QSPAPR_pci_host *host,
+                                   QTestState *qts,
+                                   QGuestAllocator *alloc)
+{
+    host->obj.get_device = QSPAPR_host_get_device;
+    qpci_init_spapr(&host->pci, qts, alloc);
+}
+
+/* ppc64/pseries machine */
+
+static void spapr_destructor(QOSGraphObject *obj)
+{
+    Qppc64_pseriesMachine *machine = (Qppc64_pseriesMachine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *spapr_get_driver(void *object, const char *interface)
+{
+    Qppc64_pseriesMachine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in ppc64/pseries\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *spapr_get_device(void *obj, const char *device)
+{
+    Qppc64_pseriesMachine *machine = obj;
+    if (!g_strcmp0(device, "spapr-pci-host-bridge")) {
+        return &machine->bridge.obj;
+    }
+
+    fprintf(stderr, "%s not present in ppc64/pseries\n", device);
+    g_assert_not_reached();
+}
+
+static void *qos_create_machine_spapr(QTestState *qts)
+{
+    Qppc64_pseriesMachine *machine = g_new0(Qppc64_pseriesMachine, 1);
+    machine->obj.get_device = spapr_get_device;
+    machine->obj.get_driver = spapr_get_driver;
+    machine->obj.destructor = spapr_destructor;
+    spapr_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS);
+
+    qos_create_QSPAPR_host(&machine->bridge, qts, &machine->alloc);
+
+    return &machine->obj;
+}
+
+static void spapr_machine_register_nodes(void)
+{
+    qos_node_create_machine("ppc64/pseries", qos_create_machine_spapr);
+    qos_node_create_driver("spapr-pci-host-bridge", NULL);
+    qos_node_contains("ppc64/pseries", "spapr-pci-host-bridge", NULL);
+    qos_node_contains("spapr-pci-host-bridge", "pci-bus-spapr", NULL);
+}
+
+libqos_init(spapr_machine_register_nodes);
+
diff --git a/tests/qtest/libqos/qgraph.c b/tests/qtest/libqos/qgraph.c
new file mode 100644 (file)
index 0000000..7a7ae2a
--- /dev/null
@@ -0,0 +1,759 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/queue.h"
+#include "libqos/qgraph_internal.h"
+#include "libqos/qgraph.h"
+
+#define QGRAPH_PRINT_DEBUG 0
+#define QOS_ROOT ""
+typedef struct QOSStackElement QOSStackElement;
+
+/* Graph Edge.*/
+struct QOSGraphEdge {
+    QOSEdgeType type;
+    char *dest;
+    void *arg;                /* just for QEDGE_CONTAINS
+                               * and QEDGE_CONSUMED_BY */
+    char *extra_device_opts;  /* added to -device option, "," is
+                               * automatically added
+                               */
+    char *before_cmd_line;    /* added before node cmd_line */
+    char *after_cmd_line;     /* added after -device options */
+    char *edge_name;          /* used by QEDGE_CONTAINS */
+    QSLIST_ENTRY(QOSGraphEdge) edge_list;
+};
+
+typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList;
+
+/**
+ * Stack used to keep track of the discovered path when using
+ * the DFS algorithm
+ */
+struct QOSStackElement {
+    QOSGraphNode *node;
+    QOSStackElement *parent;
+    QOSGraphEdge *parent_edge;
+    int length;
+};
+
+/* Each enty in these hash table will consist of <string, node/edge> pair. */
+static GHashTable *edge_table;
+static GHashTable *node_table;
+
+/* stack used by the DFS algorithm to store the path from machine to test */
+static QOSStackElement qos_node_stack[QOS_PATH_MAX_ELEMENT_SIZE];
+static int qos_node_tos;
+
+/**
+ * add_edge(): creates an edge of type @type
+ * from @source to @dest node, and inserts it in the
+ * edges hash table
+ *
+ * Nodes @source and @dest do not necessarily need to exist.
+ * Possibility to add also options (see #QOSGraphEdgeOptions)
+ * edge->edge_name is used as identifier for get_device relationships,
+ * so by default is equal to @dest.
+ */
+static void add_edge(const char *source, const char *dest,
+                     QOSEdgeType type, QOSGraphEdgeOptions *opts)
+{
+    char *key;
+    QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source);
+    QOSGraphEdgeOptions def_opts = { };
+
+    if (!list) {
+        list = g_new0(QOSGraphEdgeList, 1);
+        key = g_strdup(source);
+        g_hash_table_insert(edge_table, key, list);
+    }
+
+    if (!opts) {
+        opts = &def_opts;
+    }
+
+    QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1);
+    edge->type = type;
+    edge->dest = g_strdup(dest);
+    edge->edge_name = g_strdup(opts->edge_name ?: dest);
+    edge->arg = g_memdup(opts->arg, opts->size_arg);
+
+    edge->before_cmd_line =
+        opts->before_cmd_line ? g_strconcat(" ", opts->before_cmd_line, NULL) : NULL;
+    edge->extra_device_opts =
+        opts->extra_device_opts ? g_strconcat(",", opts->extra_device_opts, NULL) : NULL;
+    edge->after_cmd_line =
+        opts->after_cmd_line ? g_strconcat(" ", opts->after_cmd_line, NULL) : NULL;
+
+    QSLIST_INSERT_HEAD(list, edge, edge_list);
+}
+
+/* destroy_edges(): frees all edges inside a given @list */
+static void destroy_edges(void *list)
+{
+    QOSGraphEdge *temp;
+    QOSGraphEdgeList *elist = list;
+
+    while (!QSLIST_EMPTY(elist)) {
+        temp = QSLIST_FIRST(elist);
+        QSLIST_REMOVE_HEAD(elist, edge_list);
+        g_free(temp->dest);
+        g_free(temp->before_cmd_line);
+        g_free(temp->after_cmd_line);
+        g_free(temp->extra_device_opts);
+        g_free(temp->edge_name);
+        g_free(temp->arg);
+        g_free(temp);
+    }
+    g_free(elist);
+}
+
+/**
+ * create_node(): creates a node @name of type @type
+ * and inserts it to the nodes hash table.
+ * By default, node is not available.
+ */
+static QOSGraphNode *create_node(const char *name, QOSNodeType type)
+{
+    if (g_hash_table_lookup(node_table, name)) {
+        g_printerr("Node %s already created\n", name);
+        abort();
+    }
+
+    QOSGraphNode *node = g_new0(QOSGraphNode, 1);
+    node->type = type;
+    node->available = false;
+    node->name = g_strdup(name);
+    g_hash_table_insert(node_table, node->name, node);
+    return node;
+}
+
+/**
+ * destroy_node(): frees a node @val from the nodes hash table.
+ * Note that node->name is not free'd since it will represent the
+ * hash table key
+ */
+static void destroy_node(void *val)
+{
+    QOSGraphNode *node = val;
+    g_free(node->command_line);
+    g_free(node);
+}
+
+/**
+ * destroy_string(): frees @key from the nodes hash table.
+ * Actually frees the node->name
+ */
+static void destroy_string(void *key)
+{
+    g_free(key);
+}
+
+/**
+ * search_node(): search for a node @key in the nodes hash table
+ * Returns the QOSGraphNode if found, #NULL otherwise
+ */
+static QOSGraphNode *search_node(const char *key)
+{
+    return g_hash_table_lookup(node_table, key);
+}
+
+/**
+ * get_edgelist(): returns the edge list (value) assigned to
+ * the @key in the edge hash table.
+ * This list will contain all edges with source equal to @key
+ *
+ * Returns: on success: the %QOSGraphEdgeList
+ *          otherwise: abort()
+ */
+static QOSGraphEdgeList *get_edgelist(const char *key)
+{
+    return g_hash_table_lookup(edge_table, key);
+}
+
+/**
+ * search_list_edges(): search for an edge with destination @dest
+ * in the given @edgelist.
+ *
+ * Returns: on success: the %QOSGraphEdge
+ *          otherwise: #NULL
+ */
+static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist,
+                                       const char *dest)
+{
+    QOSGraphEdge *tmp, *next;
+    if (!edgelist) {
+        return NULL;
+    }
+    QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) {
+        if (g_strcmp0(tmp->dest, dest) == 0) {
+            break;
+        }
+    }
+    return tmp;
+}
+
+/**
+ * search_machine(): search for a machine @name in the node hash
+ * table. A machine is the child of the root node.
+ * This function forces the research in the childs of the root,
+ * to check the node is a proper machine
+ *
+ * Returns: on success: the %QOSGraphNode
+ *          otherwise: #NULL
+ */
+static QOSGraphNode *search_machine(const char *name)
+{
+    QOSGraphNode *n;
+    QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT);
+    QOSGraphEdge *e = search_list_edges(root_list, name);
+    if (!e) {
+        return NULL;
+    }
+    n = search_node(e->dest);
+    if (n->type == QNODE_MACHINE) {
+        return n;
+    }
+    return NULL;
+}
+
+/**
+ * create_interface(): checks if there is already
+ * a node @node in the node hash table, if not
+ * creates a node @node of type #QNODE_INTERFACE
+ * and inserts it. If there is one, check it's
+ * a #QNODE_INTERFACE and abort() if it's not.
+ */
+static void create_interface(const char *node)
+{
+    QOSGraphNode *interface;
+    interface = search_node(node);
+    if (!interface) {
+        create_node(node, QNODE_INTERFACE);
+    } else if (interface->type != QNODE_INTERFACE) {
+        fprintf(stderr, "Error: Node %s is not an interface\n", node);
+        abort();
+    }
+}
+
+/**
+ * build_machine_cmd_line(): builds the command line for the machine
+ * @node. The node name must be a valid qemu identifier, since it
+ * will be used to build the command line.
+ *
+ * It is also possible to pass an optional @args that will be
+ * concatenated to the command line.
+ *
+ * For machines, prepend -M to the machine name. ", @rgs" is added
+ * after the -M <machine> command.
+ */
+static void build_machine_cmd_line(QOSGraphNode *node, const char *args)
+{
+    char *machine = qos_get_machine_type(node->name);
+    if (args) {
+        node->command_line = g_strconcat("-M ", machine, ",", args, NULL);
+    } else {
+        node->command_line = g_strconcat("-M ", machine, " ", NULL);
+    }
+}
+
+/**
+ * build_driver_cmd_line(): builds the command line for the driver
+ * @node. The node name must be a valid qemu identifier, since it
+ * will be used to build the command line.
+ *
+ * Driver do not need additional command line, since it will be
+ * provided by the edge options.
+ *
+ * For drivers, prepend -device to the node name.
+ */
+static void build_driver_cmd_line(QOSGraphNode *node)
+{
+    node->command_line = g_strconcat(" -device ", node->name, NULL);
+}
+
+/* qos_print_cb(): callback prints all path found by the DFS algorithm. */
+static void qos_print_cb(QOSGraphNode *path, int length)
+{
+    #if QGRAPH_PRINT_DEBUG
+        printf("%d elements\n", length);
+
+        if (!path) {
+            return;
+        }
+
+        while (path->path_edge) {
+            printf("%s ", path->name);
+            switch (path->path_edge->type) {
+            case QEDGE_PRODUCES:
+                printf("--PRODUCES--> ");
+                break;
+            case QEDGE_CONSUMED_BY:
+                printf("--CONSUMED_BY--> ");
+                break;
+            case QEDGE_CONTAINS:
+                printf("--CONTAINS--> ");
+                break;
+            }
+            path = search_node(path->path_edge->dest);
+        }
+
+        printf("%s\n\n", path->name);
+    #endif
+}
+
+/* qos_push(): push a node @el and edge @e in the qos_node_stack */
+static void qos_push(QOSGraphNode *el, QOSStackElement *parent,
+                     QOSGraphEdge *e)
+{
+    int len = 0; /* root is not counted */
+    if (qos_node_tos == QOS_PATH_MAX_ELEMENT_SIZE) {
+        g_printerr("QOSStack: full stack, cannot push");
+        abort();
+    }
+
+    if (parent) {
+        len = parent->length + 1;
+    }
+    qos_node_stack[qos_node_tos++] = (QOSStackElement) {
+        .node = el,
+        .parent = parent,
+        .parent_edge = e,
+        .length = len,
+    };
+}
+
+/* qos_tos(): returns the top of stack, without popping */
+static QOSStackElement *qos_tos(void)
+{
+    return &qos_node_stack[qos_node_tos - 1];
+}
+
+/* qos_pop(): pops an element from the tos, setting it unvisited*/
+static QOSStackElement *qos_pop(void)
+{
+    if (qos_node_tos == 0) {
+        g_printerr("QOSStack: empty stack, cannot pop");
+        abort();
+    }
+    QOSStackElement *e = qos_tos();
+    e->node->visited = false;
+    qos_node_tos--;
+    return e;
+}
+
+/**
+ * qos_reverse_path(): reverses the found path, going from
+ * test-to-machine to machine-to-test
+ */
+static QOSGraphNode *qos_reverse_path(QOSStackElement *el)
+{
+    if (!el) {
+        return NULL;
+    }
+
+    el->node->path_edge = NULL;
+
+    while (el->parent) {
+        el->parent->node->path_edge = el->parent_edge;
+        el = el->parent;
+    }
+
+    return el->node;
+}
+
+/**
+ * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it
+ * starts from the root @machine and walks all possible path until it
+ * reaches a test node.
+ * At that point, it reverses the path found and invokes the @callback.
+ *
+ * Being Depth First Search, time complexity is O(|V| + |E|), while
+ * space is O(|V|). In this case, the maximum stack size is set by
+ * QOS_PATH_MAX_ELEMENT_SIZE.
+ */
+static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback)
+{
+    QOSGraphNode *v, *dest_node, *path;
+    QOSStackElement *s_el;
+    QOSGraphEdge *e, *next;
+    QOSGraphEdgeList *list;
+
+    qos_push(root, NULL, NULL);
+
+    while (qos_node_tos > 0) {
+        s_el = qos_tos();
+        v = s_el->node;
+        if (v->visited) {
+            qos_pop();
+            continue;
+        }
+        v->visited = true;
+        list = get_edgelist(v->name);
+        if (!list) {
+            qos_pop();
+            if (v->type == QNODE_TEST) {
+                v->visited = false;
+                path = qos_reverse_path(s_el);
+                callback(path, s_el->length);
+            }
+        } else {
+            QSLIST_FOREACH_SAFE(e, list, edge_list, next) {
+                dest_node = search_node(e->dest);
+
+                if (!dest_node) {
+                    fprintf(stderr, "node %s in %s -> %s does not exist\n",
+                            e->dest, v->name, e->dest);
+                    abort();
+                }
+
+                if (!dest_node->visited && dest_node->available) {
+                    qos_push(dest_node, s_el, e);
+                }
+            }
+        }
+    }
+}
+
+/* QGRAPH API*/
+
+QOSGraphNode *qos_graph_get_node(const char *key)
+{
+    return search_node(key);
+}
+
+bool qos_graph_has_node(const char *node)
+{
+    QOSGraphNode *n = search_node(node);
+    return n != NULL;
+}
+
+QOSNodeType qos_graph_get_node_type(const char *node)
+{
+    QOSGraphNode *n = search_node(node);
+    if (n) {
+        return n->type;
+    }
+    return -1;
+}
+
+bool qos_graph_get_node_availability(const char *node)
+{
+    QOSGraphNode *n = search_node(node);
+    if (n) {
+        return n->available;
+    }
+    return false;
+}
+
+QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest)
+{
+    QOSGraphEdgeList *list = get_edgelist(node);
+    return search_list_edges(list, dest);
+}
+
+QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge)
+{
+    if (!edge) {
+        return -1;
+    }
+    return edge->type;;
+}
+
+char *qos_graph_edge_get_dest(QOSGraphEdge *edge)
+{
+    if (!edge) {
+        return NULL;
+    }
+    return edge->dest;
+}
+
+void *qos_graph_edge_get_arg(QOSGraphEdge *edge)
+{
+    if (!edge) {
+        return NULL;
+    }
+    return edge->arg;
+}
+
+char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge)
+{
+    if (!edge) {
+        return NULL;
+    }
+    return edge->after_cmd_line;
+}
+
+char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge)
+{
+    if (!edge) {
+        return NULL;
+    }
+    return edge->before_cmd_line;
+}
+
+char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge)
+{
+    if (!edge) {
+        return NULL;
+    }
+    return edge->extra_device_opts;
+}
+
+char *qos_graph_edge_get_name(QOSGraphEdge *edge)
+{
+    if (!edge) {
+        return NULL;
+    }
+    return edge->edge_name;
+}
+
+bool qos_graph_has_edge(const char *start, const char *dest)
+{
+    QOSGraphEdgeList *list = get_edgelist(start);
+    QOSGraphEdge *e = search_list_edges(list, dest);
+    return e != NULL;
+}
+
+QOSGraphNode *qos_graph_get_machine(const char *node)
+{
+    return search_machine(node);
+}
+
+bool qos_graph_has_machine(const char *node)
+{
+    QOSGraphNode *m = search_machine(node);
+    return m != NULL;
+}
+
+void qos_print_graph(void)
+{
+    qos_graph_foreach_test_path(qos_print_cb);
+}
+
+void qos_graph_init(void)
+{
+    if (!node_table) {
+        node_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                           destroy_string, destroy_node);
+        create_node(QOS_ROOT, QNODE_DRIVER);
+    }
+
+    if (!edge_table) {
+        edge_table = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                           destroy_string, destroy_edges);
+    }
+}
+
+void qos_graph_destroy(void)
+{
+    if (node_table) {
+        g_hash_table_destroy(node_table);
+    }
+
+    if (edge_table) {
+        g_hash_table_destroy(edge_table);
+    }
+
+    node_table = NULL;
+    edge_table = NULL;
+}
+
+void qos_node_destroy(void *key)
+{
+    g_hash_table_remove(node_table, key);
+}
+
+void qos_edge_destroy(void *key)
+{
+    g_hash_table_remove(edge_table, key);
+}
+
+void qos_add_test(const char *name, const char *interface,
+                  QOSTestFunc test_func, QOSGraphTestOptions *opts)
+{
+    QOSGraphNode *node;
+    char *test_name = g_strdup_printf("%s-tests/%s", interface, name);;
+    QOSGraphTestOptions def_opts = { };
+
+    if (!opts) {
+        opts = &def_opts;
+    }
+    node = create_node(test_name, QNODE_TEST);
+    node->u.test.function = test_func;
+    node->u.test.arg = opts->arg;
+    assert(!opts->edge.arg);
+    assert(!opts->edge.size_arg);
+
+    node->u.test.before = opts->before;
+    node->u.test.subprocess = opts->subprocess;
+    node->available = true;
+    add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge);
+    g_free(test_name);
+}
+
+void qos_node_create_machine(const char *name, QOSCreateMachineFunc function)
+{
+    qos_node_create_machine_args(name, function, NULL);
+}
+
+void qos_node_create_machine_args(const char *name,
+                                  QOSCreateMachineFunc function,
+                                  const char *opts)
+{
+    QOSGraphNode *node = create_node(name, QNODE_MACHINE);
+    build_machine_cmd_line(node, opts);
+    node->u.machine.constructor = function;
+    add_edge(QOS_ROOT, name, QEDGE_CONTAINS, NULL);
+}
+
+void qos_node_create_driver(const char *name, QOSCreateDriverFunc function)
+{
+    QOSGraphNode *node = create_node(name, QNODE_DRIVER);
+    build_driver_cmd_line(node);
+    node->u.driver.constructor = function;
+}
+
+void qos_node_contains(const char *container, const char *contained,
+                       QOSGraphEdgeOptions *opts, ...)
+{
+    va_list va;
+
+    if (opts == NULL) {
+        add_edge(container, contained, QEDGE_CONTAINS, NULL);
+        return;
+    }
+
+    va_start(va, opts);
+    do {
+        add_edge(container, contained, QEDGE_CONTAINS, opts);
+        opts = va_arg(va, QOSGraphEdgeOptions *);
+    } while (opts != NULL);
+
+    va_end(va);
+}
+
+void qos_node_produces(const char *producer, const char *interface)
+{
+    create_interface(interface);
+    add_edge(producer, interface, QEDGE_PRODUCES, NULL);
+}
+
+void qos_node_consumes(const char *consumer, const char *interface,
+                       QOSGraphEdgeOptions *opts)
+{
+    create_interface(interface);
+    add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts);
+}
+
+void qos_graph_node_set_availability(const char *node, bool av)
+{
+    QOSGraphEdgeList *elist;
+    QOSGraphNode *n = search_node(node);
+    QOSGraphEdge *e, *next;
+    if (!n) {
+        return;
+    }
+    n->available = av;
+    elist = get_edgelist(node);
+    if (!elist) {
+        return;
+    }
+    QSLIST_FOREACH_SAFE(e, elist, edge_list, next) {
+        if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) {
+            qos_graph_node_set_availability(e->dest, av);
+        }
+    }
+}
+
+void qos_graph_foreach_test_path(QOSTestCallback fn)
+{
+    QOSGraphNode *root = qos_graph_get_node(QOS_ROOT);
+    qos_traverse_graph(root, fn);
+}
+
+QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts)
+{
+    QOSGraphObject *obj;
+
+    g_assert(node->type == QNODE_MACHINE);
+    obj = node->u.machine.constructor(qts);
+    obj->free = g_free;
+    return obj;
+}
+
+QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
+                               QGuestAllocator *alloc, void *arg)
+{
+    QOSGraphObject *obj;
+
+    g_assert(node->type == QNODE_DRIVER);
+    obj = node->u.driver.constructor(parent, alloc, arg);
+    obj->free = g_free;
+    return obj;
+}
+
+void qos_object_destroy(QOSGraphObject *obj)
+{
+    if (!obj) {
+        return;
+    }
+    if (obj->destructor) {
+        obj->destructor(obj);
+    }
+    if (obj->free) {
+        obj->free(obj);
+    }
+}
+
+void qos_object_queue_destroy(QOSGraphObject *obj)
+{
+    g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj);
+}
+
+void qos_object_start_hw(QOSGraphObject *obj)
+{
+    if (obj->start_hw) {
+        obj->start_hw(obj);
+    }
+}
+
+char *qos_get_machine_type(char *name)
+{
+    while (*name != '\0' && *name != '/') {
+        name++;
+    }
+
+    if (!*name || !name[1]) {
+        fprintf(stderr, "Machine name has to be of the form <arch>/<machine>\n");
+        abort();
+    }
+
+    return name + 1;
+}
+
+void qos_delete_cmd_line(const char *name)
+{
+    QOSGraphNode *node = search_node(name);
+    if (node) {
+        g_free(node->command_line);
+        node->command_line = NULL;
+    }
+}
diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h
new file mode 100644 (file)
index 0000000..3a25dda
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef QGRAPH_H
+#define QGRAPH_H
+
+#include <gmodule.h>
+#include "qemu/module.h"
+#include "malloc.h"
+
+/* maximum path length */
+#define QOS_PATH_MAX_ELEMENT_SIZE 50
+
+typedef struct QOSGraphObject QOSGraphObject;
+typedef struct QOSGraphNode QOSGraphNode;
+typedef struct QOSGraphEdge QOSGraphEdge;
+typedef struct QOSGraphNodeOptions QOSGraphNodeOptions;
+typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions;
+typedef struct QOSGraphTestOptions QOSGraphTestOptions;
+
+/* Constructor for drivers, machines and test */
+typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc,
+                                      void *addr);
+typedef void *(*QOSCreateMachineFunc) (QTestState *qts);
+typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc);
+
+/* QOSGraphObject functions */
+typedef void *(*QOSGetDriver) (void *object, const char *interface);
+typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name);
+typedef void (*QOSDestructorFunc) (QOSGraphObject *object);
+typedef void (*QOSStartFunct) (QOSGraphObject *object);
+
+/* Test options functions */
+typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg);
+
+/**
+ * SECTION: qgraph.h
+ * @title: Qtest Driver Framework
+ * @short_description: interfaces to organize drivers and tests
+ *                     as nodes in a graph
+ *
+ * This Qgraph API provides all basic functions to create a graph
+ * and instantiate nodes representing machines, drivers and tests
+ * representing their relations with CONSUMES, PRODUCES, and CONTAINS
+ * edges.
+ *
+ * The idea is to have a framework where each test asks for a specific
+ * driver, and the framework takes care of allocating the proper devices
+ * required and passing the correct command line arguments to QEMU.
+ *
+ * A node can be of four types:
+ * - QNODE_MACHINE:   for example "arm/raspi2"
+ * - QNODE_DRIVER:    for example "generic-sdhci"
+ * - QNODE_INTERFACE: for example "sdhci" (interface for all "-sdhci" drivers)
+ *                     an interface is not explicitly created, it will be auto-
+ *                     matically instantiated when a node consumes or produces
+ *                     it.
+ * - QNODE_TEST:      for example "sdhci-test", consumes an interface and tests
+ *                    the functions provided
+ *
+ * Notes for the nodes:
+ * - QNODE_MACHINE: each machine struct must have a QGuestAllocator and
+ *                  implement get_driver to return the allocator passing
+ *                  "memory". The function can also return NULL if the
+ *                  allocator is not set.
+ * - QNODE_DRIVER:  driver names must be unique, and machines and nodes
+ *                  planned to be "consumed" by other nodes must match QEMU
+ *                  drivers name, otherwise they won't be discovered
+ *
+ * An edge relation between two nodes (drivers or machines) X and Y can be:
+ * - X CONSUMES Y: Y can be plugged into X
+ * - X PRODUCES Y: X provides the interface Y
+ * - X CONTAINS Y: Y is part of X component
+ *
+ * Basic framework steps are the following:
+ * - All nodes and edges are created in their respective
+ *   machine/driver/test files
+ * - The framework starts QEMU and asks for a list of available devices
+ *   and machines (note that only machines and "consumed" nodes are mapped
+ *   1:1 with QEMU devices)
+ * - The framework walks the graph starting from the available machines and
+ *   performs a Depth First Search for tests
+ * - Once a test is found, the path is walked again and all drivers are
+ *   allocated accordingly and the final interface is passed to the test
+ * - The test is executed
+ * - Unused objects are cleaned and the path discovery is continued
+ *
+ * Depending on the QEMU binary used, only some drivers/machines will be
+ * available and only test that are reached by them will be executed.
+ *
+ * <example>
+ *   <title>Creating new driver an its interface</title>
+ *   <programlisting>
+ #include "libqos/qgraph.h"
+
+ struct My_driver {
+     QOSGraphObject obj;
+     Node_produced prod;
+     Node_contained cont;
+ }
+
+ static void my_destructor(QOSGraphObject *obj)
+ {
+    g_free(obj);
+ }
+
+ static void my_get_driver(void *object, const char *interface) {
+    My_driver *dev = object;
+    if (!g_strcmp0(interface, "my_interface")) {
+        return &dev->prod;
+    }
+    abort();
+ }
+
+ static void my_get_device(void *object, const char *device) {
+    My_driver *dev = object;
+    if (!g_strcmp0(device, "my_driver_contained")) {
+        return &dev->cont;
+    }
+    abort();
+ }
+
+ static void *my_driver_constructor(void *node_consumed,
+                                    QOSGraphObject *alloc)
+ {
+    My_driver dev = g_new(My_driver, 1);
+    // get the node pointed by the produce edge
+    dev->obj.get_driver = my_get_driver;
+    // get the node pointed by the contains
+    dev->obj.get_device = my_get_device;
+    // free the object
+    dev->obj.destructor = my_destructor;
+    do_something_with_node_consumed(node_consumed);
+    // set all fields of contained device
+    init_contained_device(&dev->cont);
+    return &dev->obj;
+ }
+
+ static void register_my_driver(void)
+ {
+     qos_node_create_driver("my_driver", my_driver_constructor);
+     // contained drivers don't need a constructor,
+     // they will be init by the parent.
+     qos_node_create_driver("my_driver_contained", NULL);
+
+    // For the sake of this example, assume machine x86_64/pc contains
+    // "other_node".
+    // This relation, along with the machine and "other_node" creation,
+    // should be defined in the x86_64_pc-machine.c file.
+    // "my_driver" will then consume "other_node"
+    qos_node_contains("my_driver", "my_driver_contained");
+    qos_node_produces("my_driver", "my_interface");
+    qos_node_consumes("my_driver", "other_node");
+ }
+ *   </programlisting>
+ * </example>
+ *
+ * In the above example, all possible types of relations are created:
+ * node "my_driver" consumes, contains and produces other nodes.
+ * more specifically:
+ * x86_64/pc -->contains--> other_node <--consumes-- my_driver
+ *                                                       |
+ *                      my_driver_contained <--contains--+
+ *                                                       |
+ *                             my_interface <--produces--+
+ *
+ * or inverting the consumes edge in consumed_by:
+ *
+ * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
+ *                                                           |
+ *                          my_driver_contained <--contains--+
+ *                                                           |
+ *                                 my_interface <--produces--+
+ *
+ * <example>
+ *   <title>Creating new test</title>
+ *   <programlisting>
+ * #include "libqos/qgraph.h"
+ *
+ * static void my_test_function(void *obj, void *data)
+ * {
+ *    Node_produced *interface_to_test = obj;
+ *    // test interface_to_test
+ * }
+ *
+ * static void register_my_test(void)
+ * {
+ *    qos_add_test("my_interface", "my_test", my_test_function);
+ * }
+ *
+ * libqos_init(register_my_test);
+ *
+ *   </programlisting>
+ * </example>
+ *
+ * Here a new test is created, consuming "my_interface" node
+ * and creating a valid path from a machine to a test.
+ * Final graph will be like this:
+ * x86_64/pc -->contains--> other_node <--consumes-- my_driver
+ *                                                        |
+ *                       my_driver_contained <--contains--+
+ *                                                        |
+ *        my_test --consumes--> my_interface <--produces--+
+ *
+ * or inverting the consumes edge in consumed_by:
+ *
+ * x86_64/pc -->contains--> other_node --consumed_by--> my_driver
+ *                                                           |
+ *                          my_driver_contained <--contains--+
+ *                                                           |
+ *        my_test <--consumed_by-- my_interface <--produces--+
+ *
+ * Assuming there the binary is
+ * QTEST_QEMU_BINARY=x86_64-softmmu/qemu-system-x86_64
+ * a valid test path will be:
+ * "/x86_64/pc/other_node/my_driver/my_interface/my_test".
+ *
+ * Additional examples are also in libqos/test-qgraph.c
+ *
+ * Command line:
+ * Command line is built by using node names and optional arguments
+ * passed by the user when building the edges.
+ *
+ * There are three types of command line arguments:
+ * - in node      : created from the node name. For example, machines will
+ *                  have "-M <machine>" to its command line, while devices
+ *                  "-device <device>". It is automatically done by the
+ *                   framework.
+ * - after node   : added as additional argument to the node name.
+ *                  This argument is added optionally when creating edges,
+ *                  by setting the parameter @after_cmd_line and
+ *                  @extra_edge_opts in #QOSGraphEdgeOptions.
+ *                  The framework automatically adds
+ *                  a comma before @extra_edge_opts,
+ *                  because it is going to add attributes
+ *                  after the destination node pointed by
+ *                  the edge containing these options, and automatically
+ *                  adds a space before @after_cmd_line, because it
+ *                  adds an additional device, not an attribute.
+ * - before node  : added as additional argument to the node name.
+ *                  This argument is added optionally when creating edges,
+ *                  by setting the parameter @before_cmd_line in
+ *                  #QOSGraphEdgeOptions. This attribute
+ *                  is going to add attributes before the destination node
+ *                  pointed by the edge containing these options. It is
+ *                  helpful to commands that are not node-representable,
+ *                  such as "-fdsev" or "-netdev".
+ *
+ * While adding command line in edges is always used, not all nodes names are
+ * used in every path walk: this is because the contained or produced ones
+ * are already added by QEMU, so only nodes that "consumes" will be used to
+ * build the command line. Also, nodes that will have { "abstract" : true }
+ * as QMP attribute will loose their command line, since they are not proper
+ * devices to be added in QEMU.
+ *
+ * Example:
+ *
+ QOSGraphEdgeOptions opts = {
+     .arg = NULL,
+     .size_arg = 0,
+     .after_cmd_line = "-device other",
+     .before_cmd_line = "-netdev something",
+     .extra_edge_opts = "addr=04.0",
+ };
+ QOSGraphNode * node = qos_node_create_driver("my_node", constructor);
+ qos_node_consumes_args("my_node", "interface", &opts);
+ *
+ * Will produce the following command line:
+ * "-netdev something -device my_node,addr=04.0 -device other"
+ */
+
+/**
+ * Edge options to be passed to the contains/consumes *_args function.
+ */
+struct QOSGraphEdgeOptions {
+    void *arg;                    /*
+                                   * optional arg that will be used by
+                                   * dest edge
+                                   */
+    uint32_t size_arg;            /*
+                                   * optional arg size that will be used by
+                                   * dest edge
+                                   */
+    const char *extra_device_opts;/*
+                                   *optional additional command line for dest
+                                   * edge, used to add additional attributes
+                                   * *after* the node command line, the
+                                   * framework automatically prepends ","
+                                   * to this argument.
+                                   */
+    const char *before_cmd_line;  /*
+                                   * optional additional command line for dest
+                                   * edge, used to add additional attributes
+                                   * *before* the node command line, usually
+                                   * other non-node represented commands,
+                                   * like "-fdsev synt"
+                                   */
+    const char *after_cmd_line;   /*
+                                   * optional extra command line to be added
+                                   * after the device command. This option
+                                   * is used to add other devices
+                                   * command line that depend on current node.
+                                   * Automatically prepends " " to this
+                                   * argument
+                                   */
+    const char *edge_name;        /*
+                                   * optional edge to differentiate multiple
+                                   * devices with same node name
+                                   */
+};
+
+/**
+ * Test options to be passed to the test functions.
+ */
+struct QOSGraphTestOptions {
+    QOSGraphEdgeOptions edge;   /* edge arguments that will be used by test.
+                                 * Note that test *does not* use edge_name,
+                                 * and uses instead arg and size_arg as
+                                 * data arg for its test function.
+                                 */
+    void *arg;                  /* passed to the .before function, or to the
+                                 * test function if there is no .before
+                                 * function
+                                 */
+    QOSBeforeTest before;       /* executed before the test. Can add
+                                 * additional parameters to the command line
+                                 * and modify the argument to the test function.
+                                 */
+    bool subprocess;            /* run the test in a subprocess */
+};
+
+/**
+ * Each driver, test or machine of this framework will have a
+ * QOSGraphObject as first field.
+ *
+ * This set of functions offered by QOSGraphObject are executed
+ * in different stages of the framework:
+ * - get_driver / get_device : Once a machine-to-test path has been
+ * found, the framework traverses it again and allocates all the
+ * nodes, using the provided constructor. To satisfy their relations,
+ * i.e. for produces or contains, where a struct constructor needs
+ * an external parameter represented by the previous node,
+ * the framework will call get_device (for contains) or
+ * get_driver (for produces), depending on the edge type, passing
+ * them the name of the next node to be taken and getting from them
+ * the corresponding pointer to the actual structure of the next node to
+ * be used in the path.
+ *
+ * - start_hw: This function is executed after all the path objects
+ * have been allocated, but before the test is run. It starts the hw, setting
+ * the initial configurations (*_device_enable) and making it ready for the
+ * test.
+ *
+ * - destructor: Opposite to the node constructor, destroys the object.
+ * This function is called after the test has been executed, and performs
+ * a complete cleanup of each node allocated field. In case no constructor
+ * is provided, no destructor will be called.
+ *
+ */
+struct QOSGraphObject {
+    /* for produces edges, returns void * */
+    QOSGetDriver get_driver;
+    /* for contains edges, returns a QOSGraphObject * */
+    QOSGetDevice get_device;
+    /* start the hw, get ready for the test */
+    QOSStartFunct start_hw;
+    /* destroy this QOSGraphObject */
+    QOSDestructorFunc destructor;
+    /* free the memory associated to the QOSGraphObject and its contained
+     * children */
+    GDestroyNotify free;
+};
+
+/**
+ * qos_graph_init(): initialize the framework, creates two hash
+ * tables: one for the nodes and another for the edges.
+ */
+void qos_graph_init(void);
+
+/**
+ * qos_graph_destroy(): deallocates all the hash tables,
+ * freeing all nodes and edges.
+ */
+void qos_graph_destroy(void);
+
+/**
+ * qos_node_destroy(): removes and frees a node from the,
+ * nodes hash table.
+ */
+void qos_node_destroy(void *key);
+
+/**
+ * qos_edge_destroy(): removes and frees an edge from the,
+ * edges hash table.
+ */
+void qos_edge_destroy(void *key);
+
+/**
+ * qos_add_test(): adds a test node @name to the nodes hash table.
+ *
+ * The test will consume a @interface node, and once the
+ * graph walking algorithm has found it, the @test_func will be
+ * executed. It also has the possibility to
+ * add an optional @opts (see %QOSGraphNodeOptions).
+ *
+ * For tests, opts->edge.arg and size_arg represent the arg to pass
+ * to @test_func
+ */
+void qos_add_test(const char *name, const char *interface,
+                  QOSTestFunc test_func,
+                  QOSGraphTestOptions *opts);
+
+/**
+ * qos_node_create_machine(): creates the machine @name and
+ * adds it to the node hash table.
+ *
+ * This node will be of type QNODE_MACHINE and have @function
+ * as constructor
+ */
+void qos_node_create_machine(const char *name, QOSCreateMachineFunc function);
+
+/**
+ * qos_node_create_machine_args(): same as qos_node_create_machine,
+ * but with the possibility to add an optional ", @opts" after -M machine
+ * command line.
+ */
+void qos_node_create_machine_args(const char *name,
+                                  QOSCreateMachineFunc function,
+                                  const char *opts);
+
+/**
+ * qos_node_create_driver(): creates the driver @name and
+ * adds it to the node hash table.
+ *
+ * This node will be of type QNODE_DRIVER and have @function
+ * as constructor
+ */
+void qos_node_create_driver(const char *name, QOSCreateDriverFunc function);
+
+/**
+ * qos_node_contains(): creates one or more edges of type QEDGE_CONTAINS
+ * and adds them to the edge list mapped to @container in the
+ * edge hash table.
+ *
+ * The edges will have @container as source and @contained as destination.
+ *
+ * If @opts is NULL, a single edge will be added with no options.
+ * If @opts is non-NULL, the arguments after @contained represent a
+ * NULL-terminated list of %QOSGraphEdgeOptions structs, and an
+ * edge will be added for each of them.
+ *
+ * This function can be useful when there are multiple devices
+ * with the same node name contained in a machine/other node
+ *
+ * For example, if "arm/raspi2" contains 2 "generic-sdhci"
+ * devices, the right commands will be:
+ * qos_node_create_machine("arm/raspi2");
+ * qos_node_create_driver("generic-sdhci", constructor);
+ * //assume rest of the fields are set NULL
+ * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" };
+ * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" };
+ * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL);
+ *
+ * Of course this also requires that the @container's get_device function
+ * should implement a case for "emmc" and "sdcard".
+ *
+ * For contains, op1.arg and op1.size_arg represent the arg to pass
+ * to @contained constructor to properly initialize it.
+ */
+void qos_node_contains(const char *container, const char *contained,
+                       QOSGraphEdgeOptions *opts, ...);
+
+/**
+ * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and
+ * adds it to the edge list mapped to @producer in the
+ * edge hash table.
+ *
+ * This edge will have @producer as source and @interface as destination.
+ */
+void qos_node_produces(const char *producer, const char *interface);
+
+/**
+ * qos_node_consumes():  creates an edge of type QEDGE_CONSUMED_BY and
+ * adds it to the edge list mapped to @interface in the
+ * edge hash table.
+ *
+ * This edge will have @interface as source and @consumer as destination.
+ * It also has the possibility to add an optional @opts
+ * (see %QOSGraphEdgeOptions)
+ */
+void qos_node_consumes(const char *consumer, const char *interface,
+                       QOSGraphEdgeOptions *opts);
+
+/**
+ * qos_invalidate_command_line(): invalidates current command line, so that
+ * qgraph framework cannot try to cache the current command line and
+ * forces QEMU to restart.
+ */
+void qos_invalidate_command_line(void);
+
+/**
+ * qos_get_current_command_line(): return the command line required by the
+ * machine and driver objects.  This is the same string that was passed to
+ * the test's "before" callback, if any.
+ */
+const char *qos_get_current_command_line(void);
+
+/**
+ * qos_allocate_objects():
+ * @qts: The #QTestState that will be referred to by the machine object.
+ * @alloc: Where to store the allocator for the machine object, or %NULL.
+ *
+ * Allocate driver objects for the current test
+ * path, but relative to the QTestState @qts.
+ *
+ * Returns a test object just like the one that was passed to
+ * the test function, but relative to @qts.
+ */
+void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc);
+
+/**
+ * qos_object_destroy(): calls the destructor for @obj
+ */
+void qos_object_destroy(QOSGraphObject *obj);
+
+/**
+ * qos_object_queue_destroy(): queue the destructor for @obj so that it is
+ * called at the end of the test
+ */
+void qos_object_queue_destroy(QOSGraphObject *obj);
+
+/**
+ * qos_object_start_hw(): calls the start_hw function for @obj
+ */
+void qos_object_start_hw(QOSGraphObject *obj);
+
+/**
+ * qos_machine_new(): instantiate a new machine node
+ * @node: A machine node to be instantiated
+ * @qts: The #QTestState that will be referred to by the machine object.
+ *
+ * Returns a machine object.
+ */
+QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts);
+
+/**
+ * qos_machine_new(): instantiate a new driver node
+ * @node: A driver node to be instantiated
+ * @parent: A #QOSGraphObject to be consumed by the new driver node
+ * @alloc: An allocator to be used by the new driver node.
+ * @arg: The argument for the consumed-by edge to @node.
+ *
+ * Calls the constructor for the driver object.
+ */
+QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent,
+                               QGuestAllocator *alloc, void *arg);
+
+
+#endif
diff --git a/tests/qtest/libqos/qgraph_internal.h b/tests/qtest/libqos/qgraph_internal.h
new file mode 100644 (file)
index 0000000..f4734c8
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef QGRAPH_INTERNAL_H
+#define QGRAPH_INTERNAL_H
+
+/* This header is declaring additional helper functions defined in
+ * libqos/qgraph.c
+ * It should not be included in tests
+ */
+
+#include "libqos/qgraph.h"
+
+typedef struct QOSGraphMachine QOSGraphMachine;
+typedef enum QOSEdgeType QOSEdgeType;
+typedef enum QOSNodeType QOSNodeType;
+
+/* callback called when the walk path algorithm found a
+ * valid path
+ */
+typedef void (*QOSTestCallback) (QOSGraphNode *path, int len);
+
+/* edge types*/
+enum QOSEdgeType {
+    QEDGE_CONTAINS,
+    QEDGE_PRODUCES,
+    QEDGE_CONSUMED_BY
+};
+
+/* node types*/
+enum QOSNodeType {
+    QNODE_MACHINE,
+    QNODE_DRIVER,
+    QNODE_INTERFACE,
+    QNODE_TEST
+};
+
+/* Graph Node */
+struct QOSGraphNode {
+    QOSNodeType type;
+    bool available;     /* set by QEMU via QMP, used during graph walk */
+    bool visited;       /* used during graph walk */
+    char *name;         /* used to identify the node */
+    char *command_line; /* used to start QEMU at test execution */
+    union {
+        struct {
+            QOSCreateDriverFunc constructor;
+        } driver;
+        struct {
+            QOSCreateMachineFunc constructor;
+        } machine;
+        struct {
+            QOSTestFunc function;
+            void *arg;
+            QOSBeforeTest before;
+            bool subprocess;
+        } test;
+    } u;
+
+    /**
+     * only used when traversing the path, never rely on that except in the
+     * qos_traverse_graph callback function
+     */
+    QOSGraphEdge *path_edge;
+};
+
+/**
+ * qos_graph_get_node(): returns the node mapped to that @key.
+ * It performs an hash map search O(1)
+ *
+ * Returns: on success: the %QOSGraphNode
+ *          otherwise: #NULL
+ */
+QOSGraphNode *qos_graph_get_node(const char *key);
+
+/**
+ * qos_graph_has_node(): returns #TRUE if the node
+ * has map has a node mapped to that @key.
+ */
+bool qos_graph_has_node(const char *node);
+
+/**
+ * qos_graph_get_node_type(): returns the %QOSNodeType
+ * of the node @node.
+ * It performs an hash map search O(1)
+ * Returns: on success: the %QOSNodeType
+ *          otherwise: #-1
+ */
+QOSNodeType qos_graph_get_node_type(const char *node);
+
+/**
+ * qos_graph_get_node_availability(): returns the availability (boolean)
+ * of the node @node.
+ */
+bool qos_graph_get_node_availability(const char *node);
+
+/**
+ * qos_graph_get_edge(): returns the edge
+ * linking of the node @node with @dest.
+ *
+ * Returns: on success: the %QOSGraphEdge
+ *          otherwise: #NULL
+ */
+QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest);
+
+/**
+ * qos_graph_edge_get_type(): returns the edge type
+ * of the edge @edge.
+ *
+ * Returns: on success: the %QOSEdgeType
+ *          otherwise: #-1
+ */
+QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge);
+
+/**
+ * qos_graph_edge_get_dest(): returns the name of the node
+ * pointed as destination of edge @edge.
+ *
+ * Returns: on success: the destination
+ *          otherwise: #NULL
+ */
+char *qos_graph_edge_get_dest(QOSGraphEdge *edge);
+
+/**
+ * qos_graph_has_edge(): returns #TRUE if there
+ * exists an edge from @start to @dest.
+ */
+bool qos_graph_has_edge(const char *start, const char *dest);
+
+/**
+ * qos_graph_edge_get_arg(): returns the args assigned
+ * to that @edge.
+ *
+ * Returns: on success: the arg
+ *          otherwise: #NULL
+ */
+void *qos_graph_edge_get_arg(QOSGraphEdge *edge);
+
+/**
+ * qos_graph_edge_get_after_cmd_line(): returns the edge
+ * command line that will be added after all the node arguments
+ * and all the before_cmd_line arguments.
+ *
+ * Returns: on success: the char* arg
+ *          otherwise: #NULL
+ */
+char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge);
+
+/**
+ * qos_graph_edge_get_before_cmd_line(): returns the edge
+ * command line that will be added before the node command
+ * line argument.
+ *
+ * Returns: on success: the char* arg
+ *          otherwise: #NULL
+ */
+char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge);
+
+/**
+ * qos_graph_edge_get_extra_device_opts(): returns the arg
+ * command line that will be added to the node command
+ * line argument.
+ *
+ * Returns: on success: the char* arg
+ *          otherwise: #NULL
+ */
+char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge);
+
+/**
+ * qos_graph_edge_get_name(): returns the name
+ * assigned to the destination node (different only)
+ * if there are multiple devices with the same node name
+ * e.g. a node has two "generic-sdhci", "emmc" and "sdcard"
+ * there will be two edges with edge_name ="emmc" and "sdcard"
+ *
+ * Returns always the char* edge_name
+ */
+char *qos_graph_edge_get_name(QOSGraphEdge *edge);
+
+/**
+ * qos_graph_get_machine(): returns the machine assigned
+ * to that @node name.
+ *
+ * It performs a search only trough the list of machines
+ * (i.e. the QOS_ROOT child).
+ *
+ * Returns: on success: the %QOSGraphNode
+ *          otherwise: #NULL
+ */
+QOSGraphNode *qos_graph_get_machine(const char *node);
+
+/**
+ * qos_graph_has_machine(): returns #TRUE if the node
+ * has map has a node mapped to that @node.
+ */
+bool qos_graph_has_machine(const char *node);
+
+
+/**
+ * qos_print_graph(): walks the graph and prints
+ * all machine-to-test paths.
+ */
+void qos_print_graph(void);
+
+/**
+ * qos_graph_foreach_test_path(): executes the Depth First search
+ * algorithm and applies @fn to all discovered paths.
+ *
+ * See qos_traverse_graph() in qgraph.c for more info on
+ * how it works.
+ */
+void qos_graph_foreach_test_path(QOSTestCallback fn);
+
+/**
+ * qos_get_machine_type(): return QEMU machine type for a machine node.
+ * This function requires every machine @name to be in the form
+ * <arch>/<machine_name>, like "arm/raspi2" or "x86_64/pc".
+ *
+ * The function will validate the format and return a pointer to
+ * @machine to <machine_name>.  For example, when passed "x86_64/pc"
+ * it will return "pc".
+ *
+ * Note that this function *does not* allocate any new string.
+ */
+char *qos_get_machine_type(char *name);
+
+/**
+ * qos_delete_cmd_line(): delete the
+ * command line present in node mapped with key @name.
+ *
+ * This function is called when the QMP query returns a node with
+ * { "abstract" : true } attribute.
+ */
+void qos_delete_cmd_line(const char *name);
+
+/**
+ * qos_graph_node_set_availability(): sets the node identified
+ * by @node with availability @av.
+ */
+void qos_graph_node_set_availability(const char *node, bool av);
+
+#endif
diff --git a/tests/qtest/libqos/rtas.c b/tests/qtest/libqos/rtas.c
new file mode 100644 (file)
index 0000000..d81ff42
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/rtas.h"
+
+static void qrtas_copy_args(QTestState *qts, uint64_t target_args,
+                            uint32_t nargs, uint32_t *args)
+{
+    int i;
+
+    for (i = 0; i < nargs; i++) {
+        qtest_writel(qts, target_args + i * sizeof(uint32_t), args[i]);
+    }
+}
+
+static void qrtas_copy_ret(QTestState *qts, uint64_t target_ret,
+                           uint32_t nret, uint32_t *ret)
+{
+    int i;
+
+    for (i = 0; i < nret; i++) {
+        ret[i] = qtest_readl(qts, target_ret + i * sizeof(uint32_t));
+    }
+}
+
+static uint64_t qrtas_call(QTestState *qts, QGuestAllocator *alloc,
+                           const char *name,
+                           uint32_t nargs, uint32_t *args,
+                           uint32_t nret, uint32_t *ret)
+{
+    uint64_t res;
+    uint64_t target_args, target_ret;
+
+    target_args = guest_alloc(alloc, nargs * sizeof(uint32_t));
+    target_ret = guest_alloc(alloc, nret * sizeof(uint32_t));
+
+    qrtas_copy_args(qts, target_args, nargs, args);
+    res = qtest_rtas_call(qts, name, nargs, target_args, nret, target_ret);
+    qrtas_copy_ret(qts, target_ret, nret, ret);
+
+    guest_free(alloc, target_ret);
+    guest_free(alloc, target_args);
+
+    return res;
+}
+
+int qrtas_get_time_of_day(QTestState *qts, QGuestAllocator *alloc,
+                          struct tm *tm, uint32_t *ns)
+{
+    int res;
+    uint32_t ret[8];
+
+    res = qrtas_call(qts, alloc, "get-time-of-day", 0, NULL, 8, ret);
+    if (res != 0) {
+        return res;
+    }
+
+    res = ret[0];
+    memset(tm, 0, sizeof(*tm));
+    tm->tm_year = ret[1] - 1900;
+    tm->tm_mon = ret[2] - 1;
+    tm->tm_mday = ret[3];
+    tm->tm_hour = ret[4];
+    tm->tm_min = ret[5];
+    tm->tm_sec = ret[6];
+    *ns = ret[7];
+
+    return res;
+}
+
+uint32_t qrtas_ibm_read_pci_config(QTestState *qts, QGuestAllocator *alloc,
+                                   uint64_t buid,
+                                   uint32_t addr, uint32_t size)
+{
+    int res;
+    uint32_t args[4], ret[2];
+
+    args[0] = addr;
+    args[1] = buid >> 32;
+    args[2] = buid & 0xffffffff;
+    args[3] = size;
+    res = qrtas_call(qts, alloc, "ibm,read-pci-config", 4, args, 2, ret);
+    if (res != 0) {
+        return -1;
+    }
+
+    if (ret[0] != 0) {
+        return -1;
+    }
+
+    return ret[1];
+}
+
+int qrtas_ibm_write_pci_config(QTestState *qts, QGuestAllocator *alloc,
+                               uint64_t buid,
+                               uint32_t addr, uint32_t size, uint32_t val)
+{
+    int res;
+    uint32_t args[5], ret[1];
+
+    args[0] = addr;
+    args[1] = buid >> 32;
+    args[2] = buid & 0xffffffff;
+    args[3] = size;
+    args[4] = val;
+    res = qrtas_call(qts, alloc, "ibm,write-pci-config", 5, args, 1, ret);
+    if (res != 0) {
+        return -1;
+    }
+
+    if (ret[0] != 0) {
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/tests/qtest/libqos/rtas.h b/tests/qtest/libqos/rtas.h
new file mode 100644 (file)
index 0000000..459e23a
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_RTAS_H
+#define LIBQOS_RTAS_H
+#include "libqos/malloc.h"
+
+int qrtas_get_time_of_day(QTestState *qts, QGuestAllocator *alloc,
+                          struct tm *tm, uint32_t *ns);
+uint32_t qrtas_ibm_read_pci_config(QTestState *qts, QGuestAllocator *alloc,
+                                   uint64_t buid, uint32_t addr, uint32_t size);
+int qrtas_ibm_write_pci_config(QTestState *qts, QGuestAllocator *alloc,
+                               uint64_t buid, uint32_t addr, uint32_t size,
+                               uint32_t val);
+#endif /* LIBQOS_RTAS_H */
diff --git a/tests/qtest/libqos/sdhci.c b/tests/qtest/libqos/sdhci.c
new file mode 100644 (file)
index 0000000..309794b
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/qgraph.h"
+#include "pci.h"
+#include "qemu/module.h"
+#include "sdhci.h"
+#include "hw/pci/pci.h"
+
+static void set_qsdhci_fields(QSDHCI *s, uint8_t version, uint8_t baseclock,
+                              bool sdma, uint64_t reg)
+{
+    s->props.version = version;
+    s->props.baseclock = baseclock;
+    s->props.capab.sdma = sdma;
+    s->props.capab.reg = reg;
+}
+
+/* Memory mapped implementation of QSDHCI */
+
+static uint16_t sdhci_mm_readw(QSDHCI *s, uint32_t reg)
+{
+    QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
+    return qtest_readw(smm->qts, smm->addr + reg);
+}
+
+static uint64_t sdhci_mm_readq(QSDHCI *s, uint32_t reg)
+{
+    QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
+    return qtest_readq(smm->qts, smm->addr + reg);
+}
+
+static void sdhci_mm_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
+{
+    QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci);
+    qtest_writeq(smm->qts, smm->addr + reg, val);
+}
+
+static void *sdhci_mm_get_driver(void *obj, const char *interface)
+{
+    QSDHCI_MemoryMapped *smm = obj;
+    if (!g_strcmp0(interface, "sdhci")) {
+        return &smm->sdhci;
+    }
+    fprintf(stderr, "%s not present in generic-sdhci\n", interface);
+    g_assert_not_reached();
+}
+
+void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts,
+                       uint32_t addr, QSDHCIProperties *common)
+{
+    sdhci->obj.get_driver = sdhci_mm_get_driver;
+    sdhci->sdhci.readw = sdhci_mm_readw;
+    sdhci->sdhci.readq = sdhci_mm_readq;
+    sdhci->sdhci.writeq = sdhci_mm_writeq;
+    memcpy(&sdhci->sdhci.props, common, sizeof(QSDHCIProperties));
+    sdhci->addr = addr;
+    sdhci->qts = qts;
+}
+
+/* PCI implementation of QSDHCI */
+
+static uint16_t sdhci_pci_readw(QSDHCI *s, uint32_t reg)
+{
+    QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
+    return qpci_io_readw(&spci->dev, spci->mem_bar, reg);
+}
+
+static uint64_t sdhci_pci_readq(QSDHCI *s, uint32_t reg)
+{
+    QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
+    return qpci_io_readq(&spci->dev, spci->mem_bar, reg);
+}
+
+static void sdhci_pci_writeq(QSDHCI *s, uint32_t reg, uint64_t val)
+{
+    QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci);
+    return qpci_io_writeq(&spci->dev, spci->mem_bar, reg, val);
+}
+
+static void *sdhci_pci_get_driver(void *object, const char *interface)
+{
+    QSDHCI_PCI *spci = object;
+    if (!g_strcmp0(interface, "sdhci")) {
+        return &spci->sdhci;
+    }
+
+    fprintf(stderr, "%s not present in sdhci-pci\n", interface);
+    g_assert_not_reached();
+}
+
+static void sdhci_pci_start_hw(QOSGraphObject *obj)
+{
+    QSDHCI_PCI *spci = (QSDHCI_PCI *)obj;
+    qpci_device_enable(&spci->dev);
+}
+
+static void sdhci_destructor(QOSGraphObject *obj)
+{
+    QSDHCI_PCI *spci = (QSDHCI_PCI *)obj;
+    qpci_iounmap(&spci->dev, spci->mem_bar);
+}
+
+static void *sdhci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
+{
+    QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1);
+    QPCIBus *bus = pci_bus;
+    uint64_t barsize;
+
+    qpci_device_init(&spci->dev, bus, addr);
+    spci->mem_bar = qpci_iomap(&spci->dev, 0, &barsize);
+    spci->sdhci.readw = sdhci_pci_readw;
+    spci->sdhci.readq = sdhci_pci_readq;
+    spci->sdhci.writeq = sdhci_pci_writeq;
+    set_qsdhci_fields(&spci->sdhci, 2, 0, 1, 0x057834b4);
+
+    spci->obj.get_driver = sdhci_pci_get_driver;
+    spci->obj.start_hw = sdhci_pci_start_hw;
+    spci->obj.destructor = sdhci_destructor;
+    return &spci->obj;
+}
+
+static void qsdhci_register_nodes(void)
+{
+    QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(4, 0),
+        .vendor_id = PCI_VENDOR_ID_REDHAT,
+        .device_id = PCI_DEVICE_ID_REDHAT_SDHCI,
+    };
+
+    QOSGraphEdgeOptions opts = {
+        .extra_device_opts = "addr=04.0",
+    };
+
+    /* generic-sdhci */
+    qos_node_create_driver("generic-sdhci", NULL);
+    qos_node_produces("generic-sdhci", "sdhci");
+
+    /* sdhci-pci */
+    add_qpci_address(&opts, &addr);
+    qos_node_create_driver("sdhci-pci", sdhci_pci_create);
+    qos_node_produces("sdhci-pci", "sdhci");
+    qos_node_consumes("sdhci-pci", "pci-bus", &opts);
+
+}
+
+libqos_init(qsdhci_register_nodes);
diff --git a/tests/qtest/libqos/sdhci.h b/tests/qtest/libqos/sdhci.h
new file mode 100644 (file)
index 0000000..a88b45a
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef QGRAPH_QSDHCI_H
+#define QGRAPH_QSDHCI_H
+
+#include "libqos/qgraph.h"
+#include "pci.h"
+
+typedef struct QSDHCI QSDHCI;
+typedef struct QSDHCI_MemoryMapped QSDHCI_MemoryMapped;
+typedef struct QSDHCI_PCI  QSDHCI_PCI;
+typedef struct QSDHCIProperties QSDHCIProperties;
+
+/* Properties common to all QSDHCI devices */
+struct QSDHCIProperties {
+    uint8_t version;
+    uint8_t baseclock;
+    struct {
+        bool sdma;
+        uint64_t reg;
+    } capab;
+};
+
+struct QSDHCI {
+    uint16_t (*readw)(QSDHCI *s, uint32_t reg);
+    uint64_t (*readq)(QSDHCI *s, uint32_t reg);
+    void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val);
+    QSDHCIProperties props;
+};
+
+/* Memory Mapped implementation of QSDHCI */
+struct QSDHCI_MemoryMapped {
+    QOSGraphObject obj;
+    QTestState *qts;
+    QSDHCI sdhci;
+    uint64_t addr;
+};
+
+/* PCI implementation of QSDHCI */
+struct QSDHCI_PCI {
+    QOSGraphObject obj;
+    QPCIDevice dev;
+    QSDHCI sdhci;
+    QPCIBar mem_bar;
+};
+
+/**
+ * qos_init_sdhci_mm(): external constructor used by all drivers/machines
+ * that "contain" a #QSDHCI_MemoryMapped driver
+ */
+void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts,
+                       uint32_t addr, QSDHCIProperties *common);
+
+#endif
diff --git a/tests/qtest/libqos/tpci200.c b/tests/qtest/libqos/tpci200.c
new file mode 100644 (file)
index 0000000..ae590a4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * QTest testcase for tpci200 PCI-IndustryPack bridge
+ *
+ * Copyright (c) 2014 SUSE LINUX Products GmbH
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/qgraph.h"
+#include "libqos/pci.h"
+
+typedef struct QTpci200 QTpci200;
+typedef struct QIpack QIpack;
+
+struct QIpack {
+
+};
+struct QTpci200 {
+    QOSGraphObject obj;
+    QPCIDevice dev;
+    QIpack ipack;
+};
+
+/* tpci200 */
+static void *tpci200_get_driver(void *obj, const char *interface)
+{
+    QTpci200 *tpci200 = obj;
+    if (!g_strcmp0(interface, "ipack")) {
+        return &tpci200->ipack;
+    }
+    if (!g_strcmp0(interface, "pci-device")) {
+        return &tpci200->dev;
+    }
+
+    fprintf(stderr, "%s not present in tpci200\n", interface);
+    g_assert_not_reached();
+}
+
+static void *tpci200_create(void *pci_bus, QGuestAllocator *alloc, void *addr)
+{
+    QTpci200 *tpci200 = g_new0(QTpci200, 1);
+    QPCIBus *bus = pci_bus;
+
+    qpci_device_init(&tpci200->dev, bus, addr);
+    tpci200->obj.get_driver = tpci200_get_driver;
+    return &tpci200->obj;
+}
+
+static void tpci200_register_nodes(void)
+{
+    QOSGraphEdgeOptions opts = {
+        .extra_device_opts = "addr=04.0,id=ipack0",
+    };
+    add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) });
+
+    qos_node_create_driver("tpci200", tpci200_create);
+    qos_node_consumes("tpci200", "pci-bus", &opts);
+    qos_node_produces("tpci200", "ipack");
+    qos_node_produces("tpci200", "pci-device");
+}
+
+libqos_init(tpci200_register_nodes);
diff --git a/tests/qtest/libqos/usb.c b/tests/qtest/libqos/usb.c
new file mode 100644 (file)
index 0000000..d7a9cb3
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * common code shared by usb tests
+ *
+ * Copyright (c) 2014 Red Hat, Inc
+ *
+ * Authors:
+ *     Gerd Hoffmann <kraxel@redhat.com>
+ *     John Snow <jsnow@redhat.com>
+ *     Igor Mammedov <imammedo@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "hw/usb/uhci-regs.h"
+#include "libqos/usb.h"
+
+void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar)
+{
+    hc->dev = qpci_device_find(pcibus, devfn);
+    g_assert(hc->dev != NULL);
+    qpci_device_enable(hc->dev);
+    hc->bar = qpci_iomap(hc->dev, bar, NULL);
+}
+
+void uhci_deinit(struct qhc *hc)
+{
+    g_free(hc->dev);
+}
+
+void uhci_port_test(struct qhc *hc, int port, uint16_t expect)
+{
+    uint16_t value = qpci_io_readw(hc->dev, hc->bar, 0x10 + 2 * port);
+    uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1);
+
+    g_assert((value & mask) == (expect & mask));
+}
+
+void usb_test_hotplug(QTestState *qts, const char *hcd_id, const char *port,
+                      void (*port_check)(void))
+{
+    char *id = g_strdup_printf("usbdev%s", port);
+    char *bus = g_strdup_printf("%s.0", hcd_id);
+
+    qtest_qmp_device_add(qts, "usb-tablet", id, "{'port': %s, 'bus': %s}",
+                         port, bus);
+
+    if (port_check) {
+        port_check();
+    }
+
+    qtest_qmp_device_del(qts, id);
+
+    g_free(bus);
+    g_free(id);
+}
diff --git a/tests/qtest/libqos/usb.h b/tests/qtest/libqos/usb.h
new file mode 100644 (file)
index 0000000..eeced39
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef LIBQOS_USB_H
+#define LIBQOS_USB_H
+
+#include "libqos/pci-pc.h"
+
+struct qhc {
+    QPCIDevice *dev;
+    QPCIBar bar;
+};
+
+void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc,
+                       uint32_t devfn, int bar);
+void uhci_port_test(struct qhc *hc, int port, uint16_t expect);
+void uhci_deinit(struct qhc *hc);
+
+void usb_test_hotplug(QTestState *qts, const char *bus_name, const char *port,
+                      void (*port_check)(void));
+#endif
diff --git a/tests/qtest/libqos/virtio-9p.c b/tests/qtest/libqos/virtio-9p.c
new file mode 100644 (file)
index 0000000..77dbfb6
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "libqos/virtio-9p.h"
+#include "libqos/qgraph.h"
+
+static QGuestAllocator *alloc;
+
+static void virtio_9p_cleanup(QVirtio9P *interface)
+{
+    qvirtqueue_cleanup(interface->vdev->bus, interface->vq, alloc);
+}
+
+static void virtio_9p_setup(QVirtio9P *interface)
+{
+    uint64_t features;
+
+    features = qvirtio_get_features(interface->vdev);
+    features &= ~(QVIRTIO_F_BAD_FEATURE | (1ull << VIRTIO_RING_F_EVENT_IDX));
+    qvirtio_set_features(interface->vdev, features);
+
+    interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0);
+    qvirtio_set_driver_ok(interface->vdev);
+}
+
+/* virtio-9p-device */
+static void virtio_9p_device_destructor(QOSGraphObject *obj)
+{
+    QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj;
+    QVirtio9P *v9p = &v_9p->v9p;
+
+    virtio_9p_cleanup(v9p);
+}
+
+static void virtio_9p_device_start_hw(QOSGraphObject *obj)
+{
+    QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj;
+    QVirtio9P *v9p = &v_9p->v9p;
+
+    virtio_9p_setup(v9p);
+}
+
+static void *virtio_9p_get_driver(QVirtio9P *v_9p,
+                                         const char *interface)
+{
+    if (!g_strcmp0(interface, "virtio-9p")) {
+        return v_9p;
+    }
+    if (!g_strcmp0(interface, "virtio")) {
+        return v_9p->vdev;
+    }
+
+    fprintf(stderr, "%s not present in virtio-9p-device\n", interface);
+    g_assert_not_reached();
+}
+
+static void *virtio_9p_device_get_driver(void *object, const char *interface)
+{
+    QVirtio9PDevice *v_9p = object;
+    return virtio_9p_get_driver(&v_9p->v9p, interface);
+}
+
+static void *virtio_9p_device_create(void *virtio_dev,
+                                     QGuestAllocator *t_alloc,
+                                     void *addr)
+{
+    QVirtio9PDevice *virtio_device = g_new0(QVirtio9PDevice, 1);
+    QVirtio9P *interface = &virtio_device->v9p;
+
+    interface->vdev = virtio_dev;
+    alloc = t_alloc;
+
+    virtio_device->obj.destructor = virtio_9p_device_destructor;
+    virtio_device->obj.get_driver = virtio_9p_device_get_driver;
+    virtio_device->obj.start_hw = virtio_9p_device_start_hw;
+
+    return &virtio_device->obj;
+}
+
+/* virtio-9p-pci */
+static void virtio_9p_pci_destructor(QOSGraphObject *obj)
+{
+    QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj;
+    QVirtio9P *interface = &v9_pci->v9p;
+    QOSGraphObject *pci_vobj =  &v9_pci->pci_vdev.obj;
+
+    virtio_9p_cleanup(interface);
+    qvirtio_pci_destructor(pci_vobj);
+}
+
+static void virtio_9p_pci_start_hw(QOSGraphObject *obj)
+{
+    QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj;
+    QVirtio9P *interface = &v9_pci->v9p;
+    QOSGraphObject *pci_vobj =  &v9_pci->pci_vdev.obj;
+
+    qvirtio_pci_start_hw(pci_vobj);
+    virtio_9p_setup(interface);
+}
+
+static void *virtio_9p_pci_get_driver(void *object, const char *interface)
+{
+    QVirtio9PPCI *v_9p = object;
+    if (!g_strcmp0(interface, "pci-device")) {
+        return v_9p->pci_vdev.pdev;
+    }
+    return virtio_9p_get_driver(&v_9p->v9p, interface);
+}
+
+static void *virtio_9p_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+                                  void *addr)
+{
+    QVirtio9PPCI *v9_pci = g_new0(QVirtio9PPCI, 1);
+    QVirtio9P *interface = &v9_pci->v9p;
+    QOSGraphObject *obj = &v9_pci->pci_vdev.obj;
+
+    virtio_pci_init(&v9_pci->pci_vdev, pci_bus, addr);
+    interface->vdev = &v9_pci->pci_vdev.vdev;
+    alloc = t_alloc;
+
+    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_9P);
+
+    obj->destructor = virtio_9p_pci_destructor;
+    obj->start_hw = virtio_9p_pci_start_hw;
+    obj->get_driver = virtio_9p_pci_get_driver;
+
+    return obj;
+}
+
+static void virtio_9p_register_nodes(void)
+{
+    const char *str_simple = "fsdev=fsdev0,mount_tag=" MOUNT_TAG;
+    const char *str_addr = "fsdev=fsdev0,addr=04.0,mount_tag=" MOUNT_TAG;
+
+    QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(4, 0),
+    };
+
+    QOSGraphEdgeOptions opts = {
+        .before_cmd_line = "-fsdev synth,id=fsdev0",
+    };
+
+    /* virtio-9p-device */
+    opts.extra_device_opts = str_simple,
+    qos_node_create_driver("virtio-9p-device", virtio_9p_device_create);
+    qos_node_consumes("virtio-9p-device", "virtio-bus", &opts);
+    qos_node_produces("virtio-9p-device", "virtio");
+    qos_node_produces("virtio-9p-device", "virtio-9p");
+
+    /* virtio-9p-pci */
+    opts.extra_device_opts = str_addr;
+    add_qpci_address(&opts, &addr);
+    qos_node_create_driver("virtio-9p-pci", virtio_9p_pci_create);
+    qos_node_consumes("virtio-9p-pci", "pci-bus", &opts);
+    qos_node_produces("virtio-9p-pci", "pci-device");
+    qos_node_produces("virtio-9p-pci", "virtio");
+    qos_node_produces("virtio-9p-pci", "virtio-9p");
+
+}
+
+libqos_init(virtio_9p_register_nodes);
diff --git a/tests/qtest/libqos/virtio-9p.h b/tests/qtest/libqos/virtio-9p.h
new file mode 100644 (file)
index 0000000..b54e89b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_9P_H
+#define TESTS_LIBQOS_VIRTIO_9P_H
+
+#include "libqos/qgraph.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+
+typedef struct QVirtio9P QVirtio9P;
+typedef struct QVirtio9PPCI QVirtio9PPCI;
+typedef struct QVirtio9PDevice QVirtio9PDevice;
+
+#define MOUNT_TAG "qtest"
+
+struct QVirtio9P {
+    QVirtioDevice *vdev;
+    QVirtQueue *vq;
+};
+
+struct QVirtio9PPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVirtio9P v9p;
+};
+
+struct QVirtio9PDevice {
+    QOSGraphObject obj;
+    QVirtio9P v9p;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio-balloon.c b/tests/qtest/libqos/virtio-balloon.c
new file mode 100644 (file)
index 0000000..42a4c58
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-balloon.h"
+
+/* virtio-balloon-device */
+static void *qvirtio_balloon_get_driver(QVirtioBalloon *v_balloon,
+                                        const char *interface)
+{
+    if (!g_strcmp0(interface, "virtio-balloon")) {
+        return v_balloon;
+    }
+    if (!g_strcmp0(interface, "virtio")) {
+        return v_balloon->vdev;
+    }
+
+    fprintf(stderr, "%s not present in virtio-balloon-device\n", interface);
+    g_assert_not_reached();
+}
+
+static void *qvirtio_balloon_device_get_driver(void *object,
+                                               const char *interface)
+{
+    QVirtioBalloonDevice *v_balloon = object;
+    return qvirtio_balloon_get_driver(&v_balloon->balloon, interface);
+}
+
+static void *virtio_balloon_device_create(void *virtio_dev,
+                                          QGuestAllocator *t_alloc,
+                                          void *addr)
+{
+    QVirtioBalloonDevice *virtio_bdevice = g_new0(QVirtioBalloonDevice, 1);
+    QVirtioBalloon *interface = &virtio_bdevice->balloon;
+
+    interface->vdev = virtio_dev;
+
+    virtio_bdevice->obj.get_driver = qvirtio_balloon_device_get_driver;
+
+    return &virtio_bdevice->obj;
+}
+
+/* virtio-balloon-pci */
+static void *qvirtio_balloon_pci_get_driver(void *object,
+                                            const char *interface)
+{
+    QVirtioBalloonPCI *v_balloon = object;
+    if (!g_strcmp0(interface, "pci-device")) {
+        return v_balloon->pci_vdev.pdev;
+    }
+    return qvirtio_balloon_get_driver(&v_balloon->balloon, interface);
+}
+
+static void *virtio_balloon_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+                                  void *addr)
+{
+    QVirtioBalloonPCI *virtio_bpci = g_new0(QVirtioBalloonPCI, 1);
+    QVirtioBalloon *interface = &virtio_bpci->balloon;
+    QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj;
+
+
+    virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr);
+    interface->vdev = &virtio_bpci->pci_vdev.vdev;
+
+    obj->get_driver = qvirtio_balloon_pci_get_driver;
+
+    return obj;
+}
+
+static void virtio_balloon_register_nodes(void)
+{
+    QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(4, 0),
+    };
+
+    QOSGraphEdgeOptions opts = {
+        .extra_device_opts = "addr=04.0",
+    };
+
+    /* virtio-balloon-device */
+    qos_node_create_driver("virtio-balloon-device",
+                            virtio_balloon_device_create);
+    qos_node_consumes("virtio-balloon-device", "virtio-bus", NULL);
+    qos_node_produces("virtio-balloon-device", "virtio");
+    qos_node_produces("virtio-balloon-device", "virtio-balloon");
+
+    /* virtio-balloon-pci */
+    add_qpci_address(&opts, &addr);
+    qos_node_create_driver("virtio-balloon-pci", virtio_balloon_pci_create);
+    qos_node_consumes("virtio-balloon-pci", "pci-bus", &opts);
+    qos_node_produces("virtio-balloon-pci", "pci-device");
+    qos_node_produces("virtio-balloon-pci", "virtio");
+    qos_node_produces("virtio-balloon-pci", "virtio-balloon");
+}
+
+libqos_init(virtio_balloon_register_nodes);
diff --git a/tests/qtest/libqos/virtio-balloon.h b/tests/qtest/libqos/virtio-balloon.h
new file mode 100644 (file)
index 0000000..52661cc
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_BALLOON_H
+#define TESTS_LIBQOS_VIRTIO_BALLOON_H
+
+#include "libqos/qgraph.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+
+typedef struct QVirtioBalloon QVirtioBalloon;
+typedef struct QVirtioBalloonPCI QVirtioBalloonPCI;
+typedef struct QVirtioBalloonDevice QVirtioBalloonDevice;
+
+struct QVirtioBalloon {
+    QVirtioDevice *vdev;
+};
+
+struct QVirtioBalloonPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVirtioBalloon balloon;
+};
+
+struct QVirtioBalloonDevice {
+    QOSGraphObject obj;
+    QVirtioBalloon balloon;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio-blk.c b/tests/qtest/libqos/virtio-blk.c
new file mode 100644 (file)
index 0000000..726e93c
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "standard-headers/linux/virtio_blk.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-blk.h"
+
+#define PCI_SLOT                0x04
+#define PCI_FN                  0x00
+
+/* virtio-blk-device */
+static void *qvirtio_blk_get_driver(QVirtioBlk *v_blk,
+                                    const char *interface)
+{
+    if (!g_strcmp0(interface, "virtio-blk")) {
+        return v_blk;
+    }
+    if (!g_strcmp0(interface, "virtio")) {
+        return v_blk->vdev;
+    }
+
+    fprintf(stderr, "%s not present in virtio-blk-device\n", interface);
+    g_assert_not_reached();
+}
+
+static void *qvirtio_blk_device_get_driver(void *object,
+                                           const char *interface)
+{
+    QVirtioBlkDevice *v_blk = object;
+    return qvirtio_blk_get_driver(&v_blk->blk, interface);
+}
+
+static void *virtio_blk_device_create(void *virtio_dev,
+                                      QGuestAllocator *t_alloc,
+                                      void *addr)
+{
+    QVirtioBlkDevice *virtio_blk = g_new0(QVirtioBlkDevice, 1);
+    QVirtioBlk *interface = &virtio_blk->blk;
+
+    interface->vdev = virtio_dev;
+
+    virtio_blk->obj.get_driver = qvirtio_blk_device_get_driver;
+
+    return &virtio_blk->obj;
+}
+
+/* virtio-blk-pci */
+static void *qvirtio_blk_pci_get_driver(void *object, const char *interface)
+{
+    QVirtioBlkPCI *v_blk = object;
+    if (!g_strcmp0(interface, "pci-device")) {
+        return v_blk->pci_vdev.pdev;
+    }
+    return qvirtio_blk_get_driver(&v_blk->blk, interface);
+}
+
+static void *virtio_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+                                      void *addr)
+{
+    QVirtioBlkPCI *virtio_blk = g_new0(QVirtioBlkPCI, 1);
+    QVirtioBlk *interface = &virtio_blk->blk;
+    QOSGraphObject *obj = &virtio_blk->pci_vdev.obj;
+
+    virtio_pci_init(&virtio_blk->pci_vdev, pci_bus, addr);
+    interface->vdev = &virtio_blk->pci_vdev.vdev;
+
+    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK);
+
+    obj->get_driver = qvirtio_blk_pci_get_driver;
+
+    return obj;
+}
+
+static void virtio_blk_register_nodes(void)
+{
+    /* FIXME: every test using these two nodes needs to setup a
+     * -drive,id=drive0 otherwise QEMU is not going to start.
+     * Therefore, we do not include "produces" edge for virtio
+     * and pci-device yet.
+    */
+
+    char *arg = g_strdup_printf("id=drv0,drive=drive0,addr=%x.%x",
+                                PCI_SLOT, PCI_FN);
+
+    QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN),
+    };
+
+    QOSGraphEdgeOptions opts = { };
+
+    /* virtio-blk-device */
+    opts.extra_device_opts = "drive=drive0";
+    qos_node_create_driver("virtio-blk-device", virtio_blk_device_create);
+    qos_node_consumes("virtio-blk-device", "virtio-bus", &opts);
+    qos_node_produces("virtio-blk-device", "virtio-blk");
+
+    /* virtio-blk-pci */
+    opts.extra_device_opts = arg;
+    add_qpci_address(&opts, &addr);
+    qos_node_create_driver("virtio-blk-pci", virtio_blk_pci_create);
+    qos_node_consumes("virtio-blk-pci", "pci-bus", &opts);
+    qos_node_produces("virtio-blk-pci", "virtio-blk");
+
+    g_free(arg);
+}
+
+libqos_init(virtio_blk_register_nodes);
diff --git a/tests/qtest/libqos/virtio-blk.h b/tests/qtest/libqos/virtio-blk.h
new file mode 100644 (file)
index 0000000..c05adc6
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_BLK_H
+#define TESTS_LIBQOS_VIRTIO_BLK_H
+
+#include "libqos/qgraph.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+
+typedef struct QVirtioBlk QVirtioBlk;
+typedef struct QVirtioBlkPCI QVirtioBlkPCI;
+typedef struct QVirtioBlkDevice QVirtioBlkDevice;
+
+/* virtqueue is created in each test */
+struct QVirtioBlk {
+    QVirtioDevice *vdev;
+};
+
+struct QVirtioBlkPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVirtioBlk blk;
+};
+
+struct QVirtioBlkDevice {
+    QOSGraphObject obj;
+    QVirtioBlk blk;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio-mmio.c b/tests/qtest/libqos/virtio-mmio.c
new file mode 100644 (file)
index 0000000..e0a2bd7
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * libqos virtio MMIO driver
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-mmio.h"
+#include "libqos/malloc.h"
+#include "libqos/qgraph.h"
+#include "standard-headers/linux/virtio_ring.h"
+
+static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    return qtest_readb(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
+}
+
+static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    return qtest_readw(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
+}
+
+static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
+}
+
+static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    return qtest_readq(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off);
+}
+
+static uint64_t qvirtio_mmio_get_features(QVirtioDevice *d)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    uint64_t lo;
+    uint64_t hi = 0;
+
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0);
+    lo = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
+
+    if (dev->version >= 2) {
+        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 1);
+        hi = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES);
+    }
+
+    return (hi << 32) | lo;
+}
+
+static void qvirtio_mmio_set_features(QVirtioDevice *d, uint64_t features)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    dev->features = features;
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0);
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features);
+
+    if (dev->version >= 2) {
+        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 1);
+        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES,
+                     features >> 32);
+    }
+}
+
+static uint64_t qvirtio_mmio_get_guest_features(QVirtioDevice *d)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    return dev->features;
+}
+
+static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    return (uint8_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS);
+}
+
+static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status);
+}
+
+static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    uint32_t isr;
+
+    isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1;
+    if (isr != 0) {
+        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1);
+        return true;
+    }
+
+    return false;
+}
+
+static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    uint32_t isr;
+
+    isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2;
+    if (isr != 0) {
+        qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2);
+        return true;
+    }
+
+    return false;
+}
+
+static void qvirtio_mmio_wait_config_isr_status(QVirtioDevice *d,
+                                                gint64 timeout_us)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    gint64 start_time = g_get_monotonic_time();
+
+    do {
+        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+        qtest_clock_step(dev->qts, 100);
+    } while (!qvirtio_mmio_get_config_isr_status(d));
+}
+
+static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index);
+
+    g_assert_cmphex(qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0);
+}
+
+static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    return (uint16_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX);
+}
+
+static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    uint64_t pfn = vq->desc / dev->page_size;
+
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn);
+}
+
+static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d,
+                                        QGuestAllocator *alloc, uint16_t index)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    QVirtQueue *vq;
+    uint64_t addr;
+
+    vq = g_malloc0(sizeof(*vq));
+    vq->vdev = d;
+    qvirtio_mmio_queue_select(d, index);
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size);
+
+    vq->index = index;
+    vq->size = qvirtio_mmio_get_queue_size(d);
+    vq->free_head = 0;
+    vq->num_free = vq->size;
+    vq->align = dev->page_size;
+    vq->indirect = dev->features & (1ull << VIRTIO_RING_F_INDIRECT_DESC);
+    vq->event = dev->features & (1ull << VIRTIO_RING_F_EVENT_IDX);
+
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size);
+
+    /* Check different than 0 */
+    g_assert_cmpint(vq->size, !=, 0);
+
+    /* Check power of 2 */
+    g_assert_cmpint(vq->size & (vq->size - 1), ==, 0);
+
+    addr = guest_alloc(alloc, qvring_size(vq->size, dev->page_size));
+    qvring_init(dev->qts, alloc, vq, addr);
+    qvirtio_mmio_set_queue_address(d, vq);
+
+    return vq;
+}
+
+static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue *vq,
+                                           QGuestAllocator *alloc)
+{
+    guest_free(alloc, vq->desc);
+    g_free(vq);
+}
+
+static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev);
+    qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index);
+}
+
+const QVirtioBus qvirtio_mmio = {
+    .config_readb = qvirtio_mmio_config_readb,
+    .config_readw = qvirtio_mmio_config_readw,
+    .config_readl = qvirtio_mmio_config_readl,
+    .config_readq = qvirtio_mmio_config_readq,
+    .get_features = qvirtio_mmio_get_features,
+    .set_features = qvirtio_mmio_set_features,
+    .get_guest_features = qvirtio_mmio_get_guest_features,
+    .get_status = qvirtio_mmio_get_status,
+    .set_status = qvirtio_mmio_set_status,
+    .get_queue_isr_status = qvirtio_mmio_get_queue_isr_status,
+    .wait_config_isr_status = qvirtio_mmio_wait_config_isr_status,
+    .queue_select = qvirtio_mmio_queue_select,
+    .get_queue_size = qvirtio_mmio_get_queue_size,
+    .set_queue_address = qvirtio_mmio_set_queue_address,
+    .virtqueue_setup = qvirtio_mmio_virtqueue_setup,
+    .virtqueue_cleanup = qvirtio_mmio_virtqueue_cleanup,
+    .virtqueue_kick = qvirtio_mmio_virtqueue_kick,
+};
+
+static void *qvirtio_mmio_get_driver(void *obj, const char *interface)
+{
+    QVirtioMMIODevice *virtio_mmio = obj;
+    if (!g_strcmp0(interface, "virtio-bus")) {
+        return &virtio_mmio->vdev;
+    }
+    fprintf(stderr, "%s not present in virtio-mmio\n", interface);
+    g_assert_not_reached();
+}
+
+static void qvirtio_mmio_start_hw(QOSGraphObject *obj)
+{
+    QVirtioMMIODevice *dev = (QVirtioMMIODevice *) obj;
+    qvirtio_start_device(&dev->vdev);
+}
+
+void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts,
+                              uint64_t addr, uint32_t page_size)
+{
+    uint32_t magic;
+    magic = qtest_readl(qts, addr + QVIRTIO_MMIO_MAGIC_VALUE);
+    g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
+
+    dev->version = qtest_readl(qts, addr + QVIRTIO_MMIO_VERSION);
+    g_assert(dev->version == 1 || dev->version == 2);
+
+    dev->qts = qts;
+    dev->addr = addr;
+    dev->page_size = page_size;
+    dev->vdev.device_type = qtest_readl(qts, addr + QVIRTIO_MMIO_DEVICE_ID);
+    dev->vdev.bus = &qvirtio_mmio;
+
+    qtest_writel(qts, addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size);
+
+    dev->obj.get_driver = qvirtio_mmio_get_driver;
+    dev->obj.start_hw = qvirtio_mmio_start_hw;
+}
+
+static void virtio_mmio_register_nodes(void)
+{
+    qos_node_create_driver("virtio-mmio", NULL);
+    qos_node_produces("virtio-mmio", "virtio-bus");
+}
+
+libqos_init(virtio_mmio_register_nodes);
diff --git a/tests/qtest/libqos/virtio-mmio.h b/tests/qtest/libqos/virtio-mmio.h
new file mode 100644 (file)
index 0000000..0e45778
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * libqos virtio MMIO definitions
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_VIRTIO_MMIO_H
+#define LIBQOS_VIRTIO_MMIO_H
+
+#include "libqos/virtio.h"
+#include "libqos/qgraph.h"
+
+#define QVIRTIO_MMIO_MAGIC_VALUE        0x000
+#define QVIRTIO_MMIO_VERSION            0x004
+#define QVIRTIO_MMIO_DEVICE_ID          0x008
+#define QVIRTIO_MMIO_VENDOR_ID          0x00C
+#define QVIRTIO_MMIO_HOST_FEATURES      0x010
+#define QVIRTIO_MMIO_HOST_FEATURES_SEL  0x014
+#define QVIRTIO_MMIO_GUEST_FEATURES     0x020
+#define QVIRTIO_MMIO_GUEST_FEATURES_SEL 0x024
+#define QVIRTIO_MMIO_GUEST_PAGE_SIZE    0x028
+#define QVIRTIO_MMIO_QUEUE_SEL          0x030
+#define QVIRTIO_MMIO_QUEUE_NUM_MAX      0x034
+#define QVIRTIO_MMIO_QUEUE_NUM          0x038
+#define QVIRTIO_MMIO_QUEUE_ALIGN        0x03C
+#define QVIRTIO_MMIO_QUEUE_PFN          0x040
+#define QVIRTIO_MMIO_QUEUE_NOTIFY       0x050
+#define QVIRTIO_MMIO_INTERRUPT_STATUS   0x060
+#define QVIRTIO_MMIO_INTERRUPT_ACK      0x064
+#define QVIRTIO_MMIO_DEVICE_STATUS      0x070
+#define QVIRTIO_MMIO_DEVICE_SPECIFIC    0x100
+
+typedef struct QVirtioMMIODevice {
+    QOSGraphObject obj;
+    QVirtioDevice vdev;
+    QTestState *qts;
+    uint64_t addr;
+    uint32_t page_size;
+    uint32_t features; /* As it cannot be read later, save it */
+    uint32_t version;
+} QVirtioMMIODevice;
+
+extern const QVirtioBus qvirtio_mmio;
+
+void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts,
+                              uint64_t addr, uint32_t page_size);
+
+#endif
diff --git a/tests/qtest/libqos/virtio-net.c b/tests/qtest/libqos/virtio-net.c
new file mode 100644 (file)
index 0000000..710d440
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-net.h"
+#include "hw/virtio/virtio-net.h"
+
+
+static QGuestAllocator *alloc;
+
+static void virtio_net_cleanup(QVirtioNet *interface)
+{
+    int i;
+
+    for (i = 0; i < interface->n_queues; i++) {
+        qvirtqueue_cleanup(interface->vdev->bus, interface->queues[i], alloc);
+    }
+    g_free(interface->queues);
+}
+
+static void virtio_net_setup(QVirtioNet *interface)
+{
+    QVirtioDevice *vdev = interface->vdev;
+    uint64_t features;
+    int i;
+
+    features = qvirtio_get_features(vdev);
+    features &= ~(QVIRTIO_F_BAD_FEATURE |
+                  (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
+                  (1ull << VIRTIO_RING_F_EVENT_IDX));
+    qvirtio_set_features(vdev, features);
+
+    if (features & (1ull << VIRTIO_NET_F_MQ)) {
+        interface->n_queues = qvirtio_config_readw(vdev, 8) * 2;
+    } else {
+        interface->n_queues = 2;
+    }
+    interface->n_queues++; /* Account for the ctrl queue */
+
+    interface->queues = g_new(QVirtQueue *, interface->n_queues);
+    for (i = 0; i < interface->n_queues; i++) {
+        interface->queues[i] = qvirtqueue_setup(vdev, alloc, i);
+    }
+    qvirtio_set_driver_ok(vdev);
+}
+
+/* virtio-net-device */
+static void qvirtio_net_device_destructor(QOSGraphObject *obj)
+{
+    QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj;
+    virtio_net_cleanup(&v_net->net);
+}
+
+static void qvirtio_net_device_start_hw(QOSGraphObject *obj)
+{
+    QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj;
+    QVirtioNet *interface = &v_net->net;
+
+    virtio_net_setup(interface);
+}
+
+static void *qvirtio_net_get_driver(QVirtioNet *v_net,
+                                    const char *interface)
+{
+    if (!g_strcmp0(interface, "virtio-net")) {
+        return v_net;
+    }
+    if (!g_strcmp0(interface, "virtio")) {
+        return v_net->vdev;
+    }
+
+    fprintf(stderr, "%s not present in virtio-net-device\n", interface);
+    g_assert_not_reached();
+}
+
+static void *qvirtio_net_device_get_driver(void *object,
+                                           const char *interface)
+{
+    QVirtioNetDevice *v_net = object;
+    return qvirtio_net_get_driver(&v_net->net, interface);
+}
+
+static void *virtio_net_device_create(void *virtio_dev,
+                                          QGuestAllocator *t_alloc,
+                                          void *addr)
+{
+    QVirtioNetDevice *virtio_ndevice = g_new0(QVirtioNetDevice, 1);
+    QVirtioNet *interface = &virtio_ndevice->net;
+
+    interface->vdev = virtio_dev;
+    alloc = t_alloc;
+
+    virtio_ndevice->obj.destructor = qvirtio_net_device_destructor;
+    virtio_ndevice->obj.get_driver = qvirtio_net_device_get_driver;
+    virtio_ndevice->obj.start_hw = qvirtio_net_device_start_hw;
+
+    return &virtio_ndevice->obj;
+}
+
+/* virtio-net-pci */
+static void qvirtio_net_pci_destructor(QOSGraphObject *obj)
+{
+    QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj;
+    QVirtioNet *interface = &v_net->net;
+    QOSGraphObject *pci_vobj =  &v_net->pci_vdev.obj;
+
+    virtio_net_cleanup(interface);
+    qvirtio_pci_destructor(pci_vobj);
+}
+
+static void qvirtio_net_pci_start_hw(QOSGraphObject *obj)
+{
+    QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj;
+    QVirtioNet *interface = &v_net->net;
+    QOSGraphObject *pci_vobj =  &v_net->pci_vdev.obj;
+
+    qvirtio_pci_start_hw(pci_vobj);
+    virtio_net_setup(interface);
+}
+
+static void *qvirtio_net_pci_get_driver(void *object,
+                                            const char *interface)
+{
+    QVirtioNetPCI *v_net = object;
+    if (!g_strcmp0(interface, "pci-device")) {
+        return v_net->pci_vdev.pdev;
+    }
+    return qvirtio_net_get_driver(&v_net->net, interface);
+}
+
+static void *virtio_net_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+                                  void *addr)
+{
+    QVirtioNetPCI *virtio_bpci = g_new0(QVirtioNetPCI, 1);
+    QVirtioNet *interface = &virtio_bpci->net;
+    QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj;
+
+    virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr);
+    interface->vdev = &virtio_bpci->pci_vdev.vdev;
+    alloc = t_alloc;
+
+    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_NET);
+
+    obj->destructor = qvirtio_net_pci_destructor;
+    obj->start_hw = qvirtio_net_pci_start_hw;
+    obj->get_driver = qvirtio_net_pci_get_driver;
+
+    return obj;
+}
+
+static void virtio_net_register_nodes(void)
+{
+    /* FIXME: every test using these nodes needs to setup a
+     * -netdev socket,id=hs0 otherwise QEMU is not going to start.
+     * Therefore, we do not include "produces" edge for virtio
+     * and pci-device yet.
+     */
+    QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(4, 0),
+    };
+
+    QOSGraphEdgeOptions opts = { };
+
+    /* virtio-net-device */
+    opts.extra_device_opts = "netdev=hs0";
+    qos_node_create_driver("virtio-net-device",
+                            virtio_net_device_create);
+    qos_node_consumes("virtio-net-device", "virtio-bus", &opts);
+    qos_node_produces("virtio-net-device", "virtio-net");
+
+    /* virtio-net-pci */
+    opts.extra_device_opts = "netdev=hs0,addr=04.0";
+    add_qpci_address(&opts, &addr);
+    qos_node_create_driver("virtio-net-pci", virtio_net_pci_create);
+    qos_node_consumes("virtio-net-pci", "pci-bus", &opts);
+    qos_node_produces("virtio-net-pci", "virtio-net");
+}
+
+libqos_init(virtio_net_register_nodes);
diff --git a/tests/qtest/libqos/virtio-net.h b/tests/qtest/libqos/virtio-net.h
new file mode 100644 (file)
index 0000000..855c67d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_NET_H
+#define TESTS_LIBQOS_VIRTIO_NET_H
+
+#include "libqos/qgraph.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+
+typedef struct QVirtioNet QVirtioNet;
+typedef struct QVirtioNetPCI QVirtioNetPCI;
+typedef struct QVirtioNetDevice QVirtioNetDevice;
+
+struct QVirtioNet {
+    QVirtioDevice *vdev;
+    int n_queues; /* total number of virtqueues (rx, tx, ctrl) */
+    QVirtQueue **queues;
+};
+
+struct QVirtioNetPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVirtioNet net;
+};
+
+struct QVirtioNetDevice {
+    QOSGraphObject obj;
+    QVirtioNet net;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio-pci-modern.c b/tests/qtest/libqos/virtio-pci-modern.c
new file mode 100644 (file)
index 0000000..18d1188
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * libqos VIRTIO 1.0 PCI driver
+ *
+ * Copyright (c) 2019 Red Hat, Inc
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "standard-headers/linux/pci_regs.h"
+#include "standard-headers/linux/virtio_pci.h"
+#include "standard-headers/linux/virtio_config.h"
+#include "virtio-pci-modern.h"
+
+static uint8_t config_readb(QVirtioDevice *d, uint64_t addr)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readb(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
+}
+
+static uint16_t config_readw(QVirtioDevice *d, uint64_t addr)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readw(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
+}
+
+static uint32_t config_readl(QVirtioDevice *d, uint64_t addr)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readl(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
+}
+
+static uint64_t config_readq(QVirtioDevice *d, uint64_t addr)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readq(dev->pdev, dev->bar, dev->device_cfg_offset + addr);
+}
+
+static uint64_t get_features(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    uint64_t lo, hi;
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            device_feature_select),
+                   0);
+    lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
+                       offsetof(struct virtio_pci_common_cfg, device_feature));
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            device_feature_select),
+                   1);
+    hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
+                       offsetof(struct virtio_pci_common_cfg, device_feature));
+
+    return (hi << 32) | lo;
+}
+
+static void set_features(QVirtioDevice *d, uint64_t features)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    /* Drivers must enable VIRTIO 1.0 or else use the Legacy interface */
+    g_assert_cmphex(features & (1ull << VIRTIO_F_VERSION_1), !=, 0);
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            guest_feature_select),
+                   0);
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            guest_feature),
+                   features);
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            guest_feature_select),
+                   1);
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            guest_feature),
+                   features >> 32);
+}
+
+static uint64_t get_guest_features(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    uint64_t lo, hi;
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            guest_feature_select),
+                   0);
+    lo = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
+                       offsetof(struct virtio_pci_common_cfg, guest_feature));
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg,
+                            guest_feature_select),
+                   1);
+    hi = qpci_io_readl(dev->pdev, dev->bar, dev->common_cfg_offset +
+                       offsetof(struct virtio_pci_common_cfg, guest_feature));
+
+    return (hi << 32) | lo;
+}
+
+static uint8_t get_status(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    return qpci_io_readb(dev->pdev, dev->bar, dev->common_cfg_offset +
+                         offsetof(struct virtio_pci_common_cfg,
+                                  device_status));
+}
+
+static void set_status(QVirtioDevice *d, uint8_t status)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    return qpci_io_writeb(dev->pdev, dev->bar, dev->common_cfg_offset +
+                          offsetof(struct virtio_pci_common_cfg,
+                                   device_status),
+                          status);
+}
+
+static bool get_msix_status(QVirtioPCIDevice *dev, uint32_t msix_entry,
+                            uint32_t msix_addr, uint32_t msix_data)
+{
+    uint32_t data;
+
+    g_assert_cmpint(msix_entry, !=, -1);
+    if (qpci_msix_masked(dev->pdev, msix_entry)) {
+        /* No ISR checking should be done if masked, but read anyway */
+        return qpci_msix_pending(dev->pdev, msix_entry);
+    }
+
+    data = qtest_readl(dev->pdev->bus->qts, msix_addr);
+    if (data == msix_data) {
+        qtest_writel(dev->pdev->bus->qts, msix_addr, 0);
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static bool get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    if (dev->pdev->msix_enabled) {
+        QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
+
+        return get_msix_status(dev, vqpci->msix_entry, vqpci->msix_addr,
+                               vqpci->msix_data);
+    }
+
+    return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 1;
+}
+
+static bool get_config_isr_status(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    if (dev->pdev->msix_enabled) {
+        return get_msix_status(dev, dev->config_msix_entry,
+                               dev->config_msix_addr, dev->config_msix_data);
+    }
+
+    return qpci_io_readb(dev->pdev, dev->bar, dev->isr_cfg_offset) & 2;
+}
+
+static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    gint64 start_time = g_get_monotonic_time();
+
+    do {
+        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+        qtest_clock_step(dev->pdev->bus->qts, 100);
+    } while (!get_config_isr_status(d));
+}
+
+static void queue_select(QVirtioDevice *d, uint16_t index)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_select),
+                   index);
+}
+
+static uint16_t get_queue_size(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    return qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset +
+                         offsetof(struct virtio_pci_common_cfg, queue_size));
+}
+
+static void set_queue_address(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_desc_lo),
+                   vq->desc);
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_desc_hi),
+                   vq->desc >> 32);
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_avail_lo),
+                   vq->avail);
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_avail_hi),
+                   vq->avail >> 32);
+
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_used_lo),
+                   vq->used);
+    qpci_io_writel(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_used_hi),
+                   vq->used >> 32);
+}
+
+static QVirtQueue *virtqueue_setup(QVirtioDevice *d, QGuestAllocator *alloc,
+                                   uint16_t index)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    QVirtQueue *vq;
+    QVirtQueuePCI *vqpci;
+    uint16_t notify_off;
+
+    vq = qvirtio_pci_virtqueue_setup_common(d, alloc, index);
+    vqpci = container_of(vq, QVirtQueuePCI, vq);
+
+    notify_off = qpci_io_readw(dev->pdev, dev->bar, dev->common_cfg_offset +
+                               offsetof(struct virtio_pci_common_cfg,
+                                        queue_notify_off));
+
+    vqpci->notify_offset = dev->notify_cfg_offset +
+                           notify_off * dev->notify_off_multiplier;
+
+    qpci_io_writew(dev->pdev, dev->bar, dev->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_enable), 1);
+
+    return vq;
+}
+
+static void virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
+
+    qpci_io_writew(dev->pdev, dev->bar, vqpci->notify_offset, vq->index);
+}
+
+static const QVirtioBus qvirtio_pci_virtio_1 = {
+    .config_readb = config_readb,
+    .config_readw = config_readw,
+    .config_readl = config_readl,
+    .config_readq = config_readq,
+    .get_features = get_features,
+    .set_features = set_features,
+    .get_guest_features = get_guest_features,
+    .get_status = get_status,
+    .set_status = set_status,
+    .get_queue_isr_status = get_queue_isr_status,
+    .wait_config_isr_status = wait_config_isr_status,
+    .queue_select = queue_select,
+    .get_queue_size = get_queue_size,
+    .set_queue_address = set_queue_address,
+    .virtqueue_setup = virtqueue_setup,
+    .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common,
+    .virtqueue_kick = virtqueue_kick,
+};
+
+static void set_config_vector(QVirtioPCIDevice *d, uint16_t entry)
+{
+    uint16_t vector;
+
+    qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, msix_config), entry);
+    vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset +
+                           offsetof(struct virtio_pci_common_cfg,
+                                    msix_config));
+    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
+}
+
+static void set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx,
+                             uint16_t entry)
+{
+    uint16_t vector;
+
+    queue_select(&d->vdev, vq_idx);
+    qpci_io_writew(d->pdev, d->bar, d->common_cfg_offset +
+                   offsetof(struct virtio_pci_common_cfg, queue_msix_vector),
+                   entry);
+    vector = qpci_io_readw(d->pdev, d->bar, d->common_cfg_offset +
+                           offsetof(struct virtio_pci_common_cfg,
+                                    queue_msix_vector));
+    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
+}
+
+static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_virtio_1 = {
+    .set_config_vector = set_config_vector,
+    .set_queue_vector = set_queue_vector,
+};
+
+static bool probe_device_type(QVirtioPCIDevice *dev)
+{
+    uint16_t vendor_id;
+    uint16_t device_id;
+
+    /* "Drivers MUST match devices with the PCI Vendor ID 0x1AF4" */
+    vendor_id = qpci_config_readw(dev->pdev, PCI_VENDOR_ID);
+    if (vendor_id != 0x1af4) {
+        return false;
+    }
+
+    /*
+     * "Any PCI device with ... PCI Device ID 0x1000 through 0x107F inclusive
+     * is a virtio device"
+     */
+    device_id = qpci_config_readw(dev->pdev, PCI_DEVICE_ID);
+    if (device_id < 0x1000 || device_id > 0x107f) {
+        return false;
+    }
+
+    /*
+     * "Devices MAY utilize a Transitional PCI Device ID range, 0x1000 to
+     * 0x103F depending on the device type"
+     */
+    if (device_id < 0x1040) {
+        /*
+         * "Transitional devices MUST have the PCI Subsystem Device ID matching
+         * the Virtio Device ID"
+         */
+        dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID);
+    } else {
+        /*
+         * "The PCI Device ID is calculated by adding 0x1040 to the Virtio
+         * Device ID"
+         */
+        dev->vdev.device_type = device_id - 0x1040;
+    }
+
+    return true;
+}
+
+/* Find the first VIRTIO 1.0 PCI structure for a given type */
+static bool find_structure(QVirtioPCIDevice *dev, uint8_t cfg_type,
+                           uint8_t *bar, uint32_t *offset, uint32_t *length,
+                           uint8_t *cfg_addr)
+{
+    uint8_t addr = 0;
+
+    while ((addr = qpci_find_capability(dev->pdev, PCI_CAP_ID_VNDR,
+                                        addr)) != 0) {
+        uint8_t type;
+
+        type = qpci_config_readb(dev->pdev,
+                addr + offsetof(struct virtio_pci_cap, cfg_type));
+        if (type != cfg_type) {
+            continue;
+        }
+
+        *bar = qpci_config_readb(dev->pdev,
+                addr + offsetof(struct virtio_pci_cap, bar));
+        *offset = qpci_config_readl(dev->pdev,
+                addr + offsetof(struct virtio_pci_cap, offset));
+        *length = qpci_config_readl(dev->pdev,
+                addr + offsetof(struct virtio_pci_cap, length));
+        if (cfg_addr) {
+            *cfg_addr = addr;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
+static bool probe_device_layout(QVirtioPCIDevice *dev)
+{
+    uint8_t bar;
+    uint8_t cfg_addr;
+    uint32_t length;
+
+    /*
+     * Due to the qpci_iomap() API we only support devices that put all
+     * structures in the same PCI BAR.  Luckily this is true with QEMU.
+     */
+
+    if (!find_structure(dev, VIRTIO_PCI_CAP_COMMON_CFG, &bar,
+                        &dev->common_cfg_offset, &length, NULL)) {
+        return false;
+    }
+    dev->bar_idx = bar;
+
+    if (!find_structure(dev, VIRTIO_PCI_CAP_NOTIFY_CFG, &bar,
+                        &dev->notify_cfg_offset, &length, &cfg_addr)) {
+        return false;
+    }
+    g_assert_cmphex(bar, ==, dev->bar_idx);
+
+    dev->notify_off_multiplier = qpci_config_readl(dev->pdev,
+            cfg_addr + offsetof(struct virtio_pci_notify_cap,
+                                notify_off_multiplier));
+
+    if (!find_structure(dev, VIRTIO_PCI_CAP_ISR_CFG, &bar,
+                        &dev->isr_cfg_offset, &length, NULL)) {
+        return false;
+    }
+    g_assert_cmphex(bar, ==, dev->bar_idx);
+
+    if (!find_structure(dev, VIRTIO_PCI_CAP_DEVICE_CFG, &bar,
+                        &dev->device_cfg_offset, &length, NULL)) {
+        return false;
+    }
+    g_assert_cmphex(bar, ==, dev->bar_idx);
+
+    return true;
+}
+
+/* Probe a VIRTIO 1.0 device */
+bool qvirtio_pci_init_virtio_1(QVirtioPCIDevice *dev)
+{
+    if (!probe_device_type(dev)) {
+        return false;
+    }
+
+    if (!probe_device_layout(dev)) {
+        return false;
+    }
+
+    dev->vdev.bus = &qvirtio_pci_virtio_1;
+    dev->msix_ops = &qvirtio_pci_msix_ops_virtio_1;
+    dev->vdev.big_endian = false;
+    return true;
+}
diff --git a/tests/qtest/libqos/virtio-pci-modern.h b/tests/qtest/libqos/virtio-pci-modern.h
new file mode 100644 (file)
index 0000000..6bf2b20
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * libqos virtio PCI VIRTIO 1.0 definitions
+ *
+ * Copyright (c) 2019 Red Hat, Inc
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_VIRTIO_PCI_MODERN_H
+#define LIBQOS_VIRTIO_PCI_MODERN_H
+
+#include "virtio-pci.h"
+
+bool qvirtio_pci_init_virtio_1(QVirtioPCIDevice *dev);
+
+#endif /* LIBQOS_VIRTIO_PCI_MODERN_H */
diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c
new file mode 100644 (file)
index 0000000..62851c2
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * libqos virtio PCI driver
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "libqos/malloc.h"
+#include "libqos/malloc-pc.h"
+#include "libqos/qgraph.h"
+#include "standard-headers/linux/virtio_ring.h"
+#include "standard-headers/linux/virtio_pci.h"
+
+#include "hw/pci/pci.h"
+#include "hw/pci/pci_regs.h"
+
+#include "virtio-pci-modern.h"
+
+/* virtio-pci is a superclass of all virtio-xxx-pci devices;
+ * the relation between virtio-pci and virtio-xxx-pci is implicit,
+ * and therefore virtio-pci does not produce virtio and is not
+ * reached by any edge, not even as a "contains" edge.
+ * In facts, every device is a QVirtioPCIDevice with
+ * additional fields, since every one has its own
+ * number of queues and various attributes.
+ * Virtio-pci provides default functions to start the
+ * hw and destroy the object, and nodes that want to
+ * override them should always remember to call the
+ * original qvirtio_pci_destructor and qvirtio_pci_start_hw.
+ */
+
+#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled))
+
+static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
+}
+
+/* PCI is always read in little-endian order
+ * but virtio ( < 1.0) is in guest order
+ * so with a big-endian guest the order has been reversed,
+ * reverse it again
+ * virtio-1.0 is always little-endian, like PCI
+ */
+
+static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    uint16_t value;
+
+    value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
+    if (qvirtio_is_big_endian(d)) {
+        value = bswap16(value);
+    }
+    return value;
+}
+
+static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    uint32_t value;
+
+    value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
+    if (qvirtio_is_big_endian(d)) {
+        value = bswap32(value);
+    }
+    return value;
+}
+
+static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    uint64_t val;
+
+    val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off);
+    if (qvirtio_is_big_endian(d)) {
+        val = bswap64(val);
+    }
+
+    return val;
+}
+
+static uint64_t qvirtio_pci_get_features(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES);
+}
+
+static void qvirtio_pci_set_features(QVirtioDevice *d, uint64_t features)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features);
+}
+
+static uint64_t qvirtio_pci_get_guest_features(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES);
+}
+
+static uint8_t qvirtio_pci_get_status(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS);
+}
+
+static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status);
+}
+
+static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq;
+    uint32_t data;
+
+    if (dev->pdev->msix_enabled) {
+        g_assert_cmpint(vqpci->msix_entry, !=, -1);
+        if (qpci_msix_masked(dev->pdev, vqpci->msix_entry)) {
+            /* No ISR checking should be done if masked, but read anyway */
+            return qpci_msix_pending(dev->pdev, vqpci->msix_entry);
+        } else {
+            data = qtest_readl(dev->pdev->bus->qts, vqpci->msix_addr);
+            if (data == vqpci->msix_data) {
+                qtest_writel(dev->pdev->bus->qts, vqpci->msix_addr, 0);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    } else {
+        return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1;
+    }
+}
+
+static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    uint32_t data;
+
+    if (dev->pdev->msix_enabled) {
+        g_assert_cmpint(dev->config_msix_entry, !=, -1);
+        if (qpci_msix_masked(dev->pdev, dev->config_msix_entry)) {
+            /* No ISR checking should be done if masked, but read anyway */
+            return qpci_msix_pending(dev->pdev, dev->config_msix_entry);
+        } else {
+            data = qtest_readl(dev->pdev->bus->qts, dev->config_msix_addr);
+            if (data == dev->config_msix_data) {
+                qtest_writel(dev->pdev->bus->qts, dev->config_msix_addr, 0);
+                return true;
+            } else {
+                return false;
+            }
+        }
+    } else {
+        return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2;
+    }
+}
+
+static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d,
+                                               gint64 timeout_us)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    gint64 start_time = g_get_monotonic_time();
+
+    do {
+        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+        qtest_clock_step(dev->pdev->bus->qts, 100);
+    } while (!qvirtio_pci_get_config_isr_status(d));
+}
+
+static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index);
+}
+
+static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM);
+}
+
+static void qvirtio_pci_set_queue_address(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    uint64_t pfn = vq->desc / VIRTIO_PCI_VRING_ALIGN;
+
+    qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn);
+}
+
+QVirtQueue *qvirtio_pci_virtqueue_setup_common(QVirtioDevice *d,
+                                               QGuestAllocator *alloc,
+                                               uint16_t index)
+{
+    uint64_t feat;
+    uint64_t addr;
+    QVirtQueuePCI *vqpci;
+    QVirtioPCIDevice *qvpcidev = container_of(d, QVirtioPCIDevice, vdev);
+
+    vqpci = g_malloc0(sizeof(*vqpci));
+    feat = d->bus->get_guest_features(d);
+
+    d->bus->queue_select(d, index);
+    vqpci->vq.vdev = d;
+    vqpci->vq.index = index;
+    vqpci->vq.size = d->bus->get_queue_size(d);
+    vqpci->vq.free_head = 0;
+    vqpci->vq.num_free = vqpci->vq.size;
+    vqpci->vq.align = VIRTIO_PCI_VRING_ALIGN;
+    vqpci->vq.indirect = feat & (1ull << VIRTIO_RING_F_INDIRECT_DESC);
+    vqpci->vq.event = feat & (1ull << VIRTIO_RING_F_EVENT_IDX);
+
+    vqpci->msix_entry = -1;
+    vqpci->msix_addr = 0;
+    vqpci->msix_data = 0x12345678;
+
+    /* Check different than 0 */
+    g_assert_cmpint(vqpci->vq.size, !=, 0);
+
+    /* Check power of 2 */
+    g_assert_cmpint(vqpci->vq.size & (vqpci->vq.size - 1), ==, 0);
+
+    addr = guest_alloc(alloc, qvring_size(vqpci->vq.size,
+                                          VIRTIO_PCI_VRING_ALIGN));
+    qvring_init(qvpcidev->pdev->bus->qts, alloc, &vqpci->vq, addr);
+    d->bus->set_queue_address(d, &vqpci->vq);
+
+    return &vqpci->vq;
+}
+
+void qvirtio_pci_virtqueue_cleanup_common(QVirtQueue *vq,
+                                          QGuestAllocator *alloc)
+{
+    QVirtQueuePCI *vqpci = container_of(vq, QVirtQueuePCI, vq);
+
+    guest_free(alloc, vq->desc);
+    g_free(vqpci);
+}
+
+static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq)
+{
+    QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev);
+    qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index);
+}
+
+static const QVirtioBus qvirtio_pci_legacy = {
+    .config_readb = qvirtio_pci_config_readb,
+    .config_readw = qvirtio_pci_config_readw,
+    .config_readl = qvirtio_pci_config_readl,
+    .config_readq = qvirtio_pci_config_readq,
+    .get_features = qvirtio_pci_get_features,
+    .set_features = qvirtio_pci_set_features,
+    .get_guest_features = qvirtio_pci_get_guest_features,
+    .get_status = qvirtio_pci_get_status,
+    .set_status = qvirtio_pci_set_status,
+    .get_queue_isr_status = qvirtio_pci_get_queue_isr_status,
+    .wait_config_isr_status = qvirtio_pci_wait_config_isr_status,
+    .queue_select = qvirtio_pci_queue_select,
+    .get_queue_size = qvirtio_pci_get_queue_size,
+    .set_queue_address = qvirtio_pci_set_queue_address,
+    .virtqueue_setup = qvirtio_pci_virtqueue_setup_common,
+    .virtqueue_cleanup = qvirtio_pci_virtqueue_cleanup_common,
+    .virtqueue_kick = qvirtio_pci_virtqueue_kick,
+};
+
+static void qvirtio_pci_set_config_vector(QVirtioPCIDevice *d, uint16_t entry)
+{
+    uint16_t vector;
+
+    qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry);
+    vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR);
+    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
+}
+
+static void qvirtio_pci_set_queue_vector(QVirtioPCIDevice *d, uint16_t vq_idx,
+                                         uint16_t entry)
+{
+    uint16_t vector;
+
+    qvirtio_pci_queue_select(&d->vdev, vq_idx);
+    qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry);
+    vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR);
+    g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR);
+}
+
+static const QVirtioPCIMSIXOps qvirtio_pci_msix_ops_legacy = {
+    .set_config_vector = qvirtio_pci_set_config_vector,
+    .set_queue_vector = qvirtio_pci_set_queue_vector,
+};
+
+void qvirtio_pci_device_enable(QVirtioPCIDevice *d)
+{
+    qpci_device_enable(d->pdev);
+    d->bar = qpci_iomap(d->pdev, d->bar_idx, NULL);
+}
+
+void qvirtio_pci_device_disable(QVirtioPCIDevice *d)
+{
+    qpci_iounmap(d->pdev, d->bar);
+}
+
+void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
+                                        QGuestAllocator *alloc, uint16_t entry)
+{
+    uint32_t control;
+    uint64_t off;
+
+    g_assert(d->pdev->msix_enabled);
+    off = d->pdev->msix_table_off + (entry * 16);
+
+    g_assert_cmpint(entry, >=, 0);
+    g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
+    vqpci->msix_entry = entry;
+
+    vqpci->msix_addr = guest_alloc(alloc, 4);
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL);
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_UPPER_ADDR,
+                   (vqpci->msix_addr >> 32) & ~0UL);
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data);
+
+    control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
+                            off + PCI_MSIX_ENTRY_VECTOR_CTRL);
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_VECTOR_CTRL,
+                   control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
+
+    d->msix_ops->set_queue_vector(d, vqpci->vq.index, entry);
+}
+
+void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
+                                        QGuestAllocator *alloc, uint16_t entry)
+{
+    uint32_t control;
+    uint64_t off;
+
+    g_assert(d->pdev->msix_enabled);
+    off = d->pdev->msix_table_off + (entry * 16);
+
+    g_assert_cmpint(entry, >=, 0);
+    g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev));
+    d->config_msix_entry = entry;
+
+    d->config_msix_data = 0x12345678;
+    d->config_msix_addr = guest_alloc(alloc, 4);
+
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL);
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_UPPER_ADDR,
+                   (d->config_msix_addr >> 32) & ~0UL);
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_DATA, d->config_msix_data);
+
+    control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar,
+                            off + PCI_MSIX_ENTRY_VECTOR_CTRL);
+    qpci_io_writel(d->pdev, d->pdev->msix_table_bar,
+                   off + PCI_MSIX_ENTRY_VECTOR_CTRL,
+                   control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT);
+
+    d->msix_ops->set_config_vector(d, entry);
+}
+
+void qvirtio_pci_destructor(QOSGraphObject *obj)
+{
+    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj;
+    qvirtio_pci_device_disable(dev);
+    g_free(dev->pdev);
+}
+
+void qvirtio_pci_start_hw(QOSGraphObject *obj)
+{
+    QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj;
+    qvirtio_pci_device_enable(dev);
+    qvirtio_start_device(&dev->vdev);
+}
+
+static void qvirtio_pci_init_legacy(QVirtioPCIDevice *dev)
+{
+    dev->vdev.device_type = qpci_config_readw(dev->pdev, PCI_SUBSYSTEM_ID);
+    dev->bar_idx = 0;
+    dev->vdev.bus = &qvirtio_pci_legacy;
+    dev->msix_ops = &qvirtio_pci_msix_ops_legacy;
+    dev->vdev.big_endian = qtest_big_endian(dev->pdev->bus->qts);
+}
+
+static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev)
+{
+    dev->pdev = pci_dev;
+    dev->config_msix_entry = -1;
+
+    if (!qvirtio_pci_init_virtio_1(dev)) {
+        qvirtio_pci_init_legacy(dev);
+    }
+
+    /* each virtio-xxx-pci device should override at least this function */
+    dev->obj.get_driver = NULL;
+    dev->obj.start_hw = qvirtio_pci_start_hw;
+    dev->obj.destructor = qvirtio_pci_destructor;
+}
+
+void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr)
+{
+    QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn);
+    g_assert_nonnull(pci_dev);
+    qvirtio_pci_init_from_pcidev(dev, pci_dev);
+}
+
+QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr)
+{
+    QVirtioPCIDevice *dev;
+    QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn);
+    if (!pci_dev) {
+        return NULL;
+    }
+
+    dev = g_new0(QVirtioPCIDevice, 1);
+    qvirtio_pci_init_from_pcidev(dev, pci_dev);
+    dev->obj.free = g_free;
+    return dev;
+}
diff --git a/tests/qtest/libqos/virtio-pci.h b/tests/qtest/libqos/virtio-pci.h
new file mode 100644 (file)
index 0000000..294d556
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * libqos virtio PCI definitions
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_VIRTIO_PCI_H
+#define LIBQOS_VIRTIO_PCI_H
+
+#include "libqos/virtio.h"
+#include "libqos/pci.h"
+#include "libqos/qgraph.h"
+
+typedef struct QVirtioPCIMSIXOps QVirtioPCIMSIXOps;
+
+typedef struct QVirtioPCIDevice {
+    QOSGraphObject obj;
+    QVirtioDevice vdev;
+    QPCIDevice *pdev;
+    QPCIBar bar;
+    const QVirtioPCIMSIXOps *msix_ops;
+    uint16_t config_msix_entry;
+    uint64_t config_msix_addr;
+    uint32_t config_msix_data;
+
+    int bar_idx;
+
+    /* VIRTIO 1.0 */
+    uint32_t common_cfg_offset;
+    uint32_t notify_cfg_offset;
+    uint32_t notify_off_multiplier;
+    uint32_t isr_cfg_offset;
+    uint32_t device_cfg_offset;
+} QVirtioPCIDevice;
+
+struct QVirtioPCIMSIXOps {
+    /* Set the Configuration Vector for MSI-X */
+    void (*set_config_vector)(QVirtioPCIDevice *d, uint16_t entry);
+
+    /* Set the Queue Vector for MSI-X */
+    void (*set_queue_vector)(QVirtioPCIDevice *d, uint16_t vq_idx,
+                             uint16_t entry);
+};
+
+typedef struct QVirtQueuePCI {
+    QVirtQueue vq;
+    uint16_t msix_entry;
+    uint64_t msix_addr;
+    uint32_t msix_data;
+
+    /* VIRTIO 1.0 */
+    uint64_t notify_offset;
+} QVirtQueuePCI;
+
+void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr);
+QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr);
+
+/* virtio-pci object functions available for subclasses that
+ * override the original start_hw and destroy
+ * function. All virtio-xxx-pci subclass that override must
+ * take care of calling these two functions in the respective
+ * places
+ */
+void qvirtio_pci_destructor(QOSGraphObject *obj);
+void qvirtio_pci_start_hw(QOSGraphObject *obj);
+
+
+void qvirtio_pci_device_enable(QVirtioPCIDevice *d);
+void qvirtio_pci_device_disable(QVirtioPCIDevice *d);
+
+void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d,
+                                        QGuestAllocator *alloc, uint16_t entry);
+void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci,
+                                        QGuestAllocator *alloc, uint16_t entry);
+
+/* Used by Legacy and Modern virtio-pci code */
+QVirtQueue *qvirtio_pci_virtqueue_setup_common(QVirtioDevice *d,
+                                               QGuestAllocator *alloc,
+                                               uint16_t index);
+void qvirtio_pci_virtqueue_cleanup_common(QVirtQueue *vq,
+                                          QGuestAllocator *alloc);
+
+#endif
diff --git a/tests/qtest/libqos/virtio-rng.c b/tests/qtest/libqos/virtio-rng.c
new file mode 100644 (file)
index 0000000..b86349e
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-rng.h"
+
+/* virtio-rng-device */
+static void *qvirtio_rng_get_driver(QVirtioRng *v_rng,
+                                    const char *interface)
+{
+    if (!g_strcmp0(interface, "virtio-rng")) {
+        return v_rng;
+    }
+    if (!g_strcmp0(interface, "virtio")) {
+        return v_rng->vdev;
+    }
+
+    fprintf(stderr, "%s not present in virtio-rng-device\n", interface);
+    g_assert_not_reached();
+}
+
+static void *qvirtio_rng_device_get_driver(void *object,
+                                           const char *interface)
+{
+    QVirtioRngDevice *v_rng = object;
+    return qvirtio_rng_get_driver(&v_rng->rng, interface);
+}
+
+static void *virtio_rng_device_create(void *virtio_dev,
+                                      QGuestAllocator *t_alloc,
+                                      void *addr)
+{
+    QVirtioRngDevice *virtio_rdevice = g_new0(QVirtioRngDevice, 1);
+    QVirtioRng *interface = &virtio_rdevice->rng;
+
+    interface->vdev = virtio_dev;
+
+    virtio_rdevice->obj.get_driver = qvirtio_rng_device_get_driver;
+
+    return &virtio_rdevice->obj;
+}
+
+/* virtio-rng-pci */
+static void *qvirtio_rng_pci_get_driver(void *object, const char *interface)
+{
+    QVirtioRngPCI *v_rng = object;
+    if (!g_strcmp0(interface, "pci-device")) {
+        return v_rng->pci_vdev.pdev;
+    }
+    return qvirtio_rng_get_driver(&v_rng->rng, interface);
+}
+
+static void *virtio_rng_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+                                   void *addr)
+{
+    QVirtioRngPCI *virtio_rpci = g_new0(QVirtioRngPCI, 1);
+    QVirtioRng *interface = &virtio_rpci->rng;
+    QOSGraphObject *obj = &virtio_rpci->pci_vdev.obj;
+
+    virtio_pci_init(&virtio_rpci->pci_vdev, pci_bus, addr);
+    interface->vdev = &virtio_rpci->pci_vdev.vdev;
+
+    obj->get_driver = qvirtio_rng_pci_get_driver;
+
+    return obj;
+}
+
+static void virtio_rng_register_nodes(void)
+{
+    QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(4, 0),
+    };
+
+    QOSGraphEdgeOptions opts = {
+        .extra_device_opts = "addr=04.0",
+    };
+
+    /* virtio-rng-device */
+    qos_node_create_driver("virtio-rng-device", virtio_rng_device_create);
+    qos_node_consumes("virtio-rng-device", "virtio-bus", NULL);
+    qos_node_produces("virtio-rng-device", "virtio");
+    qos_node_produces("virtio-rng-device", "virtio-rng");
+
+    /* virtio-rng-pci */
+    add_qpci_address(&opts, &addr);
+    qos_node_create_driver("virtio-rng-pci", virtio_rng_pci_create);
+    qos_node_consumes("virtio-rng-pci", "pci-bus", &opts);
+    qos_node_produces("virtio-rng-pci", "pci-device");
+    qos_node_produces("virtio-rng-pci", "virtio");
+    qos_node_produces("virtio-rng-pci", "virtio-rng");
+}
+
+libqos_init(virtio_rng_register_nodes);
diff --git a/tests/qtest/libqos/virtio-rng.h b/tests/qtest/libqos/virtio-rng.h
new file mode 100644 (file)
index 0000000..9e192f1
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_RNG_H
+#define TESTS_LIBQOS_VIRTIO_RNG_H
+
+#include "libqos/qgraph.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+
+typedef struct QVirtioRng QVirtioRng;
+typedef struct QVirtioRngPCI QVirtioRngPCI;
+typedef struct QVirtioRngDevice QVirtioRngDevice;
+
+struct QVirtioRng {
+    QVirtioDevice *vdev;
+};
+
+struct QVirtioRngPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVirtioRng rng;
+};
+
+struct QVirtioRngDevice {
+    QOSGraphObject obj;
+    QVirtioRng rng;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio-scsi.c b/tests/qtest/libqos/virtio-scsi.c
new file mode 100644 (file)
index 0000000..de739be
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "standard-headers/linux/virtio_ids.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-scsi.h"
+
+/* virtio-scsi-device */
+static void *qvirtio_scsi_get_driver(QVirtioSCSI *v_scsi,
+                                     const char *interface)
+{
+    if (!g_strcmp0(interface, "virtio-scsi")) {
+        return v_scsi;
+    }
+    if (!g_strcmp0(interface, "virtio")) {
+        return v_scsi->vdev;
+    }
+
+    fprintf(stderr, "%s not present in virtio-scsi-device\n", interface);
+    g_assert_not_reached();
+}
+
+static void *qvirtio_scsi_device_get_driver(void *object,
+                                            const char *interface)
+{
+    QVirtioSCSIDevice *v_scsi = object;
+    return qvirtio_scsi_get_driver(&v_scsi->scsi, interface);
+}
+
+static void *virtio_scsi_device_create(void *virtio_dev,
+                                          QGuestAllocator *t_alloc,
+                                          void *addr)
+{
+    QVirtioSCSIDevice *virtio_bdevice = g_new0(QVirtioSCSIDevice, 1);
+    QVirtioSCSI *interface = &virtio_bdevice->scsi;
+
+    interface->vdev = virtio_dev;
+
+    virtio_bdevice->obj.get_driver = qvirtio_scsi_device_get_driver;
+
+    return &virtio_bdevice->obj;
+}
+
+/* virtio-scsi-pci */
+static void *qvirtio_scsi_pci_get_driver(void *object,
+                                         const char *interface)
+{
+    QVirtioSCSIPCI *v_scsi = object;
+    if (!g_strcmp0(interface, "pci-device")) {
+        return v_scsi->pci_vdev.pdev;
+    }
+    return qvirtio_scsi_get_driver(&v_scsi->scsi, interface);
+}
+
+static void *virtio_scsi_pci_create(void *pci_bus,
+                                    QGuestAllocator *t_alloc,
+                                    void *addr)
+{
+    QVirtioSCSIPCI *virtio_spci = g_new0(QVirtioSCSIPCI, 1);
+    QVirtioSCSI *interface = &virtio_spci->scsi;
+    QOSGraphObject *obj = &virtio_spci->pci_vdev.obj;
+
+    virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr);
+    interface->vdev = &virtio_spci->pci_vdev.vdev;
+
+    g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_SCSI);
+
+    obj->get_driver = qvirtio_scsi_pci_get_driver;
+
+    return obj;
+}
+
+static void virtio_scsi_register_nodes(void)
+{
+    QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(4, 0),
+    };
+
+    QOSGraphEdgeOptions opts = {
+        .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,"
+                           "file.read-zeroes=on,format=raw",
+        .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0",
+    };
+
+    /* virtio-scsi-device */
+    opts.extra_device_opts = "id=vs0";
+    qos_node_create_driver("virtio-scsi-device",
+                            virtio_scsi_device_create);
+    qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts);
+    qos_node_produces("virtio-scsi-device", "virtio-scsi");
+
+    /* virtio-scsi-pci */
+    opts.extra_device_opts = "id=vs0,addr=04.0";
+    add_qpci_address(&opts, &addr);
+    qos_node_create_driver("virtio-scsi-pci", virtio_scsi_pci_create);
+    qos_node_consumes("virtio-scsi-pci", "pci-bus", &opts);
+    qos_node_produces("virtio-scsi-pci", "pci-device");
+    qos_node_produces("virtio-scsi-pci", "virtio-scsi");
+}
+
+libqos_init(virtio_scsi_register_nodes);
diff --git a/tests/qtest/libqos/virtio-scsi.h b/tests/qtest/libqos/virtio-scsi.h
new file mode 100644 (file)
index 0000000..4ca19a6
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_SCSI_H
+#define TESTS_LIBQOS_VIRTIO_SCSI_H
+
+#include "libqos/qgraph.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+
+typedef struct QVirtioSCSI QVirtioSCSI;
+typedef struct QVirtioSCSIPCI QVirtioSCSIPCI;
+typedef struct QVirtioSCSIDevice QVirtioSCSIDevice;
+
+struct QVirtioSCSI {
+    QVirtioDevice *vdev;
+};
+
+struct QVirtioSCSIPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVirtioSCSI scsi;
+};
+
+struct QVirtioSCSIDevice {
+    QOSGraphObject obj;
+    QVirtioSCSI scsi;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio-serial.c b/tests/qtest/libqos/virtio-serial.c
new file mode 100644 (file)
index 0000000..3e5b8b8
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "qemu/module.h"
+#include "libqos/qgraph.h"
+#include "libqos/virtio-serial.h"
+
+static void *qvirtio_serial_get_driver(QVirtioSerial *v_serial,
+                                       const char *interface)
+{
+    if (!g_strcmp0(interface, "virtio-serial")) {
+        return v_serial;
+    }
+    if (!g_strcmp0(interface, "virtio")) {
+        return v_serial->vdev;
+    }
+
+    fprintf(stderr, "%s not present in virtio-serial-device\n", interface);
+    g_assert_not_reached();
+}
+
+static void *qvirtio_serial_device_get_driver(void *object,
+                                              const char *interface)
+{
+    QVirtioSerialDevice *v_serial = object;
+    return qvirtio_serial_get_driver(&v_serial->serial, interface);
+}
+
+static void *virtio_serial_device_create(void *virtio_dev,
+                                         QGuestAllocator *t_alloc,
+                                         void *addr)
+{
+    QVirtioSerialDevice *virtio_device = g_new0(QVirtioSerialDevice, 1);
+    QVirtioSerial *interface = &virtio_device->serial;
+
+    interface->vdev = virtio_dev;
+
+    virtio_device->obj.get_driver = qvirtio_serial_device_get_driver;
+
+    return &virtio_device->obj;
+}
+
+/* virtio-serial-pci */
+static void *qvirtio_serial_pci_get_driver(void *object, const char *interface)
+{
+    QVirtioSerialPCI *v_serial = object;
+    if (!g_strcmp0(interface, "pci-device")) {
+        return v_serial->pci_vdev.pdev;
+    }
+    return qvirtio_serial_get_driver(&v_serial->serial, interface);
+}
+
+static void *virtio_serial_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+                                      void *addr)
+{
+    QVirtioSerialPCI *virtio_spci = g_new0(QVirtioSerialPCI, 1);
+    QVirtioSerial *interface = &virtio_spci->serial;
+    QOSGraphObject *obj = &virtio_spci->pci_vdev.obj;
+
+    virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr);
+    interface->vdev = &virtio_spci->pci_vdev.vdev;
+
+    obj->get_driver = qvirtio_serial_pci_get_driver;
+
+    return obj;
+}
+
+static void virtio_serial_register_nodes(void)
+{
+   QPCIAddress addr = {
+        .devfn = QPCI_DEVFN(4, 0),
+    };
+
+    QOSGraphEdgeOptions edge_opts = { };
+
+    /* virtio-serial-device */
+    edge_opts.extra_device_opts = "id=vser0";
+    qos_node_create_driver("virtio-serial-device",
+                            virtio_serial_device_create);
+    qos_node_consumes("virtio-serial-device", "virtio-bus", &edge_opts);
+    qos_node_produces("virtio-serial-device", "virtio");
+    qos_node_produces("virtio-serial-device", "virtio-serial");
+
+    /* virtio-serial-pci */
+    edge_opts.extra_device_opts = "id=vser0,addr=04.0";
+    add_qpci_address(&edge_opts, &addr);
+    qos_node_create_driver("virtio-serial-pci", virtio_serial_pci_create);
+    qos_node_consumes("virtio-serial-pci", "pci-bus", &edge_opts);
+    qos_node_produces("virtio-serial-pci", "pci-device");
+    qos_node_produces("virtio-serial-pci", "virtio");
+    qos_node_produces("virtio-serial-pci", "virtio-serial");
+}
+
+libqos_init(virtio_serial_register_nodes);
diff --git a/tests/qtest/libqos/virtio-serial.h b/tests/qtest/libqos/virtio-serial.h
new file mode 100644 (file)
index 0000000..080fa84
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_SERIAL_H
+#define TESTS_LIBQOS_VIRTIO_SERIAL_H
+
+#include "libqos/qgraph.h"
+#include "libqos/virtio.h"
+#include "libqos/virtio-pci.h"
+
+typedef struct QVirtioSerial QVirtioSerial;
+typedef struct QVirtioSerialPCI QVirtioSerialPCI;
+typedef struct QVirtioSerialDevice QVirtioSerialDevice;
+
+struct QVirtioSerial {
+    QVirtioDevice *vdev;
+};
+
+struct QVirtioSerialPCI {
+    QVirtioPCIDevice pci_vdev;
+    QVirtioSerial serial;
+};
+
+struct QVirtioSerialDevice {
+    QOSGraphObject obj;
+    QVirtioSerial serial;
+};
+
+#endif
diff --git a/tests/qtest/libqos/virtio.c b/tests/qtest/libqos/virtio.c
new file mode 100644 (file)
index 0000000..9aa3606
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * libqos virtio driver
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "libqtest.h"
+#include "libqos/virtio.h"
+#include "standard-headers/linux/virtio_config.h"
+#include "standard-headers/linux/virtio_ring.h"
+
+/*
+ * qtest_readX/writeX() functions transfer host endian from/to guest endian.
+ * This works great for Legacy VIRTIO devices where we need guest endian
+ * accesses.  For VIRTIO 1.0 the vring is little-endian so the automatic guest
+ * endianness conversion is not wanted.
+ *
+ * The following qvirtio_readX/writeX() functions handle Legacy and VIRTIO 1.0
+ * accesses seamlessly.
+ */
+static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr)
+{
+    uint16_t val = qtest_readw(qts, addr);
+
+    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
+        val = bswap16(val);
+    }
+    return val;
+}
+
+static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr)
+{
+    uint32_t val = qtest_readl(qts, addr);
+
+    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
+        val = bswap32(val);
+    }
+    return val;
+}
+
+static void qvirtio_writew(QVirtioDevice *d, QTestState *qts,
+                           uint64_t addr, uint16_t val)
+{
+    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
+        val = bswap16(val);
+    }
+    qtest_writew(qts, addr, val);
+}
+
+static void qvirtio_writel(QVirtioDevice *d, QTestState *qts,
+                           uint64_t addr, uint32_t val)
+{
+    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
+        val = bswap32(val);
+    }
+    qtest_writel(qts, addr, val);
+}
+
+static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts,
+                           uint64_t addr, uint64_t val)
+{
+    if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) {
+        val = bswap64(val);
+    }
+    qtest_writeq(qts, addr, val);
+}
+
+uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr)
+{
+    g_assert_true(d->features_negotiated);
+    return d->bus->config_readb(d, addr);
+}
+
+uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr)
+{
+    g_assert_true(d->features_negotiated);
+    return d->bus->config_readw(d, addr);
+}
+
+uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr)
+{
+    g_assert_true(d->features_negotiated);
+    return d->bus->config_readl(d, addr);
+}
+
+uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr)
+{
+    g_assert_true(d->features_negotiated);
+    return d->bus->config_readq(d, addr);
+}
+
+uint64_t qvirtio_get_features(QVirtioDevice *d)
+{
+    return d->bus->get_features(d);
+}
+
+void qvirtio_set_features(QVirtioDevice *d, uint64_t features)
+{
+    d->features = features;
+    d->bus->set_features(d, features);
+
+    /*
+     * This could be a separate function for drivers that want to access
+     * configuration space before setting FEATURES_OK, but no existing users
+     * need that and it's less code for callers if this is done implicitly.
+    */
+    if (features & (1ull << VIRTIO_F_VERSION_1)) {
+        uint8_t status = d->bus->get_status(d) |
+                         VIRTIO_CONFIG_S_FEATURES_OK;
+
+        d->bus->set_status(d, status);
+        g_assert_cmphex(d->bus->get_status(d), ==, status);
+    }
+
+    d->features_negotiated = true;
+}
+
+QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
+                             QGuestAllocator *alloc, uint16_t index)
+{
+    g_assert_true(d->features_negotiated);
+    return d->bus->virtqueue_setup(d, alloc, index);
+}
+
+void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
+                        QGuestAllocator *alloc)
+{
+    return bus->virtqueue_cleanup(vq, alloc);
+}
+
+void qvirtio_reset(QVirtioDevice *d)
+{
+    d->bus->set_status(d, 0);
+    g_assert_cmphex(d->bus->get_status(d), ==, 0);
+    d->features_negotiated = false;
+}
+
+void qvirtio_set_acknowledge(QVirtioDevice *d)
+{
+    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE);
+    g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE);
+}
+
+void qvirtio_set_driver(QVirtioDevice *d)
+{
+    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER);
+    g_assert_cmphex(d->bus->get_status(d), ==,
+                    VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE);
+}
+
+void qvirtio_set_driver_ok(QVirtioDevice *d)
+{
+    d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK);
+    g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK |
+                    VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE |
+                    (d->features & (1ull << VIRTIO_F_VERSION_1) ?
+                     VIRTIO_CONFIG_S_FEATURES_OK : 0));
+}
+
+void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d,
+                            QVirtQueue *vq, gint64 timeout_us)
+{
+    gint64 start_time = g_get_monotonic_time();
+
+    for (;;) {
+        qtest_clock_step(qts, 100);
+        if (d->bus->get_queue_isr_status(d, vq)) {
+            return;
+        }
+        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+    }
+}
+
+/* Wait for the status byte at given guest memory address to be set
+ *
+ * The virtqueue interrupt must not be raised, making this useful for testing
+ * event_index functionality.
+ */
+uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d,
+                                        QVirtQueue *vq,
+                                        uint64_t addr,
+                                        gint64 timeout_us)
+{
+    gint64 start_time = g_get_monotonic_time();
+    uint8_t val;
+
+    while ((val = qtest_readb(qts, addr)) == 0xff) {
+        qtest_clock_step(qts, 100);
+        g_assert(!d->bus->get_queue_isr_status(d, vq));
+        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+    }
+    return val;
+}
+
+/*
+ * qvirtio_wait_used_elem:
+ * @desc_idx: The next expected vq->desc[] index in the used ring
+ * @len: A pointer that is filled with the length written into the buffer, may
+ *       be NULL
+ * @timeout_us: How many microseconds to wait before failing
+ *
+ * This function waits for the next completed request on the used ring.
+ */
+void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d,
+                            QVirtQueue *vq,
+                            uint32_t desc_idx,
+                            uint32_t *len,
+                            gint64 timeout_us)
+{
+    gint64 start_time = g_get_monotonic_time();
+
+    for (;;) {
+        uint32_t got_desc_idx;
+
+        qtest_clock_step(qts, 100);
+
+        if (d->bus->get_queue_isr_status(d, vq) &&
+            qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) {
+            g_assert_cmpint(got_desc_idx, ==, desc_idx);
+            return;
+        }
+
+        g_assert(g_get_monotonic_time() - start_time <= timeout_us);
+    }
+}
+
+void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us)
+{
+    d->bus->wait_config_isr_status(d, timeout_us);
+}
+
+void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
+                 uint64_t addr)
+{
+    int i;
+
+    vq->desc = addr;
+    vq->avail = vq->desc + vq->size * sizeof(struct vring_desc);
+    vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
+        + vq->align - 1) & ~(vq->align - 1));
+
+    for (i = 0; i < vq->size - 1; i++) {
+        /* vq->desc[i].addr */
+        qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * i), 0);
+        /* vq->desc[i].next */
+        qvirtio_writew(vq->vdev, qts, vq->desc + (16 * i) + 14, i + 1);
+    }
+
+    /* vq->avail->flags */
+    qvirtio_writew(vq->vdev, qts, vq->avail, 0);
+    /* vq->avail->idx */
+    qvirtio_writew(vq->vdev, qts, vq->avail + 2, 0);
+    /* vq->avail->used_event */
+    qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), 0);
+
+    /* vq->used->flags */
+    qvirtio_writew(vq->vdev, qts, vq->used, 0);
+    /* vq->used->avail_event */
+    qvirtio_writew(vq->vdev, qts, vq->used + 2 +
+                   sizeof(struct vring_used_elem) * vq->size, 0);
+}
+
+QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
+                                               QGuestAllocator *alloc,
+                                               uint16_t elem)
+{
+    int i;
+    QVRingIndirectDesc *indirect = g_malloc(sizeof(*indirect));
+
+    indirect->index = 0;
+    indirect->elem = elem;
+    indirect->desc = guest_alloc(alloc, sizeof(struct vring_desc) * elem);
+
+    for (i = 0; i < elem - 1; ++i) {
+        /* indirect->desc[i].addr */
+        qvirtio_writeq(d, qs, indirect->desc + (16 * i), 0);
+        /* indirect->desc[i].flags */
+        qvirtio_writew(d, qs, indirect->desc + (16 * i) + 12,
+                       VRING_DESC_F_NEXT);
+        /* indirect->desc[i].next */
+        qvirtio_writew(d, qs, indirect->desc + (16 * i) + 14, i + 1);
+    }
+
+    return indirect;
+}
+
+void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
+                              QVRingIndirectDesc *indirect,
+                              uint64_t data, uint32_t len, bool write)
+{
+    uint16_t flags;
+
+    g_assert_cmpint(indirect->index, <, indirect->elem);
+
+    flags = qvirtio_readw(d, qts, indirect->desc +
+                                  (16 * indirect->index) + 12);
+
+    if (write) {
+        flags |= VRING_DESC_F_WRITE;
+    }
+
+    /* indirect->desc[indirect->index].addr */
+    qvirtio_writeq(d, qts, indirect->desc + (16 * indirect->index), data);
+    /* indirect->desc[indirect->index].len */
+    qvirtio_writel(d, qts, indirect->desc + (16 * indirect->index) + 8, len);
+    /* indirect->desc[indirect->index].flags */
+    qvirtio_writew(d, qts, indirect->desc + (16 * indirect->index) + 12,
+                   flags);
+
+    indirect->index++;
+}
+
+uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
+                        uint32_t len, bool write, bool next)
+{
+    uint16_t flags = 0;
+    vq->num_free--;
+
+    if (write) {
+        flags |= VRING_DESC_F_WRITE;
+    }
+
+    if (next) {
+        flags |= VRING_DESC_F_NEXT;
+    }
+
+    /* vq->desc[vq->free_head].addr */
+    qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head), data);
+    /* vq->desc[vq->free_head].len */
+    qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8, len);
+    /* vq->desc[vq->free_head].flags */
+    qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12, flags);
+
+    return vq->free_head++; /* Return and increase, in this order */
+}
+
+uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq,
+                                 QVRingIndirectDesc *indirect)
+{
+    g_assert(vq->indirect);
+    g_assert_cmpint(vq->size, >=, indirect->elem);
+    g_assert_cmpint(indirect->index, ==, indirect->elem);
+
+    vq->num_free--;
+
+    /* vq->desc[vq->free_head].addr */
+    qvirtio_writeq(vq->vdev, qts, vq->desc + (16 * vq->free_head),
+                   indirect->desc);
+    /* vq->desc[vq->free_head].len */
+    qvirtio_writel(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 8,
+                   sizeof(struct vring_desc) * indirect->elem);
+    /* vq->desc[vq->free_head].flags */
+    qvirtio_writew(vq->vdev, qts, vq->desc + (16 * vq->free_head) + 12,
+                   VRING_DESC_F_INDIRECT);
+
+    return vq->free_head++; /* Return and increase, in this order */
+}
+
+void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
+                     uint32_t free_head)
+{
+    /* vq->avail->idx */
+    uint16_t idx = qvirtio_readw(d, qts, vq->avail + 2);
+    /* vq->used->flags */
+    uint16_t flags;
+    /* vq->used->avail_event */
+    uint16_t avail_event;
+
+    /* vq->avail->ring[idx % vq->size] */
+    qvirtio_writew(d, qts, vq->avail + 4 + (2 * (idx % vq->size)), free_head);
+    /* vq->avail->idx */
+    qvirtio_writew(d, qts, vq->avail + 2, idx + 1);
+
+    /* Must read after idx is updated */
+    flags = qvirtio_readw(d, qts, vq->avail);
+    avail_event = qvirtio_readw(d, qts, vq->used + 4 +
+                                sizeof(struct vring_used_elem) * vq->size);
+
+    /* < 1 because we add elements to avail queue one by one */
+    if ((flags & VRING_USED_F_NO_NOTIFY) == 0 &&
+                            (!vq->event || (uint16_t)(idx-avail_event) < 1)) {
+        d->bus->virtqueue_kick(d, vq);
+    }
+}
+
+/*
+ * qvirtqueue_get_buf:
+ * @desc_idx: A pointer that is filled with the vq->desc[] index, may be NULL
+ * @len: A pointer that is filled with the length written into the buffer, may
+ *       be NULL
+ *
+ * This function gets the next used element if there is one ready.
+ *
+ * Returns: true if an element was ready, false otherwise
+ */
+bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
+                        uint32_t *len)
+{
+    uint16_t idx;
+    uint64_t elem_addr, addr;
+
+    idx = qvirtio_readw(vq->vdev, qts,
+                        vq->used + offsetof(struct vring_used, idx));
+    if (idx == vq->last_used_idx) {
+        return false;
+    }
+
+    elem_addr = vq->used +
+        offsetof(struct vring_used, ring) +
+        (vq->last_used_idx % vq->size) *
+        sizeof(struct vring_used_elem);
+
+    if (desc_idx) {
+        addr = elem_addr + offsetof(struct vring_used_elem, id);
+        *desc_idx = qvirtio_readl(vq->vdev, qts, addr);
+    }
+
+    if (len) {
+        addr = elem_addr + offsetof(struct vring_used_elem, len);
+        *len = qvirtio_readw(vq->vdev, qts, addr);
+    }
+
+    vq->last_used_idx++;
+    return true;
+}
+
+void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx)
+{
+    g_assert(vq->event);
+
+    /* vq->avail->used_event */
+    qvirtio_writew(vq->vdev, qts, vq->avail + 4 + (2 * vq->size), idx);
+}
+
+void qvirtio_start_device(QVirtioDevice *vdev)
+{
+    qvirtio_reset(vdev);
+    qvirtio_set_acknowledge(vdev);
+    qvirtio_set_driver(vdev);
+}
+
+bool qvirtio_is_big_endian(QVirtioDevice *d)
+{
+    return d->big_endian;
+}
diff --git a/tests/qtest/libqos/virtio.h b/tests/qtest/libqos/virtio.h
new file mode 100644 (file)
index 0000000..529ef75
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * libqos virtio definitions
+ *
+ * Copyright (c) 2014 Marc MarĂ­
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LIBQOS_VIRTIO_H
+#define LIBQOS_VIRTIO_H
+
+#include "libqos/malloc.h"
+#include "standard-headers/linux/virtio_ring.h"
+
+#define QVIRTIO_F_BAD_FEATURE           0x40000000ull
+
+typedef struct QVirtioBus QVirtioBus;
+
+typedef struct QVirtioDevice {
+    const QVirtioBus *bus;
+    /* Device type */
+    uint16_t device_type;
+    uint64_t features;
+    bool big_endian;
+    bool features_negotiated;
+} QVirtioDevice;
+
+typedef struct QVirtQueue {
+    QVirtioDevice *vdev;
+    uint64_t desc; /* This points to an array of struct vring_desc */
+    uint64_t avail; /* This points to a struct vring_avail */
+    uint64_t used; /* This points to a struct vring_used */
+    uint16_t index;
+    uint32_t size;
+    uint32_t free_head;
+    uint32_t num_free;
+    uint32_t align;
+    uint16_t last_used_idx;
+    bool indirect;
+    bool event;
+} QVirtQueue;
+
+typedef struct QVRingIndirectDesc {
+    uint64_t desc; /* This points to an array fo struct vring_desc */
+    uint16_t index;
+    uint16_t elem;
+} QVRingIndirectDesc;
+
+struct QVirtioBus {
+    uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr);
+    uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr);
+    uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr);
+    uint64_t (*config_readq)(QVirtioDevice *d, uint64_t addr);
+
+    /* Get features of the device */
+    uint64_t (*get_features)(QVirtioDevice *d);
+
+    /* Set features of the device */
+    void (*set_features)(QVirtioDevice *d, uint64_t features);
+
+    /* Get features of the guest */
+    uint64_t (*get_guest_features)(QVirtioDevice *d);
+
+    /* Get status of the device */
+    uint8_t (*get_status)(QVirtioDevice *d);
+
+    /* Set status of the device  */
+    void (*set_status)(QVirtioDevice *d, uint8_t status);
+
+    /* Get the queue ISR status of the device */
+    bool (*get_queue_isr_status)(QVirtioDevice *d, QVirtQueue *vq);
+
+    /* Wait for the configuration ISR status of the device */
+    void (*wait_config_isr_status)(QVirtioDevice *d, gint64 timeout_us);
+
+    /* Select a queue to work on */
+    void (*queue_select)(QVirtioDevice *d, uint16_t index);
+
+    /* Get the size of the selected queue */
+    uint16_t (*get_queue_size)(QVirtioDevice *d);
+
+    /* Set the address of the selected queue */
+    void (*set_queue_address)(QVirtioDevice *d, QVirtQueue *vq);
+
+    /* Setup the virtqueue specified by index */
+    QVirtQueue *(*virtqueue_setup)(QVirtioDevice *d, QGuestAllocator *alloc,
+                                                                uint16_t index);
+
+    /* Free virtqueue resources */
+    void (*virtqueue_cleanup)(QVirtQueue *vq, QGuestAllocator *alloc);
+
+    /* Notify changes in virtqueue */
+    void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq);
+};
+
+static inline uint32_t qvring_size(uint32_t num, uint32_t align)
+{
+    return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num)
+        + align - 1) & ~(align - 1))
+        + sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num;
+}
+
+uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr);
+uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr);
+uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr);
+uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr);
+uint64_t qvirtio_get_features(QVirtioDevice *d);
+void qvirtio_set_features(QVirtioDevice *d, uint64_t features);
+bool qvirtio_is_big_endian(QVirtioDevice *d);
+
+void qvirtio_reset(QVirtioDevice *d);
+void qvirtio_set_acknowledge(QVirtioDevice *d);
+void qvirtio_set_driver(QVirtioDevice *d);
+void qvirtio_set_driver_ok(QVirtioDevice *d);
+
+void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d,
+                            QVirtQueue *vq, gint64 timeout_us);
+uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d,
+                                        QVirtQueue *vq,
+                                        uint64_t addr,
+                                        gint64 timeout_us);
+void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d,
+                            QVirtQueue *vq,
+                            uint32_t desc_idx,
+                            uint32_t *len,
+                            gint64 timeout_us);
+void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us);
+QVirtQueue *qvirtqueue_setup(QVirtioDevice *d,
+                             QGuestAllocator *alloc, uint16_t index);
+void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq,
+                        QGuestAllocator *alloc);
+
+void qvring_init(QTestState *qts, const QGuestAllocator *alloc, QVirtQueue *vq,
+                 uint64_t addr);
+QVRingIndirectDesc *qvring_indirect_desc_setup(QTestState *qs, QVirtioDevice *d,
+                                               QGuestAllocator *alloc,
+                                               uint16_t elem);
+void qvring_indirect_desc_add(QVirtioDevice *d, QTestState *qts,
+                              QVRingIndirectDesc *indirect,
+                              uint64_t data, uint32_t len, bool write);
+uint32_t qvirtqueue_add(QTestState *qts, QVirtQueue *vq, uint64_t data,
+                        uint32_t len, bool write, bool next);
+uint32_t qvirtqueue_add_indirect(QTestState *qts, QVirtQueue *vq,
+                                 QVRingIndirectDesc *indirect);
+void qvirtqueue_kick(QTestState *qts, QVirtioDevice *d, QVirtQueue *vq,
+                     uint32_t free_head);
+bool qvirtqueue_get_buf(QTestState *qts, QVirtQueue *vq, uint32_t *desc_idx,
+                        uint32_t *len);
+
+void qvirtqueue_set_used_event(QTestState *qts, QVirtQueue *vq, uint16_t idx);
+
+void qvirtio_start_device(QVirtioDevice *vdev);
+
+#endif
diff --git a/tests/qtest/libqos/x86_64_pc-machine.c b/tests/qtest/libqos/x86_64_pc-machine.c
new file mode 100644 (file)
index 0000000..6dfa705
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * libqos driver framework
+ *
+ * Copyright (c) 2018 Emanuele Giuseppe Esposito <e.emanuelegiuseppe@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest.h"
+#include "libqos/qgraph.h"
+#include "pci-pc.h"
+#include "qemu/module.h"
+#include "malloc-pc.h"
+
+typedef struct QX86PCMachine QX86PCMachine;
+typedef struct i440FX_pcihost i440FX_pcihost;
+typedef struct QSDHCI_PCI  QSDHCI_PCI;
+
+struct i440FX_pcihost {
+    QOSGraphObject obj;
+    QPCIBusPC pci;
+};
+
+struct QX86PCMachine {
+    QOSGraphObject obj;
+    QGuestAllocator alloc;
+    i440FX_pcihost bridge;
+};
+
+/* i440FX_pcihost */
+
+static QOSGraphObject *i440FX_host_get_device(void *obj, const char *device)
+{
+    i440FX_pcihost *host = obj;
+    if (!g_strcmp0(device, "pci-bus-pc")) {
+        return &host->pci.obj;
+    }
+    fprintf(stderr, "%s not present in i440FX-pcihost\n", device);
+    g_assert_not_reached();
+}
+
+static void qos_create_i440FX_host(i440FX_pcihost *host,
+                                   QTestState *qts,
+                                   QGuestAllocator *alloc)
+{
+    host->obj.get_device = i440FX_host_get_device;
+    qpci_init_pc(&host->pci, qts, alloc);
+}
+
+/* x86_64/pc machine */
+
+static void pc_destructor(QOSGraphObject *obj)
+{
+    QX86PCMachine *machine = (QX86PCMachine *) obj;
+    alloc_destroy(&machine->alloc);
+}
+
+static void *pc_get_driver(void *object, const char *interface)
+{
+    QX86PCMachine *machine = object;
+    if (!g_strcmp0(interface, "memory")) {
+        return &machine->alloc;
+    }
+
+    fprintf(stderr, "%s not present in x86_64/pc\n", interface);
+    g_assert_not_reached();
+}
+
+static QOSGraphObject *pc_get_device(void *obj, const char *device)
+{
+    QX86PCMachine *machine = obj;
+    if (!g_strcmp0(device, "i440FX-pcihost")) {
+        return &machine->bridge.obj;
+    }
+
+    fprintf(stderr, "%s not present in x86_64/pc\n", device);
+    g_assert_not_reached();
+}
+
+static void *qos_create_machine_pc(QTestState *qts)
+{
+    QX86PCMachine *machine = g_new0(QX86PCMachine, 1);
+    machine->obj.get_device = pc_get_device;
+    machine->obj.get_driver = pc_get_driver;
+    machine->obj.destructor = pc_destructor;
+    pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS);
+    qos_create_i440FX_host(&machine->bridge, qts, &machine->alloc);
+
+    return &machine->obj;
+}
+
+static void pc_machine_register_nodes(void)
+{
+    qos_node_create_machine("i386/pc", qos_create_machine_pc);
+    qos_node_contains("i386/pc", "i440FX-pcihost", NULL);
+
+    qos_node_create_machine("x86_64/pc", qos_create_machine_pc);
+    qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL);
+
+    qos_node_create_driver("i440FX-pcihost", NULL);
+    qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL);
+}
+
+libqos_init(pc_machine_register_nodes);