units: add a tpm2.target synchronization point and small generator that pulls in
Distributions apparently only compile a subset of TPM2 drivers into the kernel. For those not compiled it but provided as kmod we need a synchronization point: we must wait before the first TPM2 interaction until the driver is available and accessible. This adds a tpm2.target unit as such a synchronization point. It's ordered after /dev/tpmrm0, and is pulled in by a generator whenever we detect that the kernel reported a TPM2 to exist but we have no device for it yet. This should solve the issue, but might create problems: if there are TPM devices supported by firmware that we don't have Linux drivers for we'll hang for a bit. Hence let's add a kernel cmdline switch to disable (or alternatively force) this logic. Fixes: #30164
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
|
||||
#define SPECIAL_TIME_SET_TARGET "time-set.target"
|
||||
#define SPECIAL_BASIC_TARGET "basic.target"
|
||||
#define SPECIAL_TPM2_TARGET "tpm2.target"
|
||||
|
||||
/* LSB compatibility */
|
||||
#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
|
||||
|
||||
@@ -13,4 +13,10 @@ executables += [
|
||||
libopenssl,
|
||||
],
|
||||
},
|
||||
|
||||
generator_template + {
|
||||
'name' : 'systemd-tpm2-generator',
|
||||
'sources' : files('tpm2-generator.c'),
|
||||
},
|
||||
|
||||
]
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "generator.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "special.h"
|
||||
#include "tpm2-util.h"
|
||||
#include "parse-util.h"
|
||||
|
||||
/* A small generator that enqueues tpm2.target as synchronization point if the TPM2 device hasn't shown up
|
||||
* yet, but the firmware reports it to exist. This is supposed to deal with systems where the TPM2 driver
|
||||
* support is built as kmod and must be loaded before it's ready to be used. The tpm2.target is only enqueued
|
||||
* if firmware says there is a TPM2 device, our userspace support for TPM2 is fully available but the TPM2
|
||||
* device hasn't shown up in /dev/ yet. */
|
||||
|
||||
static const char *arg_dest = NULL;
|
||||
static int arg_tpm2_wait = -1; /* tri-state: negative → don't know */
|
||||
|
||||
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
|
||||
int r;
|
||||
|
||||
assert(key);
|
||||
|
||||
if (proc_cmdline_key_streq(key, "systemd.tpm2-wait")) {
|
||||
r = value ? parse_boolean(value) : 1;
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse 'systemd.tpm2-wait' kernel command line argument, ignoring: %s", value);
|
||||
else
|
||||
arg_tpm2_wait = r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_tpm_target_symlink(void) {
|
||||
int r;
|
||||
|
||||
if (arg_tpm2_wait == 0) {
|
||||
log_debug("Not generating tpm2.target synchronization point, as this was explicitly turned off via kernel command line.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg_tpm2_wait < 0) {
|
||||
Tpm2Support support = tpm2_support();
|
||||
|
||||
if (FLAGS_SET(support, TPM2_SUPPORT_DRIVER)) {
|
||||
log_debug("Not generating tpm2.target synchronization point, as TPM2 device is already present.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(support, TPM2_SUPPORT_FIRMWARE)) {
|
||||
log_debug("Not generating tpm2.target synchronization point, as firmware reports no TPM2 present.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(support, TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM|TPM2_SUPPORT_LIBRARIES)) {
|
||||
log_debug("Not generating tpm2.target synchronization point, as userspace support for TPM2 is not complete.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = generator_add_symlink(arg_dest, SPECIAL_SYSINIT_TARGET, "wants", SYSTEM_DATA_UNIT_DIR "/" SPECIAL_TPM2_TARGET);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to hook in tpm2.target: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run(const char *dest, const char *dest_early, const char *dest_late) {
|
||||
int r;
|
||||
|
||||
assert_se(arg_dest = dest);
|
||||
|
||||
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
|
||||
|
||||
return generate_tpm_target_symlink();
|
||||
}
|
||||
|
||||
DEFINE_MAIN_GENERATOR_FUNCTION(run);
|
||||
Reference in New Issue
Block a user