[PATCH v4 0/5] MR11267: ntdll: Use getauxval(AT_HWCAP) to detect ARM/ARM64 processor features
The motivation here is to simplify processor feature detection on ARM/ARM64 by using `getauxval(AT_HWCAP)` instead of parsing `/proc/cpuinfo` (which is meant for human readability). Corresponding `FEAT_` strings for each feature are also included for use on macOS. And FreeBSD uses the same HWCAP_ constants as Linux, in the future this should work there with minor changes. @mstorsjo, this should build with fairly old headers too. The flags we use on ARM32 are all quite old, so I don’t think there’s a need to define them. For ARM64 I’m defining almost everything we use (`AT_HWCAP2`, and everything newer than the earliest `HWCAP_` flags). Let me know if additional defines are needed. References: - For most ARM64 PF_ features, Microsoft’s [documentation for `IsProcessorFeaturePresent()`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-pro...) documents the corresponding ARM64 `FEAT_`. - ARM’s own documentation is clunky, but googling for a `FEAT_` string will get you ARM’s documentation and what registers can be used to identify it (i.e. for `FEAT_SHA3`, [the documentation](https://developer.arm.com/documentation/109697/2026_03/Feature-descriptions/...) says that it sets `ID_AA64ISAR0_EL1.SHA3`. - The Linux kernel documents which `HWCAP_` constant corresponds to various register values: https://docs.kernel.org/arch/arm64/elf_hwcaps.html -- v4: ntdll: Define HWCAP_CPUID on ARM64. ntdll: Detect ARM64 processor features on macOS, and on Linux using getauxval(AT_HWCAP). ntdll: Don't include <asm/hwcap.h> on ARM64 Linux. ntdll: Detect ARM32 processor features using getauxval(AT_HWCAP). https://gitlab.winehq.org/wine/wine/-/merge_requests/11267
From: Brendan Shanks <bshanks@codeweavers.com> Windows sets all the ARM32 features (except EXTERNAL_CACHE_AVAILABLE) even on a CPU and OS that doesn't support ARM32. --- dlls/ntdll/unix/system.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index cf2883f05ae..85e17899df9 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -707,6 +707,12 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) if (native_machine == IMAGE_FILE_MACHINE_ARMNT) return; + features[PF_ARM_VFP_32_REGISTERS_AVAILABLE] = TRUE; + features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE] = TRUE; + features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE] = TRUE; + features[PF_ARM_64BIT_LOADSTORE_ATOMIC] = TRUE; + features[PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE] = TRUE; + features[PF_ARM_V8_INSTRUCTIONS_AVAILABLE] = TRUE; features[PF_NX_ENABLED] = TRUE; @@ -715,11 +721,6 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) { switch (supported_machines[i]) { - case IMAGE_FILE_MACHINE_ARMNT: - features[PF_ARM_VFP_32_REGISTERS_AVAILABLE] = TRUE; - features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE] = TRUE; - features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE] = TRUE; - break; case IMAGE_FILE_MACHINE_I386: features[PF_MMX_INSTRUCTIONS_AVAILABLE] = TRUE; features[PF_XMMI_INSTRUCTIONS_AVAILABLE] = TRUE; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11267
From: Brendan Shanks <bshanks@codeweavers.com> --- dlls/ntdll/unix/system.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 85e17899df9..944c0cb810a 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -558,6 +558,23 @@ static int has_feature( const char *line, const char *feat ) return 0; } +#if defined(AT_HWCAP) && defined(__arm__) +static BOOLEAN has_capability( int hwcap, unsigned long hwcap_bit ) +{ + unsigned long type; + switch (hwcap) + { + case 1: type = AT_HWCAP; break; + default: return FALSE; + } + return !!(getauxval( type ) & hwcap_bit); +} + +#define HAS_FEATURE(hwcap, hwcap_bit, ...) has_capability( hwcap, hwcap_bit ) +#else +#define HAS_FEATURE(hwcap, hwcap_bit, ...) 0 +#endif + static void init_cpu_model(void) { unsigned int implementer = 0x41, part = 0, variant = 0, revision = 0; @@ -671,9 +688,6 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) value++; if ((s = strchr( value, '\n' ))) *s = 0; if (strcmp( line, "Features" )) continue; - features[PF_ARM_VFP_32_REGISTERS_AVAILABLE] = has_feature( value, "vfpv3" ); - features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE] = has_feature( value, "neon" ); - features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE] = has_feature( value, "idivt" ); if (native_machine == IMAGE_FILE_MACHINE_ARMNT) break; features[PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE] = has_feature( value, "crc32" ); features[PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE] = has_feature( value, "aes" ); @@ -705,8 +719,11 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) features[PF_FASTFAIL_AVAILABLE] = TRUE; features[PF_COMPARE_EXCHANGE_DOUBLE] = TRUE; - if (native_machine == IMAGE_FILE_MACHINE_ARMNT) return; - +#ifdef __arm__ + features[PF_ARM_VFP_32_REGISTERS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_ARM_VFPv3 ); + features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_ARM_NEON ); + features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE] = HAS_FEATURE( 1, HWCAP_ARM_IDIVT ); +#else features[PF_ARM_VFP_32_REGISTERS_AVAILABLE] = TRUE; features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE] = TRUE; features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE] = TRUE; @@ -735,6 +752,7 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) break; } } +#endif } #endif /* End architecture specific feature detection for CPUs */ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11267
From: Brendan Shanks <bshanks@codeweavers.com> <sys/auxv.h> already defines the HWCAP_ constants. --- dlls/ntdll/unix/system.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 944c0cb810a..0bcca4a7a26 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -1965,8 +1965,6 @@ static WORD append_smbios_boot_info( struct smbios_buffer *buf ) #ifdef __aarch64__ #ifdef linux -#include <asm/hwcap.h> - static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs, WORD logical_thread_id ) { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11267
From: Brendan Shanks <bshanks@codeweavers.com> --- dlls/ntdll/unix/system.c | 253 +++++++++++++++++++++++---------------- 1 file changed, 152 insertions(+), 101 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index 0bcca4a7a26..fbcc1dd4c97 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -24,6 +24,7 @@ #include "config.h" +#include <assert.h> #include <fcntl.h> #include <string.h> #include <stdarg.h> @@ -73,6 +74,57 @@ # include <hwloc.h> #endif +#ifdef __linux__ +# define AT_HWCAP2 26 +#endif + +#if defined(__aarch64__) && defined(AT_HWCAP) +# define HWCAP_AES (1 << 3) +# define HWCAP_CRC32 (1 << 7) +# define HWCAP_ATOMICS (1 << 8) +# define HWCAP_FPHP (1 << 9) +# define HWCAP_JSCVT (1 << 13) +# define HWCAP_LRCPC (1 << 15) +# define HWCAP_SHA3 (1 << 17) +# define HWCAP_ASIMDDP (1 << 20) +# define HWCAP_SHA512 (1 << 21) +# define HWCAP_SVE (1 << 22) +# define HWCAP_USCAT (1 << 25) +# define HWCAP_SME2P2 (1UL << 42) +# define HWCAP_SME_SBITPERM (1UL << 43) +# define HWCAP_SME_AES (1UL << 44) +# define HWCAP2_SVE2 (1 << 1) +# define HWCAP2_SVEAES (1 << 2) +# define HWCAP2_SVEPMULL (1 << 3) +# define HWCAP2_SVEBITPERM (1 << 4) +# define HWCAP2_SVESHA3 (1 << 5) +# define HWCAP2_SVESM4 (1 << 6) +# define HWCAP2_SVEI8MM (1 << 9) +# define HWCAP2_SVEF32MM (1 << 10) +# define HWCAP2_SVEF64MM (1 << 11) +# define HWCAP2_SVEBF16 (1 << 12) +# define HWCAP2_I8MM (1 << 13) +# define HWCAP2_BF16 (1 << 14) +# define HWCAP2_SME (1 << 23) +# define HWCAP2_SME_I16I64 (1 << 24) +# define HWCAP2_SME_F64F64 (1 << 25) +# define HWCAP2_SME_FA64 (1 << 30) +# define HWCAP2_EBF16 (1UL << 32) +# define HWCAP2_SVE_EBF16 (1UL << 33) +# define HWCAP2_SVE2P1 (1UL << 36) +# define HWCAP2_SME2 (1UL << 37) +# define HWCAP2_SME2P1 (1UL << 38) +# define HWCAP2_SME_F16F16 (1UL << 42) +# define HWCAP2_SME_B16B16 (1UL << 41) +# define HWCAP2_SVE_B16B16 (1UL << 45) +# define HWCAP2_SME_LUTV2 (1UL << 57) +# define HWCAP2_SME_F8F16 (1UL << 58) +# define HWCAP2_SME_F8F32 (1UL << 59) +# define HWCAP2_SME_SF8FMA (1UL << 60) +# define HWCAP2_SME_SF8DP4 (1UL << 61) +# define HWCAP2_SME_SF8DP2 (1UL << 62) +#endif + #include "ntstatus.h" #include "windef.h" #include "winternl.h" @@ -523,7 +575,7 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) features[PF_AVX512F_INSTRUCTIONS_AVAILABLE] = !!(regs[1] & (1 << 16)); features[PF_RDPID_INSTRUCTION_AVAILABLE] = !!(regs[2] & (1 << 22)); features[PF_MOVDIR64B_INSTRUCTION_AVAILABLE]= !!(regs[2] & (1 << 28)); -#if defined(__linux__) && defined(AT_HWCAP2) +#if defined(__linux__) features[PF_RDWRFSGSBASE_AVAILABLE] &= !!(getauxval( AT_HWCAP2 ) & 2); #endif } @@ -545,36 +597,66 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) #elif defined(__arm__) || defined(__aarch64__) -static int has_feature( const char *line, const char *feat ) -{ - size_t len = strlen(feat); - - while (*line) - { - while (*line == ' ' || *line == '\t') line++; - if (!strncmp( line, feat, len ) && (!line[len] || isspace(line[len]))) return 1; - while (*line && *line != ' ' && *line != '\t') line++; - } - return 0; -} - -#if defined(AT_HWCAP) && defined(__arm__) +#if defined(AT_HWCAP) static BOOLEAN has_capability( int hwcap, unsigned long hwcap_bit ) { unsigned long type; switch (hwcap) { case 1: type = AT_HWCAP; break; + case 2: type = AT_HWCAP2; break; default: return FALSE; } return !!(getauxval( type ) & hwcap_bit); } #define HAS_FEATURE(hwcap, hwcap_bit, ...) has_capability( hwcap, hwcap_bit ) +#elif defined(__APPLE__) +static BOOLEAN has_feature( const char *feature ) +{ + char buf[200]; + int val; + size_t size = sizeof(val); + + snprintf( buf, sizeof(buf), "hw.optional.arm.%s", feature ); + if (!sysctlbyname( buf, &val, &size, NULL, 0 )) + return !!val; + return FALSE; +} + +static BOOLEAN has_features( int dummy, ... ) +{ + BOOLEAN ret = TRUE; + const char *feature; + va_list args; + + va_start( args, dummy ); + + while ((feature = va_arg( args, const char * ))) + { + if (has_feature( feature )) + continue; + + ret = FALSE; + break; + } + + va_end( args ); + return ret; +} +#define HAS_FEATURE(hwcap, hwcap_bit, ...) has_features( 0, __VA_ARGS__, NULL ) #else #define HAS_FEATURE(hwcap, hwcap_bit, ...) 0 #endif +static void set_feature_bitmap( ULONG flag, BOOLEAN enabled ) +{ + assert( flag >= PROCESSOR_FEATURE_MAX ); + if (!enabled) return; + flag -= PROCESSOR_FEATURE_MAX; + cpu_features_bitmap[flag / 64] |= 1ull << (flag % 64); +} + static void init_cpu_model(void) { unsigned int implementer = 0x41, part = 0, variant = 0, revision = 0; @@ -600,46 +682,6 @@ static void init_cpu_model(void) else if (!strcmp( line, "CPU part" )) part = strtoul( value, NULL, 0); else if (!strcmp( line, "CPU variant" )) variant = strtoul( value, NULL, 0); else if (!strcmp( line, "CPU revision" )) revision = strtoul( value, NULL, 0); - else if (!strcmp( line, "Features" )) - { - static const struct { ULONG flag; const char *name; } features[] = - { - { PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE, "sha3" }, - { PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE, "sha512" }, - { PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE, "i8mm" }, - { PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE, "fphp" }, - { PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE, "bf16" }, - { PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE, "ebf16" }, - { PF_ARM_SME_INSTRUCTIONS_AVAILABLE, "sme" }, - { PF_ARM_SME2_INSTRUCTIONS_AVAILABLE, "sme2" }, - { PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE, "sme2p1" }, - { PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE, "sme2p2" }, - { PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE, "smeaes" }, - { PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE, "smesbitperm" }, - /* The PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE and - * PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE flags aren't exposed by - * the Linux kernel, see - * https://lists.infradead.org/pipermail/linux-arm-kernel/2025-January/991187.h... */ - { PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE, "smesf8dp2" }, - { PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE, "smesf8dp4" }, - { PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE, "smesf8fma" }, - { PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE, "smef8f32" }, - { PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE, "smef8f16" }, - { PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE, "smef16f16" }, - { PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE, "smeb16b16" }, - { PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE, "smef64f64" }, - { PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE, "smei16i64" }, - { PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE, "smelutv2" }, - { PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE, "smefa64" }, - }; - - for (unsigned int i = 0; i < ARRAY_SIZE(features); i++) - { - ULONG flag = features[i].flag - PROCESSOR_FEATURE_MAX; - if (!has_feature( value, features[i].name )) continue; - cpu_features_bitmap[flag / 64] |= 1ull << (flag % 64); - } - } } fclose( f ); } @@ -661,6 +703,38 @@ static void init_cpu_model(void) case 0x66: strcpy( cpu_vendor, "Faraday" ); break; case 0x69: strcpy( cpu_vendor, "Intel" ); break; } + +#ifdef __aarch64__ + set_feature_bitmap( PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 1, HWCAP_SHA3, "FEAT_SHA3" ) ); + set_feature_bitmap( PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 1, HWCAP_SHA512, "FEAT_SHA512" ) ); + set_feature_bitmap( PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_I8MM, "FEAT_I8MM" ) ); + set_feature_bitmap( PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 1, HWCAP_FPHP, "FEAT_FP16" ) ); + set_feature_bitmap( PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_BF16, "FEAT_BF16" ) ); + set_feature_bitmap( PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_EBF16, "FEAT_EBF16" ) ); + set_feature_bitmap( PF_ARM_SME_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME, "FEAT_SME" ) ); + set_feature_bitmap( PF_ARM_SME2_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME2, "FEAT_SME2" ) ); + set_feature_bitmap( PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME2P1, "FEAT_SME2p1" ) ); + set_feature_bitmap( PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 1, HWCAP_SME2P2, "FEAT_SME2p2" ) ); + set_feature_bitmap( PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 1, HWCAP_SME_AES, "FEAT_SSVE_AES" ) ); + set_feature_bitmap( PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 1, HWCAP_SME_SBITPERM, "FEAT_SSVE_BitPerm" ) ); + /* The PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE and + * PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE flags aren't exposed by + * the Linux kernel, see + * https://lists.infradead.org/pipermail/linux-arm-kernel/2025-January/991187.h... */ + set_feature_bitmap( PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 0, 0, "FEAT_SSVE_F8F16MM" ) ); + set_feature_bitmap( PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 0, 0, "FEAT_SSVE_F8F32MM" ) ); + set_feature_bitmap( PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_SF8DP2, "FEAT_SSVE_FP8DOT2" ) ); + set_feature_bitmap( PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_SF8DP4, "FEAT_SSVE_FP8DOT4" ) ); + set_feature_bitmap( PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_SF8FMA, "FEAT_SSVE_FP8FMA" ) ); + set_feature_bitmap( PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_F8F32, "FEAT_SME_F8F32" ) ); + set_feature_bitmap( PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_F8F16, "FEAT_SME_F8F16" ) ); + set_feature_bitmap( PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_F16F16, "FEAT_SME_F16F16" ) ); + set_feature_bitmap( PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_B16B16, "FEAT_SME_B16B16" ) ); + set_feature_bitmap( PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_F64F64, "FEAT_SME_F64F64" ) ); + set_feature_bitmap( PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_I16I64, "FEAT_SME_I16I64" ) ); + set_feature_bitmap( PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_LUTV2, "FEAT_SME_LUTv2" ) ); + set_feature_bitmap( PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_FA64, "FEAT_SME_FA64" ) ); +#endif } static ULONGLONG get_cpu_features(void) @@ -672,50 +746,6 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) { BOOLEAN *features = data->ProcessorFeatures; -#ifdef linux - FILE *f = fopen("/proc/cpuinfo", "r"); - if (f) - { - char *s, *value, line[512]; - while (fgets( line, sizeof(line), f )) - { - /* NOTE: the ':' is the only character we can rely on */ - if (!(value = strchr(line,':'))) continue; - /* terminate the valuename */ - s = value - 1; - while ((s >= line) && (*s == ' ' || *s == '\t')) s--; - s[1] = 0; - value++; - if ((s = strchr( value, '\n' ))) *s = 0; - if (strcmp( line, "Features" )) continue; - if (native_machine == IMAGE_FILE_MACHINE_ARMNT) break; - features[PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE] = has_feature( value, "crc32" ); - features[PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE] = has_feature( value, "aes" ); - features[PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE] = has_feature( value, "atomics" ); - features[PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE] = has_feature( value, "asimddp" ); - features[PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE] = has_feature( value, "jscvt" ); - features[PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE] = has_feature( value, "lrcpc" ); - features[PF_ARM_SVE_INSTRUCTIONS_AVAILABLE] = has_feature( value, "sve" ); - features[PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE] = has_feature( value, "sve2" ); - features[PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE] = has_feature( value, "sve2p1" ); - features[PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE] = has_feature( value, "sveaes" ); - features[PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svepmull" ); - features[PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svebitperm" ); - features[PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svebf16" ); - features[PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE] = has_feature( value, "sveebf16" ); - features[PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE] = has_feature( value, "sveb16b16" ); - features[PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svesha3" ); - features[PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svesm4" ); - features[PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svei8mm" ); - features[PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svef32mm" ); - features[PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svef64mm" ); - features[PF_ARM_LSE2_AVAILABLE] = has_feature( value, "uscat" ); - break; - } - fclose( f ); - } -#endif - features[PF_FASTFAIL_AVAILABLE] = TRUE; features[PF_COMPARE_EXCHANGE_DOUBLE] = TRUE; @@ -730,8 +760,29 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data ) features[PF_ARM_64BIT_LOADSTORE_ATOMIC] = TRUE; features[PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE] = TRUE; - features[PF_ARM_V8_INSTRUCTIONS_AVAILABLE] = TRUE; - features[PF_NX_ENABLED] = TRUE; + features[PF_ARM_V8_INSTRUCTIONS_AVAILABLE] = TRUE; + features[PF_NX_ENABLED] = TRUE; + features[PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_AES, "FEAT_AES" ); + features[PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_CRC32, "FEAT_CRC32" ); + features[PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_ATOMICS, "FEAT_LSE" ); + features[PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_ASIMDDP, "FEAT_DotProd" ); + features[PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_JSCVT, "FEAT_JSCVT" ); + features[PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_LRCPC, "FEAT_LRCPC" ); + features[PF_ARM_SVE_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 1, HWCAP_SVE, "FEAT_SVE" ); + features[PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVE2, "FEAT_SVE2" ); + features[PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVE2P1, "FEAT_SVE2p1" ); + features[PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEAES, "FEAT_SVE_AES" ); + features[PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEPMULL, "FEAT_SVE_PMULL128" ); + features[PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEBITPERM, "FEAT_SVE_BitPerm" ); + features[PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEBF16, "FEAT_SVE", "FEAT_BF16" ); + features[PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVE_EBF16, "FEAT_SVE", "FEAT_EBF16" ); + features[PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVE_B16B16, "FEAT_SVE_B16B16" ); + features[PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVESHA3, "FEAT_SVE_SHA3" ); + features[PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVESM4, "FEAT_SVE_SM4" ); + features[PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEI8MM, "FEAT_SVE", "FEAT_I8MM" ); + features[PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEF32MM, "FEAT_F32MM" ); + features[PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEF64MM, "FEAT_F64MM" ); + features[PF_ARM_LSE2_AVAILABLE] = HAS_FEATURE( 1, HWCAP_USCAT, "FEAT_LSE2" ); /* add features for other architectures supported by wow64 */ for (unsigned int i = 0; i < supported_machines_count; i++) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11267
From: Brendan Shanks <bshanks@codeweavers.com> --- dlls/ntdll/unix/system.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c index fbcc1dd4c97..217d458c2db 100644 --- a/dlls/ntdll/unix/system.c +++ b/dlls/ntdll/unix/system.c @@ -83,6 +83,7 @@ # define HWCAP_CRC32 (1 << 7) # define HWCAP_ATOMICS (1 << 8) # define HWCAP_FPHP (1 << 9) +# define HWCAP_CPUID (1 << 11) # define HWCAP_JSCVT (1 << 13) # define HWCAP_LRCPC (1 << 15) # define HWCAP_SHA3 (1 << 17) @@ -2034,7 +2035,6 @@ static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs regs[regidx++] = (struct smbios_wine_id_reg_value_arm64){ 0x4000, value }; } -#ifdef HWCAP_CPUID if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) { WARN( "Skipping ID register population as kernel is missing emulation support.\n" ); @@ -2067,9 +2067,6 @@ static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs READ_ID_REG( 0x5801 ); /* CTR_EL0 */ /* Windows exposes SCTLR_EL1, ACTLR_EL1, TTBR0_EL1 and MAIR_EL1, but these are inaccessible under * linux so leave them unpopulated. */ -#else - WARN( "Skipping ID register population as HWCAP_CPUID isn't supported.\n" ); -#endif #undef READ_ID_REG #undef STR -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11267
On Tue Jun 30 10:33:21 2026 +0000, Martin Storsjö wrote:
Erm, sorry, I must have been confused in my comment above. Even with the naming `HWCAP_ARM_NEON`, glibc and musl both define it, but with differing spelling. So even with that choice we do need an `#ifndef` it we don't want it to warn. So I think we do need a fallback, with `#ifndef`, in either case. There's no single name that is universally defined by this set of three different system headers - unless we readd `<asm/hwcap.h>`, which is needed on glibc to define `HWCAP_NEON`. Ah, I had been looking at `asm/hwcap.h` when I wrote the code and forgot to check `bits/hwcap.h` when we switched to using that.
Maybe we could just use `HWCAP_ARM_NEON` without any fallback define, since `bits/hwcap.h` from glibc and musl both define it and we're not supporting any toolchains old enough to need it defined. That leaves out FreeBSD, but honestly I don't think there's anyone building or using Wine on ARM32 FreeBSD. We didn't have any feature detection code path for it before either. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11267#note_144557
On Tue Jun 30 20:04:52 2026 +0000, Brendan Shanks wrote:
Ah, I had been looking at `asm/hwcap.h` when I wrote the code and forgot to check `bits/hwcap.h` when we switched to using that. Maybe we could just use `HWCAP_ARM_NEON` without any fallback define, since `bits/hwcap.h` from glibc and musl both define it and we're not supporting any toolchains old enough to need it defined. That leaves out FreeBSD, but honestly I don't think there's anyone building or using Wine on ARM32 FreeBSD. We didn't have any feature detection code path for it before either. Ok, fair enough, that works for me too. Yeah ARM32 support overall is very esoteric at this point in any case.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11267#note_144559
This merge request was approved by Martin Storsjö. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11267
Thanks, this seems to work fine for me now! -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11267#note_144562
Maybe we could just use HWCAP_ARM_NEON without any fallback define, since bits/hwcap.h from glibc and musl both define it and we're not supporting any toolchains old enough to need it defined.
That breaks the Android build, it only has `asm/hwcap.h` with `HWCAP_NEON` etc. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11267#note_144638
On Wed Jul 1 21:00:13 2026 +0000, Alexandre Julliard wrote:
Maybe we could just use HWCAP_ARM_NEON without any fallback define, since bits/hwcap.h from glibc and musl both define it and we're not supporting any toolchains old enough to need it defined. That breaks the Android build, it only has `asm/hwcap.h` with `HWCAP_NEON` etc. Ugh, I forgot about Android. With that in mind I think it's best to use `HWCAP_NEON`, and include `asm/hwcap.h` to get the define on glibc systems. `asm/hwcap.h` comes from the kernel uapi headers (typically installed by the `linux-headers` package), they should be as acceptable to use as headers from the C library.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11267#note_144662
participants (4)
-
Alexandre Julliard (@julliard) -
Brendan Shanks -
Brendan Shanks (@bshanks) -
Martin Storsjö (@mstorsjo)