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

e_powersaver.c (10937B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  Based on documentation provided by Dave Jones. Thanks!
      4 *
      5 *  BIG FAT DISCLAIMER: Work in progress code. Possibly *dangerous*
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/kernel.h>
     11#include <linux/module.h>
     12#include <linux/init.h>
     13#include <linux/cpufreq.h>
     14#include <linux/ioport.h>
     15#include <linux/slab.h>
     16#include <linux/timex.h>
     17#include <linux/io.h>
     18#include <linux/delay.h>
     19
     20#include <asm/cpu_device_id.h>
     21#include <asm/msr.h>
     22#include <asm/tsc.h>
     23
     24#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
     25#include <linux/acpi.h>
     26#include <acpi/processor.h>
     27#endif
     28
     29#define EPS_BRAND_C7M	0
     30#define EPS_BRAND_C7	1
     31#define EPS_BRAND_EDEN	2
     32#define EPS_BRAND_C3	3
     33#define EPS_BRAND_C7D	4
     34
     35struct eps_cpu_data {
     36	u32 fsb;
     37#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
     38	u32 bios_limit;
     39#endif
     40	struct cpufreq_frequency_table freq_table[];
     41};
     42
     43static struct eps_cpu_data *eps_cpu[NR_CPUS];
     44
     45/* Module parameters */
     46static int freq_failsafe_off;
     47static int voltage_failsafe_off;
     48static int set_max_voltage;
     49
     50#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
     51static int ignore_acpi_limit;
     52
     53static struct acpi_processor_performance *eps_acpi_cpu_perf;
     54
     55/* Minimum necessary to get acpi_processor_get_bios_limit() working */
     56static int eps_acpi_init(void)
     57{
     58	eps_acpi_cpu_perf = kzalloc(sizeof(*eps_acpi_cpu_perf),
     59				      GFP_KERNEL);
     60	if (!eps_acpi_cpu_perf)
     61		return -ENOMEM;
     62
     63	if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
     64								GFP_KERNEL)) {
     65		kfree(eps_acpi_cpu_perf);
     66		eps_acpi_cpu_perf = NULL;
     67		return -ENOMEM;
     68	}
     69
     70	if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
     71		free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
     72		kfree(eps_acpi_cpu_perf);
     73		eps_acpi_cpu_perf = NULL;
     74		return -EIO;
     75	}
     76	return 0;
     77}
     78
     79static int eps_acpi_exit(struct cpufreq_policy *policy)
     80{
     81	if (eps_acpi_cpu_perf) {
     82		acpi_processor_unregister_performance(0);
     83		free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
     84		kfree(eps_acpi_cpu_perf);
     85		eps_acpi_cpu_perf = NULL;
     86	}
     87	return 0;
     88}
     89#endif
     90
     91static unsigned int eps_get(unsigned int cpu)
     92{
     93	struct eps_cpu_data *centaur;
     94	u32 lo, hi;
     95
     96	if (cpu)
     97		return 0;
     98	centaur = eps_cpu[cpu];
     99	if (centaur == NULL)
    100		return 0;
    101
    102	/* Return current frequency */
    103	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
    104	return centaur->fsb * ((lo >> 8) & 0xff);
    105}
    106
    107static int eps_set_state(struct eps_cpu_data *centaur,
    108			 struct cpufreq_policy *policy,
    109			 u32 dest_state)
    110{
    111	u32 lo, hi;
    112	int i;
    113
    114	/* Wait while CPU is busy */
    115	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
    116	i = 0;
    117	while (lo & ((1 << 16) | (1 << 17))) {
    118		udelay(16);
    119		rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
    120		i++;
    121		if (unlikely(i > 64)) {
    122			return -ENODEV;
    123		}
    124	}
    125	/* Set new multiplier and voltage */
    126	wrmsr(MSR_IA32_PERF_CTL, dest_state & 0xffff, 0);
    127	/* Wait until transition end */
    128	i = 0;
    129	do {
    130		udelay(16);
    131		rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
    132		i++;
    133		if (unlikely(i > 64)) {
    134			return -ENODEV;
    135		}
    136	} while (lo & ((1 << 16) | (1 << 17)));
    137
    138#ifdef DEBUG
    139	{
    140	u8 current_multiplier, current_voltage;
    141
    142	/* Print voltage and multiplier */
    143	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
    144	current_voltage = lo & 0xff;
    145	pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);
    146	current_multiplier = (lo >> 8) & 0xff;
    147	pr_info("Current multiplier = %d\n", current_multiplier);
    148	}
    149#endif
    150	return 0;
    151}
    152
    153static int eps_target(struct cpufreq_policy *policy, unsigned int index)
    154{
    155	struct eps_cpu_data *centaur;
    156	unsigned int cpu = policy->cpu;
    157	unsigned int dest_state;
    158	int ret;
    159
    160	if (unlikely(eps_cpu[cpu] == NULL))
    161		return -ENODEV;
    162	centaur = eps_cpu[cpu];
    163
    164	/* Make frequency transition */
    165	dest_state = centaur->freq_table[index].driver_data & 0xffff;
    166	ret = eps_set_state(centaur, policy, dest_state);
    167	if (ret)
    168		pr_err("Timeout!\n");
    169	return ret;
    170}
    171
    172static int eps_cpu_init(struct cpufreq_policy *policy)
    173{
    174	unsigned int i;
    175	u32 lo, hi;
    176	u64 val;
    177	u8 current_multiplier, current_voltage;
    178	u8 max_multiplier, max_voltage;
    179	u8 min_multiplier, min_voltage;
    180	u8 brand = 0;
    181	u32 fsb;
    182	struct eps_cpu_data *centaur;
    183	struct cpuinfo_x86 *c = &cpu_data(0);
    184	struct cpufreq_frequency_table *f_table;
    185	int k, step, voltage;
    186	int states;
    187#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
    188	unsigned int limit;
    189#endif
    190
    191	if (policy->cpu != 0)
    192		return -ENODEV;
    193
    194	/* Check brand */
    195	pr_info("Detected VIA ");
    196
    197	switch (c->x86_model) {
    198	case 10:
    199		rdmsr(0x1153, lo, hi);
    200		brand = (((lo >> 2) ^ lo) >> 18) & 3;
    201		pr_cont("Model A ");
    202		break;
    203	case 13:
    204		rdmsr(0x1154, lo, hi);
    205		brand = (((lo >> 4) ^ (lo >> 2))) & 0x000000ff;
    206		pr_cont("Model D ");
    207		break;
    208	}
    209
    210	switch (brand) {
    211	case EPS_BRAND_C7M:
    212		pr_cont("C7-M\n");
    213		break;
    214	case EPS_BRAND_C7:
    215		pr_cont("C7\n");
    216		break;
    217	case EPS_BRAND_EDEN:
    218		pr_cont("Eden\n");
    219		break;
    220	case EPS_BRAND_C7D:
    221		pr_cont("C7-D\n");
    222		break;
    223	case EPS_BRAND_C3:
    224		pr_cont("C3\n");
    225		return -ENODEV;
    226	}
    227	/* Enable Enhanced PowerSaver */
    228	rdmsrl(MSR_IA32_MISC_ENABLE, val);
    229	if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
    230		val |= MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP;
    231		wrmsrl(MSR_IA32_MISC_ENABLE, val);
    232		/* Can be locked at 0 */
    233		rdmsrl(MSR_IA32_MISC_ENABLE, val);
    234		if (!(val & MSR_IA32_MISC_ENABLE_ENHANCED_SPEEDSTEP)) {
    235			pr_info("Can't enable Enhanced PowerSaver\n");
    236			return -ENODEV;
    237		}
    238	}
    239
    240	/* Print voltage and multiplier */
    241	rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
    242	current_voltage = lo & 0xff;
    243	pr_info("Current voltage = %dmV\n", current_voltage * 16 + 700);
    244	current_multiplier = (lo >> 8) & 0xff;
    245	pr_info("Current multiplier = %d\n", current_multiplier);
    246
    247	/* Print limits */
    248	max_voltage = hi & 0xff;
    249	pr_info("Highest voltage = %dmV\n", max_voltage * 16 + 700);
    250	max_multiplier = (hi >> 8) & 0xff;
    251	pr_info("Highest multiplier = %d\n", max_multiplier);
    252	min_voltage = (hi >> 16) & 0xff;
    253	pr_info("Lowest voltage = %dmV\n", min_voltage * 16 + 700);
    254	min_multiplier = (hi >> 24) & 0xff;
    255	pr_info("Lowest multiplier = %d\n", min_multiplier);
    256
    257	/* Sanity checks */
    258	if (current_multiplier == 0 || max_multiplier == 0
    259	    || min_multiplier == 0)
    260		return -EINVAL;
    261	if (current_multiplier > max_multiplier
    262	    || max_multiplier <= min_multiplier)
    263		return -EINVAL;
    264	if (current_voltage > 0x1f || max_voltage > 0x1f)
    265		return -EINVAL;
    266	if (max_voltage < min_voltage
    267	    || current_voltage < min_voltage
    268	    || current_voltage > max_voltage)
    269		return -EINVAL;
    270
    271	/* Check for systems using underclocked CPU */
    272	if (!freq_failsafe_off && max_multiplier != current_multiplier) {
    273		pr_info("Your processor is running at different frequency then its maximum. Aborting.\n");
    274		pr_info("You can use freq_failsafe_off option to disable this check.\n");
    275		return -EINVAL;
    276	}
    277	if (!voltage_failsafe_off && max_voltage != current_voltage) {
    278		pr_info("Your processor is running at different voltage then its maximum. Aborting.\n");
    279		pr_info("You can use voltage_failsafe_off option to disable this check.\n");
    280		return -EINVAL;
    281	}
    282
    283	/* Calc FSB speed */
    284	fsb = cpu_khz / current_multiplier;
    285
    286#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
    287	/* Check for ACPI processor speed limit */
    288	if (!ignore_acpi_limit && !eps_acpi_init()) {
    289		if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
    290			pr_info("ACPI limit %u.%uGHz\n",
    291				limit/1000000,
    292				(limit%1000000)/10000);
    293			eps_acpi_exit(policy);
    294			/* Check if max_multiplier is in BIOS limits */
    295			if (limit && max_multiplier * fsb > limit) {
    296				pr_info("Aborting\n");
    297				return -EINVAL;
    298			}
    299		}
    300	}
    301#endif
    302
    303	/* Allow user to set lower maximum voltage then that reported
    304	 * by processor */
    305	if (brand == EPS_BRAND_C7M && set_max_voltage) {
    306		u32 v;
    307
    308		/* Change mV to something hardware can use */
    309		v = (set_max_voltage - 700) / 16;
    310		/* Check if voltage is within limits */
    311		if (v >= min_voltage && v <= max_voltage) {
    312			pr_info("Setting %dmV as maximum\n", v * 16 + 700);
    313			max_voltage = v;
    314		}
    315	}
    316
    317	/* Calc number of p-states supported */
    318	if (brand == EPS_BRAND_C7M)
    319		states = max_multiplier - min_multiplier + 1;
    320	else
    321		states = 2;
    322
    323	/* Allocate private data and frequency table for current cpu */
    324	centaur = kzalloc(struct_size(centaur, freq_table, states + 1),
    325			  GFP_KERNEL);
    326	if (!centaur)
    327		return -ENOMEM;
    328	eps_cpu[0] = centaur;
    329
    330	/* Copy basic values */
    331	centaur->fsb = fsb;
    332#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
    333	centaur->bios_limit = limit;
    334#endif
    335
    336	/* Fill frequency and MSR value table */
    337	f_table = &centaur->freq_table[0];
    338	if (brand != EPS_BRAND_C7M) {
    339		f_table[0].frequency = fsb * min_multiplier;
    340		f_table[0].driver_data = (min_multiplier << 8) | min_voltage;
    341		f_table[1].frequency = fsb * max_multiplier;
    342		f_table[1].driver_data = (max_multiplier << 8) | max_voltage;
    343		f_table[2].frequency = CPUFREQ_TABLE_END;
    344	} else {
    345		k = 0;
    346		step = ((max_voltage - min_voltage) * 256)
    347			/ (max_multiplier - min_multiplier);
    348		for (i = min_multiplier; i <= max_multiplier; i++) {
    349			voltage = (k * step) / 256 + min_voltage;
    350			f_table[k].frequency = fsb * i;
    351			f_table[k].driver_data = (i << 8) | voltage;
    352			k++;
    353		}
    354		f_table[k].frequency = CPUFREQ_TABLE_END;
    355	}
    356
    357	policy->cpuinfo.transition_latency = 140000; /* 844mV -> 700mV in ns */
    358	policy->freq_table = &centaur->freq_table[0];
    359
    360	return 0;
    361}
    362
    363static int eps_cpu_exit(struct cpufreq_policy *policy)
    364{
    365	unsigned int cpu = policy->cpu;
    366
    367	/* Bye */
    368	kfree(eps_cpu[cpu]);
    369	eps_cpu[cpu] = NULL;
    370	return 0;
    371}
    372
    373static struct cpufreq_driver eps_driver = {
    374	.verify		= cpufreq_generic_frequency_table_verify,
    375	.target_index	= eps_target,
    376	.init		= eps_cpu_init,
    377	.exit		= eps_cpu_exit,
    378	.get		= eps_get,
    379	.name		= "e_powersaver",
    380	.attr		= cpufreq_generic_attr,
    381};
    382
    383
    384/* This driver will work only on Centaur C7 processors with
    385 * Enhanced SpeedStep/PowerSaver registers */
    386static const struct x86_cpu_id eps_cpu_id[] = {
    387	X86_MATCH_VENDOR_FAM_FEATURE(CENTAUR, 6, X86_FEATURE_EST, NULL),
    388	{}
    389};
    390MODULE_DEVICE_TABLE(x86cpu, eps_cpu_id);
    391
    392static int __init eps_init(void)
    393{
    394	if (!x86_match_cpu(eps_cpu_id) || boot_cpu_data.x86_model < 10)
    395		return -ENODEV;
    396	if (cpufreq_register_driver(&eps_driver))
    397		return -EINVAL;
    398	return 0;
    399}
    400
    401static void __exit eps_exit(void)
    402{
    403	cpufreq_unregister_driver(&eps_driver);
    404}
    405
    406/* Allow user to overclock his machine or to change frequency to higher after
    407 * unloading module */
    408module_param(freq_failsafe_off, int, 0644);
    409MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
    410module_param(voltage_failsafe_off, int, 0644);
    411MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
    412#if IS_ENABLED(CONFIG_ACPI_PROCESSOR)
    413module_param(ignore_acpi_limit, int, 0644);
    414MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
    415#endif
    416module_param(set_max_voltage, int, 0644);
    417MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
    418
    419MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
    420MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
    421MODULE_LICENSE("GPL");
    422
    423module_init(eps_init);
    424module_exit(eps_exit);