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

dma-ring.c (5887B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright(c) 2016-2018 Intel Corporation. All rights reserved.
      4 */
      5#include <linux/dma-mapping.h>
      6#include <linux/mei.h>
      7
      8#include "mei_dev.h"
      9
     10/**
     11 * mei_dmam_dscr_alloc() - allocate a managed coherent buffer
     12 *     for the dma descriptor
     13 * @dev: mei_device
     14 * @dscr: dma descriptor
     15 *
     16 * Return:
     17 * * 0       - on success or zero allocation request
     18 * * -EINVAL - if size is not power of 2
     19 * * -ENOMEM - of allocation has failed
     20 */
     21static int mei_dmam_dscr_alloc(struct mei_device *dev,
     22			       struct mei_dma_dscr *dscr)
     23{
     24	if (!dscr->size)
     25		return 0;
     26
     27	if (WARN_ON(!is_power_of_2(dscr->size)))
     28		return -EINVAL;
     29
     30	if (dscr->vaddr)
     31		return 0;
     32
     33	dscr->vaddr = dmam_alloc_coherent(dev->dev, dscr->size, &dscr->daddr,
     34					  GFP_KERNEL);
     35	if (!dscr->vaddr)
     36		return -ENOMEM;
     37
     38	return 0;
     39}
     40
     41/**
     42 * mei_dmam_dscr_free() - free a managed coherent buffer
     43 *     from the dma descriptor
     44 * @dev: mei_device
     45 * @dscr: dma descriptor
     46 */
     47static void mei_dmam_dscr_free(struct mei_device *dev,
     48			       struct mei_dma_dscr *dscr)
     49{
     50	if (!dscr->vaddr)
     51		return;
     52
     53	dmam_free_coherent(dev->dev, dscr->size, dscr->vaddr, dscr->daddr);
     54	dscr->vaddr = NULL;
     55}
     56
     57/**
     58 * mei_dmam_ring_free() - free dma ring buffers
     59 * @dev: mei device
     60 */
     61void mei_dmam_ring_free(struct mei_device *dev)
     62{
     63	int i;
     64
     65	for (i = 0; i < DMA_DSCR_NUM; i++)
     66		mei_dmam_dscr_free(dev, &dev->dr_dscr[i]);
     67}
     68
     69/**
     70 * mei_dmam_ring_alloc() - allocate dma ring buffers
     71 * @dev: mei device
     72 *
     73 * Return: -ENOMEM on allocation failure 0 otherwise
     74 */
     75int mei_dmam_ring_alloc(struct mei_device *dev)
     76{
     77	int i;
     78
     79	for (i = 0; i < DMA_DSCR_NUM; i++)
     80		if (mei_dmam_dscr_alloc(dev, &dev->dr_dscr[i]))
     81			goto err;
     82
     83	return 0;
     84
     85err:
     86	mei_dmam_ring_free(dev);
     87	return -ENOMEM;
     88}
     89
     90/**
     91 * mei_dma_ring_is_allocated() - check if dma ring is allocated
     92 * @dev: mei device
     93 *
     94 * Return: true if dma ring is allocated
     95 */
     96bool mei_dma_ring_is_allocated(struct mei_device *dev)
     97{
     98	return !!dev->dr_dscr[DMA_DSCR_HOST].vaddr;
     99}
    100
    101static inline
    102struct hbm_dma_ring_ctrl *mei_dma_ring_ctrl(struct mei_device *dev)
    103{
    104	return (struct hbm_dma_ring_ctrl *)dev->dr_dscr[DMA_DSCR_CTRL].vaddr;
    105}
    106
    107/**
    108 * mei_dma_ring_reset() - reset the dma control block
    109 * @dev: mei device
    110 */
    111void mei_dma_ring_reset(struct mei_device *dev)
    112{
    113	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
    114
    115	if (!ctrl)
    116		return;
    117
    118	memset(ctrl, 0, sizeof(*ctrl));
    119}
    120
    121/**
    122 * mei_dma_copy_from() - copy from dma ring into buffer
    123 * @dev: mei device
    124 * @buf: data buffer
    125 * @offset: offset in slots.
    126 * @n: number of slots to copy.
    127 */
    128static size_t mei_dma_copy_from(struct mei_device *dev, unsigned char *buf,
    129				u32 offset, u32 n)
    130{
    131	unsigned char *dbuf = dev->dr_dscr[DMA_DSCR_DEVICE].vaddr;
    132
    133	size_t b_offset = offset << 2;
    134	size_t b_n = n << 2;
    135
    136	memcpy(buf, dbuf + b_offset, b_n);
    137
    138	return b_n;
    139}
    140
    141/**
    142 * mei_dma_copy_to() - copy to a buffer to the dma ring
    143 * @dev: mei device
    144 * @buf: data buffer
    145 * @offset: offset in slots.
    146 * @n: number of slots to copy.
    147 */
    148static size_t mei_dma_copy_to(struct mei_device *dev, unsigned char *buf,
    149			      u32 offset, u32 n)
    150{
    151	unsigned char *hbuf = dev->dr_dscr[DMA_DSCR_HOST].vaddr;
    152
    153	size_t b_offset = offset << 2;
    154	size_t b_n = n << 2;
    155
    156	memcpy(hbuf + b_offset, buf, b_n);
    157
    158	return b_n;
    159}
    160
    161/**
    162 * mei_dma_ring_read() - read data from the ring
    163 * @dev: mei device
    164 * @buf: buffer to read into: may be NULL in case of droping the data.
    165 * @len: length to read.
    166 */
    167void mei_dma_ring_read(struct mei_device *dev, unsigned char *buf, u32 len)
    168{
    169	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
    170	u32 dbuf_depth;
    171	u32 rd_idx, rem, slots;
    172
    173	if (WARN_ON(!ctrl))
    174		return;
    175
    176	dev_dbg(dev->dev, "reading from dma %u bytes\n", len);
    177
    178	if (!len)
    179		return;
    180
    181	dbuf_depth = dev->dr_dscr[DMA_DSCR_DEVICE].size >> 2;
    182	rd_idx = READ_ONCE(ctrl->dbuf_rd_idx) & (dbuf_depth - 1);
    183	slots = mei_data2slots(len);
    184
    185	/* if buf is NULL we drop the packet by advancing the pointer.*/
    186	if (!buf)
    187		goto out;
    188
    189	if (rd_idx + slots > dbuf_depth) {
    190		buf += mei_dma_copy_from(dev, buf, rd_idx, dbuf_depth - rd_idx);
    191		rem = slots - (dbuf_depth - rd_idx);
    192		rd_idx = 0;
    193	} else {
    194		rem = slots;
    195	}
    196
    197	mei_dma_copy_from(dev, buf, rd_idx, rem);
    198out:
    199	WRITE_ONCE(ctrl->dbuf_rd_idx, ctrl->dbuf_rd_idx + slots);
    200}
    201
    202static inline u32 mei_dma_ring_hbuf_depth(struct mei_device *dev)
    203{
    204	return dev->dr_dscr[DMA_DSCR_HOST].size >> 2;
    205}
    206
    207/**
    208 * mei_dma_ring_empty_slots() - calaculate number of empty slots in dma ring
    209 * @dev: mei_device
    210 *
    211 * Return: number of empty slots
    212 */
    213u32 mei_dma_ring_empty_slots(struct mei_device *dev)
    214{
    215	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
    216	u32 wr_idx, rd_idx, hbuf_depth, empty;
    217
    218	if (!mei_dma_ring_is_allocated(dev))
    219		return 0;
    220
    221	if (WARN_ON(!ctrl))
    222		return 0;
    223
    224	/* easier to work in slots */
    225	hbuf_depth = mei_dma_ring_hbuf_depth(dev);
    226	rd_idx = READ_ONCE(ctrl->hbuf_rd_idx);
    227	wr_idx = READ_ONCE(ctrl->hbuf_wr_idx);
    228
    229	if (rd_idx > wr_idx)
    230		empty = rd_idx - wr_idx;
    231	else
    232		empty = hbuf_depth - (wr_idx - rd_idx);
    233
    234	return empty;
    235}
    236
    237/**
    238 * mei_dma_ring_write - write data to dma ring host buffer
    239 *
    240 * @dev: mei_device
    241 * @buf: data will be written
    242 * @len: data length
    243 */
    244void mei_dma_ring_write(struct mei_device *dev, unsigned char *buf, u32 len)
    245{
    246	struct hbm_dma_ring_ctrl *ctrl = mei_dma_ring_ctrl(dev);
    247	u32 hbuf_depth;
    248	u32 wr_idx, rem, slots;
    249
    250	if (WARN_ON(!ctrl))
    251		return;
    252
    253	dev_dbg(dev->dev, "writing to dma %u bytes\n", len);
    254	hbuf_depth = mei_dma_ring_hbuf_depth(dev);
    255	wr_idx = READ_ONCE(ctrl->hbuf_wr_idx) & (hbuf_depth - 1);
    256	slots = mei_data2slots(len);
    257
    258	if (wr_idx + slots > hbuf_depth) {
    259		buf += mei_dma_copy_to(dev, buf, wr_idx, hbuf_depth - wr_idx);
    260		rem = slots - (hbuf_depth - wr_idx);
    261		wr_idx = 0;
    262	} else {
    263		rem = slots;
    264	}
    265
    266	mei_dma_copy_to(dev, buf, wr_idx, rem);
    267
    268	WRITE_ONCE(ctrl->hbuf_wr_idx, ctrl->hbuf_wr_idx + slots);
    269}