Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be0d9b6c authored by Antonino A. Daplas's avatar Antonino A. Daplas Committed by Linus Torvalds
Browse files

[PATCH] fbdev: Fix incorrect unaligned access in little-endian machines



The drawing function cfbfillrect does not work correctly when access is not
unsigned-long aligned.  It manifests as extra lines of pixels that are not
complete drawn.  Reversing the shift operator solves the problem, so I would
presume that this bug would manifest only on little endian machines.  The
function cfbcopyarea may also have this bug.

Aligned access should present no problems.

Signed-off-by: default avatarAntonino Daplas <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 7275b4b6
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -64,8 +64,8 @@ bitcpy(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem *src
	int const shift = dst_idx-src_idx;
	int left, right;

	first = ~0UL >> dst_idx;
	last = ~(~0UL >> ((dst_idx+n) % bits));
	first = FB_SHIFT_HIGH(~0UL, dst_idx);
	last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));

	if (!shift) {
		// Same alignment for source and dest
@@ -216,8 +216,8 @@ bitcpy_rev(unsigned long __iomem *dst, int dst_idx, const unsigned long __iomem

	shift = dst_idx-src_idx;

	first = ~0UL << (bits - 1 - dst_idx);
	last = ~(~0UL << (bits - 1 - ((dst_idx-n) % bits)));
	first = FB_SHIFT_LOW(~0UL, bits - 1 - dst_idx);
	last = ~(FB_SHIFT_LOW(~0UL, bits - 1 - ((dst_idx-n) % bits)));

	if (!shift) {
		// Same alignment for source and dest
+8 −8
Original line number Diff line number Diff line
@@ -110,8 +110,8 @@ bitfill_aligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat, unsi
	if (!n)
		return;

	first = ~0UL >> dst_idx;
	last = ~(~0UL >> ((dst_idx+n) % bits));
	first = FB_SHIFT_HIGH(~0UL, dst_idx);
	last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));

	if (dst_idx+n <= bits) {
		// Single word
@@ -167,8 +167,8 @@ bitfill_unaligned(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
	if (!n)
		return;

	first = ~0UL >> dst_idx;
	last = ~(~0UL >> ((dst_idx+n) % bits));
	first = FB_SHIFT_HIGH(~0UL, dst_idx);
	last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));

	if (dst_idx+n <= bits) {
		// Single word
@@ -221,8 +221,8 @@ bitfill_aligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat,
	if (!n)
		return;

	first = ~0UL >> dst_idx;
	last = ~(~0UL >> ((dst_idx+n) % bits));
	first = FB_SHIFT_HIGH(~0UL, dst_idx);
	last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));

	if (dst_idx+n <= bits) {
		// Single word
@@ -290,8 +290,8 @@ bitfill_unaligned_rev(unsigned long __iomem *dst, int dst_idx, unsigned long pat
	if (!n)
		return;

	first = ~0UL >> dst_idx;
	last = ~(~0UL >> ((dst_idx+n) % bits));
	first = FB_SHIFT_HIGH(~0UL, dst_idx);
	last = ~(FB_SHIFT_HIGH(~0UL, (dst_idx+n) % bits));

	if (dst_idx+n <= bits) {
		// Single word
+12 −24
Original line number Diff line number Diff line
@@ -76,18 +76,6 @@ static u32 cfb_tab32[] = {
#define FB_WRITEL fb_writel
#define FB_READL  fb_readl

#if defined (__BIG_ENDIAN)
#define LEFT_POS(bpp)          (32 - bpp)
#define SHIFT_HIGH(val, bits)  ((val) >> (bits))
#define SHIFT_LOW(val, bits)   ((val) << (bits))
#define BIT_NR(b)              (7 - (b))
#else
#define LEFT_POS(bpp)          (0)
#define SHIFT_HIGH(val, bits)  ((val) << (bits))
#define SHIFT_LOW(val, bits)   ((val) >> (bits))
#define BIT_NR(b)              (b)
#endif

static inline void color_imageblit(const struct fb_image *image, 
				   struct fb_info *p, u8 __iomem *dst1, 
				   u32 start_index,
@@ -109,7 +97,7 @@ static inline void color_imageblit(const struct fb_image *image,
		val = 0;
		
		if (start_index) {
			u32 start_mask = ~(SHIFT_HIGH(~(u32)0, start_index));
			u32 start_mask = ~(FB_SHIFT_HIGH(~(u32)0, start_index));
			val = FB_READL(dst) & start_mask;
			shift = start_index;
		}
@@ -119,20 +107,20 @@ static inline void color_imageblit(const struct fb_image *image,
				color = palette[*src];
			else
				color = *src;
			color <<= LEFT_POS(bpp);
			val |= SHIFT_HIGH(color, shift);
			color <<= FB_LEFT_POS(bpp);
			val |= FB_SHIFT_HIGH(color, shift);
			if (shift >= null_bits) {
				FB_WRITEL(val, dst++);
	
				val = (shift == null_bits) ? 0 : 
					SHIFT_LOW(color, 32 - shift);
					FB_SHIFT_LOW(color, 32 - shift);
			}
			shift += bpp;
			shift &= (32 - 1);
			src++;
		}
		if (shift) {
			u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
			u32 end_mask = FB_SHIFT_HIGH(~(u32)0, shift);

			FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
		}
@@ -162,8 +150,8 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info *
	u32 i, j, l;
	
	dst2 = (u32 __iomem *) dst1;
	fgcolor <<= LEFT_POS(bpp);
	bgcolor <<= LEFT_POS(bpp);
	fgcolor <<= FB_LEFT_POS(bpp);
	bgcolor <<= FB_LEFT_POS(bpp);

	for (i = image->height; i--; ) {
		shift = val = 0;
@@ -174,21 +162,21 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info *

		/* write leading bits */
		if (start_index) {
			u32 start_mask = ~(SHIFT_HIGH(~(u32)0, start_index));
			u32 start_mask = ~(FB_SHIFT_HIGH(~(u32)0,start_index));
			val = FB_READL(dst) & start_mask;
			shift = start_index;
		}

		while (j--) {
			l--;
			color = (*s & 1 << (BIT_NR(l))) ? fgcolor : bgcolor;
			val |= SHIFT_HIGH(color, shift);
			color = (*s & 1 << (FB_BIT_NR(l))) ? fgcolor : bgcolor;
			val |= FB_SHIFT_HIGH(color, shift);
			
			/* Did the bitshift spill bits to the next long? */
			if (shift >= null_bits) {
				FB_WRITEL(val, dst++);
				val = (shift == null_bits) ? 0 :
					 SHIFT_LOW(color,32 - shift);
					FB_SHIFT_LOW(color,32 - shift);
			}
			shift += bpp;
			shift &= (32 - 1);
@@ -197,7 +185,7 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info *

		/* write trailing bits */
 		if (shift) {
			u32 end_mask = SHIFT_HIGH(~(u32)0, shift);
			u32 end_mask = FB_SHIFT_HIGH(~(u32)0, shift);

			FB_WRITEL((FB_READL(dst) & end_mask) | val, dst);
		}
+12 −0
Original line number Diff line number Diff line
@@ -833,6 +833,18 @@ struct fb_info {
#define fb_writeq(b,addr) (*(volatile u64 *) (addr) = (b))
#define fb_memset memset

#endif

#if defined (__BIG_ENDIAN)
#define FB_LEFT_POS(bpp)          (32 - bpp)
#define FB_SHIFT_HIGH(val, bits)  ((val) >> (bits))
#define FB_SHIFT_LOW(val, bits)   ((val) << (bits))
#define FB_BIT_NR(b)              (7 - (b))
#else
#define FB_LEFT_POS(bpp)          (0)
#define FB_SHIFT_HIGH(val, bits)  ((val) << (bits))
#define FB_SHIFT_LOW(val, bits)   ((val) >> (bits))
#define FB_BIT_NR(b)              (b)
#endif

    /*