analyze: introduce cpu-features command

This may be useful when configuring the ConditionCPUFeature= setting.
This commit is contained in:
Yu Watanabe
2026-09-23 12:51:26 +09:00
parent ef3e0ff7bb
commit 724d18a22a
11 changed files with 203 additions and 2 deletions
+30
View File
@@ -224,6 +224,12 @@
<arg choice="plain">architectures</arg>
<arg choice="opt" rep="repeat"><replaceable>NAME</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">cpu-features</arg>
<arg choice="opt" rep="repeat"><replaceable>NAME</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
@@ -1159,6 +1165,30 @@ x86-64 native</programlisting>
</example>
</refsect2>
<refsect2>
<title><command>systemd-analyze cpu-features <optional><replaceable>NAME</replaceable>...</optional></command></title>
<para>Lists all known CPU features for the system's CPU architecture, indicating which of them are
supported. The listed feature names are those understood by <varname>ConditionCPUFeature=</varname>;
see <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details. If one or more feature names are specified, only those are listed.</para>
<para>The command is currently supported only on arm64, x86, and x86_64 systems.</para>
<example>
<title>Table output on an x86_64 system</title>
<programlisting>$ systemd-analyze cpu-features
NAME SUPPORT
abm ✓
adx ✓
aes ✓
apic ✓
avx ✓
avx2 ✓
…</programlisting>
</example>
</refsect2>
<refsect2>
<title><command>systemd-analyze smbios11</command></title>
+5
View File
@@ -2058,6 +2058,11 @@
among others. For the full list, see the documentation on
<ulink url="https://docs.kernel.org/arch/arm64/elf_hwcaps.html">ARM64 ELF hwcaps</ulink>.</para>
<para>See
<citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>cpu-features</command> command for the complete list of known CPU features for the system.
</para>
<para>To avoid ambiguities in mixed-architecture fleets, the value can be prefixed
with the architecture name using the form <literal>ARCH.FEATURE</literal>. If the
architecture prefix does not match the running system, the condition evaluates
+12
View File
@@ -56,6 +56,10 @@ __get_architectures() {
systemd-analyze --no-legend --no-pager architectures 2>/dev/null | { while read -r a b; do echo " $a"; done; }
}
__get_cpu_features() {
systemd-analyze --no-legend --no-pager cpu-features 2>/dev/null | { while read -r a b; do echo " $a"; done; }
}
__get_filesystem_sets() {
local line
systemd-analyze filesystems --no-pager 2>/dev/null | while IFS= read -r line; do
@@ -91,6 +95,7 @@ _systemd_analyze() {
[DLOPEN_METADATA]='dlopen-metadata'
[PLOT]='plot'
[ARCHITECTURES]='architectures'
[CPU_FEATURES]='cpu-features'
[FDSTORE]='fdstore'
[CAPABILITY]='capability'
[TRANSIENT_SETTINGS]='transient-settings'
@@ -250,6 +255,13 @@ _systemd_analyze() {
comps=$( __get_architectures )
fi
elif __contains_word "$verb" ${VERBS[CPU_FEATURES]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager --json=off --json=pretty --json=short --no-legend'
else
comps=$( __get_cpu_features )
fi
elif __contains_word "$verb" ${VERBS[FDSTORE]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global -H --host -M --machine --no-pager --json=off --json=pretty --json=short --root --image'
+8
View File
@@ -52,6 +52,13 @@
_describe -t architectures 'architectures' _architectures || compadd "$@"
}
(( $+functions[_systemd-analyze_cpu-features] )) ||
_systemd-analyze_cpu-features() {
local -a _features
_features=( $(systemd-analyze --quiet --no-pager --no-legend cpu-features 2>/dev/null | { while read -r a b; do echo " $a"; done; }) )
_describe -t features 'cpu features' _features || compadd "$@"
}
(( $+functions[_systemd-analyze_plot] )) ||
_systemd-analyze_plot() {
local -a _options
@@ -95,6 +102,7 @@
'has-tpm2:Report whether TPM2 support is available'
'transient-settings:List transient settings for unit types'
'architectures:List known architectures'
'cpu-features:List known CPU features'
'smbios11:List strings passed via SMBIOS Type #11'
'chid:List local CHIDs'
'compare-versions:Compare two version strings'
+77
View File
@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "analyze.h"
#include "analyze-cpu-features.h"
#include "ansi-color.h"
#include "architecture.h"
#include "format-table.h"
#include "log.h"
#include "string-util.h"
#include "strv.h"
#include "virt.h"
static int add_feature(Table *table, const char *feature, bool known) {
int r;
assert(table);
assert(feature);
if (known) {
bool has = has_cpu_with_flag(feature);
r = table_add_many(table,
TABLE_STRING, feature,
TABLE_BOOLEAN_CHECKMARK, has,
TABLE_SET_COLOR, ansi_highlight_green_red(has));
} else
r = table_add_many(table,
TABLE_STRING, feature,
TABLE_TRISTATE, -1,
TABLE_SET_COLOR, ansi_grey());
if (r < 0)
return table_log_add_error(r);
return 0;
}
int verb_cpu_features(int argc, char *argv[], uintptr_t _data, void *userdata) {
int r;
_cleanup_strv_free_ char **features = NULL;
r = cpu_known_features(&features);
if (r < 0)
return log_oom();
if (strv_isempty(features))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"No known CPU features for the %s architecture.",
architecture_to_string(native_architecture()));
_cleanup_(table_unrefp) Table *table = table_new("name", "support");
if (!table)
return log_oom();
(void) table_set_sort(table, (size_t) 0);
table_set_ersatz_string(table, TABLE_ERSATZ_DASH);
char **args = strv_skip(argv, 1);
if (args)
STRV_FOREACH(feat, args) {
_cleanup_free_ char *lower = strdup(*feat);
if (!lower)
return log_oom();
ascii_strlower(lower);
r = add_feature(table, lower, strv_contains(features, lower));
if (r < 0)
return r;
}
else
STRV_FOREACH(feat, features) {
r = add_feature(table, *feat, /* known= */ true);
if (r < 0)
return r;
}
return table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
}
+6
View File
@@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "forward.h"
int verb_cpu_features(int argc, char *argv[], uintptr_t _data, void *userdata);
+8 -2
View File
@@ -20,6 +20,7 @@
#include "analyze-chid.h"
#include "analyze-compare-versions.h"
#include "analyze-condition.h"
#include "analyze-cpu-features.h"
#include "analyze-critical-chain.h"
#include "analyze-dlopen-metadata.h"
#include "analyze-dot.h"
@@ -243,6 +244,8 @@ VERB_SCOPE(, verb_filesystems, "filesystems", "[NAME...]\0", VERB_ANY, VERB_ANY,
"List known filesystems");
VERB_SCOPE(, verb_architectures, "architectures", "[NAME...]\0", VERB_ANY, VERB_ANY, 0,
"List known architectures");
VERB_SCOPE(, verb_cpu_features, "cpu-features", "[NAME...]\0", VERB_ANY, VERB_ANY, 0,
"List known CPU features");
VERB_SCOPE(, verb_smbios11, "smbios11", NULL, VERB_ANY, 1, 0,
"List strings passed via SMBIOS Type #11");
VERB_SCOPE(, verb_chid, "chid", NULL, VERB_ANY, VERB_ANY, 0,
@@ -578,9 +581,12 @@ static int parse_argv(int argc, char *argv[], char ***ret_args) {
"Option --offline= requires one or more units to perform a security review.");
if (arg_json_format_flags != SD_JSON_FORMAT_OFF &&
!STRPTR_IN_SET(verb, "security", "inspect-elf", "dlopen-metadata", "plot", "fdstore", "pcrs", "nvpcrs", "architectures", "capability", "exit-status", "chid", "blame", "identify-tpm2"))
!STRPTR_IN_SET(verb,
"security", "inspect-elf", "dlopen-metadata", "plot", "fdstore", "pcrs", "nvpcrs",
"architectures", "cpu-features", "capability", "exit-status", "chid", "blame",
"identify-tpm2"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --json= is only supported for security, inspect-elf, dlopen-metadata, plot, fdstore, pcrs, nvpcrs, architectures, capability, exit-status, chid, blame, identify-tpm2 right now.");
"Option --json= is only supported for security, inspect-elf, dlopen-metadata, plot, fdstore, pcrs, nvpcrs, architectures, cpu-features, capability, exit-status, chid, blame, identify-tpm2 right now.");
if (arg_threshold != 100 && !streq_ptr(verb, "security"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+1
View File
@@ -9,6 +9,7 @@ systemd_analyze_sources = files(
'analyze-chid.c',
'analyze-compare-versions.c',
'analyze-condition.c',
'analyze-cpu-features.c',
'analyze-critical-chain.c',
'analyze-dlopen-metadata.c',
'analyze-dot.c',
+45
View File
@@ -1137,6 +1137,51 @@ bool has_cpu_with_flag(const char *flag) {
#endif
}
int cpu_known_features(char ***ret) {
assert(ret);
#if defined(__i386__) || defined(__x86_64__)
static const struct {
const struct cpuid_table_entry *table;
size_t n_entries;
} tables[] = {
{ leaf1_edx, ELEMENTSOF(leaf1_edx), },
{ leaf1_ecx, ELEMENTSOF(leaf1_ecx), },
{ leaf7_ebx, ELEMENTSOF(leaf7_ebx), },
{ leaf81_ecx, ELEMENTSOF(leaf81_ecx), },
{ leaf81_edx, ELEMENTSOF(leaf81_edx), },
{ leaf87_edx, ELEMENTSOF(leaf87_edx), },
};
#elif defined(__aarch64__)
static const struct {
const struct hwcap_table_entry *table;
size_t n_entries;
} tables[] = {
{ hwcap, ELEMENTSOF(hwcap), },
{ hwcap2, ELEMENTSOF(hwcap2), },
{ hwcap3, ELEMENTSOF(hwcap3), },
};
#endif
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
_cleanup_strv_free_ char **features = NULL;
size_t n = 0;
int r;
FOREACH_ELEMENT(table, tables)
FOREACH_ARRAY(i, table->table, table->n_entries) {
r = strv_extend_with_size(&features, &n, i->name);
if (r < 0)
return r;
}
*ret = TAKE_PTR(features);
#else
*ret = NULL;
#endif
return 0;
}
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
[VIRTUALIZATION_NONE] = "none",
[VIRTUALIZATION_KVM] = "kvm",
+1
View File
@@ -64,3 +64,4 @@ int running_in_chroot(void);
DECLARE_STRING_TABLE_LOOKUP(virtualization, Virtualization);
bool has_cpu_with_flag(const char *flag);
int cpu_known_features(char ***ret);
+10
View File
@@ -1125,6 +1125,16 @@ systemd-analyze architectures x86-64
systemd-analyze architectures native
systemd-analyze architectures uname
if systemd-analyze --no-legend architectures native | grep -E '^(x86|x86-64|arm64|arm64-be)[[:space:]]' >/dev/null; then
rc=0
else
rc=1
fi
assert_rc "$rc" systemd-analyze cpu-features
assert_rc "$rc" systemd-analyze cpu-features --json=pretty
assert_rc "$rc" systemd-analyze cpu-features foo hoge fpu fp
assert_rc "$rc" systemd-analyze cpu-features --json=pretty foo hoge fpu fp avx
systemd-analyze smbios11
systemd-analyze smbios11 -q