From: Giedrius Statkevičius Date: Wed, 11 Nov 2020 20:45:58 +0000 (+0200) Subject: condition: add CPUFeature X-Git-Tag: v248-rc1~89 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=68337e55f62cf49b7bdfb73dc5662e23b0ea17fa;p=thirdparty%2Fsystemd.git condition: add CPUFeature Taking a stab at implementing #14479. Add {Condition,Assert}CPUFeature to `systemd-analyze` & friends. Implement it by executing the CPUID instruction. Add tables for common x86/i386 features. Tested via unit tests + checked that commands such as: ```bash systemd-analyze condition 'AssertCPUFeature = rdrand' ``` Succeed as expected and that commands such as ```bash systemd-analyze condition 'AssertCPUFeature = foobar' ``` Fail as expected. Finally, I have amended the `systemd.unit` manual page with the new condition and the list of all currently supported flags. --- diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index cc9eedd60b2..da350844bd7 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 6160b852dc6..335f59d6fc6 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 03aa1a72be3..378c7c4d232 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 485b3bab39e..1e63956c05f 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 78534902907..75c430e9e07 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 15099d8df8b..25483d015b3 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; }