@@ -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=y
+ ---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
@@ -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
new file mode 100644
@@ -0,0 +1,230 @@
+/*
+ * Kernel based bootsplash.
+ *
+ * (Main file: Glue code, workers, timer, PM, kernel and userland API)
+ *
+ * Authors:
+ * Max Staudt <mstaudt@suse.com>
+ *
+ *
+ * Very loosely based on the old kernel splash project at
+ * http://www.bootsplash.org/ and SUSE improvements.
+ */
+
+#define pr_fmt(fmt) "bootsplash: " fmt
+
+
+#include <linux/bootsplash.h>
+#include <linux/console.h>
+#include <linux/device.h> /* dev_warn() */
+#include <linux/fb.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/selection.h> /* console_blanked */
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/vt_kern.h>
+#include <linux/workqueue.h>
+
+#include "bootsplash_internal.h"
+
+
+
+/* We only have one splash screen, so let's keep a single
+ * global instance of the internal state.
+ */
+struct splash_priv splash_global;
+
+
+
+
+
+/*
+ * Worker callbacks
+ */
+
+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 DECLARE_WORK(splash_work_redraw_vc, splash_callback_redraw_vc);
+
+
+
+
+/*
+ * Rendering: External API
+ */
+
+static bool is_fb_compatible(struct fb_info *info)
+{
+ if (!(info->flags & FBINFO_BE_MATH) != !fb_be_math(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 when an fbcon 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)
+{
+ unsigned long flags;
+ bool was_enabled;
+
+ /* Take the lock as a memory barrier around splash_global.enabled */
+ spin_lock_irqsave(&splash_global.state_lock, flags);
+ was_enabled = splash_global.enabled;
+ spin_unlock_irqrestore(&splash_global.state_lock, flags);
+
+ return was_enabled;
+}
+
+void bootsplash_disable(void)
+{
+ unsigned long flags;
+ bool was_enabled;
+
+ spin_lock_irqsave(&splash_global.state_lock, flags);
+
+ was_enabled = splash_global.enabled;
+ splash_global.enabled = false;
+
+ spin_unlock_irqrestore(&splash_global.state_lock, flags);
+
+
+ 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 */
+ queue_work(splash_global.wq, &splash_work_redraw_vc);
+ }
+ }
+}
+
+void bootsplash_enable(void)
+{
+ unsigned long flags;
+ bool was_enabled;
+
+ if (oops_in_progress)
+ return;
+
+ spin_lock_irqsave(&splash_global.state_lock, flags);
+
+ was_enabled = splash_global.enabled;
+ splash_global.enabled = true;
+
+ spin_unlock_irqrestore(&splash_global.state_lock, flags);
+
+
+ if (!was_enabled)
+ queue_work(splash_global.wq, &splash_work_redraw_vc);
+}
+
+
+
+
+/*
+ * Main init/exit functions
+ */
+
+void bootsplash_init(void)
+{
+ /* Initialized already? */
+ if (splash_global.wq)
+ return;
+
+
+ spin_lock_init(&splash_global.state_lock);
+ if (!splash_global.wq)
+ splash_global.wq = alloc_workqueue("bootsplash",
+ WQ_UNBOUND | WQ_FREEZABLE,
+ 1);
+ if (!splash_global.wq) {
+ pr_err("Failed to allocate work queue.\n");
+ goto err;
+ }
+
+ return;
+
+err:
+ pr_err("Failed to initialize.\n");
+}
+
+
+
+module_param_named(enable, splash_global.enabled, bool, 0444);
+MODULE_PARM_DESC(enable, "Enable/disable kernel bootsplash");
new file mode 100644
@@ -0,0 +1,68 @@
+/*
+ * Kernel based bootsplash.
+ *
+ * (Internal data structures used at runtime)
+ *
+ * Authors:
+ * Max Staudt <mstaudt@suse.com>
+ *
+ *
+ * Very loosely based on the old kernel splash project at
+ * http://www.bootsplash.org/ and SUSE improvements.
+ */
+
+#ifndef __BOOTSPLASH_INTERNAL_H
+#define __BOOTSPLASH_INTERNAL_H
+
+
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+
+
+/*
+ * Runtime types
+ */
+
+struct splash_priv {
+ /* This lock only synchronizes the enabled/disabled state
+ *
+ * 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.
+ */
+ spinlock_t state_lock;
+ bool enabled;
+
+ /* We use our own workqueue so we don't have to worry about blocking
+ * on console_lock() while animating and thus freezing the system
+ * during suspend.
+ */
+ struct workqueue_struct *wq;
+};
+
+
+
+
+/*
+ * Internal global variables
+ */
+extern struct splash_priv splash_global;
+
+
+
+
+/*
+ * Rendering functions
+ */
+
+void bootsplash_do_render_background(struct fb_info *info);
+
+#endif
new file mode 100644
@@ -0,0 +1,93 @@
+/*
+ * Kernel based bootsplash.
+ *
+ * (Rendering functions)
+ *
+ * Authors:
+ * Max Staudt <mstaudt@suse.com>
+ *
+ *
+ * Very loosely based on the old kernel splash project at
+ * http://www.bootsplash.org/ and SUSE improvements.
+ */
+
+#define pr_fmt(fmt) "bootsplash: " fmt
+
+
+#include <linux/bootsplash.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <linux/types.h>
+
+#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(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;
+ }
+ }
+}
new file mode 100644
@@ -0,0 +1,89 @@
+/*
+ * linux/drivers/video/fbdev/core/dummyblit.c -- Dummy Blitting Operation
+ *
+ * Copyright (C) 2017 Max Staudt <mstaudt@suse.de>
+ *
+ * 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
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <asm/types.h>
+#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 softback_lines, 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(fbcon_set_dummyops);
+
+MODULE_AUTHOR("Max Staudt <mstaudt@suse.de>");
+MODULE_DESCRIPTION("Dummy Blitting Operation");
+MODULE_LICENSE("GPL");
@@ -80,6 +80,7 @@
#include <asm/irq.h>
#include "fbcon.h"
+#include <linux/bootsplash.h>
#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,9 @@ 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);
+
+ if (bootsplash_would_render_now())
+ fbcon_set_dummyops(ops);
}
static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount)
@@ -2184,6 +2193,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);
@@ -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
new file mode 100644
@@ -0,0 +1,45 @@
+/*
+ * Kernel based bootsplash.
+ *
+ * Authors:
+ * Max Staudt <mstaudt@suse.com>
+ *
+ *
+ * Very loosely based on the old kernel splash project at
+ * http://www.bootsplash.org/ and SUSE improvements.
+ */
+
+#ifndef __LINUX_BOOTSPLASH_H
+#define __LINUX_BOOTSPLASH_H
+
+#include <linux/fb.h>
+
+
+#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