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

emu8000_patch.c (6829B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  Patch routines for the emu8000 (AWE32/64)
      4 *
      5 *  Copyright (C) 1999 Steve Ratcliffe
      6 *  Copyright (C) 1999-2000 Takashi Iwai <tiwai@suse.de>
      7 */
      8
      9#include "emu8000_local.h"
     10
     11#include <linux/sched/signal.h>
     12#include <linux/uaccess.h>
     13#include <linux/moduleparam.h>
     14
     15static int emu8000_reset_addr;
     16module_param(emu8000_reset_addr, int, 0444);
     17MODULE_PARM_DESC(emu8000_reset_addr, "reset write address at each time (makes slowdown)");
     18
     19
     20/*
     21 * Open up channels.
     22 */
     23static int
     24snd_emu8000_open_dma(struct snd_emu8000 *emu, int write)
     25{
     26	int i;
     27
     28	/* reserve all 30 voices for loading */
     29	for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
     30		snd_emux_lock_voice(emu->emu, i);
     31		snd_emu8000_dma_chan(emu, i, write);
     32	}
     33
     34	/* assign voice 31 and 32 to ROM */
     35	EMU8000_VTFT_WRITE(emu, 30, 0);
     36	EMU8000_PSST_WRITE(emu, 30, 0x1d8);
     37	EMU8000_CSL_WRITE(emu, 30, 0x1e0);
     38	EMU8000_CCCA_WRITE(emu, 30, 0x1d8);
     39	EMU8000_VTFT_WRITE(emu, 31, 0);
     40	EMU8000_PSST_WRITE(emu, 31, 0x1d8);
     41	EMU8000_CSL_WRITE(emu, 31, 0x1e0);
     42	EMU8000_CCCA_WRITE(emu, 31, 0x1d8);
     43
     44	return 0;
     45}
     46
     47/*
     48 * Close all dram channels.
     49 */
     50static void
     51snd_emu8000_close_dma(struct snd_emu8000 *emu)
     52{
     53	int i;
     54
     55	for (i = 0; i < EMU8000_DRAM_VOICES; i++) {
     56		snd_emu8000_dma_chan(emu, i, EMU8000_RAM_CLOSE);
     57		snd_emux_unlock_voice(emu->emu, i);
     58	}
     59}
     60
     61/*
     62 */
     63
     64#define BLANK_LOOP_START	4
     65#define BLANK_LOOP_END		8
     66#define BLANK_LOOP_SIZE		12
     67#define BLANK_HEAD_SIZE		48
     68
     69/*
     70 * Read a word from userland, taking care of conversions from
     71 * 8bit samples etc.
     72 */
     73static unsigned short
     74read_word(const void __user *buf, int offset, int mode)
     75{
     76	unsigned short c;
     77	if (mode & SNDRV_SFNT_SAMPLE_8BITS) {
     78		unsigned char cc;
     79		get_user(cc, (unsigned char __user *)buf + offset);
     80		c = cc << 8; /* convert 8bit -> 16bit */
     81	} else {
     82#ifdef SNDRV_LITTLE_ENDIAN
     83		get_user(c, (unsigned short __user *)buf + offset);
     84#else
     85		unsigned short cc;
     86		get_user(cc, (unsigned short __user *)buf + offset);
     87		c = swab16(cc);
     88#endif
     89	}
     90	if (mode & SNDRV_SFNT_SAMPLE_UNSIGNED)
     91		c ^= 0x8000; /* unsigned -> signed */
     92	return c;
     93}
     94
     95/*
     96 */
     97static void
     98snd_emu8000_write_wait(struct snd_emu8000 *emu)
     99{
    100	while ((EMU8000_SMALW_READ(emu) & 0x80000000) != 0) {
    101		schedule_timeout_interruptible(1);
    102		if (signal_pending(current))
    103			break;
    104	}
    105}
    106
    107/*
    108 * write sample word data
    109 *
    110 * You should not have to keep resetting the address each time
    111 * as the chip is supposed to step on the next address automatically.
    112 * It mostly does, but during writes of some samples at random it
    113 * completely loses words (every one in 16 roughly but with no
    114 * obvious pattern).
    115 *
    116 * This is therefore much slower than need be, but is at least
    117 * working.
    118 */
    119static inline void
    120write_word(struct snd_emu8000 *emu, int *offset, unsigned short data)
    121{
    122	if (emu8000_reset_addr) {
    123		if (emu8000_reset_addr > 1)
    124			snd_emu8000_write_wait(emu);
    125		EMU8000_SMALW_WRITE(emu, *offset);
    126	}
    127	EMU8000_SMLD_WRITE(emu, data);
    128	*offset += 1;
    129}
    130
    131/*
    132 * Write the sample to EMU800 memory.  This routine is invoked out of
    133 * the generic soundfont routines as a callback.
    134 */
    135int
    136snd_emu8000_sample_new(struct snd_emux *rec, struct snd_sf_sample *sp,
    137		       struct snd_util_memhdr *hdr,
    138		       const void __user *data, long count)
    139{
    140	int  i;
    141	int  rc;
    142	int  offset;
    143	int  truesize;
    144	int  dram_offset, dram_start;
    145	struct snd_emu8000 *emu;
    146
    147	emu = rec->hw;
    148	if (snd_BUG_ON(!sp))
    149		return -EINVAL;
    150
    151	if (sp->v.size == 0)
    152		return 0;
    153
    154	/* be sure loop points start < end */
    155	if (sp->v.loopstart > sp->v.loopend)
    156		swap(sp->v.loopstart, sp->v.loopend);
    157
    158	/* compute true data size to be loaded */
    159	truesize = sp->v.size;
    160	if (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP))
    161		truesize += sp->v.loopend - sp->v.loopstart;
    162	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK)
    163		truesize += BLANK_LOOP_SIZE;
    164
    165	sp->block = snd_util_mem_alloc(hdr, truesize * 2);
    166	if (sp->block == NULL) {
    167		/*snd_printd("EMU8000: out of memory\n");*/
    168		/* not ENOMEM (for compatibility) */
    169		return -ENOSPC;
    170	}
    171
    172	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_8BITS) {
    173		if (!access_ok(data, sp->v.size))
    174			return -EFAULT;
    175	} else {
    176		if (!access_ok(data, sp->v.size * 2))
    177			return -EFAULT;
    178	}
    179
    180	/* recalculate address offset */
    181	sp->v.end -= sp->v.start;
    182	sp->v.loopstart -= sp->v.start;
    183	sp->v.loopend -= sp->v.start;
    184	sp->v.start = 0;
    185
    186	/* dram position (in word) -- mem_offset is byte */
    187	dram_offset = EMU8000_DRAM_OFFSET + (sp->block->offset >> 1);
    188	dram_start = dram_offset;
    189
    190	/* set the total size (store onto obsolete checksum value) */
    191	sp->v.truesize = truesize * 2; /* in bytes */
    192
    193	snd_emux_terminate_all(emu->emu);
    194	rc = snd_emu8000_open_dma(emu, EMU8000_RAM_WRITE);
    195	if (rc)
    196		return rc;
    197
    198	/* Set the address to start writing at */
    199	snd_emu8000_write_wait(emu);
    200	EMU8000_SMALW_WRITE(emu, dram_offset);
    201
    202	/*snd_emu8000_init_fm(emu);*/
    203
    204#if 0
    205	/* first block - write 48 samples for silence */
    206	if (! sp->block->offset) {
    207		for (i = 0; i < BLANK_HEAD_SIZE; i++) {
    208			write_word(emu, &dram_offset, 0);
    209		}
    210	}
    211#endif
    212
    213	offset = 0;
    214	for (i = 0; i < sp->v.size; i++) {
    215		unsigned short s;
    216
    217		s = read_word(data, offset, sp->v.mode_flags);
    218		offset++;
    219		write_word(emu, &dram_offset, s);
    220
    221		/* we may take too long time in this loop.
    222		 * so give controls back to kernel if needed.
    223		 */
    224		cond_resched();
    225
    226		if (i == sp->v.loopend &&
    227		    (sp->v.mode_flags & (SNDRV_SFNT_SAMPLE_BIDIR_LOOP|SNDRV_SFNT_SAMPLE_REVERSE_LOOP)))
    228		{
    229			int looplen = sp->v.loopend - sp->v.loopstart;
    230			int k;
    231
    232			/* copy reverse loop */
    233			for (k = 1; k <= looplen; k++) {
    234				s = read_word(data, offset - k, sp->v.mode_flags);
    235				write_word(emu, &dram_offset, s);
    236			}
    237			if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_BIDIR_LOOP) {
    238				sp->v.loopend += looplen;
    239			} else {
    240				sp->v.loopstart += looplen;
    241				sp->v.loopend += looplen;
    242			}
    243			sp->v.end += looplen;
    244		}
    245	}
    246
    247	/* if no blank loop is attached in the sample, add it */
    248	if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_NO_BLANK) {
    249		for (i = 0; i < BLANK_LOOP_SIZE; i++) {
    250			write_word(emu, &dram_offset, 0);
    251		}
    252		if (sp->v.mode_flags & SNDRV_SFNT_SAMPLE_SINGLESHOT) {
    253			sp->v.loopstart = sp->v.end + BLANK_LOOP_START;
    254			sp->v.loopend = sp->v.end + BLANK_LOOP_END;
    255		}
    256	}
    257
    258	/* add dram offset */
    259	sp->v.start += dram_start;
    260	sp->v.end += dram_start;
    261	sp->v.loopstart += dram_start;
    262	sp->v.loopend += dram_start;
    263
    264	snd_emu8000_close_dma(emu);
    265	snd_emu8000_init_fm(emu);
    266
    267	return 0;
    268}
    269
    270/*
    271 * free a sample block
    272 */
    273int
    274snd_emu8000_sample_free(struct snd_emux *rec, struct snd_sf_sample *sp,
    275			struct snd_util_memhdr *hdr)
    276{
    277	if (sp->block) {
    278		snd_util_mem_free(hdr, sp->block);
    279		sp->block = NULL;
    280	}
    281	return 0;
    282}
    283
    284
    285/*
    286 * sample_reset callback - terminate voices
    287 */
    288void
    289snd_emu8000_sample_reset(struct snd_emux *rec)
    290{
    291	snd_emux_terminate_all(rec);
    292}