| /* |
| * 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", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "" |
| }; |