cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pti.c (7224B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Intel(R) Trace Hub PTI output driver
      4 *
      5 * Copyright (C) 2014-2016 Intel Corporation.
      6 */
      7
      8#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
      9
     10#include <linux/types.h>
     11#include <linux/module.h>
     12#include <linux/device.h>
     13#include <linux/sizes.h>
     14#include <linux/printk.h>
     15#include <linux/slab.h>
     16#include <linux/mm.h>
     17#include <linux/io.h>
     18
     19#include "intel_th.h"
     20#include "pti.h"
     21
     22struct pti_device {
     23	void __iomem		*base;
     24	struct intel_th_device	*thdev;
     25	unsigned int		mode;
     26	unsigned int		freeclk;
     27	unsigned int		clkdiv;
     28	unsigned int		patgen;
     29	unsigned int		lpp_dest_mask;
     30	unsigned int		lpp_dest;
     31};
     32
     33/* map PTI widths to MODE settings of PTI_CTL register */
     34static const unsigned int pti_mode[] = {
     35	0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
     36};
     37
     38static int pti_width_mode(unsigned int width)
     39{
     40	int i;
     41
     42	for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
     43		if (pti_mode[i] == width)
     44			return i;
     45
     46	return -EINVAL;
     47}
     48
     49static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
     50			 char *buf)
     51{
     52	struct pti_device *pti = dev_get_drvdata(dev);
     53
     54	return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
     55}
     56
     57static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
     58			  const char *buf, size_t size)
     59{
     60	struct pti_device *pti = dev_get_drvdata(dev);
     61	unsigned long val;
     62	int ret;
     63
     64	ret = kstrtoul(buf, 10, &val);
     65	if (ret)
     66		return ret;
     67
     68	ret = pti_width_mode(val);
     69	if (ret < 0)
     70		return ret;
     71
     72	pti->mode = ret;
     73
     74	return size;
     75}
     76
     77static DEVICE_ATTR_RW(mode);
     78
     79static ssize_t
     80freerunning_clock_show(struct device *dev, struct device_attribute *attr,
     81		       char *buf)
     82{
     83	struct pti_device *pti = dev_get_drvdata(dev);
     84
     85	return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
     86}
     87
     88static ssize_t
     89freerunning_clock_store(struct device *dev, struct device_attribute *attr,
     90			const char *buf, size_t size)
     91{
     92	struct pti_device *pti = dev_get_drvdata(dev);
     93	unsigned long val;
     94	int ret;
     95
     96	ret = kstrtoul(buf, 10, &val);
     97	if (ret)
     98		return ret;
     99
    100	pti->freeclk = !!val;
    101
    102	return size;
    103}
    104
    105static DEVICE_ATTR_RW(freerunning_clock);
    106
    107static ssize_t
    108clock_divider_show(struct device *dev, struct device_attribute *attr,
    109		   char *buf)
    110{
    111	struct pti_device *pti = dev_get_drvdata(dev);
    112
    113	return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
    114}
    115
    116static ssize_t
    117clock_divider_store(struct device *dev, struct device_attribute *attr,
    118		    const char *buf, size_t size)
    119{
    120	struct pti_device *pti = dev_get_drvdata(dev);
    121	unsigned long val;
    122	int ret;
    123
    124	ret = kstrtoul(buf, 10, &val);
    125	if (ret)
    126		return ret;
    127
    128	if (!is_power_of_2(val) || val > 8 || !val)
    129		return -EINVAL;
    130
    131	pti->clkdiv = val;
    132
    133	return size;
    134}
    135
    136static DEVICE_ATTR_RW(clock_divider);
    137
    138static struct attribute *pti_output_attrs[] = {
    139	&dev_attr_mode.attr,
    140	&dev_attr_freerunning_clock.attr,
    141	&dev_attr_clock_divider.attr,
    142	NULL,
    143};
    144
    145static const struct attribute_group pti_output_group = {
    146	.attrs	= pti_output_attrs,
    147};
    148
    149static int intel_th_pti_activate(struct intel_th_device *thdev)
    150{
    151	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
    152	u32 ctl = PTI_EN;
    153
    154	if (pti->patgen)
    155		ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
    156	if (pti->freeclk)
    157		ctl |= PTI_FCEN;
    158	ctl |= pti->mode << __ffs(PTI_MODE);
    159	ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
    160	ctl |= pti->lpp_dest << __ffs(LPP_DEST);
    161
    162	iowrite32(ctl, pti->base + REG_PTI_CTL);
    163
    164	intel_th_trace_enable(thdev);
    165
    166	return 0;
    167}
    168
    169static void intel_th_pti_deactivate(struct intel_th_device *thdev)
    170{
    171	struct pti_device *pti = dev_get_drvdata(&thdev->dev);
    172
    173	intel_th_trace_disable(thdev);
    174
    175	iowrite32(0, pti->base + REG_PTI_CTL);
    176}
    177
    178static void read_hw_config(struct pti_device *pti)
    179{
    180	u32 ctl = ioread32(pti->base + REG_PTI_CTL);
    181
    182	pti->mode	= (ctl & PTI_MODE) >> __ffs(PTI_MODE);
    183	pti->clkdiv	= (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
    184	pti->freeclk	= !!(ctl & PTI_FCEN);
    185
    186	if (!pti_mode[pti->mode])
    187		pti->mode = pti_width_mode(4);
    188	if (!pti->clkdiv)
    189		pti->clkdiv = 1;
    190
    191	if (pti->thdev->output.type == GTH_LPP) {
    192		if (ctl & LPP_PTIPRESENT)
    193			pti->lpp_dest_mask |= LPP_DEST_PTI;
    194		if (ctl & LPP_BSSBPRESENT)
    195			pti->lpp_dest_mask |= LPP_DEST_EXI;
    196		if (ctl & LPP_DEST)
    197			pti->lpp_dest = 1;
    198	}
    199}
    200
    201static int intel_th_pti_probe(struct intel_th_device *thdev)
    202{
    203	struct device *dev = &thdev->dev;
    204	struct resource *res;
    205	struct pti_device *pti;
    206	void __iomem *base;
    207
    208	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
    209	if (!res)
    210		return -ENODEV;
    211
    212	base = devm_ioremap(dev, res->start, resource_size(res));
    213	if (!base)
    214		return -ENOMEM;
    215
    216	pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
    217	if (!pti)
    218		return -ENOMEM;
    219
    220	pti->thdev = thdev;
    221	pti->base = base;
    222
    223	read_hw_config(pti);
    224
    225	dev_set_drvdata(dev, pti);
    226
    227	return 0;
    228}
    229
    230static void intel_th_pti_remove(struct intel_th_device *thdev)
    231{
    232}
    233
    234static struct intel_th_driver intel_th_pti_driver = {
    235	.probe	= intel_th_pti_probe,
    236	.remove	= intel_th_pti_remove,
    237	.activate	= intel_th_pti_activate,
    238	.deactivate	= intel_th_pti_deactivate,
    239	.attr_group	= &pti_output_group,
    240	.driver	= {
    241		.name	= "pti",
    242		.owner	= THIS_MODULE,
    243	},
    244};
    245
    246static const char * const lpp_dest_str[] = { "pti", "exi" };
    247
    248static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,
    249			     char *buf)
    250{
    251	struct pti_device *pti = dev_get_drvdata(dev);
    252	ssize_t ret = 0;
    253	int i;
    254
    255	for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {
    256		const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";
    257
    258		if (!(pti->lpp_dest_mask & BIT(i)))
    259			continue;
    260
    261		ret += scnprintf(buf + ret, PAGE_SIZE - ret,
    262				 fmt, lpp_dest_str[i]);
    263	}
    264
    265	if (ret)
    266		buf[ret - 1] = '\n';
    267
    268	return ret;
    269}
    270
    271static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,
    272			      const char *buf, size_t size)
    273{
    274	struct pti_device *pti = dev_get_drvdata(dev);
    275	int i;
    276
    277	i = sysfs_match_string(lpp_dest_str, buf);
    278	if (i < 0)
    279		return i;
    280
    281	if (!(pti->lpp_dest_mask & BIT(i)))
    282		return -EINVAL;
    283
    284	pti->lpp_dest = i;
    285	return size;
    286}
    287
    288static DEVICE_ATTR_RW(lpp_dest);
    289
    290static struct attribute *lpp_output_attrs[] = {
    291	&dev_attr_mode.attr,
    292	&dev_attr_freerunning_clock.attr,
    293	&dev_attr_clock_divider.attr,
    294	&dev_attr_lpp_dest.attr,
    295	NULL,
    296};
    297
    298static const struct attribute_group lpp_output_group = {
    299	.attrs	= lpp_output_attrs,
    300};
    301
    302static struct intel_th_driver intel_th_lpp_driver = {
    303	.probe		= intel_th_pti_probe,
    304	.remove		= intel_th_pti_remove,
    305	.activate	= intel_th_pti_activate,
    306	.deactivate	= intel_th_pti_deactivate,
    307	.attr_group	= &lpp_output_group,
    308	.driver	= {
    309		.name	= "lpp",
    310		.owner	= THIS_MODULE,
    311	},
    312};
    313
    314static int __init intel_th_pti_lpp_init(void)
    315{
    316	int err;
    317
    318	err = intel_th_driver_register(&intel_th_pti_driver);
    319	if (err)
    320		return err;
    321
    322	err = intel_th_driver_register(&intel_th_lpp_driver);
    323	if (err) {
    324		intel_th_driver_unregister(&intel_th_pti_driver);
    325		return err;
    326	}
    327
    328	return 0;
    329}
    330
    331module_init(intel_th_pti_lpp_init);
    332
    333static void __exit intel_th_pti_lpp_exit(void)
    334{
    335	intel_th_driver_unregister(&intel_th_pti_driver);
    336	intel_th_driver_unregister(&intel_th_lpp_driver);
    337}
    338
    339module_exit(intel_th_pti_lpp_exit);
    340
    341MODULE_LICENSE("GPL v2");
    342MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");
    343MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");