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

msm_rd.c (9836B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2013 Red Hat
      4 * Author: Rob Clark <robdclark@gmail.com>
      5 */
      6
      7/* For debugging crashes, userspace can:
      8 *
      9 *   tail -f /sys/kernel/debug/dri/<minor>/rd > logfile.rd
     10 *
     11 * to log the cmdstream in a format that is understood by freedreno/cffdump
     12 * utility.  By comparing the last successfully completed fence #, to the
     13 * cmdstream for the next fence, you can narrow down which process and submit
     14 * caused the gpu crash/lockup.
     15 *
     16 * Additionally:
     17 *
     18 *   tail -f /sys/kernel/debug/dri/<minor>/hangrd > logfile.rd
     19 *
     20 * will capture just the cmdstream from submits which triggered a GPU hang.
     21 *
     22 * This bypasses drm_debugfs_create_files() mainly because we need to use
     23 * our own fops for a bit more control.  In particular, we don't want to
     24 * do anything if userspace doesn't have the debugfs file open.
     25 *
     26 * The module-param "rd_full", which defaults to false, enables snapshotting
     27 * all (non-written) buffers in the submit, rather than just cmdstream bo's.
     28 * This is useful to capture the contents of (for example) vbo's or textures,
     29 * or shader programs (if not emitted inline in cmdstream).
     30 */
     31
     32#include <linux/circ_buf.h>
     33#include <linux/debugfs.h>
     34#include <linux/kfifo.h>
     35#include <linux/uaccess.h>
     36#include <linux/wait.h>
     37
     38#include <drm/drm_file.h>
     39
     40#include "msm_drv.h"
     41#include "msm_gpu.h"
     42#include "msm_gem.h"
     43
     44bool rd_full = false;
     45MODULE_PARM_DESC(rd_full, "If true, $debugfs/.../rd will snapshot all buffer contents");
     46module_param_named(rd_full, rd_full, bool, 0600);
     47
     48#ifdef CONFIG_DEBUG_FS
     49
     50enum rd_sect_type {
     51	RD_NONE,
     52	RD_TEST,       /* ascii text */
     53	RD_CMD,        /* ascii text */
     54	RD_GPUADDR,    /* u32 gpuaddr, u32 size */
     55	RD_CONTEXT,    /* raw dump */
     56	RD_CMDSTREAM,  /* raw dump */
     57	RD_CMDSTREAM_ADDR, /* gpu addr of cmdstream */
     58	RD_PARAM,      /* u32 param_type, u32 param_val, u32 bitlen */
     59	RD_FLUSH,      /* empty, clear previous params */
     60	RD_PROGRAM,    /* shader program, raw dump */
     61	RD_VERT_SHADER,
     62	RD_FRAG_SHADER,
     63	RD_BUFFER_CONTENTS,
     64	RD_GPU_ID,
     65	RD_CHIP_ID,
     66};
     67
     68#define BUF_SZ 512  /* should be power of 2 */
     69
     70/* space used: */
     71#define circ_count(circ) \
     72	(CIRC_CNT((circ)->head, (circ)->tail, BUF_SZ))
     73#define circ_count_to_end(circ) \
     74	(CIRC_CNT_TO_END((circ)->head, (circ)->tail, BUF_SZ))
     75/* space available: */
     76#define circ_space(circ) \
     77	(CIRC_SPACE((circ)->head, (circ)->tail, BUF_SZ))
     78#define circ_space_to_end(circ) \
     79	(CIRC_SPACE_TO_END((circ)->head, (circ)->tail, BUF_SZ))
     80
     81struct msm_rd_state {
     82	struct drm_device *dev;
     83
     84	bool open;
     85
     86	/* current submit to read out: */
     87	struct msm_gem_submit *submit;
     88
     89	/* fifo access is synchronized on the producer side by
     90	 * gpu->lock held by submit code (otherwise we could
     91	 * end up w/ cmds logged in different order than they
     92	 * were executed).  And read_lock synchronizes the reads
     93	 */
     94	struct mutex read_lock;
     95
     96	wait_queue_head_t fifo_event;
     97	struct circ_buf fifo;
     98
     99	char buf[BUF_SZ];
    100};
    101
    102static void rd_write(struct msm_rd_state *rd, const void *buf, int sz)
    103{
    104	struct circ_buf *fifo = &rd->fifo;
    105	const char *ptr = buf;
    106
    107	while (sz > 0) {
    108		char *fptr = &fifo->buf[fifo->head];
    109		int n;
    110
    111		wait_event(rd->fifo_event, circ_space(&rd->fifo) > 0 || !rd->open);
    112		if (!rd->open)
    113			return;
    114
    115		/* Note that smp_load_acquire() is not strictly required
    116		 * as CIRC_SPACE_TO_END() does not access the tail more
    117		 * than once.
    118		 */
    119		n = min(sz, circ_space_to_end(&rd->fifo));
    120		memcpy(fptr, ptr, n);
    121
    122		smp_store_release(&fifo->head, (fifo->head + n) & (BUF_SZ - 1));
    123		sz  -= n;
    124		ptr += n;
    125
    126		wake_up_all(&rd->fifo_event);
    127	}
    128}
    129
    130static void rd_write_section(struct msm_rd_state *rd,
    131		enum rd_sect_type type, const void *buf, int sz)
    132{
    133	rd_write(rd, &type, 4);
    134	rd_write(rd, &sz, 4);
    135	rd_write(rd, buf, sz);
    136}
    137
    138static ssize_t rd_read(struct file *file, char __user *buf,
    139		size_t sz, loff_t *ppos)
    140{
    141	struct msm_rd_state *rd = file->private_data;
    142	struct circ_buf *fifo = &rd->fifo;
    143	const char *fptr = &fifo->buf[fifo->tail];
    144	int n = 0, ret = 0;
    145
    146	mutex_lock(&rd->read_lock);
    147
    148	ret = wait_event_interruptible(rd->fifo_event,
    149			circ_count(&rd->fifo) > 0);
    150	if (ret)
    151		goto out;
    152
    153	/* Note that smp_load_acquire() is not strictly required
    154	 * as CIRC_CNT_TO_END() does not access the head more than
    155	 * once.
    156	 */
    157	n = min_t(int, sz, circ_count_to_end(&rd->fifo));
    158	if (copy_to_user(buf, fptr, n)) {
    159		ret = -EFAULT;
    160		goto out;
    161	}
    162
    163	smp_store_release(&fifo->tail, (fifo->tail + n) & (BUF_SZ - 1));
    164	*ppos += n;
    165
    166	wake_up_all(&rd->fifo_event);
    167
    168out:
    169	mutex_unlock(&rd->read_lock);
    170	if (ret)
    171		return ret;
    172	return n;
    173}
    174
    175static int rd_open(struct inode *inode, struct file *file)
    176{
    177	struct msm_rd_state *rd = inode->i_private;
    178	struct drm_device *dev = rd->dev;
    179	struct msm_drm_private *priv = dev->dev_private;
    180	struct msm_gpu *gpu = priv->gpu;
    181	uint64_t val;
    182	uint32_t gpu_id;
    183	uint32_t zero = 0;
    184	int ret = 0;
    185
    186	if (!gpu)
    187		return -ENODEV;
    188
    189	mutex_lock(&gpu->lock);
    190
    191	if (rd->open) {
    192		ret = -EBUSY;
    193		goto out;
    194	}
    195
    196	file->private_data = rd;
    197	rd->open = true;
    198
    199	/* the parsing tools need to know gpu-id to know which
    200	 * register database to load.
    201	 *
    202	 * Note: These particular params do not require a context
    203	 */
    204	gpu->funcs->get_param(gpu, NULL, MSM_PARAM_GPU_ID, &val, &zero);
    205	gpu_id = val;
    206
    207	rd_write_section(rd, RD_GPU_ID, &gpu_id, sizeof(gpu_id));
    208
    209	gpu->funcs->get_param(gpu, NULL, MSM_PARAM_CHIP_ID, &val, &zero);
    210	rd_write_section(rd, RD_CHIP_ID, &val, sizeof(val));
    211
    212out:
    213	mutex_unlock(&gpu->lock);
    214	return ret;
    215}
    216
    217static int rd_release(struct inode *inode, struct file *file)
    218{
    219	struct msm_rd_state *rd = inode->i_private;
    220
    221	rd->open = false;
    222	wake_up_all(&rd->fifo_event);
    223
    224	return 0;
    225}
    226
    227
    228static const struct file_operations rd_debugfs_fops = {
    229	.owner = THIS_MODULE,
    230	.open = rd_open,
    231	.read = rd_read,
    232	.llseek = no_llseek,
    233	.release = rd_release,
    234};
    235
    236
    237static void rd_cleanup(struct msm_rd_state *rd)
    238{
    239	if (!rd)
    240		return;
    241
    242	mutex_destroy(&rd->read_lock);
    243	kfree(rd);
    244}
    245
    246static struct msm_rd_state *rd_init(struct drm_minor *minor, const char *name)
    247{
    248	struct msm_rd_state *rd;
    249
    250	rd = kzalloc(sizeof(*rd), GFP_KERNEL);
    251	if (!rd)
    252		return ERR_PTR(-ENOMEM);
    253
    254	rd->dev = minor->dev;
    255	rd->fifo.buf = rd->buf;
    256
    257	mutex_init(&rd->read_lock);
    258
    259	init_waitqueue_head(&rd->fifo_event);
    260
    261	debugfs_create_file(name, S_IFREG | S_IRUGO, minor->debugfs_root, rd,
    262			    &rd_debugfs_fops);
    263
    264	return rd;
    265}
    266
    267int msm_rd_debugfs_init(struct drm_minor *minor)
    268{
    269	struct msm_drm_private *priv = minor->dev->dev_private;
    270	struct msm_rd_state *rd;
    271	int ret;
    272
    273	/* only create on first minor: */
    274	if (priv->rd)
    275		return 0;
    276
    277	rd = rd_init(minor, "rd");
    278	if (IS_ERR(rd)) {
    279		ret = PTR_ERR(rd);
    280		goto fail;
    281	}
    282
    283	priv->rd = rd;
    284
    285	rd = rd_init(minor, "hangrd");
    286	if (IS_ERR(rd)) {
    287		ret = PTR_ERR(rd);
    288		goto fail;
    289	}
    290
    291	priv->hangrd = rd;
    292
    293	return 0;
    294
    295fail:
    296	msm_rd_debugfs_cleanup(priv);
    297	return ret;
    298}
    299
    300void msm_rd_debugfs_cleanup(struct msm_drm_private *priv)
    301{
    302	rd_cleanup(priv->rd);
    303	priv->rd = NULL;
    304
    305	rd_cleanup(priv->hangrd);
    306	priv->hangrd = NULL;
    307}
    308
    309static void snapshot_buf(struct msm_rd_state *rd,
    310		struct msm_gem_submit *submit, int idx,
    311		uint64_t iova, uint32_t size, bool full)
    312{
    313	struct msm_gem_object *obj = submit->bos[idx].obj;
    314	unsigned offset = 0;
    315	const char *buf;
    316
    317	if (iova) {
    318		offset = iova - submit->bos[idx].iova;
    319	} else {
    320		iova = submit->bos[idx].iova;
    321		size = obj->base.size;
    322	}
    323
    324	/*
    325	 * Always write the GPUADDR header so can get a complete list of all the
    326	 * buffers in the cmd
    327	 */
    328	rd_write_section(rd, RD_GPUADDR,
    329			(uint32_t[3]){ iova, size, iova >> 32 }, 12);
    330
    331	if (!full)
    332		return;
    333
    334	/* But only dump the contents of buffers marked READ */
    335	if (!(submit->bos[idx].flags & MSM_SUBMIT_BO_READ))
    336		return;
    337
    338	msm_gem_lock(&obj->base);
    339	buf = msm_gem_get_vaddr_active(&obj->base);
    340	if (IS_ERR(buf))
    341		goto out_unlock;
    342
    343	buf += offset;
    344
    345	rd_write_section(rd, RD_BUFFER_CONTENTS, buf, size);
    346
    347	msm_gem_put_vaddr_locked(&obj->base);
    348
    349out_unlock:
    350	msm_gem_unlock(&obj->base);
    351}
    352
    353/* called under gpu->lock */
    354void msm_rd_dump_submit(struct msm_rd_state *rd, struct msm_gem_submit *submit,
    355		const char *fmt, ...)
    356{
    357	struct task_struct *task;
    358	char msg[256];
    359	int i, n;
    360
    361	if (!rd->open)
    362		return;
    363
    364	/* writing into fifo is serialized by caller, and
    365	 * rd->read_lock is used to serialize the reads
    366	 */
    367	WARN_ON(!mutex_is_locked(&submit->gpu->lock));
    368
    369	if (fmt) {
    370		va_list args;
    371
    372		va_start(args, fmt);
    373		n = vscnprintf(msg, sizeof(msg), fmt, args);
    374		va_end(args);
    375
    376		rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
    377	}
    378
    379	rcu_read_lock();
    380	task = pid_task(submit->pid, PIDTYPE_PID);
    381	if (task) {
    382		n = scnprintf(msg, sizeof(msg), "%.*s/%d: fence=%u",
    383				TASK_COMM_LEN, task->comm,
    384				pid_nr(submit->pid), submit->seqno);
    385	} else {
    386		n = scnprintf(msg, sizeof(msg), "???/%d: fence=%u",
    387				pid_nr(submit->pid), submit->seqno);
    388	}
    389	rcu_read_unlock();
    390
    391	rd_write_section(rd, RD_CMD, msg, ALIGN(n, 4));
    392
    393	for (i = 0; i < submit->nr_bos; i++)
    394		snapshot_buf(rd, submit, i, 0, 0, should_dump(submit, i));
    395
    396	for (i = 0; i < submit->nr_cmds; i++) {
    397		uint32_t szd  = submit->cmd[i].size; /* in dwords */
    398
    399		/* snapshot cmdstream bo's (if we haven't already): */
    400		if (!should_dump(submit, i)) {
    401			snapshot_buf(rd, submit, submit->cmd[i].idx,
    402					submit->cmd[i].iova, szd * 4, true);
    403		}
    404	}
    405
    406	for (i = 0; i < submit->nr_cmds; i++) {
    407		uint64_t iova = submit->cmd[i].iova;
    408		uint32_t szd  = submit->cmd[i].size; /* in dwords */
    409
    410		switch (submit->cmd[i].type) {
    411		case MSM_SUBMIT_CMD_IB_TARGET_BUF:
    412			/* ignore IB-targets, we've logged the buffer, the
    413			 * parser tool will follow the IB based on the logged
    414			 * buffer/gpuaddr, so nothing more to do.
    415			 */
    416			break;
    417		case MSM_SUBMIT_CMD_CTX_RESTORE_BUF:
    418		case MSM_SUBMIT_CMD_BUF:
    419			rd_write_section(rd, RD_CMDSTREAM_ADDR,
    420				(uint32_t[3]){ iova, szd, iova >> 32 }, 12);
    421			break;
    422		}
    423	}
    424}
    425#endif