Hello, Peter.

On Sat, Dec 31, 2022 at 16:13:23 +0000, Alan Mackenzie wrote:
> On Sat, Dec 31, 2022 at 15:47:01 +0000, Peter Humphrey wrote:
> > Hello Alan,
> > On Saturday, 31 December 2022 14:08:43 GMT you wrote:

[ .... ]

> > I think you've put your finger on it:

> > $ file /lib/rc/console/font
> > /lib/rc/console/font: Linux/i386 PC Screen Font v2 data, 256 characters, 
> > Unicode directory, 22x11

> > I use consolefont="ter-122n" from the terminus-font package. It's a long 
> > time 
> > since I was able to read a high-resolution screen in its native resolution.

That's a nice font.  I could get used to it if I wasn't so attached to
the 8x16 font.

> > Is there some way I can get the UEFI BIOS to boot with that font, or a 
> > larger 
> > one? Or perhaps let the system boot without setting a font and then 
> > changing 
> > it later?

> Probably, but it would be better if I just fixed the bug(s) in my changes to
> the kernel.  Changing font size is something one should be able to do.

OK, the bug was that I was trying to free memory by calling the wrong
kernel function kfree, when it should have been kvfree.  With that
correction, the kernel now boots in 11x22, at least for me.

> > Neither of those looks easy to do. I'd better have a good root through the 
> > BIOS options to start with.

> A happy new year to you (and everybody else here), and give me somewhere
> between a few hours and a few days, and this bug should get fixed.

The included patch is still imperfect.  When booting in 11x22, it doesn't
handle the early boot messages at all well.  Also, I'm a little confused
by what a low-level scroll function is meant to do - sometimes, scrolling
happens when you type a CR, and want a line on the screen to be space
filled.  Other times, you type <shift><PgUp> and don't want any space
filling to happen.  So I'm not convinced that scrolling, invoked by, say,
an editor program, will work correctly.

> Again, thanks for such effective testing!

So, please try the attached patch, which is "at the same level" as my
patch from three days ago.  For anybody who wants to try it new, I'm
repeating the instructions from that post:

>>>> To use it, please apply the supplied patch ON TOP OF the patch I
>>>> posted here on 12th December.  Proceed as documented in that post,
>>>> up until configuring the kernel - in Device drivers/Graphic
>>>> support/Console display driver support, there's an extra item
>>>> "Enable a working GPM for scrolled back scrollback buffer in System
>>>> RAM" which should be enabled by default.  Check this is set up
>>>> properly.  Then build and install the kernel.  Then reboot into it
>>>> and try it out.

> > -- 
> > Regards,
> > Peter.

