summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2019-05-01 16:19:13 +0000
committerRobert Mustacchi <rm@joyent.com>2019-05-09 03:17:34 +0000
commitd0e58ef5d66890a3cd67c9c6eb8c823f9865a70f (patch)
tree4ceb1df56c86753eb2548678b40e7ce33d96b5ba
parentc18e9bc303e04175d63c5c51206b2ce6f6efe6a4 (diff)
downloadillumos-gate-d0e58ef5d66890a3cd67c9c6eb8c823f9865a70f.tar.gz
10896 Want support for AMD Zen CPC events
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com> Reviewed by: Patrick Mooney <patrick.mooney@joyent.com> Reviewed by: Dan McDonald <danmcd@joyent.com> Approved by: Richard Lowe <richlowe@richlowe.net>
-rw-r--r--exception_lists/copyright1
-rw-r--r--usr/src/data/Makefile5
-rw-r--r--usr/src/data/amdpmc/Makefile30
-rw-r--r--usr/src/data/amdpmc/README11
-rw-r--r--usr/src/data/amdpmc/f17h_core.json1043
-rw-r--r--usr/src/man/man3cpc/cpc.3cpc20
-rw-r--r--usr/src/man/man3lib/libcpc.3lib13
-rw-r--r--usr/src/pkg/manifests/diagnostic-cpu-counters.mf1
-rw-r--r--usr/src/tools/cpcgen/cpcgen.c811
-rw-r--r--usr/src/uts/i86pc/os/cpuid.c7
-rw-r--r--usr/src/uts/intel/ia32/os/cpc_subr.c7
-rw-r--r--usr/src/uts/intel/opteron_pcbe/Makefile32
-rw-r--r--usr/src/uts/intel/pcbe/opteron_pcbe.c270
-rw-r--r--usr/src/uts/intel/pcbe/opteron_pcbe_table.h105
-rw-r--r--usr/src/uts/intel/sys/x86_archext.h7
15 files changed, 2147 insertions, 216 deletions
diff --git a/exception_lists/copyright b/exception_lists/copyright
index ab3be84044..dbefcb0eac 100644
--- a/exception_lists/copyright
+++ b/exception_lists/copyright
@@ -97,6 +97,7 @@ usr/src/common/bzip2/bzlib_private.h
usr/src/common/bzip2/huffman.c
usr/src/common/ficl/*
usr/src/contrib/*
+usr/src/data/amdpmc/*
usr/src/data/hwdata/THIRDPARTYLICENSE.efifixes.descrip
usr/src/data/hwdata/THIRDPARTYLICENSE.efifixes.tmpl
usr/src/data/hwdata/pci.ids
diff --git a/usr/src/data/Makefile b/usr/src/data/Makefile
index 50d941aed2..49e3a1cf82 100644
--- a/usr/src/data/Makefile
+++ b/usr/src/data/Makefile
@@ -11,6 +11,7 @@
#
# Copyright 2017 Nexenta Systems, Inc.
+# Copyright 2019 Joyent, Inc.
#
COMMON_SUBDIRS= consfonts \
@@ -19,11 +20,11 @@ COMMON_SUBDIRS= consfonts \
terminfo \
zoneinfo
-i386_SUBDIRS= perfmon
+i386_SUBDIRS= amdpmc perfmon
SUBDIRS = $(COMMON_SUBDIRS) $($(MACH)_SUBDIRS)
-MSGSUBDIRS= zoneinfo
+MSGSUBDIRS= zoneinfo
all := TARGET=all
clean := TARGET=clean
diff --git a/usr/src/data/amdpmc/Makefile b/usr/src/data/amdpmc/Makefile
new file mode 100644
index 0000000000..a04579bf10
--- /dev/null
+++ b/usr/src/data/amdpmc/Makefile
@@ -0,0 +1,30 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2019 Joyent, Inc.
+#
+
+include $(SRC)/data/Makefile.data
+
+ROOTMAN3CPC = $(ROOT)/usr/share/man/man3cpc
+
+all: $(ROOTMAN3CPC)
+ $(CPCGEN) -a -m -d $(SRC)/data/amdpmc -o $(ROOTMAN3CPC)
+
+install: all
+
+clean:
+
+$(ROOTMAN3CPC):
+ $(INS.dir)
+
+include $(SRC)/data/Makefile.targ
diff --git a/usr/src/data/amdpmc/README b/usr/src/data/amdpmc/README
new file mode 100644
index 0000000000..e7c4783950
--- /dev/null
+++ b/usr/src/data/amdpmc/README
@@ -0,0 +1,11 @@
+AMD Performance Monitor Counters
+
+This directory contains structured forms of the AMD Performance Monitor
+Counters. These data files are created based on information in AMD's
+documentation. This data is transformed into manual pages and C files
+that are used to implement the kernel performance monitoring interfaces.
+For more information, see the cpcgen tool. This data is derived from the
+following documents:
+
+FAMILY DOCUMENT
+17h OSRR for AMD Family 17h processors, Models 00h-2Fh (56255 Rev 3.03 - July, 2018)
diff --git a/usr/src/data/amdpmc/f17h_core.json b/usr/src/data/amdpmc/f17h_core.json
new file mode 100644
index 0000000000..f200ee93a3
--- /dev/null
+++ b/usr/src/data/amdpmc/f17h_core.json
@@ -0,0 +1,1043 @@
+[
+{
+ "mnemonic": "Core::X86::Pmc::Core::FpuPipeAssignment",
+ "name": "FpuPipeAssignment",
+ "code": "0x000",
+ "summary": "FPU Pipe Assignment",
+ "description": "The number of operations (uOps) and dual-pipeuOps dispatched to each of the 4 FPU execution pipelines. This event reflects how busy the FPU pipelines are and may be used for workload characterization. This includes all operations performed by x87, MMXTM, and SSE instructions, including moves. Each increment represents a one-cycle dispatch event. This event is a speculative event. (See Core::X86::Pmc::Core::ExRetMmxFpInstr). Since this event includes non- numeric operations it is not suitable for measuring MFLOPS.",
+ "units": [ {
+ "name": "Dual3",
+ "bit": 7,
+ "rw": "Read-only",
+ "description": "Total number multi-pipe uOps assigned to Pipe 3"
+ }, {
+ "name": "Dual2",
+ "bit": 6,
+ "rw": "Read-only",
+ "description": "Total number multi-pipe uOps assigned to Pipe 2"
+ }, {
+ "name": "Dual1",
+ "bit": 5,
+ "rw": "Read-only",
+ "description": "Total number multi-pipe uOps assigned to Pipe 1"
+ }, {
+ "name": "Dual0",
+ "bit": 4,
+ "rw": "Read-only",
+ "description": "Total number multi-pipe uOps assigned to Pipe 0"
+ }, {
+ "name": "Total3",
+ "bit": 3,
+ "rw": "Read-only",
+ "description": "Total number uOps assigned to Pipe 3"
+ }, {
+ "name": "Total2",
+ "bit": 2,
+ "rw": "Read-only",
+ "description": "Total number uOps assigned to Pipe 2"
+ }, {
+ "name": "Total1",
+ "bit": 1,
+ "rw": "Read-only",
+ "description": "Total number uOps assigned to Pipe 1"
+ }, {
+ "name": "Total0",
+ "bit": 0,
+ "rw": "Read-only",
+ "description": "Total number uOps assigned to Pipe 0"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::FpSchedEmpty",
+ "name": "FpSchedEmpty",
+ "code": "0x001",
+ "summary": "FP Scheduler Empty",
+ "description": "This is a speculative event. The number of cycles in which the FPU scheduler is empty. Note that some Ops like FP loads bypass the scheduler. Invert this (Core::X86::Msr::PERF_CTL[Inv] == 1) to count cycles in which at least one FPU operation is present in the FPU."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::FpRetx87FpOps",
+ "name": "FpRetx87FpOps",
+ "code": "0x002",
+ "summary": "Retired x87 Floating Point Operations",
+ "description": "The number of x87 floating-point Ops that have retired. The number of events logged per cycle can vary from 0 to 8.",
+ "units": [
+ {
+ "name": "DivSqrROps",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "Divide and square root Ops"
+ }, {
+ "name": "MulOps",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Multiply Ops"
+ }, {
+ "name": "AddSubOps",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": " Add/subtract Ops"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::FpRetSseAvxOps",
+ "name": "FpRetSseAvxOps",
+ "code": "0x003",
+ "summary": "Retired SSE/AVX Operations",
+ "description": "This is a retire-based event. The number of retired SSE/AVX FLOPS. The number of events logged per cycle can vary from 0 to 64. This event can count above 15. See 2.1.11.2 [Large Increment per Cycle Events]",
+ "units": [
+ {
+ "name": "DpMultAddFlops",
+ "bit": 7,
+ "rw": "Read-write",
+ "description": "Double precision multiply-add FLOPS. Multiply-add counts as 2 FLOPS."
+ }, {
+ "name": "DpDivFlops",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "Double precision divide/square root FLOPS."
+ }, {
+ "name": "DpMultFlops",
+ "bit": 5,
+ "rw": "Read-write",
+ "description": "Double precision multiply FLOPS."
+ }, {
+ "name": "DpAddSubFlops",
+ "bit": 4,
+ "rw": "Read-write",
+ "description": "Double precision add/subtract FLOPS."
+ }, {
+ "name": "SpMultAddFlops",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "Single precision multiply-add FLOP. Multiply-add counts as 2 FLOPS."
+ }, {
+ "name": "SpDivFlops",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "Single-precision divide/square root FLOPS"
+ }, {
+ "name": "SpMultFlops",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Single-precision multiply FLOPS"
+ }, {
+ "name": "SpAddSubFlops",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Single-precision add/subtract FLOPS"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::FpNumMovElimScalOp",
+ "name": "FpNumMovElimScalOp",
+ "code": "0x004",
+ "summary": "Number of Move Elimination and Scalar Op Optimization",
+ "description": "This is a dispatch based speculative event, and is useful for measuring the effectiveness of the Move elimination and Scalar code optimization schemes.",
+ "units": [ {
+ "name": "Optimized",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "Number of Scalar Ops optimized"
+ }, {
+ "name": "OptPotential",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "Number of Ops that are candidates for optimization (have Z-bit either set or pass)."
+ }, {
+ "name": "SseMovOpsElim",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Number of SSE Move Ops eliminated"
+ }, {
+ "name": "SseMovOps",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Number of SSE Move Ops"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::FpRetiredSerOps",
+ "name": "FpRetiredSerOps",
+ "code": "0x005",
+ "summary": "Retired Serializing Ops",
+ "description": "The number of serializing Ops retired.",
+ "units": [ {
+ "name": "X87CtrlRet",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "x87 control word mispredict traps due to mispredictions in RC or PC, or changes in mask bits"
+ }, {
+ "name": "X87BotRet",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "x87 bottom-executing uOps retired"
+ }, {
+ "name": "SseCtrlRet",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "SSE control word mispredict traps due to mispredictions in RC, FTZ or DAZ, or changes in mask bits"
+ }, {
+ "name": "SseBotRet",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "SSE bottom-executing uOps retired"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsBadStatus2",
+ "name": "LsBadStatus2",
+ "code": "0x024",
+ "summary": "Bad Status 2",
+ "description": "Store To Load Interlock (STLI) are loads that were unable to complete because of a possible match with an older store, and the older store could not do STLF for some reason. There are a number of reasons why this occurs, and this perfmon organizes them into three major groups.",
+ "units": [ {
+ "name": "StlfNoData",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "The load is capable of forwarding from an older store (i.e. the address match/overlap between the load and the older store) was good and everything works from an address perspective, but the store's data has not been produced by EX or FP yet so it can't be forwarded."
+ }, {
+ "name": "StliOther",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "All the other reasons. The most common among these is that there is only a partial overlap between the store and the load, for example there's an 8B store to address A and a 16B load starting at address A. STLF can't be performed in this case because only some of the load's data is coming fromthe store, so the load gets StliOther. Another StliOther case is if the load hits a non-cacheable store that's sitting in the non-cacheable buffers (WCBs)."
+ }, {
+ "name": "StliNoState",
+ "bit": 0,
+ "rw": "Rewad-write",
+ "description": "The STLF is validated using DC way instead of an address compare. The store that wants to STLF is required to be a DC hit and have a valid DC way. The STLF candidate store is chosen based on address bits 11:0 overlap, and the DC way of that store is compared to the way of the load. If the store is in a DC miss state, then it doesn't have a valid DC way and so cannot validate STLF. The load gets StliNoState and can't complete. Read-write"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsLocks",
+ "name": "LsLocks",
+ "code": "0x025",
+ "summary": "Locks",
+ "unit_mode": "or",
+ "units": [ {
+ "name": "SpecLockMapCommit",
+ "bit": 3,
+ "rw": "Read-write"
+ }, {
+ "name": "SpecLock",
+ "bit": 2,
+ "rw": "Read-write"
+ }, {
+ "name": "NonSpecLock",
+ "bit": 1,
+ "rw": "Read-write"
+ }, {
+ "name": "BusLock",
+ "bit": 0,
+ "rw": "Read-write"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsRetClClush",
+ "name": "LsRetClClush",
+ "code": "0x026",
+ "summary": "Retired CLFLUSH Instructions",
+ "description": "The number of retired CLFLUSH instructions. This is a non-speculative event."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsRetCpuid",
+ "name": "LsRetCpuid",
+ "code": "0x027",
+ "summary": "Retired CPUID Instructions",
+ "description": "The number of CPUID instructions retired."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsDispatch",
+ "name": "LsDispatch",
+ "code": "0x029",
+ "summary": "LS Dispatch",
+ "description": "Counts the number of operations dispatched to the LS unit.",
+ "unit_mode": "add",
+ "units": [ {
+ "name": "LdStDispatch",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "Load-op-Stores"
+ }, {
+ "name": "StoreDispatch",
+ "bit": 1,
+ "rw": "Read-write"
+ }, {
+ "name": "LdDispatch",
+ "bit": 0,
+ "rw": "Read-write"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsSmiRx",
+ "name": "LsSmiRx",
+ "code": "0x02B",
+ "summary": "SMIs Received",
+ "description": "Counts the number of SMIs received."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsSTLF",
+ "name": "LsSTLF",
+ "code": "0x035",
+ "summary": "Store to Load Forward",
+ "description": "Number of STLF hits."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsStCommitCancel2",
+ "name": "LsStCommitCancel2",
+ "code": "0x037",
+ "summary": "Store Commit Cancels 2",
+ "units": [ {
+ "name": "StCommitCancelWcbFull",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "A non-cacheable store and the non-cacheable commit buffer is full."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsDcAccesses",
+ "name": "LsDcAccesses",
+ "code": "0x040",
+ "summary": "Data Cache Accesses",
+ "description": "The number of accesses to the data cache for load and store references. This may include certain microcode scratchpad accesses, although these are generally rare. Each increment represents an eight-byte access, although the instruction may only be accessing a portion of that. This event is a speculative event."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsRefillsFromSys",
+ "name": "LsRefillsFromSys",
+ "code": "0x043",
+ "summary": "Data Cache Refills from System",
+ "description": "Demand Data Cache Fills by Data Source.",
+ "units": [ {
+ "name": "LS_MABRESP_RMT_DRAM",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "DRAM or IO from different die."
+ }, {
+ "name": "LS_MABRESP_RMT_CACHE",
+ "bit": 4,
+ "rw": "Read-write",
+ "description": "Hit in cache; Remote CCX and the address's Home Node is on a different die."
+ }, {
+ "name": "LS_MABRESP_LCL_DRAM",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "DRAM or IO from this thread's die."
+ }, {
+ "name": "LS_MABRESP_LCL_CACHE",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Hit in cache; local CCX (not Local L2), or Remote CCX and the address's Home Node is on this thread's die."
+ }, {
+ "name": "MABRESP_LCL_L2",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Local L2 hit."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsL1DTlbMiss",
+ "name": "LsL1DTlbMiss",
+ "code": "0x045",
+ "summary": "L1 DTLB Miss",
+ "units": [ {
+ "name": "TlbReload1GL2Miss",
+ "bit": 7,
+ "rw": "Read-write"
+ }, {
+ "name": "TlbReload2ML2Miss",
+ "bit": 6,
+ "rw": "Read-write"
+ }, {
+ "name": "TlbReload32KL2Miss",
+ "bit": 5,
+ "rw": "Read-write"
+ }, {
+ "name": "TlbReload4KL2Miss",
+ "bit": 4,
+ "rw": "Read-write"
+ }, {
+ "name": "TlbReload1GL2Hit",
+ "bit": 3,
+ "rw": "Read-write"
+ }, {
+ "name": "TlbReload2ML2Hit",
+ "bit": 2,
+ "rw": "Read-write"
+ }, {
+ "name": "TlbReload32KL2Hit",
+ "bit": 1,
+ "rw": "Read-write"
+ }, {
+ "name": "TlbReload4KL2Hit",
+ "bit": 0,
+ "rw": "Read-write"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsTablewalker",
+ "name": "LsTablewalker",
+ "code": "0x046",
+ "summary": "Tablewalker allocation",
+ "units": [ {
+ "name": "PerfMonTablewalkAllocIside1",
+ "bit": 3,
+ "rw": "Read-write"
+ }, {
+ "name": "PerfMonTablewalkAllocIside0",
+ "bit": 2,
+ "rw": "Read-write"
+ }, {
+ "name": "PerfMonTablewalkAllocDside1",
+ "bit": 1,
+ "rw": "Read-write"
+ }, {
+ "name": "PerfMonTablewalkAllocDside0",
+ "bit": 0,
+ "rw": "Read-write"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsMisalAccesses",
+ "name": "LsMisalAccesses",
+ "code": "0x047",
+ "summary": "Misaligned loads"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsPrefInstrDisp",
+ "name": "LsPrefInstrDisp",
+ "code": "0x04B",
+ "summary": "Prefetch Instructions Dispatched",
+ "description": "Software Prefetch Instructions Dispatched.",
+ "units": [ {
+ "name": "PrefetchNTA",
+ "bit": 2,
+ "rw": "Read-write"
+ }, {
+ "name": "StorePrefetchW",
+ "bit": 1,
+ "rw": "Read-write"
+ }, {
+ "name": "LoadPrefetchW",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Prefetch, Prefetch_T0_T1_T2"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsInefSwPref",
+ "name": "LsInefSwPref",
+ "code": "0x052",
+ "summary": "Ineffective Software Prefetchs",
+ "description": "The number of software prefetches that did not fetch data outside of the processor core.",
+ "units": [ {
+ "name": "MabMchCnt",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Software PREFETCH instruction saw a match on an already-allocated miss request buffer."
+ }, {
+ "name": "DataPipeSwPfDcHit",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Software PREFETCH instruction saw a DC hit."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsSwPfDcFills",
+ "name": "LsSwPfDcFills",
+ "code": "0x059",
+ "summary": "Software Prefetch Data Cache Fills",
+ "description": "Software Prefetch Data Cache Fills by Data Source",
+ "units": [ {
+ "name": "LS_MABRESP_RMT_DRAM",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "DRAM or IO from different die."
+ }, {
+ "name": "LS_MABRESP_RMT_CACHE",
+ "bit": 4,
+ "rw": "Read-write",
+ "description": "Hit in cache; Remote CCX and the address's Home Node is on a different die."
+ }, {
+ "name": "LS_MABRESP_LCL_DRAM",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "DRAM or IO from this thread's die."
+ }, {
+ "name": "LS_MABRESP_LCL_CACHE",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Hit in cache; local CCX (not Local L2), or Remote CCX and the address's Home Node is on this thread's die."
+ }, {
+ "name": "MABRESP_LCL_L2",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Local L2 hit."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsHwPfDcFills",
+ "name": "LsHwPfDcFills",
+ "code": "0x05A",
+ "summary": "Hardware Prefetch Data Cache Fills",
+ "description": "Hardware Prefetch Data Cache Fills by Data Source",
+ "units": [ {
+ "name": "LS_MABRESP_RMT_DRAM",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "DRAM or IO from different die."
+ }, {
+ "name": "LS_MABRESP_RMT_CACHE",
+ "bit": 4,
+ "rw": "Read-write",
+ "description": "Hit in cache; Remote CCX and the address's Home Node is on a different die."
+ }, {
+ "name": "LS_MABRESP_LCL_DRAM",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "DRAM or IO from this thread's die."
+ }, {
+ "name": "LS_MABRESP_LCL_CACHE",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Hit in cache; local CCX (not Local L2), or Remote CCX and the address's Home Node is on this thread's die."
+ }, {
+ "name": "MABRESP_LCL_L2",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Local L2 hit."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsTwDcFills",
+ "name": "LsTwDcFills",
+ "code": "0x05B",
+ "summary": "Table Walker Data Cache Fills by Data Source",
+ "units": [ {
+ "name": "LS_MABRESP_RMT_DRAM",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "DRAM or IO from different die."
+ }, {
+ "name": "LS_MABRESP_RMT_CACHE",
+ "bit": 4,
+ "rw": "Read-write",
+ "description": "Hit in cache; Remote CCX and the address's Home Node is on a different die."
+ }, {
+ "name": "LS_MABRESP_LCL_DRAM",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "DRAM or IO from this thread's die."
+ }, {
+ "name": "LS_MABRESP_LCL_CACHE",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Hit in cache; local CCX (not Local L2), or Remote CCX and the address's Home Node is on this thread's die."
+ }, {
+ "name": "MABRESP_LCL_L2",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Local L2 hit."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::LsNotHaltedCyc",
+ "name": "LsNotHaltedCyc",
+ "code": "0x076",
+ "summary": "Cycles not in Halt"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::IcFw32",
+ "name": "IcFw32",
+ "code": "0x080",
+ "summary": "32 Byte Instruction Cache Fetch",
+ "description": "The number of 32B fetch windows transferred from IC pipe to DE instruction decoder (includes non-cacheable and cacheable fill responses)."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::IcFw32Miss",
+ "name": "IcFw32Miss",
+ "code": "0x081",
+ "summary": "32 Byte Instruction Cache Misses",
+ "description": "The number of 32B fetch windows tried to read the L1 IC and missed in the full tag."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::IcCacheFillL2",
+ "name": "IcCacheFillL2",
+ "code": "0x082",
+ "summary": "Instruction Cache Refills from L2",
+ "description": "The number of 64 byte instruction cache line was fulfilled from the L2 cache."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::IcCacheFillSys",
+ "name": "IcCacheFillSys",
+ "code": "0x083",
+ "summary": "Instruction Cache Refills from System",
+ "description": "The number of 64 byte instruction cache line fulfilled from system memory or another cache."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::BpL1TlbMissL2Hit",
+ "name": "BpL1TlbMissL2Hit",
+ "code": "0x084",
+ "summary": "L1 ITLB Miss, L2 ITLB Hit",
+ "description": "The number of instruction fetches that miss in the L1 ITLB but hit in the L2 ITLB."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::BpL1TlbMissL2Miss",
+ "name": "BpL1TlbMissL2Miss",
+ "code": "0x085",
+ "summary": "L1 ITLB Miss, L2 ITLB Miss",
+ "description": "The number of instruction fetches that miss in both the L1 and L2 TLBs"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::IcFetchStall",
+ "name": "IcFetchStall",
+ "code": "0x087",
+ "summary": "Instruction Pipe Stall",
+ "units": [ {
+ "name": "IcStallAny",
+ "bit": 2,
+ "rw": "Read-write ",
+ "description": "Instruction Cache pipeline was stalled during this clock cycle for any reason."
+ }, {
+ "name": "IcStallDqEmpty",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Instruction Cache pipeline was stalled during this clock cycle due to upstream not providing fetch addresses quickly."
+ }, {
+ "name": "IcStallBackPressure",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Instruction Cache pipeline was stalled during this clock cycle due to downstream queues being full."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::BpL1BTBCorrect",
+ "name": "BpL1BTBCorrect",
+ "code": "0x08A",
+ "summary": "L1 BTB Correction"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::BpL2BTBCorrect",
+ "name": "BpL2BTBCorrect",
+ "code": "0x08B",
+ "summary": "L2 BTB Correction"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::IcCacheInval",
+ "name": "IcCacheInval",
+ "code": "0x08C",
+ "summary": "Instruction Cache Lines Invalidated",
+ "description": "The number of instruction cache lines invalidated. A non-SMC event is CMC (cross modifying code), either from the other thread of the core or another core.",
+ "units": [ {
+ "name": "L2InvalidatingProbe",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "IC line invalidated due to L2 invalidating probe (external or LS)."
+ }, {
+ "name": "FillInvalidated",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "IC line invalidated due to overwriting fill response."
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::BpTlbRel",
+ "name": "BpTlbRel",
+ "code": "0x099",
+ "summary": "ITLB Reloads",
+ "description": "The number of ITLB reload requests."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::IcOcModeSwitch",
+ "name": "IcOcModeSwitch",
+ "code": "0x28A",
+ "summary": "OC Mode Switch",
+ "units": [ {
+ "name": "OcIcModeSwitch",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "OC to IC mode switch"
+ }, {
+ "name": "IcOcModeSwitch",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "IC to OC mode switch"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::DeDisDispatchTokenStalls0",
+ "name": "DeDisDispatchTokenStalls0",
+ "code": "0x0AF",
+ "summary": "Dynamic Tokens Dispatch Stall Cycles 0",
+ "description": "Cycles where a dispatch group is valid but does not get dispatched due to a token stall.",
+ "units": [ {
+ "name": "RetireTokenStall",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "RETIRE Tokens unavailable"
+ }, {
+ "name": "AGSQTokenStall",
+ "bit": 5,
+ "rw": "Read-write",
+ "description": "AGSQ Tokens unavailable"
+ }, {
+ "name": "ALUTokenStall",
+ "bit": 4,
+ "rw": "Read-write",
+ "description": "ALU tokens total unavailable"
+ }, {
+ "name": "ALSQ3_0_TokenStall",
+ "bit": 3,
+ "rw": "Read-write"
+ }, {
+ "name": "ALSQ3TokenStall",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "ALSQ 3 Tokens unavailable"
+ }, {
+ "name": "ALSQ2TokenStall",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "ALSQ 2 Tokens unavailable"
+ }, {
+ "name": "ALSQ1TokenStall",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "ALSQ 1 Tokens unavailable"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetInstr",
+ "name": "ExRetInstr",
+ "code": "0x0C0",
+ "summary": "Retired Instructions"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetCops",
+ "name": "ExRetCops",
+ "code": "0x0C1",
+ "summary": "Retired Uops",
+ "description": "The number of uOps retired. This includes all processor activity (instructions, exceptions, interrupts, microcode assists, etc.). The number of events logged per cycle can vary from 0 to 4."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetBrn",
+ "name": "ExRetBrn",
+ "code": "0x0C2",
+ "summary": "Retired Branch Instructions",
+ "description": "The number of branch instructions retired. This includes all types of architectural control flow changes, including exceptions and interrupts."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetBrnMisp",
+ "name": "ExRetBrnMisp",
+ "code": "0x0C3",
+ "summary": "Retired Branch Instructions Mispredicted",
+ "description": "The number of branch instructions retired, of any type, that were not correctly predicted. This includes those for which prediction is not attempted (far control transfers, exceptions and interrupts)."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetBrnTkn",
+ "name": "ExRetBrnTkn",
+ "code": "0x0C4",
+ "summary": "Retired Taken Branch Instructions",
+ "description": "The number of taken branches that were retired. This includes all types of architectural control flow changes, including exceptions and interrupts."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetBrnTknMisp",
+ "name": "ExRetBrnTknMisp",
+ "code": "0x0C5",
+ "summary": "Retired Taken Branch Instructions Mispredicted",
+ "description": "The number of retired taken branch instructions that were mispredicted."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetBrnFar",
+ "name": "ExRetBrnFar",
+ "code": "0x0C6",
+ "summary": "Retired Far Control Transfers",
+ "description": "The number of far control transfers retired including far call/jump/return, IRET, SYSCALL and SYSRET, plus exceptions and interrupts. Far control transfers are not subject to branch prediction."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetBrnResync",
+ "name": "ExRetBrnResync",
+ "code": "0x0C7",
+ "summary": "Retired Branch Resyncs",
+ "description": "The number of resync branches. These reflect pipeline restarts due to certain microcode assists and events such as writes to the active instruction stream, among other things. Each occurrence reflects a restart penalty similar to a branch mispredict. This is relatively rare."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetNearRet",
+ "name": "ExRetNearRet",
+ "code": "0x0C8",
+ "summary": "Retired Near Returns",
+ "description": "The number of near return instructions (RET or RET Iw) retired."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetNearRetMispred",
+ "name": "ExRetNearRetMispred",
+ "code": "0x0C9",
+ "summary": "Retired Near Returns Mispredicted",
+ "description": "The number of near returns retired that were not correctly predicted by the return address predictor. Each such mispredict incurs the same penalty as a mispredicted conditional branch instruction."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetBrnIndMisp",
+ "name": "ExRetBrnIndMisp",
+ "code": "0x0CA",
+ "summary": "Retired Indirect Branch Instructions Mispredicted"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetMmxFpInstr",
+ "name": "ExRetMmxFpInstr",
+ "code": "0x0CB",
+ "summary": "Retired MMXTM/FP Instructions",
+ "description": "The number of MMX, SSE or x87 instructions retired. The UnitMask allows the selection of the individual classes of instructions as given in the table. Each increment represents one complete instruction. Since this event includes non- numeric instructions it is not suitable for measuring MFLOPS.",
+ "units": [ {
+ "name": "SseInstr",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "SSE instructions (SSE, SSE2, SSE3, SSSE3, SSE4A, SSE41, SSE42, AVX)."
+ }, {
+ "name": "MmxInstr",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "MMX instructions."
+ }, {
+ "name": "X87Instr",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "x87 instructions"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExRetCond",
+ "name": "ExRetCond",
+ "code": "0x0D1",
+ "summary": "Retired Conditional Branch Instructions"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExDivBusy",
+ "name": "ExDivBusy",
+ "code": "0x0D3",
+ "summary": "Div Cycles Busy count"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExDivCount",
+ "name": "ExDivCount",
+ "code": "0x0D4",
+ "summary": "Div Op Count"
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::ExTaggedIbsOps",
+ "name": "ExTaggedIbsOps",
+ "code": "0x1CF",
+ "summary": "Tagged IBS Ops",
+ "units": [ {
+ "name": "IbsCountRollover",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "Number of times an op could not be tagged by IBS because of a previous tagged op that has not retired."
+ }, {
+ "name": "IbsTaggedOpsRet",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Number of Ops tagged by IBS that retired"
+ }, {
+ "name": "IbsTaggedOps",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Number of Ops tagged by IBS"
+ } ]
+}, {
+ "mnemonic": "Core::X86::Pmc::Core::ExRetFusBrnchInst",
+ "name": "ExRetFusBrnchInst",
+ "code": "0x1D0",
+ "summary": "Retired Fused Branch Instructions",
+ "description": "The number of fused retired branch instructions retired per cycle. The number of events logged per cycle can vary from 0 to 3."
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::L2RequestG1",
+ "name": "L2RequestG1",
+ "code": "0x060",
+ "summary": "Requests to L2 Group1",
+ "units": [ {
+ "name": "RdBlkL",
+ "bit": 7,
+ "rw": "Read-write"
+ }, {
+ "name": "RdBlkX",
+ "bit": 6,
+ "rw": "Read-write"
+ }, {
+ "name": "LsRdBlkC_S",
+ "bit": 5,
+ "rw": "Read-write"
+ }, {
+ "name": "CacheableIcRead",
+ "bit": 4,
+ "rw": "Read-write"
+ }, {
+ "name": "ChangeToX",
+ "bit": 3,
+ "rw": "Read-write"
+ }, {
+ "name": "PrefetchL2",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "Assume core should also count these and allow the breakdown between H/W vs. S/W and LS vs. IC."
+ }, {
+ "name": "L2HwPf",
+ "bit": 1,
+ "rw": "Read-write"
+ }, {
+ "name": "OtherRequests",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Events covered by Core::X86::Pmc::Core::L2RequestG2."
+ } ]
+}, {
+ "mnemonic": "Core::X86::Pmc::Core::L2RequestG2",
+ "name": "L2RequestG2",
+ "code": "0x061",
+ "summary": "Requests to L2 Group2",
+ "description": "Multi-events in that LS and IF requests can be received simultaneous.",
+ "units": [ {
+ "name": "Group1",
+ "bit": 7,
+ "rw": "Read-write",
+ "description": "All Group 1 commands not in unit0."
+ }, {
+ "name": "LsRdSized",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "RdSized, RdSized32, RdSized64."
+ }, {
+ "name": "LsRdSizedNC",
+ "bit": 5,
+ "rw": "Read-write",
+ "description": "RdSizedNC, RdSized32NC, RdSized64NC."
+ }, {
+ "name": "IcRdSized",
+ "bit": 4,
+ "rw": "Read-write"
+ }, {
+ "name": "IcRdSizedNC",
+ "bit": 3,
+ "rw": "Read-write"
+ }, {
+ "name": "SmcInval",
+ "bit": 2,
+ "rw": "Read-write"
+ }, {
+ "name": "BusLocksOriginator",
+ "bit": 1,
+ "rw": "Read-write"
+ }, {
+ "name": "BusLocksResponses",
+ "bit": 0,
+ "rw": "Read-write"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::L2Latancy",
+ "name": "L2Latancy",
+ "code": "0x062",
+ "summary": "L2 Latency",
+ "description": "Total cycles spent waiting for L2 fills to complete from L3 or memory, divided by four. This may be used to calculate average latency by multiplying this count by four and then dividing by the total number of L2 fills (unit mask Core::X86::Pmc::Core::L2RequestG1 == FEh). Event counts are for both threads. To calculate average latency, the number of fills from both threads must be used.",
+ "units": [ {
+ "name": "L2CyclesWaitingOnFills",
+ "bit": 0,
+ "rw": "Read-write"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::L2WbcReq",
+ "name": "L2WbcReq",
+ "code": "0x063",
+ "summary": "LS to L2 WBC requests",
+ "units": [ {
+ "name": "WcbWrite",
+ "bit": 6,
+ "rw": "Read-write"
+ }, {
+ "name": "WcbClose",
+ "bit": 5,
+ "rw": "Read-write"
+ }, {
+ "name": "CacheLineFlush",
+ "bit": 4,
+ "rw": "Read-write"
+ }, {
+ "name": "I_LineFlush",
+ "bit": 3,
+ "rw": "Read-write"
+ }, {
+ "name": "ZeroByteStore",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "This becomes WriteNoData at SDP; this count does not include DVM Sync Ops and bus locks which are counted in Core::X86::Pmc::Core::L2RequestG2."
+ }, {
+ "name": "LocalIcClr",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "Local IC Clear"
+ }, {
+ "name": "CLZero",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "Cache Line Zero"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::L2CacheReqStat",
+ "name": "L2CacheReqStat",
+ "code": "0x064",
+ "summary": "Core to L2 Cacheable Request Access Status",
+ "description": "This event does not count accesses to the L2 cache by the L2 prefetcher, but it does count accesses by the L1 prefetcher.",
+ "units": [ {
+ "name": "LsRdBlkCS",
+ "bit": 7,
+ "rw": "Read-write",
+ "description": "LS ReadBlock C/S Hit"
+ }, {
+ "name": "LsRdBlkLHitX",
+ "bit": 6,
+ "rw": "Read-write",
+ "description": "LS Read Block L Hit X"
+ }, {
+ "name": "LsRdBlkLHitS",
+ "bit": 5,
+ "rw": "Read-write",
+ "description": "LsRdBlkL Hit Shared"
+ }, {
+ "name": "LsRdBlkX",
+ "bit": 4,
+ "rw": "Read-write",
+ "description": "LsRdBlkX/ChgToX Hit X. Count RdBlkX finding Shared as a Miss."
+ }, {
+ "name": "LsRdBlkC",
+ "bit": 3,
+ "rw": "Read-write",
+ "description": "LS Read Block C S L X Change to X Miss"
+ }, {
+ "name": "IcFillHitX",
+ "bit": 2,
+ "rw": "Read-write",
+ "description": "IC Fill Hit Exclusive Stale"
+ }, {
+ "name": "IcFillHitS",
+ "bit": 1,
+ "rw": "Read-write",
+ "description": "IC Fill Hit Shared"
+ }, {
+ "name": "IcFillMiss",
+ "bit": 0,
+ "rw": "Read-write",
+ "description": "IC Fill Miss"
+ } ]
+},
+{
+ "mnemonic": "Core::X86::Pmc::Core::L2FillPending",
+ "name": "L2FillPending",
+ "code": "0x06D",
+ "summary": "Cycles with fill pending from L2",
+ "description": "Total cycles spent with one or more fill requests in flight from L2.",
+ "units": [ {
+ "name": "L2FillBusy.",
+ "bit": 0,
+ "rw": "Read-write."
+ } ]
+}
+]
diff --git a/usr/src/man/man3cpc/cpc.3cpc b/usr/src/man/man3cpc/cpc.3cpc
index 9b5edbabb1..d25dc393f8 100644
--- a/usr/src/man/man3cpc/cpc.3cpc
+++ b/usr/src/man/man3cpc/cpc.3cpc
@@ -3,7 +3,7 @@
.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE or http://www.opensolaris.org/os/licensing. 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]
.\" Copyright (c) 2019, Joyent, Inc.
-.Dd January 8, 2019
+.Dd March 25, 2019
.Dt CPC 3CPC
.Os
.Sh NAME
@@ -78,11 +78,10 @@ and
.Xr cpc_walk_generic_events_pic 3CPC
functions allow an application to determine the generic events supported
on the underlying platform.
-.Ss Processor Specific Events
-Manual pages specific to events for recent Intel processors are
-available.
-The following manual pages cover the following Intel processor models
-which are listed in hexadecimal:
+.Ss Intel Processor Specific Events
+The following manual pages provide more detailed information on the
+events available for the specific Intel processor models.
+The covered processor models are listed in hexadecimal.
.Bl -tag -width Xr
.It Xr bdw_de_events 3CPC
Intel Broadwell-DE events; covers model 56h.
@@ -127,6 +126,15 @@ Intel Westmere-EP-SP events; covers model 25h.
.It Xr wsm_ex_events 3CPC
Intel Westmere-EX events; covers model 2fh.
.El
+.Ss AMD Processor Specific Events
+The following manual pages provide more detailed information on the
+events available for the specific AMD processor models.
+The covered processor families are listed in hexadecimal.
+.Bl -tag -width Xr
+.It Xr amd_f17h_events 3CPC
+AMD Family 17h processors, including models 00-2fh.
+Include Ryzen, ThreadRipper, and EPYC branded processors.
+.El
.Ss Using Attributes
Some processors have advanced performance counter capabilities that are
configured with attributes.
diff --git a/usr/src/man/man3lib/libcpc.3lib b/usr/src/man/man3lib/libcpc.3lib
index 5a2159e2d5..d4438e0c8b 100644
--- a/usr/src/man/man3lib/libcpc.3lib
+++ b/usr/src/man/man3lib/libcpc.3lib
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved.
+.\" Copyright 2019 Joyent, Inc.
.\" 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 or http://www.opensolaris.org/os/licensing. 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]
-.TH LIBCPC 3LIB "Oct 8, 2008"
+.TH LIBCPC 3LIB "March 25, 2019"
.SH NAME
libcpc \- CPU performance counter library
.SH SYNOPSIS
@@ -13,12 +14,12 @@ cc [ \fIflag\fR... ] \fIfile\fR... \fB-lcpc\fR [ \fIlibrary\fR... ]
.fi
.SH DESCRIPTION
-.sp
.LP
Functions in this library provide access to CPU performance counters on
-platforms that contain the appropriate hardware.
+platforms that contain the appropriate hardware. A more detailed
+explanation of the library and a list of processor-specific events is
+available in \fBcpc\fR(3CPC).
.SH INTERFACES
-.sp
.LP
The shared object \fBlibcpc.so.1\fR provides the public interfaces defined
below. See \fBIntro\fR(3) for additional information on shared object
@@ -63,7 +64,6 @@ l l .
.TE
.SH FILES
-.sp
.ne 2
.na
\fB\fB/usr/lib/libcpc.so.1\fR\fR
@@ -82,7 +82,6 @@ shared object
.RE
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -97,12 +96,10 @@ MT-Level Safe
.TE
.SH SEE ALSO
-.sp
.LP
\fBcputrack\fR(1), \fBcpustat\fR(1M), \fBIntro\fR(3), \fBcpc\fR(3CPC),
\fBattributes\fR(5), \fBxVM\fR(5)
.SH NOTES
-.sp
.LP
There is no support for access to performance counters in the \fBxVM\fR(5)
environment. The \fB-h\fR operations for \fBcputrack\fR(1) are not enabled.
diff --git a/usr/src/pkg/manifests/diagnostic-cpu-counters.mf b/usr/src/pkg/manifests/diagnostic-cpu-counters.mf
index 0a86a9054c..8f6934cfd3 100644
--- a/usr/src/pkg/manifests/diagnostic-cpu-counters.mf
+++ b/usr/src/pkg/manifests/diagnostic-cpu-counters.mf
@@ -66,6 +66,7 @@ file path=usr/lib/libpctx.so.1
file path=usr/sbin/cpustat mode=0555
file path=usr/share/man/man1/cputrack.1
file path=usr/share/man/man1m/cpustat.1m
+$(i386_ONLY)file path=usr/share/man/man3cpc/amd_f17h_events.3cpc
$(i386_ONLY)file path=usr/share/man/man3cpc/bdw_de_events.3cpc
$(i386_ONLY)file path=usr/share/man/man3cpc/bdw_events.3cpc
$(i386_ONLY)file path=usr/share/man/man3cpc/bdx_events.3cpc
diff --git a/usr/src/tools/cpcgen/cpcgen.c b/usr/src/tools/cpcgen/cpcgen.c
index b006c2f6b4..1e76e10351 100644
--- a/usr/src/tools/cpcgen/cpcgen.c
+++ b/usr/src/tools/cpcgen/cpcgen.c
@@ -14,7 +14,8 @@
*/
/*
- * This file transforms the perfmon data files into C files and manual pages.
+ * This program transforms Intel perfmon and AMD PMC data files into C files and
+ * manual pages.
*/
#include <stdio.h>
@@ -33,12 +34,19 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <dirent.h>
#include <json_nvlist.h>
#define EXIT_USAGE 2
#define CPROC_MAX_STEPPINGS 16
+typedef enum {
+ CPCGEN_MODE_UNKNOWN = 0,
+ CPCGEN_MODE_INTEL,
+ CPCGEN_MODE_AMD
+} cpc_mode_t;
+
typedef struct cpc_proc {
struct cpc_proc *cproc_next;
uint_t cproc_family;
@@ -75,7 +83,7 @@ typedef struct cpc_whitelist {
* so that processors that illumos doesn't support or run on aren't generated
* (generally the Xeon Phi).
*/
-static cpc_whitelist_t cpcgen_whitelist[] = {
+static cpc_whitelist_t cpcgen_intel_whitelist[] = {
/* Nehalem */
{ "NHM-EP", "nhm_ep", CPC_FILE_CORE },
{ "NHM-EX", "nhm_ex", CPC_FILE_CORE },
@@ -121,7 +129,7 @@ typedef struct cpc_papi {
* event. We use the title as opposed to the event codes because those can
* change somewhat arbitrarily between processor generations.
*/
-static cpc_papi_t cpcgen_papi_map[] = {
+static cpc_papi_t cpcgen_intel_papi_map[] = {
{ "CPU_CLK_UNHALTED.THREAD_P", "PAPI_tot_cyc" },
{ "INST_RETIRED.ANY_P", "PAPI_tot_ins" },
{ "BR_INST_RETIRED.ALL_BRANCHES", "PAPI_br_ins" },
@@ -150,22 +158,26 @@ static cpc_papi_t cpcgen_papi_map[] = {
};
typedef struct cpcgen_ops {
+ void (*cgen_op_gather)(const char *, const char *);
+ void (*cgen_op_common)(int);
char *(*cgen_op_name)(cpc_map_t *);
+ boolean_t (*cgen_op_skip)(nvlist_t *, const char *, uint_t);
boolean_t (*cgen_op_file_before)(FILE *, cpc_map_t *);
boolean_t (*cgen_op_file_after)(FILE *, cpc_map_t *);
boolean_t (*cgen_op_event)(FILE *, nvlist_t *, const char *, uint32_t);
} cpcgen_ops_t;
static cpcgen_ops_t cpcgen_ops;
-static const char *cpcgen_mapfile = "/mapfile.csv";
+static const char *cpcgen_intel_mapfile = "/mapfile.csv";
static const char *cpcgen_progname;
static cpc_map_t *cpcgen_maps;
+static cpc_mode_t cpcgen_mode = CPCGEN_MODE_UNKNOWN;
/*
* Constants used for generating data.
*/
/* BEGIN CSTYLED */
-static const char *cpcgen_cfile_header = ""
+static const char *cpcgen_cfile_intel_header = ""
"/*\n"
" * Copyright (c) 2018, Intel Corporation\n"
" * Copyright (c) 2018, Joyent, Inc\n"
@@ -205,17 +217,17 @@ static const char *cpcgen_cfile_header = ""
"\n";
/* END CSTYLED */
-static const char *cpcgen_cfile_table_start = ""
+static const char *cpcgen_cfile_intel_table_start = ""
"#include <core_pcbe_table.h>\n"
"\n"
"const struct events_table_t pcbe_core_events_%s[] = {\n";
-static const char *cpcgen_cfile_table_end = ""
+static const char *cpcgen_cfile_intel_table_end = ""
"\t{ NT_END, 0, 0, \"\" }\n"
"};\n";
/* BEGIN CSTYLED */
-static const char *cpcgen_manual_header = ""
+static const char *cpcgen_manual_intel_intel_header = ""
".\\\" Copyright (c) 2018, Intel Corporation \n"
".\\\" Copyright (c) 2018, Joyent, Inc.\n"
".\\\" All rights reserved.\n"
@@ -269,19 +281,97 @@ static const char *cpcgen_manual_header = ""
".Bl -bullet\n";
/* END CSTYLED */
-static const char *cpcgen_manual_data = ""
+static const char *cpcgen_manual_intel_data = ""
".El\n"
".Pp\n"
"The following events are supported:\n"
".Bl -tag -width Sy\n";
-static const char *cpcgen_manual_trailer = ""
+static const char *cpcgen_manual_intel_trailer = ""
".El\n"
".Sh SEE ALSO\n"
".Xr cpc 3CPC\n"
".Pp\n"
".Lk https://download.01.org/perfmon/index/";
+static const char *cpcgen_cfile_cddl_header = ""
+"/*\n"
+" * This file and its contents are supplied under the terms of the\n"
+" * Common Development and Distribution License (\"CDDL\"), version 1.0.\n"
+" * You may only use this file in accordance with the terms of version\n"
+" * 1.0 of the CDDL.\n"
+" *\n"
+" * A full copy of the text of the CDDL should have accompanied this\n"
+" * source. A copy of the CDDL is also available via the Internet at\n"
+" * http://www.illumos.org/license/CDDL.\n"
+" */\n"
+"\n"
+"/*\n"
+" * Copyright 2019 Joyent, Inc\n"
+" */\n"
+"\n"
+"/*\n"
+" * This file was automatically generated by cpcgen.\n"
+" */\n"
+"\n"
+"/*\n"
+" * Do not modify this file. Your changes will be lost!\n"
+" */\n"
+"\n";
+
+static const char *cpcgen_manual_amd_header = ""
+".\\\" This file was automatically generated by cpcgen from the data file\n"
+".\\\" data/amdpmc/%s\n"
+".\\\"\n"
+".\\\" Do not modify this file. Your changes will be lost!\n"
+".\\\"\n"
+".\\\" We would like to thank AMD for providing the PMC data for use in\n"
+".\\\" our manual pages.\n"
+".Dd March 25, 2019\n"
+".Dt AMD_%s_EVENTS 3CPC\n"
+".Os\n"
+".Sh NAME\n"
+".Nm amd_%s_events\n"
+".Nd AMD family %s processor performance monitoring events\n"
+".Sh DESCRIPTION\n"
+"This manual page describes events specfic to AMD family %s processors.\n"
+"For more information, please consult the appropriate AMD BIOS and Kernel\n"
+"Developer's guide or Open-Source Register Reference manual.\n"
+".Pp\n"
+"Each of the events listed below includes the AMD mnemonic which matches\n"
+"the name found in the AMD manual and a brief summary of the event.\n"
+"If available, a more detailed description of the event follows and then\n"
+"any additional unit values that modify the event.\n"
+"Each unit can be combined to create a new event in the system by placing\n"
+"the '.' character between the event name and the unit name.\n"
+".Pp\n"
+"The following events are supported:\n"
+".Bl -tag -width Sy\n";
+
+static const char *cpcgen_manual_amd_trailer = ""
+".El\n"
+".Sh SEE ALSO\n"
+".Xr cpc 3CPC\n";
+
+static const char *cpcgen_cfile_amd_header = ""
+"/*\n"
+" * This file was automatically generated by cpcgen from the data file\n"
+" * data/perfmon%s\n"
+" *\n"
+" * Do not modify this file. Your changes will be lost!\n"
+" */\n"
+"\n";
+
+static const char *cpcgen_cfile_amd_table_start = ""
+"#include <opteron_pcbe_table.h>\n"
+"#include <sys/null.h>\n"
+"\n"
+"const amd_event_t opteron_pcbe_%s_events[] = {\n";
+
+static const char *cpcgen_cfile_amd_table_end = ""
+"\t{ NULL, 0, 0 }\n"
+"};\n";
+
static cpc_map_t *
cpcgen_map_lookup(const char *path)
{
@@ -496,14 +586,14 @@ cpcgen_use_arch(const char *path, cpc_type_t type, const char *platform)
len = slash - path - 1;
assert(len > 0);
- for (i = 0; cpcgen_whitelist[i].cwhite_short != NULL; i++) {
+ for (i = 0; cpcgen_intel_whitelist[i].cwhite_short != NULL; i++) {
if (platform != NULL && strcasecmp(platform,
- cpcgen_whitelist[i].cwhite_short) != 0)
+ cpcgen_intel_whitelist[i].cwhite_short) != 0)
continue;
- if (strncmp(path + 1, cpcgen_whitelist[i].cwhite_short,
+ if (strncmp(path + 1, cpcgen_intel_whitelist[i].cwhite_short,
len) == 0 &&
- (cpcgen_whitelist[i].cwhite_mask & type) == type) {
- return (cpcgen_whitelist[i].cwhite_human);
+ (cpcgen_intel_whitelist[i].cwhite_mask & type) == type) {
+ return (cpcgen_intel_whitelist[i].cwhite_human);
}
}
@@ -511,11 +601,113 @@ cpcgen_use_arch(const char *path, cpc_type_t type, const char *platform)
}
/*
+ * Determine which CPU Vendor we're transmuting data from.
+ */
+static void
+cpcgen_determine_vendor(const char *datadir)
+{
+ char *mappath;
+ struct stat st;
+
+ if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) {
+ err(EXIT_FAILURE, "failed to construct path to mapfile");
+ }
+
+ if (stat(mappath, &st) == 0) {
+ cpcgen_mode = CPCGEN_MODE_INTEL;
+ } else {
+ if (errno != ENOENT) {
+ err(EXIT_FAILURE, "stat(2) of %s failed unexpectedly");
+ }
+
+ cpcgen_mode = CPCGEN_MODE_AMD;
+ }
+
+ free(mappath);
+}
+
+/*
+ * Read in all the data files that exist for AMD.
+ *
+ * Our family names for AMD systems are based on the family and type so a given
+ * name will look like f17h_core.json.
+ */
+static void
+cpcgen_read_amd(const char *datadir, const char *platform)
+{
+ DIR *dir;
+ struct dirent *d;
+ const char *suffix = ".json";
+ const size_t slen = strlen(suffix);
+
+ if ((dir = opendir(datadir)) == NULL) {
+ err(EXIT_FAILURE, "failed to open directory %s", datadir);
+ }
+
+ while ((d = readdir(dir)) != NULL) {
+ char *name, *c;
+ cpc_map_t *map;
+ nvlist_t *parsed;
+
+ if ((name = strdup(d->d_name)) == NULL) {
+ errx(EXIT_FAILURE, "ran out of memory duplicating "
+ "name %s", d->d_name);
+ }
+ c = strstr(name, suffix);
+
+ if (c == NULL) {
+ free(name);
+ continue;
+ }
+
+ if (*(c + slen) != '\0') {
+ free(name);
+ continue;
+ }
+
+ *c = '\0';
+ c = strchr(name, '_');
+ if (c == NULL) {
+ free(name);
+ continue;
+ }
+ *c = '\0';
+ c++;
+ if (strcmp(c, "core") != 0) {
+ errx(EXIT_FAILURE, "unexpected AMD JSON file name: %s",
+ d->d_name);
+ }
+
+ if (platform != NULL && strcmp(platform, name) != 0) {
+ free(name);
+ continue;
+ }
+
+ if ((map = calloc(1, sizeof (cpc_map_t))) == NULL) {
+ err(EXIT_FAILURE, "failed to allocate space for cpc "
+ "file");
+ }
+
+ parsed = cpcgen_read_datafile(datadir, d->d_name);
+ if ((map->cmap_path = strdup(d->d_name)) == NULL) {
+ err(EXIT_FAILURE, "failed to duplicate path string");
+ }
+ map->cmap_type = CPC_FILE_CORE;
+ map->cmap_data = parsed;
+ map->cmap_name = name;
+ map->cmap_procs = NULL;
+
+ map->cmap_next = cpcgen_maps;
+ cpcgen_maps = map;
+ }
+}
+
+/*
* Read in the mapfile.csv that is used to map between processor families and
* parse this. Each line has a comma separated value.
*/
static void
-cpcgen_read_mapfile(const char *datadir, const char *platform)
+cpcgen_read_intel(const char *datadir, const char *platform)
{
FILE *map;
char *mappath, *last;
@@ -523,7 +715,7 @@ cpcgen_read_mapfile(const char *datadir, const char *platform)
size_t datalen = 0;
uint_t lineno;
- if (asprintf(&mappath, "%s/%s", datadir, cpcgen_mapfile) == -1) {
+ if (asprintf(&mappath, "%s/%s", datadir, cpcgen_intel_mapfile) == -1) {
err(EXIT_FAILURE, "failed to construct path to mapfile");
}
@@ -596,9 +788,10 @@ cpcgen_read_mapfile(const char *datadir, const char *platform)
map->cmap_type = type;
map->cmap_data = parsed;
- map->cmap_next = cpcgen_maps;
map->cmap_name = name;
map->cmap_procs = NULL;
+
+ map->cmap_next = cpcgen_maps;
cpcgen_maps = map;
}
@@ -630,7 +823,7 @@ cpcgen_read_mapfile(const char *datadir, const char *platform)
}
static char *
-cpcgen_manual_name(cpc_map_t *map)
+cpcgen_manual_intel_name(cpc_map_t *map)
{
char *name;
@@ -644,7 +837,7 @@ cpcgen_manual_name(cpc_map_t *map)
}
static boolean_t
-cpcgen_manual_file_before(FILE *f, cpc_map_t *map)
+cpcgen_manual_intel_file_before(FILE *f, cpc_map_t *map)
{
size_t i;
char *upper;
@@ -659,13 +852,14 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map)
upper[i] = toupper(upper[i]);
}
- if (fprintf(f, cpcgen_manual_header, map->cmap_path, upper,
+ if (fprintf(f, cpcgen_manual_intel_intel_header, map->cmap_path, upper,
map->cmap_name) == -1) {
warn("failed to write out manual header for %s",
map->cmap_name);
free(upper);
return (B_FALSE);
}
+ free(upper);
for (proc = map->cmap_procs; proc != NULL; proc = proc->cproc_next) {
if (proc->cproc_nsteps > 0) {
@@ -679,7 +873,6 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map)
warn("failed to write out model "
"information for %s",
map->cmap_name);
- free(upper);
return (B_FALSE);
}
}
@@ -688,17 +881,14 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map)
proc->cproc_family, proc->cproc_model) == -1) {
warn("failed to write out model information "
"for %s", map->cmap_name);
- free(upper);
return (B_FALSE);
}
}
}
- if (fprintf(f, cpcgen_manual_data, map->cmap_path, upper,
- map->cmap_name) == -1) {
+ if (fprintf(f, cpcgen_manual_intel_data) == -1) {
warn("failed to write out manual header for %s",
map->cmap_name);
- free(upper);
return (B_FALSE);
}
@@ -707,9 +897,9 @@ cpcgen_manual_file_before(FILE *f, cpc_map_t *map)
}
static boolean_t
-cpcgen_manual_file_after(FILE *f, cpc_map_t *map)
+cpcgen_manual_intel_file_after(FILE *f, cpc_map_t *map)
{
- if (fprintf(f, cpcgen_manual_trailer) == -1) {
+ if (fprintf(f, cpcgen_manual_intel_trailer) == -1) {
warn("failed to write out manual header for %s",
map->cmap_name);
return (B_FALSE);
@@ -719,7 +909,8 @@ cpcgen_manual_file_after(FILE *f, cpc_map_t *map)
}
static boolean_t
-cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent)
+cpcgen_manual_intel_event(FILE *f, nvlist_t *nvl, const char *path,
+ uint32_t ent)
{
char *event, *lname, *brief = NULL, *public = NULL, *errata = NULL;
size_t i;
@@ -753,20 +944,20 @@ cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent)
}
if (fprintf(f, ".It Sy %s\n", lname) == -1) {
- warn("failed to write out probe entry %s", event);
+ warn("failed to write out event entry %s", event);
free(lname);
return (B_FALSE);
}
if (public != NULL) {
if (fprintf(f, "%s\n", public) == -1) {
- warn("failed to write out probe entry %s", event);
+ warn("failed to write out event entry %s", event);
free(lname);
return (B_FALSE);
}
} else if (brief != NULL) {
if (fprintf(f, "%s\n", brief) == -1) {
- warn("failed to write out probe entry %s", event);
+ warn("failed to write out event entry %s", event);
free(lname);
return (B_FALSE);
}
@@ -776,7 +967,7 @@ cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent)
if (fprintf(f, ".Pp\nThe following errata may apply to this: "
"%s\n", errata) == -1) {
- warn("failed to write out probe entry %s", event);
+ warn("failed to write out event entry %s", event);
free(lname);
return (B_FALSE);
}
@@ -787,7 +978,7 @@ cpcgen_manual_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent)
}
static char *
-cpcgen_cfile_name(cpc_map_t *map)
+cpcgen_cfile_intel_name(cpc_map_t *map)
{
char *name;
@@ -800,15 +991,15 @@ cpcgen_cfile_name(cpc_map_t *map)
}
static boolean_t
-cpcgen_cfile_file_before(FILE *f, cpc_map_t *map)
+cpcgen_cfile_intel_before(FILE *f, cpc_map_t *map)
{
- if (fprintf(f, cpcgen_cfile_header, map->cmap_path) == -1) {
+ if (fprintf(f, cpcgen_cfile_intel_header, map->cmap_path) == -1) {
warn("failed to write header to temporary file for %s",
map->cmap_path);
return (B_FALSE);
}
- if (fprintf(f, cpcgen_cfile_table_start, map->cmap_name) == -1) {
+ if (fprintf(f, cpcgen_cfile_intel_table_start, map->cmap_name) == -1) {
warn("failed to write header to temporary file for %s",
map->cmap_path);
return (B_FALSE);
@@ -818,9 +1009,9 @@ cpcgen_cfile_file_before(FILE *f, cpc_map_t *map)
}
static boolean_t
-cpcgen_cfile_file_after(FILE *f, cpc_map_t *map)
+cpcgen_cfile_intel_after(FILE *f, cpc_map_t *map)
{
- if (fprintf(f, cpcgen_cfile_table_end) == -1) {
+ if (fprintf(f, cpcgen_cfile_intel_table_end) == -1) {
warn("failed to write footer to temporary file for %s",
map->cmap_path);
return (B_FALSE);
@@ -830,7 +1021,7 @@ cpcgen_cfile_file_after(FILE *f, cpc_map_t *map)
}
static boolean_t
-cpcgen_cfile_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
+cpcgen_cfile_intel_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
{
char *ecode, *umask, *name, *counter, *lname, *cmask;
size_t i;
@@ -903,12 +1094,12 @@ cpcgen_cfile_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
/*
* Check if we have any PAPI aliases.
*/
- for (i = 0; cpcgen_papi_map[i].cpapi_intc != NULL; i++) {
- if (strcmp(name, cpcgen_papi_map[i].cpapi_intc) != 0)
+ for (i = 0; cpcgen_intel_papi_map[i].cpapi_intc != NULL; i++) {
+ if (strcmp(name, cpcgen_intel_papi_map[i].cpapi_intc) != 0)
continue;
if (fprintf(f, "\t{ %s, %s, %s, \"%s\" },\n", ecode, umask,
- cmask, cpcgen_papi_map[i].cpapi_papi) == -1) {
+ cmask, cpcgen_intel_papi_map[i].cpapi_papi) == -1) {
warn("failed to write out entry %s from %s", name,
path);
return (B_FALSE);
@@ -977,11 +1168,23 @@ cpcgen_generate_map(FILE *f, cpc_map_t *map, boolean_t start)
}
/*
+ * This is a wrapper around unlinkat that makes sure that we don't clobber
+ * errno, which is used for properly printing out error messages below.
+ */
+static void
+cpcgen_remove_tmpfile(int dirfd, const char *path)
+{
+ int e = errno;
+ (void) unlinkat(dirfd, path, 0);
+ errno = e;
+}
+
+/*
* Generate a header file that declares all of these arrays and provide a map
* for models to the corresponding table to use.
*/
static void
-cpcgen_common_files(int dirfd)
+cpcgen_common_intel_files(int dirfd)
{
const char *fname = "core_pcbe_cpcgen.h";
char *tmpname;
@@ -999,16 +1202,12 @@ cpcgen_common_files(int dirfd)
}
if ((f = fdopen(fd, "w")) == NULL) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
err(EXIT_FAILURE, "failed to fdopen temporary file");
}
- if (fprintf(f, cpcgen_cfile_header, cpcgen_mapfile) == -1) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write header to temporary file "
"for %s", fname);
}
@@ -1023,9 +1222,7 @@ cpcgen_common_files(int dirfd)
"extern const struct events_table_t *core_cpcgen_table(uint_t, "
"uint_t);\n"
"\n") == -1) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write header to "
"temporary file for %s", fname);
}
@@ -1033,9 +1230,7 @@ cpcgen_common_files(int dirfd)
for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
if (fprintf(f, "extern const struct events_table_t "
"pcbe_core_events_%s[];\n", map->cmap_name) == -1) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write entry to "
"temporary file for %s", fname);
}
@@ -1047,17 +1242,13 @@ cpcgen_common_files(int dirfd)
"#endif\n"
"\n"
"#endif /* _CORE_PCBE_CPCGEN_H */\n") == -1) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write header to "
"temporary file for %s", fname);
}
if (fflush(f) != 0 || fclose(f) != 0) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
err(EXIT_FAILURE, "failed to flush and close temporary file");
}
@@ -1080,16 +1271,12 @@ cpcgen_common_files(int dirfd)
}
if ((f = fdopen(fd, "w")) == NULL) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
err(EXIT_FAILURE, "failed to fdopen temporary file");
}
- if (fprintf(f, cpcgen_cfile_header, cpcgen_mapfile) == -1) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ if (fprintf(f, cpcgen_cfile_intel_header, cpcgen_intel_mapfile) == -1) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write header to temporary file "
"for %s", fname);
}
@@ -1101,18 +1288,14 @@ cpcgen_common_files(int dirfd)
"const struct events_table_t *\n"
"core_cpcgen_table(uint_t model, uint_t stepping)\n"
"{\n") == -1) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write header to "
"temporary file for %s", fname);
}
for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
if (!cpcgen_generate_map(f, map, map == cpcgen_maps)) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write to temporary "
"file for %s", fname);
}
@@ -1122,17 +1305,13 @@ cpcgen_common_files(int dirfd)
"\t\t\treturn (NULL);\n"
"\t}\n"
"}\n") == -1) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to write header to "
"temporary file for %s", fname);
}
if (fflush(f) != 0 || fclose(f) != 0) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
err(EXIT_FAILURE, "failed to flush and close temporary file");
}
@@ -1161,7 +1340,7 @@ cpcgen_common_files(int dirfd)
* - If more than one value is specified in the EventCode or UMask values
*/
static boolean_t
-cpcgen_skip_entry(nvlist_t *nvl, const char *path, uint_t ent)
+cpcgen_skip_intel_entry(nvlist_t *nvl, const char *path, uint_t ent)
{
char *event, *msridx, *msrval, *taken, *offcore, *counter;
char *ecode, *umask;
@@ -1213,6 +1392,382 @@ cpcgen_skip_entry(nvlist_t *nvl, const char *path, uint_t ent)
return (B_FALSE);
}
+static char *
+cpcgen_manual_amd_name(cpc_map_t *map)
+{
+ char *name;
+
+ if (asprintf(&name, "amd_%s_events.3cpc", map->cmap_name) == -1) {
+ warn("failed to assemble file name for %s", map->cmap_path);
+ return (NULL);
+ }
+
+ return (name);
+}
+
+static boolean_t
+cpcgen_manual_amd_file_before(FILE *f, cpc_map_t *map)
+{
+ size_t i;
+ char *upper;
+ const char *family;
+
+ if ((upper = strdup(map->cmap_name)) == NULL) {
+ warn("failed to duplicate manual name for %s", map->cmap_name);
+ return (B_FALSE);
+ }
+
+ for (i = 0; upper[i] != '\0'; i++) {
+ upper[i] = toupper(upper[i]);
+ }
+
+ family = map->cmap_name + 1;
+
+ if (fprintf(f, cpcgen_manual_amd_header, map->cmap_path, upper,
+ family, family, family) == -1) {
+ warn("failed to write out manual header for %s",
+ map->cmap_name);
+ free(upper);
+ return (B_FALSE);
+ }
+
+ free(upper);
+ return (B_TRUE);
+}
+
+static boolean_t
+cpcgen_manual_amd_file_after(FILE *f, cpc_map_t *map)
+{
+ if (fprintf(f, cpcgen_manual_amd_trailer) == -1) {
+ warn("failed to write out manual header for %s",
+ map->cmap_name);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+cpcgen_manual_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint32_t ent)
+{
+ char *name, *mnemonic = NULL, *summary = NULL, *desc = NULL;
+ char *umode;
+ nvlist_t *units = NULL;
+ uint32_t i, length;
+
+ if (nvlist_lookup_string(nvl, "name", &name) != 0) {
+ warnx("Found event without 'name' property in %s, entry %u",
+ path, ent);
+ return (B_FALSE);
+ }
+
+ if (nvlist_lookup_string(nvl, "mnemonic", &mnemonic) != 0 ||
+ nvlist_lookup_string(nvl, "summary", &summary) != 0) {
+ warnx("event %s in %s, entry %u, missing required fields",
+ name, path, ent);
+ return (B_FALSE);
+ }
+
+ /*
+ * Allow the other fields to be missing.
+ */
+ (void) nvlist_lookup_string(nvl, "description", &desc);
+ (void) nvlist_lookup_nvlist(nvl, "units", &units);
+
+ if (fprintf(f, ".It Sy %s\n", name) == -1) {
+ warn("failed to write out event entry %s", name);
+ }
+
+ if (fprintf(f, ".Sy %s -\n"
+ "%s\n", mnemonic, summary) == -1) {
+ warn("failed to write out event entry %s", name);
+ return (B_FALSE);
+ }
+
+ if (desc != NULL) {
+ if (fprintf(f, ".Pp\n%s\n", desc) == -1) {
+ warn("failed to write out event entry %s", name);
+ return (B_FALSE);
+ }
+ }
+
+ if (units == NULL)
+ return (B_TRUE);
+
+ /*
+ * Skip units we don't know how to handle.
+ */
+ if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) {
+ return (B_TRUE);
+ }
+
+ if (fprintf(f, ".Pp\n"
+ "This event has the following units which may be used\n"
+ "to modify the behavior of the event:\n"
+ ".Bl -tag -width Sy\n") == -1) {
+ warn("failed to write out event entry %s", name);
+ return (B_FALSE);
+ }
+
+ if (nvlist_lookup_uint32(units, "length", &length) != 0) {
+ warnx("found units array, but could not look up length "
+ "property for events %s (index %u) in file %s",
+ name, ent, path);
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < length; i++) {
+ nvlist_t *uvl;
+ char num[64];
+ char *uname, *udesc = NULL;
+
+ (void) snprintf(num, sizeof (num), "%u", i);
+ if (nvlist_lookup_nvlist(units, num, &uvl) != 0) {
+ warnx("failed to look up unit %u for event %s (index "
+ "%u) in file %s", i, name, ent, path);
+ return (B_FALSE);
+ }
+
+ if (nvlist_lookup_string(uvl, "name", &uname) != 0) {
+ warnx("failed to find required members for unit array "
+ "entry %u of event %s (index %u) from file %s",
+ i, name, ent, path);
+ return (B_FALSE);
+ }
+ (void) nvlist_lookup_string(uvl, "description", &udesc);
+ if (fprintf(f, ".It Sy %s\n", uname) == -1) {
+ warn("failed to write out event entry %s", name);
+ return (B_FALSE);
+ }
+
+ if (udesc != NULL) {
+ if (fprintf(f, "%s\n", udesc) == -1) {
+ warn("failed to write out event entry %s",
+ name);
+ return (B_FALSE);
+ }
+ }
+ }
+
+ if (fprintf(f, ".El\n") == -1) {
+ warn("failed to write out event entry %s",
+ name);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static char *
+cpcgen_cfile_amd_name(cpc_map_t *map)
+{
+ char *name;
+
+ if (asprintf(&name, "opteron_pcbe_%s.c", map->cmap_name) == -1) {
+ warn("failed to assemble file name for %s", map->cmap_path);
+ return (NULL);
+ }
+
+ return (name);
+}
+
+/*
+ * Generate a header file that can be used to synthesize the data events we care
+ * about.
+ */
+static void
+cpcgen_common_amd_files(int dirfd)
+{
+ const char *fname = "opteron_pcbe_cpcgen.h";
+ char *tmpname;
+ int fd;
+ FILE *f;
+ cpc_map_t *map;
+
+ if (asprintf(&tmpname, ".%s.%d", fname, getpid()) == -1) {
+ err(EXIT_FAILURE, "failed to construct temporary file name");
+ }
+
+ if ((fd = openat(dirfd, tmpname, O_RDWR | O_CREAT, 0644)) < 0) {
+ err(EXIT_FAILURE, "failed to create temporary file %s",
+ tmpname);
+ }
+
+ if ((f = fdopen(fd, "w")) == NULL) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
+ err(EXIT_FAILURE, "failed to fdopen temporary file");
+ }
+
+ if (fprintf(f, cpcgen_cfile_cddl_header) == -1) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
+ err(EXIT_FAILURE, "failed to write header to "
+ "temporary file for %s", fname);
+ }
+
+ if (fprintf(f, "#ifndef _OPTERON_PCBE_CPCGEN_H\n"
+ "#define\t_OPTERON_PCBE_CPCGEN_H\n"
+ "\n"
+ "#ifdef __cplusplus\n"
+ "extern \"C\" {\n"
+ "#endif\n"
+ "\n") == -1) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
+ err(EXIT_FAILURE, "failed to write header to "
+ "temporary file for %s", fname);
+ }
+
+ for (map = cpcgen_maps; map != NULL; map = map->cmap_next) {
+ if (fprintf(f, "extern const amd_event_t "
+ "opteron_pcbe_%s_events[];\n", map->cmap_name) == -1) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
+ err(EXIT_FAILURE, "failed to write header to "
+ "temporary file for %s", fname);
+ }
+ }
+
+ if (fprintf(f, "\n"
+ "#ifdef __cplusplus\n"
+ "}\n"
+ "#endif\n"
+ "\n"
+ "#endif /* _OPTERON_PCBE_CPCGEN_H */\n") == -1) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
+ err(EXIT_FAILURE, "failed to write header to "
+ "temporary file for %s", fname);
+ }
+
+
+
+ if (fflush(f) != 0 || fclose(f) != 0) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
+ err(EXIT_FAILURE, "failed to flush and close temporary file");
+ }
+
+ if (renameat(dirfd, tmpname, dirfd, fname) != 0) {
+ err(EXIT_FAILURE, "failed to rename temporary file %s",
+ tmpname);
+ }
+
+ free(tmpname);
+}
+
+static boolean_t
+cpcgen_cfile_amd_before(FILE *f, cpc_map_t *map)
+{
+ if (fprintf(f, cpcgen_cfile_amd_header, map->cmap_name) == -1) {
+ warn("failed to write header to temporary file for %s",
+ map->cmap_path);
+ return (B_FALSE);
+ }
+
+ if (fprintf(f, cpcgen_cfile_amd_table_start, map->cmap_name) == -1) {
+ warn("failed to write header to temporary file for %s",
+ map->cmap_path);
+ return (B_FALSE);
+ }
+
+
+ return (B_TRUE);
+}
+
+static boolean_t
+cpcgen_cfile_amd_after(FILE *f, cpc_map_t *map)
+{
+ if (fprintf(f, cpcgen_cfile_amd_table_end) == -1) {
+ warn("failed to write footer to temporary file for %s",
+ map->cmap_path);
+ return (B_FALSE);
+ }
+
+ return (B_TRUE);
+}
+
+static boolean_t
+cpcgen_cfile_amd_event(FILE *f, nvlist_t *nvl, const char *path, uint_t ent)
+{
+ char *name, *code, *umode;
+ uint32_t i, length;
+ nvlist_t *units;
+
+ if (nvlist_lookup_string(nvl, "name", &name) != 0) {
+ warnx("Found event without 'name' property in %s, entry %u",
+ path, ent);
+ return (B_FALSE);
+ }
+
+ if (nvlist_lookup_string(nvl, "code", &code) != 0) {
+ warnx("event %s (index %u) from %s missing required properties "
+ "for C translation", name, path, ent);
+ return (B_FALSE);
+ }
+
+ if (fprintf(f, "\t{ \"%s\", %s, 0 },\n", name, code) == -1) {
+ warn("failed to write out entry %s from %s", name, path);
+ return (B_FALSE);
+ }
+
+ /*
+ * The 'units' array is optional. If the rule has a specific 'unit_mode'
+ * indicating how the units should be combined, skip that. We don't know
+ * how to properly process that right now.
+ */
+ if (nvlist_lookup_nvlist(nvl, "units", &units) != 0) {
+ return (B_TRUE);
+ }
+
+ if (nvlist_lookup_string(nvl, "unit_mode", &umode) == 0) {
+ return (B_TRUE);
+ }
+
+ if (nvlist_lookup_uint32(units, "length", &length) != 0) {
+ warnx("found units array, but could not look up length "
+ "property for events %s (index %u) in file %s",
+ name, ent, path);
+ return (B_FALSE);
+ }
+
+ for (i = 0; i < length; i++) {
+ nvlist_t *uvl;
+ char num[64];
+ char *uname, *urw;
+ int32_t bit;
+
+ (void) snprintf(num, sizeof (num), "%u", i);
+ if (nvlist_lookup_nvlist(units, num, &uvl) != 0) {
+ warnx("failed to look up unit %u for event %s (index "
+ "%u) in file %s", i, name, ent, path);
+ return (B_FALSE);
+ }
+
+ if (nvlist_lookup_string(uvl, "name", &uname) != 0 ||
+ nvlist_lookup_string(uvl, "rw", &urw) != 0 ||
+ nvlist_lookup_int32(uvl, "bit", &bit) != 0) {
+ warnx("failed to find required members for unit array "
+ "entry %u of event %s (index %u) from file %s",
+ i, name, ent, path);
+ dump_nvlist(uvl, 0);
+ return (B_FALSE);
+ }
+
+ if (bit < 0 || bit > 31) {
+ warnx("event %s (index %u) from file %s has invalid "
+ "bit value: %d; skipping", name, ent, path, bit);
+ continue;
+ }
+
+ if (strcasecmp(urw, "Read-write") != 0)
+ continue;
+
+ if (fprintf(f, "\t{ \"%s.%s\", %s, 0x%x },\n", name, uname,
+ code, 1U << bit) == -1) {
+ warn("failed to write out entry %s from %s", name,
+ path);
+ return (B_FALSE);
+ }
+ }
+
+ return (B_TRUE);
+}
/*
* For each processor family, generate a data file that contains all of the
@@ -1249,14 +1804,12 @@ cpcgen_gen(int dirfd)
}
if ((f = fdopen(fd, "w")) == NULL) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
err(EXIT_FAILURE, "failed to fdopen temporary file");
}
if (!cpcgen_ops.cgen_op_file_before(f, map)) {
- (void) unlinkat(dirfd, tmpname, 0);
+ cpcgen_remove_tmpfile(dirfd, tmpname);
exit(EXIT_FAILURE);
}
@@ -1277,30 +1830,31 @@ cpcgen_gen(int dirfd)
(void) snprintf(num, sizeof (num), "%u", i);
if ((ret = nvlist_lookup_nvlist(map->cmap_data,
num, &nvl)) != 0) {
+ cpcgen_remove_tmpfile(dirfd, tmpname);
errx(EXIT_FAILURE, "failed to look up array "
"entry %u in parsed data for %s: %s", i,
map->cmap_path, strerror(ret));
}
- if (cpcgen_skip_entry(nvl, map->cmap_path, i))
+ if (cpcgen_ops.cgen_op_skip != NULL &&
+ cpcgen_ops.cgen_op_skip(nvl, map->cmap_path, i)) {
continue;
+ }
if (!cpcgen_ops.cgen_op_event(f, nvl, map->cmap_path,
i)) {
- (void) unlinkat(dirfd, tmpname, 0);
+ cpcgen_remove_tmpfile(dirfd, tmpname);
exit(EXIT_FAILURE);
}
}
if (!cpcgen_ops.cgen_op_file_after(f, map)) {
- (void) unlinkat(dirfd, tmpname, 0);
+ cpcgen_remove_tmpfile(dirfd, tmpname);
exit(EXIT_FAILURE);
}
if (fflush(f) != 0 || fclose(f) != 0) {
- int e = errno;
- (void) unlinkat(dirfd, tmpname, 0);
- errno = e;
+ cpcgen_remove_tmpfile(dirfd, tmpname);
err(EXIT_FAILURE, "failed to flush and close "
"temporary file");
}
@@ -1333,9 +1887,9 @@ cpcgen_usage(const char *fmt, ...)
"\t-a generate data for all platforms\n"
"\t-c generate C file for CPC\n"
"\t-d specify the directory containt perfmon data\n"
- "\t-h generate header file and common files\n"
+ "\t-H generate header file and common files\n"
"\t-m generate manual pages for CPC data\n"
- "\t-o outut files in directory outdir\n"
+ "\t-o output files in directory outdir\n"
"\t-p generate data for a specified platform\n",
cpcgen_progname);
}
@@ -1416,7 +1970,6 @@ main(int argc, char *argv[])
return (2);
}
-
if (outdir == NULL) {
cpcgen_usage("Missing required output directory (-o)\n");
return (2);
@@ -1431,28 +1984,62 @@ main(int argc, char *argv[])
return (2);
}
- cpcgen_read_mapfile(datadir, platform);
+ cpcgen_determine_vendor(datadir);
+
+ switch (cpcgen_mode) {
+ case CPCGEN_MODE_INTEL:
+ cpcgen_ops.cgen_op_gather = cpcgen_read_intel;
+ cpcgen_ops.cgen_op_common = cpcgen_common_intel_files;
+ cpcgen_ops.cgen_op_skip = cpcgen_skip_intel_entry;
+ if (do_mpage) {
+ cpcgen_ops.cgen_op_name = cpcgen_manual_intel_name;
+ cpcgen_ops.cgen_op_file_before =
+ cpcgen_manual_intel_file_before;
+ cpcgen_ops.cgen_op_file_after =
+ cpcgen_manual_intel_file_after;
+ cpcgen_ops.cgen_op_event = cpcgen_manual_intel_event;
+ } else {
+ cpcgen_ops.cgen_op_name = cpcgen_cfile_intel_name;
+ cpcgen_ops.cgen_op_file_before =
+ cpcgen_cfile_intel_before;
+ cpcgen_ops.cgen_op_file_after =
+ cpcgen_cfile_intel_after;
+ cpcgen_ops.cgen_op_event = cpcgen_cfile_intel_event;
+ }
+ break;
+ case CPCGEN_MODE_AMD:
+ cpcgen_ops.cgen_op_gather = cpcgen_read_amd;
+ cpcgen_ops.cgen_op_common = cpcgen_common_amd_files;
+ cpcgen_ops.cgen_op_skip = NULL;
+ if (do_mpage) {
+ cpcgen_ops.cgen_op_name = cpcgen_manual_amd_name;
+ cpcgen_ops.cgen_op_file_before =
+ cpcgen_manual_amd_file_before;
+ cpcgen_ops.cgen_op_file_after =
+ cpcgen_manual_amd_file_after;
+ cpcgen_ops.cgen_op_event = cpcgen_manual_amd_event;
+ } else {
+ cpcgen_ops.cgen_op_name = cpcgen_cfile_amd_name;
+ cpcgen_ops.cgen_op_file_before =
+ cpcgen_cfile_amd_before;
+ cpcgen_ops.cgen_op_file_after = cpcgen_cfile_amd_after;
+ cpcgen_ops.cgen_op_event = cpcgen_cfile_amd_event;
- if (do_header) {
- cpcgen_common_files(outdirfd);
- return (0);
+ }
+ break;
+ default:
+ errx(EXIT_FAILURE, "failed to determine if operating on AMD or "
+ "Intel");
+ break;
}
- if (do_mpage) {
- cpcgen_ops.cgen_op_name = cpcgen_manual_name;
- cpcgen_ops.cgen_op_file_before = cpcgen_manual_file_before;
- cpcgen_ops.cgen_op_file_after = cpcgen_manual_file_after;
- cpcgen_ops.cgen_op_event = cpcgen_manual_event;
- }
+ cpcgen_ops.cgen_op_gather(datadir, platform);
- if (do_cfile) {
- cpcgen_ops.cgen_op_name = cpcgen_cfile_name;
- cpcgen_ops.cgen_op_file_before = cpcgen_cfile_file_before;
- cpcgen_ops.cgen_op_file_after = cpcgen_cfile_file_after;
- cpcgen_ops.cgen_op_event = cpcgen_cfile_event;
+ if (do_header) {
+ cpcgen_ops.cgen_op_common(outdirfd);
+ return (0);
}
-
cpcgen_gen(outdirfd);
return (0);
diff --git a/usr/src/uts/i86pc/os/cpuid.c b/usr/src/uts/i86pc/os/cpuid.c
index f0d6890daa..4e01cc127c 100644
--- a/usr/src/uts/i86pc/os/cpuid.c
+++ b/usr/src/uts/i86pc/os/cpuid.c
@@ -1036,7 +1036,8 @@ static char *x86_feature_names[NUM_X86_FEATURES] = {
"xop",
"fma4",
"tbm",
- "avx512_vnni"
+ "avx512_vnni",
+ "amd_pcec"
};
boolean_t
@@ -3081,6 +3082,10 @@ cpuid_pass1(cpu_t *cpu, uchar_t *featureset)
add_x86_feature(featureset, X86FSET_TOPOEXT);
}
+ if (cp->cp_ecx & CPUID_AMD_ECX_PCEC) {
+ add_x86_feature(featureset, X86FSET_AMD_PCEC);
+ }
+
if (cp->cp_ecx & CPUID_AMD_ECX_XOP) {
add_x86_feature(featureset, X86FSET_XOP);
}
diff --git a/usr/src/uts/intel/ia32/os/cpc_subr.c b/usr/src/uts/intel/ia32/os/cpc_subr.c
index b15c57a8e5..f7b86fd602 100644
--- a/usr/src/uts/intel/ia32/os/cpc_subr.c
+++ b/usr/src/uts/intel/ia32/os/cpc_subr.c
@@ -20,6 +20,7 @@
*/
/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -139,6 +140,12 @@ kcpc_hw_init(cpu_t *cp)
strands_perfmon_shared = 1;
}
}
+ } else if (cpuid_getvendor(cpu[0]) == X86_VENDOR_AMD) {
+ /*
+ * On AMD systems with HT, all of the performance
+ * monitors exist on a per-logical CPU basis.
+ */
+ strands_perfmon_shared = 0;
} else {
strands_perfmon_shared = 1;
}
diff --git a/usr/src/uts/intel/opteron_pcbe/Makefile b/usr/src/uts/intel/opteron_pcbe/Makefile
index aea8e89fbe..6ddb761ccd 100644
--- a/usr/src/uts/intel/opteron_pcbe/Makefile
+++ b/usr/src/uts/intel/opteron_pcbe/Makefile
@@ -21,6 +21,7 @@
#
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
+# Copyright 2019 Joyent, Inc.
#
#
# This Makefile builds the AMD Opteron/Athlon64 Performance Counter BackEnd.
@@ -29,10 +30,22 @@
UTSBASE = ../..
#
+# The following objects are autogenerated by cpcgen.
+#
+CPCGEN_OBJS = \
+ opteron_pcbe_f17h.o
+
+CPCGEN_COMMON = opteron_pcbe_cpcgen.h
+CPCGEN_CMD = $(CPCGEN) -d $(SRC)/data/amdpmc -o .
+CPCGEN_SRCS = $(CPCGEN_OBJS:%.o=%.c) opteron_pcbe_cpcgen.h
+
+
+#
# Define module and object file sets.
#
MODULE = pcbe.AuthenticAMD
OBJECTS = $(OPTERON_PCBE_OBJS:%=$(OBJS_DIR)/%)
+OBJECTS += $(CPCGEN_OBJS:%=$(OBJS_DIR)/%)
LINTS = $(OPTERON_PCBE_OBJS:%.o=$(LINTS_DIR)/%.ln)
ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
@@ -41,13 +54,16 @@ ROOTMODULE = $(USR_PCBE_DIR)/$(MODULE)
#
include $(UTSBASE)/intel/Makefile.intel
+CPPFLAGS += -I$(UTSBASE)/intel/opteron_pcbe
+CLEANFILES += $(CPCGEN_SRCS)
+
#
# Define targets.
#
-ALL_TARGET = $(BINARY)
+ALL_TARGET = $(CPCGEN_COMMON) .WAIT $(BINARY)
LINT_MODULE = opteron_pcbe
LINT_TARGET = $(LINT_MODULE).lint
-INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+INSTALL_TARGET = $(CPCGEN_COMMON) .WAIT $(BINARY) $(ROOTMODULE)
#
# For now, disable these lint checks; maintainers should endeavor
@@ -78,6 +94,18 @@ clean.lint: $(CLEAN_LINT_DEPS)
install: $(INSTALL_DEPS)
+opteron_pcbe_cpcgen.h:
+ $(CPCGEN_CMD) -a -H
+
+opteron_pcbe_%.c: $(CPCGEN_COMMON)
+ $(CPCGEN_CMD) -c -p \
+ $$(echo $@ | \
+ $(SED) -e 's/opteron_pcbe_//g' -e 's/_/-/g' -e 's/.c$$//g')
+
+$(OBJS_DIR)/%.o: %.c
+ $(COMPILE.c) -I$(SRC)/uts/intel/pcbe/ -o $@ $<
+ $(CTFCONVERT_O)
+
#
# Include common targets.
#
diff --git a/usr/src/uts/intel/pcbe/opteron_pcbe.c b/usr/src/uts/intel/pcbe/opteron_pcbe.c
index cb97d21b78..6a875ec681 100644
--- a/usr/src/uts/intel/pcbe/opteron_pcbe.c
+++ b/usr/src/uts/intel/pcbe/opteron_pcbe.c
@@ -65,6 +65,7 @@
/*
* Portions Copyright 2009 Advanced Micro Devices, Inc.
+ * Copyright 2019 Joyent, Inc.
*/
/*
@@ -86,6 +87,9 @@
#include <sys/ddi.h>
#include <sys/sunddi.h>
+#include "opteron_pcbe_table.h"
+#include <opteron_pcbe_cpcgen.h>
+
static int opt_pcbe_init(void);
static uint_t opt_pcbe_ncounters(void);
static const char *opt_pcbe_impl_name(void);
@@ -120,11 +124,33 @@ static pcbe_ops_t opt_pcbe_ops = {
};
/*
+ * Base MSR addresses for the PerfEvtSel registers and the counters themselves.
+ * Add counter number to base address to get corresponding MSR address.
+ */
+#define PES_BASE_ADDR 0xC0010000
+#define PIC_BASE_ADDR 0xC0010004
+
+/*
+ * Base MSR addresses for the PerfEvtSel registers and counters. The counter and
+ * event select registers are interleaved, so one needs to multiply the counter
+ * number by two to determine what they should be set to.
+ */
+#define PES_EXT_BASE_ADDR 0xC0010200
+#define PIC_EXT_BASE_ADDR 0xC0010201
+
+/*
+ * The number of counters present depends on which CPU features are present.
+ */
+#define OPT_PCBE_DEF_NCOUNTERS 4
+#define OPT_PCBE_EXT_NCOUNTERS 6
+
+/*
* Define offsets and masks for the fields in the Performance
* Event-Select (PES) registers.
*/
#define OPT_PES_HOST_SHIFT 41
#define OPT_PES_GUEST_SHIFT 40
+#define OPT_PES_EVSELHI_SHIFT 32
#define OPT_PES_CMASK_SHIFT 24
#define OPT_PES_CMASK_MASK 0xFF
#define OPT_PES_INV_SHIFT 23
@@ -153,36 +179,54 @@ typedef struct _opt_pcbe_config {
uint64_t opt_rawpic; /* Raw counter value */
} opt_pcbe_config_t;
-opt_pcbe_config_t nullcfgs[4] = {
+opt_pcbe_config_t nullcfgs[OPT_PCBE_EXT_NCOUNTERS] = {
{ 0, 0, 0 },
{ 1, 0, 0 },
{ 2, 0, 0 },
- { 3, 0, 0 }
+ { 3, 0, 0 },
+ { 4, 0, 0 },
+ { 5, 0, 0 },
};
-typedef struct _amd_event {
- char *name;
- uint16_t emask; /* Event mask setting */
-} amd_event_t;
+typedef uint64_t (*opt_pcbe_addr_f)(uint_t);
-typedef struct _amd_generic_event {
- char *name;
- char *event;
- uint8_t umask;
-} amd_generic_event_t;
+typedef struct opt_pcbe_data {
+ uint_t opd_ncounters;
+ uint_t opd_cmask;
+ opt_pcbe_addr_f opd_pesf;
+ opt_pcbe_addr_f opd_picf;
+} opt_pcbe_data_t;
-/*
- * Base MSR addresses for the PerfEvtSel registers and the counters themselves.
- * Add counter number to base address to get corresponding MSR address.
- */
-#define PES_BASE_ADDR 0xC0010000
-#define PIC_BASE_ADDR 0xC0010004
+opt_pcbe_data_t opd;
#define MASK48 0xFFFFFFFFFFFF
#define EV_END {NULL, 0}
#define GEN_EV_END {NULL, NULL, 0 }
+/*
+ * The following Macros are used to define tables of events that are used by
+ * various families and some generic classes of events.
+ *
+ * When programming a performance counter there are two different values that we
+ * need to set:
+ *
+ * o Event - Determines the general class of event that is being used.
+ * o Unit - A further breakdown that gives more specific value.
+ *
+ * Prior to the introduction of family 17h support, all family specific events
+ * were programmed based on their event. The generic events, which tried to
+ * provide PAPI mappings to events specified an additional unit mask.
+ *
+ * Starting with Family 17h, CPU performance counters default to using both the
+ * unit mask and the event select. Generic events are always aliases to a
+ * specific event/unit pair, hence why the units for them are always zero. In
+ * addition, the naming of events in family 17h has been changed to reflect
+ * AMD's guide. While this is a departure from what people are used to, it is
+ * believed that matching the more detailed literature that folks are told to
+ * reference is more valuable.
+ */
+
#define AMD_cmn_events \
{ "FP_dispatched_fpu_ops", 0x0 }, \
{ "FP_cycles_no_fpu_ops_retired", 0x1 }, \
@@ -372,54 +416,70 @@ typedef struct _amd_generic_event {
{ "PAPI_l3_ldm", "L3_miss", 0xf3 }, \
{ "PAPI_l3_tcm", "L3_miss", 0xf7 }
-#define AMD_PCBE_SUPPORTED(family) (((family) >= 0xf) && ((family) <= 0x11))
-
-static amd_event_t family_f_events[] = {
+static const amd_event_t family_f_events[] = {
AMD_cmn_events,
AMD_FAMILY_f_events,
EV_END
};
-static amd_event_t family_10h_events[] = {
+static const amd_event_t family_10h_events[] = {
AMD_cmn_events,
AMD_FAMILY_10h_events,
EV_END
};
-static amd_event_t family_11h_events[] = {
+static const amd_event_t family_11h_events[] = {
AMD_cmn_events,
AMD_FAMILY_11h_events,
EV_END
};
-static amd_generic_event_t opt_generic_events[] = {
+static const amd_generic_event_t opt_generic_events[] = {
AMD_cmn_generic_events,
OPT_cmn_generic_events,
GEN_EV_END
};
-static amd_generic_event_t family_10h_generic_events[] = {
+static const amd_generic_event_t family_10h_generic_events[] = {
AMD_cmn_generic_events,
AMD_FAMILY_10h_generic_events,
GEN_EV_END
};
+/*
+ * For Family 17h, the cpcgen utility generates all of our events including ones
+ * that need specific unit codes, therefore we leave all unit codes out of
+ * these.
+ */
+static const amd_generic_event_t family_17h_papi_events[] = {
+ { "PAPI_br_cn", "ExRetCond" },
+ { "PAPI_br_ins", "ExRetBrnMis" },
+ { "PAPI_fpu_idl", "FpSchedEmpty" },
+ { "PAPI_tot_cyc", "LsNotHaltedCyc" },
+ { "PAPI_tot_ins", "ExRetInstr" },
+ { "PAPI_tlb_dm", "LsL1DTlbMiss" },
+ { "PAPI_tlb_im", "BpL1TlbMissL2Miss" },
+ { "PAPI_tot_cyc", "LsNotHaltedCyc" },
+ GEN_EV_END
+};
+
static char *evlist;
static size_t evlist_sz;
-static amd_event_t *amd_events = NULL;
-static uint_t amd_family;
-static amd_generic_event_t *amd_generic_events = NULL;
+static const amd_event_t *amd_events = NULL;
+static uint_t amd_family, amd_model;
+static const amd_generic_event_t *amd_generic_events = NULL;
-#define AMD_CPUREF_SIZE 256
-static char amd_generic_bkdg[AMD_CPUREF_SIZE];
-static char amd_fam_f_rev_ae_bkdg[] = "See \"BIOS and Kernel Developer's " \
+static char amd_fam_f_rev_ae_bkdg[] = "See \"BIOS and Kernel Developer's "
"Guide for AMD Athlon 64 and AMD Opteron Processors\" (AMD publication 26094)";
-static char amd_fam_f_NPT_bkdg[] = "See \"BIOS and Kernel Developer's Guide " \
+static char amd_fam_f_NPT_bkdg[] = "See \"BIOS and Kernel Developer's Guide "
"for AMD NPT Family 0Fh Processors\" (AMD publication 32559)";
-static char amd_fam_10h_bkdg[] = "See \"BIOS and Kernel Developer's Guide " \
+static char amd_fam_10h_bkdg[] = "See \"BIOS and Kernel Developer's Guide "
"(BKDG) For AMD Family 10h Processors\" (AMD publication 31116)";
-static char amd_fam_11h_bkdg[] = "See \"BIOS and Kernel Developer's Guide " \
+static char amd_fam_11h_bkdg[] = "See \"BIOS and Kernel Developer's Guide "
"(BKDG) For AMD Family 11h Processors\" (AMD publication 41256)";
+static char amd_fam_17h_reg[] = "See \"Open-Source Register Reference For "
+"AMD Family 17h Processors Models 00h-2Fh\" (AMD publication 56255) and "
+"amd_f17h_events(3CPC)";
static char amd_pcbe_impl_name[64];
static char *amd_pcbe_cpuref;
@@ -428,14 +488,42 @@ static char *amd_pcbe_cpuref;
#define BITS(v, u, l) \
(((v) >> (l)) & ((1 << (1 + (u) - (l))) - 1))
+static uint64_t
+opt_pcbe_pes_addr(uint_t counter)
+{
+ ASSERT3U(counter, <, opd.opd_ncounters);
+ return (PES_BASE_ADDR + counter);
+}
+
+static uint64_t
+opt_pcbe_pes_ext_addr(uint_t counter)
+{
+ ASSERT3U(counter, <, opd.opd_ncounters);
+ return (PES_EXT_BASE_ADDR + 2 * counter);
+}
+
+static uint64_t
+opt_pcbe_pic_addr(uint_t counter)
+{
+ ASSERT3U(counter, <, opd.opd_ncounters);
+ return (PIC_BASE_ADDR + 2 * counter);
+}
+
+static uint64_t
+opt_pcbe_pic_ext_addr(uint_t counter)
+{
+ ASSERT3U(counter, <, opd.opd_ncounters);
+ return (PIC_EXT_BASE_ADDR + 2 * counter);
+}
static int
opt_pcbe_init(void)
{
- amd_event_t *evp;
- amd_generic_event_t *gevp;
+ const amd_event_t *evp;
+ const amd_generic_event_t *gevp;
amd_family = cpuid_getfamily(CPU);
+ amd_model = cpuid_getmodel(CPU);
/*
* Make sure this really _is_ an Opteron or Athlon 64 system. The kernel
@@ -445,14 +533,29 @@ opt_pcbe_init(void)
if (cpuid_getvendor(CPU) != X86_VENDOR_AMD || amd_family < 0xf)
return (-1);
- if (amd_family == 0xf)
+ if (amd_family == 0xf) {
/* Some tools expect this string for family 0fh */
(void) snprintf(amd_pcbe_impl_name, sizeof (amd_pcbe_impl_name),
"AMD Opteron & Athlon64");
- else
+ } else {
(void) snprintf(amd_pcbe_impl_name, sizeof (amd_pcbe_impl_name),
- "AMD Family %02xh%s", amd_family,
- AMD_PCBE_SUPPORTED(amd_family) ? "" :" (unsupported)");
+ "AMD Family %02xh", amd_family);
+ }
+
+ /*
+ * Determine whether or not the extended counter set is supported on
+ * this processor.
+ */
+ if (is_x86_feature(x86_featureset, X86FSET_AMD_PCEC)) {
+ opd.opd_ncounters = OPT_PCBE_EXT_NCOUNTERS;
+ opd.opd_pesf = opt_pcbe_pes_ext_addr;
+ opd.opd_picf = opt_pcbe_pic_ext_addr;
+ } else {
+ opd.opd_ncounters = OPT_PCBE_DEF_NCOUNTERS;
+ opd.opd_pesf = opt_pcbe_pes_addr;
+ opd.opd_picf = opt_pcbe_pic_addr;
+ }
+ opd.opd_cmask = (1 << opd.opd_ncounters) - 1;
/*
* Figure out processor revision here and assign appropriate
@@ -478,22 +581,17 @@ opt_pcbe_init(void)
amd_pcbe_cpuref = amd_fam_11h_bkdg;
amd_events = family_11h_events;
amd_generic_events = opt_generic_events;
+ } else if (amd_family == 0x17 && amd_model <= 0x2f) {
+ amd_pcbe_cpuref = amd_fam_17h_reg;
+ amd_events = opteron_pcbe_f17h_events;
+ amd_generic_events = family_17h_papi_events;
} else {
-
- amd_pcbe_cpuref = amd_generic_bkdg;
- (void) snprintf(amd_pcbe_cpuref, AMD_CPUREF_SIZE,
- "See BIOS and Kernel Developer's Guide " \
- "(BKDG) For AMD Family %02xh Processors. " \
- "(Note that this pcbe does not explicitly " \
- "support this family)", amd_family);
-
/*
- * For families that are not explicitly supported we'll use
- * events for family 0xf. Even if they are not quite right,
- * it's OK --- we state that pcbe is unsupported.
+ * Different families have different meanings on events and even
+ * worse (like family 15h), different constraints around
+ * programming these values.
*/
- amd_events = family_f_events;
- amd_generic_events = opt_generic_events;
+ return (-1);
}
/*
@@ -534,7 +632,7 @@ opt_pcbe_init(void)
static uint_t
opt_pcbe_ncounters(void)
{
- return (4);
+ return (opd.opd_ncounters);
}
static const char *
@@ -563,10 +661,10 @@ opt_pcbe_list_attrs(void)
return ("edge,pc,inv,cmask,umask");
}
-static amd_generic_event_t *
+static const amd_generic_event_t *
find_generic_event(char *name)
{
- amd_generic_event_t *gevp;
+ const amd_generic_event_t *gevp;
for (gevp = amd_generic_events; gevp->name != NULL; gevp++)
if (strcmp(name, gevp->name) == 0)
@@ -575,10 +673,10 @@ find_generic_event(char *name)
return (NULL);
}
-static amd_event_t *
+static const amd_event_t *
find_event(char *name)
{
- amd_event_t *evp;
+ const amd_event_t *evp;
for (evp = amd_events; evp->name != NULL; evp++)
if (strcmp(name, evp->name) == 0)
@@ -600,7 +698,7 @@ opt_pcbe_event_coverage(char *event)
/*
* Fortunately, all counters can count all events.
*/
- return (0xF);
+ return (opd.opd_cmask);
}
static uint64_t
@@ -610,7 +708,7 @@ opt_pcbe_overflow_bitmap(void)
* Unfortunately, this chip cannot detect which counter overflowed, so
* we must act as if they all did.
*/
- return (0xF);
+ return (opd.opd_cmask);
}
/*ARGSUSED*/
@@ -618,12 +716,12 @@ static int
opt_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
{
- opt_pcbe_config_t *cfg;
- amd_event_t *evp;
- amd_event_t ev_raw = { "raw", 0};
- amd_generic_event_t *gevp;
- int i;
- uint64_t evsel = 0, evsel_tmp = 0;
+ opt_pcbe_config_t *cfg;
+ const amd_event_t *evp;
+ amd_event_t ev_raw = { "raw", 0};
+ const amd_generic_event_t *gevp;
+ int i;
+ uint64_t evsel = 0, evsel_tmp = 0;
/*
* If we've been handed an existing configuration, we need only preset
@@ -635,7 +733,7 @@ opt_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
return (0);
}
- if (picnum >= 4)
+ if (picnum >= opd.opd_ncounters)
return (CPC_INVALID_PICNUM);
if ((evp = find_event(event)) == NULL) {
@@ -674,9 +772,10 @@ opt_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
/* Set bits [35:32] for extended part of Event Select field */
evsel_tmp = evp->emask & 0x0f00;
- evsel |= evsel_tmp << 24;
+ evsel |= evsel_tmp << OPT_PES_EVSELHI_SHIFT;
evsel |= evp->emask & 0x00ff;
+ evsel |= evp->unit << OPT_PES_UMASK_SHIFT;
if (flags & CPC_COUNT_USER)
evsel |= OPT_PES_USR;
@@ -722,8 +821,10 @@ opt_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
static void
opt_pcbe_program(void *token)
{
- opt_pcbe_config_t *cfgs[4] = { &nullcfgs[0], &nullcfgs[1],
- &nullcfgs[2], &nullcfgs[3] };
+ opt_pcbe_config_t *cfgs[OPT_PCBE_EXT_NCOUNTERS] = { &nullcfgs[0],
+ &nullcfgs[1], &nullcfgs[2],
+ &nullcfgs[3], &nullcfgs[4],
+ &nullcfgs[5] };
opt_pcbe_config_t *pcfg = NULL;
int i;
ulong_t curcr4 = getcr4();
@@ -743,7 +844,7 @@ opt_pcbe_program(void *token)
pcfg = (opt_pcbe_config_t *)kcpc_next_config(token, pcfg, NULL);
if (pcfg != NULL) {
- ASSERT(pcfg->opt_picno < 4);
+ ASSERT(pcfg->opt_picno < opd.opd_ncounters);
cfgs[pcfg->opt_picno] = pcfg;
}
} while (pcfg != NULL);
@@ -754,13 +855,13 @@ opt_pcbe_program(void *token)
* counters are all enabled as closely together in time as possible.
*/
- for (i = 0; i < 4; i++) {
- wrmsr(PES_BASE_ADDR + i, cfgs[i]->opt_evsel);
- wrmsr(PIC_BASE_ADDR + i, cfgs[i]->opt_rawpic);
+ for (i = 0; i < opd.opd_ncounters; i++) {
+ wrmsr(opd.opd_pesf(i), cfgs[i]->opt_evsel);
+ wrmsr(opd.opd_picf(i), cfgs[i]->opt_rawpic);
}
- for (i = 0; i < 4; i++) {
- wrmsr(PES_BASE_ADDR + i, cfgs[i]->opt_evsel |
+ for (i = 0; i < opd.opd_ncounters; i++) {
+ wrmsr(opd.opd_pesf(i), cfgs[i]->opt_evsel |
(uint64_t)(uintptr_t)OPT_PES_ENABLE);
}
}
@@ -770,8 +871,8 @@ opt_pcbe_allstop(void)
{
int i;
- for (i = 0; i < 4; i++)
- wrmsr(PES_BASE_ADDR + i, 0ULL);
+ for (i = 0; i < opd.opd_ncounters; i++)
+ wrmsr(opd.opd_pesf(i), 0ULL);
/*
* Disable non-privileged access to the counter registers.
@@ -782,16 +883,17 @@ opt_pcbe_allstop(void)
static void
opt_pcbe_sample(void *token)
{
- opt_pcbe_config_t *cfgs[4] = { NULL, NULL, NULL, NULL };
+ opt_pcbe_config_t *cfgs[OPT_PCBE_EXT_NCOUNTERS] = { NULL, NULL,
+ NULL, NULL, NULL, NULL };
opt_pcbe_config_t *pcfg = NULL;
int i;
- uint64_t curpic[4];
- uint64_t *addrs[4];
+ uint64_t curpic[OPT_PCBE_EXT_NCOUNTERS];
+ uint64_t *addrs[OPT_PCBE_EXT_NCOUNTERS];
uint64_t *tmp;
int64_t diff;
- for (i = 0; i < 4; i++)
- curpic[i] = rdmsr(PIC_BASE_ADDR + i);
+ for (i = 0; i < opd.opd_ncounters; i++)
+ curpic[i] = rdmsr(opd.opd_picf(i));
/*
* Query kernel for all configs which are co-programmed.
@@ -800,13 +902,13 @@ opt_pcbe_sample(void *token)
pcfg = (opt_pcbe_config_t *)kcpc_next_config(token, pcfg, &tmp);
if (pcfg != NULL) {
- ASSERT(pcfg->opt_picno < 4);
+ ASSERT3U(pcfg->opt_picno, <, opd.opd_ncounters);
cfgs[pcfg->opt_picno] = pcfg;
addrs[pcfg->opt_picno] = tmp;
}
} while (pcfg != NULL);
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < opd.opd_ncounters; i++) {
if (cfgs[i] == NULL)
continue;
diff --git a/usr/src/uts/intel/pcbe/opteron_pcbe_table.h b/usr/src/uts/intel/pcbe/opteron_pcbe_table.h
new file mode 100644
index 0000000000..78a504b7ec
--- /dev/null
+++ b/usr/src/uts/intel/pcbe/opteron_pcbe_table.h
@@ -0,0 +1,105 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * 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
+ * documentation and/or other materials provided with the distribution.
+ * * 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.
+ */
+
+/*
+ * Portions Copyright 2009 Advanced Micro Devices, Inc.
+ * Copyright 2019 Joyent, Inc.
+ */
+
+/*
+ * Structure definition for AMD PCBE events.
+ */
+
+#ifndef _OPTERON_PCBE_TABLE_H
+#define _OPTERON_PCBE_TABLE_H
+
+/*
+ * Structure definition for PCBE events.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+
+
+typedef struct _amd_event {
+ char *name;
+ uint16_t emask; /* Event mask setting */
+ uint8_t unit;
+} amd_event_t;
+
+typedef struct _amd_generic_event {
+ char *name;
+ char *event;
+ uint8_t umask;
+} amd_generic_event_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OPTERON_PCBE_TABLE_H */
diff --git a/usr/src/uts/intel/sys/x86_archext.h b/usr/src/uts/intel/sys/x86_archext.h
index be50ef1292..585a6576bc 100644
--- a/usr/src/uts/intel/sys/x86_archext.h
+++ b/usr/src/uts/intel/sys/x86_archext.h
@@ -160,6 +160,10 @@ extern "C" {
#define CPUID_AMD_EDX_3DNowx 0x40000000 /* AMD: extensions to 3DNow! */
#define CPUID_AMD_EDX_3DNow 0x80000000 /* AMD: 3DNow! instructions */
+/*
+ * AMD extended function 0x80000001 %ecx
+ */
+
#define CPUID_AMD_ECX_AHF64 0x00000001 /* LAHF and SAHF in long mode */
#define CPUID_AMD_ECX_CMP_LGCY 0x00000002 /* AMD: multicore chip */
#define CPUID_AMD_ECX_SVM 0x00000004 /* AMD: secure VM */
@@ -563,6 +567,7 @@ extern "C" {
#define X86FSET_FMA4 89
#define X86FSET_TBM 90
#define X86FSET_AVX512VNNI 91
+#define X86FSET_AMD_PCEC 92
/*
* Intel Deep C-State invariant TSC in leaf 0x80000007.
@@ -932,7 +937,7 @@ extern "C" {
#if defined(_KERNEL) || defined(_KMEMUSER)
-#define NUM_X86_FEATURES 92
+#define NUM_X86_FEATURES 93
extern uchar_t x86_featureset[];
extern void free_x86_featureset(void *featureset);