[libvirt] [PATCH 05/10] qemu: Reorganize -cpu arguments

John Ferlan jferlan at redhat.com
Wed Feb 17 00:44:15 UTC 2016


Reorganize the module to put all the -cpu argument processing code
together after the -machine to form a logical order of processing for
qemuBuildCommandLine working top down in the module.

Signed-off-by: John Ferlan <jferlan at redhat.com>
---
 src/qemu/qemu_command.c | 2798 ++++++++++++++++++++++++-----------------------
 1 file changed, 1409 insertions(+), 1389 deletions(-)

diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index ede651a..9dbc4a3 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -383,1012 +383,1419 @@ qemuBuildMachineCommandLine(virCommandPtr cmd,
     return 0;
 }
 
+
+/** Start -cpu arguments */
 static int
-qemuBuildObjectCommandLinePropsInternal(const char *key,
-                                        const virJSONValue *value,
-                                        virBufferPtr buf,
-                                        bool nested)
+qemuBuildCpuModelArgStr(virQEMUDriverPtr driver,
+                        const virDomainDef *def,
+                        virBufferPtr buf,
+                        virQEMUCapsPtr qemuCaps,
+                        bool *hasHwVirt,
+                        bool migrating)
 {
-    virJSONValuePtr elem;
-    virBitmapPtr bitmap = NULL;
-    ssize_t pos = -1;
-    ssize_t end;
+    int ret = -1;
     size_t i;
+    virCPUDefPtr host = NULL;
+    virCPUDefPtr guest = NULL;
+    virCPUDefPtr cpu = NULL;
+    virCPUDefPtr featCpu = NULL;
+    size_t ncpus = 0;
+    char **cpus = NULL;
+    virCPUDataPtr data = NULL;
+    virCPUDataPtr hostData = NULL;
+    char *compare_msg = NULL;
+    virCPUCompareResult cmp;
+    const char *preferred;
+    virCapsPtr caps = NULL;
+    bool compareAgainstHost = ((def->virtType == VIR_DOMAIN_VIRT_KVM ||
+                                def->cpu->mode != VIR_CPU_MODE_CUSTOM) &&
+                               def->cpu->mode != VIR_CPU_MODE_HOST_PASSTHROUGH);
 
-    switch ((virJSONType) value->type) {
-    case VIR_JSON_TYPE_STRING:
-        virBufferAsprintf(buf, ",%s=%s", key, value->data.string);
-        break;
+    if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
+        goto cleanup;
 
-    case VIR_JSON_TYPE_NUMBER:
-        virBufferAsprintf(buf, ",%s=%s", key, value->data.number);
-        break;
+    host = caps->host.cpu;
 
-    case VIR_JSON_TYPE_BOOLEAN:
-        if (value->data.boolean)
-            virBufferAsprintf(buf, ",%s=yes", key);
-        else
-            virBufferAsprintf(buf, ",%s=no", key);
+    if (!host ||
+        !host->model ||
+        (ncpus = virQEMUCapsGetCPUDefinitions(qemuCaps, &cpus)) == 0) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("CPU specification not supported by hypervisor"));
+        goto cleanup;
+    }
 
-        break;
+    if (!(cpu = virCPUDefCopy(def->cpu)))
+        goto cleanup;
 
-    case VIR_JSON_TYPE_ARRAY:
-        if (nested) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("nested -object property arrays are not supported"));
-            return -1;
-        }
+    if (cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
+        !migrating &&
+        cpuUpdate(cpu, host) < 0)
+        goto cleanup;
 
-        if (virJSONValueGetArrayAsBitmap(value, &bitmap) == 0) {
-            while ((pos = virBitmapNextSetBit(bitmap, pos)) > -1) {
-                if ((end = virBitmapNextClearBit(bitmap, pos)) < 0)
-                    end = virBitmapLastSetBit(bitmap) + 1;
+    /* For non-KVM, CPU features are emulated, so host compat doesn't matter */
+    if (compareAgainstHost) {
+        bool noTSX = false;
 
-                if (end - 1 > pos) {
-                    virBufferAsprintf(buf, ",%s=%zd-%zd", key, pos, end - 1);
-                    pos = end;
+        cmp = cpuGuestData(host, cpu, &data, &compare_msg);
+        switch (cmp) {
+        case VIR_CPU_COMPARE_INCOMPATIBLE:
+            if (cpuEncode(host->arch, host, NULL, &hostData,
+                          NULL, NULL, NULL, NULL) == 0 &&
+                (!cpuHasFeature(hostData, "hle") ||
+                 !cpuHasFeature(hostData, "rtm")) &&
+                (STREQ_NULLABLE(cpu->model, "Haswell") ||
+                 STREQ_NULLABLE(cpu->model, "Broadwell")))
+                noTSX = true;
+
+            if (compare_msg) {
+                if (noTSX) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("guest and host CPU are not compatible: "
+                                     "%s; try using '%s-noTSX' CPU model"),
+                                   compare_msg, cpu->model);
                 } else {
-                    virBufferAsprintf(buf, ",%s=%zd", key, pos);
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("guest and host CPU are not compatible: "
+                                     "%s"),
+                                   compare_msg);
+                }
+            } else {
+                if (noTSX) {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                                   _("guest CPU is not compatible with host "
+                                     "CPU; try using '%s-noTSX' CPU model"),
+                                   cpu->model);
+                } else {
+                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                                   _("guest CPU is not compatible with host "
+                                     "CPU"));
                 }
             }
-        } else {
-            /* fallback, treat the array as a non-bitmap, adding the key
-             * for each member */
-            for (i = 0; i < virJSONValueArraySize(value); i++) {
-                elem = virJSONValueArrayGet((virJSONValuePtr)value, i);
+            /* fall through */
+        case VIR_CPU_COMPARE_ERROR:
+            goto cleanup;
 
-                /* recurse to avoid duplicating code */
-                if (qemuBuildObjectCommandLinePropsInternal(key, elem, buf,
-                                                            true) < 0)
-                    return -1;
-            }
+        default:
+            break;
         }
-        break;
+    }
 
-    case VIR_JSON_TYPE_OBJECT:
-    case VIR_JSON_TYPE_NULL:
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("NULL and OBJECT JSON types can't be converted to "
-                         "commandline string"));
-        return -1;
+    /* Only 'svm' requires --enable-nesting. The nested
+     * 'vmx' patches now simply hook off the CPU features
+     */
+    if ((def->os.arch == VIR_ARCH_X86_64 || def->os.arch == VIR_ARCH_I686) &&
+         compareAgainstHost) {
+        int hasSVM = cpuHasFeature(data, "svm");
+        if (hasSVM < 0)
+            goto cleanup;
+        *hasHwVirt = hasSVM > 0 ? true : false;
     }
 
-    virBitmapFree(bitmap);
-    return 0;
-}
+    if ((cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) ||
+        ((cpu->mode == VIR_CPU_MODE_HOST_MODEL) &&
+          ARCH_IS_PPC64(def->os.arch))) {
+        const char *mode = virCPUModeTypeToString(cpu->mode);
+        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_HOST)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("CPU mode '%s' is not supported by QEMU"
+                             " binary"), mode);
+            goto cleanup;
+        }
+        if (def->virtType != VIR_DOMAIN_VIRT_KVM) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("CPU mode '%s' is only supported with kvm"),
+                           mode);
+            goto cleanup;
+        }
+        virBufferAddLit(buf, "host");
 
+        if (def->os.arch == VIR_ARCH_ARMV7L &&
+            host->arch == VIR_ARCH_AARCH64) {
+            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_AARCH64_OFF)) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("QEMU binary does not support CPU "
+                                 "host-passthrough for armv7l on "
+                                 "aarch64 host"));
+                goto cleanup;
+            }
 
-static int
-qemuBuildObjectCommandLineProps(const char *key,
-                                const virJSONValue *value,
-                                void *opaque)
-{
-    return qemuBuildObjectCommandLinePropsInternal(key, value, opaque, false);
-}
+            virBufferAddLit(buf, ",aarch64=off");
+        }
 
+        if (ARCH_IS_PPC64(def->os.arch) &&
+            cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
+            def->cpu->model != NULL) {
+            virBufferAsprintf(buf, ",compat=%s", def->cpu->model);
+        } else {
+            featCpu = cpu;
+        }
 
-char *
-qemuBuildObjectCommandlineFromJSON(const char *type,
-                                   const char *alias,
-                                   virJSONValuePtr props)
-{
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    char *ret = NULL;
+    } else {
+        if (VIR_ALLOC(guest) < 0)
+            goto cleanup;
+        if (VIR_STRDUP(guest->vendor_id, cpu->vendor_id) < 0)
+            goto cleanup;
 
-    virBufferAsprintf(&buf, "%s,id=%s", type, alias);
+        if (compareAgainstHost) {
+            guest->arch = host->arch;
+            if (cpu->match == VIR_CPU_MATCH_MINIMUM)
+                preferred = host->model;
+            else
+                preferred = cpu->model;
 
-    if (virJSONValueObjectForeachKeyValue(props,
-                                          qemuBuildObjectCommandLineProps,
-                                          &buf) < 0)
-        goto cleanup;
+            guest->type = VIR_CPU_TYPE_GUEST;
+            guest->fallback = cpu->fallback;
+            if (cpuDecode(guest, data,
+                          (const char **)cpus, ncpus, preferred) < 0)
+                goto cleanup;
+        } else {
+            guest->arch = def->os.arch;
+            if (VIR_STRDUP(guest->model, cpu->model) < 0)
+                goto cleanup;
+        }
+        virBufferAdd(buf, guest->model, -1);
+        if (guest->vendor_id)
+            virBufferAsprintf(buf, ",vendor=%s", guest->vendor_id);
+        featCpu = guest;
+    }
 
-    if (virBufferCheckError(&buf) < 0)
-        goto cleanup;
+    if (featCpu) {
+        for (i = 0; i < featCpu->nfeatures; i++) {
+            char sign;
+            if (featCpu->features[i].policy == VIR_CPU_FEATURE_DISABLE)
+                sign = '-';
+            else
+                sign = '+';
 
-    ret = virBufferContentAndReset(&buf);
+            virBufferAsprintf(buf, ",%c%s", sign, featCpu->features[i].name);
+        }
+    }
 
+    ret = 0;
  cleanup:
-    virBufferFreeAndReset(&buf);
+    virObjectUnref(caps);
+    VIR_FREE(compare_msg);
+    cpuDataFree(data);
+    cpuDataFree(hostData);
+    virCPUDefFree(guest);
+    virCPUDefFree(cpu);
     return ret;
 }
 
 
 static int
-qemuBuildDeviceAddressStr(virBufferPtr buf,
-                          virDomainDefPtr domainDef,
-                          virDomainDeviceInfoPtr info,
-                          virQEMUCapsPtr qemuCaps)
+qemuBuildCpuArgStr(virQEMUDriverPtr driver,
+                   const virDomainDef *def,
+                   virQEMUCapsPtr qemuCaps,
+                   char **opt,
+                   bool *hasHwVirt,
+                   bool migrating)
 {
-    int ret = -1;
-    char *devStr = NULL;
-    const char *contAlias = NULL;
+    virArch hostarch = virArchFromHost();
+    const char *default_model;
+    bool have_cpu = false;
+    int ret = -1;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    size_t i;
 
-    if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
-        size_t i;
+    *hasHwVirt = false;
 
-        if (!(devStr = virDomainPCIAddressAsString(&info->addr.pci)))
-            goto cleanup;
-        for (i = 0; i < domainDef->ncontrollers; i++) {
-            virDomainControllerDefPtr cont = domainDef->controllers[i];
+    if (def->os.arch == VIR_ARCH_I686)
+        default_model = "qemu32";
+    else
+        default_model = "qemu64";
 
-            if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
-                cont->idx == info->addr.pci.bus) {
-                contAlias = cont->info.alias;
-                if (!contAlias) {
-                    virReportError(VIR_ERR_INTERNAL_ERROR,
-                                   _("Device alias was not set for PCI "
-                                     "controller with index %u required "
-                                     "for device at address %s"),
-                                   info->addr.pci.bus, devStr);
-                    goto cleanup;
-                }
-                break;
-            }
-        }
-        if (!contAlias) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Could not find PCI "
-                             "controller with index %u required "
-                             "for device at address %s"),
-                           info->addr.pci.bus, devStr);
+    if (def->cpu &&
+        (def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) {
+        if (qemuBuildCpuModelArgStr(driver, def, &buf, qemuCaps,
+                                    hasHwVirt, migrating) < 0)
             goto cleanup;
+        have_cpu = true;
+    } else {
+        /*
+         * Need to force a 32-bit guest CPU type if
+         *
+         *  1. guest OS is i686
+         *  2. host OS is x86_64
+         *  3. emulator is qemu-kvm or kvm
+         *
+         * Or
+         *
+         *  1. guest OS is i686
+         *  2. emulator is qemu-system-x86_64
+         */
+        if (def->os.arch == VIR_ARCH_I686 &&
+            ((hostarch == VIR_ARCH_X86_64 &&
+              strstr(def->emulator, "kvm")) ||
+             strstr(def->emulator, "x86_64"))) {
+            virBufferAdd(&buf, default_model, -1);
+            have_cpu = true;
         }
+    }
 
-        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
-            if (info->addr.pci.function != 0) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("Only PCI device addresses with function=0 "
-                                 "are supported with this QEMU binary"));
-                goto cleanup;
-            }
-            if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("'multifunction=on' is not supported with "
-                                 "this QEMU binary"));
-                goto cleanup;
-            }
-        }
+    /* Handle paravirtual timers  */
+    for (i = 0; i < def->clock.ntimers; i++) {
+        virDomainTimerDefPtr timer = def->clock.timers[i];
 
-        if (info->addr.pci.bus != 0 &&
-            !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("Multiple PCI buses are not supported "
-                             "with this QEMU binary"));
-            goto cleanup;
+        if (timer->present == -1)
+            continue;
+
+        if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK) {
+            virBufferAsprintf(&buf, "%s,%ckvmclock",
+                              have_cpu ? "" : default_model,
+                              timer->present ? '+' : '-');
+            have_cpu = true;
+        } else if (timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK &&
+                   timer->present) {
+            virBufferAsprintf(&buf, "%s,hv_time",
+                              have_cpu ? "" : default_model);
+            have_cpu = true;
         }
