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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list