[PATCH] vc_screen once more

From: Petr Vandrovec (vandrove@vc.cvut.cz)
Date: Thu Feb 17 2000 - 08:01:09 EST


Hi Linus,
  during reading through code I found that when code was converted from
old put_user to new in-kernel buffer, it started to try to store unaligned
shorts and it is fatal on some architectures, AFAIK. So there is patch.
For vcs_write, I added simple get_unaligned, as I did not found any
reasonable way to fix it except splitting loop for attributed and
non-attributed (vcsa & vcs) paths completely.
  For vcs_read, I rewrite inside loop so that now always character
+ attribute is copied into buffer - code is shorter, gets rid of
ifdef __BIG_ENDIAN and I believe that it is more readable. I also
changed logic for computing buffer sizes for HEADER_SIZE data,
original code had some problems with writting beyond buffer when
pos was in range 1..HEADER_SIZE-1.
  Patch was created on 2.3.46.
                                        Thanks,
                                                Petr Vandrovec
                                                vandrove@vc.cvut.cz

diff -urdN linux/drivers/char/vc_screen.c linux/drivers/char/vc_screen.c
--- linux/drivers/char/vc_screen.c Wed Feb 16 23:42:05 2000
+++ linux/drivers/char/vc_screen.c Thu Feb 17 12:44:41 2000
@@ -37,6 +37,7 @@
 #include <linux/console.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
+#include <asm/unaligned.h>
 
 #undef attr
 #undef org
@@ -175,71 +176,58 @@
                                 con_buf0[1] = (char) video_num_columns;
                                 getconsxy(currcons, con_buf0 + 2);
 
- tmp_count = HEADER_SIZE - p;
+ con_buf_start += p;
+ this_round += p;
+ if (this_round > CON_BUF_SIZE) {
+ this_round = CON_BUF_SIZE;
+ orig_count = this_round - p;
+ }
+
+ tmp_count = HEADER_SIZE;
                                 if (tmp_count > this_round)
                                         tmp_count = this_round;
 
                                 /* Advance state pointers and move on. */
                                 this_round -= tmp_count;
- con_buf_start += p;
- orig_count -= p;
- p += tmp_count;
- con_buf0 = con_buf + p;
+ p = HEADER_SIZE;
+ con_buf0 = con_buf + HEADER_SIZE;
+ /* If this_round >= 0, then p is even... */
+ } else if (p & 1) {
+ /* Skip first byte for output if start address is odd
+ * Update region sizes up/down depending on free
+ * space in buffer.
+ */
+ con_buf_start++;
+ if (this_round < CON_BUF_SIZE)
+ this_round++;
+ else
+ orig_count--;
                         }
- p -= HEADER_SIZE;
- col = (p/2) % maxcol;
                         if (this_round > 0) {
- char tmp_byte;
-
- org = screen_pos(currcons, p/2, viewed);
- if ((p & 1) && this_round > 0) {
-#ifdef __BIG_ENDIAN
- tmp_byte = vcs_scr_readw(currcons, org++) & 0xff;
-#else
- tmp_byte = vcs_scr_readw(currcons, org++) >> 8;
-#endif
-
- *con_buf0++ = tmp_byte;
+ unsigned short *tmp_buf = (unsigned short *)con_buf0;
 
- this_round--;
- p++;
- if (++col == maxcol) {
- org = screen_pos(currcons, p/2, viewed);
- col = 0;
- }
- }
+ p -= HEADER_SIZE;
                                 p /= 2;
+ col = p % maxcol;
+
+ org = screen_pos(currcons, p, viewed);
                                 p += maxcol - col;
- }
 
- if (this_round > 1) {
- size_t tmp_count = this_round;
- unsigned short *tmp_buf = (unsigned short *)con_buf0;
+ /* Buffer has even length, so we can always copy
+ * character + attribute. We do not copy last byte
+ * to userspace if this_round is odd.
+ */
+ this_round = (this_round + 1) >> 1;
 
- while (tmp_count > 1) {
+ while (this_round) {
                                         *tmp_buf++ = vcs_scr_readw(currcons, org++);
- tmp_count -= 2;
+ this_round --;
                                         if (++col == maxcol) {
                                                 org = screen_pos(currcons, p, viewed);
                                                 col = 0;
                                                 p += maxcol;
                                         }
                                 }
-
- /* Advance pointers, and move on. */
- this_round = tmp_count;
- con_buf0 = (char*)tmp_buf;
- }
- if (this_round > 0) {
- char tmp_byte;
-
-#ifdef __BIG_ENDIAN
- tmp_byte = vcs_scr_readw(currcons, org) >> 8;
-#else
- tmp_byte = vcs_scr_readw(currcons, org) & 0xff;
-#endif
-
- *con_buf0++ = tmp_byte;
                         }
                 }
 
@@ -415,7 +403,7 @@
                         while (this_round > 1) {
                                 unsigned short w;
 
- w = *((const unsigned short *)con_buf0);
+ w = get_unaligned(((const unsigned short *)con_buf0));
                                 vcs_scr_writew(currcons, w, org++);
                                 con_buf0 += 2;
                                 this_round -= 2;

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Feb 23 2000 - 21:00:18 EST