18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Ideas taken over from the perf userspace tool (included in the Linus
68c2ecf20Sopenharmony_ci *  kernel git repo): subcommand builtins and param parsing.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <stdio.h>
108c2ecf20Sopenharmony_ci#include <stdlib.h>
118c2ecf20Sopenharmony_ci#include <string.h>
128c2ecf20Sopenharmony_ci#include <unistd.h>
138c2ecf20Sopenharmony_ci#include <errno.h>
148c2ecf20Sopenharmony_ci#include <sched.h>
158c2ecf20Sopenharmony_ci#include <sys/types.h>
168c2ecf20Sopenharmony_ci#include <sys/stat.h>
178c2ecf20Sopenharmony_ci#include <sys/utsname.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "builtin.h"
208c2ecf20Sopenharmony_ci#include "helpers/helpers.h"
218c2ecf20Sopenharmony_ci#include "helpers/bitmask.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int cmd_help(int argc, const char **argv);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* Global cpu_info object available for all binaries
288c2ecf20Sopenharmony_ci * Info only retrieved from CPU 0
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Values will be zero/unknown on non X86 archs
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_cistruct cpupower_cpu_info cpupower_cpu_info;
338c2ecf20Sopenharmony_ciint run_as_root;
348c2ecf20Sopenharmony_ciint base_cpu;
358c2ecf20Sopenharmony_ci/* Affected cpus chosen by -c/--cpu param */
368c2ecf20Sopenharmony_cistruct bitmask *cpus_chosen;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#ifdef DEBUG
398c2ecf20Sopenharmony_ciint be_verbose;
408c2ecf20Sopenharmony_ci#endif
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic void print_help(void);
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistruct cmd_struct {
458c2ecf20Sopenharmony_ci	const char *cmd;
468c2ecf20Sopenharmony_ci	int (*main)(int, const char **);
478c2ecf20Sopenharmony_ci	int needs_root;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct cmd_struct commands[] = {
518c2ecf20Sopenharmony_ci	{ "frequency-info",	cmd_freq_info,	0	},
528c2ecf20Sopenharmony_ci	{ "frequency-set",	cmd_freq_set,	1	},
538c2ecf20Sopenharmony_ci	{ "idle-info",		cmd_idle_info,	0	},
548c2ecf20Sopenharmony_ci	{ "idle-set",		cmd_idle_set,	1	},
558c2ecf20Sopenharmony_ci	{ "set",		cmd_set,	1	},
568c2ecf20Sopenharmony_ci	{ "info",		cmd_info,	0	},
578c2ecf20Sopenharmony_ci	{ "monitor",		cmd_monitor,	0	},
588c2ecf20Sopenharmony_ci	{ "help",		cmd_help,	0	},
598c2ecf20Sopenharmony_ci	/*	{ "bench",	cmd_bench,	1	}, */
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic void print_help(void)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	unsigned int i;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci#ifdef DEBUG
678c2ecf20Sopenharmony_ci	printf(_("Usage:\tcpupower [-d|--debug] [-c|--cpu cpulist ] <command> [<args>]\n"));
688c2ecf20Sopenharmony_ci#else
698c2ecf20Sopenharmony_ci	printf(_("Usage:\tcpupower [-c|--cpu cpulist ] <command> [<args>]\n"));
708c2ecf20Sopenharmony_ci#endif
718c2ecf20Sopenharmony_ci	printf(_("Supported commands are:\n"));
728c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(commands); i++)
738c2ecf20Sopenharmony_ci		printf("\t%s\n", commands[i].cmd);
748c2ecf20Sopenharmony_ci	printf(_("\nNot all commands can make use of the -c cpulist option.\n"));
758c2ecf20Sopenharmony_ci	printf(_("\nUse 'cpupower help <command>' for getting help for above commands.\n"));
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int print_man_page(const char *subpage)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int len;
818c2ecf20Sopenharmony_ci	char *page;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	len = 10; /* enough for "cpupower-" */
848c2ecf20Sopenharmony_ci	if (subpage != NULL)
858c2ecf20Sopenharmony_ci		len += strlen(subpage);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	page = malloc(len);
888c2ecf20Sopenharmony_ci	if (!page)
898c2ecf20Sopenharmony_ci		return -ENOMEM;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	sprintf(page, "cpupower");
928c2ecf20Sopenharmony_ci	if ((subpage != NULL) && strcmp(subpage, "help")) {
938c2ecf20Sopenharmony_ci		strcat(page, "-");
948c2ecf20Sopenharmony_ci		strcat(page, subpage);
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	execlp("man", "man", page, NULL);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* should not be reached */
1008c2ecf20Sopenharmony_ci	return -EINVAL;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int cmd_help(int argc, const char **argv)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	if (argc > 1) {
1068c2ecf20Sopenharmony_ci		print_man_page(argv[1]); /* exits within execlp() */
1078c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	print_help();
1118c2ecf20Sopenharmony_ci	return EXIT_SUCCESS;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void print_version(void)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	printf(PACKAGE " " VERSION "\n");
1178c2ecf20Sopenharmony_ci	printf(_("Report errors and bugs to %s, please.\n"), PACKAGE_BUGREPORT);
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic void handle_options(int *argc, const char ***argv)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	int ret, x, new_argc = 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (*argc < 1)
1258c2ecf20Sopenharmony_ci		return;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	for (x = 0;  x < *argc && ((*argv)[x])[0] == '-'; x++) {
1288c2ecf20Sopenharmony_ci		const char *param = (*argv)[x];
1298c2ecf20Sopenharmony_ci		if (!strcmp(param, "-h") || !strcmp(param, "--help")) {
1308c2ecf20Sopenharmony_ci			print_help();
1318c2ecf20Sopenharmony_ci			exit(EXIT_SUCCESS);
1328c2ecf20Sopenharmony_ci		} else if (!strcmp(param, "-c") || !strcmp(param, "--cpu")) {
1338c2ecf20Sopenharmony_ci			if (*argc < 2) {
1348c2ecf20Sopenharmony_ci				print_help();
1358c2ecf20Sopenharmony_ci				exit(EXIT_FAILURE);
1368c2ecf20Sopenharmony_ci			}
1378c2ecf20Sopenharmony_ci			if (!strcmp((*argv)[x+1], "all"))
1388c2ecf20Sopenharmony_ci				bitmask_setall(cpus_chosen);
1398c2ecf20Sopenharmony_ci			else {
1408c2ecf20Sopenharmony_ci				ret = bitmask_parselist(
1418c2ecf20Sopenharmony_ci						(*argv)[x+1], cpus_chosen);
1428c2ecf20Sopenharmony_ci				if (ret < 0) {
1438c2ecf20Sopenharmony_ci					fprintf(stderr, _("Error parsing cpu "
1448c2ecf20Sopenharmony_ci							  "list\n"));
1458c2ecf20Sopenharmony_ci					exit(EXIT_FAILURE);
1468c2ecf20Sopenharmony_ci				}
1478c2ecf20Sopenharmony_ci			}
1488c2ecf20Sopenharmony_ci			x += 1;
1498c2ecf20Sopenharmony_ci			/* Cut out param: cpupower -c 1 info -> cpupower info */
1508c2ecf20Sopenharmony_ci			new_argc += 2;
1518c2ecf20Sopenharmony_ci			continue;
1528c2ecf20Sopenharmony_ci		} else if (!strcmp(param, "-v") ||
1538c2ecf20Sopenharmony_ci			!strcmp(param, "--version")) {
1548c2ecf20Sopenharmony_ci			print_version();
1558c2ecf20Sopenharmony_ci			exit(EXIT_SUCCESS);
1568c2ecf20Sopenharmony_ci#ifdef DEBUG
1578c2ecf20Sopenharmony_ci		} else if (!strcmp(param, "-d") || !strcmp(param, "--debug")) {
1588c2ecf20Sopenharmony_ci			be_verbose = 1;
1598c2ecf20Sopenharmony_ci			new_argc++;
1608c2ecf20Sopenharmony_ci			continue;
1618c2ecf20Sopenharmony_ci#endif
1628c2ecf20Sopenharmony_ci		} else {
1638c2ecf20Sopenharmony_ci			fprintf(stderr, "Unknown option: %s\n", param);
1648c2ecf20Sopenharmony_ci			print_help();
1658c2ecf20Sopenharmony_ci			exit(EXIT_FAILURE);
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci	*argc -= new_argc;
1698c2ecf20Sopenharmony_ci	*argv += new_argc;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ciint main(int argc, const char *argv[])
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	const char *cmd;
1758c2ecf20Sopenharmony_ci	unsigned int i, ret;
1768c2ecf20Sopenharmony_ci	struct stat statbuf;
1778c2ecf20Sopenharmony_ci	struct utsname uts;
1788c2ecf20Sopenharmony_ci	char pathname[32];
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	cpus_chosen = bitmask_alloc(sysconf(_SC_NPROCESSORS_CONF));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	argc--;
1838c2ecf20Sopenharmony_ci	argv += 1;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	handle_options(&argc, &argv);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	cmd = argv[0];
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (argc < 1) {
1908c2ecf20Sopenharmony_ci		print_help();
1918c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	setlocale(LC_ALL, "");
1958c2ecf20Sopenharmony_ci	textdomain(PACKAGE);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Turn "perf cmd --help" into "perf help cmd" */
1988c2ecf20Sopenharmony_ci	if (argc > 1 && !strcmp(argv[1], "--help")) {
1998c2ecf20Sopenharmony_ci		argv[1] = argv[0];
2008c2ecf20Sopenharmony_ci		argv[0] = cmd = "help";
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	base_cpu = sched_getcpu();
2048c2ecf20Sopenharmony_ci	if (base_cpu < 0) {
2058c2ecf20Sopenharmony_ci		fprintf(stderr, _("No valid cpus found.\n"));
2068c2ecf20Sopenharmony_ci		return EXIT_FAILURE;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	get_cpu_info(&cpupower_cpu_info);
2108c2ecf20Sopenharmony_ci	run_as_root = !geteuid();
2118c2ecf20Sopenharmony_ci	if (run_as_root) {
2128c2ecf20Sopenharmony_ci		ret = uname(&uts);
2138c2ecf20Sopenharmony_ci		sprintf(pathname, "/dev/cpu/%d/msr", base_cpu);
2148c2ecf20Sopenharmony_ci		if (!ret && !strcmp(uts.machine, "x86_64") &&
2158c2ecf20Sopenharmony_ci		    stat(pathname, &statbuf) != 0) {
2168c2ecf20Sopenharmony_ci			if (system("modprobe msr") == -1)
2178c2ecf20Sopenharmony_ci	fprintf(stderr, _("MSR access not available.\n"));
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(commands); i++) {
2228c2ecf20Sopenharmony_ci		struct cmd_struct *p = commands + i;
2238c2ecf20Sopenharmony_ci		if (strcmp(p->cmd, cmd))
2248c2ecf20Sopenharmony_ci			continue;
2258c2ecf20Sopenharmony_ci		if (!run_as_root && p->needs_root) {
2268c2ecf20Sopenharmony_ci			fprintf(stderr, _("Subcommand %s needs root "
2278c2ecf20Sopenharmony_ci					  "privileges\n"), cmd);
2288c2ecf20Sopenharmony_ci			return EXIT_FAILURE;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci		ret = p->main(argc, argv);
2318c2ecf20Sopenharmony_ci		if (cpus_chosen)
2328c2ecf20Sopenharmony_ci			bitmask_free(cpus_chosen);
2338c2ecf20Sopenharmony_ci		return ret;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	print_help();
2368c2ecf20Sopenharmony_ci	return EXIT_FAILURE;
2378c2ecf20Sopenharmony_ci}
238