niagara2_pcbe.c revision 4496171313bed39e96f21bc2f9faf2868e267ae3
/*
* 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
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Niagara2 Performance Counter Backend
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/archsystm.h>
#include <sys/cpc_impl.h>
#include <sys/cpc_pcbe.h>
#include <sys/machsystm.h>
#include <sys/niagara2regs.h>
static int ni2_pcbe_init(void);
static uint_t ni2_pcbe_ncounters(void);
static const char *ni2_pcbe_impl_name(void);
static const char *ni2_pcbe_cpuref(void);
static char *ni2_pcbe_list_attrs(void);
static uint64_t ni2_pcbe_overflow_bitmap(void);
void *token);
static void ni2_pcbe_program(void *token);
static void ni2_pcbe_allstop(void);
static void ni2_pcbe_sample(void *token);
static void ni2_pcbe_free(void *config);
extern void ultra_setpcr(uint64_t);
extern uint64_t ultra_getpcr(void);
extern void ultra_setpic(uint64_t);
extern uint64_t ultra_getpic(void);
extern uint64_t ultra_gettick(void);
extern char cpu_module_name[];
};
typedef struct _ni2_pcbe_config {
typedef struct _ni2_event {
const char *name;
} ni2_event_t;
static ni2_event_t ni2_events[] = {
{ "Idle_strands", 0x000, 0x00 },
{ "Br_completed", 0x201, 0x7f },
{ "Br_taken", 0x202, 0x7f },
{ "Instr_FGU_arithmetic", 0x204, 0x7f },
{ "Instr_ld", 0x208, 0x7f },
{ "Instr_st", 0x210, 0x7f },
{ "Instr_sw", 0x220, 0x7f },
{ "Instr_other", 0x240, 0x7f },
{ "Instr_cnt", 0x27d, 0x7f },
{ "IC_miss", 0x301, 0x3f },
{ "DC_miss", 0x302, 0x3f },
{ "ITLB_miss", 0x304, 0x3f },
{ "DTLB_miss", 0x308, 0x3f },
{ "L2_imiss", 0x310, 0x3f },
{ "L2_dmiss_ld", 0x320, 0x3f },
{ "ITLB_HWTW_ref_L2", 0x404, 0x3c },
{ "DTLB_HWTW_ref_L2", 0x408, 0x3c },
{ "ITLB_HWTW_miss_L2", 0x410, 0x3c },
{ "DTLB_HWTW_miss_L2", 0x420, 0x3c },
{ "Stream_ld_to_PCX", 0x501, 0x3f },
{ "Stream_st_to_PCX", 0x502, 0x3f },
{ "CPU_ld_to_PCX", 0x504, 0x3f },
{ "CPU_ifetch_to_PCX", 0x508, 0x3f },
{ "CPU_st_to_PCX", 0x510, 0x3f },
{ "MMU_ld_to_PCX", 0x520, 0x3f },
{ "DES_3DES_op", 0x601, 0x3f },
{ "AES_op", 0x602, 0x3f },
{ "RC4_op", 0x604, 0x3f },
{ "MD5_SHA-1_SHA-256_op", 0x608, 0x3f },
{ "MA_op", 0x610, 0x3f },
{ "CRC_TCPIP_cksum", 0x620, 0x3f },
{ "DES_3DES_busy_cycle", 0x701, 0x3f },
{ "AES_busy_cycle", 0x702, 0x3f },
{ "RC4_busy_cycle", 0x704, 0x3f },
{ "MD5_SHA-1_SHA-256_busy_cycle", 0x708, 0x3f },
{ "MA_busy_cycle", 0x710, 0x3f },
{ "CRC_MPA_cksum", 0x720, 0x3f },
};
static const char *ni2_impl_name = "UltraSPARC T2";
static char *evlist;
static uint16_t pcr_pic0_mask;
static uint16_t pcr_pic1_mask;
#define CPU_REF_URL " Documentation for Sun processors can be found at: " \
static const char *niagara2_cpuref = "See the \"UltraSPARC T2 User's Manual\" "
"for descriptions of these events." CPU_REF_URL;
static int
ni2_pcbe_init(void)
{
int status;
/*
* Validate API version for Niagara2 specific hypervisor services
*/
"or unsupported major number: group: 0x%x major: 0x%lx "
"minor: 0x%lx errno: %d", HSVC_GROUP_NIAGARA2_CPU,
}
/*
* Construct event list.
*
* First pass: Calculate size needed. We'll need an additional byte
* for the NULL pointer during the last strcat.
*
* Second pass: Copy strings.
*/
evlist[0] = '\0';
}
/*
* Remove trailing comma.
*/
return (0);
}
static uint_t
ni2_pcbe_ncounters(void)
{
return (2);
}
static const char *
ni2_pcbe_impl_name(void)
{
return (ni2_impl_name);
}
static const char *
ni2_pcbe_cpuref(void)
{
return (niagara2_cpuref);
}
static char *
{
return (evlist);
}
static char *
ni2_pcbe_list_attrs(void)
{
if (niagara2_hsvc_available == B_TRUE)
return ("hpriv,emask");
else
return ("emask");
}
static ni2_event_t *
find_event(char *name)
{
return (evp);
return (NULL);
}
/*ARGSUSED*/
static uint64_t
ni2_pcbe_event_coverage(char *event)
{
/*
* Fortunately, both pic0 and pic1 can count all events.
*/
return (0x3);
}
#ifdef N2_ERRATUM_112
#endif
/*
* These processors cannot tell which counter overflowed. The PCBE interface
* requires such processors to act as if _all_ counters had overflowed.
*/
static uint64_t
ni2_pcbe_overflow_bitmap(void)
{
#ifdef N2_ERRATUM_112
#endif
pcr = ultra_getpcr();
#ifdef N2_ERRATUM_112
/*
* Niagara2 1.x silicon can generate a duplicate overflow trap per
* event. If we take an overflow trap with no counters overflowing,
* return a non-zero bitmask with no OV bit set for supported
* counter so that the framework can ignore this trap.
*/
tstamp = ultra_gettick();
if (overflow)
#endif
pic = ultra_getpic();
#ifdef N2_ERRATUM_134
/*
* In Niagara2 1.x silicon, PMU doesn't set OV bit for precise events.
* So, if we take a trap with the counter within the overflow range
* and the OV bit is not set, we assume OV bit should have been set.
*/
if (PIC_IN_OV_RANGE(pic0))
overflow |= 0x1;
if (PIC_IN_OV_RANGE(pic1))
overflow |= 0x2;
#endif
/*
* Reset the pic, if it is within the overflow range.
*/
pic0 = 0;
update_pic = B_TRUE;
}
pic1 = 0;
update_pic = B_TRUE;
}
if (update_pic)
return (overflow);
}
/*ARGSUSED*/
static int
{
int i;
/*
* If we've been handed an existing configuration, we need only preset
* the counter value.
*/
return (0);
}
if (picnum > 1)
return (CPC_INVALID_PICNUM);
return (CPC_INVALID_EVENT);
for (i = 0; i < nattrs; i++) {
flags |= CPC_COUNT_HPRIV;
return (CPC_ATTRIBUTE_OUT_OF_RANGE);
} else
return (CPC_INVALID_ATTRIBUTE);
}
/*
* Find other requests that will be programmed with this one, and ensure
* the flags don't conflict.
*/
return (CPC_CONFLICTING_REQS);
return (0);
}
static void
ni2_pcbe_program(void *token)
{
/* enable trap-on-event for pic0 and pic1 */
NULL)
}
if (pic0->pcbe_picno != 0) {
/*
* pic0 is counter 1, so if we need the null config it should
* be counter 0.
*/
nullcfg.pcbe_picno = 0;
}
/*
* UltraSPARC does not allow pic0 to be configured differently
* from pic1. If the flags on these two configurations are
* different, they are incompatible. This condition should be
* caught at configure time.
*/
/*
* PCR is set by HV using API call hv_niagara_setperf().
* Silently ignore hvpriv events if access is denied.
*/
} else
/*
* On UltraSPARC, only read-to-read counts are accurate. We cannot
* expect the value we wrote into the PIC, above, to be there after
* starting the counter. We must sample the counter value now and use
* that as the baseline for future samples.
*/
curpic = ultra_getpic();
}
static void
ni2_pcbe_allstop(void)
{
}
static void
ni2_pcbe_sample(void *token)
{
curpic = ultra_getpic();
}
if (pic0->pcbe_picno != 0) {
nullcfg.pcbe_picno = 0;
}
if (diff < 0)
if (diff < 0)
}
static void
ni2_pcbe_free(void *config)
{
}
"UltraSPARC T2 Performance Counters v%I%",
};
static struct modlinkage modl = {
&modlpcbe,
};
int
_init(void)
{
if (ni2_pcbe_init() != 0)
return (ENOTSUP);
return (mod_install(&modl));
}
int
_fini(void)
{
return (mod_remove(&modl));
}
int
{
}