| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * 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"
 | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:41 -04:00
										 |  |  | #include "qemu/host-utils.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:42 -04:00
										 |  |  | #include "qemu/atomic.h"
 | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | int qemu_icache_linesize = 0; | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:41 -04:00
										 |  |  | int qemu_icache_linesize_log; | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | int qemu_dcache_linesize = 0; | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:41 -04:00
										 |  |  | int qemu_dcache_linesize_log; | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Operating system specific detection mechanisms. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-04 18:19:00 +01:00
										 |  |  | #if defined(_WIN32)
 | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-21 10:25:46 +02:00
										 |  |  | #elif defined(__APPLE__)
 | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | # include <sys/sysctl.h>
 | 
					
						
							|  |  |  | 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); | 
					
						
							| 
									
										
										
										
											2019-08-21 10:25:46 +02:00
										 |  |  |     if (!sysctlbyname("hw.cachelinesize", &size, &len, NULL, 0)) { | 
					
						
							|  |  |  |         *isize = *dsize = size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 | 
					
						
							|  |  |  | # include <sys/sysctl.h>
 | 
					
						
							|  |  |  | static void sys_cache_info(int *isize, int *dsize) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     /* There's only a single sysctl for both I/D cache line sizes.  */ | 
					
						
							|  |  |  |     int size; | 
					
						
							|  |  |  |     size_t len = sizeof(size); | 
					
						
							|  |  |  |     if (!sysctlbyname("machdep.cacheline_size", &size, &len, NULL, 0)) { | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  |         *isize = *dsize = size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | /* POSIX */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void sys_cache_info(int *isize, int *dsize) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | # ifdef _SC_LEVEL1_ICACHE_LINESIZE
 | 
					
						
							| 
									
										
										
										
											2019-10-17 09:37:13 -03:00
										 |  |  |     int tmp_isize = (int) sysconf(_SC_LEVEL1_ICACHE_LINESIZE); | 
					
						
							|  |  |  |     if (tmp_isize > 0) { | 
					
						
							|  |  |  |         *isize = tmp_isize; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | # endif
 | 
					
						
							|  |  |  | # ifdef _SC_LEVEL1_DCACHE_LINESIZE
 | 
					
						
							| 
									
										
										
										
											2019-10-17 09:37:13 -03:00
										 |  |  |     int tmp_dsize = (int) sysconf(_SC_LEVEL1_DCACHE_LINESIZE); | 
					
						
							|  |  |  |     if (tmp_dsize > 0) { | 
					
						
							|  |  |  |         *dsize = tmp_dsize; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | # 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) { | 
					
						
							| 
									
										
										
										
											2019-05-07 12:55:03 +01:00
										 |  |  |         uint64_t ctr; | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         /* 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__)
 | 
					
						
							| 
									
										
										
										
											2017-07-10 22:55:24 -03:00
										 |  |  | # include "elf.h"
 | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:41 -04:00
										 |  |  |     assert((isize & (isize - 1)) == 0); | 
					
						
							|  |  |  |     assert((dsize & (dsize - 1)) == 0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  |     qemu_icache_linesize = isize; | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:41 -04:00
										 |  |  |     qemu_icache_linesize_log = ctz32(isize); | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  |     qemu_dcache_linesize = dsize; | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:41 -04:00
										 |  |  |     qemu_dcache_linesize_log = ctz32(dsize); | 
					
						
							| 
									
										
										
										
											2018-09-10 19:27:42 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     atomic64_init(); | 
					
						
							| 
									
										
										
										
											2017-06-06 20:17:04 -04:00
										 |  |  | } |