-        virBufferAsprintf(buf, ",bus=%s", contAlias);
+    }
 
-        if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON)
-            virBufferAddLit(buf, ",multifunction=on");
-        else if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_OFF)
-            virBufferAddLit(buf, ",multifunction=off");
-        virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot);
-        if (info->addr.pci.function != 0)
-           virBufferAsprintf(buf, ".0x%x", info->addr.pci.function);
-    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
-        if (!(contAlias = virDomainControllerAliasFind(domainDef,
-                                                       VIR_DOMAIN_CONTROLLER_TYPE_USB,
-                                                       info->addr.usb.bus)))
-            goto cleanup;
-        virBufferAsprintf(buf, ",bus=%s.0,port=%s", contAlias, info->addr.usb.port);
-    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) {
-        if (info->addr.spaprvio.has_reg)
-            virBufferAsprintf(buf, ",reg=0x%llx", info->addr.spaprvio.reg);
-    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
-        if (info->addr.ccw.assigned)
-            virBufferAsprintf(buf, ",devno=%x.%x.%04x",
-                              info->addr.ccw.cssid,
-                              info->addr.ccw.ssid,
-                              info->addr.ccw.devno);
-    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA) {
-        virBufferAsprintf(buf, ",iobase=0x%x,irq=0x%x",
-                          info->addr.isa.iobase,
-                          info->addr.isa.irq);
+    if (def->apic_eoi) {
+        char sign;
+        if (def->apic_eoi == VIR_TRISTATE_SWITCH_ON)
+            sign = '+';
+        else
+            sign = '-';
+
+        virBufferAsprintf(&buf, "%s,%ckvm_pv_eoi",
+                          have_cpu ? "" : default_model,
+                          sign);
+        have_cpu = true;
     }
 
-    ret = 0;
- cleanup:
-    VIR_FREE(devStr);
-    return ret;
-}
+    if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK]) {
+        char sign;
+        if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK] == VIR_TRISTATE_SWITCH_ON)
+            sign = '+';
+        else
+            sign = '-';
 
-static int
-qemuBuildRomStr(virBufferPtr buf,
-                virDomainDeviceInfoPtr info,
-                virQEMUCapsPtr qemuCaps)
-{
-    if (info->rombar || info->romfile) {
-        if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           "%s", _("rombar and romfile are supported only for PCI devices"));
-            return -1;
+        virBufferAsprintf(&buf, "%s,%ckvm_pv_unhalt",
+                          have_cpu ? "" : default_model,
+                          sign);
+        have_cpu = true;
+    }
+
+    if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) {
+        if (!have_cpu) {
+            virBufferAdd(&buf, default_model, -1);
+            have_cpu = true;
         }
-        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_ROMBAR)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           "%s", _("rombar and romfile not supported in this QEMU binary"));
-            return -1;
+
+        for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) {
+            switch ((virDomainHyperv) i) {
+            case VIR_DOMAIN_HYPERV_RELAXED:
+            case VIR_DOMAIN_HYPERV_VAPIC:
+                if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON)
+                    virBufferAsprintf(&buf, ",hv_%s",
+                                      virDomainHypervTypeToString(i));
+                break;
+
+            case VIR_DOMAIN_HYPERV_SPINLOCKS:
+                if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON)
+                    virBufferAsprintf(&buf, ",hv_spinlocks=0x%x",
+                                      def->hyperv_spinlocks);
+                break;
+
+            /* coverity[dead_error_begin] */
+            case VIR_DOMAIN_HYPERV_LAST:
+                break;
+            }
         }
+    }
 
-        switch (info->rombar) {
-        case VIR_TRISTATE_SWITCH_OFF:
-            virBufferAddLit(buf, ",rombar=0");
-            break;
-        case VIR_TRISTATE_SWITCH_ON:
-            virBufferAddLit(buf, ",rombar=1");
-            break;
-        default:
+    for (i = 0; i < def->npanics; i++) {
+        if (def->panics[i]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV) {
+            if (!have_cpu) {
+                virBufferAdd(&buf, default_model, -1);
+                have_cpu = true;
+            }
+
+            virBufferAddLit(&buf, ",hv_crash");
             break;
         }
-        if (info->romfile)
-           virBufferAsprintf(buf, ",romfile=%s", info->romfile);
     }
-    return 0;
-}
 
-static int
-qemuBuildIoEventFdStr(virBufferPtr buf,
-                      virTristateSwitch use,
-                      virQEMUCapsPtr qemuCaps)
-{
-    if (use && virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_IOEVENTFD))
-        virBufferAsprintf(buf, ",ioeventfd=%s",
-                          virTristateSwitchTypeToString(use));
-    return 0;
-}
+    if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
+        if (!have_cpu) {
+            virBufferAdd(&buf, default_model, -1);
+            have_cpu = true;
+        }
 
-#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \
-  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ "
+        for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) {
+            switch ((virDomainKVM) i) {
+            case VIR_DOMAIN_KVM_HIDDEN:
+                if (def->kvm_features[i] == VIR_TRISTATE_SWITCH_ON)
+                    virBufferAddLit(&buf, ",kvm=off");
+                break;
 
-static int
-qemuSafeSerialParamValue(const char *value)
-{
-    if (strspn(value, QEMU_SERIAL_PARAM_ACCEPTED_CHARS) != strlen(value)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("driver serial '%s' contains unsafe characters"),
-                       value);
-        return -1;
+            /* coverity[dead_error_begin] */
+            case VIR_DOMAIN_KVM_LAST:
+                break;
+            }
+        }
     }
 
-    return 0;
-}
-
-static char *
-qemuGetSecretString(virConnectPtr conn,
-                    const char *scheme,
-                    bool encoded,
-                    virStorageAuthDefPtr authdef,
-                    virSecretUsageType secretUsageType)
-{
-    size_t secret_size;
-    virSecretPtr sec = NULL;
-    char *secret = NULL;
-    char uuidStr[VIR_UUID_STRING_BUFLEN];
-
-    /* look up secret */
-    switch (authdef->secretType) {
-    case VIR_STORAGE_SECRET_TYPE_UUID:
-        sec = virSecretLookupByUUID(conn, authdef->secret.uuid);
-        virUUIDFormat(authdef->secret.uuid, uuidStr);
-        break;
-    case VIR_STORAGE_SECRET_TYPE_USAGE:
-        sec = virSecretLookupByUsage(conn, secretUsageType,
-                                     authdef->secret.usage);
-        break;
-    }
+    if (def->features[VIR_DOMAIN_FEATURE_PMU]) {
+        virTristateSwitch pmu = def->features[VIR_DOMAIN_FEATURE_PMU];
+        if (!have_cpu)
+            virBufferAdd(&buf, default_model, -1);
 
-    if (!sec) {
-        if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
-            virReportError(VIR_ERR_NO_SECRET,
-                           _("%s no secret matches uuid '%s'"),
-                           scheme, uuidStr);
-        } else {
-            virReportError(VIR_ERR_NO_SECRET,
-                           _("%s no secret matches usage value '%s'"),
-                           scheme, authdef->secret.usage);
-        }
-        goto cleanup;
+        virBufferAsprintf(&buf, ",pmu=%s",
+                          virTristateSwitchTypeToString(pmu));
+        have_cpu = true;
     }
 
-    secret = (char *)conn->secretDriver->secretGetValue(sec, &secret_size, 0,
-                                                        VIR_SECRET_GET_VALUE_INTERNAL_CALL);
-    if (!secret) {
-        if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("could not get value of the secret for "
-                             "username '%s' using uuid '%s'"),
-                           authdef->username, uuidStr);
-        } else {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("could not get value of the secret for "
-                             "username '%s' using usage value '%s'"),
-                           authdef->username, authdef->secret.usage);
-        }
+    if (virBufferCheckError(&buf) < 0)
         goto cleanup;
-    }
 
-    if (encoded) {
-        char *base64 = NULL;
+    *opt = virBufferContentAndReset(&buf);
 
-        base64_encode_alloc(secret, secret_size, &base64);
-        VIR_FREE(secret);
-        if (!base64) {
-            virReportOOMError();
-            goto cleanup;
-        }
-        secret = base64;
-    }
+    ret = 0;
 
  cleanup:
-    virObjectUnref(sec);
-    return secret;
+    return ret;
 }
 
 
 static int
-qemuNetworkDriveGetPort(int protocol,
-                        const char *port)
+qemuBuildCpuCommandLine(virCommandPtr cmd,
+                        virQEMUDriverPtr driver,
+                        const virDomainDef *def,
+                        virQEMUCapsPtr qemuCaps,
+                        bool migrating)
 {
-    int ret = 0;
+    char *cpu;
+    bool hasHwVirt = false;
 
-    if (port) {
-        if (virStrToLong_i(port, NULL, 10, &ret) < 0 || ret < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("failed to parse port number '%s'"),
-                           port);
-            return -1;
-        }
+    if (qemuBuildCpuArgStr(driver, def, qemuCaps,
+                           &cpu, &hasHwVirt, migrating) < 0)
+        goto error;
 
-        return ret;
+    if (cpu) {
+        virCommandAddArgList(cmd, "-cpu", cpu, NULL);
+        VIR_FREE(cpu);
+
+        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NESTING) && hasHwVirt)
+            virCommandAddArg(cmd, "-enable-nesting");
     }
 
-    switch ((virStorageNetProtocol) protocol) {
-        case VIR_STORAGE_NET_PROTOCOL_HTTP:
-            return 80;
+    return 0;
 
-        case VIR_STORAGE_NET_PROTOCOL_HTTPS:
-            return 443;
+ error:
+    return -1;
+}
 
-        case VIR_STORAGE_NET_PROTOCOL_FTP:
-            return 21;
 
