mirror of
https://github.com/systemd/systemd
synced 2024-10-15 04:24:19 +00:00
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.
This commit is contained in:
parent
b1b4e9204c
commit
68337e55f6
|
@ -1489,6 +1489,68 @@
|
|||
to the container and not the physically available ones.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>ConditionCPUFeature=</varname></term>
|
||||
|
||||
<listitem><para>Verify that a given CPU feature is available via the <literal>CPUID</literal>
|
||||
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
|
||||
<literal>1</literal>, <literal>7</literal>, <literal>0x80000001</literal>, and
|
||||
<literal>0x80000007</literal>. Valid values are:
|
||||
<literal>fpu</literal>,
|
||||
<literal>vme</literal>,
|
||||
<literal>de</literal>,
|
||||
<literal>pse</literal>,
|
||||
<literal>tsc</literal>,
|
||||
<literal>msr</literal>,
|
||||
<literal>pae</literal>,
|
||||
<literal>mce</literal>,
|
||||
<literal>cx8</literal>,
|
||||
<literal>apic</literal>,
|
||||
<literal>sep</literal>,
|
||||
<literal>mtrr</literal>,
|
||||
<literal>pge</literal>,
|
||||
<literal>mca</literal>,
|
||||
<literal>cmov</literal>,
|
||||
<literal>pat</literal>,
|
||||
<literal>pse36</literal>,
|
||||
<literal>clflush</literal>,
|
||||
<literal>mmx</literal>,
|
||||
<literal>fxsr</literal>,
|
||||
<literal>sse</literal>,
|
||||
<literal>sse2</literal>,
|
||||
<literal>ht</literal>,
|
||||
<literal>pni</literal>,
|
||||
<literal>pclmul</literal>,
|
||||
<literal>monitor</literal>,
|
||||
<literal>ssse3</literal>,
|
||||
<literal>fma3</literal>,
|
||||
<literal>cx16</literal>,
|
||||
<literal>sse4_1</literal>,
|
||||
<literal>sse4_2</literal>,
|
||||
<literal>movbe</literal>,
|
||||
<literal>popcnt</literal>,
|
||||
<literal>aes</literal>,
|
||||
<literal>xsave</literal>,
|
||||
<literal>osxsave</literal>,
|
||||
<literal>avx</literal>,
|
||||
<literal>f16c</literal>,
|
||||
<literal>rdrand</literal>,
|
||||
<literal>bmi1</literal>,
|
||||
<literal>avx2</literal>,
|
||||
<literal>bmi2</literal>,
|
||||
<literal>rdseed</literal>,
|
||||
<literal>adx</literal>,
|
||||
<literal>sha_ni</literal>,
|
||||
<literal>syscall</literal>,
|
||||
<literal>rdtscp</literal>,
|
||||
<literal>lm</literal>,
|
||||
<literal>lahf_lm</literal>,
|
||||
<literal>abm</literal>,
|
||||
<literal>constant_tsc</literal>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>AssertArchitecture=</varname></term>
|
||||
<term><varname>AssertVirtualization=</varname></term>
|
||||
|
|
125
src/basic/virt.c
125
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",
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -19,6 +19,7 @@ typedef enum ConditionType {
|
|||
CONDITION_MEMORY,
|
||||
CONDITION_CPUS,
|
||||
CONDITION_ENVIRONMENT,
|
||||
CONDITION_CPU_FEATURE,
|
||||
|
||||
CONDITION_NEEDS_UPDATE,
|
||||
CONDITION_FIRST_BOOT,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue