Add a kmsg option, which will display the last lines of kmsg,
and should be similar to fbcon.
Add a drm.panic_screen module parameter, so you can choose between
the different panic screens available.
two options currently, but more will be added later:
 * "user": a short message telling the user to reboot the machine.
 * "kmsg": fill the screen with the last lines of kmsg.

You can even change it at runtime by writing to
/sys/module/drm/parameters/panic_screen

v2:
 * use module parameter instead of Kconfig choice
   (Javier Martinez Canillas)

Signed-off-by: Jocelyn Falempe <jfale...@redhat.com>
---
 drivers/gpu/drm/Kconfig     |  11 ++++
 drivers/gpu/drm/drm_panic.c | 108 ++++++++++++++++++++++++++++++++----
 2 files changed, 109 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 9703429de6b9..944815cee080 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -137,6 +137,17 @@ config DRM_PANIC_DEBUG
          This is unsafe and should not be enabled on a production build.
          If in doubt, say "N".
 
+config DRM_PANIC_SCREEN
+       string "Panic screen formater"
+       default "user"
+       depends on DRM_PANIC
+       help
+         This option enable to choose what will be displayed when a kernel
+         panic occurs. You can choose between "user", a short message telling
+         the user to reboot the system, or "kmsg" which will display the last
+         lines of kmsg.
+         Default is "user"
+
 config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
         bool "Enable refcount backtrace history in the DP MST helpers"
        depends on STACKTRACE_SUPPORT
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 5dc9e98108ed..2e11273a8ad6 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -12,6 +12,7 @@
 #include <linux/kmsg_dump.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/printk.h>
 #include <linux/types.h>
 
 #include <drm/drm_drv.h>
@@ -27,6 +28,12 @@ MODULE_AUTHOR("Jocelyn Falempe");
 MODULE_DESCRIPTION("DRM panic handler");
 MODULE_LICENSE("GPL");
 
+static char drm_panic_screen[16] = CONFIG_DRM_PANIC_SCREEN;
+module_param_string(panic_screen, drm_panic_screen, sizeof(drm_panic_screen), 
0644);
+MODULE_PARM_DESC(panic_screen,
+                "Choose what will be displayed by drm_panic, 'user' or 'kmsg' 
[default="
+                CONFIG_DRM_PANIC_SCREEN "]");
+
 /**
  * DOC: overview
  *
@@ -437,24 +444,18 @@ static void draw_txt_rectangle(struct drm_scanout_buffer 
*sb,
        }
 }
 
-/*
- * Draw the panic message at the center of the screen
- */
-static void draw_panic_static(struct drm_scanout_buffer *sb)
+static void draw_panic_static_user(struct drm_scanout_buffer *sb)
 {
        size_t msg_lines = ARRAY_SIZE(panic_msg);
        size_t logo_lines = ARRAY_SIZE(logo);
-       u32 fg_color = CONFIG_DRM_PANIC_FOREGROUND_COLOR;
-       u32 bg_color = CONFIG_DRM_PANIC_BACKGROUND_COLOR;
+       u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, 
sb->format->format);
+       u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, 
sb->format->format);
        const struct font_desc *font = get_default_font(sb->width, sb->height, 
NULL, NULL);
        struct drm_rect r_screen, r_logo, r_msg;
 
        if (!font)
                return;
 
-       fg_color = convert_from_xrgb8888(fg_color, sb->format->format);
-       bg_color = convert_from_xrgb8888(bg_color, sb->format->format);
-
        r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
 
        r_logo = DRM_RECT_INIT(0, 0,
@@ -477,6 +478,84 @@ static void draw_panic_static(struct drm_scanout_buffer 
*sb)
        draw_txt_rectangle(sb, font, panic_msg, msg_lines, true, &r_msg, 
fg_color);
 }
 