-        case VIR_STORAGE_NET_PROTOCOL_FTPS:
-            return 990;
+static int
+qemuBuildObjectCommandLinePropsInternal(const char *key,
+                                        const virJSONValue *value,
+                                        virBufferPtr buf,
+                                        bool nested)
+{
+    virJSONValuePtr elem;
+    virBitmapPtr bitmap = NULL;
+    ssize_t pos = -1;
+    ssize_t end;
+    size_t i;
 
-        case VIR_STORAGE_NET_PROTOCOL_TFTP:
-            return 69;
+    switch ((virJSONType) value->type) {
+    case VIR_JSON_TYPE_STRING:
+        virBufferAsprintf(buf, ",%s=%s", key, value->data.string);
+        break;
 
-        case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
-            return 7000;
+    case VIR_JSON_TYPE_NUMBER:
+        virBufferAsprintf(buf, ",%s=%s", key, value->data.number);
+        break;
 
-        case VIR_STORAGE_NET_PROTOCOL_NBD:
-            return 10809;
+    case VIR_JSON_TYPE_BOOLEAN:
+        if (value->data.boolean)
+            virBufferAsprintf(buf, ",%s=yes", key);
+        else
+            virBufferAsprintf(buf, ",%s=no", key);
 
-        case VIR_STORAGE_NET_PROTOCOL_ISCSI:
-        case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
-            /* no default port specified */
-            return 0;
+        break;
 
-        case VIR_STORAGE_NET_PROTOCOL_RBD:
-        case VIR_STORAGE_NET_PROTOCOL_LAST:
-        case VIR_STORAGE_NET_PROTOCOL_NONE:
-            /* not applicable */
+    case VIR_JSON_TYPE_ARRAY:
+        if (nested) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("nested -object property arrays are not supported"));
             return -1;
+        }
+
+        if (virJSONValueGetArrayAsBitmap(value, &bitmap) == 0) {
+            while ((pos = virBitmapNextSetBit(bitmap, pos)) > -1) {
+                if ((end = virBitmapNextClearBit(bitmap, pos)) < 0)
+                    end = virBitmapLastSetBit(bitmap) + 1;
+
+                if (end - 1 > pos) {
+                    virBufferAsprintf(buf, ",%s=%zd-%zd", key, pos, end - 1);
+                    pos = end;
+                } else {
+                    virBufferAsprintf(buf, ",%s=%zd", key, pos);
+                }
+            }
+        } else {
+            /* fallback, treat the array as a non-bitmap, adding the key
+             * for each member */
+            for (i = 0; i < virJSONValueArraySize(value); i++) {
+                elem = virJSONValueArrayGet((virJSONValuePtr)value, i);
+
+                /* recurse to avoid duplicating code */
+                if (qemuBuildObjectCommandLinePropsInternal(key, elem, buf,
+                                                            true) < 0)
+                    return -1;
+            }
+        }
+        break;
+
+    case VIR_JSON_TYPE_OBJECT:
+    case VIR_JSON_TYPE_NULL:
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("NULL and OBJECT JSON types can't be converted to "
+                         "commandline string"));
+        return -1;
     }
 
-    return -1;
+    virBitmapFree(bitmap);
+    return 0;
 }
 
-#define QEMU_DEFAULT_NBD_PORT "10809"
 
-static char *
-qemuBuildNetworkDriveURI(virStorageSourcePtr src,
-                         const char *username,
-                         const char *secret)
+static int
+qemuBuildObjectCommandLineProps(const char *key,
+                                const virJSONValue *value,
+                                void *opaque)
 {
-    char *ret = NULL;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    virURIPtr uri = NULL;
-    size_t i;
+    return qemuBuildObjectCommandLinePropsInternal(key, value, opaque, false);
+}
 
-    switch ((virStorageNetProtocol) src->protocol) {
-        case VIR_STORAGE_NET_PROTOCOL_NBD:
-            if (src->nhosts != 1) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("protocol '%s' accepts only one host"),
-                               virStorageNetProtocolTypeToString(src->protocol));
-                goto cleanup;
-            }
 
-            if (!((src->hosts->name && strchr(src->hosts->name, ':')) ||
-                  (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP &&
-                   !src->hosts->name) ||
-                  (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX &&
-                   src->hosts->socket &&
-                   src->hosts->socket[0] != '/'))) {
+char *
+qemuBuildObjectCommandlineFromJSON(const char *type,
+                                   const char *alias,
+                                   virJSONValuePtr props)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char *ret = NULL;
 
-                virBufferAddLit(&buf, "nbd:");
+    virBufferAsprintf(&buf, "%s,id=%s", type, alias);
 
-                switch (src->hosts->transport) {
-                case VIR_STORAGE_NET_HOST_TRANS_TCP:
-                    virBufferStrcat(&buf, src->hosts->name, NULL);
-                    virBufferAsprintf(&buf, ":%s",
-                                      src->hosts->port ? src->hosts->port :
-                                      QEMU_DEFAULT_NBD_PORT);
-                    break;
+    if (virJSONValueObjectForeachKeyValue(props,
+                                          qemuBuildObjectCommandLineProps,
+                                          &buf) < 0)
+        goto cleanup;
 
-                case VIR_STORAGE_NET_HOST_TRANS_UNIX:
-                    if (!src->hosts->socket) {
-                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                                       _("socket attribute required for "
-                                         "unix transport"));
-                        goto cleanup;
-                    }
+    if (virBufferCheckError(&buf) < 0)
+        goto cleanup;
 
-                    virBufferAsprintf(&buf, "unix:%s", src->hosts->socket);
-                    break;
+    ret = virBufferContentAndReset(&buf);
 
-                default:
-                    virReportError(VIR_ERR_INTERNAL_ERROR,
-                                   _("nbd does not support transport '%s'"),
-                                   virStorageNetHostTransportTypeToString(src->hosts->transport));
-                    goto cleanup;
-                }
+ cleanup:
+    virBufferFreeAndReset(&buf);
+    return ret;
+}
 
-                if (src->path)
-                    virBufferAsprintf(&buf, ":exportname=%s", src->path);
-
-                if (virBufferCheckError(&buf) < 0)
-                    goto cleanup;
 
-                ret = virBufferContentAndReset(&buf);
-                goto cleanup;
-            }
-            /* fallthrough */
-            /* NBD code uses same formatting scheme as others in some cases */
+static int
+qemuBuildDeviceAddressStr(virBufferPtr buf,
+                          virDomainDefPtr domainDef,
+                          virDomainDeviceInfoPtr info,
+                          virQEMUCapsPtr qemuCaps)
+{
+    int ret = -1;
+    char *devStr = NULL;
+    const char *contAlias = NULL;
 
-        case VIR_STORAGE_NET_PROTOCOL_HTTP:
-        case VIR_STORAGE_NET_PROTOCOL_HTTPS:
-        case VIR_STORAGE_NET_PROTOCOL_FTP:
-        case VIR_STORAGE_NET_PROTOCOL_FTPS:
-        case VIR_STORAGE_NET_PROTOCOL_TFTP:
-        case VIR_STORAGE_NET_PROTOCOL_ISCSI:
-        case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
-            if (src->nhosts != 1) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("protocol '%s' accepts only one host"),
-                               virStorageNetProtocolTypeToString(src->protocol));
-                goto cleanup;
-            }
+    if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+        size_t i;
 
-            if (VIR_ALLOC(uri) < 0)
-                goto cleanup;
+        if (!(devStr = virDomainPCIAddressAsString(&info->addr.pci)))
+            goto cleanup;
+        for (i = 0; i < domainDef->ncontrollers; i++) {
+            virDomainControllerDefPtr cont = domainDef->controllers[i];
 
-            if (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP) {
-                if (VIR_STRDUP(uri->scheme,
-                               virStorageNetProtocolTypeToString(src->protocol)) < 0)
-                    goto cleanup;
-            } else {
-                if (virAsprintf(&uri->scheme, "%s+%s",
-                                virStorageNetProtocolTypeToString(src->protocol),
-                                virStorageNetHostTransportTypeToString(src->hosts->transport)) < 0)
+            if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
+                cont->idx == info->addr.pci.bus) {
+                contAlias = cont->info.alias;
+                if (!contAlias) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("Device alias was not set for PCI "
+                                     "controller with index %u required "
+                                     "for device at address %s"),
+                                   info->addr.pci.bus, devStr);
                     goto cleanup;
-            }
-
-            if ((uri->port = qemuNetworkDriveGetPort(src->protocol, src->hosts->port)) < 0)
-                goto cleanup;
-
-            if (src->path) {
-                if (src->volume) {
-                    if (virAsprintf(&uri->path, "/%s%s",
-                                    src->volume, src->path) < 0)
-                        goto cleanup;
-                } else {
-                    if (virAsprintf(&uri->path, "%s%s",
-                                    src->path[0] == '/' ? "" : "/",
-                                    src->path) < 0)
-                        goto cleanup;
-                }
-            }
-
-            if (src->hosts->socket &&
-                virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0)
-                goto cleanup;
-
-            if (username) {
-                if (secret) {
-                    if (virAsprintf(&uri->user, "%s:%s", username, secret) < 0)
-                        goto cleanup;
-                } else {
-                    if (VIR_STRDUP(uri->user, username) < 0)
-                        goto cleanup;
                 }
+                break;
             }
+        }
+        if (!contAlias) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Could not find PCI "
+                             "controller with index %u required "
+                             "for device at address %s"),
+                           info->addr.pci.bus, devStr);
+            goto cleanup;
+        }
 
-            if (VIR_STRDUP(uri->server, src->hosts->name) < 0)
-                goto cleanup;
-
-            ret = virURIFormat(uri);
-
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
-            if (!src->path) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("missing disk source for 'sheepdog' protocol"));
-                goto cleanup;
-            }
-
-            if (src->nhosts == 0) {
-                if (virAsprintf(&ret, "sheepdog:%s", src->path) < 0)
-                    goto cleanup;
-            } else if (src->nhosts == 1) {
-                if (virAsprintf(&ret, "sheepdog:%s:%s:%s",
-                                src->hosts->name,
-                                src->hosts->port ? src->hosts->port : "7000",
-                                src->path) < 0)
-                    goto cleanup;
-            } else {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("protocol 'sheepdog' accepts up to one host"));
+        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
+            if (info->addr.pci.function != 0) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               _("Only PCI device addresses with function=0 "
+                                 "are supported with this QEMU binary"));
                 goto cleanup;
             }
-
-            break;
-
-        case VIR_STORAGE_NET_PROTOCOL_RBD:
-            if (strchr(src->path, ':')) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("':' not allowed in RBD source volume name '%s'"),
-                               src->path);
+            if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               _("'multifunction=on' is not supported with "
+                                 "this QEMU binary"));
                 goto cleanup;
             }
+        }
 
-            virBufferStrcat(&buf, "rbd:", src->path, NULL);
-
-            if (src->snapshot)
-                virBufferEscape(&buf, '\\', ":", "@%s", src->snapshot);
-
-            if (username) {
-                virBufferEscape(&buf, '\\', ":", ":id=%s", username);
-                virBufferEscape(&buf, '\\', ":",
-                                ":key=%s:auth_supported=cephx\\;none",
-                                secret);
-            } else {
-                virBufferAddLit(&buf, ":auth_supported=none");
-            }
-
-            if (src->nhosts > 0) {
-                virBufferAddLit(&buf, ":mon_host=");
-                for (i = 0; i < src->nhosts; i++) {
-                    if (i)
-                        virBufferAddLit(&buf, "\\;");
-
-                    /* assume host containing : is ipv6 */
-                    if (strchr(src->hosts[i].name, ':'))
-                        virBufferEscape(&buf, '\\', ":", "[%s]",
-                                        src->hosts[i].name);
-                    else
-                        virBufferAsprintf(&buf, "%s", src->hosts[i].name);
-
-                    if (src->hosts[i].port)
-                        virBufferAsprintf(&buf, "\\:%s", src->hosts[i].port);
-                }
-            }
-
-            if (src->configFile)
-                virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile);
-
-            if (virBufferCheckError(&buf) < 0)
-                goto cleanup;
-
-            ret = virBufferContentAndReset(&buf);
-            break;
-
+        if (info->addr.pci.bus != 0 &&
+            !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Multiple PCI buses are not supported "
+                             "with this QEMU binary"));
+            goto cleanup;
+        }
+        virBufferAsprintf(buf, ",bus=%s", contAlias);
 
-        case VIR_STORAGE_NET_PROTOCOL_LAST:
-        case VIR_STORAGE_NET_PROTOCOL_NONE:
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Unexpected network protocol '%s'"),
-                           virStorageNetProtocolTypeToString(src->protocol));
+        if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON)
+            virBufferAddLit(buf, ",multifunction=on");
+        else if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_OFF)
+            virBufferAddLit(buf, ",multifunction=off");
+        virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot);
+        if (info->addr.pci.function != 0)
+           virBufferAsprintf(buf, ".0x%x", info->addr.pci.function);
+    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
+        if (!(contAlias = virDomainControllerAliasFind(domainDef,
+                                                       VIR_DOMAIN_CONTROLLER_TYPE_USB,
+                                                       info->addr.usb.bus)))
             goto cleanup;
