| /* | 
 |  * pfmlib_amd64.c : support for the AMD64 architected PMU | 
 |  * 		    (for both 64 and 32 bit modes) | 
 |  * | 
 |  * Copyright (c) 2009 Google, Inc | 
 |  * Contributed by Stephane Eranian <eranian@gmail.com> | 
 |  * | 
 |  * Based on: | 
 |  * Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P. | 
 |  * Contributed by Stephane Eranian <eranian@hpl.hp.com> | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
 |  * of this software and associated documentation files (the "Software"), to deal | 
 |  * in the Software without restriction, including without limitation the rights | 
 |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | 
 |  * of the Software, and to permit persons to whom the Software is furnished to do so, | 
 |  * subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in all | 
 |  * copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | 
 |  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | 
 |  * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | 
 |  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | 
 |  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | 
 |  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
 |  */ | 
 | #include <sys/types.h> | 
 | #include <string.h> | 
 | #include <stdlib.h> | 
 |  | 
 | /* private headers */ | 
 | #include "pfmlib_priv.h"		/* library private */ | 
 | #include "pfmlib_amd64_priv.h"		/* architecture private */ | 
 |  | 
 | const pfmlib_attr_desc_t amd64_mods[]={ | 
 | 	PFM_ATTR_B("k", "monitor at priv level 0"),		/* monitor priv level 0 */ | 
 | 	PFM_ATTR_B("u", "monitor at priv level 1, 2, 3"),	/* monitor priv level 1, 2, 3 */ | 
 | 	PFM_ATTR_B("e", "edge level"),				/* edge */ | 
 | 	PFM_ATTR_B("i", "invert"),				/* invert */ | 
 | 	PFM_ATTR_I("c", "counter-mask in range [0-255]"),	/* counter-mask */ | 
 | 	PFM_ATTR_B("h", "monitor in hypervisor"),		/* monitor in hypervisor*/ | 
 | 	PFM_ATTR_B("g", "measure in guest"),			/* monitor in guest */ | 
 | 	PFM_ATTR_NULL /* end-marker to avoid exporting number of entries */ | 
 | }; | 
 |  | 
 | pfmlib_pmu_t amd64_support; | 
 | pfm_amd64_config_t pfm_amd64_cfg; | 
 |  | 
 | static int | 
 | amd64_num_mods(void *this, int idx) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	unsigned int mask; | 
 |  | 
 | 	mask = pe[idx].modmsk; | 
 | 	return pfmlib_popcnt(mask); | 
 | } | 
 |  | 
 | static inline int | 
 | amd64_eflag(void *this, int idx, int flag) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	return !!(pe[idx].flags & flag); | 
 | } | 
 |  | 
 | static inline int | 
 | amd64_uflag(void *this, int idx, int attr, int flag) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	return !!(pe[idx].umasks[attr].uflags & flag); | 
 | } | 
 |  | 
 | static inline int | 
 | amd64_event_ibsfetch(void *this, int idx) | 
 | { | 
 | 	return amd64_eflag(this, idx, AMD64_FL_IBSFE); | 
 | } | 
 |  | 
 | static inline int | 
 | amd64_event_ibsop(void *this, int idx) | 
 | { | 
 | 	return amd64_eflag(this, idx, AMD64_FL_IBSOP); | 
 | } | 
 |  | 
 | static inline int | 
 | amd64_from_rev(unsigned int flags) | 
 | { | 
 |         return ((flags) >> 8) & 0xff; | 
 | } | 
 |  | 
 | static inline int | 
 | amd64_till_rev(unsigned int flags) | 
 | { | 
 |         int till = (((flags)>>16) & 0xff); | 
 |         if (!till) | 
 |                 return 0xff; | 
 |         return till; | 
 | } | 
 |  | 
 | static void | 
 | amd64_get_revision(pfm_amd64_config_t *cfg) | 
 | { | 
 | 	pfm_pmu_t rev = PFM_PMU_NONE; | 
 |  | 
 |         if (cfg->family == 6) { | 
 |                 cfg->revision = PFM_PMU_AMD64_K7; | 
 | 		return; | 
 | 	} | 
 |  | 
 |         if (cfg->family == 15) { | 
 |                 switch (cfg->model >> 4) { | 
 |                 case 0: | 
 |                         if (cfg->model == 5 && cfg->stepping < 2) { | 
 |                                 rev = PFM_PMU_AMD64_K8_REVB; | 
 | 				break; | 
 |                         } | 
 |                         if (cfg->model == 4 && cfg->stepping == 0) { | 
 |                                 rev = PFM_PMU_AMD64_K8_REVB; | 
 | 				break; | 
 |                         } | 
 |                         rev = PFM_PMU_AMD64_K8_REVC; | 
 | 			break; | 
 |                 case 1: | 
 |                         rev = PFM_PMU_AMD64_K8_REVD; | 
 | 			break; | 
 |                 case 2: | 
 |                 case 3: | 
 |                         rev = PFM_PMU_AMD64_K8_REVE; | 
 | 			break; | 
 |                 case 4: | 
 |                 case 5: | 
 |                 case 0xc: | 
 |                         rev = PFM_PMU_AMD64_K8_REVF; | 
 | 			break; | 
 |                 case 6: | 
 |                 case 7: | 
 |                 case 8: | 
 |                         rev = PFM_PMU_AMD64_K8_REVG; | 
 | 			break; | 
 |                 default: | 
 |                         rev = PFM_PMU_AMD64_K8_REVB; | 
 |                 } | 
 |         } else if (cfg->family == 16) { /* family 10h */ | 
 |                 switch (cfg->model) { | 
 |                 case 4: | 
 |                 case 5: | 
 |                 case 6: | 
 |                         rev = PFM_PMU_AMD64_FAM10H_SHANGHAI; | 
 | 			break; | 
 |                 case 8: | 
 |                 case 9: | 
 |                         rev = PFM_PMU_AMD64_FAM10H_ISTANBUL; | 
 | 			break; | 
 |                 default: | 
 |                         rev = PFM_PMU_AMD64_FAM10H_BARCELONA; | 
 |                 } | 
 |         } else if (cfg->family == 17) { /* family 11h */ | 
 |                 switch (cfg->model) { | 
 | 		default: | 
 | 		        rev = PFM_PMU_AMD64_FAM11H_TURION; | 
 | 		} | 
 |         } else if (cfg->family == 18) { /* family 12h */ | 
 |                 switch (cfg->model) { | 
 | 		default: | 
 | 		        rev = PFM_PMU_AMD64_FAM12H_LLANO; | 
 | 		} | 
 |         } else if (cfg->family == 20) { /* family 14h */ | 
 |                 switch (cfg->model) { | 
 |                 default: | 
 |                         rev = PFM_PMU_AMD64_FAM14H_BOBCAT; | 
 |                 } | 
 | 	} else if (cfg->family == 21) { /* family 15h */ | 
 | 		rev = PFM_PMU_AMD64_FAM15H_INTERLAGOS; | 
 | 	} | 
 |         cfg->revision = rev; | 
 | } | 
 |  | 
 | /* | 
 |  * .byte 0x53 == push ebx. it's universal for 32 and 64 bit | 
 |  * .byte 0x5b == pop ebx. | 
 |  * Some gcc's (4.1.2 on Core2) object to pairing push/pop and ebx in 64 bit mode. | 
 |  * Using the opcode directly avoids this problem. | 
 |  */ | 
 | static inline void | 
 | cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d) | 
 | { | 
 |   __asm__ __volatile__ (".byte 0x53\n\tcpuid\n\tmovl %%ebx, %%esi\n\t.byte 0x5b" | 
 |        : "=a" (*a), | 
 | 	     "=S" (*b), | 
 | 		 "=c" (*c), | 
 | 		 "=d" (*d) | 
 |        : "a" (op)); | 
 | } | 
 |  | 
 | static int | 
 | amd64_event_valid(void *this, int i) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	pfmlib_pmu_t *pmu = this; | 
 | 	int flags; | 
 |  | 
 | 	flags = pe[i].flags; | 
 |  | 
 |         if (pmu->pmu_rev  < amd64_from_rev(flags)) | 
 |                 return 0; | 
 |  | 
 |         if (pmu->pmu_rev > amd64_till_rev(flags)) | 
 |                 return 0; | 
 |  | 
 |         /* no restrictions or matches restrictions */ | 
 |         return 1; | 
 | } | 
 |  | 
 | static int | 
 | amd64_umask_valid(void *this, int i, int attr) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	int flags; | 
 |  | 
 | 	flags = pe[i].umasks[attr].uflags; | 
 |  | 
 |         if (pmu->pmu_rev < amd64_from_rev(flags)) | 
 |                 return 0; | 
 |  | 
 |         if (pmu->pmu_rev > amd64_till_rev(flags)) | 
 |                 return 0; | 
 |  | 
 |         /* no restrictions or matches restrictions */ | 
 |         return 1; | 
 | } | 
 |  | 
 | static unsigned int | 
 | amd64_num_umasks(void *this, int pidx) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	unsigned int i, n = 0; | 
 |  | 
 | 	/* unit masks + modifiers */ | 
 | 	for (i = 0; i < pe[pidx].numasks; i++) | 
 | 		if (amd64_umask_valid(this, pidx, i)) | 
 | 			n++; | 
 | 	return n; | 
 | } | 
 |  | 
 | static int | 
 | amd64_get_umask(void *this, int pidx, int attr_idx) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	unsigned int i; | 
 | 	int n; | 
 |  | 
 | 	for (i=0, n = 0; i < pe[pidx].numasks; i++) { | 
 | 		if (!amd64_umask_valid(this, pidx, i)) | 
 | 			continue; | 
 | 		if (n++ == attr_idx) | 
 | 			return i; | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | static inline int | 
 | amd64_attr2mod(void *this, int pidx, int attr_idx) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	size_t x; | 
 | 	int n; | 
 |  | 
 | 	n = attr_idx - amd64_num_umasks(this, pidx); | 
 |  | 
 | 	pfmlib_for_each_bit(x, pe[pidx].modmsk) { | 
 | 		if (n == 0) | 
 | 			break; | 
 | 		n--; | 
 | 	} | 
 | 	return x; | 
 | } | 
 |  | 
 | void amd64_display_reg(void *this, pfmlib_event_desc_t *e, pfm_amd64_reg_t reg) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 |  | 
 | 	if (IS_FAMILY_10H(pmu) || IS_FAMILY_15H(pmu)) | 
 | 		__pfm_vbprintf("[0x%"PRIx64" event_sel=0x%x umask=0x%x os=%d usr=%d en=%d int=%d inv=%d edge=%d cnt_mask=%d guest=%d host=%d] %s\n", | 
 | 			reg.val, | 
 | 			reg.sel_event_mask | (reg.sel_event_mask2 << 8), | 
 | 			reg.sel_unit_mask, | 
 | 			reg.sel_os, | 
 | 			reg.sel_usr, | 
 | 			reg.sel_en, | 
 | 			reg.sel_int, | 
 | 			reg.sel_inv, | 
 | 			reg.sel_edge, | 
 | 			reg.sel_cnt_mask, | 
 | 			reg.sel_guest, | 
 | 			reg.sel_host, | 
 | 			e->fstr); | 
 | 	else | 
 | 		__pfm_vbprintf("[0x%"PRIx64" event_sel=0x%x umask=0x%x os=%d usr=%d en=%d int=%d inv=%d edge=%d cnt_mask=%d] %s\n", | 
 | 			reg.val, | 
 | 			reg.sel_event_mask, | 
 | 			reg.sel_unit_mask, | 
 | 			reg.sel_os, | 
 | 			reg.sel_usr, | 
 | 			reg.sel_en, | 
 | 			reg.sel_int, | 
 | 			reg.sel_inv, | 
 | 			reg.sel_edge, | 
 | 			reg.sel_cnt_mask, | 
 | 			e->fstr); | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_detect(void *this) | 
 | { | 
 | 	unsigned int a, b, c, d; | 
 | 	char buffer[128]; | 
 |  | 
 | 	if (pfm_amd64_cfg.family) | 
 | 		return PFM_SUCCESS; | 
 |  | 
 | 	cpuid(0, &a, &b, &c, &d); | 
 | 	strncpy(&buffer[0], (char *)(&b), 4); | 
 | 	strncpy(&buffer[4], (char *)(&d), 4); | 
 | 	strncpy(&buffer[8], (char *)(&c), 4); | 
 | 	buffer[12] = '\0'; | 
 |  | 
 | 	if (strcmp(buffer, "AuthenticAMD")) | 
 | 		return PFM_ERR_NOTSUPP; | 
 |  | 
 | 	cpuid(1, &a, &b, &c, &d); | 
 | 	pfm_amd64_cfg.family = (a >> 8) & 0x0000000f;  // bits 11 - 8 | 
 | 	pfm_amd64_cfg.model  = (a >> 4) & 0x0000000f;  // Bits  7 - 4 | 
 | 	if (pfm_amd64_cfg.family == 0xf) { | 
 | 		pfm_amd64_cfg.family += (a >> 20) & 0x000000ff; // Extended family | 
 | 		pfm_amd64_cfg.model  |= (a >> 12) & 0x000000f0; // Extended model | 
 | 	} | 
 | 	pfm_amd64_cfg.stepping= a & 0x0000000f;  // bits  3 - 0 | 
 |  | 
 | 	amd64_get_revision(&pfm_amd64_cfg); | 
 |  | 
 | 	if (pfm_amd64_cfg.revision == PFM_PMU_NONE) | 
 | 		return PFM_ERR_NOTSUPP; | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_family_detect(void *this) | 
 | { | 
 | 	struct pfmlib_pmu *pmu = this; | 
 | 	int ret; | 
 |  | 
 | 	ret = pfm_amd64_detect(this); | 
 | 	if (ret != PFM_SUCCESS) | 
 | 		return ret; | 
 |  | 
 | 	ret = pfm_amd64_cfg.revision; | 
 | 	return ret == pmu->cpu_family ? PFM_SUCCESS : PFM_ERR_NOTSUPP; | 
 | } | 
 |  | 
 | static int | 
 | amd64_add_defaults(void *this, pfmlib_event_desc_t *e, unsigned int msk, uint64_t *umask) | 
 | { | 
 | 	const amd64_entry_t *ent, *pe = this_pe(this); | 
 | 	unsigned int i; | 
 | 	int j, k, added, omit, numasks_grp; | 
 | 	int idx; | 
 |  | 
 | 	k = e->nattrs; | 
 | 	ent = pe+e->event; | 
 |  | 
 | 	for(i=0; msk; msk >>=1, i++) { | 
 |  | 
 | 		if (!(msk & 0x1)) | 
 | 			continue; | 
 |  | 
 | 		added = omit = numasks_grp = 0; | 
 |  | 
 | 		for (j = 0; j < e->npattrs; j++) { | 
 | 			if (e->pattrs[j].ctrl != PFM_ATTR_CTRL_PMU) | 
 | 				continue; | 
 |  | 
 | 			if (e->pattrs[j].type != PFM_ATTR_UMASK) | 
 | 				continue; | 
 |  | 
 | 			idx = e->pattrs[j].idx; | 
 |  | 
 | 			if (ent->umasks[idx].grpid != i) | 
 | 				continue; | 
 |  | 
 | 			/* number of umasks in this group */ | 
 | 			numasks_grp++; | 
 |  | 
 | 			if (amd64_uflag(this, e->event, idx, AMD64_FL_DFL)) { | 
 | 				DPRINT("added default for %s j=%d idx=%d\n", ent->umasks[idx].uname, j, idx); | 
 |  | 
 | 				*umask |= ent->umasks[idx].ucode; | 
 |  | 
 | 				e->attrs[k].id = j; /* pattrs index */ | 
 | 				e->attrs[k].ival = 0; | 
 | 				k++; | 
 |  | 
 | 				added++; | 
 | 			} | 
 | 			if (amd64_uflag(this, e->event, idx, AMD64_FL_OMIT)) | 
 | 				omit++; | 
 | 		} | 
 | 		/* | 
 | 		 * fail if no default was found AND at least one umasks cannot be omitted | 
 | 		 * in the group | 
 | 		 */ | 
 | 		if (!added && omit != numasks_grp) { | 
 | 			DPRINT("no default found for event %s unit mask group %d\n", ent->name, i); | 
 | 			return PFM_ERR_UMASK; | 
 | 		} | 
 | 	} | 
 | 	e->nattrs = k; | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_get_encoding(void *this, pfmlib_event_desc_t *e) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	pfm_amd64_reg_t reg; | 
 | 	pfm_event_attr_info_t *a; | 
 | 	uint64_t umask = 0; | 
 | 	unsigned int plmmsk = 0; | 
 | 	int k, ret, grpid; | 
 | 	unsigned int grpmsk, ugrpmsk = 0; | 
 | 	int grpcounts[AMD64_MAX_GRP]; | 
 | 	int ncombo[AMD64_MAX_GRP]; | 
 |  | 
 | 	memset(grpcounts, 0, sizeof(grpcounts)); | 
 | 	memset(ncombo, 0, sizeof(ncombo)); | 
 |  | 
 | 	e->fstr[0] = '\0'; | 
 |  | 
 | 	reg.val = 0; /* assume reserved bits are zerooed */ | 
 |  | 
 | 	grpmsk = (1 << pe[e->event].ngrp)-1; | 
 |  | 
 | 	if (amd64_event_ibsfetch(this, e->event)) | 
 | 		reg.ibsfetch.en = 1; | 
 | 	else if (amd64_event_ibsop(this, e->event)) | 
 | 		reg.ibsop.en = 1; | 
 | 	else { | 
 | 		reg.sel_event_mask  = pe[e->event].code; | 
 | 		reg.sel_event_mask2 = pe[e->event].code >> 8; | 
 | 		reg.sel_en = 1; /* force enable */ | 
 | 		reg.sel_int = 1; /* force APIC  */ | 
 | 	} | 
 |  | 
 | 	for(k=0; k < e->nattrs; k++) { | 
 | 		a = attr(e, k); | 
 |  | 
 | 		if (a->ctrl != PFM_ATTR_CTRL_PMU) | 
 | 			continue; | 
 |  | 
 | 		if (a->type == PFM_ATTR_UMASK) { | 
 | 			grpid = pe[e->event].umasks[a->idx].grpid; | 
 | 			++grpcounts[grpid]; | 
 |  | 
 | 			/* | 
 | 		 	 * upper layer has removed duplicates | 
 | 		 	 * so if we come here more than once, it is for two | 
 | 		 	 * diinct umasks | 
 | 		 	 */ | 
 | 			if (amd64_uflag(this, e->event, a->idx, AMD64_FL_NCOMBO)) | 
 | 				ncombo[grpid] = 1; | 
 | 			/* | 
 | 			 * if more than one umask in this group but one is marked | 
 | 			 * with ncombo, then fail. It is okay to combine umask within | 
 | 			 * a group as long as none is tagged with NCOMBO | 
 | 			 */ | 
 | 			if (grpcounts[grpid] > 1 && ncombo[grpid])  { | 
 | 				DPRINT("event does not support unit mask combination within a group\n"); | 
 | 				return PFM_ERR_FEATCOMB; | 
 | 			} | 
 |  | 
 | 			umask |= pe[e->event].umasks[a->idx].ucode; | 
 | 			ugrpmsk  |= 1 << pe[e->event].umasks[a->idx].grpid; | 
 |  | 
 | 		} else if (a->type == PFM_ATTR_RAW_UMASK) { | 
 |  | 
 | 			/* there can only be one RAW_UMASK per event */ | 
 |  | 
 | 			/* sanity checks */ | 
 | 			if (a->idx & ~0xff) { | 
 | 				DPRINT("raw umask is invalid\n"); | 
 | 				return PFM_ERR_ATTR; | 
 | 			} | 
 | 			/* override umask */ | 
 | 			umask = a->idx & 0xff; | 
 | 			ugrpmsk = grpmsk; | 
 |  | 
 | 		} else { /* modifiers */ | 
 | 			uint64_t ival = e->attrs[k].ival; | 
 |  | 
 | 			switch(a->idx) { //amd64_attr2mod(this, e->osid, e->event, a->idx)) { | 
 | 				case AMD64_ATTR_I: /* invert */ | 
 | 					reg.sel_inv = !!ival; | 
 | 					break; | 
 | 				case AMD64_ATTR_E: /* edge */ | 
 | 					reg.sel_edge = !!ival; | 
 | 					break; | 
 | 				case AMD64_ATTR_C: /* counter-mask */ | 
 | 					if (ival > 255) | 
 | 						return PFM_ERR_ATTR_VAL; | 
 | 					reg.sel_cnt_mask = ival; | 
 | 					break; | 
 | 				case AMD64_ATTR_U: /* USR */ | 
 | 					reg.sel_usr = !!ival; | 
 | 					plmmsk |= _AMD64_ATTR_U; | 
 | 					break; | 
 | 				case AMD64_ATTR_K: /* OS */ | 
 | 					reg.sel_os = !!ival; | 
 | 					plmmsk |= _AMD64_ATTR_K; | 
 | 					break; | 
 | 				case AMD64_ATTR_G: /* GUEST */ | 
 | 					reg.sel_guest = !!ival; | 
 | 					plmmsk |= _AMD64_ATTR_G; | 
 | 					break; | 
 | 				case AMD64_ATTR_H: /* HOST */ | 
 | 					reg.sel_host = !!ival; | 
 | 					plmmsk |= _AMD64_ATTR_H; | 
 | 					break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * handle case where no priv level mask was passed. | 
 | 	 * then we use the dfl_plm | 
 | 	 */ | 
 | 	if (!(plmmsk & (_AMD64_ATTR_K|_AMD64_ATTR_U|_AMD64_ATTR_H))) { | 
 | 		if (e->dfl_plm & PFM_PLM0) | 
 | 			reg.sel_os = 1; | 
 | 		if (e->dfl_plm & PFM_PLM3) | 
 | 			reg.sel_usr = 1; | 
 | 		if ((IS_FAMILY_10H(this) || IS_FAMILY_15H(this)) | 
 | 		     && e->dfl_plm & PFM_PLMH) | 
 | 			reg.sel_host = 1; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * check that there is at least of unit mask in each unit | 
 | 	 * mask group | 
 | 	 */ | 
 | 	if (ugrpmsk != grpmsk) { | 
 | 		ugrpmsk ^= grpmsk; | 
 | 		ret = amd64_add_defaults(this, e, ugrpmsk, &umask); | 
 | 		if (ret != PFM_SUCCESS) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	reg.sel_unit_mask = umask; | 
 |  | 
 | 	e->codes[0] = reg.val; | 
 | 	e->count = 1; | 
 |  | 
 | 	/* | 
 | 	 * reorder all the attributes such that the fstr appears always | 
 | 	 * the same regardless of how the attributes were submitted. | 
 | 	 */ | 
 | 	evt_strcat(e->fstr, "%s", pe[e->event].name); | 
 | 	pfmlib_sort_attr(e); | 
 | 	for (k = 0; k < e->nattrs; k++) { | 
 | 		a = attr(e, k); | 
 | 		if (a->ctrl != PFM_ATTR_CTRL_PMU) | 
 | 			continue; | 
 | 		if (a->type == PFM_ATTR_UMASK) | 
 | 			evt_strcat(e->fstr, ":%s", pe[e->event].umasks[a->idx].uname); | 
 | 		else if (a->type == PFM_ATTR_RAW_UMASK) | 
 | 			evt_strcat(e->fstr, ":0x%x", a->idx); | 
 | 	} | 
 |  | 
 | 	for (k = 0; k < e->npattrs; k++) { | 
 | 		int idx; | 
 |  | 
 | 		if (e->pattrs[k].ctrl != PFM_ATTR_CTRL_PMU) | 
 | 			continue; | 
 |  | 
 | 		if (e->pattrs[k].type == PFM_ATTR_UMASK) | 
 | 			continue; | 
 |  | 
 | 		idx = e->pattrs[k].idx; | 
 | 		switch(idx) { | 
 | 		case AMD64_ATTR_K: | 
 | 			evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_os); | 
 | 			break; | 
 | 		case AMD64_ATTR_U: | 
 | 			evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_usr); | 
 | 			break; | 
 | 		case AMD64_ATTR_E: | 
 | 			evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_edge); | 
 | 			break; | 
 | 		case AMD64_ATTR_I: | 
 | 			evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_inv); | 
 | 			break; | 
 | 		case AMD64_ATTR_C: | 
 | 			evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_cnt_mask); | 
 | 			break; | 
 | 		case AMD64_ATTR_H: | 
 | 			evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_host); | 
 | 			break; | 
 | 		case AMD64_ATTR_G: | 
 | 			evt_strcat(e->fstr, ":%s=%lu", amd64_mods[idx].name, reg.sel_guest); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	amd64_display_reg(this, e, reg); | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_get_event_first(void *this) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 | 	int idx; | 
 |  | 
 | 	for(idx=0; idx < pmu->pme_count; idx++) | 
 | 		if (amd64_event_valid(this, idx)) | 
 | 			return idx; | 
 | 	return -1; | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_get_event_next(void *this, int idx) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 |  | 
 | 	/* basic validity checks on idx down by caller */ | 
 | 	if (idx >= (pmu->pme_count-1)) | 
 | 		return -1; | 
 |  | 
 | 	/* validate event fo this host PMU */ | 
 | 	if (!amd64_event_valid(this, idx)) | 
 | 		return -1; | 
 |  | 
 | 	for(++idx; idx < pmu->pme_count; idx++) { | 
 | 		if (amd64_event_valid(this, idx)) | 
 | 			return idx; | 
 | 	} | 
 | 	return -1; | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_event_is_valid(void *this, int pidx) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 |  | 
 | 	if (pidx < 0 || pidx >= pmu->pme_count) | 
 | 		return 0; | 
 |  | 
 | 	/* valid revision */ | 
 | 	return amd64_event_valid(this, pidx); | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_get_event_attr_info(void *this, int pidx, int attr_idx, pfm_event_attr_info_t *info) | 
 | { | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	int numasks, idx; | 
 |  | 
 | 	numasks = amd64_num_umasks(this, pidx); | 
 |  | 
 | 	if (attr_idx < numasks) { | 
 | 		idx = amd64_get_umask(this, pidx, attr_idx); | 
 | 		if (idx == -1) | 
 | 			return PFM_ERR_ATTR; | 
 |  | 
 | 		info->name = pe[pidx].umasks[idx].uname; | 
 | 		info->desc = pe[pidx].umasks[idx].udesc; | 
 | 		info->code = pe[pidx].umasks[idx].ucode; | 
 | 		info->type = PFM_ATTR_UMASK; | 
 | 		info->is_dfl = amd64_uflag(this, pidx, idx, AMD64_FL_DFL); | 
 | 	} else { | 
 | 		idx = amd64_attr2mod(this, pidx, attr_idx); | 
 | 		info->name = amd64_mods[idx].name; | 
 | 		info->desc = amd64_mods[idx].desc; | 
 | 		info->type = amd64_mods[idx].type; | 
 | 		info->code = idx; | 
 | 		info->is_dfl = 0; | 
 | 	} | 
 | 	info->is_precise = 0; | 
 | 	info->equiv  = NULL; | 
 | 	info->ctrl   = PFM_ATTR_CTRL_PMU; | 
 | 	info->idx    = idx; /* namespace specific index */ | 
 | 	info->dfl_val64 = 0; | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_get_event_info(void *this, int idx, pfm_event_info_t *info) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 |  | 
 | 	info->name  = pe[idx].name; | 
 | 	info->desc  = pe[idx].desc; | 
 | 	info->equiv = NULL; | 
 | 	info->code  = pe[idx].code; | 
 | 	info->idx   = idx; | 
 | 	info->pmu   = pmu->pmu; | 
 |  | 
 | 	info->is_precise = 0; | 
 | 	info->nattrs  = amd64_num_umasks(this, idx); | 
 | 	info->nattrs += amd64_num_mods(this, idx); | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | int | 
 | pfm_amd64_validate_table(void *this, FILE *fp) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 | 	const amd64_entry_t *pe = this_pe(this); | 
 | 	const char *name =  pmu->name; | 
 | 	unsigned int j, k; | 
 | 	int i, ndfl; | 
 | 	int error = 0; | 
 |  | 
 | 	if (!pmu->atdesc) { | 
 | 		fprintf(fp, "pmu: %s missing attr_desc\n", pmu->name); | 
 | 		error++; | 
 | 	} | 
 |  | 
 | 	if (!pmu->supported_plm && pmu->type == PFM_PMU_TYPE_CORE) { | 
 | 		fprintf(fp, "pmu: %s supported_plm not set\n", pmu->name); | 
 | 		error++; | 
 | 	} | 
 |  | 
 | 	for(i=0; i < pmu->pme_count; i++) { | 
 |  | 
 | 		if (!pe[i].name) { | 
 | 			fprintf(fp, "pmu: %s event%d: :: no name (prev event was %s)\n", pmu->name, i, | 
 | 			i > 1 ? pe[i-1].name : "??"); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		if (!pe[i].desc) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: no description\n", name, i, pe[i].name); | 
 | 			error++; | 
 |  | 
 | 		} | 
 | 		if (pe[i].numasks && pe[i].umasks == NULL) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: numasks but no umasks\n", pmu->name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		if (pe[i].numasks == 0 && pe[i].umasks) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: numasks=0 but umasks defined\n", pmu->name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		if (pe[i].numasks && pe[i].ngrp == 0) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: ngrp cannot be zero\n", name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		if (pe[i].numasks == 0 && pe[i].ngrp) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: ngrp must be zero\n", name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		if (pe[i].ngrp >= AMD64_MAX_GRP) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: ngrp too big (max=%d)\n", name, i, pe[i].name, AMD64_MAX_GRP); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		for(ndfl = 0, j= 0; j < pe[i].numasks; j++) { | 
 |  | 
 | 			if (!pe[i].umasks[j].uname) { | 
 | 				fprintf(fp, "pmu: %s event%d: %s umask%d :: no name\n", pmu->name, i, pe[i].name, j); | 
 | 				error++; | 
 | 			} | 
 |  | 
 | 			if (!pe[i].umasks[j].udesc) { | 
 | 				fprintf(fp, "pmu: %s event%d:%s umask%d: %s :: no description\n", name, i, pe[i].name, j, pe[i].umasks[j].uname); | 
 | 				error++; | 
 | 			} | 
 |  | 
 | 			if (pe[i].ngrp && pe[i].umasks[j].grpid >= pe[i].ngrp) { | 
 | 				fprintf(fp, "pmu: %s event%d: %s umask%d: %s :: invalid grpid %d (must be < %d)\n", name, i, pe[i].name, j, pe[i].umasks[j].uname, pe[i].umasks[j].grpid, pe[i].ngrp); | 
 | 				error++; | 
 | 			} | 
 |  | 
 | 			if (pe[i].umasks[j].uflags & AMD64_FL_DFL) { | 
 | 				for(k=0; k < j; k++) | 
 | 					if ((pe[i].umasks[k].uflags == pe[i].umasks[j].uflags) | 
 | 					    && (pe[i].umasks[k].grpid == pe[i].umasks[j].grpid)) | 
 | 						ndfl++; | 
 | 				if (pe[i].numasks == 1) | 
 | 					ndfl = 1; | 
 | 			} | 
 | 		} | 
 |  | 
 | 		if (pe[i].numasks > 1 && ndfl) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: more than one default unit mask with same code\n", name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		/* if only one umask, then ought to be default */ | 
 | 		if (pe[i].numasks == 1 && ndfl != 1) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s, only one umask but no default\n", pmu->name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		if (pe[i].flags & AMD64_FL_NCOMBO) { | 
 | 			fprintf(fp, "pmu: %s event%d: %s :: NCOMBO is unit mask only flag\n", name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 |  | 
 | 		for(j=0; j < pe[i].numasks; j++) { | 
 |  | 
 | 			if (pe[i].umasks[j].uflags & AMD64_FL_NCOMBO) | 
 | 				continue; | 
 |  | 
 | 			for(k=j+1; k < pe[i].numasks; k++) { | 
 | 				if (pe[i].umasks[k].uflags & AMD64_FL_NCOMBO) | 
 | 					continue; | 
 | 				if ((pe[i].umasks[j].ucode &  pe[i].umasks[k].ucode)) { | 
 | 					fprintf(fp, "pmu: %s event%d: %s :: umask %s and %s have overlapping code bits\n", name, i, pe[i].name, pe[i].umasks[j].uname, pe[i].umasks[k].uname); | 
 | 					error++; | 
 | 				} | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return error ? PFM_ERR_INVAL : PFM_SUCCESS; | 
 | } | 
 |  | 
 | unsigned int | 
 | pfm_amd64_get_event_nattrs(void *this, int pidx) | 
 | { | 
 | 	unsigned int nattrs; | 
 | 	nattrs  = amd64_num_umasks(this, pidx); | 
 | 	nattrs += amd64_num_mods(this, pidx); | 
 | 	return nattrs; | 
 | } | 
 |  | 
 | int pfm_amd64_get_num_events(void *this) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 | 	int i, num = 0; | 
 |  | 
 | 	/* | 
 | 	 * count actual number of events for specific PMU. | 
 | 	 * Table may contain more events for the family than | 
 | 	 * what a specific model actually supports. | 
 | 	 */ | 
 | 	for (i = 0; i < pmu->pme_count; i++) | 
 | 		if (amd64_event_valid(this, i)) | 
 | 			num++; | 
 | 	return num; | 
 | } |