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

platform_profile.c (4234B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2
      3/* Platform profile sysfs interface */
      4
      5#include <linux/acpi.h>
      6#include <linux/bits.h>
      7#include <linux/init.h>
      8#include <linux/mutex.h>
      9#include <linux/platform_profile.h>
     10#include <linux/sysfs.h>
     11
     12static struct platform_profile_handler *cur_profile;
     13static DEFINE_MUTEX(profile_lock);
     14
     15static const char * const profile_names[] = {
     16	[PLATFORM_PROFILE_LOW_POWER] = "low-power",
     17	[PLATFORM_PROFILE_COOL] = "cool",
     18	[PLATFORM_PROFILE_QUIET] = "quiet",
     19	[PLATFORM_PROFILE_BALANCED] = "balanced",
     20	[PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance",
     21	[PLATFORM_PROFILE_PERFORMANCE] = "performance",
     22};
     23static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
     24
     25static ssize_t platform_profile_choices_show(struct device *dev,
     26					struct device_attribute *attr,
     27					char *buf)
     28{
     29	int len = 0;
     30	int err, i;
     31
     32	err = mutex_lock_interruptible(&profile_lock);
     33	if (err)
     34		return err;
     35
     36	if (!cur_profile) {
     37		mutex_unlock(&profile_lock);
     38		return -ENODEV;
     39	}
     40
     41	for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) {
     42		if (len == 0)
     43			len += sysfs_emit_at(buf, len, "%s", profile_names[i]);
     44		else
     45			len += sysfs_emit_at(buf, len, " %s", profile_names[i]);
     46	}
     47	len += sysfs_emit_at(buf, len, "\n");
     48	mutex_unlock(&profile_lock);
     49	return len;
     50}
     51
     52static ssize_t platform_profile_show(struct device *dev,
     53					struct device_attribute *attr,
     54					char *buf)
     55{
     56	enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
     57	int err;
     58
     59	err = mutex_lock_interruptible(&profile_lock);
     60	if (err)
     61		return err;
     62
     63	if (!cur_profile) {
     64		mutex_unlock(&profile_lock);
     65		return -ENODEV;
     66	}
     67
     68	err = cur_profile->profile_get(cur_profile, &profile);
     69	mutex_unlock(&profile_lock);
     70	if (err)
     71		return err;
     72
     73	/* Check that profile is valid index */
     74	if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
     75		return -EIO;
     76
     77	return sysfs_emit(buf, "%s\n", profile_names[profile]);
     78}
     79
     80static ssize_t platform_profile_store(struct device *dev,
     81			    struct device_attribute *attr,
     82			    const char *buf, size_t count)
     83{
     84	int err, i;
     85
     86	err = mutex_lock_interruptible(&profile_lock);
     87	if (err)
     88		return err;
     89
     90	if (!cur_profile) {
     91		mutex_unlock(&profile_lock);
     92		return -ENODEV;
     93	}
     94
     95	/* Scan for a matching profile */
     96	i = sysfs_match_string(profile_names, buf);
     97	if (i < 0) {
     98		mutex_unlock(&profile_lock);
     99		return -EINVAL;
    100	}
    101
    102	/* Check that platform supports this profile choice */
    103	if (!test_bit(i, cur_profile->choices)) {
    104		mutex_unlock(&profile_lock);
    105		return -EOPNOTSUPP;
    106	}
    107
    108	err = cur_profile->profile_set(cur_profile, i);
    109	if (!err)
    110		sysfs_notify(acpi_kobj, NULL, "platform_profile");
    111
    112	mutex_unlock(&profile_lock);
    113	if (err)
    114		return err;
    115	return count;
    116}
    117
    118static DEVICE_ATTR_RO(platform_profile_choices);
    119static DEVICE_ATTR_RW(platform_profile);
    120
    121static struct attribute *platform_profile_attrs[] = {
    122	&dev_attr_platform_profile_choices.attr,
    123	&dev_attr_platform_profile.attr,
    124	NULL
    125};
    126
    127static const struct attribute_group platform_profile_group = {
    128	.attrs = platform_profile_attrs
    129};
    130
    131void platform_profile_notify(void)
    132{
    133	if (!cur_profile)
    134		return;
    135	sysfs_notify(acpi_kobj, NULL, "platform_profile");
    136}
    137EXPORT_SYMBOL_GPL(platform_profile_notify);
    138
    139int platform_profile_register(struct platform_profile_handler *pprof)
    140{
    141	int err;
    142
    143	mutex_lock(&profile_lock);
    144	/* We can only have one active profile */
    145	if (cur_profile) {
    146		mutex_unlock(&profile_lock);
    147		return -EEXIST;
    148	}
    149
    150	/* Sanity check the profile handler field are set */
    151	if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
    152		!pprof->profile_set || !pprof->profile_get) {
    153		mutex_unlock(&profile_lock);
    154		return -EINVAL;
    155	}
    156
    157	err = sysfs_create_group(acpi_kobj, &platform_profile_group);
    158	if (err) {
    159		mutex_unlock(&profile_lock);
    160		return err;
    161	}
    162
    163	cur_profile = pprof;
    164	mutex_unlock(&profile_lock);
    165	return 0;
    166}
    167EXPORT_SYMBOL_GPL(platform_profile_register);
    168
    169int platform_profile_remove(void)
    170{
    171	sysfs_remove_group(acpi_kobj, &platform_profile_group);
    172
    173	mutex_lock(&profile_lock);
    174	cur_profile = NULL;
    175	mutex_unlock(&profile_lock);
    176	return 0;
    177}
    178EXPORT_SYMBOL_GPL(platform_profile_remove);
    179
    180MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
    181MODULE_LICENSE("GPL");