| /* |
| * cacheinfo.c - helpers to query the host about its caches |
| * |
| * Copyright (C) 2017, Emilio G. Cota <cota@braap.org> |
| * License: GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| */ |
| |
| #include "qemu/osdep.h" |
| |
| int qemu_icache_linesize = 0; |
| int qemu_dcache_linesize = 0; |
| |
| /* |
| * Operating system specific detection mechanisms. |
| */ |
| |
| #if defined(_WIN32) |
| |
| static void sys_cache_info(int *isize, int *dsize) |
| { |
| SYSTEM_LOGICAL_PROCESSOR_INFORMATION *buf; |
| DWORD size = 0; |
| BOOL success; |
| size_t i, n; |
| |
| /* Check for the required buffer size first. Note that if the zero |
| size we use for the probe results in success, then there is no |
| data available; fail in that case. */ |
| success = GetLogicalProcessorInformation(0, &size); |
| if (success || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
| return; |
| } |
| |
| n = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
| size = n * sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); |
| buf = g_new0(SYSTEM_LOGICAL_PROCESSOR_INFORMATION, n); |
| if (!GetLogicalProcessorInformation(buf, &size)) { |
| goto fail; |
| } |
| |
| for (i = 0; i < n; i++) { |
| if (buf[i].Relationship == RelationCache |
| && buf[i].Cache.Level == 1) { |
| switch (buf[i].Cache.Type) { |
| case CacheUnified: |
| *isize = *dsize = buf[i].Cache.LineSize; |
| break; |
| case CacheInstruction: |
| *isize = buf[i].Cache.LineSize; |
| break; |
| case CacheData: |
| *dsize = buf[i].Cache.LineSize; |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| fail: |
| g_free(buf); |
| } |
| |
| #elif defined(__APPLE__) \ |
| || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) |
| # include <sys/sysctl.h> |
| # if defined(__APPLE__) |
| # define SYSCTL_CACHELINE_NAME "hw.cachelinesize" |
| # else |
| # define SYSCTL_CACHELINE_NAME "machdep.cacheline_size" |
| # endif |
| |
| static void sys_cache_info(int *isize, int *dsize) |
| { |
| /* There's only a single sysctl for both I/D cache line sizes. */ |
| long size; |
| size_t len = sizeof(size); |
| if (!sysctlbyname(SYSCTL_CACHELINE_NAME, &size, &len, NULL, 0)) { |
| *isize = *dsize = size; |
| } |
| } |
| |
| #else |
| /* POSIX */ |
| |
| static void sys_cache_info(int *isize, int *dsize) |
| { |
| # ifdef _SC_LEVEL1_ICACHE_LINESIZE |
| *isize = sysconf(_SC_LEVEL1_ICACHE_LINESIZE); |
| # endif |
| # ifdef _SC_LEVEL1_DCACHE_LINESIZE |
| *dsize = sysconf(_SC_LEVEL1_DCACHE_LINESIZE); |
| # endif |
| } |
| #endif /* sys_cache_info */ |
| |
| /* |
| * Architecture (+ OS) specific detection mechanisms. |
| */ |
| |
| #if defined(__aarch64__) |
| |
| static void arch_cache_info(int *isize, int *dsize) |
| { |
| if (*isize == 0 || *dsize == 0) { |
| unsigned long ctr; |
| |
| /* The real cache geometry is in CCSIDR_EL1/CLIDR_EL1/CSSELR_EL1, |
| but (at least under Linux) these are marked protected by the |
| kernel. However, CTR_EL0 contains the minimum linesize in the |
| entire hierarchy, and is used by userspace cache flushing. */ |
| asm volatile("mrs\t%0, ctr_el0" : "=r"(ctr)); |
| if (*isize == 0) { |
| *isize = 4 << (ctr & 0xf); |
| } |
| if (*dsize == 0) { |
| *dsize = 4 << ((ctr >> 16) & 0xf); |
| } |
| } |
| } |
| |
| #elif defined(_ARCH_PPC) && defined(__linux__) |
| # include "elf.h" |
| |
| static void arch_cache_info(int *isize, int *dsize) |
| { |
| if (*isize == 0) { |
| *isize = qemu_getauxval(AT_ICACHEBSIZE); |
| } |
| if (*dsize == 0) { |
| *dsize = qemu_getauxval(AT_DCACHEBSIZE); |
| } |
| } |
| |
| #else |
| static void arch_cache_info(int *isize, int *dsize) { } |
| #endif /* arch_cache_info */ |
| |
| /* |
| * ... and if all else fails ... |
| */ |
| |
| static void fallback_cache_info(int *isize, int *dsize) |
| { |
| /* If we can only find one of the two, assume they're the same. */ |
| if (*isize) { |
| if (*dsize) { |
| /* Success! */ |
| } else { |
| *dsize = *isize; |
| } |
| } else if (*dsize) { |
| *isize = *dsize; |
| } else { |
| #if defined(_ARCH_PPC) |
| /* For PPC, we're going to use the icache size computed for |
| flush_icache_range. Which means that we must use the |
| architecture minimum. */ |
| *isize = *dsize = 16; |
| #else |
| /* Otherwise, 64 bytes is not uncommon. */ |
| *isize = *dsize = 64; |
| #endif |
| } |
| } |
| |
| static void __attribute__((constructor)) init_cache_info(void) |
| { |
| int isize = 0, dsize = 0; |
| |
| sys_cache_info(&isize, &dsize); |
| arch_cache_info(&isize, &dsize); |
| fallback_cache_info(&isize, &dsize); |
| |
| qemu_icache_linesize = isize; |
| qemu_dcache_linesize = dsize; |
| } |