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

uapi.c (7222B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* Copyright (c) 2020 NVIDIA Corporation */
      3
      4#include <linux/host1x.h>
      5#include <linux/iommu.h>
      6#include <linux/list.h>
      7
      8#include <drm/drm_drv.h>
      9#include <drm/drm_file.h>
     10#include <drm/drm_utils.h>
     11
     12#include "drm.h"
     13#include "uapi.h"
     14
     15static void tegra_drm_mapping_release(struct kref *ref)
     16{
     17	struct tegra_drm_mapping *mapping =
     18		container_of(ref, struct tegra_drm_mapping, ref);
     19
     20	host1x_bo_unpin(mapping->map);
     21	host1x_bo_put(mapping->bo);
     22
     23	kfree(mapping);
     24}
     25
     26void tegra_drm_mapping_put(struct tegra_drm_mapping *mapping)
     27{
     28	kref_put(&mapping->ref, tegra_drm_mapping_release);
     29}
     30
     31static void tegra_drm_channel_context_close(struct tegra_drm_context *context)
     32{
     33	struct tegra_drm_mapping *mapping;
     34	unsigned long id;
     35
     36	xa_for_each(&context->mappings, id, mapping)
     37		tegra_drm_mapping_put(mapping);
     38
     39	xa_destroy(&context->mappings);
     40
     41	host1x_channel_put(context->channel);
     42
     43	kfree(context);
     44}
     45
     46void tegra_drm_uapi_close_file(struct tegra_drm_file *file)
     47{
     48	struct tegra_drm_context *context;
     49	struct host1x_syncpt *sp;
     50	unsigned long id;
     51
     52	xa_for_each(&file->contexts, id, context)
     53		tegra_drm_channel_context_close(context);
     54
     55	xa_for_each(&file->syncpoints, id, sp)
     56		host1x_syncpt_put(sp);
     57
     58	xa_destroy(&file->contexts);
     59	xa_destroy(&file->syncpoints);
     60}
     61
     62static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u32 class)
     63{
     64	struct tegra_drm_client *client;
     65
     66	list_for_each_entry(client, &tegra->clients, list)
     67		if (client->base.class == class)
     68			return client;
     69
     70	return NULL;
     71}
     72
     73int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file)
     74{
     75	struct tegra_drm_file *fpriv = file->driver_priv;
     76	struct tegra_drm *tegra = drm->dev_private;
     77	struct drm_tegra_channel_open *args = data;
     78	struct tegra_drm_client *client = NULL;
     79	struct tegra_drm_context *context;
     80	int err;
     81
     82	if (args->flags)
     83		return -EINVAL;
     84
     85	context = kzalloc(sizeof(*context), GFP_KERNEL);
     86	if (!context)
     87		return -ENOMEM;
     88
     89	client = tegra_drm_find_client(tegra, args->host1x_class);
     90	if (!client) {
     91		err = -ENODEV;
     92		goto free;
     93	}
     94
     95	if (client->shared_channel) {
     96		context->channel = host1x_channel_get(client->shared_channel);
     97	} else {
     98		context->channel = host1x_channel_request(&client->base);
     99		if (!context->channel) {
    100			err = -EBUSY;
    101			goto free;
    102		}
    103	}
    104
    105	err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX),
    106		       GFP_KERNEL);
    107	if (err < 0)
    108		goto put_channel;
    109
    110	context->client = client;
    111	xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1);
    112
    113	args->version = client->version;
    114	args->capabilities = 0;
    115
    116	if (device_get_dma_attr(client->base.dev) == DEV_DMA_COHERENT)
    117		args->capabilities |= DRM_TEGRA_CHANNEL_CAP_CACHE_COHERENT;
    118
    119	return 0;
    120
    121put_channel:
    122	host1x_channel_put(context->channel);
    123free:
    124	kfree(context);
    125
    126	return err;
    127}
    128
    129int tegra_drm_ioctl_channel_close(struct drm_device *drm, void *data, struct drm_file *file)
    130{
    131	struct tegra_drm_file *fpriv = file->driver_priv;
    132	struct drm_tegra_channel_close *args = data;
    133	struct tegra_drm_context *context;
    134
    135	mutex_lock(&fpriv->lock);
    136
    137	context = xa_load(&fpriv->contexts, args->context);
    138	if (!context) {
    139		mutex_unlock(&fpriv->lock);
    140		return -EINVAL;
    141	}
    142
    143	xa_erase(&fpriv->contexts, args->context);
    144
    145	mutex_unlock(&fpriv->lock);
    146
    147	tegra_drm_channel_context_close(context);
    148
    149	return 0;
    150}
    151
    152int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_file *file)
    153{
    154	struct tegra_drm_file *fpriv = file->driver_priv;
    155	struct drm_tegra_channel_map *args = data;
    156	struct tegra_drm_mapping *mapping;
    157	struct tegra_drm_context *context;
    158	enum dma_data_direction direction;
    159	int err = 0;
    160
    161	if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE)
    162		return -EINVAL;
    163
    164	mutex_lock(&fpriv->lock);
    165
    166	context = xa_load(&fpriv->contexts, args->context);
    167	if (!context) {
    168		mutex_unlock(&fpriv->lock);
    169		return -EINVAL;
    170	}
    171
    172	mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
    173	if (!mapping) {
    174		err = -ENOMEM;
    175		goto unlock;
    176	}
    177
    178	kref_init(&mapping->ref);
    179
    180	mapping->bo = tegra_gem_lookup(file, args->handle);
    181	if (!mapping->bo) {
    182		err = -EINVAL;
    183		goto free;
    184	}
    185
    186	switch (args->flags & DRM_TEGRA_CHANNEL_MAP_READ_WRITE) {
    187	case DRM_TEGRA_CHANNEL_MAP_READ_WRITE:
    188		direction = DMA_BIDIRECTIONAL;
    189		break;
    190
    191	case DRM_TEGRA_CHANNEL_MAP_WRITE:
    192		direction = DMA_FROM_DEVICE;
    193		break;
    194
    195	case DRM_TEGRA_CHANNEL_MAP_READ:
    196		direction = DMA_TO_DEVICE;
    197		break;
    198
    199	default:
    200		err = -EINVAL;
    201		goto put_gem;
    202	}
    203
    204	mapping->map = host1x_bo_pin(context->client->base.dev, mapping->bo, direction, NULL);
    205	if (IS_ERR(mapping->map)) {
    206		err = PTR_ERR(mapping->map);
    207		goto put_gem;
    208	}
    209
    210	mapping->iova = mapping->map->phys;
    211	mapping->iova_end = mapping->iova + host1x_to_tegra_bo(mapping->bo)->gem.size;
    212
    213	err = xa_alloc(&context->mappings, &args->mapping, mapping, XA_LIMIT(1, U32_MAX),
    214		       GFP_KERNEL);
    215	if (err < 0)
    216		goto unpin;
    217
    218	mutex_unlock(&fpriv->lock);
    219
    220	return 0;
    221
    222unpin:
    223	host1x_bo_unpin(mapping->map);
    224put_gem:
    225	host1x_bo_put(mapping->bo);
    226free:
    227	kfree(mapping);
    228unlock:
    229	mutex_unlock(&fpriv->lock);
    230	return err;
    231}
    232
    233int tegra_drm_ioctl_channel_unmap(struct drm_device *drm, void *data, struct drm_file *file)
    234{
    235	struct tegra_drm_file *fpriv = file->driver_priv;
    236	struct drm_tegra_channel_unmap *args = data;
    237	struct tegra_drm_mapping *mapping;
    238	struct tegra_drm_context *context;
    239
    240	mutex_lock(&fpriv->lock);
    241
    242	context = xa_load(&fpriv->contexts, args->context);
    243	if (!context) {
    244		mutex_unlock(&fpriv->lock);
    245		return -EINVAL;
    246	}
    247
    248	mapping = xa_erase(&context->mappings, args->mapping);
    249
    250	mutex_unlock(&fpriv->lock);
    251
    252	if (!mapping)
    253		return -EINVAL;
    254
    255	tegra_drm_mapping_put(mapping);
    256	return 0;
    257}
    258
    259int tegra_drm_ioctl_syncpoint_allocate(struct drm_device *drm, void *data, struct drm_file *file)
    260{
    261	struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
    262	struct tegra_drm_file *fpriv = file->driver_priv;
    263	struct drm_tegra_syncpoint_allocate *args = data;
    264	struct host1x_syncpt *sp;
    265	int err;
    266
    267	if (args->id)
    268		return -EINVAL;
    269
    270	sp = host1x_syncpt_alloc(host1x, HOST1X_SYNCPT_CLIENT_MANAGED, current->comm);
    271	if (!sp)
    272		return -EBUSY;
    273
    274	args->id = host1x_syncpt_id(sp);
    275
    276	err = xa_insert(&fpriv->syncpoints, args->id, sp, GFP_KERNEL);
    277	if (err) {
    278		host1x_syncpt_put(sp);
    279		return err;
    280	}
    281
    282	return 0;
    283}
    284
    285int tegra_drm_ioctl_syncpoint_free(struct drm_device *drm, void *data, struct drm_file *file)
    286{
    287	struct tegra_drm_file *fpriv = file->driver_priv;
    288	struct drm_tegra_syncpoint_allocate *args = data;
    289	struct host1x_syncpt *sp;
    290
    291	mutex_lock(&fpriv->lock);
    292	sp = xa_erase(&fpriv->syncpoints, args->id);
    293	mutex_unlock(&fpriv->lock);
    294
    295	if (!sp)
    296		return -EINVAL;
    297
    298	host1x_syncpt_put(sp);
    299
    300	return 0;
    301}
    302
    303int tegra_drm_ioctl_syncpoint_wait(struct drm_device *drm, void *data, struct drm_file *file)
    304{
    305	struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
    306	struct drm_tegra_syncpoint_wait *args = data;
    307	signed long timeout_jiffies;
    308	struct host1x_syncpt *sp;
    309
    310	if (args->padding != 0)
    311		return -EINVAL;
    312
    313	sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
    314	if (!sp)
    315		return -EINVAL;
    316
    317	timeout_jiffies = drm_timeout_abs_to_jiffies(args->timeout_ns);
    318
    319	return host1x_syncpt_wait(sp, args->threshold, timeout_jiffies, &args->value);
    320}