Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 1 | /* |
| 2 | * cacheinfo.c - helpers to query the host about its caches |
| 3 | * |
| 4 | * Copyright (C) 2017, Emilio G. Cota <cota@braap.org> |
| 5 | * License: GNU GPL, version 2 or later. |
| 6 | * See the COPYING file in the top-level directory. |
| 7 | */ |
| 8 | |
| 9 | #include "qemu/osdep.h" |
Emilio G. Cota | 5fe2103 | 2018-09-10 19:27:41 -0400 | [diff] [blame] | 10 | #include "qemu/host-utils.h" |
Emilio G. Cota | 782da5b | 2018-09-10 19:27:42 -0400 | [diff] [blame] | 11 | #include "qemu/atomic.h" |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 12 | |
| 13 | int qemu_icache_linesize = 0; |
Emilio G. Cota | 5fe2103 | 2018-09-10 19:27:41 -0400 | [diff] [blame] | 14 | int qemu_icache_linesize_log; |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 15 | int qemu_dcache_linesize = 0; |
Emilio G. Cota | 5fe2103 | 2018-09-10 19:27:41 -0400 | [diff] [blame] | 16 | int qemu_dcache_linesize_log; |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 17 | |
| 18 | /* |
| 19 | * Operating system specific detection mechanisms. |
| 20 | */ |
| 21 | |
Peter Maydell | 7872375 | 2017-09-04 18:19:00 +0100 | [diff] [blame] | 22 | #if defined(_WIN32) |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 23 | |
| 24 | static void sys_cache_info(int *isize, int *dsize) |
| 25 | { |
| 26 | SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf; |
| 27 | DWORD size = 0; |
| 28 | BOOL success; |
| 29 | size_t i, n; |
| 30 | |
| 31 | /* Check for the required buffer size first. Note that if the zero |
| 32 | size we use for the probe results in success, then there is no |
| 33 | data available; fail in that case. */ |
| 34 | success = GetLogicalProcessorInformation(0, &size); |
| 35 | if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
| 36 | return; |
| 37 | } |
| 38 | |
| 39 | n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
| 40 | size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
| 41 | buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n); |
| 42 | if (!GetLogicalProcessorInformation(buf, &size)) { |
| 43 | goto fail; |
| 44 | } |
| 45 | |
| 46 | for (i = 0; i < n; i++) { |
| 47 | if (buf[i].Relationship == RelationCache |
| 48 | && buf[i].Cache.Level == 1) { |
| 49 | switch (buf[i].Cache.Type) { |
| 50 | case CacheUnified: |
| 51 | *isize = *dsize = buf[i].Cache.LineSize; |
| 52 | break; |
| 53 | case CacheInstruction: |
| 54 | *isize = buf[i].Cache.LineSize; |
| 55 | break; |
| 56 | case CacheData: |
| 57 | *dsize = buf[i].Cache.LineSize; |
| 58 | break; |
| 59 | default: |
| 60 | break; |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | fail: |
| 65 | g_free(buf); |
| 66 | } |
| 67 | |
Justin Hibbits | 5ca156c | 2019-08-21 10:25:46 +0200 | [diff] [blame] | 68 | #elif defined(__APPLE__) |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 69 | # include <sys/sysctl.h> |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 70 | static void sys_cache_info(int *isize, int *dsize) |
| 71 | { |
| 72 | /* There's only a single sysctl for both I/D cache line sizes. */ |
| 73 | long size; |
| 74 | size_t len = sizeof(size); |
Justin Hibbits | 5ca156c | 2019-08-21 10:25:46 +0200 | [diff] [blame] | 75 | if (!sysctlbyname("hw.cachelinesize", &size, &len, NULL, 0)) { |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 76 | *isize = *dsize = size; |
| 77 | } |
| 78 | } |
Justin Hibbits | 5ca156c | 2019-08-21 10:25:46 +0200 | [diff] [blame] | 79 | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |
| 80 | # include <sys/sysctl.h> |
| 81 | static void sys_cache_info(int *isize, int *dsize) |
| 82 | { |
| 83 | /* There's only a single sysctl for both I/D cache line sizes. */ |
| 84 | int size; |
| 85 | size_t len = sizeof(size); |
| 86 | if (!sysctlbyname("machdep.cacheline_size", &size, &len, NULL, 0)) { |
| 87 | *isize = *dsize = size; |
| 88 | } |
| 89 | } |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 90 | #else |
| 91 | /* POSIX */ |
| 92 | |
| 93 | static void sys_cache_info(int *isize, int *dsize) |
| 94 | { |
| 95 | # ifdef _SC_LEVEL1_ICACHE_LINESIZE |
Carlos Santos | 00b5032 | 2019-10-17 09:37:13 -0300 | [diff] [blame] | 96 | int tmp_isize = (int) sysconf(_SC_LEVEL1_ICACHE_LINESIZE); |
| 97 | if (tmp_isize > 0) { |
| 98 | *isize = tmp_isize; |
| 99 | } |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 100 | # endif |
| 101 | # ifdef _SC_LEVEL1_DCACHE_LINESIZE |
Carlos Santos | 00b5032 | 2019-10-17 09:37:13 -0300 | [diff] [blame] | 102 | int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); |
| 103 | if (tmp_dsize > 0) { |
| 104 | *dsize = tmp_dsize; |
| 105 | } |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 106 | # endif |
| 107 | } |
| 108 | #endif /* sys_cache_info */ |
| 109 | |
| 110 | /* |
| 111 | * Architecture (+ OS) specific detection mechanisms. |
| 112 | */ |
| 113 | |
| 114 | #if defined(__aarch64__) |
| 115 | |
| 116 | static void arch_cache_info(int *isize, int *dsize) |
| 117 | { |
| 118 | if (*isize == 0 || *dsize == 0) { |
Cao Jiaxi | 8041336 | 2019-05-07 12:55:03 +0100 | [diff] [blame] | 119 | uint64_t ctr; |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 120 | |
| 121 | /* The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, |
| 122 | but (at least under Linux) these are marked protected by the |
| 123 | kernel. However, CTR_EL0 contains the minimum linesize in the |
| 124 | entire hierarchy, and is used by userspace cache flushing. */ |
| 125 | asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr)); |
| 126 | if (*isize == 0) { |
| 127 | *isize = 4 << (ctr & 0xf); |
| 128 | } |
| 129 | if (*dsize == 0) { |
| 130 | *dsize = 4 << ((ctr >> 16) & 0xf); |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | #elif defined(_ARCH_PPC) && defined(__linux__) |
Philippe Mathieu-Daudé | 810d5ca | 2017-07-10 22:55:24 -0300 | [diff] [blame] | 136 | # include "elf.h" |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 137 | |
| 138 | static void arch_cache_info(int *isize, int *dsize) |
| 139 | { |
| 140 | if (*isize == 0) { |
| 141 | *isize = qemu_getauxval(AT_ICACHEBSIZE); |
| 142 | } |
| 143 | if (*dsize == 0) { |
| 144 | *dsize = qemu_getauxval(AT_DCACHEBSIZE); |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | #else |
| 149 | static void arch_cache_info(int *isize, int *dsize) { } |
| 150 | #endif /* arch_cache_info */ |
| 151 | |
| 152 | /* |
| 153 | * ... and if all else fails ... |
| 154 | */ |
| 155 | |
| 156 | static void fallback_cache_info(int *isize, int *dsize) |
| 157 | { |
| 158 | /* If we can only find one of the two, assume they're the same. */ |
| 159 | if (*isize) { |
| 160 | if (*dsize) { |
| 161 | /* Success! */ |
| 162 | } else { |
| 163 | *dsize = *isize; |
| 164 | } |
| 165 | } else if (*dsize) { |
| 166 | *isize = *dsize; |
| 167 | } else { |
| 168 | #if defined(_ARCH_PPC) |
Richard Henderson | 1da8de3 | 2020-12-12 10:38:21 -0600 | [diff] [blame] | 169 | /* |
| 170 | * For PPC, we're going to use the cache sizes computed for |
| 171 | * flush_idcache_range. Which means that we must use the |
| 172 | * architecture minimum. |
| 173 | */ |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 174 | *isize = *dsize = 16; |
| 175 | #else |
| 176 | /* Otherwise, 64 bytes is not uncommon. */ |
| 177 | *isize = *dsize = 64; |
| 178 | #endif |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | static void __attribute__((constructor)) init_cache_info(void) |
| 183 | { |
| 184 | int isize = 0, dsize = 0; |
| 185 | |
| 186 | sys_cache_info(&isize, &dsize); |
| 187 | arch_cache_info(&isize, &dsize); |
| 188 | fallback_cache_info(&isize, &dsize); |
| 189 | |
Emilio G. Cota | 5fe2103 | 2018-09-10 19:27:41 -0400 | [diff] [blame] | 190 | assert((isize & (isize - 1)) == 0); |
| 191 | assert((dsize & (dsize - 1)) == 0); |
| 192 | |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 193 | qemu_icache_linesize = isize; |
Emilio G. Cota | 5fe2103 | 2018-09-10 19:27:41 -0400 | [diff] [blame] | 194 | qemu_icache_linesize_log = ctz32(isize); |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 195 | qemu_dcache_linesize = dsize; |
Emilio G. Cota | 5fe2103 | 2018-09-10 19:27:41 -0400 | [diff] [blame] | 196 | qemu_dcache_linesize_log = ctz32(dsize); |
Emilio G. Cota | 782da5b | 2018-09-10 19:27:42 -0400 | [diff] [blame] | 197 | |
Stefan Hajnoczi | d73415a | 2020-09-23 11:56:46 +0100 | [diff] [blame] | 198 | qatomic64_init(); |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 199 | } |