/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* This file contains preset event names from the Performance Application
* Programming Interface v3.5 which included the following notice:
*
* Copyright (c) 2005,6
* Innovative Computing Labs
* Computer Science Department,
* University of Tennessee,
* Knoxville, TN.
* All Rights Reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* * Neither the name of the University of Tennessee nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*
* This open source software license conforms to the BSD License template.
*/
/*
* Performance Counter Back-End for Pentium 4.
*/
#include <sys/cpc_impl.h>
#include <sys/cpc_pcbe.h>
#include <sys/inttypes.h>
#include <sys/archsystm.h>
#include <sys/x86_archext.h>
#include <sys/privregs.h>
static int p4_pcbe_init(void);
static uint_t p4_pcbe_ncounters(void);
static const char *p4_pcbe_impl_name(void);
static const char *p4_pcbe_cpuref(void);
static char *p4_pcbe_list_attrs(void);
static uint64_t p4_pcbe_overflow_bitmap(void);
void *token);
static void p4_pcbe_program(void *token);
static void p4_pcbe_allstop(void);
static void p4_pcbe_sample(void *token);
static void p4_pcbe_free(void *config);
extern int cpuid_get_clogid(cpu_t *);
};
/*
* P4 Configuration Flags.
*/
typedef struct _p4_pcbe_config {
typedef struct _p4_escr {
int pe_num;
} p4_escr_t;
/*
* CCCR field definitions.
*
* Note that the Intel Developer's Manual states that the reserved field at
* bit location 16 and 17 must be set to 11. (??)
*/
/*
* HyperThreaded ESCR fields.
*/
#define ESCR_T1_USR_SHIFT 0
/*
* ESCRs are grouped by counter; each group of ESCRs is associated with a
* distinct group of counters. Use these macros to fill in the table below.
*/
/*
* Table describing the 45 Event Selection and Control Registers (ESCRs).
*/
};
typedef struct _p4_ctr {
} p4_ctr_t;
};
typedef struct _p4_event {
} p4_event_t;
typedef struct _p4_generic_event {
char *name;
char *event;
#define C(n) (1 << n)
C(12)|C(13)|C(14)|C(15)|C(16) },
C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
C(4)|C(5)|C(6)|C(7)},
{ NULL, 0, 0, 0, 0 }
};
{ "PAPI_br_msp", "branch_retired", 0xa, C(12)|C(13)|C(14)|C(15)|C(16) },
{ "PAPI_br_ins", "branch_retired", 0xf, C(12)|C(13)|C(14)|C(15)|C(16) },
{ "PAPI_br_tkn", "branch_retired", 0xc, C(12)|C(13)|C(14)|C(15)|C(16) },
{ "PAPI_br_ntk", "branch_retired", 0x3, C(12)|C(13)|C(14)|C(15)|C(16) },
{ "PAPI_br_prc", "branch_retired", 0x5, C(12)|C(13)|C(14)|C(15)|C(16) },
{ "PAPI_tot_ins", "instr_retired", 0x3, C(12)|C(13)|C(14)|C(15)|C(16)|C(17) },
{ "PAPI_tot_cyc", "global_power_events", 0x1, C(0)|C(1)|C(2)|C(3) },
{ "PAPI_tlb_dm", "page_walk_type", 0x1, C(0)|C(1)|C(2)|C(3) },
{ "PAPI_tlb_im", "page_walk_type", 0x2, C(0)|C(1)|C(2)|C(3) },
{ "PAPI_tlb_tm", "page_walk_type", 0x3, C(0)|C(1)|C(2)|C(3) },
{ "PAPI_l1_icm", "BPU_fetch_request", 0x1, C(0)|C(1)|C(2)|C(3) },
{ "PAPI_l2_ldm", "BSQ_cache_reference", 0x100, C(0)|C(1)|C(2)|C(3) },
{ "PAPI_l2_stm", "BSQ_cache_reference", 0x400, C(0)|C(1)|C(2)|C(3) },
{ "PAPI_l2_tcm", "BSQ_cache_reference", 0x500, C(0)|C(1)|C(2)|C(3) },
};
/*
* Indicates whether the "rdpmc" instruction is available on this processor.
*/
static int p4_rdpmc_avail = 0;
/*
* If set, this processor has HyperThreading.
*/
static int p4_htt = 0;
static int
p4_pcbe_init(void)
{
int i;
/*
* If we're not running on a P4, refuse to load.
*/
return (-1);
/*
* Set up the event lists for each counter.
*
* First pass calculates the size of the event list, and the second
* pass copies each event name into the event list.
*/
for (i = 0; i < 18; i++) {
size = 0;
if (ev->pe_ctr_mask & C(i))
}
}
/*
* We use 'size + 1' here to ensure room for the final
* strcat when it terminates the string.
*/
*p4_eventlist[i] = '\0';
if (ev->pe_ctr_mask & C(i)) {
}
}
}
}
/*
* Remove trailing ','
*/
}
p4_rdpmc_avail = 1;
/*
* The X86_HTT flag may disappear soon, so we'll isolate the impact of
* its demise to the following if().
*/
p4_htt = 1;
return (0);
}
static uint_t
p4_pcbe_ncounters(void)
{
return (18);
}
static const char *
p4_pcbe_impl_name(void)
{
if (p4_htt)
return (PCBE_IMPL_NAME_P4HT);
return ("Pentium 4");
}
static const char *
p4_pcbe_cpuref(void)
{
return ("See Appendix A.1 of the \"IA-32 Intel Architecture Software " \
"Developer's Manual Volume 3: System Programming Guide,\" " \
"Order # 245472-012, 2003");
}
static char *
{
return (p4_eventlist[picnum]);
}
static char *
p4_pcbe_list_attrs(void)
{
if (p4_htt)
return (P4_ATTRS ",active_thread,count_sibling_usr,"
"count_sibling_sys");
return (P4_ATTRS);
}
static p4_generic_event_t *
{
return (gevp);
return (NULL);
}
static p4_event_t *
{
return (evp);
return (NULL);
}
static uint64_t
{
else
return (0);
}
return (ev->pe_ctr_mask);
}
static uint64_t
p4_pcbe_overflow_bitmap(void)
{
extern int kcpc_hw_overflow_intr_installed;
int i;
/*
* The CCCR's OVF bit indicates that the corresponding counter has
* overflowed. It must be explicitly cleared by software, so it is
* safe to read the CCCR values here.
*/
for (i = 0; i < 18; i++) {
ret |= (1 << i);
}
/*
* Pentium 4 and Xeon turn off the CPC interrupt mask bit in the LVT at
* every overflow. Turn it back on here.
*/
(*kcpc_hw_enable_cpc_intr)();
return (ret);
}
static int
{
int i;
for (i = 0; i < 18; i++) {
continue;
return (1);
}
return (0);
}
static void
{
do {
}
}
}
/*
* Programming a counter:
*
* Select event.
* Choose an ESCR capable of counting that event.
* Set up the ESCR with the desired parameters (usr, sys, tag).
* Set up the CCCR to point to the selected ESCR.
* Set the CCCR parameters (overflow, cascade, edge, etc).
*/
static int
void *token)
{
int escr_ndx;
int i;
int use_tag = 0;
int compare = 0;
int complement = 0;
int threshold = 0;
int edge = 0;
int invalid_attr = 0;
/*
* If we've been handed an existing configuration, we need only preset
* the counter value.
*/
return (0);
}
return (CPC_INVALID_PICNUM);
/*
* For generic events a HTT processor is only allowed
* to specify the 'active_thread', 'count_sibling_usr'
* and 'count_sibling_sys' attributes.
*/
if (p4_htt)
for (i = 0; i < nattrs; i++)
invalid_attr = 1;
if ((p4_htt && invalid_attr) ||
return (CPC_ATTRIBUTE_OUT_OF_RANGE);
} else {
return (CPC_INVALID_EVENT);
}
}
/*
* Find an ESCR capable of counting this event.
*/
break;
}
/*
* All ESCRs capable of counting this event are already being
* used.
*/
if (escr_ndx == ESCR_MAX_INDEX)
return (CPC_RESOURCE_UNAVAIL);
/*
* At this point, ev points to the desired event and escr is the index
* of a capable and available ESCR.
*
* Now process and verify the attributes.
*/
for (i = 0; i < nattrs; i++) {
!= ev->pe_escr_mask)
return (CPC_ATTRIBUTE_OUT_OF_RANGE);
continue;
return (CPC_ATTRIBUTE_OUT_OF_RANGE);
use_tag = 1;
continue;
compare = 1;
continue;
complement = 1;
continue;
return (CPC_ATTRIBUTE_OUT_OF_RANGE);
continue;
edge = 1;
continue;
}
/*
* The remaining attributes are valid only on HyperThreaded P4s
* for processes with the "cpc_cpu" privilege.
*/
if (p4_htt == 0)
return (CPC_INVALID_ATTRIBUTE);
if (secpolicy_cpc_cpu(crgetcred()) != 0)
return (CPC_ATTR_REQUIRES_PRIVILEGE);
return (CPC_ATTRIBUTE_OUT_OF_RANGE);
sibling_usr = 1;
sibling_sys = 1;
} else
return (CPC_INVALID_ATTRIBUTE);
}
/*
* Make sure the counter can count this event
*/
return (CPC_PIC_NOT_CAPABLE);
/*
* Find an ESCR that lines up with the event _and_ the counter.
*/
break;
}
if (escr_ndx == ESCR_MAX_INDEX)
return (CPC_RESOURCE_UNAVAIL);
KM_SLEEP);
(emask << ESCR_EVMASK_SHIFT);
if (use_tag == 1) {
}
if (p4_htt) {
/*
* This is a HyperThreaded P4. Since we don't know which
* logical CPU this configuration will eventually be programmed
* on, we can't yet decide which fields of the ESCR to select.
*
* Record the necessary information in the flags for later.
*/
if (flags & CPC_COUNT_USER)
if (flags & CPC_COUNT_SYSTEM)
if (p4_htt && sibling_usr)
if (p4_htt && sibling_sys)
} else {
/*
* This is not HyperThreaded, so we can determine the exact
* ESCR value necessary now.
*/
if (flags & CPC_COUNT_USER)
if (flags & CPC_COUNT_SYSTEM)
}
/*
* Even on non-HT P4s, Intel states the active_thread field (marked as
* "reserved" for the non-HT chips) must be set to all 1s.
*/
if (compare)
if (complement)
if (edge)
if (flags & CPC_OVF_NOTIFY_EMT) {
if (p4_htt)
else {
/*
* If the user has asked for notification of overflows,
* we automatically program the hardware to generate an
* interrupt on overflow.
*
* This can only be programmed now if this P4 doesn't
* have HyperThreading. If it does, we must wait until
* we know which logical CPU we'll be programming.
*/
}
}
return (0);
}
static void
{
int i;
if (p4_rdpmc_avail) {
if (kcpc_allow_nonpriv(token))
else
}
/*
* Ideally we would start all counters with a single operation, but in
* P4 each counter is enabled individually via its CCCR. To minimize the
* probe effect of enabling the counters, we do it in two passes: the
* first programs the counter and ESCR, and the second programs the
* CCCR (and thus enables the counter).
*/
if (p4_htt) {
for (i = 0; i < 18; i++) {
continue;
}
for (i = 0; i < 18; i++) {
continue;
/*
* We always target the overflow interrupt at the
* logical CPU which is doing the counting.
*/
}
} else {
for (i = 0; i < 18; i++) {
continue;
}
for (i = 0; i < 18; i++) {
continue;
}
}
}
static void
p4_pcbe_allstop(void)
{
int i;
for (i = 0; i < 18; i++)
}
static void
{
int i;
for (i = 0; i < 18; i++)
for (i = 0; i < 18; i++) {
continue;
if (diff < 0)
}
}
static void
{
}
"Pentium 4 Performance Counters",
};
&modlpcbe,
};
int
_init(void)
{
if (p4_pcbe_init() != 0)
return (ENOTSUP);
return (mod_install(&modl));
}
int
_fini(void)
{
return (mod_remove(&modl));
}
int
{
}