1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2018, Matthew Macy
5 * Copyright (c) 2021, The FreeBSD Foundation
6 *
7 * Portions of this software were developed by Mitchell Horne
8 * under sponsorship from the FreeBSD Foundation.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 */
32
33#include <sys/types.h>
34#include <sys/errno.h>
35#include <sys/pmc.h>
36#include <sys/sysctl.h>
37#include <stddef.h>
38#include <stdlib.h>
39#include <limits.h>
40#include <regex.h>
41#include <string.h>
42#include <pmc.h>
43#include <pmclog.h>
44#include <assert.h>
45#include <libpmcstat.h>
46#include "pmu-events/pmu-events.h"
47
48struct pmu_alias {
49	const char *pa_alias;
50	const char *pa_name;
51};
52
53#if defined(__amd64__) || defined(__i386__)
54typedef enum {
55	PMU_INVALID,
56	PMU_INTEL,
57	PMU_AMD,
58} pmu_mfr_t;
59
60static struct pmu_alias pmu_intel_alias_table[] = {
61	{"UNHALTED_CORE_CYCLES", "cpu_clk_unhalted.thread"},
62	{"UNHALTED-CORE-CYCLES", "cpu_clk_unhalted.thread"},
63	{"LLC_MISSES", "LONGEST_LAT_CACHE.MISS"},
64	{"LLC-MISSES", "LONGEST_LAT_CACHE.MISS"},
65	{"LLC_REFERENCE", "LONGEST_LAT_CACHE.REFERENCE"},
66	{"LLC-REFERENCE", "LONGEST_LAT_CACHE.REFERENCE"},
67	{"LLC_MISS_RHITM", "mem_load_l3_miss_retired.remote_hitm"},
68	{"LLC-MISS-RHITM", "mem_load_l3_miss_retired.remote_hitm"},
69	{"RESOURCE_STALL", "RESOURCE_STALLS.ANY"},
70	{"RESOURCE_STALLS_ANY", "RESOURCE_STALLS.ANY"},
71	{"BRANCH_INSTRUCTION_RETIRED", "BR_INST_RETIRED.ALL_BRANCHES"},
72	{"BRANCH-INSTRUCTION-RETIRED", "BR_INST_RETIRED.ALL_BRANCHES"},
73	{"BRANCH_MISSES_RETIRED", "BR_MISP_RETIRED.ALL_BRANCHES"},
74	{"BRANCH-MISSES-RETIRED", "BR_MISP_RETIRED.ALL_BRANCHES"},
75	{"unhalted-cycles", "cpu_clk_unhalted.thread"},
76	{"instructions", "inst_retired.any"},
77	{"branch-mispredicts", "br_misp_retired.all_branches"},
78	{"branches", "br_inst_retired.all_branches"},
79	{"interrupts", "hw_interrupts.received"},
80	{"ic-misses", "frontend_retired.l1i_miss"},
81	{NULL, NULL},
82};
83
84static struct pmu_alias pmu_amd_alias_table[] = {
85	{"UNHALTED_CORE_CYCLES", "ls_not_halted_cyc"},
86	{"UNHALTED-CORE-CYCLES", "ls_not_halted_cyc"},
87	{"LLC_MISSES", "l3_comb_clstr_state.request_miss"},
88	{"LLC-MISSES", "l3_comb_clstr_state.request_miss"},
89	{"LLC_REFERENCE", "l3_request_g1.caching_l3_cache_accesses"},
90	{"LLC-REFERENCE", "l3_request_g1.caching_l3_cache_accesses"},
91	{"BRANCH_INSTRUCTION_RETIRED", "ex_ret_brn"},
92	{"BRANCH-INSTRUCTION-RETIRED", "ex_ret_brn"},
93	{"BRANCH_MISSES_RETIRED", "ex_ret_brn_misp"},
94	{"BRANCH-MISSES-RETIRED", "ex_ret_brn_misp"},
95	{"unhalted-cycles", "ls_not_halted_cyc"},
96	{"instructions", "ex_ret_instr",},
97	{"branch-mispredicts", "ex_ret_brn_misp"},
98	{"branches", "ex_ret_brn"},
99	{"interrupts", "ls_int_taken"}, /* Not on amdzen1 */
100	{NULL, NULL},
101};
102
103
104static pmu_mfr_t
105pmu_events_mfr(void)
106{
107	char buf[PMC_CPUID_LEN];
108	size_t s = sizeof(buf);
109	pmu_mfr_t mfr;
110
111	if (sysctlbyname("kern.hwpmc.cpuid", buf, &s,
112	    (void *)NULL, 0) == -1)
113		return (PMU_INVALID);
114	if (strcasestr(buf, "AuthenticAMD") != NULL ||
115	    strcasestr(buf, "HygonGenuine") != NULL)
116		mfr = PMU_AMD;
117	else if (strcasestr(buf, "GenuineIntel") != NULL)
118		mfr = PMU_INTEL;
119	else
120		mfr = PMU_INVALID;
121	return (mfr);
122}
123
124/*
125 *  The Intel fixed mode counters are:
126 *	"inst_retired.any",
127 *	"cpu_clk_unhalted.thread",
128 *	"cpu_clk_unhalted.thread_any",
129 *	"cpu_clk_unhalted.ref_tsc",
130 *
131 */
132
133static const char *
134pmu_alias_get(const char *name)
135{
136	pmu_mfr_t mfr;
137	struct pmu_alias *pa;
138	struct pmu_alias *pmu_alias_table;
139
140	if ((mfr = pmu_events_mfr()) == PMU_INVALID)
141		return (name);
142	if (mfr == PMU_AMD)
143		pmu_alias_table = pmu_amd_alias_table;
144	else if (mfr == PMU_INTEL)
145		pmu_alias_table = pmu_intel_alias_table;
146	else
147		return (name);
148
149	for (pa = pmu_alias_table; pa->pa_alias != NULL; pa++)
150		if (strcasecmp(name, pa->pa_alias) == 0)
151			return (pa->pa_name);
152
153	return (name);
154}
155#elif defined(__powerpc64__)
156
157static const char *
158pmu_alias_get(const char *name)
159{
160	return (name);
161}
162
163#elif defined(__aarch64__)
164
165static struct pmu_alias pmu_armv8_alias_table[] = {
166	{"UNHALTED_CORE_CYCLES", "CPU_CYCLES"},
167	{"UNHALTED-CORE-CYCLES", "CPU_CYCLES"},
168	{"LLC_MISSES", "LL_CACHE_MISS_RD"},
169	{"LLC-MISSES", "LL_CACHE_MISS_RD"},
170	{"LLC_REFERENCE", "LL_CACHE_RD"},
171	{"LLC-REFERENCE", "LL_CACHE_RD"},
172	{"BRANCH_INSTRUCTION_RETIRED", "BR_RETIRED"},
173	{"BRANCH-INSTRUCTION-RETIRED", "BR_RETIRED"},
174	{"BRANCH_MISSES_RETIRED", "BR_MIS_PRED_RETIRED"},
175	{"BRANCH-MISSES-RETIRED", "BR_MIS_PRED_RETIRED"},
176	{"unhalted-cycles", "CPU_CYCLES"},
177	{"instructions", "INST_RETIRED",},
178	{"branch-mispredicts", "BR_MIS_PRED_RETIRED"},
179	{"branches", "BR_RETIRED"},
180	{"interrupts", "EXC_IRQ"},
181	{NULL, NULL},
182};
183
184static const char *
185pmu_alias_get(const char *name)
186{
187	struct pmu_alias *pa;
188
189	for (pa = pmu_armv8_alias_table; pa->pa_alias != NULL; pa++)
190		if (strcasecmp(name, pa->pa_alias) == 0)
191			return (pa->pa_name);
192
193	return (name);
194}
195
196#else
197
198static const char *
199pmu_alias_get(const char *name)
200{
201
202	return (name);
203}
204#endif
205
206struct pmu_event_desc {
207	uint64_t ped_period;
208	uint64_t ped_offcore_rsp;
209	uint64_t ped_l3_thread;
210	uint64_t ped_l3_slice;
211	uint32_t ped_event;
212	uint32_t ped_frontend;
213	uint32_t ped_ldlat;
214	uint32_t ped_config1;
215	int16_t	ped_umask;
216	uint8_t	ped_cmask;
217	uint8_t	ped_any;
218	uint8_t	ped_inv;
219	uint8_t	ped_edge;
220	uint8_t	ped_fc_mask;
221	uint8_t	ped_ch_mask;
222};
223
224static const struct pmu_events_map *
225pmu_events_map_get(const char *cpuid)
226{
227	regex_t re;
228	regmatch_t pmatch[1];
229	char buf[PMC_CPUID_LEN];
230	size_t s = sizeof(buf);
231	int match;
232	const struct pmu_events_map *pme;
233
234	if (cpuid != NULL) {
235		strlcpy(buf, cpuid, s);
236	} else {
237		if (sysctlbyname("kern.hwpmc.cpuid", buf, &s,
238		    (void *)NULL, 0) == -1)
239			return (NULL);
240	}
241	for (pme = pmu_events_map; pme->cpuid != NULL; pme++) {
242		if (regcomp(&re, pme->cpuid, REG_EXTENDED) != 0) {
243			printf("regex '%s' failed to compile, ignoring\n",
244			    pme->cpuid);
245			continue;
246		}
247		match = regexec(&re, buf, 1, pmatch, 0);
248		regfree(&re);
249		if (match == 0) {
250			if (pmatch[0].rm_so == 0 && (buf[pmatch[0].rm_eo] == 0
251			    || buf[pmatch[0].rm_eo] == '-'))
252				return (pme);
253		}
254	}
255	return (NULL);
256}
257
258static const struct pmu_event *
259pmu_event_get(const char *cpuid, const char *event_name, int *idx)
260{
261	const struct pmu_events_map *pme;
262	const struct pmu_event *pe;
263	int i;
264
265	if ((pme = pmu_events_map_get(cpuid)) == NULL)
266		return (NULL);
267	for (i = 0, pe = pme->table; pe->name || pe->desc || pe->event; pe++, i++) {
268		if (pe->name == NULL)
269			continue;
270		if (strcasecmp(pe->name, event_name) == 0) {
271			if (idx)
272				*idx = i;
273			return (pe);
274		}
275	}
276	return (NULL);
277}
278
279int
280pmc_pmu_idx_get_by_event(const char *cpuid, const char *event)
281{
282	int idx;
283	const char *realname;
284
285	realname = pmu_alias_get(event);
286	if (pmu_event_get(cpuid, realname, &idx) == NULL)
287		return (-1);
288	return (idx);
289}
290
291const char *
292pmc_pmu_event_get_by_idx(const char *cpuid, int idx)
293{
294	const struct pmu_events_map *pme;
295
296	if ((pme = pmu_events_map_get(cpuid)) == NULL)
297		return (NULL);
298	assert(pme->table[idx].name);
299	return (pme->table[idx].name);
300}
301
302static int
303pmu_parse_event(struct pmu_event_desc *ped, const char *eventin)
304{
305	char *event;
306	char *kvp, *key, *value, *r;
307	char *debug;
308
309	if ((event = strdup(eventin)) == NULL)
310		return (ENOMEM);
311	r = event;
312	bzero(ped, sizeof(*ped));
313	ped->ped_period = DEFAULT_SAMPLE_COUNT;
314	ped->ped_umask = -1;
315	while ((kvp = strsep(&event, ",")) != NULL) {
316		key = strsep(&kvp, "=");
317		if (key == NULL)
318			abort();
319		value = kvp;
320		if (strcmp(key, "umask") == 0)
321			ped->ped_umask = strtol(value, NULL, 16);
322		else if (strcmp(key, "event") == 0)
323			ped->ped_event = strtol(value, NULL, 16);
324		else if (strcmp(key, "period") == 0)
325			ped->ped_period = strtol(value, NULL, 10);
326		else if (strcmp(key, "offcore_rsp") == 0)
327			ped->ped_offcore_rsp = strtol(value, NULL, 16);
328		else if (strcmp(key, "any") == 0)
329			ped->ped_any = strtol(value, NULL, 10);
330		else if (strcmp(key, "cmask") == 0)
331			ped->ped_cmask = strtol(value, NULL, 10);
332		else if (strcmp(key, "inv") == 0)
333			ped->ped_inv = strtol(value, NULL, 10);
334		else if (strcmp(key, "edge") == 0)
335			ped->ped_edge = strtol(value, NULL, 10);
336		else if (strcmp(key, "frontend") == 0)
337			ped->ped_frontend = strtol(value, NULL, 16);
338		else if (strcmp(key, "ldlat") == 0)
339			ped->ped_ldlat = strtol(value, NULL, 16);
340		else if (strcmp(key, "fc_mask") == 0)
341			ped->ped_fc_mask = strtol(value, NULL, 16);
342		else if (strcmp(key, "ch_mask") == 0)
343			ped->ped_ch_mask = strtol(value, NULL, 16);
344		else if (strcmp(key, "config1") == 0)
345			ped->ped_config1 = strtol(value, NULL, 16);
346		else if (strcmp(key, "l3_thread_mask") == 0)
347			ped->ped_l3_thread = strtol(value, NULL, 16);
348		else if (strcmp(key, "l3_slice_mask") == 0)
349			ped->ped_l3_slice = strtol(value, NULL, 16);
350		else {
351			debug = getenv("PMUDEBUG");
352			if (debug != NULL && strcmp(debug, "true") == 0 && value != NULL)
353				printf("unrecognized kvpair: %s:%s\n", key, value);
354		}
355	}
356	free(r);
357	return (0);
358}
359
360uint64_t
361pmc_pmu_sample_rate_get(const char *event_name)
362{
363	const struct pmu_event *pe;
364	struct pmu_event_desc ped;
365
366	event_name = pmu_alias_get(event_name);
367	if ((pe = pmu_event_get(NULL, event_name, NULL)) == NULL)
368		return (DEFAULT_SAMPLE_COUNT);
369	if (pe->event == NULL)
370		return (DEFAULT_SAMPLE_COUNT);
371	if (pmu_parse_event(&ped, pe->event))
372		return (DEFAULT_SAMPLE_COUNT);
373	return (ped.ped_period);
374}
375
376int
377pmc_pmu_enabled(void)
378{
379
380	return (pmu_events_map_get(NULL) != NULL);
381}
382
383void
384pmc_pmu_print_counters(const char *event_name)
385{
386	const struct pmu_events_map *pme;
387	const struct pmu_event *pe;
388	struct pmu_event_desc ped;
389	char *debug;
390	int do_debug;
391
392	debug = getenv("PMUDEBUG");
393	do_debug = 0;
394
395	if (debug != NULL && strcmp(debug, "true") == 0)
396		do_debug = 1;
397	if ((pme = pmu_events_map_get(NULL)) == NULL)
398		return;
399	for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
400		if (pe->name == NULL)
401			continue;
402		if (event_name != NULL && strcasestr(pe->name, event_name) == NULL)
403			continue;
404		printf("\t%s\n", pe->name);
405		if (do_debug)
406			pmu_parse_event(&ped, pe->event);
407	}
408}
409
410void
411pmc_pmu_print_counter_desc(const char *ev)
412{
413	const struct pmu_events_map *pme;
414	const struct pmu_event *pe;
415
416	if ((pme = pmu_events_map_get(NULL)) == NULL)
417		return;
418	for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
419		if (pe->name == NULL)
420			continue;
421		if (strcasestr(pe->name, ev) != NULL &&
422		    pe->desc != NULL)
423			printf("%s:\t%s\n", pe->name, pe->desc);
424	}
425}
426
427void
428pmc_pmu_print_counter_desc_long(const char *ev)
429{
430	const struct pmu_events_map *pme;
431	const struct pmu_event *pe;
432
433	if ((pme = pmu_events_map_get(NULL)) == NULL)
434		return;
435	for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
436		if (pe->name == NULL)
437			continue;
438		if (strcasestr(pe->name, ev) != NULL) {
439			if (pe->long_desc != NULL)
440				printf("%s:\n%s\n", pe->name, pe->long_desc);
441			else if (pe->desc != NULL)
442				printf("%s:\t%s\n", pe->name, pe->desc);
443		}
444	}
445}
446
447void
448pmc_pmu_print_counter_full(const char *ev)
449{
450	const struct pmu_events_map *pme;
451	const struct pmu_event *pe;
452
453	if ((pme = pmu_events_map_get(NULL)) == NULL)
454		return;
455	for (pe = pme->table; pe->name || pe->desc || pe->event; pe++) {
456		if (pe->name == NULL)
457			continue;
458		if (strcasestr(pe->name, ev) == NULL)
459			continue;
460		printf("name: %s\n", pe->name);
461		if (pe->long_desc != NULL)
462			printf("desc: %s\n", pe->long_desc);
463		else if (pe->desc != NULL)
464			printf("desc: %s\n", pe->desc);
465		if (pe->event != NULL)
466			printf("event: %s\n", pe->event);
467		if (pe->topic != NULL)
468			printf("topic: %s\n", pe->topic);
469		if (pe->pmu != NULL)
470			printf("pmu: %s\n", pe->pmu);
471		if (pe->unit != NULL)
472			printf("unit: %s\n", pe->unit);
473		if (pe->perpkg != NULL)
474			printf("perpkg: %s\n", pe->perpkg);
475		if (pe->metric_expr != NULL)
476			printf("metric_expr: %s\n", pe->metric_expr);
477		if (pe->metric_name != NULL)
478			printf("metric_name: %s\n", pe->metric_name);
479		if (pe->metric_group != NULL)
480			printf("metric_group: %s\n", pe->metric_group);
481	}
482}
483
484#if defined(__amd64__) || defined(__i386__)
485static int
486pmc_pmu_amd_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm,
487	struct pmu_event_desc *ped)
488{
489	struct pmc_md_amd_op_pmcallocate *amd;
490	const struct pmu_event *pe;
491	int idx = -1;
492
493	amd = &pm->pm_md.pm_amd;
494	if (ped->ped_umask > 0) {
495		pm->pm_caps |= PMC_CAP_QUALIFIER;
496		amd->pm_amd_config |= AMD_PMC_TO_UNITMASK(ped->ped_umask);
497	}
498	pm->pm_class = PMC_CLASS_K8;
499	pe = pmu_event_get(NULL, event_name, &idx);
500
501	if (strcmp("l3cache", pe->topic) == 0){
502		amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK(ped->ped_event);
503		amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_L3_CACHE;
504		amd->pm_amd_config |= AMD_PMC_TO_L3SLICE(ped->ped_l3_slice);
505		amd->pm_amd_config |= AMD_PMC_TO_L3CORE(ped->ped_l3_thread);
506	}
507	else if (strcmp("data fabric", pe->topic) == 0){
508
509		amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK_DF(ped->ped_event);
510		amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_DATA_FABRIC;
511	}
512	else{
513		amd->pm_amd_config |= AMD_PMC_TO_EVENTMASK(ped->ped_event);
514		amd->pm_amd_sub_class = PMC_AMD_SUB_CLASS_CORE;
515		if ((pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0 ||
516			(pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) ==
517			(PMC_CAP_USER|PMC_CAP_SYSTEM))
518			amd->pm_amd_config |= (AMD_PMC_USR | AMD_PMC_OS);
519		else if (pm->pm_caps & PMC_CAP_USER)
520			amd->pm_amd_config |= AMD_PMC_USR;
521		else if (pm->pm_caps & PMC_CAP_SYSTEM)
522			amd->pm_amd_config |= AMD_PMC_OS;
523		if (ped->ped_edge)
524			amd->pm_amd_config |= AMD_PMC_EDGE;
525		if (ped->ped_inv)
526			amd->pm_amd_config |= AMD_PMC_INVERT;
527		if (pm->pm_caps & PMC_CAP_INTERRUPT)
528			amd->pm_amd_config |= AMD_PMC_INT;
529	}
530	return (0);
531}
532
533static int
534pmc_pmu_intel_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm,
535	struct pmu_event_desc *ped)
536{
537	struct pmc_md_iap_op_pmcallocate *iap;
538
539	iap = &pm->pm_md.pm_iap;
540	if (strcasestr(event_name, "UNC_") == event_name ||
541	    strcasestr(event_name, "uncore") != NULL) {
542		pm->pm_class = PMC_CLASS_UCP;
543		pm->pm_caps |= PMC_CAP_QUALIFIER;
544	} else if (ped->ped_event == 0x0) {
545		pm->pm_class = PMC_CLASS_IAF;
546	} else {
547		pm->pm_class = PMC_CLASS_IAP;
548		pm->pm_caps |= PMC_CAP_QUALIFIER;
549	}
550	iap->pm_iap_config |= IAP_EVSEL(ped->ped_event);
551	if (ped->ped_umask > 0)
552		iap->pm_iap_config |= IAP_UMASK(ped->ped_umask);
553	iap->pm_iap_config |= IAP_CMASK(ped->ped_cmask);
554	iap->pm_iap_rsp = ped->ped_offcore_rsp;
555
556	if ((pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0 ||
557		(pm->pm_caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) ==
558		(PMC_CAP_USER|PMC_CAP_SYSTEM))
559		iap->pm_iap_config |= (IAP_USR | IAP_OS);
560	else if (pm->pm_caps & PMC_CAP_USER)
561		iap->pm_iap_config |= IAP_USR;
562	else if (pm->pm_caps & PMC_CAP_SYSTEM)
563		iap->pm_iap_config |= IAP_OS;
564	if (ped->ped_edge)
565		iap->pm_iap_config |= IAP_EDGE;
566	if (ped->ped_any)
567		iap->pm_iap_config |= IAP_ANY;
568	if (ped->ped_inv)
569		iap->pm_iap_config |= IAP_INV;
570	if (pm->pm_caps & PMC_CAP_INTERRUPT)
571		iap->pm_iap_config |= IAP_INT;
572	return (0);
573}
574
575static int
576pmc_pmu_pmcallocate_md(const char *event_name, struct pmc_op_pmcallocate *pm)
577{
578	const struct pmu_event *pe;
579	struct pmu_event_desc ped;
580	pmu_mfr_t mfr;
581	int idx = -1;
582
583	if ((mfr = pmu_events_mfr()) == PMU_INVALID)
584		return (ENOENT);
585
586	bzero(&pm->pm_md, sizeof(pm->pm_md));
587	pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE);
588	event_name = pmu_alias_get(event_name);
589	if ((pe = pmu_event_get(NULL, event_name, &idx)) == NULL)
590		return (ENOENT);
591	assert(idx >= 0);
592	pm->pm_ev = idx;
593
594	if (pe->event == NULL)
595		return (ENOENT);
596	if (pmu_parse_event(&ped, pe->event))
597		return (ENOENT);
598
599	if (mfr == PMU_INTEL)
600		return (pmc_pmu_intel_pmcallocate(event_name, pm, &ped));
601	else
602		return (pmc_pmu_amd_pmcallocate(event_name, pm, &ped));
603}
604
605#elif defined(__powerpc64__)
606
607static int
608pmc_pmu_pmcallocate_md(const char *event_name, struct pmc_op_pmcallocate *pm)
609{
610	const struct pmu_event *pe;
611	struct pmu_event_desc ped;
612	int idx = -1;
613
614	bzero(&pm->pm_md, sizeof(pm->pm_md));
615	pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE);
616	event_name = pmu_alias_get(event_name);
617
618	if ((pe = pmu_event_get(NULL, event_name, &idx)) == NULL)
619		return (ENOENT);
620	if (pe->event == NULL)
621		return (ENOENT);
622	if (pmu_parse_event(&ped, pe->event))
623		return (ENOENT);
624
625	pm->pm_ev = idx;
626	pm->pm_md.pm_event = ped.ped_event;
627	pm->pm_class = PMC_CLASS_POWER8;
628	return (0);
629}
630
631#elif defined(__aarch64__)
632
633static int
634pmc_pmu_pmcallocate_md(const char *event_name, struct pmc_op_pmcallocate *pm)
635{
636	const struct pmu_event *pe;
637	struct pmu_event_desc ped;
638	int idx = -1;
639
640	event_name = pmu_alias_get(event_name);
641	if ((pe = pmu_event_get(NULL, event_name, &idx)) == NULL)
642		return (ENOENT);
643	if (pe->event == NULL)
644		return (ENOENT);
645	if (pmu_parse_event(&ped, pe->event))
646		return (ENOENT);
647
648	assert(idx >= 0);
649	pm->pm_ev = idx;
650	pm->pm_md.pm_md_config = ped.ped_event;
651	pm->pm_class = PMC_CLASS_ARMV8;
652	pm->pm_caps |= (PMC_CAP_READ | PMC_CAP_WRITE);
653
654	return (0);
655}
656
657#else
658
659static int
660pmc_pmu_pmcallocate_md(const char *e __unused, struct pmc_op_pmcallocate *p __unused)
661{
662	return (EOPNOTSUPP);
663}
664#endif
665
666int
667pmc_pmu_pmcallocate(const char *event_name, struct pmc_op_pmcallocate *pm)
668{
669	int error;
670
671	error = pmc_pmu_pmcallocate_md(event_name, pm);
672	if (error != 0) {
673		/* Reset any changes. */
674		pm->pm_ev = 0;
675		pm->pm_caps = 0;
676		pm->pm_class = 0;
677
678		return (error);
679	}
680
681	pm->pm_flags |= PMC_F_EV_PMU;
682	return (0);
683}
684