diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml
index cc9eedd60b..da350844bd 100644
--- a/man/systemd.unit.xml
+++ b/man/systemd.unit.xml
@@ -1489,6 +1489,68 @@
to the container and not the physically available ones.
+
+ ConditionCPUFeature=
+
+ Verify that a given CPU feature is available via the CPUID
+ instruction. This condition only does something on i386 and x86-64 processors. On other
+ processors it is assumed that the CPU does not support the given feature. It checks the leaves
+ 1, 7, 0x80000001, and
+ 0x80000007. Valid values are:
+ fpu,
+ vme,
+ de,
+ pse,
+ tsc,
+ msr,
+ pae,
+ mce,
+ cx8,
+ apic,
+ sep,
+ mtrr,
+ pge,
+ mca,
+ cmov,
+ pat,
+ pse36,
+ clflush,
+ mmx,
+ fxsr,
+ sse,
+ sse2,
+ ht,
+ pni,
+ pclmul,
+ monitor,
+ ssse3,
+ fma3,
+ cx16,
+ sse4_1,
+ sse4_2,
+ movbe,
+ popcnt,
+ aes,
+ xsave,
+ osxsave,
+ avx,
+ f16c,
+ rdrand,
+ bmi1,
+ avx2,
+ bmi2,
+ rdseed,
+ adx,
+ sha_ni,
+ syscall,
+ rdtscp,
+ lm,
+ lahf_lm,
+ abm,
+ constant_tsc.
+
+
+
AssertArchitecture=
AssertVirtualization=
diff --git a/src/basic/virt.c b/src/basic/virt.c
index 6160b852dc..335f59d6fc 100644
--- a/src/basic/virt.c
+++ b/src/basic/virt.c
@@ -786,6 +786,131 @@ int running_in_chroot(void) {
return r == 0;
}
+#if defined(__i386__) || defined(__x86_64__)
+struct cpuid_table_entry {
+ uint32_t flag_bit;
+ const char *name;
+};
+
+static const struct cpuid_table_entry leaf1_edx[] = {
+ { 0, "fpu" },
+ { 1, "vme" },
+ { 2, "de" },
+ { 3, "pse" },
+ { 4, "tsc" },
+ { 5, "msr" },
+ { 6, "pae" },
+ { 7, "mce" },
+ { 8, "cx8" },
+ { 9, "apic" },
+ { 11, "sep" },
+ { 12, "mtrr" },
+ { 13, "pge" },
+ { 14, "mca" },
+ { 15, "cmov" },
+ { 16, "pat" },
+ { 17, "pse36" },
+ { 19, "clflush" },
+ { 23, "mmx" },
+ { 24, "fxsr" },
+ { 25, "sse" },
+ { 26, "sse2" },
+ { 28, "ht" },
+};
+
+static const struct cpuid_table_entry leaf1_ecx[] = {
+ { 0, "pni" },
+ { 1, "pclmul" },
+ { 3, "monitor" },
+ { 9, "ssse3" },
+ { 12, "fma3" },
+ { 13, "cx16" },
+ { 19, "sse4_1" },
+ { 20, "sse4_2" },
+ { 22, "movbe" },
+ { 23, "popcnt" },
+ { 25, "aes" },
+ { 26, "xsave" },
+ { 27, "osxsave" },
+ { 28, "avx" },
+ { 29, "f16c" },
+ { 30, "rdrand" },
+};
+
+static const struct cpuid_table_entry leaf7_ebx[] = {
+ { 3, "bmi1" },
+ { 5, "avx2" },
+ { 8, "bmi2" },
+ { 18, "rdseed" },
+ { 19, "adx" },
+ { 29, "sha_ni" },
+};
+
+static const struct cpuid_table_entry leaf81_edx[] = {
+ { 11, "syscall" },
+ { 27, "rdtscp" },
+ { 29, "lm" },
+};
+
+static const struct cpuid_table_entry leaf81_ecx[] = {
+ { 0, "lahf_lm" },
+ { 5, "abm" },
+};
+
+static const struct cpuid_table_entry leaf87_edx[] = {
+ { 8, "constant_tsc" },
+};
+
+static bool given_flag_in_set(const char *flag, const struct cpuid_table_entry *set, size_t set_size, uint32_t val) {
+ for (size_t i = 0; i < set_size; i++) {
+ if ((UINT32_C(1) << set[i].flag_bit) & val &&
+ streq(flag, set[i].name))
+ return true;
+ }
+ return false;
+}
+
+static bool real_has_cpu_with_flag(const char *flag) {
+ uint32_t eax, ebx, ecx, edx;
+
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
+ if (given_flag_in_set(flag, leaf1_ecx, ELEMENTSOF(leaf1_ecx), ecx))
+ return true;
+
+ if (given_flag_in_set(flag, leaf1_edx, ELEMENTSOF(leaf1_edx), edx))
+ return true;
+ }
+
+ if (__get_cpuid(7, &eax, &ebx, &ecx, &edx)) {
+ if (given_flag_in_set(flag, leaf7_ebx, ELEMENTSOF(leaf7_ebx), ebx))
+ return true;
+ }
+
+ if (__get_cpuid(0x80000001U, &eax, &ebx, &ecx, &edx)) {
+ if (given_flag_in_set(flag, leaf81_ecx, ELEMENTSOF(leaf81_ecx), ecx))
+ return true;
+
+ if (given_flag_in_set(flag, leaf81_edx, ELEMENTSOF(leaf81_edx), edx))
+ return true;
+ }
+
+ if (__get_cpuid(0x80000007U, &eax, &ebx, &ecx, &edx))
+ if (given_flag_in_set(flag, leaf87_edx, ELEMENTSOF(leaf87_edx), edx))
+ return true;
+
+ return false;
+}
+#endif
+
+bool has_cpu_with_flag(const char *flag) {
+ /* CPUID is an x86 specific interface. Assume on all others that no CPUs have those flags. */
+#if defined(__i386__) || defined(__x86_64__)
+ return real_has_cpu_with_flag(flag);
+#else
+ return false;
+#endif
+}
+
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
[VIRTUALIZATION_NONE] = "none",
[VIRTUALIZATION_KVM] = "kvm",
diff --git a/src/basic/virt.h b/src/basic/virt.h
index 03aa1a72be..378c7c4d23 100644
--- a/src/basic/virt.h
+++ b/src/basic/virt.h
@@ -61,3 +61,4 @@ int running_in_chroot(void);
const char *virtualization_to_string(int v) _const_;
int virtualization_from_string(const char *s) _pure_;
+bool has_cpu_with_flag(const char *flag);
diff --git a/src/shared/condition.c b/src/shared/condition.c
index 485b3bab39..1e63956c05 100644
--- a/src/shared/condition.c
+++ b/src/shared/condition.c
@@ -756,6 +756,14 @@ static int condition_test_path_is_read_write(Condition *c, char **env) {
return path_is_read_only_fs(c->parameter) <= 0;
}
+static int condition_test_cpufeature(Condition *c, char **env) {
+ assert(c);
+ assert(c->parameter);
+ assert(c->type == CONDITION_CPU_FEATURE);
+
+ return has_cpu_with_flag(ascii_strlower(c->parameter));
+}
+
static int condition_test_path_is_encrypted(Condition *c, char **env) {
int r;
@@ -834,6 +842,7 @@ int condition_test(Condition *c, char **env) {
[CONDITION_CPUS] = condition_test_cpus,
[CONDITION_MEMORY] = condition_test_memory,
[CONDITION_ENVIRONMENT] = condition_test_environment,
+ [CONDITION_CPU_FEATURE] = condition_test_cpufeature,
};
int r, b;
@@ -956,6 +965,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_CPUS] = "ConditionCPUs",
[CONDITION_MEMORY] = "ConditionMemory",
[CONDITION_ENVIRONMENT] = "ConditionEnvironment",
+ [CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
};
DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
@@ -987,6 +997,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
[CONDITION_CPUS] = "AssertCPUs",
[CONDITION_MEMORY] = "AssertMemory",
[CONDITION_ENVIRONMENT] = "AssertEnvironment",
+ [CONDITION_CPU_FEATURE] = "AssertCPUFeature",
};
DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);
diff --git a/src/shared/condition.h b/src/shared/condition.h
index 7853490290..75c430e9e0 100644
--- a/src/shared/condition.h
+++ b/src/shared/condition.h
@@ -19,6 +19,7 @@ typedef enum ConditionType {
CONDITION_MEMORY,
CONDITION_CPUS,
CONDITION_ENVIRONMENT,
+ CONDITION_CPU_FEATURE,
CONDITION_NEEDS_UPDATE,
CONDITION_FIRST_BOOT,
diff --git a/src/test/test-condition.c b/src/test/test-condition.c
index 15099d8df8..25483d015b 100644
--- a/src/test/test-condition.c
+++ b/src/test/test-condition.c
@@ -439,6 +439,27 @@ static void test_condition_test_kernel_version(void) {
condition_free(condition);
}
+#if defined(__i386__) || defined(__x86_64__)
+static void test_condition_test_cpufeature(void) {
+ Condition *condition;
+
+ condition = condition_new(CONDITION_CPU_FEATURE, "fpu", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) > 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_CPU_FEATURE, "somecpufeaturethatreallydoesntmakesense", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_CPU_FEATURE, "a", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition, environ) == 0);
+ condition_free(condition);
+}
+#endif
+
static void test_condition_test_security(void) {
Condition *condition;
@@ -864,6 +885,9 @@ int main(int argc, char *argv[]) {
test_condition_test_cpus();
test_condition_test_memory();
test_condition_test_environment();
+#if defined(__i386__) || defined(__x86_64__)
+ test_condition_test_cpufeature();
+#endif
return 0;
}