Hello, Jorge.

On Fri, Sep 24, 2021 at 09:22:45 +0100, Jorge Almeida wrote:
> On Thu, Sep 23, 2021 at 6:03 PM Alan Mackenzie <a...@muc.de> wrote:

[ .... ]

> It still fails:
> $ patch -p0 <../patch_for_5.14.diff
> patching file ./drivers/tty/vt/vt.c
> Hunk #1 FAILED at 3208.
> 1 out of 1 hunk FAILED -- saving rejects to file ./drivers/tty/vt/vt.c.rej

Apologies once more.  Late last night I managed to get a Linux kernel
git repository set up.  :-)  So, at least if there are any more
failures, they'll be systematic failures rather than erratic failures.
;-)

>From this I've constructed a complete clean 5.14.5 patch, which I've
attached.  Please start again from a gentoo-sources without any previous
traces of the scrollback patches, and apply that patch.  _Surely_ it
should work this time.

To apply the patch (you surely know this already), cd to the top of the
kernel tree, and use

    $ patch -p1 < 5.14.5-scroll-20210924.diff

..  Alternatively, if you've got git, you could use

    $ git apply 5.14.5-scroll-20210924.diff

..  Please let me know again how it works out.  Thanks!

[ .... ]

> Thanks,

> Jorge Almeida

-- 
Alan Mackenzie (Nuremberg, Germany).

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index ef981d3b7bb4..17b51bdc9f6e 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -134,6 +134,11 @@ const struct consw *conswitchp;
 #define DEFAULT_BELL_DURATION  (HZ/8)
 #define DEFAULT_CURSOR_BLINK_MS        200
 
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static unsigned int console_soft_scrollback_size =
+       1024 * CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE;
+#endif
+
 struct vc vc_cons [MAX_NR_CONSOLES];
 
 #ifndef VT_SINGLE_DRIVER
