text: /* **********************************************************
   * Copyright (c) 2013-2017 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.
   */
  
  /* Copyright (c) 2003-2007 Determina Corp. */
  /* Copyright (c) 2001-2003 Massachusetts Institute of Technology */
  /* Copyright (c) 2000-2001 Hewlett-Packard Company */
  
  /*
   * proc_shared.c - processor-specific shared routines
   */
  
  #include "../globals.h"
  #include "proc.h"
  #include "instr.h"        /* for dr_insert_{save,restore}_fpstate */
  #include "instrument.h"   /* for dr_insert_{save,restore}_fpstate */
  #include "instr_create.h" /* for dr_insert_{save,restore}_fpstate */
  
  #ifdef DEBUG
  /* case 10450: give messages to clients */
  /* we can't undef ASSERT b/c of DYNAMO_OPTION */
  #    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
  
  /* cache_line_size is exported for efficient access.
   * 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; /* bits that should be 0 to be cache-line-aligned */
  cpu_info_t cpu_info = { VENDOR_UNKNOWN,
                          0,
                          0,
                          0,
                          0,
                          CACHE_SIZE_UNKNOWN,
                          CACHE_SIZE_UNKNOWN,
                          CACHE_SIZE_UNKNOWN,
                          { 0, 0, 0, 0 },
                          { 0x6e6b6e75, 0x006e776f } };
  
  void
  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);
  }
  
  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;
  }
  
  /* FIXME: Add MODEL_ constants to proc.h?? */
  uint
  proc_get_model(void)
  {
      return cpu_info.model;
  }
  
  uint
  proc_get_stepping(void)
  {
      return cpu_info.stepping;
  }
  
  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;
  }
  
  /* check to see if addr is cache aligned */
  bool
  proc_is_cache_aligned(void *addr)
  {
      return (((ptr_uint_t)addr & mask) == 0x0);
  }
  
  /* Given an address or number of bytes sz, return a number >= sz that is divisible
     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; /* sz already a multiple of the line size */
  
      return ((sz + cache_line_size) & ~mask);
  }
  
  /* yes same result as PAGE_START...FIXME: get rid of one of them? */
  void *
  proc_get_containing_page(void *addr)
  {
      return (void *)(((ptr_uint_t)addr) & ~(PAGE_SIZE - 1));
  }
  