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

trident_memory.c (8145B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
      4 *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
      5 *  Copyright (c) by Scott McNab <sdm@fractalgraphics.com.au>
      6 *
      7 *  Trident 4DWave-NX memory page allocation (TLB area)
      8 *  Trident chip can handle only 16MByte of the memory at the same time.
      9 */
     10
     11#include <linux/io.h>
     12#include <linux/pci.h>
     13#include <linux/time.h>
     14#include <linux/mutex.h>
     15
     16#include <sound/core.h>
     17#include "trident.h"
     18
     19/* page arguments of these two macros are Trident page (4096 bytes), not like
     20 * aligned pages in others
     21 */
     22#define __set_tlb_bus(trident,page,addr) \
     23	(trident)->tlb.entries[page] = cpu_to_le32((addr) & ~(SNDRV_TRIDENT_PAGE_SIZE-1))
     24#define __tlb_to_addr(trident,page) \
     25	(dma_addr_t)le32_to_cpu((trident->tlb.entries[page]) & ~(SNDRV_TRIDENT_PAGE_SIZE - 1))
     26
     27#if PAGE_SIZE == 4096
     28/* page size == SNDRV_TRIDENT_PAGE_SIZE */
     29#define ALIGN_PAGE_SIZE		PAGE_SIZE	/* minimum page size for allocation */
     30#define MAX_ALIGN_PAGES		SNDRV_TRIDENT_MAX_PAGES	/* maxmium aligned pages */
     31/* fill TLB entrie(s) corresponding to page with ptr */
     32#define set_tlb_bus(trident,page,addr) __set_tlb_bus(trident,page,addr)
     33/* fill TLB entrie(s) corresponding to page with silence pointer */
     34#define set_silent_tlb(trident,page)	__set_tlb_bus(trident, page, trident->tlb.silent_page->addr)
     35/* get aligned page from offset address */
     36#define get_aligned_page(offset)	((offset) >> 12)
     37/* get offset address from aligned page */
     38#define aligned_page_offset(page)	((page) << 12)
     39/* get PCI physical address from aligned page */
     40#define page_to_addr(trident,page)	__tlb_to_addr(trident, page)
     41
     42#elif PAGE_SIZE == 8192
     43/* page size == SNDRV_TRIDENT_PAGE_SIZE x 2*/
     44#define ALIGN_PAGE_SIZE		PAGE_SIZE
     45#define MAX_ALIGN_PAGES		(SNDRV_TRIDENT_MAX_PAGES / 2)
     46#define get_aligned_page(offset)	((offset) >> 13)
     47#define aligned_page_offset(page)	((page) << 13)
     48#define page_to_addr(trident,page)	__tlb_to_addr(trident, (page) << 1)
     49
     50/* fill TLB entries -- we need to fill two entries */
     51static inline void set_tlb_bus(struct snd_trident *trident, int page,
     52			       dma_addr_t addr)
     53{
     54	page <<= 1;
     55	__set_tlb_bus(trident, page, addr);
     56	__set_tlb_bus(trident, page+1, addr + SNDRV_TRIDENT_PAGE_SIZE);
     57}
     58static inline void set_silent_tlb(struct snd_trident *trident, int page)
     59{
     60	page <<= 1;
     61	__set_tlb_bus(trident, page, trident->tlb.silent_page->addr);
     62	__set_tlb_bus(trident, page+1, trident->tlb.silent_page->addr);
     63}
     64
     65#else
     66/* arbitrary size */
     67#define UNIT_PAGES		(PAGE_SIZE / SNDRV_TRIDENT_PAGE_SIZE)
     68#define ALIGN_PAGE_SIZE		(SNDRV_TRIDENT_PAGE_SIZE * UNIT_PAGES)
     69#define MAX_ALIGN_PAGES		(SNDRV_TRIDENT_MAX_PAGES / UNIT_PAGES)
     70/* Note: if alignment doesn't match to the maximum size, the last few blocks
     71 * become unusable.  To use such blocks, you'll need to check the validity
     72 * of accessing page in set_tlb_bus and set_silent_tlb.  search_empty()
     73 * should also check it, too.
     74 */
     75#define get_aligned_page(offset)	((offset) / ALIGN_PAGE_SIZE)
     76#define aligned_page_offset(page)	((page) * ALIGN_PAGE_SIZE)
     77#define page_to_addr(trident,page)	__tlb_to_addr(trident, (page) * UNIT_PAGES)
     78
     79/* fill TLB entries -- UNIT_PAGES entries must be filled */
     80static inline void set_tlb_bus(struct snd_trident *trident, int page,
     81			       dma_addr_t addr)
     82{
     83	int i;
     84	page *= UNIT_PAGES;
     85	for (i = 0; i < UNIT_PAGES; i++, page++) {
     86		__set_tlb_bus(trident, page, addr);
     87		addr += SNDRV_TRIDENT_PAGE_SIZE;
     88	}
     89}
     90static inline void set_silent_tlb(struct snd_trident *trident, int page)
     91{
     92	int i;
     93	page *= UNIT_PAGES;
     94	for (i = 0; i < UNIT_PAGES; i++, page++)
     95		__set_tlb_bus(trident, page, trident->tlb.silent_page->addr);
     96}
     97
     98#endif /* PAGE_SIZE */
     99
    100/* first and last (aligned) pages of memory block */
    101#define firstpg(blk)	(((struct snd_trident_memblk_arg *)snd_util_memblk_argptr(blk))->first_page)
    102#define lastpg(blk)	(((struct snd_trident_memblk_arg *)snd_util_memblk_argptr(blk))->last_page)
    103
    104/*
    105 * search empty pages which may contain given size
    106 */
    107static struct snd_util_memblk *
    108search_empty(struct snd_util_memhdr *hdr, int size)
    109{
    110	struct snd_util_memblk *blk;
    111	int page, psize;
    112	struct list_head *p;
    113
    114	psize = get_aligned_page(size + ALIGN_PAGE_SIZE -1);
    115	page = 0;
    116	list_for_each(p, &hdr->block) {
    117		blk = list_entry(p, struct snd_util_memblk, list);
    118		if (page + psize <= firstpg(blk))
    119			goto __found_pages;
    120		page = lastpg(blk) + 1;
    121	}
    122	if (page + psize > MAX_ALIGN_PAGES)
    123		return NULL;
    124
    125__found_pages:
    126	/* create a new memory block */
    127	blk = __snd_util_memblk_new(hdr, psize * ALIGN_PAGE_SIZE, p->prev);
    128	if (blk == NULL)
    129		return NULL;
    130	blk->offset = aligned_page_offset(page); /* set aligned offset */
    131	firstpg(blk) = page;
    132	lastpg(blk) = page + psize - 1;
    133	return blk;
    134}
    135
    136
    137/*
    138 * check if the given pointer is valid for pages
    139 */
    140static int is_valid_page(unsigned long ptr)
    141{
    142	if (ptr & ~0x3fffffffUL) {
    143		snd_printk(KERN_ERR "max memory size is 1GB!!\n");
    144		return 0;
    145	}
    146	if (ptr & (SNDRV_TRIDENT_PAGE_SIZE-1)) {
    147		snd_printk(KERN_ERR "page is not aligned\n");
    148		return 0;
    149	}
    150	return 1;
    151}
    152
    153/*
    154 * page allocation for DMA (Scatter-Gather version)
    155 */
    156static struct snd_util_memblk *
    157snd_trident_alloc_sg_pages(struct snd_trident *trident,
    158			   struct snd_pcm_substream *substream)
    159{
    160	struct snd_util_memhdr *hdr;
    161	struct snd_util_memblk *blk;
    162	struct snd_pcm_runtime *runtime = substream->runtime;
    163	int idx, page;
    164
    165	if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
    166		       runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES *
    167					SNDRV_TRIDENT_PAGE_SIZE))
    168		return NULL;
    169	hdr = trident->tlb.memhdr;
    170	if (snd_BUG_ON(!hdr))
    171		return NULL;
    172
    173	
    174
    175	mutex_lock(&hdr->block_mutex);
    176	blk = search_empty(hdr, runtime->dma_bytes);
    177	if (blk == NULL) {
    178		mutex_unlock(&hdr->block_mutex);
    179		return NULL;
    180	}
    181			   
    182	/* set TLB entries */
    183	idx = 0;
    184	for (page = firstpg(blk); page <= lastpg(blk); page++, idx++) {
    185		unsigned long ofs = idx << PAGE_SHIFT;
    186		dma_addr_t addr = snd_pcm_sgbuf_get_addr(substream, ofs);
    187		if (! is_valid_page(addr)) {
    188			__snd_util_mem_free(hdr, blk);
    189			mutex_unlock(&hdr->block_mutex);
    190			return NULL;
    191		}
    192		set_tlb_bus(trident, page, addr);
    193	}
    194	mutex_unlock(&hdr->block_mutex);
    195	return blk;
    196}
    197
    198/*
    199 * page allocation for DMA (contiguous version)
    200 */
    201static struct snd_util_memblk *
    202snd_trident_alloc_cont_pages(struct snd_trident *trident,
    203			     struct snd_pcm_substream *substream)
    204{
    205	struct snd_util_memhdr *hdr;
    206	struct snd_util_memblk *blk;
    207	int page;
    208	struct snd_pcm_runtime *runtime = substream->runtime;
    209	dma_addr_t addr;
    210
    211	if (snd_BUG_ON(runtime->dma_bytes <= 0 ||
    212		       runtime->dma_bytes > SNDRV_TRIDENT_MAX_PAGES *
    213					SNDRV_TRIDENT_PAGE_SIZE))
    214		return NULL;
    215	hdr = trident->tlb.memhdr;
    216	if (snd_BUG_ON(!hdr))
    217		return NULL;
    218
    219	mutex_lock(&hdr->block_mutex);
    220	blk = search_empty(hdr, runtime->dma_bytes);
    221	if (blk == NULL) {
    222		mutex_unlock(&hdr->block_mutex);
    223		return NULL;
    224	}
    225			   
    226	/* set TLB entries */
    227	addr = runtime->dma_addr;
    228	for (page = firstpg(blk); page <= lastpg(blk); page++,
    229	     addr += SNDRV_TRIDENT_PAGE_SIZE) {
    230		if (! is_valid_page(addr)) {
    231			__snd_util_mem_free(hdr, blk);
    232			mutex_unlock(&hdr->block_mutex);
    233			return NULL;
    234		}
    235		set_tlb_bus(trident, page, addr);
    236	}
    237	mutex_unlock(&hdr->block_mutex);
    238	return blk;
    239}
    240
    241/*
    242 * page allocation for DMA
    243 */
    244struct snd_util_memblk *
    245snd_trident_alloc_pages(struct snd_trident *trident,
    246			struct snd_pcm_substream *substream)
    247{
    248	if (snd_BUG_ON(!trident || !substream))
    249		return NULL;
    250	if (substream->dma_buffer.dev.type == SNDRV_DMA_TYPE_DEV_SG)
    251		return snd_trident_alloc_sg_pages(trident, substream);
    252	else
    253		return snd_trident_alloc_cont_pages(trident, substream);
    254}
    255
    256
    257/*
    258 * release DMA buffer from page table
    259 */
    260int snd_trident_free_pages(struct snd_trident *trident,
    261			   struct snd_util_memblk *blk)
    262{
    263	struct snd_util_memhdr *hdr;
    264	int page;
    265
    266	if (snd_BUG_ON(!trident || !blk))
    267		return -EINVAL;
    268
    269	hdr = trident->tlb.memhdr;
    270	mutex_lock(&hdr->block_mutex);
    271	/* reset TLB entries */
    272	for (page = firstpg(blk); page <= lastpg(blk); page++)
    273		set_silent_tlb(trident, page);
    274	/* free memory block */
    275	__snd_util_mem_free(hdr, blk);
    276	mutex_unlock(&hdr->block_mutex);
    277	return 0;
    278}