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

ptdma-dmaengine.c (9485B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * AMD Passthrough DMA device driver
      4 * -- Based on the CCP driver
      5 *
      6 * Copyright (C) 2016,2021 Advanced Micro Devices, Inc.
      7 *
      8 * Author: Sanjay R Mehta <sanju.mehta@amd.com>
      9 * Author: Gary R Hook <gary.hook@amd.com>
     10 */
     11
     12#include "ptdma.h"
     13#include "../dmaengine.h"
     14#include "../virt-dma.h"
     15
     16static inline struct pt_dma_chan *to_pt_chan(struct dma_chan *dma_chan)
     17{
     18	return container_of(dma_chan, struct pt_dma_chan, vc.chan);
     19}
     20
     21static inline struct pt_dma_desc *to_pt_desc(struct virt_dma_desc *vd)
     22{
     23	return container_of(vd, struct pt_dma_desc, vd);
     24}
     25
     26static void pt_free_chan_resources(struct dma_chan *dma_chan)
     27{
     28	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
     29
     30	vchan_free_chan_resources(&chan->vc);
     31}
     32
     33static void pt_synchronize(struct dma_chan *dma_chan)
     34{
     35	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
     36
     37	vchan_synchronize(&chan->vc);
     38}
     39
     40static void pt_do_cleanup(struct virt_dma_desc *vd)
     41{
     42	struct pt_dma_desc *desc = to_pt_desc(vd);
     43	struct pt_device *pt = desc->pt;
     44
     45	kmem_cache_free(pt->dma_desc_cache, desc);
     46}
     47
     48static int pt_dma_start_desc(struct pt_dma_desc *desc)
     49{
     50	struct pt_passthru_engine *pt_engine;
     51	struct pt_device *pt;
     52	struct pt_cmd *pt_cmd;
     53	struct pt_cmd_queue *cmd_q;
     54
     55	desc->issued_to_hw = 1;
     56
     57	pt_cmd = &desc->pt_cmd;
     58	pt = pt_cmd->pt;
     59	cmd_q = &pt->cmd_q;
     60	pt_engine = &pt_cmd->passthru;
     61
     62	pt->tdata.cmd = pt_cmd;
     63
     64	/* Execute the command */
     65	pt_cmd->ret = pt_core_perform_passthru(cmd_q, pt_engine);
     66
     67	return 0;
     68}
     69
     70static struct pt_dma_desc *pt_next_dma_desc(struct pt_dma_chan *chan)
     71{
     72	/* Get the next DMA descriptor on the active list */
     73	struct virt_dma_desc *vd = vchan_next_desc(&chan->vc);
     74
     75	return vd ? to_pt_desc(vd) : NULL;
     76}
     77
     78static struct pt_dma_desc *pt_handle_active_desc(struct pt_dma_chan *chan,
     79						 struct pt_dma_desc *desc)
     80{
     81	struct dma_async_tx_descriptor *tx_desc;
     82	struct virt_dma_desc *vd;
     83	unsigned long flags;
     84
     85	/* Loop over descriptors until one is found with commands */
     86	do {
     87		if (desc) {
     88			if (!desc->issued_to_hw) {
     89				/* No errors, keep going */
     90				if (desc->status != DMA_ERROR)
     91					return desc;
     92			}
     93
     94			tx_desc = &desc->vd.tx;
     95			vd = &desc->vd;
     96		} else {
     97			tx_desc = NULL;
     98		}
     99
    100		spin_lock_irqsave(&chan->vc.lock, flags);
    101
    102		if (desc) {
    103			if (desc->status != DMA_COMPLETE) {
    104				if (desc->status != DMA_ERROR)
    105					desc->status = DMA_COMPLETE;
    106
    107				dma_cookie_complete(tx_desc);
    108				dma_descriptor_unmap(tx_desc);
    109				list_del(&desc->vd.node);
    110			} else {
    111				/* Don't handle it twice */
    112				tx_desc = NULL;
    113			}
    114		}
    115
    116		desc = pt_next_dma_desc(chan);
    117
    118		spin_unlock_irqrestore(&chan->vc.lock, flags);
    119
    120		if (tx_desc) {
    121			dmaengine_desc_get_callback_invoke(tx_desc, NULL);
    122			dma_run_dependencies(tx_desc);
    123			vchan_vdesc_fini(vd);
    124		}
    125	} while (desc);
    126
    127	return NULL;
    128}
    129
    130static void pt_cmd_callback(void *data, int err)
    131{
    132	struct pt_dma_desc *desc = data;
    133	struct dma_chan *dma_chan;
    134	struct pt_dma_chan *chan;
    135	int ret;
    136
    137	if (err == -EINPROGRESS)
    138		return;
    139
    140	dma_chan = desc->vd.tx.chan;
    141	chan = to_pt_chan(dma_chan);
    142
    143	if (err)
    144		desc->status = DMA_ERROR;
    145
    146	while (true) {
    147		/* Check for DMA descriptor completion */
    148		desc = pt_handle_active_desc(chan, desc);
    149
    150		/* Don't submit cmd if no descriptor or DMA is paused */
    151		if (!desc)
    152			break;
    153
    154		ret = pt_dma_start_desc(desc);
    155		if (!ret)
    156			break;
    157
    158		desc->status = DMA_ERROR;
    159	}
    160}
    161
    162static struct pt_dma_desc *pt_alloc_dma_desc(struct pt_dma_chan *chan,
    163					     unsigned long flags)
    164{
    165	struct pt_dma_desc *desc;
    166
    167	desc = kmem_cache_zalloc(chan->pt->dma_desc_cache, GFP_NOWAIT);
    168	if (!desc)
    169		return NULL;
    170
    171	vchan_tx_prep(&chan->vc, &desc->vd, flags);
    172
    173	desc->pt = chan->pt;
    174	desc->pt->cmd_q.int_en = !!(flags & DMA_PREP_INTERRUPT);
    175	desc->issued_to_hw = 0;
    176	desc->status = DMA_IN_PROGRESS;
    177
    178	return desc;
    179}
    180
    181static struct pt_dma_desc *pt_create_desc(struct dma_chan *dma_chan,
    182					  dma_addr_t dst,
    183					  dma_addr_t src,
    184					  unsigned int len,
    185					  unsigned long flags)
    186{
    187	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
    188	struct pt_passthru_engine *pt_engine;
    189	struct pt_dma_desc *desc;
    190	struct pt_cmd *pt_cmd;
    191
    192	desc = pt_alloc_dma_desc(chan, flags);
    193	if (!desc)
    194		return NULL;
    195
    196	pt_cmd = &desc->pt_cmd;
    197	pt_cmd->pt = chan->pt;
    198	pt_engine = &pt_cmd->passthru;
    199	pt_cmd->engine = PT_ENGINE_PASSTHRU;
    200	pt_engine->src_dma = src;
    201	pt_engine->dst_dma = dst;
    202	pt_engine->src_len = len;
    203	pt_cmd->pt_cmd_callback = pt_cmd_callback;
    204	pt_cmd->data = desc;
    205
    206	desc->len = len;
    207
    208	return desc;
    209}
    210
    211static struct dma_async_tx_descriptor *
    212pt_prep_dma_memcpy(struct dma_chan *dma_chan, dma_addr_t dst,
    213		   dma_addr_t src, size_t len, unsigned long flags)
    214{
    215	struct pt_dma_desc *desc;
    216
    217	desc = pt_create_desc(dma_chan, dst, src, len, flags);
    218	if (!desc)
    219		return NULL;
    220
    221	return &desc->vd.tx;
    222}
    223
    224static struct dma_async_tx_descriptor *
    225pt_prep_dma_interrupt(struct dma_chan *dma_chan, unsigned long flags)
    226{
    227	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
    228	struct pt_dma_desc *desc;
    229
    230	desc = pt_alloc_dma_desc(chan, flags);
    231	if (!desc)
    232		return NULL;
    233
    234	return &desc->vd.tx;
    235}
    236
    237static void pt_issue_pending(struct dma_chan *dma_chan)
    238{
    239	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
    240	struct pt_dma_desc *desc;
    241	unsigned long flags;
    242	bool engine_is_idle = true;
    243
    244	spin_lock_irqsave(&chan->vc.lock, flags);
    245
    246	desc = pt_next_dma_desc(chan);
    247	if (desc)
    248		engine_is_idle = false;
    249
    250	vchan_issue_pending(&chan->vc);
    251
    252	desc = pt_next_dma_desc(chan);
    253
    254	spin_unlock_irqrestore(&chan->vc.lock, flags);
    255
    256	/* If there was nothing active, start processing */
    257	if (engine_is_idle)
    258		pt_cmd_callback(desc, 0);
    259}
    260
    261static enum dma_status
    262pt_tx_status(struct dma_chan *c, dma_cookie_t cookie,
    263		struct dma_tx_state *txstate)
    264{
    265	struct pt_device *pt = to_pt_chan(c)->pt;
    266	struct pt_cmd_queue *cmd_q = &pt->cmd_q;
    267
    268	pt_check_status_trans(pt, cmd_q);
    269	return dma_cookie_status(c, cookie, txstate);
    270}
    271
    272static int pt_pause(struct dma_chan *dma_chan)
    273{
    274	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
    275	unsigned long flags;
    276
    277	spin_lock_irqsave(&chan->vc.lock, flags);
    278	pt_stop_queue(&chan->pt->cmd_q);
    279	spin_unlock_irqrestore(&chan->vc.lock, flags);
    280
    281	return 0;
    282}
    283
    284static int pt_resume(struct dma_chan *dma_chan)
    285{
    286	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
    287	struct pt_dma_desc *desc = NULL;
    288	unsigned long flags;
    289
    290	spin_lock_irqsave(&chan->vc.lock, flags);
    291	pt_start_queue(&chan->pt->cmd_q);
    292	desc = pt_next_dma_desc(chan);
    293	spin_unlock_irqrestore(&chan->vc.lock, flags);
    294
    295	/* If there was something active, re-start */
    296	if (desc)
    297		pt_cmd_callback(desc, 0);
    298
    299	return 0;
    300}
    301
    302static int pt_terminate_all(struct dma_chan *dma_chan)
    303{
    304	struct pt_dma_chan *chan = to_pt_chan(dma_chan);
    305	unsigned long flags;
    306	struct pt_cmd_queue *cmd_q = &chan->pt->cmd_q;
    307	LIST_HEAD(head);
    308
    309	iowrite32(SUPPORTED_INTERRUPTS, cmd_q->reg_control + 0x0010);
    310	spin_lock_irqsave(&chan->vc.lock, flags);
    311	vchan_get_all_descriptors(&chan->vc, &head);
    312	spin_unlock_irqrestore(&chan->vc.lock, flags);
    313
    314	vchan_dma_desc_free_list(&chan->vc, &head);
    315	vchan_free_chan_resources(&chan->vc);
    316
    317	return 0;
    318}
    319
    320int pt_dmaengine_register(struct pt_device *pt)
    321{
    322	struct pt_dma_chan *chan;
    323	struct dma_device *dma_dev = &pt->dma_dev;
    324	char *cmd_cache_name;
    325	char *desc_cache_name;
    326	int ret;
    327
    328	pt->pt_dma_chan = devm_kzalloc(pt->dev, sizeof(*pt->pt_dma_chan),
    329				       GFP_KERNEL);
    330	if (!pt->pt_dma_chan)
    331		return -ENOMEM;
    332
    333	cmd_cache_name = devm_kasprintf(pt->dev, GFP_KERNEL,
    334					"%s-dmaengine-cmd-cache",
    335					dev_name(pt->dev));
    336	if (!cmd_cache_name)
    337		return -ENOMEM;
    338
    339	desc_cache_name = devm_kasprintf(pt->dev, GFP_KERNEL,
    340					 "%s-dmaengine-desc-cache",
    341					 dev_name(pt->dev));
    342	if (!desc_cache_name) {
    343		ret = -ENOMEM;
    344		goto err_cache;
    345	}
    346
    347	pt->dma_desc_cache = kmem_cache_create(desc_cache_name,
    348					       sizeof(struct pt_dma_desc), 0,
    349					       SLAB_HWCACHE_ALIGN, NULL);
    350	if (!pt->dma_desc_cache) {
    351		ret = -ENOMEM;
    352		goto err_cache;
    353	}
    354
    355	dma_dev->dev = pt->dev;
    356	dma_dev->src_addr_widths = DMA_SLAVE_BUSWIDTH_64_BYTES;
    357	dma_dev->dst_addr_widths = DMA_SLAVE_BUSWIDTH_64_BYTES;
    358	dma_dev->directions = DMA_MEM_TO_MEM;
    359	dma_dev->residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
    360	dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
    361	dma_cap_set(DMA_INTERRUPT, dma_dev->cap_mask);
    362
    363	/*
    364	 * PTDMA is intended to be used with the AMD NTB devices, hence
    365	 * marking it as DMA_PRIVATE.
    366	 */
    367	dma_cap_set(DMA_PRIVATE, dma_dev->cap_mask);
    368
    369	INIT_LIST_HEAD(&dma_dev->channels);
    370
    371	chan = pt->pt_dma_chan;
    372	chan->pt = pt;
    373
    374	/* Set base and prep routines */
    375	dma_dev->device_free_chan_resources = pt_free_chan_resources;
    376	dma_dev->device_prep_dma_memcpy = pt_prep_dma_memcpy;
    377	dma_dev->device_prep_dma_interrupt = pt_prep_dma_interrupt;
    378	dma_dev->device_issue_pending = pt_issue_pending;
    379	dma_dev->device_tx_status = pt_tx_status;
    380	dma_dev->device_pause = pt_pause;
    381	dma_dev->device_resume = pt_resume;
    382	dma_dev->device_terminate_all = pt_terminate_all;
    383	dma_dev->device_synchronize = pt_synchronize;
    384
    385	chan->vc.desc_free = pt_do_cleanup;
    386	vchan_init(&chan->vc, dma_dev);
    387
    388	dma_set_mask_and_coherent(pt->dev, DMA_BIT_MASK(64));
    389
    390	ret = dma_async_device_register(dma_dev);
    391	if (ret)
    392		goto err_reg;
    393
    394	return 0;
    395
    396err_reg:
    397	kmem_cache_destroy(pt->dma_desc_cache);
    398
    399err_cache:
    400	kmem_cache_destroy(pt->dma_cmd_cache);
    401
    402	return ret;
    403}
    404
    405void pt_dmaengine_unregister(struct pt_device *pt)
    406{
    407	struct dma_device *dma_dev = &pt->dma_dev;
    408
    409	dma_async_device_unregister(dma_dev);
    410
    411	kmem_cache_destroy(pt->dma_desc_cache);
    412	kmem_cache_destroy(pt->dma_cmd_cache);
    413}