Skip to content

Commit 68337e5

Browse files
GiedriusSyuwata
authored andcommitted
condition: add CPUFeature
Taking a stab at implementing systemd#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.
1 parent b1b4e92 commit 68337e5

File tree

6 files changed

+224
-0
lines changed

6 files changed

+224
-0
lines changed

man/systemd.unit.xml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,6 +1489,68 @@
14891489
to the container and not the physically available ones.</para></listitem>
14901490
</varlistentry>
14911491

1492+
<varlistentry>
1493+
<term><varname>ConditionCPUFeature=</varname></term>
1494+
1495+
<listitem><para>Verify that a given CPU feature is available via the <literal>CPUID</literal>
1496+
instruction. This condition only does something on i386 and x86-64 processors. On other
1497+
processors it is assumed that the CPU does not support the given feature. It checks the leaves
1498+
<literal>1</literal>, <literal>7</literal>, <literal>0x80000001</literal>, and
1499+
<literal>0x80000007</literal>. Valid values are:
1500+
<literal>fpu</literal>,
1501+
<literal>vme</literal>,
1502+
<literal>de</literal>,
1503+
<literal>pse</literal>,
1504+
<literal>tsc</literal>,
1505+
<literal>msr</literal>,
1506+
<literal>pae</literal>,
1507+
<literal>mce</literal>,
1508+
<literal>cx8</literal>,
1509+
<literal>apic</literal>,
1510+
<literal>sep</literal>,
1511+
<literal>mtrr</literal>,
1512+
<literal>pge</literal>,
1513+
<literal>mca</literal>,
1514+
<literal>cmov</literal>,
1515+
<literal>pat</literal>,
1516+
<literal>pse36</literal>,
1517+
<literal>clflush</literal>,
1518+
<literal>mmx</literal>,
1519+
<literal>fxsr</literal>,
1520+
<literal>sse</literal>,
1521+
<literal>sse2</literal>,
1522+
<literal>ht</literal>,
1523+
<literal>pni</literal>,
1524+
<literal>pclmul</literal>,
1525+
<literal>monitor</literal>,
1526+
<literal>ssse3</literal>,
1527+
<literal>fma3</literal>,
1528+
<literal>cx16</literal>,
1529+
<literal>sse4_1</literal>,
1530+
<literal>sse4_2</literal>,
1531+
<literal>movbe</literal>,
1532+
<literal>popcnt</literal>,
1533+
<literal>aes</literal>,
1534+
<literal>xsave</literal>,
1535+
<literal>osxsave</literal>,
1536+
<literal>avx</literal>,
1537+
<literal>f16c</literal>,
1538+
<literal>rdrand</literal>,
1539+
<literal>bmi1</literal>,
1540+
<literal>avx2</literal>,
1541+
<literal>bmi2</literal>,
1542+
<literal>rdseed</literal>,
1543+
<literal>adx</literal>,
1544+
<literal>sha_ni</literal>,
1545+
<literal>syscall</literal>,
1546+
<literal>rdtscp</literal>,
1547+
<literal>lm</literal>,
1548+
<literal>lahf_lm</literal>,
1549+
<literal>abm</literal>,
1550+
<literal>constant_tsc</literal>.</para>
1551+
</listitem>
1552+
</varlistentry>
1553+
14921554
<varlistentry>
14931555
<term><varname>AssertArchitecture=</varname></term>
14941556
<term><varname>AssertVirtualization=</varname></term>

src/basic/virt.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,131 @@ int running_in_chroot(void) {
786786
return r == 0;
787787
}
788788

