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

drm_dp_aux_dev.c (8652B)


      1/*
      2 * Copyright © 2015 Intel Corporation
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice (including the next
     12 * paragraph) shall be included in all copies or substantial portions of the
     13 * Software.
     14 *
     15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
     21 * IN THE SOFTWARE.
     22 *
     23 * Authors:
     24 *    Rafael Antognolli <rafael.antognolli@intel.com>
     25 *
     26 */
     27
     28#include <linux/device.h>
     29#include <linux/fs.h>
     30#include <linux/init.h>
     31#include <linux/kernel.h>
     32#include <linux/module.h>
     33#include <linux/sched/signal.h>
     34#include <linux/slab.h>
     35#include <linux/uaccess.h>
     36#include <linux/uio.h>
     37
     38#include <drm/display/drm_dp_helper.h>
     39#include <drm/display/drm_dp_mst_helper.h>
     40#include <drm/drm_crtc.h>
     41#include <drm/drm_print.h>
     42
     43#include "drm_dp_helper_internal.h"
     44
     45struct drm_dp_aux_dev {
     46	unsigned index;
     47	struct drm_dp_aux *aux;
     48	struct device *dev;
     49	struct kref refcount;
     50	atomic_t usecount;
     51};
     52
     53#define DRM_AUX_MINORS	256
     54#define AUX_MAX_OFFSET	(1 << 20)
     55static DEFINE_IDR(aux_idr);
     56static DEFINE_MUTEX(aux_idr_mutex);
     57static struct class *drm_dp_aux_dev_class;
     58static int drm_dev_major = -1;
     59
     60static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_minor(unsigned index)
     61{
     62	struct drm_dp_aux_dev *aux_dev = NULL;
     63
     64	mutex_lock(&aux_idr_mutex);
     65	aux_dev = idr_find(&aux_idr, index);
     66	if (aux_dev && !kref_get_unless_zero(&aux_dev->refcount))
     67		aux_dev = NULL;
     68	mutex_unlock(&aux_idr_mutex);
     69
     70	return aux_dev;
     71}
     72
     73static struct drm_dp_aux_dev *alloc_drm_dp_aux_dev(struct drm_dp_aux *aux)
     74{
     75	struct drm_dp_aux_dev *aux_dev;
     76	int index;
     77
     78	aux_dev = kzalloc(sizeof(*aux_dev), GFP_KERNEL);
     79	if (!aux_dev)
     80		return ERR_PTR(-ENOMEM);
     81	aux_dev->aux = aux;
     82	atomic_set(&aux_dev->usecount, 1);
     83	kref_init(&aux_dev->refcount);
     84
     85	mutex_lock(&aux_idr_mutex);
     86	index = idr_alloc(&aux_idr, aux_dev, 0, DRM_AUX_MINORS, GFP_KERNEL);
     87	mutex_unlock(&aux_idr_mutex);
     88	if (index < 0) {
     89		kfree(aux_dev);
     90		return ERR_PTR(index);
     91	}
     92	aux_dev->index = index;
     93
     94	return aux_dev;
     95}
     96
     97static void release_drm_dp_aux_dev(struct kref *ref)
     98{
     99	struct drm_dp_aux_dev *aux_dev =
    100		container_of(ref, struct drm_dp_aux_dev, refcount);
    101
    102	kfree(aux_dev);
    103}
    104
    105static ssize_t name_show(struct device *dev,
    106			 struct device_attribute *attr, char *buf)
    107{
    108	ssize_t res;
    109	struct drm_dp_aux_dev *aux_dev =
    110		drm_dp_aux_dev_get_by_minor(MINOR(dev->devt));
    111
    112	if (!aux_dev)
    113		return -ENODEV;
    114
    115	res = sprintf(buf, "%s\n", aux_dev->aux->name);
    116	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
    117
    118	return res;
    119}
    120static DEVICE_ATTR_RO(name);
    121
    122static struct attribute *drm_dp_aux_attrs[] = {
    123	&dev_attr_name.attr,
    124	NULL,
    125};
    126ATTRIBUTE_GROUPS(drm_dp_aux);
    127
    128static int auxdev_open(struct inode *inode, struct file *file)
    129{
    130	unsigned int minor = iminor(inode);
    131	struct drm_dp_aux_dev *aux_dev;
    132
    133	aux_dev = drm_dp_aux_dev_get_by_minor(minor);
    134	if (!aux_dev)
    135		return -ENODEV;
    136
    137	file->private_data = aux_dev;
    138	return 0;
    139}
    140
    141static loff_t auxdev_llseek(struct file *file, loff_t offset, int whence)
    142{
    143	return fixed_size_llseek(file, offset, whence, AUX_MAX_OFFSET);
    144}
    145
    146static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
    147{
    148	struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
    149	loff_t pos = iocb->ki_pos;
    150	ssize_t res = 0;
    151
    152	if (!atomic_inc_not_zero(&aux_dev->usecount))
    153		return -ENODEV;
    154
    155	iov_iter_truncate(to, AUX_MAX_OFFSET - pos);
    156
    157	while (iov_iter_count(to)) {
    158		uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
    159		ssize_t todo = min(iov_iter_count(to), sizeof(buf));
    160
    161		if (signal_pending(current)) {
    162			res = -ERESTARTSYS;
    163			break;
    164		}
    165
    166		res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
    167
    168		if (res <= 0)
    169			break;
    170
    171		if (copy_to_iter(buf, res, to) != res) {
    172			res = -EFAULT;
    173			break;
    174		}
    175
    176		pos += res;
    177	}
    178
    179	if (pos != iocb->ki_pos)
    180		res = pos - iocb->ki_pos;
    181	iocb->ki_pos = pos;
    182
    183	if (atomic_dec_and_test(&aux_dev->usecount))
    184		wake_up_var(&aux_dev->usecount);
    185
    186	return res;
    187}
    188
    189static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
    190{
    191	struct drm_dp_aux_dev *aux_dev = iocb->ki_filp->private_data;
    192	loff_t pos = iocb->ki_pos;
    193	ssize_t res = 0;
    194
    195	if (!atomic_inc_not_zero(&aux_dev->usecount))
    196		return -ENODEV;
    197
    198	iov_iter_truncate(from, AUX_MAX_OFFSET - pos);
    199
    200	while (iov_iter_count(from)) {
    201		uint8_t buf[DP_AUX_MAX_PAYLOAD_BYTES];
    202		ssize_t todo = min(iov_iter_count(from), sizeof(buf));
    203
    204		if (signal_pending(current)) {
    205			res = -ERESTARTSYS;
    206			break;
    207		}
    208
    209		if (!copy_from_iter_full(buf, todo, from)) {
    210			res = -EFAULT;
    211			break;
    212		}
    213
    214		res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
    215
    216		if (res <= 0)
    217			break;
    218
    219		pos += res;
    220	}
    221
    222	if (pos != iocb->ki_pos)
    223		res = pos - iocb->ki_pos;
    224	iocb->ki_pos = pos;
    225
    226	if (atomic_dec_and_test(&aux_dev->usecount))
    227		wake_up_var(&aux_dev->usecount);
    228
    229	return res;
    230}
    231
    232static int auxdev_release(struct inode *inode, struct file *file)
    233{
    234	struct drm_dp_aux_dev *aux_dev = file->private_data;
    235
    236	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
    237	return 0;
    238}
    239
    240static const struct file_operations auxdev_fops = {
    241	.owner		= THIS_MODULE,
    242	.llseek		= auxdev_llseek,
    243	.read_iter	= auxdev_read_iter,
    244	.write_iter	= auxdev_write_iter,
    245	.open		= auxdev_open,
    246	.release	= auxdev_release,
    247};
    248
    249#define to_auxdev(d) container_of(d, struct drm_dp_aux_dev, aux)
    250
    251static struct drm_dp_aux_dev *drm_dp_aux_dev_get_by_aux(struct drm_dp_aux *aux)
    252{
    253	struct drm_dp_aux_dev *iter, *aux_dev = NULL;
    254	int id;
    255
    256	/* don't increase kref count here because this function should only be
    257	 * used by drm_dp_aux_unregister_devnode. Thus, it will always have at
    258	 * least one reference - the one that drm_dp_aux_register_devnode
    259	 * created
    260	 */
    261	mutex_lock(&aux_idr_mutex);
    262	idr_for_each_entry(&aux_idr, iter, id) {
    263		if (iter->aux == aux) {
    264			aux_dev = iter;
    265			break;
    266		}
    267	}
    268	mutex_unlock(&aux_idr_mutex);
    269	return aux_dev;
    270}
    271
    272void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux)
    273{
    274	struct drm_dp_aux_dev *aux_dev;
    275	unsigned int minor;
    276
    277	aux_dev = drm_dp_aux_dev_get_by_aux(aux);
    278	if (!aux_dev) /* attach must have failed */
    279		return;
    280
    281	/*
    282	 * As some AUX adapters may exist as platform devices which outlive their respective DRM
    283	 * devices, we clear drm_dev to ensure that we never accidentally reference a stale pointer
    284	 */
    285	aux->drm_dev = NULL;
    286
    287	mutex_lock(&aux_idr_mutex);
    288	idr_remove(&aux_idr, aux_dev->index);
    289	mutex_unlock(&aux_idr_mutex);
    290
    291	atomic_dec(&aux_dev->usecount);
    292	wait_var_event(&aux_dev->usecount, !atomic_read(&aux_dev->usecount));
    293
    294	minor = aux_dev->index;
    295	if (aux_dev->dev)
    296		device_destroy(drm_dp_aux_dev_class,
    297			       MKDEV(drm_dev_major, minor));
    298
    299	DRM_DEBUG("drm_dp_aux_dev: aux [%s] unregistering\n", aux->name);
    300	kref_put(&aux_dev->refcount, release_drm_dp_aux_dev);
    301}
    302
    303int drm_dp_aux_register_devnode(struct drm_dp_aux *aux)
    304{
    305	struct drm_dp_aux_dev *aux_dev;
    306	int res;
    307
    308	aux_dev = alloc_drm_dp_aux_dev(aux);
    309	if (IS_ERR(aux_dev))
    310		return PTR_ERR(aux_dev);
    311
    312	aux_dev->dev = device_create(drm_dp_aux_dev_class, aux->dev,
    313				     MKDEV(drm_dev_major, aux_dev->index), NULL,
    314				     "drm_dp_aux%d", aux_dev->index);
    315	if (IS_ERR(aux_dev->dev)) {
    316		res = PTR_ERR(aux_dev->dev);
    317		aux_dev->dev = NULL;
    318		goto error;
    319	}
    320
    321	DRM_DEBUG("drm_dp_aux_dev: aux [%s] registered as minor %d\n",
    322		  aux->name, aux_dev->index);
    323	return 0;
    324error:
    325	drm_dp_aux_unregister_devnode(aux);
    326	return res;
    327}
    328
    329int drm_dp_aux_dev_init(void)
    330{
    331	int res;
    332
    333	drm_dp_aux_dev_class = class_create(THIS_MODULE, "drm_dp_aux_dev");
    334	if (IS_ERR(drm_dp_aux_dev_class)) {
    335		return PTR_ERR(drm_dp_aux_dev_class);
    336	}
    337	drm_dp_aux_dev_class->dev_groups = drm_dp_aux_groups;
    338
    339	res = register_chrdev(0, "aux", &auxdev_fops);
    340	if (res < 0)
    341		goto out;
    342	drm_dev_major = res;
    343
    344	return 0;
    345out:
    346	class_destroy(drm_dp_aux_dev_class);
    347	return res;
    348}
    349
    350void drm_dp_aux_dev_exit(void)
    351{
    352	unregister_chrdev(drm_dev_major, "aux");
    353	class_destroy(drm_dp_aux_dev_class);
    354}