Refactor OSDK and Asterinas Docker build systems

This commit is contained in:
Hsy-Intel
2025-04-30 22:14:32 +08:00
committed by Tate, Hongliang Tian
parent 149c00f5fc
commit a14d5a5017
11 changed files with 269 additions and 301 deletions

View File

@ -1 +0,0 @@
**/Dockerfile

View File

@ -0,0 +1,172 @@
# SPDX-License-Identifier: MPL-2.0
FROM ubuntu:22.04 AS build-base
ARG DEBIAN_FRONTEND=noninteractive
RUN apt update && apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
git \
python3-pip \
python-is-python3 \
wget
RUN pip3 install iq tomli
#= Build QEMU =================================================================
FROM build-base AS build-qemu
RUN apt update && apt-get install -y --no-install-recommends \
libgcrypt-dev `# optional build dependency` \
libglib2.0-dev `# build dependency` \
libpixman-1-dev `# build dependency` \
libusb-dev `# optional build dependency` \
meson \
ninja-build
RUN apt clean && rm -rf /var/lib/apt/lists/*
FROM build-qemu AS qemu
# Fetch and install QEMU from the official source
#
# The QEMU version in the Ubuntu 22.04 repository is 6.*, which has a bug to cause OVMF debug to fail.
# The libslirp dependency is for QEMU's network backend.
WORKDIR /root
RUN wget -O qemu.tar.xz https://download.qemu.org/qemu-9.1.0.tar.xz \
&& mkdir /root/qemu \
&& tar xf qemu.tar.xz --strip-components=1 -C /root/qemu \
&& rm qemu.tar.xz
WORKDIR /root/qemu
RUN ./configure --target-list=x86_64-softmmu --prefix=/usr/local/qemu --enable-slirp \
&& make -j \
&& make install
WORKDIR /root
RUN rm -rf /root/qemu
#= Build OVMF =================================================================
FROM build-base AS build-ovmf
RUN apt update && apt-get install -y --no-install-recommends \
bison \
flex \
iasl \
nasm \
uuid-dev
RUN apt clean && rm -rf /var/lib/apt/lists/*
RUN git --version
FROM build-ovmf AS ovmf
# Fetch and build OVMF from the EDK2 official source
WORKDIR /root
RUN git clone --depth 1 --branch stable/202408 --recurse-submodules --shallow-submodules https://github.com/tianocore/edk2.git
WORKDIR /root/edk2
RUN /bin/bash -c "source ./edksetup.sh \
&& make -C BaseTools \
&& build -a X64 -t GCC5 -b DEBUG -p OvmfPkg/OvmfPkgX64.dsc -D DEBUG_ON_SERIAL_PORT \
&& build -a X64 -t GCC5 -b RELEASE -p OvmfPkg/OvmfPkgX64.dsc"
#= Build GRUB =================================================================
FROM build-base AS build-grub
RUN apt update && apt-get install -y --no-install-recommends \
autoconf \
automake \
autopoint \
bison \
flex \
gawk \
gettext \
libfreetype6-dev \
pkg-config
RUN apt clean && rm -rf /var/lib/apt/lists/*
FROM build-grub AS grub
# Fetch and install GRUB from the GNU official source
#
# We have installed grub-efi-amd64-bin just for the unicode.pf2 file, which is not included
# in the GRUB release. The Ubuntu release notoriously modifies the GRUB source code and enforce
# EFI handover boot, which is deprecated. So we have to build GRUB from source.
WORKDIR /root
# See also: https://github.com/asterinas/asterinas/pull/1710
RUN git clone --single-branch -b asterinas/2.12 https://github.com/asterinas/grub.git \
&& git -C grub checkout 0633bc8
# Fetch and install the Unicode font data for grub.
RUN wget -O unifont.pcf.gz https://unifoundry.com/pub/unifont/unifont-15.1.04/font-builds/unifont-15.1.04.pcf.gz \
&& mkdir -pv /usr/share/fonts/unifont \
&& gunzip -c unifont.pcf.gz > /usr/share/fonts/unifont/unifont.pcf \
&& rm unifont.pcf.gz
WORKDIR /root/grub
RUN echo depends bli part_gpt > grub-core/extra_deps.lst \
&& ./bootstrap \
&& ./configure \
--target=x86_64 \
--disable-efiemu \
--with-platform=efi \
--enable-grub-mkfont \
--prefix=/usr/local/grub \
--disable-werror \
&& make -j \
&& make install
WORKDIR /root
RUN rm -rf /root/grub
#= The final stages to produce the OSDK development image ====================
FROM build-base AS rust
# Install all OSDK dependent packages
RUN apt update \
&& apt install -y \
build-essential \
curl \
gdb \
grub-efi-amd64 \
grub2-common \
libpixman-1-dev `# running dependency for QEMU` \
mtools `# used by grub-mkrescue` \
xorriso \
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
# Install Rust with both nightly and stable
ENV PATH="/root/.cargo/bin:${PATH}"
ARG ASTER_RUST_VERSION
RUN curl https://sh.rustup.rs -sSf | \
sh -s -- --default-toolchain ${ASTER_RUST_VERSION} -y \
&& rustup toolchain install stable \
&& rm -rf /root/.cargo/registry && rm -rf /root/.cargo/git \
&& cargo -V \
&& rustup component add rust-src rustc-dev llvm-tools-preview
# Install cargo tools
RUN cargo install \
cargo-binutils \
mdbook \
typos-cli
# Install QEMU built from the previous stages
COPY --from=qemu /usr/local/qemu /usr/local/qemu
ENV PATH="/usr/local/qemu/bin:${PATH}"
ENV LD_LIBRARY_PATH="/usr/local/qemu/lib/x86_64-linux-gnu:${LD_LIBRARY_PATH}"
# Install OVMF built from the previous stages
COPY --from=ovmf /root/edk2/Build/OvmfX64/DEBUG_GCC5/FV/ /root/ovmf/debug
COPY --from=ovmf /root/edk2/Build/OvmfX64/RELEASE_GCC5/FV/ /root/ovmf/release
# Install GRUB built from the previous stages
COPY --from=grub /usr/local/grub /usr/local/grub
ENV PATH="/usr/local/grub/bin:${PATH}"
# Make a symbolic link for `unicode.pf2` from Ubuntu 22.04 package
RUN ln -sf /usr/share/grub/unicode.pf2 /usr/local/grub/share/grub/unicode.pf2
VOLUME [ "/root/asterinas" ]
WORKDIR /root/asterinas

View File

@ -1,36 +0,0 @@
# SPDX-License-Identifier: MPL-2.0
FROM {% base_image %}
ARG DEBIAN_FRONTEND=noninteractive
RUN apt update \
&& apt install -y \
build-essential \
curl \
gdb \
grub-efi-amd64 \
grub2-common \
libpixman-1-dev `# running dependency for QEMU` \
mtools `# used by grub-mkrescue` \
xorriso \
{% qemu_ovmf_installation %} \
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
# Install Rust of both nightly and stable channel
ENV PATH="/root/.cargo/bin:${PATH}"
ARG ASTER_RUST_VERSION
RUN curl https://sh.rustup.rs -sSf | \
sh -s -- --default-toolchain ${ASTER_RUST_VERSION} -y \
&& rustup toolchain install stable \
&& rm -rf /root/.cargo/registry && rm -rf /root/.cargo/git \
&& cargo -V \
&& rustup component add rust-src rustc-dev llvm-tools-preview
# Install cargo-binutils
RUN cargo install cargo-binutils
VOLUME [ "/root/asterinas" ]
WORKDIR /root/asterinas

View File

@ -0,0 +1,40 @@
# OSDK Development Docker Images
The OSDK development Docker images provide the development environment for using and developing OSDK.
## Building Docker Images
To build an OSDK development Docker image and test it on your local machine, navigate to the root directory of the Asterinas source code tree and execute the following command:
```bash
cd <asterinas dir>
# Build Docker image
docker buildx build \
-f osdk/tools/docker/Dockerfile \
--build-arg ASTER_RUST_VERSION=$(grep "channel" rust-toolchain.toml | awk -F '"' '{print $2}') \
-t asterinas/osdk:$(cat DOCKER_IMAGE_VERSION) \
.
```
Intel TDX has some special requirements on the development environment such as QEMU.
So we offer a TDX-specific version of the OSDK development Docker image.
You need to build the general-purpose Docker image before building the TDX-specific one
as the former is used by the latter one as the base image.
```bash
cd <asterinas dir>
# Build Intel TDX Docker image
docker buildx build \
-f osdk/tools/docker/tdx/Dockerfile \
--build-arg ASTER_RUST_VERSION=$(grep "channel" rust-toolchain.toml | awk -F '"' '{print $2}') \
--build-arg BASE_VERSION=$(cat DOCKER_IMAGE_VERSION) \
-t asterinas/osdk:$(cat DOCKER_IMAGE_VERSION)-tdx \
.
```
## Tagging and Uploading Docker Images
The Docker images are tagged according to the version specified
in the `DOCKER_IMAGE_VERSION` file at the project root.
Check out the [version bump](https://asterinas.github.io/book/to-contribute/version-bump.html) documentation
on how new versions of the Docker images are released.

View File

@ -1,25 +0,0 @@
#!/bin/bash
# SPDX-License-Identifier: MPL-2.0
set -e
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
ASTER_ROOT_DIR=${SCRIPT_DIR}/../../..
ASTER_RUST_VERSION=$( grep -m1 -o 'nightly-[0-9]\+-[0-9]\+-[0-9]\+' ${ASTER_ROOT_DIR}/rust-toolchain.toml )
VERSION=$( cat ${ASTER_ROOT_DIR}/VERSION )
DOCKERFILE=${SCRIPT_DIR}/Dockerfile
if [ "$1" = "intel-tdx" ]; then
IMAGE_NAME="asterinas/osdk:${VERSION}-tdx"
python3 gen_dockerfile.py --intel-tdx
else
IMAGE_NAME="asterinas/osdk:${VERSION}"
python3 gen_dockerfile.py
fi
docker build \
-t ${IMAGE_NAME} \
--build-arg ASTER_RUST_VERSION=${ASTER_RUST_VERSION} \
-f ${DOCKERFILE} \
${SCRIPT_DIR}

View File

@ -1,78 +0,0 @@
# SPDX-License-Identifier: MPL-2.0
import re
import argparse
import os
import sys
import logging
# Setup logging
logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
def parse_arguments():
parser = argparse.ArgumentParser(description='The Dockerfile generator for OSDK.')
parser.add_argument('--intel-tdx', action='store_true', help='Include Intel TDX support')
parser.add_argument(
'--out-dir',
type=str,
default='.',
help='Output the Dockerfile under this directory. \
By default, the output directory is the current working directory.'
)
return parser.parse_args()
def validate_out_dir(out_dir):
if os.path.isabs(out_dir):
print("Error: The --out-dir argument must be a relative path.")
sys.exit(1)
def setup_output_directory(out_dir):
template_dir = os.path.dirname(os.path.abspath(__file__))
if out_dir == '.':
return template_dir
output_directory_path = os.path.join(template_dir, out_dir)
if not os.path.exists(output_directory_path):
os.makedirs(output_directory_path)
return output_directory_path
def load_template(template_dir):
template_file = os.path.join(template_dir, 'Dockerfile.template')
if not os.path.isfile(template_file):
logging.error(f"Template file {template_file} does not exist.")
sys.exit(1)
with open(template_file, 'r') as file:
return file.read()
def generate_dockerfile_content(variables, template_content):
for var_name, var_value in variables.items():
template_content = re.sub(r'{%\s*' + var_name + r'\s*%}', var_value, template_content)
return template_content
def write_dockerfile(output_directory, content):
output_path = os.path.join(output_directory, 'Dockerfile')
with open(output_path, 'w') as file:
file.write(content)
logging.info(f'Dockerfile has been generated at {output_path}.')
def main():
args = parse_arguments()
validate_out_dir(args.out_dir)
variables = {
'base_image': r'ubuntu:22.04',
'qemu_ovmf_installation': r"""ovmf \
qemu-system-x86""",
}
if args.intel_tdx:
variables['base_image'] = r'intelcczoo/tdvm:ubuntu22.04-mvp_2023ww15'
variables['qemu_ovmf_installation'] = r''
template_dir = os.path.dirname(os.path.abspath(__file__))
output_directory = setup_output_directory(args.out_dir)
template_content = load_template(template_dir)
dockerfile_content = generate_dockerfile_content(variables, template_content)
write_dockerfile(output_directory, dockerfile_content)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,38 @@
# SPDX-License-Identifier: MPL-2.0
ARG BASE_VERSION
FROM asterinas/osdk:${BASE_VERSION} AS build-base
# Fetch and install QEMU from the intel-staging/qemu-tdx source
FROM build-base AS build-qemu-tdx
RUN apt update && apt-get install -y --no-install-recommends \
libgcrypt-dev `# optional build dependency` \
libglib2.0-dev `# build dependency` \
libpixman-1-dev `# build dependency` \
libusb-dev `# optional build dependency` \
meson \
ninja-build
RUN apt clean && rm -rf /var/lib/apt/lists/*
FROM build-qemu-tdx AS qemu-tdx
WORKDIR /root
RUN git clone -b tdx-qemu-upstream-2024.02.29-v8.2.0 https://github.com/intel-staging/qemu-tdx.git
WORKDIR /root/qemu-tdx
COPY osdk/tools/docker/tdx/tdx_qemu.patch /root/qemu-tdx
RUN git apply tdx_qemu.patch \
&& mkdir build \
&& cd build \
&& ../configure --enable-kvm --target-list=x86_64-softmmu --prefix=/usr/local/qemu --enable-slirp \
&& make -j \
&& make install
WORKDIR /root
RUN rm -rf /root/qemu-tdx
FROM build-base
# Install QEMU built from the previous stages
COPY --from=qemu-tdx /usr/local/qemu /usr/local/qemu
WORKDIR /root/asterinas

View File

@ -0,0 +1,251 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2025 Intel Corporation
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index 2c83b6d270..b9aebe1ea6 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -2983,6 +2983,8 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
rb = qemu_ram_block_from_host(addr, false, &offset);
+ memory_region_convert_mem_attr(&section, !to_private);
+
if (to_private) {
if (rb->page_size != qemu_host_page_size) {
/*
diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c
index 745ead0034..6cef1b5ff2 100644
--- a/backends/hostmem-memfd.c
+++ b/backends/hostmem-memfd.c
@@ -56,6 +56,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
ram_flags = backend->share ? RAM_SHARED : 0;
ram_flags |= backend->reserve ? 0 : RAM_NORESERVE;
ram_flags |= backend->guest_memfd ? RAM_GUEST_MEMFD : 0;
+ ram_flags |= m->hugetlb ? RAM_GUEST_MEMFD_HUGETLB : 0;
return memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), name,
backend->size, ram_flags, fd, 0, errp);
}
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 059bfdc07a..d3f7cc93e7 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -251,6 +251,7 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section)
return (!memory_region_is_ram(section->mr) &&
!memory_region_is_iommu(section->mr)) ||
memory_region_is_protected(section->mr) ||
+ memory_region_has_guest_memfd(section->mr) ||
/*
* Sizing an enabled 64-bit BAR can cause spurious mappings to
* addresses in the upper part of the 64-bit address space. These
@@ -347,12 +348,9 @@ out:
rcu_read_unlock();
}
-static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl,
- MemoryRegionSection *section)
+static void vfio_notify_discard_generic(VFIOContainerBase *bcontainer,
+ MemoryRegionSection *section)
{
- VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener,
- listener);
- VFIOContainerBase *bcontainer = vrdl->bcontainer;
const hwaddr size = int128_get64(section->size);
const hwaddr iova = section->offset_within_address_space;
int ret;
@@ -365,12 +363,10 @@ static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl,
}
}
-static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
- MemoryRegionSection *section)
+static int vfio_notify_populate_generic(VFIOContainerBase *bcontainer,
+ MemoryRegionSection *section,
+ uint64_t granularity)
{
- VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener,
- listener);
- VFIOContainerBase *bcontainer = vrdl->bcontainer;
const hwaddr end = section->offset_within_region +
int128_get64(section->size);
hwaddr start, next, iova;
@@ -382,7 +378,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
* unmap in minimum granularity later.
*/
for (start = section->offset_within_region; start < end; start = next) {
- next = ROUND_UP(start + 1, vrdl->granularity);
+ next = ROUND_UP(start + 1, granularity);
next = MIN(next, end);
iova = start - section->offset_within_region +
@@ -393,13 +389,31 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
vaddr, section->readonly);
if (ret) {
/* Rollback */
- vfio_ram_discard_notify_discard(rdl, section);
+ vfio_notify_discard_generic(bcontainer, section);
return ret;
}
}
return 0;
}
+static void vfio_ram_discard_notify_discard(RamDiscardListener *rdl,
+ MemoryRegionSection *section)
+{
+ VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener,
+ listener);
+
+ vfio_notify_discard_generic(vrdl->bcontainer, section);
+}
+
+static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl,
+ MemoryRegionSection *section)
+{
+ VFIORamDiscardListener *vrdl = container_of(rdl, VFIORamDiscardListener,
+ listener);
+
+ return vfio_notify_populate_generic(vrdl->bcontainer, section, vrdl->granularity);
+}
+
static void vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer,
MemoryRegionSection *section)
{
@@ -1353,6 +1367,19 @@ static void vfio_listener_log_sync(MemoryListener *listener,
}
}
+static void vfio_listener_convert_mem_attr(MemoryListener *listener,
+ MemoryRegionSection *section,
+ bool shared)
+{
+ VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, listener);
+
+ if (shared)
+ vfio_notify_populate_generic(bcontainer, section,
+ 1ULL << (63 - clz64(bcontainer->pgsizes)));
+ else
+ vfio_notify_discard_generic(bcontainer, section);
+}
+
const MemoryListener vfio_memory_listener = {
.name = "vfio",
.region_add = vfio_listener_region_add,
@@ -1360,6 +1387,7 @@ const MemoryListener vfio_memory_listener = {
.log_global_start = vfio_listener_log_global_start,
.log_global_stop = vfio_listener_log_global_stop,
.log_sync = vfio_listener_log_sync,
+ .convert_mem_attr = vfio_listener_convert_mem_attr,
};
void vfio_reset_handler(void *opaque)
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 1e351f6fc8..d17acdb2ea 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -246,6 +246,9 @@ typedef struct IOMMUTLBEvent {
/* RAM can be private that has kvm guest memfd backend */
#define RAM_GUEST_MEMFD (1 << 12)
+/* Hugetlb can be private that has kvm guest memfd backend */
+#define RAM_GUEST_MEMFD_HUGETLB (1 << 13)
+
static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn,
IOMMUNotifierFlag flags,
hwaddr start, hwaddr end,
@@ -1086,6 +1089,19 @@ struct MemoryListener {
*/
void (*coalesced_io_del)(MemoryListener *listener, MemoryRegionSection *section,
hwaddr addr, hwaddr len);
+
+ /**
+ * @convert_mem_attr:
+ *
+ * Called during the memory attribute conversion.
+ *
+ * @listener: The #MemoryListener
+ * @section: The MemoryRegionSection
+ * @shared: convert memory attribute from private to shared
+ */
+ void (*convert_mem_attr)(MemoryListener *listener, MemoryRegionSection *section,
+ bool shared);
+
/**
* @priority:
*
@@ -2541,6 +2557,14 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr,
*/
void memory_global_dirty_log_sync(bool last_stage);
+/**
+ * memory_region_convert_mem_attr: convert the memory attribute
+ * @section: the #MemoryRegionSection to be converted
+ * @shared: if true, convert attribute from private to shared;
+ * if false, convert from shared to private
+ */
+void memory_region_convert_mem_attr(MemoryRegionSection *section, bool shared);
+
/**
* memory_global_dirty_log_sync: synchronize the dirty log for all memory
*
diff --git a/system/memory.c b/system/memory.c
index 85a22408e9..e9a94e1654 100644
--- a/system/memory.c
+++ b/system/memory.c
@@ -3009,6 +3009,21 @@ void memory_global_dirty_log_stop(unsigned int flags)
memory_global_dirty_log_do_stop(flags);
}
+void memory_region_convert_mem_attr(MemoryRegionSection *section, bool shared)
+{
+ MemoryListener *listener;
+ if (!section->mr || !memory_region_has_guest_memfd(section->mr)) {
+ return;
+ }
+
+ QTAILQ_FOREACH(listener, &memory_listeners, link) {
+ if (!listener->convert_mem_attr) {
+ continue;
+ }
+ listener->convert_mem_attr(listener, section, shared);
+ }
+}
+
static void listener_add_address_space(MemoryListener *listener,
AddressSpace *as)
{
diff --git a/system/physmem.c b/system/physmem.c
index 8c9368bc99..688f76e425 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -1803,6 +1803,10 @@ static void dirty_memory_extend(ram_addr_t old_ram_size,
}
}
+#ifdef CONFIG_KVM
+#define KVM_GUEST_MEMFD_HUGETLB (1 << 1)
+#endif
+
static void ram_block_add(RAMBlock *new_block, Error **errp)
{
const bool noreserve = qemu_ram_is_noreserve(new_block);
@@ -1844,8 +1848,8 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
if (kvm_enabled() && (new_block->flags & RAM_GUEST_MEMFD)) {
assert(new_block->guest_memfd < 0);
- new_block->guest_memfd = kvm_create_guest_memfd(new_block->max_length,
- 0, errp);
+ new_block->guest_memfd = kvm_create_guest_memfd(new_block->max_length,
+ (new_block->flags & RAM_GUEST_MEMFD_HUGETLB) ? KVM_GUEST_MEMFD_HUGETLB : 0, errp);
if (new_block->guest_memfd < 0) {
qemu_mutex_unlock_ramlist();
return;
@@ -1914,7 +1918,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
/* Just support these ram flags by now. */
assert((ram_flags & ~(RAM_SHARED | RAM_PMEM | RAM_NORESERVE |
RAM_PROTECTED | RAM_NAMED_FILE | RAM_READONLY |
- RAM_READONLY_FD | RAM_GUEST_MEMFD)) == 0);
+ RAM_READONLY_FD | RAM_GUEST_MEMFD |RAM_GUEST_MEMFD_HUGETLB)) == 0);
if (xen_enabled()) {
error_setg(errp, "-mem-path not supported with Xen");