-- 
Alan Mackenzie (Nuremberg, Germany).

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index d7aadca5107d..7bea89f03e75 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -291,6 +291,28 @@ static inline bool con_should_update(const struct vc_data 
*vc)
 static inline unsigned short *screenpos(const struct vc_data *vc, int offset,
                bool viewed)
 {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       unsigned long softback_pos, scrolled_expanse;
+
+       if (vc->vc_softback_curr == vc->vc_origin)
+               return (unsigned short *)(vc->vc_origin + offset);
+       else {
+               scrolled_expanse = vc->vc_softback_in - vc->vc_softback_curr;
+               if (scrolled_expanse < 0)
+                       scrolled_expanse += vc->vc_softback_end
+                               - vc->vc_softback_buf;
+               if (offset >= scrolled_expanse)
+                       return (unsigned short *)(vc->vc_origin
+                                                 + (offset - 
scrolled_expanse));
+               else {
+                       softback_pos = vc->vc_softback_curr + offset;
+                       if (softback_pos >= vc->vc_softback_end)
+                               softback_pos -= vc->vc_softback_end
+                                       - vc->vc_softback_buf;
+               }
+       }
+       return (unsigned short *)softback_pos;
+#else
        unsigned short *p;
 
        if (!viewed)
@@ -300,6 +322,7 @@ static inline unsigned short *screenpos(const struct 
vc_data *vc, int offset,
        else
                p = vc->vc_sw->con_screen_pos(vc, offset);
        return p;
+#endif
 }
 
 /* Called  from the keyboard irq path.. */
@@ -321,101 +344,106 @@ void schedule_console_callback(void)
  * Code to manage unicode-based screen buffers
  */
 
-#ifdef NO_VC_UNI_SCREEN
-/* this disables and optimizes related code away at compile time */
-#define get_vc_uniscr(vc) NULL
-#else
-#define get_vc_uniscr(vc) vc->vc_uni_screen
-#endif
-
 #define VC_UNI_SCREEN_DEBUG 0
 
 typedef uint32_t char32_t;
 
-/*
- * Our screen buffer is preceded by an array of line pointers so that
- * scrolling only implies some pointer shuffling.
- */
-struct uni_screen {
-       char32_t *lines[0];
-};
+#define vc_uniscr_buf_end(vc) vc->vc_uniscr_buf + vc->vc_uniscr_char_size
 
-static struct uni_screen *vc_uniscr_alloc(unsigned int cols, unsigned int rows)
+static int vc_uniscr_alloc(struct vc_data *vc, unsigned int cols, unsigned int 
rows)
 {
-       struct uni_screen *uniscr;
-       void *p;
-       unsigned int memsize, i;
+       uint32_t *p;
 
-       /* allocate everything in one go */
-       memsize = cols * rows * sizeof(char32_t);
-       memsize += rows * sizeof(char32_t *);
-       p = vzalloc(memsize);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+       vc->vc_uniscr_char_size =
+               cols * rows
+               + ((vc->vc_softback_end - vc->vc_softback_buf) / 2);
+#else
+       vc->vc_uniscr_char_size = cols * (rows + 1); /* 1 row for the circular 
buffer admin */
+#endif
+       p = (uint32_t *)vzalloc (sizeof (uint32_t) * vc->vc_uniscr_char_size);
+       vc->vc_uniscr_buf = p;
        if (!p)
-               return NULL;
-
-       /* initial line pointers */
-       uniscr = p;
-       p = uniscr->lines + rows;
-       for (i = 0; i < rows; i++) {
-               uniscr->lines[i] = p;
-               p += cols * sizeof(char32_t);
-       }
-       return uniscr;
-}
-
-static void vc_uniscr_free(struct uni_screen *uniscr)
-{
-       vfree(uniscr);
+               return -ENOMEM;
+       vc->vc_uniscr_curr = p;
+       return 0;
 }
 
-static void vc_uniscr_set(struct vc_data *vc, struct uni_screen *new_uniscr)
+static void vc_uniscr_free(struct vc_data *vc)
 {
-       vc_uniscr_free(vc->vc_uni_screen);
-       vc->vc_uni_screen = new_uniscr;
+       kvfree(vc->vc_uniscr_buf);
+       vc->vc_uniscr_buf = NULL;
 }
 
 static void vc_uniscr_putc(struct vc_data *vc, char32_t uc)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
-
-       if (uniscr)
-               uniscr->lines[vc->state.y][vc->state.x] = uc;
+       uint32_t *pos;
+
+       if (vc->vc_uniscr_buf) {
+               pos = vc->vc_uniscr_curr
+                       + vc->state.y * vc->vc_cols
+                       + vc->state.x;
+               if (pos >= vc_uniscr_buf_end(vc))
+                       pos -= vc->vc_uniscr_char_size;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+               pos -= vc->vc_softback_lines * vc->vc_cols;
+               if (pos < vc->vc_uniscr_buf)
+                       pos += vc->vc_uniscr_char_size;
+#endif
+               *pos = uc;
+       }
 }
 
 static void vc_uniscr_insert(struct vc_data *vc, unsigned int nr)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
+       unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+       uint32_t *ln = vc->vc_uniscr_curr + y * cols;
 
-       if (uniscr) {
-               char32_t *ln = uniscr->lines[vc->state.y];
-               unsigned int x = vc->state.x, cols = vc->vc_cols;
-
-               memmove(&ln[x + nr], &ln[x], (cols - x - nr) * sizeof(*ln));
+       if (vc->vc_uniscr_buf) {
+               if (ln >= vc_uniscr_buf_end(vc))
+                       ln -= vc->vc_uniscr_char_size;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+               ln -= vc->vc_softback_lines * vc->vc_cols;
+               if (ln < vc->vc_uniscr_buf)
+                       ln += vc->vc_uniscr_char_size;
+#endif
+               memmove(&ln[x + nr], &ln[x], (cols - x - nr) * 
sizeof(uint32_t));
                memset32(&ln[x], ' ', nr);
        }
 }
 
 static void vc_uniscr_delete(struct vc_data *vc, unsigned int nr)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
+       unsigned int x = vc->state.x, y = vc->state.y, cols = vc->vc_cols;
+       uint32_t *ln = vc->vc_uniscr_curr + y * cols;
 
-       if (uniscr) {
-               char32_t *ln = uniscr->lines[vc->state.y];
-               unsigned int x = vc->state.x, cols = vc->vc_cols;
-
-               memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(*ln));
+       if (vc->vc_uniscr_buf) {
+               if (ln >= vc_uniscr_buf_end(vc))
+                       ln -= vc->vc_uniscr_char_size;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+               ln -= vc->vc_softback_lines * vc->vc_cols;
+               if (ln < vc->vc_uniscr_buf)
+                       ln += vc->vc_uniscr_char_size;
+#endif
+               memcpy(&ln[x], &ln[x + nr], (cols - x - nr) * sizeof(uint32_t));
                memset32(&ln[cols - nr], ' ', nr);
        }
 }
 
