diff --git a/NEWS b/NEWS
index 240c12e9cf..0c0212a2ec 100644
--- a/NEWS
+++ b/NEWS
@@ -603,6 +603,12 @@ CHANGES WITH 256-rc1:
--ssh-key-type= to optionally set up transient SSH keys to pass to the
invoked VMs in order to be able to SSH into them once booted.
+ * systemd-vmspawn will no enable various "HyperV enlightenments" and
+ the "VM Generation ID" on the VMs.
+
+ * A new environment variable $SYSTEMD_VMSPAWN_QEMU_EXTRA may carry
+ additional qemu command line options to pass to qemu.
+
systemd-repart:
* systemd-repart gained new options --generate-fstab= and
@@ -638,6 +644,10 @@ CHANGES WITH 256-rc1:
sd_journal_stream_fd() but creates a log stream targeted at a
specific log namespace.
+ * The sd-id128 API gained a new API call
+ sd_id128_get_invocation_app_specific() for acquiring an app-specific
+ ID that is derived from the service invocation ID.
+
systemd-cryptsetup/systemd-cryptenroll:
* systemd-cryptenroll can now enroll directly with a PKCS11 public key
diff --git a/TODO b/TODO
index 830d27a716..548a7c31fc 100644
--- a/TODO
+++ b/TODO
@@ -329,10 +329,7 @@ Features:
PCRs.
* vmspawn:
- - enable hyperv extension by default (https://www.qemu.org/docs/master/system/i386/hyperv.html)
- - register with machined
- run in scope unit when invoked from command line, and machined registration is off
- - support --directory= via virtiofs
- sd_notify support
- --ephemeral support
- --read-only support
diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md
index 961601c72e..8068d0d33c 100644
--- a/docs/ENVIRONMENT.md
+++ b/docs/ENVIRONMENT.md
@@ -191,6 +191,9 @@ All tools:
expected format is six groups of two hexadecimal digits separated by colons,
e.g. `SYSTEMD_VMSPAWN_NETWORK_MAC=12:34:56:78:90:AB`
+* `$SYSTEMD_VMSPAWN_QEMU_EXTRA=…` – may contain additional command line
+ arguments to append the qemu command line.
+
`systemd-logind`:
* `$SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1` — if set, report that
diff --git a/man/sd_id128_get_machine.xml b/man/sd_id128_get_machine.xml
index 6904f2953c..59f3266e6f 100644
--- a/man/sd_id128_get_machine.xml
+++ b/man/sd_id128_get_machine.xml
@@ -63,6 +63,12 @@
sd_id128_t *ret
+
+ int sd_id128_get_invocation_app_specific
+ sd_id128_t app_id
+ sd_id128_t *ret
+
+
@@ -126,12 +132,16 @@
for details. The ID is cached internally. In future a different mechanism to determine the invocation ID
may be added.
+ sd_id128_get_invocation_app_specific() derives an application-specific ID from
+ the invocation ID.
+
Note that sd_id128_get_machine_app_specific(),
- sd_id128_get_boot(), sd_id128_get_boot_app_specific(), and
- sd_id128_get_invocation() always return UUID Variant 1 Version 4 compatible IDs.
- sd_id128_get_machine() will also return a UUID Variant 1 Version 4 compatible ID on
- new installations but might not on older. It is possible to convert the machine ID non-reversibly into a
- UUID Variant 1 Version 4 compatible one. For more information, see
+ sd_id128_get_boot(), sd_id128_get_boot_app_specific(),
+ sd_id128_get_invocation() and
+ sd_id128_get_invocation_app_specific always return UUID Variant 1 Version 4
+ compatible IDs. sd_id128_get_machine() will also return a UUID Variant 1 Version 4
+ compatible ID on new installations but might not on older. It is possible to convert the machine ID
+ non-reversibly into a UUID Variant 1 Version 4 compatible one. For more information, see
machine-id5. It is
hence guaranteed that these functions will never return the ID consisting of all zero or all one bits
(SD_ID128_NULL, SD_ID128_ALLF) — with the possible exception of
@@ -262,6 +272,7 @@ As man:sd-id128(3) macro:
sd_id128_get_machine_app_specific() was added in version 233.
sd_id128_get_boot_app_specific() was added in version 240.
sd_id128_get_app_specific() was added in version 255.
+ sd_id128_get_invocation_app_specific() was added in version 256.
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 7e2c0781fe..09703506f9 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -447,7 +447,7 @@ static Virtualization detect_vm_zvm(void) {
/* Returns a short identifier for the various VM implementations */
Virtualization detect_vm(void) {
static thread_local Virtualization cached_found = _VIRTUALIZATION_INVALID;
- bool other = false;
+ bool other = false, hyperv = false;
int xen_dom0 = 0;
Virtualization v, dmi;
@@ -504,7 +504,12 @@ Virtualization detect_vm(void) {
v = detect_vm_cpuid();
if (v < 0)
return v;
- if (v == VIRTUALIZATION_VM_OTHER)
+ if (v == VIRTUALIZATION_MICROSOFT)
+ /* QEMU sets the CPUID string to hyperv's, in case it provides hyperv enlightenments. Let's
+ * hence not return Microsoft here but just use the other mechanisms first to make a better
+ * decision. */
+ hyperv = true;
+ else if (v == VIRTUALIZATION_VM_OTHER)
other = true;
else if (v != VIRTUALIZATION_NONE)
goto finish;
@@ -545,8 +550,15 @@ Virtualization detect_vm(void) {
return v;
finish:
- if (v == VIRTUALIZATION_NONE && other)
- v = VIRTUALIZATION_VM_OTHER;
+ /* None of the checks above gave us a clear answer, hence let's now use fallback logic: if hyperv
+ * enlightenments are available but the VMM wasn't recognized as anything yet, it's probably
+ * Microsoft. */
+ if (v == VIRTUALIZATION_NONE) {
+ if (hyperv)
+ v = VIRTUALIZATION_MICROSOFT;
+ else if (other)
+ v = VIRTUALIZATION_VM_OTHER;
+ }
cached_found = v;
log_debug("Found VM virtualization %s", virtualization_to_string(v));
diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym
index 89de4b37ca..78b4453462 100644
--- a/src/libsystemd/libsystemd.sym
+++ b/src/libsystemd/libsystemd.sym
@@ -839,6 +839,7 @@ LIBSYSTEMD_256 {
global:
sd_bus_creds_get_pidfd_dup;
sd_bus_creds_new_from_pidfd;
+ sd_id128_get_invocation_app_specific;
sd_journal_stream_fd_with_namespace;
sd_event_source_get_inotify_path;
} LIBSYSTEMD_255;
diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c
index 4336d3f1b7..62b8aaa347 100644
--- a/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libsystemd/sd-id128/sd-id128.c
@@ -390,3 +390,16 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
return sd_id128_get_app_specific(id, app_id, ret);
}
+
+_public_ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+ sd_id128_t id;
+ int r;
+
+ assert_return(ret, -EINVAL);
+
+ r = sd_id128_get_invocation(&id);
+ if (r < 0)
+ return r;
+
+ return sd_id128_get_app_specific(id, app_id, ret);
+}
diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h
index a984a9d85e..a9210526b6 100644
--- a/src/systemd/sd-id128.h
+++ b/src/systemd/sd-id128.h
@@ -53,6 +53,7 @@ int sd_id128_get_invocation(sd_id128_t *ret);
int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
+int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret);
#define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
{ .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
diff --git a/src/test/test-id128.c b/src/test/test-id128.c
index 3ddbeec0fc..48fdbba6c7 100644
--- a/src/test/test-id128.c
+++ b/src/test/test-id128.c
@@ -199,7 +199,7 @@ TEST(id128) {
}
TEST(sd_id128_get_invocation) {
- sd_id128_t id;
+ sd_id128_t id = SD_ID128_NULL;
int r;
/* Query the invocation ID */
@@ -208,6 +208,36 @@ TEST(sd_id128_get_invocation) {
log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
else
log_info("Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
+
+ sd_id128_t appid = SD_ID128_NULL;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,04), &appid);
+ if (r < 0)
+ log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+ else {
+ assert(!sd_id128_equal(id, appid));
+ log_info("Per-App Invocation ID: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid));
+ }
+
+ sd_id128_t appid2 = SD_ID128_NULL;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,05), &appid2); /* slightly different appid */
+ if (r < 0)
+ log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+ else {
+ assert(!sd_id128_equal(id, appid2));
+ assert(!sd_id128_equal(appid, appid2));
+ log_info("Per-App Invocation ID 2: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid2));
+ }
+
+ sd_id128_t appid3 = SD_ID128_NULL;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(59,36,e9,92,fd,11,42,fe,87,c9,e9,b5,6c,9e,4f,04), &appid3); /* same appid as before */
+ if (r < 0)
+ log_warning_errno(r, "Failed to get invocation ID, ignoring: %m");
+ else {
+ assert(!sd_id128_equal(id, appid3));
+ assert(sd_id128_equal(appid, appid3));
+ assert(!sd_id128_equal(appid2, appid3));
+ log_info("Per-App Invocation ID 3: " SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(appid3));
+ }
}
TEST(benchmark_sd_id128_get_machine_app_specific) {
diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c
index d3d37fd5d3..9366ce111d 100644
--- a/src/vmspawn/vmspawn.c
+++ b/src/vmspawn/vmspawn.c
@@ -1294,6 +1294,24 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
if (strv_extend_many(&cmdline, "-uuid", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
return log_oom();
+ /* Derive a vmgenid automatically from the invocation ID, in a deterministic way. */
+ sd_id128_t vmgenid;
+ r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(bd,84,6d,e3,e4,7d,4b,6c,a6,85,4a,87,0f,3c,a3,a0), &vmgenid);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to get invocation ID, making up randomized vmgenid: %m");
+
+ r = sd_id128_randomize(&vmgenid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to make up randomized vmgenid: %m");
+ }
+
+ _cleanup_free_ char *vmgenid_device = NULL;
+ if (asprintf(&vmgenid_device, "vmgenid,guid=" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(vmgenid)) < 0)
+ return log_oom();
+
+ if (strv_extend_many(&cmdline, "-device", vmgenid_device) < 0)
+ return log_oom();
+
/* if we are going to be starting any units with state then create our runtime dir */
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0) {
r = runtime_directory(&arg_runtime_directory, arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn");
@@ -1421,7 +1439,13 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
pass_fds[n_pass_fds++] = device_fd;
}
- r = strv_extend_many(&cmdline, "-cpu", "max");
+ r = strv_extend_many(&cmdline, "-cpu",
+#ifdef __x86_64__
+ "max,hv_relaxed,hv-vapic,hv-time"
+#else
+ "max"
+#endif
+ );
if (r < 0)
return log_oom();
@@ -1875,6 +1899,18 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
return log_error_errno(r, "Failed to call getsockname on VSOCK: %m");
}
+ const char *e = secure_getenv("SYSTEMD_VMSPAWN_QEMU_EXTRA");
+ if (e) {
+ _cleanup_strv_free_ char **extra = NULL;
+
+ r = strv_split_full(&extra, e, /* separator= */ NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+ if (r < 0)
+ return log_error_errno(r, "Failed to split $SYSTEMD_VMSPAWN_QEMU_EXTRA environment variable: %m");
+
+ if (strv_extend_strv(&cmdline, extra, /* filter_duplicates= */ false) < 0)
+ return log_oom();
+ }
+
if (DEBUG_LOGGING) {
_cleanup_free_ char *joined = quote_command_line(cmdline, SHELL_ESCAPE_EMPTY);
if (!joined)