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" |
| 10 | |
| 11 | int qemu_icache_linesize = 0; |
| 12 | int qemu_dcache_linesize = 0; |
| 13 | |
| 14 | /* |
| 15 | * Operating system specific detection mechanisms. |
| 16 | */ |
| 17 | |
Peter Maydell | 7872375 | 2017-09-04 18:19:00 +0100 | [diff] [blame] | 18 | #if defined(_WIN32) |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 19 | |
| 20 | static void sys_cache_info(int *isize, int *dsize) |
| 21 | { |
| 22 | SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf; |
| 23 | DWORD size = 0; |
| 24 | BOOL success; |
| 25 | size_t i, n; |
| 26 | |
| 27 | /* Check for the required buffer size first. Note that if the zero |
| 28 | size we use for the probe results in success, then there is no |
| 29 | data available; fail in that case. */ |
| 30 | success = GetLogicalProcessorInformation(0, &size); |
| 31 | if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
| 32 | return; |
| 33 | } |
| 34 | |
| 35 | n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
| 36 | size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
| 37 | buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n); |
| 38 | if (!GetLogicalProcessorInformation(buf, &size)) { |
| 39 | goto fail; |
| 40 | } |
| 41 | |
| 42 | for (i = 0; i < n; i++) { |
| 43 | if (buf[i].Relationship == RelationCache |
| 44 | && buf[i].Cache.Level == 1) { |
| 45 | switch (buf[i].Cache.Type) { |
| 46 | case CacheUnified: |
| 47 | *isize = *dsize = buf[i].Cache.LineSize; |
| 48 | break; |
| 49 | case CacheInstruction: |
| 50 | *isize = buf[i].Cache.LineSize; |
| 51 | break; |
| 52 | case CacheData: |
| 53 | *dsize = buf[i].Cache.LineSize; |
| 54 | break; |
| 55 | default: |
| 56 | break; |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | fail: |
| 61 | g_free(buf); |
| 62 | } |
| 63 | |
| 64 | #elif defined(__APPLE__) \ |
| 65 | || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |
| 66 | # include <sys/sysctl.h> |
| 67 | # if defined(__APPLE__) |
| 68 | # define SYSCTL_CACHELINE_NAME "hw.cachelinesize" |
| 69 | # else |
| 70 | # define SYSCTL_CACHELINE_NAME "machdep.cacheline_size" |
| 71 | # endif |
| 72 | |
| 73 | static void sys_cache_info(int *isize, int *dsize) |
| 74 | { |
| 75 | /* There's only a single sysctl for both I/D cache line sizes. */ |
| 76 | long size; |
| 77 | size_t len = sizeof(size); |
| 78 | if (!sysctlbyname(SYSCTL_CACHELINE_NAME, &size, &len, NULL, 0)) { |
| 79 | *isize = *dsize = size; |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | #else |
| 84 | /* POSIX */ |
| 85 | |
| 86 | static void sys_cache_info(int *isize, int *dsize) |
| 87 | { |
| 88 | # ifdef _SC_LEVEL1_ICACHE_LINESIZE |
| 89 | *isize = sysconf(_SC_LEVEL1_ICACHE_LINESIZE); |
| 90 | # endif |
| 91 | # ifdef _SC_LEVEL1_DCACHE_LINESIZE |
| 92 | *dsize = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); |
| 93 | # endif |
| 94 | } |
| 95 | #endif /* sys_cache_info */ |
| 96 | |
| 97 | /* |
| 98 | * Architecture (+ OS) specific detection mechanisms. |
| 99 | */ |
| 100 | |
| 101 | #if defined(__aarch64__) |
| 102 | |
| 103 | static void arch_cache_info(int *isize, int *dsize) |
| 104 | { |
| 105 | if (*isize == 0 || *dsize == 0) { |
Pranith Kumar | 2ae96c1 | 2017-06-30 11:39:46 -0400 | [diff] [blame] | 106 | unsigned long ctr; |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 107 | |
| 108 | /* The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, |
| 109 | but (at least under Linux) these are marked protected by the |
| 110 | kernel. However, CTR_EL0 contains the minimum linesize in the |
| 111 | entire hierarchy, and is used by userspace cache flushing. */ |
| 112 | asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr)); |
| 113 | if (*isize == 0) { |
| 114 | *isize = 4 << (ctr & 0xf); |
| 115 | } |
| 116 | if (*dsize == 0) { |
| 117 | *dsize = 4 << ((ctr >> 16) & 0xf); |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | #elif defined(_ARCH_PPC) && defined(__linux__) |
Philippe Mathieu-Daudé | 810d5ca | 2017-07-10 22:55:24 -0300 | [diff] [blame] | 123 | # include "elf.h" |
Emilio G. Cota | b255b2c | 2017-06-06 20:17:04 -0400 | [diff] [blame] | 124 | |
| 125 | static void arch_cache_info(int *isize, int *dsize) |
| 126 | { |
| 127 | if (*isize == 0) { |
| 128 | *isize = qemu_getauxval(AT_ICACHEBSIZE); |
| 129 | } |
| 130 | if (*dsize == 0) { |
| 131 | *dsize = qemu_getauxval(AT_DCACHEBSIZE); |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | #else |
| 136 | static void arch_cache_info(int *isize, int *dsize) { } |
| 137 | #endif /* arch_cache_info */ |
| 138 | |
| 139 | /* |
| 140 | * ... and if all else fails ... |
| 141 | */ |
| 142 | |
| 143 | static void fallback_cache_info(int *isize, int *dsize) |
| 144 | { |
| 145 | /* If we can only find one of the two, assume they're the same. */ |
| 146 | if (*isize) { |
| 147 | if (*dsize) { |
| 148 | /* Success! */ |
| 149 | } else { |
| 150 | *dsize = *isize; |
| 151 | } |
| 152 | } else if (*dsize) { |
| 153 | *isize = *dsize; |
| 154 | } else { |
| 155 | #if defined(_ARCH_PPC) |
| 156 | /* For PPC, we're going to use the icache size computed for |
| 157 | flush_icache_range. Which means that we must use the |
| 158 | architecture minimum. */ |
| 159 | *isize = *dsize = 16; |
| 160 | #else |
| 161 | /* Otherwise, 64 bytes is not uncommon. */ |
| 162 | *isize = *dsize = 64; |
| 163 | #endif |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | static void __attribute__((constructor)) init_cache_info(void) |
| 168 | { |
| 169 | int isize = 0, dsize = 0; |
| 170 | |
| 171 | sys_cache_info(&isize, &dsize); |
| 172 | arch_cache_info(&isize, &dsize); |
| 173 | fallback_cache_info(&isize, &dsize); |
| 174 | |
| 175 | qemu_icache_linesize = isize; |
| 176 | qemu_dcache_linesize = dsize; |
| 177 | } |