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 (14621B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * sysfs.c sysfs ABI access functions for TMON program
      4 *
      5 * Copyright (C) 2013 Intel Corporation. All rights reserved.
      6 *
      7 * Author: Jacob Pan <jacob.jun.pan@linux.intel.com>
      8 */
      9#include <unistd.h>
     10#include <stdio.h>
     11#include <stdlib.h>
     12#include <string.h>
     13#include <stdint.h>
     14#include <dirent.h>
     15#include <libintl.h>
     16#include <ctype.h>
     17#include <time.h>
     18#include <syslog.h>
     19#include <sys/time.h>
     20#include <errno.h>
     21
     22#include "tmon.h"
     23
     24struct tmon_platform_data ptdata;
     25const char *trip_type_name[] = {
     26	"critical",
     27	"hot",
     28	"passive",
     29	"active",
     30};
     31
     32int sysfs_set_ulong(char *path, char *filename, unsigned long val)
     33{
     34	FILE *fd;
     35	int ret = -1;
     36	char filepath[256];
     37
     38	snprintf(filepath, 256, "%s/%s", path, filename);
     39
     40	fd = fopen(filepath, "w");
     41	if (!fd) {
     42		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
     43		return ret;
     44	}
     45	ret = fprintf(fd, "%lu", val);
     46	fclose(fd);
     47
     48	return 0;
     49}
     50
     51/* history of thermal data, used for control algo */
     52#define NR_THERMAL_RECORDS 3
     53struct thermal_data_record trec[NR_THERMAL_RECORDS];
     54int cur_thermal_record; /* index to the trec array */
     55
     56static int sysfs_get_ulong(char *path, char *filename, unsigned long *p_ulong)
     57{
     58	FILE *fd;
     59	int ret = -1;
     60	char filepath[256];
     61
     62	snprintf(filepath, 256, "%s/%s", path, filename);
     63
     64	fd = fopen(filepath, "r");
     65	if (!fd) {
     66		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
     67		return ret;
     68	}
     69	ret = fscanf(fd, "%lu", p_ulong);
     70	fclose(fd);
     71
     72	return 0;
     73}
     74
     75static int sysfs_get_string(char *path, char *filename, char *str)
     76{
     77	FILE *fd;
     78	int ret = -1;
     79	char filepath[256];
     80
     81	snprintf(filepath, 256, "%s/%s", path, filename);
     82
     83	fd = fopen(filepath, "r");
     84	if (!fd) {
     85		syslog(LOG_ERR, "Err: open %s: %s\n", __func__, filepath);
     86		return ret;
     87	}
     88	ret = fscanf(fd, "%256s", str);
     89	fclose(fd);
     90
     91	return ret;
     92}
     93
     94/* get states of the cooling device instance */
     95static int probe_cdev(struct cdev_info *cdi, char *path)
     96{
     97	sysfs_get_string(path, "type", cdi->type);
     98	sysfs_get_ulong(path, "max_state",  &cdi->max_state);
     99	sysfs_get_ulong(path, "cur_state", &cdi->cur_state);
    100
    101	syslog(LOG_INFO, "%s: %s: type %s, max %lu, curr %lu inst %d\n",
    102		__func__, path,
    103		cdi->type, cdi->max_state, cdi->cur_state, cdi->instance);
    104
    105	return 0;
    106}
    107
    108static int str_to_trip_type(char *name)
    109{
    110	int i;
    111
    112	for (i = 0; i < NR_THERMAL_TRIP_TYPE; i++) {
    113		if (!strcmp(name, trip_type_name[i]))
    114			return i;
    115	}
    116
    117	return -ENOENT;
    118}
    119
    120/* scan and fill in trip point info for a thermal zone and trip point id */
    121static int get_trip_point_data(char *tz_path, int tzid, int tpid)
    122{
    123	char filename[256];
    124	char temp_str[256];
    125	int trip_type;
    126
    127	if (tpid >= MAX_NR_TRIP)
    128		return -EINVAL;
    129	/* check trip point type */
    130	snprintf(filename, sizeof(filename), "trip_point_%d_type", tpid);
    131	sysfs_get_string(tz_path, filename, temp_str);
    132	trip_type = str_to_trip_type(temp_str);
    133	if (trip_type < 0) {
    134		syslog(LOG_ERR, "%s:%s no matching type\n", __func__, temp_str);
    135		return -ENOENT;
    136	}
    137	ptdata.tzi[tzid].tp[tpid].type = trip_type;
    138	syslog(LOG_INFO, "%s:tz:%d tp:%d:type:%s type id %d\n", __func__, tzid,
    139		tpid, temp_str, trip_type);
    140
    141	/* TODO: check attribute */
    142
    143	return 0;
    144}
    145
    146/* return instance id for file format such as trip_point_4_temp */
    147static int get_instance_id(char *name, int pos, int skip)
    148{
    149	char *ch;
    150	int i = 0;
    151
    152	ch = strtok(name, "_");
    153	while (ch != NULL) {
    154		++i;
    155		syslog(LOG_INFO, "%s:%s:%s:%d", __func__, name, ch, i);
    156		ch = strtok(NULL, "_");
    157		if (pos == i)
    158			return atol(ch + skip);
    159	}
    160
    161	return -1;
    162}
    163
    164/* Find trip point info of a thermal zone */
    165static int find_tzone_tp(char *tz_name, char *d_name, struct tz_info *tzi,
    166			int tz_id)
    167{
    168	int tp_id;
    169	unsigned long temp_ulong;
    170
    171	if (strstr(d_name, "trip_point") &&
    172		strstr(d_name, "temp")) {
    173		/* check if trip point temp is non-zero
    174		 * ignore 0/invalid trip points
    175		 */
    176		sysfs_get_ulong(tz_name, d_name, &temp_ulong);
    177		if (temp_ulong < MAX_TEMP_KC) {
    178			tzi->nr_trip_pts++;
    179			/* found a valid trip point */
    180			tp_id = get_instance_id(d_name, 2, 0);
    181			syslog(LOG_DEBUG, "tzone %s trip %d temp %lu tpnode %s",
    182				tz_name, tp_id, temp_ulong, d_name);
    183			if (tp_id < 0 || tp_id >= MAX_NR_TRIP) {
    184				syslog(LOG_ERR, "Failed to find TP inst %s\n",
    185					d_name);
    186				return -1;
    187			}
    188			get_trip_point_data(tz_name, tz_id, tp_id);
    189			tzi->tp[tp_id].temp = temp_ulong;
    190		}
    191	}
    192
    193	return 0;
    194}
    195
    196/* check cooling devices for binding info. */
    197static int find_tzone_cdev(struct dirent *nl, char *tz_name,
    198			struct tz_info *tzi, int tz_id, int cid)
    199{
    200	unsigned long trip_instance = 0;
    201	char cdev_name_linked[256];
    202	char cdev_name[256];
    203	char cdev_trip_name[256];
    204	int cdev_id;
    205
    206	if (nl->d_type == DT_LNK) {
    207		syslog(LOG_DEBUG, "TZ%d: cdev: %s cid %d\n", tz_id, nl->d_name,
    208			cid);
    209		tzi->nr_cdev++;
    210		if (tzi->nr_cdev > ptdata.nr_cooling_dev) {
    211			syslog(LOG_ERR, "Err: Too many cdev? %d\n",
    212				tzi->nr_cdev);
    213			return -EINVAL;
    214		}
    215		/* find the link to real cooling device record binding */
    216		snprintf(cdev_name, 256, "%s/%s", tz_name, nl->d_name);
    217		memset(cdev_name_linked, 0, sizeof(cdev_name_linked));
    218		if (readlink(cdev_name, cdev_name_linked,
    219				sizeof(cdev_name_linked) - 1) != -1) {
    220			cdev_id = get_instance_id(cdev_name_linked, 1,
    221						sizeof("device") - 1);
    222			syslog(LOG_DEBUG, "cdev %s linked to %s : %d\n",
    223				cdev_name, cdev_name_linked, cdev_id);
    224			tzi->cdev_binding |= (1 << cdev_id);
    225
    226			/* find the trip point in which the cdev is binded to
    227			 * in this tzone
    228			 */
    229			snprintf(cdev_trip_name, 256, "%s%s", nl->d_name,
    230				"_trip_point");
    231			sysfs_get_ulong(tz_name, cdev_trip_name,
    232					&trip_instance);
    233			/* validate trip point range, e.g. trip could return -1
    234			 * when passive is enabled
    235			 */
    236			if (trip_instance > MAX_NR_TRIP)
    237				trip_instance = 0;
    238			tzi->trip_binding[cdev_id] |= 1 << trip_instance;
    239			syslog(LOG_DEBUG, "cdev %s -> trip:%lu: 0x%lx %d\n",
    240				cdev_name, trip_instance,
    241				tzi->trip_binding[cdev_id],
    242				cdev_id);
    243
    244
    245		}
    246		return 0;
    247	}
    248
    249	return -ENODEV;
    250}
    251
    252
    253
    254/*****************************************************************************
    255 * Before calling scan_tzones, thermal sysfs must be probed to determine
    256 * the number of thermal zones and cooling devices.
    257 * We loop through each thermal zone and fill in tz_info struct, i.e.
    258 * ptdata.tzi[]
    259root@jacob-chiefriver:~# tree -d /sys/class/thermal/thermal_zone0
    260/sys/class/thermal/thermal_zone0
    261|-- cdev0 -> ../cooling_device4
    262|-- cdev1 -> ../cooling_device3
    263|-- cdev10 -> ../cooling_device7
    264|-- cdev11 -> ../cooling_device6
    265|-- cdev12 -> ../cooling_device5
    266|-- cdev2 -> ../cooling_device2
    267|-- cdev3 -> ../cooling_device1
    268|-- cdev4 -> ../cooling_device0
    269|-- cdev5 -> ../cooling_device12
    270|-- cdev6 -> ../cooling_device11
    271|-- cdev7 -> ../cooling_device10
    272|-- cdev8 -> ../cooling_device9
    273|-- cdev9 -> ../cooling_device8
    274|-- device -> ../../../LNXSYSTM:00/device:62/LNXTHERM:00
    275|-- power
    276`-- subsystem -> ../../../../class/thermal
    277*****************************************************************************/
    278static int scan_tzones(void)
    279{
    280	DIR *dir;
    281	struct dirent **namelist;
    282	char tz_name[256];
    283	int i, j, n, k = 0;
    284
    285	if (!ptdata.nr_tz_sensor)
    286		return -1;
    287
    288	for (i = 0; i <= ptdata.max_tz_instance; i++) {
    289		memset(tz_name, 0, sizeof(tz_name));
    290		snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE, i);
    291
    292		dir = opendir(tz_name);
    293		if (!dir) {
    294			syslog(LOG_INFO, "Thermal zone %s skipped\n", tz_name);
    295			continue;
    296		}
    297		/* keep track of valid tzones */
    298		n = scandir(tz_name, &namelist, 0, alphasort);
    299		if (n < 0)
    300			syslog(LOG_ERR, "scandir failed in %s",  tz_name);
    301		else {
    302			sysfs_get_string(tz_name, "type", ptdata.tzi[k].type);
    303			ptdata.tzi[k].instance = i;
    304			/* detect trip points and cdev attached to this tzone */
    305			j = 0; /* index for cdev */
    306			ptdata.tzi[k].nr_cdev = 0;
    307			ptdata.tzi[k].nr_trip_pts = 0;
    308			while (n--) {
    309				char *temp_str;
    310
    311				if (find_tzone_tp(tz_name, namelist[n]->d_name,
    312							&ptdata.tzi[k], k))
    313					break;
    314				temp_str = strstr(namelist[n]->d_name, "cdev");
    315				if (!temp_str) {
    316					free(namelist[n]);
    317					continue;
    318				}
    319				if (!find_tzone_cdev(namelist[n], tz_name,
    320							&ptdata.tzi[k], i, j))
    321					j++; /* increment cdev index */
    322				free(namelist[n]);
    323			}
    324			free(namelist);
    325		}
    326		/*TODO: reverse trip points */
    327		closedir(dir);
    328		syslog(LOG_INFO, "TZ %d has %d cdev\n",	i,
    329			ptdata.tzi[k].nr_cdev);
    330		k++;
    331	}
    332
    333	return 0;
    334}
    335
    336static int scan_cdevs(void)
    337{
    338	DIR *dir;
    339	struct dirent **namelist;
    340	char cdev_name[256];
    341	int i, n, k = 0;
    342
    343	if (!ptdata.nr_cooling_dev) {
    344		fprintf(stderr, "No cooling devices found\n");
    345		return 0;
    346	}
    347	for (i = 0; i <= ptdata.max_cdev_instance; i++) {
    348		memset(cdev_name, 0, sizeof(cdev_name));
    349		snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV, i);
    350
    351		dir = opendir(cdev_name);
    352		if (!dir) {
    353			syslog(LOG_INFO, "Cooling dev %s skipped\n", cdev_name);
    354			/* there is a gap in cooling device id, check again
    355			 * for the same index.
    356			 */
    357			continue;
    358		}
    359
    360		n = scandir(cdev_name, &namelist, 0, alphasort);
    361		if (n < 0)
    362			syslog(LOG_ERR, "scandir failed in %s",  cdev_name);
    363		else {
    364			sysfs_get_string(cdev_name, "type", ptdata.cdi[k].type);
    365			ptdata.cdi[k].instance = i;
    366			if (strstr(ptdata.cdi[k].type, ctrl_cdev)) {
    367				ptdata.cdi[k].flag |= CDEV_FLAG_IN_CONTROL;
    368				syslog(LOG_DEBUG, "control cdev id %d\n", i);
    369			}
    370			while (n--)
    371				free(namelist[n]);
    372			free(namelist);
    373		}
    374		closedir(dir);
    375		k++;
    376	}
    377	return 0;
    378}
    379
    380
    381int probe_thermal_sysfs(void)
    382{
    383	DIR *dir;
    384	struct dirent **namelist;
    385	int n;
    386
    387	dir = opendir(THERMAL_SYSFS);
    388	if (!dir) {
    389		fprintf(stderr, "\nNo thermal sysfs, exit\n");
    390		return -1;
    391	}
    392	n = scandir(THERMAL_SYSFS, &namelist, 0, alphasort);
    393	if (n < 0)
    394		syslog(LOG_ERR, "scandir failed in thermal sysfs");
    395	else {
    396		/* detect number of thermal zones and cooling devices */
    397		while (n--) {
    398			int inst;
    399
    400			if (strstr(namelist[n]->d_name, CDEV)) {
    401				inst = get_instance_id(namelist[n]->d_name, 1,
    402						sizeof("device") - 1);
    403				/* keep track of the max cooling device since
    404				 * there may be gaps.
    405				 */
    406				if (inst > ptdata.max_cdev_instance)
    407					ptdata.max_cdev_instance = inst;
    408
    409				syslog(LOG_DEBUG, "found cdev: %s %d %d\n",
    410					namelist[n]->d_name,
    411					ptdata.nr_cooling_dev,
    412					ptdata.max_cdev_instance);
    413				ptdata.nr_cooling_dev++;
    414			} else if (strstr(namelist[n]->d_name, TZONE)) {
    415				inst = get_instance_id(namelist[n]->d_name, 1,
    416						sizeof("zone") - 1);
    417				if (inst > ptdata.max_tz_instance)
    418					ptdata.max_tz_instance = inst;
    419
    420				syslog(LOG_DEBUG, "found tzone: %s %d %d\n",
    421					namelist[n]->d_name,
    422					ptdata.nr_tz_sensor,
    423					ptdata.max_tz_instance);
    424				ptdata.nr_tz_sensor++;
    425			}
    426			free(namelist[n]);
    427		}
    428		free(namelist);
    429	}
    430	syslog(LOG_INFO, "found %d tzone(s), %d cdev(s), target zone %d\n",
    431		ptdata.nr_tz_sensor, ptdata.nr_cooling_dev,
    432		target_thermal_zone);
    433	closedir(dir);
    434
    435	if (!ptdata.nr_tz_sensor) {
    436		fprintf(stderr, "\nNo thermal zones found, exit\n\n");
    437		return -1;
    438	}
    439
    440	ptdata.tzi = calloc(ptdata.max_tz_instance+1, sizeof(struct tz_info));
    441	if (!ptdata.tzi) {
    442		fprintf(stderr, "Err: allocate tz_info\n");
    443		return -1;
    444	}
    445
    446	/* we still show thermal zone information if there is no cdev */
    447	if (ptdata.nr_cooling_dev) {
    448		ptdata.cdi = calloc(ptdata.max_cdev_instance + 1,
    449				sizeof(struct cdev_info));
    450		if (!ptdata.cdi) {
    451			free(ptdata.tzi);
    452			fprintf(stderr, "Err: allocate cdev_info\n");
    453			return -1;
    454		}
    455	}
    456
    457	/* now probe tzones */
    458	if (scan_tzones())
    459		return -1;
    460	if (scan_cdevs())
    461		return -1;
    462	return 0;
    463}
    464
    465/* convert sysfs zone instance to zone array index */
    466int zone_instance_to_index(int zone_inst)
    467{
    468	int i;
    469
    470	for (i = 0; i < ptdata.nr_tz_sensor; i++)
    471		if (ptdata.tzi[i].instance == zone_inst)
    472			return i;
    473	return -ENOENT;
    474}
    475
    476/* read temperature of all thermal zones */
    477int update_thermal_data()
    478{
    479	int i;
    480	int next_thermal_record = cur_thermal_record + 1;
    481	char tz_name[256];
    482	static unsigned long samples;
    483
    484	if (!ptdata.nr_tz_sensor) {
    485		syslog(LOG_ERR, "No thermal zones found!\n");
    486		return -1;
    487	}
    488
    489	/* circular buffer for keeping historic data */
    490	if (next_thermal_record >= NR_THERMAL_RECORDS)
    491		next_thermal_record = 0;
    492	gettimeofday(&trec[next_thermal_record].tv, NULL);
    493	if (tmon_log) {
    494		fprintf(tmon_log, "%lu ", ++samples);
    495		fprintf(tmon_log, "%3.1f ", p_param.t_target);
    496	}
    497	for (i = 0; i < ptdata.nr_tz_sensor; i++) {
    498		memset(tz_name, 0, sizeof(tz_name));
    499		snprintf(tz_name, 256, "%s/%s%d", THERMAL_SYSFS, TZONE,
    500			ptdata.tzi[i].instance);
    501		sysfs_get_ulong(tz_name, "temp",
    502				&trec[next_thermal_record].temp[i]);
    503		if (tmon_log)
    504			fprintf(tmon_log, "%lu ",
    505				trec[next_thermal_record].temp[i] / 1000);
    506	}
    507	cur_thermal_record = next_thermal_record;
    508	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
    509		char cdev_name[256];
    510		unsigned long val;
    511
    512		snprintf(cdev_name, 256, "%s/%s%d", THERMAL_SYSFS, CDEV,
    513			ptdata.cdi[i].instance);
    514		probe_cdev(&ptdata.cdi[i], cdev_name);
    515		val = ptdata.cdi[i].cur_state;
    516		if (val > 1000000)
    517			val = 0;
    518		if (tmon_log)
    519			fprintf(tmon_log, "%lu ", val);
    520	}
    521
    522	if (tmon_log) {
    523		fprintf(tmon_log, "\n");
    524		fflush(tmon_log);
    525	}
    526
    527	return 0;
    528}
    529
    530void set_ctrl_state(unsigned long state)
    531{
    532	char ctrl_cdev_path[256];
    533	int i;
    534	unsigned long cdev_state;
    535
    536	if (no_control)
    537		return;
    538	/* set all ctrl cdev to the same state */
    539	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
    540		if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
    541			if (ptdata.cdi[i].max_state < 10) {
    542				strcpy(ctrl_cdev, "None.");
    543				return;
    544			}
    545			/* scale to percentage of max_state */
    546			cdev_state = state * ptdata.cdi[i].max_state/100;
    547			syslog(LOG_DEBUG,
    548				"ctrl cdev %d set state %lu scaled to %lu\n",
    549				ptdata.cdi[i].instance, state, cdev_state);
    550			snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
    551				CDEV, ptdata.cdi[i].instance);
    552			syslog(LOG_DEBUG, "ctrl cdev path %s", ctrl_cdev_path);
    553			sysfs_set_ulong(ctrl_cdev_path, "cur_state",
    554					cdev_state);
    555		}
    556	}
    557}
    558
    559void get_ctrl_state(unsigned long *state)
    560{
    561	char ctrl_cdev_path[256];
    562	int ctrl_cdev_id = -1;
    563	int i;
    564
    565	/* TODO: take average of all ctrl types. also consider change based on
    566	 * uevent. Take the first reading for now.
    567	 */
    568	for (i = 0; i < ptdata.nr_cooling_dev; i++) {
    569		if (ptdata.cdi[i].flag & CDEV_FLAG_IN_CONTROL) {
    570			ctrl_cdev_id = ptdata.cdi[i].instance;
    571			syslog(LOG_INFO, "ctrl cdev %d get state\n",
    572				ptdata.cdi[i].instance);
    573			break;
    574		}
    575	}
    576	if (ctrl_cdev_id == -1) {
    577		*state = 0;
    578		return;
    579	}
    580	snprintf(ctrl_cdev_path, 256, "%s/%s%d", THERMAL_SYSFS,
    581		CDEV, ctrl_cdev_id);
    582	sysfs_get_ulong(ctrl_cdev_path, "cur_state", state);
    583}
    584
    585void free_thermal_data(void)
    586{
    587	free(ptdata.tzi);
    588	free(ptdata.cdi);
    589}