Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / base / cpu.cc
index 78064e2..ef3309d 100644 (file)
@@ -4,15 +4,24 @@
 
 #include "base/cpu.h"
 
+#include <stdlib.h>
 #include <string.h>
 
 #include <algorithm>
 
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
 #include "build/build_config.h"
 
+#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#endif
+
 #if defined(ARCH_CPU_X86_FAMILY)
 #if defined(_MSC_VER)
 #include <intrin.h>
+#include <immintrin.h>  // For _xgetbv()
 #endif
 #endif
 
@@ -33,11 +42,17 @@ CPU::CPU()
     has_ssse3_(false),
     has_sse41_(false),
     has_sse42_(false),
+    has_avx_(false),
+    has_avx_hardware_(false),
+    has_aesni_(false),
     has_non_stop_time_stamp_counter_(false),
+    has_broken_neon_(false),
     cpu_vendor_("unknown") {
   Initialize();
 }
 
+namespace {
+
 #if defined(ARCH_CPU_X86_FAMILY)
 #ifndef _MSC_VER
 
@@ -53,16 +68,6 @@ void __cpuid(int cpu_info[4], int info_type) {
   );
 }
 
-void __cpuidex(int cpu_info[4], int info_type, int info_index) {
-  __asm__ volatile (
-    "mov %%ebx, %%edi\n"
-    "cpuid\n"
-    "xchg %%edi, %%ebx\n"
-    : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
-    : "a"(info_type), "c"(info_index)
-  );
-}
-
 #else
 
 void __cpuid(int cpu_info[4], int info_type) {
@@ -73,18 +78,119 @@ void __cpuid(int cpu_info[4], int info_type) {
   );
 }
 
-void __cpuidex(int cpu_info[4], int info_type, int info_index) {
-  __asm__ volatile (
-    "cpuid \n\t"
-    : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
-    : "a"(info_type), "c"(info_index)
-  );
+#endif
+
+// _xgetbv returns the value of an Intel Extended Control Register (XCR).
+// Currently only XCR0 is defined by Intel so |xcr| should always be zero.
+uint64 _xgetbv(uint32 xcr) {
+  uint32 eax, edx;
+
+  __asm__ volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (xcr));
+  return (static_cast<uint64>(edx) << 32) | eax;
 }
 
-#endif
-#endif  // _MSC_VER
+#endif  // !_MSC_VER
 #endif  // ARCH_CPU_X86_FAMILY
 
