libvirt/src/libxl/libxl_conf.c
Marek Marczykowski-Górecki f253dc90f5 libxl: use b_info->{acpi,acpi} when available
b_info->u.hvm.{acpi,apic} are deprecated. But also, on recent libxl
version (4.14) the old one seems to be broken. While libxl part should
be fixed too, update the usage here and at some point drop support for
the old version.
b_info->acpi was added in Xen 4.8
b_info->apic was added in Xen 4.10
Xen 4.10 is the oldest version that still has security support (until
December 2020).

Signed-off-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Jim Fehlig <jfehlig@suse.com>
2020-09-18 08:51:47 +02:00

2540 lines
82 KiB
C

/*
* libxl_conf.c: libxl configuration management
*
* Copyright (C) 2012-2014, 2016 Red Hat, Inc.
* Copyright (c) 2011-2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
* Copyright (C) 2011 Univention GmbH.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <libxl.h>
#include <sys/types.h>
#include "internal.h"
#include "virlog.h"
#include "virerror.h"
#include "datatypes.h"
#include "virconf.h"
#include "virfile.h"
#include "virstring.h"
#include "viralloc.h"
#include "viruuid.h"
#include "vircommand.h"
#include "virsocketaddr.h"
#include "libxl_domain.h"
#include "libxl_conf.h"
#include "libxl_utils.h"
#include "virstoragefile.h"
#include "virsecret.h"
#include "cpu/cpu.h"
#include "xen_common.h"
#include "xen_xl.h"
#include "virnetdevvportprofile.h"
#include "virenum.h"
#define VIR_FROM_THIS VIR_FROM_LIBXL
VIR_LOG_INIT("libxl.libxl_conf");
static virClassPtr libxlDriverConfigClass;
static void libxlDriverConfigDispose(void *obj);
static int libxlConfigOnceInit(void)
{
if (!VIR_CLASS_NEW(libxlDriverConfig, virClassForObject()))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(libxlConfig);
static void
libxlDriverConfigDispose(void *obj)
{
libxlDriverConfigPtr cfg = obj;
virObjectUnref(cfg->caps);
libxl_ctx_free(cfg->ctx);
if (cfg->logger)
libxlLoggerFree(cfg->logger);
VIR_FREE(cfg->configBaseDir);
VIR_FREE(cfg->configDir);
VIR_FREE(cfg->autostartDir);
VIR_FREE(cfg->logDir);
VIR_FREE(cfg->stateDir);
VIR_FREE(cfg->libDir);
VIR_FREE(cfg->saveDir);
VIR_FREE(cfg->autoDumpDir);
VIR_FREE(cfg->lockManagerName);
VIR_FREE(cfg->channelDir);
virFirmwareFreeList(cfg->firmwares, cfg->nfirmwares);
}
static libxl_action_on_shutdown
libxlActionFromVirLifecycle(virDomainLifecycleAction action)
{
switch (action) {
case VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY:
return LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART:
return LIBXL_ACTION_ON_SHUTDOWN_RESTART;
case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART_RENAME:
return LIBXL_ACTION_ON_SHUTDOWN_RESTART_RENAME;
case VIR_DOMAIN_LIFECYCLE_ACTION_PRESERVE:
return LIBXL_ACTION_ON_SHUTDOWN_PRESERVE;
case VIR_DOMAIN_LIFECYCLE_ACTION_COREDUMP_DESTROY:
return LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY;
case VIR_DOMAIN_LIFECYCLE_ACTION_COREDUMP_RESTART:
return LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART;
case VIR_DOMAIN_LIFECYCLE_ACTION_LAST:
break;
}
return 0;
}
static int
libxlMakeDomCreateInfo(libxl_ctx *ctx,
virDomainDefPtr def,
libxl_domain_create_info *c_info)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
libxl_domain_create_info_init(c_info);
if (def->os.type == VIR_DOMAIN_OSTYPE_HVM ||
def->os.type == VIR_DOMAIN_OSTYPE_XENPVH) {
#ifdef WITH_XEN_PVH
c_info->type = def->os.type == VIR_DOMAIN_OSTYPE_HVM ?
LIBXL_DOMAIN_TYPE_HVM : LIBXL_DOMAIN_TYPE_PVH;
#else
if (def->os.type == VIR_DOMAIN_OSTYPE_XENPVH) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("PVH guest os type not supported"));
return -1;
}
c_info->type = LIBXL_DOMAIN_TYPE_HVM;
#endif
switch ((virTristateSwitch) def->features[VIR_DOMAIN_FEATURE_HAP]) {
case VIR_TRISTATE_SWITCH_OFF:
libxl_defbool_set(&c_info->hap, false);
break;
case VIR_TRISTATE_SWITCH_ON:
libxl_defbool_set(&c_info->hap, true);
break;
case VIR_TRISTATE_SWITCH_ABSENT:
case VIR_TRISTATE_SWITCH_LAST:
break;
}
} else {
c_info->type = LIBXL_DOMAIN_TYPE_PV;
}
#ifdef LIBXL_HAVE_CREATEINFO_PASSTHROUGH
if (def->features[VIR_DOMAIN_FEATURE_XEN] == VIR_TRISTATE_SWITCH_ON) {
switch ((virTristateSwitch) def->xen_features[VIR_DOMAIN_XEN_PASSTHROUGH]) {
case VIR_TRISTATE_SWITCH_ON:
if (def->xen_passthrough_mode == VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SYNC_PT)
c_info->passthrough = LIBXL_PASSTHROUGH_SYNC_PT;
else if (def->xen_passthrough_mode == VIR_DOMAIN_XEN_PASSTHROUGH_MODE_SHARE_PT)
c_info->passthrough = LIBXL_PASSTHROUGH_SHARE_PT;
else
c_info->passthrough = LIBXL_PASSTHROUGH_ENABLED;
break;
case VIR_TRISTATE_SWITCH_OFF:
c_info->passthrough = LIBXL_PASSTHROUGH_DISABLED;
break;
case VIR_TRISTATE_SWITCH_ABSENT:
case VIR_TRISTATE_SWITCH_LAST:
break;
}
}
#endif
c_info->name = g_strdup(def->name);
if (def->nseclabels &&
def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_STATIC) {
if (libxl_flask_context_to_sid(ctx,
def->seclabels[0]->label,
strlen(def->seclabels[0]->label),
&c_info->ssidref)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to resolve security label '%s'"),
def->seclabels[0]->label);
}
}
virUUIDFormat(def->uuid, uuidstr);
if (libxl_uuid_from_string(&c_info->uuid, uuidstr)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight failed to parse UUID '%s'"), uuidstr);
goto error;
}
return 0;
error:
libxl_domain_create_info_dispose(c_info);
return -1;
}
static int
libxlMakeChrdevStr(virDomainChrDefPtr def, char **buf)
{
virDomainChrSourceDefPtr srcdef = def->source;
const char *type = virDomainChrTypeToString(srcdef->type);
if (!type) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("unknown chrdev type"));
return -1;
}
switch (srcdef->type) {
case VIR_DOMAIN_CHR_TYPE_NULL:
case VIR_DOMAIN_CHR_TYPE_STDIO:
case VIR_DOMAIN_CHR_TYPE_VC:
case VIR_DOMAIN_CHR_TYPE_PTY:
*buf = g_strdup(type);
break;
case VIR_DOMAIN_CHR_TYPE_FILE:
case VIR_DOMAIN_CHR_TYPE_PIPE:
*buf = g_strdup_printf("%s:%s", type, srcdef->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_DEV:
*buf = g_strdup(srcdef->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_UDP: {
const char *connectHost = srcdef->data.udp.connectHost;
const char *bindHost = srcdef->data.udp.bindHost;
const char *bindService = srcdef->data.udp.bindService;
if (connectHost == NULL)
connectHost = "";
if (bindHost == NULL)
bindHost = "";
if (bindService == NULL)
bindService = "0";
*buf = g_strdup_printf("udp:%s:%s@%s:%s", connectHost,
srcdef->data.udp.connectService, bindHost, bindService);
break;
}
case VIR_DOMAIN_CHR_TYPE_TCP: {
const char *prefix;
if (srcdef->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET)
prefix = "telnet";
else
prefix = "tcp";
*buf = g_strdup_printf("%s:%s:%s%s", prefix, srcdef->data.tcp.host,
srcdef->data.tcp.service,
srcdef->data.tcp.listen ? ",server,nowait" : "");
break;
}
case VIR_DOMAIN_CHR_TYPE_UNIX:
*buf = g_strdup_printf("unix:%s%s", srcdef->data.nix.path,
srcdef->data.nix.listen ? ",server,nowait" : "");
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported chardev '%s'"), type);
return -1;
}
return 0;
}
static int
libxlMakeDomBuildInfo(virDomainDefPtr def,
libxlDriverConfigPtr cfg,
virCapsPtr caps,
libxl_domain_config *d_config)
{
virDomainClockDef clock = def->clock;
libxl_ctx *ctx = cfg->ctx;
libxl_domain_build_info *b_info = &d_config->b_info;
bool hvm = def->os.type == VIR_DOMAIN_OSTYPE_HVM;
bool pvh = def->os.type == VIR_DOMAIN_OSTYPE_XENPVH;
size_t i;
size_t nusbdevice = 0;
libxl_domain_build_info_init(b_info);
if (hvm) {
libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_HVM);
} else if (pvh) {
#ifdef WITH_XEN_PVH
libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_PVH);
#else
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("PVH guest os type not supported"));
return -1;
#endif
} else {
libxl_domain_build_info_init_type(b_info, LIBXL_DOMAIN_TYPE_PV);
}
b_info->max_vcpus = virDomainDefGetVcpusMax(def);
if (libxl_cpu_bitmap_alloc(ctx, &b_info->avail_vcpus, b_info->max_vcpus))
return -1;
libxl_bitmap_set_none(&b_info->avail_vcpus);
for (i = 0; i < virDomainDefGetVcpus(def); i++)
libxl_bitmap_set((&b_info->avail_vcpus), i);
switch ((virDomainClockOffsetType) clock.offset) {
case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE:
if (clock.data.variable.basis == VIR_DOMAIN_CLOCK_BASIS_LOCALTIME)
libxl_defbool_set(&b_info->localtime, true);
b_info->rtc_timeoffset = clock.data.variable.adjustment;
break;
case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME:
libxl_defbool_set(&b_info->localtime, true);
break;
/* Nothing to do since UTC is the default in libxl */
case VIR_DOMAIN_CLOCK_OFFSET_UTC:
break;
case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported clock offset '%s'"),
virDomainClockOffsetTypeToString(clock.offset));
return -1;
case VIR_DOMAIN_CLOCK_OFFSET_LAST:
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unexpected clock offset '%d'"), clock.offset);
return -1;
}
for (i = 0; i < clock.ntimers; i++) {
switch ((virDomainTimerNameType) clock.timers[i]->name) {
case VIR_DOMAIN_TIMER_NAME_TSC:
switch (clock.timers[i]->mode) {
case VIR_DOMAIN_TIMER_MODE_NATIVE:
b_info->tsc_mode = LIBXL_TSC_MODE_NATIVE;
break;
case VIR_DOMAIN_TIMER_MODE_PARAVIRT:
b_info->tsc_mode = LIBXL_TSC_MODE_NATIVE_PARAVIRT;
break;
case VIR_DOMAIN_TIMER_MODE_EMULATE:
b_info->tsc_mode = LIBXL_TSC_MODE_ALWAYS_EMULATE;
break;
default:
b_info->tsc_mode = LIBXL_TSC_MODE_DEFAULT;
}
break;
case VIR_DOMAIN_TIMER_NAME_HPET:
if (!hvm) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported timer type (name) '%s'"),
virDomainTimerNameTypeToString(clock.timers[i]->name));
return -1;
}
if (clock.timers[i]->present == 1)
libxl_defbool_set(&b_info->u.hvm.hpet, 1);
break;
case VIR_DOMAIN_TIMER_NAME_PLATFORM:
case VIR_DOMAIN_TIMER_NAME_KVMCLOCK:
case VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK:
case VIR_DOMAIN_TIMER_NAME_RTC:
case VIR_DOMAIN_TIMER_NAME_PIT:
case VIR_DOMAIN_TIMER_NAME_ARMVTIMER:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported timer type (name) '%s'"),
virDomainTimerNameTypeToString(clock.timers[i]->name));
return -1;
case VIR_DOMAIN_TIMER_NAME_LAST:
break;
}
}
if (def->cputune.sharesSpecified)
b_info->sched_params.weight = def->cputune.shares;
/* Xen requires the memory sizes to be rounded to 1MiB increments */
virDomainDefSetMemoryTotal(def,
VIR_ROUND_UP(virDomainDefGetMemoryInitial(def), 1024));
def->mem.cur_balloon = VIR_ROUND_UP(def->mem.cur_balloon, 1024);
b_info->max_memkb = virDomainDefGetMemoryInitial(def);
b_info->target_memkb = def->mem.cur_balloon;
for (i = 0; i < def->ncontrollers; i++) {
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_XENBUS) {
if (def->controllers[i]->opts.xenbusopts.maxEventChannels > 0)
b_info->event_channels = def->controllers[i]->opts.xenbusopts.maxEventChannels;
#ifdef LIBXL_HAVE_BUILDINFO_GRANT_LIMITS
if (def->controllers[i]->opts.xenbusopts.maxGrantFrames > 0)
b_info->max_grant_frames = def->controllers[i]->opts.xenbusopts.maxGrantFrames;
#endif
}
}
if (hvm || pvh) {
if (caps &&
def->cpu && def->cpu->mode == (VIR_CPU_MODE_HOST_PASSTHROUGH)) {
bool hasHwVirt = false;
bool svm = false, vmx = false;
char xlCPU[32];
/* enable nested HVM only if global nested_hvm option enable it and
* host support it */
if (ARCH_IS_X86(def->os.arch)) {
vmx = virCPUCheckFeature(caps->host.arch, caps->host.cpu, "vmx");
svm = virCPUCheckFeature(caps->host.arch, caps->host.cpu, "svm");
hasHwVirt = cfg->nested_hvm && (vmx | svm);
}
if (def->cpu->nfeatures) {
for (i = 0; i < def->cpu->nfeatures; i++) {
switch (def->cpu->features[i].policy) {
case VIR_CPU_FEATURE_DISABLE:
case VIR_CPU_FEATURE_FORBID:
if ((vmx && STREQ(def->cpu->features[i].name, "vmx")) ||
(svm && STREQ(def->cpu->features[i].name, "svm"))) {
hasHwVirt = false;
continue;
}
g_snprintf(xlCPU,
sizeof(xlCPU),
"%s=0",
xenTranslateCPUFeature(
def->cpu->features[i].name,
false));
if (libxl_cpuid_parse_config(&b_info->cpuid, xlCPU)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported cpu feature '%s'"),
def->cpu->features[i].name);
return -1;
}
break;
case VIR_CPU_FEATURE_FORCE:
case VIR_CPU_FEATURE_REQUIRE:
if ((vmx && STREQ(def->cpu->features[i].name, "vmx")) ||
(svm && STREQ(def->cpu->features[i].name, "svm"))) {
hasHwVirt = true;
continue;
}
g_snprintf(xlCPU,
sizeof(xlCPU),
"%s=1",
xenTranslateCPUFeature(
def->cpu->features[i].name, false));
if (libxl_cpuid_parse_config(&b_info->cpuid, xlCPU)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported cpu feature '%s'"),
def->cpu->features[i].name);
return -1;
}
break;
case VIR_CPU_FEATURE_OPTIONAL:
case VIR_CPU_FEATURE_LAST:
break;
}
}
}
#ifdef LIBXL_HAVE_BUILDINFO_NESTED_HVM
libxl_defbool_set(&b_info->nested_hvm, hasHwVirt);
#else
if (hvm) {
libxl_defbool_set(&b_info->u.hvm.nested_hvm, hasHwVirt);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported nested HVM setting for %s machine on this Xen version"),
def->os.machine);
return -1;
}
#endif
}
if (def->cpu && def->cpu->mode == VIR_CPU_MODE_CUSTOM) {
VIR_WARN("Ignoring CPU with mode=custom, update your config to "
"mode=host-passthrough to avoid risk of changed guest "
"semantics when mode=custom is supported in the future");
}
}
if (hvm) {
char bootorder[VIR_DOMAIN_BOOT_LAST + 1];
libxl_defbool_set(&b_info->u.hvm.pae,
def->features[VIR_DOMAIN_FEATURE_PAE] ==
VIR_TRISTATE_SWITCH_ON);
#ifdef LIBXL_HAVE_BUILDINFO_APIC
libxl_defbool_set(&b_info->apic,
def->features[VIR_DOMAIN_FEATURE_APIC] ==
VIR_TRISTATE_SWITCH_ON);
/*
* Strictly speaking b_info->acpi was introduced earlier (Xen 4.8), but
* there is no separate #define in libxl.h.
*/
libxl_defbool_set(&b_info->acpi,
def->features[VIR_DOMAIN_FEATURE_ACPI] ==
VIR_TRISTATE_SWITCH_ON);
#else
libxl_defbool_set(&b_info->u.hvm.apic,
def->features[VIR_DOMAIN_FEATURE_APIC] ==
VIR_TRISTATE_SWITCH_ON);
libxl_defbool_set(&b_info->u.hvm.acpi,
def->features[VIR_DOMAIN_FEATURE_ACPI] ==
VIR_TRISTATE_SWITCH_ON);
#endif
/* copy SLIC table path to acpi_firmware */
if (def->os.slic_table)
b_info->u.hvm.acpi_firmware = g_strdup(def->os.slic_table);
if (def->nsounds > 0) {
/*
* Use first sound device. man xl.cfg(5) describes soundhw as
* a single device. From the man page: soundhw=DEVICE
*/
virDomainSoundDefPtr snd = def->sounds[0];
b_info->u.hvm.soundhw = g_strdup(virDomainSoundModelTypeToString(snd->model));
}
for (i = 0; i < def->os.nBootDevs; i++) {
switch (def->os.bootDevs[i]) {
case VIR_DOMAIN_BOOT_FLOPPY:
bootorder[i] = 'a';
break;
default:
case VIR_DOMAIN_BOOT_DISK:
bootorder[i] = 'c';
break;
case VIR_DOMAIN_BOOT_CDROM:
bootorder[i] = 'd';
break;
case VIR_DOMAIN_BOOT_NET:
bootorder[i] = 'n';
break;
}
}
if (def->os.nBootDevs == 0) {
bootorder[0] = 'c';
bootorder[1] = '\0';
} else {
bootorder[def->os.nBootDevs] = '\0';
}
b_info->u.hvm.boot = g_strdup(bootorder);
#ifdef LIBXL_HAVE_BUILDINFO_KERNEL
b_info->cmdline = g_strdup(def->os.cmdline);
b_info->kernel = g_strdup(def->os.kernel);
b_info->ramdisk = g_strdup(def->os.initrd);
#endif
/*
* Currently libxl only allows specifying the type of BIOS.
* If the type is PFLASH, we assume OVMF and set libxl_bios_type
* to LIBXL_BIOS_TYPE_OVMF. The path to the OVMF firmware is
* configured when building Xen using '--with-system-ovmf='. If
* not specified, LIBXL_FIRMWARE_DIR/ovmf.bin is used. In the
* future, Xen will support a user-specified firmware path. See
* https://lists.xenproject.org/archives/html/xen-devel/2016-03/msg01628.html
*/
if (virDomainDefHasOldStyleUEFI(def))
b_info->u.hvm.bios = LIBXL_BIOS_TYPE_OVMF;
if (def->emulator) {
if (!virFileExists(def->emulator)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("emulator '%s' not found"),
def->emulator);
return -1;
}
if (!virFileIsExecutable(def->emulator)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("emulator '%s' is not executable"),
def->emulator);
return -1;
}
VIR_FREE(b_info->device_model);
b_info->device_model = g_strdup(def->emulator);
b_info->device_model_version = libxlDomainGetEmulatorType(def);
}
if (def->nserials) {
if (def->nserials == 1) {
if (libxlMakeChrdevStr(def->serials[0], &b_info->u.hvm.serial) <
0)
return -1;
} else {
#ifdef LIBXL_HAVE_BUILDINFO_SERIAL_LIST
if (VIR_ALLOC_N(b_info->u.hvm.serial_list, def->nserials + 1) <
0)
return -1;
for (i = 0; i < def->nserials; i++) {
if (libxlMakeChrdevStr(def->serials[i],
&b_info->u.hvm.serial_list[i]) < 0)
{
libxl_string_list_dispose(&b_info->u.hvm.serial_list);
return -1;
}
}
b_info->u.hvm.serial_list[i] = NULL;
#else
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s",
_("Only one serial device is supported by libxl"));
return -1;
#endif
}
}
if (def->nparallels) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s",
_("Parallel devices are not supported by libxl"));
return -1;
}
/* Disable VNC and SDL until explicitly enabled */
libxl_defbool_set(&b_info->u.hvm.vnc.enable, 0);
libxl_defbool_set(&b_info->u.hvm.sdl.enable, 0);
for (i = 0; i < def->ninputs; i++) {
char **usbdevice;
if (def->inputs[i]->bus != VIR_DOMAIN_INPUT_BUS_USB)
continue;
#ifdef LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST
if (VIR_EXPAND_N(b_info->u.hvm.usbdevice_list, nusbdevice, 1) < 0)
return -1;
#else
nusbdevice++;
if (nusbdevice > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("libxenlight supports only one input device"));
return -1;
}
#endif
#ifdef LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST
usbdevice = &b_info->u.hvm.usbdevice_list[nusbdevice - 1];
#else
usbdevice = &b_info->u.hvm.usbdevice;
#endif
switch (def->inputs[i]->type) {
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
VIR_FREE(*usbdevice);
*usbdevice = g_strdup("mouse");
break;
case VIR_DOMAIN_INPUT_TYPE_TABLET:
VIR_FREE(*usbdevice);
*usbdevice = g_strdup("tablet");
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Unknown input device type"));
return -1;
}
}
#ifdef LIBXL_HAVE_BUILDINFO_USBDEVICE_LIST
/* NULL-terminate usbdevice_list */
if (nusbdevice > 0 &&
VIR_EXPAND_N(b_info->u.hvm.usbdevice_list, nusbdevice, 1) < 0) {
VIR_DISPOSE_N(b_info->u.hvm.usbdevice_list, nusbdevice);
return -1;
}
#endif
} else if (pvh) {
b_info->cmdline = g_strdup(def->os.cmdline);
b_info->kernel = g_strdup(def->os.kernel);
b_info->ramdisk = g_strdup(def->os.initrd);
#ifdef LIBXL_HAVE_BUILDINFO_BOOTLOADER
b_info->bootloader = g_strdup(def->os.bootloader);
if (def->os.bootloaderArgs) {
if (!(b_info->bootloader_args =
virStringSplit(def->os.bootloaderArgs, " \t\n", 0)))
return -1;
}
#endif
} else {
/*
* For compatibility with the legacy xen toolstack, default to pygrub
* if bootloader is not specified AND direct kernel boot is not specified.
*/
if (def->os.bootloader) {
b_info->u.pv.bootloader = g_strdup(def->os.bootloader);
} else if (def->os.kernel == NULL) {
b_info->u.pv.bootloader = g_strdup(LIBXL_BOOTLOADER_PATH);
}
if (def->os.bootloaderArgs) {
if (!(b_info->u.pv.bootloader_args =
virStringSplit(def->os.bootloaderArgs, " \t\n", 0)))
return -1;
}
b_info->u.pv.cmdline = g_strdup(def->os.cmdline);
if (def->os.kernel) {
/* libxl_init_build_info() sets kernel.path = g_strdup("hvmloader") */
VIR_FREE(b_info->u.pv.kernel);
b_info->u.pv.kernel = g_strdup(def->os.kernel);
}
b_info->u.pv.ramdisk = g_strdup(def->os.initrd);
if (def->features[VIR_DOMAIN_FEATURE_XEN] == VIR_TRISTATE_SWITCH_ON) {
switch ((virTristateSwitch) def->xen_features[VIR_DOMAIN_XEN_E820_HOST]) {
case VIR_TRISTATE_SWITCH_ON:
libxl_defbool_set(&b_info->u.pv.e820_host, true);
break;
case VIR_TRISTATE_SWITCH_OFF:
libxl_defbool_set(&b_info->u.pv.e820_host, false);
break;
case VIR_TRISTATE_SWITCH_ABSENT:
case VIR_TRISTATE_SWITCH_LAST:
break;
}
}
}
/* only the 'xen' balloon device model is supported */
if (def->memballoon) {
int model = def->memballoon->model;
switch ((virDomainMemballoonModel)model) {
case VIR_DOMAIN_MEMBALLOON_MODEL_XEN:
break;
case VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO:
case VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO_TRANSITIONAL:
case VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO_NON_TRANSITIONAL:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported balloon device model '%s'"),
virDomainMemballoonModelTypeToString(model));
return -1;
case VIR_DOMAIN_MEMBALLOON_MODEL_NONE:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s",
_("balloon device cannot be disabled"));
return -1;
case VIR_DOMAIN_MEMBALLOON_MODEL_LAST:
default:
virReportEnumRangeError(virDomainMemballoonModel, model);
return -1;
}
}
/* Allow libxl to calculate shadow memory requirements */
b_info->shadow_memkb =
libxl_get_required_shadow_memory(b_info->max_memkb,
b_info->max_vcpus);
if (def->namespaceData) {
libxlDomainXmlNsDefPtr nsdata = def->namespaceData;
if (nsdata->num_args > 0)
b_info->extra = g_strdupv(nsdata->args);
}
return 0;
}
#ifdef LIBXL_HAVE_VNUMA
static int
libxlMakeVnumaList(virDomainDefPtr def,
libxl_ctx *ctx,
libxl_domain_config *d_config)
{
int ret = -1;
size_t i, j;
size_t nr_nodes;
size_t num_vnuma;
bool simulate = false;
virBitmapPtr bitmap = NULL;
virDomainNumaPtr numa = def->numa;
libxl_domain_build_info *b_info = &d_config->b_info;
libxl_physinfo physinfo;
libxl_vnode_info *vnuma_nodes = NULL;
if (!numa)
return 0;
num_vnuma = virDomainNumaGetNodeCount(numa);
if (!num_vnuma)
return 0;
libxl_physinfo_init(&physinfo);
if (libxl_get_physinfo(ctx, &physinfo) < 0) {
libxl_physinfo_dispose(&physinfo);
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxl_get_physinfo_info failed"));
return -1;
}
nr_nodes = physinfo.nr_nodes;
libxl_physinfo_dispose(&physinfo);
if (num_vnuma > nr_nodes) {
VIR_WARN("Number of configured numa cells %zu exceeds available physical nodes %zu. All cells will use physical node 0",
num_vnuma, nr_nodes);
simulate = true;
}
/*
* allocate the vnuma_nodes for assignment under b_info.
*/
if (VIR_ALLOC_N(vnuma_nodes, num_vnuma) < 0)
return -1;
/*
* parse the vnuma vnodes data.
*/
for (i = 0; i < num_vnuma; i++) {
int cpu;
libxl_bitmap vcpu_bitmap;
libxl_vnode_info *p = &vnuma_nodes[i];
libxl_vnode_info_init(p);
/* pnode */
p->pnode = simulate ? 0 : i;
/* memory size */
p->memkb = virDomainNumaGetNodeMemorySize(numa, i);
/* vcpus */
bitmap = virDomainNumaGetNodeCpumask(numa, i);
if (bitmap == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("vnuma sibling %zu missing vcpus set"), i);
goto cleanup;
}
if ((cpu = virBitmapNextSetBit(bitmap, -1)) < 0)
goto cleanup;
libxl_bitmap_init(&vcpu_bitmap);
if (libxl_cpu_bitmap_alloc(ctx, &vcpu_bitmap, b_info->max_vcpus)) {
virReportOOMError();
goto cleanup;
}
do {
libxl_bitmap_set(&vcpu_bitmap, cpu);
} while ((cpu = virBitmapNextSetBit(bitmap, cpu)) >= 0);
libxl_bitmap_copy_alloc(ctx, &p->vcpus, &vcpu_bitmap);
libxl_bitmap_dispose(&vcpu_bitmap);
/* vdistances */
if (VIR_ALLOC_N(p->distances, num_vnuma) < 0)
goto cleanup;
p->num_distances = num_vnuma;
for (j = 0; j < num_vnuma; j++)
p->distances[j] = virDomainNumaGetNodeDistance(numa, i, j);
}
b_info->vnuma_nodes = vnuma_nodes;
b_info->num_vnuma_nodes = num_vnuma;
ret = 0;
cleanup:
if (ret) {
for (i = 0; i < num_vnuma; i++) {
libxl_vnode_info *p = &vnuma_nodes[i];
VIR_FREE(p->distances);
}
VIR_FREE(vnuma_nodes);
}
return ret;
}
#endif
static int
libxlDiskSetDiscard(libxl_device_disk *x_disk, int discard)
{
if (!x_disk->readwrite)
return 0;
#if defined(LIBXL_HAVE_LIBXL_DEVICE_DISK_DISCARD_ENABLE)
switch ((virDomainDiskDiscard)discard) {
case VIR_DOMAIN_DISK_DISCARD_DEFAULT:
case VIR_DOMAIN_DISK_DISCARD_LAST:
break;
case VIR_DOMAIN_DISK_DISCARD_UNMAP:
libxl_defbool_set(&x_disk->discard_enable, true);
break;
case VIR_DOMAIN_DISK_DISCARD_IGNORE:
libxl_defbool_set(&x_disk->discard_enable, false);
break;
}
return 0;
#else
if (discard == VIR_DOMAIN_DISK_DISCARD_DEFAULT)
return 0;
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This version of libxenlight does not support "
"disk 'discard' option passing"));
return -1;
#endif
}
static char *
libxlMakeNetworkDiskSrcStr(virStorageSourcePtr src,
const char *username,
const char *secret)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
size_t i;
switch ((virStorageNetProtocol) src->protocol) {
case VIR_STORAGE_NET_PROTOCOL_NBD:
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:
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
case VIR_STORAGE_NET_PROTOCOL_SSH:
case VIR_STORAGE_NET_PROTOCOL_VXHS:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
virReportError(VIR_ERR_NO_SUPPORT,
_("Unsupported network block protocol '%s'"),
virStorageNetProtocolTypeToString(src->protocol));
return NULL;
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);
return NULL;
}
virBufferStrcat(&buf, "rbd:", src->volume, "/", src->path, NULL);
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, "\\:%u", src->hosts[i].port);
}
}
if (src->configFile)
virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile);
return virBufferContentAndReset(&buf);
}
return NULL;
}
static int
libxlMakeNetworkDiskSrc(virStorageSourcePtr src, char **srcstr)
{
virConnectPtr conn = NULL;
uint8_t *secret = NULL;
VIR_AUTODISPOSE_STR base64secret = NULL;
size_t secretlen = 0;
char *username = NULL;
int ret = -1;
*srcstr = NULL;
if (src->auth && src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) {
username = src->auth->username;
if (!(conn = virConnectOpen("xen:///system")))
goto cleanup;
if (virSecretGetSecretString(conn, &src->auth->seclookupdef,
VIR_SECRET_USAGE_TYPE_CEPH,
&secret, &secretlen) < 0)
goto cleanup;
/* RBD expects an encoded secret */
base64secret = g_base64_encode(secret, secretlen);
}
if (!(*srcstr = libxlMakeNetworkDiskSrcStr(src, username, base64secret)))
goto cleanup;
ret = 0;
cleanup:
VIR_DISPOSE_N(secret, secretlen);
virObjectUnref(conn);
return ret;
}
int
libxlMakeDisk(virDomainDiskDefPtr l_disk, libxl_device_disk *x_disk)
{
const char *driver = virDomainDiskGetDriver(l_disk);
int format = virDomainDiskGetFormat(l_disk);
int actual_type = virStorageSourceGetActualType(l_disk->src);
libxl_device_disk_init(x_disk);
if (actual_type == VIR_STORAGE_TYPE_NETWORK) {
if (STRNEQ_NULLABLE(driver, "qemu")) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("only the 'qemu' driver can be used with network disks"));
return -1;
}
if (libxlMakeNetworkDiskSrc(l_disk->src, &x_disk->pdev_path) < 0)
return -1;
} else {
x_disk->pdev_path = g_strdup(virDomainDiskGetSource(l_disk));
}
x_disk->vdev = g_strdup(l_disk->dst);
if (driver) {
if (STREQ(driver, "tap") || STREQ(driver, "tap2")) {
switch (format) {
case VIR_STORAGE_FILE_QCOW:
x_disk->format = LIBXL_DISK_FORMAT_QCOW;
x_disk->backend = LIBXL_DISK_BACKEND_QDISK;
break;
case VIR_STORAGE_FILE_QCOW2:
x_disk->format = LIBXL_DISK_FORMAT_QCOW2;
x_disk->backend = LIBXL_DISK_BACKEND_QDISK;
break;
case VIR_STORAGE_FILE_VHD:
x_disk->format = LIBXL_DISK_FORMAT_VHD;
x_disk->backend = LIBXL_DISK_BACKEND_TAP;
break;
case VIR_STORAGE_FILE_RAW:
x_disk->format = LIBXL_DISK_FORMAT_RAW;
x_disk->backend = LIBXL_DISK_BACKEND_TAP;
break;
#ifdef LIBXL_HAVE_QED
case VIR_STORAGE_FILE_QED:
x_disk->format = LIBXL_DISK_FORMAT_QED;
x_disk->backend = LIBXL_DISK_BACKEND_QDISK;
break;
#endif
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight does not support disk format %s "
"with disk driver %s"),
virStorageFileFormatTypeToString(format),
driver);
return -1;
}
} else if (STREQ(driver, "qemu")) {
x_disk->backend = LIBXL_DISK_BACKEND_QDISK;
switch (format) {
case VIR_STORAGE_FILE_QCOW:
x_disk->format = LIBXL_DISK_FORMAT_QCOW;
break;
case VIR_STORAGE_FILE_QCOW2:
x_disk->format = LIBXL_DISK_FORMAT_QCOW2;
break;
#ifdef LIBXL_HAVE_QED
case VIR_STORAGE_FILE_QED:
x_disk->format = LIBXL_DISK_FORMAT_QED;
break;
#endif
case VIR_STORAGE_FILE_VHD:
x_disk->format = LIBXL_DISK_FORMAT_VHD;
break;
case VIR_STORAGE_FILE_RAW:
x_disk->format = LIBXL_DISK_FORMAT_RAW;
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight does not support disk format %s "
"with disk driver %s"),
virStorageFileFormatTypeToString(format),
driver);
return -1;
}
} else if (STREQ(driver, "file")) {
if (format != VIR_STORAGE_FILE_RAW) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight does not support disk format %s "
"with disk driver %s"),
virStorageFileFormatTypeToString(format),
driver);
return -1;
}
x_disk->format = LIBXL_DISK_FORMAT_RAW;
x_disk->backend = LIBXL_DISK_BACKEND_QDISK;
} else if (STREQ(driver, "phy")) {
if (format != VIR_STORAGE_FILE_RAW) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight does not support disk format %s "
"with disk driver %s"),
virStorageFileFormatTypeToString(format),
driver);
return -1;
}
x_disk->format = LIBXL_DISK_FORMAT_RAW;
x_disk->backend = LIBXL_DISK_BACKEND_PHY;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("libxenlight does not support disk driver %s"),
driver);
return -1;
}
} else {
/*
* If driverName is not specified, default to raw as per
* xl-disk-configuration.txt in the xen documentation and let
* libxl pick a suitable backend.
*/
x_disk->format = LIBXL_DISK_FORMAT_RAW;
x_disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
}
/* XXX is this right? */
x_disk->removable = 1;
x_disk->readwrite = !l_disk->src->readonly;
x_disk->is_cdrom = l_disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM ? 1 : 0;
if (libxlDiskSetDiscard(x_disk, l_disk->discard) < 0)
return -1;
/* An empty CDROM must have the empty format, otherwise libxl fails. */
if (x_disk->is_cdrom && !x_disk->pdev_path)
x_disk->format = LIBXL_DISK_FORMAT_EMPTY;
if (l_disk->transient) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxenlight does not support transient disks"));
return -1;
}
if (l_disk->domain_name) {
#ifdef LIBXL_HAVE_DEVICE_BACKEND_DOMNAME
x_disk->backend_domname = g_strdup(l_disk->domain_name);
#else
virReportError(VIR_ERR_XML_DETAIL, "%s",
_("this version of libxenlight does not "
"support backend domain name"));
return -1;
#endif
}
return 0;
}
static int
libxlMakeDiskList(virDomainDefPtr def, libxl_domain_config *d_config)
{
virDomainDiskDefPtr *l_disks = def->disks;
int ndisks = def->ndisks;
libxl_device_disk *x_disks;
size_t i;
if (VIR_ALLOC_N(x_disks, ndisks) < 0)
return -1;
for (i = 0; i < ndisks; i++) {
if (libxlMakeDisk(l_disks[i], &x_disks[i]) < 0)
goto error;
}
d_config->disks = x_disks;
d_config->num_disks = ndisks;
return 0;
error:
for (i = 0; i < ndisks; i++)
libxl_device_disk_dispose(&x_disks[i]);
VIR_FREE(x_disks);
return -1;
}
/*
* Update libvirt disk config with libxl disk config.
*
* This function can be used to update the libvirt disk config with default
* values selected by libxl. Currently only the backend type is selected by
* libxl when not explicitly specified by the user.
*/
void
libxlUpdateDiskDef(virDomainDiskDefPtr l_disk, libxl_device_disk *x_disk)
{
const char *driver = NULL;
if (virDomainDiskGetDriver(l_disk))
return;
switch (x_disk->backend) {
case LIBXL_DISK_BACKEND_QDISK:
driver = "qemu";
break;
case LIBXL_DISK_BACKEND_TAP:
driver = "tap";
break;
case LIBXL_DISK_BACKEND_PHY:
driver = "phy";
break;
case LIBXL_DISK_BACKEND_UNKNOWN:
break;
}
if (driver)
ignore_value(virDomainDiskSetDriver(l_disk, driver));
}
int
libxlMakeNic(virDomainDefPtr def,
virDomainNetDefPtr l_nic,
libxl_device_nic *x_nic,
bool attach)
{
virDomainNetType actual_type = virDomainNetGetActualType(l_nic);
virNetworkPtr network = NULL;
virConnectPtr conn = NULL;
const virNetDevBandwidth *actual_bw;
const virNetDevVPortProfile *port_profile;
const virNetDevVlan *virt_vlan;
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
size_t i;
const char *script = NULL;
int ret = -1;
/* TODO: Where is mtu stored?
*
* x_nics[i].mtu = 1492;
*/
if (l_nic->script && !(actual_type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
actual_type == VIR_DOMAIN_NET_TYPE_ETHERNET)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("specifying a script is only supported with "
"interface types bridge and ethernet"));
return -1;
}
libxl_device_nic_init(x_nic);
virMacAddrGetRaw(&l_nic->mac, x_nic->mac);
/*
* The nictype field of libxl_device_nic structure tells Xen which type of
* NIC device to create for the domain. LIBXL_NIC_TYPE_VIF specifies a
* PV NIC. LIBXL_NIC_TYPE_VIF_IOEMU specifies a PV and emulated NIC,
* allowing the domain to choose which NIC to use and unplug the unused
* one. LIBXL_NIC_TYPE_VIF_IOEMU is only valid for HVM domains. Further,
* if hotplugging the NIC, emulated NICs are currently not supported.
* Alternatively one could set LIBXL_NIC_TYPE_UNKNOWN and let libxl decide,
* but its behaviour might not be consistent across all libvirt supported
* versions. The other nictype values are well established already, hence
* we manually select our own default and mimic xl/libxl behaviour starting
* xen commit 32e9d0f ("libxl: nic type defaults to vif in hotplug for
* hvm guest").
*/
if (virDomainNetGetModelString(l_nic)) {
if ((def->os.type == VIR_DOMAIN_OSTYPE_XEN ||
def->os.type == VIR_DOMAIN_OSTYPE_XENPVH) &&
l_nic->model != VIR_DOMAIN_NET_MODEL_NETFRONT) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only model 'netfront' is supported for "
"Xen PV(H) domains"));
return -1;
}
x_nic->model = g_strdup(virDomainNetGetModelString(l_nic));
if (l_nic->model == VIR_DOMAIN_NET_MODEL_NETFRONT)
x_nic->nictype = LIBXL_NIC_TYPE_VIF;
else
x_nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
} else {
if (def->os.type == VIR_DOMAIN_OSTYPE_HVM && !attach)
x_nic->nictype = LIBXL_NIC_TYPE_VIF_IOEMU;
else
x_nic->nictype = LIBXL_NIC_TYPE_VIF;
}
x_nic->ifname = g_strdup(l_nic->ifname);
port_profile = virDomainNetGetActualVirtPortProfile(l_nic);
virt_vlan = virDomainNetGetActualVlan(l_nic);
script = l_nic->script;
switch (actual_type) {
case VIR_DOMAIN_NET_TYPE_BRIDGE:
virBufferAddStr(&buf, virDomainNetGetActualBridgeName(l_nic));
/*
* A bit of special handling if vif will be connected to an
* openvswitch bridge
*/
if (port_profile &&
port_profile->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) {
/*
* If a custom script is not specified for openvswitch, use
* Xen's vif-openvswitch script
*/
if (!script)
script = "vif-openvswitch";
/*
* libxl_device_nic->bridge supports an extended format for
* specifying VLAN tags and trunks when using openvswitch
*
* BRIDGE_NAME[.VLAN][:TRUNK:TRUNK]
*
* See Xen's networking wiki for more details
* https://wiki.xenproject.org/wiki/Xen_Networking#Open_vSwitch
*/
if (virt_vlan && virt_vlan->nTags > 0) {
if (virt_vlan->trunk) {
for (i = 0; i < virt_vlan->nTags; i++)
virBufferAsprintf(&buf, ":%d", virt_vlan->tag[i]);
} else {
virBufferAsprintf(&buf, ".%d", virt_vlan->tag[0]);
}
}
}
x_nic->bridge = virBufferContentAndReset(&buf);
G_GNUC_FALLTHROUGH;
case VIR_DOMAIN_NET_TYPE_ETHERNET:
x_nic->script = g_strdup(script);
if (l_nic->guestIP.nips > 0) {
x_nic->ip = xenMakeIPList(&l_nic->guestIP);
if (!x_nic->ip)
goto cleanup;
}
break;
case VIR_DOMAIN_NET_TYPE_NETWORK:
{
if (!(conn = virConnectOpen("xen:///system")))
goto cleanup;
if (!(network =
virNetworkLookupByName(conn, l_nic->data.network.name))) {
goto cleanup;
}
if (l_nic->guestIP.nips > 0) {
x_nic->ip = xenMakeIPList(&l_nic->guestIP);
if (!x_nic->ip)
goto cleanup;
}
if (!(x_nic->bridge = virNetworkGetBridgeName(network)))
goto cleanup;
break;
}
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_DIRECT:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported interface type %s"),
virDomainNetTypeToString(l_nic->type));
goto cleanup;
}
if (l_nic->domain_name) {
#ifdef LIBXL_HAVE_DEVICE_BACKEND_DOMNAME
x_nic->backend_domname = g_strdup(l_nic->domain_name);
#else
virReportError(VIR_ERR_XML_DETAIL, "%s",
_("this version of libxenlight does not "
"support backend domain name"));
goto cleanup;
#endif
}
/*
* Set bandwidth.
* From $xen-sources/docs/misc/xl-network-configuration.markdown:
*
*
* Specifies the rate at which the outgoing traffic will be limited to.
* The default if this keyword is not specified is unlimited.
*
* The rate may be specified as "<RATE>/s" or optionally "<RATE>/s@<INTERVAL>".
*
* `RATE` is in bytes and can accept suffixes:
* GB, MB, KB, B for bytes.
* Gb, Mb, Kb, b for bits.
* `INTERVAL` is in microseconds and can accept suffixes: ms, us, s.
* It determines the frequency at which the vif transmission credit
* is replenished. The default is 50ms.
* Vif rate limiting is credit-based. It means that for "1MB/s@20ms",
* the available credit will be equivalent of the traffic you would have
* done at "1MB/s" during 20ms. This will results in a credit of 20,000
* bytes replenished every 20,000 us.
*
*
* libvirt doesn't support the notion of rate limiting over an interval.
* Similar to xl's behavior when interval is not specified, set a default
* interval of 50ms and calculate the number of bytes per interval based
* on the specified average bandwidth.
*/
actual_bw = virDomainNetGetActualBandwidth(l_nic);
if (actual_bw && actual_bw->out && actual_bw->out->average) {
uint64_t bytes_per_sec = actual_bw->out->average * 1024;
uint64_t bytes_per_interval =
(((uint64_t) bytes_per_sec * 50000UL) / 1000000UL);
x_nic->rate_bytes_per_interval = bytes_per_interval;
x_nic->rate_interval_usecs = 50000UL;
}
ret = 0;
cleanup:
virObjectUnref(network);
virObjectUnref(conn);
return ret;
}
static int
libxlMakeNicList(virDomainDefPtr def, libxl_domain_config *d_config)
{
virDomainNetDefPtr *l_nics = def->nets;
size_t nnics = def->nnets;
libxl_device_nic *x_nics;
size_t i, nvnics = 0;
if (VIR_ALLOC_N(x_nics, nnics) < 0)
return -1;
for (i = 0; i < nnics; i++) {
if (virDomainNetGetActualType(l_nics[i]) == VIR_DOMAIN_NET_TYPE_HOSTDEV)
continue;
if (libxlMakeNic(def, l_nics[i], &x_nics[nvnics], false))
goto error;
/*
* The devid (at least right now) will not get initialized by
* libxl in the setup case but is required for starting the
* device-model.
*/
if (x_nics[nvnics].devid < 0)
x_nics[nvnics].devid = nvnics;
nvnics++;
}
VIR_SHRINK_N(x_nics, nnics, nnics - nvnics);
d_config->nics = x_nics;
d_config->num_nics = nvnics;
return 0;
error:
for (i = 0; i < nnics; i++)
libxl_device_nic_dispose(&x_nics[i]);
VIR_FREE(x_nics);
return -1;
}
int
libxlMakeVfb(virPortAllocatorRangePtr graphicsports,
virDomainGraphicsDefPtr l_vfb,
libxl_device_vfb *x_vfb)
{
unsigned short port;
virDomainGraphicsListenDefPtr glisten = NULL;
libxl_device_vfb_init(x_vfb);
switch (l_vfb->type) {
case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
libxl_defbool_set(&x_vfb->sdl.enable, 1);
libxl_defbool_set(&x_vfb->vnc.enable, 0);
libxl_defbool_set(&x_vfb->sdl.opengl, 0);
x_vfb->sdl.display = g_strdup(l_vfb->data.sdl.display);
x_vfb->sdl.xauthority = g_strdup(l_vfb->data.sdl.xauth);
break;
case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
libxl_defbool_set(&x_vfb->vnc.enable, 1);
libxl_defbool_set(&x_vfb->sdl.enable, 0);
/* driver handles selection of free port */
libxl_defbool_set(&x_vfb->vnc.findunused, 0);
if (l_vfb->data.vnc.autoport) {
if (virPortAllocatorAcquire(graphicsports, &port) < 0)
return -1;
l_vfb->data.vnc.port = port;
}
x_vfb->vnc.display = l_vfb->data.vnc.port - LIBXL_VNC_PORT_MIN;
if ((glisten = virDomainGraphicsGetListen(l_vfb, 0))) {
if (glisten->address) {
/* libxl_device_vfb_init() does g_strdup("127.0.0.1") */
VIR_FREE(x_vfb->vnc.listen);
x_vfb->vnc.listen = g_strdup(glisten->address);
} else {
glisten->address = g_strdup(VIR_LOOPBACK_IPV4_ADDR);
}
}
x_vfb->vnc.passwd = g_strdup(l_vfb->data.vnc.auth.passwd);
x_vfb->keymap = g_strdup(l_vfb->data.vnc.keymap);
break;
case VIR_DOMAIN_GRAPHICS_TYPE_RDP:
case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP:
case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS:
case VIR_DOMAIN_GRAPHICS_TYPE_LAST:
break;
}
return 0;
}
static int
libxlMakeVfbList(virPortAllocatorRangePtr graphicsports,
virDomainDefPtr def,
libxl_domain_config *d_config)
{
virDomainGraphicsDefPtr *l_vfbs = def->graphics;
int nvfbs = def->ngraphics;
libxl_device_vfb *x_vfbs;
libxl_device_vkb *x_vkbs;
size_t i;
if (nvfbs == 0)
return 0;
if (VIR_ALLOC_N(x_vfbs, nvfbs) < 0)
return -1;
if (VIR_ALLOC_N(x_vkbs, nvfbs) < 0) {
VIR_FREE(x_vfbs);
return -1;
}
for (i = 0; i < nvfbs; i++) {
libxl_device_vkb_init(&x_vkbs[i]);
if (libxlMakeVfb(graphicsports, l_vfbs[i], &x_vfbs[i]) < 0)
goto error;
}
d_config->vfbs = x_vfbs;
d_config->vkbs = x_vkbs;
d_config->num_vfbs = d_config->num_vkbs = nvfbs;
return 0;
error:
for (i = 0; i < nvfbs; i++) {
libxl_device_vfb_dispose(&x_vfbs[i]);
libxl_device_vkb_dispose(&x_vkbs[i]);
}
VIR_FREE(x_vfbs);
VIR_FREE(x_vkbs);
return -1;
}
/*
* Populate vfb info in libxl_domain_build_info struct for HVM domains.
* Prior to calling this function, libxlMakeVfbList must be called to
* populate libxl_domain_config->vfbs.
*/
static int
libxlMakeBuildInfoVfb(virPortAllocatorRangePtr graphicsports,
virDomainDefPtr def,
libxl_domain_config *d_config)
{
libxl_domain_build_info *b_info = &d_config->b_info;
libxl_device_vfb x_vfb;
size_t i;
if (def->os.type != VIR_DOMAIN_OSTYPE_HVM)
return 0;
if (def->ngraphics == 0)
return 0;
/*
* Prefer SPICE, otherwise use first libxl_device_vfb device in
* libxl_domain_config->vfbs. Prior to calling this function,
*/
for (i = 0; i < def->ngraphics; i++) {
virDomainGraphicsDefPtr l_vfb = def->graphics[i];
unsigned short port;
virDomainGraphicsListenDefPtr glisten = NULL;
if (l_vfb->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
continue;
libxl_defbool_set(&b_info->u.hvm.spice.enable, true);
if (l_vfb->data.spice.autoport) {
if (virPortAllocatorAcquire(graphicsports, &port) < 0)
return -1;
l_vfb->data.spice.port = port;
}
b_info->u.hvm.spice.port = l_vfb->data.spice.port;
if ((glisten = virDomainGraphicsGetListen(l_vfb, 0))) {
if (glisten->address) {
b_info->u.hvm.spice.host = g_strdup(glisten->address);
} else {
b_info->u.hvm.spice.host = g_strdup(VIR_LOOPBACK_IPV4_ADDR);
glisten->address = g_strdup(VIR_LOOPBACK_IPV4_ADDR);
}
}
b_info->u.hvm.keymap = g_strdup(l_vfb->data.spice.keymap);
if (l_vfb->data.spice.auth.passwd) {
b_info->u.hvm.spice.passwd = g_strdup(l_vfb->data.spice.auth.passwd);
libxl_defbool_set(&b_info->u.hvm.spice.disable_ticketing, false);
} else {
libxl_defbool_set(&b_info->u.hvm.spice.disable_ticketing, true);
}
switch (l_vfb->data.spice.mousemode) {
/* client mouse mode is default in xl.cfg */
case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_DEFAULT:
case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_CLIENT:
libxl_defbool_set(&b_info->u.hvm.spice.agent_mouse, true);
break;
case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_SERVER:
libxl_defbool_set(&b_info->u.hvm.spice.agent_mouse, false);
break;
case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_LAST:
break;
}
#ifdef LIBXL_HAVE_SPICE_VDAGENT
if (l_vfb->data.spice.copypaste == VIR_TRISTATE_BOOL_YES) {
libxl_defbool_set(&b_info->u.hvm.spice.vdagent, true);
libxl_defbool_set(&b_info->u.hvm.spice.clipboard_sharing, true);
} else {
libxl_defbool_set(&b_info->u.hvm.spice.vdagent, false);
libxl_defbool_set(&b_info->u.hvm.spice.clipboard_sharing, false);
}
#endif
return 0;
}
x_vfb = d_config->vfbs[0];
if (libxl_defbool_val(x_vfb.vnc.enable)) {
libxl_defbool_set(&b_info->u.hvm.vnc.enable, true);
b_info->u.hvm.vnc.listen = g_strdup(x_vfb.vnc.listen);
b_info->u.hvm.vnc.passwd = g_strdup(x_vfb.vnc.passwd);
b_info->u.hvm.vnc.display = x_vfb.vnc.display;
libxl_defbool_set(&b_info->u.hvm.vnc.findunused,
libxl_defbool_val(x_vfb.vnc.findunused));
} else if (libxl_defbool_val(x_vfb.sdl.enable)) {
libxl_defbool_set(&b_info->u.hvm.sdl.enable, true);
libxl_defbool_set(&b_info->u.hvm.sdl.opengl,
libxl_defbool_val(x_vfb.sdl.opengl));
b_info->u.hvm.sdl.display = g_strdup(x_vfb.sdl.display);
b_info->u.hvm.sdl.xauthority = g_strdup(x_vfb.sdl.xauthority);
}
b_info->u.hvm.keymap = g_strdup(x_vfb.keymap);
return 0;
}
/*
* Get domain0 autoballoon configuration. Honor user-specified
* setting in libxl.conf first. If not specified, autoballooning
* is disabled when domain0's memory is set with 'dom0_mem'.
* Otherwise autoballooning is enabled.
*/
static int
libxlGetAutoballoonConf(libxlDriverConfigPtr cfg,
virConfPtr conf)
{
g_autoptr(GRegex) regex = NULL;
g_autoptr(GError) err = NULL;
int res;
res = virConfGetValueBool(conf, "autoballoon", &cfg->autoballoon);
if (res < 0)
return -1;
else if (res == 1)
return 0;
regex = g_regex_new("(^| )dom0_mem=((|min:|max:)[0-9]+[bBkKmMgG]?,?)+($| )",
0, 0, &err);
if (!regex) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to compile regex %s"), err->message);
return -1;
}
cfg->autoballoon = !g_regex_match(regex, cfg->verInfo->commandline, 0, NULL);
return 0;
}
libxlDriverConfigPtr
libxlDriverConfigNew(void)
{
libxlDriverConfigPtr cfg;
if (libxlConfigInitialize() < 0)
return NULL;
if (!(cfg = virObjectNew(libxlDriverConfigClass)))
return NULL;
cfg->configBaseDir = g_strdup(LIBXL_CONFIG_BASE_DIR);
cfg->configDir = g_strdup(LIBXL_CONFIG_DIR);
cfg->autostartDir = g_strdup(LIBXL_AUTOSTART_DIR);
cfg->logDir = g_strdup(LIBXL_LOG_DIR);
cfg->stateDir = g_strdup(LIBXL_STATE_DIR);
cfg->libDir = g_strdup(LIBXL_LIB_DIR);
cfg->saveDir = g_strdup(LIBXL_SAVE_DIR);
cfg->autoDumpDir = g_strdup(LIBXL_DUMP_DIR);
cfg->channelDir = g_strdup(LIBXL_CHANNEL_DIR);
#ifdef DEFAULT_LOADER_NVRAM
if (virFirmwareParseList(DEFAULT_LOADER_NVRAM,
&cfg->firmwares,
&cfg->nfirmwares) < 0)
goto error;
#else
if (VIR_ALLOC_N(cfg->firmwares, 1) < 0)
goto error;
cfg->nfirmwares = 1;
if (VIR_ALLOC(cfg->firmwares[0]) < 0)
goto error;
cfg->firmwares[0]->name = g_strdup(LIBXL_FIRMWARE_DIR "/ovmf.bin");
#endif
/* Always add hvmloader to firmwares */
if (VIR_REALLOC_N(cfg->firmwares, cfg->nfirmwares + 1) < 0)
goto error;
cfg->nfirmwares++;
if (VIR_ALLOC(cfg->firmwares[cfg->nfirmwares - 1]) < 0)
goto error;
cfg->firmwares[cfg->nfirmwares - 1]->name = g_strdup(LIBXL_FIRMWARE_DIR "/hvmloader");
/* defaults for keepalive messages */
cfg->keepAliveInterval = 5;
cfg->keepAliveCount = 5;
return cfg;
error:
virObjectUnref(cfg);
return NULL;
}
int
libxlDriverConfigInit(libxlDriverConfigPtr cfg)
{
unsigned int free_mem;
if (virFileMakePath(cfg->logDir) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to create log dir '%s': %s"),
cfg->logDir,
g_strerror(errno));
return -1;
}
cfg->logger = libxlLoggerNew(cfg->logDir, virLogGetDefaultPriority());
if (!cfg->logger) {
VIR_ERROR(_("cannot create logger for libxenlight, disabling driver"));
return -1;
}
if (libxl_ctx_alloc(&cfg->ctx, LIBXL_VERSION, 0, (xentoollog_logger *)cfg->logger)) {
VIR_ERROR(_("cannot initialize libxenlight context, probably not "
"running in a Xen Dom0, disabling driver"));
return -1;
}
if ((cfg->verInfo = libxl_get_version_info(cfg->ctx)) == NULL) {
VIR_ERROR(_("cannot version information from libxenlight, "
"disabling driver"));
return -1;
}
cfg->version = (cfg->verInfo->xen_version_major * 1000000) +
(cfg->verInfo->xen_version_minor * 1000);
/* This will fill xenstore info about free and dom0 memory if missing,
* should be called before starting first domain */
if (libxl_get_free_memory(cfg->ctx, &free_mem)) {
VIR_ERROR(_("Unable to configure libxl's memory management parameters"));
return -1;
}
return 0;
}
libxlDriverConfigPtr
libxlDriverConfigGet(libxlDriverPrivatePtr driver)
{
libxlDriverConfigPtr cfg;
libxlDriverLock(driver);
cfg = virObjectRef(driver->config);
libxlDriverUnlock(driver);
return cfg;
}
int libxlDriverConfigLoadFile(libxlDriverConfigPtr cfg,
const char *filename)
{
g_autoptr(virConf) conf = NULL;
/* Check the file is readable before opening it, otherwise
* libvirt emits an error.
*/
if (access(filename, R_OK) == -1) {
VIR_INFO("Could not read libxl config file %s", filename);
return 0;
}
if (!(conf = virConfReadFile(filename, 0)))
return -1;
/* setup autoballoon */
if (libxlGetAutoballoonConf(cfg, conf) < 0)
return -1;
if (virConfGetValueString(conf, "lock_manager", &cfg->lockManagerName) < 0)
return -1;
if (virConfGetValueInt(conf, "keepalive_interval", &cfg->keepAliveInterval) < 0)
return -1;
if (virConfGetValueUInt(conf, "keepalive_count", &cfg->keepAliveCount) < 0)
return -1;
if (virConfGetValueBool(conf, "nested_hvm", &cfg->nested_hvm) < 0)
return -1;
return 0;
}
/*
* dom0's maximum memory can be controlled by the user with the 'dom0_mem' Xen
* command line parameter. E.g. to set dom0's initial memory to 4G and max
* memory to 8G: dom0_mem=4G,max:8G
* Supported unit suffixes are [bBkKmMgGtT]. If not specified the default
* unit is kilobytes.
*
* If not constrained by the user, dom0 can effectively use all host memory.
* This function returns the configured maximum memory for dom0 in kilobytes,
* either the user-specified value or total physical memory as a default.
*/
int
libxlDriverGetDom0MaxmemConf(libxlDriverConfigPtr cfg,
unsigned long long *maxmem)
{
char **cmd_tokens = NULL;
char **mem_tokens = NULL;
size_t i;
size_t j;
libxl_physinfo physinfo;
int ret = -1;
if (cfg->verInfo->commandline == NULL ||
!(cmd_tokens = virStringSplit(cfg->verInfo->commandline, " ", 0)))
goto physmem;
for (i = 0; cmd_tokens[i] != NULL; i++) {
if (!STRPREFIX(cmd_tokens[i], "dom0_mem="))
continue;
if (!(mem_tokens = virStringSplit(cmd_tokens[i], ",", 0)))
break;
for (j = 0; mem_tokens[j] != NULL; j++) {
if (STRPREFIX(mem_tokens[j], "max:")) {
char *p = mem_tokens[j] + 4;
unsigned long long multiplier = 1;
while (g_ascii_isdigit(*p))
p++;
if (virStrToLong_ull(mem_tokens[j] + 4, &p, 10, maxmem) < 0)
break;
if (*p) {
switch (*p) {
case 'm':
case 'M':
multiplier = 1024;
break;
case 'g':
case 'G':
multiplier = 1024 * 1024;
break;
case 't':
case 'T':
multiplier = 1024 * 1024 * 1024;
break;
}
}
*maxmem = *maxmem * multiplier;
ret = 0;
goto cleanup;
}
}
g_strfreev(mem_tokens);
mem_tokens = NULL;
}
physmem:
/* No 'max' specified in dom0_mem, so dom0 can use all physical memory */
libxl_physinfo_init(&physinfo);
if (libxl_get_physinfo(cfg->ctx, &physinfo)) {
VIR_WARN("libxl_get_physinfo failed");
goto cleanup;
}
*maxmem = (physinfo.total_pages * cfg->verInfo->pagesize) / 1024;
libxl_physinfo_dispose(&physinfo);
ret = 0;
cleanup:
g_strfreev(cmd_tokens);
g_strfreev(mem_tokens);
return ret;
}
#ifdef LIBXL_HAVE_DEVICE_CHANNEL
static int
libxlPrepareChannel(virDomainChrDefPtr channel,
const char *channelDir,
const char *domainName)
{
if (channel->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN &&
channel->source->type == VIR_DOMAIN_CHR_TYPE_UNIX &&
!channel->source->data.nix.path) {
const char *target = channel->target.name;
if (!target)
target = "unknown.sock";
channel->source->data.nix.path = g_strdup_printf("%s/%s-%s", channelDir,
domainName,
target);
channel->source->data.nix.listen = true;
}
return 0;
}
static int
libxlMakeChannel(virDomainChrDefPtr l_channel,
libxl_device_channel *x_channel)
{
libxl_device_channel_init(x_channel);
if (l_channel->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("channel target type not supported"));
return -1;
}
switch (l_channel->source->type) {
case VIR_DOMAIN_CHR_TYPE_PTY:
x_channel->connection = LIBXL_CHANNEL_CONNECTION_PTY;
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:
x_channel->connection = LIBXL_CHANNEL_CONNECTION_SOCKET;
x_channel->u.socket.path = g_strdup(l_channel->source->data.nix.path);
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("channel source type not supported"));
break;
}
if (!l_channel->target.name) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("channel target name missing"));
return -1;
}
x_channel->name = g_strdup(l_channel->target.name);
return 0;
}
static int
libxlMakeChannelList(const char *channelDir,
virDomainDefPtr def,
libxl_domain_config *d_config)
{
virDomainChrDefPtr *l_channels = def->channels;
size_t nchannels = def->nchannels;
libxl_device_channel *x_channels;
size_t i, nvchannels = 0;
if (VIR_ALLOC_N(x_channels, nchannels) < 0)
return -1;
for (i = 0; i < nchannels; i++) {
if (l_channels[i]->deviceType != VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL)
continue;
if (libxlPrepareChannel(l_channels[i], channelDir, def->name) < 0)
goto error;
if (libxlMakeChannel(l_channels[i], &x_channels[nvchannels]) < 0)
goto error;
nvchannels++;
}
VIR_SHRINK_N(x_channels, nchannels, nchannels - nvchannels);
d_config->channels = x_channels;
d_config->num_channels = nvchannels;
return 0;
error:
for (i = 0; i < nchannels; i++)
libxl_device_channel_dispose(&x_channels[i]);
VIR_FREE(x_channels);
return -1;
}
#endif
#ifdef LIBXL_HAVE_PVUSB
int
libxlMakeUSBController(virDomainControllerDefPtr controller,
libxl_device_usbctrl *usbctrl)
{
usbctrl->devid = controller->idx;
if (controller->type != VIR_DOMAIN_CONTROLLER_TYPE_USB)
return -1;
if (controller->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT) {
usbctrl->version = 2;
usbctrl->type = LIBXL_USBCTRL_TYPE_QUSB;
} else {
switch (controller->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1:
usbctrl->version = 1;
usbctrl->type = LIBXL_USBCTRL_TYPE_QUSB;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2:
usbctrl->version = 2;
usbctrl->type = LIBXL_USBCTRL_TYPE_QUSB;
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unsupported usb model"));
return -1;
}
}
if (controller->opts.usbopts.ports == -1)
usbctrl->ports = 8;
else
usbctrl->ports = controller->opts.usbopts.ports;
return 0;
}
static int
libxlMakeDefaultUSBControllers(virDomainDefPtr def,
libxl_domain_config *d_config)
{
virDomainControllerDefPtr l_controller = NULL;
libxl_device_usbctrl *x_controllers = NULL;
size_t nusbdevs = 0;
size_t ncontrollers;
size_t i;
for (i = 0; i < def->nhostdevs; i++) {
if (def->hostdevs[i]->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
def->hostdevs[i]->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
nusbdevs++;
}
/* No controllers needed if there are no USB devs */
if (nusbdevs == 0)
return 0;
/* Create USB controllers with 8 ports */
ncontrollers = VIR_DIV_UP(nusbdevs, 8);
if (VIR_ALLOC_N(x_controllers, ncontrollers) < 0)
return -1;
for (i = 0; i < ncontrollers; i++) {
if (!(l_controller = virDomainControllerDefNew(VIR_DOMAIN_CONTROLLER_TYPE_USB)))
goto error;
l_controller->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2;
l_controller->idx = i;
l_controller->opts.usbopts.ports = 8;
libxl_device_usbctrl_init(&x_controllers[i]);
if (libxlMakeUSBController(l_controller, &x_controllers[i]) < 0)
goto error;
if (virDomainControllerInsert(def, l_controller) < 0)
goto error;
l_controller = NULL;
}
d_config->usbctrls = x_controllers;
d_config->num_usbctrls = ncontrollers;
return 0;
error:
virDomainControllerDefFree(l_controller);
for (i = 0; i < ncontrollers; i++)
libxl_device_usbctrl_dispose(&x_controllers[i]);
VIR_FREE(x_controllers);
return -1;
}
static int
libxlMakeUSBControllerList(virDomainDefPtr def, libxl_domain_config *d_config)
{
virDomainControllerDefPtr *l_controllers = def->controllers;
size_t ncontrollers = def->ncontrollers;
size_t nusbctrls = 0;
libxl_device_usbctrl *x_usbctrls;
size_t i, j;
for (i = 0; i < ncontrollers; i++) {
if (l_controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB)
nusbctrls++;
}
if (nusbctrls == 0)
return libxlMakeDefaultUSBControllers(def, d_config);
if (VIR_ALLOC_N(x_usbctrls, nusbctrls) < 0)
return -1;
for (i = 0, j = 0; i < ncontrollers && j < nusbctrls; i++) {
if (l_controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_USB)
continue;
libxl_device_usbctrl_init(&x_usbctrls[j]);
if (libxlMakeUSBController(l_controllers[i],
&x_usbctrls[j]) < 0)
goto error;
j++;
}
d_config->usbctrls = x_usbctrls;
d_config->num_usbctrls = nusbctrls;
return 0;
error:
for (i = 0; i < nusbctrls; i++)
libxl_device_usbctrl_dispose(&x_usbctrls[i]);
VIR_FREE(x_usbctrls);
return -1;
}
int
libxlMakeUSB(virDomainHostdevDefPtr hostdev, libxl_device_usbdev *usbdev)
{
virDomainHostdevSubsysUSBPtr usbsrc = &hostdev->source.subsys.u.usb;
virUSBDevicePtr usb = NULL;
int ret = -1;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
return ret;
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
return ret;
if (usbsrc->bus > 0 && usbsrc->device > 0) {
usbdev->u.hostdev.hostbus = usbsrc->bus;
usbdev->u.hostdev.hostaddr = usbsrc->device;
} else {
if (virHostdevFindUSBDevice(hostdev, true, &usb) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("failed to find USB device busnum:devnum "
"for %x:%x"),
usbsrc->vendor, usbsrc->product);
goto cleanup;
}
usbdev->u.hostdev.hostbus = virUSBDeviceGetBus(usb);
usbdev->u.hostdev.hostaddr = virUSBDeviceGetDevno(usb);
}
ret = 0;
cleanup:
virUSBDeviceFree(usb);
return ret;
}
static int
libxlMakeUSBList(virDomainDefPtr def, libxl_domain_config *d_config)
{
virDomainHostdevDefPtr *l_hostdevs = def->hostdevs;
size_t nhostdevs = def->nhostdevs;
size_t nusbdevs = 0;
libxl_device_usbdev *x_usbdevs;
size_t i, j;
if (nhostdevs == 0)
return 0;
if (VIR_ALLOC_N(x_usbdevs, nhostdevs) < 0)
return -1;
for (i = 0, j = 0; i < nhostdevs; i++) {
if (l_hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
if (l_hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
continue;
libxl_device_usbdev_init(&x_usbdevs[j]);
if (libxlMakeUSB(l_hostdevs[i], &x_usbdevs[j]) < 0)
goto error;
nusbdevs++;
j++;
}
VIR_SHRINK_N(x_usbdevs, nhostdevs, nhostdevs - nusbdevs);
d_config->usbdevs = x_usbdevs;
d_config->num_usbdevs = nusbdevs;
return 0;
error:
for (i = 0; i < nusbdevs; i++)
libxl_device_usbdev_dispose(&x_usbdevs[i]);
VIR_FREE(x_usbdevs);
return -1;
}
#endif
int
libxlMakePCI(virDomainHostdevDefPtr hostdev, libxl_device_pci *pcidev)
{
virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
return -1;
if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
return -1;
pcidev->domain = pcisrc->addr.domain;
pcidev->bus = pcisrc->addr.bus;
pcidev->dev = pcisrc->addr.slot;
pcidev->func = pcisrc->addr.function;
pcidev->permissive = hostdev->writeFiltering == VIR_TRISTATE_BOOL_NO;
return 0;
}
static int
libxlMakePCIList(virDomainDefPtr def, libxl_domain_config *d_config)
{
virDomainHostdevDefPtr *l_hostdevs = def->hostdevs;
size_t nhostdevs = def->nhostdevs;
size_t npcidevs = 0;
libxl_device_pci *x_pcidevs;
size_t i, j;
if (nhostdevs == 0)
return 0;
if (VIR_ALLOC_N(x_pcidevs, nhostdevs) < 0)
return -1;
for (i = 0, j = 0; i < nhostdevs; i++) {
if (l_hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS)
continue;
if (l_hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
continue;
libxl_device_pci_init(&x_pcidevs[j]);
if (libxlMakePCI(l_hostdevs[i], &x_pcidevs[j]) < 0)
goto error;
npcidevs++;
j++;
}
VIR_SHRINK_N(x_pcidevs, nhostdevs, nhostdevs - npcidevs);
d_config->pcidevs = x_pcidevs;
d_config->num_pcidevs = npcidevs;
return 0;
error:
for (i = 0; i < npcidevs; i++)
libxl_device_pci_dispose(&x_pcidevs[i]);
VIR_FREE(x_pcidevs);
return -1;
}
static int
libxlMakeVideo(virDomainDefPtr def, libxl_domain_config *d_config)
{
libxl_domain_build_info *b_info = &d_config->b_info;
int dm_type = libxlDomainGetEmulatorType(def);
if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_HVM)
return 0;
/*
* Take the first defined video device (graphics card) to display
* on the first graphics device (display).
*/
if (def->nvideos) {
switch (def->videos[0]->type) {
case VIR_DOMAIN_VIDEO_TYPE_VGA:
case VIR_DOMAIN_VIDEO_TYPE_XEN:
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_STD;
if (dm_type == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
if (def->videos[0]->vram < 16 * 1024) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("videoram must be at least 16MB for VGA"));
return -1;
}
} else {
if (def->videos[0]->vram < 8 * 1024) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("videoram must be at least 8MB for VGA"));
return -1;
}
}
break;
case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
if (dm_type == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
if (def->videos[0]->vram < 8 * 1024) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("videoram must be at least 8MB for CIRRUS"));
return -1;
}
} else {
if (def->videos[0]->vram < 4 * 1024) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("videoram must be at least 4MB for CIRRUS"));
return -1;
}
}
break;
#ifdef LIBXL_HAVE_QXL
case VIR_DOMAIN_VIDEO_TYPE_QXL:
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_QXL;
if (def->videos[0]->vram < 128 * 1024) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("videoram must be at least 128MB for QXL"));
return -1;
}
break;
#endif
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("video type %s is not supported by libxl"),
virDomainVideoTypeToString(def->videos[0]->type));
return -1;
}
/* vram validated for each video type, now set it */
b_info->video_memkb = def->videos[0]->vram;
} else {
libxl_defbool_set(&b_info->u.hvm.nographic, 1);
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_NONE;
}
return 0;
}
int
libxlDriverNodeGetInfo(libxlDriverPrivatePtr driver, virNodeInfoPtr info)
{
libxl_physinfo phy_info;
virArch hostarch = virArchFromHost();
libxlDriverConfigPtr cfg = libxlDriverConfigGet(driver);
int ret = -1;
libxl_physinfo_init(&phy_info);
if (libxl_get_physinfo(cfg->ctx, &phy_info)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("libxl_get_physinfo_info failed"));
goto cleanup;
}
if (virStrcpyStatic(info->model, virArchToString(hostarch)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("host arch %s is too big for destination"),
virArchToString(hostarch));
goto cleanup;
}
info->memory = phy_info.total_pages * (cfg->verInfo->pagesize / 1024);
info->cpus = phy_info.nr_cpus;
info->nodes = phy_info.nr_nodes;
info->cores = phy_info.cores_per_socket;
info->threads = phy_info.threads_per_core;
info->sockets = 1;
info->mhz = phy_info.cpu_khz / 1000;
ret = 0;
cleanup:
libxl_physinfo_dispose(&phy_info);
virObjectUnref(cfg);
return ret;
}
int
libxlBuildDomainConfig(virPortAllocatorRangePtr graphicsports,
virDomainDefPtr def,
libxlDriverConfigPtr cfg,
libxl_domain_config *d_config)
{
virCapsPtr caps = cfg->caps;
libxl_ctx *ctx = cfg->ctx;
libxl_domain_config_init(d_config);
if (libxlMakeDomCreateInfo(ctx, def, &d_config->c_info) < 0)
return -1;
if (libxlMakeDomBuildInfo(def, cfg, caps, d_config) < 0)
return -1;
#ifdef LIBXL_HAVE_VNUMA
if (libxlMakeVnumaList(def, ctx, d_config) < 0)
return -1;
#endif
if (libxlMakeDiskList(def, d_config) < 0)
return -1;
if (libxlMakeNicList(def, d_config) < 0)
return -1;
if (libxlMakeVfbList(graphicsports, def, d_config) < 0)
return -1;
if (libxlMakeBuildInfoVfb(graphicsports, def, d_config) < 0)
return -1;
if (libxlMakePCIList(def, d_config) < 0)
return -1;
#ifdef LIBXL_HAVE_PVUSB
if (libxlMakeUSBControllerList(def, d_config) < 0)
return -1;
if (libxlMakeUSBList(def, d_config) < 0)
return -1;
#endif
#ifdef LIBXL_HAVE_DEVICE_CHANNEL
if (libxlMakeChannelList(cfg->channelDir, def, d_config) < 0)
return -1;
#endif
/*
* Now that any potential VFBs are defined, update the build info with
* the data of the primary display. Some day libxl might implicitly do
* so but as it does not right now, better be explicit.
*/
if (libxlMakeVideo(def, d_config) < 0)
return -1;
d_config->on_reboot = libxlActionFromVirLifecycle(def->onReboot);
d_config->on_poweroff = libxlActionFromVirLifecycle(def->onPoweroff);
d_config->on_crash = libxlActionFromVirLifecycle(def->onCrash);
return 0;
}
virDomainXMLOptionPtr
libxlCreateXMLConf(libxlDriverPrivatePtr driver)
{
libxlDomainDefParserConfig.priv = driver;
return virDomainXMLOptionNew(&libxlDomainDefParserConfig,
&libxlDomainXMLPrivateDataCallbacks,
&libxlDriverDomainXMLNamespace,
NULL, NULL);
}