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