Add support for aarch64 CPUFeatures (#42902)

Extend real_has_cpu_with_flag to support aarch64 CPU Features using
hwcaps.

With this PR, users can find out if their Arm system supports
architecture-specific features such as BTI as follows:

```
$ systemd-analyze condition 'ConditionCPUFeature=bti'
```
This commit is contained in:
Lennart Poettering
2026-07-10 14:41:01 +02:00
committed by GitHub
6 changed files with 508 additions and 15 deletions
+25 -5
View File
@@ -1988,11 +1988,10 @@
<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:
<listitem><para>On i386 and x86-64, verify that a given CPU feature is available via the
<literal>CPUID</literal> instruction. 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>,
@@ -2045,6 +2044,27 @@
<literal>abm</literal>,
<literal>constant_tsc</literal>.</para>
<para>On arm64, valid values are the corresponding arm64 feature names,
such as
<literal>asimd</literal>,
<literal>paca</literal>,
<literal>pacg</literal>,
<literal>bti</literal>,
<literal>mte</literal>,
among others. For the full list, see the documentation on
<ulink url="https://docs.kernel.org/arch/arm64/elf_hwcaps.html">ARM64 ELF hwcaps</ulink>.</para>
<para>To avoid ambiguities in mixed-architecture fleets, the value can be prefixed
with the architecture name using the form <literal>ARCH.FEATURE</literal>. If the
architecture prefix does not match the running system, the condition evaluates
to false. For example, one can specify <literal>arm64.bti</literal> on arm64
systems to check if <literal>bti</literal> is supported. In this example,
<literal>ConditionCPUFeature=arm64.bti</literal> and
<literal>ConditionCPUFeature=bti</literal> are functionally equivalent.
See <varname>ConditionArchitecture=</varname> for valid architecture names.</para>
<para>On architectures other than x86 and arm64, it is assumed that the CPU does not support the given feature.</para>
<xi:include href="version-info.xml" xpointer="v248"/>
</listitem>
</varlistentry>
+178 -2
View File
@@ -3,6 +3,9 @@
#if defined(__i386__) || defined(__x86_64__)
#include <cpuid.h>
#endif
#if defined(__aarch64__)
#include <sys/auxv.h>
#endif
#include <stdlib.h>
#include <threads.h>
#include <unistd.h>
@@ -951,9 +954,182 @@ static bool real_has_cpu_with_flag(const char *flag) {
}
#endif
#if defined(__aarch64__)
struct hwcap_table_entry {
uint64_t flag_mask;
const char *name;
};
static const struct hwcap_table_entry hwcap[] = {
{ HWCAP_FP, "fp" },
{ HWCAP_ASIMD, "asimd" },
{ HWCAP_EVTSTRM, "evtstrm" },
{ HWCAP_AES, "aes" },
{ HWCAP_PMULL, "pmull" },
{ HWCAP_SHA1, "sha1" },
{ HWCAP_SHA2, "sha2" },
{ HWCAP_CRC32, "crc32" },
{ HWCAP_ATOMICS, "atomics" },
{ HWCAP_FPHP, "fphp" },
{ HWCAP_ASIMDHP, "asimdhp" },
{ HWCAP_CPUID, "cpuid" },
{ HWCAP_ASIMDRDM, "asimdrdm" },
{ HWCAP_JSCVT, "jscvt" },
{ HWCAP_FCMA, "fcma" },
{ HWCAP_LRCPC, "lrcpc" },
{ HWCAP_DCPOP, "dcpop" },
{ HWCAP_SHA3, "sha3" },
{ HWCAP_SM3, "sm3" },
{ HWCAP_SM4, "sm4" },
{ HWCAP_ASIMDDP, "asimddp" },
{ HWCAP_SHA512, "sha512" },
{ HWCAP_SVE, "sve" },
{ HWCAP_ASIMDFHM, "asimdfhm" },
{ HWCAP_DIT, "dit" },
{ HWCAP_USCAT, "uscat" },
{ HWCAP_ILRCPC, "ilrcpc" },
{ HWCAP_FLAGM, "flagm" },
{ HWCAP_SSBS, "ssbs" },
{ HWCAP_SB, "sb" },
{ HWCAP_PACA, "paca" },
{ HWCAP_PACG, "pacg" },
{ HWCAP_GCS, "gcs" },
{ HWCAP_CMPBR, "cmpbr" },
{ HWCAP_FPRCVT, "fprcvt" },
{ HWCAP_F8MM8, "f8mm8" },
{ HWCAP_F8MM4, "f8mm4" },
{ HWCAP_SVE_F16MM, "svef16mm" },
{ HWCAP_SVE_ELTPERM, "sveeltperm" },
{ HWCAP_SVE_AES2, "sveaes2" },
{ HWCAP_SVE_BFSCALE, "svebfscale" },
{ HWCAP_SVE2P2, "sve2p2" },
{ HWCAP_SME2P2, "sme2p2" },
{ HWCAP_SME_SBITPERM, "smesbitperm" },
{ HWCAP_SME_AES, "smeaes" },
{ HWCAP_SME_SFEXPA, "smesfexpa" },
{ HWCAP_SME_STMOP, "smestmop" },
{ HWCAP_SME_SMOP4, "smesmop4" },
};
static const struct hwcap_table_entry hwcap2[] = {
{ HWCAP2_DCPODP, "dcpodp" },
{ HWCAP2_SVE2, "sve2" },
{ HWCAP2_SVEAES, "sveaes" },
{ HWCAP2_SVEPMULL, "svepmull" },
{ HWCAP2_SVEBITPERM, "svebitperm" },
{ HWCAP2_SVESHA3, "svesha3" },
{ HWCAP2_SVESM4, "svesm4" },
{ HWCAP2_FLAGM2, "flagm2" },
{ HWCAP2_FRINT, "frint" },
{ HWCAP2_SVEI8MM, "svei8mm" },
{ HWCAP2_SVEF32MM, "svef32mm" },
{ HWCAP2_SVEF64MM, "svef64mm" },
{ HWCAP2_SVEBF16, "svebf16" },
{ HWCAP2_I8MM, "i8mm" },
{ HWCAP2_BF16, "bf16" },
{ HWCAP2_DGH, "dgh" },
{ HWCAP2_RNG, "rng" },
{ HWCAP2_BTI, "bti" },
{ HWCAP2_MTE, "mte" },
{ HWCAP2_ECV, "ecv" },
{ HWCAP2_AFP, "afp" },
{ HWCAP2_RPRES, "rpres" },
{ HWCAP2_MTE3, "mte3" },
{ HWCAP2_SME, "sme" },
{ HWCAP2_SME_I16I64, "smei16i64" },
{ HWCAP2_SME_F64F64, "smef64f64" },
{ HWCAP2_SME_I8I32, "smei8i32" },
{ HWCAP2_SME_F16F32, "smef16f32" },
{ HWCAP2_SME_B16F32, "smeb16f32" },
{ HWCAP2_SME_F32F32, "smef32f32" },
{ HWCAP2_SME_FA64, "smefa64" },
{ HWCAP2_WFXT, "wfxt" },
{ HWCAP2_EBF16, "ebf16" },
{ HWCAP2_SVE_EBF16, "sveebf16" },
{ HWCAP2_CSSC, "cssc" },
{ HWCAP2_RPRFM, "rprfm" },
{ HWCAP2_SVE2P1, "sve2p1" },
{ HWCAP2_SME2, "sme2" },
{ HWCAP2_SME2P1, "sme2p1" },
{ HWCAP2_SME_I16I32, "smei16i32" },
{ HWCAP2_SME_BI32I32, "smebi32i32" },
{ HWCAP2_SME_B16B16, "smeb16b16" },
{ HWCAP2_SME_F16F16, "smef16f16" },
{ HWCAP2_MOPS, "mops" },
{ HWCAP2_HBC, "hbc" },
{ HWCAP2_SVE_B16B16, "sveb16b16" },
{ HWCAP2_LRCPC3, "lrcpc3" },
{ HWCAP2_LSE128, "lse128" },
{ HWCAP2_FPMR, "fpmr" },
{ HWCAP2_LUT, "lut" },
{ HWCAP2_FAMINMAX, "faminmax" },
{ HWCAP2_F8CVT, "f8cvt" },
{ HWCAP2_F8FMA, "f8fma" },
{ HWCAP2_F8DP4, "f8dp4" },
{ HWCAP2_F8DP2, "f8dp2" },
{ HWCAP2_F8E4M3, "f8e4m3" },
{ HWCAP2_F8E5M2, "f8e5m2" },
{ HWCAP2_SME_LUTV2, "smelutv2" },
{ HWCAP2_SME_F8F16, "smef8f16" },
{ HWCAP2_SME_F8F32, "smef8f32" },
{ HWCAP2_SME_SF8FMA, "smesf8fma" },
{ HWCAP2_SME_SF8DP4, "smesf8dp4" },
{ HWCAP2_SME_SF8DP2, "smesf8dp2" },
{ HWCAP2_POE, "poe" },
};
static const struct hwcap_table_entry hwcap3[] = {
{ HWCAP3_MTE_FAR, "mtefar" },
{ HWCAP3_MTE_STORE_ONLY, "mtestoreonly" },
{ HWCAP3_LSFE, "lsfe" },
{ HWCAP3_LS64, "ls64" },
{ HWCAP3_SVE_B16MM, "sveb16mm" },
{ HWCAP3_SVE2P3, "sve2p3" },
{ HWCAP3_SME_LUT6, "smelut6" },
{ HWCAP3_SME2P3, "sme2p3" },
{ HWCAP3_F16MM, "f16mm" },
{ HWCAP3_F16F32DOT, "f16f32dot" },
{ HWCAP3_F16F32MM, "f16f32mm" },
{ HWCAP3_SVE_LUT6, "svelut6" },
};
static bool given_flag_in_hwcap_set(
const char *flag,
const struct hwcap_table_entry *set,
size_t set_size,
unsigned long val) {
assert(set);
for (size_t i = 0; i < set_size; i++)
if ((set[i].flag_mask & val) && streq(flag, set[i].name))
return true;
return false;
}
static bool real_has_cpu_with_flag(const char *flag) {
unsigned long val;
val = getauxval(AT_HWCAP);
if (given_flag_in_hwcap_set(flag, hwcap, ELEMENTSOF(hwcap), val))
return true;
val = getauxval(AT_HWCAP2);
if (given_flag_in_hwcap_set(flag, hwcap2, ELEMENTSOF(hwcap2), val))
return true;
val = getauxval(AT_HWCAP3);
if (given_flag_in_hwcap_set(flag, hwcap3, ELEMENTSOF(hwcap3), val))
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__)
/* Check the architecture-specific CPU feature interface when available. */
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
return real_has_cpu_with_flag(flag);
#else
return false;
+9
View File
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include_next <elf.h> /* IWYU pragma: export */
/* AT_HWCAP3 was added in glibc 2.39. */
#ifndef AT_HWCAP3
#define AT_HWCAP3 29
#endif
+229
View File
@@ -0,0 +1,229 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include_next <bits/hwcap.h> /* IWYU pragma: export */
/* All other capabilities are defined in glibc-2.34 / musl 1.2.6 or earlier */
#if defined(__aarch64__)
#ifndef HWCAP_GCS
#define HWCAP_GCS (1UL << 32)
#endif
#ifndef HWCAP_CMPBR
#define HWCAP_CMPBR (1UL << 33)
#endif
#ifndef HWCAP_FPRCVT
#define HWCAP_FPRCVT (1UL << 34)
#endif
#ifndef HWCAP_F8MM8
#define HWCAP_F8MM8 (1UL << 35)
#endif
#ifndef HWCAP_F8MM4
#define HWCAP_F8MM4 (1UL << 36)
#endif
#ifndef HWCAP_SVE_F16MM
#define HWCAP_SVE_F16MM (1UL << 37)
#endif
#ifndef HWCAP_SVE_ELTPERM
#define HWCAP_SVE_ELTPERM (1UL << 38)
#endif
#ifndef HWCAP_SVE_AES2
#define HWCAP_SVE_AES2 (1UL << 39)
#endif
#ifndef HWCAP_SVE_BFSCALE
#define HWCAP_SVE_BFSCALE (1UL << 40)
#endif
#ifndef HWCAP_SVE2P2
#define HWCAP_SVE2P2 (1UL << 41)
#endif
#ifndef HWCAP_SME2P2
#define HWCAP_SME2P2 (1UL << 42)
#endif
#ifndef HWCAP_SME_SBITPERM
#define HWCAP_SME_SBITPERM (1UL << 43)
#endif
#ifndef HWCAP_SME_AES
#define HWCAP_SME_AES (1UL << 44)
#endif
#ifndef HWCAP_SME_SFEXPA
#define HWCAP_SME_SFEXPA (1UL << 45)
#endif
#ifndef HWCAP_SME_STMOP
#define HWCAP_SME_STMOP (1UL << 46)
#endif
#ifndef HWCAP_SME_SMOP4
#define HWCAP_SME_SMOP4 (1UL << 47)
#endif
#ifndef HWCAP2_ECV
#define HWCAP2_ECV (1 << 19)
#endif
#ifndef HWCAP2_AFP
#define HWCAP2_AFP (1 << 20)
#endif
#ifndef HWCAP2_RPRES
#define HWCAP2_RPRES (1 << 21)
#endif
#ifndef HWCAP2_MTE3
#define HWCAP2_MTE3 (1 << 22)
#endif
#ifndef HWCAP2_SME
#define HWCAP2_SME (1 << 23)
#endif
#ifndef HWCAP2_SME_I16I64
#define HWCAP2_SME_I16I64 (1 << 24)
#endif
#ifndef HWCAP2_SME_F64F64
#define HWCAP2_SME_F64F64 (1 << 25)
#endif
#ifndef HWCAP2_SME_I8I32
#define HWCAP2_SME_I8I32 (1 << 26)
#endif
#ifndef HWCAP2_SME_F16F32
#define HWCAP2_SME_F16F32 (1 << 27)
#endif
#ifndef HWCAP2_SME_B16F32
#define HWCAP2_SME_B16F32 (1 << 28)
#endif
#ifndef HWCAP2_SME_F32F32
#define HWCAP2_SME_F32F32 (1 << 29)
#endif
#ifndef HWCAP2_SME_FA64
#define HWCAP2_SME_FA64 (1 << 30)
#endif
#ifndef HWCAP2_WFXT
#define HWCAP2_WFXT (1UL << 31)
#endif
#ifndef HWCAP2_EBF16
#define HWCAP2_EBF16 (1UL << 32)
#endif
#ifndef HWCAP2_SVE_EBF16
#define HWCAP2_SVE_EBF16 (1UL << 33)
#endif
#ifndef HWCAP2_CSSC
#define HWCAP2_CSSC (1UL << 34)
#endif
#ifndef HWCAP2_RPRFM
#define HWCAP2_RPRFM (1UL << 35)
#endif
#ifndef HWCAP2_SVE2P1
#define HWCAP2_SVE2P1 (1UL << 36)
#endif
#ifndef HWCAP2_SME2
#define HWCAP2_SME2 (1UL << 37)
#endif
#ifndef HWCAP2_SME2P1
#define HWCAP2_SME2P1 (1UL << 38)
#endif
#ifndef HWCAP2_SME_I16I32
#define HWCAP2_SME_I16I32 (1UL << 39)
#endif
#ifndef HWCAP2_SME_BI32I32
#define HWCAP2_SME_BI32I32 (1UL << 40)
#endif
#ifndef HWCAP2_SME_B16B16
#define HWCAP2_SME_B16B16 (1UL << 41)
#endif
#ifndef HWCAP2_SME_F16F16
#define HWCAP2_SME_F16F16 (1UL << 42)
#endif
#ifndef HWCAP2_MOPS
#define HWCAP2_MOPS (1UL << 43)
#endif
#ifndef HWCAP2_HBC
#define HWCAP2_HBC (1UL << 44)
#endif
#ifndef HWCAP2_SVE_B16B16
#define HWCAP2_SVE_B16B16 (1UL << 45)
#endif
#ifndef HWCAP2_LRCPC3
#define HWCAP2_LRCPC3 (1UL << 46)
#endif
#ifndef HWCAP2_LSE128
#define HWCAP2_LSE128 (1UL << 47)
#endif
#ifndef HWCAP2_FPMR
#define HWCAP2_FPMR (1UL << 48)
#endif
#ifndef HWCAP2_LUT
#define HWCAP2_LUT (1UL << 49)
#endif
#ifndef HWCAP2_FAMINMAX
#define HWCAP2_FAMINMAX (1UL << 50)
#endif
#ifndef HWCAP2_F8CVT
#define HWCAP2_F8CVT (1UL << 51)
#endif
#ifndef HWCAP2_F8FMA
#define HWCAP2_F8FMA (1UL << 52)
#endif
#ifndef HWCAP2_F8DP4
#define HWCAP2_F8DP4 (1UL << 53)
#endif
#ifndef HWCAP2_F8DP2
#define HWCAP2_F8DP2 (1UL << 54)
#endif
#ifndef HWCAP2_F8E4M3
#define HWCAP2_F8E4M3 (1UL << 55)
#endif
#ifndef HWCAP2_F8E5M2
#define HWCAP2_F8E5M2 (1UL << 56)
#endif
#ifndef HWCAP2_SME_LUTV2
#define HWCAP2_SME_LUTV2 (1UL << 57)
#endif
#ifndef HWCAP2_SME_F8F16
#define HWCAP2_SME_F8F16 (1UL << 58)
#endif
#ifndef HWCAP2_SME_F8F32
#define HWCAP2_SME_F8F32 (1UL << 59)
#endif
#ifndef HWCAP2_SME_SF8FMA
#define HWCAP2_SME_SF8FMA (1UL << 60)
#endif
#ifndef HWCAP2_SME_SF8DP4
#define HWCAP2_SME_SF8DP4 (1UL << 61)
#endif
#ifndef HWCAP2_SME_SF8DP2
#define HWCAP2_SME_SF8DP2 (1UL << 62)
#endif
#ifndef HWCAP2_POE
#define HWCAP2_POE (1UL << 63)
#endif
#ifndef HWCAP3_MTE_FAR
#define HWCAP3_MTE_FAR (1UL << 0)
#endif
#ifndef HWCAP3_MTE_STORE_ONLY
#define HWCAP3_MTE_STORE_ONLY (1UL << 1)
#endif
#ifndef HWCAP3_LSFE
#define HWCAP3_LSFE (1UL << 2)
#endif
#ifndef HWCAP3_LS64
#define HWCAP3_LS64 (1UL << 3)
#endif
#ifndef HWCAP3_SVE_B16MM
#define HWCAP3_SVE_B16MM (1UL << 4)
#endif
#ifndef HWCAP3_SVE2P3
#define HWCAP3_SVE2P3 (1UL << 5)
#endif
#ifndef HWCAP3_SME_LUT6
#define HWCAP3_SME_LUT6 (1UL << 6)
#endif
#ifndef HWCAP3_SME2P3
#define HWCAP3_SME2P3 (1UL << 7)
#endif
#ifndef HWCAP3_F16MM
#define HWCAP3_F16MM (1UL << 8)
#endif
#ifndef HWCAP3_F16F32DOT
#define HWCAP3_F16F32DOT (1UL << 9)
#endif
#ifndef HWCAP3_F16F32MM
#define HWCAP3_F16F32MM (1UL << 10)
#endif
#ifndef HWCAP3_SVE_LUT6
#define HWCAP3_SVE_LUT6 (1UL << 11)
#endif
#endif
+28 -7
View File
@@ -516,21 +516,19 @@ static int condition_test_virtualization(Condition *c, char **env) {
return v != VIRTUALIZATION_NONE && streq(c->parameter, virtualization_to_string(v));
}
static int condition_test_architecture(Condition *c, char **env) {
static int condition_test_architecture_parameter(const char *parameter) {
Architecture a, b;
assert(c);
assert(c->parameter);
assert(c->type == CONDITION_ARCHITECTURE);
assert(parameter);
a = uname_architecture();
if (a < 0)
return a;
if (streq(c->parameter, "native"))
if (streq(parameter, "native"))
b = native_architecture();
else {
b = architecture_from_string(c->parameter);
b = architecture_from_string(parameter);
if (b < 0) /* unknown architecture? Then it's definitely not ours */
return false;
}
@@ -538,6 +536,14 @@ static int condition_test_architecture(Condition *c, char **env) {
return a == b;
}
static int condition_test_architecture(Condition *c, char **env) {
assert(c);
assert(c->parameter);
assert(c->type == CONDITION_ARCHITECTURE);
return condition_test_architecture_parameter(c->parameter);
}
#define DTCOMPAT_FILE "/proc/device-tree/compatible"
static int condition_test_firmware_devicetree_compatible(const char *dtcarg) {
int r;
@@ -1080,11 +1086,26 @@ static int condition_test_path_is_read_write(Condition *c, char **env) {
}
static int condition_test_cpufeature(Condition *c, char **env) {
_cleanup_free_ char *cpu_arch = NULL, *feature = NULL;
int r;
assert(c);
assert(c->parameter);
assert(c->type == CONDITION_CPU_FEATURE);
return has_cpu_with_flag(ascii_strlower(c->parameter));
r = split_pair(c->parameter, ".", &cpu_arch, &feature);
if (r >= 0) {
r = condition_test_architecture_parameter(cpu_arch);
if (r <= 0)
return r;
} else if (r == -EINVAL) {
feature = strdup(c->parameter);
if (!feature)
return -ENOMEM;
} else
return r;
return has_cpu_with_flag(ascii_strlower(feature));
}
static int condition_test_path_is_encrypted(Condition *c, char **env) {
+39 -1
View File
@@ -846,14 +846,52 @@ TEST(condition_test_credential) {
ASSERT_OK(set_unset_env("ENCRYPTED_CREDENTIALS_DIRECTORY", d2, /* overwrite= */ true));
}
#if defined(__i386__) || defined(__x86_64__)
#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__)
TEST(condition_test_cpufeature) {
Condition *condition;
#if defined(__i386__)
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "fpu", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "x86.fpu", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "bogus.fpu", false, false)));
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
#elif defined(__x86_64__)
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "fpu", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "x86-64.fpu", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "bogus.fpu", false, false)));
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
#elif defined(__aarch64__)
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "fp", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "asimd", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "arm64.asimd", false, false)));
ASSERT_OK_POSITIVE(condition_test(condition, environ));
condition_free(condition);
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "bogus.asimd", false, false)));
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);
#endif
ASSERT_NOT_NULL((condition = condition_new(CONDITION_CPU_FEATURE, "somecpufeaturethatreallydoesntmakesense", false, false)));
ASSERT_OK_ZERO(condition_test(condition, environ));
condition_free(condition);