+        virBufferAsprintf(buf, ",bus=%s.0,port=%s", contAlias, info->addr.usb.port);
+    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) {
+        if (info->addr.spaprvio.has_reg)
+            virBufferAsprintf(buf, ",reg=0x%llx", info->addr.spaprvio.reg);
+    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
+        if (info->addr.ccw.assigned)
+            virBufferAsprintf(buf, ",devno=%x.%x.%04x",
+                              info->addr.ccw.cssid,
+                              info->addr.ccw.ssid,
+                              info->addr.ccw.devno);
+    } else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA) {
+        virBufferAsprintf(buf, ",iobase=0x%x,irq=0x%x",
+                          info->addr.isa.iobase,
+                          info->addr.isa.irq);
     }
 
+    ret = 0;
  cleanup:
-    virBufferFreeAndReset(&buf);
-    virURIFree(uri);
-
+    VIR_FREE(devStr);
     return ret;
 }
 
-
-int
-qemuGetDriveSourceString(virStorageSourcePtr src,
-                         virConnectPtr conn,
-                         char **source)
+static int
+qemuBuildRomStr(virBufferPtr buf,
+                virDomainDeviceInfoPtr info,
+                virQEMUCapsPtr qemuCaps)
 {
-    int actualType = virStorageSourceGetActualType(src);
-    char *secret = NULL;
-    char *username = NULL;
-    int ret = -1;
-
-    *source = NULL;
-
-    /* return 1 for empty sources */
-    if (virStorageSourceIsEmpty(src))
-        return 1;
+    if (info->rombar || info->romfile) {
+        if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           "%s", _("rombar and romfile are supported only for PCI devices"));
+            return -1;
+        }
+        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_ROMBAR)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           "%s", _("rombar and romfile not supported in this QEMU binary"));
+            return -1;
+        }
 
-    if (conn) {
-        if (actualType == VIR_STORAGE_TYPE_NETWORK &&
-            src->auth &&
-            (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI ||
-             src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) {
-            bool encode = false;
-            int secretType = VIR_SECRET_USAGE_TYPE_ISCSI;
-            const char *protocol = virStorageNetProtocolTypeToString(src->protocol);
-            username = src->auth->username;
-
-            if (src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) {
-                /* qemu requires the secret to be encoded for RBD */
-                encode = true;
-                secretType = VIR_SECRET_USAGE_TYPE_CEPH;
-            }
-
-            if (!(secret = qemuGetSecretString(conn,
-                                               protocol,
-                                               encode,
-                                               src->auth,
-                                               secretType)))
-                goto cleanup;
+        switch (info->rombar) {
+        case VIR_TRISTATE_SWITCH_OFF:
+            virBufferAddLit(buf, ",rombar=0");
+            break;
+        case VIR_TRISTATE_SWITCH_ON:
+            virBufferAddLit(buf, ",rombar=1");
+            break;
+        default:
+            break;
         }
+        if (info->romfile)
+           virBufferAsprintf(buf, ",romfile=%s", info->romfile);
     }
+    return 0;
+}
 
-    switch ((virStorageType) actualType) {
-    case VIR_STORAGE_TYPE_BLOCK:
-    case VIR_STORAGE_TYPE_FILE:
-    case VIR_STORAGE_TYPE_DIR:
-        if (VIR_STRDUP(*source, src->path) < 0)
-            goto cleanup;
-
-        break;
+static int
+qemuBuildIoEventFdStr(virBufferPtr buf,
+                      virTristateSwitch use,
+                      virQEMUCapsPtr qemuCaps)
+{
+    if (use && virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_IOEVENTFD))
+        virBufferAsprintf(buf, ",ioeventfd=%s",
+                          virTristateSwitchTypeToString(use));
+    return 0;
+}
 
-    case VIR_STORAGE_TYPE_NETWORK:
-        if (!(*source = qemuBuildNetworkDriveURI(src, username, secret)))
-            goto cleanup;
-        break;
+#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \
+  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ "
 
-    case VIR_STORAGE_TYPE_VOLUME:
-    case VIR_STORAGE_TYPE_NONE:
-    case VIR_STORAGE_TYPE_LAST:
-        break;
+static int
+qemuSafeSerialParamValue(const char *value)
+{
+    if (strspn(value, QEMU_SERIAL_PARAM_ACCEPTED_CHARS) != strlen(value)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("driver serial '%s' contains unsafe characters"),
+                       value);
+        return -1;
     }
 
-    ret = 0;
-
- cleanup:
-    VIR_FREE(secret);
-    return ret;
+    return 0;
 }
 
-
-/* Perform disk definition config validity checks */
-int
-qemuCheckDiskConfig(virDomainDiskDefPtr disk)
+static char *
+qemuGetSecretString(virConnectPtr conn,
+                    const char *scheme,
+                    bool encoded,
+                    virStorageAuthDefPtr authdef,
+                    virSecretUsageType secretUsageType)
 {
-    if (virDiskNameToIndex(disk->dst) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unsupported disk type '%s'"), disk->dst);
-        goto error;
-    }
+    size_t secret_size;
+    virSecretPtr sec = NULL;
+    char *secret = NULL;
+    char uuidStr[VIR_UUID_STRING_BUFLEN];
 
-    if (disk->wwn) {
-        if ((disk->bus != VIR_DOMAIN_DISK_BUS_IDE) &&
-            (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("Only ide and scsi disk support wwn"));
-            goto error;
-        }
+    /* look up secret */
+    switch (authdef->secretType) {
+    case VIR_STORAGE_SECRET_TYPE_UUID:
+        sec = virSecretLookupByUUID(conn, authdef->secret.uuid);
+        virUUIDFormat(authdef->secret.uuid, uuidStr);
+        break;
+    case VIR_STORAGE_SECRET_TYPE_USAGE:
+        sec = virSecretLookupByUsage(conn, secretUsageType,
+                                     authdef->secret.usage);
+        break;
     }
 
-    if ((disk->vendor || disk->product) &&
-        disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("Only scsi disk supports vendor and product"));
-            goto error;
+    if (!sec) {
+        if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
+            virReportError(VIR_ERR_NO_SECRET,
+                           _("%s no secret matches uuid '%s'"),
+                           scheme, uuidStr);
+        } else {
+            virReportError(VIR_ERR_NO_SECRET,
+                           _("%s no secret matches usage value '%s'"),
+                           scheme, authdef->secret.usage);
+        }
+        goto cleanup;
     }
 
-    if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) {
-        /* make sure that both the bus supports type='lun' (SG_IO). */
-        if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO &&
-            disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("disk device='lun' is not supported for bus='%s'"),
-                           virDomainDiskQEMUBusTypeToString(disk->bus));
-            goto error;
-        }
-        if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
-            if (disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_ISCSI) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("disk device='lun' is not supported "
-                                 "for protocol='%s'"),
-                               virStorageNetProtocolTypeToString(disk->src->protocol));
-                goto error;
-            }
-        } else if (!virDomainDiskSourceIsBlockType(disk->src, true)) {
-            goto error;
-        }
-        if (disk->wwn) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("Setting wwn is not supported for lun device"));
-            goto error;
-        }
-        if (disk->vendor || disk->product) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("Setting vendor or product is not supported "
-                             "for lun device"));
-            goto error;
+    secret = (char *)conn->secretDriver->secretGetValue(sec, &secret_size, 0,
+                                                        VIR_SECRET_GET_VALUE_INTERNAL_CALL);
+    if (!secret) {
+        if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("could not get value of the secret for "
+                             "username '%s' using uuid '%s'"),
+                           authdef->username, uuidStr);
+        } else {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("could not get value of the secret for "
+                             "username '%s' using usage value '%s'"),
+                           authdef->username, authdef->secret.usage);
         }
+        goto cleanup;
     }
-    return 0;
- error:
-    return -1;
-}
 
+    if (encoded) {
+        char *base64 = NULL;
 
-/* Check whether the device address is using either 'ccw' or default s390
- * address format and whether that's "legal" for the current qemu and/or
- * guest os.machine type. This is the corollary to the code which doesn't
- * find the address type set using an emulator that supports either 'ccw'
- * or s390 and sets the address type based on the capabilities.
- *
- * If the address is using 'ccw' or s390 and it's not supported, generate
- * an error and return false; otherwise, return true.
- */
-bool
-qemuCheckCCWS390AddressSupport(virDomainDefPtr def,
-                               virDomainDeviceInfo info,
-                               virQEMUCapsPtr qemuCaps,
-                               const char *devicename)
-{
-    if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
-        if (!qemuDomainMachineIsS390CCW(def)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("cannot use CCW address type for device "
-                             "'%s' using machine type '%s'"),
-                       devicename, def->os.machine);
-            return false;
-        } else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("CCW address type is not supported by "
-                             "this QEMU"));
-            return false;
-        }
-    } else if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) {
-        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("virtio S390 address type is not supported by "
-                             "this QEMU"));
-            return false;
+        base64_encode_alloc(secret, secret_size, &base64);
+        VIR_FREE(secret);
+        if (!base64) {
+            virReportOOMError();
+            goto cleanup;
         }
+        secret = base64;
     }
-    return true;
+
+ cleanup:
+    virObjectUnref(sec);
+    return secret;
 }
 
 
-/* Qemu 1.2 and later have a binary flag -enable-fips that must be
- * used for VNC auth to obey FIPS settings; but the flag only
- * exists on Linux, and with no way to probe for it via QMP.  Our
- * solution: if FIPS mode is required, then unconditionally use
- * the flag, regardless of qemu version, for the following matrix:
- *
- *                          old QEMU            new QEMU
- * FIPS enabled             doesn't start       VNC auth disabled
- * FIPS disabled/missing    VNC auth enabled    VNC auth enabled
- */
-bool
-qemuCheckFips(void)
+static int
+qemuNetworkDriveGetPort(int protocol,
+                        const char *port)
 {
-    bool ret = false;
+    int ret = 0;
 
-    if (virFileExists("/proc/sys/crypto/fips_enabled")) {
-        char *buf = NULL;
+    if (port) {
+        if (virStrToLong_i(port, NULL, 10, &ret) < 0 || ret < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("failed to parse port number '%s'"),
+                           port);
+            return -1;
+        }
 
-        if (virFileReadAll("/proc/sys/crypto/fips_enabled", 10, &buf) < 0)
-            return ret;
-        if (STREQ(buf, "1\n"))
-            ret = true;
-        VIR_FREE(buf);
+        return ret;
     }
 
-    return ret;
-}
-
-
-char *
-qemuBuildDriveStr(virConnectPtr conn,
-                  virDomainDiskDefPtr disk,
-                  bool bootable,
-                  virQEMUCapsPtr qemuCaps)
-{
-    virBuffer opt = VIR_BUFFER_INITIALIZER;
-    const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
-    const char *trans =
-        virDomainDiskGeometryTransTypeToString(disk->geometry.trans);
-    int idx = virDiskNameToIndex(disk->dst);
-    int busid = -1, unitid = -1;
-    char *source = NULL;
-    int actualType = virStorageSourceGetActualType(disk->src);
+    switch ((virStorageNetProtocol) protocol) {
+        case VIR_STORAGE_NET_PROTOCOL_HTTP:
+            return 80;
 
-    if (idx < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unsupported disk type '%s'"), disk->dst);
-        goto error;
-    }
+        case VIR_STORAGE_NET_PROTOCOL_HTTPS:
+            return 443;
 
-    switch (disk->bus) {
-    case VIR_DOMAIN_DISK_BUS_SCSI:
-        if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("unexpected address type for scsi disk"));
-            goto error;
-        }
+        case VIR_STORAGE_NET_PROTOCOL_FTP:
+            return 21;
 
-        /* Setting bus= attr for SCSI drives, causes a controller
-         * to be created. Yes this is slightly odd. It is not possible
-         * to have > 1 bus on a SCSI controller (yet). */
-        if (disk->info.addr.drive.bus != 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("SCSI controller only supports 1 bus"));
-            goto error;
-        }
-        busid = disk->info.addr.drive.controller;
-        unitid = disk->info.addr.drive.unit;
-        break;
+        case VIR_STORAGE_NET_PROTOCOL_FTPS:
+            return 990;
 
-    case VIR_DOMAIN_DISK_BUS_IDE:
-        if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("unexpected address type for ide disk"));
-            goto error;
-        }
-        /* We can only have 1 IDE controller (currently) */
-        if (disk->info.addr.drive.controller != 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Only 1 %s controller is supported"), bus);
-            goto error;
-        }
-        busid = disk->info.addr.drive.bus;
-        unitid = disk->info.addr.drive.unit;
-        break;
+        case VIR_STORAGE_NET_PROTOCOL_TFTP:
+            return 69;
 
