diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml
index e64f9be57e..6a02291666 100644
--- a/man/systemd-analyze.xml
+++ b/man/systemd-analyze.xml
@@ -195,6 +195,11 @@
OPTIONS
has-tpm2
+
+ systemd-analyze
+ OPTIONS
+ identify-tpm2
+
systemd-analyze
OPTIONS
@@ -1028,6 +1033,26 @@ default ignore - -
+
+ systemd-analyze identify-tpm2
+
+ Shows vendor information about the TPM 2.0 device discovered.
+
+
+ Example Output
+
+ Family Indicator: 2.0
+ Level: 0
+ Revision: 1.59
+Specification Date: Mon 2023-01-09
+ Manufacturer: STM
+ Vendor String: ST33KTPM2XSPI
+ Firmware Version: 9.258
+
+
+
+
+
systemd-analyze pcrs PCR…
diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze
index a863af7aff..b194c74b63 100644
--- a/shell-completion/bash/systemd-analyze
+++ b/shell-completion/bash/systemd-analyze
@@ -77,7 +77,7 @@ _systemd_analyze() {
)
local -A VERBS=(
- [STANDALONE]='time blame unit-files unit-paths exit-status compare-versions timestamp timespan pcrs nvpcrs srk has-tpm2 smbios11 chid image-policy'
+ [STANDALONE]='time blame unit-files unit-paths exit-status compare-versions timestamp timespan pcrs nvpcrs srk has-tpm2 identify-tpm2 smbios11 chid image-policy'
[CRITICAL_CHAIN]='critical-chain'
[DOT]='dot'
[DUMP]='dump'
diff --git a/src/analyze/analyze-has-tpm2.c b/src/analyze/analyze-has-tpm2.c
index 3e13be9f16..a63c44ebc6 100644
--- a/src/analyze/analyze-has-tpm2.c
+++ b/src/analyze/analyze-has-tpm2.c
@@ -2,8 +2,130 @@
#include "analyze.h"
#include "analyze-has-tpm2.h"
+#include "format-table.h"
+#include "log.h"
+#include "string-util.h"
+#include "time-util.h"
#include "tpm2-util.h"
int verb_has_tpm2(int argc, char **argv, void *userdata) {
return verb_has_tpm2_generic(arg_quiet);
}
+
+int verb_identify_tpm2(int argc, char **argv, void *userdata) {
+#if HAVE_TPM2
+ int r;
+
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
+ r = tpm2_context_new_or_warn(/* device= */ NULL, &c);
+ if (r < 0)
+ return r;
+
+ Tpm2VendorInfo info;
+ r = tpm2_get_vendor_info(c, &info);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire TPM2 vendor information: %m");
+
+ _cleanup_(table_unrefp) Table *table = table_new_vertical();
+ if (!table)
+ return log_oom();
+
+ if (!isempty(info.family_indicator)) {
+ r = table_add_many(
+ table,
+ TABLE_FIELD, "Family Indicator",
+ TABLE_STRING, info.family_indicator);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ _cleanup_free_ char *rv = NULL;
+ if (asprintf(&rv, "%" PRIu32 ".%" PRIu32,
+ info.revision_major,
+ info.revision_minor) < 0)
+ return log_oom();
+
+ r = table_add_many(
+ table,
+ TABLE_FIELD, "Level",
+ TABLE_UINT32, info.level,
+ TABLE_FIELD, "Revision",
+ TABLE_STRING, rv);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ if (info.year >= 1900) {
+ struct tm tm = {
+ .tm_year = info.year - 1900,
+ .tm_mon = 0, /* january */
+ .tm_mday = info.day_of_year, /* timegm() will normalize this */
+ };
+
+ usec_t ts;
+ r = mktime_or_timegm_usec(&tm, /* utc= */ true, &ts);
+ if (r < 0)
+ log_debug_errno(r, "Failed to convert the specification date, ignoring.");
+ else {
+ r = table_add_many(
+ table,
+ TABLE_FIELD, "Specification Date",
+ TABLE_TIMESTAMP_DATE, ts);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+ }
+
+ if (!isempty(info.manufacturer)) {
+ r = table_add_many(
+ table,
+ TABLE_FIELD, "Manufacturer",
+ TABLE_STRING, info.manufacturer);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ if (!isempty(info.vendor_string)) {
+ r = table_add_many(
+ table,
+ TABLE_FIELD, "Vendor String",
+ TABLE_STRING, info.vendor_string);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ if (info.vendor_tpm_type != 0) {
+ r = table_add_many(
+ table,
+ TABLE_FIELD, "Vendor TPM Type",
+ TABLE_UINT32_HEX_0x, info.vendor_tpm_type);
+ if (r < 0)
+ return table_log_add_error(r);
+ }
+
+ /* Show the first two 16bit words of the firmware version as major/minor */
+ _cleanup_free_ char *fw = NULL;
+ if (asprintf(&fw, "%" PRIu16 ".%" PRIu16,
+ info.firmware_version_major,
+ info.firmware_version_minor) < 0)
+ return log_oom();
+
+ /* Show the second 32bit as a single value, if non-zero */
+ if (info.firmware_version2 != 0 && strextendf(&fw, ".%" PRIu32, info.firmware_version2) < 0)
+ return log_oom();
+
+ r = table_add_many(
+ table,
+ TABLE_FIELD, "Firmware Version",
+ TABLE_STRING, fw);
+ if (r < 0)
+ return table_log_add_error(r);
+
+ r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, /* show_header= */ false);
+ if (r < 0)
+ return r;
+
+ return EXIT_SUCCESS;
+#else
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support not enabled at build time.");
+#endif
+}
diff --git a/src/analyze/analyze-has-tpm2.h b/src/analyze/analyze-has-tpm2.h
index c7c639228d..b7d750090b 100644
--- a/src/analyze/analyze-has-tpm2.h
+++ b/src/analyze/analyze-has-tpm2.h
@@ -2,3 +2,4 @@
#pragma once
int verb_has_tpm2(int argc, char *argv[], void *userdata);
+int verb_identify_tpm2(int argc, char *argv[], void *userdata);
diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c
index c69f03ec15..af456314db 100644
--- a/src/analyze/analyze.c
+++ b/src/analyze/analyze.c
@@ -258,6 +258,7 @@ static int help(int argc, char *argv[], void *userdata) {
" dlopen-metadata FILE Parse and print ELF dlopen metadata\n"
"\n%3$sTPM Operations:%4$s\n"
" has-tpm2 Report whether TPM2 support is available\n"
+ " identify-tpm2 Show TPM2 vendor information\n"
" pcrs [PCR...] Show TPM2 PCRs and their names\n"
" nvpcrs [NVPCR...] Show additional TPM2 PCRs stored in NV indexes\n"
" srk [>FILE] Write TPM2 SRK (to FILE)\n"
@@ -810,6 +811,7 @@ static int run(int argc, char *argv[]) {
{ "fdstore", 2, VERB_ANY, 0, verb_fdstore },
{ "image-policy", 2, 2, 0, verb_image_policy },
{ "has-tpm2", VERB_ANY, 1, 0, verb_has_tpm2 },
+ { "identify-tpm2", VERB_ANY, 1, 0, verb_identify_tpm2 },
{ "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs },
{ "nvpcrs", VERB_ANY, VERB_ANY, 0, verb_nvpcrs },
{ "srk", VERB_ANY, 1, 0, verb_srk },