/* * Copyright (c) 2006, 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 "logging/log.hpp" #include "precompiled.hpp" #include "runtime/os.hpp" #include "runtime/vm_version.hpp" #define CPUINFO_LINE_SIZE 1024 class CPUinfo { public: CPUinfo(const char* field) : _string(NULL) { char line[CPUINFO_LINE_SIZE]; FILE* fp = fopen("/proc/cpuinfo", "r"); if (fp != NULL) { while (fgets(line, sizeof(line), fp) != NULL) { assert(strlen(line) < sizeof(line) - 1, "buffer too small (%d)", CPUINFO_LINE_SIZE); const char* vstr = match_field(line, field); if (vstr != NULL) { // We have a matching line and a valid starting point to the value of // the field, copy the string for keeps. _string = strdup(vstr); break; } } fclose(fp); } } ~CPUinfo() { free((void*)_string); } const char* value() const { return _string; } bool valid() const { return _string != NULL; } bool match(const char* s) const { return valid() ? strcmp(_string, s) == 0 : false; } private: const char* _string; const char* match_field(char line[CPUINFO_LINE_SIZE], const char* field); const char* match_alo(const char* text, const char* exp); const char* match_seq(const char* text, const char* seq); }; /* Given a line of text read from /proc/cpuinfo, determine if the property header * matches the field specified, according to the following regexp: ""\W+:\W+ * * If we have a matching expression, return a pointer to the first character after * the matching pattern, i.e. the "value", otherwise return NULL. */ const char* CPUinfo::match_field(char line[CPUINFO_LINE_SIZE], const char* field) { return match_alo(match_seq(match_alo(match_seq(line, field), "\t "), ":"), "\t "); } /* Match a sequence of at-least-one character in the string expression (exp) to * the text input. */ const char* CPUinfo::match_alo(const char* text, const char* exp) { if (text == NULL) return NULL; const char* chp; for (chp = &text[0]; *chp != '\0'; chp++) { if (strchr(exp, *chp) == NULL) break; } return text < chp ? chp : NULL; } /* Match an exact sequence of characters as specified by the string expression * (seq) to the text input. */ const char* CPUinfo::match_seq(const char* text, const char* seq) { if (text == NULL) return NULL; while (*seq != '\0') { if (*seq != *text++) break; else seq++; } return *seq == '\0' ? text : NULL; } typedef struct { const uint32_t hash; bool seen; const char* const name; const uint64_t mask; } FeatureEntry; static uint64_t parse_features(FeatureEntry feature_tbl[], const char input[]); void VM_Version::platform_features() { // Some of the features reported via "cpucaps", such as; 'flush', 'stbar', // 'swap', 'muldiv', 'ultra3', 'blkinit', 'n2', 'mul32', 'div32', 'fsmuld' // and 'v8plus', are either SPARC V8, supported by all HW or simply nonsense // (the 'ultra3' "property"). // // Entries marked as 'NYI' are not yet supported via "cpucaps" but are // expected to have the names used in the table below (these are SPARC M7 // features or more recent). // // NOTE: Table sorted on lookup/hash ID. static FeatureEntry s_feature_tbl[] = { { 0x006f, false, "v9", ISA_v9_msk }, // Mandatory { 0x00a6, false, "md5", ISA_md5_msk }, { 0x00ce, false, "adi", ISA_adi_msk }, // NYI { 0x00d7, false, "ima", ISA_ima_msk }, { 0x00d9, false, "aes", ISA_aes_msk }, { 0x00db, false, "hpc", ISA_hpc_msk }, { 0x00dc, false, "des", ISA_des_msk }, { 0x00ed, false, "sha1", ISA_sha1_msk }, { 0x00f2, false, "vis", ISA_vis1_msk }, { 0x0104, false, "vis2", ISA_vis2_msk }, { 0x0105, false, "vis3", ISA_vis3_msk }, { 0x0114, false, "sha512", ISA_sha512_msk }, { 0x0119, false, "sha256", ISA_sha256_msk }, { 0x011a, false, "fmaf", ISA_fmaf_msk }, { 0x0132, false, "popc", ISA_popc_msk }, { 0x0140, false, "crc32c", ISA_crc32c_msk }, { 0x0147, false, "vis3b", ISA_vis3b_msk }, // NYI { 0x017e, false, "pause", ISA_pause_msk }, { 0x0182, false, "mwait", ISA_mwait_msk }, // NYI { 0x018b, false, "mpmul", ISA_mpmul_msk }, { 0x018e, false, "sparc5", ISA_sparc5_msk }, // NYI { 0x01a9, false, "cbcond", ISA_cbcond_msk }, { 0x01c3, false, "vamask", ISA_vamask_msk }, // NYI { 0x01ca, false, "kasumi", ISA_kasumi_msk }, { 0x01e3, false, "xmpmul", ISA_xmpmul_msk }, // NYI { 0x022c, false, "montmul", ISA_mont_msk }, { 0x0234, false, "montsqr", ISA_mont_msk }, { 0x0238, false, "camellia", ISA_camellia_msk }, { 0x024a, false, "ASIBlkInit", ISA_blk_init_msk }, { 0x0284, false, "xmontmul", ISA_xmont_msk }, // NYI { 0x02e6, false, "pause_nsec", ISA_pause_nsec_msk }, // NYI { 0x0000, false, NULL, 0 } }; CPUinfo caps("cpucaps"); // Read "cpucaps" from /proc/cpuinfo. assert(caps.valid(), "must be"); _features = parse_features(s_feature_tbl, caps.value()); assert(has_v9(), "must be"); // Basic SPARC-V9 required (V8 not supported). CPUinfo type("type"); bool is_sun4v = type.match("sun4v"); // All Oracle SPARC + Fujitsu Athena+ bool is_sun4u = type.match("sun4u"); // All other Fujitsu uint64_t synthetic = 0; if (is_sun4v) { // Indirect and direct branches are equally fast. synthetic = CPU_fast_ind_br_msk; // Fast IDIV, BIS and LD available on Niagara Plus. if (has_vis2()) { synthetic |= (CPU_fast_idiv_msk | CPU_fast_ld_msk); // ...on Core C4 however, we prefer not to use BIS. if (!has_sparc5()) { synthetic |= CPU_fast_bis_msk; } } // Niagara Core C3 supports fast RDPC and block zeroing. if (has_ima()) { synthetic |= (CPU_fast_rdpc_msk | CPU_blk_zeroing_msk); } // Niagara Core C3 and C4 have slow CMOVE. if (!has_ima()) { synthetic |= CPU_fast_cmove_msk; } } else if (is_sun4u) { // SPARC64 only have fast IDIV and RDPC. synthetic |= (CPU_fast_idiv_msk | CPU_fast_rdpc_msk); } else { log_info(os, cpu)("Unable to derive CPU features: %s", type.value()); } _features += synthetic; // Including CPU derived/synthetic features. } //////////////////////////////////////////////////////////////////////////////// static uint32_t uhash32(const char name[]); static void update_table(FeatureEntry feature_tbl[], uint32_t hv, const char* ch1p, const char* endp); /* Given a feature table, parse the input text holding the string value of * 'cpucaps' as reported by '/proc/cpuinfo', in order to complete the table * with information on each admissible feature (whether present or not). * * Return the composite bit-mask representing the features found. */ static uint64_t parse_features(FeatureEntry feature_tbl[], const char input[]) { log_info(os, cpu)("Parse CPU features: %s\n", input); #ifdef ASSERT // Verify that hash value entries in the table are unique and ordered. uint32_t prev = 0; for (uint k = 0; feature_tbl[k].name != NULL; k++) { feature_tbl[k].seen = false; assert(feature_tbl[k].hash == uhash32(feature_tbl[k].name), "feature '%s' has mismatching hash 0x%08x (expected 0x%08x).\n", feature_tbl[k].name, feature_tbl[k].hash, uhash32(feature_tbl[k].name)); assert(prev < feature_tbl[k].hash, "feature '%s' has invalid hash 0x%08x (previous is 0x%08x).\n", feature_tbl[k].name, feature_tbl[k].hash, prev); prev = feature_tbl[k].hash; } #endif // Identify features from the input, consisting of a string with features // separated by commas (or whitespace), e.g. "flush,muldiv,v9,mul32,div32, // v8plus,popc,vis". uint32_t hv = 0; const char* ch1p = &input[0]; uint i = 0; do { char ch = input[i]; if (isalnum(ch) || ch == '_') { hv += (ch - 32u); } else if (isspace(ch) || ch == ',' || ch == '\0') { // end-of-token if (ch1p < &input[i]) { update_table(feature_tbl, hv, ch1p, &input[i]); } ch1p = &input[i + 1]; hv = 0; } else { // Handle non-accepted input robustly. log_info(os, cpu)("Bad token in feature string: '%c' (0x%02x).\n", ch, ch); ch1p = &input[i + 1]; hv = 0; } } while (input[i++] != '\0'); // Compute actual bit-mask representation. uint64_t mask = 0; for (uint k = 0; feature_tbl[k].name != NULL; k++) { mask |= feature_tbl[k].seen ? feature_tbl[k].mask : 0; } return mask; } static uint32_t uhash32(const char name[]) { uint32_t hv = 0; for (uint i = 0; name[i] != '\0'; i++) { hv += (name[i] - 32u); } return hv; } static bool verify_match(const char name[], const char* ch1p, const char* endp); static void update_table(FeatureEntry feature_tbl[], uint32_t hv, const char* ch1p, const char* endp) { assert(ch1p < endp, "at least one character"); // Look for a hash value in the table. Since this table is a small one (and // is expected to stay small), we use a simple linear search (iff the table // grows large, we may consider to adopt a binary ditto, or a perfect hash). for (uint k = 0; feature_tbl[k].name != NULL; k++) { uint32_t hash = feature_tbl[k].hash; if (hash < hv) continue; if (hash == hv) { const char* name = feature_tbl[k].name; if (verify_match(name, ch1p, endp)) { feature_tbl[k].seen = true; break; } } // Either a non-matching feature (when hash == hv) or hash > hv. In either // case we break out of the loop and terminate the search (note that the // table is assumed to be uniquely sorted on the hash). break; } } static bool verify_match(const char name[], const char* ch1p, const char* endp) { size_t len = strlen(name); if (len != static_cast(endp - ch1p)) { return false; } for (uint i = 0; ch1p + i < endp; i++) { if (name[i] != ch1p[i]) return false; } return true; }