* Copyright (c) 2013-2022 Google, Inc. All rights reserved.
* Copyright (c) 2000-2008 VMware, Inc. All rights reserved.
* **********************************************************/
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
* proc_shared.c - processor-specific shared routines
*/
#include "../globals.h"
#include "proc.h"
#include "instr.h"
#include "instrument.h"
#include "instr_create_shared.h"
#ifdef DEBUG
# undef ASSERT_TRUNCATE
# undef ASSERT_BITFIELD_TRUNCATE
# undef ASSERT_NOT_REACHED
# define ASSERT_TRUNCATE DO_NOT_USE_ASSERT_USE_CLIENT_ASSERT_INSTEAD
# define ASSERT_BITFIELD_TRUNCATE DO_NOT_USE_ASSERT_USE_CLIENT_ASSERT_INSTEAD
# define ASSERT_NOT_REACHED DO_NOT_USE_ASSERT_USE_CLIENT_ASSERT_INSTEAD
#endif
* FIXME: In case the processor doesn't support the
* cpuid instruction, use a default value of 32.
* (see case 463 for discussion)
*/
size_t cache_line_size = 32;
static ptr_uint_t mask;
cpu_info_t cpu_info = { VENDOR_UNKNOWN,
#ifdef AARCHXX
0,
0,
#endif
0,
0,
0,
0,
CACHE_SIZE_UNKNOWN,
CACHE_SIZE_UNKNOWN,
CACHE_SIZE_UNKNOWN,
#if defined(RISCV64)
{ 0 },
#else
{ 0, 0, 0, 0 },
#endif
{ 0x6e6b6e75, 0x006e776f } };
void
proc_set_cache_size(uint val, uint *dst)
{
CLIENT_ASSERT(dst != NULL, "invalid internal param");
switch (val) {
case 8: *dst = CACHE_SIZE_8_KB; break;
case 16: *dst = CACHE_SIZE_16_KB; break;
case 32: *dst = CACHE_SIZE_32_KB; break;
case 64: *dst = CACHE_SIZE_64_KB; break;
case 128: *dst = CACHE_SIZE_128_KB; break;
case 256: *dst = CACHE_SIZE_256_KB; break;
case 512: *dst = CACHE_SIZE_512_KB; break;
case 1024: *dst = CACHE_SIZE_1_MB; break;
case 2048: *dst = CACHE_SIZE_2_MB; break;
default: SYSLOG_INTERNAL_ERROR("Unknown processor cache size"); break;
}
}
void
proc_init(void)
{
LOG(GLOBAL, LOG_TOP, 1, "Running on a %d CPU machine\n", get_num_processors());
proc_init_arch();
CLIENT_ASSERT(cache_line_size > 0, "invalid cache line size");
mask = (cache_line_size - 1);
LOG(GLOBAL, LOG_TOP, 1, "Cache line size is %d bytes\n", cache_line_size);
LOG(GLOBAL, LOG_TOP, 1, "L1 icache=%s, L1 dcache=%s, L2 cache=%s\n",
proc_get_cache_size_str(proc_get_L1_icache_size()),
proc_get_cache_size_str(proc_get_L1_dcache_size()),
proc_get_cache_size_str(proc_get_L2_cache_size()));
LOG(GLOBAL, LOG_TOP, 1, "Processor brand string = %s\n", cpu_info.brand_string);
LOG(GLOBAL, LOG_TOP, 1, "Type=0x%x, Family=0x%x, Model=0x%x, Stepping=0x%x\n",
cpu_info.type, cpu_info.family, cpu_info.model, cpu_info.stepping);
#ifdef AARCHXX
# define PROC_CPUINFO "/proc/cpuinfo"
# define CPU_ARCH_LINE_FORMAT "CPU architecture: %u\n"
file_t cpuinfo = os_open(PROC_CPUINFO, OS_OPEN_READ);
if (cpuinfo != INVALID_FILE) {
char *buf = global_heap_alloc(PAGE_SIZE HEAPACCT(ACCT_OTHER));
ssize_t nread = os_read(cpuinfo, buf, PAGE_SIZE - 1);
if (nread > 0) {
buf[nread] = '\0';
char *arch_line = strstr(buf, "CPU architecture");
if (arch_line != NULL &&
sscanf(arch_line, CPU_ARCH_LINE_FORMAT, &cpu_info.architecture) == 1) {
LOG(GLOBAL, LOG_ALL, 2, "Processor architecture: %u\n",
cpu_info.architecture);
}
}
global_heap_free(buf, PAGE_SIZE HEAPACCT(ACCT_OTHER));
os_close(cpuinfo);
}
#endif
}
uint
proc_get_vendor(void)
{
return cpu_info.vendor;
}
DR_API
int
proc_set_vendor(uint new_vendor)
{
if (new_vendor == VENDOR_INTEL || new_vendor == VENDOR_AMD ||
new_vendor == VENDOR_ARM) {
uint old_vendor = cpu_info.vendor;
SELF_UNPROTECT_DATASEC(DATASEC_RARELY_PROT);
cpu_info.vendor = new_vendor;
SELF_PROTECT_DATASEC(DATASEC_RARELY_PROT);
return old_vendor;
} else {
CLIENT_ASSERT(false, "invalid vendor");
return -1;
}
}
uint
proc_get_family(void)
{
return cpu_info.family;
}
uint
proc_get_type(void)
{
return cpu_info.type;
}
uint
proc_get_model(void)
{
return cpu_info.model;
}
uint
proc_get_stepping(void)
{
return cpu_info.stepping;
}
#ifdef AARCHXX
uint
proc_get_architecture(void)
{
return cpu_info.architecture;
}
uint
proc_get_vector_length_bytes(void)
{
return cpu_info.sve_vector_length_bytes;
}
#endif
features_t *
proc_get_all_feature_bits(void)
{
return &cpu_info.features;
}
char *
proc_get_brand_string(void)
{
return (char *)cpu_info.brand_string;
}
cache_size_t
proc_get_L1_icache_size(void)
{
return cpu_info.L1_icache_size;
}
cache_size_t
proc_get_L1_dcache_size(void)
{
return cpu_info.L1_dcache_size;
}
cache_size_t
proc_get_L2_cache_size(void)
{
return cpu_info.L2_cache_size;
}
const char *
proc_get_cache_size_str(cache_size_t size)
{
static const char *strings[] = { "8 KB", "16 KB", "32 KB", "64 KB", "128 KB",
"256 KB", "512 KB", "1 MB", "2 MB", "unknown" };
CLIENT_ASSERT(size <= CACHE_SIZE_UNKNOWN, "proc_get_cache_size_str: invalid size");
return strings[size];
}
size_t
proc_get_cache_line_size(void)
{
return cache_line_size;
}
bool
proc_is_cache_aligned(void *addr)
{
return (((ptr_uint_t)addr & mask) == 0x0);
}
by the cache line size. */
ptr_uint_t
proc_bump_to_end_of_cache_line(ptr_uint_t sz)
{
if ((sz & mask) == 0x0)
return sz;
return ((sz + cache_line_size) & ~mask);
}
void *
proc_get_containing_page(void *addr)
{
return (void *)(((ptr_uint_t)addr) & ~(PAGE_SIZE - 1));
}