@@ -287,7 +292,7 @@ static inline unsigned short *screenpos(const struct 
vc_data *vc, int offset,
                bool viewed)
 {
        unsigned short *p;
-       
+
        if (!viewed)
                p = (unsigned short *)(vc->vc_origin + offset);
        else if (!vc->vc_sw->con_screen_pos)
@@ -616,6 +621,218 @@ static void vc_uniscr_debug_check(struct vc_data *vc)
        }
 }
 
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void con_update_softback(struct vc_data *vc)
+{
+       int l = vc->vc_softback_size / vc->vc_size_row;
+       if (l > 5)
+       {
+               vc->vc_softback_end = vc->vc_softback_buf + l * vc->vc_size_row;
+               vc->vc_softback_top = vc->vc_softback_buf;
+       }
+       else
+               /* Smaller scrollback makes no sense, and 0 would screw
+                  the operation totally */
+               vc->vc_softback_top = 0;
+}
+
+static int concon_set_origin(struct vc_data *vc)
+{
+       if (vc->vc_softback_lines)
+               concon_scrolldelta(vc, vc->vc_softback_lines);
+       return 0;
+}
+
+#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) 
* vc->vc_size_row)
+
+static void con_redraw_softback(struct vc_data *vc, /* struct display *p, */
+                               long delta)
+{
+       int count = vc->vc_rows;
+       unsigned short *d, *s;
+       unsigned long n;
+       int line = 0;
+
+       if (!vc->vc_softback_lines)
+               vc->vc_char_at_pos = scr_readw((u16 *)vc->vc_pos);
+
+       d = (u16 *) vc->vc_softback_curr;
+       if (d == (u16 *) vc->vc_softback_in)
+               d = (u16 *) vc->vc_origin;
+       n = vc->vc_softback_curr + delta * vc->vc_size_row;
+       vc->vc_softback_lines -= delta;
+       if (delta < 0) {
+               if (vc->vc_softback_curr < vc->vc_softback_top
+                   && n < vc->vc_softback_buf) {
+                       n += vc->vc_softback_end - vc->vc_softback_buf;
+                       if (n < vc->vc_softback_top) {
+                               vc->vc_softback_lines -=
+                                   (vc->vc_softback_top - n) / vc->vc_size_row;
+                               n = vc->vc_softback_top;
+                       }
+               } else if (vc->vc_softback_curr >= vc->vc_softback_top
+                          && n < vc->vc_softback_top) {
+                       vc->vc_softback_lines -=
+                           (vc->vc_softback_top - n) / vc->vc_size_row;
+                       n = vc->vc_softback_top;
+               }
+       } else {
+               if (vc->vc_softback_curr > vc->vc_softback_in
+                   && n >= vc->vc_softback_end) {
+                       n += vc->vc_softback_buf - vc->vc_softback_end;
+                       if (n > vc->vc_softback_in) {
+                               n = vc->vc_softback_in;
+                               vc->vc_softback_lines = 0;
+                       }
+               } else if (vc->vc_softback_curr <= vc->vc_softback_in
+                          && n > vc->vc_softback_in) {
+                       n = vc->vc_softback_in;
+                       vc->vc_softback_lines = 0;
+               }
+       }
+       if (n == vc->vc_softback_curr)
+               return;
+       vc->vc_softback_curr = n;
+       /* If we're not scrolled any more, restore the character to the cursor
+        * position */
+       if (!vc->vc_softback_lines)
+               scr_writew(vc->vc_char_at_pos, (u16 *)vc->vc_pos);
+       s = (u16 *) vc->vc_softback_curr;
+       if (s == (u16 *) vc->vc_softback_in)
+               s = (u16 *) vc->vc_origin;
+       while (count--) {
+               unsigned short *start;
+               unsigned short *le;
+               unsigned short c;
+               int x = 0;
+               unsigned short attr = 1;
+
+               start = s;
+               le = advance_row(s, 1);
+               /* Temporarily overwrite the character at the cursor position
+                * with the one we actually want to see on the screen.  */
+               if (count == vc->vc_rows - vc->state.y - 1)
+               {
+                       c = scr_readw((u16 *)(s + vc->state.x));
+                       scr_writew(c, (u16 *)vc->vc_pos);
+                       vc->vc_sw->con_putcs
+                               (vc, (u16 *)vc->vc_pos, 1, line, vc->state.x);
+               }
+               do {
+                       c = scr_readw(s);
+                       if (attr != (c & 0xff00)) {
+                               attr = c & 0xff00;
+                               if (s > start) {
+                                       vc->vc_sw->con_putcs(
+                                               vc, start, s - start,
+                                               line, x);
+                                       x += s - start;
+                                       start = s;
+                               }
+                       }
+                       if (c == scr_readw(d)) {
+                               if (s > start) {
+                                       vc->vc_sw->con_putcs(
+                                               vc, start, s - start,
+                                               line, x);
+                                       x += s - start + 1;
+                                       start = s + 1;
+                               } else {
+                                       x++;
+                                       start++;
+                               }
+                       }
+                       s++;
+                       d++;
+               } while (s < le);
+               if (s > start)
+                       vc->vc_sw->con_putcs(vc, start, s - start, line, x);
+               line++;
+               if (d == (u16 *) vc->vc_softback_end)
+                       d = (u16 *) vc->vc_softback_buf;
+               if (d == (u16 *) vc->vc_softback_in)
+                       d = (u16 *) vc->vc_origin;
+               if (s == (u16 *) vc->vc_softback_end)
+                       s = (u16 *) vc->vc_softback_buf;
+               if (s == (u16 *) vc->vc_softback_in)
+                       s = (u16 *) vc->vc_origin;
+       }
+}
+
+static inline void con_softback_note(struct vc_data *vc, int t,
+                                    int count)
+{
+       unsigned short *p;
+
+       if (vc->vc_num != fg_console)
+               return;
+       p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row);
+
+       while (count) {
+               scr_memcpyw((u16 *) vc->vc_softback_in, p, vc->vc_size_row);
+               count--;
+               p = advance_row(p, 1);
+               vc->vc_softback_in += vc->vc_size_row;
+               if (vc->vc_softback_in == vc->vc_softback_end)
+                       vc->vc_softback_in = vc->vc_softback_buf;
+               if (vc->vc_softback_in == vc->vc_softback_top) {
+                       vc->vc_softback_top += vc->vc_size_row;
+                       if (vc->vc_softback_top == vc->vc_softback_end)
+                               vc->vc_softback_top = vc->vc_softback_buf;
+               }
+       }
+       vc->vc_softback_curr = vc->vc_softback_in;
+}
+
+void concon_scrolldelta(struct vc_data *vc, int lines)
+{
+       /* struct display *disp = &fb_display[fg_console]; */
+       /* int offset, limit, scrollback_old; */
+
+       if (vc->vc_softback_top) {
+               if (vc->vc_num != fg_console)
+                       return;
+               if (vc->vc_mode != KD_TEXT || !lines)
+                       return;
+#if 0
+               if (logo_shown >= 0) {
+                       struct vc_data *conp2 = vc_cons[logo_shown].d;
+
+                       if (conp2->vc_top == logo_lines
+                           && conp2->vc_bottom == conp2->vc_rows)
+                               conp2->vc_top = 0;
+                       if (logo_shown == vc->vc_num) {
+                               unsigned long p, q;
+                               int i;
+
+                               p = vc->vc_softback_in;
+                               q = vc->vc_origin +
+                                   logo_lines * vc->vc_size_row;
+                               for (i = 0; i < logo_lines; i++) {
+                                       if (p == vc->vc_softback_top)
+                                               break;
+                                       if (p == vc->vc_softback_buf)
+                                               p = vc->vc_softback_end;
+                                       p -= vc->vc_size_row;
+                                       q -= vc->vc_size_row;
+                                       scr_memcpyw((u16 *) q, (u16 *) p,
+                                                   vc->vc_size_row);
+                               }
+                               vc->vc_softback_in = vc->vc_softback_curr = p;
+                               update_region(vc, vc->vc_origin,
+                                             logo_lines * vc->vc_cols);
+                       }
+                       logo_shown = FBCON_LOGO_CANSHOW;
+               }
+#endif
+               vc->vc_sw->con_cursor(vc, CM_ERASE /* | CM_SOFTBACK */);
+               con_redraw_softback(vc, /* disp, */ lines);
+               if (!vc->vc_softback_lines)
+                       vc->vc_sw->con_cursor(vc, CM_DRAW /* | CM_SOFTBACK */);
+       }
+}
+
+#endif  /* CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK */
 
 static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
                enum con_scroll dir, unsigned int nr)
