summaryrefslogtreecommitdiff
path: root/src/basic/virt.c
diff options
context:
space:
mode:
authorGiedrius Statkevičius <giedriuswork@gmail.com>2020-11-11 22:45:58 +0200
committerYu Watanabe <watanabe.yu+github@gmail.com>2021-02-17 15:31:29 +0900
commit68337e55f62cf49b7bdfb73dc5662e23b0ea17fa (patch)
tree494f0944d4b54579df479c6a9dd06ef0809376a3 /src/basic/virt.c
parentb1b4e9204c8260956825e2b9733c95903e215e31 (diff)
downloadsystemd-68337e55f62cf49b7bdfb73dc5662e23b0ea17fa.tar.gz
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.
Diffstat (limited to 'src/basic/virt.c')
-rw-r--r--src/basic/virt.c125
1 files changed, 125 insertions, 0 deletions
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",