789+
#if defined(__i386__) || defined(__x86_64__)
790+
struct cpuid_table_entry {
791+
uint32_t flag_bit;
792+
const char *name;
793+
};
794+
795+
static const struct cpuid_table_entry leaf1_edx[] = {
796+
{ 0, "fpu" },
797+
{ 1, "vme" },
798+
{ 2, "de" },
799+
{ 3, "pse" },
800+
{ 4, "tsc" },
801+
{ 5, "msr" },
802+
{ 6, "pae" },
803+
{ 7, "mce" },
804+
{ 8, "cx8" },
805+
{ 9, "apic" },
806+
{ 11, "sep" },
807+
{ 12, "mtrr" },
808+
{ 13, "pge" },
809+
{ 14, "mca" },
810+
{ 15, "cmov" },
811+
{ 16, "pat" },
812+
{ 17, "pse36" },
813+
{ 19, "clflush" },
814+
{ 23, "mmx" },
815+
{ 24, "fxsr" },
816+
{ 25, "sse" },
817+
{ 26, "sse2" },
818+
{ 28, "ht" },
819+
};
820+
821+
static const struct cpuid_table_entry leaf1_ecx[] = {
822+
{ 0, "pni" },
823+
{ 1, "pclmul" },
824+
{ 3, "monitor" },
825+
{ 9, "ssse3" },
826+
{ 12, "fma3" },
827+
{ 13, "cx16" },
828+
{ 19, "sse4_1" },
829+
{ 20, "sse4_2" },
830+
{ 22, "movbe" },
831+
{ 23, "popcnt" },
832+
{ 25, "aes" },
833+
{ 26, "xsave" },
834+
{ 27, "osxsave" },
835+
{ 28, "avx" },
836+
{ 29, "f16c" },
837+
{ 30, "rdrand" },
838+
};
839+
840+
static const struct cpuid_table_entry leaf7_ebx[] = {
841+
{ 3, "bmi1" },
842+
{ 5, "avx2" },
843+
{ 8, "bmi2" },
844+
{ 18, "rdseed" },
845+
{ 19, "adx" },
846+
{ 29, "sha_ni" },
847+
};
848+
849+
static const struct cpuid_table_entry leaf81_edx[] = {
850+
{ 11, "syscall" },
851+
{ 27, "rdtscp" },
852+
{ 29, "lm" },
853+
};
854+
855+
static const struct cpuid_table_entry leaf81_ecx[] = {
856+
{ 0, "lahf_lm" },
857+
{ 5, "abm" },
858+
};
859+
860+
static const struct cpuid_table_entry leaf87_edx[] = {
861+
{ 8, "constant_tsc" },
862+
};
863+
864+
static bool given_flag_in_set(const char *flag, const struct cpuid_table_entry *set, size_t set_size, uint32_t val) {
865+
for (size_t i = 0; i < set_size; i++) {
866+
if ((UINT32_C(1) << set[i].flag_bit) & val &&
867+
streq(flag, set[i].name))
868+
return true;
869+
}
870+
return false;
871+
}
872+
873+
static bool real_has_cpu_with_flag(const char *flag) {
874+
uint32_t eax, ebx, ecx, edx;
875+
876+
if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) {
877+
if (given_flag_in_set(flag, leaf1_ecx, ELEMENTSOF(leaf1_ecx), ecx))
878+
return true;
879+
880+
if (given_flag_in_set(flag, leaf1_edx, ELEMENTSOF(leaf1_edx), edx))
881+
return true;
882+
}
883+
884+
if (__get_cpuid(7, &eax, &ebx, &ecx, &edx)) {
885+
if (given_flag_in_set(flag, leaf7_ebx, ELEMENTSOF(leaf7_ebx), ebx))
886+
return true;
887+
}
888+
889+
if (__get_cpuid(0x80000001U, &eax, &ebx, &ecx, &edx)) {
890+
if (given_flag_in_set(flag, leaf81_ecx, ELEMENTSOF(leaf81_ecx), ecx))
891+
return true;
892+
893+
if (given_flag_in_set(flag, leaf81_edx, ELEMENTSOF(leaf81_edx), edx))
894+
return true;
895+
}
896+
897+
if (__get_cpuid(0x80000007U, &eax, &ebx, &ecx, &edx))
898+
if (given_flag_in_set(flag, leaf87_edx, ELEMENTSOF(leaf87_edx), edx))
899+
return true;
900+
901+
return false;
902+
}
903+
#endif
904+
905+
bool has_cpu_with_flag(const char *flag) {
906+
/* CPUID is an x86 specific interface. Assume on all others that no CPUs have those flags. */
907+
#if defined(__i386__) || defined(__x86_64__)
908+
return real_has_cpu_with_flag(flag);
909+
#else
910+
return false;
911+
#endif
912+
}
913+
789914
static const char *const virtualization_table[_VIRTUALIZATION_MAX] = {
790915
[VIRTUALIZATION_NONE] = "none",
791916
[VIRTUALIZATION_KVM] = "kvm",

src/basic/virt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,4 @@ int running_in_chroot(void);
6161

6262
const char *virtualization_to_string(int v) _const_;
6363
int virtualization_from_string(const char *s) _pure_;
64+
bool has_cpu_with_flag(const char *flag);

src/shared/condition.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,14 @@ static int condition_test_path_is_read_write(Condition *c, char **env) {
756756
return path_is_read_only_fs(c->parameter) <= 0;
757757
}
758758

759+
static int condition_test_cpufeature(Condition *c, char **env) {
760+
assert(c);
761+
assert(c->parameter);
762+
assert(c->type == CONDITION_CPU_FEATURE);
763+
764+
return has_cpu_with_flag(ascii_strlower(c->parameter));
765+
}
766+
759767
static int condition_test_path_is_encrypted(Condition *c, char **env) {
760768
int r;
761769

@@ -834,6 +842,7 @@ int condition_test(Condition *c, char **env) {
834842
[CONDITION_CPUS] = condition_test_cpus,
835843
[CONDITION_MEMORY] = condition_test_memory,
836844
[CONDITION_ENVIRONMENT] = condition_test_environment,
845+
[CONDITION_CPU_FEATURE] = condition_test_cpufeature,
837846
};
838847

839848
int r, b;
@@ -956,6 +965,7 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = {
956965
[CONDITION_CPUS] = "ConditionCPUs",
957966
[CONDITION_MEMORY] = "ConditionMemory",
958967
[CONDITION_ENVIRONMENT] = "ConditionEnvironment",
968+
[CONDITION_CPU_FEATURE] = "ConditionCPUFeature",
959969
};
960970

961971
DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType);
@@ -987,6 +997,7 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = {
987997
[CONDITION_CPUS] = "AssertCPUs",
988998
[CONDITION_MEMORY] = "AssertMemory",
989999
[CONDITION_ENVIRONMENT] = "AssertEnvironment",
1000+
[CONDITION_CPU_FEATURE] = "AssertCPUFeature",
9901001
};
9911002

9921003
DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType);

