cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

intel_epb.c (6750B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Intel Performance and Energy Bias Hint support.
      4 *
      5 * Copyright (C) 2019 Intel Corporation
      6 *
      7 * Author:
      8 *	Rafael J. Wysocki <rafael.j.wysocki@intel.com>
      9 */
     10
     11#include <linux/cpuhotplug.h>
     12#include <linux/cpu.h>
     13#include <linux/device.h>
     14#include <linux/kernel.h>
     15#include <linux/string.h>
     16#include <linux/syscore_ops.h>
     17#include <linux/pm.h>
     18
     19#include <asm/cpu_device_id.h>
     20#include <asm/cpufeature.h>
     21#include <asm/msr.h>
     22
     23/**
     24 * DOC: overview
     25 *
     26 * The Performance and Energy Bias Hint (EPB) allows software to specify its
     27 * preference with respect to the power-performance tradeoffs present in the
     28 * processor.  Generally, the EPB is expected to be set by user space (directly
     29 * via sysfs or with the help of the x86_energy_perf_policy tool), but there are
     30 * two reasons for the kernel to update it.
     31 *
     32 * First, there are systems where the platform firmware resets the EPB during
     33 * system-wide transitions from sleep states back into the working state
     34 * effectively causing the previous EPB updates by user space to be lost.
     35 * Thus the kernel needs to save the current EPB values for all CPUs during
     36 * system-wide transitions to sleep states and restore them on the way back to
     37 * the working state.  That can be achieved by saving EPB for secondary CPUs
     38 * when they are taken offline during transitions into system sleep states and
     39 * for the boot CPU in a syscore suspend operation, so that it can be restored
     40 * for the boot CPU in a syscore resume operation and for the other CPUs when
     41 * they are brought back online.  However, CPUs that are already offline when
     42 * a system-wide PM transition is started are not taken offline again, but their
     43 * EPB values may still be reset by the platform firmware during the transition,
     44 * so in fact it is necessary to save the EPB of any CPU taken offline and to
     45 * restore it when the given CPU goes back online at all times.
     46 *
     47 * Second, on many systems the initial EPB value coming from the platform
     48 * firmware is 0 ('performance') and at least on some of them that is because
     49 * the platform firmware does not initialize EPB at all with the assumption that
     50 * the OS will do that anyway.  That sometimes is problematic, as it may cause
     51 * the system battery to drain too fast, for example, so it is better to adjust
     52 * it on CPU bring-up and if the initial EPB value for a given CPU is 0, the
     53 * kernel changes it to 6 ('normal').
     54 */
     55
     56static DEFINE_PER_CPU(u8, saved_epb);
     57
     58#define EPB_MASK	0x0fULL
     59#define EPB_SAVED	0x10ULL
     60#define MAX_EPB		EPB_MASK
     61
     62enum energy_perf_value_index {
     63	EPB_INDEX_PERFORMANCE,
     64	EPB_INDEX_BALANCE_PERFORMANCE,
     65	EPB_INDEX_NORMAL,
     66	EPB_INDEX_BALANCE_POWERSAVE,
     67	EPB_INDEX_POWERSAVE,
     68};
     69
     70static u8 energ_perf_values[] = {
     71	[EPB_INDEX_PERFORMANCE] = ENERGY_PERF_BIAS_PERFORMANCE,
     72	[EPB_INDEX_BALANCE_PERFORMANCE] = ENERGY_PERF_BIAS_BALANCE_PERFORMANCE,
     73	[EPB_INDEX_NORMAL] = ENERGY_PERF_BIAS_NORMAL,
     74	[EPB_INDEX_BALANCE_POWERSAVE] = ENERGY_PERF_BIAS_BALANCE_POWERSAVE,
     75	[EPB_INDEX_POWERSAVE] = ENERGY_PERF_BIAS_POWERSAVE,
     76};
     77
     78static int intel_epb_save(void)
     79{
     80	u64 epb;
     81
     82	rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
     83	/*
     84	 * Ensure that saved_epb will always be nonzero after this write even if
     85	 * the EPB value read from the MSR is 0.
     86	 */
     87	this_cpu_write(saved_epb, (epb & EPB_MASK) | EPB_SAVED);
     88
     89	return 0;
     90}
     91
     92static void intel_epb_restore(void)
     93{
     94	u64 val = this_cpu_read(saved_epb);
     95	u64 epb;
     96
     97	rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb);
     98	if (val) {
     99		val &= EPB_MASK;
    100	} else {
    101		/*
    102		 * Because intel_epb_save() has not run for the current CPU yet,
    103		 * it is going online for the first time, so if its EPB value is
    104		 * 0 ('performance') at this point, assume that it has not been
    105		 * initialized by the platform firmware and set it to 6
    106		 * ('normal').
    107		 */
    108		val = epb & EPB_MASK;
    109		if (val == ENERGY_PERF_BIAS_PERFORMANCE) {
    110			val = energ_perf_values[EPB_INDEX_NORMAL];
    111			pr_warn_once("ENERGY_PERF_BIAS: Set to 'normal', was 'performance'\n");
    112		}
    113	}
    114	wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, (epb & ~EPB_MASK) | val);
    115}
    116
    117static struct syscore_ops intel_epb_syscore_ops = {
    118	.suspend = intel_epb_save,
    119	.resume = intel_epb_restore,
    120};
    121
    122static const char * const energy_perf_strings[] = {
    123	[EPB_INDEX_PERFORMANCE] = "performance",
    124	[EPB_INDEX_BALANCE_PERFORMANCE] = "balance-performance",
    125	[EPB_INDEX_NORMAL] = "normal",
    126	[EPB_INDEX_BALANCE_POWERSAVE] = "balance-power",
    127	[EPB_INDEX_POWERSAVE] = "power",
    128};
    129
    130static ssize_t energy_perf_bias_show(struct device *dev,
    131				     struct device_attribute *attr,
    132				     char *buf)
    133{
    134	unsigned int cpu = dev->id;
    135	u64 epb;
    136	int ret;
    137
    138	ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
    139	if (ret < 0)
    140		return ret;
    141
    142	return sprintf(buf, "%llu\n", epb);
    143}
    144
    145static ssize_t energy_perf_bias_store(struct device *dev,
    146				      struct device_attribute *attr,
    147				      const char *buf, size_t count)
    148{
    149	unsigned int cpu = dev->id;
    150	u64 epb, val;
    151	int ret;
    152
    153	ret = __sysfs_match_string(energy_perf_strings,
    154				   ARRAY_SIZE(energy_perf_strings), buf);
    155	if (ret >= 0)
    156		val = energ_perf_values[ret];
    157	else if (kstrtou64(buf, 0, &val) || val > MAX_EPB)
    158		return -EINVAL;
    159
    160	ret = rdmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS, &epb);
    161	if (ret < 0)
    162		return ret;
    163
    164	ret = wrmsrl_on_cpu(cpu, MSR_IA32_ENERGY_PERF_BIAS,
    165			    (epb & ~EPB_MASK) | val);
    166	if (ret < 0)
    167		return ret;
    168
    169	return count;
    170}
    171
    172static DEVICE_ATTR_RW(energy_perf_bias);
    173
    174static struct attribute *intel_epb_attrs[] = {
    175	&dev_attr_energy_perf_bias.attr,
    176	NULL
    177};
    178
    179static const struct attribute_group intel_epb_attr_group = {
    180	.name = power_group_name,
    181	.attrs =  intel_epb_attrs
    182};
    183
    184static int intel_epb_online(unsigned int cpu)
    185{
    186	struct device *cpu_dev = get_cpu_device(cpu);
    187
    188	intel_epb_restore();
    189	if (!cpuhp_tasks_frozen)
    190		sysfs_merge_group(&cpu_dev->kobj, &intel_epb_attr_group);
    191
    192	return 0;
    193}
    194
    195static int intel_epb_offline(unsigned int cpu)
    196{
    197	struct device *cpu_dev = get_cpu_device(cpu);
    198
    199	if (!cpuhp_tasks_frozen)
    200		sysfs_unmerge_group(&cpu_dev->kobj, &intel_epb_attr_group);
    201
    202	intel_epb_save();
    203	return 0;
    204}
    205
    206static const struct x86_cpu_id intel_epb_normal[] = {
    207	X86_MATCH_INTEL_FAM6_MODEL(ALDERLAKE_L, 7),
    208	{}
    209};
    210
    211static __init int intel_epb_init(void)
    212{
    213	const struct x86_cpu_id *id = x86_match_cpu(intel_epb_normal);
    214	int ret;
    215
    216	if (!boot_cpu_has(X86_FEATURE_EPB))
    217		return -ENODEV;
    218
    219	if (id)
    220		energ_perf_values[EPB_INDEX_NORMAL] = id->driver_data;
    221
    222	ret = cpuhp_setup_state(CPUHP_AP_X86_INTEL_EPB_ONLINE,
    223				"x86/intel/epb:online", intel_epb_online,
    224				intel_epb_offline);
    225	if (ret < 0)
    226		goto err_out_online;
    227
    228	register_syscore_ops(&intel_epb_syscore_ops);
    229	return 0;
    230
    231err_out_online:
    232	cpuhp_remove_state(CPUHP_AP_X86_INTEL_EPB_ONLINE);
    233	return ret;
    234}
    235subsys_initcall(intel_epb_init);