|  | /* | 
|  | * 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), | 
|  | }; |