From 33d9a4cfc20c5ef01abf7d04e4d950235ffb20b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Pe=C4=8Dovnik?= Date: Mon, 21 Sep 2020 19:02:26 +0200 Subject: [PATCH] Rollback Linus changes related to software scrollback support which prevented bootsplash logo to show up (#2219) https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/drivers/video/fbdev/core?h=v5.8.10&id=20782abbbdfe922496a28f9cc0c3c0030f7dfb8f --- lib/compilation-prepare.sh | 12 +- patch/misc/0001-bootsplash-5.8.patch | 746 ------------------ ...n-remove-software-scrollback-support.patch | 338 ++++++++ ...ove-now-unusued-softback_lines-curso.patch | 184 +++++ ...rt-fbcon-remove-soft-scrollback-code.patch | 512 ++++++++++++ 5 files changed, 1042 insertions(+), 750 deletions(-) delete mode 100644 patch/misc/0001-bootsplash-5.8.patch create mode 100644 patch/misc/bootsplash-5.8.10-0001-Revert-vgacon-remove-software-scrollback-support.patch create mode 100644 patch/misc/bootsplash-5.8.10-0002-Revert-fbcon-remove-now-unusued-softback_lines-curso.patch create mode 100644 patch/misc/bootsplash-5.8.10-0003-Revert-fbcon-remove-soft-scrollback-code.patch diff --git a/lib/compilation-prepare.sh b/lib/compilation-prepare.sh index f350ba335..2736c9cd4 100644 --- a/lib/compilation-prepare.sh +++ b/lib/compilation-prepare.sh @@ -78,11 +78,15 @@ compilation_prepare() if linux-version compare "${version}" ge 5.4; then display_alert "Adding" "Kernel splash file" "info" - if linux-version compare "${version}" ge 5.8 && linux-version compare "${version}" lt 5.9 ; then - process_patch_file "${SRC}/patch/misc/0001-bootsplash-5.8.patch" "applying -- assumes patchlevel 5.8.10 or above" - else - process_patch_file "${SRC}/patch/misc/0001-bootsplash.patch" "applying" + + if linux-version compare "${version}" ge 5.8.10; then + + process_patch_file "${SRC}/patch/misc/bootsplash-5.8.10-0001-Revert-vgacon-remove-software-scrollback-support.patch" "applying" + process_patch_file "${SRC}/patch/misc/bootsplash-5.8.10-0002-Revert-fbcon-remove-now-unusued-softback_lines-curso.patch" "applying" + process_patch_file "${SRC}/patch/misc/bootsplash-5.8.10-0003-Revert-fbcon-remove-soft-scrollback-code.patch" "applying" fi + + process_patch_file "${SRC}/patch/misc/0001-bootsplash.patch" "applying" process_patch_file "${SRC}/patch/misc/0002-bootsplash.patch" "applying" process_patch_file "${SRC}/patch/misc/0003-bootsplash.patch" "applying" process_patch_file "${SRC}/patch/misc/0004-bootsplash.patch" "applying" diff --git a/patch/misc/0001-bootsplash-5.8.patch b/patch/misc/0001-bootsplash-5.8.patch deleted file mode 100644 index 4d9d733bf..000000000 --- a/patch/misc/0001-bootsplash-5.8.patch +++ /dev/null @@ -1,746 +0,0 @@ -diff --git a/MAINTAINERS b/MAINTAINERS -index a74227ad082e..b5633b56391e 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -2705,6 +2705,14 @@ S: Supported - F: drivers/net/bonding/ - F: include/uapi/linux/if_bonding.h - -+BOOTSPLASH -+M: Max Staudt -+L: linux-fbdev@vger.kernel.org -+S: Maintained -+F: drivers/video/fbdev/core/bootsplash*.* -+F: drivers/video/fbdev/core/dummycon.c -+F: include/linux/bootsplash.h -+ - BPF (Safe dynamic programs and tools) - M: Alexei Starovoitov - M: Daniel Borkmann -diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig -index 7f1f1fbcef9e..f3ff976266fe 100644 ---- a/drivers/video/console/Kconfig -+++ b/drivers/video/console/Kconfig -@@ -151,6 +151,30 @@ config FRAMEBUFFER_CONSOLE_ROTATION - such that other users of the framebuffer will remain normally - oriented. - -+config BOOTSPLASH -+ bool "Bootup splash screen" -+ depends on FRAMEBUFFER_CONSOLE -+ help -+ This option enables the Linux bootsplash screen. -+ -+ The bootsplash is a full-screen logo or animation indicating a -+ booting system. It replaces the classic scrolling text with a -+ graphical alternative, similar to other systems. -+ -+ Since this is technically implemented as a hook on top of fbcon, -+ it can only work if the FRAMEBUFFER_CONSOLE is enabled and a -+ framebuffer driver is active. Thus, to get a text-free boot, -+ the system needs to boot with vesafb, efifb, or similar. -+ -+ Once built into the kernel, the bootsplash needs to be enabled -+ with bootsplash.enabled=1 and a splash file needs to be supplied. -+ -+ Further documentation can be found in: -+ Documentation/fb/bootsplash.txt -+ -+ If unsure, say N. -+ This is typically used by distributors and system integrators. -+ - config STI_CONSOLE - bool "STI text console" - depends on PARISC -diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile -index 73493bbd7a15..66895321928e 100644 ---- a/drivers/video/fbdev/core/Makefile -+++ b/drivers/video/fbdev/core/Makefile -@@ -29,3 +29,6 @@ obj-$(CONFIG_FB_SYS_IMAGEBLIT) += sysimgblt.o - obj-$(CONFIG_FB_SYS_FOPS) += fb_sys_fops.o - obj-$(CONFIG_FB_SVGALIB) += svgalib.o - obj-$(CONFIG_FB_DDC) += fb_ddc.o -+ -+obj-$(CONFIG_BOOTSPLASH) += bootsplash.o bootsplash_render.o \ -+ dummyblit.o -diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c -new file mode 100644 -index 000000000000..e449755af268 ---- /dev/null -+++ b/drivers/video/fbdev/core/bootsplash.c -@@ -0,0 +1,294 @@ -+/* -+ * Kernel based bootsplash. -+ * -+ * (Main file: Glue code, workers, timer, PM, kernel and userland API) -+ * -+ * Authors: -+ * Max Staudt -+ * -+ * SPDX-License-Identifier: GPL-2.0 -+ */ -+ -+#define pr_fmt(fmt) "bootsplash: " fmt -+ -+ -+#include -+#include -+#include -+#include /* dev_warn() */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include /* console_blanked */ -+#include -+#include -+#include -+#include -+#include -+ -+#include "bootsplash_internal.h" -+ -+ -+/* -+ * We only have one splash screen, so let's keep a single -+ * instance of the internal state. -+ */ -+static struct splash_priv splash_state; -+ -+ -+static void splash_callback_redraw_vc(struct work_struct *ignored) -+{ -+ if (console_blanked) -+ return; -+ -+ console_lock(); -+ if (vc_cons[fg_console].d) -+ update_screen(vc_cons[fg_console].d); -+ console_unlock(); -+} -+ -+ -+static bool is_fb_compatible(const struct fb_info *info) -+{ -+ if (!(info->flags & FBINFO_BE_MATH) -+ != !fb_be_math((struct fb_info *)info)) { -+ dev_warn(info->device, -+ "Can't draw on foreign endianness framebuffer.\n"); -+ -+ return false; -+ } -+ -+ if (info->flags & FBINFO_MISC_TILEBLITTING) { -+ dev_warn(info->device, -+ "Can't draw splash on tiling framebuffer.\n"); -+ -+ return false; -+ } -+ -+ if (info->fix.type != FB_TYPE_PACKED_PIXELS -+ || (info->fix.visual != FB_VISUAL_TRUECOLOR -+ && info->fix.visual != FB_VISUAL_DIRECTCOLOR)) { -+ dev_warn(info->device, -+ "Can't draw splash on non-packed or non-truecolor framebuffer.\n"); -+ -+ dev_warn(info->device, -+ " type: %u visual: %u\n", -+ info->fix.type, info->fix.visual); -+ -+ return false; -+ } -+ -+ if (info->var.bits_per_pixel != 16 -+ && info->var.bits_per_pixel != 24 -+ && info->var.bits_per_pixel != 32) { -+ dev_warn(info->device, -+ "We only support drawing on framebuffers with 16, 24, or 32 bpp, not %d.\n", -+ info->var.bits_per_pixel); -+ -+ return false; -+ } -+ -+ return true; -+} -+ -+ -+/* -+ * Called by fbcon_switch() when an instance is activated or refreshed. -+ */ -+void bootsplash_render_full(struct fb_info *info) -+{ -+ if (!is_fb_compatible(info)) -+ return; -+ -+ bootsplash_do_render_background(info); -+} -+ -+ -+/* -+ * External status enquiry and on/off switch -+ */ -+bool bootsplash_would_render_now(void) -+{ -+ return !oops_in_progress -+ && !console_blanked -+ && bootsplash_is_enabled(); -+} -+ -+bool bootsplash_is_enabled(void) -+{ -+ bool was_enabled; -+ -+ /* Make sure we have the newest state */ -+ smp_rmb(); -+ -+ was_enabled = test_bit(0, &splash_state.enabled); -+ -+ return was_enabled; -+} -+ -+void bootsplash_disable(void) -+{ -+ int was_enabled; -+ -+ was_enabled = test_and_clear_bit(0, &splash_state.enabled); -+ -+ if (was_enabled) { -+ if (oops_in_progress) { -+ /* Redraw screen now so we can see a panic */ -+ if (vc_cons[fg_console].d) -+ update_screen(vc_cons[fg_console].d); -+ } else { -+ /* No urgency, redraw at next opportunity */ -+ schedule_work(&splash_state.work_redraw_vc); -+ } -+ } -+} -+ -+void bootsplash_enable(void) -+{ -+ bool was_enabled; -+ -+ if (oops_in_progress) -+ return; -+ -+ was_enabled = test_and_set_bit(0, &splash_state.enabled); -+ -+ if (!was_enabled) -+ schedule_work(&splash_state.work_redraw_vc); -+} -+ -+ -+/* -+ * Userland API via platform device in sysfs -+ */ -+static ssize_t splash_show_enabled(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ return sprintf(buf, "%d\n", bootsplash_is_enabled()); -+} -+ -+static ssize_t splash_store_enabled(struct device *device, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ bool enable; -+ int err; -+ -+ if (!buf || !count) -+ return -EFAULT; -+ -+ err = kstrtobool(buf, &enable); -+ if (err) -+ return err; -+ -+ if (enable) -+ bootsplash_enable(); -+ else -+ bootsplash_disable(); -+ -+ return count; -+} -+ -+static DEVICE_ATTR(enabled, 0644, splash_show_enabled, splash_store_enabled); -+ -+ -+static struct attribute *splash_dev_attrs[] = { -+ &dev_attr_enabled.attr, -+ NULL -+}; -+ -+ATTRIBUTE_GROUPS(splash_dev); -+ -+ -+ -+ -+/* -+ * Power management fixup via platform device -+ * -+ * When the system is woken from sleep or restored after hibernating, we -+ * cannot expect the screen contents to still be present in video RAM. -+ * Thus, we have to redraw the splash if we're currently active. -+ */ -+static int splash_resume(struct device *device) -+{ -+ if (bootsplash_would_render_now()) -+ schedule_work(&splash_state.work_redraw_vc); -+ -+ return 0; -+} -+ -+static int splash_suspend(struct device *device) -+{ -+ cancel_work_sync(&splash_state.work_redraw_vc); -+ -+ return 0; -+} -+ -+ -+static const struct dev_pm_ops splash_pm_ops = { -+ .thaw = splash_resume, -+ .restore = splash_resume, -+ .resume = splash_resume, -+ .suspend = splash_suspend, -+ .freeze = splash_suspend, -+}; -+ -+static struct platform_driver splash_driver = { -+ .driver = { -+ .name = "bootsplash", -+ .pm = &splash_pm_ops, -+ }, -+}; -+ -+ -+/* -+ * Main init -+ */ -+void bootsplash_init(void) -+{ -+ int ret; -+ -+ /* Initialized already? */ -+ if (splash_state.splash_device) -+ return; -+ -+ -+ /* Register platform device to export user API */ -+ ret = platform_driver_register(&splash_driver); -+ if (ret) { -+ pr_err("platform_driver_register() failed: %d\n", ret); -+ goto err; -+ } -+ -+ splash_state.splash_device -+ = platform_device_alloc("bootsplash", 0); -+ -+ if (!splash_state.splash_device) -+ goto err_driver; -+ -+ splash_state.splash_device->dev.groups = splash_dev_groups; -+ -+ ret = platform_device_add(splash_state.splash_device); -+ if (ret) { -+ pr_err("platform_device_add() failed: %d\n", ret); -+ goto err_device; -+ } -+ -+ -+ INIT_WORK(&splash_state.work_redraw_vc, splash_callback_redraw_vc); -+ -+ return; -+ -+err_device: -+ platform_device_put(splash_state.splash_device); -+ splash_state.splash_device = NULL; -+err_driver: -+ platform_driver_unregister(&splash_driver); -+err: -+ pr_err("Failed to initialize.\n"); -+} -diff --git a/drivers/video/fbdev/core/bootsplash_internal.h b/drivers/video/fbdev/core/bootsplash_internal.h -new file mode 100644 -index 000000000000..b11da5cb90bf ---- /dev/null -+++ b/drivers/video/fbdev/core/bootsplash_internal.h -@@ -0,0 +1,55 @@ -+/* -+ * Kernel based bootsplash. -+ * -+ * (Internal data structures used at runtime) -+ * -+ * Authors: -+ * Max Staudt -+ * -+ * SPDX-License-Identifier: GPL-2.0 -+ */ -+ -+#ifndef __BOOTSPLASH_INTERNAL_H -+#define __BOOTSPLASH_INTERNAL_H -+ -+ -+#include -+#include -+#include -+#include -+#include -+ -+ -+/* -+ * Runtime types -+ */ -+struct splash_priv { -+ /* -+ * Enabled/disabled state, to be used with atomic bit operations. -+ * Bit 0: 0 = Splash hidden -+ * 1 = Splash shown -+ * -+ * Note: fbcon.c uses this twice, by calling -+ * bootsplash_would_render_now() in set_blitting_type() and -+ * in fbcon_switch(). -+ * This is racy, but eventually consistent: Turning the -+ * splash on/off will cause a redraw, which calls -+ * fbcon_switch(), which calls set_blitting_type(). -+ * So the last on/off toggle will make things consistent. -+ */ -+ unsigned long enabled; -+ -+ /* Our gateway to userland via sysfs */ -+ struct platform_device *splash_device; -+ -+ struct work_struct work_redraw_vc; -+}; -+ -+ -+ -+/* -+ * Rendering functions -+ */ -+void bootsplash_do_render_background(struct fb_info *info); -+ -+#endif -diff --git a/drivers/video/fbdev/core/bootsplash_render.c b/drivers/video/fbdev/core/bootsplash_render.c -new file mode 100644 -index 000000000000..4d7e0117f653 ---- /dev/null -+++ b/drivers/video/fbdev/core/bootsplash_render.c -@@ -0,0 +1,93 @@ -+/* -+ * Kernel based bootsplash. -+ * -+ * (Rendering functions) -+ * -+ * Authors: -+ * Max Staudt -+ * -+ * SPDX-License-Identifier: GPL-2.0 -+ */ -+ -+#define pr_fmt(fmt) "bootsplash: " fmt -+ -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include "bootsplash_internal.h" -+ -+ -+ -+ -+/* -+ * Rendering: Internal drawing routines -+ */ -+ -+ -+/* -+ * Pack pixel into target format and do Big/Little Endian handling. -+ * This would be a good place to handle endianness conversion if necessary. -+ */ -+static inline u32 pack_pixel(const struct fb_var_screeninfo *dst_var, -+ u8 red, u8 green, u8 blue) -+{ -+ u32 dstpix; -+ -+ /* Quantize pixel */ -+ red = red >> (8 - dst_var->red.length); -+ green = green >> (8 - dst_var->green.length); -+ blue = blue >> (8 - dst_var->blue.length); -+ -+ /* Pack pixel */ -+ dstpix = red << (dst_var->red.offset) -+ | green << (dst_var->green.offset) -+ | blue << (dst_var->blue.offset); -+ -+ /* -+ * Move packed pixel to the beginning of the memory cell, -+ * so we can memcpy() it out easily -+ */ -+#ifdef __BIG_ENDIAN -+ switch (dst_var->bits_per_pixel) { -+ case 16: -+ dstpix <<= 16; -+ break; -+ case 24: -+ dstpix <<= 8; -+ break; -+ case 32: -+ break; -+ } -+#else -+ /* This is intrinsically unnecessary on Little Endian */ -+#endif -+ -+ return dstpix; -+} -+ -+ -+void bootsplash_do_render_background(struct fb_info *info) -+{ -+ unsigned int x, y; -+ u32 dstpix; -+ u32 dst_octpp = info->var.bits_per_pixel / 8; -+ -+ dstpix = pack_pixel(&info->var, -+ 0, -+ 0, -+ 0); -+ -+ for (y = 0; y < info->var.yres_virtual; y++) { -+ u8 *dstline = info->screen_buffer + (y * info->fix.line_length); -+ -+ for (x = 0; x < info->var.xres_virtual; x++) { -+ memcpy(dstline, &dstpix, dst_octpp); -+ -+ dstline += dst_octpp; -+ } -+ } -+} -diff --git a/drivers/video/fbdev/core/dummyblit.c b/drivers/video/fbdev/core/dummyblit.c -new file mode 100644 -index 000000000000..8c22ff92ce24 ---- /dev/null -+++ b/drivers/video/fbdev/core/dummyblit.c -@@ -0,0 +1,89 @@ -+/* -+ * linux/drivers/video/fbdev/core/dummyblit.c -- Dummy Blitting Operation -+ * -+ * Authors: -+ * Max Staudt -+ * -+ * These functions are used in place of blitblit/tileblit to suppress -+ * fbcon's text output while a splash is shown. -+ * -+ * Only suppressing actual rendering keeps the text buffer in the VC layer -+ * intact and makes it easy to switch back from the bootsplash to a full -+ * text console with a simple redraw (with the original functions in place). -+ * -+ * Based on linux/drivers/video/fbdev/core/bitblit.c -+ * and linux/drivers/video/fbdev/core/tileblit.c -+ * -+ * SPDX-License-Identifier: GPL-2.0 -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include "fbcon.h" -+ -+static void dummy_bmove(struct vc_data *vc, struct fb_info *info, int sy, -+ int sx, int dy, int dx, int height, int width) -+{ -+ ; -+} -+ -+static void dummy_clear(struct vc_data *vc, struct fb_info *info, int sy, -+ int sx, int height, int width) -+{ -+ ; -+} -+ -+static void dummy_putcs(struct vc_data *vc, struct fb_info *info, -+ const unsigned short *s, int count, int yy, int xx, -+ int fg, int bg) -+{ -+ ; -+} -+ -+static void dummy_clear_margins(struct vc_data *vc, struct fb_info *info, -+ int color, int bottom_only) -+{ -+ ; -+} -+ -+static void dummy_cursor(struct vc_data *vc, struct fb_info *info, int mode, -+ int fg, int bg) -+{ -+ ; -+} -+ -+static int dummy_update_start(struct fb_info *info) -+{ -+ /* -+ * Copied from bitblit.c and tileblit.c -+ * -+ * As of Linux 4.12, nobody seems to care about our return value. -+ */ -+ struct fbcon_ops *ops = info->fbcon_par; -+ int err; -+ -+ err = fb_pan_display(info, &ops->var); -+ ops->var.xoffset = info->var.xoffset; -+ ops->var.yoffset = info->var.yoffset; -+ ops->var.vmode = info->var.vmode; -+ return err; -+} -+ -+void fbcon_set_dummyops(struct fbcon_ops *ops) -+{ -+ ops->bmove = dummy_bmove; -+ ops->clear = dummy_clear; -+ ops->putcs = dummy_putcs; -+ ops->clear_margins = dummy_clear_margins; -+ ops->cursor = dummy_cursor; -+ ops->update_start = dummy_update_start; -+ ops->rotate_font = NULL; -+} -+EXPORT_SYMBOL_GPL(fbcon_set_dummyops); -+ -+MODULE_AUTHOR("Max Staudt "); -+MODULE_DESCRIPTION("Dummy Blitting Operation"); -+MODULE_LICENSE("GPL"); -diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c -index 04612f938bab..9a39a6fcfe98 100644 ---- a/drivers/video/fbdev/core/fbcon.c -+++ b/drivers/video/fbdev/core/fbcon.c -@@ -80,6 +80,7 @@ - #include - - #include "fbcon.h" -+#include - - #ifdef FBCONDEBUG - # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) -@@ -542,6 +543,8 @@ static int do_fbcon_takeover(int show_logo) - for (i = first_fb_vc; i <= last_fb_vc; i++) - con2fb_map[i] = info_idx; - -+ bootsplash_init(); -+ - err = do_take_over_console(&fb_con, first_fb_vc, last_fb_vc, - fbcon_is_default); - -@@ -661,6 +664,9 @@ static void set_blitting_type(struct vc_data *vc, struct fb_info *info) - else { - fbcon_set_rotation(info); - fbcon_set_bitops(ops); -+ -+ if (bootsplash_would_render_now()) -+ fbcon_set_dummyops(ops); - } - } - -@@ -683,6 +689,19 @@ static void set_blitting_type(struct vc_data *vc, struct fb_info *info) - ops->p = &fb_display[vc->vc_num]; - fbcon_set_rotation(info); - fbcon_set_bitops(ops); -+ -+ /* -+ * Note: -+ * This is *eventually correct*. -+ * Setting the fbcon operations and drawing the splash happen at -+ * different points in time. If the splash is enabled/disabled -+ * in between, then bootsplash_{en,dis}able will schedule a -+ * redraw, which will again render the splash (or not) and set -+ * the correct fbcon ops. -+ * The last run will then be the right one. -+ */ -+ if (bootsplash_would_render_now()) -+ fbcon_set_dummyops(ops); - } - - static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) -@@ -2184,6 +2203,9 @@ static int fbcon_switch(struct vc_data *vc) - info = registered_fb[con2fb_map[vc->vc_num]]; - ops = info->fbcon_par; - -+ if (bootsplash_would_render_now()) -+ bootsplash_render_full(info); -+ - if (softback_top) { - if (softback_lines) - fbcon_set_origin(vc); -diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h -index 18f3ac144237..45f94347fe5e 100644 ---- a/drivers/video/fbdev/core/fbcon.h -+++ b/drivers/video/fbdev/core/fbcon.h -@@ -214,6 +214,11 @@ static inline int attr_col_ec(int shift, struct vc_data *vc, - #define SCROLL_REDRAW 0x004 - #define SCROLL_PAN_REDRAW 0x005 - -+#ifdef CONFIG_BOOTSPLASH -+extern void fbcon_set_dummyops(struct fbcon_ops *ops); -+#else /* CONFIG_BOOTSPLASH */ -+#define fbcon_set_dummyops(x) -+#endif /* CONFIG_BOOTSPLASH */ - #ifdef CONFIG_FB_TILEBLITTING - extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info); - #endif -diff --git a/include/linux/bootsplash.h b/include/linux/bootsplash.h -new file mode 100644 -index 000000000000..c6dd0b43180d ---- /dev/null -+++ b/include/linux/bootsplash.h -@@ -0,0 +1,43 @@ -+/* -+ * Kernel based bootsplash. -+ * -+ * Authors: -+ * Max Staudt -+ * -+ * SPDX-License-Identifier: GPL-2.0 -+ */ -+ -+#ifndef __LINUX_BOOTSPLASH_H -+#define __LINUX_BOOTSPLASH_H -+ -+#include -+ -+ -+#ifdef CONFIG_BOOTSPLASH -+ -+extern void bootsplash_render_full(struct fb_info *info); -+ -+extern bool bootsplash_would_render_now(void); -+ -+extern bool bootsplash_is_enabled(void); -+extern void bootsplash_disable(void); -+extern void bootsplash_enable(void); -+ -+extern void bootsplash_init(void); -+ -+#else /* CONFIG_BOOTSPLASH */ -+ -+#define bootsplash_render_full(x) -+ -+#define bootsplash_would_render_now() (false) -+ -+#define bootsplash_is_enabled() (false) -+#define bootsplash_disable() -+#define bootsplash_enable() -+ -+#define bootsplash_init() -+ -+#endif /* CONFIG_BOOTSPLASH */ -+ -+ -+#endif diff --git a/patch/misc/bootsplash-5.8.10-0001-Revert-vgacon-remove-software-scrollback-support.patch b/patch/misc/bootsplash-5.8.10-0001-Revert-vgacon-remove-software-scrollback-support.patch new file mode 100644 index 000000000..78fdc2eb5 --- /dev/null +++ b/patch/misc/bootsplash-5.8.10-0001-Revert-vgacon-remove-software-scrollback-support.patch @@ -0,0 +1,338 @@ +From 440d5cecaf940b722f9f78a14db7f1e0bc0f61e8 Mon Sep 17 00:00:00 2001 +From: Igor Pecovnik +Date: Mon, 21 Sep 2020 17:15:09 +0200 +Subject: [PATCH 1/3] Revert "vgacon: remove software scrollback support" + +This reverts commit 20782abbbdfe922496a28f9cc0c3c0030f7dfb8f. +--- + drivers/video/console/Kconfig | 46 ++++++ + drivers/video/console/vgacon.c | 221 +++++++++++++++++++++++++- + 6 files changed, 270 insertions(+), 1 deletion(-) + +diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig +index 39deb22a4180..5e850cc9f891 100644 +--- a/drivers/video/console/Kconfig ++++ b/drivers/video/console/Kconfig +@@ -22,6 +22,52 @@ config VGA_CONSOLE + + Say Y. + ++config VGACON_SOFT_SCROLLBACK ++ bool "Enable Scrollback Buffer in System RAM" ++ depends on VGA_CONSOLE ++ default n ++ help ++ The scrollback buffer of the standard VGA console is located in ++ the VGA RAM. The size of this RAM is fixed and is quite small. ++ If you require a larger scrollback buffer, this can be placed in ++ System RAM which is dynamically allocated during initialization. ++ Placing the scrollback buffer in System RAM will slightly slow ++ down the console. ++ ++ If you want this feature, say 'Y' here and enter the amount of ++ RAM to allocate for this buffer. If unsure, say 'N'. ++ ++config VGACON_SOFT_SCROLLBACK_SIZE ++ int "Scrollback Buffer Size (in KB)" ++ depends on VGACON_SOFT_SCROLLBACK ++ range 1 1024 ++ default "64" ++ help ++ Enter the amount of System RAM to allocate for scrollback ++ buffers of VGA consoles. Each 64KB will give you approximately ++ 16 80x25 screenfuls of scrollback buffer. ++ ++config VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT ++ bool "Persistent Scrollback History for each console by default" ++ depends on VGACON_SOFT_SCROLLBACK ++ default n ++ help ++ Say Y here if the scrollback history should persist by default when ++ switching between consoles. Otherwise, the scrollback history will be ++ flushed each time the console is switched. 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 MDA_CONSOLE + depends on !M68K && !PARISC && ISA + tristate "MDA text console (dual-headed)" +diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c +index 6d0418e88ad7..e9254b3085a3 100644 +--- a/drivers/video/console/vgacon.c ++++ b/drivers/video/console/vgacon.c +@@ -165,6 +165,214 @@ static inline void vga_set_mem_top(struct vc_data *c) + write_vga(12, (c->vc_visible_origin - vga_vram_base) / 2); + } + ++#ifdef CONFIG_VGACON_SOFT_SCROLLBACK ++/* software scrollback */ ++struct vgacon_scrollback_info { ++ void *data; ++ int tail; ++ int size; ++ int rows; ++ int cnt; ++ int cur; ++ int save; ++ int restore; ++}; ++ ++static struct vgacon_scrollback_info *vgacon_scrollback_cur; ++static struct vgacon_scrollback_info vgacon_scrollbacks[MAX_NR_CONSOLES]; ++static bool scrollback_persistent = \ ++ IS_ENABLED(CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT); ++module_param_named(scrollback_persistent, scrollback_persistent, bool, 0000); ++MODULE_PARM_DESC(scrollback_persistent, "Enable persistent scrollback for all vga consoles"); ++ ++static void vgacon_scrollback_reset(int vc_num, size_t reset_size) ++{ ++ struct vgacon_scrollback_info *scrollback = &vgacon_scrollbacks[vc_num]; ++ ++ if (scrollback->data && reset_size > 0) ++ memset(scrollback->data, 0, reset_size); ++ ++ scrollback->cnt = 0; ++ scrollback->tail = 0; ++ scrollback->cur = 0; ++} ++ ++static void vgacon_scrollback_init(int vc_num) ++{ ++ int pitch = vga_video_num_columns * 2; ++ size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; ++ int rows = size / pitch; ++ void *data; ++ ++ data = kmalloc_array(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE, 1024, ++ GFP_NOWAIT); ++ ++ 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) ++{ ++ if (!scrollback_persistent) ++ vc_num = 0; ++ ++ if (!vgacon_scrollbacks[vc_num].data) { ++ vgacon_scrollback_init(vc_num); ++ } else { ++ if (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); ++ } ++ } ++} ++ ++static void vgacon_scrollback_startup(void) ++{ ++ vgacon_scrollback_cur = &vgacon_scrollbacks[0]; ++ vgacon_scrollback_init(0); ++} ++ ++static void vgacon_scrollback_update(struct vc_data *c, int t, int count) ++{ ++ void *p; ++ ++ if (!vgacon_scrollback_cur->data || !vgacon_scrollback_cur->size || ++ c->vc_num != fg_console) ++ return; ++ ++ p = (void *) (c->vc_origin + t * c->vc_size_row); ++ ++ while (count--) { ++ if ((vgacon_scrollback_cur->tail + c->vc_size_row) > ++ vgacon_scrollback_cur->size) ++ vgacon_scrollback_cur->tail = 0; ++ ++ scr_memcpyw(vgacon_scrollback_cur->data + ++ vgacon_scrollback_cur->tail, ++ p, c->vc_size_row); ++ ++ vgacon_scrollback_cur->cnt++; ++ p += c->vc_size_row; ++ vgacon_scrollback_cur->tail += c->vc_size_row; ++ ++ if (vgacon_scrollback_cur->tail >= vgacon_scrollback_cur->size) ++ vgacon_scrollback_cur->tail = 0; ++ ++ if (vgacon_scrollback_cur->cnt > vgacon_scrollback_cur->rows) ++ vgacon_scrollback_cur->cnt = vgacon_scrollback_cur->rows; ++ ++ vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt; ++ } ++} ++ ++static void vgacon_restore_screen(struct vc_data *c) ++{ ++ c->vc_origin = c->vc_visible_origin; ++ vgacon_scrollback_cur->save = 0; ++ ++ if (!vga_is_gfx && !vgacon_scrollback_cur->restore) { ++ scr_memcpyw((u16 *) c->vc_origin, (u16 *) c->vc_screenbuf, ++ c->vc_screenbuf_size > vga_vram_size ? ++ vga_vram_size : c->vc_screenbuf_size); ++ vgacon_scrollback_cur->restore = 1; ++ vgacon_scrollback_cur->cur = vgacon_scrollback_cur->cnt; ++ } ++} ++ ++static void vgacon_scrolldelta(struct vc_data *c, int lines) ++{ ++ int start, end, count, soff; ++ ++ if (!lines) { ++ vgacon_restore_screen(c); ++ return; ++ } ++ ++ if (!vgacon_scrollback_cur->data) ++ return; ++ ++ if (!vgacon_scrollback_cur->save) { ++ vgacon_cursor(c, CM_ERASE); ++ vgacon_save_screen(c); ++ c->vc_origin = (unsigned long)c->vc_screenbuf; ++ vgacon_scrollback_cur->save = 1; ++ } ++ ++ vgacon_scrollback_cur->restore = 0; ++ start = vgacon_scrollback_cur->cur + lines; ++ end = start + abs(lines); ++ ++ if (start < 0) ++ start = 0; ++ ++ if (start > vgacon_scrollback_cur->cnt) ++ start = vgacon_scrollback_cur->cnt; ++ ++ if (end < 0) ++ end = 0; ++ ++ if (end > vgacon_scrollback_cur->cnt) ++ end = vgacon_scrollback_cur->cnt; ++ ++ vgacon_scrollback_cur->cur = start; ++ count = end - start; ++ soff = vgacon_scrollback_cur->tail - ++ ((vgacon_scrollback_cur->cnt - end) * c->vc_size_row); ++ soff -= count * c->vc_size_row; ++ ++ if (soff < 0) ++ soff += vgacon_scrollback_cur->size; ++ ++ count = vgacon_scrollback_cur->cnt - start; ++ ++ if (count > c->vc_rows) ++ count = c->vc_rows; ++ ++ if (count) { ++ int copysize; ++ ++ int diff = c->vc_rows - count; ++ void *d = (void *) c->vc_visible_origin; ++ void *s = (void *) c->vc_screenbuf; ++ ++ count *= c->vc_size_row; ++ /* how much memory to end of buffer left? */ ++ copysize = min(count, vgacon_scrollback_cur->size - soff); ++ scr_memcpyw(d, vgacon_scrollback_cur->data + soff, copysize); ++ d += copysize; ++ count -= copysize; ++ ++ if (count) { ++ scr_memcpyw(d, vgacon_scrollback_cur->data, count); ++ d += count; ++ } ++ ++ if (diff) ++ scr_memcpyw(d, s, diff * c->vc_size_row); ++ } else ++ vgacon_cursor(c, CM_MOVE); ++} ++ ++static void vgacon_flush_scrollback(struct vc_data *c) ++{ ++ size_t size = CONFIG_VGACON_SOFT_SCROLLBACK_SIZE * 1024; ++ ++ vgacon_scrollback_reset(c->vc_num, size); ++} ++#else ++#define vgacon_scrollback_startup(...) do { } while (0) ++#define vgacon_scrollback_init(...) 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) + { + if (c->vc_origin != c->vc_visible_origin) +@@ -178,6 +386,11 @@ static void vgacon_scrolldelta(struct vc_data *c, int lines) + vga_set_mem_top(c); + } + ++static void vgacon_flush_scrollback(struct vc_data *c) ++{ ++} ++#endif /* CONFIG_VGACON_SOFT_SCROLLBACK */ ++ + static const char *vgacon_startup(void) + { + const char *display_desc = NULL; +@@ -360,7 +573,10 @@ static const char *vgacon_startup(void) + vgacon_xres = screen_info.orig_video_cols * VGA_FONTWIDTH; + vgacon_yres = vga_scan_lines; + +- vga_init_done = true; ++ if (!vga_init_done) { ++ vgacon_scrollback_startup(); ++ vga_init_done = true; ++ } + + return display_desc; + } +@@ -651,6 +867,7 @@ static int vgacon_switch(struct vc_data *c) + vgacon_doresize(c, c->vc_cols, c->vc_rows); + } + ++ vgacon_scrollback_switch(c->vc_num); + return 0; /* Redrawing not needed */ + } + +@@ -1167,6 +1384,7 @@ static bool vgacon_scroll(struct vc_data *c, unsigned int t, unsigned int b, + oldo = c->vc_origin; + delta = lines * c->vc_size_row; + if (dir == SM_UP) { ++ vgacon_scrollback_update(c, t, lines); + if (c->vc_scr_end + delta >= vga_vram_end) { + scr_memcpyw((u16 *) vga_vram_base, + (u16 *) (oldo + delta), +@@ -1230,6 +1448,7 @@ const struct consw vga_con = { + .con_save_screen = vgacon_save_screen, + .con_build_attr = vgacon_build_attr, + .con_invert_region = vgacon_invert_region, ++ .con_flush_scrollback = vgacon_flush_scrollback, + }; + EXPORT_SYMBOL(vga_con); + +-- +2.25.1 + diff --git a/patch/misc/bootsplash-5.8.10-0002-Revert-fbcon-remove-now-unusued-softback_lines-curso.patch b/patch/misc/bootsplash-5.8.10-0002-Revert-fbcon-remove-now-unusued-softback_lines-curso.patch new file mode 100644 index 000000000..4a37f2a00 --- /dev/null +++ b/patch/misc/bootsplash-5.8.10-0002-Revert-fbcon-remove-now-unusued-softback_lines-curso.patch @@ -0,0 +1,184 @@ +From 103bdb34be969b844d4854733adc47cdd5d7d236 Mon Sep 17 00:00:00 2001 +From: Igor Pecovnik +Date: Mon, 21 Sep 2020 17:15:30 +0200 +Subject: [PATCH 2/3] Revert "fbcon: remove now unusued 'softback_lines' + cursor() argument" + +This reverts commit ffa74c8e58b8f42b2d95b29443befba2e28fb260. +--- + drivers/video/fbdev/core/bitblit.c | 11 ++++++++++- + drivers/video/fbdev/core/fbcon.c | 4 ++-- + drivers/video/fbdev/core/fbcon.h | 2 +- + drivers/video/fbdev/core/fbcon_ccw.c | 11 ++++++++++- + drivers/video/fbdev/core/fbcon_cw.c | 11 ++++++++++- + drivers/video/fbdev/core/fbcon_ud.c | 11 ++++++++++- + drivers/video/fbdev/core/tileblit.c | 2 +- + 7 files changed, 44 insertions(+), 8 deletions(-) + +diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c +index 436365efae73..35ebeeccde4d 100644 +--- a/drivers/video/fbdev/core/bitblit.c ++++ b/drivers/video/fbdev/core/bitblit.c +@@ -234,7 +234,7 @@ static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, + } + + static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, +- int fg, int bg) ++ int softback_lines, int fg, int bg) + { + struct fb_cursor cursor; + struct fbcon_ops *ops = info->fbcon_par; +@@ -247,6 +247,15 @@ static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, + + cursor.set = 0; + ++ if (softback_lines) { ++ if (y + softback_lines >= vc->vc_rows) { ++ mode = CM_ERASE; ++ ops->cursor_flash = 0; ++ return; ++ } else ++ y += softback_lines; ++ } ++ + c = scr_readw((u16 *) vc->vc_pos); + attribute = get_attribute(info, c); + src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); +diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c +index b36bfe10c712..b30a667663ef 100644 +--- a/drivers/video/fbdev/core/fbcon.c ++++ b/drivers/video/fbdev/core/fbcon.c +@@ -394,7 +394,7 @@ static void fb_flashcursor(struct work_struct *work) + c = scr_readw((u16 *) vc->vc_pos); + mode = (!ops->cursor_flash || ops->cursor_state.enable) ? + CM_ERASE : CM_DRAW; +- ops->cursor(vc, info, mode, get_color(vc, info, c, 1), ++ ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); + console_unlock(); + } +@@ -1345,7 +1345,7 @@ static void fbcon_cursor(struct vc_data *vc, int mode) + + ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; + +- ops->cursor(vc, info, mode, get_color(vc, info, c, 1), ++ ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); + } + +diff --git a/drivers/video/fbdev/core/fbcon.h b/drivers/video/fbdev/core/fbcon.h +index 78bb14c03643..20dea853765f 100644 +--- a/drivers/video/fbdev/core/fbcon.h ++++ b/drivers/video/fbdev/core/fbcon.h +@@ -62,7 +62,7 @@ struct fbcon_ops { + void (*clear_margins)(struct vc_data *vc, struct fb_info *info, + int color, int bottom_only); + void (*cursor)(struct vc_data *vc, struct fb_info *info, int mode, +- int fg, int bg); ++ int softback_lines, int fg, int bg); + int (*update_start)(struct fb_info *info); + int (*rotate_font)(struct fb_info *info, struct vc_data *vc); + struct fb_var_screeninfo var; /* copy of the current fb_var_screeninfo */ +diff --git a/drivers/video/fbdev/core/fbcon_ccw.c b/drivers/video/fbdev/core/fbcon_ccw.c +index 71ad6967a70e..78f3a5621478 100644 +--- a/drivers/video/fbdev/core/fbcon_ccw.c ++++ b/drivers/video/fbdev/core/fbcon_ccw.c +@@ -219,7 +219,7 @@ static void ccw_clear_margins(struct vc_data *vc, struct fb_info *info, + } + + static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, +- int fg, int bg) ++ int softback_lines, int fg, int bg) + { + struct fb_cursor cursor; + struct fbcon_ops *ops = info->fbcon_par; +@@ -236,6 +236,15 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode, + + cursor.set = 0; + ++ if (softback_lines) { ++ if (y + softback_lines >= vc->vc_rows) { ++ mode = CM_ERASE; ++ ops->cursor_flash = 0; ++ return; ++ } else ++ y += softback_lines; ++ } ++ + c = scr_readw((u16 *) vc->vc_pos); + attribute = get_attribute(info, c); + src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); +diff --git a/drivers/video/fbdev/core/fbcon_cw.c b/drivers/video/fbdev/core/fbcon_cw.c +index 31fe5dd651d4..fd098ff17574 100644 +--- a/drivers/video/fbdev/core/fbcon_cw.c ++++ b/drivers/video/fbdev/core/fbcon_cw.c +@@ -202,7 +202,7 @@ static void cw_clear_margins(struct vc_data *vc, struct fb_info *info, + } + + static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, +- int fg, int bg) ++ int softback_lines, int fg, int bg) + { + struct fb_cursor cursor; + struct fbcon_ops *ops = info->fbcon_par; +@@ -219,6 +219,15 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode, + + cursor.set = 0; + ++ if (softback_lines) { ++ if (y + softback_lines >= vc->vc_rows) { ++ mode = CM_ERASE; ++ ops->cursor_flash = 0; ++ return; ++ } else ++ y += softback_lines; ++ } ++ + c = scr_readw((u16 *) vc->vc_pos); + attribute = get_attribute(info, c); + src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.width)); +diff --git a/drivers/video/fbdev/core/fbcon_ud.c b/drivers/video/fbdev/core/fbcon_ud.c +index b2dd1370e39b..e165a3fad29a 100644 +--- a/drivers/video/fbdev/core/fbcon_ud.c ++++ b/drivers/video/fbdev/core/fbcon_ud.c +@@ -249,7 +249,7 @@ static void ud_clear_margins(struct vc_data *vc, struct fb_info *info, + } + + static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, +- int fg, int bg) ++ int softback_lines, int fg, int bg) + { + struct fb_cursor cursor; + struct fbcon_ops *ops = info->fbcon_par; +@@ -267,6 +267,15 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode, + + cursor.set = 0; + ++ if (softback_lines) { ++ if (y + softback_lines >= vc->vc_rows) { ++ mode = CM_ERASE; ++ ops->cursor_flash = 0; ++ return; ++ } else ++ y += softback_lines; ++ } ++ + c = scr_readw((u16 *) vc->vc_pos); + attribute = get_attribute(info, c); + src = ops->fontbuffer + ((c & charmask) * (w * vc->vc_font.height)); +diff --git a/drivers/video/fbdev/core/tileblit.c b/drivers/video/fbdev/core/tileblit.c +index eb664dbf96f6..93390312957f 100644 +--- a/drivers/video/fbdev/core/tileblit.c ++++ b/drivers/video/fbdev/core/tileblit.c +@@ -80,7 +80,7 @@ static void tile_clear_margins(struct vc_data *vc, struct fb_info *info, + } + + static void tile_cursor(struct vc_data *vc, struct fb_info *info, int mode, +- int fg, int bg) ++ int softback_lines, int fg, int bg) + { + struct fb_tilecursor cursor; + int use_sw = (vc->vc_cursor_type & 0x10); +-- +2.25.1 + diff --git a/patch/misc/bootsplash-5.8.10-0003-Revert-fbcon-remove-soft-scrollback-code.patch b/patch/misc/bootsplash-5.8.10-0003-Revert-fbcon-remove-soft-scrollback-code.patch new file mode 100644 index 000000000..4854c0c6d --- /dev/null +++ b/patch/misc/bootsplash-5.8.10-0003-Revert-fbcon-remove-soft-scrollback-code.patch @@ -0,0 +1,512 @@ +From b204d7d23b3d15b338207b37930ebbe0141007ce Mon Sep 17 00:00:00 2001 +From: Igor Pecovnik +Date: Mon, 21 Sep 2020 17:15:44 +0200 +Subject: [PATCH 3/3] Revert "fbcon: remove soft scrollback code" + +This reverts commit 245a228891e3627e47921db1ec1b6612f118158b. +--- + drivers/video/fbdev/core/fbcon.c | 334 ++++++++++++++++++++++++++++++- + 1 file changed, 330 insertions(+), 4 deletions(-) + +diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c +index b30a667663ef..fbf10e62bcde 100644 +--- a/drivers/video/fbdev/core/fbcon.c ++++ b/drivers/video/fbdev/core/fbcon.c +@@ -122,6 +122,12 @@ static int logo_lines; + /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO + enums. */ + static int logo_shown = FBCON_LOGO_CANSHOW; ++/* Software scrollback */ ++static int fbcon_softback_size = 32768; ++static unsigned long softback_buf, softback_curr; ++static unsigned long softback_in; ++static unsigned long softback_top, softback_end; ++static int softback_lines; + /* console mappings */ + static int first_fb_vc; + static int last_fb_vc = MAX_NR_CONSOLES - 1; +@@ -161,6 +167,8 @@ static int margin_color; + + static const struct consw fb_con; + ++#define CM_SOFTBACK (8) ++ + #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) + + static int fbcon_set_origin(struct vc_data *); +@@ -365,6 +373,18 @@ static int get_color(struct vc_data *vc, struct fb_info *info, + return color; + } + ++static void fbcon_update_softback(struct vc_data *vc) ++{ ++ int l = fbcon_softback_size / vc->vc_size_row; ++ ++ if (l > 5) ++ softback_end = softback_buf + l * vc->vc_size_row; ++ else ++ /* Smaller scrollback makes no sense, and 0 would screw ++ the operation totally */ ++ softback_top = 0; ++} ++ + static void fb_flashcursor(struct work_struct *work) + { + struct fb_info *info = container_of(work, struct fb_info, queue); +@@ -394,7 +414,7 @@ static void fb_flashcursor(struct work_struct *work) + c = scr_readw((u16 *) vc->vc_pos); + mode = (!ops->cursor_flash || ops->cursor_state.enable) ? + CM_ERASE : CM_DRAW; +- ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1), ++ ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); + console_unlock(); + } +@@ -451,7 +471,13 @@ static int __init fb_console_setup(char *this_opt) + } + + if (!strncmp(options, "scrollback:", 11)) { +- pr_warn("Ignoring scrollback size option\n"); ++ options += 11; ++ if (*options) { ++ fbcon_softback_size = simple_strtoul(options, &options, 0); ++ if (*options == 'k' || *options == 'K') { ++ fbcon_softback_size *= 1024; ++ } ++ } + continue; + } + +@@ -996,6 +1022,31 @@ static const char *fbcon_startup(void) + + set_blitting_type(vc, info); + ++ if (info->fix.type != FB_TYPE_TEXT) { ++ if (fbcon_softback_size) { ++ if (!softback_buf) { ++ softback_buf = ++ (unsigned long) ++ kvmalloc(fbcon_softback_size, ++ GFP_KERNEL); ++ if (!softback_buf) { ++ fbcon_softback_size = 0; ++ softback_top = 0; ++ } ++ } ++ } else { ++ if (softback_buf) { ++ kvfree((void *) softback_buf); ++ softback_buf = 0; ++ softback_top = 0; ++ } ++ } ++ if (softback_buf) ++ softback_in = softback_top = softback_curr = ++ softback_buf; ++ softback_lines = 0; ++ } ++ + /* Setup default font */ + if (!p->fontdata && !vc->vc_font.data) { + if (!fontname[0] || !(font = find_font(fontname))) +@@ -1169,6 +1220,9 @@ static void fbcon_init(struct vc_data *vc, int init) + if (logo) + fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); + ++ if (vc == svc && softback_buf) ++ fbcon_update_softback(vc); ++ + if (ops->rotate_font && ops->rotate_font(info, vc)) { + ops->rotate = FB_ROTATE_UR; + set_blitting_type(vc, info); +@@ -1331,6 +1385,7 @@ static void fbcon_cursor(struct vc_data *vc, int mode) + { + struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; + struct fbcon_ops *ops = info->fbcon_par; ++ int y; + int c = scr_readw((u16 *) vc->vc_pos); + + ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); +@@ -1344,8 +1399,16 @@ static void fbcon_cursor(struct vc_data *vc, int mode) + fbcon_add_cursor_timer(info); + + ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; ++ if (mode & CM_SOFTBACK) { ++ mode &= ~CM_SOFTBACK; ++ y = softback_lines; ++ } else { ++ if (softback_lines) ++ fbcon_set_origin(vc); ++ y = 0; ++ } + +- ops->cursor(vc, info, mode, 0, get_color(vc, info, c, 1), ++ ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), + get_color(vc, info, c, 0)); + } + +@@ -1416,6 +1479,8 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, + + if (con_is_visible(vc)) { + update_screen(vc); ++ if (softback_buf) ++ fbcon_update_softback(vc); + } + } + +@@ -1553,6 +1618,99 @@ static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) + scrollback_current = 0; + } + ++static void fbcon_redraw_softback(struct vc_data *vc, struct fbcon_display *p, ++ long delta) ++{ ++ int count = vc->vc_rows; ++ unsigned short *d, *s; ++ unsigned long n; ++ int line = 0; ++ ++ d = (u16 *) softback_curr; ++ if (d == (u16 *) softback_in) ++ d = (u16 *) vc->vc_origin; ++ n = softback_curr + delta * vc->vc_size_row; ++ softback_lines -= delta; ++ if (delta < 0) { ++ if (softback_curr < softback_top && n < softback_buf) { ++ n += softback_end - softback_buf; ++ if (n < softback_top) { ++ softback_lines -= ++ (softback_top - n) / vc->vc_size_row; ++ n = softback_top; ++ } ++ } else if (softback_curr >= softback_top ++ && n < softback_top) { ++ softback_lines -= ++ (softback_top - n) / vc->vc_size_row; ++ n = softback_top; ++ } ++ } else { ++ if (softback_curr > softback_in && n >= softback_end) { ++ n += softback_buf - softback_end; ++ if (n > softback_in) { ++ n = softback_in; ++ softback_lines = 0; ++ } ++ } else if (softback_curr <= softback_in && n > softback_in) { ++ n = softback_in; ++ softback_lines = 0; ++ } ++ } ++ if (n == softback_curr) ++ return; ++ softback_curr = n; ++ s = (u16 *) softback_curr; ++ if (s == (u16 *) 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); ++ do { ++ c = scr_readw(s); ++ if (attr != (c & 0xff00)) { ++ attr = c & 0xff00; ++ if (s > start) { ++ fbcon_putcs(vc, start, s - start, ++ line, x); ++ x += s - start; ++ start = s; ++ } ++ } ++ if (c == scr_readw(d)) { ++ if (s > start) { ++ fbcon_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) ++ fbcon_putcs(vc, start, s - start, line, x); ++ line++; ++ if (d == (u16 *) softback_end) ++ d = (u16 *) softback_buf; ++ if (d == (u16 *) softback_in) ++ d = (u16 *) vc->vc_origin; ++ if (s == (u16 *) softback_end) ++ s = (u16 *) softback_buf; ++ if (s == (u16 *) softback_in) ++ s = (u16 *) vc->vc_origin; ++ } ++} ++ + static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, + int line, int count, int dy) + { +@@ -1692,6 +1850,31 @@ static void fbcon_redraw(struct vc_data *vc, struct fbcon_display *p, + } + } + ++static inline void fbcon_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 *) softback_in, p, vc->vc_size_row); ++ count--; ++ p = advance_row(p, 1); ++ softback_in += vc->vc_size_row; ++ if (softback_in == softback_end) ++ softback_in = softback_buf; ++ if (softback_in == softback_top) { ++ softback_top += vc->vc_size_row; ++ if (softback_top == softback_end) ++ softback_top = softback_buf; ++ } ++ } ++ softback_curr = softback_in; ++} ++ + static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, + enum con_scroll dir, unsigned int count) + { +@@ -1714,6 +1897,8 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b, + case SM_UP: + if (count > vc->vc_rows) /* Maximum realistic size */ + count = vc->vc_rows; ++ if (softback_top) ++ fbcon_softback_note(vc, t, count); + if (logo_shown >= 0) + goto redraw_up; + switch (p->scrollmode) { +@@ -2084,6 +2269,14 @@ static int fbcon_switch(struct vc_data *vc) + info = registered_fb[con2fb_map[vc->vc_num]]; + ops = info->fbcon_par; + ++ if (softback_top) { ++ if (softback_lines) ++ fbcon_set_origin(vc); ++ softback_top = softback_curr = softback_in = softback_buf; ++ softback_lines = 0; ++ fbcon_update_softback(vc); ++ } ++ + if (logo_shown >= 0) { + struct vc_data *conp2 = vc_cons[logo_shown].d; + +@@ -2407,6 +2600,9 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, + int cnt; + char *old_data = NULL; + ++ if (con_is_visible(vc) && softback_lines) ++ fbcon_set_origin(vc); ++ + resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); + if (p->userfont) + old_data = vc->vc_font.data; +@@ -2432,6 +2628,8 @@ static int fbcon_do_set_font(struct vc_data *vc, int w, int h, + cols /= w; + rows /= h; + vc_resize(vc, cols, rows); ++ if (con_is_visible(vc) && softback_buf) ++ fbcon_update_softback(vc); + } else if (con_is_visible(vc) + && vc->vc_mode == KD_TEXT) { + fbcon_clear_margins(vc, 0); +@@ -2590,7 +2788,19 @@ static void fbcon_set_palette(struct vc_data *vc, const unsigned char *table) + + static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) + { +- return (u16 *) (vc->vc_origin + offset); ++ unsigned long p; ++ int line; ++ ++ if (vc->vc_num != fg_console || !softback_lines) ++ return (u16 *) (vc->vc_origin + offset); ++ line = offset / vc->vc_size_row; ++ if (line >= softback_lines) ++ return (u16 *) (vc->vc_origin + offset - ++ softback_lines * vc->vc_size_row); ++ p = softback_curr + offset; ++ if (p >= softback_end) ++ p += softback_buf - softback_end; ++ return (u16 *) p; + } + + static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, +@@ -2604,7 +2814,22 @@ static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, + + x = offset % vc->vc_cols; + y = offset / vc->vc_cols; ++ if (vc->vc_num == fg_console) ++ y += softback_lines; + ret = pos + (vc->vc_cols - x) * 2; ++ } else if (vc->vc_num == fg_console && softback_lines) { ++ unsigned long offset = pos - softback_curr; ++ ++ if (pos < softback_curr) ++ offset += softback_end - softback_buf; ++ offset /= 2; ++ x = offset % vc->vc_cols; ++ y = offset / vc->vc_cols; ++ ret = pos + (vc->vc_cols - x) * 2; ++ if (ret == softback_end) ++ ret = softback_buf; ++ if (ret == softback_in) ++ ret = vc->vc_origin; + } else { + /* Should not happen */ + x = y = 0; +@@ -2632,11 +2857,106 @@ static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) + a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | + (((a) & 0x0700) << 4); + scr_writew(a, p++); ++ if (p == (u16 *) softback_end) ++ p = (u16 *) softback_buf; ++ if (p == (u16 *) softback_in) ++ p = (u16 *) vc->vc_origin; ++ } ++} ++ ++static void fbcon_scrolldelta(struct vc_data *vc, int lines) ++{ ++ struct fb_info *info = registered_fb[con2fb_map[fg_console]]; ++ struct fbcon_ops *ops = info->fbcon_par; ++ struct fbcon_display *disp = &fb_display[fg_console]; ++ int offset, limit, scrollback_old; ++ ++ if (softback_top) { ++ if (vc->vc_num != fg_console) ++ return; ++ if (vc->vc_mode != KD_TEXT || !lines) ++ return; ++ 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 = softback_in; ++ q = vc->vc_origin + ++ logo_lines * vc->vc_size_row; ++ for (i = 0; i < logo_lines; i++) { ++ if (p == softback_top) ++ break; ++ if (p == softback_buf) ++ p = softback_end; ++ p -= vc->vc_size_row; ++ q -= vc->vc_size_row; ++ scr_memcpyw((u16 *) q, (u16 *) p, ++ vc->vc_size_row); ++ } ++ softback_in = softback_curr = p; ++ update_region(vc, vc->vc_origin, ++ logo_lines * vc->vc_cols); ++ } ++ logo_shown = FBCON_LOGO_CANSHOW; ++ } ++ fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); ++ fbcon_redraw_softback(vc, disp, lines); ++ fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); ++ return; + } ++ ++ if (!scrollback_phys_max) ++ return; ++ ++ scrollback_old = scrollback_current; ++ scrollback_current -= lines; ++ if (scrollback_current < 0) ++ scrollback_current = 0; ++ else if (scrollback_current > scrollback_max) ++ scrollback_current = scrollback_max; ++ if (scrollback_current == scrollback_old) ++ return; ++ ++ if (fbcon_is_inactive(vc, info)) ++ return; ++ ++ fbcon_cursor(vc, CM_ERASE); ++ ++ offset = disp->yscroll - scrollback_current; ++ limit = disp->vrows; ++ switch (disp->scrollmode) { ++ case SCROLL_WRAP_MOVE: ++ info->var.vmode |= FB_VMODE_YWRAP; ++ break; ++ case SCROLL_PAN_MOVE: ++ case SCROLL_PAN_REDRAW: ++ limit -= vc->vc_rows; ++ info->var.vmode &= ~FB_VMODE_YWRAP; ++ break; ++ } ++ if (offset < 0) ++ offset += limit; ++ else if (offset >= limit) ++ offset -= limit; ++ ++ ops->var.xoffset = 0; ++ ops->var.yoffset = offset * vc->vc_font.height; ++ ops->update_start(info); ++ ++ if (!scrollback_current) ++ fbcon_cursor(vc, CM_DRAW); + } + + static int fbcon_set_origin(struct vc_data *vc) + { ++ if (softback_lines) ++ fbcon_scrolldelta(vc, softback_lines); + return 0; + } + +@@ -2700,6 +3020,8 @@ static void fbcon_modechanged(struct fb_info *info) + + fbcon_set_palette(vc, color_table); + update_screen(vc); ++ if (softback_buf) ++ fbcon_update_softback(vc); + } + } + +@@ -3110,6 +3432,7 @@ static const struct consw fb_con = { + .con_font_default = fbcon_set_def_font, + .con_font_copy = fbcon_copy_font, + .con_set_palette = fbcon_set_palette, ++ .con_scrolldelta = fbcon_scrolldelta, + .con_set_origin = fbcon_set_origin, + .con_invert_region = fbcon_invert_region, + .con_screen_pos = fbcon_screen_pos, +@@ -3344,6 +3667,9 @@ static void fbcon_exit(void) + } + #endif + ++ kvfree((void *)softback_buf); ++ softback_buf = 0UL; ++ + for_each_registered_fb(i) { + int pending = 0; + +-- +2.25.1 +