src/hotspot/cpu/x86/vm_version_ext_x86.cpp
changeset 50113 caf115bb98ad
child 51070 2f4c3cac8556
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/cpu/x86/vm_version_ext_x86.cpp	Tue May 15 20:24:34 2018 +0200
@@ -0,0 +1,967 @@
+/*
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "jvm.h"
+#include "utilities/macros.hpp"
+#include "asm/macroAssembler.hpp"
+#include "asm/macroAssembler.inline.hpp"
+#include "memory/allocation.inline.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/java.hpp"
+#include "runtime/stubCodeGenerator.hpp"
+#include "vm_version_ext_x86.hpp"
+
+typedef enum {
+   CPU_FAMILY_8086_8088  = 0,
+   CPU_FAMILY_INTEL_286  = 2,
+   CPU_FAMILY_INTEL_386  = 3,
+   CPU_FAMILY_INTEL_486  = 4,
+   CPU_FAMILY_PENTIUM    = 5,
+   CPU_FAMILY_PENTIUMPRO = 6,    // Same family several models
+   CPU_FAMILY_PENTIUM_4  = 0xF
+} FamilyFlag;
+
+ typedef enum {
+    RDTSCP_FLAG  = 0x08000000, // bit 27
+    INTEL64_FLAG = 0x20000000  // bit 29
+  } _featureExtendedEdxFlag;
+
+#define CPUID_STANDARD_FN   0x0
+#define CPUID_STANDARD_FN_1 0x1
+#define CPUID_STANDARD_FN_4 0x4
+#define CPUID_STANDARD_FN_B 0xb
+
+#define CPUID_EXTENDED_FN   0x80000000
+#define CPUID_EXTENDED_FN_1 0x80000001
+#define CPUID_EXTENDED_FN_2 0x80000002
+#define CPUID_EXTENDED_FN_3 0x80000003
+#define CPUID_EXTENDED_FN_4 0x80000004
+#define CPUID_EXTENDED_FN_7 0x80000007
+#define CPUID_EXTENDED_FN_8 0x80000008
+
+typedef enum {
+   FPU_FLAG     = 0x00000001,
+   VME_FLAG     = 0x00000002,
+   DE_FLAG      = 0x00000004,
+   PSE_FLAG     = 0x00000008,
+   TSC_FLAG     = 0x00000010,
+   MSR_FLAG     = 0x00000020,
+   PAE_FLAG     = 0x00000040,
+   MCE_FLAG     = 0x00000080,
+   CX8_FLAG     = 0x00000100,
+   APIC_FLAG    = 0x00000200,
+   SEP_FLAG     = 0x00000800,
+   MTRR_FLAG    = 0x00001000,
+   PGE_FLAG     = 0x00002000,
+   MCA_FLAG     = 0x00004000,
+   CMOV_FLAG    = 0x00008000,
+   PAT_FLAG     = 0x00010000,
+   PSE36_FLAG   = 0x00020000,
+   PSNUM_FLAG   = 0x00040000,
+   CLFLUSH_FLAG = 0x00080000,
+   DTS_FLAG     = 0x00200000,
+   ACPI_FLAG    = 0x00400000,
+   MMX_FLAG     = 0x00800000,
+   FXSR_FLAG    = 0x01000000,
+   SSE_FLAG     = 0x02000000,
+   SSE2_FLAG    = 0x04000000,
+   SS_FLAG      = 0x08000000,
+   HTT_FLAG     = 0x10000000,
+   TM_FLAG      = 0x20000000
+} FeatureEdxFlag;
+
+static BufferBlob* cpuid_brand_string_stub_blob;
+static const int   cpuid_brand_string_stub_size = 550;
+
+extern "C" {
+  typedef void (*getCPUIDBrandString_stub_t)(void*);
+}
+
+static getCPUIDBrandString_stub_t getCPUIDBrandString_stub = NULL;
+
+class VM_Version_Ext_StubGenerator: public StubCodeGenerator {
+ public:
+
+  VM_Version_Ext_StubGenerator(CodeBuffer *c) : StubCodeGenerator(c) {}
+
+  address generate_getCPUIDBrandString(void) {
+    // Flags to test CPU type.
+    const uint32_t HS_EFL_AC           = 0x40000;
+    const uint32_t HS_EFL_ID           = 0x200000;
+    // Values for when we don't have a CPUID instruction.
+    const int      CPU_FAMILY_SHIFT = 8;
+    const uint32_t CPU_FAMILY_386   = (3 << CPU_FAMILY_SHIFT);
+    const uint32_t CPU_FAMILY_486   = (4 << CPU_FAMILY_SHIFT);
+
+    Label detect_486, cpu486, detect_586, done, ext_cpuid;
+
+    StubCodeMark mark(this, "VM_Version_Ext", "getCPUIDNameInfo_stub");
+#   define __ _masm->
+
+    address start = __ pc();
+
+    //
+    // void getCPUIDBrandString(VM_Version::CpuidInfo* cpuid_info);
+    //
+    // LP64: rcx and rdx are first and second argument registers on windows
+
+    __ push(rbp);
+#ifdef _LP64
+    __ mov(rbp, c_rarg0); // cpuid_info address
+#else
+    __ movptr(rbp, Address(rsp, 8)); // cpuid_info address
+#endif
+    __ push(rbx);
+    __ push(rsi);
+    __ pushf();          // preserve rbx, and flags
+    __ pop(rax);
+    __ push(rax);
+    __ mov(rcx, rax);
+    //
+    // if we are unable to change the AC flag, we have a 386
+    //
+    __ xorl(rax, HS_EFL_AC);
+    __ push(rax);
+    __ popf();
+    __ pushf();
+    __ pop(rax);
+    __ cmpptr(rax, rcx);
+    __ jccb(Assembler::notEqual, detect_486);
+
+    __ movl(rax, CPU_FAMILY_386);
+    __ jmp(done);
+
+    //
+    // If we are unable to change the ID flag, we have a 486 which does
+    // not support the "cpuid" instruction.
+    //
+    __ bind(detect_486);
+    __ mov(rax, rcx);
+    __ xorl(rax, HS_EFL_ID);
+    __ push(rax);
+    __ popf();
+    __ pushf();
+    __ pop(rax);
+    __ cmpptr(rcx, rax);
+    __ jccb(Assembler::notEqual, detect_586);
+
+    __ bind(cpu486);
+    __ movl(rax, CPU_FAMILY_486);
+    __ jmp(done);
+
+    //
+    // At this point, we have a chip which supports the "cpuid" instruction
+    //
+    __ bind(detect_586);
+    __ xorl(rax, rax);
+    __ cpuid();
+    __ orl(rax, rax);
+    __ jcc(Assembler::equal, cpu486);   // if cpuid doesn't support an input
+                                        // value of at least 1, we give up and
+                                        // assume a 486
+
+    //
+    // Extended cpuid(0x80000000) for processor brand string detection
+    //
+    __ bind(ext_cpuid);
+    __ movl(rax, CPUID_EXTENDED_FN);
+    __ cpuid();
+    __ cmpl(rax, CPUID_EXTENDED_FN_4);
+    __ jcc(Assembler::below, done);
+
+    //
+    // Extended cpuid(0x80000002)  // first 16 bytes in brand string
+    //
+    __ movl(rax, CPUID_EXTENDED_FN_2);
+    __ cpuid();
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_0_offset())));
+    __ movl(Address(rsi, 0), rax);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_1_offset())));
+    __ movl(Address(rsi, 0), rbx);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_2_offset())));
+    __ movl(Address(rsi, 0), rcx);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_3_offset())));
+    __ movl(Address(rsi,0), rdx);
+
+    //
+    // Extended cpuid(0x80000003) // next 16 bytes in brand string
+    //
+    __ movl(rax, CPUID_EXTENDED_FN_3);
+    __ cpuid();
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_4_offset())));
+    __ movl(Address(rsi, 0), rax);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_5_offset())));
+    __ movl(Address(rsi, 0), rbx);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_6_offset())));
+    __ movl(Address(rsi, 0), rcx);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_7_offset())));
+    __ movl(Address(rsi,0), rdx);
+
+    //
+    // Extended cpuid(0x80000004) // last 16 bytes in brand string
+    //
+    __ movl(rax, CPUID_EXTENDED_FN_4);
+    __ cpuid();
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_8_offset())));
+    __ movl(Address(rsi, 0), rax);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_9_offset())));
+    __ movl(Address(rsi, 0), rbx);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_10_offset())));
+    __ movl(Address(rsi, 0), rcx);
+    __ lea(rsi, Address(rbp, in_bytes(VM_Version_Ext::proc_name_11_offset())));
+    __ movl(Address(rsi,0), rdx);
+
+    //
+    // return
+    //
+    __ bind(done);
+    __ popf();
+    __ pop(rsi);
+    __ pop(rbx);
+    __ pop(rbp);
+    __ ret(0);
+
+#   undef __
+
+    return start;
+  };
+};
+
+
+// VM_Version_Ext statics
+const size_t VM_Version_Ext::VENDOR_LENGTH = 13;
+const size_t VM_Version_Ext::CPU_EBS_MAX_LENGTH = (3 * 4 * 4 + 1);
+const size_t VM_Version_Ext::CPU_TYPE_DESC_BUF_SIZE = 256;
+const size_t VM_Version_Ext::CPU_DETAILED_DESC_BUF_SIZE = 4096;
+char* VM_Version_Ext::_cpu_brand_string = NULL;
+jlong VM_Version_Ext::_max_qualified_cpu_frequency = 0;
+
+int VM_Version_Ext::_no_of_threads = 0;
+int VM_Version_Ext::_no_of_cores = 0;
+int VM_Version_Ext::_no_of_packages = 0;
+
+void VM_Version_Ext::initialize(void) {
+  ResourceMark rm;
+
+  cpuid_brand_string_stub_blob = BufferBlob::create("getCPUIDBrandString_stub", cpuid_brand_string_stub_size);
+  if (cpuid_brand_string_stub_blob == NULL) {
+    vm_exit_during_initialization("Unable to allocate getCPUIDBrandString_stub");
+  }
+  CodeBuffer c(cpuid_brand_string_stub_blob);
+  VM_Version_Ext_StubGenerator g(&c);
+  getCPUIDBrandString_stub = CAST_TO_FN_PTR(getCPUIDBrandString_stub_t,
+                                   g.generate_getCPUIDBrandString());
+}
+
+const char* VM_Version_Ext::cpu_model_description(void) {
+  uint32_t cpu_family = extended_cpu_family();
+  uint32_t cpu_model = extended_cpu_model();
+  const char* model = NULL;
+
+  if (cpu_family == CPU_FAMILY_PENTIUMPRO) {
+    for (uint32_t i = 0; i <= cpu_model; i++) {
+      model = _model_id_pentium_pro[i];
+      if (model == NULL) {
+        break;
+      }
+    }
+  }
+  return model;
+}
+
+const char* VM_Version_Ext::cpu_brand_string(void) {
+  if (_cpu_brand_string == NULL) {
+    _cpu_brand_string = NEW_C_HEAP_ARRAY_RETURN_NULL(char, CPU_EBS_MAX_LENGTH, mtInternal);
+    if (NULL == _cpu_brand_string) {
+      return NULL;
+    }
+    int ret_val = cpu_extended_brand_string(_cpu_brand_string, CPU_EBS_MAX_LENGTH);
+    if (ret_val != OS_OK) {
+      FREE_C_HEAP_ARRAY(char, _cpu_brand_string);
+      _cpu_brand_string = NULL;
+    }
+  }
+  return _cpu_brand_string;
+}
+
+const char* VM_Version_Ext::cpu_brand(void) {
+  const char*  brand  = NULL;
+
+  if ((_cpuid_info.std_cpuid1_ebx.value & 0xFF) > 0) {
+    int brand_num = _cpuid_info.std_cpuid1_ebx.value & 0xFF;
+    brand = _brand_id[0];
+    for (int i = 0; brand != NULL && i <= brand_num; i += 1) {
+      brand = _brand_id[i];
+    }
+  }
+  return brand;
+}
+
+bool VM_Version_Ext::cpu_is_em64t(void) {
+  return ((_cpuid_info.ext_cpuid1_edx.value & INTEL64_FLAG) == INTEL64_FLAG);
+}
+
+bool VM_Version_Ext::is_netburst(void) {
+  return (is_intel() && (extended_cpu_family() == CPU_FAMILY_PENTIUM_4));
+}
+
+bool VM_Version_Ext::supports_tscinv_ext(void) {
+  if (!supports_tscinv_bit()) {
+    return false;
+  }
+
+  if (is_intel()) {
+    return true;
+  }
+
+  if (is_amd()) {
+    return !is_amd_Barcelona();
+  }
+
+  return false;
+}
+
+void VM_Version_Ext::resolve_cpu_information_details(void) {
+
+  // in future we want to base this information on proper cpu
+  // and cache topology enumeration such as:
+  // Intel 64 Architecture Processor Topology Enumeration
+  // which supports system cpu and cache topology enumeration
+  // either using 2xAPICIDs or initial APICIDs
+
+  // currently only rough cpu information estimates
+  // which will not necessarily reflect the exact configuration of the system
+
+  // this is the number of logical hardware threads
+  // visible to the operating system
+  _no_of_threads = os::processor_count();
+
+  // find out number of threads per cpu package
+  int threads_per_package = threads_per_core() * cores_per_cpu();
+
+  // use amount of threads visible to the process in order to guess number of sockets
+  _no_of_packages = _no_of_threads / threads_per_package;
+
+  // process might only see a subset of the total number of threads
+  // from a single processor package. Virtualization/resource management for example.
+  // If so then just write a hard 1 as num of pkgs.
+  if (0 == _no_of_packages) {
+    _no_of_packages = 1;
+  }
+
+  // estimate the number of cores
+  _no_of_cores = cores_per_cpu() * _no_of_packages;
+}
+
+int VM_Version_Ext::number_of_threads(void) {
+  if (_no_of_threads == 0) {
+   resolve_cpu_information_details();
+  }
+  return _no_of_threads;
+}
+
+int VM_Version_Ext::number_of_cores(void) {
+  if (_no_of_cores == 0) {
+    resolve_cpu_information_details();
+  }
+  return _no_of_cores;
+}
+
+int VM_Version_Ext::number_of_sockets(void) {
+  if (_no_of_packages == 0) {
+    resolve_cpu_information_details();
+  }
+  return _no_of_packages;
+}
+
+const char* VM_Version_Ext::cpu_family_description(void) {
+  int cpu_family_id = extended_cpu_family();
+  if (is_amd()) {
+    return _family_id_amd[cpu_family_id];
+  }
+  if (is_intel()) {
+    if (cpu_family_id == CPU_FAMILY_PENTIUMPRO) {
+      return cpu_model_description();
+    }
+    return _family_id_intel[cpu_family_id];
+  }
+  return "Unknown x86";
+}
+
+int VM_Version_Ext::cpu_type_description(char* const buf, size_t buf_len) {
+  assert(buf != NULL, "buffer is NULL!");
+  assert(buf_len >= CPU_TYPE_DESC_BUF_SIZE, "buffer len should at least be == CPU_TYPE_DESC_BUF_SIZE!");
+
+  const char* cpu_type = NULL;
+  const char* x64 = NULL;
+
+  if (is_intel()) {
+    cpu_type = "Intel";
+    x64 = cpu_is_em64t() ? " Intel64" : "";
+  } else if (is_amd()) {
+    cpu_type = "AMD";
+    x64 = cpu_is_em64t() ? " AMD64" : "";
+  } else {
+    cpu_type = "Unknown x86";
+    x64 = cpu_is_em64t() ? " x86_64" : "";
+  }
+
+  jio_snprintf(buf, buf_len, "%s %s%s SSE SSE2%s%s%s%s%s%s%s%s",
+    cpu_type,
+    cpu_family_description(),
+    supports_ht() ? " (HT)" : "",
+    supports_sse3() ? " SSE3" : "",
+    supports_ssse3() ? " SSSE3" : "",
+    supports_sse4_1() ? " SSE4.1" : "",
+    supports_sse4_2() ? " SSE4.2" : "",
+    supports_sse4a() ? " SSE4A" : "",
+    is_netburst() ? " Netburst" : "",
+    is_intel_family_core() ? " Core" : "",
+    x64);
+
+  return OS_OK;
+}
+
+int VM_Version_Ext::cpu_extended_brand_string(char* const buf, size_t buf_len) {
+  assert(buf != NULL, "buffer is NULL!");
+  assert(buf_len >= CPU_EBS_MAX_LENGTH, "buffer len should at least be == CPU_EBS_MAX_LENGTH!");
+  assert(getCPUIDBrandString_stub != NULL, "not initialized");
+
+  // invoke newly generated asm code to fetch CPU Brand String
+  getCPUIDBrandString_stub(&_cpuid_info);
+
+  // fetch results into buffer
+  *((uint32_t*) &buf[0])  = _cpuid_info.proc_name_0;
+  *((uint32_t*) &buf[4])  = _cpuid_info.proc_name_1;
+  *((uint32_t*) &buf[8])  = _cpuid_info.proc_name_2;
+  *((uint32_t*) &buf[12]) = _cpuid_info.proc_name_3;
+  *((uint32_t*) &buf[16]) = _cpuid_info.proc_name_4;
+  *((uint32_t*) &buf[20]) = _cpuid_info.proc_name_5;
+  *((uint32_t*) &buf[24]) = _cpuid_info.proc_name_6;
+  *((uint32_t*) &buf[28]) = _cpuid_info.proc_name_7;
+  *((uint32_t*) &buf[32]) = _cpuid_info.proc_name_8;
+  *((uint32_t*) &buf[36]) = _cpuid_info.proc_name_9;
+  *((uint32_t*) &buf[40]) = _cpuid_info.proc_name_10;
+  *((uint32_t*) &buf[44]) = _cpuid_info.proc_name_11;
+
+  return OS_OK;
+}
+
+size_t VM_Version_Ext::cpu_write_support_string(char* const buf, size_t buf_len) {
+  assert(buf != NULL, "buffer is NULL!");
+  assert(buf_len > 0, "buffer len not enough!");
+
+  unsigned int flag = 0;
+  unsigned int fi = 0;
+  size_t       written = 0;
+  const char*  prefix = "";
+
+#define WRITE_TO_BUF(string)                                                          \
+  {                                                                                   \
+    int res = jio_snprintf(&buf[written], buf_len - written, "%s%s", prefix, string); \
+    if (res < 0 || (size_t) res >= buf_len - 1) {                                     \
+      buf[buf_len-1] = '\0';                                                          \
+      return buf_len - 1;                                                             \
+    }                                                                                 \
+    written += res;                                                                   \
+    if (prefix[0] == '\0') {                                                          \
+      prefix = ", ";                                                                  \
+    }                                                                                 \
+  }
+
+  for (flag = 1, fi = 0; flag <= 0x20000000 ; flag <<= 1, fi++) {
+    if (flag == HTT_FLAG && (((_cpuid_info.std_cpuid1_ebx.value >> 16) & 0xff) <= 1)) {
+      continue; /* no hyperthreading */
+    } else if (flag == SEP_FLAG && (cpu_family() == CPU_FAMILY_PENTIUMPRO && ((_cpuid_info.std_cpuid1_eax.value & 0xff) < 0x33))) {
+      continue; /* no fast system call */
+    }
+    if ((_cpuid_info.std_cpuid1_edx.value & flag) && strlen(_feature_edx_id[fi]) > 0) {
+      WRITE_TO_BUF(_feature_edx_id[fi]);
+    }
+  }
+
+  for (flag = 1, fi = 0; flag <= 0x20000000; flag <<= 1, fi++) {
+    if ((_cpuid_info.std_cpuid1_ecx.value & flag) && strlen(_feature_ecx_id[fi]) > 0) {
+      WRITE_TO_BUF(_feature_ecx_id[fi]);
+    }
+  }
+
+  for (flag = 1, fi = 0; flag <= 0x20000000 ; flag <<= 1, fi++) {
+    if ((_cpuid_info.ext_cpuid1_ecx.value & flag) && strlen(_feature_extended_ecx_id[fi]) > 0) {
+      WRITE_TO_BUF(_feature_extended_ecx_id[fi]);
+    }
+  }
+
+  for (flag = 1, fi = 0; flag <= 0x20000000; flag <<= 1, fi++) {
+    if ((_cpuid_info.ext_cpuid1_edx.value & flag) && strlen(_feature_extended_edx_id[fi]) > 0) {
+      WRITE_TO_BUF(_feature_extended_edx_id[fi]);
+    }
+  }
+
+  if (supports_tscinv_bit()) {
+      WRITE_TO_BUF("Invariant TSC");
+  }
+
+  return written;
+}
+
+/**
+ * Write a detailed description of the cpu to a given buffer, including
+ * feature set.
+ */
+int VM_Version_Ext::cpu_detailed_description(char* const buf, size_t buf_len) {
+  assert(buf != NULL, "buffer is NULL!");
+  assert(buf_len >= CPU_DETAILED_DESC_BUF_SIZE, "buffer len should at least be == CPU_DETAILED_DESC_BUF_SIZE!");
+
+  static const char* unknown = "<unknown>";
+  char               vendor_id[VENDOR_LENGTH];
+  const char*        family = NULL;
+  const char*        model = NULL;
+  const char*        brand = NULL;
+  int                outputLen = 0;
+
+  family = cpu_family_description();
+  if (family == NULL) {
+    family = unknown;
+  }
+
+  model = cpu_model_description();
+  if (model == NULL) {
+    model = unknown;
+  }
+
+  brand = cpu_brand_string();
+
+  if (brand == NULL) {
+    brand = cpu_brand();
+    if (brand == NULL) {
+      brand = unknown;
+    }
+  }
+
+  *((uint32_t*) &vendor_id[0]) = _cpuid_info.std_vendor_name_0;
+  *((uint32_t*) &vendor_id[4]) = _cpuid_info.std_vendor_name_2;
+  *((uint32_t*) &vendor_id[8]) = _cpuid_info.std_vendor_name_1;
+  vendor_id[VENDOR_LENGTH-1] = '\0';
+
+  outputLen = jio_snprintf(buf, buf_len, "Brand: %s, Vendor: %s\n"
+    "Family: %s (0x%x), Model: %s (0x%x), Stepping: 0x%x\n"
+    "Ext. family: 0x%x, Ext. model: 0x%x, Type: 0x%x, Signature: 0x%8.8x\n"
+    "Features: ebx: 0x%8.8x, ecx: 0x%8.8x, edx: 0x%8.8x\n"
+    "Ext. features: eax: 0x%8.8x, ebx: 0x%8.8x, ecx: 0x%8.8x, edx: 0x%8.8x\n"
+    "Supports: ",
+    brand,
+    vendor_id,
+    family,
+    extended_cpu_family(),
+    model,
+    extended_cpu_model(),
+    cpu_stepping(),
+    _cpuid_info.std_cpuid1_eax.bits.ext_family,
+    _cpuid_info.std_cpuid1_eax.bits.ext_model,
+    _cpuid_info.std_cpuid1_eax.bits.proc_type,
+    _cpuid_info.std_cpuid1_eax.value,
+    _cpuid_info.std_cpuid1_ebx.value,
+    _cpuid_info.std_cpuid1_ecx.value,
+    _cpuid_info.std_cpuid1_edx.value,
+    _cpuid_info.ext_cpuid1_eax,
+    _cpuid_info.ext_cpuid1_ebx,
+    _cpuid_info.ext_cpuid1_ecx,
+    _cpuid_info.ext_cpuid1_edx);
+
+  if (outputLen < 0 || (size_t) outputLen >= buf_len - 1) {
+    buf[buf_len-1] = '\0';
+    return OS_ERR;
+  }
+
+  cpu_write_support_string(&buf[outputLen], buf_len - outputLen);
+
+  return OS_OK;
+}
+
+const char* VM_Version_Ext::cpu_name(void) {
+  char cpu_type_desc[CPU_TYPE_DESC_BUF_SIZE];
+  size_t cpu_desc_len = sizeof(cpu_type_desc);
+
+  cpu_type_description(cpu_type_desc, cpu_desc_len);
+  char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, cpu_desc_len, mtTracing);
+  if (NULL == tmp) {
+    return NULL;
+  }
+  strncpy(tmp, cpu_type_desc, cpu_desc_len);
+  return tmp;
+}
+
+const char* VM_Version_Ext::cpu_description(void) {
+  char cpu_detailed_desc_buffer[CPU_DETAILED_DESC_BUF_SIZE];
+  size_t cpu_detailed_desc_len = sizeof(cpu_detailed_desc_buffer);
+
+  cpu_detailed_description(cpu_detailed_desc_buffer, cpu_detailed_desc_len);
+
+  char* tmp = NEW_C_HEAP_ARRAY_RETURN_NULL(char, cpu_detailed_desc_len, mtTracing);
+
+  if (NULL == tmp) {
+    return NULL;
+  }
+
+  strncpy(tmp, cpu_detailed_desc_buffer, cpu_detailed_desc_len);
+  return tmp;
+}
+
+/**
+ *  See Intel Application note 485 (chapter 10) for details
+ *  on frequency extraction from cpu brand string.
+ *  http://www.intel.com/content/dam/www/public/us/en/documents/application-notes/processor-identification-cpuid-instruction-note.pdf
+ *
+ */
+jlong VM_Version_Ext::max_qualified_cpu_freq_from_brand_string(void) {
+  // get brand string
+  const char* const brand_string = cpu_brand_string();
+  if (brand_string == NULL) {
+    return 0;
+  }
+
+  const u8 MEGA = 1000000;
+  u8 multiplier = 0;
+  jlong frequency = 0;
+
+  // the frequency information in the cpu brand string
+  // is given in either of two formats "x.xxyHz" or "xxxxyHz",
+  // where y=M,G,T and x is digits
+  const char* Hz_location = strchr(brand_string, 'H');
+
+  if (Hz_location != NULL) {
+    if (*(Hz_location + 1) == 'z') {
+      // switch on y in "yHz"
+      switch(*(Hz_location - 1)) {
+        case 'M' :
+          // Set multiplier to frequency is in Hz
+          multiplier = MEGA;
+          break;
+        case 'G' :
+          multiplier = MEGA * 1000;
+          break;
+        case 'T' :
+          multiplier = MEGA * 1000 * 1000;
+          break;
+      }
+    }
+  }
+
+  if (multiplier > 0) {
+    // compute frequency (in Hz) from brand string
+    if (*(Hz_location - 4) == '.') { // if format is "x.xx"
+      frequency =  (jlong)(*(Hz_location - 5) - '0') * (multiplier);
+      frequency += (jlong)(*(Hz_location - 3) - '0') * (multiplier / 10);
+      frequency += (jlong)(*(Hz_location - 2) - '0') * (multiplier / 100);
+    } else { // format is "xxxx"
+      frequency =  (jlong)(*(Hz_location - 5) - '0') * 1000;
+      frequency += (jlong)(*(Hz_location - 4) - '0') * 100;
+      frequency += (jlong)(*(Hz_location - 3) - '0') * 10;
+      frequency += (jlong)(*(Hz_location - 2) - '0');
+      frequency *= multiplier;
+    }
+  }
+  return frequency;
+}
+
+
+jlong VM_Version_Ext::maximum_qualified_cpu_frequency(void) {
+  if (_max_qualified_cpu_frequency == 0) {
+    _max_qualified_cpu_frequency = max_qualified_cpu_freq_from_brand_string();
+  }
+  return _max_qualified_cpu_frequency;
+}
+
+const char* const VM_Version_Ext::_family_id_intel[] = {
+  "8086/8088",
+  "",
+  "286",
+  "386",
+  "486",
+  "Pentium",
+  "Pentium Pro",   //or Pentium-M/Woodcrest depeding on model
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "Pentium 4"
+};
+
+const char* const VM_Version_Ext::_family_id_amd[] = {
+  "",
+  "",
+  "",
+  "",
+  "5x86",
+  "K5/K6",
+  "Athlon/AthlonXP",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "Opteron/Athlon64",
+  "Opteron QC/Phenom"  // Barcelona et.al.
+};
+// Partially from Intel 64 and IA-32 Architecture Software Developer's Manual,
+// September 2013, Vol 3C Table 35-1
+const char* const VM_Version_Ext::_model_id_pentium_pro[] = {
+  "",
+  "Pentium Pro",
+  "",
+  "Pentium II model 3",
+  "",
+  "Pentium II model 5/Xeon/Celeron",
+  "Celeron",
+  "Pentium III/Pentium III Xeon",
+  "Pentium III/Pentium III Xeon",
+  "Pentium M model 9",    // Yonah
+  "Pentium III, model A",
+  "Pentium III, model B",
+  "",
+  "Pentium M model D",    // Dothan
+  "",
+  "Core 2",               // 0xf Woodcrest/Conroe/Merom/Kentsfield/Clovertown
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "Celeron",              // 0x16 Celeron 65nm
+  "Core 2",               // 0x17 Penryn / Harpertown
+  "",
+  "",
+  "Core i7",              // 0x1A CPU_MODEL_NEHALEM_EP
+  "Atom",                 // 0x1B Z5xx series Silverthorn
+  "",
+  "Core 2",               // 0x1D Dunnington (6-core)
+  "Nehalem",              // 0x1E CPU_MODEL_NEHALEM
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "Westmere",             // 0x25 CPU_MODEL_WESTMERE
+  "",
+  "",
+  "",                     // 0x28
+  "",
+  "Sandy Bridge",         // 0x2a "2nd Generation Intel Core i7, i5, i3"
+  "",
+  "Westmere-EP",          // 0x2c CPU_MODEL_WESTMERE_EP
+  "Sandy Bridge-EP",      // 0x2d CPU_MODEL_SANDYBRIDGE_EP
+  "Nehalem-EX",           // 0x2e CPU_MODEL_NEHALEM_EX
+  "Westmere-EX",          // 0x2f CPU_MODEL_WESTMERE_EX
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "Ivy Bridge",           // 0x3a
+  "",
+  "Haswell",              // 0x3c "4th Generation Intel Core Processor"
+  "",                     // 0x3d "Next Generation Intel Core Processor"
+  "Ivy Bridge-EP",        // 0x3e "Next Generation Intel Xeon Processor E7 Family"
+  "",                     // 0x3f "Future Generation Intel Xeon Processor"
+  "",
+  "",
+  "",
+  "",
+  "",
+  "Haswell",              // 0x45 "4th Generation Intel Core Processor"
+  "Haswell",              // 0x46 "4th Generation Intel Core Processor"
+  NULL
+};
+
+/* Brand ID is for back compability
+ * Newer CPUs uses the extended brand string */
+const char* const VM_Version_Ext::_brand_id[] = {
+  "",
+  "Celeron processor",
+  "Pentium III processor",
+  "Intel Pentium III Xeon processor",
+  "",
+  "",
+  "",
+  "",
+  "Intel Pentium 4 processor",
+  NULL
+};
+
+
+const char* const VM_Version_Ext::_feature_edx_id[] = {
+  "On-Chip FPU",
+  "Virtual Mode Extensions",
+  "Debugging Extensions",
+  "Page Size Extensions",
+  "Time Stamp Counter",
+  "Model Specific Registers",
+  "Physical Address Extension",
+  "Machine Check Exceptions",
+  "CMPXCHG8B Instruction",
+  "On-Chip APIC",
+  "",
+  "Fast System Call",
+  "Memory Type Range Registers",
+  "Page Global Enable",
+  "Machine Check Architecture",
+  "Conditional Mov Instruction",
+  "Page Attribute Table",
+  "36-bit Page Size Extension",
+  "Processor Serial Number",
+  "CLFLUSH Instruction",
+  "",
+  "Debug Trace Store feature",
+  "ACPI registers in MSR space",
+  "Intel Architecture MMX Technology",
+  "Fast Float Point Save and Restore",
+  "Streaming SIMD extensions",
+  "Streaming SIMD extensions 2",
+  "Self-Snoop",
+  "Hyper Threading",
+  "Thermal Monitor",
+  "",
+  "Pending Break Enable"
+};
+
+const char* const VM_Version_Ext::_feature_extended_edx_id[] = {
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "SYSCALL/SYSRET",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "Execute Disable Bit",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "RDTSCP",
+  "",
+  "Intel 64 Architecture",
+  "",
+  ""
+};
+
+const char* const VM_Version_Ext::_feature_ecx_id[] = {
+  "Streaming SIMD Extensions 3",
+  "PCLMULQDQ",
+  "64-bit DS Area",
+  "MONITOR/MWAIT instructions",
+  "CPL Qualified Debug Store",
+  "Virtual Machine Extensions",
+  "Safer Mode Extensions",
+  "Enhanced Intel SpeedStep technology",
+  "Thermal Monitor 2",
+  "Supplemental Streaming SIMD Extensions 3",
+  "L1 Context ID",
+  "",
+  "Fused Multiply-Add",
+  "CMPXCHG16B",
+  "xTPR Update Control",
+  "Perfmon and Debug Capability",
+  "",
+  "Process-context identifiers",
+  "Direct Cache Access",
+  "Streaming SIMD extensions 4.1",
+  "Streaming SIMD extensions 4.2",
+  "x2APIC",
+  "MOVBE",
+  "Popcount instruction",
+  "TSC-Deadline",
+  "AESNI",
+  "XSAVE",
+  "OSXSAVE",
+  "AVX",
+  "F16C",
+  "RDRAND",
+  ""
+};
+
+const char* const VM_Version_Ext::_feature_extended_ecx_id[] = {
+  "LAHF/SAHF instruction support",
+  "Core multi-processor leagacy mode",
+  "",
+  "",
+  "",
+  "Advanced Bit Manipulations: LZCNT",
+  "SSE4A: MOVNTSS, MOVNTSD, EXTRQ, INSERTQ",
+  "Misaligned SSE mode",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  ""
+};