/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <string.h>
#include <alloca.h>
#include <stdlib.h>
#include <stdio.h>
#include <libintl.h>
#include "libcpc.h"
#include "libcpc_impl.h"
/*
* Configuration data for Pentium Pro performance counters.
*
* Definitions taken from [3]. See the reference to
* understand what any of these settings actually means.
*
* [3] "Pentium Pro Family Developer's Manual, Volume 3:
* Operating Systems Writer's Manual," January 1996
*/
#define V_END 0
/*
* map from "cpu version" to flag bits
*/
V_P5, /* CPC_PENTIUM */
V_P6, /* CPC_PENTIUM_PRO */
};
struct nametable {
const char *name;
};
/*
* Basic Pentium events
*/
#define P5_EVENTS(v) \
{v, 0x0, "data_read"}, \
{v, 0x1, "data_write"}, \
{v, 0x2, "data_tlb_miss"}, \
{v, 0x3, "data_read_miss"}, \
{v, 0x4, "data_write_miss"}, \
{v, 0x5, "write_hit_to_M_or_E"}, \
{v, 0x6, "dcache_lines_wrback"}, \
{v, 0x7, "external_snoops"}, \
{v, 0x8, "external_dcache_snoop_hits"}, \
{v, 0x9, "memory_access_in_both_pipes"}, \
{v, 0xa, "bank_conflicts"}, \
{v, 0xb, "misaligned_ref"}, \
{v, 0xc, "code_read"}, \
{v, 0xd, "code_tlb_miss"}, \
{v, 0xe, "code_cache_miss"}, \
{v, 0xf, "any_segreg_loaded"}, \
{v, 0x12, "branches"}, \
{v, 0x13, "btb_hits"}, \
{v, 0x14, "taken_or_btb_hit"}, \
{v, 0x15, "pipeline_flushes"}, \
{v, 0x16, "instr_exec"}, \
{v, 0x17, "instr_exec_V_pipe"}, \
{v, 0x18, "clks_bus_cycle"}, \
{v, 0x19, "clks_full_wbufs"}, \
{v, 0x1a, "pipe_stall_read"}, \
{v, 0x1b, "stall_on_write_ME"}, \
{v, 0x1c, "locked_bus_cycle"}, \
{v, 0x1d, "io_rw_cycles"}, \
{v, 0x1e, "reads_noncache_mem"}, \
{v, 0x1f, "pipeline_agi_stalls"}, \
{v, 0x22, "flops"}, \
{v, 0x23, "bp_match_dr0"}, \
{v, 0x24, "bp_match_dr1"}, \
{v, 0x25, "bp_match_dr2"}, \
{v, 0x26, "bp_match_dr3"}, \
{v, 0x27, "hw_intrs"}, \
{v, 0x28, "data_rw"}, \
{v, 0x29, "data_rw_miss"}
{V_END}
};
{V_END}
};
};
/*
* Pentium Pro and Pentium II events
*/
/*
* Data cache unit
*/
/*
* Instruction fetch unit
*/
/*
* L2 cache
*/
/*
* External bus logic
*/
/*
* Floating point unit
*/
/*
* Memory ordering
*/
/*
* Instruction decoding and retirement
*/
/*
* Interrupts
*/
/*
* Branches
*/
/*
* Stalls
*/
/*
* Segment register loads
*/
/*
* Clocks
*/
/*
* MMX
*/
{V_END}
};
static int
{
return (0);
cpuver -= CPC_PENTIUM;
if (cpuver < 0 ||
return (0);
return (1);
}
/*ARGSUSED*/
static int
{
return (0);
case V_P5:
break;
case V_P6:
switch (n->bits) {
case 0xc1: /* flops */
case 0x10: /* fp_comp_ops_exe */
case 0x14: /* cycles_div_busy */
/* only reg0 counts these */
if (regno == 1)
return (0);
break;
case 0x11: /* fp_assist */
case 0x12: /* mul */
case 0x13: /* div */
/* only 1 can count these */
if (regno == 0)
return (0);
break;
default:
break;
}
break;
default:
return (0);
}
return (1);
}
static const struct nametable *
{
const struct nametable *n;
return (NULL);
case V_P5:
n = P5mmx_names[regno];
break;
case V_P6:
n = P6_names;
break;
default:
n = NULL;
break;
}
return (n);
}
void
{
const struct nametable *n;
return;
}
const char *
{
const struct nametable *n;
return (NULL);
return (n->name);
return (NULL);
}
/*
* Register names can be specified as strings or even as numbers
*/
int
{
const struct nametable *n;
long value;
return (-1);
return (0);
}
return (0);
}
return (-1);
}
const char *
{
case V_P5:
return ("Pentium");
return ("Pentium with MMX");
case V_P6:
return ("Pentium Pro, Pentium II");
return ("Pentium Pro with MMX, Pentium II");
default:
break;
}
return (NULL);
}
const char *
{
case V_P5:
return (gettext(
"See Appendix A.2 of the \"Intel Architecture "
"Software Developer's Manual,\" 243192, 1997"));
case V_P6:
return (gettext(
"See Appendix A.1 of the \"Intel Architecture "
"Software Developer's Manual,\" 243192, 1997"));
default:
break;
}
return (NULL);
}
/*
* This is a functional interface to allow CPUs with fewer %pic registers
* to share the same data structure as those with more %pic registers
* within the same instruction set family.
*/
{
switch (cpuver) {
case CPC_PENTIUM:
case CPC_PENTIUM_MMX:
case CPC_PENTIUM_PRO:
case CPC_PENTIUM_PRO_MMX:
default:
return (0);
}
}
#define BITS(v, u, l) \
(((v) >> (l)) & ((1 << (1 + (u) - (l))) - 1))
#include "getcpuid.h"
/*
* Return the version of the current processor.
*
* Version -1 is defined as 'not performance counter capable'
*/
int
cpc_getcpuver(void)
{
if (ver != -1)
return (ver);
{
return (ver);
}
if (maxeax >= 1) {
/*
* map family and model into the performance
* counter architectures we currently understand.
*
* See application note AP485 (from developer.intel.com)
* for further explanation.
*/
switch (family) {
case 5: /* Pentium and Pentium with MMX */
break;
case 6: /* Pentium Pro and Pentium II and III */
break;
default:
case 0xf: /* Pentium IV */
break;
}
}
return (ver);
}