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

telemetry.c (3801B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Intel Platform Monitory Technology Telemetry driver
      4 *
      5 * Copyright (c) 2020, Intel Corporation.
      6 * All Rights Reserved.
      7 *
      8 * Author: "David E. Box" <david.e.box@linux.intel.com>
      9 */
     10
     11#include <linux/auxiliary_bus.h>
     12#include <linux/kernel.h>
     13#include <linux/module.h>
     14#include <linux/pci.h>
     15#include <linux/slab.h>
     16#include <linux/uaccess.h>
     17#include <linux/overflow.h>
     18
     19#include "../vsec.h"
     20#include "class.h"
     21
     22#define TELEM_SIZE_OFFSET	0x0
     23#define TELEM_GUID_OFFSET	0x4
     24#define TELEM_BASE_OFFSET	0x8
     25#define TELEM_ACCESS(v)		((v) & GENMASK(3, 0))
     26/* size is in bytes */
     27#define TELEM_SIZE(v)		(((v) & GENMASK(27, 12)) >> 10)
     28
     29/* Used by client hardware to identify a fixed telemetry entry*/
     30#define TELEM_CLIENT_FIXED_BLOCK_GUID	0x10000000
     31
     32struct pmt_telem_priv {
     33	int				num_entries;
     34	struct intel_pmt_entry		entry[];
     35};
     36
     37static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
     38				      struct device *dev)
     39{
     40	u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET);
     41
     42	if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID)
     43		return false;
     44
     45	return intel_pmt_is_early_client_hw(dev);
     46}
     47
     48static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
     49				   struct intel_pmt_header *header,
     50				   struct device *dev)
     51{
     52	void __iomem *disc_table = entry->disc_table;
     53
     54	if (pmt_telem_region_overlaps(entry, dev))
     55		return 1;
     56
     57	header->access_type = TELEM_ACCESS(readl(disc_table));
     58	header->guid = readl(disc_table + TELEM_GUID_OFFSET);
     59	header->base_offset = readl(disc_table + TELEM_BASE_OFFSET);
     60
     61	/* Size is measured in DWORDS, but accessor returns bytes */
     62	header->size = TELEM_SIZE(readl(disc_table));
     63
     64	/*
     65	 * Some devices may expose non-functioning entries that are
     66	 * reserved for future use. They have zero size. Do not fail
     67	 * probe for these. Just ignore them.
     68	 */
     69	if (header->size == 0)
     70		return 1;
     71
     72	return 0;
     73}
     74
     75static DEFINE_XARRAY_ALLOC(telem_array);
     76static struct intel_pmt_namespace pmt_telem_ns = {
     77	.name = "telem",
     78	.xa = &telem_array,
     79	.pmt_header_decode = pmt_telem_header_decode,
     80};
     81
     82static void pmt_telem_remove(struct auxiliary_device *auxdev)
     83{
     84	struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev);
     85	int i;
     86
     87	for (i = 0; i < priv->num_entries; i++)
     88		intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns);
     89}
     90
     91static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
     92{
     93	struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev);
     94	struct pmt_telem_priv *priv;
     95	size_t size;
     96	int i, ret;
     97
     98	size = struct_size(priv, entry, intel_vsec_dev->num_resources);
     99	priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
    100	if (!priv)
    101		return -ENOMEM;
    102
    103	auxiliary_set_drvdata(auxdev, priv);
    104
    105	for (i = 0; i < intel_vsec_dev->num_resources; i++) {
    106		struct intel_pmt_entry *entry = &priv->entry[priv->num_entries];
    107
    108		ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i);
    109		if (ret < 0)
    110			goto abort_probe;
    111		if (ret)
    112			continue;
    113
    114		priv->num_entries++;
    115	}
    116
    117	return 0;
    118abort_probe:
    119	pmt_telem_remove(auxdev);
    120	return ret;
    121}
    122
    123static const struct auxiliary_device_id pmt_telem_id_table[] = {
    124	{ .name = "intel_vsec.telemetry" },
    125	{}
    126};
    127MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table);
    128
    129static struct auxiliary_driver pmt_telem_aux_driver = {
    130	.id_table	= pmt_telem_id_table,
    131	.remove		= pmt_telem_remove,
    132	.probe		= pmt_telem_probe,
    133};
    134
    135static int __init pmt_telem_init(void)
    136{
    137	return auxiliary_driver_register(&pmt_telem_aux_driver);
    138}
    139module_init(pmt_telem_init);
    140
    141static void __exit pmt_telem_exit(void)
    142{
    143	auxiliary_driver_unregister(&pmt_telem_aux_driver);
    144	xa_destroy(&telem_array);
    145}
    146module_exit(pmt_telem_exit);
    147
    148MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
    149MODULE_DESCRIPTION("Intel PMT Telemetry driver");
    150MODULE_LICENSE("GPL v2");