1 /* 2 * SPDX-License-Identifier: GPL-2.0-or-later 3 * Host specific cpu identification for AArch64. 4 */ 5 6 #include "qemu/osdep.h" 7 #include "host/cpuinfo.h" 8 9 #ifdef CONFIG_LINUX 10 # ifdef CONFIG_GETAUXVAL 11 # include <sys/auxv.h> 12 # else 13 # include <asm/hwcap.h> 14 # include "elf.h" 15 # endif 16 # ifndef HWCAP2_BTI 17 # define HWCAP2_BTI 0 /* added in glibc 2.32 */ 18 # endif 19 #endif 20 #ifdef CONFIG_ELF_AUX_INFO 21 #include <sys/auxv.h> 22 #endif 23 #ifdef CONFIG_DARWIN 24 # include <sys/sysctl.h> 25 #endif 26 #if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) 27 # include <machine/armreg.h> 28 # include <machine/cpu.h> 29 # include <sys/types.h> 30 # include <sys/sysctl.h> 31 #endif 32 33 unsigned cpuinfo; 34 35 #ifdef CONFIG_DARWIN 36 static bool sysctl_for_bool(const char *name) 37 { 38 int val = 0; 39 size_t len = sizeof(val); 40 41 if (sysctlbyname(name, &val, &len, NULL, 0) == 0) { 42 return val != 0; 43 } 44 45 /* 46 * We might in the future ask for properties not present in older kernels, 47 * but we're only asking about static properties, all of which should be 48 * 'int'. So we shouldn't see ENOMEM (val too small), or any of the other 49 * more exotic errors. 50 */ 51 assert(errno == ENOENT); 52 return false; 53 } 54 #endif 55 56 /* Called both as constructor and (possibly) via other constructors. */ 57 unsigned __attribute__((constructor)) cpuinfo_init(void) 58 { 59 unsigned info = cpuinfo; 60 61 if (info) { 62 return info; 63 } 64 65 info = CPUINFO_ALWAYS; 66 67 #if defined(CONFIG_LINUX) || defined(CONFIG_ELF_AUX_INFO) 68 unsigned long hwcap = qemu_getauxval(AT_HWCAP); 69 info |= (hwcap & HWCAP_ATOMICS ? CPUINFO_LSE : 0); 70 info |= (hwcap & HWCAP_USCAT ? CPUINFO_LSE2 : 0); 71 info |= (hwcap & HWCAP_AES ? CPUINFO_AES : 0); 72 info |= (hwcap & HWCAP_PMULL ? CPUINFO_PMULL : 0); 73 74 unsigned long hwcap2 = qemu_getauxval(AT_HWCAP2); 75 info |= (hwcap2 & HWCAP2_BTI ? CPUINFO_BTI : 0); 76 #endif 77 #ifdef CONFIG_DARWIN 78 info |= sysctl_for_bool("hw.optional.arm.FEAT_LSE") * CPUINFO_LSE; 79 info |= sysctl_for_bool("hw.optional.arm.FEAT_LSE2") * CPUINFO_LSE2; 80 info |= sysctl_for_bool("hw.optional.arm.FEAT_AES") * CPUINFO_AES; 81 info |= sysctl_for_bool("hw.optional.arm.FEAT_PMULL") * CPUINFO_PMULL; 82 info |= sysctl_for_bool("hw.optional.arm.FEAT_BTI") * CPUINFO_BTI; 83 #endif 84 #if defined(__OpenBSD__) && !defined(CONFIG_ELF_AUX_INFO) 85 int mib[2]; 86 uint64_t isar0; 87 uint64_t pfr1; 88 size_t len; 89 90 mib[0] = CTL_MACHDEP; 91 mib[1] = CPU_ID_AA64ISAR0; 92 len = sizeof(isar0); 93 if (sysctl(mib, 2, &isar0, &len, NULL, 0) != -1) { 94 if (ID_AA64ISAR0_ATOMIC(isar0) >= ID_AA64ISAR0_ATOMIC_IMPL) { 95 info |= CPUINFO_LSE; 96 } 97 if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_BASE) { 98 info |= CPUINFO_AES; 99 } 100 if (ID_AA64ISAR0_AES(isar0) >= ID_AA64ISAR0_AES_PMULL) { 101 info |= CPUINFO_PMULL; 102 } 103 } 104 105 mib[0] = CTL_MACHDEP; 106 mib[1] = CPU_ID_AA64PFR1; 107 len = sizeof(pfr1); 108 if (sysctl(mib, 2, &pfr1, &len, NULL, 0) != -1) { 109 if (ID_AA64PFR1_BT(pfr1) >= ID_AA64PFR1_BT_IMPL) { 110 info |= CPUINFO_BTI; 111 } 112 } 113 #endif 114 115 cpuinfo = info; 116 return info; 117 } 118