src/shared/condition.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ typedef enum ConditionType {
1919
CONDITION_MEMORY,
2020
CONDITION_CPUS,
2121
CONDITION_ENVIRONMENT,
22+
CONDITION_CPU_FEATURE,
2223

2324
CONDITION_NEEDS_UPDATE,
2425
CONDITION_FIRST_BOOT,

src/test/test-condition.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,27 @@ static void test_condition_test_kernel_version(void) {
439439
condition_free(condition);
440440
}
441441

442+
#if defined(__i386__) || defined(__x86_64__)
443+
static void test_condition_test_cpufeature(void) {
444+
Condition *condition;
445+
446+
condition = condition_new(CONDITION_CPU_FEATURE, "fpu", false, false);
447+
assert_se(condition);
448+
assert_se(condition_test(condition, environ) > 0);
449+
condition_free(condition);
450+
451+
condition = condition_new(CONDITION_CPU_FEATURE, "somecpufeaturethatreallydoesntmakesense", false, false);
452+
assert_se(condition);
453+
assert_se(condition_test(condition, environ) == 0);
454+
condition_free(condition);
455+
456+
condition = condition_new(CONDITION_CPU_FEATURE, "a", false, false);
457+
assert_se(condition);
458+
assert_se(condition_test(condition, environ) == 0);
459+
condition_free(condition);
460+
}
461+
#endif
462+
442463
static void test_condition_test_security(void) {
443464
Condition *condition;
444465

@@ -864,6 +885,9 @@ int main(int argc, char *argv[]) {
864885
test_condition_test_cpus();
865886
test_condition_test_memory();
866887
test_condition_test_environment();
888+
#if defined(__i386__) || defined(__x86_64__)
889+
test_condition_test_cpufeature();
890+
#endif
867891

868892
return 0;
869893
}

0 commit comments

Comments
 (0)