]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
vmspawn: add swtpm feature
authorSam Leonard <sam.leonard@codethink.co.uk>
Tue, 7 Nov 2023 14:04:11 +0000 (14:04 +0000)
committerSam Leonard <sam.leonard@codethink.co.uk>
Fri, 9 Feb 2024 11:43:18 +0000 (11:43 +0000)
man/systemd-vmspawn.xml
src/vmspawn/meson.build
src/vmspawn/vmspawn-scope.c [new file with mode: 0644]
src/vmspawn/vmspawn-scope.h [new file with mode: 0644]
src/vmspawn/vmspawn-util.h
src/vmspawn/vmspawn.c

index d5ece4acea00c03443d43a9d6a6b6a22d5ee7ffd..d5eac5f50e3d076646bd2cbf111ba82e5f398eef 100644 (file)
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><option>--tpm=</option><replaceable>BOOL</replaceable></term>
+
+        <listitem>
+          <para>Configure whether to use qemu with a virtual TPM or not.</para>
+          <para>If the option is not specified vmspawn will detect the presence of <citerefentry project='man-pages'>
+          <refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry> and use it if available.
+          If yes is specified <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+          is always used, and vice versa if no is set <citerefentry project='man-pages'><refentrytitle>swtpm</refentrytitle>
+          <manvolnum>8</manvolnum></citerefentry> is never used.</para>
+          <para>Note: the virtual TPM used may change in future.</para>
+          <xi:include href="version-info.xml" xpointer="v256"/>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><option>--qemu-gui</option></term>
 
