</varlistentry>
<varlistentry>
- <term><option>--ram=<replaceable>BYTES</replaceable></option></term>
+ <term><option>--ram=<replaceable>BYTES</replaceable>[:<replaceable>MAXBYTES</replaceable>[:<replaceable>SLOTS</replaceable>]]</option></term>
- <listitem><para>The amount of memory to start the virtual machine with.
- Defaults to 2G.</para>
+ <listitem><para>The amount of memory to start the virtual machine with. Defaults to 2G.
+ If a maximum size is specified after a colon, memory hotplug is enabled with the given
+ upper limit. The number of hotplug slots can optionally be specified after a second colon
+ and defaults to 1.</para>
<xi:include href="version-info.xml" xpointer="v255"/>
</listitem>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--cxl=<replaceable>BOOL</replaceable></option></term>
+
+ <listitem><para>Controls whether to enable CXL (Compute Express Link) support in the virtual
+ machine. CXL is a high-speed interconnect standard that allows CPUs to access memory attached to
+ devices such as accelerators and memory expanders, enabling flexible memory pooling and expansion
+ beyond what is physically installed on the motherboard. Only supported on x86_64 and aarch64
+ architectures.</para>
+
+ <xi:include href="version-info.xml" xpointer="v261"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>--vsock=<replaceable>BOOL</replaceable></option></term>
static char **arg_property = NULL;
static char *arg_cpus = NULL;
static uint64_t arg_ram = UINT64_C(2) * U64_GB;
+static uint64_t arg_ram_max = 0;
+static unsigned arg_ram_slots = 0;
static int arg_kvm = -1;
+static bool arg_cxl = false;
static int arg_vsock = -1;
static unsigned arg_vsock_cid = VMADDR_CID_ANY;
static int arg_tpm = -1;
" scsi-cd; default: virtio-blk)\n"
"\n%3$sHost Configuration:%4$s\n"
" --cpus=CPUS Configure number of CPUs in guest\n"
- " --ram=BYTES Configure guest's RAM size\n"
+ " --ram=BYTES[:MAXBYTES[:SLOTS]]\n"
+ " Configure guest's RAM size (and max/slots for\n"
+ " hotplug)\n"
" --kvm=BOOL Enable use of KVM\n"
+ " --cxl=BOOL Enable use of CXL\n"
" --vsock=BOOL Override autodetection of VSOCK support\n"
" --vsock-cid=CID Specify the CID to use for the guest's VSOCK support\n"
" --tpm=BOOL Enable use of a virtual TPM\n"
return 0;
}
+static int parse_ram(const char *s) {
+ _cleanup_free_ char *ram = NULL, *ram_max = NULL, *ram_slots = NULL;
+ int r;
+
+ assert(s);
+
+ const char *p = s;
+ r = extract_many_words(&p, ":", EXTRACT_DONT_COALESCE_SEPARATORS, &ram, &ram_max, &ram_slots);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse --ram=%s", s);
+ if (!isempty(p))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected trailing data in --ram=%s", s);
+
+ r = parse_size(ram, 1024, &arg_ram);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
+
+ if (!isempty(ram_max)) {
+ r = parse_size(ram_max, 1024, &arg_ram_max);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
+ } else
+ arg_ram_max = 0;
+
+ if (!isempty(ram_slots)) {
+ r = safe_atou(ram_slots, &arg_ram_slots);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse --ram=%s: %m", s);
+ } else
+ arg_ram_slots = 0;
+
+ return 0;
+}
+
static int parse_argv(int argc, char *argv[]) {
int r;
ARG_CPUS,
ARG_RAM,
ARG_KVM,
+ ARG_CXL,
ARG_VSOCK,
ARG_VSOCK_CID,
ARG_TPM,
{ "ram", required_argument, NULL, ARG_RAM },
{ "qemu-mem", required_argument, NULL, ARG_RAM }, /* Compat alias */
{ "kvm", required_argument, NULL, ARG_KVM },
+ { "cxl", required_argument, NULL, ARG_CXL },
{ "qemu-kvm", required_argument, NULL, ARG_KVM }, /* Compat alias */
{ "vsock", required_argument, NULL, ARG_VSOCK },
{ "qemu-vsock", required_argument, NULL, ARG_VSOCK }, /* Compat alias */
break;
case ARG_RAM:
- r = parse_size(optarg, 1024, &arg_ram);
+ r = parse_ram(optarg);
if (r < 0)
- return log_error_errno(r, "Failed to parse --ram=%s: %m", optarg);
+ return r;
break;
case ARG_KVM:
return r;
break;
+ case ARG_CXL:
+ r = parse_boolean_argument("--cxl=", optarg, &arg_cxl);
+ if (r < 0)
+ return r;
+ if (arg_cxl && !ARCHITECTURE_SUPPORTS_CXL)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "CXL not supported on %s.", architecture_to_string(native_architecture()));
+ break;
+
case ARG_VSOCK:
r = parse_tristate_argument_with_auto("--vsock=", optarg, &arg_vsock);
if (r < 0)
if (!strv_isempty(arg_bind_user_groups) && strv_isempty(arg_bind_user))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-group= without --bind-user=");
+ if (arg_ram_max > 0 && arg_ram_max < arg_ram)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Maximum RAM size must be greater than or equal to initial RAM size");
+
+ if (arg_ram_slots > 0 && arg_ram_max == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Memory hotplug slots require a maximum RAM size");
+
if (arg_ephemeral && arg_extra_drives.n_drives > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --ephemeral with --extra-drive=");
return r;
}
+ if (arg_cxl) {
+ r = qemu_config_key(config_file, "cxl", "on");
+ if (r < 0)
+ return r;
+ }
+
if (arg_directory || arg_runtime_mounts.n_mounts != 0) {
r = qemu_config_key(config_file, "memory-backend", "mem");
if (r < 0)
if (r < 0)
return r;
+ if (arg_ram_max > 0) {
+ r = qemu_config_keyf(config_file, "maxmem", "%" PRIu64 "M", DIV_ROUND_UP(arg_ram_max, U64_MB));
+ if (r < 0)
+ return r;
+
+ r = qemu_config_keyf(config_file, "slots", "%u", arg_ram_slots > 0 ? arg_ram_slots : 1u);
+ if (r < 0)
+ return r;
+ }
+
r = qemu_config_section(config_file, "object", "rng0",
"qom-type", "rng-random",
"filename", "/dev/urandom");