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

uncore-frequency.c (6580B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Intel Uncore Frequency Setting
      4 * Copyright (c) 2022, Intel Corporation.
      5 * All rights reserved.
      6 *
      7 * Provide interface to set MSR 620 at a granularity of per die. On CPU online,
      8 * one control CPU is identified per die to read/write limit. This control CPU
      9 * is changed, if the CPU state is changed to offline. When the last CPU is
     10 * offline in a die then remove the sysfs object for that die.
     11 * The majority of actual code is related to sysfs create and read/write
     12 * attributes.
     13 *
     14 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
     15 */
     16
     17#include <linux/cpu.h>
     18#include <linux/module.h>
     19#include <linux/slab.h>
     20#include <linux/suspend.h>
     21#include <asm/cpu_device_id.h>
     22#include <asm/intel-family.h>
     23
     24#include "uncore-frequency-common.h"
     25
     26/* Max instances for uncore data, one for each die */
     27static int uncore_max_entries __read_mostly;
     28/* Storage for uncore data for all instances */
     29static struct uncore_data *uncore_instances;
     30/* Stores the CPU mask of the target CPUs to use during uncore read/write */
     31static cpumask_t uncore_cpu_mask;
     32/* CPU online callback register instance */
     33static enum cpuhp_state uncore_hp_state __read_mostly;
     34
     35#define MSR_UNCORE_RATIO_LIMIT	0x620
     36#define MSR_UNCORE_PERF_STATUS	0x621
     37#define UNCORE_FREQ_KHZ_MULTIPLIER	100000
     38
     39static int uncore_read_control_freq(struct uncore_data *data, unsigned int *min,
     40				    unsigned int *max)
     41{
     42	u64 cap;
     43	int ret;
     44
     45	if (data->control_cpu < 0)
     46		return -ENXIO;
     47
     48	ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap);
     49	if (ret)
     50		return ret;
     51
     52	*max = (cap & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
     53	*min = ((cap & GENMASK(14, 8)) >> 8) * UNCORE_FREQ_KHZ_MULTIPLIER;
     54
     55	return 0;
     56}
     57
     58static int uncore_write_control_freq(struct uncore_data *data, unsigned int input,
     59				     unsigned int min_max)
     60{
     61	int ret;
     62	u64 cap;
     63
     64	input /= UNCORE_FREQ_KHZ_MULTIPLIER;
     65	if (!input || input > 0x7F)
     66		return -EINVAL;
     67
     68	if (data->control_cpu < 0)
     69		return -ENXIO;
     70
     71	ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, &cap);
     72	if (ret)
     73		return ret;
     74
     75	if (min_max) {
     76		cap &= ~0x7F;
     77		cap |= input;
     78	} else  {
     79		cap &= ~GENMASK(14, 8);
     80		cap |= (input << 8);
     81	}
     82
     83	ret = wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT, cap);
     84	if (ret)
     85		return ret;
     86
     87	data->stored_uncore_data = cap;
     88
     89	return 0;
     90}
     91
     92static int uncore_read_freq(struct uncore_data *data, unsigned int *freq)
     93{
     94	u64 ratio;
     95	int ret;
     96
     97	if (data->control_cpu < 0)
     98		return -ENXIO;
     99
    100	ret = rdmsrl_on_cpu(data->control_cpu, MSR_UNCORE_PERF_STATUS, &ratio);
    101	if (ret)
    102		return ret;
    103
    104	*freq = (ratio & 0x7F) * UNCORE_FREQ_KHZ_MULTIPLIER;
    105
    106	return 0;
    107}
    108
    109/* Caller provides protection */
    110static struct uncore_data *uncore_get_instance(unsigned int cpu)
    111{
    112	int id = topology_logical_die_id(cpu);
    113
    114	if (id >= 0 && id < uncore_max_entries)
    115		return &uncore_instances[id];
    116
    117	return NULL;
    118}
    119
    120static int uncore_event_cpu_online(unsigned int cpu)
    121{
    122	struct uncore_data *data;
    123	int target;
    124
    125	/* Check if there is an online cpu in the package for uncore MSR */
    126	target = cpumask_any_and(&uncore_cpu_mask, topology_die_cpumask(cpu));
    127	if (target < nr_cpu_ids)
    128		return 0;
    129
    130	/* Use this CPU on this die as a control CPU */
    131	cpumask_set_cpu(cpu, &uncore_cpu_mask);
    132
    133	data = uncore_get_instance(cpu);
    134	if (!data)
    135		return 0;
    136
    137	data->package_id = topology_physical_package_id(cpu);
    138	data->die_id = topology_die_id(cpu);
    139
    140	return uncore_freq_add_entry(data, cpu);
    141}
    142
    143static int uncore_event_cpu_offline(unsigned int cpu)
    144{
    145	struct uncore_data *data;
    146	int target;
    147
    148	data = uncore_get_instance(cpu);
    149	if (!data)
    150		return 0;
    151
    152	/* Check if existing cpu is used for uncore MSRs */
    153	if (!cpumask_test_and_clear_cpu(cpu, &uncore_cpu_mask))
    154		return 0;
    155
    156	/* Find a new cpu to set uncore MSR */
    157	target = cpumask_any_but(topology_die_cpumask(cpu), cpu);
    158
    159	if (target < nr_cpu_ids) {
    160		cpumask_set_cpu(target, &uncore_cpu_mask);
    161		uncore_freq_add_entry(data, target);
    162	} else {
    163		uncore_freq_remove_die_entry(data);
    164	}
    165
    166	return 0;
    167}
    168
    169static int uncore_pm_notify(struct notifier_block *nb, unsigned long mode,
    170			    void *_unused)
    171{
    172	int i;
    173
    174	switch (mode) {
    175	case PM_POST_HIBERNATION:
    176	case PM_POST_RESTORE:
    177	case PM_POST_SUSPEND:
    178		for (i = 0; i < uncore_max_entries; ++i) {
    179			struct uncore_data *data = &uncore_instances[i];
    180
    181			if (!data || !data->valid || !data->stored_uncore_data)
    182				return 0;
    183
    184			wrmsrl_on_cpu(data->control_cpu, MSR_UNCORE_RATIO_LIMIT,
    185				      data->stored_uncore_data);
    186		}
    187		break;
    188	default:
    189		break;
    190	}
    191	return 0;
    192}
    193
    194static struct notifier_block uncore_pm_nb = {
    195	.notifier_call = uncore_pm_notify,
    196};
    197
    198static const struct x86_cpu_id intel_uncore_cpu_ids[] = {
    199	X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_G,	NULL),
    200	X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_X,	NULL),
    201	X86_MATCH_INTEL_FAM6_MODEL(BROADWELL_D,	NULL),
    202	X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_X,	NULL),
    203	X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X,	NULL),
    204	X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D,	NULL),
    205	X86_MATCH_INTEL_FAM6_MODEL(SAPPHIRERAPIDS_X, NULL),
    206	{}
    207};
    208MODULE_DEVICE_TABLE(x86cpu, intel_uncore_cpu_ids);
    209
    210static int __init intel_uncore_init(void)
    211{
    212	const struct x86_cpu_id *id;
    213	int ret;
    214
    215	if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
    216		return -ENODEV;
    217
    218	id = x86_match_cpu(intel_uncore_cpu_ids);
    219	if (!id)
    220		return -ENODEV;
    221
    222	uncore_max_entries = topology_max_packages() *
    223					topology_max_die_per_package();
    224	uncore_instances = kcalloc(uncore_max_entries,
    225				   sizeof(*uncore_instances), GFP_KERNEL);
    226	if (!uncore_instances)
    227		return -ENOMEM;
    228
    229	ret = uncore_freq_common_init(uncore_read_control_freq, uncore_write_control_freq,
    230				      uncore_read_freq);
    231	if (ret)
    232		goto err_free;
    233
    234	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
    235				"platform/x86/uncore-freq:online",
    236				uncore_event_cpu_online,
    237				uncore_event_cpu_offline);
    238	if (ret < 0)
    239		goto err_rem_kobj;
    240
    241	uncore_hp_state = ret;
    242
    243	ret = register_pm_notifier(&uncore_pm_nb);
    244	if (ret)
    245		goto err_rem_state;
    246
    247	return 0;
    248
    249err_rem_state:
    250	cpuhp_remove_state(uncore_hp_state);
    251err_rem_kobj:
    252	uncore_freq_common_exit();
    253err_free:
    254	kfree(uncore_instances);
    255
    256	return ret;
    257}
    258module_init(intel_uncore_init)
    259
    260static void __exit intel_uncore_exit(void)
    261{
    262	int i;
    263
    264	unregister_pm_notifier(&uncore_pm_nb);
    265	cpuhp_remove_state(uncore_hp_state);
    266	for (i = 0; i < uncore_max_entries; ++i)
    267		uncore_freq_remove_die_entry(&uncore_instances[i]);
    268	uncore_freq_common_exit();
    269	kfree(uncore_instances);
    270}
    271module_exit(intel_uncore_exit)
    272
    273MODULE_IMPORT_NS(INTEL_UNCORE_FREQUENCY);
    274MODULE_LICENSE("GPL v2");
    275MODULE_DESCRIPTION("Intel Uncore Frequency Limits Driver");