@@ -626,6 +843,10 @@ static void con_scroll(struct vc_data *vc, unsigned int t, 
unsigned int b,
                nr = b - t - 1;
        if (b > vc->vc_rows || t >= b || nr < 1)
                return;
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       if (dir == SM_UP && vc->vc_softback_top)
+               con_softback_note (vc, t, nr);
+#endif
        vc_uniscr_scroll(vc, t, b, dir, nr);
        if (con_is_visible(vc) && vc->vc_sw->con_scroll(vc, t, b, dir, nr))
                return;
@@ -641,6 +862,56 @@ static void con_scroll(struct vc_data *vc, unsigned int t, 
unsigned int b,
        scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);
 }
 
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+static void do_update_region(struct vc_data *vc, unsigned long start, int 
count)
+{
+       unsigned int xx, yy, offset;
+       u16 *p;
+
+       unsigned long origin =
+               (start >= vc->vc_softback_buf && start < vc->vc_softback_end)
+               ? start >= vc->vc_softback_curr
+                 ? vc->vc_softback_curr
+                 : vc->vc_softback_curr
+                   - (vc->vc_softback_end - vc->vc_softback_buf)
+               : vc->vc_origin;
+       p = (u16 *) start;
+               offset = (start - origin) / 2;
+               xx = offset % vc->vc_cols;
+               yy = offset / vc->vc_cols;
+       for(;;) {
+               u16 attrib = scr_readw(p) & 0xff00;
+               int startx = xx;
+               u16 *q = p;
+               while (xx < vc->vc_cols && count) {
+                       if (attrib != (scr_readw(p) & 0xff00)) {
+                               if (p > q)
+                                       vc->vc_sw->con_putcs(vc, q, p-q, yy, 
startx);
+                               startx = xx;
+                               q = p;
+                               attrib = scr_readw(p) & 0xff00;
+                       }
+                       p++;
+                       xx++;
+                       count--;
+               }
+               if (p > q)
+                       vc->vc_sw->con_putcs(vc, q, p-q, yy, startx);
+               if (p == (u16 *) vc->vc_softback_end)
+                       p = (u16 *)vc->vc_softback_buf;
+               if (p == (u16 *) vc->vc_softback_in)
+                       p = (u16 *)vc->vc_origin;
+               if (!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
 static void do_update_region(struct vc_data *vc, unsigned long start, int 
count)
 {
        unsigned int xx, yy, offset;
@@ -684,6 +955,7 @@ static void do_update_region(struct vc_data *vc, unsigned 
long start, int count)
                }
        }
 }
+#endif
 
 void update_region(struct vc_data *vc, unsigned long start, int count)
 {
@@ -692,7 +964,10 @@ void update_region(struct vc_data *vc, unsigned long 
start, int count)
        if (con_should_update(vc)) {
                hide_cursor(vc);
                do_update_region(vc, start, count);
-               set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+               if (!vc->vc_softback_lines)
+#endif
+                       set_cursor(vc);
        }
 }
 
@@ -927,8 +1202,17 @@ static void set_origin(struct vc_data *vc)
        WARN_CONSOLE_UNLOCKED();
 
        if (!con_is_visible(vc) ||
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+           (
+            !concon_set_origin (vc) &&
+            (
+#endif
            !vc->vc_sw->con_set_origin ||
-           !vc->vc_sw->con_set_origin(vc))
+           !vc->vc_sw->con_set_origin(vc)
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+                    ))
+#endif
+                                         )
                vc->vc_origin = (unsigned long)vc->vc_screenbuf;
        vc->vc_visible_origin = vc->vc_origin;
        vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