-    case VIR_DOMAIN_DISK_BUS_FDC:
-        if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("unexpected address type for fdc disk"));
-            goto error;
-        }
-        /* We can only have 1 FDC controller (currently) */
-        if (disk->info.addr.drive.controller != 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Only 1 %s controller is supported"), bus);
-            goto error;
-        }
-        /* We can only have 1 FDC bus (currently) */
-        if (disk->info.addr.drive.bus != 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Only 1 %s bus is supported"), bus);
-            goto error;
-        }
-        if (disk->info.addr.drive.target != 0) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("target must be 0 for controller fdc"));
-            goto error;
-        }
-        unitid = disk->info.addr.drive.unit;
+        case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
+            return 7000;
 
-        break;
+        case VIR_STORAGE_NET_PROTOCOL_NBD:
+            return 10809;
 
-    case VIR_DOMAIN_DISK_BUS_VIRTIO:
-        idx = -1;
-        break;
+        case VIR_STORAGE_NET_PROTOCOL_ISCSI:
+        case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
+            /* no default port specified */
+            return 0;
 
-    case VIR_DOMAIN_DISK_BUS_XEN:
-    case VIR_DOMAIN_DISK_BUS_SD:
-        /* Xen and SD have no address type currently, so assign
-         * based on index */
-        break;
+        case VIR_STORAGE_NET_PROTOCOL_RBD:
+        case VIR_STORAGE_NET_PROTOCOL_LAST:
+        case VIR_STORAGE_NET_PROTOCOL_NONE:
+            /* not applicable */
+            return -1;
     }
 
-    if (qemuGetDriveSourceString(disk->src, conn, &source) < 0)
-        goto error;
+    return -1;
+}
 
-    if (source &&
-        !((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY ||
-           disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) &&
-          disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) {
+#define QEMU_DEFAULT_NBD_PORT "10809"
 
-        virBufferAddLit(&opt, "file=");
+static char *
+qemuBuildNetworkDriveURI(virStorageSourcePtr src,
+                         const char *username,
+                         const char *secret)
+{
+    char *ret = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    virURIPtr uri = NULL;
+    size_t i;
 
-        switch (actualType) {
-        case VIR_STORAGE_TYPE_DIR:
-            /* QEMU only supports magic FAT format for now */
-            if (disk->src->format > 0 &&
-                disk->src->format != VIR_STORAGE_FILE_FAT) {
+    switch ((virStorageNetProtocol) src->protocol) {
+        case VIR_STORAGE_NET_PROTOCOL_NBD:
+            if (src->nhosts != 1) {
                 virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("unsupported disk driver type for '%s'"),
-                               virStorageFileFormatTypeToString(disk->src->format));
-                goto error;
+                               _("protocol '%s' accepts only one host"),
+                               virStorageNetProtocolTypeToString(src->protocol));
+                goto cleanup;
             }
 
-            if (!disk->src->readonly) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("cannot create virtual FAT disks in read-write mode"));
-                goto error;
-            }
+            if (!((src->hosts->name && strchr(src->hosts->name, ':')) ||
+                  (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP &&
+                   !src->hosts->name) ||
+                  (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX &&
+                   src->hosts->socket &&
+                   src->hosts->socket[0] != '/'))) {
 
-            virBufferAddLit(&opt, "fat:");
+                virBufferAddLit(&buf, "nbd:");
 
-            if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)
-                virBufferAddLit(&opt, "floppy:");
+                switch (src->hosts->transport) {
+                case VIR_STORAGE_NET_HOST_TRANS_TCP:
+                    virBufferStrcat(&buf, src->hosts->name, NULL);
+                    virBufferAsprintf(&buf, ":%s",
+                                      src->hosts->port ? src->hosts->port :
+                                      QEMU_DEFAULT_NBD_PORT);
+                    break;
 
-            break;
+                case VIR_STORAGE_NET_HOST_TRANS_UNIX:
+                    if (!src->hosts->socket) {
+                        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                       _("socket attribute required for "
+                                         "unix transport"));
+                        goto cleanup;
+                    }
 
-        case VIR_STORAGE_TYPE_BLOCK:
-            if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               disk->src->type == VIR_STORAGE_TYPE_VOLUME ?
-                               _("tray status 'open' is invalid for block type volume") :
-                               _("tray status 'open' is invalid for block type disk"));
-                goto error;
-            }
+                    virBufferAsprintf(&buf, "unix:%s", src->hosts->socket);
+                    break;
 
-            break;
+                default:
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("nbd does not support transport '%s'"),
+                                   virStorageNetHostTransportTypeToString(src->hosts->transport));
+                    goto cleanup;
+                }
 
-        default:
-            break;
-        }
+                if (src->path)
+                    virBufferAsprintf(&buf, ":exportname=%s", src->path);
 
-        virBufferEscape(&opt, ',', ",", "%s,", source);
+                if (virBufferCheckError(&buf) < 0)
+                    goto cleanup;
 
-        if (disk->src->format > 0 &&
-            disk->src->type != VIR_STORAGE_TYPE_DIR)
-            virBufferAsprintf(&opt, "format=%s,",
-                              virStorageFileFormatTypeToString(disk->src->format));
-    }
-    VIR_FREE(source);
+                ret = virBufferContentAndReset(&buf);
+                goto cleanup;
+            }
+            /* fallthrough */
+            /* NBD code uses same formatting scheme as others in some cases */
 
-    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
-        virBufferAddLit(&opt, "if=none");
-    else
-        virBufferAsprintf(&opt, "if=%s", bus);
+        case VIR_STORAGE_NET_PROTOCOL_HTTP:
+        case VIR_STORAGE_NET_PROTOCOL_HTTPS:
+        case VIR_STORAGE_NET_PROTOCOL_FTP:
+        case VIR_STORAGE_NET_PROTOCOL_FTPS:
+        case VIR_STORAGE_NET_PROTOCOL_TFTP:
+        case VIR_STORAGE_NET_PROTOCOL_ISCSI:
+        case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
+            if (src->nhosts != 1) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("protocol '%s' accepts only one host"),
+                               virStorageNetProtocolTypeToString(src->protocol));
+                goto cleanup;
+            }
 
-    if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
-        if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) {
-            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD))
-                virBufferAddLit(&opt, ",media=cdrom");
-        } else if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) {
-            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD))
-                virBufferAddLit(&opt, ",media=cdrom");
-        } else {
-            virBufferAddLit(&opt, ",media=cdrom");
-        }
-    }
+            if (VIR_ALLOC(uri) < 0)
+                goto cleanup;
 
