#include <sys/cpuvar.h>
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/cpc_impl.h>
#include <sys/cpc_pcbe.h>
#include <sys/modctl.h>
#include <sys/machsystm.h>
#include <sys/sdt.h>
#include <sys/niagararegs.h>
static int ni_pcbe_init(void);
static uint_t ni_pcbe_ncounters(void);
static const char *ni_pcbe_impl_name(void);
static const char *ni_pcbe_cpuref(void);
static char *ni_pcbe_list_events(uint_t picnum);
static char *ni_pcbe_list_attrs(void);
static uint64_t ni_pcbe_event_coverage(char *event);
static uint64_t ni_pcbe_overflow_bitmap(void);
static int ni_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
void *token);
static void ni_pcbe_program(void *token);
static void ni_pcbe_allstop(void);
static void ni_pcbe_sample(void *token);
static void ni_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);
pcbe_ops_t ni_pcbe_ops = {
PCBE_VER_1,
CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,
ni_pcbe_ncounters,
ni_pcbe_impl_name,
ni_pcbe_cpuref,
ni_pcbe_list_events,
ni_pcbe_list_attrs,
ni_pcbe_event_coverage,
ni_pcbe_overflow_bitmap,
ni_pcbe_configure,
ni_pcbe_program,
ni_pcbe_allstop,
ni_pcbe_sample,
ni_pcbe_free
};
typedef struct _ni_pcbe_config {
uint8_t pcbe_picno;
uint32_t pcbe_bits;
uint32_t pcbe_flags;
uint32_t pcbe_pic;
} ni_pcbe_config_t;
struct nametable {
const uint8_t bits;
const char *name;
};
typedef struct _ni_generic_events {
char *name;
char *event;
} ni_generic_event_t;
#define ULTRA_PCR_PRIVPIC (UINT64_C(1) << CPC_PCR_PRIVPIC)
#define NT_END 0xFF
#define GEN_EVT_END { NULL, NULL }
static const uint64_t allstopped = ULTRA_PCR_PRIVPIC;
static const struct nametable Niagara_names1[] = {
{0x00, "Instr_cnt"},
{NT_END, ""}
};
static const struct nametable Niagara_names0[] = {
{0x0, "SB_full"},
{0x1, "FP_instr_cnt"},
{0x2, "IC_miss"},
{0x3, "DC_miss"},
{0x4, "ITLB_miss"},
{0x5, "DTLB_miss"},
{0x6, "L2_imiss"},
{0x7, "L2_dmiss_ld"},
{NT_END, ""}
};
static const struct nametable *Niagara_names[2] = {
Niagara_names0,
Niagara_names1
};
static const ni_generic_event_t Niagara_generic_names1[] = {
{ "PAPI_tot_ins", "Instr_cnt" },
{ NULL, NULL }
};
static const ni_generic_event_t Niagara_generic_names0[] = {
{ "PAPI_l2_icm", "L2_imiss" },
{ "PAPI_l2_ldm", "L2_dmiss_ld" },
{ "PAPI_fp_ops", "FP_instr_cnt" },
{ "PAPI_fp_ins", "FP_instr_cnt" },
{ "PAPI_l1_icm", "IC_miss" },
{ "PAPI_l1_dcm", "DC_miss" },
{ "PAPI_tlb_im", "ITLB_miss" },
{ "PAPI_tlb_dm", "DTLB_miss" },
{ NULL, NULL }
};
static const ni_generic_event_t *Niagara_generic_names[2] = {
Niagara_generic_names0,
Niagara_generic_names1
};
static const struct nametable **events;
static const ni_generic_event_t **generic_events;
static const char *ni_impl_name = "UltraSPARC T1";
static char *pic_events[2];
static uint16_t pcr_pic0_mask;
static uint16_t pcr_pic1_mask;
#define CPU_REF_URL " Documentation for Sun processors can be found at: " \
"http://www.sun.com/processors/manuals"
static const char *niagara_cpuref = "See the \"UltraSPARC T1 User's Manual\" "
"for descriptions of these events." CPU_REF_URL;
static int
ni_pcbe_init(void)
{
const struct nametable *n;
const ni_generic_event_t *gevp;
int i;
size_t size;
events = Niagara_names;
generic_events = Niagara_generic_names;
pcr_pic0_mask = CPC_PCR_PIC0_MASK;
pcr_pic1_mask = CPC_PCR_PIC1_MASK;
for (i = 0; i < 2; i++) {
size = 0;
for (n = events[i]; n->bits != NT_END; n++)
size += strlen(n->name) + 1;
for (gevp = generic_events[i]; gevp->name != NULL; gevp++)
size += strlen(gevp->name) + 1;
pic_events[i] = kmem_alloc(size + 1, KM_SLEEP);
*pic_events[i] = '\0';
for (n = events[i]; n->bits != NT_END; n++) {
(void) strcat(pic_events[i], n->name);
(void) strcat(pic_events[i], ",");
}
for (gevp = generic_events[i]; gevp->name != NULL; gevp++) {
(void) strcat(pic_events[i], gevp->name);
(void) strcat(pic_events[i], ",");
}
pic_events[i][size - 1] = '\0';
}
return (0);
}
static uint_t
ni_pcbe_ncounters(void)
{
return (2);
}
static const char *
ni_pcbe_impl_name(void)
{
return (ni_impl_name);
}
static const char *
ni_pcbe_cpuref(void)
{
return (niagara_cpuref);
}
static char *
ni_pcbe_list_events(uint_t picnum)
{
ASSERT(picnum >= 0 && picnum < cpc_ncounters);
return (pic_events[picnum]);
}
static char *
ni_pcbe_list_attrs(void)
{
return ("");
}
static const ni_generic_event_t *
find_generic_event(int regno, char *name)
{
const ni_generic_event_t *gevp;
for (gevp = generic_events[regno]; gevp->name != NULL; gevp++) {
if (strcmp(gevp->name, name) == 0)
return (gevp);
}
return (NULL);
}
static const struct nametable *
find_event(int regno, char *name)
{
const struct nametable *n;
n = events[regno];
for (; n->bits != NT_END; n++)
if (strcmp(name, n->name) == 0)
return (n);
return (NULL);
}
static uint64_t
ni_pcbe_event_coverage(char *event)
{
uint64_t bitmap = 0;
if ((find_event(0, event) != NULL) ||
(find_generic_event(0, event) != NULL))
bitmap = 0x1;
if ((find_event(1, event) != NULL) ||
(find_generic_event(1, event) != NULL))
bitmap |= 0x2;
return (bitmap);
}
static uint64_t
ni_pcbe_overflow_bitmap(void)
{
uint64_t pcr, overflow;
pcr = ultra_getpcr();
DTRACE_PROBE1(niagara__getpcr, uint64_t, pcr);
overflow = (pcr & CPC_PCR_OVF_MASK) >>
CPC_PCR_OVF_SHIFT;
#if 0
if (overflow)
ultra_setpcr(pcr & ~CPC_PCR_OVF_MASK);
#endif
return (overflow);
}
static int
ni_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
{
ni_pcbe_config_t *conf;
const struct nametable *n;
const ni_generic_event_t *gevp;
ni_pcbe_config_t *other_config;
if (*data != NULL) {
conf = *data;
conf->pcbe_pic = (uint32_t)preset;
return (0);
}
if (picnum < 0 || picnum > 1)
return (CPC_INVALID_PICNUM);
if (nattrs != 0)
return (CPC_INVALID_ATTRIBUTE);
if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
(other_config->pcbe_flags != flags))
return (CPC_CONFLICTING_REQS);
if ((n = find_event(picnum, event)) == NULL) {
if ((gevp = find_generic_event(picnum, event)) != NULL) {
n = find_event(picnum, gevp->event);
ASSERT(n != NULL);
} else {
return (CPC_INVALID_EVENT);
}
}
conf = kmem_alloc(sizeof (ni_pcbe_config_t), KM_SLEEP);
conf->pcbe_picno = picnum;
conf->pcbe_bits = (uint32_t)n->bits;
conf->pcbe_flags = flags;
conf->pcbe_pic = (uint32_t)preset;
*data = conf;
return (0);
}
static void
ni_pcbe_program(void *token)
{
ni_pcbe_config_t *pic0;
ni_pcbe_config_t *pic1;
ni_pcbe_config_t *tmp;
ni_pcbe_config_t empty = { 1, 0x1c, 0, 0 };
uint64_t pcr;
uint64_t curpic;
if ((pic0 = (ni_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
NULL)
panic("ni_pcbe: token %p has no configs", token);
if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
pic1 = ∅
empty.pcbe_flags = pic0->pcbe_flags;
}
if (pic0->pcbe_picno != 0) {
empty.pcbe_picno = 0;
#if 0
empty.pcbe_bits = 0x14;
#endif
tmp = pic0;
pic0 = pic1;
pic1 = tmp;
}
if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
panic("ni_pcbe: bad config on token %p\n", token);
ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
ultra_setpcr(allstopped);
ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
(uint64_t)pic0->pcbe_pic);
pcr = (pic0->pcbe_bits & pcr_pic0_mask) << CPC_PCR_PIC0_SHIFT;
pcr |= (pic1->pcbe_bits & pcr_pic1_mask) << CPC_PCR_PIC1_SHIFT;
if (pic0->pcbe_flags & CPC_COUNT_USER)
pcr |= (1ull << CPC_PCR_USR);
if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
pcr |= (1ull << CPC_PCR_SYS);
DTRACE_PROBE1(niagara__setpcr, uint64_t, pcr);
ultra_setpcr(pcr);
curpic = ultra_getpic();
pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
DTRACE_PROBE1(niagara__newpic, uint64_t, curpic);
}
static void
ni_pcbe_allstop(void)
{
ultra_setpcr(allstopped);
}
static void
ni_pcbe_sample(void *token)
{
uint64_t curpic;
int64_t diff;
uint64_t *pic0_data;
uint64_t *pic1_data;
uint64_t *dtmp;
uint64_t tmp;
ni_pcbe_config_t *pic0;
ni_pcbe_config_t *pic1;
ni_pcbe_config_t empty = { 1, 0, 0, 0 };
ni_pcbe_config_t *ctmp;
curpic = ultra_getpic();
DTRACE_PROBE1(niagara__getpic, uint64_t, curpic);
if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
panic("%s: token %p has no configs", ni_impl_name, token);
if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
pic1 = ∅
pic1_data = &tmp;
}
if (pic0->pcbe_picno != 0) {
empty.pcbe_picno = 0;
ctmp = pic0;
pic0 = pic1;
pic1 = ctmp;
dtmp = pic0_data;
pic0_data = pic1_data;
pic1_data = dtmp;
}
if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
panic("%s: bad config on token %p\n", ni_impl_name, token);
diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
if (diff < 0)
diff += (1ll << 32);
*pic0_data += diff;
diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
if (diff < 0)
diff += (1ll << 32);
*pic1_data += diff;
pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
}
static void
ni_pcbe_free(void *config)
{
kmem_free(config, sizeof (ni_pcbe_config_t));
}
static struct modlpcbe modlpcbe = {
&mod_pcbeops,
"UltraSPARC T1 Performance Counters",
&ni_pcbe_ops
};
static struct modlinkage modl = {
MODREV_1,
&modlpcbe,
};
int
_init(void)
{
if (ni_pcbe_init() != 0)
return (ENOTSUP);
return (mod_install(&modl));
}
int
_fini(void)
{
return (mod_remove(&modl));
}
int
_info(struct modinfo *mi)
{
return (mod_info(&modl, mi));
}