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

sysfs.c (10591B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
      4 *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc.
      5 */
      6
      7#include <stdio.h>
      8#include <errno.h>
      9#include <stdlib.h>
     10#include <string.h>
     11#include <sys/types.h>
     12#include <sys/stat.h>
     13#include <fcntl.h>
     14#include <unistd.h>
     15
     16#include "helpers/sysfs.h"
     17
     18unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
     19{
     20	int fd;
     21	ssize_t numread;
     22
     23	fd = open(path, O_RDONLY);
     24	if (fd == -1)
     25		return 0;
     26
     27	numread = read(fd, buf, buflen - 1);
     28	if (numread < 1) {
     29		close(fd);
     30		return 0;
     31	}
     32
     33	buf[numread] = '\0';
     34	close(fd);
     35
     36	return (unsigned int) numread;
     37}
     38
     39/*
     40 * Detect whether a CPU is online
     41 *
     42 * Returns:
     43 *     1 -> if CPU is online
     44 *     0 -> if CPU is offline
     45 *     negative errno values in error case
     46 */
     47int sysfs_is_cpu_online(unsigned int cpu)
     48{
     49	char path[SYSFS_PATH_MAX];
     50	int fd;
     51	ssize_t numread;
     52	unsigned long long value;
     53	char linebuf[MAX_LINE_LEN];
     54	char *endp;
     55	struct stat statbuf;
     56
     57	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
     58
     59	if (stat(path, &statbuf) != 0)
     60		return 0;
     61
     62	/*
     63	 * kernel without CONFIG_HOTPLUG_CPU
     64	 * -> cpuX directory exists, but not cpuX/online file
     65	 */
     66	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
     67	if (stat(path, &statbuf) != 0)
     68		return 1;
     69
     70	fd = open(path, O_RDONLY);
     71	if (fd == -1)
     72		return -errno;
     73
     74	numread = read(fd, linebuf, MAX_LINE_LEN - 1);
     75	if (numread < 1) {
     76		close(fd);
     77		return -EIO;
     78	}
     79	linebuf[numread] = '\0';
     80	close(fd);
     81
     82	value = strtoull(linebuf, &endp, 0);
     83	if (value > 1)
     84		return -EINVAL;
     85
     86	return value;
     87}
     88
     89/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
     90
     91
     92/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
     93
     94/*
     95 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
     96 * exists.
     97 * For example the functionality to disable c-states was introduced in later
     98 * kernel versions, this function can be used to explicitly check for this
     99 * feature.
    100 *
    101 * returns 1 if the file exists, 0 otherwise.
    102 */
    103unsigned int sysfs_idlestate_file_exists(unsigned int cpu,
    104					 unsigned int idlestate,
    105					 const char *fname)
    106{
    107	char path[SYSFS_PATH_MAX];
    108	struct stat statbuf;
    109
    110
    111	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
    112		 cpu, idlestate, fname);
    113	if (stat(path, &statbuf) != 0)
    114		return 0;
    115	return 1;
    116}
    117
    118/*
    119 * helper function to read file from /sys into given buffer
    120 * fname is a relative path under "cpuX/cpuidle/stateX/" dir
    121 * cstates starting with 0, C0 is not counted as cstate.
    122 * This means if you want C1 info, pass 0 as idlestate param
    123 */
    124unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,
    125			     const char *fname, char *buf, size_t buflen)
    126{
    127	char path[SYSFS_PATH_MAX];
    128	int fd;
    129	ssize_t numread;
    130
    131	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
    132		 cpu, idlestate, fname);
    133
    134	fd = open(path, O_RDONLY);
    135	if (fd == -1)
    136		return 0;
    137
    138	numread = read(fd, buf, buflen - 1);
    139	if (numread < 1) {
    140		close(fd);
    141		return 0;
    142	}
    143
    144	buf[numread] = '\0';
    145	close(fd);
    146
    147	return (unsigned int) numread;
    148}
    149
    150/* 
    151 * helper function to write a new value to a /sys file
    152 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
    153 *
    154 * Returns the number of bytes written or 0 on error
    155 */
    156static
    157unsigned int sysfs_idlestate_write_file(unsigned int cpu,
    158					unsigned int idlestate,
    159					const char *fname,
    160					const char *value, size_t len)
    161{
    162	char path[SYSFS_PATH_MAX];
    163	int fd;
    164	ssize_t numwrite;
    165
    166	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
    167		 cpu, idlestate, fname);
    168
    169	fd = open(path, O_WRONLY);
    170	if (fd == -1)
    171		return 0;
    172
    173	numwrite = write(fd, value, len);
    174	if (numwrite < 1) {
    175		close(fd);
    176		return 0;
    177	}
    178
    179	close(fd);
    180
    181	return (unsigned int) numwrite;
    182}
    183
    184/* read access to files which contain one numeric value */
    185
    186enum idlestate_value {
    187	IDLESTATE_USAGE,
    188	IDLESTATE_POWER,
    189	IDLESTATE_LATENCY,
    190	IDLESTATE_TIME,
    191	IDLESTATE_DISABLE,
    192	MAX_IDLESTATE_VALUE_FILES
    193};
    194
    195static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
    196	[IDLESTATE_USAGE] = "usage",
    197	[IDLESTATE_POWER] = "power",
    198	[IDLESTATE_LATENCY] = "latency",
    199	[IDLESTATE_TIME]  = "time",
    200	[IDLESTATE_DISABLE]  = "disable",
    201};
    202
    203static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,
    204						     unsigned int idlestate,
    205						     enum idlestate_value which)
    206{
    207	unsigned long long value;
    208	unsigned int len;
    209	char linebuf[MAX_LINE_LEN];
    210	char *endp;
    211
    212	if (which >= MAX_IDLESTATE_VALUE_FILES)
    213		return 0;
    214
    215	len = sysfs_idlestate_read_file(cpu, idlestate,
    216					idlestate_value_files[which],
    217					linebuf, sizeof(linebuf));
    218	if (len == 0)
    219		return 0;
    220
    221	value = strtoull(linebuf, &endp, 0);
    222
    223	if (endp == linebuf || errno == ERANGE)
    224		return 0;
    225
    226	return value;
    227}
    228
    229/* read access to files which contain one string */
    230
    231enum idlestate_string {
    232	IDLESTATE_DESC,
    233	IDLESTATE_NAME,
    234	MAX_IDLESTATE_STRING_FILES
    235};
    236
    237static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
    238	[IDLESTATE_DESC] = "desc",
    239	[IDLESTATE_NAME] = "name",
    240};
    241
    242
    243static char *sysfs_idlestate_get_one_string(unsigned int cpu,
    244					unsigned int idlestate,
    245					enum idlestate_string which)
    246{
    247	char linebuf[MAX_LINE_LEN];
    248	char *result;
    249	unsigned int len;
    250
    251	if (which >= MAX_IDLESTATE_STRING_FILES)
    252		return NULL;
    253
    254	len = sysfs_idlestate_read_file(cpu, idlestate,
    255					idlestate_string_files[which],
    256					linebuf, sizeof(linebuf));
    257	if (len == 0)
    258		return NULL;
    259
    260	result = strdup(linebuf);
    261	if (result == NULL)
    262		return NULL;
    263
    264	if (result[strlen(result) - 1] == '\n')
    265		result[strlen(result) - 1] = '\0';
    266
    267	return result;
    268}
    269
    270/*
    271 * Returns:
    272 *    1  if disabled
    273 *    0  if enabled
    274 *    -1 if idlestate is not available
    275 *    -2 if disabling is not supported by the kernel
    276 */
    277int sysfs_is_idlestate_disabled(unsigned int cpu,
    278				unsigned int idlestate)
    279{
    280	if (sysfs_get_idlestate_count(cpu) <= idlestate)
    281		return -1;
    282
    283	if (!sysfs_idlestate_file_exists(cpu, idlestate,
    284				 idlestate_value_files[IDLESTATE_DISABLE]))
    285		return -2;
    286	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
    287}
    288
    289/*
    290 * Pass 1 as last argument to disable or 0 to enable the state
    291 * Returns:
    292 *    0  on success
    293 *    negative values on error, for example:
    294 *      -1 if idlestate is not available
    295 *      -2 if disabling is not supported by the kernel
    296 *      -3 No write access to disable/enable C-states
    297 */
    298int sysfs_idlestate_disable(unsigned int cpu,
    299			    unsigned int idlestate,
    300			    unsigned int disable)
    301{
    302	char value[SYSFS_PATH_MAX];
    303	int bytes_written;
    304
    305	if (sysfs_get_idlestate_count(cpu) <= idlestate)
    306		return -1;
    307
    308	if (!sysfs_idlestate_file_exists(cpu, idlestate,
    309				 idlestate_value_files[IDLESTATE_DISABLE]))
    310		return -2;
    311
    312	snprintf(value, SYSFS_PATH_MAX, "%u", disable);
    313
    314	bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable",
    315						   value, sizeof(disable));
    316	if (bytes_written)
    317		return 0;
    318	return -3;
    319}
    320
    321unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
    322					  unsigned int idlestate)
    323{
    324	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
    325}
    326
    327unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
    328					unsigned int idlestate)
    329{
    330	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
    331}
    332
    333unsigned long long sysfs_get_idlestate_time(unsigned int cpu,
    334					unsigned int idlestate)
    335{
    336	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);
    337}
    338
    339char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
    340{
    341	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
    342}
    343
    344char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)
    345{
    346	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);
    347}
    348
    349/*
    350 * Returns number of supported C-states of CPU core cpu
    351 * Negativ in error case
    352 * Zero if cpuidle does not export any C-states
    353 */
    354unsigned int sysfs_get_idlestate_count(unsigned int cpu)
    355{
    356	char file[SYSFS_PATH_MAX];
    357	struct stat statbuf;
    358	int idlestates = 1;
    359
    360
    361	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
    362	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
    363		return 0;
    364
    365	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
    366	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
    367		return 0;
    368
    369	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
    370		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
    371			 "cpu%u/cpuidle/state%d", cpu, idlestates);
    372		idlestates++;
    373	}
    374	idlestates--;
    375	return idlestates;
    376}
    377
    378/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
    379
    380/*
    381 * helper function to read file from /sys into given buffer
    382 * fname is a relative path under "cpu/cpuidle/" dir
    383 */
    384static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
    385					    size_t buflen)
    386{
    387	char path[SYSFS_PATH_MAX];
    388
    389	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
    390
    391	return sysfs_read_file(path, buf, buflen);
    392}
    393
    394
    395
    396/* read access to files which contain one string */
    397
    398enum cpuidle_string {
    399	CPUIDLE_GOVERNOR,
    400	CPUIDLE_GOVERNOR_RO,
    401	CPUIDLE_DRIVER,
    402	MAX_CPUIDLE_STRING_FILES
    403};
    404
    405static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
    406	[CPUIDLE_GOVERNOR]	= "current_governor",
    407	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",
    408	[CPUIDLE_DRIVER]	= "current_driver",
    409};
    410
    411
    412static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
    413{
    414	char linebuf[MAX_LINE_LEN];
    415	char *result;
    416	unsigned int len;
    417
    418	if (which >= MAX_CPUIDLE_STRING_FILES)
    419		return NULL;
    420
    421	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
    422				linebuf, sizeof(linebuf));
    423	if (len == 0)
    424		return NULL;
    425
    426	result = strdup(linebuf);
    427	if (result == NULL)
    428		return NULL;
    429
    430	if (result[strlen(result) - 1] == '\n')
    431		result[strlen(result) - 1] = '\0';
    432
    433	return result;
    434}
    435
    436char *sysfs_get_cpuidle_governor(void)
    437{
    438	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
    439	if (!tmp)
    440		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
    441	else
    442		return tmp;
    443}
    444
    445char *sysfs_get_cpuidle_driver(void)
    446{
    447	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
    448}
    449/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
    450
    451/*
    452 * Get sched_mc or sched_smt settings
    453 * Pass "mc" or "smt" as argument
    454 *
    455 * Returns negative value on failure
    456 */
    457int sysfs_get_sched(const char *smt_mc)
    458{
    459	return -ENODEV;
    460}
    461
    462/*
    463 * Get sched_mc or sched_smt settings
    464 * Pass "mc" or "smt" as argument
    465 *
    466 * Returns negative value on failure
    467 */
    468int sysfs_set_sched(const char *smt_mc, int val)
    469{
    470	return -ENODEV;
    471}