-    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
-        virBufferAsprintf(&opt, ",id=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias);
-    } else {
-        if (busid == -1 && unitid == -1) {
-            if (idx != -1)
-                virBufferAsprintf(&opt, ",index=%d", idx);
-        } else {
-            if (busid != -1)
-                virBufferAsprintf(&opt, ",bus=%d", busid);
-            if (unitid != -1)
-                virBufferAsprintf(&opt, ",unit=%d", unitid);
-        }
-    }
-    if (bootable &&
-        virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_BOOT) &&
-        (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK ||
+            if (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP) {
+                if (VIR_STRDUP(uri->scheme,
+                               virStorageNetProtocolTypeToString(src->protocol)) < 0)
+                    goto cleanup;
+            } else {
+                if (virAsprintf(&uri->scheme, "%s+%s",
+                                virStorageNetProtocolTypeToString(src->protocol),
+                                virStorageNetHostTransportTypeToString(src->hosts->transport)) < 0)
+                    goto cleanup;
+            }
+
+            if ((uri->port = qemuNetworkDriveGetPort(src->protocol, src->hosts->port)) < 0)
+                goto cleanup;
+
+            if (src->path) {
+                if (src->volume) {
+                    if (virAsprintf(&uri->path, "/%s%s",
+                                    src->volume, src->path) < 0)
+                        goto cleanup;
+                } else {
+                    if (virAsprintf(&uri->path, "%s%s",
+                                    src->path[0] == '/' ? "" : "/",
+                                    src->path) < 0)
+                        goto cleanup;
+                }
+            }
+
+            if (src->hosts->socket &&
+                virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0)
+                goto cleanup;
+
+            if (username) {
+                if (secret) {
+                    if (virAsprintf(&uri->user, "%s:%s", username, secret) < 0)
+                        goto cleanup;
+                } else {
+                    if (VIR_STRDUP(uri->user, username) < 0)
+                        goto cleanup;
+                }
+            }
+
+            if (VIR_STRDUP(uri->server, src->hosts->name) < 0)
+                goto cleanup;
+
+            ret = virURIFormat(uri);
+
+            break;
+
+        case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
+            if (!src->path) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("missing disk source for 'sheepdog' protocol"));
+                goto cleanup;
+            }
+
+            if (src->nhosts == 0) {
+                if (virAsprintf(&ret, "sheepdog:%s", src->path) < 0)
+                    goto cleanup;
+            } else if (src->nhosts == 1) {
+                if (virAsprintf(&ret, "sheepdog:%s:%s:%s",
+                                src->hosts->name,
+                                src->hosts->port ? src->hosts->port : "7000",
+                                src->path) < 0)
+                    goto cleanup;
+            } else {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("protocol 'sheepdog' accepts up to one host"));
+                goto cleanup;
+            }
+
+            break;
+
+        case VIR_STORAGE_NET_PROTOCOL_RBD:
+            if (strchr(src->path, ':')) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("':' not allowed in RBD source volume name '%s'"),
+                               src->path);
+                goto cleanup;
+            }
+
+            virBufferStrcat(&buf, "rbd:", src->path, NULL);
+
+            if (src->snapshot)
+                virBufferEscape(&buf, '\\', ":", "@%s", src->snapshot);
+
+            if (username) {
+                virBufferEscape(&buf, '\\', ":", ":id=%s", username);
+                virBufferEscape(&buf, '\\', ":",
+                                ":key=%s:auth_supported=cephx\\;none",
+                                secret);
+            } else {
+                virBufferAddLit(&buf, ":auth_supported=none");
+            }
+
+            if (src->nhosts > 0) {
+                virBufferAddLit(&buf, ":mon_host=");
+                for (i = 0; i < src->nhosts; i++) {
+                    if (i)
+                        virBufferAddLit(&buf, "\\;");
+
+                    /* assume host containing : is ipv6 */
+                    if (strchr(src->hosts[i].name, ':'))
+                        virBufferEscape(&buf, '\\', ":", "[%s]",
+                                        src->hosts[i].name);
+                    else
+                        virBufferAsprintf(&buf, "%s", src->hosts[i].name);
+
+                    if (src->hosts[i].port)
+                        virBufferAsprintf(&buf, "\\:%s", src->hosts[i].port);
+                }
+            }
+
+            if (src->configFile)
+                virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile);
+
+            if (virBufferCheckError(&buf) < 0)
+                goto cleanup;
+
+            ret = virBufferContentAndReset(&buf);
+            break;
+
+
+        case VIR_STORAGE_NET_PROTOCOL_LAST:
+        case VIR_STORAGE_NET_PROTOCOL_NONE:
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unexpected network protocol '%s'"),
+                           virStorageNetProtocolTypeToString(src->protocol));
+            goto cleanup;
+    }
+
+ cleanup:
+    virBufferFreeAndReset(&buf);
+    virURIFree(uri);
+
+    return ret;
+}
+
+
+int
+qemuGetDriveSourceString(virStorageSourcePtr src,
+                         virConnectPtr conn,
+                         char **source)
+{
+    int actualType = virStorageSourceGetActualType(src);
+    char *secret = NULL;
+    char *username = NULL;
+    int ret = -1;
+
+    *source = NULL;
+
+    /* return 1 for empty sources */
+    if (virStorageSourceIsEmpty(src))
+        return 1;
+
+    if (conn) {
+        if (actualType == VIR_STORAGE_TYPE_NETWORK &&
+            src->auth &&
+            (src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI ||
+             src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) {
+            bool encode = false;
+            int secretType = VIR_SECRET_USAGE_TYPE_ISCSI;
+            const char *protocol = virStorageNetProtocolTypeToString(src->protocol);
+            username = src->auth->username;
+
+            if (src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) {
+                /* qemu requires the secret to be encoded for RBD */
+                encode = true;
+                secretType = VIR_SECRET_USAGE_TYPE_CEPH;
+            }
+
+            if (!(secret = qemuGetSecretString(conn,
+                                               protocol,
+                                               encode,
+                                               src->auth,
+                                               secretType)))
+                goto cleanup;
+        }
+    }
+
+    switch ((virStorageType) actualType) {
+    case VIR_STORAGE_TYPE_BLOCK:
+    case VIR_STORAGE_TYPE_FILE:
+    case VIR_STORAGE_TYPE_DIR:
+        if (VIR_STRDUP(*source, src->path) < 0)
+            goto cleanup;
+
+        break;
+
+    case VIR_STORAGE_TYPE_NETWORK:
+        if (!(*source = qemuBuildNetworkDriveURI(src, username, secret)))
+            goto cleanup;
+        break;
+
+    case VIR_STORAGE_TYPE_VOLUME:
+    case VIR_STORAGE_TYPE_NONE:
+    case VIR_STORAGE_TYPE_LAST:
+        break;
+    }
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(secret);
+    return ret;
+}
+
+
+/* Perform disk definition config validity checks */
+int
+qemuCheckDiskConfig(virDomainDiskDefPtr disk)
+{
+    if (virDiskNameToIndex(disk->dst) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported disk type '%s'"), disk->dst);
+        goto error;
+    }
+
+    if (disk->wwn) {
+        if ((disk->bus != VIR_DOMAIN_DISK_BUS_IDE) &&
+            (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Only ide and scsi disk support wwn"));
+            goto error;
+        }
+    }
+
+    if ((disk->vendor || disk->product) &&
+        disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Only scsi disk supports vendor and product"));
+            goto error;
+    }
+
+    if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) {
+        /* make sure that both the bus supports type='lun' (SG_IO). */
+        if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO &&
+            disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("disk device='lun' is not supported for bus='%s'"),
+                           virDomainDiskQEMUBusTypeToString(disk->bus));
+            goto error;
+        }
+        if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
+            if (disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_ISCSI) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("disk device='lun' is not supported "
+                                 "for protocol='%s'"),
+                               virStorageNetProtocolTypeToString(disk->src->protocol));
+                goto error;
+            }
+        } else if (!virDomainDiskSourceIsBlockType(disk->src, true)) {
+            goto error;
+        }
+        if (disk->wwn) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Setting wwn is not supported for lun device"));
+            goto error;
+        }
+        if (disk->vendor || disk->product) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("Setting vendor or product is not supported "
+                             "for lun device"));
+            goto error;
+        }
+    }
+    return 0;
+ error:
+    return -1;
+}
+
+
+/* Check whether the device address is using either 'ccw' or default s390
+ * address format and whether that's "legal" for the current qemu and/or
+ * guest os.machine type. This is the corollary to the code which doesn't
+ * find the address type set using an emulator that supports either 'ccw'
+ * or s390 and sets the address type based on the capabilities.
+ *
+ * If the address is using 'ccw' or s390 and it's not supported, generate
+ * an error and return false; otherwise, return true.
+ */
+bool
+qemuCheckCCWS390AddressSupport(virDomainDefPtr def,
+                               virDomainDeviceInfo info,
+                               virQEMUCapsPtr qemuCaps,
+                               const char *devicename)
+{
+    if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
+        if (!qemuDomainMachineIsS390CCW(def)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("cannot use CCW address type for device "
+                             "'%s' using machine type '%s'"),
+                       devicename, def->os.machine);
+            return false;
+        } else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("CCW address type is not supported by "
+                             "this QEMU"));
+            return false;
+        }
+    } else if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) {
+        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("virtio S390 address type is not supported by "
+                             "this QEMU"));
+            return false;
+        }
+    }
+    return true;
+}
+
+
+/* Qemu 1.2 and later have a binary flag -enable-fips that must be
+ * used for VNC auth to obey FIPS settings; but the flag only
+ * exists on Linux, and with no way to probe for it via QMP.  Our
+ * solution: if FIPS mode is required, then unconditionally use
+ * the flag, regardless of qemu version, for the following matrix:
+ *
+ *                          old QEMU            new QEMU
+ * FIPS enabled             doesn't start       VNC auth disabled
+ * FIPS disabled/missing    VNC auth enabled    VNC auth enabled
+ */
+bool
+qemuCheckFips(void)
+{
+    bool ret = false;
+
+    if (virFileExists("/proc/sys/crypto/fips_enabled")) {
+        char *buf = NULL;
+
+        if (virFileReadAll("/proc/sys/crypto/fips_enabled", 10, &buf) < 0)
+            return ret;
+        if (STREQ(buf, "1\n"))
+            ret = true;
+        VIR_FREE(buf);
+    }
+
+    return ret;
+}
+
+
+char *
+qemuBuildDriveStr(virConnectPtr conn,
+                  virDomainDiskDefPtr disk,
+                  bool bootable,
+                  virQEMUCapsPtr qemuCaps)
+{
+    virBuffer opt = VIR_BUFFER_INITIALIZER;
+    const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
+    const char *trans =
+        virDomainDiskGeometryTransTypeToString(disk->geometry.trans);
+    int idx = virDiskNameToIndex(disk->dst);
+    int busid = -1, unitid = -1;
+    char *source = NULL;
+    int actualType = virStorageSourceGetActualType(disk->src);
+
+    if (idx < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported disk type '%s'"), disk->dst);
+        goto error;
+    }
+
+    switch (disk->bus) {
+    case VIR_DOMAIN_DISK_BUS_SCSI:
+        if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("unexpected address type for scsi disk"));
+            goto error;
+        }
+
+        /* Setting bus= attr for SCSI drives, causes a controller
+         * to be created. Yes this is slightly odd. It is not possible
+         * to have > 1 bus on a SCSI controller (yet). */
+        if (disk->info.addr.drive.bus != 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("SCSI controller only supports 1 bus"));
+            goto error;
+        }
+        busid = disk->info.addr.drive.controller;
+        unitid = disk->info.addr.drive.unit;
+        break;
+
+    case VIR_DOMAIN_DISK_BUS_IDE:
+        if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("unexpected address type for ide disk"));
+            goto error;
+        }
+        /* We can only have 1 IDE controller (currently) */
+        if (disk->info.addr.drive.controller != 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Only 1 %s controller is supported"), bus);
+            goto error;
+        }
+        busid = disk->info.addr.drive.bus;
+        unitid = disk->info.addr.drive.unit;
+        break;
+
+    case VIR_DOMAIN_DISK_BUS_FDC:
+        if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("unexpected address type for fdc disk"));
+            goto error;
+        }
+        /* We can only have 1 FDC controller (currently) */
+        if (disk->info.addr.drive.controller != 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Only 1 %s controller is supported"), bus);
+            goto error;
+        }
+        /* We can only have 1 FDC bus (currently) */
+        if (disk->info.addr.drive.bus != 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Only 1 %s bus is supported"), bus);
+            goto error;
+        }
+        if (disk->info.addr.drive.target != 0) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("target must be 0 for controller fdc"));
+            goto error;
+        }
+        unitid = disk->info.addr.drive.unit;
+
+        break;
+
+    case VIR_DOMAIN_DISK_BUS_VIRTIO:
+        idx = -1;
+        break;
+
+    case VIR_DOMAIN_DISK_BUS_XEN:
+    case VIR_DOMAIN_DISK_BUS_SD:
+        /* Xen and SD have no address type currently, so assign
+         * based on index */
+        break;
+    }
+
+    if (qemuGetDriveSourceString(disk->src, conn, &source) < 0)
+        goto error;
+
+    if (source &&
+        !((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY ||
+           disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) &&
+          disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) {
+
+        virBufferAddLit(&opt, "file=");
+
+        switch (actualType) {
+        case VIR_STORAGE_TYPE_DIR:
+            /* QEMU only supports magic FAT format for now */
+            if (disk->src->format > 0 &&
+                disk->src->format != VIR_STORAGE_FILE_FAT) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("unsupported disk driver type for '%s'"),
+                               virStorageFileFormatTypeToString(disk->src->format));
+                goto error;
+            }
+
+            if (!disk->src->readonly) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("cannot create virtual FAT disks in read-write mode"));
+                goto error;
+            }
+
+            virBufferAddLit(&opt, "fat:");
+
+            if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)
+                virBufferAddLit(&opt, "floppy:");
+
+            break;
+
+        case VIR_STORAGE_TYPE_BLOCK:
+            if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               disk->src->type == VIR_STORAGE_TYPE_VOLUME ?
+                               _("tray status 'open' is invalid for block type volume") :
+                               _("tray status 'open' is invalid for block type disk"));
+                goto error;
+            }
+
+            break;
+
+        default:
+            break;
+        }
+
+        virBufferEscape(&opt, ',', ",", "%s,", source);
+
+        if (disk->src->format > 0 &&
+            disk->src->type != VIR_STORAGE_TYPE_DIR)
+            virBufferAsprintf(&opt, "format=%s,",
+                              virStorageFileFormatTypeToString(disk->src->format));
+    }
+    VIR_FREE(source);
+
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
+        virBufferAddLit(&opt, "if=none");
+    else
+        virBufferAsprintf(&opt, "if=%s", bus);
+
+    if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
+        if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) {
+            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD))
+                virBufferAddLit(&opt, ",media=cdrom");
+        } else if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) {
+            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD))
+                virBufferAddLit(&opt, ",media=cdrom");
+        } else {
+            virBufferAddLit(&opt, ",media=cdrom");
+        }
+    }
+
+    if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
+        virBufferAsprintf(&opt, ",id=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias);
+    } else {
+        if (busid == -1 && unitid == -1) {
+            if (idx != -1)
+                virBufferAsprintf(&opt, ",index=%d", idx);
+        } else {
+            if (busid != -1)
+                virBufferAsprintf(&opt, ",bus=%d", busid);
+            if (unitid != -1)
+                virBufferAsprintf(&opt, ",unit=%d", unitid);
+        }
+    }
+    if (bootable &&
+        virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_BOOT) &&
+        (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK ||
          disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) &&
         disk->bus != VIR_DOMAIN_DISK_BUS_IDE)
         virBufferAddLit(&opt, ",boot=on");
@@ -4569,220 +4976,17 @@ static char *qemuBuildSmbiosBiosStr(virSysinfoBIOSDefPtr def)
     virBufferAddLit(&buf, "type=0");
 
     /* 0:Vendor */
