diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index 347fc816d0..b9a346897e 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1988,11 +1988,10 @@ 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: + On i386 and x86-64, verify that a given CPU feature is available via the + CPUID instruction. It checks the leaves 1, + 7, 0x80000001, and 0x80000007. Valid + values are: fpu, vme, de, @@ -2045,6 +2044,27 @@ abm, constant_tsc. + On arm64, valid values are the corresponding arm64 feature names, + such as + asimd, + paca, + pacg, + bti, + mte, + among others. For the full list, see the documentation on + ARM64 ELF hwcaps. + + To avoid ambiguities in mixed-architecture fleets, the value can be prefixed + with the architecture name using the form ARCH.FEATURE. If the + architecture prefix does not match the running system, the condition evaluates + to false. For example, one can specify arm64.bti on arm64 + systems to check if bti is supported. In this example, + ConditionCPUFeature=arm64.bti and + ConditionCPUFeature=bti are functionally equivalent. + See ConditionArchitecture= for valid architecture names. + + On architectures other than x86 and arm64, it is assumed that the CPU does not support the given feature. + diff --git a/src/basic/virt.c b/src/basic/virt.c index 81470c2e83..aa3f4f2b58 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -3,6 +3,9 @@ #if defined(__i386__) || defined(__x86_64__) #include #endif +#if defined(__aarch64__) +#include +#endif #include #include #include @@ -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; diff --git a/src/include/glibc/elf.h b/src/include/glibc/elf.h new file mode 100644 index 0000000000..886e4d3528 --- /dev/null +++ b/src/include/glibc/elf.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include_next /* IWYU pragma: export */ + +/* AT_HWCAP3 was added in glibc 2.39. */ +#ifndef AT_HWCAP3 +#define AT_HWCAP3 29 +#endif diff --git a/src/include/override/bits/hwcap.h b/src/include/override/bits/hwcap.h new file mode 100644 index 0000000000..154ec71b50 --- /dev/null +++ b/src/include/override/bits/hwcap.h @@ -0,0 +1,229 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include_next /* 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 diff --git a/src/shared/condition.c b/src/shared/condition.c index fc77d9f06e..2a831371c6 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -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) { diff --git a/src/test/test-condition.c b/src/test/test-condition.c index a35d08ef63..52990981ff 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -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);