index 800d7c362c5b2b5143e4d3d8ba9b88fedf3f0ebd..a67c66ebe1dc6d6226a7fd6396efa141424b34d1 100644 (file)
@@ -3,6 +3,7 @@
 libvmspawn_core_sources = files(
         'vmspawn-settings.c',
         'vmspawn-util.c',
+        'vmspawn-scope.c',
 )
 libvmspawn_core = static_library(
         'vmspawn-core',
diff --git a/src/vmspawn/vmspawn-scope.c b/src/vmspawn/vmspawn-scope.c
new file mode 100644 (file)
index 0000000..676dee0
--- /dev/null
@@ -0,0 +1,302 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stdio.h>
+
+#include "sd-bus.h"
+
+#include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-unit-util.h"
+#include "bus-util.h"
+#include "bus-wait-for-jobs.h"
+#include "escape.h"
+#include "macro.h"
+#include "process-util.h"
+#include "random-util.h"
+#include "socket-util.h"
+#include "strv.h"
+#include "unit-def.h"
+#include "unit-name.h"
+#include "vmspawn-scope.h"
+
+int start_transient_scope(sd_bus *bus, const char *machine_name, bool allow_pidfd, char **ret_scope) {
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL, *m = NULL;
+        _cleanup_free_ char *scope = NULL, *description = NULL;
+        const char *object;
+        int r;
+
+        assert(bus);
+        assert(machine_name);
+
+        /* Creates a transient scope unit which tracks the lifetime of the current process */
+
+        r = bus_wait_for_jobs_new(bus, &w);
+        if (r < 0)
+                return log_error_errno(r, "Could not watch job: %m");
+
+        if (asprintf(&scope, "machine-%"PRIu64"-%s.scope", random_u64(), machine_name) < 0)
+                return log_oom();
+
+        description = strjoin("Virtual Machine ", machine_name);
+        if (!description)
+                return log_oom();
+
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "ss", /* name */ scope, /* mode */ "fail");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        /* Properties */
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "(sa)(sa)(sa)",
+                                  "Description", "s",  description,
+                                  "AddRef",      "b",  1,
+                                  "CollectMode", "s",  "inactive-or-failed");
+
+        if (allow_pidfd) {
+                _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
+                r = pidref_set_pid(&pidref, getpid_cached());
+                if (r < 0)
+                        return log_error_errno(r, "Failed to allocate PID reference: %m");
+
+                r = bus_append_scope_pidref(m, &pidref);
+        } else
+                r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, getpid_cached());
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        /* No auxiliary units */
+        r = sd_bus_message_append(
+                        m,
+                        "a(sa(sv))",
+                        0);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0) {
+                /* If this failed with a property we couldn't write, this is quite likely because the server
+                 * doesn't support PIDFDs yet, let's try without. */
+                if (allow_pidfd &&
+                    sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
+                        return start_transient_scope(bus, machine_name, false, ret_scope);
+
+                return log_error_errno(r, "Failed to start transient scope unit: %s", bus_error_message(&error, r));
+        }
+
+        r = sd_bus_message_read(reply, "o", &object);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        r = bus_wait_for_jobs_one(w, object, /* quiet */ false, NULL);
+        if (r < 0)
+                return r;
+
+        if (ret_scope)
+                *ret_scope = TAKE_PTR(scope);
+
+        return 0;
+}
+
+static int message_add_commands(sd_bus_message *m, const char *exec_type, char ***commands, size_t n_commands) {
+        int r;
+
+        assert(m);
+        assert(exec_type);
+        assert(commands || n_commands == 0);
+
+        /* A small helper for adding an ExecStart / ExecStopPost / etc.. property to an sd_bus_message */
+
+        r = sd_bus_message_open_container(m, 'r', "sv");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "s", exec_type);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'v', "a(sasb)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'a', "(sasb)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        FOREACH_ARRAY(cmd, commands, n_commands) {
+                char **cmdline = *cmd;
+
+                r = sd_bus_message_open_container(m, 'r', "sasb");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "s", cmdline[0]);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append_strv(m, cmdline);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "b", 0);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_close_container(m);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        return 0;
+}
+
+void socket_service_pair_done(SocketServicePair *p) {
+        p->exec_start = strv_free(p->exec_start);
+        p->exec_stop_post = strv_free(p->exec_stop_post);
+        p->unit_name_prefix = mfree(p->unit_name_prefix);
+        p->runtime_directory = mfree(p->runtime_directory);
+        p->listen_address = mfree(p->listen_address);
+        p->socket_type = 0;
+}
+
+int start_socket_service_pair(sd_bus *bus, const char *scope, SocketServicePair *p) {
+        _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        _cleanup_free_ char *service_desc = NULL, *service_name = NULL, *socket_name = NULL;
+        const char *object, *socket_type_str;
+        int r;
+
+        /* Starts a socket/service unit pair bound to the given scope. */
+
+        assert(bus);
+        assert(scope);
+        assert(p);
+        assert(p->unit_name_prefix);
+        assert(p->exec_start);
+        assert(p->listen_address);
+
+        r = bus_wait_for_jobs_new(bus, &w);
+        if (r < 0)
+                return log_error_errno(r, "Could not watch job: %m");
+
+        socket_name = strjoin(p->unit_name_prefix, ".socket");
+        if (!socket_name)
+                return log_oom();
+
+        service_name = strjoin(p->unit_name_prefix, ".service");
+        if (!service_name)
+                return log_oom();
+
+        service_desc = quote_command_line(p->exec_start, SHELL_ESCAPE_EMPTY);
+        if (!service_desc)
+                return log_oom();
+
+        socket_type_str = socket_address_type_to_string(p->socket_type);
+        if (!socket_type_str)
+                return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Invalid socket type: %d", p->socket_type);
+
+        r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "StartTransientUnit");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "ssa(sv)",
+                                  /* ss - name, mode */
+                                  socket_name, "fail",
+                                  /* a(sv) - Properties */
+                                  5,
+                                  "Description", "s",     p->listen_address,
+                                  "AddRef",      "b",     1,
+                                  "BindsTo",     "as",    1, scope,
+                                  "Listen",      "a(ss)", 1, socket_type_str, p->listen_address,
+                                  "CollectMode", "s",     "inactive-or-failed");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        /* aux */
+        r = sd_bus_message_open_container(m, 'a', "(sa(sv))");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'r', "sa(sv)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "s", service_name);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_open_container(m, 'a', "(sv)");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
+                                  "Description", "s",  service_desc,
+                                  "AddRef",      "b",  1,
+                                  "BindsTo",     "as", 1, scope,
+                                  "CollectMode", "s",  "inactive-or-failed");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        if (p->runtime_directory) {
+                r = sd_bus_message_append(m, "(sv)", "RuntimeDirectory", "as", 1, p->runtime_directory);
+                if (r < 0)
+                        return bus_log_create_error(r);
+        }
+
+        r = message_add_commands(m, "ExecStart", &p->exec_start, 1);
+        if (r < 0)
+                return r;
+
+        if (p->exec_stop_post) {
+                r = message_add_commands(m, "ExecStopPost", &p->exec_stop_post, 1);
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_close_container(m);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
+        if (r < 0)
+                return log_error_errno(r, "Failed to start %s as transient unit: %s", p->exec_start[0], bus_error_message(&error, r));
+
+        r = sd_bus_message_read(reply, "o", &object);
+        if (r < 0)
+                return bus_log_parse_error(r);
+
+        return bus_wait_for_jobs_one(w, object, /* quiet */ false, NULL);
+}
diff --git a/src/vmspawn/vmspawn-scope.h b/src/vmspawn/vmspawn-scope.h
new file mode 100644 (file)
index 0000000..b807c3b
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stdbool.h>
+
+#include "sd-bus.h"
+
+#include "macro.h"
+
+typedef struct SocketServicePair {
+        char **exec_start;
+        char **exec_stop_post;
+        char *unit_name_prefix;
+        char *runtime_directory;
+        char *listen_address;
+        int socket_type;
+} SocketServicePair;
+
+void socket_service_pair_done(SocketServicePair *p);
+
+int start_transient_scope(sd_bus *bus, const char *machine_name, bool allow_pidfd, char **ret_scope);
+int start_socket_service_pair(sd_bus *bus, const char *scope, SocketServicePair *p);
index f2d1b621e21fbab17b9ebf3b114ea1026328a1bc..c3dbdf205b1a02af21d81e416d06a9435d8d0707 100644 (file)
 #define ARCHITECTURE_SUPPORTS_SMBIOS 0
 #endif
 