@@ -1004,7 +1288,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
                hide_cursor(old_vc);
                if (!con_is_visible(old_vc)) {
                        save_screen(old_vc);
-                       set_origin(old_vc);
                }
                if (tty0dev)
                        sysfs_notify(&tty0dev->kobj, NULL, "active");
@@ -1017,7 +1300,6 @@ void redraw_screen(struct vc_data *vc, int is_switch)
                int update;
                int old_was_color = vc->vc_can_do_color;
 
-               set_origin(vc);
                update = vc->vc_sw->con_switch(vc);
                set_palette(vc);
                /*
@@ -1032,9 +1314,19 @@ void redraw_screen(struct vc_data *vc, int is_switch)
                }
 
                if (update && vc->vc_mode != KD_GRAPHICS)
-                       do_update_region(vc, vc->vc_origin, 
vc->vc_screenbuf_size / 2);
+                       do_update_region(vc,
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+                                        vc->vc_softback_lines
+                                        ? vc->vc_softback_curr
+                                        :
+#endif
+                                          vc->vc_origin,
+                                        vc->vc_screenbuf_size / 2);
        }
-       set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       if (!vc->vc_softback_lines)
+#endif
+               set_cursor(vc);
        if (is_switch) {
                vt_set_leds_compute_shiftstate();
                notify_update(vc);
@@ -1112,12 +1404,28 @@ int vc_allocate(unsigned int currcons)  /* return 0 on 
success */
        int err;
 
        WARN_CONSOLE_UNLOCKED();
-
        if (currcons >= MAX_NR_CONSOLES)
                return -ENXIO;
 
        if (vc_cons[currcons].d)
+       {
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+               vc = vc_cons[currcons].d;
+               if (!vc->vc_softback_size) {
+                       /* vc was partially initialized by __init. */
+                       vc->vc_softback_size = console_soft_scrollback_size;
+                       vc->vc_softback_buf =
+                               (unsigned long)kzalloc(vc->vc_softback_size, 
GFP_KERNEL);
+                       if (vc->vc_softback_buf) {
+                               vc->vc_softback_in = vc->vc_softback_top =
+                                       vc->vc_softback_curr = 
vc->vc_softback_buf;
+                               vc->vc_softback_lines = 0;
+                               con_update_softback(vc);
+                       }
+               }
+#endif
                return 0;
+       }
 
        /* due to the granularity of kmalloc, we waste some memory here */
        /* the alloc is done in two steps, to optimize the common situation
@@ -1157,6 +1465,18 @@ int vc_allocate(unsigned int currcons)   /* return 0 on 
success */
        vcs_make_sysfs(currcons);
        atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param);
 
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       vc->vc_softback_size = console_soft_scrollback_size;
+       err = -ENOMEM;
+       vc->vc_softback_buf =
+               (unsigned long)kzalloc(vc->vc_softback_size, GFP_KERNEL);
+       if (!vc->vc_softback_buf)
+               goto err_free;
+       vc->vc_softback_in = vc->vc_softback_top = vc->vc_softback_curr =
+               vc->vc_softback_buf;
+       vc->vc_softback_lines = 0;
+       con_update_softback(vc);
+#endif
        return 0;
 err_free:
        visual_deinit(vc);
