1 /* 2 * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "logging/log.hpp" 26 #include "precompiled.hpp" 27 #include "runtime/os.hpp" 28 #include "runtime/vm_version.hpp" 29 30 31 #define CPUINFO_LINE_SIZE 1024 32 33 34 class CPUinfo { 35 public: 36 CPUinfo(const char* field) : _string(NULL) { 37 38 char line[CPUINFO_LINE_SIZE]; 39 FILE* fp = fopen("/proc/cpuinfo", "r"); 40 41 if (fp != NULL) { 42 while (fgets(line, sizeof(line), fp) != NULL) { 43 assert(strlen(line) < sizeof(line) - 1, 44 "buffer too small (%d)", CPUINFO_LINE_SIZE); 45 46 const char* vstr = match_field(line, field); 47 48 if (vstr != NULL) { 49 // We have a matching line and a valid starting point to the value of 50 // the field, copy the string for keeps. 51 _string = strdup(vstr); 52 break; 53 } 54 } 55 fclose(fp); 56 } 57 } 58 59 ~CPUinfo() { free((void*)_string); } 60 61 const char* value() const { return _string; } 62 63 bool valid() const { return _string != NULL; } 64 65 bool match(const char* s) const { 66 return valid() ? strcmp(_string, s) == 0 : false; 67 } 68 69 private: 70 const char* _string; 71 72 const char* match_field(char line[CPUINFO_LINE_SIZE], const char* field); 73 const char* match_alo(const char* text, const char* exp); 74 const char* match_seq(const char* text, const char* seq); 75 }; 76 77 /* Given a line of text read from /proc/cpuinfo, determine if the property header 78 * matches the field specified, according to the following regexp: "<field>"\W+:\W+ 79 * 80 * If we have a matching expression, return a pointer to the first character after 81 * the matching pattern, i.e. the "value", otherwise return NULL. 82 */ 83 const char* CPUinfo::match_field(char line[CPUINFO_LINE_SIZE], const char* field) { 84 return match_alo(match_seq(match_alo(match_seq(line, field), "\t "), ":"), "\t "); 85 } 86 87 /* Match a sequence of at-least-one character in the string expression (exp) to 88 * the text input. 89 */ 90 const char* CPUinfo::match_alo(const char* text, const char* exp) { 91 if (text == NULL) return NULL; 92 93 const char* chp; 94 95 for (chp = &text[0]; *chp != '\0'; chp++) { 96 if (strchr(exp, *chp) == NULL) break; 97 } 98 99 return text < chp ? chp : NULL; 100 } 101 102 /* Match an exact sequence of characters as specified by the string expression 103 * (seq) to the text input. 104 */ 105 const char* CPUinfo::match_seq(const char* text, const char* seq) { 106 if (text == NULL) return NULL; 107 108 while (*seq != '\0') { 109 if (*seq != *text++) break; else seq++; 110 } 111 112 return *seq == '\0' ? text : NULL; 113 } 114 115 116 typedef struct { 117 const uint32_t hash; 118 bool seen; 119 const char* const name; 120 const uint64_t mask; 121 } FeatureEntry; 122 123 124 static uint64_t parse_features(FeatureEntry feature_tbl[], const char input[]); 125 126 127 void VM_Version::platform_features() { 128 129 // Some of the features reported via "cpucaps", such as; 'flush', 'stbar', 130 // 'swap', 'muldiv', 'ultra3', 'blkinit', 'n2', 'mul32', 'div32', 'fsmuld' 131 // and 'v8plus', are either SPARC V8, supported by all HW or simply nonsense 132 // (the 'ultra3' "property"). 133 // 134 // Entries marked as 'NYI' are not yet supported via "cpucaps" but are 135 // expected to have the names used in the table below (these are SPARC M7 136 // features or more recent). 137 // 138 // NOTE: Table sorted on lookup/hash ID. 139 140 static FeatureEntry s_feature_tbl[] = { 141 { 0x006f, false, "v9", ISA_v9_msk }, // Mandatory 142 { 0x00a6, false, "md5", ISA_md5_msk }, 143 { 0x00ce, false, "adi", ISA_adi_msk }, // NYI 144 { 0x00d7, false, "ima", ISA_ima_msk }, 145 { 0x00d9, false, "aes", ISA_aes_msk }, 146 { 0x00db, false, "hpc", ISA_hpc_msk }, 147 { 0x00dc, false, "des", ISA_des_msk }, 148 { 0x00ed, false, "sha1", ISA_sha1_msk }, 149 { 0x00f2, false, "vis", ISA_vis1_msk }, 150 { 0x0104, false, "vis2", ISA_vis2_msk }, 151 { 0x0105, false, "vis3", ISA_vis3_msk }, 152 { 0x0114, false, "sha512", ISA_sha512_msk }, 153 { 0x0119, false, "sha256", ISA_sha256_msk }, 154 { 0x011a, false, "fmaf", ISA_fmaf_msk }, 155 { 0x0132, false, "popc", ISA_popc_msk }, 156 { 0x0140, false, "crc32c", ISA_crc32c_msk }, 157 { 0x0147, false, "vis3b", ISA_vis3b_msk }, // NYI 158 { 0x017e, false, "pause", ISA_pause_msk }, 159 { 0x0182, false, "mwait", ISA_mwait_msk }, // NYI 160 { 0x018b, false, "mpmul", ISA_mpmul_msk }, 161 { 0x018e, false, "sparc5", ISA_sparc5_msk }, // NYI 162 { 0x01a9, false, "cbcond", ISA_cbcond_msk }, 163 { 0x01c3, false, "vamask", ISA_vamask_msk }, // NYI 164 { 0x01ca, false, "kasumi", ISA_kasumi_msk }, 165 { 0x01e3, false, "xmpmul", ISA_xmpmul_msk }, // NYI 166 { 0x022c, false, "montmul", ISA_mont_msk }, 167 { 0x0234, false, "montsqr", ISA_mont_msk }, 168 { 0x0238, false, "camellia", ISA_camellia_msk }, 169 { 0x024a, false, "ASIBlkInit", ISA_blk_init_msk }, 170 { 0x0284, false, "xmontmul", ISA_xmont_msk }, // NYI 171 { 0x02e6, false, "pause_nsec", ISA_pause_nsec_msk }, // NYI 172 173 { 0x0000, false, NULL, 0 } 174 }; 175 176 CPUinfo caps("cpucaps"); // Read "cpucaps" from /proc/cpuinfo. 177 178 assert(caps.valid(), "must be"); 179 180 _features = parse_features(s_feature_tbl, caps.value()); 181 182 assert(has_v9(), "must be"); // Basic SPARC-V9 required (V8 not supported). 183 184 CPUinfo type("type"); 185 186 bool is_sun4v = type.match("sun4v"); // All Oracle SPARC + Fujitsu Athena+ 187 bool is_sun4u = type.match("sun4u"); // All other Fujitsu 188 189 uint64_t synthetic = 0; 190 191 if (is_sun4v) { 192 // Indirect and direct branches are equally fast. 193 synthetic = CPU_fast_ind_br_msk; 194 // Fast IDIV, BIS and LD available on Niagara Plus. 195 if (has_vis2()) { 196 synthetic |= (CPU_fast_idiv_msk | CPU_fast_ld_msk); 197 // ...on Core C4 however, we prefer not to use BIS. 198 if (!has_sparc5()) { 199 synthetic |= CPU_fast_bis_msk; 200 } 201 } 202 // Niagara Core C3 supports fast RDPC and block zeroing. 203 if (has_ima()) { 204 synthetic |= (CPU_fast_rdpc_msk | CPU_blk_zeroing_msk); 205 } 206 // Niagara Core C3 and C4 have slow CMOVE. 207 if (!has_ima()) { 208 synthetic |= CPU_fast_cmove_msk; 209 } 210 } else if (is_sun4u) { 211 // SPARC64 only have fast IDIV and RDPC. 212 synthetic |= (CPU_fast_idiv_msk | CPU_fast_rdpc_msk); 213 } else { 214 log_info(os, cpu)("Unable to derive CPU features: %s", type.value()); 215 } 216 217 _features += synthetic; // Including CPU derived/synthetic features. 218 } 219 220 221 //////////////////////////////////////////////////////////////////////////////// 222 223 static uint32_t uhash32(const char name[]); 224 225 static void update_table(FeatureEntry feature_tbl[], uint32_t hv, 226 const char* ch1p, 227 const char* endp); 228 229 /* Given a feature table, parse the input text holding the string value of 230 * 'cpucaps' as reported by '/proc/cpuinfo', in order to complete the table 231 * with information on each admissible feature (whether present or not). 232 * 233 * Return the composite bit-mask representing the features found. 234 */ 235 static uint64_t parse_features(FeatureEntry feature_tbl[], const char input[]) { 236 log_info(os, cpu)("Parse CPU features: %s\n", input); 237 238 #ifdef ASSERT 239 // Verify that hash value entries in the table are unique and ordered. 240 241 uint32_t prev = 0; 242 243 for (uint k = 0; feature_tbl[k].name != NULL; k++) { 244 feature_tbl[k].seen = false; 245 246 assert(feature_tbl[k].hash == uhash32(feature_tbl[k].name), 247 "feature '%s' has mismatching hash 0x%08x (expected 0x%08x).\n", 248 feature_tbl[k].name, 249 feature_tbl[k].hash, 250 uhash32(feature_tbl[k].name)); 251 252 assert(prev < feature_tbl[k].hash, 253 "feature '%s' has invalid hash 0x%08x (previous is 0x%08x).\n", 254 feature_tbl[k].name, 255 feature_tbl[k].hash, 256 prev); 257 258 prev = feature_tbl[k].hash; 259 } 260 #endif 261 // Identify features from the input, consisting of a string with features 262 // separated by commas (or whitespace), e.g. "flush,muldiv,v9,mul32,div32, 263 // v8plus,popc,vis". 264 265 uint32_t hv = 0; 266 const char* ch1p = &input[0]; 267 uint i = 0; 268 269 do { 270 char ch = input[i]; 271 272 if (isalnum(ch) || ch == '_') { 273 hv += (ch - 32u); 274 } 275 else if (isspace(ch) || ch == ',' || ch == '\0') { // end-of-token 276 if (ch1p < &input[i]) { 277 update_table(feature_tbl, hv, ch1p, &input[i]); 278 } 279 ch1p = &input[i + 1]; hv = 0; 280 } else { 281 // Handle non-accepted input robustly. 282 log_info(os, cpu)("Bad token in feature string: '%c' (0x%02x).\n", ch, ch); 283 ch1p = &input[i + 1]; hv = 0; 284 } 285 } 286 while (input[i++] != '\0'); 287 288 // Compute actual bit-mask representation. 289 290 uint64_t mask = 0; 291 292 for (uint k = 0; feature_tbl[k].name != NULL; k++) { 293 mask |= feature_tbl[k].seen ? feature_tbl[k].mask : 0; 294 } 295 296 return mask; 297 } 298 299 static uint32_t uhash32(const char name[]) { 300 uint32_t hv = 0; 301 302 for (uint i = 0; name[i] != '\0'; i++) { 303 hv += (name[i] - 32u); 304 } 305 306 return hv; 307 } 308 309 static bool verify_match(const char name[], const char* ch1p, const char* endp); 310 311 static void update_table(FeatureEntry feature_tbl[], uint32_t hv, const char* ch1p, const char* endp) { 312 assert(ch1p < endp, "at least one character"); 313 314 // Look for a hash value in the table. Since this table is a small one (and 315 // is expected to stay small), we use a simple linear search (iff the table 316 // grows large, we may consider to adopt a binary ditto, or a perfect hash). 317 318 for (uint k = 0; feature_tbl[k].name != NULL; k++) { 319 uint32_t hash = feature_tbl[k].hash; 320 321 if (hash < hv) continue; 322 323 if (hash == hv) { 324 const char* name = feature_tbl[k].name; 325 326 if (verify_match(name, ch1p, endp)) { 327 feature_tbl[k].seen = true; 328 break; 329 } 330 } 331 332 // Either a non-matching feature (when hash == hv) or hash > hv. In either 333 // case we break out of the loop and terminate the search (note that the 334 // table is assumed to be uniquely sorted on the hash). 335 336 break; 337 } 338 } 339 340 static bool verify_match(const char name[], const char* ch1p, const char* endp) { 341 size_t len = strlen(name); 342 343 if (len != static_cast<size_t>(endp - ch1p)) { 344 return false; 345 } 346 347 for (uint i = 0; ch1p + i < endp; i++) { 348 if (name[i] != ch1p[i]) return false; 349 } 350 351 return true; 352 }