+#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__)
+#define ARCHITECTURE_SUPPORTS_TPM 1
+#else
+#define ARCHITECTURE_SUPPORTS_TPM 0
+#endif
+
 #if defined(__arm__) || defined(__aarch64__)
 #define DEFAULT_SERIAL_TTY "ttyAMA0"
 #elif defined(__s390__) || defined(__s390x__)
index cc21efbf4421f6685af327c6ee02556abdfbbec1..8138d142e87ba189598e3ab609a08901d8dab908 100644 (file)
@@ -3,7 +3,6 @@
 #include <getopt.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include "alloc-util.h"
 #include "log.h"
 #include "machine-credential.h"
 #include "main-func.h"
+#include "mkdir.h"
 #include "pager.h"
 #include "parse-argument.h"
 #include "parse-util.h"
+#include "path-lookup.h"
 #include "path-util.h"
 #include "pretty-print.h"
 #include "process-util.h"
+#include "rm-rf.h"
 #include "sd-event.h"
 #include "signal-util.h"
 #include "socket-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
+#include "unit-name.h"
+#include "vmspawn-scope.h"
 #include "vmspawn-settings.h"
 #include "vmspawn-util.h"
 
@@ -45,16 +49,21 @@ static uint64_t arg_qemu_mem = UINT64_C(2) * U64_GB;
 static int arg_qemu_kvm = -1;
 static int arg_qemu_vsock = -1;
 static unsigned arg_vsock_cid = VMADDR_CID_ANY;
+static int arg_tpm = -1;
 static bool arg_qemu_gui = false;
 static int arg_secure_boot = -1;
 static MachineCredentialContext arg_credentials = {};
 static SettingsMask arg_settings_mask = 0;
 static char **arg_parameters = NULL;
 static char *arg_firmware = NULL;
+static char *arg_runtime_directory = NULL;
+static bool arg_runtime_directory_created = false;
+static bool arg_privileged = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_qemu_smp, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_runtime_directory, freep);
 STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
 STATIC_DESTRUCTOR_REGISTER(arg_firmware, freep);
@@ -84,6 +93,7 @@ static int help(void) {
                "     --qemu-kvm=BOOL        Configure whether to use KVM or not\n"
                "     --qemu-vsock=BOOL      Configure whether to use qemu with a vsock or not\n"
                "     --vsock-cid=           Specify the CID to use for the qemu guest's vsock\n"
+               "     --tpm=BOOL             Configure whether to use a virtual TPM or not\n"
                "     --qemu-gui             Start QEMU in graphical mode\n"
                "     --secure-boot=BOOL     Configure whether to search for firmware which\n"
                "                            supports Secure Boot\n"
@@ -117,6 +127,7 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_QEMU_KVM,
                 ARG_QEMU_VSOCK,
                 ARG_VSOCK_CID,
+                ARG_TPM,
                 ARG_QEMU_GUI,
                 ARG_SECURE_BOOT,
                 ARG_SET_CREDENTIAL,
@@ -136,6 +147,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "qemu-kvm",        required_argument, NULL, ARG_QEMU_KVM        },
                 { "qemu-vsock",      required_argument, NULL, ARG_QEMU_VSOCK      },
                 { "vsock-cid",       required_argument, NULL, ARG_VSOCK_CID       },