-    if (def->vendor)
-        virBufferAsprintf(&buf, ",vendor=%s", def->vendor);
-    /* 0:BIOS Version */
-    if (def->version)
-        virBufferAsprintf(&buf, ",version=%s", def->version);
-    /* 0:BIOS Release Date */
-    if (def->date)
-        virBufferAsprintf(&buf, ",date=%s", def->date);
-    /* 0:System BIOS Major Release and 0:System BIOS Minor Release */
-    if (def->release)
-        virBufferAsprintf(&buf, ",release=%s", def->release);
-
-    if (virBufferCheckError(&buf) < 0)
-        goto error;
-
-    return virBufferContentAndReset(&buf);
-
- error:
-    virBufferFreeAndReset(&buf);
-    return NULL;
-}
-
-static char *qemuBuildSmbiosSystemStr(virSysinfoSystemDefPtr def,
-                                      bool skip_uuid)
-{
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-
-    if (!def ||
-        (!def->manufacturer && !def->product && !def->version &&
-         !def->serial && (!def->uuid || skip_uuid) &&
-         def->sku && !def->family))
-        return NULL;
-
-    virBufferAddLit(&buf, "type=1");
-
-    /* 1:Manufacturer */
-    if (def->manufacturer)
-        virBufferAsprintf(&buf, ",manufacturer=%s",
-                          def->manufacturer);
-     /* 1:Product Name */
-    if (def->product)
-        virBufferAsprintf(&buf, ",product=%s", def->product);
-    /* 1:Version */
-    if (def->version)
-        virBufferAsprintf(&buf, ",version=%s", def->version);
-    /* 1:Serial Number */
-    if (def->serial)
-        virBufferAsprintf(&buf, ",serial=%s", def->serial);
-    /* 1:UUID */
-    if (def->uuid && !skip_uuid)
-        virBufferAsprintf(&buf, ",uuid=%s", def->uuid);
-    /* 1:SKU Number */
-    if (def->sku)
-        virBufferAsprintf(&buf, ",sku=%s", def->sku);
-    /* 1:Family */
-    if (def->family)
-        virBufferAsprintf(&buf, ",family=%s", def->family);
-
-    if (virBufferCheckError(&buf) < 0)
-        goto error;
-
-    return virBufferContentAndReset(&buf);
-
- error:
-    virBufferFreeAndReset(&buf);
-    return NULL;
-}
-
-static char *qemuBuildSmbiosBaseBoardStr(virSysinfoBaseBoardDefPtr def)
-{
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-
-    if (!def)
-        return NULL;
-
-    virBufferAddLit(&buf, "type=2");
-
-    /* 2:Manufacturer */
-    if (def->manufacturer)
-        virBufferAsprintf(&buf, ",manufacturer=%s",
-                          def->manufacturer);
-    /* 2:Product Name */
-    if (def->product)
-        virBufferAsprintf(&buf, ",product=%s", def->product);
-    /* 2:Version */
-    if (def->version)
-        virBufferAsprintf(&buf, ",version=%s", def->version);
-    /* 2:Serial Number */
-    if (def->serial)
-        virBufferAsprintf(&buf, ",serial=%s", def->serial);
-    /* 2:Asset Tag */
-    if (def->asset)
-        virBufferAsprintf(&buf, ",asset=%s", def->asset);
-    /* 2:Location */
-    if (def->location)
-        virBufferAsprintf(&buf, ",location=%s", def->location);
-
-    if (virBufferCheckError(&buf) < 0)
-        goto error;
-
-    return virBufferContentAndReset(&buf);
-
- error:
-    virBufferFreeAndReset(&buf);
-    return NULL;
-}
-
-static char *
-qemuBuildClockArgStr(virDomainClockDefPtr def)
-{
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-
-    switch (def->offset) {
-    case VIR_DOMAIN_CLOCK_OFFSET_UTC:
-        virBufferAddLit(&buf, "base=utc");
-        break;
-
-    case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME:
-    case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE:
-        virBufferAddLit(&buf, "base=localtime");
-        break;
-
-    case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: {
-        time_t now = time(NULL);
-        struct tm nowbits;
-
-        if (def->data.variable.basis == VIR_DOMAIN_CLOCK_BASIS_LOCALTIME) {
-            long localOffset;
-
-            /* in the case of basis='localtime', rather than trying to
-             * keep that basis (and associated offset from UTC) in the
-             * status and deal with adding in the difference each time
-             * there is an RTC_CHANGE event, it is simpler and less
-             * error prone to just convert the adjustment an offset
-             * from UTC right now (and change the status to
-             * "basis='utc' to reflect this). This eliminates
-             * potential errors in both RTC_CHANGE events and in
-             * migration (in the case that the status of DST, or the
-             * timezone of the destination host, changed relative to
-             * startup).
-             */
-            if (virTimeLocalOffsetFromUTC(&localOffset) < 0)
-               goto error;
-            def->data.variable.adjustment += localOffset;
-            def->data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC;
-        }
-
-        now += def->data.variable.adjustment;
-        gmtime_r(&now, &nowbits);
-
-        /* when an RTC_CHANGE event is received from qemu, we need to
-         * have the adjustment used at domain start time available to
-         * compute the new offset from UTC. As this new value is
-         * itself stored in def->data.variable.adjustment, we need to
-         * save a copy of it now.
-        */
-        def->data.variable.adjustment0 = def->data.variable.adjustment;
-
-        virBufferAsprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d",
-                          nowbits.tm_year + 1900,
-                          nowbits.tm_mon + 1,
-                          nowbits.tm_mday,
-                          nowbits.tm_hour,
-                          nowbits.tm_min,
-                          nowbits.tm_sec);
-    }   break;
-
-    default:
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                       _("unsupported clock offset '%s'"),
-                       virDomainClockOffsetTypeToString(def->offset));
-        goto error;
-    }
-
-    /* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */
-    size_t i;
-    for (i = 0; i < def->ntimers; i++) {
-        if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) {
-            switch (def->timers[i]->track) {
-            case -1: /* unspecified - use hypervisor default */
-                break;
-            case VIR_DOMAIN_TIMER_TRACK_BOOT:
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unsupported rtc timer track '%s'"),
-                               virDomainTimerTrackTypeToString(def->timers[i]->track));
-                goto error;
-            case VIR_DOMAIN_TIMER_TRACK_GUEST:
-                virBufferAddLit(&buf, ",clock=vm");
-                break;
-            case VIR_DOMAIN_TIMER_TRACK_WALL:
-                virBufferAddLit(&buf, ",clock=host");
-                break;
-            }
-
-            switch (def->timers[i]->tickpolicy) {
-            case -1:
-            case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY:
-                /* This is the default - missed ticks delivered when
-                   next scheduled, at normal rate */
-                break;
-            case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP:
-                /* deliver ticks at a faster rate until caught up */
-                virBufferAddLit(&buf, ",driftfix=slew");
-                break;
-            case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
-            case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("unsupported rtc timer tickpolicy '%s'"),
-                               virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy));
-                goto error;
-            }
-            break; /* no need to check other timers - there is only one rtc */
-        }
-    }
+    if (def->vendor)
+        virBufferAsprintf(&buf, ",vendor=%s", def->vendor);
+    /* 0:BIOS Version */
+    if (def->version)
+        virBufferAsprintf(&buf, ",version=%s", def->version);
+    /* 0:BIOS Release Date */
+    if (def->date)
+        virBufferAsprintf(&buf, ",date=%s", def->date);
+    /* 0:System BIOS Major Release and 0:System BIOS Minor Release */
+    if (def->release)
+        virBufferAsprintf(&buf, ",release=%s", def->release);
 
     if (virBufferCheckError(&buf) < 0)
         goto error;
@@ -4794,378 +4998,207 @@ qemuBuildClockArgStr(virDomainClockDefPtr def)
     return NULL;
 }
 
-static int
-qemuBuildCpuModelArgStr(virQEMUDriverPtr driver,
-                        const virDomainDef *def,
-                        virBufferPtr buf,
-                        virQEMUCapsPtr qemuCaps,
-                        bool *hasHwVirt,
-                        bool migrating)
+static char *qemuBuildSmbiosSystemStr(virSysinfoSystemDefPtr def,
+                                      bool skip_uuid)
 {
-    int ret = -1;
-    size_t i;
-    virCPUDefPtr host = NULL;
-    virCPUDefPtr guest = NULL;
-    virCPUDefPtr cpu = NULL;
-    virCPUDefPtr featCpu = NULL;
-    size_t ncpus = 0;
-    char **cpus = NULL;
-    virCPUDataPtr data = NULL;
-    virCPUDataPtr hostData = NULL;
-    char *compare_msg = NULL;
-    virCPUCompareResult cmp;
-    const char *preferred;
-    virCapsPtr caps = NULL;
-    bool compareAgainstHost = ((def->virtType == VIR_DOMAIN_VIRT_KVM ||
-                                def->cpu->mode != VIR_CPU_MODE_CUSTOM) &&
-                               def->cpu->mode != VIR_CPU_MODE_HOST_PASSTHROUGH);
-
-    if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
-        goto cleanup;
-
-    host = caps->host.cpu;
-
-    if (!host ||
-        !host->model ||
-        (ncpus = virQEMUCapsGetCPUDefinitions(qemuCaps, &cpus)) == 0) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("CPU specification not supported by hypervisor"));
-        goto cleanup;
-    }
-
-    if (!(cpu = virCPUDefCopy(def->cpu)))
-        goto cleanup;
-
-    if (cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
-        !migrating &&
-        cpuUpdate(cpu, host) < 0)
-        goto cleanup;
-
-    /* For non-KVM, CPU features are emulated, so host compat doesn't matter */
-    if (compareAgainstHost) {
-        bool noTSX = false;
-
-        cmp = cpuGuestData(host, cpu, &data, &compare_msg);
-        switch (cmp) {
-        case VIR_CPU_COMPARE_INCOMPATIBLE:
-            if (cpuEncode(host->arch, host, NULL, &hostData,
-                          NULL, NULL, NULL, NULL) == 0 &&
-                (!cpuHasFeature(hostData, "hle") ||
-                 !cpuHasFeature(hostData, "rtm")) &&
-                (STREQ_NULLABLE(cpu->model, "Haswell") ||
-                 STREQ_NULLABLE(cpu->model, "Broadwell")))
-                noTSX = true;
-
-            if (compare_msg) {
-                if (noTSX) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("guest and host CPU are not compatible: "
-                                     "%s; try using '%s-noTSX' CPU model"),
-                                   compare_msg, cpu->model);
-                } else {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("guest and host CPU are not compatible: "
-                                     "%s"),
-                                   compare_msg);
-                }
-            } else {
-                if (noTSX) {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                                   _("guest CPU is not compatible with host "
-                                     "CPU; try using '%s-noTSX' CPU model"),
-                                   cpu->model);
-                } else {
-                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                                   _("guest CPU is not compatible with host "
-                                     "CPU"));
-                }
-            }
-            /* fall through */
-        case VIR_CPU_COMPARE_ERROR:
-            goto cleanup;
-
-        default:
-            break;
-        }
-    }
-
-    /* Only 'svm' requires --enable-nesting. The nested
-     * 'vmx' patches now simply hook off the CPU features
-     */
-    if ((def->os.arch == VIR_ARCH_X86_64 || def->os.arch == VIR_ARCH_I686) &&
-         compareAgainstHost) {
-        int hasSVM = cpuHasFeature(data, "svm");
-        if (hasSVM < 0)
-            goto cleanup;
-        *hasHwVirt = hasSVM > 0 ? true : false;
-    }
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
 
-    if ((cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) ||
-        ((cpu->mode == VIR_CPU_MODE_HOST_MODEL) &&
-          ARCH_IS_PPC64(def->os.arch))) {
-        const char *mode = virCPUModeTypeToString(cpu->mode);
-        if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_HOST)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("CPU mode '%s' is not supported by QEMU"
-                             " binary"), mode);
-            goto cleanup;
-        }
-        if (def->virtType != VIR_DOMAIN_VIRT_KVM) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("CPU mode '%s' is only supported with kvm"),
-                           mode);
-            goto cleanup;
-        }
-        virBufferAddLit(buf, "host");
+    if (!def ||
+        (!def->manufacturer && !def->product && !def->version &&
+         !def->serial && (!def->uuid || skip_uuid) &&
+         def->sku && !def->family))
+        return NULL;
 
-        if (def->os.arch == VIR_ARCH_ARMV7L &&
-            host->arch == VIR_ARCH_AARCH64) {
-            if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_AARCH64_OFF)) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                               _("QEMU binary does not support CPU "
-                                 "host-passthrough for armv7l on "
-                                 "aarch64 host"));
-                goto cleanup;
-            }
+    virBufferAddLit(&buf, "type=1");
 
-            virBufferAddLit(buf, ",aarch64=off");
-        }
+    /* 1:Manufacturer */
+    if (def->manufacturer)
+        virBufferAsprintf(&buf, ",manufacturer=%s",
+                          def->manufacturer);
+     /* 1:Product Name */
+    if (def->product)
+        virBufferAsprintf(&buf, ",product=%s", def->product);
+    /* 1:Version */
+    if (def->version)
+        virBufferAsprintf(&buf, ",version=%s", def->version);
+    /* 1:Serial Number */
+    if (def->serial)
+        virBufferAsprintf(&buf, ",serial=%s", def->serial);
+    /* 1:UUID */
+    if (def->uuid && !skip_uuid)
+        virBufferAsprintf(&buf, ",uuid=%s", def->uuid);
+    /* 1:SKU Number */
+    if (def->sku)
+        virBufferAsprintf(&buf, ",sku=%s", def->sku);
+    /* 1:Family */
+    if (def->family)
+        virBufferAsprintf(&buf, ",family=%s", def->family);
 
-        if (ARCH_IS_PPC64(def->os.arch) &&
-            cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
-            def->cpu->model != NULL) {
-            virBufferAsprintf(buf, ",compat=%s", def->cpu->model);
-        } else {
-            featCpu = cpu;
-        }
+    if (virBufferCheckError(&buf) < 0)
+        goto error;
 
-    } else {
-        if (VIR_ALLOC(guest) < 0)
-            goto cleanup;
-        if (VIR_STRDUP(guest->vendor_id, cpu->vendor_id) < 0)
-            goto cleanup;
+    return virBufferContentAndReset(&buf);
 
-        if (compareAgainstHost) {
-            guest->arch = host->arch;
-            if (cpu->match == VIR_CPU_MATCH_MINIMUM)
-                preferred = host->model;
-            else
-                preferred = cpu->model;
+ error:
+    virBufferFreeAndReset(&buf);
+    return NULL;
+}
 
-            guest->type = VIR_CPU_TYPE_GUEST;
-            guest->fallback = cpu->fallback;
-            if (cpuDecode(guest, data,
-                          (const char **)cpus, ncpus, preferred) < 0)
-                goto cleanup;
-        } else {
-            guest->arch = def->os.arch;
-            if (VIR_STRDUP(guest->model, cpu->model) < 0)
-                goto cleanup;
-        }
-        virBufferAdd(buf, guest->model, -1);
-        if (guest->vendor_id)
-            virBufferAsprintf(buf, ",vendor=%s", guest->vendor_id);
-        featCpu = guest;
-    }
+static char *qemuBuildSmbiosBaseBoardStr(virSysinfoBaseBoardDefPtr def)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
 
