1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2019, Joyent, Inc. 14 * Copyright 2021 Oxide Computer Company 15 */ 16 17 /* 18 * This program transforms Intel perfmon and AMD PMC data files into C files and 19 * manual pages. 20 */ 21 22 #include <stdio.h> 23 #include <stdarg.h> 24 #include <unistd.h> 25 #include <err.h> 26 #include <libgen.h> 27 #include <libnvpair.h> 28 #include <strings.h> 29 #include <errno.h> 30 #include <limits.h> 31 #include <sys/mman.h> 32 #include <sys/param.h> 33 #include <assert.h> 34 #include <ctype.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <fcntl.h> 38 #include <dirent.h> 39 40 #include <json_nvlist.h> 41 42 #define EXIT_USAGE 2 43 #define CPROC_MAX_STEPPINGS 16 44 45 typedef enum { 46 CPCGEN_MODE_UNKNOWN = 0, 47 CPCGEN_MODE_INTEL, 48 CPCGEN_MODE_AMD 49 } cpc_mode_t; 50 51 typedef struct cpc_proc { 52 struct cpc_proc *cproc_next; 53 uint_t cproc_family; 54 uint_t cproc_model; 55 uint_t cproc_nsteps; 56 uint_t cproc_steppings[CPROC_MAX_STEPPINGS]; 57 } cpc_proc_t; 58 59 typedef enum cpc_file_type { 60 CPC_FILE_CORE = 1 << 0, 61 CPC_FILE_OFF_CORE = 1 << 1, 62 CPC_FILE_UNCORE = 1 << 2, 63 CPC_FILE_FP_MATH = 1 << 3, 64 CPC_FILE_UNCORE_EXP = 1 << 4 65 } cpc_type_t; 66 67 typedef struct cpc_map { 68 struct cpc_map *cmap_next; 69 cpc_type_t cmap_type; 70 nvlist_t *cmap_data; 71 char *cmap_path; 72 const char *cmap_name; 73 cpc_proc_t *cmap_procs; 74 } cpc_map_t; 75 76 typedef struct cpc_whitelist { 77 const char *cwhite_short; 78 const char *cwhite_human; 79 uint_t cwhite_mask; 80 } cpc_whitelist_t; 81 82 /* 83 * List of architectures that we support generating this data for. This is done 84 * so that processors that illumos doesn't support or run on aren't generated 85 * (generally the Xeon Phi). 86 */ 87 static cpc_whitelist_t cpcgen_intel_whitelist[] = { 88 /* Nehalem */ 89 { "NHM-EP", "nhm_ep", CPC_FILE_CORE }, 90 { "NHM-EX", "nhm_ex", CPC_FILE_CORE }, 91 /* Westmere */ 92 { "WSM-EP-DP", "wsm_ep_dp", CPC_FILE_CORE }, 93 { "WSM-EP-SP", "wsm_ep_sp", CPC_FILE_CORE }, 94 { "WSM-EX", "wsm_ex", CPC_FILE_CORE }, 95 /* Sandy Bridge */ 96 { "SNB", "snb", CPC_FILE_CORE }, 97 { "JKT", "jkt", CPC_FILE_CORE }, 98 /* Ivy Bridge */ 99 { "IVB", "ivb", CPC_FILE_CORE }, 100 { "IVT", "ivt", CPC_FILE_CORE }, 101 /* Haswell */ 102 { "HSW", "hsw", CPC_FILE_CORE }, 103 { "HSX", "hsx", CPC_FILE_CORE }, 104 /* Broadwell */ 105 { "BDW", "bdw", CPC_FILE_CORE }, 106 { "BDW-DE", "bdw_de", CPC_FILE_CORE }, 107 { "BDX", "bdx", CPC_FILE_CORE }, 108 /* Skylake */ 109 { "SKL", "skl", CPC_FILE_CORE }, 110 { "SKX", "skx", CPC_FILE_CORE }, 111 /* Cascade Lake */ 112 { "CLX", "clx", CPC_FILE_CORE }, 113 /* Ice Lake */ 114 { "ICL", "icl", CPC_FILE_CORE }, 115 /* Tiger Lake */ 116 { "TGL", "tgl", CPC_FILE_CORE }, 117 /* Atom */ 118 { "BNL", "bnl", CPC_FILE_CORE }, 119 { "SLM", "slm", CPC_FILE_CORE }, 120 { "GLM", "glm", CPC_FILE_CORE }, 121 { "GLP", "glp", CPC_FILE_CORE }, 122 { "SNR", "snr", CPC_FILE_CORE }, 123 { NULL } 124 }; 125 126 typedef struct cpc_papi { 127 const char *cpapi_intc; 128 const char *cpapi_papi; 129 } cpc_papi_t; 130 131 /* 132 * This table maps events with an Intel specific name to the corresponding PAPI 133 * name. There may be multiple Intel events which map to the same PAPI event. 134 * This is usually because different processors have different names for an 135 * event. We use the title as opposed to the event codes because those can 136 * change somewhat arbitrarily between processor generations. 137 */ 138 static cpc_papi_t cpcgen_intel_papi_map[] = { 139 { "CPU_CLK_UNHALTED.THREAD_P", "PAPI_tot_cyc" }, 140 { "INST_RETIRED.ANY_P", "PAPI_tot_ins" }, 141 { "BR_INST_RETIRED.ALL_BRANCHES", "PAPI_br_ins" }, 142 { "BR_MISP_RETIRED.ALL_BRANCHES", "PAPI_br_msp" }, 143 { "BR_INST_RETIRED.CONDITIONAL", "PAPI_br_cn" }, 144 { "CYCLE_ACTIVITY.CYCLES_L1D_MISS", "PAPI_l1_dcm" }, 145 { "L1I.HITS", "PAPI_l1_ich" }, 146 { "ICACHE.HIT", "PAPI_l1_ich" }, 147 { "L1I.MISS", "PAPI_L1_icm" }, 148 { "ICACHE.MISSES", "PAPI_l1_icm" }, 149 { "L1I.READS", "PAPI_l1_ica" }, 150 { "ICACHE.ACCESSES", "PAPI_l1_ica" }, 151 { "L1I.READS", "PAPI_l1_icr" }, 152 { "ICACHE.ACCESSES", "PAPI_l1_icr" }, 153 { "L2_RQSTS.CODE_RD_MISS", "PAPI_l2_icm" }, 154 { "L2_RQSTS.MISS", "PAPI_l2_tcm" }, 155 { "ITLB_MISSES.MISS_CAUSES_A_WALK", "PAPI_tlb_im" }, 156 { "DTLB_LOAD_MISSES.MISS_CAUSES_A_WALK", "PAPI_tlb_dm" }, 157 { "PAGE_WALKS.D_SIDE_WALKS", "PAPI_tlb_dm" }, 158 { "PAGE_WALKS.I_SIDE_WALKS", "PAPI_tlb_im" }, 159 { "PAGE_WALKS.WALKS", "PAPI_tlb_tl" }, 160 { "INST_QUEUE_WRITES", "PAPI_tot_iis" }, 161 { "MEM_INST_RETIRED.STORES" "PAPI_sr_ins" }, 162 { "MEM_INST_RETIRED.LOADS" "PAPI_ld_ins" }, 163 { NULL, NULL } 164 }; 165 166 typedef struct cpcgen_ops { 167 void (*cgen_op_gather)(const char *, const char *); 168 void (*cgen_op_common)(int); 169 char *(*cgen_op_name)(cpc_map_t *); 170 boolean_t (*cgen_op_skip)(nvlist_t *, const char *, uint_t); 171 boolean_t (*cgen_op_file_before)(FILE *, cpc_map_t *); 172 boolean_t (*cgen_op_file_after)(FILE *, cpc_map_t *); 173 boolean_t (*cgen_op_event)(FILE *, nvlist_t *, const char *, uint32_t); 174 } cpcgen_ops_t; 175 176 static cpcgen_ops_t cpcgen_ops; 177 static const char *cpcgen_intel_mapfile = "/mapfile.csv"; 178 static const char *cpcgen_progname; 179 static cpc_map_t *cpcgen_maps; 180 static cpc_mode_t cpcgen_mode = CPCGEN_MODE_UNKNOWN; 181 182 /* 183 * Constants used for generating data. 184 */ 185 /* BEGIN CSTYLED */ 186 static const char *cpcgen_cfile_intel_header = "" 187 "/*\n" 188 " * Copyright (c) 2018, Intel Corporation\n" 189 " * Copyright (c) 2018, Joyent, Inc\n" 190 " * All rights reserved.\n" 191 " *\n" 192 " * Redistribution and use in source and binary forms, with or without\n" 193 " * modification, are permitted provided that the following conditions are met:\n" 194 " * \n" 195 " * 1. Redistributions of source code must retain the above copyright notice,\n" 196 " * this list of conditions and the following disclaimer.\n" 197 " * \n" 198 " * 2. Redistributions in binary form must reproduce the above copyright \n" 199 " * notice, this list of conditions and the following disclaimer in the\n" 200 " * documentation and/or other materials provided with the distribution.\n" 201 " * \n" 202 " * 3. Neither the name of the Intel Corporation nor the names of its \n" 203 " * contributors may be used to endorse or promote products derived from\n" 204 " * this software without specific prior written permission.\n" 205 " *\n" 206 " * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" 207 " * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" 208 " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" 209 " * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n" 210 " * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n" 211 " * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n" 212 " * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n" 213 " * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n" 214 " * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n" 215 " * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n" 216 " * POSSIBILITY OF SUCH DAMAGE.\n" 217 " *\n" 218 " * This file was automatically generated by cpcgen from the data file\n" 219 " * data/perfmon%s\n" 220 " *\n" 221 " * Do not modify this file. Your changes will be lost!\n" 222 " */\n" 223 "\n"; 224 /* END CSTYLED */ 225 226 static const char *cpcgen_cfile_intel_table_start = "" 227 "#include <core_pcbe_table.h>\n" 228 "\n" 229 "const struct events_table_t pcbe_core_events_%s[] = {\n"; 230 231 static const char *cpcgen_cfile_intel_table_end = "" 232 "\t{ NT_END, 0, 0, \"\" }\n" 233 "};\n"; 234 235 /* BEGIN CSTYLED */ 236 static const char *cpcgen_manual_intel_intel_header = "" 237 ".\\\" Copyright (c) 2018, Intel Corporation \n" 238 ".\\\" Copyright (c) 2018, Joyent, Inc.\n" 239 ".\\\" All rights reserved.\n" 240 ".\\\"\n" 241 ".\\\" Redistribution and use in source and binary forms, with or without \n" 242 ".\\\" modification, are permitted provided that the following conditions are met:\n" 243 ".\\\"\n" 244 ".\\\" 1. Redistributions of source code must retain the above copyright notice,\n" 245 ".\\\" this list of conditions and the following disclaimer.\n" 246 ".\\\"\n" 247 ".\\\" 2. Redistributions in binary form must reproduce the above copyright\n" 248 ".\\\" notice, this list of conditions and the following disclaimer in the\n" 249 ".\\\" documentation and/or other materials provided with the distribution.\n" 250 ".\\\"\n" 251 ".\\\" 3. Neither the name of the Intel Corporation nor the names of its\n" 252 ".\\\" contributors may be used to endorse or promote products derived from\n" 253 ".\\\" this software without specific prior written permission.\n" 254 ".\\\"\n" 255 ".\\\" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\n" 256 ".\\\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n" 257 ".\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n" 258 ".\\\" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\n" 259 ".\\\" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n" 260 ".\\\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n" 261 ".\\\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n" 262 ".\\\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n" 263 ".\\\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n" 264 ".\\\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n" 265 ".\\\" POSSIBILITY OF SUCH DAMAGE.\n" 266 ".\\\"\n" 267 ".\\\" This file was automatically generated by cpcgen from the data file\n" 268 ".\\\" data/perfmon%s\n" 269 ".\\\"\n" 270 ".\\\" Do not modify this file. Your changes will be lost!\n" 271 ".\\\"\n" 272 ".\\\" We would like to thank Intel for providing the perfmon data for use in\n" 273 ".\\\" our manual pages.\n" 274 ".Dd June 18, 2018\n" 275 ".Dt %s_EVENTS 3CPC\n" 276 ".Os\n" 277 ".Sh NAME\n" 278 ".Nm %s_events\n" 279 ".Nd processor model specific performance counter events\n" 280 ".Sh DESCRIPTION\n" 281 "This manual page describes events specific to the following Intel CPU\n" 282 "models and is derived from Intel's perfmon data.\n" 283 "For more information, please consult the Intel Software Developer's Manual " 284 "or Intel's perfmon website.\n" 285 ".Pp\n" 286 "CPU models described by this document:\n" 287 ".Bl -bullet\n"; 288 /* END CSTYLED */ 289 290 static const char *cpcgen_manual_intel_data = "" 291 ".El\n" 292 ".Pp\n" 293 "The following events are supported:\n" 294 ".Bl -tag -width Sy\n"; 295 296 static const char *cpcgen_manual_intel_trailer = "" 297 ".El\n" 298 ".Sh SEE ALSO\n" 299 ".Xr cpc 3CPC\n" 300 ".Pp\n" 301 ".Lk https://download.01.org/perfmon/index/"; 302 303 static const char *cpcgen_cfile_cddl_header = "" 304 "/*\n" 305 " * This file and its contents are supplied under the terms of the\n" 306 " * Common Development and Distribution License (\"CDDL\"), version 1.0.\n" 307 " * You may only use this file in accordance with the terms of version\n" 308 " * 1.0 of the CDDL.\n" 309 " *\n" 310 " * A full copy of the text of the CDDL should have accompanied this\n" 311 " * source. A copy of the CDDL is also available via the Internet at\n" 312 " * http://www.illumos.org/license/CDDL.\n" 313 " */\n" 314 "\n" 315 "/*\n" 316 " * Copyright 2019 Joyent, Inc\n" 317 " */\n" 318 "\n" 319 "/*\n" 320 " * This file was automatically generated by cpcgen.\n" 321 " */\n" 322 "\n" 323 "/*\n" 324 " * Do not modify this file. Your changes will be lost!\n" 325 " */\n" 326 "\n"; 327 328 static const char *cpcgen_manual_amd_header = "" 329 ".\\\" This file was automatically generated by cpcgen from the data file\n" 330 ".\\\" data/amdpmc/%s\n" 331 ".\\\"\n" 332 ".\\\" Do not modify this file. Your changes will be lost!\n" 333 ".\\\"\n" 334 ".\\\" We would like to thank AMD for providing the PMC data for use in\n" 335 ".\\\" our manual pages.\n" 336 ".Dd March 25, 2019\n" 337 ".Dt AMD_%s_EVENTS 3CPC\n" 338 ".Os\n" 339 ".Sh NAME\n" 340 ".Nm amd_%s_events\n" 341 ".Nd AMD Family %s processor performance monitoring events\n" 342 ".Sh DESCRIPTION\n" 343 "This manual page describes events specfic to AMD Family %s processors.\n" 344 "For more information, please consult the appropriate AMD BIOS and Kernel\n" 345 "Developer's guide or Open-Source Register Reference.\n" 346 ".Pp\n" 347 "Each of the events listed below includes the AMD mnemonic which matches\n" 348 "the name found in the AMD manual and a brief summary of the event.\n" 349 "If available, a more detailed description of the event follows and then\n" 350 "any additional unit values that modify the event.\n" 351 "Each unit can be combined to create a new event in the system by placing\n" 352 "the '.' character between the event name and the unit name.\n" 353 ".Pp\n" 354 "The following events are supported:\n" 355 ".Bl -tag -width Sy\n"; 356 357 static const char *cpcgen_manual_amd_trailer = "" 358 ".El\n" 359 ".Sh SEE ALSO\n" 360 ".Xr cpc 3CPC\n"; 361 362 static const char *cpcgen_cfile_amd_header = "" 363 "/*\n" 364 " * This file was automatically generated by cpcgen from the data file\n" 365 " * data/perfmon%s\n" 366 " *\n" 367 " * Do not modify this file. Your changes will be lost!\n" 368 " */\n" 369 "\n"; 370 371 static const char *cpcgen_cfile_amd_table_start = "" 372 "#include <opteron_pcbe_table.h>\n" 373 "#include <sys/null.h>\n" 374 "\n" 375 "const amd_event_t opteron_pcbe_%s_events[] = {\n"; 376 377 static const char *cpcgen_cfile_amd_table_end = "" 378 "\t{ NULL, 0, 0 }\n" 379 "};\n"; 380 381 static cpc_map_t * 382 cpcgen_map_lookup(const char *path) 383 { 384 cpc_map_t *m; 385 386 for (m = cpcgen_maps; m != NULL; m = m->cmap_next) { 387 if (strcmp(path, m->cmap_path) == 0) { 388 return (m); 389 } 390 } 391 392 return (NULL); 393 } 394 395 /* 396 * Parse a string of the form 'GenuineIntel-6-2E' and get out the family and 397 * model. 398 */ 399 static void 400 cpcgen_parse_model(char *fsr, uint_t *family, uint_t *model, uint_t *nstepp, 401 uint_t *steppings) 402 { 403 const char *bstr = "GenuineIntel"; 404 const char *brand, *fam, *mod, *step; 405 char *last; 406 long l; 407 uint_t nstep = 0; 408 409 /* 410 * Tokeninze the string. There may be an optional stepping portion, 411 * which has a range of steppings enclosed by '[' and ']' characters. 412 * While the other parts are required, the stepping may be missing. 413 */ 414 if ((brand = strtok_r(fsr, "-", &last)) == NULL || 415 (fam = strtok_r(NULL, "-", &last)) == NULL || 416 (mod = strtok_r(NULL, "-", &last)) == NULL) { 417 errx(EXIT_FAILURE, "failed to parse processor id \"%s\"", fsr); 418 } 419 step = strtok_r(NULL, "-", &last); 420 421 if (strcmp(bstr, brand) != 0) { 422 errx(EXIT_FAILURE, "brand string \"%s\" did not match \"%s\"", 423 brand, bstr); 424 } 425 426 errno = 0; 427 l = strtol(fam, &last, 16); 428 if (errno != 0 || l < 0 || l >= INT_MAX || *last != '\0') { 429 errx(EXIT_FAILURE, "failed to parse family \"%s\"", fam); 430 } 431 *family = (uint_t)l; 432 433 l = strtol(mod, &last, 16); 434 if (errno != 0 || l < 0 || l >= INT_MAX || *last != '\0') { 435 errx(EXIT_FAILURE, "failed to parse model \"%s\"", mod); 436 } 437 *model = (uint_t)l; 438 439 if (step == NULL) { 440 *nstepp = 0; 441 return; 442 } 443 444 if (*step != '[' || ((last = strrchr(step, ']')) == NULL)) { 445 errx(EXIT_FAILURE, "failed to parse stepping \"%s\": missing " 446 "stepping range brackets", step); 447 } 448 step++; 449 *last = '\0'; 450 while (*step != '\0') { 451 if (!isxdigit(*step)) { 452 errx(EXIT_FAILURE, "failed to parse stepping: invalid " 453 "stepping identifier '0x%x'", *step); 454 } 455 456 if (nstep >= CPROC_MAX_STEPPINGS) { 457 errx(EXIT_FAILURE, "failed to parse stepping: " 458 "encountered too many steppings"); 459 } 460 461 switch (*step) { 462 case '0': 463 steppings[nstep] = 0x0; 464 break; 465 case '1': 466 steppings[nstep] = 0x1; 467 break; 468 case '2': 469 steppings[nstep] = 0x2; 470 break; 471 case '3': 472 steppings[nstep] = 0x3; 473 break; 474 case '4': 475 steppings[nstep] = 0x4; 476 break; 477 case '5': 478 steppings[nstep] = 0x5; 479 break; 480 case '6': 481 steppings[nstep] = 0x6; 482 break; 483 case '7': 484 steppings[nstep] = 0x7; 485 break; 486 case '8': 487 steppings[nstep] = 0x8; 488 break; 489 case '9': 490 steppings[nstep] = 0x9; 491 break; 492 case 'a': 493 case 'A': 494 steppings[nstep] = 0xa; 495 break; 496 case 'b': 497 case 'B': 498 steppings[nstep] = 0xb; 499 break; 500 case 'c': 501 case 'C': 502 steppings[nstep] = 0xc; 503 break; 504 case 'd': 505 case 'D': 506 steppings[nstep] = 0xd; 507 break; 508 case 'e': 509 case 'E': 510 steppings[nstep] = 0xe; 511 break; 512 case 'f': 513 case 'F': 514 steppings[nstep] = 0xf; 515 break; 516 default: 517 errx(EXIT_FAILURE, "encountered non-hex stepping " 518 "character: '%c'", *step); 519 } 520 nstep++; 521 step++; 522 } 523 524 *nstepp = nstep; 525 } 526 527 static nvlist_t * 528 cpcgen_read_datafile(const char *datadir, const char *file) 529 { 530 int fd; 531 char *path; 532 struct stat st; 533 void *map; 534 nvlist_t *nvl; 535 nvlist_parse_json_error_t jerr; 536 537 if (asprintf(&path, "%s/%s", datadir, file) == -1) { 538 err(EXIT_FAILURE, "failed to construct path to data file %s", 539 file); 540 } 541 542 if ((fd = open(path, O_RDONLY)) < 0) { 543 err(EXIT_FAILURE, "failed to open data file %s", path); 544 } 545 546 if (fstat(fd, &st) != 0) { 547 err(EXIT_FAILURE, "failed to stat %s", path); 548 } 549 550 if ((map = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, 551 fd, 0)) == MAP_FAILED) { 552 err(EXIT_FAILURE, "failed to mmap %s", path); 553 } 554 555 if (nvlist_parse_json(map, st.st_size, &nvl, NVJSON_FORCE_INTEGER, 556 &jerr) != 0) { 557 errx(EXIT_FAILURE, "failed to parse file %s at pos %ld: %s", 558 path, jerr.nje_pos, jerr.nje_message); 559 } 560 561 if (munmap(map, st.st_size) != 0) { 562 err(EXIT_FAILURE, "failed to munmap %s", path); 563 } 564 565 if (close(fd) != 0) { 566 err(EXIT_FAILURE, "failed to close data file %s", path); 567 } 568 free(path); 569 570 return (nvl); 571 } 572 573 /* 574 * Check the whitelist to see if we should use this model. 575 */ 576 static const char * 577 cpcgen_use_arch(const char *path, cpc_type_t type, const char *platform) 578 { 579 const char *slash; 580 size_t len; 581 uint_t i; 582 583 if (*path != '/') { 584 errx(EXIT_FAILURE, "invalid path in mapfile: \"%s\": missing " 585 "leading '/'", path); 586 } 587 if ((slash = strchr(path + 1, '/')) == NULL) { 588 errx(EXIT_FAILURE, "invalid path in mapfile: \"%s\": missing " 589 "second '/'", path); 590 } 591 /* Account for the last '/' character. */ 592 len = slash - path - 1; 593 assert(len > 0); 594 595 for (i = 0; cpcgen_intel_whitelist[i].cwhite_short != NULL; i++) { 596 if (platform != NULL && strcasecmp(platform, 597 cpcgen_intel_whitelist[i].cwhite_short) != 0) 598 continue; 599 if (strncmp(path + 1, cpcgen_intel_whitelist[i].cwhite_short, 600 len) == 0 && 601 (cpcgen_intel_whitelist[i].cwhite_mask & type) == type) { 602 return (cpcgen_intel_whitelist[i].cwhite_human); 603 } 604 } 605 606 return (NULL); 607 } 608 609 /* 610 * Determine which CPU Vendor we're transmuting data from. 611 */ 612 static void 613 cpcgen_determine_vendor(const char *datadir) 614 { 615 char *mappath; 616 struct stat st; 617 618 if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) { 619 err(EXIT_FAILURE, "failed to construct path to mapfile"); 620 } 621 622 if (stat(mappath, &st) == 0) { 623 cpcgen_mode = CPCGEN_MODE_INTEL; 624 } else { 625 if (errno != ENOENT) { 626 err(EXIT_FAILURE, "stat(2) of %s failed unexpectedly"); 627 } 628 629 cpcgen_mode = CPCGEN_MODE_AMD; 630 } 631 632 free(mappath); 633 } 634 635 /* 636 * Read in all the data files that exist for AMD. 637 * 638 * Our family names for AMD systems are based on the family and type so a given 639 * name will look like f17h_<core>_core.json. 640 */ 641 static void 642 cpcgen_read_amd(const char *datadir, const char *platform) 643 { 644 DIR *dir; 645 struct dirent *d; 646 const char *suffix = ".json"; 647 const size_t slen = strlen(suffix); 648 649 if ((dir = opendir(datadir)) == NULL) { 650 err(EXIT_FAILURE, "failed to open directory %s", datadir); 651 } 652 653 while ((d = readdir(dir)) != NULL) { 654 char *name, *c; 655 cpc_map_t *map; 656 nvlist_t *parsed; 657 658 if ((name = strdup(d->d_name)) == NULL) { 659 errx(EXIT_FAILURE, "ran out of memory duplicating " 660 "name %s", d->d_name); 661 } 662 c = strstr(name, suffix); 663 664 if (c == NULL) { 665 free(name); 666 continue; 667 } 668 669 /* 670 * Chop off the .json. Next, make sure we have both _ present. 671 */ 672 if (*(c + slen) != '\0') { 673 free(name); 674 continue; 675 } 676 *c = '\0'; 677 678 c = strchr(name, '_'); 679 if (c == NULL) { 680 errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s", 681 d->d_name); 682 } 683 684 c++; 685 c = strchr(c, '_'); 686 if (c == NULL) { 687 errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s", 688 d->d_name); 689 } 690 *c = '\0'; 691 c++; 692 if (strcmp(c, "core") != 0) { 693 errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s", 694 d->d_name); 695 } 696 697 if (platform != NULL && strcmp(platform, name) != 0) { 698 free(name); 699 continue; 700 } 701 702 if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) { 703 err(EXIT_FAILURE, "failed to allocate space for cpc " 704 "file"); 705 } 706 707 parsed = cpcgen_read_datafile(datadir, d->d_name); 708 if ((map->cmap_path = strdup(d->d_name)) == NULL) { 709 err(EXIT_FAILURE, "failed to duplicate path string"); 710 } 711 map->cmap_type = CPC_FILE_CORE; 712 map->cmap_data = parsed; 713 map->cmap_name = name; 714 map->cmap_procs = NULL; 715 716 map->cmap_next = cpcgen_maps; 717 cpcgen_maps = map; 718 } 719 } 720 721 /* 722 * Read in the mapfile.csv that is used to map between processor families and 723 * parse this. Each line has a comma separated value. 724 */ 725 static void 726 cpcgen_read_intel(const char *datadir, const char *platform) 727 { 728 FILE *map; 729 char *mappath, *last; 730 char *data = NULL; 731 size_t datalen = 0; 732 uint_t lineno; 733 734 if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) { 735 err(EXIT_FAILURE, "failed to construct path to mapfile"); 736 } 737 738 if ((map = fopen(mappath, "r")) == NULL) { 739 err(EXIT_FAILURE, "failed to open data mapfile %s", mappath); 740 } 741 742 lineno = 0; 743 while (getline(&data, &datalen, map) != -1) { 744 char *fstr, *path, *tstr; 745 const char *name; 746 uint_t family, model, nsteps; 747 uint_t steppings[CPROC_MAX_STEPPINGS]; 748 749 cpc_type_t type; 750 cpc_map_t *map; 751 cpc_proc_t *proc; 752 753 /* 754 * The first line contains the header: 755 * Family-model,Version,Filename,EventType 756 */ 757 lineno++; 758 if (lineno == 1) { 759 continue; 760 } 761 762 if ((fstr = strtok_r(data, ",", &last)) == NULL || 763 strtok_r(NULL, ",", &last) == NULL || 764 (path = strtok_r(NULL, ",", &last)) == NULL || 765 (tstr = strtok_r(NULL, "\n", &last)) == NULL) { 766 errx(EXIT_FAILURE, "failed to parse mapfile line " 767 "%u in %s", lineno, mappath); 768 } 769 770 cpcgen_parse_model(fstr, &family, &model, &nsteps, steppings); 771 772 if (strcmp(tstr, "core") == 0) { 773 type = CPC_FILE_CORE; 774 } else if (strcmp(tstr, "offcore") == 0) { 775 type = CPC_FILE_OFF_CORE; 776 } else if (strcmp(tstr, "uncore") == 0) { 777 type = CPC_FILE_UNCORE; 778 } else if (strcmp(tstr, "fp_arith_inst") == 0) { 779 type = CPC_FILE_FP_MATH; 780 } else if (strcmp(tstr, "uncore experimental") == 0) { 781 type = CPC_FILE_UNCORE_EXP; 782 } else { 783 errx(EXIT_FAILURE, "unknown file type \"%s\" on line " 784 "%u", tstr, lineno); 785 } 786 787 if ((name = cpcgen_use_arch(path, type, platform)) == NULL) 788 continue; 789 790 if ((map = cpcgen_map_lookup(path)) == NULL) { 791 nvlist_t *parsed; 792 793 parsed = cpcgen_read_datafile(datadir, path); 794 795 if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) { 796 err(EXIT_FAILURE, "failed to allocate space " 797 "for cpc file"); 798 } 799 800 if ((map->cmap_path = strdup(path)) == NULL) { 801 err(EXIT_FAILURE, "failed to duplicate path " 802 "string"); 803 } 804 805 map->cmap_type = type; 806 map->cmap_data = parsed; 807 map->cmap_name = name; 808 map->cmap_procs = NULL; 809 810 map->cmap_next = cpcgen_maps; 811 cpcgen_maps = map; 812 } 813 814 if ((proc = calloc(1, sizeof (cpc_proc_t))) == NULL) { 815 err(EXIT_FAILURE, "failed to allocate memory for " 816 "family and model tracking"); 817 } 818 819 proc->cproc_family = family; 820 proc->cproc_model = model; 821 proc->cproc_nsteps = nsteps; 822 if (nsteps > 0) { 823 bcopy(steppings, proc->cproc_steppings, 824 sizeof (steppings)); 825 } 826 proc->cproc_next = map->cmap_procs; 827 map->cmap_procs = proc; 828 } 829 830 if (errno != 0 || ferror(map)) { 831 err(EXIT_FAILURE, "failed to read %s", mappath); 832 } 833 834 if (fclose(map) == EOF) { 835 err(EXIT_FAILURE, "failed to close %s", mappath); 836 } 837 free(data); 838 free(mappath); 839 } 840 841 static char * 842 cpcgen_manual_intel_name(cpc_map_t *map) 843 { 844 char *name; 845 846 if (asprintf(&name, "%s_events.3cpc", map->cmap_name) == -1) { 847 warn("failed to assemble manual page name for %s", 848 map->cmap_path); 849 return (NULL); 850 } 851 852 return (name); 853 } 854 855 static boolean_t 856 cpcgen_manual_intel_file_before(FILE *f, cpc_map_t *map) 857 { 858 size_t i; 859 char *upper; 860 cpc_proc_t *proc; 861 862 if ((upper = strdup(map->cmap_name)) == NULL) { 863 warn("failed to duplicate manual name for %s", map->cmap_name); 864 return (B_FALSE); 865 } 866 867 for (i = 0; upper[i] != '\0'; i++) { 868 upper[i] = toupper(upper[i]); 869 } 870 871 if (fprintf(f, cpcgen_manual_intel_intel_header, map->cmap_path, upper, 872 map->cmap_name) == -1) { 873 warn("failed to write out manual header for %s", 874 map->cmap_name); 875 free(upper); 876 return (B_FALSE); 877 } 878 free(upper); 879 880 for (proc = map->cmap_procs; proc != NULL; proc = proc->cproc_next) { 881 if (proc->cproc_nsteps > 0) { 882 uint_t step; 883 884 for (step = 0; step < proc->cproc_nsteps; step++) { 885 if (fprintf(f, ".It\n.Sy Family 0x%x, Model " 886 "0x%x, Stepping 0x%x\n", 887 proc->cproc_family, proc->cproc_model, 888 proc->cproc_steppings[step]) == -1) { 889 warn("failed to write out model " 890 "information for %s", 891 map->cmap_name); 892 return (B_FALSE); 893 } 894 } 895 } else { 896 if (fprintf(f, ".It\n.Sy Family 0x%x, Model 0x%x\n", 897 proc->cproc_family, proc->cproc_model) == -1) { 898 warn("failed to write out model information " 899 "for %s", map->cmap_name); 900 return (B_FALSE); 901 } 902 } 903 } 904 905 if (fprintf(f, cpcgen_manual_intel_data) == -1) { 906 warn("failed to write out manual header for %s", 907 map->cmap_name); 908 return (B_FALSE); 909 } 910 911 return (B_TRUE); 912 } 913 914 static boolean_t 915 cpcgen_manual_intel_file_after(FILE *f, cpc_map_t *map) 916 { 917 if (fprintf(f, cpcgen_manual_intel_trailer) == -1) { 918 warn("failed to write out manual header for %s", 919 map->cmap_name); 920 return (B_FALSE); 921 } 922 923 return (B_TRUE); 924 } 925 926 static boolean_t 927 cpcgen_manual_intel_event(FILE *f, nvlist_t *nvl, const char *path, 928 uint32_t ent) 929 { 930 char *event, *lname, *brief = NULL, *public = NULL, *errata = NULL; 931 size_t i; 932 933 if (nvlist_lookup_string(nvl, "EventName", &event) != 0) { 934 warnx("Found event without 'EventName' property " 935 "in %s, entry %u", path, ent); 936 return (B_FALSE); 937 } 938 939 /* 940 * Intel uses capital names. CPC historically uses lower case names. 941 */ 942 if ((lname = strdup(event)) == NULL) { 943 err(EXIT_FAILURE, "failed to duplicate event name %s", event); 944 } 945 for (i = 0; lname[i] != '\0'; i++) { 946 lname[i] = tolower(event[i]); 947 } 948 949 /* 950 * Try to get the other event fields, but if they're not there, don't 951 * worry about it. 952 */ 953 (void) nvlist_lookup_string(nvl, "BriefDescription", &brief); 954 (void) nvlist_lookup_string(nvl, "PublicDescription", &public); 955 (void) nvlist_lookup_string(nvl, "Errata", &errata); 956 if (errata != NULL && (strcmp(errata, "0") == 0 || 957 strcmp(errata, "null") == 0)) { 958 errata = NULL; 959 } 960 961 if (fprintf(f, ".It Sy %s\n", lname) == -1) { 962 warn("failed to write out event entry %s", event); 963 free(lname); 964 return (B_FALSE); 965 } 966 967 if (public != NULL) { 968 if (fprintf(f, "%s\n", public) == -1) { 969 warn("failed to write out event entry %s", event); 970 free(lname); 971 return (B_FALSE); 972 } 973 } else if (brief != NULL) { 974 if (fprintf(f, "%s\n", brief) == -1) { 975 warn("failed to write out event entry %s", event); 976 free(lname); 977 return (B_FALSE); 978 } 979 } 980 981 if (errata != NULL) { 982 if (fprintf(f, ".Pp\nThe following errata may apply to this: " 983 "%s\n", errata) == -1) { 984 985 warn("failed to write out event entry %s", event); 986 free(lname); 987 return (B_FALSE); 988 } 989 } 990 991 free(lname); 992 return (B_TRUE); 993 } 994 995 static char * 996 cpcgen_cfile_intel_name(cpc_map_t *map) 997 { 998 char *name; 999 1000 if (asprintf(&name, "core_pcbe_%s.c", map->cmap_name) == -1) { 1001 warn("failed to assemble file name for %s", map->cmap_path); 1002 return (NULL); 1003 } 1004 1005 return (name); 1006 } 1007 1008 static boolean_t 1009 cpcgen_cfile_intel_before(FILE *f, cpc_map_t *map) 1010 { 1011 if (fprintf(f, cpcgen_cfile_intel_header, map->cmap_path) == -1) { 1012 warn("failed to write header to temporary file for %s", 1013 map->cmap_path); 1014 return (B_FALSE); 1015 } 1016 1017 if (fprintf(f, cpcgen_cfile_intel_table_start, map->cmap_name) == -1) { 1018 warn("failed to write header to temporary file for %s", 1019 map->cmap_path); 1020 return (B_FALSE); 1021 } 1022 1023 return (B_TRUE); 1024 } 1025 1026 static boolean_t 1027 cpcgen_cfile_intel_after(FILE *f, cpc_map_t *map) 1028 { 1029 if (fprintf(f, cpcgen_cfile_intel_table_end) == -1) { 1030 warn("failed to write footer to temporary file for %s", 1031 map->cmap_path); 1032 return (B_FALSE); 1033 } 1034 1035 return (B_TRUE); 1036 } 1037 1038 static boolean_t 1039 cpcgen_cfile_intel_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent) 1040 { 1041 char *ecode, *umask, *name, *counter, *lname, *cmask; 1042 size_t i; 1043 1044 if (nvlist_lookup_string(nvl, "EventName", &name) != 0) { 1045 warnx("Found event without 'EventName' property " 1046 "in %s, entry %u", path, ent); 1047 return (B_FALSE); 1048 } 1049 1050 if (nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 || 1051 nvlist_lookup_string(nvl, "UMask", &umask) != 0 || 1052 nvlist_lookup_string(nvl, "Counter", &counter) != 0) { 1053 warnx("event %s (index %u) from %s, missing " 1054 "required properties for C file translation", 1055 name, ent, path); 1056 return (B_FALSE); 1057 } 1058 1059 /* 1060 * While we could try and parse the counters manually, just do this the 1061 * max power way for now based on all possible values. 1062 */ 1063 if (strcmp(counter, "0") == 0 || strcmp(counter, "0,") == 0) { 1064 cmask = "C0"; 1065 } else if (strcmp(counter, "1") == 0) { 1066 cmask = "C1"; 1067 } else if (strcmp(counter, "2") == 0) { 1068 cmask = "C2"; 1069 } else if (strcmp(counter, "3") == 0) { 1070 cmask = "C3"; 1071 } else if (strcmp(counter, "0,1") == 0) { 1072 cmask = "C0|C1"; 1073 } else if (strcmp(counter, "0,1,2") == 0) { 1074 cmask = "C0|C1|C2"; 1075 } else if (strcmp(counter, "0,1,2,3") == 0) { 1076 cmask = "C0|C1|C2|C3"; 1077 } else if (strcmp(counter, "0,1,2,3,4,5,6,7") == 0) { 1078 /* 1079 * We don't support the larger number of counters on some 1080 * platforms right now, so just truncate it to the supported 1081 * set. 1082 */ 1083 cmask = "C0|C1|C2|C3"; 1084 } else if (strcmp(counter, "0,2,3") == 0) { 1085 cmask = "C0|C2|C3"; 1086 } else if (strcmp(counter, "1,2,3") == 0) { 1087 cmask = "C1|C2|C3"; 1088 } else if (strcmp(counter, "2,3") == 0) { 1089 cmask = "C2|C3"; 1090 } else { 1091 warnx("event %s (index %u) from %s, has unknown " 1092 "counter value \"%s\"", name, ent, path, counter); 1093 return (B_FALSE); 1094 } 1095 1096 1097 /* 1098 * Intel uses capital names. CPC historically uses lower case names. 1099 */ 1100 if ((lname = strdup(name)) == NULL) { 1101 err(EXIT_FAILURE, "failed to duplicate event name %s", name); 1102 } 1103 for (i = 0; lname[i] != '\0'; i++) { 1104 lname[i] = tolower(name[i]); 1105 } 1106 1107 if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask, cmask, 1108 lname) == -1) { 1109 warn("failed to write out entry %s from %s", name, path); 1110 free(lname); 1111 return (B_FALSE); 1112 } 1113 1114 free(lname); 1115 1116 /* 1117 * Check if we have any PAPI aliases. 1118 */ 1119 for (i = 0; cpcgen_intel_papi_map[i].cpapi_intc != NULL; i++) { 1120 if (strcmp(name, cpcgen_intel_papi_map[i].cpapi_intc) != 0) 1121 continue; 1122 1123 if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask, 1124 cmask, cpcgen_intel_papi_map[i].cpapi_papi) == -1) { 1125 warn("failed to write out entry %s from %s", name, 1126 path); 1127 return (B_FALSE); 1128 } 1129 } 1130 1131 return (B_TRUE); 1132 } 1133 1134 static boolean_t 1135 cpcgen_generate_map(FILE *f, cpc_map_t *map, boolean_t start) 1136 { 1137 cpc_proc_t *p; 1138 1139 if (fprintf(f, "\t%sif (", start ? "" : "} else ") == -1) { 1140 return (B_FALSE); 1141 } 1142 1143 for (p = map->cmap_procs; p != NULL; p = p->cproc_next) { 1144 /* 1145 * Make sure the line is padded so the generated C code looks 1146 * like reasonable C style. 1147 */ 1148 if (p != map->cmap_procs) { 1149 if (fputs("\t ", f) == -1) { 1150 return (B_FALSE); 1151 } 1152 } 1153 1154 if (p->cproc_nsteps > 0) { 1155 uint_t i; 1156 1157 if (fprintf(f, "(model == 0x%x &&\n\t (", 1158 p->cproc_model) == -1) { 1159 return (B_FALSE); 1160 } 1161 1162 for (i = 0; i < p->cproc_nsteps; i++) { 1163 if (fprintf(f, "stepping == 0x%x%s", 1164 p->cproc_steppings[i], 1165 i + 1 != p->cproc_nsteps ? 1166 " ||\n\t " : "") == -1) { 1167 return (B_FALSE); 1168 } 1169 } 1170 1171 if (fputs("))", f) == -1) { 1172 return (B_FALSE); 1173 } 1174 } else if (fprintf(f, "model == 0x%x", p->cproc_model) == -1) { 1175 return (B_FALSE); 1176 } 1177 1178 if (fprintf(f, "%s\n", 1179 p->cproc_next != NULL ? " ||" : ") {") == -1) { 1180 return (B_FALSE); 1181 } 1182 } 1183 1184 if (fprintf(f, "\t\t\treturn (pcbe_core_events_%s);\n", 1185 map->cmap_name) == -1) { 1186 return (B_FALSE); 1187 } 1188 1189 return (B_TRUE); 1190 } 1191 1192 /* 1193 * This is a wrapper around unlinkat that makes sure that we don't clobber 1194 * errno, which is used for properly printing out error messages below. 1195 */ 1196 static void 1197 cpcgen_remove_tmpfile(int dirfd, const char *path) 1198 { 1199 int e = errno; 1200 (void) unlinkat(dirfd, path, 0); 1201 errno = e; 1202 } 1203 1204 /* 1205 * Generate a header file that declares all of these arrays and provide a map 1206 * for models to the corresponding table to use. 1207 */ 1208 static void 1209 cpcgen_common_intel_files(int dirfd) 1210 { 1211 const char *fname = "core_pcbe_cpcgen.h"; 1212 char *tmpname; 1213 int fd; 1214 FILE *f; 1215 cpc_map_t *map; 1216 1217 if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) { 1218 err(EXIT_FAILURE, "failed to construct temporary file name"); 1219 } 1220 1221 if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) { 1222 err(EXIT_FAILURE, "failed to create temporary file %s", 1223 tmpname); 1224 } 1225 1226 if ((f = fdopen(fd, "w")) == NULL) { 1227 cpcgen_remove_tmpfile(dirfd, tmpname); 1228 err(EXIT_FAILURE, "failed to fdopen temporary file"); 1229 } 1230 1231 if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) { 1232 cpcgen_remove_tmpfile(dirfd, tmpname); 1233 errx(EXIT_FAILURE, "failed to write header to temporary file " 1234 "for %s", fname); 1235 } 1236 1237 if (fprintf(f, "#ifndef _CORE_PCBE_CPCGEN_H\n" 1238 "#define\t_CORE_PCBE_CPCGEN_H\n" 1239 "\n" 1240 "#ifdef __cplusplus\n" 1241 "extern \"C\" {\n" 1242 "#endif\n" 1243 "\n" 1244 "extern const struct events_table_t *core_cpcgen_table(uint_t, " 1245 "uint_t);\n" 1246 "\n") == -1) { 1247 cpcgen_remove_tmpfile(dirfd, tmpname); 1248 errx(EXIT_FAILURE, "failed to write header to " 1249 "temporary file for %s", fname); 1250 } 1251 1252 for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { 1253 if (fprintf(f, "extern const struct events_table_t " 1254 "pcbe_core_events_%s[];\n", map->cmap_name) == -1) { 1255 cpcgen_remove_tmpfile(dirfd, tmpname); 1256 errx(EXIT_FAILURE, "failed to write entry to " 1257 "temporary file for %s", fname); 1258 } 1259 } 1260 1261 if (fprintf(f, "\n" 1262 "#ifdef __cplusplus\n" 1263 "}\n" 1264 "#endif\n" 1265 "\n" 1266 "#endif /* _CORE_PCBE_CPCGEN_H */\n") == -1) { 1267 cpcgen_remove_tmpfile(dirfd, tmpname); 1268 errx(EXIT_FAILURE, "failed to write header to " 1269 "temporary file for %s", fname); 1270 } 1271 1272 if (fflush(f) != 0 || fclose(f) != 0) { 1273 cpcgen_remove_tmpfile(dirfd, tmpname); 1274 err(EXIT_FAILURE, "failed to flush and close temporary file"); 1275 } 1276 1277 if (renameat(dirfd, tmpname, dirfd, fname) != 0) { 1278 err(EXIT_FAILURE, "failed to rename temporary file %s", 1279 tmpname); 1280 } 1281 1282 free(tmpname); 1283 1284 /* Now again for the .c file. */ 1285 fname = "core_pcbe_cpcgen.c"; 1286 if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) { 1287 err(EXIT_FAILURE, "failed to construct temporary file name"); 1288 } 1289 1290 if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) { 1291 err(EXIT_FAILURE, "failed to create temporary file %s", 1292 tmpname); 1293 } 1294 1295 if ((f = fdopen(fd, "w")) == NULL) { 1296 cpcgen_remove_tmpfile(dirfd, tmpname); 1297 err(EXIT_FAILURE, "failed to fdopen temporary file"); 1298 } 1299 1300 if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) { 1301 cpcgen_remove_tmpfile(dirfd, tmpname); 1302 errx(EXIT_FAILURE, "failed to write header to temporary file " 1303 "for %s", fname); 1304 } 1305 1306 if (fprintf(f, "#include <core_pcbe_table.h>\n" 1307 "#include <sys/null.h>\n" 1308 "#include \"core_pcbe_cpcgen.h\"\n" 1309 "\n" 1310 "const struct events_table_t *\n" 1311 "core_cpcgen_table(uint_t model, uint_t stepping)\n" 1312 "{\n") == -1) { 1313 cpcgen_remove_tmpfile(dirfd, tmpname); 1314 errx(EXIT_FAILURE, "failed to write header to " 1315 "temporary file for %s", fname); 1316 } 1317 1318 for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { 1319 if (!cpcgen_generate_map(f, map, map == cpcgen_maps)) { 1320 cpcgen_remove_tmpfile(dirfd, tmpname); 1321 errx(EXIT_FAILURE, "failed to write to temporary " 1322 "file for %s", fname); 1323 } 1324 } 1325 1326 if (fprintf(f, "\t} else {\n" 1327 "\t\t\treturn (NULL);\n" 1328 "\t}\n" 1329 "}\n") == -1) { 1330 cpcgen_remove_tmpfile(dirfd, tmpname); 1331 errx(EXIT_FAILURE, "failed to write header to " 1332 "temporary file for %s", fname); 1333 } 1334 1335 if (fflush(f) != 0 || fclose(f) != 0) { 1336 cpcgen_remove_tmpfile(dirfd, tmpname); 1337 err(EXIT_FAILURE, "failed to flush and close temporary file"); 1338 } 1339 1340 if (renameat(dirfd, tmpname, dirfd, fname) != 0) { 1341 err(EXIT_FAILURE, "failed to rename temporary file %s", 1342 tmpname); 1343 } 1344 1345 free(tmpname); 1346 } 1347 1348 /* 1349 * Look at a rule to determine whether or not we should consider including it or 1350 * not. At this point we've already filtered things such that we only get core 1351 * events. 1352 * 1353 * To consider an entry, we currently apply the following criteria: 1354 * 1355 * - The MSRIndex and MSRValue are zero. Programming additional MSRs is no 1356 * supported right now. 1357 * - TakenAlone is non-zero, which means that it cannot run at the same time as 1358 * another field. 1359 * - Offcore is one, indicating that it is off the core and we need to figure 1360 * out if we can support this. 1361 * - If the counter is fixed, don't use it for now. "32"-"35" is another name 1362 * for the fixed counters. 1363 * - If more than one value is specified in the EventCode or UMask values 1364 */ 1365 static boolean_t 1366 cpcgen_skip_intel_entry(nvlist_t *nvl, const char *path, uint_t ent) 1367 { 1368 char *event, *msridx, *msrval, *taken, *offcore, *counter; 1369 char *ecode, *umask; 1370 1371 /* 1372 * Require EventName, it's kind of useless without that. 1373 */ 1374 if (nvlist_lookup_string(nvl, "EventName", &event) != 0) { 1375 errx(EXIT_FAILURE, "Found event without 'EventName' property " 1376 "in %s, entry %u", path, ent); 1377 } 1378 1379 /* 1380 * If we can't find an expected value, whine about it. 1381 */ 1382 if (nvlist_lookup_string(nvl, "MSRIndex", &msridx) != 0 || 1383 nvlist_lookup_string(nvl, "MSRValue", &msrval) != 0 || 1384 nvlist_lookup_string(nvl, "Counter", &counter) != 0 || 1385 nvlist_lookup_string(nvl, "EventCode", &ecode) != 0 || 1386 nvlist_lookup_string(nvl, "UMask", &umask) != 0 || 1387 nvlist_lookup_string(nvl, "Offcore", &offcore) != 0) { 1388 warnx("Skipping event %s (index %u) from %s, missing required " 1389 "property", event, ent, path); 1390 return (B_TRUE); 1391 } 1392 1393 /* 1394 * MSRIndex and MSRvalue comes as either "0" or "0x00". 1395 */ 1396 if ((strcmp(msridx, "0") != 0 && strcmp(msridx, "0x00") != 0) || 1397 (strcmp(msrval, "0") != 0 && strcmp(msridx, "0x00") != 0) || 1398 strcmp(offcore, "0") != 0 || strchr(ecode, ',') != NULL || 1399 strchr(umask, ',') != NULL) { 1400 return (B_TRUE); 1401 } 1402 1403 /* 1404 * Unfortunately, not everything actually has "TakenAlone". If it 1405 * doesn't, we assume that it doesn't have to be. 1406 */ 1407 if (nvlist_lookup_string(nvl, "TakenAlone", &taken) == 0 && 1408 strcmp(taken, "0") != 0) { 1409 return (B_TRUE); 1410 } 1411 1412 1413 if (strncasecmp(counter, "fixed", strlen("fixed")) == 0) 1414 return (B_TRUE); 1415 if (strcmp(counter, "32") == 0 || strcmp(counter, "33") == 0 || 1416 strcmp(counter, "34") == 0 || strcmp(counter, "35") == 0) 1417 return (B_TRUE); 1418 1419 return (B_FALSE); 1420 } 1421 static char * 1422 cpcgen_manual_amd_name(cpc_map_t *map) 1423 { 1424 char *name; 1425 1426 if (asprintf(&name, "amd_%s_events.3cpc", map->cmap_name) == -1) { 1427 warn("failed to assemble file name for %s", map->cmap_path); 1428 return (NULL); 1429 } 1430 1431 return (name); 1432 } 1433 1434 static boolean_t 1435 cpcgen_manual_amd_file_before(FILE *f, cpc_map_t *map) 1436 { 1437 size_t i; 1438 char *upper, *desc, *c; 1439 1440 if ((upper = strdup(map->cmap_name)) == NULL) { 1441 warn("failed to duplicate manual name for %s", map->cmap_name); 1442 return (B_FALSE); 1443 } 1444 1445 if ((desc = strdup(map->cmap_name)) == NULL) { 1446 warn("failed to duplicate manual name for %s", map->cmap_name); 1447 free(upper); 1448 return (B_FALSE); 1449 } 1450 1451 for (i = 0; upper[i] != '\0'; i++) { 1452 upper[i] = toupper(upper[i]); 1453 } 1454 1455 desc++; 1456 c = strchr(desc, '_'); 1457 if (c != NULL) { 1458 *c = ' '; 1459 c++; 1460 *c = toupper(*c); 1461 } 1462 1463 if (fprintf(f, cpcgen_manual_amd_header, map->cmap_path, upper, 1464 map->cmap_name, desc, desc) == -1) { 1465 warn("failed to write out manual header for %s", 1466 map->cmap_name); 1467 free(upper); 1468 free(desc); 1469 return (B_FALSE); 1470 } 1471 1472 free(upper); 1473 free(desc); 1474 return (B_TRUE); 1475 } 1476 1477 static boolean_t 1478 cpcgen_manual_amd_file_after(FILE *f, cpc_map_t *map) 1479 { 1480 if (fprintf(f, cpcgen_manual_amd_trailer) == -1) { 1481 warn("failed to write out manual header for %s", 1482 map->cmap_name); 1483 return (B_FALSE); 1484 } 1485 1486 return (B_TRUE); 1487 } 1488 1489 static boolean_t 1490 cpcgen_manual_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent) 1491 { 1492 char *name, *mnemonic = NULL, *summary = NULL, *desc = NULL; 1493 char *umode; 1494 nvlist_t *units = NULL; 1495 uint32_t i, length; 1496 1497 if (nvlist_lookup_string(nvl, "name", &name) != 0) { 1498 warnx("Found event without 'name' property in %s, entry %u", 1499 path, ent); 1500 return (B_FALSE); 1501 } 1502 1503 if (nvlist_lookup_string(nvl, "mnemonic", &mnemonic) != 0 || 1504 nvlist_lookup_string(nvl, "summary", &summary) != 0) { 1505 warnx("event %s in %s, entry %u, missing required fields", 1506 name, path, ent); 1507 return (B_FALSE); 1508 } 1509 1510 /* 1511 * Allow the other fields to be missing. 1512 */ 1513 (void) nvlist_lookup_string(nvl, "description", &desc); 1514 (void) nvlist_lookup_nvlist(nvl, "units", &units); 1515 1516 if (fprintf(f, ".It Sy %s\n", name) == -1) { 1517 warn("failed to write out event entry %s", name); 1518 } 1519 1520 if (fprintf(f, ".Sy %s -\n" 1521 "%s\n", mnemonic, summary) == -1) { 1522 warn("failed to write out event entry %s", name); 1523 return (B_FALSE); 1524 } 1525 1526 if (desc != NULL) { 1527 if (fprintf(f, ".Pp\n%s\n", desc) == -1) { 1528 warn("failed to write out event entry %s", name); 1529 return (B_FALSE); 1530 } 1531 } 1532 1533 if (units == NULL) 1534 return (B_TRUE); 1535 1536 /* 1537 * Skip units we don't know how to handle. 1538 */ 1539 if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) { 1540 return (B_TRUE); 1541 } 1542 1543 if (fprintf(f, ".Pp\n" 1544 "This event has the following units which may be used\n" 1545 "to modify the behavior of the event:\n" 1546 ".Bl -tag -width Sy\n") == -1) { 1547 warn("failed to write out event entry %s", name); 1548 return (B_FALSE); 1549 } 1550 1551 if (nvlist_lookup_uint32(units, "length", &length) != 0) { 1552 warnx("found units array, but could not look up length " 1553 "property for events %s (index %u) in file %s", 1554 name, ent, path); 1555 return (B_FALSE); 1556 } 1557 1558 for (i = 0; i < length; i++) { 1559 nvlist_t *uvl; 1560 char num[64]; 1561 char *uname, *udesc = NULL; 1562 1563 (void) snprintf(num, sizeof (num), "%u", i); 1564 if (nvlist_lookup_nvlist(units, num, &uvl) != 0) { 1565 warnx("failed to look up unit %u for event %s (index " 1566 "%u) in file %s", i, name, ent, path); 1567 return (B_FALSE); 1568 } 1569 1570 if (nvlist_lookup_string(uvl, "name", &uname) != 0) { 1571 warnx("failed to find required members for unit array " 1572 "entry %u of event %s (index %u) from file %s", 1573 i, name, ent, path); 1574 return (B_FALSE); 1575 } 1576 (void) nvlist_lookup_string(uvl, "description", &udesc); 1577 if (fprintf(f, ".It Sy %s\n", uname) == -1) { 1578 warn("failed to write out event entry %s", name); 1579 return (B_FALSE); 1580 } 1581 1582 if (udesc != NULL) { 1583 if (fprintf(f, "%s\n", udesc) == -1) { 1584 warn("failed to write out event entry %s", 1585 name); 1586 return (B_FALSE); 1587 } 1588 } 1589 } 1590 1591 if (fprintf(f, ".El\n") == -1) { 1592 warn("failed to write out event entry %s", 1593 name); 1594 return (B_FALSE); 1595 } 1596 1597 return (B_TRUE); 1598 } 1599 1600 static char * 1601 cpcgen_cfile_amd_name(cpc_map_t *map) 1602 { 1603 char *name; 1604 1605 if (asprintf(&name, "opteron_pcbe_%s.c", map->cmap_name) == -1) { 1606 warn("failed to assemble file name for %s", map->cmap_path); 1607 return (NULL); 1608 } 1609 1610 return (name); 1611 } 1612 1613 /* 1614 * Generate a header file that can be used to synthesize the data events we care 1615 * about. 1616 */ 1617 static void 1618 cpcgen_common_amd_files(int dirfd) 1619 { 1620 const char *fname = "opteron_pcbe_cpcgen.h"; 1621 char *tmpname; 1622 int fd; 1623 FILE *f; 1624 cpc_map_t *map; 1625 1626 if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) { 1627 err(EXIT_FAILURE, "failed to construct temporary file name"); 1628 } 1629 1630 if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) { 1631 err(EXIT_FAILURE, "failed to create temporary file %s", 1632 tmpname); 1633 } 1634 1635 if ((f = fdopen(fd, "w")) == NULL) { 1636 cpcgen_remove_tmpfile(dirfd, tmpname); 1637 err(EXIT_FAILURE, "failed to fdopen temporary file"); 1638 } 1639 1640 if (fprintf(f, cpcgen_cfile_cddl_header) == -1) { 1641 cpcgen_remove_tmpfile(dirfd, tmpname); 1642 err(EXIT_FAILURE, "failed to write header to " 1643 "temporary file for %s", fname); 1644 } 1645 1646 if (fprintf(f, "#ifndef _OPTERON_PCBE_CPCGEN_H\n" 1647 "#define\t_OPTERON_PCBE_CPCGEN_H\n" 1648 "\n" 1649 "#ifdef __cplusplus\n" 1650 "extern \"C\" {\n" 1651 "#endif\n" 1652 "\n") == -1) { 1653 cpcgen_remove_tmpfile(dirfd, tmpname); 1654 err(EXIT_FAILURE, "failed to write header to " 1655 "temporary file for %s", fname); 1656 } 1657 1658 for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { 1659 if (fprintf(f, "extern const amd_event_t " 1660 "opteron_pcbe_%s_events[];\n", map->cmap_name) == -1) { 1661 cpcgen_remove_tmpfile(dirfd, tmpname); 1662 err(EXIT_FAILURE, "failed to write header to " 1663 "temporary file for %s", fname); 1664 } 1665 } 1666 1667 if (fprintf(f, "\n" 1668 "#ifdef __cplusplus\n" 1669 "}\n" 1670 "#endif\n" 1671 "\n" 1672 "#endif /* _OPTERON_PCBE_CPCGEN_H */\n") == -1) { 1673 cpcgen_remove_tmpfile(dirfd, tmpname); 1674 err(EXIT_FAILURE, "failed to write header to " 1675 "temporary file for %s", fname); 1676 } 1677 1678 1679 1680 if (fflush(f) != 0 || fclose(f) != 0) { 1681 cpcgen_remove_tmpfile(dirfd, tmpname); 1682 err(EXIT_FAILURE, "failed to flush and close temporary file"); 1683 } 1684 1685 if (renameat(dirfd, tmpname, dirfd, fname) != 0) { 1686 err(EXIT_FAILURE, "failed to rename temporary file %s", 1687 tmpname); 1688 } 1689 1690 free(tmpname); 1691 } 1692 1693 static boolean_t 1694 cpcgen_cfile_amd_before(FILE *f, cpc_map_t *map) 1695 { 1696 if (fprintf(f, cpcgen_cfile_amd_header, map->cmap_name) == -1) { 1697 warn("failed to write header to temporary file for %s", 1698 map->cmap_path); 1699 return (B_FALSE); 1700 } 1701 1702 if (fprintf(f, cpcgen_cfile_amd_table_start, map->cmap_name) == -1) { 1703 warn("failed to write header to temporary file for %s", 1704 map->cmap_path); 1705 return (B_FALSE); 1706 } 1707 1708 1709 return (B_TRUE); 1710 } 1711 1712 static boolean_t 1713 cpcgen_cfile_amd_after(FILE *f, cpc_map_t *map) 1714 { 1715 if (fprintf(f, cpcgen_cfile_amd_table_end) == -1) { 1716 warn("failed to write footer to temporary file for %s", 1717 map->cmap_path); 1718 return (B_FALSE); 1719 } 1720 1721 return (B_TRUE); 1722 } 1723 1724 static boolean_t 1725 cpcgen_cfile_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent) 1726 { 1727 char *name, *code, *umode; 1728 uint32_t i, length; 1729 nvlist_t *units; 1730 1731 if (nvlist_lookup_string(nvl, "name", &name) != 0) { 1732 warnx("Found event without 'name' property in %s, entry %u", 1733 path, ent); 1734 return (B_FALSE); 1735 } 1736 1737 if (nvlist_lookup_string(nvl, "code", &code) != 0) { 1738 warnx("event %s (index %u) from %s missing required properties " 1739 "for C translation", name, path, ent); 1740 return (B_FALSE); 1741 } 1742 1743 if (fprintf(f, "\t{ \"%s\", %s, 0 },\n", name, code) == -1) { 1744 warn("failed to write out entry %s from %s", name, path); 1745 return (B_FALSE); 1746 } 1747 1748 /* 1749 * The 'units' array is optional. If the rule has a specific 'unit_mode' 1750 * indicating how the units should be combined, skip that. We don't know 1751 * how to properly process that right now. 1752 */ 1753 if (nvlist_lookup_nvlist(nvl, "units", &units) != 0) { 1754 return (B_TRUE); 1755 } 1756 1757 if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) { 1758 return (B_TRUE); 1759 } 1760 1761 if (nvlist_lookup_uint32(units, "length", &length) != 0) { 1762 warnx("found units array, but could not look up length " 1763 "property for events %s (index %u) in file %s", 1764 name, ent, path); 1765 return (B_FALSE); 1766 } 1767 1768 for (i = 0; i < length; i++) { 1769 nvlist_t *uvl; 1770 char num[64]; 1771 char *uname, *urw; 1772 int32_t bit; 1773 1774 (void) snprintf(num, sizeof (num), "%u", i); 1775 if (nvlist_lookup_nvlist(units, num, &uvl) != 0) { 1776 warnx("failed to look up unit %u for event %s (index " 1777 "%u) in file %s", i, name, ent, path); 1778 return (B_FALSE); 1779 } 1780 1781 if (nvlist_lookup_string(uvl, "name", &uname) != 0 || 1782 nvlist_lookup_string(uvl, "rw", &urw) != 0 || 1783 nvlist_lookup_int32(uvl, "bit", &bit) != 0) { 1784 warnx("failed to find required members for unit array " 1785 "entry %u of event %s (index %u) from file %s", 1786 i, name, ent, path); 1787 dump_nvlist(uvl, 0); 1788 return (B_FALSE); 1789 } 1790 1791 if (bit < 0 || bit > 31) { 1792 warnx("event %s (index %u) from file %s has invalid " 1793 "bit value: %d; skipping", name, ent, path, bit); 1794 continue; 1795 } 1796 1797 if (strcasecmp(urw, "Read-write") != 0) 1798 continue; 1799 1800 if (fprintf(f, "\t{ \"%s.%s\", %s, 0x%x },\n", name, uname, 1801 code, 1U << bit) == -1) { 1802 warn("failed to write out entry %s from %s", name, 1803 path); 1804 return (B_FALSE); 1805 } 1806 } 1807 1808 return (B_TRUE); 1809 } 1810 1811 /* 1812 * For each processor family, generate a data file that contains all of the 1813 * events that we support. Also generate a header that can be included that 1814 * declares all of the tables. 1815 */ 1816 static void 1817 cpcgen_gen(int dirfd) 1818 { 1819 cpc_map_t *map = cpcgen_maps; 1820 1821 if (map == NULL) { 1822 errx(EXIT_FAILURE, "no platforms found or matched"); 1823 } 1824 1825 for (map = cpcgen_maps; map != NULL; map = map->cmap_next) { 1826 int fd, ret; 1827 FILE *f; 1828 char *tmpname, *name; 1829 uint32_t length, i; 1830 1831 if ((name = cpcgen_ops.cgen_op_name(map)) == NULL) { 1832 exit(EXIT_FAILURE); 1833 } 1834 1835 if (asprintf(&tmpname, ".%s.%d", name, getpid()) == -1) { 1836 err(EXIT_FAILURE, "failed to construct temporary file " 1837 "name"); 1838 } 1839 1840 if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0444)) < 0) { 1841 err(EXIT_FAILURE, "failed to create temporary file %s", 1842 tmpname); 1843 } 1844 1845 if ((f = fdopen(fd, "w")) == NULL) { 1846 cpcgen_remove_tmpfile(dirfd, tmpname); 1847 err(EXIT_FAILURE, "failed to fdopen temporary file"); 1848 } 1849 1850 if (!cpcgen_ops.cgen_op_file_before(f, map)) { 1851 cpcgen_remove_tmpfile(dirfd, tmpname); 1852 exit(EXIT_FAILURE); 1853 } 1854 1855 /* 1856 * Iterate over array contents. 1857 */ 1858 if ((ret = nvlist_lookup_uint32(map->cmap_data, "length", 1859 &length)) != 0) { 1860 errx(EXIT_FAILURE, "failed to look up length property " 1861 "in parsed data for %s: %s", map->cmap_path, 1862 strerror(ret)); 1863 } 1864 1865 for (i = 0; i < length; i++) { 1866 nvlist_t *nvl; 1867 char num[64]; 1868 1869 (void) snprintf(num, sizeof (num), "%u", i); 1870 if ((ret = nvlist_lookup_nvlist(map->cmap_data, 1871 num, &nvl)) != 0) { 1872 cpcgen_remove_tmpfile(dirfd, tmpname); 1873 errx(EXIT_FAILURE, "failed to look up array " 1874 "entry %u in parsed data for %s: %s", i, 1875 map->cmap_path, strerror(ret)); 1876 } 1877 1878 if (cpcgen_ops.cgen_op_skip != NULL && 1879 cpcgen_ops.cgen_op_skip(nvl, map->cmap_path, i)) { 1880 continue; 1881 } 1882 1883 if (!cpcgen_ops.cgen_op_event(f, nvl, map->cmap_path, 1884 i)) { 1885 cpcgen_remove_tmpfile(dirfd, tmpname); 1886 exit(EXIT_FAILURE); 1887 } 1888 } 1889 1890 if (!cpcgen_ops.cgen_op_file_after(f, map)) { 1891 cpcgen_remove_tmpfile(dirfd, tmpname); 1892 exit(EXIT_FAILURE); 1893 } 1894 1895 if (fflush(f) != 0 || fclose(f) != 0) { 1896 cpcgen_remove_tmpfile(dirfd, tmpname); 1897 err(EXIT_FAILURE, "failed to flush and close " 1898 "temporary file"); 1899 } 1900 1901 if (renameat(dirfd, tmpname, dirfd, name) != 0) { 1902 err(EXIT_FAILURE, "failed to rename temporary file %s", 1903 tmpname); 1904 } 1905 1906 free(name); 1907 free(tmpname); 1908 } 1909 } 1910 1911 static void 1912 cpcgen_usage(const char *fmt, ...) 1913 { 1914 if (fmt != NULL) { 1915 va_list ap; 1916 1917 (void) fprintf(stderr, "%s: ", cpcgen_progname); 1918 va_start(ap, fmt); 1919 (void) vfprintf(stderr, fmt, ap); 1920 va_end(ap); 1921 } 1922 1923 (void) fprintf(stderr, "Usage: %s -a|-p platform -c|-H|-m -d datadir " 1924 "-o outdir\n" 1925 "\n" 1926 "\t-a generate data for all platforms\n" 1927 "\t-c generate C file for CPC\n" 1928 "\t-d specify the directory containt perfmon data\n" 1929 "\t-H generate header file and common files\n" 1930 "\t-m generate manual pages for CPC data\n" 1931 "\t-o output files in directory outdir\n" 1932 "\t-p generate data for a specified platform\n", 1933 cpcgen_progname); 1934 } 1935 1936 int 1937 main(int argc, char *argv[]) 1938 { 1939 int c, outdirfd; 1940 boolean_t do_mpage = B_FALSE, do_cfile = B_FALSE, do_header = B_FALSE, 1941 do_all = B_FALSE; 1942 const char *datadir = NULL, *outdir = NULL, *platform = NULL; 1943 uint_t count = 0; 1944 1945 cpcgen_progname = basename(argv[0]); 1946 1947 while ((c = getopt(argc, argv, ":acd:hHmo:p:")) != -1) { 1948 switch (c) { 1949 case 'a': 1950 do_all = B_TRUE; 1951 break; 1952 case 'c': 1953 do_cfile = B_TRUE; 1954 break; 1955 case 'd': 1956 datadir = optarg; 1957 break; 1958 case 'm': 1959 do_mpage = B_TRUE; 1960 break; 1961 case 'H': 1962 do_header = B_TRUE; 1963 break; 1964 case 'o': 1965 outdir = optarg; 1966 break; 1967 case 'p': 1968 platform = optarg; 1969 break; 1970 case ':': 1971 cpcgen_usage("Option -%c requires an operand\n", 1972 optopt); 1973 return (2); 1974 case '?': 1975 cpcgen_usage("Unknown option: -%c\n", optopt); 1976 return (2); 1977 case 'h': 1978 default: 1979 cpcgen_usage(NULL); 1980 return (2); 1981 } 1982 } 1983 1984 count = 0; 1985 if (do_mpage) 1986 count++; 1987 if (do_cfile) 1988 count++; 1989 if (do_header) 1990 count++; 1991 if (count > 1) { 1992 cpcgen_usage("Only one of -c, -h, and -m may be specified\n"); 1993 return (2); 1994 } else if (count == 0) { 1995 cpcgen_usage("One of -c, -h, and -m is required\n"); 1996 return (2); 1997 } 1998 1999 count = 0; 2000 if (do_all) 2001 count++; 2002 if (platform != NULL) 2003 count++; 2004 if (count > 1) { 2005 cpcgen_usage("Only one of -a and -p may be specified\n"); 2006 return (2); 2007 } else if (count == 0) { 2008 cpcgen_usage("One of -a and -p is required\n"); 2009 return (2); 2010 } 2011 2012 if (outdir == NULL) { 2013 cpcgen_usage("Missing required output directory (-o)\n"); 2014 return (2); 2015 } 2016 2017 if ((outdirfd = open(outdir, O_RDONLY)) < 0) { 2018 err(EXIT_FAILURE, "failed to open output directory %s", outdir); 2019 } 2020 2021 if (datadir == NULL) { 2022 cpcgen_usage("Missing required data directory (-d)\n"); 2023 return (2); 2024 } 2025 2026 cpcgen_determine_vendor(datadir); 2027 2028 switch (cpcgen_mode) { 2029 case CPCGEN_MODE_INTEL: 2030 cpcgen_ops.cgen_op_gather = cpcgen_read_intel; 2031 cpcgen_ops.cgen_op_common = cpcgen_common_intel_files; 2032 cpcgen_ops.cgen_op_skip = cpcgen_skip_intel_entry; 2033 if (do_mpage) { 2034 cpcgen_ops.cgen_op_name = cpcgen_manual_intel_name; 2035 cpcgen_ops.cgen_op_file_before = 2036 cpcgen_manual_intel_file_before; 2037 cpcgen_ops.cgen_op_file_after = 2038 cpcgen_manual_intel_file_after; 2039 cpcgen_ops.cgen_op_event = cpcgen_manual_intel_event; 2040 } else { 2041 cpcgen_ops.cgen_op_name = cpcgen_cfile_intel_name; 2042 cpcgen_ops.cgen_op_file_before = 2043 cpcgen_cfile_intel_before; 2044 cpcgen_ops.cgen_op_file_after = 2045 cpcgen_cfile_intel_after; 2046 cpcgen_ops.cgen_op_event = cpcgen_cfile_intel_event; 2047 } 2048 break; 2049 case CPCGEN_MODE_AMD: 2050 cpcgen_ops.cgen_op_gather = cpcgen_read_amd; 2051 cpcgen_ops.cgen_op_common = cpcgen_common_amd_files; 2052 cpcgen_ops.cgen_op_skip = NULL; 2053 if (do_mpage) { 2054 cpcgen_ops.cgen_op_name = cpcgen_manual_amd_name; 2055 cpcgen_ops.cgen_op_file_before = 2056 cpcgen_manual_amd_file_before; 2057 cpcgen_ops.cgen_op_file_after = 2058 cpcgen_manual_amd_file_after; 2059 cpcgen_ops.cgen_op_event = cpcgen_manual_amd_event; 2060 } else { 2061 cpcgen_ops.cgen_op_name = cpcgen_cfile_amd_name; 2062 cpcgen_ops.cgen_op_file_before = 2063 cpcgen_cfile_amd_before; 2064 cpcgen_ops.cgen_op_file_after = cpcgen_cfile_amd_after; 2065 cpcgen_ops.cgen_op_event = cpcgen_cfile_amd_event; 2066 2067 } 2068 break; 2069 default: 2070 errx(EXIT_FAILURE, "failed to determine if operating on AMD or " 2071 "Intel"); 2072 break; 2073 } 2074 2075 cpcgen_ops.cgen_op_gather(datadir, platform); 2076 2077 if (do_header) { 2078 cpcgen_ops.cgen_op_common(outdirfd); 2079 return (0); 2080 } 2081 2082 cpcgen_gen(outdirfd); 2083 2084 return (0); 2085 } 2086