+                { "tpm",             required_argument, NULL, ARG_TPM             },
                 { "qemu-gui",        no_argument,       NULL, ARG_QEMU_GUI        },
                 { "secure-boot",     required_argument, NULL, ARG_SECURE_BOOT     },
                 { "set-credential",  required_argument, NULL, ARG_SET_CREDENTIAL  },
@@ -228,6 +240,12 @@ static int parse_argv(int argc, char *argv[]) {
                         }
                         break;
 
+                case ARG_TPM:
+                        r = parse_tristate(optarg, &arg_tpm);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to parse --tpm=%s: %m", optarg);
+                        break;
+
                 case ARG_QEMU_GUI:
                         arg_qemu_gui = true;
                         break;
@@ -482,12 +500,80 @@ static int cmdline_add_vsock(char ***cmdline, int vsock_fd) {
         return 0;
 }
 
+static int start_tpm(sd_bus *bus, const char *scope, const char *tpm, const char **ret_state_tempdir) {
+        _cleanup_(rm_rf_physical_and_freep) char *state_dir = NULL;
+        _cleanup_free_ char *scope_prefix = NULL;
+        _cleanup_(socket_service_pair_done) SocketServicePair ssp = {
+                .socket_type = SOCK_STREAM,
+        };
+        int r;
+
+        assert(bus);
+        assert(scope);
+        assert(tpm);
+        assert(ret_state_tempdir);
+
+        r = unit_name_to_prefix(scope, &scope_prefix);
+        if (r < 0)
+                return log_error_errno(r, "Failed to strip .scope suffix from scope: %m");
+
+        ssp.unit_name_prefix = strjoin(scope_prefix, "-tpm");
+        if (!ssp.unit_name_prefix)
+                return log_oom();
+
+        state_dir = path_join(arg_runtime_directory, ssp.unit_name_prefix);
+        if (!state_dir)
+                return log_oom();
+
+        if (arg_runtime_directory_created) {
+                ssp.runtime_directory = path_join("systemd/vmspawn", ssp.unit_name_prefix);
+                if (!ssp.runtime_directory)
+                        return log_oom();
+        }
+
+        ssp.listen_address = path_join(state_dir, "sock");
+        if (!ssp.listen_address)
+                return log_oom();
+
+        ssp.exec_start = strv_new(tpm, "socket", "--tpm2", "--tpmstate");
+        if (!ssp.exec_start)
+                return log_oom();
+
+        r = strv_extendf(&ssp.exec_start, "dir=%s", state_dir);
+        if (r < 0)
+                return log_oom();
+
+        r = strv_extend_many(&ssp.exec_start, "--ctrl", "type=unixio,fd=3");
+        if (r < 0)
+                return log_oom();
+
+        r = start_socket_service_pair(bus, scope, &ssp);
+        if (r < 0)
+                return r;
+
+        *ret_state_tempdir = TAKE_PTR(state_dir);
+
+        return 0;
+}
+
 static int run_virtual_machine(void) {
         _cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL;
+        _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+        _cleanup_close_ int vsock_fd = -EBADF;
+        _cleanup_free_ char *machine = NULL, *qemu_binary = NULL, *mem = NULL, *trans_scope = NULL;
         _cleanup_strv_free_ char **cmdline = NULL;
-        _cleanup_free_ char *machine = NULL, *qemu_binary = NULL, *mem = NULL;
         int r;
-        _cleanup_close_ int vsock_fd = -EBADF;
+
+        if (arg_privileged)
+                r = sd_bus_default_system(&bus);
+        else
+                r = sd_bus_default_user(&bus);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to systemd bus: %m");
+
+        r = start_transient_scope(bus, arg_machine, /* allow_pidfd= */ true, &trans_scope);
+        if (r < 0)
+                return r;
 
         bool use_kvm = arg_qemu_kvm > 0;
         if (arg_qemu_kvm < 0) {
@@ -538,6 +624,20 @@ static int run_virtual_machine(void) {
         if (!cmdline)
                 return log_oom();
 
+        /* if we are going to be starting any units with state then create our runtime dir */
+        if (arg_tpm != 0) {
+                r = runtime_directory(&arg_runtime_directory, arg_privileged ? RUNTIME_SCOPE_SYSTEM : RUNTIME_SCOPE_USER, "systemd/vmspawn");
+                if (r < 0)
+                        return log_error_errno(r, "Failed to lookup runtime directory: %m");
+                if (r) {
+                        /* r > 0 means we need to create our own runtime dir */
+                        r = mkdir_p(arg_runtime_directory, 0755);
+                        if (r < 0)
+                                return log_error_errno(r, "Failed to create runtime directory: %m");
+                        arg_runtime_directory_created = true;
+                }
+        }
+
         bool use_vsock = arg_qemu_vsock > 0 && ARCHITECTURE_SUPPORTS_SMBIOS;
         if (arg_qemu_vsock < 0) {
                 r = qemu_check_vsock_support();
@@ -685,6 +785,58 @@ static int run_virtual_machine(void) {
         } else
                 log_warning("Cannot append extra args to kernel cmdline, native architecture doesn't support SMBIOS");
 
+        /* disable TPM autodetection if the user's hardware doesn't support it */
+        if (!ARCHITECTURE_SUPPORTS_TPM) {
+                if (arg_tpm < 0) {
+                        arg_tpm = 0;
+                        log_debug("TPM not support on %s, disabling tpm autodetection and continuing", architecture_to_string(native_architecture()));
+                } else if (arg_tpm > 0)
+                        return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM not supported on %s, refusing", architecture_to_string(native_architecture()));
+        }
+
+        _cleanup_free_ char *swtpm = NULL;
+        if (arg_tpm != 0) {
+                r = find_executable("swtpm", &swtpm);
+                if (r < 0) {
+                        /* log if the user asked for swtpm and we cannot find it */
+                        if (arg_tpm > 0)
+                                return log_error_errno(r, "Failed to find swtpm binary: %m");
+                        /* also log if we got an error other than ENOENT from find_executable */
+                        if (r != -ENOENT && arg_tpm < 0)
+                                return log_error_errno(r, "Error detecting swtpm: %m");
+                }
+        }
+
+        _cleanup_free_ const char *tpm_state_tempdir = NULL;
+        if (swtpm) {
+                r = start_tpm(bus, trans_scope, swtpm, &tpm_state_tempdir);
+                if (r < 0) {
+                        /* only bail if the user asked for a tpm */
+                        if (arg_tpm > 0)
+                                return log_error_errno(r, "Failed to start tpm: %m");
+                        log_debug_errno(r, "Failed to start tpm, ignoring: %m");
+                }
+
+                r = strv_extend(&cmdline, "-chardev");
+                if (r < 0)
+                        return log_oom();
+
+                r = strv_extendf(&cmdline, "socket,id=chrtpm,path=%s/sock", tpm_state_tempdir);
+                if (r < 0)
+                        return log_oom();
+
+                r = strv_extend_many(&cmdline, "-tpmdev", "emulator,id=tpm0,chardev=chrtpm");
+                if (r < 0)
+                        return log_oom();
+
+                if (native_architecture() == ARCHITECTURE_X86_64)
+                        r = strv_extend_many(&cmdline, "-device", "tpm-tis,tpmdev=tpm0");
+                else if (IN_SET(native_architecture(), ARCHITECTURE_ARM64, ARCHITECTURE_ARM64_BE))
+                        r = strv_extend_many(&cmdline, "-device", "tpm-tis-device,tpmdev=tpm0");
+                if (r < 0)
+                        return log_oom();
+        }
+
         if (use_vsock) {
                 vsock_fd = open_vsock();
                 if (vsock_fd < 0)
@@ -803,6 +955,8 @@ static int run(int argc, char *argv[]) {
 
         log_setup();
 
+        arg_privileged = getuid() == 0;
+
         r = parse_argv(argc, argv);
         if (r <= 0)
                 return r;