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

v3d_perfmon.c (4956B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright (C) 2021 Raspberry Pi
      4 */
      5
      6#include "v3d_drv.h"
      7#include "v3d_regs.h"
      8
      9#define V3D_PERFMONID_MIN	1
     10#define V3D_PERFMONID_MAX	U32_MAX
     11
     12void v3d_perfmon_get(struct v3d_perfmon *perfmon)
     13{
     14	if (perfmon)
     15		refcount_inc(&perfmon->refcnt);
     16}
     17
     18void v3d_perfmon_put(struct v3d_perfmon *perfmon)
     19{
     20	if (perfmon && refcount_dec_and_test(&perfmon->refcnt))
     21		kfree(perfmon);
     22}
     23
     24void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon)
     25{
     26	unsigned int i;
     27	u32 mask;
     28	u8 ncounters;
     29
     30	if (WARN_ON_ONCE(!perfmon || v3d->active_perfmon))
     31		return;
     32
     33	ncounters = perfmon->ncounters;
     34	mask = GENMASK(ncounters - 1, 0);
     35
     36	for (i = 0; i < ncounters; i++) {
     37		u32 source = i / 4;
     38		u32 channel = V3D_SET_FIELD(perfmon->counters[i], V3D_PCTR_S0);
     39
     40		i++;
     41		channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
     42					 V3D_PCTR_S1);
     43		i++;
     44		channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
     45					 V3D_PCTR_S2);
     46		i++;
     47		channel |= V3D_SET_FIELD(i < ncounters ? perfmon->counters[i] : 0,
     48					 V3D_PCTR_S3);
     49		V3D_CORE_WRITE(0, V3D_V4_PCTR_0_SRC_X(source), channel);
     50	}
     51
     52	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_CLR, mask);
     53	V3D_CORE_WRITE(0, V3D_PCTR_0_OVERFLOW, mask);
     54	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, mask);
     55
     56	v3d->active_perfmon = perfmon;
     57}
     58
     59void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon,
     60		      bool capture)
     61{
     62	unsigned int i;
     63
     64	if (!perfmon || !v3d->active_perfmon)
     65		return;
     66
     67	mutex_lock(&perfmon->lock);
     68	if (perfmon != v3d->active_perfmon) {
     69		mutex_unlock(&perfmon->lock);
     70		return;
     71	}
     72
     73	if (capture)
     74		for (i = 0; i < perfmon->ncounters; i++)
     75			perfmon->values[i] += V3D_CORE_READ(0, V3D_PCTR_0_PCTRX(i));
     76
     77	V3D_CORE_WRITE(0, V3D_V4_PCTR_0_EN, 0);
     78
     79	v3d->active_perfmon = NULL;
     80	mutex_unlock(&perfmon->lock);
     81}
     82
     83struct v3d_perfmon *v3d_perfmon_find(struct v3d_file_priv *v3d_priv, int id)
     84{
     85	struct v3d_perfmon *perfmon;
     86
     87	mutex_lock(&v3d_priv->perfmon.lock);
     88	perfmon = idr_find(&v3d_priv->perfmon.idr, id);
     89	v3d_perfmon_get(perfmon);
     90	mutex_unlock(&v3d_priv->perfmon.lock);
     91
     92	return perfmon;
     93}
     94
     95void v3d_perfmon_open_file(struct v3d_file_priv *v3d_priv)
     96{
     97	mutex_init(&v3d_priv->perfmon.lock);
     98	idr_init(&v3d_priv->perfmon.idr);
     99}
    100
    101static int v3d_perfmon_idr_del(int id, void *elem, void *data)
    102{
    103	struct v3d_perfmon *perfmon = elem;
    104
    105	v3d_perfmon_put(perfmon);
    106
    107	return 0;
    108}
    109
    110void v3d_perfmon_close_file(struct v3d_file_priv *v3d_priv)
    111{
    112	mutex_lock(&v3d_priv->perfmon.lock);
    113	idr_for_each(&v3d_priv->perfmon.idr, v3d_perfmon_idr_del, NULL);
    114	idr_destroy(&v3d_priv->perfmon.idr);
    115	mutex_unlock(&v3d_priv->perfmon.lock);
    116}
    117
    118int v3d_perfmon_create_ioctl(struct drm_device *dev, void *data,
    119			     struct drm_file *file_priv)
    120{
    121	struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
    122	struct drm_v3d_perfmon_create *req = data;
    123	struct v3d_perfmon *perfmon;
    124	unsigned int i;
    125	int ret;
    126
    127	/* Number of monitored counters cannot exceed HW limits. */
    128	if (req->ncounters > DRM_V3D_MAX_PERF_COUNTERS ||
    129	    !req->ncounters)
    130		return -EINVAL;
    131
    132	/* Make sure all counters are valid. */
    133	for (i = 0; i < req->ncounters; i++) {
    134		if (req->counters[i] >= V3D_PERFCNT_NUM)
    135			return -EINVAL;
    136	}
    137
    138	perfmon = kzalloc(struct_size(perfmon, values, req->ncounters),
    139			  GFP_KERNEL);
    140	if (!perfmon)
    141		return -ENOMEM;
    142
    143	for (i = 0; i < req->ncounters; i++)
    144		perfmon->counters[i] = req->counters[i];
    145
    146	perfmon->ncounters = req->ncounters;
    147
    148	refcount_set(&perfmon->refcnt, 1);
    149	mutex_init(&perfmon->lock);
    150
    151	mutex_lock(&v3d_priv->perfmon.lock);
    152	ret = idr_alloc(&v3d_priv->perfmon.idr, perfmon, V3D_PERFMONID_MIN,
    153			V3D_PERFMONID_MAX, GFP_KERNEL);
    154	mutex_unlock(&v3d_priv->perfmon.lock);
    155
    156	if (ret < 0) {
    157		kfree(perfmon);
    158		return ret;
    159	}
    160
    161	req->id = ret;
    162
    163	return 0;
    164}
    165
    166int v3d_perfmon_destroy_ioctl(struct drm_device *dev, void *data,
    167			      struct drm_file *file_priv)
    168{
    169	struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
    170	struct drm_v3d_perfmon_destroy *req = data;
    171	struct v3d_perfmon *perfmon;
    172
    173	mutex_lock(&v3d_priv->perfmon.lock);
    174	perfmon = idr_remove(&v3d_priv->perfmon.idr, req->id);
    175	mutex_unlock(&v3d_priv->perfmon.lock);
    176
    177	if (!perfmon)
    178		return -EINVAL;
    179
    180	v3d_perfmon_put(perfmon);
    181
    182	return 0;
    183}
    184
    185int v3d_perfmon_get_values_ioctl(struct drm_device *dev, void *data,
    186				 struct drm_file *file_priv)
    187{
    188	struct v3d_dev *v3d = to_v3d_dev(dev);
    189	struct v3d_file_priv *v3d_priv = file_priv->driver_priv;
    190	struct drm_v3d_perfmon_get_values *req = data;
    191	struct v3d_perfmon *perfmon;
    192	int ret = 0;
    193
    194	if (req->pad != 0)
    195		return -EINVAL;
    196
    197	mutex_lock(&v3d_priv->perfmon.lock);
    198	perfmon = idr_find(&v3d_priv->perfmon.idr, req->id);
    199	v3d_perfmon_get(perfmon);
    200	mutex_unlock(&v3d_priv->perfmon.lock);
    201
    202	if (!perfmon)
    203		return -EINVAL;
    204
    205	v3d_perfmon_stop(v3d, perfmon, true);
    206
    207	if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values,
    208			 perfmon->ncounters * sizeof(u64)))
    209		ret = -EFAULT;
    210
    211	v3d_perfmon_put(perfmon);
    212
    213	return ret;
    214}