diff options
Diffstat (limited to 'tools')
32 files changed, 2673 insertions, 498 deletions
diff --git a/tools/hv/vmbus_testing b/tools/hv/vmbus_testing new file mode 100755 index 000000000000..e7212903dd1d --- /dev/null +++ b/tools/hv/vmbus_testing @@ -0,0 +1,376 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# +# Program to allow users to fuzz test Hyper-V drivers +# by interfacing with Hyper-V debugfs attributes. +# Current test methods available: +# 1. delay testing +# +# Current file/directory structure of hyper-V debugfs: +# /sys/kernel/debug/hyperv/UUID +# /sys/kernel/debug/hyperv/UUID/<test-state filename> +# /sys/kernel/debug/hyperv/UUID/<test-method sub-directory> +# +# author: Branden Bonaby <brandonbonaby94@gmail.com> + +import os +import cmd +import argparse +import glob +from argparse import RawDescriptionHelpFormatter +from argparse import RawTextHelpFormatter +from enum import Enum + +# Do not change unless, you change the debugfs attributes +# in /drivers/hv/debugfs.c. All fuzz testing +# attributes will start with "fuzz_test". + +# debugfs path for hyperv must exist before proceeding +debugfs_hyperv_path = "/sys/kernel/debug/hyperv" +if not os.path.isdir(debugfs_hyperv_path): + print("{} doesn't exist/check permissions".format(debugfs_hyperv_path)) + exit(-1) + +class dev_state(Enum): + off = 0 + on = 1 + +# File names, that correspond to the files created in +# /drivers/hv/debugfs.c +class f_names(Enum): + state_f = "fuzz_test_state" + buff_f = "fuzz_test_buffer_interrupt_delay" + mess_f = "fuzz_test_message_delay" + +# Both single_actions and all_actions are used +# for error checking and to allow for some subparser +# names to be abbreviated. Do not abbreviate the +# test method names, as it will become less intuitive +# as to what the user can do. If you do decide to +# abbreviate the test method name, make sure the main +# function reflects this change. + +all_actions = [ + "disable_all", + "D", + "enable_all", + "view_all", + "V" +] + +single_actions = [ + "disable_single", + "d", + "enable_single", + "view_single", + "v" +] + +def main(): + + file_map = recursive_file_lookup(debugfs_hyperv_path, dict()) + args = parse_args() + if (not args.action): + print ("Error, no options selected...exiting") + exit(-1) + arg_set = { k for (k,v) in vars(args).items() if v and k != "action" } + arg_set.add(args.action) + path = args.path if "path" in arg_set else None + if (path and path[-1] == "/"): + path = path[:-1] + validate_args_path(path, arg_set, file_map) + if (path and "enable_single" in arg_set): + state_path = locate_state(path, file_map) + set_test_state(state_path, dev_state.on.value, args.quiet) + + # Use subparsers as the key for different actions + if ("delay" in arg_set): + validate_delay_values(args.delay_time) + if (args.enable_all): + set_delay_all_devices(file_map, args.delay_time, + args.quiet) + else: + set_delay_values(path, file_map, args.delay_time, + args.quiet) + elif ("disable_all" in arg_set or "D" in arg_set): + disable_all_testing(file_map) + elif ("disable_single" in arg_set or "d" in arg_set): + disable_testing_single_device(path, file_map) + elif ("view_all" in arg_set or "V" in arg_set): + get_all_devices_test_status(file_map) + elif ("view_single" in arg_set or "v" in arg_set): + get_device_test_values(path, file_map) + +# Get the state location +def locate_state(device, file_map): + return file_map[device][f_names.state_f.value] + +# Validate delay values to make sure they are acceptable to +# enable delays on a device +def validate_delay_values(delay): + + if (delay[0] == -1 and delay[1] == -1): + print("\nError, At least 1 value must be greater than 0") + exit(-1) + for i in delay: + if (i < -1 or i == 0 or i > 1000): + print("\nError, Values must be equal to -1 " + "or be > 0 and <= 1000") + exit(-1) + +# Validate argument path +def validate_args_path(path, arg_set, file_map): + + if (not path and any(element in arg_set for element in single_actions)): + print("Error, path (-p) REQUIRED for the specified option. " + "Use (-h) to check usage.") + exit(-1) + elif (path and any(item in arg_set for item in all_actions)): + print("Error, path (-p) NOT REQUIRED for the specified option. " + "Use (-h) to check usage." ) + exit(-1) + elif (path not in file_map and any(item in arg_set + for item in single_actions)): + print("Error, path '{}' not a valid vmbus device".format(path)) + exit(-1) + +# display Testing status of single device +def get_device_test_values(path, file_map): + + for name in file_map[path]: + file_location = file_map[path][name] + print( name + " = " + str(read_test_files(file_location))) + +# Create a map of the vmbus devices and their associated files +# [key=device, value = [key = filename, value = file path]] +def recursive_file_lookup(path, file_map): + + for f_path in glob.iglob(path + '**/*'): + if (os.path.isfile(f_path)): + if (f_path.rsplit("/",2)[0] == debugfs_hyperv_path): + directory = f_path.rsplit("/",1)[0] + else: + directory = f_path.rsplit("/",2)[0] + f_name = f_path.split("/")[-1] + if (file_map.get(directory)): + file_map[directory].update({f_name:f_path}) + else: + file_map[directory] = {f_name:f_path} + elif (os.path.isdir(f_path)): + recursive_file_lookup(f_path,file_map) + return file_map + +# display Testing state of devices +def get_all_devices_test_status(file_map): + + for device in file_map: + if (get_test_state(locate_state(device, file_map)) is 1): + print("Testing = ON for: {}" + .format(device.split("/")[5])) + else: + print("Testing = OFF for: {}" + .format(device.split("/")[5])) + +# read the vmbus device files, path must be absolute path before calling +def read_test_files(path): + try: + with open(path,"r") as f: + file_value = f.readline().strip() + return int(file_value) + + except IOError as e: + errno, strerror = e.args + print("I/O error({0}): {1} on file {2}" + .format(errno, strerror, path)) + exit(-1) + except ValueError: + print ("Element to int conversion error in: \n{}".format(path)) + exit(-1) + +# writing to vmbus device files, path must be absolute path before calling +def write_test_files(path, value): + + try: + with open(path,"w") as f: + f.write("{}".format(value)) + except IOError as e: + errno, strerror = e.args + print("I/O error({0}): {1} on file {2}" + .format(errno, strerror, path)) + exit(-1) + +# set testing state of device +def set_test_state(state_path, state_value, quiet): + + write_test_files(state_path, state_value) + if (get_test_state(state_path) is 1): + if (not quiet): + print("Testing = ON for device: {}" + .format(state_path.split("/")[5])) + else: + if (not quiet): + print("Testing = OFF for device: {}" + .format(state_path.split("/")[5])) + +# get testing state of device +def get_test_state(state_path): + #state == 1 - test = ON + #state == 0 - test = OFF + return read_test_files(state_path) + +# write 1 - 1000 microseconds, into a single device using the +# fuzz_test_buffer_interrupt_delay and fuzz_test_message_delay +# debugfs attributes +def set_delay_values(device, file_map, delay_length, quiet): + + try: + interrupt = file_map[device][f_names.buff_f.value] + message = file_map[device][f_names.mess_f.value] + + # delay[0]- buffer interrupt delay, delay[1]- message delay + if (delay_length[0] >= 0 and delay_length[0] <= 1000): + write_test_files(interrupt, delay_length[0]) + if (delay_length[1] >= 0 and delay_length[1] <= 1000): + write_test_files(message, delay_length[1]) + if (not quiet): + print("Buffer delay testing = {} for: {}" + .format(read_test_files(interrupt), + interrupt.split("/")[5])) + print("Message delay testing = {} for: {}" + .format(read_test_files(message), + message.split("/")[5])) + except IOError as e: + errno, strerror = e.args + print("I/O error({0}): {1} on files {2}{3}" + .format(errno, strerror, interrupt, message)) + exit(-1) + +# enabling delay testing on all devices +def set_delay_all_devices(file_map, delay, quiet): + + for device in (file_map): + set_test_state(locate_state(device, file_map), + dev_state.on.value, + quiet) + set_delay_values(device, file_map, delay, quiet) + +# disable all testing on a SINGLE device. +def disable_testing_single_device(device, file_map): + + for name in file_map[device]: + file_location = file_map[device][name] + write_test_files(file_location, dev_state.off.value) + print("ALL testing now OFF for {}".format(device.split("/")[-1])) + +# disable all testing on ALL devices +def disable_all_testing(file_map): + + for device in file_map: + disable_testing_single_device(device, file_map) + +def parse_args(): + parser = argparse.ArgumentParser(prog = "vmbus_testing",usage ="\n" + "%(prog)s [delay] [-h] [-e|-E] -t [-p]\n" + "%(prog)s [view_all | V] [-h]\n" + "%(prog)s [disable_all | D] [-h]\n" + "%(prog)s [disable_single | d] [-h|-p]\n" + "%(prog)s [view_single | v] [-h|-p]\n" + "%(prog)s --version\n", + description = "\nUse lsvmbus to get vmbus device type " + "information.\n" "\nThe debugfs root path is " + "/sys/kernel/debug/hyperv", + formatter_class = RawDescriptionHelpFormatter) + subparsers = parser.add_subparsers(dest = "action") + parser.add_argument("--version", action = "version", + version = '%(prog)s 0.1.0') + parser.add_argument("-q","--quiet", action = "store_true", + help = "silence none important test messages." + " This will only work when enabling testing" + " on a device.") + # Use the path parser to hold the --path attribute so it can + # be shared between subparsers. Also do the same for the state + # parser, as all testing methods will use --enable_all and + # enable_single. + path_parser = argparse.ArgumentParser(add_help=False) + path_parser.add_argument("-p","--path", metavar = "", + help = "Debugfs path to a vmbus device. The path " + "must be the absolute path to the device.") + state_parser = argparse.ArgumentParser(add_help=False) + state_group = state_parser.add_mutually_exclusive_group(required = True) + state_group.add_argument("-E", "--enable_all", action = "store_const", + const = "enable_all", + help = "Enable the specified test type " + "on ALL vmbus devices.") + state_group.add_argument("-e", "--enable_single", + action = "store_const", + const = "enable_single", + help = "Enable the specified test type on a " + "SINGLE vmbus device.") + parser_delay = subparsers.add_parser("delay", + parents = [state_parser, path_parser], + help = "Delay the ring buffer interrupt or the " + "ring buffer message reads in microseconds.", + prog = "vmbus_testing", + usage = "%(prog)s [-h]\n" + "%(prog)s -E -t [value] [value]\n" + "%(prog)s -e -t [value] [value] -p", + description = "Delay the ring buffer interrupt for " + "vmbus devices, or delay the ring buffer message " + "reads for vmbus devices (both in microseconds). This " + "is only on the host to guest channel.") + parser_delay.add_argument("-t", "--delay_time", metavar = "", nargs = 2, + type = check_range, default =[0,0], required = (True), + help = "Set [buffer] & [message] delay time. " + "Value constraints: -1 == value " + "or 0 < value <= 1000.\n" + "Use -1 to keep the previous value for that delay " + "type, or a value > 0 <= 1000 to change the delay " + "time.") + parser_dis_all = subparsers.add_parser("disable_all", + aliases = ['D'], prog = "vmbus_testing", + usage = "%(prog)s [disable_all | D] -h\n" + "%(prog)s [disable_all | D]\n", + help = "Disable ALL testing on ALL vmbus devices.", + description = "Disable ALL testing on ALL vmbus " + "devices.") + parser_dis_single = subparsers.add_parser("disable_single", + aliases = ['d'], + parents = [path_parser], prog = "vmbus_testing", + usage = "%(prog)s [disable_single | d] -h\n" + "%(prog)s [disable_single | d] -p\n", + help = "Disable ALL testing on a SINGLE vmbus device.", + description = "Disable ALL testing on a SINGLE vmbus " + "device.") + parser_view_all = subparsers.add_parser("view_all", aliases = ['V'], + help = "View the test state for ALL vmbus devices.", + prog = "vmbus_testing", + usage = "%(prog)s [view_all | V] -h\n" + "%(prog)s [view_all | V]\n", + description = "This shows the test state for ALL the " + "vmbus devices.") + parser_view_single = subparsers.add_parser("view_single", + aliases = ['v'],parents = [path_parser], + help = "View the test values for a SINGLE vmbus " + "device.", + description = "This shows the test values for a SINGLE " + "vmbus device.", prog = "vmbus_testing", + usage = "%(prog)s [view_single | v] -h\n" + "%(prog)s [view_single | v] -p") + + return parser.parse_args() + +# value checking for range checking input in parser +def check_range(arg1): + + try: + val = int(arg1) + except ValueError as err: + raise argparse.ArgumentTypeError(str(err)) + if val < -1 or val > 1000: + message = ("\n\nvalue must be -1 or 0 < value <= 1000. " + "Value program received: {}\n").format(val) + raise argparse.ArgumentTypeError(message) + return val + +if __name__ == "__main__": + main() diff --git a/tools/iio/Build b/tools/iio/Build index f74cbda64710..8d0f3af3723f 100644 --- a/tools/iio/Build +++ b/tools/iio/Build @@ -1,3 +1,4 @@ +iio_utils-y += iio_utils.o lsiio-y += lsiio.o iio_utils.o iio_event_monitor-y += iio_event_monitor.o iio_utils.o iio_generic_buffer-y += iio_generic_buffer.o iio_utils.o diff --git a/tools/iio/Makefile b/tools/iio/Makefile index e22378dba244..3de763d9ab70 100644 --- a/tools/iio/Makefile +++ b/tools/iio/Makefile @@ -32,20 +32,24 @@ $(OUTPUT)include/linux/iio: ../../include/uapi/linux/iio prepare: $(OUTPUT)include/linux/iio +IIO_UTILS_IN := $(OUTPUT)iio_utils-in.o +$(IIO_UTILS_IN): prepare FORCE + $(Q)$(MAKE) $(build)=iio_utils + LSIIO_IN := $(OUTPUT)lsiio-in.o -$(LSIIO_IN): prepare FORCE +$(LSIIO_IN): prepare FORCE $(OUTPUT)iio_utils-in.o $(Q)$(MAKE) $(build)=lsiio $(OUTPUT)lsiio: $(LSIIO_IN) $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ IIO_EVENT_MONITOR_IN := $(OUTPUT)iio_event_monitor-in.o -$(IIO_EVENT_MONITOR_IN): prepare FORCE +$(IIO_EVENT_MONITOR_IN): prepare FORCE $(OUTPUT)iio_utils-in.o $(Q)$(MAKE) $(build)=iio_event_monitor $(OUTPUT)iio_event_monitor: $(IIO_EVENT_MONITOR_IN) $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ IIO_GENERIC_BUFFER_IN := $(OUTPUT)iio_generic_buffer-in.o -$(IIO_GENERIC_BUFFER_IN): prepare FORCE +$(IIO_GENERIC_BUFFER_IN): prepare FORCE $(OUTPUT)iio_utils-in.o $(Q)$(MAKE) $(build)=iio_generic_buffer $(OUTPUT)iio_generic_buffer: $(IIO_GENERIC_BUFFER_IN) $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@ diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 2a9890c8395a..944183f9ed5a 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -11,10 +11,11 @@ struct process_cmd_struct { char *feature; char *command; - void (*process_fn)(void); + void (*process_fn)(int arg); + int arg; }; -static const char *version_str = "v1.0"; +static const char *version_str = "v1.1"; static const int supported_api_ver = 1; static struct isst_if_platform_info isst_platform_info; static char *progname; @@ -22,6 +23,7 @@ static int debug_flag; static FILE *outf; static int cpu_model; +static int cpu_stepping; #define MAX_CPUS_IN_ONE_REQ 64 static short max_target_cpus; @@ -39,6 +41,7 @@ static unsigned long long fact_trl; static int out_format_json; static int cmd_help; static int force_online_offline; +static int auto_mode; /* clos related */ static int current_clos = -1; @@ -70,7 +73,16 @@ void debug_printf(const char *format, ...) va_end(args); } -static void update_cpu_model(void) + +int is_clx_n_platform(void) +{ + if (cpu_model == 0x55) + if (cpu_stepping == 0x6 || cpu_stepping == 0x7) + return 1; + return 0; +} + +static int update_cpu_model(void) { unsigned int ebx, ecx, edx; unsigned int fms, family; @@ -80,6 +92,33 @@ static void update_cpu_model(void) cpu_model = (fms >> 4) & 0xf; if (family == 6 || family == 0xf) cpu_model += ((fms >> 16) & 0xf) << 4; + + cpu_stepping = fms & 0xf; + /* only three CascadeLake-N models are supported */ + if (is_clx_n_platform()) { + FILE *fp; + size_t n = 0; + char *line = NULL; + int ret = 1; + + fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + err(-1, "cannot open /proc/cpuinfo\n"); + + while (getline(&line, &n, fp) > 0) { + if (strstr(line, "model name")) { + if (strstr(line, "6252N") || + strstr(line, "6230N") || + strstr(line, "5218N")) + ret = 0; + break; + } + } + free(line); + fclose(fp); + return ret; + } + return 0; } /* Open a file, and exit on failure */ @@ -161,6 +200,11 @@ int get_physical_die_id(int cpu) return ret; } +int get_cpufreq_base_freq(int cpu) +{ + return parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", cpu); +} + int get_topo_max_cpus(void) { return topo_max_cpus; @@ -169,7 +213,7 @@ int get_topo_max_cpus(void) static void set_cpu_online_offline(int cpu, int state) { char buffer[128]; - int fd; + int fd, ret; snprintf(buffer, sizeof(buffer), "/sys/devices/system/cpu/cpu%d/online", cpu); @@ -179,9 +223,12 @@ static void set_cpu_online_offline(int cpu, int state) err(-1, "%s open failed", buffer); if (state) - write(fd, "1\n", 2); + ret = write(fd, "1\n", 2); else - write(fd, "0\n", 2); + ret = write(fd, "0\n", 2); + + if (ret == -1) + perror("Online/Offline: Operation failed\n"); close(fd); } @@ -291,6 +338,7 @@ void free_cpu_set(cpu_set_t *cpu_set) } static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; +static long long core_mask[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; static void set_cpu_present_cpu_mask(void) { size_t size; @@ -315,13 +363,33 @@ static void set_cpu_present_cpu_mask(void) pkg_id = get_physical_package_id(i); if (pkg_id < MAX_PACKAGE_COUNT && - die_id < MAX_DIE_PER_PACKAGE) + die_id < MAX_DIE_PER_PACKAGE) { + int core_id = get_physical_core_id(i); + cpu_cnt[pkg_id][die_id]++; + core_mask[pkg_id][die_id] |= (1ULL << core_id); + } } closedir(dir); } } +int get_core_count(int pkg_id, int die_id) +{ + int cnt = 0; + + if (pkg_id < MAX_PACKAGE_COUNT && die_id < MAX_DIE_PER_PACKAGE) { + int i; + + for (i = 0; i < sizeof(long long) * 8; ++i) { + if (core_mask[pkg_id][die_id] & (1ULL << i)) + cnt++; + } + } + + return cnt; +} + int get_cpu_count(int pkg_id, int die_id) { if (pkg_id < MAX_PACKAGE_COUNT && die_id < MAX_DIE_PER_PACKAGE) @@ -532,12 +600,6 @@ int isst_send_mbox_command(unsigned int cpu, unsigned char command, if (!ret && !write) *resp = value; break; - case CLOS_PM_QOS_CONFIG: - ret = isst_send_mmio_command(cpu, PM_QOS_CONFIG_OFFSET, - write, &value); - if (!ret && !write) - *resp = value; - break; case CLOS_STATUS: break; default: @@ -562,6 +624,7 @@ int isst_send_mbox_command(unsigned int cpu, unsigned char command, fprintf(outf, "Error: mbox_cmd cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n", cpu, command, sub_command, parameter, req_data); + return -1; } else { *resp = mbox_cmds.mbox_cmd[0].resp_data; debug_printf( @@ -678,7 +741,7 @@ static void exec_on_get_ctdp_cpu(int cpu, void *arg1, void *arg2, void *arg3, } #define _get_tdp_level(desc, suffix, object, help) \ - static void get_tdp_##object(void) \ + static void get_tdp_##object(int arg) \ { \ struct isst_pkg_ctdp ctdp; \ \ @@ -708,6 +771,152 @@ _get_tdp_level("get-config-current_level", levels, current_level, "Current TDP Level"); _get_tdp_level("get-lock-status", levels, locked, "TDP lock status"); +struct isst_pkg_ctdp clx_n_pkg_dev; + +static int clx_n_get_base_ratio(void) +{ + FILE *fp; + char *begin, *end, *line = NULL; + char number[5]; + float value = 0; + size_t n = 0; + + fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + err(-1, "cannot open /proc/cpuinfo\n"); + + while (getline(&line, &n, fp) > 0) { + if (strstr(line, "model name")) { + /* this is true for CascadeLake-N */ + begin = strstr(line, "@ ") + 2; + end = strstr(line, "GHz"); + strncpy(number, begin, end - begin); + value = atof(number) * 10; + break; + } + } + free(line); + fclose(fp); + + return (int)(value); +} + +static int clx_n_config(int cpu) +{ + int i, ret, pkg_id, die_id; + unsigned long cpu_bf; + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + ctdp_level->core_cpumask_size = + alloc_cpu_set(&ctdp_level->core_cpumask); + + /* find the frequency base ratio */ + ctdp_level->tdp_ratio = clx_n_get_base_ratio(); + if (ctdp_level->tdp_ratio == 0) { + debug_printf("CLX: cn base ratio is zero\n"); + ret = -1; + goto error_ret; + } + + /* find the high and low priority frequencies */ + pbf_info->p1_high = 0; + pbf_info->p1_low = ~0; + + pkg_id = get_physical_package_id(cpu); + die_id = get_physical_die_id(cpu); + + for (i = 0; i < topo_max_cpus; i++) { + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + if (pkg_id != get_physical_package_id(i) || + die_id != get_physical_die_id(i)) + continue; + + CPU_SET_S(i, ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + + cpu_bf = parse_int_file(1, + "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", + i); + if (cpu_bf > pbf_info->p1_high) + pbf_info->p1_high = cpu_bf; + if (cpu_bf < pbf_info->p1_low) + pbf_info->p1_low = cpu_bf; + } + + if (pbf_info->p1_high == ~0UL) { + debug_printf("CLX: maximum base frequency not set\n"); + ret = -1; + goto error_ret; + } + + if (pbf_info->p1_low == 0) { + debug_printf("CLX: minimum base frequency not set\n"); + ret = -1; + goto error_ret; + } + + /* convert frequencies back to ratios */ + pbf_info->p1_high = pbf_info->p1_high / 100000; + pbf_info->p1_low = pbf_info->p1_low / 100000; + + /* create high priority cpu mask */ + pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask); + for (i = 0; i < topo_max_cpus; i++) { + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + if (pkg_id != get_physical_package_id(i) || + die_id != get_physical_die_id(i)) + continue; + + cpu_bf = parse_int_file(1, + "/sys/devices/system/cpu/cpu%d/cpufreq/base_frequency", + i); + cpu_bf = cpu_bf / 100000; + if (cpu_bf == pbf_info->p1_high) + CPU_SET_S(i, pbf_info->core_cpumask_size, + pbf_info->core_cpumask); + } + + /* extra ctdp & pbf struct parameters */ + ctdp_level->processed = 1; + ctdp_level->pbf_support = 1; /* PBF is always supported and enabled */ + ctdp_level->pbf_enabled = 1; + ctdp_level->fact_support = 0; /* FACT is never supported */ + ctdp_level->fact_enabled = 0; + + return 0; + +error_ret: + free_cpu_set(ctdp_level->core_cpumask); + return ret; +} + +static void dump_clx_n_config_for_cpu(int cpu, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + int ret; + + ret = clx_n_config(cpu); + if (ret) { + perror("isst_get_process_ctdp"); + } else { + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + isst_ctdp_display_information(cpu, outf, tdp_level, &clx_n_pkg_dev); + free_cpu_set(ctdp_level->core_cpumask); + free_cpu_set(pbf_info->core_cpumask); + } +} + static void dump_isst_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, void *arg4) { @@ -724,8 +933,10 @@ static void dump_isst_config_for_cpu(int cpu, void *arg1, void *arg2, } } -static void dump_isst_config(void) +static void dump_isst_config(int arg) { + void *fn; + if (cmd_help) { fprintf(stderr, "Print Intel(R) Speed Select Technology Performance profile configuration\n"); @@ -737,14 +948,17 @@ static void dump_isst_config(void) exit(0); } + if (!is_clx_n_platform()) + fn = dump_isst_config_for_cpu; + else + fn = dump_clx_n_config_for_cpu; + isst_ctdp_display_information_start(outf); if (max_target_cpus) - for_each_online_target_cpu_in_set(dump_isst_config_for_cpu, - NULL, NULL, NULL, NULL); + for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(dump_isst_config_for_cpu, NULL, - NULL, NULL, NULL); + for_each_online_package_in_set(fn, NULL, NULL, NULL, NULL); isst_ctdp_display_information_end(outf); } @@ -787,7 +1001,7 @@ static void set_tdp_level_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, } } -static void set_tdp_level(void) +static void set_tdp_level(int arg) { if (cmd_help) { fprintf(stderr, "Set Config TDP level\n"); @@ -812,6 +1026,26 @@ static void set_tdp_level(void) isst_ctdp_display_information_end(outf); } +static void clx_n_dump_pbf_config_for_cpu(int cpu, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + int ret; + + ret = clx_n_config(cpu); + if (ret) { + perror("isst_get_process_ctdp"); + } else { + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + isst_pbf_display_information(cpu, outf, tdp_level, pbf_info); + free_cpu_set(ctdp_level->core_cpumask); + free_cpu_set(pbf_info->core_cpumask); + } +} + static void dump_pbf_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, void *arg4) { @@ -827,8 +1061,10 @@ static void dump_pbf_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, } } -static void dump_pbf_config(void) +static void dump_pbf_config(int arg) { + void *fn; + if (cmd_help) { fprintf(stderr, "Print Intel(R) Speed Select Technology base frequency configuration for a TDP level\n"); @@ -842,72 +1078,372 @@ static void dump_pbf_config(void) exit(1); } + if (!is_clx_n_platform()) + fn = dump_pbf_config_for_cpu; + else + fn = clx_n_dump_pbf_config_for_cpu; + isst_ctdp_display_information_start(outf); + if (max_target_cpus) - for_each_online_target_cpu_in_set(dump_pbf_config_for_cpu, NULL, - NULL, NULL, NULL); + for_each_online_target_cpu_in_set(fn, NULL, NULL, NULL, NULL); else - for_each_online_package_in_set(dump_pbf_config_for_cpu, NULL, - NULL, NULL, NULL); + for_each_online_package_in_set(fn, NULL, NULL, NULL, NULL); + isst_ctdp_display_information_end(outf); } -static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, - void *arg4) +static int set_clos_param(int cpu, int clos, int epp, int wt, int min, int max) { + struct isst_clos_config clos_config; int ret; - int status = *(int *)arg4; - ret = isst_set_pbf_fact_status(cpu, 1, status); + ret = isst_pm_get_clos(cpu, clos, &clos_config); if (ret) { - perror("isst_set_pbf"); - } else { - if (status) - isst_display_result(cpu, outf, "base-freq", "enable", - ret); + perror("isst_pm_get_clos"); + return ret; + } + clos_config.clos_min = min; + clos_config.clos_max = max; + clos_config.epp = epp; + clos_config.clos_prop_prio = wt; + ret = isst_set_clos(cpu, clos, &clos_config); + if (ret) { + perror("isst_pm_set_clos"); + return ret; + } + + return 0; +} + +static int set_cpufreq_scaling_min_max(int cpu, int max, int freq) +{ + char buffer[128], freq_str[16]; + int fd, ret, len; + + if (max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu); + + fd = open(buffer, O_WRONLY); + if (fd < 0) + return fd; + + snprintf(freq_str, sizeof(freq_str), "%d", freq); + len = strlen(freq_str); + ret = write(fd, freq_str, len); + if (ret == -1) { + close(fd); + return ret; + } + close(fd); + + return 0; +} + +static int set_clx_pbf_cpufreq_scaling_min_max(int cpu) +{ + struct isst_pkg_ctdp_level_info *ctdp_level; + struct isst_pbf_info *pbf_info; + int i, pkg_id, die_id, freq, freq_high, freq_low; + int ret; + + ret = clx_n_config(cpu); + if (ret) { + perror("set_clx_pbf_cpufreq_scaling_min_max"); + return ret; + } + + ctdp_level = &clx_n_pkg_dev.ctdp_level[0]; + pbf_info = &ctdp_level->pbf_info; + freq_high = pbf_info->p1_high * 100000; + freq_low = pbf_info->p1_low * 100000; + + pkg_id = get_physical_package_id(cpu); + die_id = get_physical_die_id(cpu); + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (pkg_id != get_physical_package_id(i) || + die_id != get_physical_die_id(i)) + continue; + + if (CPU_ISSET_S(i, pbf_info->core_cpumask_size, + pbf_info->core_cpumask)) + freq = freq_high; else - isst_display_result(cpu, outf, "base-freq", "disable", - ret); + freq = freq_low; + + set_cpufreq_scaling_min_max(i, 1, freq); + set_cpufreq_scaling_min_max(i, 0, freq); } + + return 0; } -static void set_pbf_enable(void) +static int set_cpufreq_scaling_min_max_from_cpuinfo(int cpu, int cpuinfo_max, int scaling_max) { - int status = 1; + char buffer[128], min_freq[16]; + int fd, ret, len; - if (cmd_help) { - fprintf(stderr, - "Enable Intel Speed Select Technology base frequency feature [No command arguments are required]\n"); - exit(0); + if (!CPU_ISSET_S(cpu, present_cpumask_size, present_cpumask)) + return -1; + + if (cpuinfo_max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_max_freq", cpu); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/cpuinfo_min_freq", cpu); + + fd = open(buffer, O_RDONLY); + if (fd < 0) + return fd; + + len = read(fd, min_freq, sizeof(min_freq)); + close(fd); + + if (len < 0) + return len; + + if (scaling_max) + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); + else + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_min_freq", cpu); + + fd = open(buffer, O_WRONLY); + if (fd < 0) + return fd; + + len = strlen(min_freq); + ret = write(fd, min_freq, len); + if (ret == -1) { + close(fd); + return ret; } + close(fd); - isst_ctdp_display_information_start(outf); - if (max_target_cpus) - for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL, - NULL, &status); + return 0; +} + +static void set_scaling_min_to_cpuinfo_max(int cpu) +{ + int i, pkg_id, die_id; + + pkg_id = get_physical_package_id(cpu); + die_id = get_physical_die_id(cpu); + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (pkg_id != get_physical_package_id(i) || + die_id != get_physical_die_id(i)) + continue; + + set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 0); + } +} + +static void set_scaling_min_to_cpuinfo_min(int cpu) +{ + int i, pkg_id, die_id; + + pkg_id = get_physical_package_id(cpu); + die_id = get_physical_die_id(cpu); + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (pkg_id != get_physical_package_id(i) || + die_id != get_physical_die_id(i)) + continue; + + set_cpufreq_scaling_min_max_from_cpuinfo(i, 0, 0); + } +} + +static void set_scaling_max_to_cpuinfo_max(int cpu) +{ + int i, pkg_id, die_id; + + pkg_id = get_physical_package_id(cpu); + die_id = get_physical_die_id(cpu); + for (i = 0; i < get_topo_max_cpus(); ++i) { + if (pkg_id != get_physical_package_id(i) || + die_id != get_physical_die_id(i)) + continue; + + set_cpufreq_scaling_min_max_from_cpuinfo(i, 1, 1); + } +} + +static int set_core_priority_and_min(int cpu, int mask_size, + cpu_set_t *cpu_mask, int min_high, + int min_low) +{ + int pkg_id, die_id, ret, i; + + if (!CPU_COUNT_S(mask_size, cpu_mask)) + return -1; + + ret = set_clos_param(cpu, 0, 0, 0, min_high, 0xff); + if (ret) + return ret; + + ret = set_clos_param(cpu, 1, 15, 15, min_low, 0xff); + if (ret) + return ret; + + ret = set_clos_param(cpu, 2, 15, 15, min_low, 0xff); + if (ret) + return ret; + + ret = set_clos_param(cpu, 3, 15, 15, min_low, 0xff); + if (ret) + return ret; + + pkg_id = get_physical_package_id(cpu); + die_id = get_physical_die_id(cpu); + for (i = 0; i < get_topo_max_cpus(); ++i) { + int clos; + + if (pkg_id != get_physical_package_id(i) || + die_id != get_physical_die_id(i)) + continue; + + if (CPU_ISSET_S(i, mask_size, cpu_mask)) + clos = 0; + else + clos = 3; + + debug_printf("Associate cpu: %d clos: %d\n", i, clos); + ret = isst_clos_associate(i, clos); + if (ret) { + perror("isst_clos_associate"); + return ret; + } + } + + return 0; +} + +static int set_pbf_core_power(int cpu) +{ + struct isst_pbf_info pbf_info; + struct isst_pkg_ctdp pkg_dev; + int ret; + + ret = isst_get_ctdp_levels(cpu, &pkg_dev); + if (ret) { + perror("isst_get_ctdp_levels"); + return ret; + } + debug_printf("Current_level: %d\n", pkg_dev.current_level); + + ret = isst_get_pbf_info(cpu, pkg_dev.current_level, &pbf_info); + if (ret) { + perror("isst_get_pbf_info"); + return ret; + } + debug_printf("p1_high: %d p1_low: %d\n", pbf_info.p1_high, + pbf_info.p1_low); + + ret = set_core_priority_and_min(cpu, pbf_info.core_cpumask_size, + pbf_info.core_cpumask, + pbf_info.p1_high, pbf_info.p1_low); + if (ret) { + perror("set_core_priority_and_min"); + return ret; + } + + ret = isst_pm_qos_config(cpu, 1, 1); + if (ret) { + perror("isst_pm_qos_config"); + return ret; + } + + return 0; +} + +static void set_pbf_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, + void *arg4) +{ + int ret; + int status = *(int *)arg4; + + if (is_clx_n_platform()) { + if (status) { + ret = 0; + if (auto_mode) + set_clx_pbf_cpufreq_scaling_min_max(cpu); + + } else { + ret = -1; + if (auto_mode) { + set_scaling_max_to_cpuinfo_max(cpu); + set_scaling_min_to_cpuinfo_min(cpu); + } + } + goto disp_result; + } + + if (auto_mode) { + if (status) { + ret = set_pbf_core_power(cpu); + if (ret) + goto disp_result; + } else { + isst_pm_qos_config(cpu, 0, 0); + } + } + + ret = isst_set_pbf_fact_status(cpu, 1, status); + if (ret) { + perror("isst_set_pbf"); + if (auto_mode) + isst_pm_qos_config(cpu, 0, 0); + } else { + if (auto_mode) { + if (status) + set_scaling_min_to_cpuinfo_max(cpu); + else + set_scaling_min_to_cpuinfo_min(cpu); + } + } + +disp_result: + if (status) + isst_display_result(cpu, outf, "base-freq", "enable", + ret); else - for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL, - NULL, &status); - isst_ctdp_display_information_end(outf); + isst_display_result(cpu, outf, "base-freq", "disable", + ret); } -static void set_pbf_disable(void) +static void set_pbf_enable(int arg) { - int status = 0; + int enable = arg; if (cmd_help) { - fprintf(stderr, - "Disable Intel Speed Select Technology base frequency feature [No command arguments are required]\n"); + if (enable) { + fprintf(stderr, + "Enable Intel Speed Select Technology base frequency feature\n"); + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Use priority of cores to set core-power associations\n"); + } else { + + fprintf(stderr, + "Disable Intel Speed Select Technology base frequency feature\n"); + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Also disable core-power associations\n"); + } exit(0); } isst_ctdp_display_information_start(outf); if (max_target_cpus) for_each_online_target_cpu_in_set(set_pbf_for_cpu, NULL, NULL, - NULL, &status); + NULL, &enable); else for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL, - NULL, &status); + NULL, &enable); isst_ctdp_display_information_end(outf); } @@ -925,7 +1461,7 @@ static void dump_fact_config_for_cpu(int cpu, void *arg1, void *arg2, fact_avx, &fact_info); } -static void dump_fact_config(void) +static void dump_fact_config(int arg) { if (cmd_help) { fprintf(stderr, @@ -960,73 +1496,156 @@ static void set_fact_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, int ret; int status = *(int *)arg4; + if (auto_mode) { + if (status) { + ret = isst_pm_qos_config(cpu, 1, 1); + if (ret) + goto disp_results; + } else { + isst_pm_qos_config(cpu, 0, 0); + } + } + ret = isst_set_pbf_fact_status(cpu, 0, status); - if (ret) + if (ret) { perror("isst_set_fact"); - else { - if (status) { - struct isst_pkg_ctdp pkg_dev; + if (auto_mode) + isst_pm_qos_config(cpu, 0, 0); - ret = isst_get_ctdp_levels(cpu, &pkg_dev); - if (ret) { - isst_display_result(cpu, outf, "turbo-freq", - "enable", ret); - return; - } + goto disp_results; + } + + /* Set TRL */ + if (status) { + struct isst_pkg_ctdp pkg_dev; + + ret = isst_get_ctdp_levels(cpu, &pkg_dev); + if (!ret) ret = isst_set_trl(cpu, fact_trl); - isst_display_result(cpu, outf, "turbo-freq", "enable", - ret); - } else { - /* Since we modified TRL during Fact enable, restore it */ - isst_set_trl_from_current_tdp(cpu, fact_trl); - isst_display_result(cpu, outf, "turbo-freq", "disable", - ret); - } + if (ret && auto_mode) + isst_pm_qos_config(cpu, 0, 0); + } + +disp_results: + if (status) { + isst_display_result(cpu, outf, "turbo-freq", "enable", ret); + } else { + /* Since we modified TRL during Fact enable, restore it */ + isst_set_trl_from_current_tdp(cpu, fact_trl); + isst_display_result(cpu, outf, "turbo-freq", "disable", ret); } } -static void set_fact_enable(void) +static void set_fact_enable(int arg) { - int status = 1; + int i, ret, enable = arg; if (cmd_help) { - fprintf(stderr, - "Enable Intel Speed Select Technology Turbo frequency feature\n"); - fprintf(stderr, - "Optional: -t|--trl : Specify turbo ratio limit\n"); + if (enable) { + fprintf(stderr, + "Enable Intel Speed Select Technology Turbo frequency feature\n"); + fprintf(stderr, + "Optional: -t|--trl : Specify turbo ratio limit\n"); + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Designate specified target CPUs with"); + fprintf(stderr, + "-C|--cpu option as as high priority using core-power feature\n"); + } else { + fprintf(stderr, + "Disable Intel Speed Select Technology turbo frequency feature\n"); + fprintf(stderr, + "Optional: -t|--trl : Specify turbo ratio limit\n"); + fprintf(stderr, + "\tOptional Arguments: -a|--auto : Also disable core-power associations\n"); + } exit(0); } isst_ctdp_display_information_start(outf); if (max_target_cpus) for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL, - NULL, &status); + NULL, &enable); else for_each_online_package_in_set(set_fact_for_cpu, NULL, NULL, - NULL, &status); + NULL, &enable); isst_ctdp_display_information_end(outf); -} -static void set_fact_disable(void) -{ - int status = 0; + if (enable && auto_mode) { + /* + * When we adjust CLOS param, we have to set for siblings also. + * So for the each user specified CPU, also add the sibling + * in the present_cpu_mask. + */ + for (i = 0; i < get_topo_max_cpus(); ++i) { + char buffer[128], sibling_list[128], *cpu_str; + int fd, len; - if (cmd_help) { - fprintf(stderr, - "Disable Intel Speed Select Technology turbo frequency feature\n"); - fprintf(stderr, - "Optional: -t|--trl : Specify turbo ratio limit\n"); - exit(0); + if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask)) + continue; + + snprintf(buffer, sizeof(buffer), + "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i); + + fd = open(buffer, O_RDONLY); + if (fd < 0) + continue; + + len = read(fd, sibling_list, sizeof(sibling_list)); + close(fd); + + if (len < 0) + continue; + + cpu_str = strtok(sibling_list, ","); + while (cpu_str != NULL) { + int cpu; + + sscanf(cpu_str, "%d", &cpu); + CPU_SET_S(cpu, target_cpumask_size, target_cpumask); + cpu_str = strtok(NULL, ","); + } + } + + for (i = 0; i < get_topo_max_cpus(); ++i) { + int clos; + + if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask)) + continue; + + ret = set_clos_param(i, 0, 0, 0, 0, 0xff); + if (ret) + goto error_disp; + + ret = set_clos_param(i, 1, 15, 15, 0, 0xff); + if (ret) + goto error_disp; + + ret = set_clos_param(i, 2, 15, 15, 0, 0xff); + if (ret) + goto error_disp; + + ret = set_clos_param(i, 3, 15, 15, 0, 0xff); + if (ret) + goto error_disp; + + if (CPU_ISSET_S(i, target_cpumask_size, target_cpumask)) + clos = 0; + else + clos = 3; + + debug_printf("Associate cpu: %d clos: %d\n", i, clos); + ret = isst_clos_associate(i, clos); + if (ret) + goto error_disp; + } + isst_display_result(i, outf, "turbo-freq --auto", "enable", 0); } - isst_ctdp_display_information_start(outf); - if (max_target_cpus) - for_each_online_target_cpu_in_set(set_fact_for_cpu, NULL, NULL, - NULL, &status); - else - for_each_online_package_in_set(set_fact_for_cpu, NULL, NULL, - NULL, &status); - isst_ctdp_display_information_end(outf); + return; + +error_disp: + isst_display_result(i, outf, "turbo-freq --auto", "enable", ret); + } static void enable_clos_qos_config(int cpu, void *arg1, void *arg2, void *arg3, @@ -1036,31 +1655,36 @@ static void enable_clos_qos_config(int cpu, void *arg1, void *arg2, void *arg3, int status = *(int *)arg4; ret = isst_pm_qos_config(cpu, status, clos_priority_type); - if (ret) { + if (ret) perror("isst_pm_qos_config"); - } else { - if (status) - isst_display_result(cpu, outf, "core-power", "enable", - ret); - else - isst_display_result(cpu, outf, "core-power", "disable", - ret); - } + + if (status) + isst_display_result(cpu, outf, "core-power", "enable", + ret); + else + isst_display_result(cpu, outf, "core-power", "disable", + ret); } -static void set_clos_enable(void) +static void set_clos_enable(int arg) { - int status = 1; + int enable = arg; if (cmd_help) { - fprintf(stderr, "Enable core-power for a package/die\n"); - fprintf(stderr, - "\tClos Enable: Specify priority type with [--priority|-p]\n"); - fprintf(stderr, "\t\t 0: Proportional, 1: Ordered\n"); + if (enable) { + fprintf(stderr, + "Enable core-power for a package/die\n"); + fprintf(stderr, + "\tClos Enable: Specify priority type with [--priority|-p]\n"); + fprintf(stderr, "\t\t 0: Proportional, 1: Ordered\n"); + } else { + fprintf(stderr, + "Disable core-power: [No command arguments are required]\n"); + } exit(0); } - if (cpufreq_sysfs_present()) { + if (enable && cpufreq_sysfs_present()) { fprintf(stderr, "cpufreq subsystem and core-power enable will interfere with each other!\n"); } @@ -1068,30 +1692,10 @@ static void set_clos_enable(void) isst_ctdp_display_information_start(outf); if (max_target_cpus) for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL, - NULL, NULL, &status); + NULL, NULL, &enable); else for_each_online_package_in_set(enable_clos_qos_config, NULL, - NULL, NULL, &status); - isst_ctdp_display_information_end(outf); -} - -static void set_clos_disable(void) -{ - int status = 0; - - if (cmd_help) { - fprintf(stderr, - "Disable core-power: [No command arguments are required]\n"); - exit(0); - } - - isst_ctdp_display_information_start(outf); - if (max_target_cpus) - for_each_online_target_cpu_in_set(enable_clos_qos_config, NULL, - NULL, NULL, &status); - else - for_each_online_package_in_set(enable_clos_qos_config, NULL, - NULL, NULL, &status); + NULL, NULL, &enable); isst_ctdp_display_information_end(outf); } @@ -1109,7 +1713,7 @@ static void dump_clos_config_for_cpu(int cpu, void *arg1, void *arg2, &clos_config); } -static void dump_clos_config(void) +static void dump_clos_config(int arg) { if (cmd_help) { fprintf(stderr, @@ -1145,7 +1749,7 @@ static void get_clos_info_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, isst_clos_display_clos_information(cpu, outf, enable, prio_type); } -static void dump_clos_info(void) +static void dump_clos_info(int arg) { if (cmd_help) { fprintf(stderr, @@ -1188,7 +1792,7 @@ static void set_clos_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, isst_display_result(cpu, outf, "core-power", "config", ret); } -static void set_clos_config(void) +static void set_clos_config(int arg) { if (cmd_help) { fprintf(stderr, @@ -1198,9 +1802,9 @@ static void set_clos_config(void) fprintf(stderr, "\tSpecify clos EPP with [--epp|-e]\n"); fprintf(stderr, "\tSpecify clos Proportional Priority [--weight|-w]\n"); - fprintf(stderr, "\tSpecify clos min with [--min|-n]\n"); - fprintf(stderr, "\tSpecify clos max with [--max|-m]\n"); - fprintf(stderr, "\tSpecify clos desired with [--desired|-d]\n"); + fprintf(stderr, "\tSpecify clos min in MHz with [--min|-n]\n"); + fprintf(stderr, "\tSpecify clos max in MHz with [--max|-m]\n"); + fprintf(stderr, "\tSpecify clos desired in MHz with [--desired|-d]\n"); exit(0); } @@ -1222,7 +1826,7 @@ static void set_clos_config(void) clos_min = 0; } if (clos_max < 0) { - fprintf(stderr, "clos max is not specified, default: 0xff\n"); + fprintf(stderr, "clos max is not specified, default: 25500 MHz\n"); clos_max = 0xff; } if (clos_desired < 0) { @@ -1252,7 +1856,7 @@ static void set_clos_assoc_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, isst_display_result(cpu, outf, "core-power", "assoc", ret); } -static void set_clos_assoc(void) +static void set_clos_assoc(int arg) { if (cmd_help) { fprintf(stderr, "Associate a clos id to a CPU\n"); @@ -1286,7 +1890,7 @@ static void get_clos_assoc_for_cpu(int cpu, void *arg1, void *arg2, void *arg3, isst_clos_display_assoc_information(cpu, outf, clos); } -static void get_clos_assoc(void) +static void get_clos_assoc(int arg) { if (cmd_help) { fprintf(stderr, "Get associate clos id to a CPU\n"); @@ -1306,27 +1910,36 @@ static void get_clos_assoc(void) isst_ctdp_display_information_end(outf); } +static struct process_cmd_struct clx_n_cmds[] = { + { "perf-profile", "info", dump_isst_config, 0 }, + { "base-freq", "info", dump_pbf_config, 0 }, + { "base-freq", "enable", set_pbf_enable, 1 }, + { "base-freq", "disable", set_pbf_enable, 0 }, + { NULL, NULL, NULL, 0 } +}; + static struct process_cmd_struct isst_cmds[] = { - { "perf-profile", "get-lock-status", get_tdp_locked }, - { "perf-profile", "get-config-levels", get_tdp_levels }, - { "perf-profile", "get-config-version", get_tdp_version }, - { "perf-profile", "get-config-enabled", get_tdp_enabled }, - { "perf-profile", "get-config-current-level", get_tdp_current_level }, - { "perf-profile", "set-config-level", set_tdp_level }, - { "perf-profile", "info", dump_isst_config }, - { "base-freq", "info", dump_pbf_config }, - { "base-freq", "enable", set_pbf_enable }, - { "base-freq", "disable", set_pbf_disable }, - { "turbo-freq", "info", dump_fact_config }, - { "turbo-freq", "enable", set_fact_enable }, - { "turbo-freq", "disable", set_fact_disable }, - { "core-power", "info", dump_clos_info }, - { "core-power", "enable", set_clos_enable }, - { "core-power", "disable", set_clos_disable }, - { "core-power", "config", set_clos_config }, - { "core-power", "get-config", dump_clos_config }, - { "core-power", "assoc", set_clos_assoc }, - { "core-power", "get-assoc", get_clos_assoc }, + { "perf-profile", "get-lock-status", get_tdp_locked, 0 }, + { "perf-profile", "get-config-levels", get_tdp_levels, 0 }, + { "perf-profile", "get-config-version", get_tdp_version, 0 }, + { "perf-profile", "get-config-enabled", get_tdp_enabled, 0 }, + { "perf-profile", "get-config-current-level", get_tdp_current_level, + 0 }, + { "perf-profile", "set-config-level", set_tdp_level, 0 }, + { "perf-profile", "info", dump_isst_config, 0 }, + { "base-freq", "info", dump_pbf_config, 0 }, + { "base-freq", "enable", set_pbf_enable, 1 }, + { "base-freq", "disable", set_pbf_enable, 0 }, + { "turbo-freq", "info", dump_fact_config, 0 }, + { "turbo-freq", "enable", set_fact_enable, 1 }, + { "turbo-freq", "disable", set_fact_enable, 0 }, + { "core-power", "info", dump_clos_info, 0 }, + { "core-power", "enable", set_clos_enable, 1 }, + { "core-power", "disable", set_clos_enable, 0 }, + { "core-power", "config", set_clos_config, 0 }, + { "core-power", "get-config", dump_clos_config, 0 }, + { "core-power", "assoc", set_clos_assoc, 0 }, + { "core-power", "get-assoc", get_clos_assoc, 0 }, { NULL, NULL, NULL } }; @@ -1417,15 +2030,19 @@ static void parse_cmd_args(int argc, int start, char **argv) { "max", required_argument, 0, 'm' }, { "priority", required_argument, 0, 'p' }, { "weight", required_argument, 0, 'w' }, + { "auto", no_argument, 0, 'a' }, { 0, 0, 0, 0 } }; option_index = start; optind = start + 1; - while ((opt = getopt_long(argc, argv, "b:l:t:c:d:e:n:m:p:w:ho", + while ((opt = getopt_long(argc, argv, "b:l:t:c:d:e:n:m:p:w:hoa", long_options, &option_index)) != -1) { switch (opt) { + case 'a': + auto_mode = 1; + break; case 'b': fact_bucket = atoi(optarg); break; @@ -1459,15 +2076,18 @@ static void parse_cmd_args(int argc, int start, char **argv) break; case 'd': clos_desired = atoi(optarg); + clos_desired /= DISP_FREQ_MULTIPLIER; break; case 'e': clos_epp = atoi(optarg); break; case 'n': clos_min = atoi(optarg); + clos_min /= DISP_FREQ_MULTIPLIER; break; case 'm': clos_max = atoi(optarg); + clos_max /= DISP_FREQ_MULTIPLIER; break; case 'p': clos_priority_type = atoi(optarg); @@ -1489,12 +2109,15 @@ static void isst_help(void) TDP, etc.\n"); printf("\nCommands : For feature=perf-profile\n"); printf("\tinfo\n"); - printf("\tget-lock-status\n"); - printf("\tget-config-levels\n"); - printf("\tget-config-version\n"); - printf("\tget-config-enabled\n"); - printf("\tget-config-current-level\n"); - printf("\tset-config-level\n"); + + if (!is_clx_n_platform()) { + printf("\tget-lock-status\n"); + printf("\tget-config-levels\n"); + printf("\tget-config-version\n"); + printf("\tget-config-enabled\n"); + printf("\tget-config-current-level\n"); + printf("\tset-config-level\n"); + } } static void pbf_help(void) @@ -1544,7 +2167,15 @@ static struct process_cmd_help_struct isst_help_cmds[] = { { NULL, NULL } }; -void process_command(int argc, char **argv) +static struct process_cmd_help_struct clx_n_help_cmds[] = { + { "perf-profile", isst_help }, + { "base-freq", pbf_help }, + { NULL, NULL } +}; + +void process_command(int argc, char **argv, + struct process_cmd_help_struct *help_cmds, + struct process_cmd_struct *cmds) { int i = 0, matched = 0; char *feature = argv[optind]; @@ -1555,23 +2186,24 @@ void process_command(int argc, char **argv) debug_printf("feature name [%s] command [%s]\n", feature, cmd); if (!strcmp(cmd, "-h") || !strcmp(cmd, "--help")) { - while (isst_help_cmds[i].feature) { - if (!strcmp(isst_help_cmds[i].feature, feature)) { - isst_help_cmds[i].process_fn(); + while (help_cmds[i].feature) { + if (!strcmp(help_cmds[i].feature, feature)) { + help_cmds[i].process_fn(); exit(0); } ++i; } } - create_cpu_map(); + if (!is_clx_n_platform()) + create_cpu_map(); i = 0; - while (isst_cmds[i].feature) { - if (!strcmp(isst_cmds[i].feature, feature) && - !strcmp(isst_cmds[i].command, cmd)) { + while (cmds[i].feature) { + if (!strcmp(cmds[i].feature, feature) && + !strcmp(cmds[i].command, cmd)) { parse_cmd_args(argc, optind + 1, argv); - isst_cmds[i].process_fn(); + cmds[i].process_fn(cmds[i].arg); matched = 1; break; } @@ -1682,17 +2314,23 @@ static void cmdline(int argc, char **argv) fprintf(stderr, "Feature name and|or command not specified\n"); exit(0); } - update_cpu_model(); + ret = update_cpu_model(); + if (ret) + err(-1, "Invalid CPU model (%d)\n", cpu_model); printf("Intel(R) Speed Select Technology\n"); printf("Executing on CPU model:%d[0x%x]\n", cpu_model, cpu_model); set_max_cpu_num(); set_cpu_present_cpu_mask(); set_cpu_target_cpu_mask(); - ret = isst_fill_platform_info(); - if (ret) - goto out; - process_command(argc, argv); + if (!is_clx_n_platform()) { + ret = isst_fill_platform_info(); + if (ret) + goto out; + process_command(argc, argv, isst_help_cmds, isst_cmds); + } else { + process_command(argc, argv, clx_n_help_cmds, clx_n_cmds); + } out: free_cpu_set(present_cpumask); free_cpu_set(target_cpumask); diff --git a/tools/power/x86/intel-speed-select/isst-core.c b/tools/power/x86/intel-speed-select/isst-core.c index 6dee5332c9d3..d14c7bcd327a 100644 --- a/tools/power/x86/intel-speed-select/isst-core.c +++ b/tools/power/x86/intel-speed-select/isst-core.c @@ -13,8 +13,14 @@ int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev) ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp); - if (ret) - return ret; + if (ret) { + pkg_dev->levels = 0; + pkg_dev->locked = 1; + pkg_dev->current_level = 0; + pkg_dev->version = 0; + pkg_dev->enabled = 0; + return 0; + } debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", cpu, resp); @@ -95,6 +101,69 @@ int isst_get_pwr_info(int cpu, int config_index, return 0; } +void isst_get_uncore_p0_p1_info(int cpu, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + ret = isst_send_mbox_command(cpu, CONFIG_TDP, + CONFIG_TDP_GET_UNCORE_P0_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->uncore_p0 = 0; + ctdp_level->uncore_p1 = 0; + return; + } + + ctdp_level->uncore_p0 = resp & GENMASK(7, 0); + ctdp_level->uncore_p1 = (resp & GENMASK(15, 8)) >> 8; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_UNCORE_P0_P1_INFO resp:%x uncore p0:%d uncore p1:%d\n", + cpu, config_index, resp, ctdp_level->uncore_p0, + ctdp_level->uncore_p1); +} + +void isst_get_p1_info(int cpu, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_P1_INFO, 0, + config_index, &resp); + if (ret) { + ctdp_level->sse_p1 = 0; + ctdp_level->avx2_p1 = 0; + ctdp_level->avx512_p1 = 0; + return; + } + + ctdp_level->sse_p1 = resp & GENMASK(7, 0); + ctdp_level->avx2_p1 = (resp & GENMASK(15, 8)) >> 8; + ctdp_level->avx512_p1 = (resp & GENMASK(23, 16)) >> 16; + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_P1_INFO resp:%x sse_p1:%d avx2_p1:%d avx512_p1:%d\n", + cpu, config_index, resp, ctdp_level->sse_p1, + ctdp_level->avx2_p1, ctdp_level->avx512_p1); +} + +void isst_get_uncore_mem_freq(int cpu, int config_index, + struct isst_pkg_ctdp_level_info *ctdp_level) +{ + unsigned int resp; + int ret; + ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_GET_MEM_FREQ, + 0, config_index, &resp); + if (ret) { + ctdp_level->mem_freq = 0; + return; + } + + ctdp_level->mem_freq = resp & GENMASK(7, 0); + debug_printf( + "cpu:%d ctdp:%d CONFIG_TDP_GET_MEM_FREQ resp:%x uncore mem_freq:%d\n", + cpu, config_index, resp, ctdp_level->mem_freq); +} + int isst_get_tjmax_info(int cpu, int config_index, struct isst_pkg_ctdp_level_info *ctdp_level) { @@ -149,6 +218,27 @@ int isst_get_coremask_info(int cpu, int config_index, return 0; } +int isst_get_get_trl_from_msr(int cpu, int *trl) +{ + unsigned long long msr_trl; + int ret; + + ret = isst_send_msr_command(cpu, 0x1AD, 0, &msr_trl); + if (ret) + return ret; + + trl[0] = msr_trl & GENMASK(7, 0); + trl[1] = (msr_trl & GENMASK(15, 8)) >> 8; + trl[2] = (msr_trl & GENMASK(23, 16)) >> 16; + trl[3] = (msr_trl & GENMASK(31, 24)) >> 24; + trl[4] = (msr_trl & GENMASK(39, 32)) >> 32; + trl[5] = (msr_trl & GENMASK(47, 40)) >> 40; + trl[6] = (msr_trl & GENMASK(55, 48)) >> 48; + trl[7] = (msr_trl & GENMASK(63, 56)) >> 56; + + return 0; +} + int isst_get_get_trl(int cpu, int level, int avx_level, int *trl) { unsigned int req, resp; @@ -245,12 +335,15 @@ int isst_set_tdp_level(int cpu, int tdp_level) int isst_get_pbf_info(int cpu, int level, struct isst_pbf_info *pbf_info) { + int i, ret, core_cnt, max; unsigned int req, resp; - int i, ret; pbf_info->core_cpumask_size = alloc_cpu_set(&pbf_info->core_cpumask); - for (i = 0; i < 2; ++i) { + core_cnt = get_core_count(get_physical_package_id(cpu), get_physical_die_id(cpu)); + max = core_cnt > 32 ? 2 : 1; + + for (i = 0; i < max; ++i) { unsigned long long mask; int count; @@ -258,7 +351,7 @@ int isst_get_pbf_info(int cpu, int level, struct isst_pbf_info *pbf_info) CONFIG_TDP_PBF_GET_CORE_MASK_INFO, 0, (i << 8) | level, &resp); if (ret) - return ret; + break; debug_printf( "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n", @@ -323,7 +416,7 @@ int isst_set_pbf_fact_status(int cpu, int pbf, int enable) ret = isst_get_ctdp_levels(cpu, &pkg_dev); if (ret) - return ret; + debug_printf("cpu:%d No support for dynamic ISST\n", cpu); current_level = pkg_dev.current_level; @@ -553,7 +646,6 @@ int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev) i); ctdp_level = &pkg_dev->ctdp_level[i]; - ctdp_level->processed = 1; ctdp_level->level = i; ctdp_level->control_cpu = cpu; ctdp_level->pkg_id = get_physical_package_id(cpu); @@ -561,7 +653,37 @@ int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev) ret = isst_get_ctdp_control(cpu, i, ctdp_level); if (ret) - return ret; + continue; + + pkg_dev->processed = 1; + ctdp_level->processed = 1; + + if (ctdp_level->pbf_support) { + ret = isst_get_pbf_info(cpu, i, &ctdp_level->pbf_info); + if (!ret) + ctdp_level->pbf_found = 1; + } + + if (ctdp_level->fact_support) { + ret = isst_get_fact_info(cpu, i, + &ctdp_level->fact_info); + if (ret) + return ret; + } + + if (!pkg_dev->enabled) { + int freq; + + freq = get_cpufreq_base_freq(cpu); + if (freq > 0) { + ctdp_level->sse_p1 = freq / 100000; + ctdp_level->tdp_ratio = ctdp_level->sse_p1; + } + + isst_get_get_trl_from_msr(cpu, ctdp_level->trl_sse_active_cores); + isst_get_trl_bucket_info(cpu, &ctdp_level->buckets_info); + continue; + } ret = isst_get_tdp_info(cpu, i, ctdp_level); if (ret) @@ -600,22 +722,11 @@ int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev) if (ret) return ret; - if (ctdp_level->pbf_support) { - ret = isst_get_pbf_info(cpu, i, &ctdp_level->pbf_info); - if (!ret) - ctdp_level->pbf_found = 1; - } - - if (ctdp_level->fact_support) { - ret = isst_get_fact_info(cpu, i, - &ctdp_level->fact_info); - if (ret) - return ret; - } + isst_get_uncore_p0_p1_info(cpu, i, ctdp_level); + isst_get_p1_info(cpu, i, ctdp_level); + isst_get_uncore_mem_freq(cpu, i, ctdp_level); } - pkg_dev->processed = 1; - return 0; } @@ -649,6 +760,27 @@ int isst_pm_qos_config(int cpu, int enable_clos, int priority_type) unsigned int req, resp; int ret; + if (!enable_clos) { + struct isst_pkg_ctdp pkg_dev; + struct isst_pkg_ctdp_level_info ctdp_level; + + ret = isst_get_ctdp_levels(cpu, &pkg_dev); + if (ret) { + debug_printf("isst_get_ctdp_levels\n"); + return ret; + } + + ret = isst_get_ctdp_control(cpu, pkg_dev.current_level, + &ctdp_level); + if (ret) + return ret; + + if (ctdp_level.fact_enabled) { + debug_printf("Turbo-freq feature must be disabled first\n"); + return -EINVAL; + } + } + ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0, &resp); if (ret) diff --git a/tools/power/x86/intel-speed-select/isst-display.c b/tools/power/x86/intel-speed-select/isst-display.c index 40346d534f78..040dd09d5eee 100644 --- a/tools/power/x86/intel-speed-select/isst-display.c +++ b/tools/power/x86/intel-speed-select/isst-display.c @@ -6,8 +6,6 @@ #include "isst.h" -#define DISP_FREQ_MULTIPLIER 100 - static void printcpulist(int str_len, char *str, int mask_size, cpu_set_t *cpu_mask) { @@ -204,6 +202,9 @@ static void _isst_pbf_display_information(int cpu, FILE *outf, int level, pbf_info->p1_low * DISP_FREQ_MULTIPLIER); format_and_print(outf, disp_level + 1, header, value); + if (is_clx_n_platform()) + return; + snprintf(header, sizeof(header), "tjunction-temperature(C)"); snprintf(value, sizeof(value), "%d", pbf_info->t_prochot); format_and_print(outf, disp_level + 1, header, value); @@ -314,7 +315,8 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, char value[256]; int i, base_level = 1; - print_package_info(cpu, outf); + if (pkg_dev->processed) + print_package_info(cpu, outf); for (i = 0; i <= pkg_dev->levels; ++i) { struct isst_pkg_ctdp_level_info *ctdp_level; @@ -334,27 +336,66 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, snprintf(value, sizeof(value), "%d", j); format_and_print(outf, base_level + 4, header, value); - snprintf(header, sizeof(header), "enable-cpu-mask"); - printcpumask(sizeof(value), value, - ctdp_level->core_cpumask_size, - ctdp_level->core_cpumask); - format_and_print(outf, base_level + 4, header, value); - - snprintf(header, sizeof(header), "enable-cpu-list"); - printcpulist(sizeof(value), value, - ctdp_level->core_cpumask_size, - ctdp_level->core_cpumask); - format_and_print(outf, base_level + 4, header, value); + if (ctdp_level->core_cpumask_size) { + snprintf(header, sizeof(header), "enable-cpu-mask"); + printcpumask(sizeof(value), value, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + format_and_print(outf, base_level + 4, header, value); + + snprintf(header, sizeof(header), "enable-cpu-list"); + printcpulist(sizeof(value), value, + ctdp_level->core_cpumask_size, + ctdp_level->core_cpumask); + format_and_print(outf, base_level + 4, header, value); + } snprintf(header, sizeof(header), "thermal-design-power-ratio"); snprintf(value, sizeof(value), "%d", ctdp_level->tdp_ratio); format_and_print(outf, base_level + 4, header, value); snprintf(header, sizeof(header), "base-frequency(MHz)"); + if (!ctdp_level->sse_p1) + ctdp_level->sse_p1 = ctdp_level->tdp_ratio; snprintf(value, sizeof(value), "%d", - ctdp_level->tdp_ratio * DISP_FREQ_MULTIPLIER); + ctdp_level->sse_p1 * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 4, header, value); + if (ctdp_level->avx2_p1) { + snprintf(header, sizeof(header), "base-frequency-avx2(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->avx2_p1 * DISP_FREQ_MULTIPLIER); + format_and_print(outf, base_level + 4, header, value); + } + + if (ctdp_level->avx512_p1) { + snprintf(header, sizeof(header), "base-frequency-avx512(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->avx512_p1 * DISP_FREQ_MULTIPLIER); + format_and_print(outf, base_level + 4, header, value); + } + + if (ctdp_level->uncore_p1) { + snprintf(header, sizeof(header), "uncore-frequency-min(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->uncore_p1 * DISP_FREQ_MULTIPLIER); + format_and_print(outf, base_level + 4, header, value); + } + + if (ctdp_level->uncore_p0) { + snprintf(header, sizeof(header), "uncore-frequency-max(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->uncore_p0 * DISP_FREQ_MULTIPLIER); + format_and_print(outf, base_level + 4, header, value); + } + + if (ctdp_level->mem_freq) { + snprintf(header, sizeof(header), "mem-frequency(MHz)"); + snprintf(value, sizeof(value), "%d", + ctdp_level->mem_freq * DISP_FREQ_MULTIPLIER); + format_and_print(outf, base_level + 4, header, value); + } + snprintf(header, sizeof(header), "speed-select-turbo-freq"); if (ctdp_level->fact_support) { @@ -377,13 +418,26 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, snprintf(value, sizeof(value), "unsupported"); format_and_print(outf, base_level + 4, header, value); - snprintf(header, sizeof(header), "thermal-design-power(W)"); - snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp); - format_and_print(outf, base_level + 4, header, value); + if (is_clx_n_platform()) { + if (ctdp_level->pbf_support) + _isst_pbf_display_information(cpu, outf, + tdp_level, + &ctdp_level->pbf_info, + base_level + 4); + continue; + } - snprintf(header, sizeof(header), "tjunction-max(C)"); - snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot); - format_and_print(outf, base_level + 4, header, value); + if (ctdp_level->pkg_tdp) { + snprintf(header, sizeof(header), "thermal-design-power(W)"); + snprintf(value, sizeof(value), "%d", ctdp_level->pkg_tdp); + format_and_print(outf, base_level + 4, header, value); + } + + if (ctdp_level->t_proc_hot) { + snprintf(header, sizeof(header), "tjunction-max(C)"); + snprintf(value, sizeof(value), "%d", ctdp_level->t_proc_hot); + format_and_print(outf, base_level + 4, header, value); + } snprintf(header, sizeof(header), "turbo-ratio-limits-sse"); format_and_print(outf, base_level + 4, header, NULL); @@ -402,41 +456,41 @@ void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level, DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 6, header, value); } - snprintf(header, sizeof(header), "turbo-ratio-limits-avx"); - format_and_print(outf, base_level + 4, header, NULL); - for (j = 0; j < 8; ++j) { - snprintf(header, sizeof(header), "bucket-%d", j); - format_and_print(outf, base_level + 5, header, NULL); - snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); - format_and_print(outf, base_level + 6, header, value); + if (ctdp_level->trl_avx_active_cores[0]) { + snprintf(header, sizeof(header), "turbo-ratio-limits-avx2"); + format_and_print(outf, base_level + 4, header, NULL); + for (j = 0; j < 8; ++j) { + snprintf(header, sizeof(header), "bucket-%d", j); + format_and_print(outf, base_level + 5, header, NULL); - snprintf(header, sizeof(header), - "max-turbo-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - ctdp_level->trl_avx_active_cores[j] * - DISP_FREQ_MULTIPLIER); - format_and_print(outf, base_level + 6, header, value); + snprintf(header, sizeof(header), "core-count"); + snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); + format_and_print(outf, base_level + 6, header, value); + + snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); + snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_active_cores[j] * DISP_FREQ_MULTIPLIER); + format_and_print(outf, base_level + 6, header, value); + } } - snprintf(header, sizeof(header), "turbo-ratio-limits-avx512"); - format_and_print(outf, base_level + 4, header, NULL); - for (j = 0; j < 8; ++j) { - snprintf(header, sizeof(header), "bucket-%d", j); - format_and_print(outf, base_level + 5, header, NULL); + if (ctdp_level->trl_avx_512_active_cores[0]) { + snprintf(header, sizeof(header), "turbo-ratio-limits-avx512"); + format_and_print(outf, base_level + 4, header, NULL); + for (j = 0; j < 8; ++j) { + snprintf(header, sizeof(header), "bucket-%d", j); + format_and_print(outf, base_level + 5, header, NULL); - snprintf(header, sizeof(header), "core-count"); - snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); - format_and_print(outf, base_level + 6, header, value); + snprintf(header, sizeof(header), "core-count"); + snprintf(value, sizeof(value), "%llu", (ctdp_level->buckets_info >> (j * 8)) & 0xff); + format_and_print(outf, base_level + 6, header, value); - snprintf(header, sizeof(header), - "max-turbo-frequency(MHz)"); - snprintf(value, sizeof(value), "%d", - ctdp_level->trl_avx_512_active_cores[j] * - DISP_FREQ_MULTIPLIER); + snprintf(header, sizeof(header), "max-turbo-frequency(MHz)"); + snprintf(value, sizeof(value), "%d", ctdp_level->trl_avx_512_active_cores[j] * DISP_FREQ_MULTIPLIER); format_and_print(outf, base_level + 6, header, value); + } } + if (ctdp_level->pbf_support) _isst_pbf_display_information(cpu, outf, i, &ctdp_level->pbf_info, @@ -509,15 +563,15 @@ void isst_clos_display_information(int cpu, FILE *outf, int clos, format_and_print(outf, 5, header, value); snprintf(header, sizeof(header), "clos-min"); - snprintf(value, sizeof(value), "%d", clos_config->clos_min); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_min * DISP_FREQ_MULTIPLIER); format_and_print(outf, 5, header, value); snprintf(header, sizeof(header), "clos-max"); - snprintf(value, sizeof(value), "%d", clos_config->clos_max); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_max * DISP_FREQ_MULTIPLIER); format_and_print(outf, 5, header, value); snprintf(header, sizeof(header), "clos-desired"); - snprintf(value, sizeof(value), "%d", clos_config->clos_desired); + snprintf(value, sizeof(value), "%d MHz", clos_config->clos_desired * DISP_FREQ_MULTIPLIER); format_and_print(outf, 5, header, value); format_and_print(outf, 1, NULL, NULL); diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index d280b27d600d..cdf0f8a6dbbf 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -69,6 +69,8 @@ #define PM_CLOS_OFFSET 0x08 #define PQR_ASSOC_OFFSET 0x20 +#define DISP_FREQ_MULTIPLIER 100 + struct isst_clos_config { int pkg_id; int die_id; @@ -161,6 +163,7 @@ struct isst_pkg_ctdp { extern int get_topo_max_cpus(void); extern int get_cpu_count(int pkg_id, int die_id); +extern int get_core_count(int pkg_id, int die_id); /* Common interfaces */ extern void debug_printf(const char *format, ...); @@ -237,4 +240,6 @@ extern void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd, extern int isst_clos_get_clos_information(int cpu, int *enable, int *type); extern void isst_clos_display_clos_information(int cpu, FILE *outf, int clos_enable, int type); +extern int is_clx_n_platform(void); +extern int get_cpufreq_base_freq(int cpu); #endif diff --git a/tools/testing/selftests/ftrace/settings b/tools/testing/selftests/ftrace/settings new file mode 100644 index 000000000000..e7b9417537fb --- /dev/null +++ b/tools/testing/selftests/ftrace/settings @@ -0,0 +1 @@ +timeout=0 diff --git a/tools/testing/selftests/ftrace/test.d/direct/ftrace-direct.tc b/tools/testing/selftests/ftrace/test.d/direct/ftrace-direct.tc new file mode 100644 index 000000000000..d75a8695bc21 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/direct/ftrace-direct.tc @@ -0,0 +1,69 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test ftrace direct functions against tracers + +rmmod ftrace-direct ||: +if ! modprobe ftrace-direct ; then + echo "No ftrace-direct sample module - please make CONFIG_SAMPLE_FTRACE_DIRECT=m" + exit_unresolved; +fi + +echo "Let the module run a little" +sleep 1 + +grep -q "my_direct_func: waking up" trace + +rmmod ftrace-direct + +test_tracer() { + tracer=$1 + + # tracer -> direct -> no direct > no tracer + echo $tracer > current_tracer + modprobe ftrace-direct + rmmod ftrace-direct + echo nop > current_tracer + + # tracer -> direct -> no tracer > no direct + echo $tracer > current_tracer + modprobe ftrace-direct + echo nop > current_tracer + rmmod ftrace-direct + + # direct -> tracer -> no tracer > no direct + modprobe ftrace-direct + echo $tracer > current_tracer + echo nop > current_tracer + rmmod ftrace-direct + + # direct -> tracer -> no direct > no notracer + modprobe ftrace-direct + echo $tracer > current_tracer + rmmod ftrace-direct + echo nop > current_tracer +} + +for t in `cat available_tracers`; do + if [ "$t" != "nop" ]; then + test_tracer $t + fi +done + +echo nop > current_tracer +rmmod ftrace-direct ||: + +# Now do the same thing with another direct function registered +echo "Running with another ftrace direct function" + +rmmod ftrace-direct-too ||: +modprobe ftrace-direct-too + +for t in `cat available_tracers`; do + if [ "$t" != "nop" ]; then + test_tracer $t + fi +done + +echo nop > current_tracer +rmmod ftrace-direct ||: +rmmod ftrace-direct-too ||: diff --git a/tools/testing/selftests/ftrace/test.d/direct/kprobe-direct.tc b/tools/testing/selftests/ftrace/test.d/direct/kprobe-direct.tc new file mode 100644 index 000000000000..801ecb63e84c --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/direct/kprobe-direct.tc @@ -0,0 +1,84 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# description: Test ftrace direct functions against kprobes + +rmmod ftrace-direct ||: +if ! modprobe ftrace-direct ; then + echo "No ftrace-direct sample module - please build with CONFIG_SAMPLE_FTRACE_DIRECT=m" + exit_unresolved; +fi + +if [ ! -f kprobe_events ]; then + echo "No kprobe_events file -please build CONFIG_KPROBE_EVENTS" + exit_unresolved; +fi + +echo "Let the module run a little" +sleep 1 + +grep -q "my_direct_func: waking up" trace + +rmmod ftrace-direct + +echo 'p:kwake wake_up_process task=$arg1' > kprobe_events + +start_direct() { + echo > trace + modprobe ftrace-direct + sleep 1 + grep -q "my_direct_func: waking up" trace +} + +stop_direct() { + rmmod ftrace-direct +} + +enable_probe() { + echo > trace + echo 1 > events/kprobes/kwake/enable + sleep 1 + grep -q "kwake:" trace +} + +disable_probe() { + echo 0 > events/kprobes/kwake/enable +} + +test_kprobes() { + # probe -> direct -> no direct > no probe + enable_probe + start_direct + stop_direct + disable_probe + + # probe -> direct -> no probe > no direct + enable_probe + start_direct + disable_probe + stop_direct + + # direct -> probe -> no probe > no direct + start_direct + enable_probe + disable_probe + stop_direct + + # direct -> probe -> no direct > no noprobe + start_direct + enable_probe + stop_direct + disable_probe +} + +test_kprobes + +# Now do this with a second registered direct function +echo "Running with another ftrace direct function" + +modprobe ftrace-direct-too + +test_kprobes + +rmmod ftrace-direct-too + +echo > kprobe_events diff --git a/tools/testing/selftests/livepatch/Makefile b/tools/testing/selftests/livepatch/Makefile index 1cf40a9e7185..3876d8d62494 100644 --- a/tools/testing/selftests/livepatch/Makefile +++ b/tools/testing/selftests/livepatch/Makefile @@ -5,6 +5,7 @@ TEST_PROGS := \ test-livepatch.sh \ test-callbacks.sh \ test-shadow-vars.sh \ - test-state.sh + test-state.sh \ + test-ftrace.sh include ../lib.mk diff --git a/tools/testing/selftests/livepatch/functions.sh b/tools/testing/selftests/livepatch/functions.sh index 79b0affd21fb..31eb09e38729 100644 --- a/tools/testing/selftests/livepatch/functions.sh +++ b/tools/testing/selftests/livepatch/functions.sh @@ -29,29 +29,45 @@ function die() { exit 1 } -function push_dynamic_debug() { - DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \ - awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}') +function push_config() { + DYNAMIC_DEBUG=$(grep '^kernel/livepatch' /sys/kernel/debug/dynamic_debug/control | \ + awk -F'[: ]' '{print "file " $1 " line " $2 " " $4}') + FTRACE_ENABLED=$(sysctl --values kernel.ftrace_enabled) } -function pop_dynamic_debug() { +function pop_config() { if [[ -n "$DYNAMIC_DEBUG" ]]; then echo -n "$DYNAMIC_DEBUG" > /sys/kernel/debug/dynamic_debug/control fi + if [[ -n "$FTRACE_ENABLED" ]]; then + sysctl kernel.ftrace_enabled="$FTRACE_ENABLED" &> /dev/null + fi } -# set_dynamic_debug() - save the current dynamic debug config and tweak -# it for the self-tests. Set a script exit trap -# that restores the original config. function set_dynamic_debug() { - push_dynamic_debug - trap pop_dynamic_debug EXIT INT TERM HUP cat <<-EOF > /sys/kernel/debug/dynamic_debug/control file kernel/livepatch/* +p func klp_try_switch_task -p EOF } +function set_ftrace_enabled() { + local sysctl="$1" + result=$(sysctl kernel.ftrace_enabled="$1" 2>&1 | paste --serial --delimiters=' ') + echo "livepatch: $result" > /dev/kmsg +} + +# setup_config - save the current config and set a script exit trap that +# restores the original config. Setup the dynamic debug +# for verbose livepatching output and turn on +# the ftrace_enabled sysctl. +function setup_config() { + push_config + set_dynamic_debug + set_ftrace_enabled 1 + trap pop_config EXIT INT TERM HUP +} + # loop_until(cmd) - loop a command until it is successful or $MAX_RETRIES, # sleep $RETRY_INTERVAL between attempts # cmd - command and its arguments to run diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh index e97a9dcb73c7..a35289b13c9c 100755 --- a/tools/testing/selftests/livepatch/test-callbacks.sh +++ b/tools/testing/selftests/livepatch/test-callbacks.sh @@ -9,7 +9,7 @@ MOD_LIVEPATCH2=test_klp_callbacks_demo2 MOD_TARGET=test_klp_callbacks_mod MOD_TARGET_BUSY=test_klp_callbacks_busy -set_dynamic_debug +setup_config # TEST: target module before livepatch diff --git a/tools/testing/selftests/livepatch/test-ftrace.sh b/tools/testing/selftests/livepatch/test-ftrace.sh new file mode 100755 index 000000000000..e2a76887f40a --- /dev/null +++ b/tools/testing/selftests/livepatch/test-ftrace.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019 Joe Lawrence <joe.lawrence@redhat.com> + +. $(dirname $0)/functions.sh + +MOD_LIVEPATCH=test_klp_livepatch + +setup_config + + +# TEST: livepatch interaction with ftrace_enabled sysctl +# - turn ftrace_enabled OFF and verify livepatches can't load +# - turn ftrace_enabled ON and verify livepatch can load +# - verify that ftrace_enabled can't be turned OFF while a livepatch is loaded + +echo -n "TEST: livepatch interaction with ftrace_enabled sysctl ... " +dmesg -C + +set_ftrace_enabled 0 +load_failing_mod $MOD_LIVEPATCH + +set_ftrace_enabled 1 +load_lp $MOD_LIVEPATCH +if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then + echo -e "FAIL\n\n" + die "livepatch kselftest(s) failed" +fi + +set_ftrace_enabled 0 +if [[ "$(cat /proc/cmdline)" != "$MOD_LIVEPATCH: this has been live patched" ]] ; then + echo -e "FAIL\n\n" + die "livepatch kselftest(s) failed" +fi +disable_lp $MOD_LIVEPATCH +unload_lp $MOD_LIVEPATCH + +check_result "livepatch: kernel.ftrace_enabled = 0 +% modprobe $MOD_LIVEPATCH +livepatch: enabling patch '$MOD_LIVEPATCH' +livepatch: '$MOD_LIVEPATCH': initializing patching transition +livepatch: failed to register ftrace handler for function 'cmdline_proc_show' (-16) +livepatch: failed to patch object 'vmlinux' +livepatch: failed to enable patch '$MOD_LIVEPATCH' +livepatch: '$MOD_LIVEPATCH': canceling patching transition, going to unpatch +livepatch: '$MOD_LIVEPATCH': completing unpatching transition +livepatch: '$MOD_LIVEPATCH': unpatching complete +modprobe: ERROR: could not insert '$MOD_LIVEPATCH': Device or resource busy +livepatch: kernel.ftrace_enabled = 1 +% modprobe $MOD_LIVEPATCH +livepatch: enabling patch '$MOD_LIVEPATCH' +livepatch: '$MOD_LIVEPATCH': initializing patching transition +livepatch: '$MOD_LIVEPATCH': starting patching transition +livepatch: '$MOD_LIVEPATCH': completing patching transition +livepatch: '$MOD_LIVEPATCH': patching complete +livepatch: sysctl: setting key \"kernel.ftrace_enabled\": Device or resource busy kernel.ftrace_enabled = 0 +% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled +livepatch: '$MOD_LIVEPATCH': initializing unpatching transition +livepatch: '$MOD_LIVEPATCH': starting unpatching transition +livepatch: '$MOD_LIVEPATCH': completing unpatching transition +livepatch: '$MOD_LIVEPATCH': unpatching complete +% rmmod $MOD_LIVEPATCH" + + +exit 0 diff --git a/tools/testing/selftests/livepatch/test-livepatch.sh b/tools/testing/selftests/livepatch/test-livepatch.sh index f05268aea859..493e3df415a1 100755 --- a/tools/testing/selftests/livepatch/test-livepatch.sh +++ b/tools/testing/selftests/livepatch/test-livepatch.sh @@ -7,7 +7,7 @@ MOD_LIVEPATCH=test_klp_livepatch MOD_REPLACE=test_klp_atomic_replace -set_dynamic_debug +setup_config # TEST: basic function patching diff --git a/tools/testing/selftests/livepatch/test-shadow-vars.sh b/tools/testing/selftests/livepatch/test-shadow-vars.sh index 04a37831e204..1aae73299114 100755 --- a/tools/testing/selftests/livepatch/test-shadow-vars.sh +++ b/tools/testing/selftests/livepatch/test-shadow-vars.sh @@ -6,7 +6,7 @@ MOD_TEST=test_klp_shadow_vars -set_dynamic_debug +setup_config # TEST: basic shadow variable API diff --git a/tools/testing/selftests/powerpc/include/utils.h b/tools/testing/selftests/powerpc/include/utils.h index 0e2b2e6284ac..e089a0c30d9a 100644 --- a/tools/testing/selftests/powerpc/include/utils.h +++ b/tools/testing/selftests/powerpc/include/utils.h @@ -34,6 +34,7 @@ int pick_online_cpu(void); int read_debugfs_file(char *debugfs_file, int *result); int write_debugfs_file(char *debugfs_file, int result); +int read_sysfs_file(char *debugfs_file, char *result, size_t result_size); void set_dscr(unsigned long val); int perf_event_open_counter(unsigned int type, unsigned long config, int group_fd); diff --git a/tools/testing/selftests/powerpc/pmu/ebb/Makefile b/tools/testing/selftests/powerpc/pmu/ebb/Makefile index 23f4caf48ffc..417306353e07 100644 --- a/tools/testing/selftests/powerpc/pmu/ebb/Makefile +++ b/tools/testing/selftests/powerpc/pmu/ebb/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 +include ../../../../../../scripts/Kbuild.include + noarg: $(MAKE) -C ../../ @@ -6,7 +8,10 @@ noarg: CFLAGS += -m64 # Toolchains may build PIE by default which breaks the assembly -LDFLAGS += -no-pie +no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \ + $(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -no-pie -x c - -o "$$TMP", -no-pie) + +LDFLAGS += $(no-pie-option) TEST_GEN_PROGS := reg_access_test event_attributes_test cycles_test \ cycles_with_freeze_test pmc56_overflow_test \ diff --git a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c index 200337daec42..c1f324afdbf3 100644 --- a/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c +++ b/tools/testing/selftests/powerpc/ptrace/perf-hwbreak.c @@ -148,6 +148,121 @@ static int runtestsingle(int readwriteflag, int exclude_user, int arraytest) return 0; } +static int runtest_dar_outside(void) +{ + void *target; + volatile __u16 temp16; + volatile __u64 temp64; + struct perf_event_attr attr; + int break_fd; + unsigned long long breaks; + int fail = 0; + size_t res; + + target = malloc(8); + if (!target) { + perror("malloc failed"); + exit(EXIT_FAILURE); + } + + /* setup counters */ + memset(&attr, 0, sizeof(attr)); + attr.disabled = 1; + attr.type = PERF_TYPE_BREAKPOINT; + attr.exclude_kernel = 1; + attr.exclude_hv = 1; + attr.exclude_guest = 1; + attr.bp_type = HW_BREAKPOINT_RW; + /* watch middle half of target array */ + attr.bp_addr = (__u64)(target + 2); + attr.bp_len = 4; + break_fd = sys_perf_event_open(&attr, 0, -1, -1, 0); + if (break_fd < 0) { + free(target); + perror("sys_perf_event_open"); + exit(EXIT_FAILURE); + } + + /* Shouldn't hit. */ + ioctl(break_fd, PERF_EVENT_IOC_RESET); + ioctl(break_fd, PERF_EVENT_IOC_ENABLE); + temp16 = *((__u16 *)target); + *((__u16 *)target) = temp16; + ioctl(break_fd, PERF_EVENT_IOC_DISABLE); + res = read(break_fd, &breaks, sizeof(unsigned long long)); + assert(res == sizeof(unsigned long long)); + if (breaks == 0) { + printf("TESTED: No overlap\n"); + } else { + printf("FAILED: No overlap: %lld != 0\n", breaks); + fail = 1; + } + + /* Hit */ + ioctl(break_fd, PERF_EVENT_IOC_RESET); + ioctl(break_fd, PERF_EVENT_IOC_ENABLE); + temp16 = *((__u16 *)(target + 1)); + *((__u16 *)(target + 1)) = temp16; + ioctl(break_fd, PERF_EVENT_IOC_DISABLE); + res = read(break_fd, &breaks, sizeof(unsigned long long)); + assert(res == sizeof(unsigned long long)); + if (breaks == 2) { + printf("TESTED: Partial overlap\n"); + } else { + printf("FAILED: Partial overlap: %lld != 2\n", breaks); + fail = 1; + } + + /* Hit */ + ioctl(break_fd, PERF_EVENT_IOC_RESET); + ioctl(break_fd, PERF_EVENT_IOC_ENABLE); + temp16 = *((__u16 *)(target + 5)); + *((__u16 *)(target + 5)) = temp16; + ioctl(break_fd, PERF_EVENT_IOC_DISABLE); + res = read(break_fd, &breaks, sizeof(unsigned long long)); + assert(res == sizeof(unsigned long long)); + if (breaks == 2) { + printf("TESTED: Partial overlap\n"); + } else { + printf("FAILED: Partial overlap: %lld != 2\n", breaks); + fail = 1; + } + + /* Shouldn't Hit */ + ioctl(break_fd, PERF_EVENT_IOC_RESET); + ioctl(break_fd, PERF_EVENT_IOC_ENABLE); + temp16 = *((__u16 *)(target + 6)); + *((__u16 *)(target + 6)) = temp16; + ioctl(break_fd, PERF_EVENT_IOC_DISABLE); + res = read(break_fd, &breaks, sizeof(unsigned long long)); + assert(res == sizeof(unsigned long long)); + if (breaks == 0) { + printf("TESTED: No overlap\n"); + } else { + printf("FAILED: No overlap: %lld != 0\n", breaks); + fail = 1; + } + + /* Hit */ + ioctl(break_fd, PERF_EVENT_IOC_RESET); + ioctl(break_fd, PERF_EVENT_IOC_ENABLE); + temp64 = *((__u64 *)target); + *((__u64 *)target) = temp64; + ioctl(break_fd, PERF_EVENT_IOC_DISABLE); + res = read(break_fd, &breaks, sizeof(unsigned long long)); + assert(res == sizeof(unsigned long long)); + if (breaks == 2) { + printf("TESTED: Full overlap\n"); + } else { + printf("FAILED: Full overlap: %lld != 2\n", breaks); + fail = 1; + } + + free(target); + close(break_fd); + return fail; +} + static int runtest(void) { int rwflag; @@ -172,7 +287,9 @@ static int runtest(void) return ret; } } - return 0; + + ret = runtest_dar_outside(); + return ret; } diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c index 3066d310f32b..7deedbc16b0b 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-hwbreak.c @@ -22,321 +22,486 @@ #include <sys/wait.h> #include "ptrace.h" -/* Breakpoint access modes */ -enum { - BP_X = 1, - BP_RW = 2, - BP_W = 4, -}; - -static pid_t child_pid; -static struct ppc_debug_info dbginfo; - -static void get_dbginfo(void) -{ - int ret; +#define SPRN_PVR 0x11F +#define PVR_8xx 0x00500000 - ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo); - if (ret) { - perror("Can't get breakpoint info\n"); - exit(-1); - } -} +bool is_8xx; -static bool hwbreak_present(void) -{ - return (dbginfo.num_data_bps != 0); -} +/* + * Use volatile on all global var so that compiler doesn't + * optimise their load/stores. Otherwise selftest can fail. + */ +static volatile __u64 glvar; -static bool dawr_present(void) -{ - return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR); -} +#define DAWR_MAX_LEN 512 +static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512))); -static void set_breakpoint_addr(void *addr) -{ - int ret; +#define A_LEN 6 +#define B_LEN 6 +struct gstruct { + __u8 a[A_LEN]; /* double word aligned */ + __u8 b[B_LEN]; /* double word unaligned */ +}; +static volatile struct gstruct gstruct __attribute__((aligned(512))); - ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr); - if (ret) { - perror("Can't set breakpoint addr\n"); - exit(-1); - } -} -static int set_hwbreakpoint_addr(void *addr, int range) +static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo) { - int ret; - - struct ppc_hw_breakpoint info; - - info.version = 1; - info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW; - info.addr_mode = PPC_BREAKPOINT_MODE_EXACT; - if (range > 0) - info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; - info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE; - info.addr = (__u64)addr; - info.addr2 = (__u64)addr + range; - info.condition_value = 0; - - ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info); - if (ret < 0) { - perror("Can't set breakpoint\n"); + if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) { + perror("Can't get breakpoint info"); exit(-1); } - return ret; } -static int del_hwbreakpoint_addr(int watchpoint_handle) +static bool dawr_present(struct ppc_debug_info *dbginfo) { - int ret; - - ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle); - if (ret < 0) { - perror("Can't delete hw breakpoint\n"); - exit(-1); - } - return ret; + return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR); } -#define DAWR_LENGTH_MAX 512 - -/* Dummy variables to test read/write accesses */ -static unsigned long long - dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)] - __attribute__((aligned(512))); -static unsigned long long *dummy_var = dummy_array; - static void write_var(int len) { - long long *plval; - char *pcval; - short *psval; - int *pival; + __u8 *pcvar; + __u16 *psvar; + __u32 *pivar; + __u64 *plvar; switch (len) { case 1: - pcval = (char *)dummy_var; - *pcval = 0xff; + pcvar = (__u8 *)&glvar; + *pcvar = 0xff; break; case 2: - psval = (short *)dummy_var; - *psval = 0xffff; + psvar = (__u16 *)&glvar; + *psvar = 0xffff; break; case 4: - pival = (int *)dummy_var; - *pival = 0xffffffff; + pivar = (__u32 *)&glvar; + *pivar = 0xffffffff; break; case 8: - plval = (long long *)dummy_var; - *plval = 0xffffffffffffffffLL; + plvar = (__u64 *)&glvar; + *plvar = 0xffffffffffffffffLL; break; } } static void read_var(int len) { - char cval __attribute__((unused)); - short sval __attribute__((unused)); - int ival __attribute__((unused)); - long long lval __attribute__((unused)); + __u8 cvar __attribute__((unused)); + __u16 svar __attribute__((unused)); + __u32 ivar __attribute__((unused)); + __u64 lvar __attribute__((unused)); switch (len) { case 1: - cval = *(char *)dummy_var; + cvar = (__u8)glvar; break; case 2: - sval = *(short *)dummy_var; + svar = (__u16)glvar; break; case 4: - ival = *(int *)dummy_var; + ivar = (__u32)glvar; break; case 8: - lval = *(long long *)dummy_var; + lvar = (__u64)glvar; break; } } -/* - * Do the r/w accesses to trigger the breakpoints. And run - * the usual traps. - */ -static void trigger_tests(void) +static void test_workload(void) { - int len, ret; + __u8 cvar __attribute__((unused)); + __u32 ivar __attribute__((unused)); + int len = 0; - ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); - if (ret) { - perror("Can't be traced?\n"); - return; + if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) { + perror("Child can't be traced?"); + exit(-1); } /* Wake up father so that it sets up the first test */ kill(getpid(), SIGUSR1); - /* Test write watchpoints */ - for (len = 1; len <= sizeof(long); len <<= 1) + /* PTRACE_SET_DEBUGREG, WO test */ + for (len = 1; len <= sizeof(glvar); len <<= 1) write_var(len); - /* Test read/write watchpoints (on read accesses) */ - for (len = 1; len <= sizeof(long); len <<= 1) + /* PTRACE_SET_DEBUGREG, RO test */ + for (len = 1; len <= sizeof(glvar); len <<= 1) read_var(len); - /* Test when breakpoint is unset */ - - /* Test write watchpoints */ - for (len = 1; len <= sizeof(long); len <<= 1) - write_var(len); + /* PTRACE_SET_DEBUGREG, RW test */ + for (len = 1; len <= sizeof(glvar); len <<= 1) { + if (rand() % 2) + read_var(len); + else + write_var(len); + } - /* Test read/write watchpoints (on read accesses) */ - for (len = 1; len <= sizeof(long); len <<= 1) - read_var(len); + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ + write_var(1); + + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ + read_var(1); + + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ + if (rand() % 2) + write_var(1); + else + read_var(1); + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ + gstruct.a[rand() % A_LEN] = 'a'; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ + cvar = gstruct.a[rand() % A_LEN]; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ + if (rand() % 2) + gstruct.a[rand() % A_LEN] = 'a'; + else + cvar = gstruct.a[rand() % A_LEN]; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ + gstruct.b[rand() % B_LEN] = 'b'; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ + cvar = gstruct.b[rand() % B_LEN]; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ + if (rand() % 2) + gstruct.b[rand() % B_LEN] = 'b'; + else + cvar = gstruct.b[rand() % B_LEN]; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ + if (rand() % 2) + *((int *)(gstruct.a + 4)) = 10; + else + ivar = *((int *)(gstruct.a + 4)); + + /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */ + if (rand() % 2) + big_var[rand() % DAWR_MAX_LEN] = 'a'; + else + cvar = big_var[rand() % DAWR_MAX_LEN]; } -static void check_success(const char *msg) +static void check_success(pid_t child_pid, const char *name, const char *type, + unsigned long saddr, int len) { - const char *msg2; int status; + siginfo_t siginfo; + unsigned long eaddr = (saddr + len - 1) | 0x7; + + saddr &= ~0x7; /* Wait for the child to SIGTRAP */ wait(&status); - msg2 = "Failed"; + ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo); - if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { - msg2 = "Child process hit the breakpoint"; + if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP || + (unsigned long)siginfo.si_addr < saddr || + (unsigned long)siginfo.si_addr > eaddr) { + printf("%s, %s, len: %d: Fail\n", name, type, len); + exit(-1); } - printf("%s Result: [%s]\n", msg, msg2); + printf("%s, %s, len: %d: Ok\n", name, type, len); + + if (!is_8xx) { + /* + * For ptrace registered watchpoint, signal is generated + * before executing load/store. Singlestep the instruction + * and then continue the test. + */ + ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0); + wait(NULL); + } } -static void launch_watchpoints(char *buf, int mode, int len, - struct ppc_debug_info *dbginfo, bool dawr) +static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr) { - const char *mode_str; - unsigned long data = (unsigned long)(dummy_var); - int wh, range; - - data &= ~0x7UL; - - if (mode == BP_W) { - data |= (1UL << 1); - mode_str = "write"; - } else { - data |= (1UL << 0); - data |= (1UL << 1); - mode_str = "read"; + if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) { + perror("PTRACE_SET_DEBUGREG failed"); + exit(-1); } +} - /* Set DABR_TRANSLATION bit */ - data |= (1UL << 2); - - /* use PTRACE_SET_DEBUGREG breakpoints */ - set_breakpoint_addr((void *)data); - ptrace(PTRACE_CONT, child_pid, NULL, 0); - sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); - check_success(buf); - /* Unregister hw brkpoint */ - set_breakpoint_addr(NULL); +static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info) +{ + int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info); - data = (data & ~7); /* remove dabr control bits */ + if (wh <= 0) { + perror("PPC_PTRACE_SETHWDEBUG failed"); + exit(-1); + } + return wh; +} - /* use PPC_PTRACE_SETHWDEBUG breakpoint */ - if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)) - return; /* not supported */ - wh = set_hwbreakpoint_addr((void *)data, 0); - ptrace(PTRACE_CONT, child_pid, NULL, 0); - sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); - check_success(buf); - /* Unregister hw brkpoint */ - del_hwbreakpoint_addr(wh); - - /* try a wider range */ - range = 8; - if (dawr) - range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1)); - wh = set_hwbreakpoint_addr((void *)data, range); - ptrace(PTRACE_CONT, child_pid, NULL, 0); - sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len); - check_success(buf); - /* Unregister hw brkpoint */ - del_hwbreakpoint_addr(wh); +static void ptrace_delhwdebug(pid_t child_pid, int wh) +{ + if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) { + perror("PPC_PTRACE_DELHWDEBUG failed"); + exit(-1); + } } -/* Set the breakpoints and check the child successfully trigger them */ -static int launch_tests(bool dawr) +#define DABR_READ_SHIFT 0 +#define DABR_WRITE_SHIFT 1 +#define DABR_TRANSLATION_SHIFT 2 + +static int test_set_debugreg(pid_t child_pid) { - char buf[1024]; - int len, i, status; + unsigned long wp_addr = (unsigned long)&glvar; + char *name = "PTRACE_SET_DEBUGREG"; + int len; + + /* PTRACE_SET_DEBUGREG, WO test*/ + wp_addr &= ~0x7UL; + wp_addr |= (1UL << DABR_WRITE_SHIFT); + wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); + for (len = 1; len <= sizeof(glvar); len <<= 1) { + ptrace_set_debugreg(child_pid, wp_addr); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "WO", wp_addr, len); + } - struct ppc_debug_info dbginfo; + /* PTRACE_SET_DEBUGREG, RO test */ + wp_addr &= ~0x7UL; + wp_addr |= (1UL << DABR_READ_SHIFT); + wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); + for (len = 1; len <= sizeof(glvar); len <<= 1) { + ptrace_set_debugreg(child_pid, wp_addr); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RO", wp_addr, len); + } - i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo); - if (i) { - perror("Can't set breakpoint info\n"); - exit(-1); + /* PTRACE_SET_DEBUGREG, RW test */ + wp_addr &= ~0x7UL; + wp_addr |= (1Ul << DABR_READ_SHIFT); + wp_addr |= (1UL << DABR_WRITE_SHIFT); + wp_addr |= (1UL << DABR_TRANSLATION_SHIFT); + for (len = 1; len <= sizeof(glvar); len <<= 1) { + ptrace_set_debugreg(child_pid, wp_addr); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RW", wp_addr, len); } - if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE)) - printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n"); - /* Write watchpoint */ - for (len = 1; len <= sizeof(long); len <<= 1) - launch_watchpoints(buf, BP_W, len, &dbginfo, dawr); + ptrace_set_debugreg(child_pid, 0); + return 0; +} - /* Read-Write watchpoint */ - for (len = 1; len <= sizeof(long); len <<= 1) - launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr); +static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type, + unsigned long addr, int len) +{ + info->version = 1; + info->trigger_type = type; + info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE; + info->addr = (__u64)addr; + info->addr2 = (__u64)addr + len; + info->condition_value = 0; + if (!len) + info->addr_mode = PPC_BREAKPOINT_MODE_EXACT; + else + info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE; +} +static void test_sethwdebug_exact(pid_t child_pid) +{ + struct ppc_hw_breakpoint info; + unsigned long wp_addr = (unsigned long)&glvar; + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT"; + int len = 1; /* hardcoded in kernel */ + int wh; + + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */ + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0); + wh = ptrace_sethwdebug(child_pid, &info); ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "WO", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); - /* - * Now we have unregistered the breakpoint, access by child - * should not cause SIGTRAP. - */ + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */ + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RO", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); - wait(&status); + /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */ + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RW", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); +} - if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) { - printf("FAIL: Child process hit the breakpoint, which is not expected\n"); - ptrace(PTRACE_CONT, child_pid, NULL, 0); - return TEST_FAIL; - } +static void test_sethwdebug_range_aligned(pid_t child_pid) +{ + struct ppc_hw_breakpoint info; + unsigned long wp_addr; + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED"; + int len; + int wh; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */ + wp_addr = (unsigned long)&gstruct.a; + len = A_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "WO", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */ + wp_addr = (unsigned long)&gstruct.a; + len = A_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RO", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */ + wp_addr = (unsigned long)&gstruct.a; + len = A_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RW", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); +} - if (WIFEXITED(status)) - printf("Child exited normally\n"); +static void test_sethwdebug_range_unaligned(pid_t child_pid) +{ + struct ppc_hw_breakpoint info; + unsigned long wp_addr; + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED"; + int len; + int wh; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */ + wp_addr = (unsigned long)&gstruct.b; + len = B_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "WO", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */ + wp_addr = (unsigned long)&gstruct.b; + len = B_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RO", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */ + wp_addr = (unsigned long)&gstruct.b; + len = B_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RW", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); - return TEST_PASS; +} + +static void test_sethwdebug_range_unaligned_dar(pid_t child_pid) +{ + struct ppc_hw_breakpoint info; + unsigned long wp_addr; + char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE"; + int len; + int wh; + + /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */ + wp_addr = (unsigned long)&gstruct.b; + len = B_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RW", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); +} + +static void test_sethwdebug_dawr_max_range(pid_t child_pid) +{ + struct ppc_hw_breakpoint info; + unsigned long wp_addr; + char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN"; + int len; + int wh; + + /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */ + wp_addr = (unsigned long)big_var; + len = DAWR_MAX_LEN; + get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len); + wh = ptrace_sethwdebug(child_pid, &info); + ptrace(PTRACE_CONT, child_pid, NULL, 0); + check_success(child_pid, name, "RW", wp_addr, len); + ptrace_delhwdebug(child_pid, wh); +} + +/* Set the breakpoints and check the child successfully trigger them */ +static void +run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr) +{ + test_set_debugreg(child_pid); + if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) { + test_sethwdebug_exact(child_pid); + + if (!is_8xx) + test_sethwdebug_range_aligned(child_pid); + if (dawr && !is_8xx) { + test_sethwdebug_range_unaligned(child_pid); + test_sethwdebug_range_unaligned_dar(child_pid); + test_sethwdebug_dawr_max_range(child_pid); + } + } } static int ptrace_hwbreak(void) { - pid_t pid; - int ret; + pid_t child_pid; + struct ppc_debug_info dbginfo; bool dawr; - pid = fork(); - if (!pid) { - trigger_tests(); + child_pid = fork(); + if (!child_pid) { + test_workload(); return 0; } wait(NULL); - child_pid = pid; + get_dbginfo(child_pid, &dbginfo); + SKIP_IF(dbginfo.num_data_bps == 0); - get_dbginfo(); - SKIP_IF(!hwbreak_present()); - dawr = dawr_present(); - - ret = launch_tests(dawr); + dawr = dawr_present(&dbginfo); + run_tests(child_pid, &dbginfo, dawr); + /* Let the child exit first. */ + ptrace(PTRACE_CONT, child_pid, NULL, 0); wait(NULL); - return ret; + /* + * Testcases exits immediately with -1 on any failure. If + * it has reached here, it means all tests were successful. + */ + return TEST_PASS; } int main(int argc, char **argv, char **envp) { + int pvr = 0; + asm __volatile__ ("mfspr %0,%1" : "=r"(pvr) : "i"(SPRN_PVR)); + if (pvr == PVR_8xx) + is_8xx = true; + return test_harness(ptrace_hwbreak, "ptrace-hwbreak"); } diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c index 25e23e73c72e..2ecfa1158e2b 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-tar.c @@ -73,7 +73,7 @@ trans: [sprn_texasr]"i"(SPRN_TEXASR), [tar_1]"i"(TAR_1), [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2), [dscr_2]"i"(DSCR_2), [tar_3]"i"(TAR_3), [dscr_3]"i"(DSCR_3) - : "memory", "r0", "r1", "r3", "r4", "r5", "r6" + : "memory", "r0", "r3", "r4", "r5", "r6", "lr" ); /* TM failed, analyse */ diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c index f603fe5a445b..6f7fb51f0809 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-spd-vsx.c @@ -74,8 +74,8 @@ trans: "3: ;" : [res] "=r" (result), [texasr] "=r" (texasr) : [sprn_texasr] "i" (SPRN_TEXASR) - : "memory", "r0", "r1", "r3", "r4", - "r7", "r8", "r9", "r10", "r11" + : "memory", "r0", "r3", "r4", + "r7", "r8", "r9", "r10", "r11", "lr" ); if (result) { diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c index e0d37f07bdeb..46ef378a15ec 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-tar.c @@ -62,7 +62,7 @@ trans: [sprn_ppr]"i"(SPRN_PPR), [sprn_texasr]"i"(SPRN_TEXASR), [tar_1]"i"(TAR_1), [dscr_1]"i"(DSCR_1), [tar_2]"i"(TAR_2), [dscr_2]"i"(DSCR_2), [cptr1] "b" (&cptr[1]) - : "memory", "r0", "r1", "r3", "r4", "r5", "r6" + : "memory", "r0", "r3", "r4", "r5", "r6" ); /* TM failed, analyse */ diff --git a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c index 8027457b97b7..70ca01234f79 100644 --- a/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c +++ b/tools/testing/selftests/powerpc/ptrace/ptrace-tm-vsx.c @@ -62,8 +62,8 @@ trans: "3: ;" : [res] "=r" (result), [texasr] "=r" (texasr) : [sprn_texasr] "i" (SPRN_TEXASR), [cptr1] "b" (&cptr[1]) - : "memory", "r0", "r1", "r3", "r4", - "r7", "r8", "r9", "r10", "r11" + : "memory", "r0", "r3", "r4", + "r7", "r8", "r9", "r10", "r11", "lr" ); if (result) { diff --git a/tools/testing/selftests/powerpc/security/Makefile b/tools/testing/selftests/powerpc/security/Makefile index 85861c46b445..eadbbff50be6 100644 --- a/tools/testing/selftests/powerpc/security/Makefile +++ b/tools/testing/selftests/powerpc/security/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0+ -TEST_GEN_PROGS := rfi_flush +TEST_GEN_PROGS := rfi_flush spectre_v2 top_srcdir = ../../../../.. CFLAGS += -I../../../../../usr/include @@ -8,3 +8,6 @@ CFLAGS += -I../../../../../usr/include include ../../lib.mk $(TEST_GEN_PROGS): ../harness.c ../utils.c + +$(OUTPUT)/spectre_v2: CFLAGS += -m64 +$(OUTPUT)/spectre_v2: ../pmu/event.c branch_loops.S diff --git a/tools/testing/selftests/powerpc/security/branch_loops.S b/tools/testing/selftests/powerpc/security/branch_loops.S new file mode 100644 index 000000000000..22e9204e3421 --- /dev/null +++ b/tools/testing/selftests/powerpc/security/branch_loops.S @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2019, Michael Ellerman, IBM Corp. + */ + +#include <ppc-asm.h> + + .data + +jump_table: + .long 0x0 + .long (.Lstate_1 - .Lstate_0) + .long (.Lstate_2 - .Lstate_0) + .long (.Lstate_3 - .Lstate_0) + .long (.Lstate_4 - .Lstate_0) + .long (.Lstate_5 - .Lstate_0) + .long (.Lstate_6 - .Lstate_0) + .long (.Lstate_7 - .Lstate_0) + + .text + +#define ITER_SHIFT 31 + +.macro state number + .balign 32 +.Lstate_\number: + .if \number==7 + li r3, 0 + .else + li r3, \number+1 + .endif + b .Lloop +.endm + +FUNC_START(pattern_cache_loop) + li r3, 0 + li r4, 1 + sldi r4, r4, ITER_SHIFT + +.Lloop: cmpdi r4, 0 + beqlr + + addi r4, r4, -1 + + ld r6, jump_table@got(%r2) + sldi r5, r3, 2 + lwax r6, r5, r6 + ld r7, .Lstate_0@got(%r2) + add r6, r6, r7 + mtctr r6 + bctr + + state 0 + state 1 + state 2 + state 3 + state 4 + state 5 + state 6 + state 7 + +FUNC_END(pattern_cache_loop) + + +FUNC_START(indirect_branch_loop) + li r3, 1 + sldi r3, r3, ITER_SHIFT + +1: cmpdi r3, 0 + beqlr + + addi r3, r3, -1 + + ld r4, 2f@got(%r2) + mtctr r4 + bctr + + .balign 32 +2: b 1b + +FUNC_END(indirect_branch_loop) diff --git a/tools/testing/selftests/powerpc/security/spectre_v2.c b/tools/testing/selftests/powerpc/security/spectre_v2.c new file mode 100644 index 000000000000..8c6b982af2a8 --- /dev/null +++ b/tools/testing/selftests/powerpc/security/spectre_v2.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2018-2019 IBM Corporation. + */ + +#define __SANE_USERSPACE_TYPES__ + +#include <sys/types.h> +#include <stdint.h> +#include <malloc.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/prctl.h> +#include "utils.h" + +#include "../pmu/event.h" + + +extern void pattern_cache_loop(void); +extern void indirect_branch_loop(void); + +static int do_count_loop(struct event *events, bool is_p9, s64 *miss_percent) +{ + u64 pred, mpred; + + prctl(PR_TASK_PERF_EVENTS_ENABLE); + + if (is_p9) + pattern_cache_loop(); + else + indirect_branch_loop(); + + prctl(PR_TASK_PERF_EVENTS_DISABLE); + + event_read(&events[0]); + event_read(&events[1]); + + // We could scale all the events by running/enabled but we're lazy + // As long as the PMU is uncontended they should all run + FAIL_IF(events[0].result.running != events[0].result.enabled); + FAIL_IF(events[1].result.running != events[1].result.enabled); + + pred = events[0].result.value; + mpred = events[1].result.value; + + if (is_p9) { + event_read(&events[2]); + event_read(&events[3]); + FAIL_IF(events[2].result.running != events[2].result.enabled); + FAIL_IF(events[3].result.running != events[3].result.enabled); + + pred += events[2].result.value; + mpred += events[3].result.value; + } + + *miss_percent = 100 * mpred / pred; + + return 0; +} + +static void setup_event(struct event *e, u64 config, char *name) +{ + event_init_named(e, config, name); + + e->attr.disabled = 1; + e->attr.exclude_kernel = 1; + e->attr.exclude_hv = 1; + e->attr.exclude_idle = 1; +} + +enum spectre_v2_state { + VULNERABLE = 0, + UNKNOWN = 1, // Works with FAIL_IF() + NOT_AFFECTED, + BRANCH_SERIALISATION, + COUNT_CACHE_DISABLED, + COUNT_CACHE_FLUSH_SW, + COUNT_CACHE_FLUSH_HW, + BTB_FLUSH, +}; + +static enum spectre_v2_state get_sysfs_state(void) +{ + enum spectre_v2_state state = UNKNOWN; + char buf[256]; + int len; + + memset(buf, 0, sizeof(buf)); + FAIL_IF(read_sysfs_file("devices/system/cpu/vulnerabilities/spectre_v2", buf, sizeof(buf))); + + // Make sure it's NULL terminated + buf[sizeof(buf) - 1] = '\0'; + + // Trim the trailing newline + len = strlen(buf); + FAIL_IF(len < 1); + buf[len - 1] = '\0'; + + printf("sysfs reports: '%s'\n", buf); + + // Order matters + if (strstr(buf, "Vulnerable")) + state = VULNERABLE; + else if (strstr(buf, "Not affected")) + state = NOT_AFFECTED; + else if (strstr(buf, "Indirect branch serialisation (kernel only)")) + state = BRANCH_SERIALISATION; + else if (strstr(buf, "Indirect branch cache disabled")) + state = COUNT_CACHE_DISABLED; + else if (strstr(buf, "Software count cache flush (hardware accelerated)")) + state = COUNT_CACHE_FLUSH_HW; + else if (strstr(buf, "Software count cache flush")) + state = COUNT_CACHE_FLUSH_SW; + else if (strstr(buf, "Branch predictor state flush")) + state = BTB_FLUSH; + + return state; +} + +#define PM_BR_PRED_CCACHE 0x040a4 // P8 + P9 +#define PM_BR_MPRED_CCACHE 0x040ac // P8 + P9 +#define PM_BR_PRED_PCACHE 0x048a0 // P9 only +#define PM_BR_MPRED_PCACHE 0x048b0 // P9 only + +#define SPRN_PVR 287 + +int spectre_v2_test(void) +{ + enum spectre_v2_state state; + struct event events[4]; + s64 miss_percent; + bool is_p9; + + state = get_sysfs_state(); + if (state == UNKNOWN) { + printf("Error: couldn't determine spectre_v2 mitigation state?\n"); + return -1; + } + + memset(events, 0, sizeof(events)); + + setup_event(&events[0], PM_BR_PRED_CCACHE, "PM_BR_PRED_CCACHE"); + setup_event(&events[1], PM_BR_MPRED_CCACHE, "PM_BR_MPRED_CCACHE"); + FAIL_IF(event_open(&events[0])); + FAIL_IF(event_open_with_group(&events[1], events[0].fd) == -1); + + is_p9 = ((mfspr(SPRN_PVR) >> 16) & 0xFFFF) == 0x4e; + + if (is_p9) { + // Count pattern cache too + setup_event(&events[2], PM_BR_PRED_PCACHE, "PM_BR_PRED_PCACHE"); + setup_event(&events[3], PM_BR_MPRED_PCACHE, "PM_BR_MPRED_PCACHE"); + + FAIL_IF(event_open_with_group(&events[2], events[0].fd) == -1); + FAIL_IF(event_open_with_group(&events[3], events[0].fd) == -1); + } + + FAIL_IF(do_count_loop(events, is_p9, &miss_percent)); + + event_report_justified(&events[0], 18, 10); + event_report_justified(&events[1], 18, 10); + event_close(&events[0]); + event_close(&events[1]); + + if (is_p9) { + event_report_justified(&events[2], 18, 10); + event_report_justified(&events[3], 18, 10); + event_close(&events[2]); + event_close(&events[3]); + } + + printf("Miss percent %lld %%\n", miss_percent); + + switch (state) { + case VULNERABLE: + case NOT_AFFECTED: + case COUNT_CACHE_FLUSH_SW: + case COUNT_CACHE_FLUSH_HW: + // These should all not affect userspace branch prediction + if (miss_percent > 15) { + printf("Branch misses > 15%% unexpected in this configuration!\n"); + printf("Possible mis-match between reported & actual mitigation\n"); + return 1; + } + break; + case BRANCH_SERIALISATION: + // This seems to affect userspace branch prediction a bit? + if (miss_percent > 25) { + printf("Branch misses > 25%% unexpected in this configuration!\n"); + printf("Possible mis-match between reported & actual mitigation\n"); + return 1; + } + break; + case COUNT_CACHE_DISABLED: + if (miss_percent < 95) { + printf("Branch misses < 20%% unexpected in this configuration!\n"); + printf("Possible mis-match between reported & actual mitigation\n"); + return 1; + } + break; + case UNKNOWN: + case BTB_FLUSH: + printf("Not sure!\n"); + return 1; + } + + printf("OK - Measured branch prediction rates match reported spectre v2 mitigation.\n"); + + return 0; +} + +int main(int argc, char *argv[]) +{ + return test_harness(spectre_v2_test, "spectre_v2"); +} diff --git a/tools/testing/selftests/powerpc/signal/sigfuz.c b/tools/testing/selftests/powerpc/signal/sigfuz.c index dade00c698c2..08f9afe3b95c 100644 --- a/tools/testing/selftests/powerpc/signal/sigfuz.c +++ b/tools/testing/selftests/powerpc/signal/sigfuz.c @@ -42,7 +42,7 @@ #include "utils.h" /* Selftest defaults */ -#define COUNT_MAX 4000 /* Number of interactions */ +#define COUNT_MAX 600 /* Number of interactions */ #define THREADS 16 /* Number of threads */ /* Arguments options */ diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-sigreturn-nt.c b/tools/testing/selftests/powerpc/tm/tm-signal-sigreturn-nt.c index 56fbf9f6bbf3..07c388147b75 100644 --- a/tools/testing/selftests/powerpc/tm/tm-signal-sigreturn-nt.c +++ b/tools/testing/selftests/powerpc/tm/tm-signal-sigreturn-nt.c @@ -10,10 +10,12 @@ */ #define _GNU_SOURCE +#include <stdio.h> #include <stdlib.h> #include <signal.h> #include "utils.h" +#include "tm.h" void trap_signal_handler(int signo, siginfo_t *si, void *uc) { @@ -29,6 +31,8 @@ int tm_signal_sigreturn_nt(void) { struct sigaction trap_sa; + SKIP_IF(!have_htm()); + trap_sa.sa_flags = SA_SIGINFO; trap_sa.sa_sigaction = trap_signal_handler; diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c index c02d24835db4..5ee0e98c4896 100644 --- a/tools/testing/selftests/powerpc/utils.c +++ b/tools/testing/selftests/powerpc/utils.c @@ -127,6 +127,26 @@ bool is_ppc64le(void) return strcmp(uts.machine, "ppc64le") == 0; } +int read_sysfs_file(char *fpath, char *result, size_t result_size) +{ + char path[PATH_MAX] = "/sys/"; + int rc = -1, fd; + + strncat(path, fpath, PATH_MAX - strlen(path) - 1); + + if ((fd = open(path, O_RDONLY)) < 0) + return rc; + + rc = read(fd, result, result_size); + + close(fd); + + if (rc < 0) + return rc; + + return 0; +} + int read_debugfs_file(char *debugfs_file, int *result) { int rc = -1, fd; diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 7f8b5c8982e3..6944b898bb53 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -35,6 +35,7 @@ #include <stdbool.h> #include <string.h> #include <time.h> +#include <limits.h> #include <linux/elf.h> #include <sys/uio.h> #include <sys/utsname.h> @@ -43,6 +44,7 @@ #include <sys/times.h> #include <sys/socket.h> #include <sys/ioctl.h> +#include <linux/kcmp.h> #include <unistd.h> #include <sys/syscall.h> @@ -112,6 +114,8 @@ struct seccomp_data { # define __NR_seccomp 383 # elif defined(__aarch64__) # define __NR_seccomp 277 +# elif defined(__riscv) +# define __NR_seccomp 277 # elif defined(__hppa__) # define __NR_seccomp 338 # elif defined(__powerpc__) @@ -204,6 +208,10 @@ struct seccomp_notif_sizes { #define PTRACE_EVENTMSG_SYSCALL_EXIT 2 #endif +#ifndef SECCOMP_USER_NOTIF_FLAG_CONTINUE +#define SECCOMP_USER_NOTIF_FLAG_CONTINUE 0x00000001 +#endif + #ifndef seccomp int seccomp(unsigned int op, unsigned int flags, void *args) { @@ -1587,6 +1595,10 @@ TEST_F(TRACE_poke, getpid_runs_normally) # define ARCH_REGS struct user_pt_regs # define SYSCALL_NUM regs[8] # define SYSCALL_RET regs[0] +#elif defined(__riscv) && __riscv_xlen == 64 +# define ARCH_REGS struct user_regs_struct +# define SYSCALL_NUM a7 +# define SYSCALL_RET a0 #elif defined(__hppa__) # define ARCH_REGS struct user_regs_struct # define SYSCALL_NUM gr[20] @@ -1676,7 +1688,7 @@ void change_syscall(struct __test_metadata *_metadata, EXPECT_EQ(0, ret) {} #if defined(__x86_64__) || defined(__i386__) || defined(__powerpc__) || \ - defined(__s390__) || defined(__hppa__) + defined(__s390__) || defined(__hppa__) || defined(__riscv) { regs.SYSCALL_NUM = syscall; } @@ -3077,7 +3089,7 @@ static int user_trap_syscall(int nr, unsigned int flags) return seccomp(SECCOMP_SET_MODE_FILTER, flags, &prog); } -#define USER_NOTIF_MAGIC 116983961184613L +#define USER_NOTIF_MAGIC INT_MAX TEST(user_notification_basic) { pid_t pid; @@ -3485,6 +3497,108 @@ TEST(seccomp_get_notif_sizes) EXPECT_EQ(sizes.seccomp_notif_resp, sizeof(struct seccomp_notif_resp)); } +static int filecmp(pid_t pid1, pid_t pid2, int fd1, int fd2) +{ +#ifdef __NR_kcmp + return syscall(__NR_kcmp, pid1, pid2, KCMP_FILE, fd1, fd2); +#else + errno = ENOSYS; + return -1; +#endif +} + +TEST(user_notification_continue) +{ + pid_t pid; + long ret; + int status, listener; + struct seccomp_notif req = {}; + struct seccomp_notif_resp resp = {}; + struct pollfd pollfd; + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + + listener = user_trap_syscall(__NR_dup, SECCOMP_FILTER_FLAG_NEW_LISTENER); + ASSERT_GE(listener, 0); + + pid = fork(); + ASSERT_GE(pid, 0); + + if (pid == 0) { + int dup_fd, pipe_fds[2]; + pid_t self; + + ret = pipe(pipe_fds); + if (ret < 0) + exit(1); + + dup_fd = dup(pipe_fds[0]); + if (dup_fd < 0) + exit(1); + + self = getpid(); + + ret = filecmp(self, self, pipe_fds[0], dup_fd); + if (ret) + exit(2); + + exit(0); + } + + pollfd.fd = listener; + pollfd.events = POLLIN | POLLOUT; + + EXPECT_GT(poll(&pollfd, 1, -1), 0); + EXPECT_EQ(pollfd.revents, POLLIN); + + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_RECV, &req), 0); + + pollfd.fd = listener; + pollfd.events = POLLIN | POLLOUT; + + EXPECT_GT(poll(&pollfd, 1, -1), 0); + EXPECT_EQ(pollfd.revents, POLLOUT); + + EXPECT_EQ(req.data.nr, __NR_dup); + + resp.id = req.id; + resp.flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; + + /* + * Verify that setting SECCOMP_USER_NOTIF_FLAG_CONTINUE enforces other + * args be set to 0. + */ + resp.error = 0; + resp.val = USER_NOTIF_MAGIC; + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), -1); + EXPECT_EQ(errno, EINVAL); + + resp.error = USER_NOTIF_MAGIC; + resp.val = 0; + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), -1); + EXPECT_EQ(errno, EINVAL); + + resp.error = 0; + resp.val = 0; + EXPECT_EQ(ioctl(listener, SECCOMP_IOCTL_NOTIF_SEND, &resp), 0) { + if (errno == EINVAL) + XFAIL(goto skip, "Kernel does not support SECCOMP_USER_NOTIF_FLAG_CONTINUE"); + } + +skip: + EXPECT_EQ(waitpid(pid, &status, 0), pid); + EXPECT_EQ(true, WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)) { + if (WEXITSTATUS(status) == 2) { + XFAIL(return, "Kernel does not support kcmp() syscall"); + return; + } + } +} + /* * TODO: * - add microbenchmarks diff --git a/tools/usb/usbip/libsrc/usbip_host_common.c b/tools/usb/usbip/libsrc/usbip_host_common.c index 2813aa821c82..d1d8ba2a4a40 100644 --- a/tools/usb/usbip/libsrc/usbip_host_common.c +++ b/tools/usb/usbip/libsrc/usbip_host_common.c @@ -57,7 +57,7 @@ static int32_t read_attr_usbip_status(struct usbip_usb_device *udev) } value = atoi(status); - + close(fd); return value; } |