+/* FIXME!!!  We need to check that NR never goes beyond the current line end.  
!!! */
 static void vc_uniscr_clear_line(struct vc_data *vc, unsigned int x,
                                 unsigned int nr)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
-
-       if (uniscr) {
-               char32_t *ln = uniscr->lines[vc->state.y];
+       if (vc->vc_uniscr_buf) {
+               uint32_t *ln = vc->vc_uniscr_curr + vc->state.y * vc->vc_cols;
 
+               if (ln >= vc_uniscr_buf_end(vc))
+                       ln -= vc->vc_uniscr_char_size;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+               ln -= vc->vc_softback_lines * vc->vc_cols;
+               if (ln < vc->vc_uniscr_buf)
+                       ln += vc->vc_uniscr_char_size;
+#endif
                memset32(&ln[x], ' ', nr);
        }
 }
@@ -423,77 +451,71 @@ static void vc_uniscr_clear_line(struct vc_data *vc, 
unsigned int x,
 static void vc_uniscr_clear_lines(struct vc_data *vc, unsigned int y,
                                  unsigned int nr)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
-
-       if (uniscr) {
+       if (vc->vc_uniscr_buf) {
                unsigned int cols = vc->vc_cols;
+               uint32_t *ln = vc->vc_uniscr_curr + y * cols;
 
-               while (nr--)
-                       memset32(uniscr->lines[y++], ' ', cols);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+               ln -= vc->vc_softback_lines * cols;
+               if (ln < vc->vc_uniscr_buf)
+                       ln += vc->vc_uniscr_char_size;
+#endif
+               while (nr--) {
+                       if (ln >= vc_uniscr_buf_end(vc))
+                               ln -= vc->vc_uniscr_char_size;
+                       memset32(ln, ' ', cols);
+                       ln += cols;
+               }
        }
 }
 
 static void vc_uniscr_scroll(struct vc_data *vc, unsigned int t, unsigned int 
b,
                             enum con_scroll dir, unsigned int nr)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
-
-       if (uniscr) {
-               unsigned int i, j, k, sz, d, clear;
-
-               sz = b - t;
-               clear = b - nr;
-               d = nr;
-               if (dir == SM_DOWN) {
-                       clear = t;
-                       d = sz - nr;
-               }
-               for (i = 0; i < gcd(d, sz); i++) {
-                       char32_t *tmp = uniscr->lines[t + i];
-                       j = i;
-                       while (1) {
-                               k = j + d;
-                               if (k >= sz)
-                                       k -= sz;
-                               if (k == i)
-                                       break;
-                               uniscr->lines[t + j] = uniscr->lines[t + k];
-                               j = k;
+       if (vc->vc_uniscr_buf) {
+               unsigned int cols = vc->vc_cols;
+               unsigned int sz, /* number of rows being scrolled */
+                       d,                /* number of rows needing blanking */
+                       clear;            /* The number of the topmost row 
needing blanking. */
+               uint32_t *dest, *src;
+               unsigned int i;
+
+               if (dir == SM_UP && t == 0 && b == vc->vc_rows) {
+                       vc->vc_uniscr_curr += nr * cols;
+                       if (vc->vc_uniscr_curr >= vc_uniscr_buf_end(vc))
+                               vc->vc_uniscr_curr -= vc->vc_uniscr_char_size;
+                       d = nr;
+                       clear = vc->vc_rows - nr;
+               } else if (dir == SM_DOWN && t == 0 && b == vc->vc_rows - nr) {
+                       vc->vc_uniscr_curr -= nr * cols;
+                       if (vc->vc_uniscr_curr < vc->vc_uniscr_buf)
+                               vc->vc_uniscr_curr += vc->vc_uniscr_char_size;
+                       d = 0;
+               } else {
+                       sz = b - t;
+                       src = vc->vc_uniscr_curr + t * cols;
+                       if (dir == SM_UP) {
+                               dest = vc->vc_uniscr_curr - nr * cols;
+                               clear = b - nr;
+                               d = nr;
+                       } else {
+                               dest = vc->vc_uniscr_curr + nr * cols;
+                               clear = t;
+                               d = sz - nr;
+                       }
+                       i = nr;
+                       while (i--) {
+                               if (dest >= vc_uniscr_buf_end(vc))
+                                       dest -= vc->vc_uniscr_char_size;
+                               if (src >= vc_uniscr_buf_end(vc))
+                                       src -= vc->vc_uniscr_char_size;
+                               memcpy(dest, src, cols * sizeof(uint32_t));
+                               dest += cols;
+                               src += cols;
                        }
-                       uniscr->lines[t + j] = tmp;
                }
-               vc_uniscr_clear_lines(vc, clear, nr);
-       }
-}
-
-static void vc_uniscr_copy_area(struct uni_screen *dst,
-                               unsigned int dst_cols,
-                               unsigned int dst_rows,
-                               struct uni_screen *src,
-                               unsigned int src_cols,
-                               unsigned int src_top_row,
-                               unsigned int src_bot_row)
-{
-       unsigned int dst_row = 0;
-
-       if (!dst)
-               return;
-
-       while (src_top_row < src_bot_row) {
-               char32_t *src_line = src->lines[src_top_row];
-               char32_t *dst_line = dst->lines[dst_row];
-
-               memcpy(dst_line, src_line, src_cols * sizeof(char32_t));
-               if (dst_cols - src_cols)
-                       memset32(dst_line + src_cols, ' ', dst_cols - src_cols);
-               src_top_row++;
-               dst_row++;
-       }
-       while (dst_row < dst_rows) {
-               char32_t *dst_line = dst->lines[dst_row];
-
-               memset32(dst_line, ' ', dst_cols);
-               dst_row++;
+               if (d)
+                       vc_uniscr_clear_lines(vc, clear, nr);
        }
 }
 
@@ -505,7 +527,6 @@ static void vc_uniscr_copy_area(struct uni_screen *dst,
  */
 int vc_uniscr_check(struct vc_data *vc)
 {
-       struct uni_screen *uniscr;
        unsigned short *p;
        int x, y, mask;
 
@@ -517,11 +538,10 @@ int vc_uniscr_check(struct vc_data *vc)
        if (!vc->vc_utf)
                return -ENODATA;
 
-       if (vc->vc_uni_screen)
+       if (vc->vc_uniscr_buf)
                return 0;
 
-       uniscr = vc_uniscr_alloc(vc->vc_cols, vc->vc_rows);
-       if (!uniscr)
+       if (vc_uniscr_alloc (vc, vc->vc_cols, vc->vc_rows))
                return -ENOMEM;
 
        /*
@@ -533,14 +553,15 @@ int vc_uniscr_check(struct vc_data *vc)
        p = (unsigned short *)vc->vc_origin;
        mask = vc->vc_hi_font_mask | 0xff;
        for (y = 0; y < vc->vc_rows; y++) {
-               char32_t *line = uniscr->lines[y];
+               uint32_t *line = vc->vc_uniscr_curr + y * vc->vc_cols;
+               if (line >= vc_uniscr_buf_end(vc))
+                       line -= vc->vc_uniscr_char_size;
                for (x = 0; x < vc->vc_cols; x++) {
                        u16 glyph = scr_readw(p++) & mask;
                        line[x] = inverse_translate(vc, glyph, true);
                }
        }
 
-       vc->vc_uni_screen = uniscr;
        return 0;
 }
 
@@ -552,12 +573,26 @@ int vc_uniscr_check(struct vc_data *vc)
 void vc_uniscr_copy_line(const struct vc_data *vc, void *dest, bool viewed,
                         unsigned int row, unsigned int col, unsigned int nr)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       uint32_t *pos;         /* Position in the unicode buffer of col/row */
+#else
        int offset = row * vc->vc_size_row + col * 2;
-       unsigned long pos;
+       unsigned long pos; /* Position in the main screen buffer of col/row */
+#endif
 
-       BUG_ON(!uniscr);
+       BUG_ON(!vc->vc_uniscr_buf);
 
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       pos = (vc->vc_uniscr_curr + row * vc->vc_cols + col);
+       if (pos >= vc_uniscr_buf_end(vc))
+               pos -= vc->vc_uniscr_char_size;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+       pos -= vc->vc_softback_lines * vc->vc_cols;
+       if (pos < vc->vc_uniscr_buf)
+               pos += vc->vc_uniscr_char_size;
+#endif
+       memcpy(dest, pos, nr * sizeof(uint32_t));
+#else
        pos = (unsigned long)screenpos(vc, offset, viewed);
        if (pos >= vc->vc_origin && pos < vc->vc_scr_end) {
                /*
@@ -567,58 +602,57 @@ void vc_uniscr_copy_line(const struct vc_data *vc, void 
*dest, bool viewed,
                 */
                row = (pos - vc->vc_origin) / vc->vc_size_row;
                col = ((pos - vc->vc_origin) % vc->vc_size_row) / 2;
-               memcpy(dest, &uniscr->lines[row][col], nr * sizeof(char32_t));
+               memcpy(dest,
+                      (void *)(vc->vc_uniscr_curr + row * vc->vc_cols + col),
+                      nr);
        } else {
                /*
-                * Scrollback is active. For now let's simply backtranslate
-                * the screen glyphs until the unicode screen buffer does
-                * synchronize with console display drivers for a scrollback
-                * buffer of its own.
+                * Scrollback is active.  So hoik the unicode characters out
+                * of the unicode circular buffer.
                 */
-               u16 *p = (u16 *)pos;
-               int mask = vc->vc_hi_font_mask | 0xff;
-               char32_t *uni_buf = dest;
-               while (nr--) {
-                       u16 glyph = scr_readw(p++) & mask;
-                       *uni_buf++ = inverse_translate(vc, glyph, true);
-               }
+               /* CAN'T HAPPEN!!!  (Hah hah!) */
        }
+#endif
 }
 
 /* this is for validation and debugging only */
 static void vc_uniscr_debug_check(struct vc_data *vc)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
-       unsigned short *p;
-       int x, y, mask;
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       /* struct uni_screen *uniscr = get_vc_uniscr(vc); */
+       /* unsigned short *p; */
+       /* int x, y, mask; */
+#endif
 
-       if (!VC_UNI_SCREEN_DEBUG || !uniscr)
+       if (!VC_UNI_SCREEN_DEBUG || !vc->vc_uniscr_buf)
                return;
 
        WARN_CONSOLE_UNLOCKED();
 
+#ifndef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
        /*
         * Make sure our unicode screen translates into the same glyphs
         * as the actual screen. This is brutal indeed.
         */
-       p = (unsigned short *)vc->vc_origin;
-       mask = vc->vc_hi_font_mask | 0xff;
-       for (y = 0; y < vc->vc_rows; y++) {
-               char32_t *line = uniscr->lines[y];
-               for (x = 0; x < vc->vc_cols; x++) {
-                       u16 glyph = scr_readw(p++) & mask;
-                       char32_t uc = line[x];
-                       int tc = conv_uni_to_pc(vc, uc);
-                       if (tc == -4)
-                               tc = conv_uni_to_pc(vc, 0xfffd);
-                       if (tc == -4)
-                               tc = conv_uni_to_pc(vc, '?');
-                       if (tc != glyph)
-                               pr_err_ratelimited(
-                                       "%s: mismatch at %d,%d: glyph=%#x 
tc=%#x\n",
-                                       __func__, x, y, glyph, tc);
-               }
-       }
+       /* p = (unsigned short *)vc->vc_origin; */
+       /* mask = vc->vc_hi_font_mask | 0xff; */
+       /* for (y = 0; y < vc->vc_rows; y++) { */
+       /*      char32_t *line = uniscr->lines[y]; */
+       /*      for (x = 0; x < vc->vc_cols; x++) { */
+       /*              u16 glyph = scr_readw(p++) & mask; */
+       /*              char32_t uc = line[x]; */
+       /*              int tc = conv_uni_to_pc(vc, uc); */
+       /*              if (tc == -4) */
+       /*                      tc = conv_uni_to_pc(vc, 0xfffd); */
+       /*              if (tc == -4) */
+       /*                      tc = conv_uni_to_pc(vc, '?'); */
+       /*              if (tc != glyph) */
+       /*                      pr_err_ratelimited( */
+       /*                              "%s: mismatch at %d,%d: glyph=%#x 
tc=%#x\n", */
+       /*                              __func__, x, y, glyph, tc); */
+       /*      } */
+       /* } */
+#endif
 }
 
 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
@@ -874,11 +908,12 @@ static void do_update_region(struct vc_data *vc, unsigned 
long start, int count)
                  ? vc->vc_softback_curr
                  : vc->vc_softback_curr
                    - (vc->vc_softback_end - vc->vc_softback_buf)
-               : vc->vc_origin;
+               : vc->vc_origin - vc->vc_softback_lines * vc->vc_size_row;
        p = (u16 *) start;
-               offset = (start - origin) / 2;
-               xx = offset % vc->vc_cols;
-               yy = offset / vc->vc_cols;
+       offset = (start - origin) / 2;
+       xx = offset % vc->vc_cols;
+       yy = offset / vc->vc_cols;
+
        for(;;) {
                u16 attrib = scr_readw(p) & 0xff00;
                int startx = xx;
@@ -905,10 +940,6 @@ static void do_update_region(struct vc_data *vc, unsigned 
long start, int count)
                        break;
                xx = 0;
                yy++;
-               /* if (vc->vc_sw->con_getxy) { */
-               /*      p = (u16 *)start; */
-               /*      start = vc->vc_sw->con_getxy(vc, start, NULL, NULL); */
-               /* } */
        }
 }
 #else
@@ -1028,51 +1059,68 @@ static void update_attr(struct vc_data *vc)
 }
 
 /* Note: inverting the screen twice should revert to the original state */
+/* OFFSET is the offset in bytes (not 16-bit characters), COUNT is a byte
+ * count (not a character count).  */
 void invert_screen(struct vc_data *vc, int offset, int count, bool viewed)
 {
        unsigned short *p;
+       int row_offset, bytes_left_in_row;
+       int row_count;
 
        WARN_CONSOLE_UNLOCKED();
 
        count /= 2;
-       p = screenpos(vc, offset, viewed);
-       if (vc->vc_sw->con_invert_region) {
-               vc->vc_sw->con_invert_region(vc, p, count);
-       } else {
-               u16 *q = p;
-               int cnt = count;
-               u16 a;
-
-               if (!vc->vc_can_do_color) {
-                       while (cnt--) {
-                           a = scr_readw(q);
-                           a ^= 0x0800;
-                           scr_writew(a, q);
-                           q++;
-                       }
-               } else if (vc->vc_hi_font_mask == 0x100) {
-                       while (cnt--) {
-                               a = scr_readw(q);
-                               a = (a & 0x11ff) |
-                                  ((a & 0xe000) >> 4) |
-                                  ((a & 0x0e00) << 4);
-                               scr_writew(a, q);
-                               q++;
-                       }
+       row_offset = offset;
+       bytes_left_in_row = vc->vc_size_row - (row_offset % vc->vc_size_row);
+       row_count = (count < bytes_left_in_row / 2)
+               ? count
+               : bytes_left_in_row / 2;
+
+       while (count) {
+               p = screenpos(vc, row_offset, viewed);
+               if (vc->vc_sw->con_invert_region) {
+                       vc->vc_sw->con_invert_region(vc, p, row_count);
                } else {
-                       while (cnt--) {
-                               a = scr_readw(q);
-                               a = (a & 0x88ff) |
-                                  ((a & 0x7000) >> 4) |
-                                  ((a & 0x0700) << 4);
-                               scr_writew(a, q);
-                               q++;
+                       u16 *q = p;
+                       int cnt = row_count;
+                       u16 a;
+
+                       if (!vc->vc_can_do_color) {
+                               while (cnt--) {
+                                       a = scr_readw(q);
+                                       a ^= 0x0800;
+                                       scr_writew(a, q);
+                                       q++;
+                               }
+                       } else if (vc->vc_hi_font_mask == 0x100) {
+                               while (cnt--) {
+                                       a = scr_readw(q);
+                                       a = (a & 0x11ff) |
+                                               ((a & 0xe000) >> 4) |
+                                               ((a & 0x0e00) << 4);
+                                       scr_writew(a, q);
+                                       q++;
+                               }
+                       } else {
+                               while (cnt--) {
+                                       a = scr_readw(q);
+                                       a = (a & 0x88ff) |
+                                               ((a & 0x7000) >> 4) |
+                                               ((a & 0x0700) << 4);
+                                       scr_writew(a, q);
+                                       q++;
+                               }
                        }
                }
-       }
 
-       if (con_should_update(vc))
-               do_update_region(vc, (unsigned long) p, count);
+               if (con_should_update(vc))
+                       do_update_region(vc, (unsigned long) p, row_count);
+               row_offset += 2 * row_count;
+               count -= row_count;
+               row_count = (count >= vc->vc_cols)
+                       ? vc->vc_cols
+                       : count;
+       }
        notify_update(vc);
 }
 
@@ -1273,7 +1321,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
 
        if (!vc) {
                /* strange ... */
-               /* printk("redraw_screen: tty %d not allocated ??\n", 
new_console+1); */
                return;
        }
 
@@ -1401,11 +1448,13 @@ int vc_allocate(unsigned int currcons)  /* return 0 on 
success */
 {
        struct vt_notifier_param param;
        struct vc_data *vc;
+       int err;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
        unsigned long new_end;
        unsigned long new_in, new_top;
        unsigned long in_residue;
        unsigned short *d;
-       int err;
+#endif
 
        WARN_CONSOLE_UNLOCKED();
        if (currcons >= MAX_NR_CONSOLES)
@@ -1425,6 +1474,7 @@ int vc_allocate(unsigned int currcons)    /* return 0 on 
success */
                                        vc->vc_softback_curr = 
vc->vc_softback_buf;
                                vc->vc_softback_lines = 0;
                                con_update_softback(vc);
+                               vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
                        }
                }
                if (vc->vc_softback_buf
@@ -1513,6 +1563,7 @@ int vc_allocate(unsigned int currcons)    /* return 0 on 
success */
                vc->vc_softback_buf;
        vc->vc_softback_lines = 0;
        con_update_softback(vc);
+       vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
 #endif
        return 0;
 err_free:
@@ -1534,6 +1585,72 @@ static inline int resize_screen(struct vc_data *vc, int 
width, int height,
        return err;
 }
 
+static int vc_copy_uniscr_to_new_area (struct vc_data *vc,
+                                      unsigned int new_rows,
+                                      unsigned int new_cols)
+{
+       unsigned int old_rows = vc->vc_rows, old_cols = vc->vc_cols;
+        uint32_t *old_uniscr_curr = vc->vc_uniscr_curr,
+               *old_uniscr_buf = vc->vc_uniscr_buf;
+       unsigned int old_uniscr_char_size = vc->vc_uniscr_char_size;
+       unsigned int new_lines;
+       unsigned int copy_cols;
+       uint32_t *dest, *src;
+       int res;
+
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+       unsigned int new_uniscr_rows;
+       unsigned int old_lines;
+       long tmp;
+
+       tmp = vc->vc_softback_curr - vc->vc_softback_top;
+       if (tmp < 0)
+               tmp += vc->vc_softback_end - vc->vc_softback_buf;
+       old_lines = tmp / vc->vc_size_row + old_rows;
+
+       if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+               return res;
+
+       new_uniscr_rows = (vc->vc_softback_end - vc->vc_softback_buf)
+               / vc->vc_size_row
+               + new_rows;
+       new_lines = min(old_lines, new_uniscr_rows);
+       copy_cols = min(old_cols, new_cols);
+
+       dest = vc->vc_uniscr_curr - (new_lines - new_rows) * new_cols;
+       if (dest < vc->vc_uniscr_buf)
+               dest += vc->vc_uniscr_char_size;
+       src = old_uniscr_curr - (old_lines - old_rows) * old_cols;
+       if (src < old_uniscr_buf)
+               src += old_uniscr_char_size;
+#else
+       if ((res = vc_uniscr_alloc(vc, new_cols, new_rows)) != 0)
+               return res;
+
+       new_lines = min(old_rows, new_rows);
+       copy_cols = min(old_cols, new_cols);
+       dest = vc->vc_uniscr_curr;
+       src = old_uniscr_curr;
+#endif
+       if (old_uniscr_buf) {
+               while (new_lines--) {
+                       memcpy(dest, src, copy_cols * sizeof(uint32_t));
+                       if (new_cols > old_cols)
+                               memset32(dest + old_cols, ' ',
+                                        new_cols - old_cols);
+                       dest += new_cols;
+                       if (dest >= vc->vc_uniscr_buf
+                           + vc->vc_uniscr_char_size)
+                               dest -= vc->vc_uniscr_char_size;
+                       src += old_cols;
+                       if (src >= old_uniscr_buf + old_uniscr_char_size)
+                               src -= old_uniscr_char_size;
+               }
+               kvfree(old_uniscr_buf);
+       }
+       return 0;
+}
+
 /**
  *     vc_do_resize    -       resizing method for the tty
  *     @tty: tty being resized
@@ -1558,7 +1675,7 @@ static int vc_do_resize(struct tty_struct *tty, struct 
vc_data *vc,
        unsigned int new_cols, new_rows, new_row_size, new_screen_size;
        unsigned int user;
        unsigned short *oldscreen, *newscreen;
-       struct uni_screen *new_uniscr = NULL;
+       uint32_t *old_uniscr = vc->vc_uniscr_buf;
 
        WARN_CONSOLE_UNLOCKED();
 
@@ -1602,14 +1719,6 @@ static int vc_do_resize(struct tty_struct *tty, struct 
vc_data *vc,
        if (!newscreen)
                return -ENOMEM;
 
-       if (get_vc_uniscr(vc)) {
-               new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
-               if (!new_uniscr) {
-                       kfree(newscreen);
-                       return -ENOMEM;
-               }
-       }
-
        if (vc_is_sel(vc))
                clear_selection();
 
@@ -1619,10 +1728,14 @@ static int vc_do_resize(struct tty_struct *tty, struct 
vc_data *vc,
        err = resize_screen(vc, new_cols, new_rows, user);
        if (err) {
                kfree(newscreen);
-               vc_uniscr_free(new_uniscr);
+               vc_uniscr_free(vc);
+               vc->vc_uniscr_buf = old_uniscr;
                return err;
        }
 
+       if ((err = vc_copy_uniscr_to_new_area(vc, new_rows, new_cols)) != 0)
+               return err;
+
        vc->vc_rows = new_rows;
        vc->vc_cols = new_cols;
        vc->vc_size_row = new_row_size;
@@ -1653,11 +1766,6 @@ static int vc_do_resize(struct tty_struct *tty, struct 
vc_data *vc,
                first_copied_row = 0;
        end = old_origin + old_row_size * min(old_rows, new_rows);
 
-       vc_uniscr_copy_area(new_uniscr, new_cols, new_rows,
-                           get_vc_uniscr(vc), rlth/2, first_copied_row,
-                           min(old_rows, new_rows));
-       vc_uniscr_set(vc, new_uniscr);
-
        update_attr(vc);
 
        while (old_origin < end) {
@@ -1757,8 +1865,8 @@ struct vc_data *vc_deallocate(unsigned int currcons)
                visual_deinit(vc);
                con_free_unimap(vc);
                put_pid(vc->vt_pid);
-               vc_uniscr_set(vc, NULL);
                kfree(vc->vc_screenbuf);
+               vc->vc_uniscr_buf = NULL;
                vc_cons[currcons].d = NULL;
        }
        return vc;
@@ -3457,11 +3565,8 @@ static void vt_console_print(struct console *co, const 
char *b, unsigned count)
        if (kmsg_console && vc_cons_allocated(kmsg_console - 1))
                vc = vc_cons[kmsg_console - 1].d;
 
-       if (!vc_cons_allocated(fg_console)) {
-               /* impossible */
-               /* printk("vt_console_print: tty %d not allocated ??\n", 
currcons+1); */
+       if (!vc_cons_allocated(fg_console))
                goto quit;
-       }
 
        if (vc->vc_mode != KD_TEXT)
                goto quit;
@@ -3758,6 +3863,10 @@ static int con_install(struct tty_driver *driver, struct 
tty_struct *tty)
 
 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
        con_update_softback(vc);
+       if (!vc->vc_uniscr_buf)
+               ret = vc_uniscr_alloc(vc, vc->vc_cols, vc->vc_rows);
+       if (ret)
+               goto unlock;
 #endif
 
        /* Still being freed */
@@ -5119,10 +5228,19 @@ EXPORT_SYMBOL_GPL(screen_glyph);
 
 u32 screen_glyph_unicode(const struct vc_data *vc, int n)
 {
-       struct uni_screen *uniscr = get_vc_uniscr(vc);
+       int y = n / vc->vc_cols, x = n % vc->vc_cols;
+       uint32_t *ln = vc->vc_uniscr_curr + y * vc->vc_cols;
 
-       if (uniscr)
-               return uniscr->lines[n / vc->vc_cols][n % vc->vc_cols];
+       if (vc->vc_uniscr_curr) {
+               if (ln >= vc_uniscr_buf_end(vc))
+                       ln -= vc->vc_uniscr_char_size;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+               ln -= vc->vc_softback_lines * vc->vc_cols;
+               if (ln < vc->vc_uniscr_buf)
+                       ln += vc->vc_uniscr_char_size;
+#endif
+               return ln[x];
+       }
        return inverse_translate(vc, screen_glyph(vc, n * 2), 1);
 }
 EXPORT_SYMBOL_GPL(screen_glyph_unicode);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 4a343877d70c..1abba103d7da 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -121,6 +121,21 @@ config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE
          position on the video takes 2 bytes of storage.  128kB will give you
          approximately four 240x67 screenfuls of scrollback buffer.
 
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_GPM
+       bool "Enable a working GPM for scrolled back scrollback buffer in 
System RAM"
+       depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       default y
+       help
+         This option buffers up Unicode characters corresponding to the glyphs
+         displayed by the scrollback buffer.  This enables the GPM mouse driver
+         (or similar) to copy characters from a scrolled back buffer.
+
+         A buffer is created for each framebuffer console, this buffer being
+         approximately twice as big as the buffer size specified in
+         FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE.
+
+         If unsure, say 'Y'.
+
 config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
        bool "Map the console to the primary display device"
        depends on FRAMEBUFFER_CONSOLE
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index acc277e73e32..1d5d944ffaa7 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -170,7 +170,9 @@ struct vc_data {
        struct vc_data **vc_display_fg;         /* [!] Ptr to var holding fg 
console for this display */
        struct uni_pagedir *vc_uni_pagedir;
        struct uni_pagedir **vc_uni_pagedir_loc; /* [!] Location of uni_pagedir 
variable for this console */
-       struct uni_screen *vc_uni_screen;       /* unicode screen content */
+       uint32_t *vc_uniscr_buf;    /* Address of unicode screen content */
+       uint32_t vc_uniscr_char_size;   /* Size of *vc-uniscr_buf in 32-bit 
chars */
+       uint32_t *vc_uniscr_curr;       /* Pos of first char on (currently 
scrolled) screen */
        /* additional information is in vt_kern.h */
 };
 

Reply via email to