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