@@ -1629,7 +1949,7 @@ struct rgb { u8 r; u8 g; u8 b; };
 
 static void rgb_from_256(int i, struct rgb *c)
 {
-       if (i < 8) {            /* Standard colours. */
+       if (i < 8) {        /* Standard colours. */
                c->r = i&1 ? 0xaa : 0x00;
                c->g = i&2 ? 0xaa : 0x00;
                c->b = i&4 ? 0xaa : 0x00;
@@ -1641,7 +1961,7 @@ static void rgb_from_256(int i, struct rgb *c)
                c->r = (i - 16) / 36 * 85 / 2;
                c->g = (i - 16) / 6 % 6 * 85 / 2;
                c->b = (i - 16) % 6 * 85 / 2;
-       } else                  /* Grayscale ramp. */
+       } else            /* Grayscale ramp. */
                c->r = c->g = c->b = i * 10 - 2312;
 }
 
@@ -2888,6 +3208,12 @@ static int do_con_write(struct tty_struct *tty, const 
unsigned char *buf, int co
 
        param.vc = vc;
 
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       /* Undo any soft scrolling - <Alt><Fn> and <Shift><PgUp/Down> do
+          not pass through this function.  */
+       concon_set_origin (vc);
+#endif
+
        while (!tty->flow.stopped && count) {
                int orig = *buf;
                buf++;
@@ -3103,7 +3429,11 @@ static void vt_console_print(struct console *co, const 
char *b, unsigned count)
        }
        if (cnt && con_is_visible(vc))
                vc->vc_sw->con_putcs(vc, start, cnt, vc->state.y, start_x);
-       set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       if (!vc->vc_softback_lines)
+#endif
+               set_cursor(vc);
+
        notify_update(vc);
 
 quit:
@@ -3324,7 +3654,11 @@ static void con_flush_chars(struct tty_struct *tty)
        /* if we race with con_close(), vt may be null */
        console_lock();
        vc = tty->driver_data;
-       if (vc)
+       if (vc
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+           && !vc->vc_softback_lines
+#endif
+             )
                set_cursor(vc);
        console_unlock();
 }
@@ -3345,6 +3679,10 @@ static int con_install(struct tty_driver *driver, struct 
tty_struct *tty)
 
        vc = vc_cons[currcons].d;
 
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       con_update_softback(vc);
+#endif
+
        /* Still being freed */
        if (vc->port.tty) {
                ret = -ERESTARTSYS;
@@ -3400,7 +3738,7 @@ static void con_cleanup(struct tty_struct *tty)
        tty_port_put(&vc->port);
 }
 
-static int default_color           = 7; /* white */
+static int default_color          = 7; /* white */
 static int default_italic_color    = 2; // green (ASCII)
 static int default_underline_color = 3; // cyan (ASCII)
 module_param_named(color, default_color, int, S_IRUGO | S_IWUSR);
@@ -4100,7 +4438,7 @@ static int do_register_con_driver(const struct consw 
*csw, int first, int last)
                        con_driver->desc = desc;
                        con_driver->node = i;
                        con_driver->flag = CON_DRIVER_FLAG_MODULE |
-                                          CON_DRIVER_FLAG_INIT;
+                                          CON_DRIVER_FLAG_INIT;
                        con_driver->first = first;
                        con_driver->last = last;
                        retval = 0;
@@ -4401,7 +4739,10 @@ void do_unblank_screen(int leaving_gfx)
        if (console_blank_hook)
                console_blank_hook(0);
        set_palette(vc);