+
+/*
+ * Draw one line of kmsg, and handle wrapping if it won't fit in the screen 
width.
+ * Return the y-offset of the next line.
+ */
+static int draw_line_with_wrap(struct drm_scanout_buffer *sb, const struct 
font_desc *font,
+                              struct drm_panic_line *line, int yoffset, u32 
fg_color)
+{
+       int chars_per_row = sb->width / font->width;
+       struct drm_rect r_txt = DRM_RECT_INIT(0, yoffset, sb->width, 
sb->height);
+       struct drm_panic_line line_wrap;
+
+       if (line->len > chars_per_row) {
+               line_wrap.len = line->len % chars_per_row;
+               line_wrap.txt = line->txt + line->len - line_wrap.len;
+               draw_txt_rectangle(sb, font, &line_wrap, 1, false, &r_txt, 
fg_color);
+               r_txt.y1 -= font->height;
+               if (r_txt.y1 < 0)
+                       return r_txt.y1;
+               while (line_wrap.txt > line->txt) {
+                       line_wrap.txt -= chars_per_row;
+                       line_wrap.len = chars_per_row;
+                       draw_txt_rectangle(sb, font, &line_wrap, 1, false, 
&r_txt, fg_color);
+                       r_txt.y1 -= font->height;
+                       if (r_txt.y1 < 0)
+                               return r_txt.y1;
+               }
+       } else {
+               draw_txt_rectangle(sb, font, line, 1, false, &r_txt, fg_color);
+               r_txt.y1 -= font->height;
+       }
+       return r_txt.y1;
+}
+
+/*
+ * Draw the kmsg buffer to the screen, starting from the youngest message at 
the bottom,
+ * and going up until reaching the top of the screen.
+ */
+static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
+{
+       u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, 
sb->format->format);
+       u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, 
sb->format->format);
+       const struct font_desc *font = get_default_font(sb->width, sb->height, 
NULL, NULL);
+       struct drm_rect r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
+       struct kmsg_dump_iter iter;
+       char kmsg_buf[512];
+       size_t kmsg_len;
+       struct drm_panic_line line;
+       int yoffset = sb->height - font->height - (sb->height % font->height) / 
2;
+
+       if (!font)
+               return;
+
+       /* Fill with the background color, and draw text on top */
+       drm_panic_fill(sb, &r_screen, bg_color);
+
+       kmsg_dump_rewind(&iter);
+       while (kmsg_dump_get_buffer(&iter, false, kmsg_buf, sizeof(kmsg_buf), 
&kmsg_len)) {
+               char *start;
+               char *end;
+
+               /* ignore terminating NUL and newline */
+               start = kmsg_buf + kmsg_len - 2;
+               end = kmsg_buf + kmsg_len - 1;
+               while (start > kmsg_buf && yoffset >= 0) {
+                       while (start > kmsg_buf && *start != '\n')
+                               start--;
+                       /* don't count the newline character */
+                       line.txt = start + (start == kmsg_buf ? 0 : 1);
+                       line.len = end - line.txt;
+
+                       yoffset = draw_line_with_wrap(sb, font, &line, yoffset, 
fg_color);
+                       end = start;
+                       start--;
+               }
+       }
+}
+
 /*
  * drm_panic_is_format_supported()
  * @format: a fourcc color code
@@ -491,6 +570,15 @@ static bool drm_panic_is_format_supported(const struct 
drm_format_info *format)
        return convert_from_xrgb8888(0xffffff, format->format) != 0;
 }
 
+static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
+{
+       if (!strcmp(drm_panic_screen, "kmsg")) {
+               draw_panic_static_kmsg(sb);
+       } else {
+               draw_panic_static_user(sb);
+       }
+}
+
 static void draw_panic_plane(struct drm_plane *plane)
 {
        struct drm_scanout_buffer sb;
@@ -503,7 +591,7 @@ static void draw_panic_plane(struct drm_plane *plane)
        ret = plane->helper_private->get_scanout_buffer(plane, &sb);
 
        if (!ret && drm_panic_is_format_supported(sb.format)) {
-               draw_panic_static(&sb);
+               draw_panic_dispatch(&sb);
                if (plane->helper_private->panic_flush)
                        plane->helper_private->panic_flush(plane);
        }
-- 
2.45.1

Reply via email to