+#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+class LazyCpuInfoValue {
+ public:
+  LazyCpuInfoValue() : has_broken_neon_(false) {
+    // This function finds the value from /proc/cpuinfo under the key "model
+    // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7
+    // and later for arm64) and is shown once per CPU. "Processor" is used in
+    // earler versions and is shown only once at the top of /proc/cpuinfo
+    // regardless of the number CPUs.
+    const char kModelNamePrefix[] = "model name\t: ";
+    const char kProcessorPrefix[] = "Processor\t: ";
+
+    // This function also calculates whether we believe that this CPU has a
+    // broken NEON unit based on these fields from cpuinfo:
+    unsigned implementer = 0, architecture = 0, variant = 0, part = 0,
+             revision = 0;
+    const struct {
+      const char key[17];
+      unsigned *result;
+    } kUnsignedValues[] = {
+      {"CPU implementer", &implementer},
+      {"CPU architecture", &architecture},
+      {"CPU variant", &variant},
+      {"CPU part", &part},
+      {"CPU revision", &revision},
+    };
+
+    std::string contents;
+    ReadFileToString(FilePath("/proc/cpuinfo"), &contents);
+    DCHECK(!contents.empty());
+    if (contents.empty()) {
+      return;
+    }
+
+    std::istringstream iss(contents);
+    std::string line;
+    while (std::getline(iss, line)) {
+      if (brand_.empty() &&
+          (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0 ||
+           line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)) {
+        brand_.assign(line.substr(strlen(kModelNamePrefix)));
+      }
+
+      for (size_t i = 0; i < arraysize(kUnsignedValues); i++) {
+        const char *key = kUnsignedValues[i].key;
+        const size_t len = strlen(key);
+
+        if (line.compare(0, len, key) == 0 &&
+            line.size() >= len + 1 &&
+            (line[len] == '\t' || line[len] == ' ' || line[len] == ':')) {
+          size_t colon_pos = line.find(':', len);
+          if (colon_pos == std::string::npos) {
+            continue;
+          }
+
+          const StringPiece line_sp(line);
+          StringPiece value_sp = line_sp.substr(colon_pos + 1);
+          while (!value_sp.empty() &&
+                 (value_sp[0] == ' ' || value_sp[0] == '\t')) {
+            value_sp = value_sp.substr(1);
+          }
+
+          // The string may have leading "0x" or not, so we use strtoul to
+          // handle that.
+          char *endptr;
+          std::string value(value_sp.as_string());
+          unsigned long int result = strtoul(value.c_str(), &endptr, 0);
+          if (*endptr == 0 && result <= UINT_MAX) {
+            *kUnsignedValues[i].result = result;
+          }
+        }
+      }
+    }
+
+    has_broken_neon_ =
+      implementer == 0x51 &&
+      architecture == 7 &&
+      variant == 1 &&
+      part == 0x4d &&
+      revision == 0;
+  }
+
+  const std::string& brand() const { return brand_; }
+  bool has_broken_neon() const { return has_broken_neon_; }
+
+ private:
+  std::string brand_;
+  bool has_broken_neon_;
+  DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue);
+};
+
+base::LazyInstance<LazyCpuInfoValue>::Leaky g_lazy_cpuinfo =
+    LAZY_INSTANCE_INITIALIZER;
+
+#endif  // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) ||
+        // defined(OS_LINUX))
+
+}  // anonymous namespace
+
 void CPU::Initialize() {
 #if defined(ARCH_CPU_X86_FAMILY)
   int cpu_info[4] = {-1};
@@ -113,14 +219,31 @@ void CPU::Initialize() {
     type_ = (cpu_info[0] >> 12) & 0x3;
     ext_model_ = (cpu_info[0] >> 16) & 0xf;
     ext_family_ = (cpu_info[0] >> 20) & 0xff;
-    has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
-    has_sse_ = (cpu_info[3] & 0x02000000) != 0;
-    has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
-    has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
+    has_mmx_ =   (cpu_info[3] & 0x00800000) != 0;
+    has_sse_ =   (cpu_info[3] & 0x02000000) != 0;
+    has_sse2_ =  (cpu_info[3] & 0x04000000) != 0;
+    has_sse3_ =  (cpu_info[2] & 0x00000001) != 0;
     has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
     has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
     has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
-    has_avx_ = (cpu_info[2] & 0x10000000) != 0;
+    has_avx_hardware_ =
+                 (cpu_info[2] & 0x10000000) != 0;
+    // AVX instructions will generate an illegal instruction exception unless
+    //   a) they are supported by the CPU,
+    //   b) XSAVE is supported by the CPU and
+    //   c) XSAVE is enabled by the kernel.
+    // See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
+    //
+    // In addition, we have observed some crashes with the xgetbv instruction
+    // even after following Intel's example code. (See crbug.com/375968.)
+    // Because of that, we also test the XSAVE bit because its description in
+    // the CPUID documentation suggests that it signals xgetbv support.
+    has_avx_ =
+        has_avx_hardware_ &&
+        (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ &&
+        (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
+        (_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */;
+    has_aesni_ = (cpu_info[2] & 0x02000000) != 0;
   }
 
   // Get the brand string of the cpu.
@@ -145,13 +268,9 @@ void CPU::Initialize() {
     __cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter);
     has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
   }
-#elif defined(ARCH_CPU_ARM_FAMILY)
-  // TODO(piman): Expand this. ARM has a CPUID register, but it's not available
-  // in user mode. /proc/cpuinfo has some information, but it's non standard,
-  // platform-specific, and not accessible from the sandbox.
-  // For some purposes, this first approximation is enough.
-  // crbug.com/313454
-  cpu_brand_.assign("ARM");
+#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+  cpu_brand_.assign(g_lazy_cpuinfo.Get().brand());
+  has_broken_neon_ = g_lazy_cpuinfo.Get().has_broken_neon();
 #endif
 }