Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F177409762
D61195.1794309082.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
20 KB
Referenced Files
None
Subscribers
None
D61195.1794309082.diff
View Options
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -267,6 +267,7 @@
inet.4 \
inet6.4 \
intel_pmc.4 \
+ ${_intel_cpu_cstate.4} \
intpm.4 \
intro.4 \
${_io.4} \
@@ -957,6 +958,7 @@
.if ${MACHINE_CPUARCH} == "amd64"
_acpi_apple.4= acpi_apple.4
+_intel_cpu_cstate.4= intel_cpu_cstate.4
_ioat.4= ioat.4
_iwx.4= iwx.4
_nvdimm.4= nvdimm.4
diff --git a/share/man/man4/intel_cpu_cstate.4 b/share/man/man4/intel_cpu_cstate.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/intel_cpu_cstate.4
@@ -0,0 +1,90 @@
+.\"
+.\" Copyright (c) 2026 The FreeBSD Foundation
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.\" This documentation was written by Olivier Certner <olce@FreeBSD.org> at
+.\" Kumacom SARL under sponsorship from the FreeBSD Foundation.
+.\"
+.Dd October 22, 2026
+.Dt INTEL_CPU_CSTATE 4
+.Os
+.Sh NAME
+.Nm intel_cpu_cstate
+.Nd Intel CPU-specific power states driver
+.Sh SYNOPSIS
+.Cd "device intel_cpu_cstate"
+.Sh DESCRIPTION
+The
+.Nm
+driver exposes CPU-specific C-state residency counters for Intel CPUs supporting
+them.
+.Pp
+Each residency counter applies to a scope among
+.Ql core ,
+.Ql module
+or
+.Ql package ,
+meaning it reports for how long all logical processors in that scope could reach
+at least its associated C-state.
+Scope
+.Ql module
+includes part of cores in a package, e.g., those sharing an L2 cache, but its
+exact definition is model-specific.
+.Pp
+The information is exposed in the form of sysctl knobs as described in the
+.Sx SYSCTL VARIABLES
+section.
+.Sh HARDWARE
+The
+.Nm
+driver supports available residency counters on Intel CPUs in the form of
+Model-Specific Registers (MSRs).
+Each CPU or CPU family has its own MSRs.
+The driver tries to autodetect those that are available.
+All CPUs up to at least the Lunar Lake microarchitecture are supported
+.Pq e.g., Intel Core Ultra Series 2
+and it is expected that CPUs with Panther Lake or Wildcat Lake
+.Pq e.g., Intel Core (Ultra) Series 3
+or Grand Ridge or Cleanwater Forest microarchitectures also work.
+.Sh SYSCTL VARIABLES
+All information exposed by the
+.Nm
+driver hangs from the
+.Va dev.cpu.N.cstate
+nodes.
+Under them, nodes are created per available counter scopes.
+In turn, under them, a knob is created for each C-state having a residency
+counter.
+As an example, the knob for a package C3 state residency counter on CPU 0 is
+.Va dev.cpu.0.cstate.package.c3 .
+.Pp
+All values are reported in microseconds (μs).
+.Pp
+A counter that stays at 0 may indicate that the corresponding C-state was never
+entered or that the CPU does not actually report residency for that case and the
+corresponding MSR, although present, is wired to 0.
+.Sh AUTHORS
+.An Olivier Certner Aq Mt olce@FreeBSD.org
+.Sh CAVEATS
+Note that the referred C-states differ from ACPI ones or those passed as a hint
+to the
+.Ql MWAIT
+instruction when the MWAIT Power Management Extension is supported.
+.Pp
+This driver is based on the Intel Software Developer Manual Volume 4 as of
+September 2026.
+.Pp
+Residency counters are accessed through non-architectural Model-Specific
+Registers (MSRs), meaning the latter's indices can vary depending on the CPU
+model.
+Barring quirks for specific models, this driver uses a default MSR index for
+each counter which was manually derived by looking up the most frequently and
+recently used index across all models for that counter.
+Newer processors may introduce a different index per counter, in which case they
+will not be supported automatically and will require a driver update.
+If an MSR is present at the default index, it is assumed to be the expected
+residency counter.
+Wrong information would be displayed if newer processors or some specific older
+models reuse that index for another register, which also requires a driver
+update to fix.
diff --git a/sys/conf/files.amd64 b/sys/conf/files.amd64
--- a/sys/conf/files.amd64
+++ b/sys/conf/files.amd64
@@ -220,6 +220,7 @@
clean "ice_ddp.fw"
dev/intel/hfsts.c optional hfsts pci
dev/intel/hfsts_isa.c optional hfsts isa pci
+dev/intel/intel_cpu_cstate.c optional intel_cpu_cstate
dev/ioat/ioat.c optional ioat pci
dev/ioat/ioat_test.c optional ioat pci
dev/ixl/if_ixl.c optional ixl pci \
diff --git a/sys/dev/intel/intel_cpu_cstate.c b/sys/dev/intel/intel_cpu_cstate.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/intel/intel_cpu_cstate.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2026 The FreeBSD Foundation
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * This software was developed by Olivier Certner <olce@FreeBSD.org> at Kumacom
+ * SARL under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/libkern.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <machine/_stdint.h>
+#include <x86/clock.h>
+#include <machine/cpufunc.h>
+#include <x86/cputypes.h>
+#include <machine/specialreg.h>
+#include <x86/x86_var.h>
+
+
+/*
+ * Full 64-bit multiplication and 64-bit shifted inverse. Used to convert
+ * counters in TSC units to μs. '__uint128_t' cannot be used in the kernel
+ * (support functions are not present).
+ */
+
+static void
+mul_64_64_128(const uint64_t a, const uint64_t b, uint64_t *const hi,
+ uint64_t *const lo)
+{
+ const uint64_t a_hi = a >> 32;
+ const uint64_t a_lo = a & UINT32_MAX;
+ const uint64_t b_hi = b >> 32;
+ const uint64_t b_lo = b & UINT32_MAX;
+ uint64_t mid, tmp;
+
+ /*
+ * Decompose the 64-bit numbers in two halves, which leads to products
+ * between 32-bit quantities only, fitting into a 64-bit integer type.
+ */
+ *hi = a_hi * b_hi;
+ *lo = a_lo * b_lo;
+ mid = a_hi * b_lo;
+ *hi += mid >> 32;
+ tmp = (mid & UINT32_MAX) << 32;
+ *lo += tmp;
+ if (tmp > *lo)
+ /* Carry. */
+ (*hi)++;
+ mid = a_lo * b_hi;
+ *hi += mid >> 32;
+ tmp = (mid & UINT32_MAX) << 32;
+ *lo += tmp;
+ if (tmp > *lo)
+ /* Carry. */
+ (*hi)++;
+}
+
+static uint64_t
+compute_2_64_inv(const uint64_t val)
+{
+ uint64_t lower, greater;
+ int ls;
+
+ if (val <= 1)
+ return (UINT64_MAX);
+
+ ls = flsll(val);
+ greater = 1ULL << (64 - ls + 1);
+ if (ls == ffsll(val))
+ return (greater);
+ lower = greater >> 1;
+
+ while (lower != greater - 1) {
+ const uint64_t cand = (lower + greater) / 2;
+ uint64_t hi, lo;
+
+ mul_64_64_128(cand, val, &hi, &lo);
+ if (hi != 0) {
+ MPASS(hi == 1);
+ if (lo == 0)
+ return (cand);
+ greater = cand;
+ } else
+ lower = cand;
+ }
+
+ return (lower);
+}
+
+static void intel_cpu_cstate_identify(driver_t *driver, device_t parent);
+static int intel_cpu_cstate_probe(device_t dev);
+static int intel_cpu_cstate_attach(device_t dev);
+static int intel_cpu_cstate_detach(device_t dev);
+
+static SYSCTL_NODE(_debug, OID_AUTO, intel_cpu_cstate, CTLFLAG_MPSAFE, NULL,
+ "intel_cpu_cstate(4) debug controls");
+
+static bool raw_counters;
+SYSCTL_BOOL(_debug_intel_cpu_cstate, OID_AUTO, raw_counters, CTLFLAG_RDTUN,
+ &raw_counters, 0,
+ "If true, reported residency values are always raw MSR values.");
+
+enum intel_cpu_cstate_residency_type {
+ CORE_C1,
+ CORE_C3,
+ CORE_C6,
+ CORE_C7,
+ MOD_C0,
+ MOD_C6,
+ PKG_C2,
+ PKG_C3,
+ PKG_C4,
+ PKG_C6,
+ PKG_C7,
+ PKG_C8,
+ PKG_C9,
+ PKG_C10,
+ RESIDENCY_TYPE_NB,
+};
+
+struct intel_cpu_cstate_residency_type_info {
+ const char *scope;
+ const char *level;
+};
+
+static const struct intel_cpu_cstate_residency_type_info
+cst_residency_type_info[RESIDENCY_TYPE_NB] = {
+ [CORE_C1] = { "core", "c1" },
+ [CORE_C3] = { "core", "c3" },
+ [CORE_C6] = { "core", "c6" },
+ [CORE_C7] = { "core", "c7" },
+ [MOD_C0] = { "module", "c0" },
+ [MOD_C6] = { "module", "c6" },
+ [PKG_C2] = { "package", "c2" },
+ [PKG_C3] = { "package", "c3" },
+ [PKG_C4] = { "package", "c4" },
+ [PKG_C6] = { "package", "c6" },
+ [PKG_C7] = { "package", "c7" },
+ [PKG_C8] = { "package", "c8" },
+ [PKG_C9] = { "package", "c9" },
+ [PKG_C10] = { "package", "c10" },
+};
+
+enum intel_cpu_cstate_residency_frequency {
+ CST_TSC = 0,
+ CST_1MHZ,
+};
+
+struct intel_cpu_cstate_residency_info {
+ /* 0 when the info is not available. */
+ uint32_t msr_index;
+ enum intel_cpu_cstate_residency_frequency frequency;
+};
+
+const struct intel_cpu_cstate_residency_info
+common_cst_residency_info[RESIDENCY_TYPE_NB] = {
+ [CORE_C1] = { .msr_index = MSR_CORE_C1_RESIDENCY },
+ [CORE_C3] = { .msr_index = MSR_CORE_C3_RESIDENCY },
+ [CORE_C6] = { .msr_index = MSR_CORE_C6_RESIDENCY },
+ [CORE_C7] = { .msr_index = MSR_CORE_C7_RESIDENCY },
+ /* Does not exist except on Phi. */
+ [MOD_C0] = { .msr_index = 0 },
+ [MOD_C6] = { .msr_index = MSR_MC6_RESIDENCY },
+ [PKG_C2] = { .msr_index = MSR_PKG_C2_RESIDENCY },
+ [PKG_C3] = { .msr_index = MSR_PKG_C3_RESIDENCY },
+ /* Does not exist except on Atom 06_27h. */
+ [PKG_C4] = { .msr_index = 0 },
+ [PKG_C6] = { .msr_index = MSR_PKG_C6_RESIDENCY },
+ [PKG_C7] = { .msr_index = MSR_PKG_C7_RESIDENCY },
+ [PKG_C8] = { .msr_index = MSR_PKG_C8_RESIDENCY },
+ [PKG_C9] = { .msr_index = MSR_PKG_C9_RESIDENCY },
+ [PKG_C10] = { .msr_index = MSR_PKG_C10_RESIDENCY },
+};
+
+struct intel_cpu_cstate_softc {
+ uint64_t tsc_inv; /* 2^64 / tsc_freq, or 0 on no conversion. */
+ struct intel_cpu_cstate_residency_info info[RESIDENCY_TYPE_NB];
+};
+
+static int
+sysctl_residency_counter(SYSCTL_HANDLER_ARGS)
+{
+ /*
+ * Frequency of reported residency counters.
+ *
+ * Currently, counters we report increment once per μs. If changing
+ * that, do not forget to also change the sysctl knob descriptions (they
+ * mention the unit).
+ */
+ const uint64_t F = 1000000;
+ const device_t dev = arg1;
+ struct intel_cpu_cstate_softc *const sc = device_get_softc(dev);
+ const size_t type = arg2;
+ const uint32_t msr = sc->info[type].msr_index;
+ uint64_t val, Fv_hi, Fv_lo, hi, lo;
+
+ KASSERT(msr != 0,
+ ("%s: Knob registered for non-existent MSR for type %zu",
+ __func__, type));
+ x86_msr_op(msr, MSR_OP_READ | MSR_OP_RENDEZVOUS_ONE |
+ MSR_OP_CPUID(device_get_unit(dev)), 0, &val);
+
+ /* Convert to μs. */
+ switch (sc->info[type].frequency) {
+ case CST_TSC:
+ /*
+ * Report the raw value when not enough resolution or precision,
+ * or explicitly requested to.
+ */
+ if (sc->tsc_inv == 0)
+ break;
+
+ /*
+ * Since 'val' is 64-bit without restrictions, multiplying it
+ * with our unit (here, C = 10^6, for microseconds) can overflow
+ * 2^64, so we use our full multiplication routine.
+ */
+ mul_64_64_128(F, val, &Fv_hi, &Fv_lo);
+ /*
+ * Division by 'tsc_freq' through multiplication by
+ * 2^64/'tsc_freq'. We need a 128-bit result for the 'Fv_lo'
+ * limb multiplication, as we are only concerned with the high
+ * 64 bits of the result (the low 64 bits are after the comma in
+ * the final result).
+ */
+ mul_64_64_128(Fv_lo, sc->tsc_inv, &hi, &lo);
+ /*
+ * We do not need the high 64 bits for the 'Fv_hi'
+ * multiplication, as we report only a 64-bit counter which
+ * naturally wraps around.
+ */
+ val = Fv_hi * sc->tsc_inv + hi;
+ break;
+
+ case CST_1MHZ:
+ /* No conversion needed. */
+ break;
+
+ default:
+ __assert_unreachable();
+ }
+
+ return (sysctl_handle_64(oidp, &val, 0, req));
+}
+
+static void
+intel_cpu_cstate_identify(driver_t *driver, device_t parent)
+{
+ const int unit = device_get_unit(parent);
+
+ /*
+ * Models with non-invariant TSC are not supposed to export residency
+ * MSRs. We leverage invariance to precompute an inverse of the TSC
+ * frequency to then ease conversion to μs.
+ */
+ if (cpu_vendor_id != CPU_VENDOR_INTEL || (!raw_counters &&
+ (!tsc_is_invariant || tsc_freq == 0)))
+ return;
+ if (resource_disabled("intel_cpu_cstate", 0))
+ return;
+ if (device_find_child(parent, "intel_cpu_cstate", unit) != NULL)
+ return;
+
+ if (BUS_ADD_CHILD(parent, 0, "intel_cpu_cstate", unit) == NULL)
+ device_printf(parent, "Cannot add driver under %s!\n",
+ device_get_nameunit(parent));
+}
+
+static int
+intel_cpu_cstate_probe(device_t dev)
+{
+ device_set_desc(dev, "Intel CPU internal C-states residency");
+
+ return (BUS_PROBE_NOWILDCARD);
+}
+
+/*
+ * Quirks for specific models.
+ *
+ * The attach() routine will probe declared MSRs and stop referencing
+ * those that do not exist. Also, specific MSRs installed here will be
+ * removed from other info types.
+ */
+static bool
+intel_cpu_cstate_set_infos_quirk(struct intel_cpu_cstate_softc *const sc)
+{
+ if (CPUID_TO_FAMILY(cpu_id) != 0x6)
+ /*
+ * Using the most frequent values is the most future-proof
+ * strategy.
+ */
+ return (false);
+
+ switch (CPUID_TO_MODEL(cpu_id)) {
+ case 0x57:
+ case 0x85:
+ /* Xeon Phi */
+ sc->info[CORE_C6].msr_index = MSR_CORE_C6_RESIDENCY_PHI;
+ sc->info[MOD_C0].msr_index = MSR_MC0_RESIDENCY_PHI;
+ sc->info[MOD_C6].msr_index = MSR_MC6_RESIDENCY_PHI;
+ return (true);
+
+ case 0x27:
+ /* Atom 45nm and 32nm */
+ sc->info[PKG_C2].msr_index = MSR_PKG_C2_RESIDENCY_ATOM;
+ sc->info[PKG_C2].frequency = CST_1MHZ;
+ sc->info[PKG_C4].msr_index = MSR_PKG_C4_RESIDENCY_ATOM;
+ sc->info[PKG_C4].frequency = CST_1MHZ;
+ sc->info[PKG_C6].msr_index = MSR_PKG_C6_RESIDENCY_ATOM;
+ sc->info[PKG_C6].frequency = CST_1MHZ;
+ return (true);
+
+ case 0x37:
+ case 0x4a:
+ case 0x4d:
+ case 0x5a:
+ case 0x5d:
+ /* Atom Silvermont, Airmont */
+ sc->info[MOD_C6].frequency = CST_1MHZ;
+ sc->info[PKG_C6].msr_index = MSR_PKG_C6_RESIDENCY_ATOM;
+ return (true);
+
+ case 0x97: /* Gen 12 */
+ case 0x9a: /* Gen 12 */
+ case 0xba: /* Gen 13 */
+ case 0xb7: /* Gen 13 */
+ case 0xbf: /* Gen 13 */
+ sc->info[MOD_C6].frequency = CST_1MHZ;
+ return (true);
+ }
+
+ return (false);
+}
+
+/*
+ * Remove MSRs introduced by quirks from the set of default/inherited MSRs
+ * (because a single MSR cannot convey different residency information at once;
+ * we might want to alias several C-states to the same counter though).
+ */
+static void
+intel_cpu_cstate_remove_duplicate_msrs(struct intel_cpu_cstate_softc *const sc)
+{
+ for (size_t i = 0; i < RESIDENCY_TYPE_NB; i++) {
+ const uint32_t msr_idx = sc->info[i].msr_index;
+
+ /* Was the index changed by quirks? */
+ if (msr_idx == common_cst_residency_info[i].msr_index)
+ continue;
+
+ for (size_t j = 0; j < RESIDENCY_TYPE_NB; j++)
+ /*
+ * If the new index was present in the defaults and was
+ * not changed, remove it.
+ */
+ if (common_cst_residency_info[j].msr_index == msr_idx &&
+ sc->info[j].msr_index == msr_idx)
+ sc->info[j].msr_index = 0;
+ }
+}
+
+static int
+intel_cpu_cstate_attach(device_t dev)
+{
+ /*
+ * Frequency of TSC under which residency counters are reported raw
+ * instead of converting them to μs (see sysctl_residency_counter()).
+ */
+ const uint64_t min_resolution = 1000000;
+ /*
+ * Minimum of the (shifted) inverse of the TSC, corresponding to the
+ * inverse of the maximum error caused by multiplying by the TSC
+ * inverse.
+ */
+ const uint64_t max_inverse_error = 1000000;
+ struct intel_cpu_cstate_softc *const sc = device_get_softc(dev);
+ const size_t all_infos_size = sizeof(common_cst_residency_info);
+ struct sysctl_ctx_list *const sysctl_ctx = device_get_sysctl_ctx(dev);
+ struct sysctl_oid *sysctl_top = NULL;
+
+ /*
+ * Precompute the TSC inverse (shifted by 64-bit) used to output
+ * residency values in duration with SI units. We impose a minimum
+ * resolution, else we just report the raw counters. We also impose
+ * a minimum (shifted) inverse resolution, to limit the conversion error
+ * to μs. We should never hit these bounds with the current values, as
+ * the TSC is expected to be in the order of magnitude of GHz (and up to
+ * 3 order of magnitudes below and above this also work).
+ */
+ if (raw_counters || tsc_freq < min_resolution)
+ sc->tsc_inv = 0;
+ else {
+ sc->tsc_inv = compute_2_64_inv(tsc_freq);
+ if (sc->tsc_inv < max_inverse_error)
+ sc->tsc_inv = 0;
+ }
+
+ /* Copy the common configuration. */
+ memcpy(sc->info, common_cst_residency_info, all_infos_size);
+
+ /* Apply quirks as necessary. */
+ if (intel_cpu_cstate_set_infos_quirk(sc))
+ intel_cpu_cstate_remove_duplicate_msrs(sc);
+
+ /*
+ * Check that MSRs actually exist, and for these create sysctl knobs.
+ */
+ for (size_t i = 0; i < RESIDENCY_TYPE_NB; i++) {
+ uint32_t *const msr_idx = &sc->info[i].msr_index;
+ const struct intel_cpu_cstate_residency_type_info *const
+ type_info = &cst_residency_type_info[i];
+ char buf[128];
+ char buf2[8];
+ struct sysctl_oid *node;
+ uint64_t val;
+ int error;
+
+ if (*msr_idx == 0)
+ continue;
+
+ error = rdmsr_safe(*msr_idx, &val);
+ if (error != 0) {
+ *msr_idx = 0;
+ continue;
+ }
+
+ if (sysctl_top == NULL) {
+ sysctl_top = SYSCTL_ADD_NODE(sysctl_ctx,
+ SYSCTL_CHILDREN(device_get_sysctl_tree(
+ device_get_parent(dev))), OID_AUTO, "cstate",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
+ "CPU-specific C-states");
+ KASSERT(sysctl_top != NULL,
+ ("%s: Error creating top sysctl node",
+ device_get_nameunit(dev)));
+ }
+
+ snprintf(buf, sizeof(buf), "C-state knobs with %s scope",
+ type_info->scope);
+ node = SYSCTL_ADD_NODE(sysctl_ctx, SYSCTL_CHILDREN(sysctl_top),
+ OID_AUTO, type_info->scope, CTLFLAG_RD | CTLFLAG_MPSAFE,
+ NULL, buf);
+ KASSERT(node != NULL,
+ ("%s: Error creating scope '%s' sysctl node",
+ device_get_nameunit(dev), type_info->scope));
+
+ /* Capitalize the level in the description. */
+ strlcpy(buf2, type_info->level, sizeof(buf2));
+ buf2[0] = toupper(buf2[0]);
+ snprintf(buf, sizeof(buf), "Per-%s %s residency counter (%s)",
+ type_info->scope, buf2, sc->tsc_inv != 0 ||
+ sc->info[i].frequency == CST_1MHZ ? "μs" : "TSC ticks");
+ node = SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(node),
+ OID_AUTO, type_info->level, CTLTYPE_U64 | CTLFLAG_RD |
+ CTLFLAG_MPSAFE, dev, i, sysctl_residency_counter, "QU",
+ buf);
+ KASSERT(node != NULL,
+ ("%s: Error creating '%s' sysctl node for scope '%s'",
+ device_get_nameunit(dev), type_info->level,
+ type_info->scope));
+ }
+
+ return (0);
+}
+
+static int
+intel_cpu_cstate_detach(device_t dev)
+{
+ return (0);
+}
+
+static device_method_t intel_cpu_cstate_methods[] = {
+ DEVMETHOD(device_identify, intel_cpu_cstate_identify),
+ DEVMETHOD(device_probe, intel_cpu_cstate_probe),
+ DEVMETHOD(device_attach, intel_cpu_cstate_attach),
+ DEVMETHOD(device_detach, intel_cpu_cstate_detach),
+ DEVMETHOD_END
+};
+
+static driver_t intel_cpu_cstate_driver = {
+ "intel_cpu_cstate",
+ intel_cpu_cstate_methods,
+ sizeof(struct intel_cpu_cstate_softc),
+};
+
+DRIVER_MODULE(intel_cpu_cstate, cpu, intel_cpu_cstate_driver, NULL, NULL);
+MODULE_VERSION(intel_cpu_cstate, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -179,6 +179,7 @@
${_igc} \
imgact_binmisc \
${_imx} \
+ ${_intel_cpu_cstate} \
${_intel_pmc} \
${_intelspi} \
${_io} \
@@ -879,6 +880,7 @@
_enic= enic
_hfsts= hfsts
_iavf= iavf
+_intel_cpu_cstate= intel_cpu_cstate
_ioat= ioat
_iwx= iwx
_ixl= ixl
diff --git a/sys/modules/intel_cpu_cstate/Makefile b/sys/modules/intel_cpu_cstate/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/intel_cpu_cstate/Makefile
@@ -0,0 +1,6 @@
+.PATH: ${SRCTOP}/sys/dev/intel
+KMOD= intel_cpu_cstate
+SRCS= intel_cpu_cstate.c
+SRCS+= bus_if.h device_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/x86/include/specialreg.h b/sys/x86/include/specialreg.h
--- a/sys/x86/include/specialreg.h
+++ b/sys/x86/include/specialreg.h
@@ -750,6 +750,41 @@
#define MSR_IA32_HWP_REQUEST 0x774
#define MSR_IA32_HWP_STATUS 0x777
+/*
+ * C-state MSRs
+ *
+ * Grouped by applicable scope and ordered by C-states, the common value being
+ * listed first in the case of differing MSR indexes for different CPUs. Only
+ * a single name is not suffixed with a particular CPU model for a given MSR
+ * index.
+ */
+/* Core scope. */
+#define MSR_CORE_C1_RESIDENCY 0x660
+#define MSR_CORE_C3_RESIDENCY 0x3fc
+#define MSR_CORE_C6_RESIDENCY 0x3fd
+#define MSR_CORE_C6_RESIDENCY_PHI 0x3ff
+#define MSR_CORE_C7_RESIDENCY 0x3fe
+
+/* Module (cores sharing cache) scope. */
+#define MSR_MC0_RESIDENCY_PHI 0x3fc
+/* Atom Silvermont, Airmont, and Gen12 and Gen13: 1Mhz frequency, else TSC. */
+#define MSR_MC6_RESIDENCY 0x664
+#define MSR_MC6_RESIDENCY_PHI 0x3fd
+
+/* Package scope. */
+#define MSR_PKG_CST_CONFIG_CONTROL 0x0e2
+#define MSR_PKG_C2_RESIDENCY 0x60d
+#define MSR_PKG_C2_RESIDENCY_ATOM 0x3f8 /* Atom 06_27h */
+#define MSR_PKG_C3_RESIDENCY 0x3f8
+#define MSR_PKG_C4_RESIDENCY_ATOM 0x3f9 /* Atom 06_27h */
+#define MSR_PKG_C6_RESIDENCY 0x3f9
+/* Atom 06_27h, Silvermont, Airmont */
+#define MSR_PKG_C6_RESIDENCY_ATOM 0x3fa
+#define MSR_PKG_C7_RESIDENCY 0x3fa
+#define MSR_PKG_C8_RESIDENCY 0x630
+#define MSR_PKG_C9_RESIDENCY 0x631
+#define MSR_PKG_C10_RESIDENCY 0x632
+
/*
* VMX MSRs
*/
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Nov 10, 11:11 AM (2 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41637556
Default Alt Text
D61195.1794309082.diff (20 KB)
Attached To
Mode
D61195: intel_cpu_cstate(4): New driver, reports CPU-specific C-states residency
Attached
Detach File
Event Timeline