diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index def69749a4..16e0709a53 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -424,3 +424,26 @@ cpuUpdate(virCPUDefPtr guest, return driver->update(guest, host); } + +int +cpuHasFeature(const char *arch, + const union cpuData *data, + const char *feature) +{ + struct cpuArchDriver *driver; + + VIR_DEBUG("arch=%s, data=%p, feature=%s", + arch, data, feature); + + if ((driver = cpuGetSubDriver(arch)) == NULL) + return -1; + + if (driver->hasFeature == NULL) { + virCPUReportError(VIR_ERR_NO_SUPPORT, + _("cannot check guest CPU data for %s architecture"), + arch); + return -1; + } + + return driver->hasFeature(data, feature); +} diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index a745917f64..76d0e8e2e6 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -82,6 +82,10 @@ typedef int (*cpuArchUpdate) (virCPUDefPtr guest, const virCPUDefPtr host); +typedef int +(*cpuArchHasFeature) (const union cpuData *data, + const char *feature); + struct cpuArchDriver { const char *name; @@ -95,6 +99,7 @@ struct cpuArchDriver { cpuArchGuestData guestData; cpuArchBaseline baseline; cpuArchUpdate update; + cpuArchHasFeature hasFeature; }; @@ -151,4 +156,10 @@ extern int cpuUpdate (virCPUDefPtr guest, const virCPUDefPtr host); +extern int +cpuHasFeature(const char *arch, + const union cpuData *data, + const char *feature); + + #endif /* __VIR_CPU_H__ */ diff --git a/src/cpu/cpu_x86.c b/src/cpu/cpu_x86.c index 19379016d0..813b49901a 100644 --- a/src/cpu/cpu_x86.c +++ b/src/cpu/cpu_x86.c @@ -1754,6 +1754,35 @@ cleanup: return ret; } +static int x86HasFeature(const union cpuData *data, + const char *name) +{ + struct x86_map *map; + struct x86_feature *feature; + int ret = -1; + int i; + + if (!(map = x86LoadMap())) + return -1; + + if (!(feature = x86FeatureFind(map, name))) + goto cleanup; + + for (i = 0 ; i < feature->ncpuid ; i++) { + struct cpuX86cpuid *cpuid; + + cpuid = x86DataCpuid(data, feature->cpuid[i].function); + if (cpuid && x86cpuidMatchMasked(cpuid, feature->cpuid + i)) { + ret = 1; + goto cleanup; + } + } + ret = 0; + +cleanup: + x86MapFree(map); + return ret; +} struct cpuArchDriver cpuDriverX86 = { .name = "x86", @@ -1771,4 +1800,5 @@ struct cpuArchDriver cpuDriverX86 = { .guestData = x86GuestData, .baseline = x86Baseline, .update = x86Update, + .hasFeature = x86HasFeature, }; diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index d46c7d8e0a..3b127d6687 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -96,6 +96,7 @@ cpuEncode; cpuGuestData; cpuNodeData; cpuUpdate; +cpuHasFeature; # cpu_conf.h diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 16145b01f3..de14f92d06 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1211,6 +1211,8 @@ static unsigned long long qemudComputeCmdFlags(const char *help, flags |= QEMUD_CMD_FLAG_NO_KVM_PIT; if (strstr(help, "-tdf")) flags |= QEMUD_CMD_FLAG_TDF; + if (strstr(help, "-enable-nesting")) + flags |= QEMUD_CMD_FLAG_NESTING; if (strstr(help, ",menu=on")) flags |= QEMUD_CMD_FLAG_BOOT_MENU; if (strstr(help, "-fsdev")) @@ -3577,7 +3579,8 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, const char *emulator, unsigned long long qemuCmdFlags, const struct utsname *ut, - char **opt) + char **opt, + bool *hasHwVirt) { const virCPUDefPtr host = driver->caps->host.cpu; virCPUDefPtr guest = NULL; @@ -3588,6 +3591,8 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, virBuffer buf = VIR_BUFFER_INITIALIZER; int i; + *hasHwVirt = false; + if (def->cpu && def->cpu->model) { if (qemudProbeCPUModels(emulator, qemuCmdFlags, ut->machine, &ncpus, &cpus) < 0) @@ -3603,6 +3608,7 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, if (ncpus > 0 && host) { virCPUCompareResult cmp; const char *preferred; + int hasSVM; cmp = cpuGuestData(host, def->cpu, &data); switch (cmp) { @@ -3629,6 +3635,14 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, if (cpuDecode(guest, data, cpus, ncpus, preferred) < 0) goto cleanup; + /* Only 'svm' requires --enable-nesting. The nested + * 'vmx' patches now simply hook off the CPU features + */ + hasSVM = cpuHasFeature(guest->arch, data, "svm"); + if (hasSVM < 0) + goto cleanup; + *hasHwVirt = hasSVM > 0 ? true : false; + virBufferVSprintf(&buf, "%s", guest->model); for (i = 0; i < guest->nfeatures; i++) { char sign; @@ -3755,6 +3769,7 @@ int qemudBuildCommandLine(virConnectPtr conn, char *cpu; char *smp; int last_good_net = -1; + bool hasHwVirt = false; uname_normalize(&ut); @@ -3948,13 +3963,18 @@ int qemudBuildCommandLine(virConnectPtr conn, ADD_ARG_LIT(def->os.machine); } - if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, &ut, &cpu) < 0) + if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, + &ut, &cpu, &hasHwVirt) < 0) goto error; if (cpu) { ADD_ARG_LIT("-cpu"); ADD_ARG_LIT(cpu); VIR_FREE(cpu); + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NESTING) && + hasHwVirt) + ADD_ARG_LIT("-enable-nesting"); } if (disableKQEMU) diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index fbd89de22e..d2e6857bb6 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -94,6 +94,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_BOOT_MENU = (1LL << 38), /* -boot menu=on support */ QEMUD_CMD_FLAG_ENABLE_KQEMU = (1LL << 39), /* -enable-kqemu flag */ QEMUD_CMD_FLAG_FSDEV = (1LL << 40), /* -fstype filesystem passthrough */ + QEMUD_CMD_FLAG_NESTING = (1LL << 41), /* -enable-nesting (SVM/VMX) */ }; /* Main driver state */ diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 56a49fd130..d072cb0e5f 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -171,7 +171,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_RTC_TD_HACK | QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | - QEMUD_CMD_FLAG_TDF, + QEMUD_CMD_FLAG_TDF | + QEMUD_CMD_FLAG_NESTING, 10005, 1, 0); DO_TEST("kvm-86", QEMUD_CMD_FLAG_VNC_COLON | @@ -194,7 +195,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_RTC_TD_HACK | QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | - QEMUD_CMD_FLAG_TDF, + QEMUD_CMD_FLAG_TDF | + QEMUD_CMD_FLAG_NESTING, 10050, 1, 0); DO_TEST("qemu-kvm-0.11.0-rc2", QEMUD_CMD_FLAG_VNC_COLON | @@ -221,7 +223,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | QEMUD_CMD_FLAG_TDF | - QEMUD_CMD_FLAG_BOOT_MENU, + QEMUD_CMD_FLAG_BOOT_MENU | + QEMUD_CMD_FLAG_NESTING, 10092, 1, 0); DO_TEST("qemu-0.12.1", QEMUD_CMD_FLAG_VNC_COLON | @@ -277,7 +280,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_NO_HPET | QEMUD_CMD_FLAG_NO_KVM_PIT | QEMUD_CMD_FLAG_TDF | - QEMUD_CMD_FLAG_BOOT_MENU, + QEMUD_CMD_FLAG_BOOT_MENU | + QEMUD_CMD_FLAG_NESTING, 12003, 1, 0); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;