-    if (featCpu) {
-        for (i = 0; i < featCpu->nfeatures; i++) {
-            char sign;
-            if (featCpu->features[i].policy == VIR_CPU_FEATURE_DISABLE)
-                sign = '-';
-            else
-                sign = '+';
+    if (!def)
+        return NULL;
 
-            virBufferAsprintf(buf, ",%c%s", sign, featCpu->features[i].name);
-        }
-    }
+    virBufferAddLit(&buf, "type=2");
 
-    ret = 0;
- cleanup:
-    virObjectUnref(caps);
-    VIR_FREE(compare_msg);
-    cpuDataFree(data);
-    cpuDataFree(hostData);
-    virCPUDefFree(guest);
-    virCPUDefFree(cpu);
-    return ret;
+    /* 2:Manufacturer */
+    if (def->manufacturer)
+        virBufferAsprintf(&buf, ",manufacturer=%s",
+                          def->manufacturer);
+    /* 2:Product Name */
+    if (def->product)
+        virBufferAsprintf(&buf, ",product=%s", def->product);
+    /* 2:Version */
+    if (def->version)
+        virBufferAsprintf(&buf, ",version=%s", def->version);
+    /* 2:Serial Number */
+    if (def->serial)
+        virBufferAsprintf(&buf, ",serial=%s", def->serial);
+    /* 2:Asset Tag */
+    if (def->asset)
+        virBufferAsprintf(&buf, ",asset=%s", def->asset);
+    /* 2:Location */
+    if (def->location)
+        virBufferAsprintf(&buf, ",location=%s", def->location);
+
+    if (virBufferCheckError(&buf) < 0)
+        goto error;
+
+    return virBufferContentAndReset(&buf);
+
+ error:
+    virBufferFreeAndReset(&buf);
+    return NULL;
 }
 
-static int
-qemuBuildCpuArgStr(virQEMUDriverPtr driver,
-                   const virDomainDef *def,
-                   virQEMUCapsPtr qemuCaps,
-                   virArch hostarch,
-                   char **opt,
-                   bool *hasHwVirt,
-                   bool migrating)
+static char *
+qemuBuildClockArgStr(virDomainClockDefPtr def)
 {
-    const char *default_model;
-    bool have_cpu = false;
-    int ret = -1;
     virBuffer buf = VIR_BUFFER_INITIALIZER;
-    size_t i;
 
-    *hasHwVirt = false;
-
-    if (def->os.arch == VIR_ARCH_I686)
-        default_model = "qemu32";
-    else
-        default_model = "qemu64";
+    switch (def->offset) {
+    case VIR_DOMAIN_CLOCK_OFFSET_UTC:
+        virBufferAddLit(&buf, "base=utc");
+        break;
 
-    if (def->cpu &&
-        (def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) {
-        if (qemuBuildCpuModelArgStr(driver, def, &buf, qemuCaps,
-                                    hasHwVirt, migrating) < 0)
-            goto cleanup;
-        have_cpu = true;
-    } else {
-        /*
-         * Need to force a 32-bit guest CPU type if
-         *
-         *  1. guest OS is i686
-         *  2. host OS is x86_64
-         *  3. emulator is qemu-kvm or kvm
-         *
-         * Or
-         *
-         *  1. guest OS is i686
-         *  2. emulator is qemu-system-x86_64
-         */
-        if (def->os.arch == VIR_ARCH_I686 &&
-            ((hostarch == VIR_ARCH_X86_64 &&
-              strstr(def->emulator, "kvm")) ||
-             strstr(def->emulator, "x86_64"))) {
-            virBufferAdd(&buf, default_model, -1);
-            have_cpu = true;
-        }
-    }
+    case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME:
+    case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE:
+        virBufferAddLit(&buf, "base=localtime");
+        break;
 
-    /* Handle paravirtual timers  */
-    for (i = 0; i < def->clock.ntimers; i++) {
-        virDomainTimerDefPtr timer = def->clock.timers[i];
+    case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: {
+        time_t now = time(NULL);
+        struct tm nowbits;
 
-        if (timer->present == -1)
-            continue;
+        if (def->data.variable.basis == VIR_DOMAIN_CLOCK_BASIS_LOCALTIME) {
+            long localOffset;
 
-        if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK) {
-            virBufferAsprintf(&buf, "%s,%ckvmclock",
-                              have_cpu ? "" : default_model,
-                              timer->present ? '+' : '-');
-            have_cpu = true;
-        } else if (timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK &&
-                   timer->present) {
-            virBufferAsprintf(&buf, "%s,hv_time",
-                              have_cpu ? "" : default_model);
-            have_cpu = true;
+            /* in the case of basis='localtime', rather than trying to
+             * keep that basis (and associated offset from UTC) in the
+             * status and deal with adding in the difference each time
+             * there is an RTC_CHANGE event, it is simpler and less
+             * error prone to just convert the adjustment an offset
+             * from UTC right now (and change the status to
+             * "basis='utc' to reflect this). This eliminates
+             * potential errors in both RTC_CHANGE events and in
+             * migration (in the case that the status of DST, or the
+             * timezone of the destination host, changed relative to
+             * startup).
+             */
+            if (virTimeLocalOffsetFromUTC(&localOffset) < 0)
+               goto error;
+            def->data.variable.adjustment += localOffset;
+            def->data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC;
         }
-    }
 
-    if (def->apic_eoi) {
-        char sign;
-        if (def->apic_eoi == VIR_TRISTATE_SWITCH_ON)
-            sign = '+';
-        else
-            sign = '-';
+        now += def->data.variable.adjustment;
+        gmtime_r(&now, &nowbits);
 
-        virBufferAsprintf(&buf, "%s,%ckvm_pv_eoi",
-                          have_cpu ? "" : default_model,
-                          sign);
-        have_cpu = true;
-    }
+        /* when an RTC_CHANGE event is received from qemu, we need to
+         * have the adjustment used at domain start time available to
+         * compute the new offset from UTC. As this new value is
+         * itself stored in def->data.variable.adjustment, we need to
+         * save a copy of it now.
+        */
+        def->data.variable.adjustment0 = def->data.variable.adjustment;
 
-    if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK]) {
-        char sign;
-        if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK] == VIR_TRISTATE_SWITCH_ON)
-            sign = '+';
-        else
-            sign = '-';
+        virBufferAsprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d",
+                          nowbits.tm_year + 1900,
+                          nowbits.tm_mon + 1,
+                          nowbits.tm_mday,
+                          nowbits.tm_hour,
+                          nowbits.tm_min,
+                          nowbits.tm_sec);
+    }   break;
 
-        virBufferAsprintf(&buf, "%s,%ckvm_pv_unhalt",
-                          have_cpu ? "" : default_model,
-                          sign);
-        have_cpu = true;
+    default:
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                       _("unsupported clock offset '%s'"),
+                       virDomainClockOffsetTypeToString(def->offset));
+        goto error;
     }
 
-    if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) {
-        if (!have_cpu) {
-            virBufferAdd(&buf, default_model, -1);
-            have_cpu = true;
-        }
-
-        for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) {
-            switch ((virDomainHyperv) i) {
-            case VIR_DOMAIN_HYPERV_RELAXED:
-            case VIR_DOMAIN_HYPERV_VAPIC:
-                if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON)
-                    virBufferAsprintf(&buf, ",hv_%s",
-                                      virDomainHypervTypeToString(i));
+    /* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */
+    size_t i;
+    for (i = 0; i < def->ntimers; i++) {
+        if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) {
+            switch (def->timers[i]->track) {
+            case -1: /* unspecified - use hypervisor default */
                 break;
-
-            case VIR_DOMAIN_HYPERV_SPINLOCKS:
-                if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON)
-                    virBufferAsprintf(&buf, ",hv_spinlocks=0x%x",
-                                      def->hyperv_spinlocks);
+            case VIR_DOMAIN_TIMER_TRACK_BOOT:
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unsupported rtc timer track '%s'"),
+                               virDomainTimerTrackTypeToString(def->timers[i]->track));
+                goto error;
+            case VIR_DOMAIN_TIMER_TRACK_GUEST:
+                virBufferAddLit(&buf, ",clock=vm");
                 break;
-
-            /* coverity[dead_error_begin] */
-            case VIR_DOMAIN_HYPERV_LAST:
+            case VIR_DOMAIN_TIMER_TRACK_WALL:
+                virBufferAddLit(&buf, ",clock=host");
                 break;
             }
-        }
-    }
-
-    for (i = 0; i < def->npanics; i++) {
-        if (def->panics[i]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV) {
-            if (!have_cpu) {
-                virBufferAdd(&buf, default_model, -1);
-                have_cpu = true;
-            }
-
-            virBufferAddLit(&buf, ",hv_crash");
-            break;
-        }
-    }
-
-    if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
-        if (!have_cpu) {
-            virBufferAdd(&buf, default_model, -1);
-            have_cpu = true;
-        }
 
-        for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) {
-            switch ((virDomainKVM) i) {
-            case VIR_DOMAIN_KVM_HIDDEN:
-                if (def->kvm_features[i] == VIR_TRISTATE_SWITCH_ON)
-                    virBufferAddLit(&buf, ",kvm=off");
+            switch (def->timers[i]->tickpolicy) {
+            case -1:
+            case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY:
+                /* This is the default - missed ticks delivered when
+                   next scheduled, at normal rate */
                 break;
-
-            /* coverity[dead_error_begin] */
-            case VIR_DOMAIN_KVM_LAST:
+            case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP:
+                /* deliver ticks at a faster rate until caught up */
+                virBufferAddLit(&buf, ",driftfix=slew");
                 break;
+            case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
+            case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                               _("unsupported rtc timer tickpolicy '%s'"),
+                               virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy));
+                goto error;
             }
+            break; /* no need to check other timers - there is only one rtc */
         }
     }
 
-    if (def->features[VIR_DOMAIN_FEATURE_PMU]) {
-        virTristateSwitch pmu = def->features[VIR_DOMAIN_FEATURE_PMU];
-        if (!have_cpu)
-            virBufferAdd(&buf, default_model, -1);
-
-        virBufferAsprintf(&buf, ",pmu=%s",
-                          virTristateSwitchTypeToString(pmu));
-        have_cpu = true;
-    }
-
     if (virBufferCheckError(&buf) < 0)
-        goto cleanup;
-
-    *opt = virBufferContentAndReset(&buf);
+        goto error;
 
-    ret = 0;
+    return virBufferContentAndReset(&buf);
 
- cleanup:
-    return ret;
+ error:
+    virBufferFreeAndReset(&buf);
+    return NULL;
 }
 
 static char *
@@ -6723,11 +6756,9 @@ qemuBuildCommandLine(virConnectPtr conn,
     virErrorPtr originalError = NULL;
     size_t i, j;
     char uuid[VIR_UUID_STRING_BUFLEN];
-    char *cpu;
     char *smp;
     int spicecnt = 0;
     int last_good_net = -1;
-    bool hasHwVirt = false;
     virCommandPtr cmd = NULL;
     bool allowReboot = true;
     bool emitBootindex = false;
@@ -6758,7 +6789,6 @@ qemuBuildCommandLine(virConnectPtr conn,
         VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL,
         VIR_DOMAIN_CONTROLLER_TYPE_CCID,
     };
-    virArch hostarch = virArchFromHost();
     virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
     virBuffer boot_buf = VIR_BUFFER_INITIALIZER;
     char *boot_order_str = NULL, *boot_opts_str = NULL;
@@ -6807,19 +6837,9 @@ qemuBuildCommandLine(virConnectPtr conn,
     if (qemuBuildMachineCommandLine(cmd, def, qemuCaps) < 0)
         goto error;
 
-    if (qemuBuildCpuArgStr(driver, def, qemuCaps,
-                           hostarch, &cpu, &hasHwVirt, !!migrateURI) < 0)
+    if (qemuBuildCpuCommandLine(cmd, driver, def, qemuCaps, !!migrateURI) < 0)
         goto error;
 
-    if (cpu) {
-        virCommandAddArgList(cmd, "-cpu", cpu, NULL);
-        VIR_FREE(cpu);
-
-        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NESTING) &&
-            hasHwVirt)
-            virCommandAddArg(cmd, "-enable-nesting");
-    }
-
     if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0)
         goto error;
 
-- 
2.5.0




More information about the libvir-list mailing list