* Re: [PATCH v5 01/18] kunit: test: add KUnit test runner core
From: Luis Chamberlain @ 2019-06-26 22:02 UTC (permalink / raw)
To: Brendan Higgins
Cc: Stephen Boyd, Frank Rowand, Greg KH, Josh Poimboeuf, Kees Cook,
Kieran Bingham, Peter Zijlstra, Rob Herring, shuah,
Theodore Ts'o, Masahiro Yamada, devicetree, dri-devel,
kunit-dev, open list:DOCUMENTATION, linux-fsdevel, linux-kbuild,
Linux Kernel Mailing List, open list:KERNEL SELFTEST FRAMEWORK,
linux-nvdimm, linux-um, Sasha Levin, Bird, Timothy,
Amir Goldstein, Dan Carpenter, Daniel Vetter, Jeff Dike,
Joel Stanley, Julia Lawall, Kevin Hilman, Knut Omang,
Logan Gunthorpe, Michael Ellerman, Petr Mladek, Randy Dunlap,
Richard Weinberger, David Rientjes, Steven Rostedt, wfg
In-Reply-To: <CAFd5g45fSdpytudDyD3Yo1ti=kU_JJ6S9yz53_L=pnZTjQFU9A@mail.gmail.com>
On Tue, Jun 25, 2019 at 11:41:47PM -0700, Brendan Higgins wrote:
> On Tue, Jun 25, 2019 at 4:02 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
> >
> > On Tue, Jun 25, 2019 at 03:14:45PM -0700, Brendan Higgins wrote:
> > > On Tue, Jun 25, 2019 at 2:44 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
> > > > Since its a new architecture and since you seem to imply most tests
> > > > don't require locking or even IRQs disabled, I think its worth to
> > > > consider the impact of adding such extreme locking requirements for
> > > > an initial ramp up.
> > >
> > > Fair enough, I can see the point of not wanting to use irq disabled
> > > until we get someone complaining about it, but I think making it
> > > thread safe is reasonable. It means there is one less thing to confuse
> > > a KUnit user and the only penalty paid is some very minor performance.
> >
> > One reason I'm really excited about kunit is speed... so by all means I
> > think we're at a good point to analyze performance optimizationsm if
> > they do make sense.
>
> Yeah, but I think there are much lower hanging fruit than this (as you
> point out below). I am all for making/keeping KUnit super fast, but I
> also don't want to waste time with premature optimizations and I think
> having thread safe expectations and non-thread safe expectations hurts
> usability.
>
> Still, I am on board with making this a mutex instead of a spinlock for now.
>
> > While on the topic of parallization, what about support for running
> > different test cases in parallel? Or at the very least different kunit
> > modules in parallel. Few questions come up based on this prospect:
> >
> > * Why not support parallelism from the start?
>
> Just because it is more work and there isn't much to gain from it right now.
>
> Some numbers:
> I currently have a collection of 86 test cases in the branch that this
> patchset is from.
Impressive, imagine 86 tests and kunit is not even *merged yet*.
> I turned on PRINTK_TIME and looked at the first
> KUnit output and the last. On UML, start time was 0.090000, and end
> time was 0.090000. Looks like sched_clock is not very good on UML.
Since you have a python thing that kicks tests, what if you just run
time on it?
> Still it seems quite likely that all of these tests run around 0.01
> seconds or less on UML: I ran KUnit with only 2 test cases enabled
> three times and got an average runtime of 1.55867 seconds with a
> standard deviation of 0.0346747. I then ran it another three times
> with all test cases enabled and got an average runtime of 1.535
> seconds with a standard deviation of 0.0150997. The second average is
> less, but that doesn't really mean anything because it is well within
> one standard deviation with a very small sample size. Nevertheless, we
> can conclude that the actual runtime of those 84 test cases is most
> likely within one standard deviation, so on the order of 0.01 seconds.
>
> On x86 running on QEMU, first message from KUnit was printed at
> 0.194251 and the last KUnit message was printed at 0.340915, meaning
> that all 86 test cases ran in about 0.146664 seconds.
Pretty impressive numbers. But can you blame me for expressing the
desire to possibly being able to do better? I am not saying -- let's
definitely have parallelism in place *now*. Just wanted to hear out
tangibles of why we *don't* want it now.
And.. also, since we are reviewing now, try to target so that the code
can later likely get a face lift to support parallelism without
requiring much changes.
> In any case, running KUnit tests in parallel is definitely something I
> plan on adding it eventually, but it just doesn't really seem worth it
> right now.
Makes sense!
> I find the incremental build time of the kernel to
> typically be between 3 and 30 seconds, and a clean build to be between
> 30 seconds to several minutes, depending on the number of available
> cores, so I don't think most users would even notice the amount of
> runtime contributed by the actual unit tests until we start getting
> into the 1000s of test cases. I don't suspect it will become an issue
> until we get into the 10,000s of test cases. I think we are a pretty
> long way off from that.
All makes sense, and agreed based on the numbers you are providing.
Thanks for the details!
> > * Are you opposed to eventually having this added? For instance, there is
> > enough code on lib/test_kmod.c for batching tons of kthreads each
> > one running its own thing for testing purposes which could be used
> > as template.
>
> I am not opposed to adding it eventually at all. I actually plan on
> doing so, just not in this patchset. There are a lot of additional
> features, improvements, and sugar that I really want to add, so much
> so that most of it doesn't belong in this patchset; I just think this
> is one of those things that belongs in a follow up. I tried to boil
> down this patchset to as small as I could while still being useful;
> this is basically an MVP. Maybe after this patchset gets merged I
> should post a list of things I have ready for review, or would like to
> work on, and people can comment on what things they want to see next.
Groovy.
> > * If we eventually *did* support it:
> > - Would logs be skewed?
>
> Probably, before I went with the TAP approach, I was tagging each
> message with the test case it came from and I could have parsed it and
> assembled a coherent view of the logs using that; now that I am using
> TAP conforming output, that won't work. I haven't really thought too
> hard about how to address it, but there are ways. For the UML users, I
> am planning on adding a feature to guarantee hermeticity between tests
> running in different modules by adding a feature that allows a single
> kernel to be built with all tests included, and then determine which
> tests get run by passing in command line arguments or something. This
> way you can get the isolation from running tests in separate
> environments without increasing the build cost. We could also use this
> method to achieve parallelism by dispatching multiple kernels at once.
Indeed. Or... wait for it... containers... There tools for these sorts
of things already. And I'm evaluating such prospects now for some other
tests I care for.
> That only works for UML, but I imagine you could do something similar
> for users running tests under qemu.
Or containers.
> > - Could we have a way to query: give me log for only kunit module
> > named "foo"?
>
> Yeah, I think that would make sense as part of the hermeticity thing I
> mentioned above.
>
> Hope that seems reasonable!
All groovy. Thanks for the details!
Luis
^ permalink raw reply
* Re: [PATCH v5 13/18] kunit: tool: add Python wrappers for running KUnit tests
From: Luis Chamberlain @ 2019-06-26 22:03 UTC (permalink / raw)
To: Brendan Higgins
Cc: Frank Rowand, Greg KH, Josh Poimboeuf, Kees Cook, Kieran Bingham,
Peter Zijlstra, Rob Herring, Stephen Boyd, shuah,
Theodore Ts'o, Masahiro Yamada, devicetree, dri-devel,
kunit-dev, open list:DOCUMENTATION, linux-fsdevel, linux-kbuild,
Linux Kernel Mailing List, open list:KERNEL SELFTEST FRAMEWORK,
linux-nvdimm, linux-um, Sasha Levin, Bird, Timothy,
Amir Goldstein, Dan Carpenter, Daniel Vetter, Jeff Dike,
Joel Stanley, Julia Lawall, Kevin Hilman, Knut Omang,
Logan Gunthorpe, Michael Ellerman, Petr Mladek, Randy Dunlap,
Richard Weinberger, David Rientjes, Steven Rostedt, wfg,
Felix Guo
In-Reply-To: <CAFd5g44kkepB2hZcpYL-NB5ZHYE5tP7W-0yducGCX7Khd9gd9w@mail.gmail.com>
On Wed, Jun 26, 2019 at 01:02:55AM -0700, Brendan Higgins wrote:
> On Tue, Jun 25, 2019 at 5:01 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
> >
> > On Mon, Jun 17, 2019 at 01:26:08AM -0700, Brendan Higgins wrote:
> > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-all_passed.log
> > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-crash.log
> > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-failure.log
> > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-no_tests_run.log
> > > create mode 100644 tools/testing/kunit/test_data/test_output_isolated_correctly.log
> > > create mode 100644 tools/testing/kunit/test_data/test_read_from_file.kconfig
> >
> > Why are these being added upstream? The commit log does not explain
> > this.
>
> Oh sorry, those are for testing purposes. I thought that was clear
> from being in the test_data directory. I will reference it in the
> commit log in the next revision.
Still, I don't get it. They seem to be results from a prior run. Why do
we need them for testing purposes?
Luis
^ permalink raw reply
* Re: [PATCH v5 01/18] kunit: test: add KUnit test runner core
From: Brendan Higgins @ 2019-06-26 22:16 UTC (permalink / raw)
To: Luis Chamberlain
Cc: Frank Rowand, Greg KH, Josh Poimboeuf, Kees Cook, Kieran Bingham,
Peter Zijlstra, Rob Herring, Stephen Boyd, shuah,
Theodore Ts'o, Masahiro Yamada, devicetree, dri-devel,
kunit-dev, open list:DOCUMENTATION, linux-fsdevel, linux-kbuild,
Linux Kernel Mailing List, open list:KERNEL SELFTEST FRAMEWORK,
linux-nvdimm, linux-um, Sasha Levin, Bird, Timothy,
Amir Goldstein, Dan Carpenter, Daniel Vetter, Jeff Dike,
Joel Stanley, Julia Lawall, Kevin Hilman, Knut Omang,
Logan Gunthorpe, Michael Ellerman, Petr Mladek, Randy Dunlap,
Richard Weinberger, David Rientjes, Steven Rostedt, wfg
In-Reply-To: <20190626033643.GX19023@42.do-not-panic.com>
On Tue, Jun 25, 2019 at 8:36 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
>
> On Tue, Jun 25, 2019 at 05:07:32PM -0700, Brendan Higgins wrote:
> > On Tue, Jun 25, 2019 at 3:33 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
> > >
> > > On Mon, Jun 17, 2019 at 01:25:56AM -0700, Brendan Higgins wrote:
> > > > +/**
> > > > + * module_test() - used to register a &struct kunit_module with KUnit.
> > > > + * @module: a statically allocated &struct kunit_module.
> > > > + *
> > > > + * Registers @module with the test framework. See &struct kunit_module for more
> > > > + * information.
> > > > + */
> > > > +#define module_test(module) \
> > > > + static int module_kunit_init##module(void) \
> > > > + { \
> > > > + return kunit_run_tests(&module); \
> > > > + } \
> > > > + late_initcall(module_kunit_init##module)
> > >
> > > Becuase late_initcall() is used, if these modules are built-in, this
> > > would preclude the ability to test things prior to this part of the
> > > kernel under UML or whatever architecture runs the tests. So, this
> > > limits the scope of testing. Small detail but the scope whould be
> > > documented.
> >
> > You aren't the first person to complain about this (and I am not sure
> > it is the first time you have complained about it). Anyway, I have
> > some follow on patches that will improve the late_initcall thing, and
> > people seemed okay with discussing the follow on patches as part of a
> > subsequent patchset after this gets merged.
> >
> > I will nevertheless document the restriction until then.
>
> To be clear, I am not complaining about it. I just find it simply
> critical to document its limitations, so folks don't try to invest
> time and energy on kunit right away for an early init test, if it
> cannot support it.
>
> If support for that requires some work, it may be worth mentioning
> as well.
Makes sense. And in anycase, it is something I do want to do, just not
right now. I will put a TODO here in the next revision.
> > > > +static void kunit_print_tap_version(void)
> > > > +{
> > > > + if (!kunit_has_printed_tap_version) {
> > > > + kunit_printk_emit(LOGLEVEL_INFO, "TAP version 14\n");
> > >
> > > What is this TAP thing? Why should we care what version it is on?
> > > Why are we printing this?
> >
> > It's part of the TAP specification[1]. Greg and Frank asked me to make
> > the intermediate format conform to TAP. Seems like something else I
> > should probable document...
>
> Yes thanks!
>
> > > > + kunit_has_printed_tap_version = true;
> > > > + }
> > > > +}
> > > > +
> > > > +static size_t kunit_test_cases_len(struct kunit_case *test_cases)
> > > > +{
> > > > + struct kunit_case *test_case;
> > > > + size_t len = 0;
> > > > +
> > > > + for (test_case = test_cases; test_case->run_case; test_case++)
> > >
> > > If we make the last test case NULL, we'd just check for test_case here,
> > > and save ourselves an extra few bytes per test module. Any reason why
> > > the last test case cannot be NULL?
> >
> > Is there anyway to make that work with a statically defined array?
>
> No you're right.
>
> > Basically, I want to be able to do something like:
> >
> > static struct kunit_case example_test_cases[] = {
> > KUNIT_CASE(example_simple_test),
> > KUNIT_CASE(example_mock_test),
> > {}
> > };
> >
> > FYI,
> > #define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name }
>
> >
> > In order to do what you are proposing, I think I need an array of
> > pointers to test cases, which is not ideal.
>
> Yeah, you're right. The only other alternative is to have a:
>
> struct kunit_module {
> const char name[256];
> int (*init)(struct kunit *test);
> void (*exit)(struct kunit *test);
> struct kunit_case *test_cases;
> + unsigned int num_cases;
> };
>
> And then something like:
>
> #define KUNIT_MODULE(name, init, exit, cases) { \
> .name = name, \
> .init = init, \
> .exit = exit, \
> .test_cases = cases,
> num_cases = ARRAY_SIZE(cases), \
> }
>
> Let's evaluate which is better: one extra test case per all test cases, or
> an extra unsigned int for each kunit module.
I am in favor of the current method since init and exit are optional
arguments. I could see myself (actually I am planning on) adding more
optional things to the kunit_module, so having optional arguments will
make my life a lot easier since I won't have to go through big
refactorings around the kernel to support new features that tie in
here.
^ permalink raw reply
* [PATCH 00/10] Intel(R) Speed Select Technology
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
Intel® Speed Select Technology (Intel® SST) — A powerful new collection of
features giving more granular control over CPU performance for optimized total
cost of ownership and performance. With Intel® SST, one server can be configured
for power and performance for variety of diverse workload requirements. In the
Linux submission code. we are using ISST to specify Intel® SST to avoid confusion
with existing use of SST for "Smart Sound Technology".
Refer to these links for overview of the technology released with some Intel® Xeon®
Scalable processor (5218N, 6230N, and 6252N):
https://www.intel.com/content/www/us/en/architecture-and-technology/speed-select-technology-article.html
https://builders.intel.com/docs/networkbuilders/intel-speed-select-technology-base-frequency-enhancing-performance.pdf
The next generation of Intel® Xeon® processors are adding more features to the
Intel® Speed Select Technology and allow dynamic configuration of these features
from OS-software level instead from BIOS. This submission is adding new features
and dynamic configuration capabilities .
Intel SST Features:
Intel® SST—Performance Profile (PP or perf-profile):
This feature allows one server to be configured for different workload requirements
instead of deploying different servers based on the workload requirement reducing total
cost of ownership. With a single server deployed, the same server can be reconfigured
dynamically to one of the supported profiles to suit the specific workload requirements.
This feature introduces a mechanism that allows multiple optimized performance profiles
per system via static and/or dynamic adjustment of TDP level and other performance
parameters.
Intel® SST—Core power (CP or core-power):
An Interface that allows user to define per core priority. This defines a mechanism
to distribute power among cores when there is a power constrained scenario. This defines
a class of service configuration. Each CPU core can be tied to a class of service and hence
an associated priority.
Intel® SST—Base Frequency (BF or base-freq):
The Intel® SST-BF feature lets user control and direct base frequency. If some critical
workload threads demand constant high guaranteed performance, then this feature can be
used to execute the thread at higher base frequency on specific set of CPUs.
Intel® SST—Turbo frequency (TF or turbo-freq):
Enables the ability to set different all core turbo ratio limits to cores based on the priority.
Using this features some cores can be configured to get higher turbo frequency by designating
them as high priority at the cost of lower or no turbo frequency on the low priority cores.
Implementation
The Intel® SST features are implemented in the firmware executing in the the power
management unit (we are calling PUNIT here for short). The mechanism to control these
features are specific to firmware implementation for Intel® Xeon® CPUs and are not architectural
features. The interface mechanism and semantics of the messages can change in future Xeon
CPUs. Hence there is minimal kernel implementation by offering direct communication
to PUNIT via set of IOCTLs. The actual messages which can be sent to PUNIT are specified
in the following document link:
https://github.com/intel/CommsPowerManagement/blob/master/intel_sst_os_interface/mailbox.md
The idea here is that user space software like cloud orchestration software based on their workload
requirement configure the system. There is a full featured "Intel Speed Select" utility
submitted to kernel power tools, which can be used to validate and exercise the features.
Types of PUNIT interfaces
There are two types of interfaces. One using Mail box communications, which is facilitated
by a PCI device or in some Xeon® CPUs using MSRs; and other using an MMIO interface, which is
used primarily for core prioritization. For hiding details a single character device is created
to handle IOCTLs. The following block diagram shows the implementation overview.
User User Space tool(intel-speed-select)/Cloud Orchestration software
IOCTLs
---------------------------------------character device------------------------------
kernel Common driver handling IOCTLs
Mail Box drivers(PCI & MSR) PCI MMIO driver
--------------------------------------------------------------------------
Hardware PUNIT
Srinivas Pandruvada (10):
platform/x86: ISST: Update ioctl-number.txt for Intel Speed Select
interface
platform/x86: ISST: Add common API to register and handle ioctls
platform/x86: ISST: Store per CPU information
platform/x86: ISST: Add IOCTL to Translate Linux logical CPU to PUNIT
CPU number
platform/x86: ISST: Add Intel Speed Select mmio interface
platform/x86: ISST: Add Intel Speed Select mailbox interface via PCI
platform/x86: ISST: Add Intel Speed Select mailbox interface via MSRs
platform/x86: ISST: Add Intel Speed Select PUNIT MSR interface
platform/x86: ISST: Restore state on resume
tools/power/x86: A tool to validate Intel Speed Select commands
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/platform/x86/Kconfig | 2 +
drivers/platform/x86/Makefile | 1 +
.../x86/intel_speed_select_if/Kconfig | 17 +
.../x86/intel_speed_select_if/Makefile | 10 +
.../intel_speed_select_if/isst_if_common.c | 672 +++++++
.../intel_speed_select_if/isst_if_common.h | 69 +
.../intel_speed_select_if/isst_if_mbox_msr.c | 216 +++
.../intel_speed_select_if/isst_if_mbox_pci.c | 214 +++
.../x86/intel_speed_select_if/isst_if_mmio.c | 180 ++
include/uapi/linux/isst_if.h | 172 ++
tools/power/x86/intel_speed_select/Makefile | 31 +
tools/power/x86/intel_speed_select/isst.h | 231 +++
.../x86/intel_speed_select/isst_config.c | 1607 +++++++++++++++++
.../power/x86/intel_speed_select/isst_core.c | 721 ++++++++
.../x86/intel_speed_select/isst_display.c | 479 +++++
16 files changed, 4623 insertions(+)
create mode 100644 drivers/platform/x86/intel_speed_select_if/Kconfig
create mode 100644 drivers/platform/x86/intel_speed_select_if/Makefile
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_common.c
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_common.h
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
create mode 100644 include/uapi/linux/isst_if.h
create mode 100644 tools/power/x86/intel_speed_select/Makefile
create mode 100644 tools/power/x86/intel_speed_select/isst.h
create mode 100644 tools/power/x86/intel_speed_select/isst_config.c
create mode 100644 tools/power/x86/intel_speed_select/isst_core.c
create mode 100644 tools/power/x86/intel_speed_select/isst_display.c
--
2.17.2
^ permalink raw reply
* [PATCH 04/10] platform/x86: ISST: Add IOCTL to Translate Linux logical CPU to PUNIT CPU number
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
Add processing for IOCTL command ISST_IF_GET_PHY_ID. This converts from the
Linux logical CPU to PUNIT CPU numbering scheme.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../intel_speed_select_if/isst_if_common.c | 74 +++++++++++++++++++
include/uapi/linux/isst_if.h | 28 +++++++
2 files changed, 102 insertions(+)
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
index 0e16cbf685d0..72e74d72724b 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -134,16 +134,90 @@ static void isst_if_cpu_info_exit(void)
kfree(isst_cpu_info);
};
+static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_cpu_map *cpu_map;
+
+ cpu_map = (struct isst_if_cpu_map *)cmd_ptr;
+ if (cpu_map->logical_cpu >= nr_cpu_ids ||
+ cpu_map->logical_cpu >= num_possible_cpus())
+ return -EINVAL;
+
+ *write_only = 0;
+ cpu_map->physical_cpu = isst_cpu_info[cpu_map->logical_cpu].punit_cpu_id;
+
+ return 0;
+}
+
+static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
+{
+ unsigned char __user *ptr;
+ u32 cmd_count;
+ u8 *cmd_ptr;
+ long ret;
+ int i;
+
+ /* Each multi command has u32 command count as the first field */
+ if (copy_from_user(&cmd_count, argp, sizeof(cmd_count)))
+ return -EFAULT;
+
+ if (!cmd_count || cmd_count > ISST_IF_CMD_LIMIT)
+ return -EINVAL;
+
+ cmd_ptr = kmalloc(cb->cmd_size, GFP_KERNEL);
+ if (!cmd_ptr)
+ return -ENOMEM;
+
+ /* cb->offset points to start of the command after the command count */
+ ptr = argp + cb->offset;
+
+ for (i = 0; i < cmd_count; ++i) {
+ int wr_only;
+
+ if (signal_pending(current)) {
+ ret = -EINTR;
+ break;
+ }
+
+ if (copy_from_user(cmd_ptr, ptr, cb->cmd_size)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = cb->cmd_callback(cmd_ptr, &wr_only, 0);
+ if (ret)
+ break;
+
+ if (!wr_only && copy_to_user(ptr, cmd_ptr, cb->cmd_size)) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ptr += cb->cmd_size;
+ }
+
+ kfree(cmd_ptr);
+
+ return i ? i : ret;
+}
+
static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
+ struct isst_if_cmd_cb cmd_cb;
long ret = -ENOTTY;
switch (cmd) {
case ISST_IF_GET_PLATFORM_INFO:
ret = isst_if_get_platform_info(argp);
break;
+ case ISST_IF_GET_PHY_ID:
+ cmd_cb.cmd_size = sizeof(struct isst_if_cpu_map);
+ cmd_cb.offset = offsetof(struct isst_if_cpu_maps, cpu_map);
+ cmd_cb.cmd_callback = isst_if_proc_phyid_req;
+ ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
+ break;
default:
break;
}
diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h
index fa94480b5f74..15d1f286a830 100644
--- a/include/uapi/linux/isst_if.h
+++ b/include/uapi/linux/isst_if.h
@@ -36,6 +36,34 @@ struct isst_if_platform_info {
__u8 mmio_supported;
};
+/**
+ * struct isst_if_cpu_map - CPU mapping between logical and physical CPU
+ * @logical_cpu: Linux logical CPU number
+ * @physical_cpu: PUNIT CPU number
+ *
+ * Used to convert from Linux logical CPU to PUNIT CPU numbering scheme.
+ * The PUNIT CPU number is different than APIC ID based CPU numbering.
+ */
+struct isst_if_cpu_map {
+ __u32 logical_cpu;
+ __u32 physical_cpu;
+};
+
+/**
+ * struct isst_if_cpu_maps - structure for CPU map IOCTL
+ * @cmd_count: Number of CPU mapping command in cpu_map[]
+ * @cpu_map[]: Holds one or more CPU map data structure
+ *
+ * This structure used with ioctl ISST_IF_GET_PHY_ID to send
+ * one or more CPU mapping commands. Here IOCTL return value indicates
+ * number of commands sent or error number if no commands have been sent.
+ */
+struct isst_if_cpu_maps {
+ __u32 cmd_count;
+ struct isst_if_cpu_map cpu_map[1];
+};
+
#define ISST_IF_MAGIC 0xFE
#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
+#define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *)
#endif
--
2.17.2
^ permalink raw reply related
* [PATCH 07/10] platform/x86: ISST: Add Intel Speed Select mailbox interface via MSRs
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
Add an IOCTL to send mailbox commands to PUNIT using PUNIT MSRs for
mailbox. Some CPU models don't have PCI device, so need to use MSRs.
A limited set of mailbox commands can be sent to PUNIT.
This MMIO interface is used by the intel-speed-select tool under
tools/x86/power to enumerate and control Intel Speed Select features.
The MBOX commands ids and semantics of the message can be checked from
the source code of the tool.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../x86/intel_speed_select_if/Makefile | 1 +
.../intel_speed_select_if/isst_if_mbox_msr.c | 180 ++++++++++++++++++
2 files changed, 181 insertions(+)
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
diff --git a/drivers/platform/x86/intel_speed_select_if/Makefile b/drivers/platform/x86/intel_speed_select_if/Makefile
index 8dec8c858649..856076206f35 100644
--- a/drivers/platform/x86/intel_speed_select_if/Makefile
+++ b/drivers/platform/x86/intel_speed_select_if/Makefile
@@ -7,3 +7,4 @@
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_msr.o
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
new file mode 100644
index 000000000000..a949ec436c73
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: Mbox via MSR Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/cpuhotplug.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/topology.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+#include <asm/cpu_device_id.h>
+#include <asm/intel-family.h>
+
+#include "isst_if_common.h"
+
+#define MSR_OS_MAILBOX_INTERFACE 0xB0
+#define MSR_OS_MAILBOX_DATA 0xB1
+#define MSR_OS_MAILBOX_BUSY_BIT 31
+
+/*
+ * Based on experiments count is never more than 1, as the MSR overhead
+ * is enough to finish the command. So here this is the worst case number.
+ */
+#define OS_MAILBOX_RETRY_COUNT 3
+
+static int isst_if_send_mbox_cmd(u8 command, u8 sub_command, u32 parameter,
+ u32 command_data, u32 *response_data)
+{
+ u32 retries;
+ u64 data;
+ int ret;
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
+ if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+ ret = 0;
+ break;
+ } while (--retries);
+
+ if (ret)
+ return ret;
+
+ /* Write DATA register */
+ wrmsrl(MSR_OS_MAILBOX_DATA, command_data);
+
+ /* Write command register */
+ data = BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT) |
+ (parameter & GENMASK_ULL(13, 0)) << 16 |
+ (sub_command << 8) |
+ command;
+ wrmsrl(MSR_OS_MAILBOX_INTERFACE, data);
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ rdmsrl(MSR_OS_MAILBOX_INTERFACE, data);
+ if (data & BIT_ULL(MSR_OS_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+
+ if (data & 0xff)
+ return -ENXIO;
+
+ if (response_data) {
+ rdmsrl(MSR_OS_MAILBOX_DATA, data);
+ *response_data = data;
+ }
+ ret = 0;
+ break;
+ } while (--retries);
+
+ return ret;
+}
+
+struct msrl_action {
+ int err;
+ struct isst_if_mbox_cmd *mbox_cmd;
+};
+
+/* revisit, smp_call_function_single should be enough for atomic mailbox! */
+static void msrl_update_func(void *info)
+{
+ struct msrl_action *act = info;
+
+ act->err = isst_if_send_mbox_cmd(act->mbox_cmd->command,
+ act->mbox_cmd->sub_command,
+ act->mbox_cmd->parameter,
+ act->mbox_cmd->req_data,
+ &act->mbox_cmd->resp_data);
+}
+
+static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct msrl_action action;
+ int ret;
+
+ action.mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
+
+ if (isst_if_mbox_cmd_invalid(action.mbox_cmd))
+ return -EINVAL;
+
+ if (isst_if_mbox_cmd_set_req(action.mbox_cmd) &&
+ !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ /*
+ * To complete mailbox command, we need to access two MSRs.
+ * So we don't want race to complete a mailbox transcation.
+ * Here smp_call ensures that msrl_update_func() has no race
+ * and also with wait flag, wait for completion.
+ * smp_call_function_single is using get_cpu() and put_cpu().
+ */
+ ret = smp_call_function_single(action.mbox_cmd->logical_cpu,
+ msrl_update_func, &action, 1);
+ if (ret)
+ return ret;
+
+ *write_only = 0;
+
+ return action.err;
+}
+
+#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
+
+static const struct x86_cpu_id isst_if_cpu_ids[] = {
+ ICPU(INTEL_FAM6_SKYLAKE_X),
+ {}
+};
+MODULE_DEVICE_TABLE(x86cpu, isst_if_cpu_ids);
+
+static int __init isst_if_mbox_init(void)
+{
+ struct isst_if_cmd_cb cb;
+ const struct x86_cpu_id *id;
+ u64 data;
+ int ret;
+
+ id = x86_match_cpu(isst_if_cpu_ids);
+ if (!id)
+ return -ENODEV;
+
+ /* Check presence of mailbox MSRs */
+ ret = rdmsrl_safe(MSR_OS_MAILBOX_INTERFACE, &data);
+ if (ret)
+ return ret;
+
+ ret = rdmsrl_safe(MSR_OS_MAILBOX_DATA, &data);
+ if (ret)
+ return ret;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
+ cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
+ cb.cmd_callback = isst_if_mbox_proc_cmd;
+ cb.owner = THIS_MODULE;
+ return isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
+}
+module_init(isst_if_mbox_init)
+
+static void __exit isst_if_mbox_exit(void)
+{
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+}
+module_exit(isst_if_mbox_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel speed select interface mailbox driver");
--
2.17.2
^ permalink raw reply related
* [PATCH 10/10] tools/power/x86: A tool to validate Intel Speed Select commands
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
The Intel(R) Speed select technologies contains four features.
Performance profile:An non architectural mechanism that allows multiple
optimized performance profiles per system via static and/or dynamic
adjustment of core count, workload, Tjmax, and TDP, etc. aka ISS
in the documentation.
Base Frequency: Enables users to increase guaranteed base frequency on
certain cores (high priority cores) in exchange for lower base frequency
on remaining cores (low priority cores). aka PBF in the documenation.
Turbo frequency: Enables the ability to set different turbo ratio limits
to cores based on priority. aka FACT in the documentation.
Core power: An Interface that allows user to define per core/tile
priority.
There is a multi level help for commands and options. This can be used
to check required arguments for each feature and commands for the
feature.
To start navigating the features start with
$sudo intel-speed-select --help
For help on a specific feature for example
$sudo intel-speed-select perf-profile --help
To get help for a command for a feature for example
$sudo intel-speed-select perf-profile get-lock-status --help
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
tools/power/x86/intel_speed_select/Makefile | 31 +
tools/power/x86/intel_speed_select/isst.h | 231 +++
.../x86/intel_speed_select/isst_config.c | 1607 +++++++++++++++++
.../power/x86/intel_speed_select/isst_core.c | 721 ++++++++
.../x86/intel_speed_select/isst_display.c | 479 +++++
5 files changed, 3069 insertions(+)
create mode 100644 tools/power/x86/intel_speed_select/Makefile
create mode 100644 tools/power/x86/intel_speed_select/isst.h
create mode 100644 tools/power/x86/intel_speed_select/isst_config.c
create mode 100644 tools/power/x86/intel_speed_select/isst_core.c
create mode 100644 tools/power/x86/intel_speed_select/isst_display.c
diff --git a/tools/power/x86/intel_speed_select/Makefile b/tools/power/x86/intel_speed_select/Makefile
new file mode 100644
index 000000000000..8363450115e2
--- /dev/null
+++ b/tools/power/x86/intel_speed_select/Makefile
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0
+CC = $(CROSS_COMPILE)gcc
+BUILD_OUTPUT := $(CURDIR)
+PREFIX ?= /usr
+DESTDIR ?=
+
+override CFLAGS += -D__EXPORTED_HEADERS__ -Wall -D_GNU_SOURCE
+override CFLAGS += -I$(CURDIR)/../../../../include/uapi/
+override CFLAGS += -I$(CURDIR)/../../../../include/
+
+%: %.c
+ @mkdir -p $(BUILD_OUTPUT)
+ $(CC) $(CFLAGS) $< -o $(BUILD_OUTPUT)/$@ $(LDFLAGS)
+
+DEPS = isst.h
+OBJ = isst_config.o isst_core.o isst_display.o
+
+%.o: %.c $(DEPS)
+ $(CC) -c -o $(BUILD_OUTPUT)/$@ $< $(CFLAGS)
+
+intel-speed-select: $(OBJ)
+ $(CC) -o $(BUILD_OUTPUT)/$@ $^ $(CFLAGS)
+
+.PHONY : clean
+clean :
+ @rm -f $(BUILD_OUTPUT)/intel-speed-select
+ @rm -f $(BUILD_OUTPUT)/*.o
+
+install : intel-speed-select
+ install -d $(DESTDIR)$(PREFIX)/sbin
+ install $(BUILD_OUTPUT)/intel-speed-select $(DESTDIR)$(PREFIX)/sbin/intel-speed-select
diff --git a/tools/power/x86/intel_speed_select/isst.h b/tools/power/x86/intel_speed_select/isst.h
new file mode 100644
index 000000000000..221881761609
--- /dev/null
+++ b/tools/power/x86/intel_speed_select/isst.h
@@ -0,0 +1,231 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Speed Select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#ifndef _ISST_H_
+#define _ISST_H_
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sched.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <getopt.h>
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <cpuid.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include <stdarg.h>
+#include <sys/ioctl.h>
+
+#define BIT(x) (1 << (x))
+#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (sizeof(long) * 8 - 1 - (h))))
+#define GENMASK_ULL(h, l) \
+ (((~0ULL) << (l)) & (~0ULL >> (sizeof(long long) * 8 - 1 - (h))))
+
+#define CONFIG_TDP 0x7f
+#define CONFIG_TDP_GET_LEVELS_INFO 0x00
+#define CONFIG_TDP_GET_TDP_CONTROL 0x01
+#define CONFIG_TDP_SET_TDP_CONTROL 0x02
+#define CONFIG_TDP_GET_TDP_INFO 0x03
+#define CONFIG_TDP_GET_PWR_INFO 0x04
+#define CONFIG_TDP_GET_TJMAX_INFO 0x05
+#define CONFIG_TDP_GET_CORE_MASK 0x06
+#define CONFIG_TDP_GET_TURBO_LIMIT_RATIOS 0x07
+#define CONFIG_TDP_SET_LEVEL 0x08
+#define CONFIG_TDP_GET_UNCORE_P0_P1_INFO 0X09
+#define CONFIG_TDP_GET_P1_INFO 0x0a
+#define CONFIG_TDP_GET_MEM_FREQ 0x0b
+
+#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES 0x10
+#define CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS 0x11
+#define CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO 0x12
+
+#define CONFIG_TDP_PBF_GET_CORE_MASK_INFO 0x20
+#define CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO 0x21
+#define CONFIG_TDP_PBF_GET_TJ_MAX_INFO 0x22
+#define CONFIG_TDP_PBF_GET_TDP_INFO 0X23
+
+#define CONFIG_CLOS 0xd0
+#define CLOS_PQR_ASSOC 0x00
+#define CLOS_PM_CLOS 0x01
+#define CLOS_PM_QOS_CONFIG 0x02
+#define CLOS_STATUS 0x03
+
+#define MBOX_CMD_WRITE_BIT 0x08
+
+#define PM_QOS_INFO_OFFSET 0x00
+#define PM_QOS_CONFIG_OFFSET 0x04
+#define PM_CLOS_OFFSET 0x08
+#define PQR_ASSOC_OFFSET 0x20
+
+struct isst_clos_config {
+ int pkg_id;
+ int die_id;
+ unsigned char epp;
+ unsigned char clos_prop_prio;
+ unsigned char clos_min;
+ unsigned char clos_max;
+ unsigned char clos_desired;
+};
+
+struct isst_fact_bucket_info {
+ int high_priority_cores_count;
+ int sse_trl;
+ int avx_trl;
+ int avx512_trl;
+};
+
+struct isst_pbf_info {
+ int pbf_acticated;
+ int pbf_available;
+ size_t core_cpumask_size;
+ cpu_set_t *core_cpumask;
+ int p1_high;
+ int p1_low;
+ int t_control;
+ int t_prochot;
+ int tdp;
+};
+
+#define ISST_TRL_MAX_ACTIVE_CORES 8
+#define ISST_FACT_MAX_BUCKETS 8
+struct isst_fact_info {
+ int lp_clipping_ratio_license_sse;
+ int lp_clipping_ratio_license_avx2;
+ int lp_clipping_ratio_license_avx512;
+ struct isst_fact_bucket_info bucket_info[ISST_FACT_MAX_BUCKETS];
+};
+
+struct isst_pkg_ctdp_level_info {
+ int processed;
+ int control_cpu;
+ int pkg_id;
+ int die_id;
+ int level;
+ int fact_support;
+ int pbf_support;
+ int fact_enabled;
+ int pbf_enabled;
+ int tdp_ratio;
+ int active;
+ int tdp_control;
+ int pkg_tdp;
+ int pkg_min_power;
+ int pkg_max_power;
+ int fact;
+ int t_proc_hot;
+ int uncore_p0;
+ int uncore_p1;
+ int sse_p1;
+ int avx2_p1;
+ int avx512_p1;
+ int mem_freq;
+ size_t core_cpumask_size;
+ cpu_set_t *core_cpumask;
+ int cpu_count;
+ int trl_sse_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
+ int trl_avx_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
+ int trl_avx_512_active_cores[ISST_TRL_MAX_ACTIVE_CORES];
+ int kobj_bucket_index;
+ int active_bucket;
+ int fact_max_index;
+ int fact_max_config;
+ int pbf_found;
+ int pbf_active;
+ struct isst_pbf_info pbf_info;
+ struct isst_fact_info fact_info;
+};
+
+#define ISST_MAX_TDP_LEVELS (4 + 1) /* +1 for base config */
+struct isst_pkg_ctdp {
+ int locked;
+ int version;
+ int processed;
+ int levels;
+ int current_level;
+ int enabled;
+ struct isst_pkg_ctdp_level_info ctdp_level[ISST_MAX_TDP_LEVELS];
+};
+
+extern int get_topo_max_cpus(void);
+extern int get_cpu_count(int pkg_id, int die_id);
+
+/* Common interfaces */
+extern void debug_printf(const char *format, ...);
+extern int out_format_is_json(void);
+extern int get_physical_package_id(int cpu);
+extern int get_physical_die_id(int cpu);
+extern size_t alloc_cpu_set(cpu_set_t **cpu_set);
+extern void free_cpu_set(cpu_set_t *cpu_set);
+extern int find_logical_cpu(int pkg_id, int die_id, int phy_cpu);
+extern int find_phy_cpu_num(int logical_cpu);
+extern int find_phy_core_num(int logical_cpu);
+extern void set_cpu_mask_from_punit_coremask(int cpu,
+ unsigned long long core_mask,
+ size_t core_cpumask_size,
+ cpu_set_t *core_cpumask,
+ int *cpu_cnt);
+
+extern int isst_send_mbox_command(unsigned int cpu, unsigned char command,
+ unsigned char sub_command,
+ unsigned int write,
+ unsigned int req_data, unsigned int *resp);
+
+extern int isst_send_msr_command(unsigned int cpu, unsigned int command,
+ int write, unsigned long long *req_resp);
+
+extern int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev);
+extern int isst_get_process_ctdp(int cpu, int tdp_level,
+ struct isst_pkg_ctdp *pkg_dev);
+extern void isst_get_process_ctdp_complete(int cpu,
+ struct isst_pkg_ctdp *pkg_dev);
+extern void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
+ struct isst_pkg_ctdp *pkg_dev);
+extern void isst_ctdp_display_information_start(FILE *outf);
+extern void isst_ctdp_display_information_end(FILE *outf);
+extern void isst_pbf_display_information(int cpu, FILE *outf, int level,
+ struct isst_pbf_info *info);
+extern int isst_set_tdp_level(int cpu, int tdp_level);
+extern int isst_set_tdp_level_msr(int cpu, int tdp_level);
+extern int isst_set_pbf_fact_status(int cpu, int pbf, int enable);
+extern int isst_get_pbf_info(int cpu, int level,
+ struct isst_pbf_info *pbf_info);
+extern void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info);
+extern int isst_get_fact_info(int cpu, int level,
+ struct isst_fact_info *fact_info);
+extern int isst_get_fact_bucket_info(int cpu, int level,
+ struct isst_fact_bucket_info *bucket_info);
+extern void isst_fact_display_information(int cpu, FILE *outf, int level,
+ int fact_bucket, int fact_avx,
+ struct isst_fact_info *fact_info);
+extern int isst_set_trl(int cpu, unsigned long long trl);
+extern int isst_set_trl_from_current_tdp(int cpu, unsigned long long trl);
+extern int isst_get_config_tdp_lock_status(int cpu);
+
+extern int isst_pm_qos_config(int cpu, int enable_clos, int priority_type);
+extern int isst_pm_get_clos(int cpu, int clos,
+ struct isst_clos_config *clos_config);
+extern int isst_set_clos(int cpu, int clos,
+ struct isst_clos_config *clos_config);
+extern int isst_clos_associate(int cpu, int clos);
+extern int isst_clos_get_assoc_status(int cpu, int *clos_id);
+extern void isst_clos_display_information(int cpu, FILE *outf, int clos,
+ struct isst_clos_config *clos_config);
+
+extern int isst_read_reg(unsigned short reg, unsigned int *val);
+extern int isst_write_reg(int reg, unsigned int val);
+
+extern void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
+ int result);
+#endif
diff --git a/tools/power/x86/intel_speed_select/isst_config.c b/tools/power/x86/intel_speed_select/isst_config.c
new file mode 100644
index 000000000000..477593b7120a
--- /dev/null
+++ b/tools/power/x86/intel_speed_select/isst_config.c
@@ -0,0 +1,1607 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include <linux/isst_if.h>
+
+#include "isst.h"
+
+struct process_cmd_struct {
+ char *feature;
+ char *command;
+ void (*process_fn)(void);
+};
+
+static const char *version_str = "v1.0";
+static const int supported_api_ver = 1;
+static struct isst_if_platform_info isst_platform_info;
+static char *progname;
+static int debug_flag;
+static FILE *outf;
+
+static int cpu_model;
+
+#define MAX_CPUS_IN_ONE_REQ 64
+static short max_target_cpus;
+static unsigned short target_cpus[MAX_CPUS_IN_ONE_REQ];
+
+static int topo_max_cpus;
+static size_t present_cpumask_size;
+static cpu_set_t *present_cpumask;
+static size_t target_cpumask_size;
+static cpu_set_t *target_cpumask;
+static int tdp_level = 0xFF;
+static int fact_bucket = 0xFF;
+static int fact_avx = 0xFF;
+static unsigned long long fact_trl;
+static int out_format_json;
+static int cmd_help;
+
+/* clos related */
+static int current_clos = -1;
+static int clos_epp = -1;
+static int clos_prop_prio = -1;
+static int clos_min = -1;
+static int clos_max = -1;
+static int clos_desired = -1;
+static int clos_priority_type;
+
+struct _cpu_map {
+ unsigned short core_id;
+ unsigned short pkg_id;
+ unsigned short die_id;
+ unsigned short punit_cpu;
+ unsigned short punit_cpu_core;
+};
+struct _cpu_map *cpu_map;
+
+void debug_printf(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+
+ if (debug_flag)
+ vprintf(format, args);
+
+ va_end(args);
+}
+
+static void update_cpu_model(void)
+{
+ unsigned int ebx, ecx, edx;
+ unsigned int fms, family;
+
+ __cpuid(1, fms, ebx, ecx, edx);
+ family = (fms >> 8) & 0xf;
+ cpu_model = (fms >> 4) & 0xf;
+ if (family == 6 || family == 0xf)
+ cpu_model += ((fms >> 16) & 0xf) << 4;
+}
+
+/* Open a file, and exit on failure */
+static FILE *fopen_or_exit(const char *path, const char *mode)
+{
+ FILE *filep = fopen(path, mode);
+
+ if (!filep)
+ err(1, "%s: open failed", path);
+
+ return filep;
+}
+
+/* Parse a file containing a single int */
+static int parse_int_file(int fatal, const char *fmt, ...)
+{
+ va_list args;
+ char path[PATH_MAX];
+ FILE *filep;
+ int value;
+
+ va_start(args, fmt);
+ vsnprintf(path, sizeof(path), fmt, args);
+ va_end(args);
+ if (fatal) {
+ filep = fopen_or_exit(path, "r");
+ } else {
+ filep = fopen(path, "r");
+ if (!filep)
+ return -1;
+ }
+ if (fscanf(filep, "%d", &value) != 1)
+ err(1, "%s: failed to parse number from file", path);
+ fclose(filep);
+
+ return value;
+}
+
+int cpufreq_sysfs_present(void)
+{
+ DIR *dir;
+
+ dir = opendir("/sys/devices/system/cpu/cpu0/cpufreq");
+ if (dir) {
+ closedir(dir);
+ return 1;
+ }
+
+ return 0;
+}
+
+int out_format_is_json(void)
+{
+ return out_format_json;
+}
+
+int get_physical_package_id(int cpu)
+{
+ return parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/topology/physical_package_id",
+ cpu);
+}
+
+int get_physical_core_id(int cpu)
+{
+ return parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/topology/core_id", cpu);
+}
+
+int get_physical_die_id(int cpu)
+{
+ int ret;
+
+ ret = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/topology/die_id",
+ cpu);
+ if (ret < 0)
+ ret = 0;
+
+ return ret;
+}
+
+int get_topo_max_cpus(void)
+{
+ return topo_max_cpus;
+}
+
+#define MAX_PACKAGE_COUNT 8
+#define MAX_DIE_PER_PACKAGE 2
+static void for_each_online_package_in_set(void (*callback)(int, void *, void *,
+ void *, void *),
+ void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int max_packages[MAX_PACKAGE_COUNT * MAX_PACKAGE_COUNT];
+ int pkg_index = 0, i;
+
+ memset(max_packages, 0xff, sizeof(max_packages));
+ for (i = 0; i < topo_max_cpus; ++i) {
+ int j, online, pkg_id, die_id = 0, skip = 0;
+
+ if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
+ continue;
+ if (i)
+ online = parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/online", i);
+ else
+ online =
+ 1; /* online entry for CPU 0 needs some special configs */
+
+ die_id = get_physical_die_id(i);
+ if (die_id < 0)
+ die_id = 0;
+ pkg_id = get_physical_package_id(i);
+ /* Create an unique id for package, die combination to store */
+ pkg_id = (MAX_PACKAGE_COUNT * pkg_id + die_id);
+
+ for (j = 0; j < pkg_index; ++j) {
+ if (max_packages[j] == pkg_id) {
+ skip = 1;
+ break;
+ }
+ }
+
+ if (!skip && online && callback) {
+ callback(i, arg1, arg2, arg3, arg4);
+ max_packages[pkg_index++] = pkg_id;
+ }
+ }
+}
+
+static void for_each_online_target_cpu_in_set(
+ void (*callback)(int, void *, void *, void *, void *), void *arg1,
+ void *arg2, void *arg3, void *arg4)
+{
+ int i;
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ int online;
+
+ if (!CPU_ISSET_S(i, target_cpumask_size, target_cpumask))
+ continue;
+ if (i)
+ online = parse_int_file(
+ 1, "/sys/devices/system/cpu/cpu%d/online", i);
+ else
+ online =
+ 1; /* online entry for CPU 0 needs some special configs */
+
+ if (online && callback)
+ callback(i, arg1, arg2, arg3, arg4);
+ }
+}
+
+#define BITMASK_SIZE 32
+static void set_max_cpu_num(void)
+{
+ FILE *filep;
+ unsigned long dummy;
+
+ topo_max_cpus = 0;
+ filep = fopen_or_exit(
+ "/sys/devices/system/cpu/cpu0/topology/thread_siblings", "r");
+ while (fscanf(filep, "%lx,", &dummy) == 1)
+ topo_max_cpus += BITMASK_SIZE;
+ fclose(filep);
+ topo_max_cpus--; /* 0 based */
+
+ debug_printf("max cpus %d\n", topo_max_cpus);
+}
+
+size_t alloc_cpu_set(cpu_set_t **cpu_set)
+{
+ cpu_set_t *_cpu_set;
+ size_t size;
+
+ _cpu_set = CPU_ALLOC((topo_max_cpus + 1));
+ if (_cpu_set == NULL)
+ err(3, "CPU_ALLOC");
+ size = CPU_ALLOC_SIZE((topo_max_cpus + 1));
+ CPU_ZERO_S(size, _cpu_set);
+
+ *cpu_set = _cpu_set;
+ return size;
+}
+
+void free_cpu_set(cpu_set_t *cpu_set)
+{
+ CPU_FREE(cpu_set);
+}
+
+static int cpu_cnt[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE];
+static void set_cpu_present_cpu_mask(void)
+{
+ size_t size;
+ DIR *dir;
+ int i;
+
+ size = alloc_cpu_set(&present_cpumask);
+ present_cpumask_size = size;
+ for (i = 0; i < topo_max_cpus; ++i) {
+ char buffer[256];
+
+ snprintf(buffer, sizeof(buffer),
+ "/sys/devices/system/cpu/cpu%d", i);
+ dir = opendir(buffer);
+ if (dir) {
+ int pkg_id, die_id;
+
+ CPU_SET_S(i, size, present_cpumask);
+ die_id = get_physical_die_id(i);
+ if (die_id < 0)
+ die_id = 0;
+
+ pkg_id = get_physical_package_id(i);
+ if (pkg_id < MAX_PACKAGE_COUNT &&
+ die_id < MAX_DIE_PER_PACKAGE)
+ cpu_cnt[pkg_id][die_id]++;
+ }
+ closedir(dir);
+ }
+}
+
+int get_cpu_count(int pkg_id, int die_id)
+{
+ if (pkg_id < MAX_PACKAGE_COUNT && die_id < MAX_DIE_PER_PACKAGE)
+ return cpu_cnt[pkg_id][die_id] + 1;
+
+ return 0;
+}
+
+static void set_cpu_target_cpu_mask(void)
+{
+ size_t size;
+ int i;
+
+ size = alloc_cpu_set(&target_cpumask);
+ target_cpumask_size = size;
+ for (i = 0; i < max_target_cpus; ++i) {
+ if (!CPU_ISSET_S(target_cpus[i], present_cpumask_size,
+ present_cpumask))
+ continue;
+
+ CPU_SET_S(target_cpus[i], size, target_cpumask);
+ }
+}
+
+static void create_cpu_map(void)
+{
+ const char *pathname = "/dev/isst_interface";
+ int i, fd = 0;
+ struct isst_if_cpu_maps map;
+
+ cpu_map = malloc(sizeof(*cpu_map) * topo_max_cpus);
+ if (!cpu_map)
+ err(3, "cpumap");
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ if (!CPU_ISSET_S(i, present_cpumask_size, present_cpumask))
+ continue;
+
+ map.cmd_count = 1;
+ map.cpu_map[0].logical_cpu = i;
+
+ debug_printf(" map logical_cpu:%d\n",
+ map.cpu_map[0].logical_cpu);
+ if (ioctl(fd, ISST_IF_GET_PHY_ID, &map) == -1) {
+ perror("ISST_IF_GET_PHY_ID");
+ fprintf(outf, "Error: map logical_cpu:%d\n",
+ map.cpu_map[0].logical_cpu);
+ continue;
+ }
+ cpu_map[i].core_id = get_physical_core_id(i);
+ cpu_map[i].pkg_id = get_physical_package_id(i);
+ cpu_map[i].die_id = get_physical_die_id(i);
+ cpu_map[i].punit_cpu = map.cpu_map[0].physical_cpu;
+ cpu_map[i].punit_cpu_core = (map.cpu_map[0].physical_cpu >>
+ 1); // shift to get core id
+
+ debug_printf(
+ "map logical_cpu:%d core: %d die:%d pkg:%d punit_cpu:%d punit_core:%d\n",
+ i, cpu_map[i].core_id, cpu_map[i].die_id,
+ cpu_map[i].pkg_id, cpu_map[i].punit_cpu,
+ cpu_map[i].punit_cpu_core);
+ }
+
+ if (fd)
+ close(fd);
+}
+
+int find_logical_cpu(int pkg_id, int die_id, int punit_core_id)
+{
+ int i;
+
+ for (i = 0; i < topo_max_cpus; ++i) {
+ if (cpu_map[i].pkg_id == pkg_id &&
+ cpu_map[i].die_id == die_id &&
+ cpu_map[i].punit_cpu_core == punit_core_id)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+void set_cpu_mask_from_punit_coremask(int cpu, unsigned long long core_mask,
+ size_t core_cpumask_size,
+ cpu_set_t *core_cpumask, int *cpu_cnt)
+{
+ int i, cnt = 0;
+ int die_id, pkg_id;
+
+ *cpu_cnt = 0;
+ die_id = get_physical_die_id(cpu);
+ pkg_id = get_physical_package_id(cpu);
+
+ for (i = 0; i < 64; ++i) {
+ if (core_mask & BIT(i)) {
+ int j;
+
+ for (j = 0; j < topo_max_cpus; ++j) {
+ if (cpu_map[j].pkg_id == pkg_id &&
+ cpu_map[j].die_id == die_id &&
+ cpu_map[j].punit_cpu_core == i) {
+ CPU_SET_S(j, core_cpumask_size,
+ core_cpumask);
+ ++cnt;
+ }
+ }
+ }
+ }
+
+ *cpu_cnt = cnt;
+}
+
+int find_phy_core_num(int logical_cpu)
+{
+ if (logical_cpu < topo_max_cpus)
+ return cpu_map[logical_cpu].punit_cpu_core;
+
+ return -EINVAL;
+}
+
+static int isst_send_mmio_command(unsigned int cpu, unsigned int reg, int write,
+ unsigned int *value)
+{
+ struct isst_if_io_regs io_regs;
+ const char *pathname = "/dev/isst_interface";
+ int cmd;
+ int fd;
+
+ debug_printf("mmio_cmd cpu:%d reg:%d write:%d\n", cpu, reg, write);
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ io_regs.req_count = 1;
+ io_regs.io_reg[0].logical_cpu = cpu;
+ io_regs.io_reg[0].reg = reg;
+ cmd = ISST_IF_IO_CMD;
+ if (write) {
+ io_regs.io_reg[0].read_write = 1;
+ io_regs.io_reg[0].value = *value;
+ } else {
+ io_regs.io_reg[0].read_write = 0;
+ }
+
+ if (ioctl(fd, cmd, &io_regs) == -1) {
+ perror("ISST_IF_IO_CMD");
+ fprintf(outf, "Error: mmio_cmd cpu:%d reg:%x read_write:%x\n",
+ cpu, reg, write);
+ } else {
+ if (!write)
+ *value = io_regs.io_reg[0].value;
+
+ debug_printf(
+ "mmio_cmd response: cpu:%d reg:%x rd_write:%x resp:%x\n",
+ cpu, reg, write, *value);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int isst_send_mbox_command(unsigned int cpu, unsigned char command,
+ unsigned char sub_command, unsigned int parameter,
+ unsigned int req_data, unsigned int *resp)
+{
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+ struct isst_if_mbox_cmds mbox_cmds = { 0 };
+
+ debug_printf(
+ "mbox_send: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x\n",
+ cpu, command, sub_command, parameter, req_data);
+
+ if (isst_platform_info.mmio_supported && command == CONFIG_CLOS) {
+ unsigned int value;
+ int write = 0;
+ int clos_id, core_id, ret = 0;
+
+ debug_printf("CLOS %d\n", cpu);
+
+ if (parameter & BIT(MBOX_CMD_WRITE_BIT)) {
+ value = req_data;
+ write = 1;
+ }
+
+ switch (sub_command) {
+ case CLOS_PQR_ASSOC:
+ core_id = parameter & 0xff;
+ ret = isst_send_mmio_command(
+ cpu, PQR_ASSOC_OFFSET + core_id * 4, write,
+ &value);
+ if (!ret && !write)
+ *resp = value;
+ break;
+ case CLOS_PM_CLOS:
+ clos_id = parameter & 0x03;
+ ret = isst_send_mmio_command(
+ cpu, PM_CLOS_OFFSET + clos_id * 4, write,
+ &value);
+ 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:
+ break;
+ }
+ return ret;
+ }
+
+ mbox_cmds.cmd_count = 1;
+ mbox_cmds.mbox_cmd[0].logical_cpu = cpu;
+ mbox_cmds.mbox_cmd[0].command = command;
+ mbox_cmds.mbox_cmd[0].sub_command = sub_command;
+ mbox_cmds.mbox_cmd[0].parameter = parameter;
+ mbox_cmds.mbox_cmd[0].req_data = req_data;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_MBOX_COMMAND, &mbox_cmds) == -1) {
+ perror("ISST_IF_MBOX_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);
+ } else {
+ *resp = mbox_cmds.mbox_cmd[0].resp_data;
+ debug_printf(
+ "mbox_cmd response: cpu:%d command:%x sub_command:%x parameter:%x req_data:%x resp:%x\n",
+ cpu, command, sub_command, parameter, req_data, *resp);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+int isst_send_msr_command(unsigned int cpu, unsigned int msr, int write,
+ unsigned long long *req_resp)
+{
+ struct isst_if_msr_cmds msr_cmds;
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ msr_cmds.cmd_count = 1;
+ msr_cmds.msr_cmd[0].logical_cpu = cpu;
+ msr_cmds.msr_cmd[0].msr = msr;
+ msr_cmds.msr_cmd[0].read_write = write;
+ if (write)
+ msr_cmds.msr_cmd[0].data = *req_resp;
+
+ if (ioctl(fd, ISST_IF_MSR_COMMAND, &msr_cmds) == -1) {
+ perror("ISST_IF_MSR_COMMAD");
+ fprintf(outf, "Error: msr_cmd cpu:%d msr:%x read_write:%d\n",
+ cpu, msr, write);
+ } else {
+ if (!write)
+ *req_resp = msr_cmds.msr_cmd[0].data;
+
+ debug_printf(
+ "msr_cmd response: cpu:%d msr:%x rd_write:%x resp:%llx %llx\n",
+ cpu, msr, write, *req_resp, msr_cmds.msr_cmd[0].data);
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int isst_fill_platform_info(void)
+{
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &isst_platform_info) == -1) {
+ perror("ISST_IF_GET_PLATFORM_INFO");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static void isst_print_platform_information(void)
+{
+ struct isst_if_platform_info platform_info;
+ const char *pathname = "/dev/isst_interface";
+ int fd;
+
+ fd = open(pathname, O_RDWR);
+ if (fd < 0)
+ err(-1, "%s open failed", pathname);
+
+ if (ioctl(fd, ISST_IF_GET_PLATFORM_INFO, &platform_info) == -1) {
+ perror("ISST_IF_GET_PLATFORM_INFO");
+ } else {
+ fprintf(outf, "Platform: API version : %d\n",
+ platform_info.api_version);
+ fprintf(outf, "Platform: Driver version : %d\n",
+ platform_info.driver_version);
+ fprintf(outf, "Platform: mbox supported : %d\n",
+ platform_info.mbox_supported);
+ fprintf(outf, "Platform: mmio supported : %d\n",
+ platform_info.mmio_supported);
+ }
+
+ close(fd);
+
+ exit(0);
+}
+
+static void exec_on_get_ctdp_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int (*fn_ptr)(int cpu, void *arg);
+ int ret;
+
+ fn_ptr = arg1;
+ ret = fn_ptr(cpu, arg2);
+ if (ret)
+ perror("get_tdp_*");
+ else
+ isst_display_result(cpu, outf, "perf-profile", (char *)arg3,
+ *(unsigned int *)arg4);
+}
+
+#define _get_tdp_level(desc, suffix, object, help) \
+ static void get_tdp_##object(void) \
+ { \
+ struct isst_pkg_ctdp ctdp; \
+\
+ if (cmd_help) { \
+ fprintf(stderr, \
+ "Print %s [No command arguments are required]\n", \
+ help); \
+ exit(0); \
+ } \
+ isst_ctdp_display_information_start(outf); \
+ if (max_target_cpus) \
+ for_each_online_target_cpu_in_set( \
+ exec_on_get_ctdp_cpu, isst_get_ctdp_##suffix, \
+ &ctdp, desc, &ctdp.object); \
+ else \
+ for_each_online_package_in_set(exec_on_get_ctdp_cpu, \
+ isst_get_ctdp_##suffix, \
+ &ctdp, desc, \
+ &ctdp.object); \
+ isst_ctdp_display_information_end(outf); \
+ }
+
+_get_tdp_level("get-config-levels", levels, levels, "TDP levels");
+_get_tdp_level("get-config-version", levels, version, "TDP version");
+_get_tdp_level("get-config-enabled", levels, enabled, "TDP enable status");
+_get_tdp_level("get-config-current_level", levels, current_level,
+ "Current TDP Level");
+_get_tdp_level("get-lock-status", levels, locked, "TDP lock status");
+
+static void dump_isst_config_for_cpu(int cpu, void *arg1, void *arg2,
+ void *arg3, void *arg4)
+{
+ struct isst_pkg_ctdp pkg_dev;
+ int ret;
+
+ memset(&pkg_dev, 0, sizeof(pkg_dev));
+ ret = isst_get_process_ctdp(cpu, tdp_level, &pkg_dev);
+ if (ret) {
+ perror("isst_get_process_ctdp");
+ } else {
+ isst_ctdp_display_information(cpu, outf, tdp_level, &pkg_dev);
+ isst_get_process_ctdp_complete(cpu, &pkg_dev);
+ }
+}
+
+static void dump_isst_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print Intel(R) Speed Select Technology Performance profile configuration\n");
+ fprintf(stderr,
+ "including base frequency and turbo frequency configurations\n");
+ fprintf(stderr, "Optional: -l|--level : Specify tdp level\n");
+ fprintf(stderr,
+ "\tIf no arguments, dump information for all TDP levels\n");
+ exit(0);
+ }
+
+ 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);
+ else
+ for_each_online_package_in_set(dump_isst_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_tdp_level_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+
+ ret = isst_set_tdp_level(cpu, tdp_level);
+ if (ret)
+ perror("set_tdp_level_for_cpu");
+ else
+ isst_display_result(cpu, outf, "perf-profile", "set_tdp_level",
+ ret);
+}
+
+static void set_tdp_level(void)
+{
+ if (cmd_help) {
+ fprintf(stderr, "Set Config TDP level\n");
+ fprintf(stderr,
+ "\t Arguments: -l|--level : Specify tdp level\n");
+ exit(0);
+ }
+
+ if (tdp_level == 0xff) {
+ fprintf(outf, "Invalid command: specify tdp_level\n");
+ exit(1);
+ }
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_tdp_level_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(set_tdp_level_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void dump_pbf_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ struct isst_pbf_info pbf_info;
+ int ret;
+
+ ret = isst_get_pbf_info(cpu, tdp_level, &pbf_info);
+ if (ret) {
+ perror("isst_get_pbf_info");
+ } else {
+ isst_pbf_display_information(cpu, outf, tdp_level, &pbf_info);
+ isst_get_pbf_info_complete(&pbf_info);
+ }
+}
+
+static void dump_pbf_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print Intel(R) Speed Select Technology base frequency configuration for a TDP level\n");
+ fprintf(stderr,
+ "\tArguments: -l|--level : Specify tdp level\n");
+ exit(0);
+ }
+
+ if (tdp_level == 0xff) {
+ fprintf(outf, "Invalid command: specify tdp_level\n");
+ exit(1);
+ }
+
+ 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);
+ else
+ for_each_online_package_in_set(dump_pbf_config_for_cpu, 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)
+{
+ int ret;
+ int status = *(int *)arg4;
+
+ ret = isst_set_pbf_fact_status(cpu, 1, status);
+ if (ret) {
+ perror("isst_set_pbf");
+ } else {
+ if (status)
+ isst_display_result(cpu, outf, "base-freq", "enable",
+ ret);
+ else
+ isst_display_result(cpu, outf, "base-freq", "disable",
+ ret);
+ }
+}
+
+static void set_pbf_enable(void)
+{
+ int status = 1;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Enable Intel Speed Select Technology base frequency feature [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(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ else
+ for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_pbf_disable(void)
+{
+ int status = 0;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Disable Intel Speed Select Technology base frequency feature [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(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ else
+ for_each_online_package_in_set(set_pbf_for_cpu, NULL, NULL,
+ NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void dump_fact_config_for_cpu(int cpu, void *arg1, void *arg2,
+ void *arg3, void *arg4)
+{
+ struct isst_fact_info fact_info;
+ int ret;
+
+ ret = isst_get_fact_info(cpu, tdp_level, &fact_info);
+ if (ret)
+ perror("isst_get_fact_bucket_info");
+ else
+ isst_fact_display_information(cpu, outf, tdp_level, fact_bucket,
+ fact_avx, &fact_info);
+}
+
+static void dump_fact_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print complete Intel Speed Select Technology turbo frequency configuration for a TDP level. Other arguments are optional.\n");
+ fprintf(stderr,
+ "\tArguments: -l|--level : Specify tdp level\n");
+ fprintf(stderr,
+ "\tArguments: -b|--bucket : Bucket index to dump\n");
+ fprintf(stderr,
+ "\tArguments: -r|--trl-type : Specify trl type: sse|avx2|avx512\n");
+ exit(0);
+ }
+
+ if (tdp_level == 0xff) {
+ fprintf(outf, "Invalid command: specify tdp_level\n");
+ exit(1);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(dump_fact_config_for_cpu,
+ NULL, NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(dump_fact_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_fact_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+ int status = *(int *)arg4;
+
+ ret = isst_set_pbf_fact_status(cpu, 0, status);
+ if (ret)
+ perror("isst_set_fact");
+ else {
+ if (status) {
+ struct isst_pkg_ctdp pkg_dev;
+
+ ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+ if (ret) {
+ isst_display_result(cpu, outf, "turbo-freq",
+ "enable", ret);
+ return;
+ }
+ 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);
+ }
+ }
+}
+
+static void set_fact_enable(void)
+{
+ int status = 1;
+
+ if (cmd_help) {
+ fprintf(stderr,
+ "Enable Intel Speed Select Technology Turbo frequency feature\n");
+ fprintf(stderr,
+ "Optional: -t|--trl : Specify turbo ratio limit\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);
+ else
+ for_each_online_package_in_set(set_fact_for_cpu, NULL, NULL,
+ NULL, &status);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_fact_disable(void)
+{
+ int status = 0;
+
+ 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);
+ }
+
+ 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);
+}
+
+static void enable_clos_qos_config(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+ int status = *(int *)arg4;
+
+ ret = isst_pm_qos_config(cpu, status, clos_priority_type);
+ 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);
+ }
+}
+
+static void set_clos_enable(void)
+{
+ int status = 1;
+
+ 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");
+ exit(0);
+ }
+
+ if (cpufreq_sysfs_present()) {
+ fprintf(stderr,
+ "cpufreq subsystem and core-power enable will interfere with each other!\n");
+ }
+
+ 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);
+ 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);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void dump_clos_config_for_cpu(int cpu, void *arg1, void *arg2,
+ void *arg3, void *arg4)
+{
+ struct isst_clos_config clos_config;
+ int ret;
+
+ ret = isst_pm_get_clos(cpu, current_clos, &clos_config);
+ if (ret)
+ perror("isst_pm_get_clos");
+ else
+ isst_clos_display_information(cpu, outf, current_clos,
+ &clos_config);
+}
+
+static void dump_clos_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Print Intel Speed Select Technology core power configuration\n");
+ fprintf(stderr,
+ "\tArguments: [-c | --clos]: Specify clos id\n");
+ exit(0);
+ }
+ if (current_clos < 0 || current_clos > 3) {
+ fprintf(stderr, "Invalid clos id\n");
+ exit(0);
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(dump_clos_config_for_cpu,
+ NULL, NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(dump_clos_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_clos_config_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ struct isst_clos_config clos_config;
+ int ret;
+
+ clos_config.pkg_id = get_physical_package_id(cpu);
+ clos_config.die_id = get_physical_die_id(cpu);
+
+ clos_config.epp = clos_epp;
+ clos_config.clos_prop_prio = clos_prop_prio;
+ clos_config.clos_min = clos_min;
+ clos_config.clos_max = clos_max;
+ clos_config.clos_desired = clos_desired;
+ ret = isst_set_clos(cpu, current_clos, &clos_config);
+ if (ret)
+ perror("isst_set_clos");
+ else
+ isst_display_result(cpu, outf, "core-power", "config", ret);
+}
+
+static void set_clos_config(void)
+{
+ if (cmd_help) {
+ fprintf(stderr,
+ "Set core-power configuration for one of the four clos ids\n");
+ fprintf(stderr,
+ "\tSpecify targeted clos id with [--clos|-c]\n");
+ 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");
+ exit(0);
+ }
+
+ if (current_clos < 0 || current_clos > 3) {
+ fprintf(stderr, "Invalid clos id\n");
+ exit(0);
+ }
+ if (clos_epp < 0 || clos_epp > 0x0F) {
+ fprintf(stderr, "clos epp is not specified, default: 0\n");
+ clos_epp = 0;
+ }
+ if (clos_prop_prio < 0 || clos_prop_prio > 0x0F) {
+ fprintf(stderr,
+ "clos frequency weight is not specified, default: 0\n");
+ clos_prop_prio = 0;
+ }
+ if (clos_min < 0) {
+ fprintf(stderr, "clos min is not specified, default: 0\n");
+ clos_min = 0;
+ }
+ if (clos_max < 0) {
+ fprintf(stderr, "clos max is not specified, default: 0xff\n");
+ clos_max = 0xff;
+ }
+ if (clos_desired < 0) {
+ fprintf(stderr, "clos desired is not specified, default: 0\n");
+ clos_desired = 0x00;
+ }
+
+ isst_ctdp_display_information_start(outf);
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_clos_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else
+ for_each_online_package_in_set(set_clos_config_for_cpu, NULL,
+ NULL, NULL, NULL);
+ isst_ctdp_display_information_end(outf);
+}
+
+static void set_clos_assoc_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int ret;
+
+ ret = isst_clos_associate(cpu, current_clos);
+ if (ret)
+ perror("isst_clos_associate");
+ else
+ isst_display_result(cpu, outf, "core-power", "assoc", ret);
+}
+
+static void set_clos_assoc(void)
+{
+ if (cmd_help) {
+ fprintf(stderr, "Associate a clos id to a CPU\n");
+ fprintf(stderr,
+ "\tSpecify targeted clos id with [--clos|-c]\n");
+ exit(0);
+ }
+
+ if (current_clos < 0 || current_clos > 3) {
+ fprintf(stderr, "Invalid clos id\n");
+ exit(0);
+ }
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(set_clos_assoc_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else {
+ fprintf(stderr,
+ "Invalid target cpu. Specify with [-c|--cpu]\n");
+ }
+}
+
+static void get_clos_assoc_for_cpu(int cpu, void *arg1, void *arg2, void *arg3,
+ void *arg4)
+{
+ int clos, ret;
+
+ ret = isst_clos_get_assoc_status(cpu, &clos);
+ if (ret)
+ perror("isst_clos_get_assoc_status");
+ else
+ isst_display_result(cpu, outf, "core-power", "get-assoc", clos);
+}
+
+static void get_clos_assoc(void)
+{
+ if (cmd_help) {
+ fprintf(stderr, "Get associate clos id to a CPU\n");
+ fprintf(stderr, "\tSpecify targeted cpu id with [--cpu|-c]\n");
+ exit(0);
+ }
+ if (max_target_cpus)
+ for_each_online_target_cpu_in_set(get_clos_assoc_for_cpu, NULL,
+ NULL, NULL, NULL);
+ else {
+ fprintf(stderr,
+ "Invalid target cpu. Specify with [-c|--cpu]\n");
+ }
+}
+
+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_config },
+ { "core-power", "enable", set_clos_enable },
+ { "core-power", "disable", set_clos_disable },
+ { "core-power", "config", set_clos_config },
+ { "core-power", "assoc", set_clos_assoc },
+ { "core-power", "get-assoc", get_clos_assoc },
+ { NULL, NULL, NULL }
+};
+
+/*
+ * parse cpuset with following syntax
+ * 1,2,4..6,8-10 and set bits in cpu_subset
+ */
+void parse_cpu_command(char *optarg)
+{
+ unsigned int start, end;
+ char *next;
+
+ next = optarg;
+
+ while (next && *next) {
+ if (*next == '-') /* no negative cpu numbers */
+ goto error;
+
+ start = strtoul(next, &next, 10);
+
+ if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
+ target_cpus[max_target_cpus++] = start;
+
+ if (*next == '\0')
+ break;
+
+ if (*next == ',') {
+ next += 1;
+ continue;
+ }
+
+ if (*next == '-') {
+ next += 1; /* start range */
+ } else if (*next == '.') {
+ next += 1;
+ if (*next == '.')
+ next += 1; /* start range */
+ else
+ goto error;
+ }
+
+ end = strtoul(next, &next, 10);
+ if (end <= start)
+ goto error;
+
+ while (++start <= end) {
+ if (max_target_cpus < MAX_CPUS_IN_ONE_REQ)
+ target_cpus[max_target_cpus++] = start;
+ }
+
+ if (*next == ',')
+ next += 1;
+ else if (*next != '\0')
+ goto error;
+ }
+
+#ifdef DEBUG
+ {
+ int i;
+
+ for (i = 0; i < max_target_cpus; ++i)
+ printf("cpu [%d] in arg\n", target_cpus[i]);
+ }
+#endif
+ return;
+
+error:
+ fprintf(stderr, "\"--cpu %s\" malformed\n", optarg);
+ exit(-1);
+}
+
+static void parse_cmd_args(int argc, int start, char **argv)
+{
+ int opt;
+ int option_index;
+
+ static struct option long_options[] = {
+ { "bucket", required_argument, 0, 'b' },
+ { "level", required_argument, 0, 'l' },
+ { "trl-type", required_argument, 0, 'r' },
+ { "trl", required_argument, 0, 't' },
+ { "help", no_argument, 0, 'h' },
+ { "clos", required_argument, 0, 'c' },
+ { "desired", required_argument, 0, 'd' },
+ { "epp", required_argument, 0, 'e' },
+ { "min", required_argument, 0, 'n' },
+ { "max", required_argument, 0, 'm' },
+ { "priority", required_argument, 0, 'p' },
+ { "weight", required_argument, 0, 'w' },
+ { 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:h",
+ long_options, &option_index)) != -1) {
+ switch (opt) {
+ case 'b':
+ fact_bucket = atoi(optarg);
+ break;
+ case 'h':
+ cmd_help = 1;
+ break;
+ case 'l':
+ tdp_level = atoi(optarg);
+ break;
+ case 't':
+ sscanf(optarg, "0x%llx", &fact_trl);
+ break;
+ case 'r':
+ if (!strncmp(optarg, "sse", 3)) {
+ fact_avx = 0x01;
+ } else if (!strncmp(optarg, "avx2", 4)) {
+ fact_avx = 0x02;
+ } else if (!strncmp(optarg, "avx512", 4)) {
+ fact_avx = 0x04;
+ } else {
+ fprintf(outf, "Invalid sse,avx options\n");
+ exit(1);
+ }
+ break;
+ /* CLOS related */
+ case 'c':
+ current_clos = atoi(optarg);
+ printf("clos %d\n", current_clos);
+ break;
+ case 'd':
+ clos_desired = atoi(optarg);
+ break;
+ case 'e':
+ clos_epp = atoi(optarg);
+ break;
+ case 'n':
+ clos_min = atoi(optarg);
+ break;
+ case 'm':
+ clos_max = atoi(optarg);
+ break;
+ case 'p':
+ clos_priority_type = atoi(optarg);
+ break;
+ case 'w':
+ clos_prop_prio = atoi(optarg);
+ break;
+ default:
+ printf("no match\n");
+ }
+ }
+}
+
+static void isst_help(void)
+{
+ printf("perf-profile:\tAn architectural mechanism that allows multiple optimized \n\
+ performance profiles per system via static and/or dynamic\n\
+ adjustment of core count, workload, Tjmax, and\n\
+ 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");
+}
+
+static void pbf_help(void)
+{
+ printf("base-freq:\tEnables users to increase guaranteed base frequency\n\
+ on certain cores (high priority cores) in exchange for lower\n\
+ base frequency on remaining cores (low priority cores).\n");
+ printf("\tcommand : info\n");
+ printf("\tcommand : enable\n");
+ printf("\tcommand : disable\n");
+}
+
+static void fact_help(void)
+{
+ printf("turbo-freq:\tEnables the ability to set different turbo ratio\n\
+ limits to cores based on priority.\n");
+ printf("\nCommand: For feature=turbo-freq\n");
+ printf("\tcommand : info\n");
+ printf("\tcommand : enable\n");
+ printf("\tcommand : disable\n");
+}
+
+static void core_power_help(void)
+{
+ printf("core-power:\tInterface that allows user to define per core/tile\n\
+ priority.\n");
+ printf("\nCommands : For feature=core-power\n");
+ printf("\tinfo\n");
+ printf("\tenable\n");
+ printf("\tdisable\n");
+ printf("\tconfig\n");
+ printf("\tassoc\n");
+ printf("\tget-assoc\n");
+}
+
+struct process_cmd_help_struct {
+ char *feature;
+ void (*process_fn)(void);
+};
+
+static struct process_cmd_help_struct isst_help_cmds[] = {
+ { "perf-profile", isst_help },
+ { "base-freq", pbf_help },
+ { "turbo-freq", fact_help },
+ { "core-power", core_power_help },
+ { NULL, NULL }
+};
+
+void process_command(int argc, char **argv)
+{
+ int i = 0, matched = 0;
+ char *feature = argv[optind];
+ char *cmd = argv[optind + 1];
+
+ if (!feature || !cmd)
+ return;
+
+ debug_printf("Domain 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();
+ exit(0);
+ }
+ ++i;
+ }
+ }
+
+ create_cpu_map();
+
+ i = 0;
+ while (isst_cmds[i].feature) {
+ if (!strcmp(isst_cmds[i].feature, feature) &&
+ !strcmp(isst_cmds[i].command, cmd)) {
+ parse_cmd_args(argc, optind + 1, argv);
+ isst_cmds[i].process_fn();
+ matched = 1;
+ break;
+ }
+ ++i;
+ }
+
+ if (!matched)
+ fprintf(stderr, "Invalid command\n");
+}
+
+static void usage(void)
+{
+ printf("Intel(R) Speed Select Technology\n");
+ printf("\nUsage:\n");
+ printf("intel-speed-select [OPTIONS] FEATURE COMMAND COMMAND_ARGUMENTS\n");
+ printf("\nUse this tool to enumerate and control the Intel Speed Select Technology features,\n");
+ printf("\nFEATURE : [perf-profile|base-freq|turbo-freq|core-power]\n");
+ printf("\nFor help on each feature, use --h|--help\n");
+ printf("\tFor example: intel-speed-select perf-profile -h\n");
+
+ printf("\nFor additional help on each command for a feature, use --h|--help\n");
+ printf("\tFor example: intel-speed-select perf-profile get-lock-status -h\n");
+ printf("\t\t This will print help for the command \"get-lock-status\" for the feature \"perf-profile\"\n");
+
+ printf("\nOPTIONS\n");
+ printf("\t[-c|--cpu] : logical cpu number\n");
+ printf("\t\tDefault: Die scoped for all dies in the system with multiple dies/package\n");
+ printf("\t\t\t Or Package scoped for all Packages when each package contains one die\n");
+ printf("\t[-d|--debug] : Debug mode\n");
+ printf("\t[-h|--help] : Print help\n");
+ printf("\t[-i|--info] : Print platform information\n");
+ printf("\t[-o|--out] : Output file\n");
+ printf("\t\t\tDefault : stderr\n");
+ printf("\t[-f|--format] : output format [json|text]. Default: text\n");
+ printf("\t[-v|--version] : Print version\n");
+
+ printf("\nResult format\n");
+ printf("\tResult display uses a common format for each command:\n");
+ printf("\tResults are formatted in text/JSON with\n");
+ printf("\t\tPackage, Die, CPU, and command specific results.\n");
+ printf("\t\t\tFor Set commands, status is 0 for success and rest for failures\n");
+ exit(1);
+}
+
+static void print_version(void)
+{
+ fprintf(outf, "Version %s\n", version_str);
+ fprintf(outf, "Build date %s time %s\n", __DATE__, __TIME__);
+ exit(0);
+}
+
+static void cmdline(int argc, char **argv)
+{
+ int opt;
+ int option_index = 0;
+
+ static struct option long_options[] = {
+ { "cpu", required_argument, 0, 'c' },
+ { "debug", no_argument, 0, 'd' },
+ { "format", required_argument, 0, 'f' },
+ { "help", no_argument, 0, 'h' },
+ { "info", no_argument, 0, 'i' },
+ { "out", required_argument, 0, 'o' },
+ { "version", no_argument, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+
+ progname = argv[0];
+ while ((opt = getopt_long_only(argc, argv, "+c:df:hio:v", long_options,
+ &option_index)) != -1) {
+ switch (opt) {
+ case 'c':
+ parse_cpu_command(optarg);
+ break;
+ case 'd':
+ debug_flag = 1;
+ printf("Debug Mode ON\n");
+ break;
+ case 'f':
+ if (!strncmp(optarg, "json", 4))
+ out_format_json = 1;
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'i':
+ isst_print_platform_information();
+ break;
+ case 'o':
+ if (outf)
+ fclose(outf);
+ outf = fopen_or_exit(optarg, "w");
+ break;
+ case 'v':
+ print_version();
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "Must run as root\n");
+ exit(0);
+ }
+
+ if (optind > (argc - 2)) {
+ fprintf(stderr, "Domain name and|or command not specified\n");
+ exit(0);
+ }
+ update_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();
+ isst_fill_platform_info();
+ if (isst_platform_info.api_version > supported_api_ver) {
+ printf("Incompatible API versions; Upgrade of tool is required\n");
+ exit(0);
+ }
+
+ process_command(argc, argv);
+}
+
+int main(int argc, char **argv)
+{
+ outf = stderr;
+ cmdline(argc, argv);
+ return 0;
+}
diff --git a/tools/power/x86/intel_speed_select/isst_core.c b/tools/power/x86/intel_speed_select/isst_core.c
new file mode 100644
index 000000000000..8de4ac39a008
--- /dev/null
+++ b/tools/power/x86/intel_speed_select/isst_core.c
@@ -0,0 +1,721 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include "isst.h"
+
+int isst_get_ctdp_levels(int cpu, struct isst_pkg_ctdp *pkg_dev)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_LEVELS_INFO, 0, 0, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_GET_LEVELS_INFO resp:%x\n", cpu, resp);
+
+ pkg_dev->version = resp & 0xff;
+ pkg_dev->levels = (resp >> 8) & 0xff;
+ pkg_dev->current_level = (resp >> 16) & 0xff;
+ pkg_dev->locked = !!(resp & BIT(24));
+ pkg_dev->enabled = !!(resp & BIT(31));
+
+ return 0;
+}
+
+int isst_get_ctdp_control(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_TDP_CONTROL, 0,
+ config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->fact_support = resp & BIT(0);
+ ctdp_level->pbf_support = !!(resp & BIT(1));
+ ctdp_level->fact_enabled = !!(resp & BIT(16));
+ ctdp_level->pbf_enabled = !!(resp & BIT(17));
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_TDP_CONTROL resp:%x fact_support:%d pbf_support: %d fact_enabled:%d pbf_enabled:%d\n",
+ cpu, resp, ctdp_level->fact_support, ctdp_level->pbf_support,
+ ctdp_level->fact_enabled, ctdp_level->pbf_enabled);
+
+ return 0;
+}
+
+int isst_get_tdp_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_TDP_INFO,
+ 0, config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->pkg_tdp = resp & GENMASK(14, 0);
+ ctdp_level->tdp_ratio = (resp & GENMASK(23, 16)) >> 16;
+
+ debug_printf(
+ "cpu:%d ctdp:%d CONFIG_TDP_GET_TDP_INFO resp:%x tdp_ratio:%d pkg_tdp:%d\n",
+ cpu, config_index, resp, ctdp_level->tdp_ratio,
+ ctdp_level->pkg_tdp);
+ return 0;
+}
+
+int isst_get_pwr_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_PWR_INFO,
+ 0, config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->pkg_max_power = resp & GENMASK(14, 0);
+ ctdp_level->pkg_min_power = (resp & GENMASK(30, 16)) >> 16;
+
+ debug_printf(
+ "cpu:%d ctdp:%d CONFIG_TDP_GET_PWR_INFO resp:%x pkg_max_power:%d pkg_min_power:%d\n",
+ cpu, config_index, resp, ctdp_level->pkg_max_power,
+ ctdp_level->pkg_min_power);
+
+ return 0;
+}
+
+int isst_get_tjmax_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_TJMAX_INFO,
+ 0, config_index, &resp);
+ if (ret)
+ return ret;
+
+ ctdp_level->t_proc_hot = resp & GENMASK(7, 0);
+
+ debug_printf(
+ "cpu:%d ctdp:%d CONFIG_TDP_GET_TJMAX_INFO resp:%x t_proc_hot:%d\n",
+ cpu, config_index, resp, ctdp_level->t_proc_hot);
+
+ return 0;
+}
+
+int isst_get_coremask_info(int cpu, int config_index,
+ struct isst_pkg_ctdp_level_info *ctdp_level)
+{
+ unsigned int resp;
+ int i, ret;
+
+ ctdp_level->cpu_count = 0;
+ for (i = 0; i < 2; ++i) {
+ unsigned long long mask;
+ int cpu_count = 0;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_CORE_MASK, 0,
+ (i << 8) | config_index, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d ctdp:%d mask:%d CONFIG_TDP_GET_CORE_MASK resp:%x\n",
+ cpu, config_index, i, resp);
+
+ mask = (unsigned long long)resp << (32 * i);
+ set_cpu_mask_from_punit_coremask(cpu, mask,
+ ctdp_level->core_cpumask_size,
+ ctdp_level->core_cpumask,
+ &cpu_count);
+ ctdp_level->cpu_count += cpu_count;
+ debug_printf("cpu:%d ctdp:%d mask:%d cpu count:%d\n", cpu,
+ config_index, i, ctdp_level->cpu_count);
+ }
+
+ return 0;
+}
+
+int isst_get_get_trl(int cpu, int level, int avx_level, int *trl)
+{
+ unsigned int req, resp;
+ int ret;
+
+ req = level | (avx_level << 16);
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_TURBO_LIMIT_RATIOS req:%x resp:%x\n",
+ cpu, req, resp);
+
+ trl[0] = resp & GENMASK(7, 0);
+ trl[1] = (resp & GENMASK(15, 8)) >> 8;
+ trl[2] = (resp & GENMASK(23, 16)) >> 16;
+ trl[3] = (resp & GENMASK(31, 24)) >> 24;
+
+ req = level | BIT(8) | (avx_level << 16);
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_TURBO_LIMIT_RATIOS, 0, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_GET_TURBO_LIMIT req:%x resp:%x\n", cpu,
+ req, resp);
+
+ trl[4] = resp & GENMASK(7, 0);
+ trl[5] = (resp & GENMASK(15, 8)) >> 8;
+ trl[6] = (resp & GENMASK(23, 16)) >> 16;
+ trl[7] = (resp & GENMASK(31, 24)) >> 24;
+
+ return 0;
+}
+
+int isst_set_tdp_level_msr(int cpu, int tdp_level)
+{
+ int ret;
+
+ debug_printf("cpu: tdp_level via MSR %d\n", cpu, tdp_level);
+
+ if (isst_get_config_tdp_lock_status(cpu)) {
+ debug_printf("cpu: tdp_locked %d\n", cpu);
+ return -1;
+ }
+
+ if (tdp_level > 2)
+ return -1; /* invalid value */
+
+ ret = isst_send_msr_command(cpu, 0x64b, 1,
+ (unsigned long long *)&tdp_level);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu: tdp_level via MSR successful %d\n", cpu, tdp_level);
+
+ return 0;
+}
+
+int isst_set_tdp_level(int cpu, int tdp_level)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP, CONFIG_TDP_SET_LEVEL, 0,
+ tdp_level, &resp);
+ if (ret)
+ return isst_set_tdp_level_msr(cpu, tdp_level);
+
+ return 0;
+}
+
+int isst_get_pbf_info(int cpu, int level, struct isst_pbf_info *pbf_info)
+{
+ 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) {
+ unsigned long long mask;
+ int count;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_PBF_GET_CORE_MASK_INFO,
+ 0, (i << 8) | level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_PBF_GET_CORE_MASK_INFO resp:%x\n",
+ cpu, resp);
+
+ mask = (unsigned long long)resp << (32 * i);
+ set_cpu_mask_from_punit_coremask(cpu, mask,
+ pbf_info->core_cpumask_size,
+ pbf_info->core_cpumask,
+ &count);
+ }
+
+ req = level;
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO, 0, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_PBF_GET_P1HI_P1LO_INFO resp:%x\n", cpu,
+ resp);
+
+ pbf_info->p1_low = resp & 0xff;
+ pbf_info->p1_high = (resp & GENMASK(15, 8)) >> 8;
+
+ req = level;
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TDP_INFO, 0, req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TDP_INFO resp:%x\n", cpu, resp);
+
+ pbf_info->tdp = resp & 0xffff;
+
+ req = level;
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP, CONFIG_TDP_PBF_GET_TJ_MAX_INFO, 0, req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_PBF_GET_TJ_MAX_INFO resp:%x\n", cpu,
+ resp);
+ pbf_info->t_control = (resp >> 8) & 0xff;
+ pbf_info->t_prochot = resp & 0xff;
+
+ return 0;
+}
+
+void isst_get_pbf_info_complete(struct isst_pbf_info *pbf_info)
+{
+ free_cpu_set(pbf_info->core_cpumask);
+}
+
+int isst_set_pbf_fact_status(int cpu, int pbf, int enable)
+{
+ struct isst_pkg_ctdp pkg_dev;
+ struct isst_pkg_ctdp_level_info ctdp_level;
+ int current_level;
+ unsigned int req = 0, resp;
+ int ret;
+
+ ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+ if (ret)
+ return ret;
+
+ current_level = pkg_dev.current_level;
+
+ ret = isst_get_ctdp_control(cpu, current_level, &ctdp_level);
+ if (ret)
+ return ret;
+
+ if (pbf) {
+ if (ctdp_level.fact_enabled)
+ req = BIT(16);
+
+ if (enable)
+ req |= BIT(17);
+ else
+ req &= ~BIT(17);
+ } else {
+ if (ctdp_level.pbf_enabled)
+ req = BIT(17);
+
+ if (enable)
+ req |= BIT(16);
+ else
+ req &= ~BIT(16);
+ }
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_SET_TDP_CONTROL, 0, req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_SET_TDP_CONTROL pbf/fact:%d req:%x\n",
+ cpu, pbf, req);
+
+ return 0;
+}
+
+int isst_get_fact_bucket_info(int cpu, int level,
+ struct isst_fact_bucket_info *bucket_info)
+{
+ unsigned int resp;
+ int i, k, ret;
+
+ for (i = 0; i < 2; ++i) {
+ int j;
+
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES, 0,
+ (i << 8) | level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_NUMCORES index:%d level:%d resp:%x\n",
+ cpu, i, level, resp);
+
+ for (j = 0; j < 4; ++j) {
+ bucket_info[j + (i * 4)].high_priority_cores_count =
+ (resp >> (j * 8)) & 0xff;
+ }
+ }
+
+ for (k = 0; k < 3; ++k) {
+ for (i = 0; i < 2; ++i) {
+ int j;
+
+ ret = isst_send_mbox_command(
+ cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS, 0,
+ (k << 16) | (i << 8) | level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf(
+ "cpu:%d CONFIG_TDP_GET_FACT_HP_TURBO_LIMIT_RATIOS index:%d level:%d avx:%d resp:%x\n",
+ cpu, i, level, k, resp);
+
+ for (j = 0; j < 4; ++j) {
+ switch (k) {
+ case 0:
+ bucket_info[j + (i * 4)].sse_trl =
+ (resp >> (j * 8)) & 0xff;
+ break;
+ case 1:
+ bucket_info[j + (i * 4)].avx_trl =
+ (resp >> (j * 8)) & 0xff;
+ break;
+ case 2:
+ bucket_info[j + (i * 4)].avx512_trl =
+ (resp >> (j * 8)) & 0xff;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int isst_get_fact_info(int cpu, int level, struct isst_fact_info *fact_info)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_TDP,
+ CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO, 0,
+ level, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CONFIG_TDP_GET_FACT_LP_CLIPPING_RATIO resp:%x\n",
+ cpu, resp);
+
+ fact_info->lp_clipping_ratio_license_sse = resp & 0xff;
+ fact_info->lp_clipping_ratio_license_avx2 = (resp >> 8) & 0xff;
+ fact_info->lp_clipping_ratio_license_avx512 = (resp >> 16) & 0xff;
+
+ ret = isst_get_fact_bucket_info(cpu, level, fact_info->bucket_info);
+
+ return ret;
+}
+
+int isst_set_trl(int cpu, unsigned long long trl)
+{
+ int ret;
+
+ if (!trl)
+ trl = 0xFFFFFFFFFFFFFFFFULL;
+
+ ret = isst_send_msr_command(cpu, 0x1AD, 1, &trl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int isst_set_trl_from_current_tdp(int cpu, unsigned long long trl)
+{
+ unsigned long long msr_trl;
+ int ret;
+
+ if (trl) {
+ msr_trl = trl;
+ } else {
+ struct isst_pkg_ctdp pkg_dev;
+ int trl[8];
+ int i;
+
+ ret = isst_get_ctdp_levels(cpu, &pkg_dev);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, pkg_dev.current_level, 0, trl);
+ if (ret)
+ return ret;
+
+ msr_trl = 0;
+ for (i = 0; i < 8; ++i) {
+ unsigned long long _trl = trl[i];
+
+ msr_trl |= (_trl << (i * 8));
+ }
+ }
+ ret = isst_send_msr_command(cpu, 0x1AD, 1, &msr_trl);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* Return 1 if locked */
+int isst_get_config_tdp_lock_status(int cpu)
+{
+ unsigned long long tdp_control = 0;
+ int ret;
+
+ ret = isst_send_msr_command(cpu, 0x64b, 0, &tdp_control);
+ if (ret)
+ return ret;
+
+ ret = !!(tdp_control & BIT(31));
+
+ return ret;
+}
+
+void isst_get_process_ctdp_complete(int cpu, struct isst_pkg_ctdp *pkg_dev)
+{
+ int i;
+
+ if (!pkg_dev->processed)
+ return;
+
+ for (i = 0; i < pkg_dev->levels; ++i) {
+ struct isst_pkg_ctdp_level_info *ctdp_level;
+
+ ctdp_level = &pkg_dev->ctdp_level[i];
+ if (ctdp_level->pbf_support)
+ free_cpu_set(ctdp_level->pbf_info.core_cpumask);
+ free_cpu_set(ctdp_level->core_cpumask);
+ }
+}
+
+int isst_get_process_ctdp(int cpu, int tdp_level, struct isst_pkg_ctdp *pkg_dev)
+{
+ int i, ret;
+
+ if (pkg_dev->processed)
+ return 0;
+
+ ret = isst_get_ctdp_levels(cpu, pkg_dev);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu: %d ctdp enable:%d current level: %d levels:%d\n",
+ cpu, pkg_dev->enabled, pkg_dev->current_level,
+ pkg_dev->levels);
+
+ for (i = 0; i <= pkg_dev->levels; ++i) {
+ struct isst_pkg_ctdp_level_info *ctdp_level;
+
+ if (tdp_level != 0xff && i != tdp_level)
+ continue;
+
+ debug_printf("cpu:%d Get Information for TDP level:%d\n", cpu,
+ 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);
+ ctdp_level->die_id = get_physical_die_id(cpu);
+
+ ret = isst_get_ctdp_control(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_tdp_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_pwr_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_tjmax_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ctdp_level->core_cpumask_size =
+ alloc_cpu_set(&ctdp_level->core_cpumask);
+ ret = isst_get_coremask_info(cpu, i, ctdp_level);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, i, 0,
+ ctdp_level->trl_sse_active_cores);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, i, 1,
+ ctdp_level->trl_avx_active_cores);
+ if (ret)
+ return ret;
+
+ ret = isst_get_get_trl(cpu, i, 2,
+ ctdp_level->trl_avx_512_active_cores);
+ 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;
+ }
+ }
+
+ pkg_dev->processed = 1;
+
+ return 0;
+}
+
+int isst_pm_qos_config(int cpu, int enable_clos, int priority_type)
+{
+ unsigned int req, resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG, 0, 0,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PM_QOS_CONFIG resp:%x\n", cpu, resp);
+
+ req = resp;
+
+ if (enable_clos)
+ req = req | BIT(1);
+ else
+ req = req & ~BIT(1);
+
+ if (priority_type)
+ req = req | BIT(2);
+ else
+ req = req & ~BIT(2);
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_QOS_CONFIG,
+ BIT(MBOX_CMD_WRITE_BIT), req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PM_QOS_CONFIG priority type:%d req:%x\n", cpu,
+ priority_type, req);
+
+ return 0;
+}
+
+int isst_pm_get_clos(int cpu, int clos, struct isst_clos_config *clos_config)
+{
+ unsigned int resp;
+ int ret;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_CLOS, clos, 0,
+ &resp);
+ if (ret)
+ return ret;
+
+ clos_config->pkg_id = get_physical_package_id(cpu);
+ clos_config->die_id = get_physical_die_id(cpu);
+
+ clos_config->epp = resp & 0x0f;
+ clos_config->clos_prop_prio = (resp >> 4) & 0x0f;
+ clos_config->clos_min = (resp >> 8) & 0xff;
+ clos_config->clos_max = (resp >> 16) & 0xff;
+ clos_config->clos_desired = (resp >> 24) & 0xff;
+
+ return 0;
+}
+
+int isst_set_clos(int cpu, int clos, struct isst_clos_config *clos_config)
+{
+ unsigned int req, resp;
+ unsigned int param;
+ int ret;
+
+ req = clos_config->epp & 0x0f;
+ req |= (clos_config->clos_prop_prio & 0x0f) << 4;
+ req |= (clos_config->clos_min & 0xff) << 8;
+ req |= (clos_config->clos_max & 0xff) << 16;
+ req |= (clos_config->clos_desired & 0xff) << 24;
+
+ param = BIT(MBOX_CMD_WRITE_BIT) | clos;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PM_CLOS, param, req,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PM_CLOS param:%x req:%x\n", cpu, param, req);
+
+ return 0;
+}
+
+int isst_clos_get_assoc_status(int cpu, int *clos_id)
+{
+ unsigned int resp;
+ unsigned int param;
+ int core_id, ret;
+
+ core_id = find_phy_core_num(cpu);
+ param = core_id;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param, 0,
+ &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x resp:%x\n", cpu, param,
+ resp);
+ *clos_id = (resp >> 16) & 0x03;
+
+ return 0;
+}
+
+int isst_clos_associate(int cpu, int clos_id)
+{
+ unsigned int req, resp;
+ unsigned int param;
+ int core_id, ret;
+
+ req = (clos_id & 0x03) << 16;
+ core_id = find_phy_core_num(cpu);
+ param = BIT(MBOX_CMD_WRITE_BIT) | core_id;
+
+ ret = isst_send_mbox_command(cpu, CONFIG_CLOS, CLOS_PQR_ASSOC, param,
+ req, &resp);
+ if (ret)
+ return ret;
+
+ debug_printf("cpu:%d CLOS_PQR_ASSOC param:%x req:%x\n", cpu, param,
+ req);
+
+ return 0;
+}
diff --git a/tools/power/x86/intel_speed_select/isst_display.c b/tools/power/x86/intel_speed_select/isst_display.c
new file mode 100644
index 000000000000..f368b8323742
--- /dev/null
+++ b/tools/power/x86/intel_speed_select/isst_display.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel dynamic_speed_select -- Enumerate and control features
+ * Copyright (c) 2019 Intel Corporation.
+ */
+
+#include "isst.h"
+
+#define DISP_FREQ_MULTIPLIER 100000
+
+static void printcpumask(int str_len, char *str, int mask_size,
+ cpu_set_t *cpu_mask)
+{
+ int i, max_cpus = get_topo_max_cpus();
+ unsigned int *mask;
+ int size, index, curr_index;
+
+ size = max_cpus / (sizeof(unsigned int) * 8);
+ if (max_cpus % (sizeof(unsigned int) * 8))
+ size++;
+
+ mask = calloc(size, sizeof(unsigned int));
+ if (!mask)
+ return;
+
+ for (i = 0; i < max_cpus; ++i) {
+ int mask_index, bit_index;
+
+ if (!CPU_ISSET_S(i, mask_size, cpu_mask))
+ continue;
+
+ mask_index = i / (sizeof(unsigned int) * 8);
+ bit_index = i % (sizeof(unsigned int) * 8);
+ mask[mask_index] |= BIT(bit_index);
+ }
+
+ curr_index = 0;
+ for (i = size - 1; i >= 0; --i) {
+ index = snprintf(&str[curr_index], str_len - curr_index, "%08x",
+ mask[i]);
+ curr_index += index;
+ if (i) {
+ strncat(&str[curr_index], ",", str_len - curr_index);
+ curr_index++;
+ }
+ }
+
+ free(mask);
+}
+
+static void format_and_print_txt(FILE *outf, int level, char *header,
+ char *value)
+{
+ char *spaces = " ";
+ static char delimiters[256];
+ int i, j = 0;
+
+ if (!level)
+ return;
+
+ if (level == 1) {
+ strcpy(delimiters, " ");
+ } else {
+ for (i = 0; i < level - 1; ++i)
+ j += snprintf(&delimiters[j], sizeof(delimiters) - j,
+ "%s", spaces);
+ }
+
+ if (header && value) {
+ fprintf(outf, "%s", delimiters);
+ fprintf(outf, "%s:%s\n", header, value);
+ } else if (header) {
+ fprintf(outf, "%s", delimiters);
+ fprintf(outf, "%s\n", header);
+ }
+}
+
+static int last_level;
+static void format_and_print(FILE *outf, int level, char *header, char *value)
+{
+ char *spaces = " ";
+ static char delimiters[256];
+ int i;
+
+ if (!out_format_is_json()) {
+ format_and_print_txt(outf, level, header, value);
+ return;
+ }
+
+ if (level == 0) {
+ if (header)
+ fprintf(outf, "{");
+ else
+ fprintf(outf, "\n}\n");
+
+ } else {
+ int j = 0;
+
+ for (i = 0; i < level; ++i)
+ j += snprintf(&delimiters[j], sizeof(delimiters) - j,
+ "%s", spaces);
+
+ if (last_level == level)
+ fprintf(outf, ",\n");
+
+ if (value) {
+ if (last_level != level)
+ fprintf(outf, "\n");
+
+ fprintf(outf, "%s\"%s\": ", delimiters, header);
+ fprintf(outf, "\"%s\"", value);
+ } else {
+ for (i = last_level - 1; i >= level; --i) {
+ int k = 0;
+
+ for (j = i; j > 0; --j)
+ k += snprintf(&delimiters[k],
+ sizeof(delimiters) - k,
+ "%s", spaces);
+ if (i == level && header)
+ fprintf(outf, "\n%s},", delimiters);
+ else
+ fprintf(outf, "\n%s}", delimiters);
+ }
+ if (abs(last_level - level) < 3)
+ fprintf(outf, "\n");
+ if (header)
+ fprintf(outf, "%s\"%s\": {", delimiters,
+ header);
+ }
+ }
+
+ last_level = level;
+}
+
+static void print_packag_info(int cpu, FILE *outf)
+{
+ char header[256];
+
+ snprintf(header, sizeof(header), "package-%d",
+ get_physical_package_id(cpu));
+ format_and_print(outf, 1, header, NULL);
+ snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
+ format_and_print(outf, 2, header, NULL);
+ snprintf(header, sizeof(header), "cpu-%d", cpu);
+ format_and_print(outf, 3, header, NULL);
+}
+
+static void _isst_pbf_display_information(int cpu, FILE *outf, int level,
+ struct isst_pbf_info *pbf_info,
+ int disp_level)
+{
+ char header[256];
+ char value[256];
+
+ snprintf(header, sizeof(header), "speed-select-base-freq");
+ format_and_print(outf, disp_level, header, NULL);
+
+ snprintf(header, sizeof(header), "high-priority-base-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ pbf_info->p1_high * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ snprintf(header, sizeof(header), "high-priority-cpu-mask");
+ printcpumask(sizeof(value), value, pbf_info->core_cpumask_size,
+ pbf_info->core_cpumask);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ snprintf(header, sizeof(header), "low-priority-base-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ pbf_info->p1_low * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, disp_level + 1, header, value);
+
+ 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);
+
+ snprintf(header, sizeof(header), "thermal-design-power(W)");
+ snprintf(value, sizeof(value), "%d", pbf_info->tdp);
+ format_and_print(outf, disp_level + 1, header, value);
+}
+
+static void _isst_fact_display_information(int cpu, FILE *outf, int level,
+ int fact_bucket, int fact_avx,
+ struct isst_fact_info *fact_info,
+ int base_level)
+{
+ struct isst_fact_bucket_info *bucket_info = fact_info->bucket_info;
+ char header[256];
+ char value[256];
+ int j;
+
+ snprintf(header, sizeof(header), "speed-select-turbo-freq");
+ format_and_print(outf, base_level, header, NULL);
+ for (j = 0; j < ISST_FACT_MAX_BUCKETS; ++j) {
+ if (fact_bucket != 0xff && fact_bucket != j)
+ continue;
+
+ if (!bucket_info[j].high_priority_cores_count)
+ break;
+
+ snprintf(header, sizeof(header), "bucket-%d", j);
+ format_and_print(outf, base_level + 1, header, NULL);
+
+ snprintf(header, sizeof(header), "high-priority-cores-count");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].high_priority_cores_count);
+ format_and_print(outf, base_level + 2, header, value);
+
+ if (fact_avx & 0x01) {
+ snprintf(header, sizeof(header),
+ "high-priority-max-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].sse_trl * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ }
+
+ if (fact_avx & 0x02) {
+ snprintf(header, sizeof(header),
+ "high-priority-max-avx2-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].avx_trl * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ }
+
+ if (fact_avx & 0x04) {
+ snprintf(header, sizeof(header),
+ "high-priority-max-avx512-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ bucket_info[j].avx512_trl *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ }
+ }
+ snprintf(header, sizeof(header),
+ "speed-select-turbo-freq-clip-frequencies");
+ format_and_print(outf, base_level + 1, header, NULL);
+ snprintf(header, sizeof(header), "low-priority-max-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ fact_info->lp_clipping_ratio_license_sse *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ snprintf(header, sizeof(header),
+ "low-priority-max-avx2-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ fact_info->lp_clipping_ratio_license_avx2 *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+ snprintf(header, sizeof(header),
+ "low-priority-max-avx512-frequency(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ fact_info->lp_clipping_ratio_license_avx512 *
+ DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 2, header, value);
+}
+
+void isst_ctdp_display_information(int cpu, FILE *outf, int tdp_level,
+ struct isst_pkg_ctdp *pkg_dev)
+{
+ char header[256];
+ char value[256];
+ int i, base_level = 1;
+
+ print_packag_info(cpu, outf);
+
+ for (i = 0; i <= pkg_dev->levels; ++i) {
+ struct isst_pkg_ctdp_level_info *ctdp_level;
+ int j;
+
+ ctdp_level = &pkg_dev->ctdp_level[i];
+ if (!ctdp_level->processed)
+ continue;
+
+ snprintf(header, sizeof(header), "perf-profile-level-%d",
+ ctdp_level->level);
+ format_and_print(outf, base_level + 3, header, NULL);
+
+ snprintf(header, sizeof(header), "cpu-count");
+ j = get_cpu_count(get_physical_die_id(cpu),
+ get_physical_die_id(cpu));
+ 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), "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(KHz)");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->tdp_ratio * DISP_FREQ_MULTIPLIER);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header),
+ "speed-select-turbo-freq-support");
+ snprintf(value, sizeof(value), "%d", ctdp_level->fact_support);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header),
+ "speed-select-base-freq-support");
+ snprintf(value, sizeof(value), "%d", ctdp_level->pbf_support);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header),
+ "speed-select-base-freq-enabled");
+ snprintf(value, sizeof(value), "%d", ctdp_level->pbf_enabled);
+ format_and_print(outf, base_level + 4, header, value);
+
+ snprintf(header, sizeof(header),
+ "speed-select-turbo-freq-enabled");
+ snprintf(value, sizeof(value), "%d", ctdp_level->fact_enabled);
+ 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);
+
+ 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);
+ 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), "%d", j);
+ format_and_print(outf, base_level + 6, header, value);
+
+ snprintf(header, sizeof(header), "turbo-ratio");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->trl_sse_active_cores[j]);
+ 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), "%d", j);
+ format_and_print(outf, base_level + 6, header, value);
+
+ snprintf(header, sizeof(header), "turbo-ratio");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->trl_avx_active_cores[j]);
+ 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);
+
+ snprintf(header, sizeof(header), "core-count");
+ snprintf(value, sizeof(value), "%d", j);
+ format_and_print(outf, base_level + 6, header, value);
+
+ snprintf(header, sizeof(header), "turbo-ratio");
+ snprintf(value, sizeof(value), "%d",
+ ctdp_level->trl_avx_512_active_cores[j]);
+ 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,
+ base_level + 4);
+ if (ctdp_level->fact_support)
+ _isst_fact_display_information(cpu, outf, i, 0xff, 0xff,
+ &ctdp_level->fact_info,
+ base_level + 4);
+ }
+
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_ctdp_display_information_start(FILE *outf)
+{
+ last_level = 0;
+ format_and_print(outf, 0, "start", NULL);
+}
+
+void isst_ctdp_display_information_end(FILE *outf)
+{
+ format_and_print(outf, 0, NULL, NULL);
+}
+
+void isst_pbf_display_information(int cpu, FILE *outf, int level,
+ struct isst_pbf_info *pbf_info)
+{
+ print_packag_info(cpu, outf);
+ _isst_pbf_display_information(cpu, outf, level, pbf_info, 4);
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_fact_display_information(int cpu, FILE *outf, int level,
+ int fact_bucket, int fact_avx,
+ struct isst_fact_info *fact_info)
+{
+ print_packag_info(cpu, outf);
+ _isst_fact_display_information(cpu, outf, level, fact_bucket, fact_avx,
+ fact_info, 4);
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_clos_display_information(int cpu, FILE *outf, int clos,
+ struct isst_clos_config *clos_config)
+{
+ char header[256];
+ char value[256];
+
+ snprintf(header, sizeof(header), "package-%d",
+ get_physical_package_id(cpu));
+ format_and_print(outf, 1, header, NULL);
+ snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
+ format_and_print(outf, 2, header, NULL);
+ snprintf(header, sizeof(header), "cpu-%d", cpu);
+ format_and_print(outf, 3, header, NULL);
+
+ snprintf(header, sizeof(header), "core-power");
+ format_and_print(outf, 4, header, NULL);
+
+ snprintf(header, sizeof(header), "clos");
+ snprintf(value, sizeof(value), "%d", clos);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "epp");
+ snprintf(value, sizeof(value), "%d", clos_config->epp);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-proportional-priority");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_prop_prio);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-min");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_min);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-max");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_max);
+ format_and_print(outf, 5, header, value);
+
+ snprintf(header, sizeof(header), "clos-desired");
+ snprintf(value, sizeof(value), "%d", clos_config->clos_desired);
+ format_and_print(outf, 5, header, value);
+
+ format_and_print(outf, 1, NULL, NULL);
+}
+
+void isst_display_result(int cpu, FILE *outf, char *feature, char *cmd,
+ int result)
+{
+ char header[256];
+ char value[256];
+
+ snprintf(header, sizeof(header), "package-%d",
+ get_physical_package_id(cpu));
+ format_and_print(outf, 1, header, NULL);
+ snprintf(header, sizeof(header), "die-%d", get_physical_die_id(cpu));
+ format_and_print(outf, 2, header, NULL);
+ snprintf(header, sizeof(header), "cpu-%d", cpu);
+ format_and_print(outf, 3, header, NULL);
+ snprintf(header, sizeof(header), "%s", feature);
+ format_and_print(outf, 4, header, NULL);
+ snprintf(header, sizeof(header), "%s", cmd);
+ snprintf(value, sizeof(value), "%d", result);
+ format_and_print(outf, 5, header, value);
+
+ format_and_print(outf, 1, NULL, NULL);
+}
--
2.17.2
^ permalink raw reply related
* [PATCH 08/10] platform/x86: ISST: Add Intel Speed Select PUNIT MSR interface
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
While using new non arhitectural features using PUNIT Mailbox and MMIO
read/write interface, still there is need to operate using MSRs to
control PUNIT. User space could have used user user-space MSR interface for
this, but when user space MSR access is disabled, then it can't. Here only
limited number of MSRs are allowed using this new interface.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../intel_speed_select_if/isst_if_common.c | 59 +++++++++++++++++++
include/uapi/linux/isst_if.h | 32 ++++++++++
2 files changed, 91 insertions(+)
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
index 391fc3f12161..de2fb5292f1c 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -25,6 +25,11 @@
static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
+static int punit_msr_white_list[] = {
+ MSR_TURBO_RATIO_LIMIT,
+ MSR_CONFIG_TDP_CONTROL,
+};
+
struct isst_valid_cmd_ranges {
u16 cmd;
u16 sub_cmd_beg;
@@ -229,6 +234,54 @@ static long isst_if_proc_phyid_req(u8 *cmd_ptr, int *write_only, int resume)
return 0;
}
+static bool match_punit_msr_white_list(int msr)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
+ if (punit_msr_white_list[i] == msr)
+ return true;
+ }
+
+ return false;
+}
+
+static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_msr_cmd *msr_cmd;
+ int ret;
+
+ msr_cmd = (struct isst_if_msr_cmd *)cmd_ptr;
+
+ if (!match_punit_msr_white_list(msr_cmd->msr))
+ return -EINVAL;
+
+ if (msr_cmd->logical_cpu >= nr_cpu_ids)
+ return -EINVAL;
+
+ if (msr_cmd->read_write) {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ ret = wrmsrl_safe_on_cpu(msr_cmd->logical_cpu,
+ msr_cmd->msr,
+ msr_cmd->data);
+ *write_only = 1;
+ } else {
+ u64 data;
+
+ ret = rdmsrl_safe_on_cpu(msr_cmd->logical_cpu,
+ msr_cmd->msr, &data);
+ if (!ret) {
+ msr_cmd->data = data;
+ *write_only = 0;
+ }
+ }
+
+
+ return ret;
+}
+
static long isst_if_exec_multi_cmd(void __user *argp, struct isst_if_cmd_cb *cb)
{
unsigned char __user *ptr;
@@ -309,6 +362,12 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
if (cb->registered)
ret = isst_if_exec_multi_cmd(argp, cb);
break;
+ case ISST_IF_MSR_COMMAND:
+ cmd_cb.cmd_size = sizeof(struct isst_if_msr_cmd);
+ cmd_cb.offset = offsetof(struct isst_if_msr_cmds, msr_cmd);
+ cmd_cb.cmd_callback = isst_if_msr_cmd_req;
+ ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
+ break;
default:
break;
}
diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h
index e4b1c2ec3279..d10b832c58c5 100644
--- a/include/uapi/linux/isst_if.h
+++ b/include/uapi/linux/isst_if.h
@@ -132,9 +132,41 @@ struct isst_if_mbox_cmds {
struct isst_if_mbox_cmd mbox_cmd[1];
};
+/**
+ * struct isst_if_msr_cmd - Structure to define msr command
+ * @read_write: Value 0: Read, 1: Write
+ * @logical_cpu: Logical CPU number
+ * @msr: MSR number
+ * @data: For write operation, data to write, for read
+ * place holder
+ *
+ * Structure to specify MSR command related to PUNIT.
+ */
+struct isst_if_msr_cmd {
+ __u32 read_write; /* Read:0, Write:1 */
+ __u32 logical_cpu;
+ __u64 msr;
+ __u64 data;
+};
+
+/**
+ * struct isst_if_msr_cmds - structure for msr commands
+ * @cmd_count: Number of mailbox commands in msr_cmd[]
+ * @msr_cmd[]: Holds one or more msr commands
+ *
+ * This structure used with ioctl ISST_IF_MSR_COMMAND to send
+ * one or more MSR commands. IOCTL return value indicates number of
+ * commands sent or error number if no commands have been sent.
+ */
+struct isst_if_msr_cmds {
+ __u32 cmd_count;
+ struct isst_if_msr_cmd msr_cmd[1];
+};
+
#define ISST_IF_MAGIC 0xFE
#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
#define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *)
#define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *)
#define ISST_IF_MBOX_COMMAND _IOWR(ISST_IF_MAGIC, 3, struct isst_if_mbox_cmds *)
+#define ISST_IF_MSR_COMMAND _IOWR(ISST_IF_MAGIC, 4, struct isst_if_msr_cmds *)
#endif
--
2.17.2
^ permalink raw reply related
* [PATCH 09/10] platform/x86: ISST: Restore state on resume
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
Commands which causes PUNIT writes, store them and restore them on system
resume. The driver stores all such requests in a hash table and stores the
the latest mailbox request parameters. On resume these commands mail box
commands are executed again. There are only 5 such mail box commands which
will trigger such processing so a very low overhead in store and execute
on resume. Also there is no order requirement for mail box commands for
these write/set commands. There is one MSR request for changing turbo
ratio limits, this also stored and get restored on resume and cpu online.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../intel_speed_select_if/isst_if_common.c | 154 ++++++++++++++++++
.../intel_speed_select_if/isst_if_common.h | 3 +
.../intel_speed_select_if/isst_if_mbox_msr.c | 38 ++++-
.../intel_speed_select_if/isst_if_mbox_pci.c | 15 ++
.../x86/intel_speed_select_if/isst_if_mmio.c | 49 ++++++
5 files changed, 258 insertions(+), 1 deletion(-)
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
index de2fb5292f1c..68d75391db57 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -10,6 +10,7 @@
#include <linux/cpufeature.h>
#include <linux/cpuhotplug.h>
#include <linux/fs.h>
+#include <linux/hashtable.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -58,6 +59,151 @@ static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
{0x7F, 0x08, 0x00},
};
+struct isst_cmd {
+ struct hlist_node hnode;
+ u64 data;
+ u32 cmd;
+ int cpu;
+ int mbox_cmd_type;
+ u32 param;
+};
+
+static DECLARE_HASHTABLE(isst_hash, 8);
+static DEFINE_MUTEX(isst_hash_lock);
+
+static int isst_store_new_cmd(int cmd, u32 cpu, int mbox_cmd_type, u32 param,
+ u32 data)
+{
+ struct isst_cmd *sst_cmd;
+
+ sst_cmd = kmalloc(sizeof(*sst_cmd), GFP_KERNEL);
+ if (!sst_cmd)
+ return -ENOMEM;
+
+ sst_cmd->cpu = cpu;
+ sst_cmd->cmd = cmd;
+ sst_cmd->mbox_cmd_type = mbox_cmd_type;
+ sst_cmd->param = param;
+ sst_cmd->data = data;
+
+ hash_add(isst_hash, &sst_cmd->hnode, sst_cmd->cmd);
+
+ return 0;
+}
+
+static void isst_delete_hash(void)
+{
+ struct isst_cmd *sst_cmd;
+ struct hlist_node *tmp;
+ int i;
+
+ hash_for_each_safe(isst_hash, i, tmp, sst_cmd, hnode) {
+ hash_del(&sst_cmd->hnode);
+ kfree(sst_cmd);
+ }
+}
+
+/**
+ * isst_store_cmd() - Store command to a hash table
+ * @cmd: Mailbox command.
+ * @sub_cmd: Mailbox sub-command or MSR id.
+ * @mbox_cmd_type: Mailbox or MSR command.
+ * @param: Mailbox parameter.
+ * @data: Mailbox request data or MSR data.
+ *
+ * Stores the command to a hash table if there is no such command already
+ * stored. If already stored update the latest parameter and data for the
+ * command.
+ *
+ * Return: Return result of store to hash table, 0 for success, others for
+ * failure.
+ */
+int isst_store_cmd(int cmd, int sub_cmd, u32 cpu, int mbox_cmd_type,
+ u32 param, u64 data)
+{
+ struct isst_cmd *sst_cmd;
+ int full_cmd, ret;
+
+ full_cmd = (cmd & GENMASK_ULL(15, 0)) << 16;
+ full_cmd |= (sub_cmd & GENMASK_ULL(15, 0));
+ mutex_lock(&isst_hash_lock);
+ hash_for_each_possible(isst_hash, sst_cmd, hnode, full_cmd) {
+ if (sst_cmd->cmd == full_cmd && sst_cmd->cpu == cpu &&
+ sst_cmd->mbox_cmd_type == mbox_cmd_type) {
+ sst_cmd->param = param;
+ sst_cmd->data = data;
+ mutex_unlock(&isst_hash_lock);
+ return 0;
+ }
+ }
+
+ ret = isst_store_new_cmd(full_cmd, cpu, mbox_cmd_type, param, data);
+ mutex_unlock(&isst_hash_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(isst_store_cmd);
+
+static void isst_mbox_resume_command(struct isst_if_cmd_cb *cb,
+ struct isst_cmd *sst_cmd)
+{
+ struct isst_if_mbox_cmd mbox_cmd;
+ int wr_only;
+
+ mbox_cmd.command = (sst_cmd->cmd & GENMASK_ULL(31, 16)) >> 16;
+ mbox_cmd.sub_command = sst_cmd->cmd & GENMASK_ULL(15, 0);
+ mbox_cmd.parameter = sst_cmd->param;
+ mbox_cmd.req_data = sst_cmd->data;
+ mbox_cmd.logical_cpu = sst_cmd->cpu;
+ (cb->cmd_callback)((u8 *)&mbox_cmd, &wr_only, 1);
+}
+
+/**
+ * isst_resume_common() - Process Resume request
+ *
+ * On resume replay all mailbox commands and MSRs.
+ *
+ * Return: None.
+ */
+void isst_resume_common(void)
+{
+ struct isst_cmd *sst_cmd;
+ int i;
+
+ hash_for_each(isst_hash, i, sst_cmd, hnode) {
+ struct isst_if_cmd_cb *cb;
+
+ if (sst_cmd->mbox_cmd_type) {
+ cb = &punit_callbacks[ISST_IF_DEV_MBOX];
+ if (cb->registered)
+ isst_mbox_resume_command(cb, sst_cmd);
+ } else {
+ wrmsrl_safe_on_cpu(sst_cmd->cpu, sst_cmd->cmd,
+ sst_cmd->data);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(isst_resume_common);
+
+static void isst_restore_msr_local(int cpu)
+{
+ struct isst_cmd *sst_cmd;
+ int i;
+
+ mutex_lock(&isst_hash_lock);
+ for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) {
+ if (!punit_msr_white_list[i])
+ break;
+
+ hash_for_each_possible(isst_hash, sst_cmd, hnode,
+ punit_msr_white_list[i]) {
+ if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu)
+ wrmsrl_safe(sst_cmd->cmd, sst_cmd->data);
+ }
+ }
+ mutex_unlock(&isst_hash_lock);
+}
+
/**
* isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
* @cmd: Pointer to the command structure to verify.
@@ -185,6 +331,8 @@ static int isst_if_cpu_online(unsigned int cpu)
}
isst_cpu_info[cpu].punit_cpu_id = data;
+ isst_restore_msr_local(cpu);
+
return 0;
}
@@ -267,6 +415,10 @@ static long isst_if_msr_cmd_req(u8 *cmd_ptr, int *write_only, int resume)
msr_cmd->msr,
msr_cmd->data);
*write_only = 1;
+ if (!ret && !resume)
+ ret = isst_store_cmd(0, msr_cmd->msr,
+ msr_cmd->logical_cpu,
+ 0, 0, msr_cmd->data);
} else {
u64 data;
@@ -507,6 +659,8 @@ void isst_if_cdev_unregister(int device_type)
mutex_lock(&punit_misc_dev_lock);
misc_usage_count--;
punit_callbacks[device_type].registered = 0;
+ if (device_type == ISST_IF_DEV_MBOX)
+ isst_delete_hash();
if (!misc_usage_count && !misc_device_ret) {
misc_deregister(&isst_if_char_driver);
isst_if_cpu_info_exit();
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
index 7c0f71221da7..1409a5bb5582 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
@@ -63,4 +63,7 @@ void isst_if_cdev_unregister(int type);
struct pci_dev *isst_if_get_pci_dev(int cpu, int bus, int dev, int fn);
bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *mbox_cmd);
bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd);
+int isst_store_cmd(int cmd, int sub_command, u32 cpu, int mbox_cmd,
+ u32 param, u64 data);
+void isst_resume_common(void);
#endif
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
index a949ec436c73..afd0b23be5ba 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_msr.c
@@ -12,6 +12,7 @@
#include <linux/pci.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
+#include <linux/suspend.h>
#include <linux/topology.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
@@ -128,11 +129,37 @@ static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
if (ret)
return ret;
+ if (!action.err && !resume && isst_if_mbox_cmd_set_req(action.mbox_cmd))
+ action.err = isst_store_cmd(action.mbox_cmd->command,
+ action.mbox_cmd->sub_command,
+ action.mbox_cmd->logical_cpu, 1,
+ action.mbox_cmd->parameter,
+ action.mbox_cmd->req_data);
*write_only = 0;
return action.err;
}
+
+static int isst_pm_notify(struct notifier_block *nb,
+ unsigned long mode, void *_unused)
+{
+ switch (mode) {
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ case PM_POST_SUSPEND:
+ isst_resume_common();
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block isst_pm_nb = {
+ .notifier_call = isst_pm_notify,
+};
+
#define ICPU(model) { X86_VENDOR_INTEL, 6, model, X86_FEATURE_ANY, }
static const struct x86_cpu_id isst_if_cpu_ids[] = {
@@ -166,12 +193,21 @@ static int __init isst_if_mbox_init(void)
cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
cb.cmd_callback = isst_if_mbox_proc_cmd;
cb.owner = THIS_MODULE;
- return isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
+ ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
+ if (ret)
+ return ret;
+
+ ret = register_pm_notifier(&isst_pm_nb);
+ if (ret)
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+
+ return ret;
}
module_init(isst_if_mbox_init)
static void __exit isst_if_mbox_exit(void)
{
+ unregister_pm_notifier(&isst_pm_nb);
isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
}
module_exit(isst_if_mbox_exit)
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
index f03e79afd3f1..100a76cc27a5 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
@@ -131,6 +131,12 @@ static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
*/
mutex_lock(&punit_dev->mutex);
ret = isst_if_mbox_cmd(pdev, mbox_cmd);
+ if (!ret && !resume && isst_if_mbox_cmd_set_req(mbox_cmd))
+ ret = isst_store_cmd(mbox_cmd->command,
+ mbox_cmd->sub_command,
+ mbox_cmd->logical_cpu, 1,
+ mbox_cmd->parameter,
+ mbox_cmd->req_data);
mutex_unlock(&punit_dev->mutex);
if (ret)
return ret;
@@ -186,11 +192,20 @@ static void isst_if_mbox_remove(struct pci_dev *pdev)
mutex_destroy(&punit_dev->mutex);
}
+static int __maybe_unused isst_if_resume(struct device *device)
+{
+ isst_resume_common();
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, NULL, isst_if_resume);
+
static struct pci_driver isst_if_pci_driver = {
.name = "isst_if_mbox_pci",
.id_table = isst_if_mbox_ids,
.probe = isst_if_mbox_probe,
.remove = isst_if_mbox_remove,
+ .driver.pm = &isst_if_pm_ops,
};
module_pci_driver(isst_if_pci_driver);
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
index 1c25a1235b9e..f7266a115a08 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
@@ -15,8 +15,20 @@
#include "isst_if_common.h"
+struct isst_mmio_range {
+ int beg;
+ int end;
+};
+
+struct isst_mmio_range mmio_range[] = {
+ {0x04, 0x14},
+ {0x20, 0xD0},
+};
+
struct isst_if_device {
void __iomem *punit_mmio;
+ u32 range_0[5];
+ u32 range_1[45];
struct mutex mutex;
};
@@ -118,11 +130,48 @@ static void isst_if_remove(struct pci_dev *pdev)
mutex_destroy(&punit_dev->mutex);
}
+static int __maybe_unused isst_if_suspend(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct isst_if_device *punit_dev;
+ int i;
+
+ punit_dev = pci_get_drvdata(pdev);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
+ punit_dev->range_0[i] = readl(punit_dev->punit_mmio +
+ mmio_range[0].beg + 4 * i);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
+ punit_dev->range_1[i] = readl(punit_dev->punit_mmio +
+ mmio_range[1].beg + 4 * i);
+
+ return 0;
+}
+
+static int __maybe_unused isst_if_resume(struct device *device)
+{
+ struct pci_dev *pdev = to_pci_dev(device);
+ struct isst_if_device *punit_dev;
+ int i;
+
+ punit_dev = pci_get_drvdata(pdev);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_0); ++i)
+ writel(punit_dev->range_0[i], punit_dev->punit_mmio +
+ mmio_range[0].beg + 4 * i);
+ for (i = 0; i < ARRAY_SIZE(punit_dev->range_1); ++i)
+ writel(punit_dev->range_1[i], punit_dev->punit_mmio +
+ mmio_range[1].beg + 4 * i);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(isst_if_pm_ops, isst_if_suspend, isst_if_resume);
+
static struct pci_driver isst_if_pci_driver = {
.name = "isst_if_pci",
.id_table = isst_if_ids,
.probe = isst_if_probe,
.remove = isst_if_remove,
+ .driver.pm = &isst_if_pm_ops,
};
module_pci_driver(isst_if_pci_driver);
--
2.17.2
^ permalink raw reply related
* [PATCH 06/10] platform/x86: ISST: Add Intel Speed Select mailbox interface via PCI
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
Add an IOCTL to send mailbox commands to PUNIT using PUNIT PCI device.
A limited set of mailbox commands can be sent to PUNIT.
This MMIO interface is used by the intel-speed-select tool under
tools/x86/power to enumerate and control Intel Speed Select features.
The MBOX commands ids and semantics of the message can be checked from
the source code of the tool.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../x86/intel_speed_select_if/Makefile | 1 +
.../intel_speed_select_if/isst_if_common.c | 85 ++++++++
.../intel_speed_select_if/isst_if_common.h | 3 +
.../intel_speed_select_if/isst_if_mbox_pci.c | 199 ++++++++++++++++++
include/uapi/linux/isst_if.h | 38 ++++
5 files changed, 326 insertions(+)
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
diff --git a/drivers/platform/x86/intel_speed_select_if/Makefile b/drivers/platform/x86/intel_speed_select_if/Makefile
index 7e94919208d3..8dec8c858649 100644
--- a/drivers/platform/x86/intel_speed_select_if/Makefile
+++ b/drivers/platform/x86/intel_speed_select_if/Makefile
@@ -6,3 +6,4 @@
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mbox_pci.o
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
index 3f96a3925bc6..391fc3f12161 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -25,6 +25,86 @@
static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
+struct isst_valid_cmd_ranges {
+ u16 cmd;
+ u16 sub_cmd_beg;
+ u16 sub_cmd_end;
+};
+
+struct isst_cmd_set_req_type {
+ u16 cmd;
+ u16 sub_cmd;
+ u16 param;
+};
+
+static const struct isst_valid_cmd_ranges isst_valid_cmds[] = {
+ {0xD0, 0x00, 0x03},
+ {0x7F, 0x00, 0x0B},
+ {0x7F, 0x10, 0x12},
+ {0x7F, 0x20, 0x23},
+};
+
+static const struct isst_cmd_set_req_type isst_cmd_set_reqs[] = {
+ {0xD0, 0x00, 0x08},
+ {0xD0, 0x01, 0x08},
+ {0xD0, 0x02, 0x08},
+ {0xD0, 0x03, 0x08},
+ {0x7F, 0x02, 0x00},
+ {0x7F, 0x08, 0x00},
+};
+
+/**
+ * isst_if_mbox_cmd_invalid() - Check invalid mailbox commands
+ * @cmd: Pointer to the command structure to verify.
+ *
+ * Invalid command to PUNIT to may result in instability of the platform.
+ * This function has a whitelist of commands, which are allowed.
+ *
+ * Return: Return true if the command is invalid, else false.
+ */
+bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd)
+{
+ int i;
+
+ if (cmd->logical_cpu >= nr_cpu_ids)
+ return true;
+
+ for (i = 0; i < ARRAY_SIZE(isst_valid_cmds); ++i) {
+ if (cmd->command == isst_valid_cmds[i].cmd &&
+ (cmd->sub_command >= isst_valid_cmds[i].sub_cmd_beg &&
+ cmd->sub_command <= isst_valid_cmds[i].sub_cmd_end)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_invalid);
+
+/**
+ * isst_if_mbox_cmd_set_req() - Check mailbox command is a set request
+ * @cmd: Pointer to the command structure to verify.
+ *
+ * Check if the given mail box level is set request and not a get request.
+ *
+ * Return: Return true if the command is set_req, else false.
+ */
+bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *cmd)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(isst_cmd_set_reqs); ++i) {
+ if (cmd->command == isst_cmd_set_reqs[i].cmd &&
+ cmd->sub_command == isst_cmd_set_reqs[i].sub_cmd &&
+ cmd->parameter == isst_cmd_set_reqs[i].param) {
+ return true;
+ }
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(isst_if_mbox_cmd_set_req);
+
static int isst_if_get_platform_info(void __user *argp)
{
struct isst_if_platform_info info;
@@ -224,6 +304,11 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
if (cb->registered)
ret = isst_if_exec_multi_cmd(argp, cb);
break;
+ case ISST_IF_MBOX_COMMAND:
+ cb = &punit_callbacks[ISST_IF_DEV_MBOX];
+ if (cb->registered)
+ ret = isst_if_exec_multi_cmd(argp, cb);
+ break;
default:
break;
}
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
index cdc7d019748a..7c0f71221da7 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
@@ -11,6 +11,7 @@
#define __ISST_IF_COMMON_H
#define INTEL_RAPL_PRIO_DEVID_0 0x3451
+#define INTEL_CFG_MBOX_DEVID_0 0x3459
/*
* Validate maximum commands in a single request.
@@ -60,4 +61,6 @@ struct isst_if_cmd_cb {
int isst_if_cdev_register(int type, struct isst_if_cmd_cb *cb);
void isst_if_cdev_unregister(int type);
struct pci_dev *isst_if_get_pci_dev(int cpu, int bus, int dev, int fn);
+bool isst_if_mbox_cmd_set_req(struct isst_if_mbox_cmd *mbox_cmd);
+bool isst_if_mbox_cmd_invalid(struct isst_if_mbox_cmd *cmd);
#endif
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
new file mode 100644
index 000000000000..f03e79afd3f1
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mbox_pci.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: Mbox via PCI Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/cpufeature.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+
+#include "isst_if_common.h"
+
+#define PUNIT_MAILBOX_DATA 0xA0
+#define PUNIT_MAILBOX_INTERFACE 0xA4
+#define PUNIT_MAILBOX_BUSY_BIT 31
+
+/*
+ * Commands has variable amount of processing time. Most of the commands will
+ * be done in 0-3 tries, but some takes up to 50.
+ * The real processing time was observed as 25us for the most of the commands
+ * at 2GHz. It is possible to optimize this count taking samples on customer
+ * systems.
+ */
+#define OS_MAILBOX_RETRY_COUNT 50
+
+struct isst_if_device {
+ struct mutex mutex;
+};
+
+static int isst_if_mbox_cmd(struct pci_dev *pdev,
+ struct isst_if_mbox_cmd *mbox_cmd)
+{
+ u32 retries, data;
+ int ret;
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
+ &data);
+ if (ret)
+ return ret;
+
+ if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+ ret = 0;
+ break;
+ } while (--retries);
+
+ if (ret)
+ return ret;
+
+ /* Write DATA register */
+ ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_DATA,
+ mbox_cmd->req_data);
+ if (ret)
+ return ret;
+
+ /* Write command register */
+ data = BIT_ULL(PUNIT_MAILBOX_BUSY_BIT) |
+ (mbox_cmd->parameter & GENMASK_ULL(13, 0)) << 16 |
+ (mbox_cmd->sub_command << 8) |
+ mbox_cmd->command;
+
+ ret = pci_write_config_dword(pdev, PUNIT_MAILBOX_INTERFACE, data);
+ if (ret)
+ return ret;
+
+ /* Poll for rb bit == 0 */
+ retries = OS_MAILBOX_RETRY_COUNT;
+ do {
+ ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_INTERFACE,
+ &data);
+ if (ret)
+ return ret;
+
+ if (data & BIT_ULL(PUNIT_MAILBOX_BUSY_BIT)) {
+ ret = -EBUSY;
+ continue;
+ }
+
+ if (data & 0xff)
+ return -ENXIO;
+
+ ret = pci_read_config_dword(pdev, PUNIT_MAILBOX_DATA, &data);
+ if (ret)
+ return ret;
+
+ mbox_cmd->resp_data = data;
+ ret = 0;
+ break;
+ } while (--retries);
+
+ return ret;
+}
+
+static long isst_if_mbox_proc_cmd(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_mbox_cmd *mbox_cmd;
+ struct isst_if_device *punit_dev;
+ struct pci_dev *pdev;
+ int ret;
+
+ mbox_cmd = (struct isst_if_mbox_cmd *)cmd_ptr;
+
+ if (isst_if_mbox_cmd_invalid(mbox_cmd))
+ return -EINVAL;
+
+ if (isst_if_mbox_cmd_set_req(mbox_cmd) && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ pdev = isst_if_get_pci_dev(mbox_cmd->logical_cpu, 1, 30, 1);
+ if (!pdev)
+ return -EINVAL;
+
+ punit_dev = pci_get_drvdata(pdev);
+ if (!punit_dev)
+ return -EINVAL;
+
+ /*
+ * Basically we are allowing one complete mailbox transaction on
+ * a mapped PCI device at a time.
+ */
+ mutex_lock(&punit_dev->mutex);
+ ret = isst_if_mbox_cmd(pdev, mbox_cmd);
+ mutex_unlock(&punit_dev->mutex);
+ if (ret)
+ return ret;
+
+ *write_only = 0;
+
+ return 0;
+}
+
+static const struct pci_device_id isst_if_mbox_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_CFG_MBOX_DEVID_0)},
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, isst_if_mbox_ids);
+
+static int isst_if_mbox_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct isst_if_device *punit_dev;
+ struct isst_if_cmd_cb cb;
+ int ret;
+
+ punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
+ if (!punit_dev)
+ return -ENOMEM;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ mutex_init(&punit_dev->mutex);
+ pci_set_drvdata(pdev, punit_dev);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cmd_size = sizeof(struct isst_if_mbox_cmd);
+ cb.offset = offsetof(struct isst_if_mbox_cmds, mbox_cmd);
+ cb.cmd_callback = isst_if_mbox_proc_cmd;
+ cb.owner = THIS_MODULE;
+ ret = isst_if_cdev_register(ISST_IF_DEV_MBOX, &cb);
+
+ if (ret)
+ mutex_destroy(&punit_dev->mutex);
+
+ return ret;
+}
+
+static void isst_if_mbox_remove(struct pci_dev *pdev)
+{
+ struct isst_if_device *punit_dev;
+
+ punit_dev = pci_get_drvdata(pdev);
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+ mutex_destroy(&punit_dev->mutex);
+}
+
+static struct pci_driver isst_if_pci_driver = {
+ .name = "isst_if_mbox_pci",
+ .id_table = isst_if_mbox_ids,
+ .probe = isst_if_mbox_probe,
+ .remove = isst_if_mbox_remove,
+};
+
+module_pci_driver(isst_if_pci_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel speed select interface pci mailbox driver");
diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h
index fe2492ade078..e4b1c2ec3279 100644
--- a/include/uapi/linux/isst_if.h
+++ b/include/uapi/linux/isst_if.h
@@ -95,8 +95,46 @@ struct isst_if_io_regs {
struct isst_if_io_reg io_reg[1];
};
+/**
+ * struct isst_if_mbox_cmd - Structure to define mail box command
+ * @logical_cpu: Logical CPU number to get target PCI device
+ * @parameter: Mailbox parameter value
+ * @req_data: Request data for the mailbox
+ * @resp_data: Response data for mailbox command response
+ * @command: Mailbox command value
+ * @sub_command: Mailbox sub command value
+ * @reserved: Unused, set to 0
+ *
+ * Structure to specify mailbox command to be sent to PUNIT.
+ */
+struct isst_if_mbox_cmd {
+ __u32 logical_cpu;
+ __u32 parameter;
+ __u32 req_data;
+ __u32 resp_data;
+ __u16 command;
+ __u16 sub_command;
+ __u32 reserved;
+};
+
+/**
+ * struct isst_if_mbox_cmds - structure for mailbox commands
+ * @cmd_count: Number of mailbox commands in mbox_cmd[]
+ * @mbox_cmd[]: Holds one or more mbox commands
+ *
+ * This structure used with ioctl ISST_IF_MBOX_COMMAND to send
+ * one or more mailbox commands to PUNIT. Here IOCTL return value
+ * indicates number of commands sent or error number if no commands have
+ * been sent.
+ */
+struct isst_if_mbox_cmds {
+ __u32 cmd_count;
+ struct isst_if_mbox_cmd mbox_cmd[1];
+};
+
#define ISST_IF_MAGIC 0xFE
#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
#define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *)
#define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *)
+#define ISST_IF_MBOX_COMMAND _IOWR(ISST_IF_MAGIC, 3, struct isst_if_mbox_cmds *)
#endif
--
2.17.2
^ permalink raw reply related
* [PATCH 05/10] platform/x86: ISST: Add Intel Speed Select mmio interface
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
Added MMIO interface to read/write specific offsets in PUNIT PCI device
which export core priortization. This MMIO interface can be used using
ioctl interface on /dev/isst_interface using IOCTL ISST_IF_IO_CMD.
This MMIO interface is used by the intel-speed-select tool under
tools/x86/power to enumerate and set core priority. The MMIO offsets and
semantics of the message can be checked from the source code of the tool.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../x86/intel_speed_select_if/Makefile | 1 +
.../intel_speed_select_if/isst_if_common.c | 6 +
.../intel_speed_select_if/isst_if_common.h | 2 +
.../x86/intel_speed_select_if/isst_if_mmio.c | 131 ++++++++++++++++++
include/uapi/linux/isst_if.h | 33 +++++
5 files changed, 173 insertions(+)
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
diff --git a/drivers/platform/x86/intel_speed_select_if/Makefile b/drivers/platform/x86/intel_speed_select_if/Makefile
index c12687672fc9..7e94919208d3 100644
--- a/drivers/platform/x86/intel_speed_select_if/Makefile
+++ b/drivers/platform/x86/intel_speed_select_if/Makefile
@@ -5,3 +5,4 @@
#
obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_mmio.o
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
index 72e74d72724b..3f96a3925bc6 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -206,6 +206,7 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
{
void __user *argp = (void __user *)arg;
struct isst_if_cmd_cb cmd_cb;
+ struct isst_if_cmd_cb *cb;
long ret = -ENOTTY;
switch (cmd) {
@@ -218,6 +219,11 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
cmd_cb.cmd_callback = isst_if_proc_phyid_req;
ret = isst_if_exec_multi_cmd(argp, &cmd_cb);
break;
+ case ISST_IF_IO_CMD:
+ cb = &punit_callbacks[ISST_IF_DEV_MMIO];
+ if (cb->registered)
+ ret = isst_if_exec_multi_cmd(argp, cb);
+ break;
default:
break;
}
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
index dade77c58b22..cdc7d019748a 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
@@ -10,6 +10,8 @@
#ifndef __ISST_IF_COMMON_H
#define __ISST_IF_COMMON_H
+#define INTEL_RAPL_PRIO_DEVID_0 0x3451
+
/*
* Validate maximum commands in a single request.
* This is enough to handle command to every core in one ioctl, or all
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
new file mode 100644
index 000000000000..1c25a1235b9e
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_mmio.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: MMIO Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+
+#include "isst_if_common.h"
+
+struct isst_if_device {
+ void __iomem *punit_mmio;
+ struct mutex mutex;
+};
+
+static long isst_if_mmio_rd_wr(u8 *cmd_ptr, int *write_only, int resume)
+{
+ struct isst_if_device *punit_dev;
+ struct isst_if_io_reg *io_reg;
+ struct pci_dev *pdev;
+
+ io_reg = (struct isst_if_io_reg *)cmd_ptr;
+ if (io_reg->reg < 0x04 || io_reg->reg > 0xD0)
+ return -EINVAL;
+
+ if (io_reg->read_write && !capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ pdev = isst_if_get_pci_dev(io_reg->logical_cpu, 0, 0, 1);
+ if (!pdev)
+ return -EINVAL;
+
+ punit_dev = pci_get_drvdata(pdev);
+ if (!punit_dev)
+ return -EINVAL;
+
+ /*
+ * Ensure that operation is complete on a PCI device to avoid read
+ * write race by using per PCI device mutex.
+ */
+ mutex_lock(&punit_dev->mutex);
+ if (io_reg->read_write) {
+ writel(io_reg->value, punit_dev->punit_mmio+io_reg->reg);
+ *write_only = 1;
+ } else {
+ io_reg->value = readl(punit_dev->punit_mmio+io_reg->reg);
+ *write_only = 0;
+ }
+ mutex_unlock(&punit_dev->mutex);
+
+ return 0;
+}
+
+static const struct pci_device_id isst_if_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, INTEL_RAPL_PRIO_DEVID_0)},
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, isst_if_ids);
+
+static int isst_if_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct isst_if_device *punit_dev;
+ struct isst_if_cmd_cb cb;
+ u32 mmio_base, pcu_base;
+ u64 base_addr;
+ int ret;
+
+ punit_dev = devm_kzalloc(&pdev->dev, sizeof(*punit_dev), GFP_KERNEL);
+ if (!punit_dev)
+ return -ENOMEM;
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return ret;
+
+ ret = pci_read_config_dword(pdev, 0xD0, &mmio_base);
+ if (ret)
+ return ret;
+
+ ret = pci_read_config_dword(pdev, 0xFC, &pcu_base);
+ if (ret)
+ return ret;
+
+ pcu_base &= GENMASK(10, 0);
+ base_addr = (u64)mmio_base << 23 | (u64) pcu_base << 12;
+ punit_dev->punit_mmio = devm_ioremap(&pdev->dev, base_addr, 256);
+ if (!punit_dev->punit_mmio)
+ return -ENOMEM;
+
+ mutex_init(&punit_dev->mutex);
+ pci_set_drvdata(pdev, punit_dev);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cmd_size = sizeof(struct isst_if_io_reg);
+ cb.offset = offsetof(struct isst_if_io_regs, io_reg);
+ cb.cmd_callback = isst_if_mmio_rd_wr;
+ cb.owner = THIS_MODULE;
+ ret = isst_if_cdev_register(ISST_IF_DEV_MMIO, &cb);
+ if (ret)
+ mutex_destroy(&punit_dev->mutex);
+
+ return ret;
+}
+
+static void isst_if_remove(struct pci_dev *pdev)
+{
+ struct isst_if_device *punit_dev;
+
+ punit_dev = pci_get_drvdata(pdev);
+ isst_if_cdev_unregister(ISST_IF_DEV_MBOX);
+ mutex_destroy(&punit_dev->mutex);
+}
+
+static struct pci_driver isst_if_pci_driver = {
+ .name = "isst_if_pci",
+ .id_table = isst_if_ids,
+ .probe = isst_if_probe,
+ .remove = isst_if_remove,
+};
+
+module_pci_driver(isst_if_pci_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel speed select interface mmio driver");
diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h
index 15d1f286a830..fe2492ade078 100644
--- a/include/uapi/linux/isst_if.h
+++ b/include/uapi/linux/isst_if.h
@@ -63,7 +63,40 @@ struct isst_if_cpu_maps {
struct isst_if_cpu_map cpu_map[1];
};
+/**
+ * struct isst_if_io_reg - Read write PUNIT IO register
+ * @read_write: Value 0: Read, 1: Write
+ * @logical_cpu: Logical CPU number to get target PCI device.
+ * @reg: PUNIT register offset
+ * @value: For write operation value to write and for
+ * for read placeholder read value
+ *
+ * Structure to specify read/write data to PUNIT registers.
+ */
+struct isst_if_io_reg {
+ __u32 read_write; /* Read:0, Write:1 */
+ __u32 logical_cpu;
+ __u32 reg;
+ __u32 value;
+};
+
+/**
+ * struct isst_if_io_regs - structure for IO register commands
+ * @cmd_count: Number of io reg commands in io_reg[]
+ * @io_reg[]: Holds one or more io_reg command structure
+ *
+ * This structure used with ioctl ISST_IF_IO_CMD to send
+ * one or more read/write commands to PUNIT. Here IOCTL return value
+ * indicates number of requests sent or error number if no requests have
+ * been sent.
+ */
+struct isst_if_io_regs {
+ __u32 req_count;
+ struct isst_if_io_reg io_reg[1];
+};
+
#define ISST_IF_MAGIC 0xFE
#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
#define ISST_IF_GET_PHY_ID _IOWR(ISST_IF_MAGIC, 1, struct isst_if_cpu_map *)
+#define ISST_IF_IO_CMD _IOW(ISST_IF_MAGIC, 2, struct isst_if_io_regs *)
#endif
--
2.17.2
^ permalink raw reply related
* [PATCH 02/10] platform/x86: ISST: Add common API to register and handle ioctls
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
Encapsulate common functions which all Intel Speed Select Technology
interface drivers can use. This creates API to register misc device for
user kernel communication and handle all common IOCTLs. As part of the
registry it allows a callback which is to handle domain specific ioctl
processing.
There can be multiple drivers register for services, which can be built
as modules. So this driver handle contention during registry and as well
as during removal. Once user space opened the misc device, the registered
driver will be prevented from removal. Also once misc device is opened by
the user space new client driver can't register, till the misc device is
closed.
There are two types of client drivers, one to handle mail box interface
and the other is to allow direct read/write to some specific MMIO space.
This common driver implements IOCTL ISST_IF_GET_PLATFORM_INFO.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
drivers/platform/x86/Kconfig | 2 +
drivers/platform/x86/Makefile | 1 +
.../x86/intel_speed_select_if/Kconfig | 17 ++
.../x86/intel_speed_select_if/Makefile | 7 +
.../intel_speed_select_if/isst_if_common.c | 182 ++++++++++++++++++
.../intel_speed_select_if/isst_if_common.h | 60 ++++++
include/uapi/linux/isst_if.h | 41 ++++
7 files changed, 310 insertions(+)
create mode 100644 drivers/platform/x86/intel_speed_select_if/Kconfig
create mode 100644 drivers/platform/x86/intel_speed_select_if/Makefile
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_common.c
create mode 100644 drivers/platform/x86/intel_speed_select_if/isst_if_common.h
create mode 100644 include/uapi/linux/isst_if.h
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 85b92a95e4c8..24c9aa4ad3d4 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1328,6 +1328,8 @@ config PCENGINES_APU2
To compile this driver as a module, choose M here: the module
will be called pcengines-apuv2.
+source "drivers/platform/x86/intel_speed_select_if/Kconfig"
+
endif # X86_PLATFORM_DEVICES
config PMC_ATOM
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 87b0069bd781..c43117d17fd8 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -98,3 +98,4 @@ obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
obj-$(CONFIG_I2C_MULTI_INSTANTIATE) += i2c-multi-instantiate.o
obj-$(CONFIG_INTEL_ATOMISP2_PM) += intel_atomisp2_pm.o
obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += intel_speed_select_if/
diff --git a/drivers/platform/x86/intel_speed_select_if/Kconfig b/drivers/platform/x86/intel_speed_select_if/Kconfig
new file mode 100644
index 000000000000..7fc1165ad4c9
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/Kconfig
@@ -0,0 +1,17 @@
+menu "Intel Speed Select Technology interface support"
+ depends on PCI
+ depends on X86_64 || COMPILE_TEST
+
+config INTEL_SPEED_SELECT_INTERFACE
+ tristate "Intel(R) Speed Select Technology interface drivers"
+ help
+ This config enables the Intel(R) Speed Select Technology interface
+ drivers. The Intel(R) speed select technology features are non
+ architectural and only supported on specific Xeon(R) servers.
+ These drivers provide interface to directly communicate with hardware
+ via MMIO and Mail boxes to enumerate and control all the speed select
+ features.
+
+ Enable this config, if there is a need to enable and control the
+ Intel(R) Speed Select Technology features from the user space.
+endmenu
diff --git a/drivers/platform/x86/intel_speed_select_if/Makefile b/drivers/platform/x86/intel_speed_select_if/Makefile
new file mode 100644
index 000000000000..c12687672fc9
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile - Intel Speed Select Interface drivers
+# Copyright (c) 2019, Intel Corporation.
+#
+
+obj-$(CONFIG_INTEL_SPEED_SELECT_INTERFACE) += isst_if_common.o
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
new file mode 100644
index 000000000000..ab2bb4862dc8
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Speed Select Interface: Common functions
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <uapi/linux/isst_if.h>
+
+#include "isst_if_common.h"
+
+static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
+
+static int isst_if_get_platform_info(void __user *argp)
+{
+ struct isst_if_platform_info info;
+
+ info.api_version = ISST_IF_API_VERSION,
+ info.driver_version = ISST_IF_DRIVER_VERSION,
+ info.max_cmds_per_ioctl = ISST_IF_CMD_LIMIT,
+ info.mbox_supported = punit_callbacks[ISST_IF_DEV_MBOX].registered;
+ info.mmio_supported = punit_callbacks[ISST_IF_DEV_MMIO].registered;
+
+ if (copy_to_user(argp, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ long ret = -ENOTTY;
+
+ switch (cmd) {
+ case ISST_IF_GET_PLATFORM_INFO:
+ ret = isst_if_get_platform_info(argp);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static DEFINE_MUTEX(punit_misc_dev_lock);
+static int misc_usage_count;
+static int misc_device_ret;
+static int misc_device_open;
+
+static int isst_if_open(struct inode *inode, struct file *file)
+{
+ int i, ret = 0;
+
+ /* Fail open, if a module is going away */
+ mutex_lock(&punit_misc_dev_lock);
+ for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
+ struct isst_if_cmd_cb *cb = &punit_callbacks[i];
+
+ if (cb->registered && !try_module_get(cb->owner)) {
+ ret = -ENODEV;
+ break;
+ }
+ }
+ if (ret) {
+ int j;
+
+ for (j = 0; j < i; ++j) {
+ struct isst_if_cmd_cb *cb;
+
+ cb = &punit_callbacks[j];
+ if (cb->registered)
+ module_put(cb->owner);
+ }
+ } else {
+ misc_device_open++;
+ }
+ mutex_unlock(&punit_misc_dev_lock);
+
+ return ret;
+}
+
+static int isst_if_relase(struct inode *inode, struct file *f)
+{
+ int i;
+
+ mutex_lock(&punit_misc_dev_lock);
+ misc_device_open--;
+ for (i = 0; i < ISST_IF_DEV_MAX; ++i) {
+ struct isst_if_cmd_cb *cb = &punit_callbacks[i];
+
+ if (cb->registered)
+ module_put(cb->owner);
+ }
+ mutex_unlock(&punit_misc_dev_lock);
+
+ return 0;
+}
+
+static const struct file_operations isst_if_char_driver_ops = {
+ .open = isst_if_open,
+ .unlocked_ioctl = isst_if_def_ioctl,
+ .release = isst_if_relase,
+};
+
+static struct miscdevice isst_if_char_driver = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "isst_interface",
+ .fops = &isst_if_char_driver_ops,
+};
+
+/**
+ * isst_if_cdev_register() - Register callback for IOCTL
+ * @device_type: The device type this callback handling.
+ * @cb: Callback structure.
+ *
+ * This function registers a callback to device type. On very first call
+ * it will register a misc device, which is used for user kernel interface.
+ * Other calls simply increment ref count. Registry will fail, if the user
+ * already opened misc device for operation. Also if the misc device
+ * creation failed, then it will not try again and all callers will get
+ * failure code.
+ *
+ * Return: Return the return value from the misc creation device or -EINVAL
+ * for unsupported device type.
+ */
+int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
+{
+ if (misc_device_ret)
+ return misc_device_ret;
+
+ if (device_type >= ISST_IF_DEV_MAX)
+ return -EINVAL;
+
+ mutex_lock(&punit_misc_dev_lock);
+ if (misc_device_open) {
+ mutex_unlock(&punit_misc_dev_lock);
+ return -EAGAIN;
+ }
+ if (!misc_usage_count) {
+ misc_device_ret = misc_register(&isst_if_char_driver);
+ if (misc_device_ret)
+ goto unlock_exit;
+ }
+ memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
+ punit_callbacks[device_type].registered = 1;
+ misc_usage_count++;
+unlock_exit:
+ mutex_unlock(&punit_misc_dev_lock);
+
+ return misc_device_ret;
+}
+EXPORT_SYMBOL_GPL(isst_if_cdev_register);
+
+/**
+ * isst_if_cdev_unregister() - Unregister callback for IOCTL
+ * @device_type: The device type to unregister.
+ *
+ * This function unregisters the previously registered callback. If this
+ * is the last callback unregistering, then misc device is removed.
+ *
+ * Return: None.
+ */
+void isst_if_cdev_unregister(int device_type)
+{
+ mutex_lock(&punit_misc_dev_lock);
+ misc_usage_count--;
+ punit_callbacks[device_type].registered = 0;
+ if (!misc_usage_count && !misc_device_ret)
+ misc_deregister(&isst_if_char_driver);
+ mutex_unlock(&punit_misc_dev_lock);
+}
+EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
new file mode 100644
index 000000000000..11f339226fb4
--- /dev/null
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Speed Select Interface: Drivers Internal defines
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#ifndef __ISST_IF_COMMON_H
+#define __ISST_IF_COMMON_H
+
+/*
+ * Validate maximum commands in a single request.
+ * This is enough to handle command to every core in one ioctl, or all
+ * possible message id to one CPU. Limit is also helpful for resonse time
+ * per IOCTL request, as PUNIT may take different times to process each
+ * request and may hold for long for too many commands.
+ */
+#define ISST_IF_CMD_LIMIT 64
+
+#define ISST_IF_API_VERSION 0x01
+#define ISST_IF_DRIVER_VERSION 0x01
+
+#define ISST_IF_DEV_MBOX 0
+#define ISST_IF_DEV_MMIO 1
+#define ISST_IF_DEV_MAX 2
+
+/**
+ * struct isst_if_cmd_cb - Used to register a IOCTL handler
+ * @registered: Used by the common code to store registry. Caller don't
+ * to touch this field
+ * @cmd_size: The command size of the individual command in IOCTL
+ * @offset: Offset to the first valid member in command structure.
+ * This will be the offset of the start of the command
+ * after command count field
+ * @cmd_callback: Callback function to handle IOCTL. The callback has the
+ * command pointer with data for command. There is a pointer
+ * called write_only, which when set, will not copy the
+ * response to user ioctl buffer. The "resume" argument
+ * can be used to avoid storing the command for replay
+ * during system resume
+ *
+ * This structure is used to register an handler for IOCTL. To avoid
+ * code duplication common code handles all the IOCTL command read/write
+ * including handling multiple command in single IOCTL. The caller just
+ * need to execute a command via the registered callback.
+ */
+struct isst_if_cmd_cb {
+ int registered;
+ int cmd_size;
+ int offset;
+ struct module *owner;
+ long (*cmd_callback)(u8 *ptr, int *write_only, int resume);
+};
+
+/* Internal interface functions */
+int isst_if_cdev_register(int type, struct isst_if_cmd_cb *cb);
+void isst_if_cdev_unregister(int type);
+#endif
diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h
new file mode 100644
index 000000000000..fa94480b5f74
--- /dev/null
+++ b/include/uapi/linux/isst_if.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Intel Speed Select Interface: OS to hardware Interface
+ * Copyright (c) 2019, Intel Corporation.
+ * All rights reserved.
+ *
+ * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+ */
+
+#ifndef __ISST_IF_H
+#define __ISST_IF_H
+
+#include <linux/types.h>
+
+/**
+ * struct isst_if_platform_info - Define platform information
+ * @api_version: Version of the firmware document, which this driver
+ * can communicate
+ * @driver_version: Driver version, which will help user to send right
+ * commands. Even if the firmware is capable, driver may
+ * not be ready
+ * @max_cmds_per_ioctl: Returns the maximum number of commands driver will
+ * accept in a single ioctl
+ * @mbox_supported: Support of mail box interface
+ * @mmio_supported: Support of mmio interface for core-power feature
+ *
+ * Used to return output of IOCTL ISST_IF_GET_PLATFORM_INFO. This
+ * information can be used by the user space, to get the driver, firmware
+ * support and also number of commands to send in a single IOCTL request.
+ */
+struct isst_if_platform_info {
+ __u16 api_version;
+ __u16 driver_version;
+ __u16 max_cmds_per_ioctl;
+ __u8 mbox_supported;
+ __u8 mmio_supported;
+};
+
+#define ISST_IF_MAGIC 0xFE
+#define ISST_IF_GET_PLATFORM_INFO _IOR(ISST_IF_MAGIC, 0, struct isst_if_platform_info *)
+#endif
--
2.17.2
^ permalink raw reply related
* [PATCH 03/10] platform/x86: ISST: Store per CPU information
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
There are two per CPU data needs to be stored and cached to avoid repeated
MSR readings for accessing them later:
- Physical to logical CPU conversion
The PUNIT uses a different CPU numbering scheme which is not APIC id based.
So we need to establish relationship between PUNIT CPU number and Linux
logical CPU numbering which is based on APIC id. There is an MSR 0x53
(MSR_THREAD_ID), which gets physical CPU number for the local CPU where it
is read. Also the CPU mask in some messages will inform which CPUs needs
to be online/offline for a TDP level. During TDP switch if user offlined
some CPUs, then the physical CPU mask can't be converted as we can't
read MSR on an offlined CPU to go to a lower TDP level by onlining more
CPUs. So the mapping needs to be established at the boot up time.
- Bus number corresponding to a CPU
A group of CPUs are in a control of a PUNIT. The PUNIT device is exported
as PCI device. To do operation on a PUNIT for a CPU, we need to find out
to which PCI device it is related to. This is done by reading MSR 0x128
(MSR_CPU_BUS_NUMBER).
So during CPU online stages the above MSRs are read and stored. Later
this stored information is used to process IOCTLs request from the user
space.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
.../intel_speed_select_if/isst_if_common.c | 114 +++++++++++++++++-
.../intel_speed_select_if/isst_if_common.h | 1 +
2 files changed, 114 insertions(+), 1 deletion(-)
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
index ab2bb4862dc8..0e16cbf685d0 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.c
@@ -7,14 +7,22 @@
* Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
*/
+#include <linux/cpufeature.h>
+#include <linux/cpuhotplug.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
#include <uapi/linux/isst_if.h>
#include "isst_if_common.h"
+#define MSR_THREAD_ID_INFO 0x53
+#define MSR_CPU_BUS_NUMBER 0x128
+
static struct isst_if_cmd_cb punit_callbacks[ISST_IF_DEV_MAX];
static int isst_if_get_platform_info(void __user *argp)
@@ -33,6 +41,99 @@ static int isst_if_get_platform_info(void __user *argp)
return 0;
}
+
+struct isst_if_cpu_info {
+ /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */
+ int bus_info[2];
+ int punit_cpu_id;
+};
+
+static struct isst_if_cpu_info *isst_cpu_info;
+
+/**
+ * isst_if_get_pci_dev() - Get the PCI device instance for a CPU
+ * @cpu: Logical CPU number.
+ * @bus_number: The bus number assigned by the hardware.
+ * @dev: The device number assigned by the hardware.
+ * @fn: The function number assigned by the hardware.
+ *
+ * Using cached bus information, find out the PCI device for a bus number,
+ * device and function.
+ *
+ * Return: Return pci_dev pointer or NULL.
+ */
+struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn)
+{
+ int bus_number;
+
+ if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids ||
+ cpu >= num_possible_cpus())
+ return NULL;
+
+ bus_number = isst_cpu_info[cpu].bus_info[bus_no];
+ if (bus_number < 0)
+ return NULL;
+
+ return pci_get_domain_bus_and_slot(0, bus_number, PCI_DEVFN(dev, fn));
+}
+EXPORT_SYMBOL_GPL(isst_if_get_pci_dev);
+
+static int isst_if_cpu_online(unsigned int cpu)
+{
+ u64 data;
+ int ret;
+
+ ret = rdmsrl_safe(MSR_CPU_BUS_NUMBER, &data);
+ if (ret) {
+ /* This is not a fatal error on MSR mailbox only I/F */
+ isst_cpu_info[cpu].bus_info[0] = -1;
+ isst_cpu_info[cpu].bus_info[1] = -1;
+ } else {
+ isst_cpu_info[cpu].bus_info[0] = data & 0xff;
+ isst_cpu_info[cpu].bus_info[1] = (data >> 8) & 0xff;
+ }
+
+ ret = rdmsrl_safe(MSR_THREAD_ID_INFO, &data);
+ if (ret) {
+ isst_cpu_info[cpu].punit_cpu_id = -1;
+ return ret;
+ }
+ isst_cpu_info[cpu].punit_cpu_id = data;
+
+ return 0;
+}
+
+static int isst_if_online_id;
+
+static int isst_if_cpu_info_init(void)
+{
+ int ret;
+
+ isst_cpu_info = kcalloc(num_possible_cpus(),
+ sizeof(*isst_cpu_info),
+ GFP_KERNEL);
+ if (!isst_cpu_info)
+ return -ENOMEM;
+
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "platform/x86/isst-if:online",
+ isst_if_cpu_online, NULL);
+ if (ret < 0) {
+ kfree(isst_cpu_info);
+ return ret;
+ }
+
+ isst_if_online_id = ret;
+
+ return 0;
+}
+
+static void isst_if_cpu_info_exit(void)
+{
+ cpuhp_remove_state(isst_if_online_id);
+ kfree(isst_cpu_info);
+};
+
static long isst_if_def_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -145,9 +246,18 @@ int isst_if_cdev_register(int device_type, struct isst_if_cmd_cb *cb)
return -EAGAIN;
}
if (!misc_usage_count) {
+ int ret;
+
misc_device_ret = misc_register(&isst_if_char_driver);
if (misc_device_ret)
goto unlock_exit;
+
+ ret = isst_if_cpu_info_init();
+ if (ret) {
+ misc_deregister(&isst_if_char_driver);
+ misc_device_ret = ret;
+ goto unlock_exit;
+ }
}
memcpy(&punit_callbacks[device_type], cb, sizeof(*cb));
punit_callbacks[device_type].registered = 1;
@@ -173,8 +283,10 @@ void isst_if_cdev_unregister(int device_type)
mutex_lock(&punit_misc_dev_lock);
misc_usage_count--;
punit_callbacks[device_type].registered = 0;
- if (!misc_usage_count && !misc_device_ret)
+ if (!misc_usage_count && !misc_device_ret) {
misc_deregister(&isst_if_char_driver);
+ isst_if_cpu_info_exit();
+ }
mutex_unlock(&punit_misc_dev_lock);
}
EXPORT_SYMBOL_GPL(isst_if_cdev_unregister);
diff --git a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
index 11f339226fb4..dade77c58b22 100644
--- a/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
+++ b/drivers/platform/x86/intel_speed_select_if/isst_if_common.h
@@ -57,4 +57,5 @@ struct isst_if_cmd_cb {
/* Internal interface functions */
int isst_if_cdev_register(int type, struct isst_if_cmd_cb *cb);
void isst_if_cdev_unregister(int type);
+struct pci_dev *isst_if_get_pci_dev(int cpu, int bus, int dev, int fn);
#endif
--
2.17.2
^ permalink raw reply related
* [PATCH 01/10] platform/x86: ISST: Update ioctl-number.txt for Intel Speed Select interface
From: Srinivas Pandruvada @ 2019-06-26 22:38 UTC (permalink / raw)
To: dvhart, andy, andriy.shevchenko, corbet
Cc: rjw, alan, lenb, prarit, darcari, linux-doc, linux-kernel,
platform-driver-x86, Srinivas Pandruvada
In-Reply-To: <20190626223851.19138-1-srinivas.pandruvada@linux.intel.com>
Reserve ioctl numbers for intel Speed Select Technology interface
drivers.
Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
Documentation/ioctl/ioctl-number.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index c9558146ac58..ab0b3f686454 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -348,3 +348,4 @@ Code Seq#(hex) Include File Comments
0xF6 all LTTng Linux Trace Toolkit Next Generation
<mailto:mathieu.desnoyers@efficios.com>
0xFD all linux/dm-ioctl.h
+0xFE all linux/isst_if.h
--
2.17.2
^ permalink raw reply related
* Re: [PATCH v5 01/18] kunit: test: add KUnit test runner core
From: Brendan Higgins @ 2019-06-26 23:00 UTC (permalink / raw)
To: Stephen Boyd
Cc: Frank Rowand, Greg KH, Josh Poimboeuf, Kees Cook, Kieran Bingham,
Luis Chamberlain, Peter Zijlstra, Rob Herring, shuah,
Theodore Ts'o, Masahiro Yamada, devicetree, dri-devel,
kunit-dev, open list:DOCUMENTATION, linux-fsdevel, linux-kbuild,
Linux Kernel Mailing List, open list:KERNEL SELFTEST FRAMEWORK,
linux-nvdimm, linux-um, Sasha Levin, Bird, Timothy,
Amir Goldstein, Dan Carpenter, Daniel Vetter, Jeff Dike,
Joel Stanley, Julia Lawall, Kevin Hilman, Knut Omang,
Logan Gunthorpe, Michael Ellerman, Petr Mladek, Randy Dunlap,
Richard Weinberger, David Rientjes, Steven Rostedt, wfg
In-Reply-To: <20190626034100.B238520883@mail.kernel.org>
On Tue, Jun 25, 2019 at 8:41 PM Stephen Boyd <sboyd@kernel.org> wrote:
>
> Quoting Brendan Higgins (2019-06-25 13:28:25)
> > On Wed, Jun 19, 2019 at 5:15 PM Stephen Boyd <sboyd@kernel.org> wrote:
> > >
> > > Quoting Brendan Higgins (2019-06-17 01:25:56)
> > > > diff --git a/kunit/test.c b/kunit/test.c
> > > > new file mode 100644
> > > > index 0000000000000..d05d254f1521f
> > > > --- /dev/null
> > > > +++ b/kunit/test.c
> > > > @@ -0,0 +1,210 @@
> > > > +// SPDX-License-Identifier: GPL-2.0
> > > > +/*
> > > > + * Base unit test (KUnit) API.
> > > > + *
> > > > + * Copyright (C) 2019, Google LLC.
> > > > + * Author: Brendan Higgins <brendanhiggins@google.com>
> > > > + */
> > > > +
> > > > +#include <linux/sched/debug.h>
> > > > +#include <kunit/test.h>
> > > > +
> > > > +static bool kunit_get_success(struct kunit *test)
> > > > +{
> > > > + unsigned long flags;
> > > > + bool success;
> > > > +
> > > > + spin_lock_irqsave(&test->lock, flags);
> > > > + success = test->success;
> > > > + spin_unlock_irqrestore(&test->lock, flags);
> > >
> > > I still don't understand the locking scheme in this code. Is the
> > > intention to make getter and setter APIs that are "safe" by adding in a
> > > spinlock that is held around getting and setting various members in the
> > > kunit structure?
> >
> > Yes, your understanding is correct. It is possible for a user to write
> > a test such that certain elements may be updated in different threads;
> > this would most likely happen in the case where someone wants to make
> > an assertion or an expectation in a thread created by a piece of code
> > under test. Although this should generally be avoided, it is possible,
> > and there are occasionally good reasons to do so, so it is
> > functionality that we should support.
> >
> > Do you think I should add a comment to this effect?
>
> No, I think the locking should be removed.
>
> >
> > > In what situation is there more than one thread reading or writing the
> > > kunit struct? Isn't it only a single process that is going to be
> >
> > As I said above, it is possible that the code under test may spawn a
> > new thread that may make an expectation or an assertion. It is not a
> > super common use case, but it is possible.
>
> Sure, sounds super possible and OK.
>
> >
> > > operating on this structure? And why do we need to disable irqs? Are we
> > > expecting to be modifying the unit tests from irq contexts?
> >
> > There are instances where someone may want to test a driver which has
> > an interrupt handler in it. I actually have (not the greatest) example
> > here. Now in these cases, I expect someone to use a mock irqchip or
> > some other fake mechanism to trigger the interrupt handler and not
> > actual hardware; technically speaking in this case, it is not going to
> > be accessed from a "real" irq context; however, the code under test
> > should think that it is in an irq context; given that, I figured it is
> > best to just treat it as a real irq context. Does that make sense?
>
> Can you please describe the scenario in which grabbing the lock here,
> updating a single variable, and then releasing the lock right after
> does anything useful vs. not having the lock? I'm looking for a two CPU
Sure.
> scenario like below, but where it is a problem. There could be three
> CPUs, or even one CPU and three threads if you want to describe the
> extra thread scenario.
>
> Here's my scenario where it isn't needed:
>
> CPU0 CPU1
> ---- ----
> kunit_run_test(&test)
> test_case_func()
> ....
> [mock hardirq]
> kunit_set_success(&test)
> [hardirq ends]
> ...
> complete(&test_done)
> wait_for_completion(&test_done)
> kunit_get_success(&test)
>
> We don't need to care about having locking here because success or
> failure only happens in one place and it's synchronized with the
> completion.
Here is the scenario I am concerned about:
CPU0 CPU1 CPU2
---- ---- ----
kunit_run_test(&test)
test_case_func()
....
schedule_work(foo_func)
[mock hardirq] foo_func()
... ...
kunit_set_success(false) kunit_set_success(false)
[hardirq ends] ...
...
complete(&test_done)
wait_for_completion(...)
kunit_get_success(&test)
In my scenario, since both CPU1 and CPU2 update the success status of
the test simultaneously, even though they are setting it to the same
value. If my understanding is correct, this could result in a
write-tear on some architectures in some circumstances. I suppose we
could just make it an atomic boolean, but I figured locking is also
fine, and generally preferred.
Also, to be clear, I am onboard with dropping then IRQ stuff for now.
I am fine moving to a mutex for the time being.
>
> >
> > > > +
> > > > + return success;
> > > > +}
> > > > +
> > > > +static void kunit_set_success(struct kunit *test, bool success)
> > > > +{
> > > > + unsigned long flags;
> > > > +
> > > > + spin_lock_irqsave(&test->lock, flags);
> > > > + test->success = success;
> > > > + spin_unlock_irqrestore(&test->lock, flags);
> > > > +}
^ permalink raw reply
* Re: [PATCH v7 1/2] fTPM: firmware TPM running in TEE
From: Jarkko Sakkinen @ 2019-06-26 23:31 UTC (permalink / raw)
To: Sasha Levin, peterhuewe, jgg
Cc: corbet, linux-kernel, linux-doc, linux-integrity, linux-kernel,
thiruan, bryankel, tee-dev, ilias.apalodimas, sumit.garg, rdunlap
In-Reply-To: <20190625201341.15865-2-sashal@kernel.org>
On Tue, 2019-06-25 at 16:13 -0400, Sasha Levin wrote:
> This patch adds support for a software-only implementation of a TPM
> running in TEE.
>
> There is extensive documentation of the design here:
>
https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/
> .
>
> As well as reference code for the firmware available here:
>
https://github.com/Microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM
>
> Tested-by: Thirupathaiah Annapureddy <thiruan@microsoft.com>
> Signed-off-by: Thirupathaiah Annapureddy <thiruan@microsoft.com>
> Signed-off-by: Sasha Levin <sashal@kernel.org>
You've used so much on this so shouldn't this have that somewhat new
co-developed-by tag? I'm also wondering can this work at all
process-wise if the original author of the patch is also the only tester
of the patch?
> ---
> drivers/char/tpm/Kconfig | 5 +
> drivers/char/tpm/Makefile | 1 +
> drivers/char/tpm/tpm_ftpm_tee.c | 356 ++++++++++++++++++++++++++++++++
> drivers/char/tpm/tpm_ftpm_tee.h | 40 ++++
> 4 files changed, 402 insertions(+)
> create mode 100644 drivers/char/tpm/tpm_ftpm_tee.c
> create mode 100644 drivers/char/tpm/tpm_ftpm_tee.h
>
> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
> index 88a3c06fc153..17bfbf9f572f 100644
> --- a/drivers/char/tpm/Kconfig
> +++ b/drivers/char/tpm/Kconfig
> @@ -164,6 +164,11 @@ config TCG_VTPM_PROXY
> /dev/vtpmX and a server-side file descriptor on which the vTPM
> can receive commands.
>
> +config TCG_FTPM_TEE
> + tristate "TEE based fTPM Interface"
> + depends on TEE && OPTEE
> + ---help---
> + This driver proxies for firmware TPM running in TEE.
>
> source "drivers/char/tpm/st33zp24/Kconfig"
> endif # TCG_TPM
> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
> index a01c4cab902a..c354cdff9c62 100644
> --- a/drivers/char/tpm/Makefile
> +++ b/drivers/char/tpm/Makefile
> @@ -33,3 +33,4 @@ obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
> obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
> obj-$(CONFIG_TCG_CRB) += tpm_crb.o
> obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
> +obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
> diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
> new file mode 100644
> index 000000000000..0312c10767bd
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_ftpm_tee.c
> @@ -0,0 +1,356 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) Microsoft Corporation
The statement does not contain the years. I'm also wondering in more
generic sense that with Git, which in its inner structure contains all
the metadata to deriving this type of information, is this more like a
legacy thing or why should be put these statements to new files?
> + *
> + * Implements a firmware TPM as described here:
> + *
>
https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/
> + *
> + * A reference implementation is available here:
> + *
>
https://github.com/microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/tee_drv.h>
> +#include <linux/tpm.h>
> +#include <linux/uuid.h>
> +
> +#include "tpm.h"
> +#include "tpm_ftpm_tee.h"
> +
> +#define DRIVER_NAME "ftpm-tee"
Should be open coded where it is used because this does not bring any
practical value.
> +
> +/*
> + * TA_FTPM_UUID: BC50D971-D4C9-42C4-82CB-343FB7F37896
> + *
> + * Randomly generated, and must correspond to the GUID on the TA side.
> + * Defined here in the reference implementation:
> + *
>
https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42
> + */
> +
Probably should delete this empty line.
> +static const uuid_t ftpm_ta_uuid =
> + UUID_INIT(0xBC50D971, 0xD4C9, 0x42C4,
> + 0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96);
> +
> +/**
> + * ftpm_tee_tpm_op_recv - retrieve fTPM response.
> + *
Should not have an empty line here.
> + * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h.
> + * @buf: the buffer to store data.
> + * @count: the number of bytes to read.
Should be aligned with a tab character.
> + *
> + * Return:
> + * In case of success the number of bytes received.
> + * On failure, -errno.
> + */
> +static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
> +{
> + struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> + size_t len;
> +
> + len = pvt_data->resp_len;
> + if (count < len) {
> + dev_err(&chip->dev,
> + "%s:Invalid size in recv: count=%zd, resp_len=%zd\n",
^ a single white space also here
> + __func__, count, len);
> + return -EIO;
> + }
> +
> + memcpy(buf, pvt_data->resp_buf, len);
> + pvt_data->resp_len = 0;
> +
> + return len;
> +}
> +
> +/**
> + * ftpm_tee_tpm_op_send - send TPM commands through the TEE shared memory.
> + *
Should not have an empty line here.
> + * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h
> + * @buf: the buffer to send.
> + * @len: the number of bytes to send.
Should be aligned with a tab character.
> + *
> + * Return:
> + * In case of success, returns 0.
> + * On failure, -errno
> + */
> +static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
> +{
> + struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
> + size_t resp_len;
> + int rc;
> + u8 *temp_buf;
> + struct tpm_header *resp_header;
> + struct tee_ioctl_invoke_arg transceive_args;
> + struct tee_param command_params[4];
> + struct tee_shm *shm = pvt_data->shm;
> +
> + if (len > MAX_COMMAND_SIZE) {
> + dev_err(&chip->dev,
> + "%s:len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM
^ a single white space also here
> TA\n",
> + __func__, len);
> + return -EIO;
> + }
> +
> + memset(&transceive_args, 0, sizeof(transceive_args));
> + memset(command_params, 0, sizeof(command_params));
> + pvt_data->resp_len = 0;
> +
> + /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */
> + transceive_args = (struct tee_ioctl_invoke_arg) {
> + .func = FTPM_OPTEE_TA_SUBMIT_COMMAND,
> + .session = pvt_data->session,
> + .num_params = 4,
> + };
> +
> + /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */
> + command_params[0] = (struct tee_param) {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
> + .u.memref = {
> + .shm = shm,
> + .size = len,
> + .shm_offs = 0,
> + },
> + };
> +
> + temp_buf = tee_shm_get_va(shm, 0);
> + if (IS_ERR(temp_buf)) {
> + dev_err(&chip->dev, "%s:tee_shm_get_va failed for transmit\n",
^ a single white space also here
> + __func__);
> + return PTR_ERR(temp_buf);
> + }
> + memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE));
> +
> + memcpy(temp_buf, buf, len);
How about:
}
memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE));
memcpy(temp_buf, buf, len);
Just looked a bit dirty how the stuff was grouped.
> +
> + command_params[1] = (struct tee_param) {
> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
> + .u.memref = {
> + .shm = shm,
> + .size = MAX_RESPONSE_SIZE,
> + .shm_offs = MAX_COMMAND_SIZE,
> + },
> + };
> +
> + rc = tee_client_invoke_func(pvt_data->ctx, &transceive_args,
> + command_params);
Aligment is not right in the 2nd like?
> + if ((rc < 0) || (transceive_args.ret != 0)) {
> + dev_err(&chip->dev, "%s:SUBMIT_COMMAND invoke error: 0x%x\n",
The white space char again...
> + __func__, transceive_args.ret);
> + return (rc < 0) ? rc : transceive_args.ret;
> + }
> +
> + temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs);
> + if (IS_ERR(temp_buf)) {
> + dev_err(&chip->dev, "%s:tee_shm_get_va failed for receive\n",
> + __func__);
> + return PTR_ERR(temp_buf);
> + }
> +
> + resp_header = (struct tpm_header *)temp_buf;
> + resp_len = be32_to_cpu(resp_header->length);
> +
> + /* sanity check resp_len */
> + if (resp_len < TPM_HEADER_SIZE) {
> + dev_err(&chip->dev, "%s:tpm response header too small\n",
> + __func__);
> + return -EIO;
> + }
> + if (resp_len > MAX_RESPONSE_SIZE) {
> + dev_err(&chip->dev,
> + "%s:resp_len=%zd exceeds MAX_RESPONSE_SIZE\n",
> + __func__, resp_len);
> + return -EIO;
> + }
> +
> + /* sanity checks look good, cache the response */
> + memcpy(pvt_data->resp_buf, temp_buf, resp_len);
> + pvt_data->resp_len = resp_len;
> +
> + return 0;
> +}
> +
> +static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip)
> +{
> + /* not supported */
> +}
> +
> +static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
> +{
> + return 0;
> +}
> +
> +static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
> +{
> + return 0;
> +}
> +
> +static const struct tpm_class_ops ftpm_tee_tpm_ops = {
> + .flags = TPM_OPS_AUTO_STARTUP,
> + .recv = ftpm_tee_tpm_op_recv,
> + .send = ftpm_tee_tpm_op_send,
> + .cancel = ftpm_tee_tpm_op_cancel,
> + .status = ftpm_tee_tpm_op_status,
> + .req_complete_mask = 0,
> + .req_complete_val = 0,
> + .req_canceled = ftpm_tee_tpm_req_canceled,
> +};
> +
> +/*
> + * Check whether this driver supports the fTPM TA in the TEE instance
> + * represented by the params (ver/data) to this function.
> + */
> +static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void
> *data)
> +{
> + /*
> + * Currently this driver only support GP Complaint OPTEE based fTPM TA
> + */
> + if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
> + (ver->gen_caps & TEE_GEN_CAP_GP))
> + return 1;
> + else
> + return 0;
> +}
> +
> +/**
> + * ftpm_tee_probe - initialize the fTPM
> + * @pdev: the platform_device description.
> + *
> + * Return:
> + * On success, 0. On failure, -errno.
> + */
> +static int ftpm_tee_probe(struct platform_device *pdev)
> +{
> + int rc;
> + struct tpm_chip *chip;
> + struct device *dev = &pdev->dev;
> + struct ftpm_tee_private *pvt_data = NULL;
> + struct tee_ioctl_open_session_arg sess_arg;
> +
> + pvt_data = devm_kzalloc(dev, sizeof(struct ftpm_tee_private),
> + GFP_KERNEL);
> + if (!pvt_data)
> + return -ENOMEM;
> +
> + dev_set_drvdata(dev, pvt_data);
> +
> + /* Open context with TEE driver */
> + pvt_data->ctx = tee_client_open_context(NULL, ftpm_tee_match, NULL,
> + NULL);
> + if (IS_ERR(pvt_data->ctx)) {
> + if (ERR_PTR(pvt_data->ctx) == -ENOENT)
> + return -EPROBE_DEFER;
> + dev_err(dev, "%s:tee_client_open_context failed\n", __func__);
> + return ERR_PTR(pvt_data->ctx);
> + }
> +
> + /* Open a session with fTPM TA */
> + memset(&sess_arg, 0, sizeof(sess_arg));
> + memcpy(sess_arg.uuid, ftpm_ta_uuid.b, TEE_IOCTL_UUID_LEN);
> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
> + sess_arg.num_params = 0;
> +
> + rc = tee_client_open_session(pvt_data->ctx, &sess_arg, NULL);
> + if ((rc < 0) || (sess_arg.ret != 0)) {
> + dev_err(dev, "%s:tee_client_open_session failed, err=%x\n",
> + __func__, sess_arg.ret);
> + rc = -EINVAL;
> + goto out_tee_session;
> + }
> + pvt_data->session = sess_arg.session;
> +
> + /* Allocate dynamic shared memory with fTPM TA */
> + pvt_data->shm = tee_shm_alloc(pvt_data->ctx,
> + (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE),
> + TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
The alignment goes a bit off also here. Seems to fit to 80 chars even
when it is nicely aligned (had to test):
pvt_data->shm = tee_shm_alloc(pvt_data->ctx,
MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE,
TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
Also had one pair of redundant parentheses.
> + if (IS_ERR(pvt_data->shm)) {
> + dev_err(dev, "%s:tee_shm_alloc failed\n", __func__);
The white space character.
> + rc = -ENOMEM;
> + goto out_shm_alloc;
> + }
> +
> + /* Allocate new struct tpm_chip instance */
> + chip = tpm_chip_alloc(dev, &ftpm_tee_tpm_ops);
> + if (IS_ERR(chip)) {
> + dev_err(dev, "%s:tpm_chip_alloc failed\n", __func__);
The white space character.
> + rc = PTR_ERR(chip);
> + goto out_chip_alloc;
> + }
> +
> + pvt_data->chip = chip;
> + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2;
> +
> + /* Create a character device for the fTPM */
> + rc = tpm_chip_register(pvt_data->chip);
> + if (rc) {
> + dev_err(dev, "%s:tpm_chip_register failed with rc=%d\n",
The white space character.
> + __func__, rc);
> + goto out_chip;
> + }
> +
> + return 0;
> +
> +out_chip:
> + put_device(&pvt_data->chip->dev);
> +out_chip_alloc:
> + tee_shm_free(pvt_data->shm);
> +out_shm_alloc:
> + tee_client_close_session(pvt_data->ctx, pvt_data->session);
> +out_tee_session:
> + tee_client_close_context(pvt_data->ctx);
> +
> + return rc;
> +}
> +
> +/**
> + * ftpm_tee_remove - remove the TPM device
> + * @pdev: the platform_device description.
> + *
> + * Return:
> + * 0 in case of success.
"0 always" ? Left me puzzling with questions in that form.
> + */
> +static int ftpm_tee_remove(struct platform_device *pdev)
> +{
> + struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev);
> +
> + /* Release the chip */
> + tpm_chip_unregister(pvt_data->chip);
> +
> + /* frees chip */
> + put_device(&pvt_data->chip->dev);
> +
> + /* Free the shared memory pool */
> + tee_shm_free(pvt_data->shm);
> +
> + /* close the existing session with fTPM TA*/
> + tee_client_close_session(pvt_data->ctx, pvt_data->session);
> +
> + /* close the context with TEE driver */
> + tee_client_close_context(pvt_data->ctx);
> +
> + /* memory allocated with devm_kzalloc() is freed automatically */
> +
> + return 0;
> +}
> +
> +static const struct of_device_id of_ftpm_tee_ids[] = {
> + { .compatible = "microsoft,ftpm" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids);
> +
> +static struct platform_driver ftpm_tee_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = of_match_ptr(of_ftpm_tee_ids),
> + },
> + .probe = ftpm_tee_probe,
> + .remove = ftpm_tee_remove,
> +};
> +
> +module_platform_driver(ftpm_tee_driver);
> +
> +MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>");
I'm also wondering why we put MODULE_AUTHOR() to new modules...
> +MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/char/tpm/tpm_ftpm_tee.h b/drivers/char/tpm/tpm_ftpm_tee.h
> new file mode 100644
> index 000000000000..b09ee7be4545
> --- /dev/null
> +++ b/drivers/char/tpm/tpm_ftpm_tee.h
> @@ -0,0 +1,40 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) Microsoft Corporation
> + */
> +
> +#ifndef __TPM_FTPM_TEE_H__
> +#define __TPM_FTPM_TEE_H__
> +
> +#include <linux/tee_drv.h>
> +#include <linux/tpm.h>
> +#include <linux/uuid.h>
> +
> +/* The TAFs ID implemented in this TA */
> +#define FTPM_OPTEE_TA_SUBMIT_COMMAND (0)
> +#define FTPM_OPTEE_TA_EMULATE_PPI (1)
> +
> +/* max. buffer size supported by fTPM */
> +#define MAX_COMMAND_SIZE 4096
> +#define MAX_RESPONSE_SIZE 4096
Two whitespace chars after "#define".
> +
> +/**
> + * struct ftpm_tee_private - fTPM's private data
> + * @chip: struct tpm_chip instance registered with tpm framework.
> + * @state: internal state
> + * @session: fTPM TA session identifier.
> + * @resp_len: cached response buffer length.
> + * @resp_buf: cached response buffer.
> + * @ctx: TEE context handler.
> + * @shm: Memory pool shared with fTPM TA in TEE.
> + */
> +struct ftpm_tee_private {
> + struct tpm_chip *chip;
> + u32 session;
> + size_t resp_len;
> + u8 resp_buf[MAX_RESPONSE_SIZE];
> + struct tee_context *ctx;
> + struct tee_shm *shm;
> +};
> +
> +#endif /* __TPM_FTPM_TEE_H__ */
/Jarkko
^ permalink raw reply
* Re: [PATCH v7 2/2] fTPM: add documentation for ftpm driver
From: Jarkko Sakkinen @ 2019-06-26 23:34 UTC (permalink / raw)
To: Sasha Levin, peterhuewe, jgg
Cc: corbet, linux-kernel, linux-doc, linux-integrity, linux-kernel,
thiruan, bryankel, tee-dev, ilias.apalodimas, sumit.garg, rdunlap
In-Reply-To: <20190625201341.15865-3-sashal@kernel.org>
On Tue, 2019-06-25 at 16:13 -0400, Sasha Levin wrote:
> This patch adds basic documentation to describe the new fTPM driver.
>
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
> Documentation/security/tpm/index.rst | 1 +
> Documentation/security/tpm/tpm_ftpm_tee.rst | 31 +++++++++++++++++++++
> 2 files changed, 32 insertions(+)
> create mode 100644 Documentation/security/tpm/tpm_ftpm_tee.rst
>
> diff --git a/Documentation/security/tpm/index.rst
> b/Documentation/security/tpm/index.rst
> index af77a7bbb070..15783668644f 100644
> --- a/Documentation/security/tpm/index.rst
> +++ b/Documentation/security/tpm/index.rst
> @@ -4,4 +4,5 @@ Trusted Platform Module documentation
>
> .. toctree::
>
> + tpm_ftpm_tee
> tpm_vtpm_proxy
> diff --git a/Documentation/security/tpm/tpm_ftpm_tee.rst
> b/Documentation/security/tpm/tpm_ftpm_tee.rst
> new file mode 100644
> index 000000000000..48de0dcec0f6
> --- /dev/null
> +++ b/Documentation/security/tpm/tpm_ftpm_tee.rst
> @@ -0,0 +1,31 @@
> +=============================================
> +Firmware TPM Driver
> +=============================================
> +
> +| Authors:
> +| Thirupathaiah Annapureddy <thiruan@microsoft.com>
> +| Sasha Levin <sashal@kernel.org>
The way I see it this thing should not exist here at all but the
first patch should have co-developed-by in addition to
signed-off-by from you. Otherwise looks good. The list of authors
is againt something that Git does better than humans.
/Jarkko
^ permalink raw reply
* Re: [PATCH v7 1/2] fTPM: firmware TPM running in TEE
From: Sasha Levin @ 2019-06-26 23:56 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: peterhuewe, jgg, corbet, linux-kernel, linux-doc, linux-integrity,
linux-kernel, thiruan, bryankel, tee-dev, ilias.apalodimas,
sumit.garg, rdunlap
In-Reply-To: <673dd30d03e8ed9825bb46ef21b2efef015f6f2a.camel@linux.intel.com>
On Thu, Jun 27, 2019 at 02:31:35AM +0300, Jarkko Sakkinen wrote:
>On Tue, 2019-06-25 at 16:13 -0400, Sasha Levin wrote:
>> This patch adds support for a software-only implementation of a TPM
>> running in TEE.
>>
>> There is extensive documentation of the design here:
>>
>https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/
>> .
>>
>> As well as reference code for the firmware available here:
>>
>https://github.com/Microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM
>>
>> Tested-by: Thirupathaiah Annapureddy <thiruan@microsoft.com>
>> Signed-off-by: Thirupathaiah Annapureddy <thiruan@microsoft.com>
>> Signed-off-by: Sasha Levin <sashal@kernel.org>
>
>You've used so much on this so shouldn't this have that somewhat new
>co-developed-by tag? I'm also wondering can this work at all
Honestly, I've just been massaging this patch more than "authoring" it.
If you feel strongly about it feel free to add a Co-authored patch with
my name, but in my mind this is just Thiru's work.
>process-wise if the original author of the patch is also the only tester
>of the patch?
There's not much we can do about this... Linaro folks have tested this
without the fTPM firmware, so at the very least it won't explode for
everyone. If for some reason non-microsoft folks see issues then we can
submit patches on top to fix this, we're not just throwing this at you
and running away.
>> ---
>> drivers/char/tpm/Kconfig | 5 +
>> drivers/char/tpm/Makefile | 1 +
>> drivers/char/tpm/tpm_ftpm_tee.c | 356 ++++++++++++++++++++++++++++++++
>> drivers/char/tpm/tpm_ftpm_tee.h | 40 ++++
>> 4 files changed, 402 insertions(+)
>> create mode 100644 drivers/char/tpm/tpm_ftpm_tee.c
>> create mode 100644 drivers/char/tpm/tpm_ftpm_tee.h
>>
>> diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
>> index 88a3c06fc153..17bfbf9f572f 100644
>> --- a/drivers/char/tpm/Kconfig
>> +++ b/drivers/char/tpm/Kconfig
>> @@ -164,6 +164,11 @@ config TCG_VTPM_PROXY
>> /dev/vtpmX and a server-side file descriptor on which the vTPM
>> can receive commands.
>>
>> +config TCG_FTPM_TEE
>> + tristate "TEE based fTPM Interface"
>> + depends on TEE && OPTEE
>> + ---help---
>> + This driver proxies for firmware TPM running in TEE.
>>
>> source "drivers/char/tpm/st33zp24/Kconfig"
>> endif # TCG_TPM
>> diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile
>> index a01c4cab902a..c354cdff9c62 100644
>> --- a/drivers/char/tpm/Makefile
>> +++ b/drivers/char/tpm/Makefile
>> @@ -33,3 +33,4 @@ obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/
>> obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o
>> obj-$(CONFIG_TCG_CRB) += tpm_crb.o
>> obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o
>> +obj-$(CONFIG_TCG_FTPM_TEE) += tpm_ftpm_tee.o
>> diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
>> new file mode 100644
>> index 000000000000..0312c10767bd
>> --- /dev/null
>> +++ b/drivers/char/tpm/tpm_ftpm_tee.c
>> @@ -0,0 +1,356 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) Microsoft Corporation
>
>The statement does not contain the years. I'm also wondering in more
>generic sense that with Git, which in its inner structure contains all
>the metadata to deriving this type of information, is this more like a
>legacy thing or why should be put these statements to new files?
There is a significant amount of newly added 2019 copyright tags in the
kernel, and this is just following suit.
>> + *
>> + * Implements a firmware TPM as described here:
>> + *
>>
>https://www.microsoft.com/en-us/research/publication/ftpm-software-implementation-tpm-chip/
>> + *
>> + * A reference implementation is available here:
>> + *
>>
>https://github.com/microsoft/ms-tpm-20-ref/tree/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/of.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/tee_drv.h>
>> +#include <linux/tpm.h>
>> +#include <linux/uuid.h>
>> +
>> +#include "tpm.h"
>> +#include "tpm_ftpm_tee.h"
>> +
>> +#define DRIVER_NAME "ftpm-tee"
>
>Should be open coded where it is used because this does not bring any
>practical value.
Good point, this was used more with debug statements that were sprinkled
around, but not anymore.
>> +
>> +/*
>> + * TA_FTPM_UUID: BC50D971-D4C9-42C4-82CB-343FB7F37896
>> + *
>> + * Randomly generated, and must correspond to the GUID on the TA side.
>> + * Defined here in the reference implementation:
>> + *
>>
>https://github.com/microsoft/ms-tpm-20-ref/blob/master/Samples/ARM32-FirmwareTPM/optee_ta/fTPM/include/fTPM.h#L42
>> + */
>> +
>
>Probably should delete this empty line.
>
>> +static const uuid_t ftpm_ta_uuid =
>> + UUID_INIT(0xBC50D971, 0xD4C9, 0x42C4,
>> + 0x82, 0xCB, 0x34, 0x3F, 0xB7, 0xF3, 0x78, 0x96);
>> +
>> +/**
>> + * ftpm_tee_tpm_op_recv - retrieve fTPM response.
>> + *
>
>Should not have an empty line here.
Really? The rest of the kdoc comments under drivers/char/tpm/ have an
empty line after function name.
I've looked at tpm_crb.c which you authored for reference.
>> + * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h.
>> + * @buf: the buffer to store data.
>> + * @count: the number of bytes to read.
>
>Should be aligned with a tab character.
I'll address this throughout the comments in the file.
>> + *
>> + * Return:
>> + * In case of success the number of bytes received.
>> + * On failure, -errno.
>> + */
>> +static int ftpm_tee_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count)
>> +{
>> + struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
>> + size_t len;
>> +
>> + len = pvt_data->resp_len;
>> + if (count < len) {
>> + dev_err(&chip->dev,
>> + "%s:Invalid size in recv: count=%zd, resp_len=%zd\n",
> ^ a single white space also here
>
>> + __func__, count, len);
>> + return -EIO;
>> + }
>> +
>> + memcpy(buf, pvt_data->resp_buf, len);
>> + pvt_data->resp_len = 0;
>> +
>> + return len;
>> +}
>> +
>> +/**
>> + * ftpm_tee_tpm_op_send - send TPM commands through the TEE shared memory.
>> + *
>
>Should not have an empty line here.
>
>> + * @chip: the tpm_chip description as specified in driver/char/tpm/tpm.h
>> + * @buf: the buffer to send.
>> + * @len: the number of bytes to send.
>
>Should be aligned with a tab character.
>
>> + *
>> + * Return:
>> + * In case of success, returns 0.
>> + * On failure, -errno
>> + */
>> +static int ftpm_tee_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t len)
>> +{
>> + struct ftpm_tee_private *pvt_data = dev_get_drvdata(chip->dev.parent);
>> + size_t resp_len;
>> + int rc;
>> + u8 *temp_buf;
>> + struct tpm_header *resp_header;
>> + struct tee_ioctl_invoke_arg transceive_args;
>> + struct tee_param command_params[4];
>> + struct tee_shm *shm = pvt_data->shm;
>> +
>> + if (len > MAX_COMMAND_SIZE) {
>> + dev_err(&chip->dev,
>> + "%s:len=%zd exceeds MAX_COMMAND_SIZE supported by fTPM
> ^ a single white space also here
>
>> TA\n",
>> + __func__, len);
>> + return -EIO;
>> + }
>> +
>> + memset(&transceive_args, 0, sizeof(transceive_args));
>> + memset(command_params, 0, sizeof(command_params));
>> + pvt_data->resp_len = 0;
>> +
>> + /* Invoke FTPM_OPTEE_TA_SUBMIT_COMMAND function of fTPM TA */
>> + transceive_args = (struct tee_ioctl_invoke_arg) {
>> + .func = FTPM_OPTEE_TA_SUBMIT_COMMAND,
>> + .session = pvt_data->session,
>> + .num_params = 4,
>> + };
>> +
>> + /* Fill FTPM_OPTEE_TA_SUBMIT_COMMAND parameters */
>> + command_params[0] = (struct tee_param) {
>> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT,
>> + .u.memref = {
>> + .shm = shm,
>> + .size = len,
>> + .shm_offs = 0,
>> + },
>> + };
>> +
>> + temp_buf = tee_shm_get_va(shm, 0);
>> + if (IS_ERR(temp_buf)) {
>> + dev_err(&chip->dev, "%s:tee_shm_get_va failed for transmit\n",
> ^ a single white space also here
>
>> + __func__);
>> + return PTR_ERR(temp_buf);
>> + }
>> + memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE));
>> +
>> + memcpy(temp_buf, buf, len);
>
>How about:
>
> }
>
> memset(temp_buf, 0, (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE));
> memcpy(temp_buf, buf, len);
>
>Just looked a bit dirty how the stuff was grouped.
Makes sense.
>> +
>> + command_params[1] = (struct tee_param) {
>> + .attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT,
>> + .u.memref = {
>> + .shm = shm,
>> + .size = MAX_RESPONSE_SIZE,
>> + .shm_offs = MAX_COMMAND_SIZE,
>> + },
>> + };
>> +
>> + rc = tee_client_invoke_func(pvt_data->ctx, &transceive_args,
>> + command_params);
>
>Aligment is not right in the 2nd like?
>
>> + if ((rc < 0) || (transceive_args.ret != 0)) {
>> + dev_err(&chip->dev, "%s:SUBMIT_COMMAND invoke error: 0x%x\n",
>
>The white space char again...
>
>> + __func__, transceive_args.ret);
>> + return (rc < 0) ? rc : transceive_args.ret;
>> + }
>> +
>> + temp_buf = tee_shm_get_va(shm, command_params[1].u.memref.shm_offs);
>> + if (IS_ERR(temp_buf)) {
>> + dev_err(&chip->dev, "%s:tee_shm_get_va failed for receive\n",
>> + __func__);
>> + return PTR_ERR(temp_buf);
>> + }
>> +
>> + resp_header = (struct tpm_header *)temp_buf;
>> + resp_len = be32_to_cpu(resp_header->length);
>> +
>> + /* sanity check resp_len */
>> + if (resp_len < TPM_HEADER_SIZE) {
>> + dev_err(&chip->dev, "%s:tpm response header too small\n",
>> + __func__);
>> + return -EIO;
>> + }
>> + if (resp_len > MAX_RESPONSE_SIZE) {
>> + dev_err(&chip->dev,
>> + "%s:resp_len=%zd exceeds MAX_RESPONSE_SIZE\n",
>> + __func__, resp_len);
>> + return -EIO;
>> + }
>> +
>> + /* sanity checks look good, cache the response */
>> + memcpy(pvt_data->resp_buf, temp_buf, resp_len);
>> + pvt_data->resp_len = resp_len;
>> +
>> + return 0;
>> +}
>> +
>> +static void ftpm_tee_tpm_op_cancel(struct tpm_chip *chip)
>> +{
>> + /* not supported */
>> +}
>> +
>> +static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
>> +{
>> + return 0;
>> +}
>> +
>> +static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
>> +{
>> + return 0;
>> +}
>> +
>> +static const struct tpm_class_ops ftpm_tee_tpm_ops = {
>> + .flags = TPM_OPS_AUTO_STARTUP,
>> + .recv = ftpm_tee_tpm_op_recv,
>> + .send = ftpm_tee_tpm_op_send,
>> + .cancel = ftpm_tee_tpm_op_cancel,
>> + .status = ftpm_tee_tpm_op_status,
>> + .req_complete_mask = 0,
>> + .req_complete_val = 0,
>> + .req_canceled = ftpm_tee_tpm_req_canceled,
>> +};
>> +
>> +/*
>> + * Check whether this driver supports the fTPM TA in the TEE instance
>> + * represented by the params (ver/data) to this function.
>> + */
>> +static int ftpm_tee_match(struct tee_ioctl_version_data *ver, const void
>> *data)
>> +{
>> + /*
>> + * Currently this driver only support GP Complaint OPTEE based fTPM TA
>> + */
>> + if ((ver->impl_id == TEE_IMPL_ID_OPTEE) &&
>> + (ver->gen_caps & TEE_GEN_CAP_GP))
>> + return 1;
>> + else
>> + return 0;
>> +}
>> +
>> +/**
>> + * ftpm_tee_probe - initialize the fTPM
>> + * @pdev: the platform_device description.
>> + *
>> + * Return:
>> + * On success, 0. On failure, -errno.
>> + */
>> +static int ftpm_tee_probe(struct platform_device *pdev)
>> +{
>> + int rc;
>> + struct tpm_chip *chip;
>> + struct device *dev = &pdev->dev;
>> + struct ftpm_tee_private *pvt_data = NULL;
>> + struct tee_ioctl_open_session_arg sess_arg;
>> +
>> + pvt_data = devm_kzalloc(dev, sizeof(struct ftpm_tee_private),
>> + GFP_KERNEL);
>> + if (!pvt_data)
>> + return -ENOMEM;
>> +
>> + dev_set_drvdata(dev, pvt_data);
>> +
>> + /* Open context with TEE driver */
>> + pvt_data->ctx = tee_client_open_context(NULL, ftpm_tee_match, NULL,
>> + NULL);
>> + if (IS_ERR(pvt_data->ctx)) {
>> + if (ERR_PTR(pvt_data->ctx) == -ENOENT)
>> + return -EPROBE_DEFER;
>> + dev_err(dev, "%s:tee_client_open_context failed\n", __func__);
>> + return ERR_PTR(pvt_data->ctx);
>> + }
>> +
>> + /* Open a session with fTPM TA */
>> + memset(&sess_arg, 0, sizeof(sess_arg));
>> + memcpy(sess_arg.uuid, ftpm_ta_uuid.b, TEE_IOCTL_UUID_LEN);
>> + sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
>> + sess_arg.num_params = 0;
>> +
>> + rc = tee_client_open_session(pvt_data->ctx, &sess_arg, NULL);
>> + if ((rc < 0) || (sess_arg.ret != 0)) {
>> + dev_err(dev, "%s:tee_client_open_session failed, err=%x\n",
>> + __func__, sess_arg.ret);
>> + rc = -EINVAL;
>> + goto out_tee_session;
>> + }
>> + pvt_data->session = sess_arg.session;
>> +
>> + /* Allocate dynamic shared memory with fTPM TA */
>> + pvt_data->shm = tee_shm_alloc(pvt_data->ctx,
>> + (MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE),
>> + TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
>
>The alignment goes a bit off also here. Seems to fit to 80 chars even
>when it is nicely aligned (had to test):
>
> pvt_data->shm = tee_shm_alloc(pvt_data->ctx,
> MAX_COMMAND_SIZE + MAX_RESPONSE_SIZE,
> TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
>
>Also had one pair of redundant parentheses.
Just in case math breaks! (fixed).
>
>> + if (IS_ERR(pvt_data->shm)) {
>> + dev_err(dev, "%s:tee_shm_alloc failed\n", __func__);
>
>The white space character.
>
>> + rc = -ENOMEM;
>> + goto out_shm_alloc;
>> + }
>> +
>> + /* Allocate new struct tpm_chip instance */
>> + chip = tpm_chip_alloc(dev, &ftpm_tee_tpm_ops);
>> + if (IS_ERR(chip)) {
>> + dev_err(dev, "%s:tpm_chip_alloc failed\n", __func__);
>
>The white space character.
>
>> + rc = PTR_ERR(chip);
>> + goto out_chip_alloc;
>> + }
>> +
>> + pvt_data->chip = chip;
>> + pvt_data->chip->flags |= TPM_CHIP_FLAG_TPM2;
>> +
>> + /* Create a character device for the fTPM */
>> + rc = tpm_chip_register(pvt_data->chip);
>> + if (rc) {
>> + dev_err(dev, "%s:tpm_chip_register failed with rc=%d\n",
>
>The white space character.
>
>> + __func__, rc);
>> + goto out_chip;
>> + }
>> +
>> + return 0;
>> +
>> +out_chip:
>> + put_device(&pvt_data->chip->dev);
>> +out_chip_alloc:
>> + tee_shm_free(pvt_data->shm);
>> +out_shm_alloc:
>> + tee_client_close_session(pvt_data->ctx, pvt_data->session);
>> +out_tee_session:
>> + tee_client_close_context(pvt_data->ctx);
>> +
>> + return rc;
>> +}
>> +
>> +/**
>> + * ftpm_tee_remove - remove the TPM device
>> + * @pdev: the platform_device description.
>> + *
>> + * Return:
>> + * 0 in case of success.
>
>"0 always" ? Left me puzzling with questions in that form.
Technically it's true :) (fixed).
>> + */
>> +static int ftpm_tee_remove(struct platform_device *pdev)
>> +{
>> + struct ftpm_tee_private *pvt_data = dev_get_drvdata(&pdev->dev);
>> +
>> + /* Release the chip */
>> + tpm_chip_unregister(pvt_data->chip);
>> +
>> + /* frees chip */
>> + put_device(&pvt_data->chip->dev);
>> +
>> + /* Free the shared memory pool */
>> + tee_shm_free(pvt_data->shm);
>> +
>> + /* close the existing session with fTPM TA*/
>> + tee_client_close_session(pvt_data->ctx, pvt_data->session);
>> +
>> + /* close the context with TEE driver */
>> + tee_client_close_context(pvt_data->ctx);
>> +
>> + /* memory allocated with devm_kzalloc() is freed automatically */
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id of_ftpm_tee_ids[] = {
>> + { .compatible = "microsoft,ftpm" },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(of, of_ftpm_tee_ids);
>> +
>> +static struct platform_driver ftpm_tee_driver = {
>> + .driver = {
>> + .name = DRIVER_NAME,
>> + .of_match_table = of_match_ptr(of_ftpm_tee_ids),
>> + },
>> + .probe = ftpm_tee_probe,
>> + .remove = ftpm_tee_remove,
>> +};
>> +
>> +module_platform_driver(ftpm_tee_driver);
>> +
>> +MODULE_AUTHOR("Thirupathaiah Annapureddy <thiruan@microsoft.com>");
>
>I'm also wondering why we put MODULE_AUTHOR() to new modules...
It shows up in modinfo and is a nice way to credit the author IMHO.
>> +MODULE_DESCRIPTION("TPM Driver for fTPM TA in TEE");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/char/tpm/tpm_ftpm_tee.h b/drivers/char/tpm/tpm_ftpm_tee.h
>> new file mode 100644
>> index 000000000000..b09ee7be4545
>> --- /dev/null
>> +++ b/drivers/char/tpm/tpm_ftpm_tee.h
>> @@ -0,0 +1,40 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) Microsoft Corporation
>> + */
>> +
>> +#ifndef __TPM_FTPM_TEE_H__
>> +#define __TPM_FTPM_TEE_H__
>> +
>> +#include <linux/tee_drv.h>
>> +#include <linux/tpm.h>
>> +#include <linux/uuid.h>
>> +
>> +/* The TAFs ID implemented in this TA */
>> +#define FTPM_OPTEE_TA_SUBMIT_COMMAND (0)
>> +#define FTPM_OPTEE_TA_EMULATE_PPI (1)
>> +
>> +/* max. buffer size supported by fTPM */
>> +#define MAX_COMMAND_SIZE 4096
>> +#define MAX_RESPONSE_SIZE 4096
>
>Two whitespace chars after "#define".
Fixed.
>> +
>> +/**
>> + * struct ftpm_tee_private - fTPM's private data
>> + * @chip: struct tpm_chip instance registered with tpm framework.
>> + * @state: internal state
>> + * @session: fTPM TA session identifier.
>> + * @resp_len: cached response buffer length.
>> + * @resp_buf: cached response buffer.
>> + * @ctx: TEE context handler.
>> + * @shm: Memory pool shared with fTPM TA in TEE.
>> + */
>> +struct ftpm_tee_private {
>> + struct tpm_chip *chip;
>> + u32 session;
>> + size_t resp_len;
>> + u8 resp_buf[MAX_RESPONSE_SIZE];
>> + struct tee_context *ctx;
>> + struct tee_shm *shm;
>> +};
>> +
>> +#endif /* __TPM_FTPM_TEE_H__ */
>
>/Jarkko
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH v7 2/2] fTPM: add documentation for ftpm driver
From: Sasha Levin @ 2019-06-26 23:59 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: peterhuewe, jgg, corbet, linux-kernel, linux-doc, linux-integrity,
linux-kernel, thiruan, bryankel, tee-dev, ilias.apalodimas,
sumit.garg, rdunlap
In-Reply-To: <4bb41de0cd2b0bf89a20dd55576acf15594ae8de.camel@linux.intel.com>
On Thu, Jun 27, 2019 at 02:34:06AM +0300, Jarkko Sakkinen wrote:
>On Tue, 2019-06-25 at 16:13 -0400, Sasha Levin wrote:
>> This patch adds basic documentation to describe the new fTPM driver.
>>
>> Signed-off-by: Sasha Levin <sashal@kernel.org>
>> ---
>> Documentation/security/tpm/index.rst | 1 +
>> Documentation/security/tpm/tpm_ftpm_tee.rst | 31 +++++++++++++++++++++
>> 2 files changed, 32 insertions(+)
>> create mode 100644 Documentation/security/tpm/tpm_ftpm_tee.rst
>>
>> diff --git a/Documentation/security/tpm/index.rst
>> b/Documentation/security/tpm/index.rst
>> index af77a7bbb070..15783668644f 100644
>> --- a/Documentation/security/tpm/index.rst
>> +++ b/Documentation/security/tpm/index.rst
>> @@ -4,4 +4,5 @@ Trusted Platform Module documentation
>>
>> .. toctree::
>>
>> + tpm_ftpm_tee
>> tpm_vtpm_proxy
>> diff --git a/Documentation/security/tpm/tpm_ftpm_tee.rst
>> b/Documentation/security/tpm/tpm_ftpm_tee.rst
>> new file mode 100644
>> index 000000000000..48de0dcec0f6
>> --- /dev/null
>> +++ b/Documentation/security/tpm/tpm_ftpm_tee.rst
>> @@ -0,0 +1,31 @@
>> +=============================================
>> +Firmware TPM Driver
>> +=============================================
>> +
>> +| Authors:
>> +| Thirupathaiah Annapureddy <thiruan@microsoft.com>
>> +| Sasha Levin <sashal@kernel.org>
>
>The way I see it this thing should not exist here at all but the
>first patch should have co-developed-by in addition to
>signed-off-by from you. Otherwise looks good. The list of authors
>is againt something that Git does better than humans.
I've dropped this part, but left the co-developed tag out, I honestly
don't think that I had significant contribution here.
--
Thanks,
Sasha
^ permalink raw reply
* Re: [PATCH v5 01/18] kunit: test: add KUnit test runner core
From: Brendan Higgins @ 2019-06-27 0:05 UTC (permalink / raw)
To: Luis Chamberlain
Cc: Stephen Boyd, Frank Rowand, Greg KH, Josh Poimboeuf, Kees Cook,
Kieran Bingham, Peter Zijlstra, Rob Herring, shuah,
Theodore Ts'o, Masahiro Yamada, devicetree, dri-devel,
kunit-dev, open list:DOCUMENTATION, linux-fsdevel, linux-kbuild,
Linux Kernel Mailing List, open list:KERNEL SELFTEST FRAMEWORK,
linux-nvdimm, linux-um, Sasha Levin, Bird, Timothy,
Amir Goldstein, Dan Carpenter, Daniel Vetter, Jeff Dike,
Joel Stanley, Julia Lawall, Kevin Hilman, Knut Omang,
Logan Gunthorpe, Michael Ellerman, Petr Mladek, Randy Dunlap,
Richard Weinberger, David Rientjes, Steven Rostedt, wfg
In-Reply-To: <20190626220204.GZ19023@42.do-not-panic.com>
On Wed, Jun 26, 2019 at 3:02 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
>
> On Tue, Jun 25, 2019 at 11:41:47PM -0700, Brendan Higgins wrote:
> > On Tue, Jun 25, 2019 at 4:02 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
> > >
> > > On Tue, Jun 25, 2019 at 03:14:45PM -0700, Brendan Higgins wrote:
> > > > On Tue, Jun 25, 2019 at 2:44 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
> > > > > Since its a new architecture and since you seem to imply most tests
> > > > > don't require locking or even IRQs disabled, I think its worth to
> > > > > consider the impact of adding such extreme locking requirements for
> > > > > an initial ramp up.
> > > >
> > > > Fair enough, I can see the point of not wanting to use irq disabled
> > > > until we get someone complaining about it, but I think making it
> > > > thread safe is reasonable. It means there is one less thing to confuse
> > > > a KUnit user and the only penalty paid is some very minor performance.
> > >
> > > One reason I'm really excited about kunit is speed... so by all means I
> > > think we're at a good point to analyze performance optimizationsm if
> > > they do make sense.
> >
> > Yeah, but I think there are much lower hanging fruit than this (as you
> > point out below). I am all for making/keeping KUnit super fast, but I
> > also don't want to waste time with premature optimizations and I think
> > having thread safe expectations and non-thread safe expectations hurts
> > usability.
> >
> > Still, I am on board with making this a mutex instead of a spinlock for now.
> >
> > > While on the topic of parallization, what about support for running
> > > different test cases in parallel? Or at the very least different kunit
> > > modules in parallel. Few questions come up based on this prospect:
> > >
> > > * Why not support parallelism from the start?
> >
> > Just because it is more work and there isn't much to gain from it right now.
> >
> > Some numbers:
> > I currently have a collection of 86 test cases in the branch that this
> > patchset is from.
>
> Impressive, imagine 86 tests and kunit is not even *merged yet*.
Full disclaimer, about half of them are KUnit tests for KUnit - to
make sure KUnit works, so I don't know if you consider that cheating.
> > I turned on PRINTK_TIME and looked at the first
> > KUnit output and the last. On UML, start time was 0.090000, and end
> > time was 0.090000. Looks like sched_clock is not very good on UML.
>
> Since you have a python thing that kicks tests, what if you just run
> time on it?
That's what I did on the this following paragraph (I just couldn't
time the tests by themselves in this case):
> > Still it seems quite likely that all of these tests run around 0.01
> > seconds or less on UML: I ran KUnit with only 2 test cases enabled
> > three times and got an average runtime of 1.55867 seconds with a
> > standard deviation of 0.0346747. I then ran it another three times
> > with all test cases enabled and got an average runtime of 1.535
> > seconds with a standard deviation of 0.0150997. The second average is
> > less, but that doesn't really mean anything because it is well within
> > one standard deviation with a very small sample size. Nevertheless, we
> > can conclude that the actual runtime of those 84 test cases is most
> > likely within one standard deviation, so on the order of 0.01 seconds.
> >
> > On x86 running on QEMU, first message from KUnit was printed at
> > 0.194251 and the last KUnit message was printed at 0.340915, meaning
> > that all 86 test cases ran in about 0.146664 seconds.
>
> Pretty impressive numbers. But can you blame me for expressing the
> desire to possibly being able to do better? I am not saying -- let's
> definitely have parallelism in place *now*. Just wanted to hear out
> tangibles of why we *don't* want it now.
I agree, faster is almost always better in these types of things, and
certainly is in this case.
In fairness to you, I also short sold the speed of KUnit in the cover
letter. I was too lazy to do this complete of an analysis back when I
wrote it (even if I did a complete timing like this, I would have to
put a bunch of asterisks since it wouldn't include the time to "boot"
the kernel or to build it, which vastly outstrip the speed of
individual test cases). And given the original numbers, I think
speeding things up would probably seem more urgent. So no, I really
cannot blame you.
Sorry if it came across that I was frustrated or impatient, but I am
actually glad you asked because I now have this public email where I
did the full analysis of how fast KUnit really is which I can refer to
in the future.
> And.. also, since we are reviewing now, try to target so that the code
> can later likely get a face lift to support parallelism without
> requiring much changes.
Also fair.
> > In any case, running KUnit tests in parallel is definitely something I
> > plan on adding it eventually, but it just doesn't really seem worth it
> > right now.
>
> Makes sense!
>
> > I find the incremental build time of the kernel to
> > typically be between 3 and 30 seconds, and a clean build to be between
> > 30 seconds to several minutes, depending on the number of available
> > cores, so I don't think most users would even notice the amount of
> > runtime contributed by the actual unit tests until we start getting
> > into the 1000s of test cases. I don't suspect it will become an issue
> > until we get into the 10,000s of test cases. I think we are a pretty
> > long way off from that.
>
> All makes sense, and agreed based on the numbers you are providing.
> Thanks for the details!
>
> > > * Are you opposed to eventually having this added? For instance, there is
> > > enough code on lib/test_kmod.c for batching tons of kthreads each
> > > one running its own thing for testing purposes which could be used
> > > as template.
> >
> > I am not opposed to adding it eventually at all. I actually plan on
> > doing so, just not in this patchset. There are a lot of additional
> > features, improvements, and sugar that I really want to add, so much
> > so that most of it doesn't belong in this patchset; I just think this
> > is one of those things that belongs in a follow up. I tried to boil
> > down this patchset to as small as I could while still being useful;
> > this is basically an MVP. Maybe after this patchset gets merged I
> > should post a list of things I have ready for review, or would like to
> > work on, and people can comment on what things they want to see next.
>
> Groovy.
Cool, I will do that then!
> > > * If we eventually *did* support it:
> > > - Would logs be skewed?
> >
> > Probably, before I went with the TAP approach, I was tagging each
> > message with the test case it came from and I could have parsed it and
> > assembled a coherent view of the logs using that; now that I am using
> > TAP conforming output, that won't work. I haven't really thought too
> > hard about how to address it, but there are ways. For the UML users, I
> > am planning on adding a feature to guarantee hermeticity between tests
> > running in different modules by adding a feature that allows a single
> > kernel to be built with all tests included, and then determine which
> > tests get run by passing in command line arguments or something. This
> > way you can get the isolation from running tests in separate
> > environments without increasing the build cost. We could also use this
> > method to achieve parallelism by dispatching multiple kernels at once.
>
> Indeed. Or... wait for it... containers... There tools for these sorts
> of things already. And I'm evaluating such prospects now for some other
> tests I care for.
Containers could definitely be useful in the long run. I have a long
term goal to build and run just parts of the kernel as I have
mentioned to you, and doing so in a totally hermetic environment could
provide a lot of value; in this case I would probably only want a
chroot, but if I want to deploy tests to run on different machines
containers could be very useful.
Actually, on the topic of containers for running tests, the presubmit
system I have set up for KUnit uses containers for deploying KUnit on
servers for testing[1]. Actually, I have some experimental patches to
make it work with LKML instead of Gerrit, but I am not sure whether it
makes more sense to go that route, with one of the many patchworks
clones that support presubmit, or something else.
> > That only works for UML, but I imagine you could do something similar
> > for users running tests under qemu.
>
> Or containers.
>
> > > - Could we have a way to query: give me log for only kunit module
> > > named "foo"?
> >
> > Yeah, I think that would make sense as part of the hermeticity thing I
> > mentioned above.
> >
> > Hope that seems reasonable!
>
> All groovy. Thanks for the details!
[1] https://kunit-review.googlesource.com/c/linux/+/1809/2#message-c243e1c9086d9432d2dcabc67a42a977b8a020ff
^ permalink raw reply
* Re: [PATCH v5 13/18] kunit: tool: add Python wrappers for running KUnit tests
From: Brendan Higgins @ 2019-06-27 0:23 UTC (permalink / raw)
To: Luis Chamberlain
Cc: Frank Rowand, Greg KH, Josh Poimboeuf, Kees Cook, Kieran Bingham,
Peter Zijlstra, Rob Herring, Stephen Boyd, shuah,
Theodore Ts'o, Masahiro Yamada, devicetree, dri-devel,
kunit-dev, open list:DOCUMENTATION, linux-fsdevel, linux-kbuild,
Linux Kernel Mailing List, open list:KERNEL SELFTEST FRAMEWORK,
linux-nvdimm, linux-um, Sasha Levin, Bird, Timothy,
Amir Goldstein, Dan Carpenter, Daniel Vetter, Jeff Dike,
Joel Stanley, Julia Lawall, Kevin Hilman, Knut Omang,
Logan Gunthorpe, Michael Ellerman, Petr Mladek, Randy Dunlap,
Richard Weinberger, David Rientjes, Steven Rostedt, wfg,
Felix Guo
In-Reply-To: <20190626220350.GA19023@42.do-not-panic.com>
On Wed, Jun 26, 2019 at 3:03 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
>
> On Wed, Jun 26, 2019 at 01:02:55AM -0700, Brendan Higgins wrote:
> > On Tue, Jun 25, 2019 at 5:01 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
> > >
> > > On Mon, Jun 17, 2019 at 01:26:08AM -0700, Brendan Higgins wrote:
> > > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-all_passed.log
> > > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-crash.log
> > > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-failure.log
> > > > create mode 100644 tools/testing/kunit/test_data/test_is_test_passed-no_tests_run.log
> > > > create mode 100644 tools/testing/kunit/test_data/test_output_isolated_correctly.log
> > > > create mode 100644 tools/testing/kunit/test_data/test_read_from_file.kconfig
> > >
> > > Why are these being added upstream? The commit log does not explain
> > > this.
> >
> > Oh sorry, those are for testing purposes. I thought that was clear
> > from being in the test_data directory. I will reference it in the
> > commit log in the next revision.
>
> Still, I don't get it. They seem to be results from a prior run. Why do
> we need them for testing purposes?
Those logs are the raw output from UML with KUnit installed. They are
for testing kunit_tool, the Python scripts added in this commit. One
of the things that kunit_tool does is parses the results output by
UML, extracts the KUnit data, and presents it in a user friendly
manner.
I added these logs so I could test that kunit_tool parses certain
kinds of output correctly. For example, I want to know that it parses
a test failure correctly and includes the appropriate context. So I
have a log from a unit test that failed, and I have a test (a Python
test that is also in this commit) that tests whether kunit_tool can
parse the log correctly.
Does that make sense?
^ permalink raw reply
* [PATCH v2 2/4] kbuild: do not create wrappers for header-test-y
From: Masahiro Yamada @ 2019-06-27 1:46 UTC (permalink / raw)
To: linux-kbuild
Cc: Sam Ravnborg, Masahiro Yamada, linux-doc, Jonathan Corbet,
linux-kernel, Michal Marek
In-Reply-To: <20190627014617.600-1-yamada.masahiro@socionext.com>
header-test-y does not work with headers in sub-directories.
For example, you can write a Makefile, like this:
include/linux/Kbuild:
header-test-y += mtd/nand.h
This entry creates a wrapper include/linux/mtd/nand.hdrtest.c with
the following content:
#include "mtd/nand.h"
To make this work, we need to add $(srctree)/include/linux to the
header search path. It would be tedious to add ccflags-y.
We could change the *.hdrtest.c rule to wrap:
#include "nand.h"
This works for in-tree build since #include "..." searches in the
relative path from the header with this directive. For O=... build,
we need to add $(srctree)/include/linux/mtd to the header search path,
which will be even more tedious.
After all, I thought it would be handier to compile headers directly
without creating wrappers.
I added a new build rule to compile %.h into %.h.s
I chose %.h.s instead of %.h.o because it was a little bit faster.
Also, for GCC, an empty assembly is smaller than an empty object.
I wrote the build rule:
$(CC) $(c_flags) -S -o $@ -x c /dev/null -include $<
instead of:
$(CC) $(c_flags) -S -o $@ -x c $<
Both work fine with GCC, but the latter is not good for Clang.
This comes down to the difference in the -Wunused-function policy.
GCC does not warn about unused 'static inline' functions at all.
Clang does not warn about the ones in included headers, but does
about the ones in the source. So, we should handle headers as
headers, not as source files.
In fact, this has been hidden since commit abb2ea7dfd82 ("compiler,
clang: suppress warning for unused static inline functions"), but we
should not rely on that.
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---
Changes in v2:
- New patch
.gitignore | 1 -
Documentation/dontdiff | 1 -
Documentation/kbuild/makefiles.txt | 3 +--
Makefile | 1 -
scripts/Makefile.build | 10 +++++-----
scripts/Makefile.lib | 2 +-
6 files changed, 7 insertions(+), 11 deletions(-)
diff --git a/.gitignore b/.gitignore
index 4bb60f0fa23b..7587ef56b92d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,7 +22,6 @@
*.elf
*.gcno
*.gz
-*.hdrtest.c
*.i
*.ko
*.lex.c
diff --git a/Documentation/dontdiff b/Documentation/dontdiff
index 554dfe4883d2..5eba889ea84d 100644
--- a/Documentation/dontdiff
+++ b/Documentation/dontdiff
@@ -19,7 +19,6 @@
*.grep
*.grp
*.gz
-*.hdrtest.c
*.html
*.i
*.jpeg
diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt
index ca4b24ec0399..5080fec34609 100644
--- a/Documentation/kbuild/makefiles.txt
+++ b/Documentation/kbuild/makefiles.txt
@@ -1023,8 +1023,7 @@ When kbuild executes, the following steps are followed (roughly):
header-test-y specifies headers (*.h) in the current directory that
should be compile tested to ensure they are self-contained,
i.e. compilable as standalone units. If CONFIG_HEADER_TEST is enabled,
- this autogenerates dummy sources to include the headers, and builds them
- as part of extra-y.
+ this builds them as part of extra-y.
--- 6.7 Commands useful for building a boot image
diff --git a/Makefile b/Makefile
index f23516980796..7f293b0431fe 100644
--- a/Makefile
+++ b/Makefile
@@ -1648,7 +1648,6 @@ clean: $(clean-dirs)
-o -name '*.dwo' -o -name '*.lst' \
-o -name '*.su' \
-o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \
- -o -name '*.hdrtest.c' \
-o -name '*.lex.c' -o -name '*.tab.[ch]' \
-o -name '*.asn1.[ch]' \
-o -name '*.symtypes' -o -name 'modules.order' \
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index ee0319560513..776842b7e6a3 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -294,14 +294,14 @@ quiet_cmd_cc_lst_c = MKLST $@
$(obj)/%.lst: $(src)/%.c FORCE
$(call if_changed_dep,cc_lst_c)
-# Dummy C sources for header test (header-test-y target)
+# header test (header-test-y target)
# ---------------------------------------------------------------------------
-quiet_cmd_header_test = HDRTEST $@
- cmd_header_test = echo "\#include \"$*.h\"" > $@
+quiet_cmd_cc_s_h = CC $@
+ cmd_cc_s_h = $(CC) $(c_flags) -S -o $@ -x c /dev/null -include $<
-$(obj)/%.hdrtest.c:
- $(call cmd,header_test)
+$(obj)/%.h.s: $(src)/%.h FORCE
+ $(call if_changed_dep,cc_s_h)
# Compile assembler sources (.S)
# ---------------------------------------------------------------------------
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 3e630fcaffd1..55ae1ec65342 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -67,7 +67,7 @@ extra-$(CONFIG_OF_ALL_DTBS) += $(patsubst %.dtb,%.dt.yaml, $(dtb-))
endif
# Test self-contained headers
-extra-$(CONFIG_HEADER_TEST) += $(patsubst %.h,%.hdrtest.o,$(header-test-y))
+extra-$(CONFIG_HEADER_TEST) += $(addsuffix .s, $(header-test-y))
# Add subdir path
--
2.17.1
^ permalink raw reply related
* [PATCH v2 3/4] kbuild: support header-test-pattern-y
From: Masahiro Yamada @ 2019-06-27 1:46 UTC (permalink / raw)
To: linux-kbuild
Cc: Sam Ravnborg, Masahiro Yamada, linux-doc, linux-kernel,
Jonathan Corbet, Michal Marek
In-Reply-To: <20190627014617.600-1-yamada.masahiro@socionext.com>
In my view, most of headers can be self-contained. So, it would be
tedious to add every header to header-test-y explicitly. We usually
end up with "all headers with some exceptions".
There are two types in exceptions:
[1] headers that are never compiled as standalone units
For examples, include/linux/compiler-gcc.h is not intended to be
included directly. We should always exclude such ones.
[2] headers that are conditionally compiled as standalone units
Some headers can be compiled only for particular architectures.
For example, include/linux/arm-cci.h can be compiled only for
arm/arm64 because it requires <asm/arm-cci.h> to exist.
Clang can compile include/soc/nps/mtm.h only for arc because
it contains an arch-specific register in inline assembler.
For [2], we can write Makefile like this:
header-test-$(CONFIG_ARM) += linux/arm-cci.h
The new syntax header-test-pattern-y will be useful to specify
"the rest".
The typical usage is like this:
header-test-pattern-y += */*.h
This adds all the headers in sub-directories to the test coverage,
but headers added to header-test- are excluded. In this regards,
header-test-pattern-y behaves like a weaker variant of header-test-y.
Caveat:
The patterns in header-test-pattern-y are prefixed with $(srctree)/$(src)/
but not $(objtree)/$(obj)/. Stale generated patterns are often left over.
For example, you will have ones when you traverse the git history for
'git bisect' without cleaning. If a wildcard is used for generated
headers, it may match to stale headers.
If you really want to compile-test generated headers, I recommend to
add them to header-test-y explicitly. One pitfall is $(srctree)/$(src)/
and $(objtree)/$(obj)/ point to the same directory for in-tree building.
So, header-test-pattern-y should be used with care. It can potentially
match to generated headers, which may be stale and fail to compile.
Caveat2:
You could use wildcard for header-test-. For example,
header-test- += asm-generic/%
... will exclude headers in asm-generic directory. Unfortunately, the
wildcard character is '%' instead of '*' because this is evaluated by
$(filter-out ...) whereas header-test-pattern-y is evaluated by
$(wildcard ...). This is a kludge, but seems useful in some places...
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---
Changes in v2:
- New patch
Documentation/kbuild/makefiles.txt | 10 ++++++++++
scripts/Makefile.lib | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/Documentation/kbuild/makefiles.txt b/Documentation/kbuild/makefiles.txt
index 5080fec34609..b817e6cefb77 100644
--- a/Documentation/kbuild/makefiles.txt
+++ b/Documentation/kbuild/makefiles.txt
@@ -1025,6 +1025,16 @@ When kbuild executes, the following steps are followed (roughly):
i.e. compilable as standalone units. If CONFIG_HEADER_TEST is enabled,
this builds them as part of extra-y.
+ header-test-pattern-y
+
+ This works as a weaker version of header-test-y, and accepts wildcard
+ patterns. The typical usage is:
+
+ header-test-pattern-y += *.h
+
+ This specifies all the files that matches to '*.h' in the current
+ directory, but the files in 'header-test-' are excluded.
+
--- 6.7 Commands useful for building a boot image
Kbuild provides a few macros that are useful when building a
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 55ae1ec65342..54444933bbab 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -67,6 +67,16 @@ extra-$(CONFIG_OF_ALL_DTBS) += $(patsubst %.dtb,%.dt.yaml, $(dtb-))
endif
# Test self-contained headers
+
+# Wildcard searches in $(srctree)/$(src)/, but not in $(objtree)/$(obj)/.
+# Stale generated headers are often left over, so wildcard matching should
+# be avoided. Please notice $(srctree)/$(src)/ and $(objtree)/$(obj) point
+# to the same location for in-tree building.
+header-test-y += $(filter-out $(header-test-), \
+ $(patsubst $(srctree)/$(src)/%, %, \
+ $(wildcard $(addprefix $(srctree)/$(src)/, \
+ $(header-test-pattern-y)))))
+
extra-$(CONFIG_HEADER_TEST) += $(addsuffix .s, $(header-test-y))
# Add subdir path
--
2.17.1
^ permalink raw reply related
* [PATCH v2 0/4] Compile-test UAPI and kernel headers
From: Masahiro Yamada @ 2019-06-27 1:46 UTC (permalink / raw)
To: linux-kbuild
Cc: Sam Ravnborg, Masahiro Yamada, Tony Luck, linux-doc,
John Fastabend, Jonathan Corbet, Jakub Kicinski, linux-riscv,
Daniel Borkmann, xdp-newbies, Anton Vorontsov, Palmer Dabbelt,
Matthias Brugger, Song Liu, Yonghong Song, Michal Marek,
Jesper Dangaard Brouer, Martin KaFai Lau, linux-mediatek,
linux-arm-kernel, Albert Ou, Colin Cross, David S. Miller,
Kees Cook, Alexei Starovoitov, netdev, linux-kernel, bpf
1/4: reworked v2.
2/4: fix a flaw I noticed when I was working on this series
3/4: maybe useful for 4/4 and in some other places
4/4: v2. compile as many headers as possible.
Changes in v2:
- Add CONFIG_CPU_{BIG,LITTLE}_ENDIAN guard to avoid build error
- Use 'header-test-' instead of 'no-header-test'
- Avoid weird 'find' warning when cleaning
- New patch
- New patch
- Add everything to test coverage, and exclude broken ones
- Rename 'Makefile' to 'Kbuild'
- Add CONFIG_KERNEL_HEADER_TEST option
Masahiro Yamada (4):
kbuild: compile-test UAPI headers to ensure they are self-contained
kbuild: do not create wrappers for header-test-y
kbuild: support header-test-pattern-y
kbuild: compile-test kernel headers to ensure they are self-contained
.gitignore | 1 -
Documentation/dontdiff | 1 -
Documentation/kbuild/makefiles.txt | 13 +-
Makefile | 4 +-
include/Kbuild | 1134 ++++++++++++++++++++++++++++
init/Kconfig | 22 +
scripts/Makefile.build | 10 +-
scripts/Makefile.lib | 12 +-
scripts/cc-system-headers.sh | 8 +
usr/.gitignore | 1 -
usr/Makefile | 2 +
usr/include/.gitignore | 3 +
usr/include/Makefile | 133 ++++
13 files changed, 1331 insertions(+), 13 deletions(-)
create mode 100644 include/Kbuild
create mode 100755 scripts/cc-system-headers.sh
create mode 100644 usr/include/.gitignore
create mode 100644 usr/include/Makefile
--
2.17.1
^ permalink raw reply
* Re: [PATCH v2 0/4] Compile-test UAPI and kernel headers
From: Masahiro Yamada @ 2019-06-27 3:12 UTC (permalink / raw)
To: Linux Kbuild mailing list
Cc: Sam Ravnborg, Tony Luck, open list:DOCUMENTATION, John Fastabend,
Jonathan Corbet, Jakub Kicinski, linux-riscv, Daniel Borkmann,
xdp-newbies, Anton Vorontsov, Palmer Dabbelt, Matthias Brugger,
Song Liu, Yonghong Song, Michal Marek, Jesper Dangaard Brouer,
Martin KaFai Lau, moderated list:ARM/Mediatek SoC support,
linux-arm-kernel, Albert Ou, Colin Cross, David S. Miller,
Kees Cook, Alexei Starovoitov, Networking,
Linux Kernel Mailing List, bpf
In-Reply-To: <20190627014617.600-1-yamada.masahiro@socionext.com>
On Thu, Jun 27, 2019 at 10:49 AM Masahiro Yamada
<yamada.masahiro@socionext.com> wrote:
>
>
> 1/4: reworked v2.
>
> 2/4: fix a flaw I noticed when I was working on this series
>
> 3/4: maybe useful for 4/4 and in some other places
>
> 4/4: v2. compile as many headers as possible.
>
If you want to test this series,
please check:
git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild.git
header-test-v2
> Changes in v2:
> - Add CONFIG_CPU_{BIG,LITTLE}_ENDIAN guard to avoid build error
> - Use 'header-test-' instead of 'no-header-test'
> - Avoid weird 'find' warning when cleaning
> - New patch
> - New patch
> - Add everything to test coverage, and exclude broken ones
> - Rename 'Makefile' to 'Kbuild'
> - Add CONFIG_KERNEL_HEADER_TEST option
>
> Masahiro Yamada (4):
> kbuild: compile-test UAPI headers to ensure they are self-contained
> kbuild: do not create wrappers for header-test-y
> kbuild: support header-test-pattern-y
> kbuild: compile-test kernel headers to ensure they are self-contained
>
> .gitignore | 1 -
> Documentation/dontdiff | 1 -
> Documentation/kbuild/makefiles.txt | 13 +-
> Makefile | 4 +-
> include/Kbuild | 1134 ++++++++++++++++++++++++++++
> init/Kconfig | 22 +
> scripts/Makefile.build | 10 +-
> scripts/Makefile.lib | 12 +-
> scripts/cc-system-headers.sh | 8 +
> usr/.gitignore | 1 -
> usr/Makefile | 2 +
> usr/include/.gitignore | 3 +
> usr/include/Makefile | 133 ++++
> 13 files changed, 1331 insertions(+), 13 deletions(-)
> create mode 100644 include/Kbuild
> create mode 100755 scripts/cc-system-headers.sh
> create mode 100644 usr/include/.gitignore
> create mode 100644 usr/include/Makefile
>
> --
> 2.17.1
>
--
Best Regards
Masahiro Yamada
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox