| /* | 
 |  * Copyright (c) 2005-2006 Hewlett-Packard Development Company, L.P. | 
 |  * Copyright (c) 2006 IBM Corp. | 
 |  * Contributed by Kevin Corry <kevcorry@us.ibm.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. | 
 |  * | 
 |  * pfmlib_intel_netburst.c | 
 |  * | 
 |  * Support for the Pentium4/Xeon/EM64T processor family (family=15). | 
 |  */ | 
 | /* private headers */ | 
 | #include "pfmlib_priv.h" | 
 | #include "pfmlib_intel_netburst_priv.h" | 
 | #include "pfmlib_intel_x86_priv.h" | 
 | #include "events/intel_netburst_events.h" | 
 |  | 
 | const pfmlib_attr_desc_t netburst_mods[]={ | 
 | 	PFM_ATTR_B("u", "monitor at priv level 1, 2, 3"),	/* monitor priv level 1, 2, 3 */ | 
 | 	PFM_ATTR_B("k", "monitor at priv level 0"),		/* monitor priv level 0 */ | 
 | 	PFM_ATTR_B("cmpl", "complement"),			/* set: <=, clear: > */ | 
 | 	PFM_ATTR_B("e", "edge"),				/* edge */ | 
 | 	PFM_ATTR_I("thr", "event threshold in range [0-15]"),	/* threshold */ | 
 | }; | 
 | #define NETBURST_MODS_COUNT (sizeof(netburst_mods)/sizeof(pfmlib_attr_desc_t)) | 
 |  | 
 |  | 
 | extern pfmlib_pmu_t netburst_support; | 
 |  | 
 | static inline int | 
 | netburst_get_numasks(int pidx) | 
 | { | 
 | 	int i = 0; | 
 | 	/* | 
 | 	 * name = NULL is end-marker | 
 | 	 */ | 
 | 	while (netburst_events[pidx].event_masks[i].name) | 
 | 		i++; | 
 | 	return i; | 
 | } | 
 |  | 
 | static void | 
 | netburst_display_reg(pfmlib_event_desc_t *e) | 
 | { | 
 | 	netburst_escr_value_t escr; | 
 | 	netburst_cccr_value_t cccr; | 
 |  | 
 | 	escr.val = e->codes[0]; | 
 | 	cccr.val = e->codes[1]; | 
 |  | 
 | 	__pfm_vbprintf("[0x%"PRIx64" 0x%"PRIx64" 0x%"PRIx64" usr=%d os=%d tag_ena=%d tag_val=%d " | 
 | 		       "evmask=0x%x evsel=0x%x escr_sel=0x%x comp=%d cmpl=%d thr=%d e=%d", | 
 | 			escr, | 
 | 			cccr, | 
 | 			e->codes[2], /* perf_event code */ | 
 | 			escr.bits.t0_usr, /* t1 is identical */ | 
 | 			escr.bits.t0_os,  /* t1 is identical */ | 
 | 			escr.bits.tag_enable, | 
 | 			escr.bits.tag_value, | 
 | 			escr.bits.event_mask, | 
 | 			escr.bits.event_select, | 
 | 			cccr.bits.escr_select, | 
 | 			cccr.bits.compare, | 
 | 			cccr.bits.complement, | 
 | 			cccr.bits.threshold, | 
 | 			cccr.bits.edge); | 
 |  | 
 | 	__pfm_vbprintf("] %s\n", e->fstr); | 
 | } | 
 |  | 
 | static int | 
 | netburst_add_defaults(pfmlib_event_desc_t *e, unsigned int *evmask) | 
 | { | 
 | 	int i, n; | 
 |  | 
 | 	n = netburst_get_numasks(e->event); | 
 |  | 
 | 	for (i = 0; i < n; i++) { | 
 |  | 
 | 		if (netburst_events[e->event].event_masks[i].flags & NETBURST_FL_DFL) | 
 | 			goto found; | 
 | 	} | 
 | 	return PFM_ERR_ATTR; | 
 | found: | 
 | 	*evmask = 1 << netburst_events[e->event].event_masks[i].bit; | 
 | 	n = e->nattrs; | 
 | 	e->attrs[n].id = i; | 
 | 	e->attrs[n].ival = i; | 
 | 	e->nattrs = n+1; | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | int | 
 | pfm_netburst_get_encoding(void *this, pfmlib_event_desc_t *e) | 
 | { | 
 | 	pfm_event_attr_info_t *a; | 
 | 	netburst_escr_value_t escr; | 
 | 	netburst_cccr_value_t cccr; | 
 | 	unsigned int evmask = 0; | 
 | 	unsigned int plmmsk = 0; | 
 | 	int umask_done = 0; | 
 | 	const char *n; | 
 | 	int k, id, bit, ret; | 
 | 	int tag_enable = 0, tag_value = 0; | 
 |  | 
 | 	e->fstr[0] = '\0'; | 
 |  | 
 | 	escr.val = 0; | 
 | 	cccr.val = 0; | 
 |  | 
 | 	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) { | 
 |  | 
 | 			bit = netburst_events[e->event].event_masks[a->idx].bit; | 
 | 			n   = netburst_events[e->event].event_masks[a->idx].name; | 
 | 			/* | 
 | 			 * umask combination seems possible, although it does | 
 | 			 * not always make sense, e.g., BOGUS vs. NBOGUS | 
 | 			 */ | 
 | 			if (bit < EVENT_MASK_BITS && n) { | 
 | 				evmask |= (1 << bit); | 
 | 			} else	if (bit >= EVENT_MASK_BITS && n) { | 
 | 				tag_value |= (1 << (bit - EVENT_MASK_BITS)); | 
 | 				tag_enable = 1; | 
 | 			} | 
 | 			umask_done = 1; | 
 | 		} else if (a->type == PFM_ATTR_RAW_UMASK) { | 
 | 			/* should not happen */ | 
 | 			return PFM_ERR_ATTR; | 
 | 		} else { | 
 | 			uint64_t ival = e->attrs[k].ival; | 
 | 			switch (a->idx) { | 
 | 			case NETBURST_ATTR_U: | 
 | 				escr.bits.t1_usr = !!ival; | 
 | 				escr.bits.t0_usr = !!ival; | 
 | 				plmmsk |= _NETBURST_ATTR_U; | 
 | 				break; | 
 | 			case NETBURST_ATTR_K: | 
 | 				escr.bits.t1_os = !!ival; | 
 | 				escr.bits.t0_os = !!ival; | 
 | 				plmmsk |= _NETBURST_ATTR_K; | 
 | 				break; | 
 | 			case NETBURST_ATTR_E: | 
 | 				if (ival) { | 
 | 					cccr.bits.compare = 1; | 
 | 					cccr.bits.edge = 1; | 
 | 				} | 
 | 				break; | 
 | 			case NETBURST_ATTR_C: | 
 | 				if (ival) { | 
 | 					cccr.bits.compare = 1; | 
 | 					cccr.bits.complement = 1; | 
 | 				} | 
 | 				break; | 
 | 			case NETBURST_ATTR_T: | 
 | 				if (ival > 15) | 
 | 					return PFM_ERR_ATTR_VAL; | 
 |  | 
 | 				if (ival) { | 
 | 					cccr.bits.compare = 1; | 
 | 					cccr.bits.threshold = ival; | 
 | 				} | 
 | 				break; | 
 | 			default: | 
 | 				return PFM_ERR_ATTR; | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	/* | 
 | 	 * handle case where no priv level mask was passed. | 
 | 	 * then we use the dfl_plm | 
 | 	 */ | 
 | 	if (!(plmmsk & (_NETBURST_ATTR_K|_NETBURST_ATTR_U))) { | 
 | 		if (e->dfl_plm & PFM_PLM0) { | 
 | 			escr.bits.t1_os = 1; | 
 | 			escr.bits.t0_os = 1; | 
 | 		} | 
 | 		if (e->dfl_plm & PFM_PLM3) { | 
 | 			escr.bits.t1_usr = 1; | 
 | 			escr.bits.t0_usr = 1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (!umask_done) { | 
 | 		ret = netburst_add_defaults(e, &evmask); | 
 | 		if (ret != PFM_SUCCESS) | 
 | 			return ret; | 
 | 	} | 
 |  | 
 | 	escr.bits.tag_enable   = tag_enable; | 
 | 	escr.bits.tag_value    = tag_value; | 
 | 	escr.bits.event_mask   = evmask; | 
 | 	escr.bits.event_select = netburst_events[e->event].event_select; | 
 |  | 
 |  | 
 | 	cccr.bits.enable        = 1; | 
 | 	cccr.bits.escr_select   = netburst_events[e->event].escr_select; | 
 | 	cccr.bits.active_thread = 3; | 
 |  | 
 | 	if (e->event == PME_REPLAY_EVENT) | 
 | 		escr.bits.event_mask &= P4_REPLAY_REAL_MASK;	 /* remove virtual mask bits */ | 
 |  | 
 | 	/* | 
 | 	 * reorder all the attributes such that the fstr appears always | 
 | 	 * the same regardless of how the attributes were submitted. | 
 | 	 */ | 
 | 	evt_strcat(e->fstr, "%s", netburst_events[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) { | 
 | 			id = e->attrs[k].id; | 
 | 			evt_strcat(e->fstr, ":%s", netburst_events[e->event].event_masks[id].name); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_K].name, escr.bits.t0_os); | 
 | 	evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_U].name, escr.bits.t0_usr); | 
 | 	evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_E].name, cccr.bits.edge); | 
 | 	evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_C].name, cccr.bits.complement); | 
 | 	evt_strcat(e->fstr, ":%s=%lu", netburst_mods[NETBURST_ATTR_T].name, cccr.bits.threshold); | 
 |  | 
 | 	e->count = 2; | 
 | 	e->codes[0] = escr.val; | 
 | 	e->codes[1] = cccr.val; | 
 |  | 
 | 	netburst_display_reg(e); | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_detect(void *this) | 
 | { | 
 | 	int ret; | 
 | 	int model; | 
 |  | 
 | 	ret = pfm_intel_x86_detect(); | 
 | 	if (ret != PFM_SUCCESS) | 
 | 		return ret; | 
 |  | 
 | 	if (pfm_intel_x86_cfg.family != 15) | 
 | 		return PFM_ERR_NOTSUPP; | 
 |  | 
 | 	model = pfm_intel_x86_cfg.model; | 
 | 	if (model == 3 || model == 4 || model == 6) | 
 | 		return PFM_ERR_NOTSUPP; | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_detect_prescott(void *this) | 
 | { | 
 | 	int ret; | 
 | 	int model; | 
 |  | 
 | 	ret = pfm_intel_x86_detect(); | 
 | 	if (ret != PFM_SUCCESS) | 
 | 		return ret; | 
 |  | 
 | 	if (pfm_intel_x86_cfg.family != 15) | 
 | 		return PFM_ERR_NOTSUPP; | 
 |  | 
 | 	/* | 
 | 	 * prescott has one more event (instr_completed) | 
 | 	 */ | 
 | 	model = pfm_intel_x86_cfg.model; | 
 | 	if (model != 3 && model != 4 && model != 6) | 
 | 		return PFM_ERR_NOTSUPP; | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_get_event_first(void *this) | 
 | { | 
 | 	pfmlib_pmu_t *p = this; | 
 | 	return p->pme_count ? 0 : -1; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_get_event_next(void *this, int idx) | 
 | { | 
 | 	pfmlib_pmu_t *p = this; | 
 |  | 
 | 	if (idx >= (p->pme_count-1)) | 
 | 		return -1; | 
 |  | 
 | 	return idx+1; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_event_is_valid(void *this, int pidx) | 
 | { | 
 | 	pfmlib_pmu_t *p = this; | 
 | 	return pidx >= 0 && pidx < p->pme_count; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_get_event_attr_info(void *this, int pidx, int attr_idx, pfm_event_attr_info_t *info) | 
 | { | 
 | 	const netburst_entry_t *pe = this_pe(this); | 
 | 	int numasks, idx; | 
 |  | 
 | 	numasks = netburst_get_numasks(pidx); | 
 | 	if (attr_idx < numasks) { | 
 | 		//idx = pfm_intel_x86_attr2umask(this, pidx, attr_idx); | 
 | 		idx = attr_idx; | 
 | 		info->name = pe[pidx].event_masks[idx].name; | 
 | 		info->desc = pe[pidx].event_masks[idx].desc; | 
 | 		info->equiv= NULL; | 
 | 		info->code = pe[pidx].event_masks[idx].bit; | 
 | 		info->type = PFM_ATTR_UMASK; | 
 | 		info->is_dfl = !!(pe[pidx].event_masks[idx].flags & NETBURST_FL_DFL); | 
 | 	} else { | 
 | 		idx = attr_idx - numasks; | 
 | 		info->name = netburst_mods[idx].name; | 
 | 		info->desc = netburst_mods[idx].desc; | 
 | 		info->equiv= NULL; | 
 | 		info->code = idx; | 
 | 		info->type = netburst_mods[idx].type; | 
 | 		info->is_dfl = 0; | 
 | 	} | 
 | 	info->ctrl = PFM_ATTR_CTRL_PMU; | 
 | 	info->idx = idx; /* namespace specific index */ | 
 | 	info->dfl_val64 = 0; | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_get_event_info(void *this, int idx, pfm_event_info_t *info) | 
 | { | 
 | 	const netburst_entry_t *pe = this_pe(this); | 
 | 	pfmlib_pmu_t *pmu = this; | 
 |  | 
 | 	/* | 
 | 	 * pmu and idx filled out by caller | 
 | 	 */ | 
 | 	info->name  = pe[idx].name; | 
 | 	info->desc  = pe[idx].desc; | 
 | 	info->code  = pe[idx].event_select | (pe[idx].escr_select << 8); | 
 | 	info->equiv = NULL; | 
 | 	info->idx   = idx; /* private index */ | 
 | 	info->pmu   = pmu->pmu; | 
 |  | 
 | 	info->nattrs  = netburst_get_numasks(idx); | 
 | 	info->nattrs += NETBURST_MODS_COUNT; | 
 |  | 
 | 	return PFM_SUCCESS; | 
 | } | 
 |  | 
 | static int | 
 | pfm_netburst_validate_table(void *this, FILE *fp) | 
 | { | 
 | 	pfmlib_pmu_t *pmu = this; | 
 | 	const netburst_entry_t *pe = netburst_events; | 
 | 	const char *name =  pmu->name; | 
 | 	int i, j, noname, ndfl; | 
 | 	int error = 0; | 
 |  | 
 | 	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++; | 
 | 		} | 
 |  | 
 | 		noname = ndfl = 0; | 
 |  | 
 | 		/* name = NULL is end-marker, veryfy there is at least one */ | 
 | 		for(j= 0; j < EVENT_MASK_BITS; j++) { | 
 |  | 
 | 			if (!pe[i].event_masks[j].name) | 
 | 				noname++; | 
 |  | 
 | 			if (pe[i].event_masks[j].name) { | 
 | 				if (!pe[i].event_masks[j].desc) { | 
 | 					fprintf(fp, "pmu: %s event%d:%s umask%d: %s :: no description\n", name, i, pe[i].name, j, pe[i].event_masks[j].name); | 
 | 					error++; | 
 | 				} | 
 | 				if (pe[i].event_masks[j].bit >= (EVENT_MASK_BITS+4)) { | 
 | 					fprintf(fp, "pmu: %s event%d:%s umask%d: %s :: invalid bit field\n", name, i, pe[i].name, j, pe[i].event_masks[j].name); | 
 | 					error++; | 
 | 				} | 
 |  | 
 | 				if (pe[i].event_masks[j].flags & NETBURST_FL_DFL) | 
 | 					ndfl++; | 
 | 			} | 
 | 		} | 
 | 		if (ndfl > 1) { | 
 | 			fprintf(fp, "pmu: %s event%d:%s :: more than one default umask\n", name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 | 		if (!noname) { | 
 | 			fprintf(fp, "pmu: %s event%d:%s :: no event mask end-marker\n", name, i, pe[i].name); | 
 | 			error++; | 
 | 		} | 
 | 	} | 
 | 	return error ? PFM_ERR_INVAL : PFM_SUCCESS; | 
 | } | 
 |  | 
 |  | 
 | static unsigned int | 
 | pfm_netburst_get_event_nattrs(void *this, int pidx) | 
 | { | 
 | 	unsigned int nattrs; | 
 | 	nattrs  = netburst_get_numasks(pidx); | 
 | 	nattrs += NETBURST_MODS_COUNT; | 
 | 	return nattrs; | 
 | } | 
 |  | 
 |  | 
 | pfmlib_pmu_t netburst_support = { | 
 | 	.desc			= "Pentium4", | 
 | 	.name			= "netburst", | 
 | 	.pmu			= PFM_PMU_INTEL_NETBURST, | 
 | 	.pme_count		= LIBPFM_ARRAY_SIZE(netburst_events) - 1, | 
 | 	.type			= PFM_PMU_TYPE_CORE, | 
 | 	.supported_plm		= INTEL_X86_PLM, | 
 | 	.atdesc			= netburst_mods, | 
 | 	.pe			= netburst_events, | 
 | 	.max_encoding		= 3, | 
 | 	.num_cntrs		= 18, | 
 |  | 
 | 	.pmu_detect		= pfm_netburst_detect, | 
 | 	.get_event_encoding[PFM_OS_NONE] = pfm_netburst_get_encoding, | 
 | 	 PFMLIB_ENCODE_PERF(pfm_netburst_get_perf_encoding), | 
 | 	.get_event_first	= pfm_netburst_get_event_first, | 
 | 	.get_event_next		= pfm_netburst_get_event_next, | 
 | 	.event_is_valid		= pfm_netburst_event_is_valid, | 
 | 	.validate_table		= pfm_netburst_validate_table, | 
 | 	.get_event_info		= pfm_netburst_get_event_info, | 
 | 	.get_event_attr_info	= pfm_netburst_get_event_attr_info, | 
 | 	.get_event_nattrs	= pfm_netburst_get_event_nattrs, | 
 | 	 PFMLIB_VALID_PERF_PATTRS(pfm_netburst_perf_validate_pattrs), | 
 | }; | 
 |  | 
 | pfmlib_pmu_t netburst_p_support = { | 
 | 	.desc			= "Pentium4 (Prescott)", | 
 | 	.name			= "netburst_p", | 
 | 	.pmu			= PFM_PMU_INTEL_NETBURST_P, | 
 | 	.pme_count		= LIBPFM_ARRAY_SIZE(netburst_events), | 
 | 	.type			= PFM_PMU_TYPE_CORE, | 
 | 	.supported_plm		= INTEL_X86_PLM, | 
 | 	.atdesc			= netburst_mods, | 
 | 	.pe			= netburst_events, | 
 | 	.max_encoding		= 3, | 
 | 	.num_cntrs		= 18, | 
 |  | 
 | 	.pmu_detect		= pfm_netburst_detect_prescott, | 
 | 	.get_event_encoding[PFM_OS_NONE] = pfm_netburst_get_encoding, | 
 | 	 PFMLIB_ENCODE_PERF(pfm_netburst_get_perf_encoding), | 
 | 	.get_event_first	= pfm_netburst_get_event_first, | 
 | 	.get_event_next		= pfm_netburst_get_event_next, | 
 | 	.event_is_valid		= pfm_netburst_event_is_valid, | 
 | 	.validate_table		= pfm_netburst_validate_table, | 
 | 	.get_event_info		= pfm_netburst_get_event_info, | 
 | 	.get_event_attr_info	= pfm_netburst_get_event_attr_info, | 
 | 	.get_event_nattrs	= pfm_netburst_get_event_nattrs, | 
 | 	 PFMLIB_VALID_PERF_PATTRS(pfm_netburst_perf_validate_pattrs), | 
 | }; |