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

cfbcopyarea.c (11546B)


      1/*
      2 *  Generic function for frame buffer with packed pixels of any depth.
      3 *
      4 *      Copyright (C)  1999-2005 James Simmons <jsimmons@www.infradead.org>
      5 *
      6 *  This file is subject to the terms and conditions of the GNU General Public
      7 *  License.  See the file COPYING in the main directory of this archive for
      8 *  more details.
      9 *
     10 * NOTES:
     11 *
     12 *  This is for cfb packed pixels. Iplan and such are incorporated in the
     13 *  drivers that need them.
     14 *
     15 *  FIXME
     16 *
     17 *  Also need to add code to deal with cards endians that are different than
     18 *  the native cpu endians. I also need to deal with MSB position in the word.
     19 *
     20 *  The two functions or copying forward and backward could be split up like
     21 *  the ones for filling, i.e. in aligned and unaligned versions. This would
     22 *  help moving some redundant computations and branches out of the loop, too.
     23 */
     24
     25#include <linux/module.h>
     26#include <linux/kernel.h>
     27#include <linux/string.h>
     28#include <linux/fb.h>
     29#include <asm/types.h>
     30#include <asm/io.h>
     31#include "fb_draw.h"
     32
     33#if BITS_PER_LONG == 32
     34#  define FB_WRITEL fb_writel
     35#  define FB_READL  fb_readl
     36#else
     37#  define FB_WRITEL fb_writeq
     38#  define FB_READL  fb_readq
     39#endif
     40
     41    /*
     42     *  Generic bitwise copy algorithm
     43     */
     44
     45static void
     46bitcpy(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
     47		const unsigned long __iomem *src, unsigned src_idx, int bits,
     48		unsigned n, u32 bswapmask)
     49{
     50	unsigned long first, last;
     51	int const shift = dst_idx-src_idx;
     52
     53#if 0
     54	/*
     55	 * If you suspect bug in this function, compare it with this simple
     56	 * memmove implementation.
     57	 */
     58	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
     59		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
     60	return;
     61#endif
     62
     63	first = fb_shifted_pixels_mask_long(p, dst_idx, bswapmask);
     64	last = ~fb_shifted_pixels_mask_long(p, (dst_idx+n) % bits, bswapmask);
     65
     66	if (!shift) {
     67		// Same alignment for source and dest
     68
     69		if (dst_idx+n <= bits) {
     70			// Single word
     71			if (last)
     72				first &= last;
     73			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
     74		} else {
     75			// Multiple destination words
     76
     77			// Leading bits
     78			if (first != ~0UL) {
     79				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
     80				dst++;
     81				src++;
     82				n -= bits - dst_idx;
     83			}
     84
     85			// Main chunk
     86			n /= bits;
     87			while (n >= 8) {
     88				FB_WRITEL(FB_READL(src++), dst++);
     89				FB_WRITEL(FB_READL(src++), dst++);
     90				FB_WRITEL(FB_READL(src++), dst++);
     91				FB_WRITEL(FB_READL(src++), dst++);
     92				FB_WRITEL(FB_READL(src++), dst++);
     93				FB_WRITEL(FB_READL(src++), dst++);
     94				FB_WRITEL(FB_READL(src++), dst++);
     95				FB_WRITEL(FB_READL(src++), dst++);
     96				n -= 8;
     97			}
     98			while (n--)
     99				FB_WRITEL(FB_READL(src++), dst++);
    100
    101			// Trailing bits
    102			if (last)
    103				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
    104		}
    105	} else {
    106		/* Different alignment for source and dest */
    107		unsigned long d0, d1;
    108		int m;
    109
    110		int const left = shift & (bits - 1);
    111		int const right = -shift & (bits - 1);
    112
    113		if (dst_idx+n <= bits) {
    114			// Single destination word
    115			if (last)
    116				first &= last;
    117			d0 = FB_READL(src);
    118			d0 = fb_rev_pixels_in_long(d0, bswapmask);
    119			if (shift > 0) {
    120				// Single source word
    121				d0 <<= left;
    122			} else if (src_idx+n <= bits) {
    123				// Single source word
    124				d0 >>= right;
    125			} else {
    126				// 2 source words
    127				d1 = FB_READL(src + 1);
    128				d1 = fb_rev_pixels_in_long(d1, bswapmask);
    129				d0 = d0 >> right | d1 << left;
    130			}
    131			d0 = fb_rev_pixels_in_long(d0, bswapmask);
    132			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
    133		} else {
    134			// Multiple destination words
    135			/** We must always remember the last value read, because in case
    136			SRC and DST overlap bitwise (e.g. when moving just one pixel in
    137			1bpp), we always collect one full long for DST and that might
    138			overlap with the current long from SRC. We store this value in
    139			'd0'. */
    140			d0 = FB_READL(src++);
    141			d0 = fb_rev_pixels_in_long(d0, bswapmask);
    142			// Leading bits
    143			if (shift > 0) {
    144				// Single source word
    145				d1 = d0;
    146				d0 <<= left;
    147				n -= bits - dst_idx;
    148			} else {
    149				// 2 source words
    150				d1 = FB_READL(src++);
    151				d1 = fb_rev_pixels_in_long(d1, bswapmask);
    152
    153				d0 = d0 >> right | d1 << left;
    154				n -= bits - dst_idx;
    155			}
    156			d0 = fb_rev_pixels_in_long(d0, bswapmask);
    157			FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
    158			d0 = d1;
    159			dst++;
    160
    161			// Main chunk
    162			m = n % bits;
    163			n /= bits;
    164			while ((n >= 4) && !bswapmask) {
    165				d1 = FB_READL(src++);
    166				FB_WRITEL(d0 >> right | d1 << left, dst++);
    167				d0 = d1;
    168				d1 = FB_READL(src++);
    169				FB_WRITEL(d0 >> right | d1 << left, dst++);
    170				d0 = d1;
    171				d1 = FB_READL(src++);
    172				FB_WRITEL(d0 >> right | d1 << left, dst++);
    173				d0 = d1;
    174				d1 = FB_READL(src++);
    175				FB_WRITEL(d0 >> right | d1 << left, dst++);
    176				d0 = d1;
    177				n -= 4;
    178			}
    179			while (n--) {
    180				d1 = FB_READL(src++);
    181				d1 = fb_rev_pixels_in_long(d1, bswapmask);
    182				d0 = d0 >> right | d1 << left;
    183				d0 = fb_rev_pixels_in_long(d0, bswapmask);
    184				FB_WRITEL(d0, dst++);
    185				d0 = d1;
    186			}
    187
    188			// Trailing bits
    189			if (m) {
    190				if (m <= bits - right) {
    191					// Single source word
    192					d0 >>= right;
    193				} else {
    194					// 2 source words
    195					d1 = FB_READL(src);
    196					d1 = fb_rev_pixels_in_long(d1,
    197								bswapmask);
    198					d0 = d0 >> right | d1 << left;
    199				}
    200				d0 = fb_rev_pixels_in_long(d0, bswapmask);
    201				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
    202			}
    203		}
    204	}
    205}
    206
    207    /*
    208     *  Generic bitwise copy algorithm, operating backward
    209     */
    210
    211static void
    212bitcpy_rev(struct fb_info *p, unsigned long __iomem *dst, unsigned dst_idx,
    213		const unsigned long __iomem *src, unsigned src_idx, int bits,
    214		unsigned n, u32 bswapmask)
    215{
    216	unsigned long first, last;
    217	int shift;
    218
    219#if 0
    220	/*
    221	 * If you suspect bug in this function, compare it with this simple
    222	 * memmove implementation.
    223	 */
    224	memmove((char *)dst + ((dst_idx & (bits - 1))) / 8,
    225		(char *)src + ((src_idx & (bits - 1))) / 8, n / 8);
    226	return;
    227#endif
    228
    229	dst += (dst_idx + n - 1) / bits;
    230	src += (src_idx + n - 1) / bits;
    231	dst_idx = (dst_idx + n - 1) % bits;
    232	src_idx = (src_idx + n - 1) % bits;
    233
    234	shift = dst_idx-src_idx;
    235
    236	first = ~fb_shifted_pixels_mask_long(p, (dst_idx + 1) % bits, bswapmask);
    237	last = fb_shifted_pixels_mask_long(p, (bits + dst_idx + 1 - n) % bits, bswapmask);
    238
    239	if (!shift) {
    240		// Same alignment for source and dest
    241
    242		if ((unsigned long)dst_idx+1 >= n) {
    243			// Single word
    244			if (first)
    245				last &= first;
    246			FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
    247		} else {
    248			// Multiple destination words
    249
    250			// Leading bits
    251			if (first) {
    252				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), first), dst);
    253				dst--;
    254				src--;
    255				n -= dst_idx+1;
    256			}
    257
    258			// Main chunk
    259			n /= bits;
    260			while (n >= 8) {
    261				FB_WRITEL(FB_READL(src--), dst--);
    262				FB_WRITEL(FB_READL(src--), dst--);
    263				FB_WRITEL(FB_READL(src--), dst--);
    264				FB_WRITEL(FB_READL(src--), dst--);
    265				FB_WRITEL(FB_READL(src--), dst--);
    266				FB_WRITEL(FB_READL(src--), dst--);
    267				FB_WRITEL(FB_READL(src--), dst--);
    268				FB_WRITEL(FB_READL(src--), dst--);
    269				n -= 8;
    270			}
    271			while (n--)
    272				FB_WRITEL(FB_READL(src--), dst--);
    273
    274			// Trailing bits
    275			if (last != -1UL)
    276				FB_WRITEL( comp( FB_READL(src), FB_READL(dst), last), dst);
    277		}
    278	} else {
    279		// Different alignment for source and dest
    280		unsigned long d0, d1;
    281		int m;
    282
    283		int const left = shift & (bits-1);
    284		int const right = -shift & (bits-1);
    285
    286		if ((unsigned long)dst_idx+1 >= n) {
    287			// Single destination word
    288			if (first)
    289				last &= first;
    290			d0 = FB_READL(src);
    291			if (shift < 0) {
    292				// Single source word
    293				d0 >>= right;
    294			} else if (1+(unsigned long)src_idx >= n) {
    295				// Single source word
    296				d0 <<= left;
    297			} else {
    298				// 2 source words
    299				d1 = FB_READL(src - 1);
    300				d1 = fb_rev_pixels_in_long(d1, bswapmask);
    301				d0 = d0 << left | d1 >> right;
    302			}
    303			d0 = fb_rev_pixels_in_long(d0, bswapmask);
    304			FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
    305		} else {
    306			// Multiple destination words
    307			/** We must always remember the last value read, because in case
    308			SRC and DST overlap bitwise (e.g. when moving just one pixel in
    309			1bpp), we always collect one full long for DST and that might
    310			overlap with the current long from SRC. We store this value in
    311			'd0'. */
    312
    313			d0 = FB_READL(src--);
    314			d0 = fb_rev_pixels_in_long(d0, bswapmask);
    315			// Leading bits
    316			if (shift < 0) {
    317				// Single source word
    318				d1 = d0;
    319				d0 >>= right;
    320			} else {
    321				// 2 source words
    322				d1 = FB_READL(src--);
    323				d1 = fb_rev_pixels_in_long(d1, bswapmask);
    324				d0 = d0 << left | d1 >> right;
    325			}
    326			d0 = fb_rev_pixels_in_long(d0, bswapmask);
    327			if (!first)
    328				FB_WRITEL(d0, dst);
    329			else
    330				FB_WRITEL(comp(d0, FB_READL(dst), first), dst);
    331			d0 = d1;
    332			dst--;
    333			n -= dst_idx+1;
    334
    335			// Main chunk
    336			m = n % bits;
    337			n /= bits;
    338			while ((n >= 4) && !bswapmask) {
    339				d1 = FB_READL(src--);
    340				FB_WRITEL(d0 << left | d1 >> right, dst--);
    341				d0 = d1;
    342				d1 = FB_READL(src--);
    343				FB_WRITEL(d0 << left | d1 >> right, dst--);
    344				d0 = d1;
    345				d1 = FB_READL(src--);
    346				FB_WRITEL(d0 << left | d1 >> right, dst--);
    347				d0 = d1;
    348				d1 = FB_READL(src--);
    349				FB_WRITEL(d0 << left | d1 >> right, dst--);
    350				d0 = d1;
    351				n -= 4;
    352			}
    353			while (n--) {
    354				d1 = FB_READL(src--);
    355				d1 = fb_rev_pixels_in_long(d1, bswapmask);
    356				d0 = d0 << left | d1 >> right;
    357				d0 = fb_rev_pixels_in_long(d0, bswapmask);
    358				FB_WRITEL(d0, dst--);
    359				d0 = d1;
    360			}
    361
    362			// Trailing bits
    363			if (m) {
    364				if (m <= bits - left) {
    365					// Single source word
    366					d0 <<= left;
    367				} else {
    368					// 2 source words
    369					d1 = FB_READL(src);
    370					d1 = fb_rev_pixels_in_long(d1,
    371								bswapmask);
    372					d0 = d0 << left | d1 >> right;
    373				}
    374				d0 = fb_rev_pixels_in_long(d0, bswapmask);
    375				FB_WRITEL(comp(d0, FB_READL(dst), last), dst);
    376			}
    377		}
    378	}
    379}
    380
    381void cfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
    382{
    383	u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
    384	u32 height = area->height, width = area->width;
    385	unsigned long const bits_per_line = p->fix.line_length*8u;
    386	unsigned long __iomem *base = NULL;
    387	int bits = BITS_PER_LONG, bytes = bits >> 3;
    388	unsigned dst_idx = 0, src_idx = 0, rev_copy = 0;
    389	u32 bswapmask = fb_compute_bswapmask(p);
    390
    391	if (p->state != FBINFO_STATE_RUNNING)
    392		return;
    393
    394	/* if the beginning of the target area might overlap with the end of
    395	the source area, be have to copy the area reverse. */
    396	if ((dy == sy && dx > sx) || (dy > sy)) {
    397		dy += height;
    398		sy += height;
    399		rev_copy = 1;
    400	}
    401
    402	// split the base of the framebuffer into a long-aligned address and the
    403	// index of the first bit
    404	base = (unsigned long __iomem *)((unsigned long)p->screen_base & ~(bytes-1));
    405	dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1));
    406	// add offset of source and target area
    407	dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel;
    408	src_idx += sy*bits_per_line + sx*p->var.bits_per_pixel;
    409
    410	if (p->fbops->fb_sync)
    411		p->fbops->fb_sync(p);
    412
    413	if (rev_copy) {
    414		while (height--) {
    415			dst_idx -= bits_per_line;
    416			src_idx -= bits_per_line;
    417			bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits,
    418				base + (src_idx / bits), src_idx % bits, bits,
    419				width*p->var.bits_per_pixel, bswapmask);
    420		}
    421	} else {
    422		while (height--) {
    423			bitcpy(p, base + (dst_idx / bits), dst_idx % bits,
    424				base + (src_idx / bits), src_idx % bits, bits,
    425				width*p->var.bits_per_pixel, bswapmask);
    426			dst_idx += bits_per_line;
    427			src_idx += bits_per_line;
    428		}
    429	}
    430}
    431
    432EXPORT_SYMBOL(cfb_copyarea);
    433
    434MODULE_AUTHOR("James Simmons <jsimmons@users.sf.net>");
    435MODULE_DESCRIPTION("Generic software accelerated copyarea");
    436MODULE_LICENSE("GPL");
    437