-       set_cursor(vc);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       if (!vc->vc_softback_lines)
+#endif
+               set_cursor(vc);
        vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num);
 }
 EXPORT_SYMBOL(do_unblank_screen);
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 840d9813b0bc..d126d6a4f2b2 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -78,6 +78,55 @@ config FRAMEBUFFER_CONSOLE
        help
          Low-level framebuffer-based console driver.
 
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       bool "Enable Scrollback Buffer in System RAM"
+       depends on FB=y && FRAMEBUFFER_CONSOLE
+       default y
+       select FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT
+       help
+         This option creates scrollback buffers for each framebuffer console,
+         or one buffer for them all.  These buffers are allocated dynamically
+         during initialisation.
+
+         If you want this feature, say 'Y' here and enter the amount of
+         RAM to allocate for this buffer.  If unsure, say 'N'.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_SIZE
+       int "Scrollback Buffer Size (in KB)"
+       depends on FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       range 1 1024
+       default "128"
+       help
+                 Enter the amount of System RAM to allocate for each scrollback
+         buffer of framebuffer consoles in kilobytes.  Each character
+         position on the video takes 2 bytes of storage.  128k will give you
+         approximately 4 240x67 screenfuls of scrollback buffer.
+
+config FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT
+       bool "Persistent Scrollback History for each framebuffer console by 
default"
+       depends on FB=y && FRAMEBUFFER_CONSOLE && 
FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       default y
+       help
+
+         Note: this option's value N has not (?yet) been implemented (2021-04).
+
+         Say Y here if the scrollback history should persist by default when
+         switching between consoles. Otherwise, the scrollback history will
+         be flushed the first time a scroll-up operation occurs on the new
+         console after the console is switched. STOUGH!!!  FIXME!!! This
+         feature can also be enabled using the boot command line parameter
+         'vgacon.scrollback_persistent=1'.
+
+         This feature might break your tool of choice to flush the scrollback
+         buffer, e.g. clear(1) will work fine but Debian's clear_console(1)
+         will be broken, which might cause security issues.
+         You can use the escape sequence \e[3J instead if this feature is
+         activated.
+
+         Note that a buffer of VGACON_SOFT_SCROLLBACK_SIZE is taken for each
+         created tty device.
+         So if you use a RAM-constrained system, say N here.
+
 config FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
        bool "Map the console to the primary display device"
        depends on FRAMEBUFFER_CONSOLE
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 22bb3892f6bd..c89cc8c605fc 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -3053,6 +3053,9 @@ static const struct consw fb_con = {
        .con_font_get           = fbcon_get_font,
        .con_font_default       = fbcon_set_def_font,
        .con_set_palette        = fbcon_set_palette,
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       .con_scrolldelta        = concon_scrolldelta,
+#endif
        .con_invert_region      = fbcon_invert_region,
        .con_screen_pos         = fbcon_screen_pos,
        .con_getxy              = fbcon_getxy,
diff --git a/include/linux/console_struct.h b/include/linux/console_struct.h
index d5b9c8d40c18..acc277e73e32 100644
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -110,6 +110,17 @@ struct vc_data {
        unsigned short  *vc_screenbuf;          /* In-memory 
character/attribute buffer */
        unsigned int    vc_screenbuf_size;
        unsigned char   vc_mode;                /* KD_TEXT, ... */
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+       unsigned int    vc_softback_size;       /* Size in bytes of scrollback 
buffer. */
+       unsigned long   vc_softback_buf;        /* Address of scrollback 
buffer. */
+       unsigned long   vc_softback_end;        /* (Just past) end of buffer. */
+       unsigned long   vc_softback_in;         /* Head pointer into circular 
buffer. */
+       unsigned long   vc_softback_top;        /* Tail pointer into circular 
buffer. */
+       unsigned long   vc_softback_curr;       /* Pos in vc_screenbuf or 
vc_softback_buf
+                                                  corresponding to visible 
screen. */
+       int             vc_softback_lines;      /* Number of lines currently 
scrolled. */
+       unsigned short  vc_char_at_pos;         /* Char at vc_pos when no soft 
scroll */
+#endif
        /* attributes for all characters on screen */
        unsigned char   vc_attr;                /* Current attributes */
        unsigned char   vc_def_color;           /* Default colors */
diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h
index 0da94a6dee15..63b83ffbef95 100644
--- a/include/linux/vt_kern.h
+++ b/include/linux/vt_kern.h
@@ -115,6 +115,9 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data 
*src_vc)
 /* vt.c */
 void vt_event_post(unsigned int event, unsigned int old, unsigned int new);
 int vt_waitactive(int n);
+#ifdef CONFIG_FRAMEBUFFER_CONSOLE_SOFT_SCROLLBACK
+void concon_scrolldelta(struct vc_data *vc, int lines);
+#endif
 void change_console(struct vc_data *new_vc);
 void reset_vc(struct vc_data *vc);
 int do_unbind_con_driver(const struct consw *csw, int first, int last,

Reply via email to