Commit aabd31c4 authored by Manuel Schölling's avatar Manuel Schölling Committed by Greg Kroah-Hartman

console: Add persistent scrollback buffers for all VGA consoles

Add a scrollback buffers for each VGA console. The benefit is that
the scrollback history is not flushed when switching between consoles
but is persistent.
The buffers are allocated on demand when a new console is opened.

This breaks tools like clear_console that rely on flushing the
scrollback history by switching back and forth between consoles
which is why this feature is disabled by default.
Use the escape sequence \e[3J instead for flushing the buffer.
Signed-off-by: default avatarManuel Schölling <manuel.schoelling@gmx.de>
Reviewed-by: default avatarAndrey Utkin <andrey_utkin@fastmail.com>
Tested-by: default avatarAndrey Utkin <andrey_utkin@fastmail.com>
Tested-by: default avatarAdam Borowski <kilobyte@angband.pl>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent bcd375f7
...@@ -43,9 +43,28 @@ config VGACON_SOFT_SCROLLBACK_SIZE ...@@ -43,9 +43,28 @@ config VGACON_SOFT_SCROLLBACK_SIZE
range 1 1024 range 1 1024
default "64" default "64"
help help
Enter the amount of System RAM to allocate for the scrollback Enter the amount of System RAM to allocate for scrollback
buffer. Each 64KB will give you approximately 16 80x25 buffers of VGA consoles. Each 64KB will give you approximately
screenfuls of scrollback buffer 16 80x25 screenfuls of scrollback buffer.
config VGACON_SOFT_SCROLLBACK_PERSISTENT
bool "Persistent Scrollback History for each console"
depends on VGACON_SOFT_SCROLLBACK
default n
help
Say Y here if the scrollback history should persist when switching
between consoles. Otherwise, the scrollback history will be flushed
each time the console is switched.
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 MDA_CONSOLE config MDA_CONSOLE
depends on !M68K && !PARISC && ISA depends on !M68K && !PARISC && ISA
......
...@@ -162,7 +162,7 @@ static inline void vga_set_mem_top(struct vc_data *c) ...@@ -162,7 +162,7 @@ static inline void vga_set_mem_top(struct vc_data *c)
#ifdef CONFIG_VGACON_SOFT_SCROLLBACK #ifdef CONFIG_VGACON_SOFT_SCROLLBACK
/* software scrollback */ /* software scrollback */
static struct vgacon_scrollback_info { struct vgacon_scrollback_info {
void *data; void *data;
int tail; int tail;
int size; int size;
...@@ -171,74 +171,110 @@ static struct vgacon_scrollback_info { ...@@ -171,74 +171,110 @@ static struct vgacon_scrollback_info {
int cur; int cur;
int save; int save;
int restore; int restore;
} vgacon_scrollback; };
static struct vgacon_scrollback_info *vgacon_scrollback_cur;
#ifdef CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT
static struct vgacon_scrollback_info vgacon_scrollbacks[MAX_NR_CONSOLES];
#else
static struct vgacon_scrollback_info vgacon_scrollbacks[1];
#endif
static void vgacon_scrollback_reset(size_t reset_size) static void vgacon_scrollback_reset(int vc_num, size_t reset_size)
{ {
if (vgacon_scrollback.data && reset_size > 0) struct vgacon_scrollback_info *scrollback = &vgacon_scrollbacks[vc_num];
memset(vgacon_scrollback.data, 0, reset_size);
if (scrollback->data && reset_size > 0)
memset(scrollback->data, 0, reset_size);
vgacon_scrollback.cnt = 0; scrollback->cnt = 0;
vgacon_scrollback.tail = 0; scrollback->tail = 0;
vgacon_scrollback.cur = 0; scrollback->cur = 0;
} }
static void vgacon_scrollback_init(int pitch) static void vgacon_scrollback_init(int vc_num)
{ {
int rows = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024/pitch; int pitch = vga_video_num_columns * 2;
size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
if (vgacon_scrollback.data) { int rows = size / pitch;
vgacon_scrollback.cnt = 0; void *data;
vgacon_scrollback.tail = 0;
vgacon_scrollback.cur = 0; data = kmalloc_array(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024,
vgacon_scrollback.rows = rows - 1; GFP_NOWAIT);
vgacon_scrollback.size = rows * pitch;
vgacon_scrollbacks[vc_num].data = data;
vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num];
vgacon_scrollback_cur->rows = rows - 1;
vgacon_scrollback_cur->size = rows * pitch;
vgacon_scrollback_reset(vc_num, size);
}
static void vgacon_scrollback_switch(int vc_num)
{
#ifndef CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT
vc_num = 0;
#endif
if (!vgacon_scrollbacks[vc_num].data) {
vgacon_scrollback_init(vc_num);
} else {
#ifdef CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT
vgacon_scrollback_cur = &vgacon_scrollbacks[vc_num];
#else
size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
vgacon_scrollback_reset(vc_num, size);
#endif
} }
} }
static void vgacon_scrollback_startup(void) static void vgacon_scrollback_startup(void)
{ {
vgacon_scrollback.data = kcalloc(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, vgacon_scrollback_cur = &vgacon_scrollbacks[0];
1024, GFP_NOWAIT); vgacon_scrollback_init(0);
vgacon_scrollback_init(vga_video_num_columns * 2);
} }
static void vgacon_scrollback_update(struct vc_data *c, int t, int count) static void vgacon_scrollback_update(struct vc_data *c, int t, int count)
{ {
void *p; void *p;
if (!vgacon_scrollback.size || c->vc_num != fg_console) if (!vgacon_scrollback_cur->data || !vgacon_scrollback_cur->size ||
c->vc_num != fg_console)
return; return;
p = (void *) (c->vc_origin + t * c->vc_size_row); p = (void *) (c->vc_origin + t * c->vc_size_row);
while (count--) { while (count--) {
scr_memcpyw(vgacon_scrollback.data + vgacon_scrollback.tail, scr_memcpyw(vgacon_scrollback_cur->data +
vgacon_scrollback_cur->tail,
p, c->vc_size_row); p, c->vc_size_row);
vgacon_scrollback.cnt++;
vgacon_scrollback_cur->cnt++;
p += c->vc_size_row; p += c->vc_size_row;
vgacon_scrollback.tail += c->vc_size_row; vgacon_scrollback_cur->tail += c->vc_size_row;
if (vgacon_scrollback.tail >= vgacon_scrollback.size) if (vgacon_scrollback_cur->tail >= vgacon_scrollback_cur->size)
vgacon_scrollback.tail = 0; vgacon_scrollback_cur->tail = 0;
if (vgacon_scrollback.cnt > vgacon_scrollback.rows) if (vgacon_scrollback_cur->cnt > vgacon_scrollback_cur->rows)
vgacon_scrollback.cnt = vgacon_scrollback.rows; vgacon_scrollback_cur->cnt = vgacon_scrollback_cur->rows;
vgacon_scrollback.cur = vgacon_scrollback.cnt; vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt;
} }
} }
static void vgacon_restore_screen(struct vc_data *c) static void vgacon_restore_screen(struct vc_data *c)
{ {
vgacon_scrollback.save = 0; vgacon_scrollback_cur->save = 0;
if (!vga_is_gfx && !vgacon_scrollback.restore) { if (!vga_is_gfx && !vgacon_scrollback_cur->restore) {
scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf,
c->vc_screenbuf_size > vga_vram_size ? c->vc_screenbuf_size > vga_vram_size ?
vga_vram_size : c->vc_screenbuf_size); vga_vram_size : c->vc_screenbuf_size);
vgacon_scrollback.restore = 1; vgacon_scrollback_cur->restore = 1;
vgacon_scrollback.cur = vgacon_scrollback.cnt; vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt;
} }
} }
...@@ -252,41 +288,41 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) ...@@ -252,41 +288,41 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines)
return; return;
} }
if (!vgacon_scrollback.data) if (!vgacon_scrollback_cur->data)
return; return;
if (!vgacon_scrollback.save) { if (!vgacon_scrollback_cur->save) {
vgacon_cursor(c, CM_ERASE); vgacon_cursor(c, CM_ERASE);
vgacon_save_screen(c); vgacon_save_screen(c);
vgacon_scrollback.save = 1; vgacon_scrollback_cur->save = 1;
} }
vgacon_scrollback.restore = 0; vgacon_scrollback_cur->restore = 0;
start = vgacon_scrollback.cur + lines; start = vgacon_scrollback_cur->cur + lines;
end = start + abs(lines); end = start + abs(lines);
if (start < 0) if (start < 0)
start = 0; start = 0;
if (start > vgacon_scrollback.cnt) if (start > vgacon_scrollback_cur->cnt)
start = vgacon_scrollback.cnt; start = vgacon_scrollback_cur->cnt;
if (end < 0) if (end < 0)
end = 0; end = 0;
if (end > vgacon_scrollback.cnt) if (end > vgacon_scrollback_cur->cnt)
end = vgacon_scrollback.cnt; end = vgacon_scrollback_cur->cnt;
vgacon_scrollback.cur = start; vgacon_scrollback_cur->cur = start;
count = end - start; count = end - start;
soff = vgacon_scrollback.tail - ((vgacon_scrollback.cnt - end) * soff = vgacon_scrollback_cur->tail -
c->vc_size_row); ((vgacon_scrollback_cur->cnt - end) * c->vc_size_row);
soff -= count * c->vc_size_row; soff -= count * c->vc_size_row;
if (soff < 0) if (soff < 0)
soff += vgacon_scrollback.size; soff += vgacon_scrollback_cur->size;
count = vgacon_scrollback.cnt - start; count = vgacon_scrollback_cur->cnt - start;
if (count > c->vc_rows) if (count > c->vc_rows)
count = c->vc_rows; count = c->vc_rows;
...@@ -300,13 +336,13 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) ...@@ -300,13 +336,13 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines)
count *= c->vc_size_row; count *= c->vc_size_row;
/* how much memory to end of buffer left? */ /* how much memory to end of buffer left? */
copysize = min(count, vgacon_scrollback.size - soff); copysize = min(count, vgacon_scrollback_cur->size - soff);
scr_memcpyw(d, vgacon_scrollback.data + soff, copysize); scr_memcpyw(d, vgacon_scrollback_cur->data + soff, copysize);
d += copysize; d += copysize;
count -= copysize; count -= copysize;
if (count) { if (count) {
scr_memcpyw(d, vgacon_scrollback.data, count); scr_memcpyw(d, vgacon_scrollback_cur->data, count);
d += count; d += count;
} }
...@@ -320,13 +356,13 @@ static void vgacon_flush_scrollback(struct vc_data *c) ...@@ -320,13 +356,13 @@ static void vgacon_flush_scrollback(struct vc_data *c)
{ {
size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024;
if (c->vc_num == fg_console) vgacon_scrollback_reset(c->vc_num, size);
vgacon_scrollback_reset(size);
} }
#else #else
#define vgacon_scrollback_startup(...) do { } while (0) #define vgacon_scrollback_startup(...) do { } while (0)
#define vgacon_scrollback_init(...) do { } while (0) #define vgacon_scrollback_init(...) do { } while (0)
#define vgacon_scrollback_update(...) do { } while (0) #define vgacon_scrollback_update(...) do { } while (0)
#define vgacon_scrollback_switch(...) do { } while (0)
static void vgacon_restore_screen(struct vc_data *c) static void vgacon_restore_screen(struct vc_data *c)
{ {
...@@ -805,7 +841,7 @@ static int vgacon_switch(struct vc_data *c) ...@@ -805,7 +841,7 @@ static int vgacon_switch(struct vc_data *c)
vgacon_doresize(c, c->vc_cols, c->vc_rows); vgacon_doresize(c, c->vc_cols, c->vc_rows);
} }
vgacon_scrollback_init(c->vc_size_row); vgacon_scrollback_switch(c->vc_num);
return 0; /* Redrawing not needed */ return 0; /* Redrawing not needed */
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment