mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
Add a new "llc_colors" parameter that defines the LLC color assignment for a domain. The user can specify one or more color ranges using the same syntax used everywhere else for color config described in the documentation. The parameter is defined as a list of strings that represent the color ranges. Documentation is also added. Golang bindings are regenerated. Based on original work from: Luca Miccio <lucmiccio@gmail.com> Signed-off-by: Carlo Nonato <carlo.nonato@minervasys.tech> Signed-off-by: Marco Solieri <marco.solieri@minervasys.tech> Reviewed-by: Anthony PERARD <anthony.perard@vates.tech>
2452 lines
86 KiB
C
2452 lines
86 KiB
C
/*
|
|
* Copyright (C) 2010 Citrix Ltd.
|
|
* Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
|
|
* Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
|
|
* Author Gianni Tedesco <gianni.tedesco@citrix.com>
|
|
*
|
|
* This program 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; version 2.1 only. with the special
|
|
* exception on linking described in file LICENSE.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*/
|
|
|
|
#include "libxl_osdeps.h" /* must come before any other headers */
|
|
|
|
#include "libxl_internal.h"
|
|
#include "libxl_arch.h"
|
|
|
|
#include <xenguest.h>
|
|
#include <xen/hvm/hvm_info_table.h>
|
|
#include <xen/hvm/e820.h>
|
|
|
|
#include <xen-xsm/flask/flask.h>
|
|
|
|
int libxl__domain_create_info_setdefault(libxl__gc *gc,
|
|
libxl_domain_create_info *c_info,
|
|
const libxl_physinfo *info)
|
|
{
|
|
if (!c_info->type) {
|
|
LOG(ERROR, "domain type unspecified");
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
libxl__arch_domain_create_info_setdefault(gc, c_info);
|
|
|
|
if (c_info->type != LIBXL_DOMAIN_TYPE_PV) {
|
|
if (info->cap_hap) {
|
|
libxl_defbool_setdefault(&c_info->hap, true);
|
|
} else if (info->cap_shadow) {
|
|
libxl_defbool_setdefault(&c_info->hap, false);
|
|
} else {
|
|
LOG(ERROR, "neither hap nor shadow paging available");
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
libxl_defbool_setdefault(&c_info->oos, true);
|
|
}
|
|
|
|
libxl_defbool_setdefault(&c_info->run_hotplug_scripts, true);
|
|
libxl_defbool_setdefault(&c_info->driver_domain, false);
|
|
|
|
if (!c_info->ssidref)
|
|
c_info->ssidref = SECINITSID_DOMU;
|
|
|
|
libxl_defbool_setdefault(&c_info->xend_suspend_evtchn_compat, false);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void libxl__rdm_setdefault(libxl__gc *gc, libxl_domain_build_info *b_info)
|
|
{
|
|
if (b_info->u.hvm.rdm.policy == LIBXL_RDM_RESERVE_POLICY_INVALID)
|
|
b_info->u.hvm.rdm.policy = LIBXL_RDM_RESERVE_POLICY_RELAXED;
|
|
|
|
if (b_info->u.hvm.rdm_mem_boundary_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->u.hvm.rdm_mem_boundary_memkb =
|
|
LIBXL_RDM_MEM_BOUNDARY_MEMKB_DEFAULT;
|
|
}
|
|
|
|
int libxl__domain_build_info_setdefault(libxl__gc *gc,
|
|
libxl_domain_build_info *b_info)
|
|
{
|
|
int i, rc;
|
|
libxl_physinfo info;
|
|
|
|
if (b_info->type != LIBXL_DOMAIN_TYPE_HVM &&
|
|
b_info->type != LIBXL_DOMAIN_TYPE_PV &&
|
|
b_info->type != LIBXL_DOMAIN_TYPE_PVH) {
|
|
LOG(ERROR, "invalid domain type");
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
/* Copy deprecated options to it's new position. */
|
|
rc = libxl__domain_build_info_copy_deprecated(CTX, b_info);
|
|
if (rc) {
|
|
LOG(ERROR, "Unable to copy deprecated fields");
|
|
return rc;
|
|
}
|
|
|
|
libxl_defbool_setdefault(&b_info->device_model_stubdomain, false);
|
|
libxl_defbool_setdefault(&b_info->vpmu, false);
|
|
|
|
if (libxl_defbool_val(b_info->device_model_stubdomain) &&
|
|
!b_info->device_model_ssidref)
|
|
b_info->device_model_ssidref = SECINITSID_DOMDM;
|
|
|
|
if (!b_info->device_model_version) {
|
|
if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
if (libxl_defbool_val(b_info->device_model_stubdomain)) {
|
|
b_info->device_model_version =
|
|
LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
|
|
} else {
|
|
b_info->device_model_version = LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
|
|
}
|
|
} else {
|
|
b_info->device_model_version =
|
|
LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN;
|
|
}
|
|
if (b_info->device_model_version
|
|
== LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
|
|
const char *dm;
|
|
|
|
dm = libxl__domain_device_model(gc, b_info);
|
|
rc = access(dm, X_OK);
|
|
if (rc < 0) {
|
|
/* qemu-xen unavailable, use qemu-xen-traditional */
|
|
if (errno == ENOENT) {
|
|
LOGE(INFO, "qemu-xen is unavailable"
|
|
", using qemu-xen-traditional instead");
|
|
b_info->device_model_version =
|
|
LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL;
|
|
} else {
|
|
LOGE(ERROR, "qemu-xen access error");
|
|
return ERROR_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (b_info->blkdev_start == NULL)
|
|
b_info->blkdev_start = libxl__strdup(NOGC, "xvda");
|
|
|
|
if (b_info->type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
if (!b_info->u.hvm.bios)
|
|
switch (b_info->device_model_version) {
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
|
|
b_info->u.hvm.bios = LIBXL_BIOS_TYPE_ROMBIOS; break;
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
|
|
b_info->u.hvm.bios = LIBXL_BIOS_TYPE_SEABIOS; break;
|
|
default:
|
|
LOG(ERROR, "unknown device model version");
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
/* Enforce BIOS<->Device Model version relationship */
|
|
switch (b_info->device_model_version) {
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
|
|
if (b_info->u.hvm.bios != LIBXL_BIOS_TYPE_ROMBIOS) {
|
|
LOG(ERROR, "qemu-xen-traditional requires bios=rombios.");
|
|
return ERROR_INVAL;
|
|
}
|
|
break;
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
|
|
if (b_info->u.hvm.bios == LIBXL_BIOS_TYPE_ROMBIOS) {
|
|
LOG(ERROR, "qemu-xen does not support bios=rombios.");
|
|
return ERROR_INVAL;
|
|
}
|
|
break;
|
|
default:abort();
|
|
}
|
|
|
|
/* Check HVM direct boot parameters, we should honour ->ramdisk and
|
|
* ->cmdline iff ->kernel is set.
|
|
*/
|
|
if (!b_info->kernel && (b_info->ramdisk || b_info->cmdline)) {
|
|
LOG(ERROR, "direct boot parameters specified but kernel missing");
|
|
return ERROR_INVAL;
|
|
}
|
|
}
|
|
|
|
if (b_info->type == LIBXL_DOMAIN_TYPE_HVM &&
|
|
libxl_defbool_val(b_info->device_model_stubdomain)) {
|
|
if (!b_info->stubdomain_kernel) {
|
|
switch (b_info->device_model_version) {
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
|
|
b_info->stubdomain_kernel =
|
|
libxl__abs_path(NOGC, "ioemu-stubdom.gz", libxl__xenfirmwaredir_path());
|
|
break;
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
|
|
b_info->stubdomain_kernel =
|
|
libxl__abs_path(NOGC,
|
|
"qemu-stubdom-linux-kernel",
|
|
libxl__xenfirmwaredir_path());
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
if (!b_info->stubdomain_ramdisk) {
|
|
switch (b_info->device_model_version) {
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
|
|
break;
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
|
|
b_info->stubdomain_ramdisk =
|
|
libxl__abs_path(NOGC,
|
|
"qemu-stubdom-linux-rootfs",
|
|
libxl__xenfirmwaredir_path());
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!b_info->max_vcpus)
|
|
b_info->max_vcpus = 1;
|
|
if (!b_info->avail_vcpus.size) {
|
|
if (libxl_cpu_bitmap_alloc(CTX, &b_info->avail_vcpus, 1)) {
|
|
LOG(ERROR, "unable to allocate avail_vcpus bitmap");
|
|
return ERROR_FAIL;
|
|
}
|
|
libxl_bitmap_set(&b_info->avail_vcpus, 0);
|
|
} else if (b_info->avail_vcpus.size > HVM_MAX_VCPUS) {
|
|
LOG(ERROR, "avail_vcpus bitmap contains too many VCPUS");
|
|
return ERROR_FAIL;
|
|
}
|
|
|
|
/* In libxl internals, we want to deal with vcpu_hard_affinity only! */
|
|
if (b_info->cpumap.size && !b_info->num_vcpu_hard_affinity) {
|
|
b_info->vcpu_hard_affinity = libxl__calloc(gc, b_info->max_vcpus,
|
|
sizeof(libxl_bitmap));
|
|
for (i = 0; i < b_info->max_vcpus; i++) {
|
|
if (libxl_cpu_bitmap_alloc(CTX, &b_info->vcpu_hard_affinity[i], 0)) {
|
|
LOG(ERROR, "failed to allocate vcpu hard affinity bitmap");
|
|
return ERROR_FAIL;
|
|
}
|
|
libxl_bitmap_copy(CTX, &b_info->vcpu_hard_affinity[i],
|
|
&b_info->cpumap);
|
|
}
|
|
b_info->num_vcpu_hard_affinity = b_info->max_vcpus;
|
|
}
|
|
|
|
libxl_defbool_setdefault(&b_info->numa_placement, true);
|
|
|
|
if (b_info->max_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->max_memkb = 32 * 1024;
|
|
if (b_info->target_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->target_memkb = b_info->max_memkb;
|
|
|
|
if (b_info->stubdomain_memkb == LIBXL_MEMKB_DEFAULT) {
|
|
if (libxl_defbool_val(b_info->device_model_stubdomain)) {
|
|
if (libxl__stubdomain_is_linux(b_info))
|
|
b_info->stubdomain_memkb = LIBXL_LINUX_STUBDOM_MEM * 1024;
|
|
else
|
|
b_info->stubdomain_memkb = 28 * 1024; // MiniOS
|
|
} else {
|
|
b_info->stubdomain_memkb = 0; // no stubdomain
|
|
}
|
|
}
|
|
|
|
libxl_defbool_setdefault(&b_info->claim_mode, false);
|
|
|
|
libxl_defbool_setdefault(&b_info->localtime, false);
|
|
|
|
libxl_defbool_setdefault(&b_info->disable_migrate, false);
|
|
|
|
for (i = 0 ; i < b_info->num_iomem; i++)
|
|
if (b_info->iomem[i].gfn == LIBXL_INVALID_GFN)
|
|
b_info->iomem[i].gfn = b_info->iomem[i].start;
|
|
|
|
if (!b_info->event_channels)
|
|
b_info->event_channels = 1023;
|
|
|
|
rc = libxl_get_physinfo(CTX, &info);
|
|
if (rc) {
|
|
LOG(ERROR, "failed to get hypervisor info");
|
|
return rc;
|
|
}
|
|
|
|
rc = libxl__arch_domain_build_info_setdefault(gc, b_info, &info);
|
|
if (rc) {
|
|
LOG(ERROR, "unable to set domain arch build info defaults");
|
|
return rc;
|
|
}
|
|
|
|
libxl_defbool_setdefault(&b_info->dm_restrict, false);
|
|
|
|
if (b_info->iommu_memkb == LIBXL_MEMKB_DEFAULT)
|
|
/* Normally defaulted in libxl__domain_create_info_setdefault */
|
|
b_info->iommu_memkb = 0;
|
|
|
|
switch (b_info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
|
|
/* Normally defaulted in libxl__domain_create_info_setdefault */
|
|
b_info->shadow_memkb = 0;
|
|
if (b_info->u.hvm.mmio_hole_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->u.hvm.mmio_hole_memkb = 0;
|
|
|
|
if (b_info->u.hvm.vga.kind == LIBXL_VGA_INTERFACE_TYPE_UNKNOWN) {
|
|
b_info->u.hvm.vga.kind = LIBXL_VGA_INTERFACE_TYPE_CIRRUS;
|
|
}
|
|
|
|
if (!b_info->u.hvm.hdtype)
|
|
b_info->u.hvm.hdtype = LIBXL_HDTYPE_IDE;
|
|
|
|
switch (b_info->device_model_version) {
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN_TRADITIONAL:
|
|
switch (b_info->u.hvm.vga.kind) {
|
|
case LIBXL_VGA_INTERFACE_TYPE_NONE:
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 0;
|
|
break;
|
|
case LIBXL_VGA_INTERFACE_TYPE_QXL:
|
|
LOG(ERROR,"qemu upstream required for qxl vga");
|
|
return ERROR_INVAL;
|
|
break;
|
|
case LIBXL_VGA_INTERFACE_TYPE_STD:
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 8 * 1024;
|
|
if (b_info->video_memkb < 8 * 1024) {
|
|
LOG(ERROR, "videoram must be at least 8 MB for STDVGA on QEMU_XEN_TRADITIONAL");
|
|
return ERROR_INVAL;
|
|
}
|
|
break;
|
|
case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
|
|
default:
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 4 * 1024;
|
|
if (b_info->video_memkb != 4 * 1024)
|
|
LOG(WARN, "ignoring videoram other than 4 MB for CIRRUS on QEMU_XEN_TRADITIONAL");
|
|
break;
|
|
}
|
|
break;
|
|
case LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN:
|
|
default:
|
|
switch (b_info->u.hvm.vga.kind) {
|
|
case LIBXL_VGA_INTERFACE_TYPE_NONE:
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 0;
|
|
break;
|
|
case LIBXL_VGA_INTERFACE_TYPE_QXL:
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT) {
|
|
b_info->video_memkb = (128 * 1024);
|
|
} else if (b_info->video_memkb < (128 * 1024)) {
|
|
LOG(ERROR,
|
|
"128 Mib videoram is the minimum for qxl default");
|
|
return ERROR_INVAL;
|
|
}
|
|
break;
|
|
case LIBXL_VGA_INTERFACE_TYPE_STD:
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 16 * 1024;
|
|
if (b_info->video_memkb < 16 * 1024) {
|
|
LOG(ERROR, "videoram must be at least 16 MB for STDVGA on QEMU_XEN");
|
|
return ERROR_INVAL;
|
|
}
|
|
break;
|
|
case LIBXL_VGA_INTERFACE_TYPE_CIRRUS:
|
|
default:
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 8 * 1024;
|
|
if (b_info->video_memkb < 8 * 1024) {
|
|
LOG(ERROR, "videoram must be at least 8 MB for CIRRUS on QEMU_XEN");
|
|
return ERROR_INVAL;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
libxl_defbool_setdefault(&b_info->u.hvm.pae, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.acpi, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.acpi_s3, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.acpi_s4, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.acpi_laptop_slate, false);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.nx, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.viridian, false);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.hpet, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.vpt_align, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.usb, false);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.vkb_device, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.xen_platform_pci, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.pirq, false);
|
|
|
|
libxl_defbool_setdefault(&b_info->u.hvm.spice.enable, false);
|
|
if (!libxl_defbool_val(b_info->u.hvm.spice.enable) &&
|
|
(b_info->u.hvm.spice.usbredirection > 0) ){
|
|
b_info->u.hvm.spice.usbredirection = 0;
|
|
LOG(WARN, "spice disabled, disabling usbredirection");
|
|
}
|
|
|
|
if (!b_info->u.hvm.usbversion &&
|
|
(b_info->u.hvm.spice.usbredirection > 0) )
|
|
b_info->u.hvm.usbversion = 2;
|
|
|
|
if ((b_info->u.hvm.usbversion || b_info->u.hvm.spice.usbredirection) &&
|
|
( libxl_defbool_val(b_info->u.hvm.usb)
|
|
|| b_info->u.hvm.usbdevice_list
|
|
|| b_info->u.hvm.usbdevice) ){
|
|
LOG(ERROR,"usbversion and/or usbredirection cannot be "
|
|
"enabled with usb and/or usbdevice parameters.");
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
if (!b_info->u.hvm.boot)
|
|
b_info->u.hvm.boot = libxl__strdup(NOGC, "cda");
|
|
|
|
libxl_defbool_setdefault(&b_info->u.hvm.vnc.enable, true);
|
|
if (libxl_defbool_val(b_info->u.hvm.vnc.enable)) {
|
|
libxl_defbool_setdefault(&b_info->u.hvm.vnc.findunused, true);
|
|
if (!b_info->u.hvm.vnc.listen)
|
|
b_info->u.hvm.vnc.listen = libxl__strdup(NOGC, "127.0.0.1");
|
|
}
|
|
|
|
libxl_defbool_setdefault(&b_info->u.hvm.sdl.enable, false);
|
|
if (libxl_defbool_val(b_info->u.hvm.sdl.enable)) {
|
|
libxl_defbool_setdefault(&b_info->u.hvm.sdl.opengl, false);
|
|
}
|
|
|
|
if (libxl_defbool_val(b_info->u.hvm.spice.enable)) {
|
|
libxl_defbool_setdefault(&b_info->u.hvm.spice.disable_ticketing,
|
|
false);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.spice.agent_mouse, true);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.spice.vdagent, false);
|
|
libxl_defbool_setdefault(&b_info->u.hvm.spice.clipboard_sharing,
|
|
false);
|
|
}
|
|
|
|
libxl_defbool_setdefault(&b_info->u.hvm.nographic, false);
|
|
|
|
libxl_defbool_setdefault(&b_info->u.hvm.gfx_passthru, false);
|
|
|
|
libxl__rdm_setdefault(gc, b_info);
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
libxl_defbool_setdefault(&b_info->u.pv.e820_host, false);
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 0;
|
|
if (b_info->shadow_memkb == LIBXL_MEMKB_DEFAULT)
|
|
/* Normally defaulted in libxl__domain_create_info_setdefault */
|
|
b_info->shadow_memkb = 0;
|
|
if (b_info->u.pv.slack_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->u.pv.slack_memkb = 0;
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PVH:
|
|
libxl_defbool_setdefault(&b_info->u.pvh.pvshim, false);
|
|
if (b_info->video_memkb == LIBXL_MEMKB_DEFAULT)
|
|
b_info->video_memkb = 0;
|
|
if (libxl_defbool_val(b_info->u.pvh.pvshim)) {
|
|
if (!b_info->u.pvh.pvshim_path)
|
|
b_info->u.pvh.pvshim_path =
|
|
libxl__sprintf(NOGC, "%s/%s",
|
|
libxl__xenfirmwaredir_path(),
|
|
PVSHIM_BASENAME);
|
|
if (!b_info->u.pvh.pvshim_cmdline)
|
|
b_info->u.pvh.pvshim_cmdline =
|
|
libxl__strdup(NOGC, PVSHIM_CMDLINE);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
LOG(ERROR, "invalid domain type %s in create info",
|
|
libxl_domain_type_to_string(b_info->type));
|
|
return ERROR_INVAL;
|
|
}
|
|
|
|
/* Configuration fields shared between PVH and HVM. */
|
|
if (b_info->type != LIBXL_DOMAIN_TYPE_PV) {
|
|
if (libxl__timer_mode_is_default(&b_info->timer_mode))
|
|
b_info->timer_mode = LIBXL_TIMER_MODE_NO_DELAY_FOR_MISSED_TICKS;
|
|
|
|
libxl_defbool_setdefault(&b_info->apic, true);
|
|
libxl_defbool_setdefault(&b_info->nested_hvm, false);
|
|
}
|
|
|
|
if (b_info->max_grant_version == LIBXL_MAX_GRANT_DEFAULT) {
|
|
if (info.cap_gnttab_v2)
|
|
b_info->max_grant_version = 2;
|
|
else if (info.cap_gnttab_v1)
|
|
b_info->max_grant_version = 1;
|
|
else
|
|
/* No grant table support reported */
|
|
b_info->max_grant_version = 0;
|
|
} else if (b_info->max_grant_version & ~XEN_DOMCTL_GRANT_version_mask) {
|
|
LOG(ERROR, "max grant version %d out of range",
|
|
b_info->max_grant_version);
|
|
return -ERROR_INVAL;
|
|
}
|
|
|
|
/* Assume that providing a bootloader user implies enabling restrict. */
|
|
libxl_defbool_setdefault(&b_info->bootloader_restrict,
|
|
!!b_info->bootloader_user);
|
|
/* ENV takes precedence over provided domain_build_info. */
|
|
if (getenv("LIBXL_BOOTLOADER_RESTRICT") ||
|
|
getenv("LIBXL_BOOTLOADER_USER"))
|
|
libxl_defbool_set(&b_info->bootloader_restrict, true);
|
|
if(getenv("LIBXL_BOOTLOADER_USER"))
|
|
b_info->bootloader_user =
|
|
libxl__strdup(gc, getenv("LIBXL_BOOTLOADER_USER"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void init_console_info(libxl__gc *gc,
|
|
libxl__device_console *console,
|
|
int dev_num)
|
|
{
|
|
libxl__device_console_init(console);
|
|
console->devid = dev_num;
|
|
console->consback = LIBXL__CONSOLE_BACKEND_XENCONSOLED;
|
|
console->output = libxl__strdup(NOGC, "pty");
|
|
/* console->{name,connection,path} are NULL on normal consoles.
|
|
Only 'channels' when mapped to consoles have a string name. */
|
|
}
|
|
|
|
int libxl__domain_build(libxl__gc *gc,
|
|
libxl_domain_config *d_config,
|
|
uint32_t domid,
|
|
libxl__domain_build_state *state)
|
|
{
|
|
libxl_domain_build_info *const info = &d_config->b_info;
|
|
char **vments = NULL, **localents = NULL;
|
|
struct timeval start_time;
|
|
int i, ret;
|
|
|
|
ret = libxl__build_pre(gc, domid, d_config, state);
|
|
if (ret)
|
|
goto out;
|
|
|
|
gettimeofday(&start_time, NULL);
|
|
|
|
switch (info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
ret = libxl__build_hvm(gc, domid, d_config, state);
|
|
if (ret)
|
|
goto out;
|
|
|
|
vments = libxl__calloc(gc, 7, sizeof(char *));
|
|
vments[0] = "rtc/timeoffset";
|
|
vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
|
|
vments[2] = "image/ostype";
|
|
vments[3] = "hvm";
|
|
vments[4] = "start_time";
|
|
vments[5] = GCSPRINTF("%jd.%02d", (intmax_t)start_time.tv_sec,(int)start_time.tv_usec/10000);
|
|
|
|
localents = libxl__calloc(gc, 13, sizeof(char *));
|
|
i = 0;
|
|
localents[i++] = "platform/acpi";
|
|
localents[i++] = libxl__acpi_defbool_val(info) ? "1" : "0";
|
|
localents[i++] = "platform/acpi_s3";
|
|
localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s3) ? "1" : "0";
|
|
localents[i++] = "platform/acpi_s4";
|
|
localents[i++] = libxl_defbool_val(info->u.hvm.acpi_s4) ? "1" : "0";
|
|
localents[i++] = "platform/acpi_laptop_slate";
|
|
localents[i++] = libxl_defbool_val(info->u.hvm.acpi_laptop_slate) ? "1" : "0";
|
|
if (info->u.hvm.mmio_hole_memkb) {
|
|
uint64_t max_ram_below_4g =
|
|
(1ULL << 32) - (info->u.hvm.mmio_hole_memkb << 10);
|
|
|
|
if (max_ram_below_4g <= HVM_BELOW_4G_MMIO_START) {
|
|
localents[i++] = "platform/mmio_hole_size";
|
|
localents[i++] =
|
|
GCSPRINTF("%"PRIu64,
|
|
info->u.hvm.mmio_hole_memkb << 10);
|
|
}
|
|
}
|
|
localents[i++] = "platform/device-model";
|
|
localents[i++] = (char *)libxl_device_model_version_to_string(info->device_model_version);
|
|
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
ret = libxl__build_pv(gc, domid, d_config, state);
|
|
if (ret)
|
|
goto out;
|
|
|
|
vments = libxl__calloc(gc, 11, sizeof(char *));
|
|
i = 0;
|
|
vments[i++] = "image/ostype";
|
|
vments[i++] = "linux";
|
|
vments[i++] = "image/kernel";
|
|
vments[i++] = (char *) state->pv_kernel.path;
|
|
vments[i++] = "start_time";
|
|
vments[i++] = GCSPRINTF("%jd.%02d", (intmax_t)start_time.tv_sec,(int)start_time.tv_usec/10000);
|
|
if (state->pv_ramdisk.path) {
|
|
vments[i++] = "image/ramdisk";
|
|
vments[i++] = (char *) state->pv_ramdisk.path;
|
|
}
|
|
if (state->pv_cmdline) {
|
|
vments[i++] = "image/cmdline";
|
|
vments[i++] = (char *) state->pv_cmdline;
|
|
}
|
|
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PVH:
|
|
state->shim_path = info->u.pvh.pvshim_path;
|
|
state->shim_cmdline = GCSPRINTF("%s%s%s",
|
|
info->u.pvh.pvshim_cmdline,
|
|
info->u.pvh.pvshim_extra ? " " : "",
|
|
info->u.pvh.pvshim_extra ? info->u.pvh.pvshim_extra : "");
|
|
|
|
ret = libxl__build_hvm(gc, domid, d_config, state);
|
|
if (ret)
|
|
goto out;
|
|
|
|
vments = libxl__calloc(gc, 3, sizeof(char *));
|
|
vments[0] = "start_time";
|
|
vments[1] = GCSPRINTF("%"PRIu64".%02ld",
|
|
(uint64_t)start_time.tv_sec,
|
|
(long)start_time.tv_usec/10000);
|
|
|
|
break;
|
|
default:
|
|
ret = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
ret = libxl__build_post(gc, domid, info, state, vments, localents);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int libxl__domain_make(libxl__gc *gc, libxl_domain_config *d_config,
|
|
libxl__domain_build_state *state,
|
|
uint32_t *domid, bool soft_reset)
|
|
{
|
|
libxl_ctx *ctx = libxl__gc_owner(gc);
|
|
int ret, rc;
|
|
const char *dom_type;
|
|
char *uuid_string;
|
|
char *dom_path, *vm_path, *libxl_path;
|
|
struct xs_permissions roperm[2];
|
|
struct xs_permissions rwperm[1];
|
|
struct xs_permissions noperm[1];
|
|
xs_transaction_t t = 0;
|
|
|
|
/* convenience aliases */
|
|
libxl_domain_create_info *info = &d_config->c_info;
|
|
libxl_domain_build_info *b_info = &d_config->b_info;
|
|
|
|
assert(soft_reset || *domid == INVALID_DOMID);
|
|
|
|
uuid_string = libxl__uuid2string(gc, info->uuid);
|
|
if (!uuid_string) {
|
|
rc = ERROR_NOMEM;
|
|
goto out;
|
|
}
|
|
|
|
if (!soft_reset) {
|
|
struct xen_domctl_createdomain create = {
|
|
.ssidref = info->ssidref,
|
|
.max_vcpus = b_info->max_vcpus,
|
|
.max_evtchn_port = b_info->event_channels,
|
|
.max_grant_frames = b_info->max_grant_frames,
|
|
.max_maptrack_frames = b_info->max_maptrack_frames,
|
|
.grant_opts = XEN_DOMCTL_GRANT_version(b_info->max_grant_version),
|
|
.vmtrace_size = ROUNDUP(b_info->vmtrace_buf_kb << 10, XC_PAGE_SHIFT),
|
|
.cpupool_id = info->poolid,
|
|
};
|
|
|
|
if (info->type != LIBXL_DOMAIN_TYPE_PV) {
|
|
create.flags |= XEN_DOMCTL_CDF_hvm;
|
|
|
|
if ( libxl_defbool_val(info->hap) )
|
|
create.flags |= XEN_DOMCTL_CDF_hap;
|
|
|
|
if ( !libxl_defbool_val(info->oos) )
|
|
create.flags |= XEN_DOMCTL_CDF_oos_off;
|
|
|
|
if ( libxl_defbool_val(b_info->nested_hvm) )
|
|
create.flags |= XEN_DOMCTL_CDF_nested_virt;
|
|
}
|
|
|
|
if (libxl_defbool_val(b_info->vpmu))
|
|
create.flags |= XEN_DOMCTL_CDF_vpmu;
|
|
|
|
assert(info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
|
|
LOG(DETAIL, "passthrough: %s",
|
|
libxl_passthrough_to_string(info->passthrough));
|
|
|
|
if (info->passthrough != LIBXL_PASSTHROUGH_DISABLED)
|
|
create.flags |= XEN_DOMCTL_CDF_iommu;
|
|
|
|
if (info->passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
|
|
create.iommu_opts |= XEN_DOMCTL_IOMMU_no_sharept;
|
|
|
|
LOG(DETAIL, "altp2m: %s", libxl_altp2m_mode_to_string(b_info->altp2m));
|
|
switch(b_info->altp2m) {
|
|
case LIBXL_ALTP2M_MODE_MIXED:
|
|
create.altp2m_opts |=
|
|
XEN_DOMCTL_ALTP2M_mode(XEN_DOMCTL_ALTP2M_mixed);
|
|
break;
|
|
|
|
case LIBXL_ALTP2M_MODE_EXTERNAL:
|
|
create.altp2m_opts |=
|
|
XEN_DOMCTL_ALTP2M_mode(XEN_DOMCTL_ALTP2M_external);
|
|
break;
|
|
|
|
case LIBXL_ALTP2M_MODE_LIMITED:
|
|
create.altp2m_opts |=
|
|
XEN_DOMCTL_ALTP2M_mode(XEN_DOMCTL_ALTP2M_limited);
|
|
break;
|
|
|
|
case LIBXL_ALTP2M_MODE_DISABLED:
|
|
/* Nothing to do - altp2m disabled is signaled as mode == 0. */
|
|
break;
|
|
}
|
|
|
|
/* Ultimately, handle is an array of 16 uint8_t, same as uuid */
|
|
libxl_uuid_copy(ctx, (libxl_uuid *)&create.handle, &info->uuid);
|
|
|
|
ret = libxl__arch_domain_prepare_config(gc, d_config, &create);
|
|
if (ret < 0) {
|
|
LOGED(ERROR, *domid, "fail to get domain config");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
for (;;) {
|
|
uint32_t local_domid;
|
|
bool recent;
|
|
|
|
if (info->domid == RANDOM_DOMID) {
|
|
uint16_t v;
|
|
|
|
ret = libxl__random_bytes(gc, (void *)&v, sizeof(v));
|
|
if (ret < 0)
|
|
break;
|
|
|
|
v &= DOMID_MASK;
|
|
if (!libxl_domid_valid_guest(v))
|
|
continue;
|
|
|
|
local_domid = v;
|
|
} else {
|
|
local_domid = info->domid; /* May not be valid */
|
|
}
|
|
|
|
ret = xc_domain_create(ctx->xch, &local_domid, &create);
|
|
if (ret < 0) {
|
|
/*
|
|
* If we generated a random domid and creation failed
|
|
* because that domid already exists then simply try
|
|
* again.
|
|
*/
|
|
if (errno == EEXIST && info->domid == RANDOM_DOMID)
|
|
continue;
|
|
|
|
LOGED(ERROR, local_domid, "domain creation fail");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/* A new domain now exists */
|
|
*domid = local_domid;
|
|
|
|
ret = xc_domain_set_llc_colors(ctx->xch, local_domid,
|
|
b_info->llc_colors,
|
|
b_info->num_llc_colors);
|
|
if (ret < 0) {
|
|
if (errno == EOPNOTSUPP) {
|
|
if (b_info->num_llc_colors > 0) {
|
|
LOGED(ERROR, local_domid,
|
|
"LLC coloring not enabled in the hypervisor");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
} else {
|
|
LOGED(ERROR, local_domid, "LLC colors allocation failed");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
rc = libxl__is_domid_recent(gc, local_domid, &recent);
|
|
if (rc)
|
|
goto out;
|
|
|
|
/* The domid is not recent, so we're done */
|
|
if (!recent)
|
|
break;
|
|
|
|
/*
|
|
* If the domid was specified then there's no point in
|
|
* trying again.
|
|
*/
|
|
if (libxl_domid_valid_guest(info->domid)) {
|
|
LOGED(ERROR, local_domid, "domain id recently used");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* The domain is recent and so cannot be used. Clear domid
|
|
* here since, if xc_domain_destroy() fails below there is
|
|
* little point calling it again in the error path.
|
|
*/
|
|
*domid = INVALID_DOMID;
|
|
|
|
ret = xc_domain_destroy(ctx->xch, local_domid);
|
|
if (ret < 0) {
|
|
LOGED(ERROR, local_domid, "domain destroy fail");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
/* The domain was successfully destroyed, so we can try again */
|
|
}
|
|
|
|
rc = libxl__arch_domain_save_config(gc, d_config, state, &create);
|
|
if (rc < 0)
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* If soft_reset is set the the domid will have been valid on entry.
|
|
* If it was not set then xc_domain_create() should have assigned a
|
|
* valid value. Either way, if we reach this point, domid should be
|
|
* valid.
|
|
*/
|
|
assert(libxl_domid_valid_guest(*domid));
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, *domid);
|
|
if (!dom_path) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
vm_path = GCSPRINTF("/vm/%s", uuid_string);
|
|
if (!vm_path) {
|
|
LOGD(ERROR, *domid, "cannot allocate create paths");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
libxl_path = libxl__xs_libxl_path(gc, *domid);
|
|
if (!libxl_path) {
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
noperm[0].id = 0;
|
|
noperm[0].perms = XS_PERM_NONE;
|
|
|
|
roperm[0].id = 0;
|
|
roperm[0].perms = XS_PERM_NONE;
|
|
roperm[1].id = *domid;
|
|
roperm[1].perms = XS_PERM_READ;
|
|
|
|
rwperm[0].id = *domid;
|
|
rwperm[0].perms = XS_PERM_NONE;
|
|
|
|
retry_transaction:
|
|
t = xs_transaction_start(ctx->xsh);
|
|
|
|
xs_rm(ctx->xsh, t, dom_path);
|
|
libxl__xs_mknod(gc, t, dom_path, roperm, ARRAY_SIZE(roperm));
|
|
|
|
xs_rm(ctx->xsh, t, vm_path);
|
|
libxl__xs_mknod(gc, t, vm_path, roperm, ARRAY_SIZE(roperm));
|
|
|
|
xs_rm(ctx->xsh, t, libxl_path);
|
|
libxl__xs_mknod(gc, t, libxl_path, noperm, ARRAY_SIZE(noperm));
|
|
libxl__xs_mknod(gc, t, GCSPRINTF("%s/device", libxl_path),
|
|
noperm, ARRAY_SIZE(noperm));
|
|
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/vm", dom_path), vm_path, strlen(vm_path));
|
|
rc = libxl__domain_rename(gc, *domid, 0, info->name, t);
|
|
if (rc)
|
|
goto out;
|
|
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/cpu", dom_path),
|
|
roperm, ARRAY_SIZE(roperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/memory", dom_path),
|
|
roperm, ARRAY_SIZE(roperm));
|
|
|
|
if (!libxl_defbool_val(info->xend_suspend_evtchn_compat)) {
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/device", dom_path),
|
|
roperm, ARRAY_SIZE(roperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/device/suspend/event-channel",
|
|
dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
} else {
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/device", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
}
|
|
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control", dom_path),
|
|
roperm, ARRAY_SIZE(roperm));
|
|
if (info->type == LIBXL_DOMAIN_TYPE_HVM)
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/hvmloader", dom_path),
|
|
roperm, ARRAY_SIZE(roperm));
|
|
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control/shutdown", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control/feature-poweroff", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control/feature-reboot", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control/feature-suspend", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control/feature-s3", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control/feature-s4", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
}
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/control/sysrq", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/data", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/drivers", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/feature", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/attr", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
libxl__xs_mknod(gc, t,
|
|
GCSPRINTF("%s/error", dom_path),
|
|
rwperm, ARRAY_SIZE(rwperm));
|
|
|
|
if (libxl_defbool_val(info->driver_domain)) {
|
|
/*
|
|
* Create a local "libxl" directory for each guest, since we might want
|
|
* to use libxl from inside the guest
|
|
*/
|
|
libxl__xs_mknod(gc, t, GCSPRINTF("%s/libxl", dom_path), rwperm,
|
|
ARRAY_SIZE(rwperm));
|
|
/*
|
|
* Create a local "device-model" directory for each guest, since we
|
|
* might want to use Qemu from inside the guest
|
|
*/
|
|
libxl__xs_mknod(gc, t, GCSPRINTF("%s/device-model", dom_path), rwperm,
|
|
ARRAY_SIZE(rwperm));
|
|
}
|
|
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/uuid", vm_path), uuid_string, strlen(uuid_string));
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/name", vm_path), info->name, strlen(info->name));
|
|
|
|
libxl__xs_writev(gc, t, dom_path, info->xsdata);
|
|
libxl__xs_writev(gc, t, GCSPRINTF("%s/platform", dom_path), info->platformdata);
|
|
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-multiprocessor-suspend", dom_path), "1", 1);
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/control/platform-feature-xs_reset_watches", dom_path), "1", 1);
|
|
|
|
dom_type = libxl_domain_type_to_string(info->type);
|
|
xs_write(ctx->xsh, t, GCSPRINTF("%s/type", libxl_path), dom_type,
|
|
strlen(dom_type));
|
|
|
|
if (!xs_transaction_end(ctx->xsh, t, 0)) {
|
|
if (errno == EAGAIN)
|
|
goto retry_transaction;
|
|
LOGED(ERROR, *domid, "domain creation ""xenstore transaction commit failed");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
t = 0;
|
|
|
|
rc = 0;
|
|
out:
|
|
if (t) xs_transaction_end(ctx->xsh, t, 1);
|
|
return rc;
|
|
}
|
|
|
|
static int store_libxl_entry(libxl__gc *gc, uint32_t domid,
|
|
libxl_domain_build_info *b_info)
|
|
{
|
|
char *path = NULL;
|
|
|
|
path = libxl__xs_libxl_path(gc, domid);
|
|
path = GCSPRINTF("%s/dm-version", path);
|
|
return libxl__xs_printf(gc, XBT_NULL, path, "%s",
|
|
libxl_device_model_version_to_string(b_info->device_model_version));
|
|
}
|
|
|
|
void libxl__domain_build_state_init(libxl__domain_build_state *state)
|
|
{
|
|
state->dm_monitor_fd = -1;
|
|
}
|
|
|
|
void libxl__domain_build_state_dispose(libxl__domain_build_state *state)
|
|
{
|
|
libxl__file_reference_unmap(&state->pv_kernel);
|
|
libxl__file_reference_unmap(&state->pv_ramdisk);
|
|
if (state->dm_monitor_fd >= 0) {
|
|
close(state->dm_monitor_fd);
|
|
state->dm_monitor_fd = -1;
|
|
}
|
|
}
|
|
|
|
/*----- main domain creation -----*/
|
|
|
|
/* We have a linear control flow; only one event callback is
|
|
* outstanding at any time. Each initiation and callback function
|
|
* arranges for the next to be called, as the very last thing it
|
|
* does. (If that particular sub-operation is not needed, a
|
|
* function will call the next event callback directly.)
|
|
*/
|
|
|
|
/* Event callbacks, in this order: */
|
|
static void domcreate_bootloader_console_available(libxl__egc *egc,
|
|
libxl__bootloader_state *bl);
|
|
static void domcreate_console_available(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs);
|
|
|
|
static void domcreate_bootloader_done(libxl__egc *egc,
|
|
libxl__bootloader_state *bl,
|
|
int rc);
|
|
static void libxl__colo_restore_setup_done(libxl__egc *egc,
|
|
libxl__colo_restore_state *crs,
|
|
int rc);
|
|
static void domcreate_stream_done(libxl__egc *egc,
|
|
libxl__stream_read_state *srs,
|
|
int ret);
|
|
static void domcreate_rebuild_done(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs,
|
|
int ret);
|
|
static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *aodevs,
|
|
int ret);
|
|
static void domcreate_devmodel_started(libxl__egc *egc,
|
|
libxl__dm_spawn_state *dmss,
|
|
int rc);
|
|
static void domcreate_attach_devices(libxl__egc *egc,
|
|
libxl__multidev *multidev,
|
|
int ret);
|
|
static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
|
|
int rc, const char *p);
|
|
|
|
/* Our own function to clean up and call the user's callback.
|
|
* The final call in the sequence. */
|
|
static void domcreate_complete(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs,
|
|
int rc);
|
|
|
|
/* If creation is not successful, this callback will be executed
|
|
* when domain destruction is finished */
|
|
static void domcreate_destruction_cb(libxl__egc *egc,
|
|
libxl__domain_destroy_state *dds,
|
|
int rc);
|
|
|
|
static bool ok_to_default_memkb_in_create(libxl__gc *gc)
|
|
{
|
|
/*
|
|
* This is a fudge. We are trying to find whether the caller
|
|
* calls the old version of libxl_domain_need_memory. If they do
|
|
* then, because it only gets the b_info, and because it can't
|
|
* update the b_info (because it's const), it will base its
|
|
* calculations on defaulting shadow_memkb and iommu_memkb to 0
|
|
* In that case we probably shouldn't default them differently
|
|
* during libxl_domain_create.
|
|
*
|
|
* The result is that the behaviour with old callers is the same
|
|
* as in 4.13: no additional memory is allocated for shadow and
|
|
* iommu (unless the caller set shadow_memkb, eg from a call to
|
|
* libxl_get_required_shadow_memory).
|
|
*/
|
|
return !CTX->libxl_domain_need_memory_0x041200_called ||
|
|
CTX->libxl_domain_need_memory_called;
|
|
/*
|
|
* Treat mixed callers as new callers. Presumably they know what
|
|
* they are doing.
|
|
*/
|
|
}
|
|
|
|
unsigned long libxl__get_required_paging_memory(unsigned long maxmem_kb,
|
|
unsigned int smp_cpus,
|
|
libxl_domain_type type,
|
|
bool hap)
|
|
{
|
|
/*
|
|
* 256 pages (1MB) per vcpu,
|
|
* plus 1 page per MiB of RAM for the P2M map (for non-PV guests),
|
|
* plus 1 page per MiB of RAM to shadow the resident processes (for shadow
|
|
* mode guests).
|
|
* plus 1 page per MiB of RAM for the architecture specific
|
|
* EXTRA_DEFAULT_PAGING_MEM_MB. On x86, this value is zero. On Arm, this
|
|
* value is 128 MiB to cover domain extended regions (enough for domains
|
|
* that are not running backend).
|
|
* This is higher than the minimum that Xen would allocate if no value
|
|
* were given (but the Xen minimum is for safety, not performance).
|
|
*/
|
|
return 4 * (256 * smp_cpus +
|
|
((type != LIBXL_DOMAIN_TYPE_PV) + !hap) *
|
|
(maxmem_kb / 1024) +
|
|
EXTRA_DEFAULT_PAGING_MEM_MB);
|
|
}
|
|
|
|
static unsigned long libxl__get_required_iommu_memory(unsigned long maxmem_kb)
|
|
{
|
|
unsigned long iommu_pages = 0, mem_pages = maxmem_kb / 4;
|
|
unsigned int level;
|
|
|
|
/* Assume a 4 level page table with 512 entries per level */
|
|
for (level = 0; level < 4; level++)
|
|
{
|
|
mem_pages = DIV_ROUNDUP(mem_pages, 512);
|
|
iommu_pages += mem_pages;
|
|
}
|
|
|
|
return iommu_pages * 4;
|
|
}
|
|
|
|
int libxl__domain_config_setdefault(libxl__gc *gc,
|
|
libxl_domain_config *d_config,
|
|
uint32_t domid /* for logging, only */)
|
|
{
|
|
libxl_ctx *ctx = libxl__gc_owner(gc);
|
|
int ret, i;
|
|
bool pod_enabled = false;
|
|
libxl_domain_create_info *c_info = &d_config->c_info;
|
|
|
|
libxl_physinfo physinfo;
|
|
ret = libxl_get_physinfo(CTX, &physinfo);
|
|
if (ret) goto error_out;
|
|
|
|
if (d_config->c_info.ssid_label) {
|
|
char *s = d_config->c_info.ssid_label;
|
|
ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
|
|
&d_config->c_info.ssidref);
|
|
if (ret) {
|
|
if (errno != ENOSYS) {
|
|
LOGD(ERROR, domid, "Invalid init_seclabel: %s", s);
|
|
goto error_out;
|
|
}
|
|
|
|
LOGD(WARN, domid, "XSM Disabled: init_seclabel not supported");
|
|
}
|
|
}
|
|
|
|
if (d_config->b_info.exec_ssid_label) {
|
|
char *s = d_config->b_info.exec_ssid_label;
|
|
ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
|
|
&d_config->b_info.exec_ssidref);
|
|
if (ret) {
|
|
if (errno != ENOSYS) {
|
|
LOGD(ERROR, domid, "Invalid seclabel: %s", s);
|
|
goto error_out;
|
|
}
|
|
|
|
LOGD(WARN, domid, "XSM Disabled: seclabel not supported");
|
|
}
|
|
}
|
|
|
|
if (d_config->b_info.device_model_ssid_label) {
|
|
char *s = d_config->b_info.device_model_ssid_label;
|
|
ret = libxl_flask_context_to_sid(ctx, s, strlen(s),
|
|
&d_config->b_info.device_model_ssidref);
|
|
if (ret) {
|
|
if (errno != ENOSYS) {
|
|
LOGD(ERROR, domid, "Invalid device_model_stubdomain_seclabel: %s", s);
|
|
goto error_out;
|
|
}
|
|
|
|
LOGD(WARN, domid,
|
|
"XSM Disabled: device_model_stubdomain_seclabel not supported");
|
|
}
|
|
}
|
|
|
|
if (d_config->c_info.pool_name) {
|
|
d_config->c_info.poolid = -1;
|
|
libxl_cpupool_qualifier_to_cpupoolid(ctx, d_config->c_info.pool_name,
|
|
&d_config->c_info.poolid,
|
|
NULL);
|
|
}
|
|
if (!libxl_cpupoolid_is_valid(ctx, d_config->c_info.poolid)) {
|
|
LOGD(ERROR, domid, "Illegal pool specified: %s",
|
|
d_config->c_info.pool_name);
|
|
ret = ERROR_INVAL;
|
|
goto error_out;
|
|
}
|
|
|
|
ret = libxl__domain_create_info_setdefault(gc, &d_config->c_info,
|
|
&physinfo);
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "Unable to set domain create info defaults");
|
|
goto error_out;
|
|
}
|
|
|
|
/*
|
|
* Note: libxl_arm directly examines num_pcidevs to decide whether
|
|
* to create a vPCI DT node, rather than using c_info->passthrough.
|
|
* This will be insufficient if and when ARM does PCI hotplug.
|
|
*/
|
|
bool need_pt = d_config->num_pcidevs || d_config->num_dtdevs;
|
|
if (c_info->passthrough == LIBXL_PASSTHROUGH_DEFAULT) {
|
|
c_info->passthrough = need_pt
|
|
? LIBXL_PASSTHROUGH_ENABLED : LIBXL_PASSTHROUGH_DISABLED;
|
|
}
|
|
|
|
bool iommu_enabled = physinfo.cap_hvm_directio;
|
|
if (c_info->passthrough != LIBXL_PASSTHROUGH_DISABLED && !iommu_enabled) {
|
|
LOGD(ERROR, domid,
|
|
"passthrough not supported on this platform\n");
|
|
ret = ERROR_INVAL;
|
|
goto error_out;
|
|
}
|
|
|
|
if (c_info->passthrough == LIBXL_PASSTHROUGH_DISABLED && need_pt) {
|
|
LOGD(ERROR, domid,
|
|
"passthrough disabled but devices are specified");
|
|
ret = ERROR_INVAL;
|
|
goto error_out;
|
|
}
|
|
|
|
ret = libxl__arch_passthrough_mode_setdefault(gc,domid,d_config,&physinfo);
|
|
if (ret) goto error_out;
|
|
|
|
/* An explicit setting should now have been chosen */
|
|
assert(c_info->passthrough != LIBXL_PASSTHROUGH_DEFAULT);
|
|
assert(c_info->passthrough != LIBXL_PASSTHROUGH_ENABLED);
|
|
|
|
/* If target_memkb is smaller than max_memkb, the subsequent call
|
|
* to libxc when building HVM domain will enable PoD mode.
|
|
*/
|
|
pod_enabled = (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV) &&
|
|
(d_config->b_info.target_memkb < d_config->b_info.max_memkb);
|
|
|
|
/* We don't support having PoD and an IOMMU at the same time for HVM
|
|
* guests. An active IOMMU cannot work with PoD because it needs a fully
|
|
* populated page-table. Prevent PoD usage if the domain has an IOMMU
|
|
* assigned, even if not active.
|
|
*/
|
|
if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
|
|
d_config->c_info.passthrough != LIBXL_PASSTHROUGH_DISABLED &&
|
|
pod_enabled) {
|
|
ret = ERROR_INVAL;
|
|
LOGD(ERROR, domid,
|
|
"IOMMU required for device passthrough but not supported together with PoD");
|
|
goto error_out;
|
|
}
|
|
|
|
/* Disallow PoD and vNUMA to be enabled at the same time because PoD
|
|
* pool is not vNUMA-aware yet.
|
|
*/
|
|
if (pod_enabled && d_config->b_info.num_vnuma_nodes) {
|
|
ret = ERROR_INVAL;
|
|
LOGD(ERROR, domid, "Cannot enable PoD and vNUMA at the same time");
|
|
goto error_out;
|
|
}
|
|
|
|
/* PV vNUMA is not yet supported because there is an issue with
|
|
* cpuid handling.
|
|
*/
|
|
if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_PV &&
|
|
d_config->b_info.num_vnuma_nodes) {
|
|
ret = ERROR_INVAL;
|
|
LOGD(ERROR, domid, "PV vNUMA is not yet supported");
|
|
goto error_out;
|
|
}
|
|
|
|
if (d_config->b_info.shadow_memkb == LIBXL_MEMKB_DEFAULT
|
|
&& ok_to_default_memkb_in_create(gc)) {
|
|
bool hap = d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
|
|
libxl_defbool_val(d_config->c_info.hap);
|
|
|
|
d_config->b_info.shadow_memkb =
|
|
libxl__get_required_paging_memory(d_config->b_info.max_memkb,
|
|
d_config->b_info.max_vcpus,
|
|
d_config->c_info.type,
|
|
hap);
|
|
}
|
|
|
|
/* No IOMMU reservation is needed if passthrough mode is not 'sync_pt' */
|
|
if (d_config->b_info.iommu_memkb == LIBXL_MEMKB_DEFAULT
|
|
&& ok_to_default_memkb_in_create(gc))
|
|
d_config->b_info.iommu_memkb =
|
|
(d_config->c_info.passthrough == LIBXL_PASSTHROUGH_SYNC_PT)
|
|
? libxl__get_required_iommu_memory(d_config->b_info.max_memkb)
|
|
: 0;
|
|
|
|
ret = libxl__domain_build_info_setdefault(gc, &d_config->b_info);
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "Unable to set domain build info defaults");
|
|
goto error_out;
|
|
}
|
|
|
|
if (d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
|
|
(libxl_defbool_val(d_config->b_info.nested_hvm) &&
|
|
((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
|
|
libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
|
|
(d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)))) {
|
|
ret = ERROR_INVAL;
|
|
LOGD(ERROR, domid, "nestedhvm and altp2mhvm cannot be used together");
|
|
goto error_out;
|
|
}
|
|
|
|
if (((d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
|
|
libxl_defbool_val(d_config->b_info.u.hvm.altp2m)) ||
|
|
(d_config->c_info.type != LIBXL_DOMAIN_TYPE_PV &&
|
|
d_config->b_info.altp2m != LIBXL_ALTP2M_MODE_DISABLED)) &&
|
|
pod_enabled) {
|
|
ret = ERROR_INVAL;
|
|
LOGD(ERROR, domid, "Cannot enable PoD and ALTP2M at the same time");
|
|
goto error_out;
|
|
}
|
|
|
|
if (libxl_defbool_val(d_config->b_info.vpmu) && !physinfo.cap_vpmu) {
|
|
ret = ERROR_INVAL;
|
|
LOGD(ERROR, domid, "vPMU not supported on this platform");
|
|
goto error_out;
|
|
}
|
|
|
|
for (i = 0; i < d_config->num_virtios; i++) {
|
|
ret = libxl__virtio_devtype.set_default(gc, domid,
|
|
&d_config->virtios[i], false);
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "Unable to set virtio defaults for device %d", i);
|
|
goto error_out;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
error_out:
|
|
return ret;
|
|
}
|
|
|
|
static void initiate_domain_create(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs)
|
|
{
|
|
STATE_AO_GC(dcs->ao);
|
|
uint32_t domid;
|
|
int i, ret;
|
|
|
|
/* convenience aliases */
|
|
libxl_domain_config *const d_config = dcs->guest_config;
|
|
libxl__domain_build_state *dbs = &dcs->build_state;
|
|
|
|
domid = dcs->domid;
|
|
libxl__domain_build_state_init(dbs);
|
|
dbs->restore = dcs->restore_fd >= 0;
|
|
|
|
ret = libxl__domain_config_setdefault(gc,d_config,domid);
|
|
if (ret) goto error_out;
|
|
|
|
ret = libxl__domain_make(gc, d_config, dbs, &domid, dcs->soft_reset);
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "cannot make domain: %d", ret);
|
|
dcs->guest_domid = domid;
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
|
|
dcs->guest_domid = domid;
|
|
dcs->sdss.dm.guest_domid = 0; /* means we haven't spawned */
|
|
|
|
/* post-4.13 todo: move these next bits of defaulting to
|
|
* libxl__domain_config_setdefault */
|
|
|
|
/*
|
|
* Set the dm version quite early so that libxl doesn't have to pass the
|
|
* build info around just to know if the domain has a device model or not.
|
|
*/
|
|
store_libxl_entry(gc, domid, &d_config->b_info);
|
|
|
|
for (i = 0; i < d_config->num_disks; i++) {
|
|
ret = libxl__disk_devtype.set_default(gc, domid, &d_config->disks[i],
|
|
false);
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "Unable to set disk defaults for disk %d", i);
|
|
goto error_out;
|
|
}
|
|
}
|
|
|
|
dcs->bl.ao = ao;
|
|
libxl_device_disk *bootdisk =
|
|
d_config->num_disks > 0 ? &d_config->disks[0] : NULL;
|
|
|
|
/*
|
|
* The devid has to be set before launching the device model. For the
|
|
* hotplug case this is done in libxl_device_nic_add but on domain
|
|
* creation this is called too late.
|
|
* Make two runs over configured NICs in order to avoid duplicate IDs
|
|
* in case the caller partially assigned IDs.
|
|
*/
|
|
ret = libxl__device_nic_set_devids(gc, d_config, domid);
|
|
if (ret)
|
|
goto error_out;
|
|
|
|
if (dbs->restore || dcs->soft_reset) {
|
|
LOGD(DEBUG, domid, "restoring, not running bootloader");
|
|
domcreate_bootloader_done(egc, &dcs->bl, 0);
|
|
} else {
|
|
LOGD(DEBUG, domid, "running bootloader");
|
|
dcs->bl.callback = domcreate_bootloader_done;
|
|
dcs->bl.console_available = domcreate_bootloader_console_available;
|
|
dcs->bl.info = &d_config->b_info;
|
|
dcs->bl.disk = bootdisk;
|
|
dcs->bl.domid = dcs->guest_domid;
|
|
|
|
dcs->bl.kernel = &dbs->pv_kernel;
|
|
dcs->bl.ramdisk = &dbs->pv_ramdisk;
|
|
|
|
libxl__bootloader_run(egc, &dcs->bl);
|
|
}
|
|
return;
|
|
|
|
error_out:
|
|
assert(ret);
|
|
domcreate_complete(egc, dcs, ret);
|
|
}
|
|
|
|
static void domcreate_bootloader_console_available(libxl__egc *egc,
|
|
libxl__bootloader_state *bl)
|
|
{
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
|
|
STATE_AO_GC(bl->ao);
|
|
domcreate_console_available(egc, dcs);
|
|
}
|
|
|
|
static void domcreate_console_available(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs) {
|
|
libxl__ao_progress_report(egc, dcs->ao, &dcs->aop_console_how,
|
|
NEW_EVENT(egc, DOMAIN_CREATE_CONSOLE_AVAILABLE,
|
|
dcs->guest_domid,
|
|
dcs->aop_console_how.for_event));
|
|
}
|
|
|
|
static void domcreate_bootloader_done(libxl__egc *egc,
|
|
libxl__bootloader_state *bl,
|
|
int rc)
|
|
{
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(bl, *dcs, bl);
|
|
STATE_AO_GC(bl->ao);
|
|
|
|
/* convenience aliases */
|
|
const uint32_t domid = dcs->guest_domid;
|
|
libxl_domain_config *const d_config = dcs->guest_config;
|
|
const int restore_fd = dcs->restore_fd;
|
|
libxl__domain_build_state *const state = &dcs->build_state;
|
|
const int checkpointed_stream = dcs->restore_params.checkpointed_stream;
|
|
libxl__colo_restore_state *const crs = &dcs->crs;
|
|
libxl_domain_build_info *const info = &d_config->b_info;
|
|
libxl__srm_restore_autogen_callbacks *const callbacks =
|
|
&dcs->srs.shs.callbacks.restore.a;
|
|
|
|
if (rc) {
|
|
domcreate_rebuild_done(egc, dcs, rc);
|
|
return;
|
|
}
|
|
|
|
/* consume bootloader outputs. state->pv_{kernel,ramdisk} have
|
|
* been initialised by the bootloader already.
|
|
*/
|
|
state->pv_cmdline = bl->cmdline;
|
|
|
|
/* We might be going to call libxl__spawn_local_dm, or _spawn_stub_dm.
|
|
* Fill in any field required by either, including both relevant
|
|
* callbacks (_spawn_stub_dm will overwrite our trespass if needed). */
|
|
dcs->sdss.dm.spawn.ao = ao;
|
|
dcs->sdss.dm.guest_config = dcs->guest_config;
|
|
dcs->sdss.dm.build_state = &dcs->build_state;
|
|
dcs->sdss.dm.callback = domcreate_devmodel_started;
|
|
dcs->sdss.callback = domcreate_devmodel_started;
|
|
|
|
if (restore_fd < 0 && !dcs->soft_reset) {
|
|
rc = libxl__domain_build(gc, d_config, domid, state);
|
|
domcreate_rebuild_done(egc, dcs, rc);
|
|
return;
|
|
}
|
|
|
|
/* Prepare environment for domcreate_stream_done */
|
|
dcs->srs.dcs = dcs;
|
|
|
|
/* Restore */
|
|
callbacks->static_data_done = libxl__srm_callout_callback_static_data_done;
|
|
callbacks->restore_results = libxl__srm_callout_callback_restore_results;
|
|
|
|
/* COLO only supports HVM now because it does not work very
|
|
* well with pv drivers:
|
|
* 1. We need to resume vm in the slow path. In this case we
|
|
* need to disconnect/reconnect backend and frontend. It
|
|
* will take too much time and the performance is very slow.
|
|
* 2. PV disk cannot reuse block replication that is implemented
|
|
* in QEMU.
|
|
*/
|
|
if (info->type != LIBXL_DOMAIN_TYPE_HVM &&
|
|
checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
|
|
LOGD(ERROR, domid, "COLO only supports HVM, unable to restore domain");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__build_pre(gc, domid, d_config, state);
|
|
if (rc)
|
|
goto out;
|
|
|
|
dcs->srs.ao = ao;
|
|
dcs->srs.fd = restore_fd;
|
|
dcs->srs.legacy = (dcs->restore_params.stream_version == 1);
|
|
dcs->srs.back_channel = false;
|
|
dcs->srs.completion_callback = domcreate_stream_done;
|
|
|
|
if (restore_fd >= 0) {
|
|
switch (checkpointed_stream) {
|
|
case LIBXL_CHECKPOINTED_STREAM_COLO:
|
|
/* colo restore setup */
|
|
crs->ao = ao;
|
|
crs->domid = domid;
|
|
crs->send_back_fd = dcs->send_back_fd;
|
|
crs->recv_fd = restore_fd;
|
|
crs->hvm = (info->type != LIBXL_DOMAIN_TYPE_PV);
|
|
crs->callback = libxl__colo_restore_setup_done;
|
|
libxl__colo_restore_setup(egc, crs);
|
|
break;
|
|
case LIBXL_CHECKPOINTED_STREAM_REMUS:
|
|
libxl__remus_restore_setup(egc, dcs);
|
|
/* fall through */
|
|
case LIBXL_CHECKPOINTED_STREAM_NONE:
|
|
libxl__stream_read_start(egc, &dcs->srs);
|
|
}
|
|
return;
|
|
}
|
|
|
|
out:
|
|
domcreate_stream_done(egc, &dcs->srs, rc);
|
|
}
|
|
|
|
static void libxl__colo_restore_setup_done(libxl__egc *egc,
|
|
libxl__colo_restore_state *crs,
|
|
int rc)
|
|
{
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(crs, *dcs, crs);
|
|
|
|
EGC_GC;
|
|
|
|
if (rc) {
|
|
LOGD(ERROR, dcs->guest_domid, "colo restore setup fails: %d", rc);
|
|
domcreate_stream_done(egc, &dcs->srs, rc);
|
|
return;
|
|
}
|
|
|
|
libxl__stream_read_start(egc, &dcs->srs);
|
|
}
|
|
|
|
int libxl__srm_callout_callback_static_data_done(unsigned int missing,
|
|
void *user)
|
|
{
|
|
libxl__save_helper_state *shs = user;
|
|
libxl__domain_create_state *dcs = shs->caller_state;
|
|
STATE_AO_GC(dcs->ao);
|
|
libxl_ctx *ctx = libxl__gc_owner(gc);
|
|
|
|
libxl_domain_config *d_config = dcs->guest_config;
|
|
libxl_domain_build_info *info = &d_config->b_info;
|
|
int rc = 0;
|
|
|
|
/*
|
|
* CPUID/MSR information is not present in pre Xen-4.14 streams.
|
|
*
|
|
* Libxl used to always regenerate the CPUID policy from first principles
|
|
* on migrate. Continue to do so for backwards compatibility when the
|
|
* stream doesn't contain any CPUID data.
|
|
*/
|
|
if (missing & XGR_SDD_MISSING_CPUID)
|
|
rc = libxl__cpuid_legacy(ctx, dcs->guest_domid, true, info);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void libxl__srm_callout_callback_restore_results(xen_pfn_t store_mfn,
|
|
xen_pfn_t console_mfn, void *user)
|
|
{
|
|
libxl__save_helper_state *shs = user;
|
|
libxl__domain_create_state *dcs = shs->caller_state;
|
|
STATE_AO_GC(dcs->ao);
|
|
libxl__domain_build_state *const state = &dcs->build_state;
|
|
|
|
state->store_mfn = store_mfn;
|
|
state->console_mfn = console_mfn;
|
|
shs->need_results = 0;
|
|
}
|
|
|
|
static void domcreate_stream_done(libxl__egc *egc,
|
|
libxl__stream_read_state *srs,
|
|
int ret)
|
|
{
|
|
/* NB perhaps only srs->dcs is valid; eg in the case of an
|
|
* early branch to domcreate_bootloader_done's `out' block */
|
|
libxl__domain_create_state *dcs = srs->dcs;
|
|
STATE_AO_GC(dcs->ao);
|
|
libxl_ctx *ctx = libxl__gc_owner(gc);
|
|
char **vments = NULL, **localents = NULL;
|
|
struct timeval start_time;
|
|
int i, esave;
|
|
|
|
/* convenience aliases */
|
|
const uint32_t domid = dcs->guest_domid;
|
|
libxl_domain_config *const d_config = dcs->guest_config;
|
|
libxl_domain_build_info *const info = &d_config->b_info;
|
|
libxl__domain_build_state *const state = &dcs->build_state;
|
|
const int fd = dcs->restore_fd;
|
|
|
|
if (ret)
|
|
goto out;
|
|
|
|
gettimeofday(&start_time, NULL);
|
|
|
|
switch (info->type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
vments = libxl__calloc(gc, 7, sizeof(char *));
|
|
vments[0] = "rtc/timeoffset";
|
|
vments[1] = (info->u.hvm.timeoffset) ? info->u.hvm.timeoffset : "";
|
|
vments[2] = "image/ostype";
|
|
vments[3] = "hvm";
|
|
vments[4] = "start_time";
|
|
vments[5] = GCSPRINTF("%jd.%02d", (intmax_t)start_time.tv_sec,(int)start_time.tv_usec/10000);
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
vments = libxl__calloc(gc, 11, sizeof(char *));
|
|
i = 0;
|
|
vments[i++] = "image/ostype";
|
|
vments[i++] = "linux";
|
|
vments[i++] = "image/kernel";
|
|
vments[i++] = (char *) state->pv_kernel.path;
|
|
vments[i++] = "start_time";
|
|
vments[i++] = GCSPRINTF("%jd.%02d", (intmax_t)start_time.tv_sec,(int)start_time.tv_usec/10000);
|
|
if (state->pv_ramdisk.path) {
|
|
vments[i++] = "image/ramdisk";
|
|
vments[i++] = (char *) state->pv_ramdisk.path;
|
|
}
|
|
if (state->pv_cmdline) {
|
|
vments[i++] = "image/cmdline";
|
|
vments[i++] = (char *) state->pv_cmdline;
|
|
}
|
|
break;
|
|
case LIBXL_DOMAIN_TYPE_PVH:
|
|
vments = libxl__calloc(gc, 3, sizeof(char *));
|
|
vments[0] = "start_time";
|
|
vments[1] = GCSPRINTF("%"PRIu64".%02ld",
|
|
(uint64_t)start_time.tv_sec,
|
|
(long)start_time.tv_usec/10000);
|
|
break;
|
|
default:
|
|
ret = ERROR_INVAL;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* The scheduler on the sending domain may be different than the
|
|
* scheduler running here. Setting the scheduler to UNKNOWN will
|
|
* cause the code to take to take whatever parameters are
|
|
* available in that scheduler, while discarding the rest.
|
|
*/
|
|
info->sched_params.sched = LIBXL_SCHEDULER_UNKNOWN;
|
|
|
|
ret = libxl__build_post(gc, domid, info, state, vments, localents);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (info->type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
state->saved_state = GCSPRINTF(
|
|
LIBXL_DEVICE_MODEL_RESTORE_FILE".%d", domid);
|
|
}
|
|
|
|
out:
|
|
if (info->type == LIBXL_DOMAIN_TYPE_PV) {
|
|
libxl__file_reference_unmap(&state->pv_kernel);
|
|
libxl__file_reference_unmap(&state->pv_ramdisk);
|
|
}
|
|
|
|
/* fd == -1 here means we're doing soft reset. */
|
|
if (fd != -1) {
|
|
esave = errno;
|
|
libxl_fd_set_nonblock(ctx, fd, 0);
|
|
errno = esave;
|
|
}
|
|
domcreate_rebuild_done(egc, dcs, ret);
|
|
}
|
|
|
|
static void domcreate_rebuild_done(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs,
|
|
int ret)
|
|
{
|
|
STATE_AO_GC(dcs->ao);
|
|
|
|
/* convenience aliases */
|
|
const uint32_t domid = dcs->guest_domid;
|
|
libxl_domain_config *const d_config = dcs->guest_config;
|
|
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "cannot (re-)build domain: %d", ret);
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
|
|
store_libxl_entry(gc, domid, &d_config->b_info);
|
|
|
|
libxl__multidev_begin(ao, &dcs->multidev);
|
|
dcs->multidev.callback = domcreate_launch_dm;
|
|
libxl__add_disks(egc, ao, domid, d_config, &dcs->multidev);
|
|
libxl__multidev_prepared(egc, &dcs->multidev, 0);
|
|
|
|
return;
|
|
|
|
error_out:
|
|
assert(ret);
|
|
domcreate_complete(egc, dcs, ret);
|
|
}
|
|
|
|
static void domcreate_launch_dm(libxl__egc *egc, libxl__multidev *multidev,
|
|
int ret)
|
|
{
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
|
|
STATE_AO_GC(dcs->ao);
|
|
int i;
|
|
|
|
/* convenience aliases */
|
|
const uint32_t domid = dcs->guest_domid;
|
|
libxl_domain_config *const d_config = dcs->guest_config;
|
|
libxl__domain_build_state *const state = &dcs->build_state;
|
|
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "unable to add disk devices");
|
|
goto error_out;
|
|
}
|
|
|
|
for (i = 0; i < d_config->b_info.num_ioports; i++) {
|
|
libxl_ioport_range *io = &d_config->b_info.ioports[i];
|
|
|
|
LOGD(DEBUG, domid, "ioports %"PRIx32"-%"PRIx32,
|
|
io->first, io->first + io->number - 1);
|
|
|
|
ret = xc_domain_ioport_permission(CTX->xch, domid,
|
|
io->first, io->number, 1);
|
|
if (ret < 0) {
|
|
LOGED(ERROR, domid,
|
|
"failed give domain access to ioports %"PRIx32"-%"PRIx32,
|
|
io->first, io->first + io->number - 1);
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < d_config->b_info.num_irqs; i++) {
|
|
int irq = d_config->b_info.irqs[i];
|
|
|
|
LOGD(DEBUG, domid, "irq %d", irq);
|
|
|
|
ret = irq >= 0 ? libxl__arch_domain_map_irq(gc, domid, irq)
|
|
: -EOVERFLOW;
|
|
if (ret) {
|
|
LOGED(ERROR, domid, "failed give domain access to irq %d", irq);
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < d_config->b_info.num_iomem; i++) {
|
|
libxl_iomem_range *io = &d_config->b_info.iomem[i];
|
|
|
|
LOGD(DEBUG, domid, "iomem %"PRIx64"-%"PRIx64,
|
|
io->start, io->start + io->number - 1);
|
|
|
|
ret = xc_domain_iomem_permission(CTX->xch, domid,
|
|
io->start, io->number, 1);
|
|
if (ret < 0) {
|
|
LOGED(ERROR, domid,
|
|
"failed give domain access to iomem range %"PRIx64"-%"PRIx64,
|
|
io->start, io->start + io->number - 1);
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
ret = xc_domain_memory_mapping(CTX->xch, domid,
|
|
io->gfn, io->start,
|
|
io->number, 1);
|
|
if (ret < 0) {
|
|
LOGED(ERROR, domid,
|
|
"failed to map to domain iomem range %"PRIx64"-%"PRIx64
|
|
" to guest address %"PRIx64,
|
|
io->start, io->start + io->number - 1, io->gfn);
|
|
ret = ERROR_FAIL;
|
|
goto error_out;
|
|
}
|
|
}
|
|
|
|
/* For both HVM and PV the 0th console is a regular console. We
|
|
map channels to IOEMU consoles starting at 1 */
|
|
for (i = 0; i < d_config->num_channels; i++) {
|
|
libxl__device_console console;
|
|
libxl__device device;
|
|
ret = libxl__init_console_from_channel(gc, &console, i + 1,
|
|
&d_config->channels[i]);
|
|
if ( ret ) {
|
|
libxl__device_console_dispose(&console);
|
|
goto error_out;
|
|
}
|
|
libxl__device_console_add(gc, domid, &console, NULL, &device);
|
|
libxl__device_console_dispose(&console);
|
|
}
|
|
|
|
for (i = 0; i < d_config->num_pvcallsifs; i++)
|
|
libxl__device_add(gc, domid, &libxl__pvcallsif_devtype,
|
|
&d_config->pvcallsifs[i]);
|
|
|
|
for (i = 0; i < d_config->num_virtios; i++)
|
|
libxl__device_add(gc, domid, &libxl__virtio_devtype,
|
|
&d_config->virtios[i]);
|
|
|
|
if (d_config->num_vkbs) {
|
|
for (i = 0; i < d_config->num_vkbs; i++) {
|
|
ret = libxl__device_add(gc, domid, &libxl__vkb_devtype,
|
|
&d_config->vkbs[i]);
|
|
if (ret) goto error_out;
|
|
}
|
|
} else if (d_config->c_info.type == LIBXL_DOMAIN_TYPE_HVM &&
|
|
libxl_defbool_val(d_config->b_info.u.hvm.vkb_device)) {
|
|
libxl_device_vkb vkb;
|
|
|
|
libxl_device_vkb_init(&vkb);
|
|
libxl__device_add(gc, domid, &libxl__vkb_devtype, &vkb);
|
|
libxl_device_vkb_dispose(&vkb);
|
|
}
|
|
|
|
switch (d_config->c_info.type) {
|
|
case LIBXL_DOMAIN_TYPE_HVM:
|
|
{
|
|
libxl__device_console console;
|
|
libxl__device device;
|
|
|
|
init_console_info(gc, &console, 0);
|
|
console.backend_domid = state->console_domid;
|
|
libxl__device_console_add(gc, domid, &console, state, &device);
|
|
libxl__device_console_dispose(&console);
|
|
|
|
dcs->sdss.dm.guest_domid = domid;
|
|
if (libxl_defbool_val(d_config->b_info.device_model_stubdomain))
|
|
libxl__spawn_stub_dm(egc, &dcs->sdss);
|
|
else
|
|
libxl__spawn_local_dm(egc, &dcs->sdss.dm);
|
|
|
|
/*
|
|
* Handle the domain's (and the related stubdomain's) access to
|
|
* the VGA framebuffer.
|
|
*/
|
|
ret = libxl__grant_vga_iomem_permission(gc, domid, d_config);
|
|
if ( ret )
|
|
goto error_out;
|
|
|
|
return;
|
|
}
|
|
case LIBXL_DOMAIN_TYPE_PV:
|
|
case LIBXL_DOMAIN_TYPE_PVH:
|
|
{
|
|
libxl__device_console console, vuart;
|
|
libxl__device device;
|
|
|
|
for (i = 0; i < d_config->num_vfbs; i++) {
|
|
libxl__device_add(gc, domid, &libxl__vfb_devtype,
|
|
&d_config->vfbs[i]);
|
|
}
|
|
|
|
if (d_config->b_info.arch_arm.vuart == LIBXL_VUART_TYPE_SBSA_UART) {
|
|
init_console_info(gc, &vuart, 0);
|
|
vuart.backend_domid = state->console_domid;
|
|
libxl__device_vuart_add(gc, domid, &vuart, state);
|
|
libxl__device_console_dispose(&vuart);
|
|
}
|
|
|
|
init_console_info(gc, &console, 0);
|
|
console.backend_domid = state->console_domid;
|
|
libxl__device_console_add(gc, domid, &console, state, &device);
|
|
libxl__device_console_dispose(&console);
|
|
|
|
ret = libxl__need_xenpv_qemu(gc, d_config);
|
|
if (ret < 0)
|
|
goto error_out;
|
|
if (ret) {
|
|
dcs->sdss.dm.guest_domid = domid;
|
|
libxl__spawn_local_dm(egc, &dcs->sdss.dm);
|
|
return;
|
|
} else {
|
|
assert(!dcs->sdss.dm.guest_domid);
|
|
domcreate_devmodel_started(egc, &dcs->sdss.dm, 0);
|
|
return;
|
|
}
|
|
}
|
|
default:
|
|
ret = ERROR_INVAL;
|
|
goto error_out;
|
|
}
|
|
abort(); /* not reached */
|
|
|
|
error_out:
|
|
assert(ret);
|
|
domcreate_complete(egc, dcs, ret);
|
|
}
|
|
|
|
static void libxl__add_dtdevs(libxl__egc *egc, libxl__ao *ao, uint32_t domid,
|
|
libxl_domain_config *d_config,
|
|
libxl__multidev *multidev)
|
|
{
|
|
AO_GC;
|
|
libxl__ao_device *aodev = libxl__multidev_prepare(multidev);
|
|
int i, rc = 0;
|
|
|
|
for (i = 0; i < d_config->num_dtdevs; i++) {
|
|
const libxl_device_dtdev *dtdev = &d_config->dtdevs[i];
|
|
|
|
LOGD(DEBUG, domid, "Assign device \"%s\" to domain", dtdev->path);
|
|
rc = xc_assign_dt_device(CTX->xch, domid, dtdev->path);
|
|
if (rc < 0) {
|
|
LOGD(ERROR, domid, "xc_assign_dtdevice failed: %d", rc);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
aodev->rc = rc;
|
|
aodev->callback(egc, aodev);
|
|
}
|
|
|
|
#define libxl_device_dtdev_list NULL
|
|
#define libxl_device_dtdev_compare NULL
|
|
#define libxl__device_from_dtdev NULL
|
|
#define libxl__device_dtdev_setdefault NULL
|
|
#define libxl__device_dtdev_update_devid NULL
|
|
static DEFINE_DEVICE_TYPE_STRUCT(dtdev, NONE, dtdevs);
|
|
|
|
const libxl__device_type *device_type_tbl[] = {
|
|
&libxl__disk_devtype,
|
|
&libxl__nic_devtype,
|
|
&libxl__vtpm_devtype,
|
|
&libxl__usbctrl_devtype,
|
|
&libxl__usbdev_devtype,
|
|
&libxl__pci_devtype,
|
|
&libxl__dtdev_devtype,
|
|
&libxl__vdispl_devtype,
|
|
&libxl__vsnd_devtype,
|
|
&libxl__virtio_devtype,
|
|
&libxl__p9_devtype,
|
|
NULL
|
|
};
|
|
|
|
static void domcreate_devmodel_started(libxl__egc *egc,
|
|
libxl__dm_spawn_state *dmss,
|
|
int ret)
|
|
{
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(dmss, *dcs, sdss.dm);
|
|
STATE_AO_GC(dmss->spawn.ao);
|
|
int domid = dcs->guest_domid;
|
|
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "device model did not start: %d", ret);
|
|
goto error_out;
|
|
}
|
|
|
|
dcs->device_type_idx = -1;
|
|
domcreate_attach_devices(egc, &dcs->multidev, 0);
|
|
return;
|
|
|
|
error_out:
|
|
assert(ret);
|
|
domcreate_complete(egc, dcs, ret);
|
|
}
|
|
|
|
static void domcreate_attach_devices(libxl__egc *egc,
|
|
libxl__multidev *multidev,
|
|
int ret)
|
|
{
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(multidev, *dcs, multidev);
|
|
STATE_AO_GC(dcs->ao);
|
|
int domid = dcs->guest_domid;
|
|
libxl_domain_config *const d_config = dcs->guest_config;
|
|
const libxl__device_type *dt;
|
|
char *tty_path;
|
|
|
|
if (ret) {
|
|
LOGD(ERROR, domid, "unable to add %s devices",
|
|
libxl__device_kind_to_string(device_type_tbl[dcs->device_type_idx]->type));
|
|
goto error_out;
|
|
}
|
|
|
|
dcs->device_type_idx++;
|
|
dt = device_type_tbl[dcs->device_type_idx];
|
|
if (dt) {
|
|
if (*libxl__device_type_get_num(dt, d_config) > 0 && !dt->skip_attach) {
|
|
/* Attach devices */
|
|
libxl__multidev_begin(ao, &dcs->multidev);
|
|
dcs->multidev.callback = domcreate_attach_devices;
|
|
dt->add(egc, ao, domid, d_config, &dcs->multidev);
|
|
libxl__multidev_prepared(egc, &dcs->multidev, 0);
|
|
return;
|
|
}
|
|
|
|
domcreate_attach_devices(egc, &dcs->multidev, 0);
|
|
return;
|
|
}
|
|
|
|
ret = libxl__console_tty_path(gc, domid, 0, LIBXL_CONSOLE_TYPE_PV, &tty_path);
|
|
if (ret) {
|
|
LOG(ERROR, "failed to get domain %d console tty path",
|
|
domid);
|
|
goto error_out;
|
|
}
|
|
|
|
dcs->console_xswait.ao = ao;
|
|
dcs->console_xswait.what = GCSPRINTF("domain %d console tty", domid);
|
|
dcs->console_xswait.path = tty_path;
|
|
dcs->console_xswait.timeout_ms = LIBXL_INIT_TIMEOUT * 1000;
|
|
dcs->console_xswait.callback = console_xswait_callback;
|
|
ret = libxl__xswait_start(gc, &dcs->console_xswait);
|
|
if (ret) {
|
|
LOG(ERROR, "unable to set up watch for domain %d console tty path",
|
|
domid);
|
|
goto error_out;
|
|
}
|
|
|
|
return;
|
|
|
|
error_out:
|
|
assert(ret);
|
|
domcreate_complete(egc, dcs, ret);
|
|
}
|
|
|
|
static void console_xswait_callback(libxl__egc *egc, libxl__xswait_state *xswa,
|
|
int rc, const char *p)
|
|
{
|
|
EGC_GC;
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(xswa, *dcs, console_xswait);
|
|
|
|
if (rc) {
|
|
if (rc == ERROR_TIMEDOUT)
|
|
LOG(ERROR, "%s: timed out", xswa->what);
|
|
goto out;
|
|
}
|
|
|
|
if (p && p[0] != '\0') {
|
|
domcreate_console_available(egc, dcs);
|
|
goto out;
|
|
}
|
|
|
|
return;
|
|
|
|
out:
|
|
libxl__xswait_stop(gc, xswa);
|
|
domcreate_complete(egc, dcs, rc);
|
|
}
|
|
|
|
static void domcreate_complete(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dcs->ao);
|
|
libxl_domain_config *const d_config = dcs->guest_config;
|
|
libxl_domain_config *d_config_saved = &dcs->guest_config_saved;
|
|
|
|
libxl__xswait_stop(gc, &dcs->console_xswait);
|
|
|
|
libxl__domain_build_state_dispose(&dcs->build_state);
|
|
|
|
if (!rc && d_config->b_info.exec_ssidref)
|
|
rc = xc_flask_relabel_domain(CTX->xch, dcs->guest_domid, d_config->b_info.exec_ssidref);
|
|
|
|
bool retain_domain = !rc || rc == ERROR_ABORTED;
|
|
|
|
if (retain_domain) {
|
|
libxl__flock *lock;
|
|
|
|
/* Note that we hold CTX lock at this point so only need to
|
|
* take data store lock
|
|
*/
|
|
lock = libxl__lock_domain_userdata(gc, dcs->guest_domid);
|
|
if (!lock) {
|
|
rc = ERROR_LOCK_FAIL;
|
|
} else {
|
|
libxl__update_domain_configuration(gc, d_config_saved, d_config);
|
|
int cfg_rc = libxl__set_domain_configuration
|
|
(gc, dcs->guest_domid, d_config_saved);
|
|
if (!rc)
|
|
rc = cfg_rc;
|
|
libxl__unlock_file(lock);
|
|
}
|
|
}
|
|
|
|
libxl_domain_config_dispose(d_config_saved);
|
|
|
|
if (!retain_domain) {
|
|
if (dcs->guest_domid > 0) {
|
|
dcs->dds.ao = ao;
|
|
dcs->dds.domid = dcs->guest_domid;
|
|
dcs->dds.callback = domcreate_destruction_cb;
|
|
libxl__domain_destroy(egc, &dcs->dds);
|
|
return;
|
|
}
|
|
dcs->guest_domid = INVALID_DOMID;
|
|
}
|
|
dcs->callback(egc, dcs, rc, dcs->guest_domid);
|
|
}
|
|
|
|
static void domcreate_destruction_cb(libxl__egc *egc,
|
|
libxl__domain_destroy_state *dds,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dds->ao);
|
|
libxl__domain_create_state *dcs = CONTAINER_OF(dds, *dcs, dds);
|
|
|
|
if (rc)
|
|
LOGD(ERROR, dds->domid, "unable to destroy domain following failed creation");
|
|
|
|
dcs->callback(egc, dcs, ERROR_FAIL, dcs->guest_domid);
|
|
}
|
|
|
|
/*----- application-facing domain creation interface -----*/
|
|
|
|
typedef struct {
|
|
libxl__domain_create_state dcs;
|
|
uint32_t *domid_out;
|
|
} libxl__app_domain_create_state;
|
|
|
|
typedef struct {
|
|
libxl__app_domain_create_state cdcs;
|
|
libxl__domain_destroy_state dds;
|
|
libxl__domain_save_state dss;
|
|
char *toolstack_buf;
|
|
uint32_t toolstack_len;
|
|
} libxl__domain_soft_reset_state;
|
|
|
|
static void domain_create_cb(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs,
|
|
int rc, uint32_t domid);
|
|
|
|
static int do_domain_create(libxl_ctx *ctx, libxl_domain_config *d_config,
|
|
uint32_t *domid, int restore_fd, int send_back_fd,
|
|
const libxl_domain_restore_params *params,
|
|
const libxl_asyncop_how *ao_how,
|
|
const libxl_asyncprogress_how *aop_console_how)
|
|
{
|
|
AO_CREATE(ctx, 0, ao_how);
|
|
libxl__app_domain_create_state *cdcs;
|
|
int rc;
|
|
|
|
GCNEW(cdcs);
|
|
cdcs->dcs.ao = ao;
|
|
cdcs->dcs.guest_config = d_config;
|
|
libxl_domain_config_init(&cdcs->dcs.guest_config_saved);
|
|
libxl_domain_config_copy(ctx, &cdcs->dcs.guest_config_saved, d_config);
|
|
cdcs->dcs.restore_fd = cdcs->dcs.libxc_fd = restore_fd;
|
|
cdcs->dcs.send_back_fd = send_back_fd;
|
|
if (restore_fd >= 0) {
|
|
cdcs->dcs.restore_params = *params;
|
|
rc = libxl__fd_flags_modify_save(gc, cdcs->dcs.restore_fd,
|
|
~(O_NONBLOCK|O_NDELAY), 0,
|
|
&cdcs->dcs.restore_fdfl);
|
|
if (rc < 0) goto out_err;
|
|
}
|
|
cdcs->dcs.callback = domain_create_cb;
|
|
cdcs->dcs.domid = INVALID_DOMID;
|
|
cdcs->dcs.soft_reset = false;
|
|
libxl__xswait_init(&cdcs->dcs.console_xswait);
|
|
|
|
if (cdcs->dcs.restore_params.checkpointed_stream ==
|
|
LIBXL_CHECKPOINTED_STREAM_COLO) {
|
|
cdcs->dcs.colo_proxy_script =
|
|
cdcs->dcs.restore_params.colo_proxy_script;
|
|
cdcs->dcs.crs.cps.is_userspace_proxy =
|
|
libxl_defbool_val(cdcs->dcs.restore_params.userspace_colo_proxy);
|
|
} else {
|
|
cdcs->dcs.colo_proxy_script = NULL;
|
|
cdcs->dcs.crs.cps.is_userspace_proxy = false;
|
|
}
|
|
|
|
libxl__ao_progress_gethow(&cdcs->dcs.aop_console_how, aop_console_how);
|
|
cdcs->domid_out = domid;
|
|
|
|
initiate_domain_create(egc, &cdcs->dcs);
|
|
|
|
return AO_INPROGRESS;
|
|
|
|
out_err:
|
|
return AO_CREATE_FAIL(rc);
|
|
|
|
}
|
|
|
|
static void domain_soft_reset_cb(libxl__egc *egc,
|
|
libxl__domain_destroy_state *dds,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dds->ao);
|
|
libxl__domain_soft_reset_state *srs = CONTAINER_OF(dds, *srs, dds);
|
|
libxl__app_domain_create_state *cdcs = &srs->cdcs;
|
|
char *savefile, *restorefile;
|
|
|
|
if (rc) {
|
|
LOGD(ERROR, dds->domid, "destruction of domain failed.");
|
|
goto error;
|
|
}
|
|
|
|
cdcs->dcs.guest_domid = dds->domid;
|
|
rc = libxl__restore_emulator_xenstore_data(&cdcs->dcs, srs->toolstack_buf,
|
|
srs->toolstack_len);
|
|
if (rc) {
|
|
LOGD(ERROR, dds->domid, "failed to restore toolstack record.");
|
|
goto error;
|
|
}
|
|
|
|
if (cdcs->dcs.guest_config->b_info.type == LIBXL_DOMAIN_TYPE_HVM) {
|
|
savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", dds->domid);
|
|
restorefile = GCSPRINTF(LIBXL_DEVICE_MODEL_RESTORE_FILE".%d",
|
|
dds->domid);
|
|
rc = rename(savefile, restorefile);
|
|
if (rc) {
|
|
LOGD(ERROR, dds->domid, "failed to rename dm save file.");
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
initiate_domain_create(egc, &cdcs->dcs);
|
|
return;
|
|
|
|
error:
|
|
domcreate_complete(egc, &cdcs->dcs, rc);
|
|
}
|
|
|
|
static void soft_reset_dm_suspended(libxl__egc *egc,
|
|
libxl__domain_suspend_state *dsps,
|
|
int rc);
|
|
static int do_domain_soft_reset(libxl_ctx *ctx,
|
|
libxl_domain_config *d_config,
|
|
uint32_t domid,
|
|
const libxl_asyncop_how *ao_how,
|
|
const libxl_asyncprogress_how
|
|
*aop_console_how)
|
|
{
|
|
AO_CREATE(ctx, 0, ao_how);
|
|
libxl__domain_soft_reset_state *srs;
|
|
libxl__app_domain_create_state *cdcs;
|
|
libxl__domain_create_state *dcs;
|
|
libxl__domain_build_state *state;
|
|
libxl__domain_save_state *dss;
|
|
const char *console_tty, *xs_store_mfn, *xs_console_mfn;
|
|
char *dom_path;
|
|
uint32_t domid_out;
|
|
int rc;
|
|
|
|
GCNEW(srs);
|
|
cdcs = &srs->cdcs;
|
|
dcs = &cdcs->dcs;
|
|
state = &dcs->build_state;
|
|
dss = &srs->dss;
|
|
|
|
srs->cdcs.dcs.ao = ao;
|
|
srs->cdcs.dcs.guest_config = d_config;
|
|
libxl_domain_config_init(&srs->cdcs.dcs.guest_config_saved);
|
|
libxl_domain_config_copy(ctx, &srs->cdcs.dcs.guest_config_saved,
|
|
d_config);
|
|
cdcs->dcs.restore_fd = -1;
|
|
cdcs->dcs.domid = domid;
|
|
cdcs->dcs.soft_reset = true;
|
|
cdcs->dcs.callback = domain_create_cb;
|
|
libxl__xswait_init(&cdcs->dcs.console_xswait);
|
|
libxl__ao_progress_gethow(&srs->cdcs.dcs.aop_console_how,
|
|
aop_console_how);
|
|
cdcs->domid_out = &domid_out;
|
|
|
|
state->soft_reset = true;
|
|
|
|
dom_path = libxl__xs_get_dompath(gc, domid);
|
|
if (!dom_path) {
|
|
LOGD(ERROR, domid, "failed to read domain path");
|
|
rc = ERROR_FAIL;
|
|
goto out;
|
|
}
|
|
|
|
rc = libxl__xs_read_checked(gc, XBT_NULL,
|
|
GCSPRINTF("%s/store/ring-ref", dom_path),
|
|
&xs_store_mfn);
|
|
if (rc) {
|
|
LOGD(ERROR, domid, "failed to read store/ring-ref.");
|
|
goto out;
|
|
}
|
|
state->store_mfn = xs_store_mfn ? atol(xs_store_mfn): 0;
|
|
|
|
rc = libxl__xs_read_checked(gc, XBT_NULL,
|
|
GCSPRINTF("%s/console/ring-ref", dom_path),
|
|
&xs_console_mfn);
|
|
if (rc) {
|
|
LOGD(ERROR, domid, "failed to read console/ring-ref.");
|
|
goto out;
|
|
}
|
|
state->console_mfn = xs_console_mfn ? atol(xs_console_mfn): 0;
|
|
|
|
rc = libxl__xs_read_mandatory(gc, XBT_NULL,
|
|
GCSPRINTF("%s/console/tty", dom_path),
|
|
&console_tty);
|
|
if (rc) {
|
|
LOGD(ERROR, domid, "failed to read console/tty.");
|
|
goto out;
|
|
}
|
|
state->console_tty = libxl__strdup(gc, console_tty);
|
|
|
|
dss->ao = ao;
|
|
dss->domid = domid;
|
|
|
|
rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf,
|
|
&srs->toolstack_len);
|
|
if (rc) {
|
|
LOGD(ERROR, domid, "failed to save toolstack record.");
|
|
goto out;
|
|
}
|
|
|
|
dss->dsps.ao = ao;
|
|
dss->dsps.domid = domid;
|
|
dss->dsps.live = false;
|
|
rc = libxl__domain_suspend_init(egc, &dss->dsps, d_config->b_info.type);
|
|
if (rc)
|
|
goto out;
|
|
dss->dsps.callback_device_model_done = soft_reset_dm_suspended;
|
|
libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */
|
|
|
|
return AO_INPROGRESS;
|
|
|
|
out:
|
|
return AO_CREATE_FAIL(rc);
|
|
}
|
|
|
|
static void soft_reset_dm_suspended(libxl__egc *egc,
|
|
libxl__domain_suspend_state *dsps,
|
|
int rc)
|
|
{
|
|
STATE_AO_GC(dsps->ao);
|
|
libxl__domain_soft_reset_state *srs =
|
|
CONTAINER_OF(dsps, *srs, dss.dsps);
|
|
libxl__app_domain_create_state *cdcs = &srs->cdcs;
|
|
|
|
libxl__domain_suspend_dispose(gc, dsps);
|
|
|
|
/*
|
|
* Ask all backends to disconnect by removing the domain from
|
|
* xenstore. On the creation path the domain will be introduced to
|
|
* xenstore again with probably different store/console/...
|
|
* channels.
|
|
*/
|
|
xs_release_domain(CTX->xsh, cdcs->dcs.domid);
|
|
|
|
srs->dds.ao = ao;
|
|
srs->dds.domid = cdcs->dcs.domid;
|
|
srs->dds.callback = domain_soft_reset_cb;
|
|
srs->dds.soft_reset = true;
|
|
libxl__domain_destroy(egc, &srs->dds);
|
|
}
|
|
|
|
static void domain_create_cb(libxl__egc *egc,
|
|
libxl__domain_create_state *dcs,
|
|
int rc, uint32_t domid)
|
|
{
|
|
libxl__app_domain_create_state *cdcs = CONTAINER_OF(dcs, *cdcs, dcs);
|
|
int flrc;
|
|
STATE_AO_GC(cdcs->dcs.ao);
|
|
|
|
*cdcs->domid_out = domid;
|
|
|
|
if (dcs->restore_fd >= 0) {
|
|
flrc = libxl__fd_flags_restore(gc,
|
|
dcs->restore_fd, dcs->restore_fdfl);
|
|
/*
|
|
* If restore has failed already then report that error not
|
|
* this one.
|
|
*/
|
|
if (flrc && !rc) rc = flrc;
|
|
}
|
|
|
|
libxl__ao_complete(egc, ao, rc);
|
|
}
|
|
|
|
|
|
static void set_disk_colo_restore(libxl_domain_config *d_config)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < d_config->num_disks; i++)
|
|
libxl_defbool_set(&d_config->disks[i].colo_restore_enable, true);
|
|
}
|
|
|
|
static void unset_disk_colo_restore(libxl_domain_config *d_config)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < d_config->num_disks; i++)
|
|
libxl_defbool_set(&d_config->disks[i].colo_restore_enable, false);
|
|
}
|
|
|
|
int libxl_domain_create_new(libxl_ctx *ctx, libxl_domain_config *d_config,
|
|
uint32_t *domid,
|
|
const libxl_asyncop_how *ao_how,
|
|
const libxl_asyncprogress_how *aop_console_how)
|
|
{
|
|
unset_disk_colo_restore(d_config);
|
|
return do_domain_create(ctx, d_config, domid, -1, -1, NULL,
|
|
ao_how, aop_console_how);
|
|
}
|
|
|
|
int libxl_domain_create_restore(libxl_ctx *ctx, libxl_domain_config *d_config,
|
|
uint32_t *domid, int restore_fd,
|
|
int send_back_fd,
|
|
const libxl_domain_restore_params *params,
|
|
const libxl_asyncop_how *ao_how,
|
|
const libxl_asyncprogress_how *aop_console_how)
|
|
{
|
|
if (params->checkpointed_stream == LIBXL_CHECKPOINTED_STREAM_COLO) {
|
|
set_disk_colo_restore(d_config);
|
|
} else {
|
|
unset_disk_colo_restore(d_config);
|
|
}
|
|
|
|
/*
|
|
* When restoring (either from a save file or for a migration domain) set
|
|
* the MSR relaxed mode and HVM PIRQs for compatibility with older Xen
|
|
* versions if the options are not set as part of the original
|
|
* configuration.
|
|
*/
|
|
libxl_defbool_setdefault(&d_config->b_info.arch_x86.msr_relaxed, true);
|
|
libxl_defbool_setdefault(&d_config->b_info.u.hvm.pirq, true);
|
|
|
|
return do_domain_create(ctx, d_config, domid, restore_fd, send_back_fd,
|
|
params, ao_how, aop_console_how);
|
|
}
|
|
|
|
int libxl_domain_soft_reset(libxl_ctx *ctx,
|
|
libxl_domain_config *d_config,
|
|
uint32_t domid,
|
|
const libxl_asyncop_how *ao_how,
|
|
const libxl_asyncprogress_how
|
|
*aop_console_how)
|
|
{
|
|
libxl_domain_build_info *const info = &d_config->b_info;
|
|
|
|
if (info->type != LIBXL_DOMAIN_TYPE_HVM) return ERROR_INVAL;
|
|
|
|
return do_domain_soft_reset(ctx, d_config, domid, ao_how,
|
|
aop_console_how);
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|