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_debugfs.c (12423B)


      1/*
      2 * Created: Sun Dec 21 13:08:50 2008 by bgamari@gmail.com
      3 *
      4 * Copyright 2008 Ben Gamari <bgamari@gmail.com>
      5 *
      6 * Permission is hereby granted, free of charge, to any person obtaining a
      7 * copy of this software and associated documentation files (the "Software"),
      8 * to deal in the Software without restriction, including without limitation
      9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10 * and/or sell copies of the Software, and to permit persons to whom the
     11 * Software is furnished to do so, subject to the following conditions:
     12 *
     13 * The above copyright notice and this permission notice (including the next
     14 * paragraph) shall be included in all copies or substantial portions of the
     15 * Software.
     16 *
     17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
     21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     23 * OTHER DEALINGS IN THE SOFTWARE.
     24 */
     25
     26#include <linux/debugfs.h>
     27#include <linux/export.h>
     28#include <linux/seq_file.h>
     29#include <linux/slab.h>
     30#include <linux/uaccess.h>
     31
     32#include <drm/drm_atomic.h>
     33#include <drm/drm_auth.h>
     34#include <drm/drm_client.h>
     35#include <drm/drm_debugfs.h>
     36#include <drm/drm_device.h>
     37#include <drm/drm_drv.h>
     38#include <drm/drm_edid.h>
     39#include <drm/drm_file.h>
     40#include <drm/drm_gem.h>
     41
     42#include "drm_crtc_internal.h"
     43#include "drm_internal.h"
     44
     45#if defined(CONFIG_DEBUG_FS)
     46
     47/***************************************************
     48 * Initialization, etc.
     49 **************************************************/
     50
     51static int drm_name_info(struct seq_file *m, void *data)
     52{
     53	struct drm_info_node *node = (struct drm_info_node *) m->private;
     54	struct drm_minor *minor = node->minor;
     55	struct drm_device *dev = minor->dev;
     56	struct drm_master *master;
     57
     58	mutex_lock(&dev->master_mutex);
     59	master = dev->master;
     60	seq_printf(m, "%s", dev->driver->name);
     61	if (dev->dev)
     62		seq_printf(m, " dev=%s", dev_name(dev->dev));
     63	if (master && master->unique)
     64		seq_printf(m, " master=%s", master->unique);
     65	if (dev->unique)
     66		seq_printf(m, " unique=%s", dev->unique);
     67	seq_printf(m, "\n");
     68	mutex_unlock(&dev->master_mutex);
     69
     70	return 0;
     71}
     72
     73static int drm_clients_info(struct seq_file *m, void *data)
     74{
     75	struct drm_info_node *node = (struct drm_info_node *) m->private;
     76	struct drm_device *dev = node->minor->dev;
     77	struct drm_file *priv;
     78	kuid_t uid;
     79
     80	seq_printf(m,
     81		   "%20s %5s %3s master a %5s %10s\n",
     82		   "command",
     83		   "pid",
     84		   "dev",
     85		   "uid",
     86		   "magic");
     87
     88	/* dev->filelist is sorted youngest first, but we want to present
     89	 * oldest first (i.e. kernel, servers, clients), so walk backwardss.
     90	 */
     91	mutex_lock(&dev->filelist_mutex);
     92	list_for_each_entry_reverse(priv, &dev->filelist, lhead) {
     93		struct task_struct *task;
     94		bool is_current_master = drm_is_current_master(priv);
     95
     96		rcu_read_lock(); /* locks pid_task()->comm */
     97		task = pid_task(priv->pid, PIDTYPE_PID);
     98		uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID;
     99		seq_printf(m, "%20s %5d %3d   %c    %c %5d %10u\n",
    100			   task ? task->comm : "<unknown>",
    101			   pid_vnr(priv->pid),
    102			   priv->minor->index,
    103			   is_current_master ? 'y' : 'n',
    104			   priv->authenticated ? 'y' : 'n',
    105			   from_kuid_munged(seq_user_ns(m), uid),
    106			   priv->magic);
    107		rcu_read_unlock();
    108	}
    109	mutex_unlock(&dev->filelist_mutex);
    110	return 0;
    111}
    112
    113static int drm_gem_one_name_info(int id, void *ptr, void *data)
    114{
    115	struct drm_gem_object *obj = ptr;
    116	struct seq_file *m = data;
    117
    118	seq_printf(m, "%6d %8zd %7d %8d\n",
    119		   obj->name, obj->size,
    120		   obj->handle_count,
    121		   kref_read(&obj->refcount));
    122	return 0;
    123}
    124
    125static int drm_gem_name_info(struct seq_file *m, void *data)
    126{
    127	struct drm_info_node *node = (struct drm_info_node *) m->private;
    128	struct drm_device *dev = node->minor->dev;
    129
    130	seq_printf(m, "  name     size handles refcount\n");
    131
    132	mutex_lock(&dev->object_name_lock);
    133	idr_for_each(&dev->object_name_idr, drm_gem_one_name_info, m);
    134	mutex_unlock(&dev->object_name_lock);
    135
    136	return 0;
    137}
    138
    139static const struct drm_info_list drm_debugfs_list[] = {
    140	{"name", drm_name_info, 0},
    141	{"clients", drm_clients_info, 0},
    142	{"gem_names", drm_gem_name_info, DRIVER_GEM},
    143};
    144#define DRM_DEBUGFS_ENTRIES ARRAY_SIZE(drm_debugfs_list)
    145
    146
    147static int drm_debugfs_open(struct inode *inode, struct file *file)
    148{
    149	struct drm_info_node *node = inode->i_private;
    150
    151	return single_open(file, node->info_ent->show, node);
    152}
    153
    154
    155static const struct file_operations drm_debugfs_fops = {
    156	.owner = THIS_MODULE,
    157	.open = drm_debugfs_open,
    158	.read = seq_read,
    159	.llseek = seq_lseek,
    160	.release = single_release,
    161};
    162
    163
    164/**
    165 * drm_debugfs_create_files - Initialize a given set of debugfs files for DRM
    166 * 			minor
    167 * @files: The array of files to create
    168 * @count: The number of files given
    169 * @root: DRI debugfs dir entry.
    170 * @minor: device minor number
    171 *
    172 * Create a given set of debugfs files represented by an array of
    173 * &struct drm_info_list in the given root directory. These files will be removed
    174 * automatically on drm_debugfs_cleanup().
    175 */
    176void drm_debugfs_create_files(const struct drm_info_list *files, int count,
    177			      struct dentry *root, struct drm_minor *minor)
    178{
    179	struct drm_device *dev = minor->dev;
    180	struct drm_info_node *tmp;
    181	int i;
    182
    183	for (i = 0; i < count; i++) {
    184		u32 features = files[i].driver_features;
    185
    186		if (features && !drm_core_check_all_features(dev, features))
    187			continue;
    188
    189		tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
    190		if (tmp == NULL)
    191			continue;
    192
    193		tmp->minor = minor;
    194		tmp->dent = debugfs_create_file(files[i].name,
    195						S_IFREG | S_IRUGO, root, tmp,
    196						&drm_debugfs_fops);
    197		tmp->info_ent = &files[i];
    198
    199		mutex_lock(&minor->debugfs_lock);
    200		list_add(&tmp->list, &minor->debugfs_list);
    201		mutex_unlock(&minor->debugfs_lock);
    202	}
    203}
    204EXPORT_SYMBOL(drm_debugfs_create_files);
    205
    206int drm_debugfs_init(struct drm_minor *minor, int minor_id,
    207		     struct dentry *root)
    208{
    209	struct drm_device *dev = minor->dev;
    210	char name[64];
    211
    212	INIT_LIST_HEAD(&minor->debugfs_list);
    213	mutex_init(&minor->debugfs_lock);
    214	sprintf(name, "%d", minor_id);
    215	minor->debugfs_root = debugfs_create_dir(name, root);
    216
    217	drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES,
    218				 minor->debugfs_root, minor);
    219
    220	if (drm_drv_uses_atomic_modeset(dev)) {
    221		drm_atomic_debugfs_init(minor);
    222	}
    223
    224	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
    225		drm_framebuffer_debugfs_init(minor);
    226
    227		drm_client_debugfs_init(minor);
    228	}
    229
    230	if (dev->driver->debugfs_init)
    231		dev->driver->debugfs_init(minor);
    232
    233	return 0;
    234}
    235
    236
    237int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
    238			     struct drm_minor *minor)
    239{
    240	struct list_head *pos, *q;
    241	struct drm_info_node *tmp;
    242	int i;
    243
    244	mutex_lock(&minor->debugfs_lock);
    245	for (i = 0; i < count; i++) {
    246		list_for_each_safe(pos, q, &minor->debugfs_list) {
    247			tmp = list_entry(pos, struct drm_info_node, list);
    248			if (tmp->info_ent == &files[i]) {
    249				debugfs_remove(tmp->dent);
    250				list_del(pos);
    251				kfree(tmp);
    252			}
    253		}
    254	}
    255	mutex_unlock(&minor->debugfs_lock);
    256	return 0;
    257}
    258EXPORT_SYMBOL(drm_debugfs_remove_files);
    259
    260static void drm_debugfs_remove_all_files(struct drm_minor *minor)
    261{
    262	struct drm_info_node *node, *tmp;
    263
    264	mutex_lock(&minor->debugfs_lock);
    265	list_for_each_entry_safe(node, tmp, &minor->debugfs_list, list) {
    266		debugfs_remove(node->dent);
    267		list_del(&node->list);
    268		kfree(node);
    269	}
    270	mutex_unlock(&minor->debugfs_lock);
    271}
    272
    273void drm_debugfs_cleanup(struct drm_minor *minor)
    274{
    275	if (!minor->debugfs_root)
    276		return;
    277
    278	drm_debugfs_remove_all_files(minor);
    279
    280	debugfs_remove_recursive(minor->debugfs_root);
    281	minor->debugfs_root = NULL;
    282}
    283
    284static int connector_show(struct seq_file *m, void *data)
    285{
    286	struct drm_connector *connector = m->private;
    287
    288	seq_printf(m, "%s\n", drm_get_connector_force_name(connector->force));
    289
    290	return 0;
    291}
    292
    293static int connector_open(struct inode *inode, struct file *file)
    294{
    295	struct drm_connector *dev = inode->i_private;
    296
    297	return single_open(file, connector_show, dev);
    298}
    299
    300static ssize_t connector_write(struct file *file, const char __user *ubuf,
    301			       size_t len, loff_t *offp)
    302{
    303	struct seq_file *m = file->private_data;
    304	struct drm_connector *connector = m->private;
    305	char buf[12];
    306
    307	if (len > sizeof(buf) - 1)
    308		return -EINVAL;
    309
    310	if (copy_from_user(buf, ubuf, len))
    311		return -EFAULT;
    312
    313	buf[len] = '\0';
    314
    315	if (sysfs_streq(buf, "on"))
    316		connector->force = DRM_FORCE_ON;
    317	else if (sysfs_streq(buf, "digital"))
    318		connector->force = DRM_FORCE_ON_DIGITAL;
    319	else if (sysfs_streq(buf, "off"))
    320		connector->force = DRM_FORCE_OFF;
    321	else if (sysfs_streq(buf, "unspecified"))
    322		connector->force = DRM_FORCE_UNSPECIFIED;
    323	else
    324		return -EINVAL;
    325
    326	return len;
    327}
    328
    329static int edid_show(struct seq_file *m, void *data)
    330{
    331	struct drm_connector *connector = m->private;
    332	struct drm_property_blob *edid = connector->edid_blob_ptr;
    333
    334	if (connector->override_edid && edid)
    335		seq_write(m, edid->data, edid->length);
    336
    337	return 0;
    338}
    339
    340static int edid_open(struct inode *inode, struct file *file)
    341{
    342	struct drm_connector *dev = inode->i_private;
    343
    344	return single_open(file, edid_show, dev);
    345}
    346
    347static ssize_t edid_write(struct file *file, const char __user *ubuf,
    348			  size_t len, loff_t *offp)
    349{
    350	struct seq_file *m = file->private_data;
    351	struct drm_connector *connector = m->private;
    352	char *buf;
    353	struct edid *edid;
    354	int ret;
    355
    356	buf = memdup_user(ubuf, len);
    357	if (IS_ERR(buf))
    358		return PTR_ERR(buf);
    359
    360	edid = (struct edid *) buf;
    361
    362	if (len == 5 && !strncmp(buf, "reset", 5)) {
    363		connector->override_edid = false;
    364		ret = drm_connector_update_edid_property(connector, NULL);
    365	} else if (len < EDID_LENGTH ||
    366		   EDID_LENGTH * (1 + edid->extensions) > len)
    367		ret = -EINVAL;
    368	else {
    369		connector->override_edid = false;
    370		ret = drm_connector_update_edid_property(connector, edid);
    371		if (!ret)
    372			connector->override_edid = true;
    373	}
    374
    375	kfree(buf);
    376
    377	return (ret) ? ret : len;
    378}
    379
    380/*
    381 * Returns the min and max vrr vfreq through the connector's debugfs file.
    382 * Example usage: cat /sys/kernel/debug/dri/0/DP-1/vrr_range
    383 */
    384static int vrr_range_show(struct seq_file *m, void *data)
    385{
    386	struct drm_connector *connector = m->private;
    387
    388	if (connector->status != connector_status_connected)
    389		return -ENODEV;
    390
    391	seq_printf(m, "Min: %u\n", (u8)connector->display_info.monitor_range.min_vfreq);
    392	seq_printf(m, "Max: %u\n", (u8)connector->display_info.monitor_range.max_vfreq);
    393
    394	return 0;
    395}
    396DEFINE_SHOW_ATTRIBUTE(vrr_range);
    397
    398static const struct file_operations drm_edid_fops = {
    399	.owner = THIS_MODULE,
    400	.open = edid_open,
    401	.read = seq_read,
    402	.llseek = seq_lseek,
    403	.release = single_release,
    404	.write = edid_write
    405};
    406
    407
    408static const struct file_operations drm_connector_fops = {
    409	.owner = THIS_MODULE,
    410	.open = connector_open,
    411	.read = seq_read,
    412	.llseek = seq_lseek,
    413	.release = single_release,
    414	.write = connector_write
    415};
    416
    417void drm_debugfs_connector_add(struct drm_connector *connector)
    418{
    419	struct drm_minor *minor = connector->dev->primary;
    420	struct dentry *root;
    421
    422	if (!minor->debugfs_root)
    423		return;
    424
    425	root = debugfs_create_dir(connector->name, minor->debugfs_root);
    426	connector->debugfs_entry = root;
    427
    428	/* force */
    429	debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector,
    430			    &drm_connector_fops);
    431
    432	/* edid */
    433	debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root, connector,
    434			    &drm_edid_fops);
    435
    436	/* vrr range */
    437	debugfs_create_file("vrr_range", S_IRUGO, root, connector,
    438			    &vrr_range_fops);
    439
    440	if (connector->funcs->debugfs_init)
    441		connector->funcs->debugfs_init(connector, root);
    442}
    443
    444void drm_debugfs_connector_remove(struct drm_connector *connector)
    445{
    446	if (!connector->debugfs_entry)
    447		return;
    448
    449	debugfs_remove_recursive(connector->debugfs_entry);
    450
    451	connector->debugfs_entry = NULL;
    452}
    453
    454void drm_debugfs_crtc_add(struct drm_crtc *crtc)
    455{
    456	struct drm_minor *minor = crtc->dev->primary;
    457	struct dentry *root;
    458	char *name;
    459
    460	name = kasprintf(GFP_KERNEL, "crtc-%d", crtc->index);
    461	if (!name)
    462		return;
    463
    464	root = debugfs_create_dir(name, minor->debugfs_root);
    465	kfree(name);
    466
    467	crtc->debugfs_entry = root;
    468
    469	drm_debugfs_crtc_crc_add(crtc);
    470}
    471
    472void drm_debugfs_crtc_remove(struct drm_crtc *crtc)
    473{
    474	debugfs_remove_recursive(crtc->debugfs_entry);
    475	crtc->debugfs_entry = NULL;
    476}
    477
    478#endif /* CONFIG_DEBUG_FS */