diff mbox series

[v2,3/3] drm/panic: Add a kmsg panic screen

Message ID 20240603095343.39588-4-jfalempe@redhat.com (mailing list archive)
State New, archived
Headers show
Series drm/panic: Add a kmsg dump screen | expand

Commit Message

Jocelyn Falempe June 3, 2024, 9:47 a.m. UTC
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 <jfalempe@redhat.com>
---
 drivers/gpu/drm/Kconfig     |  11 ++++
 drivers/gpu/drm/drm_panic.c | 108 ++++++++++++++++++++++++++++++++----
 2 files changed, 109 insertions(+), 10 deletions(-)

Comments

Dan Carpenter June 4, 2024, 9:48 a.m. UTC | #1
Hi Jocelyn,

kernel test robot noticed the following build warnings:

url:    https://github.com/intel-lab-lkp/linux/commits/Jocelyn-Falempe/drm-panic-only-draw-the-foreground-color-in-drm_panic_blit/20240603-181247
base:   86266829ea755f737762ebda614c59b136c8feac
patch link:    https://lore.kernel.org/r/20240603095343.39588-4-jfalempe%40redhat.com
patch subject: [PATCH v2 3/3] drm/panic: Add a kmsg panic screen
config: i386-randconfig-141-20240604 (https://download.01.org/0day-ci/archive/20240604/202406041051.KuVqtZCd-lkp@intel.com/config)
compiler: gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202406041051.KuVqtZCd-lkp@intel.com/

smatch warnings:
drivers/gpu/drm/drm_panic.c:531 draw_panic_static_kmsg() warn: variable dereferenced before check 'font' (see line 529)

vim +/font +531 drivers/gpu/drm/drm_panic.c

c259bba1e69ff2 Jocelyn Falempe 2024-06-03  519  static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  520  {
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  521  	u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  522  	u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  523  	const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  524  	struct drm_rect r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  525  	struct kmsg_dump_iter iter;
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  526  	char kmsg_buf[512];
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  527  	size_t kmsg_len;
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  528  	struct drm_panic_line line;
c259bba1e69ff2 Jocelyn Falempe 2024-06-03 @529  	int yoffset = sb->height - font->height - (sb->height % font->height) / 2;
                                                                                   ^^^^^^^^^^^^                 ^^^^^^^^^^^^
Unchecked dereferences

c259bba1e69ff2 Jocelyn Falempe 2024-06-03  530  
c259bba1e69ff2 Jocelyn Falempe 2024-06-03 @531  	if (!font)
                                                             ^^^^
Checked too late

c259bba1e69ff2 Jocelyn Falempe 2024-06-03  532  		return;
c259bba1e69ff2 Jocelyn Falempe 2024-06-03  533
Javier Martinez Canillas June 7, 2024, 9:16 a.m. UTC | #2
Jocelyn Falempe <jfalempe@redhat.com> writes:

> 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
>

Great!

> v2:
>  * use module parameter instead of Kconfig choice
>    (Javier Martinez Canillas)
>
> Signed-off-by: Jocelyn Falempe <jfalempe@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.

Maybe I would mention here that this is only about the default, but that
can be changed using the "drm.panic_screen=" kernel cmdline parameter or
writting to the /sys/module/drm/parameters/panic_screen sysfs entry.

[...]

> +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);

Dan reported that get_default_font() can return 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;
> +

... so you have to calculate yoffset after checking if the font is not NULL.

with that fixed:

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Jocelyn Falempe June 10, 2024, 12:10 p.m. UTC | #3
On 07/06/2024 11:16, Javier Martinez Canillas wrote:
> Jocelyn Falempe <jfalempe@redhat.com> writes:
> 
>> 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
>>
> 
> Great!
> 
>> v2:
>>   * use module parameter instead of Kconfig choice
>>     (Javier Martinez Canillas)
>>
>> Signed-off-by: Jocelyn Falempe <jfalempe@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.
> 
> Maybe I would mention here that this is only about the default, but that
> can be changed using the "drm.panic_screen=" kernel cmdline parameter or
> writting to the /sys/module/drm/parameters/panic_screen sysfs entry.
> 
> [...]

Done
> 
>> +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);
> 
> Dan reported that get_default_font() can return 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;
>> +
> 
> ... so you have to calculate yoffset after checking if the font is not NULL.

Yes I fixed that too.

> 
> with that fixed:
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

Thanks a lot.

I just pushed this series to drm-misc-next.

Best regards,
diff mbox series

Patch

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);
 	}