* [PATCH 01/12] drm/panic: Allocate QR-code buffers statically
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
@ 2026-08-18 12:27 ` Thomas Zimmermann
2026-08-18 13:09 ` sashiko-bot
2026-08-18 12:28 ` [PATCH 02/12] drm/panic: Make allocation of zlib workspace more robust Thomas Zimmermann
` (10 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:27 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Declare qrbuf1 and qrbuf2 as static arrays so that the module loader
allocates them for us. Avoids the kmalloc later on. Also allows for
using sizeof() to get the number of bytes in each array. Access the
arrays once with memset, so that the physical pages are available on
a panic.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 41 ++++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index e576c4791861..0685f7d8dba2 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -628,24 +628,23 @@ MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR code");
#define WINDOW_BITS 12
#define MEM_LEVEL 4
-static char *qrbuf1;
-static char *qrbuf2;
+static u8 qrbuf1[QR_BUFFER1_SIZE];
+static u8 qrbuf2[QR_BUFFER2_SIZE];
static struct z_stream_s stream;
static void __init drm_panic_qr_init(void)
{
- qrbuf1 = kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL);
- qrbuf2 = kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL);
+ /* best-effort allocation; can be NULL */
stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
GFP_KERNEL);
+
+ /* touch memory so that pages are there in the case of a panic */
+ memset(qrbuf1, 0, sizeof(qrbuf1));
+ memset(qrbuf2, 0, sizeof(qrbuf2));
}
static void drm_panic_qr_exit(void)
{
- kfree(qrbuf1);
- qrbuf1 = NULL;
- kfree(qrbuf2);
- qrbuf2 = NULL;
kfree(stream.workspace);
stream.workspace = NULL;
}
@@ -656,13 +655,17 @@ static int drm_panic_get_qr_code_url(u8 **qr_image)
char url[256];
size_t kmsg_len, max_kmsg_size;
char *kmsg;
- int max_qr_data_size, url_len;
+ int ret;
+ size_t max_qr_data_size, url_len;
- url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=",
- utsname()->machine, utsname()->release);
+ ret = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=",
+ utsname()->machine, utsname()->release);
+ if (ret >= sizeof(url))
+ return -EINVAL;
+ url_len = ret;
max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len);
- max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, QR_BUFFER1_SIZE);
+ max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, sizeof(qrbuf1));
/* get kmsg to buffer 1 */
kmsg_dump_rewind(&iter);
@@ -681,7 +684,7 @@ static int drm_panic_get_qr_code_url(u8 **qr_image)
stream.avail_in = kmsg_len;
stream.total_in = 0;
stream.next_out = qrbuf2;
- stream.avail_out = QR_BUFFER2_SIZE;
+ stream.avail_out = sizeof(qrbuf2);
stream.total_out = 0;
if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END)
@@ -703,8 +706,8 @@ static int drm_panic_get_qr_code_url(u8 **qr_image)
*qr_image = qrbuf2;
/* generate qr code image in buffer2 */
- return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SIZE,
- qrbuf1, QR_BUFFER1_SIZE);
+ return drm_panic_qr_generate(url, qrbuf2, stream.total_out, sizeof(qrbuf2),
+ qrbuf1, sizeof(qrbuf1));
}
static int drm_panic_get_qr_code_raw(u8 **qr_image)
@@ -712,7 +715,7 @@ static int drm_panic_get_qr_code_raw(u8 **qr_image)
struct kmsg_dump_iter iter;
size_t kmsg_len;
size_t max_kmsg_size = min(drm_panic_qr_max_data_size(panic_qr_version, 0),
- QR_BUFFER1_SIZE);
+ sizeof(qrbuf1));
kmsg_dump_rewind(&iter);
kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
@@ -720,8 +723,8 @@ static int drm_panic_get_qr_code_raw(u8 **qr_image)
return -ENODATA;
*qr_image = qrbuf1;
- return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, QR_BUFFER1_SIZE,
- qrbuf2, QR_BUFFER2_SIZE);
+ return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, sizeof(qrbuf1),
+ qrbuf2, sizeof(qrbuf2));
}
static int drm_panic_get_qr_code(u8 **qr_image)
@@ -748,7 +751,7 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
int qr_width, qr_canvas_width, qr_pitch, v_margin;
u8 *qr_image;
- if (!font || !qrbuf1 || !qrbuf2 || !stream.workspace)
+ if (!font || !stream.workspace)
return -ENOMEM;
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 01/12] drm/panic: Allocate QR-code buffers statically
2026-08-18 12:27 ` [PATCH 01/12] drm/panic: Allocate QR-code buffers statically Thomas Zimmermann
@ 2026-08-18 13:09 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:09 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: imx, dri-devel, ojeda, Frank.Li, intel-xe, linux-hyperv,
intel-gfx, wsa+renesas
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] This problem wasn't introduced by this patch, but using the `CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL` Kconfig macro directly as part of the format string in `snprintf` creates a format string vulnerability. If the configured URL contains URL-encoded characters that form valid format specifiers (e.g., `?reason=kernel%20panic`, where `%20p` is a format specifier for a padded pointer), `snprintf` will consume unintended arguments. This shifts the argument mapping for the explicit `%s` specifiers, causing them to read and dereference uninitialized stack memory, leading to a nested kernel oops during a panic.
--
--- Patch [1]: [PATCH 01/12] drm/panic: Allocate QR-code buffers statically ---
commit 8f4057e44664878e84dd3de301552d418b19254c
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Allocate QR-code buffers statically
Declare qrbuf1 and qrbuf2 as static arrays so that the module loader
allocates them for us. Avoids the kmalloc later on. Access the arrays
once with memset, so that the physical pages are available on a panic.
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
[ ... ]
> @@ -656,13 +655,17 @@ static int drm_panic_get_qr_code_url(u8 **qr_image)
> char url[256];
> size_t kmsg_len, max_kmsg_size;
> char *kmsg;
> - int max_qr_data_size, url_len;
> + int ret;
> + size_t max_qr_data_size, url_len;
>
> - url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=",
> - utsname()->machine, utsname()->release);
> + ret = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=",
> + utsname()->machine, utsname()->release);
[Severity: High]
This isn't a bug introduced by this patch, but does constructing the
format string directly from CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL create
a format string vulnerability in drm_panic_get_qr_code_url()?
If a system builder configures the url string with standard url-encoded
characters that happen to match valid format specifiers (such as %20p
or %20d), snprintf() will try to consume unintended arguments.
Could this shift the argument mapping for the explicit %s specifiers,
causing them to dereference uninitialized stack memory and trigger a
nested oops that prevents the panic screen from displaying?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 02/12] drm/panic: Make allocation of zlib workspace more robust
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
2026-08-18 12:27 ` [PATCH 01/12] drm/panic: Allocate QR-code buffers statically Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 12:28 ` [PATCH 03/12] drm/panic: Return -EINVAL if font is not available Thomas Zimmermann
` (9 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Test the zlib workspace size to not have a negative value. This
would be a driver bug. Also touch allocated memory so that the
pages are available upon a panic.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 0685f7d8dba2..705b72bf0d83 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -634,13 +634,19 @@ static struct z_stream_s stream;
static void __init drm_panic_qr_init(void)
{
+ int wssize;
+
+ wssize = zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL);
+ if (WARN_ON_ONCE(wssize < 0))
+ return;
/* best-effort allocation; can be NULL */
- stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
- GFP_KERNEL);
+ stream.workspace = kmalloc(wssize, GFP_KERNEL);
/* touch memory so that pages are there in the case of a panic */
memset(qrbuf1, 0, sizeof(qrbuf1));
memset(qrbuf2, 0, sizeof(qrbuf2));
+ if (stream.workspace)
+ memset(stream.workspace, 0, wssize);
}
static void drm_panic_qr_exit(void)
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 03/12] drm/panic: Return -EINVAL if font is not available
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
2026-08-18 12:27 ` [PATCH 01/12] drm/panic: Allocate QR-code buffers statically Thomas Zimmermann
2026-08-18 12:28 ` [PATCH 02/12] drm/panic: Make allocation of zlib workspace more robust Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:04 ` sashiko-bot
2026-08-18 12:28 ` [PATCH 04/12] drm/panic: Return errno codes if panic output fails Thomas Zimmermann
` (8 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Font lookup does not allocate memory. Hence reporting ENOMEM on
failed lookups is misleading. Report EINVAL instead.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 705b72bf0d83..96d238bfceee 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -757,8 +757,10 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
int qr_width, qr_canvas_width, qr_pitch, v_margin;
u8 *qr_image;
- if (!font || !stream.workspace)
+ if (!stream.workspace)
return -ENOMEM;
+ if (!font)
+ return -EINVAL;
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 03/12] drm/panic: Return -EINVAL if font is not available
2026-08-18 12:28 ` [PATCH 03/12] drm/panic: Return -EINVAL if font is not available Thomas Zimmermann
@ 2026-08-18 13:04 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:04 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: intel-xe, ojeda, Frank.Li, intel-gfx, imx, wsa+renesas,
linux-hyperv, dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing NULL check for the result of `kmap_local_page_try_from_panic()` in `drm_panic_write_pixel24_xpage()` leads to a NULL pointer dereference.
--
--- Patch [3]: [PATCH 03/12] drm/panic: Return -EINVAL if font is not available ---
commit 5ad93562bf6ffaf36ef771a5c8eebc2aa43f5621
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Return -EINVAL if font is not available
Font lookup does not allocate memory. Hence reporting ENOMEM on
failed lookups is misleading. Report EINVAL instead.
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> index 705b72bf0d839..96d238bfceee9 100644
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
[ ... ]
> static void drm_panic_write_pixel24_xpage(void *vaddr, struct page *next_page,
> unsigned int offset, u32 color)
> {
> u8 *vaddr2;
> u8 *p = vaddr + offset;
>
> vaddr2 = kmap_local_page_try_from_panic(next_page);
>
> *p++ = color & 0xff;
> color >>= 8;
>
> if (offset == PAGE_SIZE - 1)
> p = vaddr2;
>
> *p++ = color & 0xff;
[Severity: High]
This isn't a bug introduced by this patch, but does
drm_panic_write_pixel24_xpage() lack a NULL check for vaddr2?
If the next page is located in HighMem, kmap_local_page_try_from_panic()
returns NULL since it is unsafe to map HighMem during a panic.
When vaddr2 is NULL and the offset crosses the page boundary
(PAGE_SIZE - 1 or PAGE_SIZE - 2), the write pointer p is set to vaddr2.
Would the subsequent write operation then dereference NULL, causing a
recursive panic that aborts the DRM panic handler?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=3
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 04/12] drm/panic: Return errno codes if panic output fails
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (2 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 03/12] drm/panic: Return -EINVAL if font is not available Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:03 ` sashiko-bot
2026-08-18 12:28 ` [PATCH 05/12] drm/panic: Pass colors to draw_panic_dispatch() Thomas Zimmermann
` (7 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Return errno codes from the panic output helpers to detect invalid
panic handling. Avoid flushing the display if an error ocured. The
unflushed display output might be helpful in debugging.
For now, test the result values in the panic test cases. A later patch
will add support for retrying failed panic output.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 41 +++++++++++++++++---------
drivers/gpu/drm/tests/drm_panic_test.c | 16 ++++++----
2 files changed, 37 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 96d238bfceee..594235661710 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -478,7 +478,7 @@ static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *
fg_color);
}
-static void draw_panic_screen_user(struct drm_scanout_buffer *sb)
+static int draw_panic_screen_user(struct drm_scanout_buffer *sb)
{
u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
sb->format->format);
@@ -489,7 +489,7 @@ static void draw_panic_screen_user(struct drm_scanout_buffer *sb)
unsigned int msg_width, msg_height;
if (!font)
- return;
+ return -EINVAL;
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
drm_panic_logo_rect(&r_logo, font);
@@ -508,6 +508,8 @@ static void draw_panic_screen_user(struct drm_scanout_buffer *sb)
drm_panic_logo_draw(sb, &r_logo, font, fg_color);
draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
+
+ return 0;
}
/*
@@ -547,7 +549,7 @@ static int draw_line_with_wrap(struct drm_scanout_buffer *sb, const struct font_
* 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_screen_kmsg(struct drm_scanout_buffer *sb)
+static int draw_panic_screen_kmsg(struct drm_scanout_buffer *sb)
{
u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
sb->format->format);
@@ -562,7 +564,7 @@ static void draw_panic_screen_kmsg(struct drm_scanout_buffer *sb)
int yoffset;
if (!font || font->width > sb->width)
- return;
+ return -EINVAL;
yoffset = sb->height - font->height - (sb->height % font->height) / 2;
@@ -589,6 +591,8 @@ static void draw_panic_screen_kmsg(struct drm_scanout_buffer *sb)
start--;
}
}
+
+ return 0;
}
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
@@ -814,10 +818,11 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
return 0;
}
-static void draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
+static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
{
if (_draw_panic_screen_qr_code(sb))
draw_panic_screen_user(sb);
+ return 0;
}
#else
static void drm_panic_qr_init(void) {};
@@ -888,23 +893,25 @@ static bool drm_panic_is_format_supported(const struct drm_format_info *format)
return drm_draw_can_convert_from_xrgb8888(format->format);
}
-static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
+static int draw_panic_dispatch(struct drm_scanout_buffer *sb)
{
+ int ret;
+
switch (drm_panic_type) {
case DRM_PANIC_TYPE_KMSG:
- draw_panic_screen_kmsg(sb);
+ ret = draw_panic_screen_kmsg(sb);
break;
-
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
case DRM_PANIC_TYPE_QR:
- draw_panic_screen_qr_code(sb);
+ ret = draw_panic_screen_qr_code(sb);
break;
#endif
-
case DRM_PANIC_TYPE_USER:
default:
- draw_panic_screen_user(sb);
+ ret = draw_panic_screen_user(sb);
}
+
+ return ret;
}
static void drm_panic_set_description(const char *description)
@@ -951,9 +958,15 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
drm_panic_set_description(description);
- draw_panic_dispatch(&sb);
- if (plane->helper_private->panic_flush)
- plane->helper_private->panic_flush(plane);
+ ret = draw_panic_dispatch(&sb);
+ if (!ret) {
+ /*
+ * Only flush if we have a panic screen to display. Otherwise
+ * it's probably better to leave the display output as-is.
+ */
+ if (plane->helper_private->panic_flush)
+ plane->helper_private->panic_flush(plane);
+ }
drm_panic_clear_description();
diff --git a/drivers/gpu/drm/tests/drm_panic_test.c b/drivers/gpu/drm/tests/drm_panic_test.c
index ad2f3a2f93b6..fdd77b0cc54c 100644
--- a/drivers/gpu/drm/tests/drm_panic_test.c
+++ b/drivers/gpu/drm/tests/drm_panic_test.c
@@ -30,7 +30,7 @@ struct drm_test_mode {
const int width;
const int height;
const u32 format;
- void (*draw_screen)(struct drm_scanout_buffer *sb);
+ int (*draw_screen)(struct drm_scanout_buffer *sb);
const char *fname;
};
@@ -87,7 +87,7 @@ static void drm_test_panic_screen_user_map(struct kunit *test)
const struct drm_test_mode *params = test->param_value;
char *fb;
int fb_size;
- int i;
+ int i, ret;
sb->format = drm_format_info(params->format);
fb_size = params->width * params->height * sb->format->cpp[0];
@@ -102,7 +102,8 @@ static void drm_test_panic_screen_user_map(struct kunit *test)
sb->height = params->height;
sb->pitch[0] = params->width * sb->format->cpp[0];
- params->draw_screen(sb);
+ ret = params->draw_screen(sb);
+ KUNIT_ASSERT_EQ(test, ret, 0);
for (i = 0; i < fb_size; i++)
drm_panic_check_color_byte(test, fb[i]);
@@ -119,7 +120,7 @@ static void drm_test_panic_screen_user_page(struct kunit *test)
{
struct drm_scanout_buffer *sb = test->priv;
const struct drm_test_mode *params = test->param_value;
- int fb_size, p, i, npages;
+ int fb_size, p, i, npages, ret;
struct page **pages;
u8 *vaddr;
@@ -146,7 +147,8 @@ static void drm_test_panic_screen_user_page(struct kunit *test)
sb->height = params->height;
sb->pitch[0] = params->width * sb->format->cpp[0];
- params->draw_screen(sb);
+ ret = params->draw_screen(sb);
+ KUNIT_ASSERT_EQ(test, ret, 0);
for (p = 0; p < npages; p++) {
int bytes_in_page = (p == npages - 1) ? fb_size - p * PAGE_SIZE : PAGE_SIZE;
@@ -182,6 +184,7 @@ static void drm_test_panic_screen_user_set_pixel(struct kunit *test)
{
struct drm_scanout_buffer *sb = test->priv;
const struct drm_test_mode *params = test->param_value;
+ int ret;
sb->format = drm_format_info(params->format);
sb->set_pixel = drm_test_panic_set_pixel;
@@ -189,7 +192,8 @@ static void drm_test_panic_screen_user_set_pixel(struct kunit *test)
sb->height = params->height;
sb->private = test;
- params->draw_screen(sb);
+ ret = params->draw_screen(sb);
+ KUNIT_ASSERT_EQ(test, ret, 0);
}
static void drm_test_panic_desc(const struct drm_test_mode *t, char *desc)
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 04/12] drm/panic: Return errno codes if panic output fails
2026-08-18 12:28 ` [PATCH 04/12] drm/panic: Return errno codes if panic output fails Thomas Zimmermann
@ 2026-08-18 13:03 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:03 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: imx, Frank.Li, wsa+renesas, dri-devel, linux-hyperv, intel-gfx,
intel-xe, ojeda
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] draw_panic_screen_qr_code() ignores the return value of its fallback function draw_panic_screen_user() and unconditionally returns 0.
--
--- Patch [4]: [PATCH 04/12] drm/panic: Return errno codes if panic output fails ---
commit 8764682ca919f839ac40d09290f5a67bb03da8dc
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Return errno codes if panic output fails
This commit updates the panic output helpers to return errno codes on
failure. This allows the driver to avoid flushing the display if an error
occurs, preserving the unflushed display output for debugging.
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
[ ... ]
> @@ -814,10 +818,11 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
> return 0;
> }
>
> -static void draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
> +static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
> {
> if (_draw_panic_screen_qr_code(sb))
> draw_panic_screen_user(sb);
> + return 0;
[Severity: Medium]
Should this return the error code from draw_panic_screen_user() if the
fallback also fails?
Because this unconditionally returns 0, draw_panic_dispatch() will report
success even if both _draw_panic_screen_qr_code() and the fallback fail.
This causes draw_panic_plane() to still flush the display buffer, which
might overwrite the unflushed output this patch aims to preserve.
> }
> #else
> static void drm_panic_qr_init(void) {};
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=4
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 05/12] drm/panic: Pass colors to draw_panic_dispatch()
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (3 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 04/12] drm/panic: Return errno codes if panic output fails Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 12:28 ` [PATCH 06/12] drm/panic: Pass global module parameters to drm_panic_dispatch() Thomas Zimmermann
` (6 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Set the panic-screen colors in draw_panic_plane() as a single point
of truth. Pass the values into the individual drawing functions.
In the test cases, replace the Kconfig-defined colors with white and
black. Allows for removal of the tests' limitations and dependency on
the exact values. All tests now always verify the generated output.
Hardcoding the test colors will later also allow for building the
test case without having DRM panic handling enabled.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 54 +++++++++++++++-----------
drivers/gpu/drm/tests/drm_panic_test.c | 26 +++++++++----
2 files changed, 49 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 594235661710..ede620b27515 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -478,12 +478,8 @@ static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *
fg_color);
}
-static int draw_panic_screen_user(struct drm_scanout_buffer *sb)
+static int draw_panic_screen_user(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
{
- u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
- sb->format->format);
- u32 bg_color = drm_draw_color_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;
unsigned int msg_width, msg_height;
@@ -491,6 +487,9 @@ static int draw_panic_screen_user(struct drm_scanout_buffer *sb)
if (!font)
return -EINVAL;
+ fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
+ bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
+
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
drm_panic_logo_rect(&r_logo, font);
@@ -549,12 +548,8 @@ static int draw_line_with_wrap(struct drm_scanout_buffer *sb, const struct font_
* 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 int draw_panic_screen_kmsg(struct drm_scanout_buffer *sb)
+static int draw_panic_screen_kmsg(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
{
- u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
- sb->format->format);
- u32 bg_color = drm_draw_color_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;
@@ -566,6 +561,9 @@ static int draw_panic_screen_kmsg(struct drm_scanout_buffer *sb)
if (!font || font->width > sb->width)
return -EINVAL;
+ fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
+ bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
+
yoffset = sb->height - font->height - (sb->height % font->height) / 2;
/* Fill with the background color, and draw text on top */
@@ -748,12 +746,9 @@ static int drm_panic_get_qr_code(u8 **qr_image)
/*
* Draw the panic message at the center of the screen, with a QR Code
*/
-static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
+static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color)
{
- u32 fg_color = drm_draw_color_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR,
- sb->format->format);
- u32 bg_color = drm_draw_color_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, r_qr, r_qr_canvas;
unsigned int max_qr_size, scale;
@@ -766,6 +761,9 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
if (!font)
return -EINVAL;
+ fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
+ bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
+
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
drm_panic_logo_rect(&r_logo, font);
@@ -818,10 +816,10 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
return 0;
}
-static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb)
+static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
{
- if (_draw_panic_screen_qr_code(sb))
- draw_panic_screen_user(sb);
+ if (_draw_panic_screen_qr_code(sb, fg_color, bg_color))
+ draw_panic_screen_user(sb, fg_color, bg_color);
return 0;
}
#else
@@ -893,22 +891,22 @@ static bool drm_panic_is_format_supported(const struct drm_format_info *format)
return drm_draw_can_convert_from_xrgb8888(format->format);
}
-static int draw_panic_dispatch(struct drm_scanout_buffer *sb)
+static int draw_panic_dispatch(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
{
int ret;
switch (drm_panic_type) {
case DRM_PANIC_TYPE_KMSG:
- ret = draw_panic_screen_kmsg(sb);
+ ret = draw_panic_screen_kmsg(sb, fg_color, bg_color);
break;
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
case DRM_PANIC_TYPE_QR:
- ret = draw_panic_screen_qr_code(sb);
+ ret = draw_panic_screen_qr_code(sb, fg_color, bg_color);
break;
#endif
case DRM_PANIC_TYPE_USER:
default:
- ret = draw_panic_screen_user(sb);
+ ret = draw_panic_screen_user(sb, fg_color, bg_color);
}
return ret;
@@ -943,6 +941,16 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
struct drm_scanout_buffer sb = { };
int ret;
unsigned long flags;
+#if defined(CONFIG_DRM_PANIC_FOREGROUND_COLOR)
+ u32 fg_color = CONFIG_DRM_PANIC_FOREGROUND_COLOR;
+#else
+ u32 fg_color = 0x00ffffff;
+#endif
+#if defined(CONFIG_DRM_PANIC_BACKGROUND_COLOR)
+ u32 bg_color = CONFIG_DRM_PANIC_BACKGROUND_COLOR;
+#else
+ u32 bg_color = 0x00000000;
+#endif
if (!drm_panic_trylock(plane->dev, flags))
return;
@@ -958,7 +966,7 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
drm_panic_set_description(description);
- ret = draw_panic_dispatch(&sb);
+ ret = draw_panic_dispatch(&sb, fg_color, bg_color);
if (!ret) {
/*
* Only flush if we have a panic screen to display. Otherwise
diff --git a/drivers/gpu/drm/tests/drm_panic_test.c b/drivers/gpu/drm/tests/drm_panic_test.c
index fdd77b0cc54c..969a096ec840 100644
--- a/drivers/gpu/drm/tests/drm_panic_test.c
+++ b/drivers/gpu/drm/tests/drm_panic_test.c
@@ -14,17 +14,10 @@
#include <linux/units.h>
#include <linux/vmalloc.h>
-/* Check the framebuffer color only if the panic colors are the default */
-#if (CONFIG_DRM_PANIC_BACKGROUND_COLOR == 0 && \
- CONFIG_DRM_PANIC_FOREGROUND_COLOR == 0xffffff)
-
static void drm_panic_check_color_byte(struct kunit *test, u8 b)
{
KUNIT_EXPECT_TRUE(test, (b == 0 || b == 0xff));
}
-#else
-static void drm_panic_check_color_byte(struct kunit *test, u8 b) {}
-#endif
struct drm_test_mode {
const int width;
@@ -48,10 +41,27 @@ struct drm_test_mode {
.width = w, \
.height = h, \
.format = f, \
- .draw_screen = draw_panic_screen_##name, \
+ .draw_screen = draw_panic_screen_ ## name ## _default, \
.fname = #name, \
}, \
+static int draw_panic_screen_user_default(struct drm_scanout_buffer *sb)
+{
+ return draw_panic_screen_user(sb, 0x00ffffff, 0x00000000);
+}
+
+static int draw_panic_screen_kmsg_default(struct drm_scanout_buffer *sb)
+{
+ return draw_panic_screen_kmsg(sb, 0x00ffffff, 0x00000000);
+}
+
+#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
+static int draw_panic_screen_qr_code_default(struct drm_scanout_buffer *sb)
+{
+ return draw_panic_screen_qr_code(sb, 0x00ffffff, 0x00000000);
+}
+#endif
+
static const struct drm_test_mode drm_test_modes_cases[] = {
DRM_TEST_MODE_LIST(user)
DRM_TEST_MODE_LIST(kmsg)
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 06/12] drm/panic: Pass global module parameters to drm_panic_dispatch()
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (4 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 05/12] drm/panic: Pass colors to draw_panic_dispatch() Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:09 ` sashiko-bot
2026-08-18 12:28 ` [PATCH 07/12] drm/panic: Retry in dispatch function if panic output fails Thomas Zimmermann
` (5 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Set the panic type and QR version in draw_panic_plane() as a single
point of truth. Pass the values into the individual drawing functions.
In the test case, always create a QR code with version 40 (i.e., the
highest supported version).
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 39 ++++++++++++++++----------
drivers/gpu/drm/tests/drm_panic_test.c | 2 +-
2 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index ede620b27515..12c39217cbcb 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -657,7 +657,7 @@ static void drm_panic_qr_exit(void)
stream.workspace = NULL;
}
-static int drm_panic_get_qr_code_url(u8 **qr_image)
+static int drm_panic_get_qr_code_url(u8 **qr_image, unsigned int qr_version)
{
struct kmsg_dump_iter iter;
char url[256];
@@ -672,7 +672,7 @@ static int drm_panic_get_qr_code_url(u8 **qr_image)
return -EINVAL;
url_len = ret;
- max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len);
+ max_qr_data_size = drm_panic_qr_max_data_size(qr_version, url_len);
max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, sizeof(qrbuf1));
/* get kmsg to buffer 1 */
@@ -718,11 +718,11 @@ static int drm_panic_get_qr_code_url(u8 **qr_image)
qrbuf1, sizeof(qrbuf1));
}
-static int drm_panic_get_qr_code_raw(u8 **qr_image)
+static int drm_panic_get_qr_code_raw(u8 **qr_image, unsigned int qr_version)
{
struct kmsg_dump_iter iter;
size_t kmsg_len;
- size_t max_kmsg_size = min(drm_panic_qr_max_data_size(panic_qr_version, 0),
+ size_t max_kmsg_size = min(drm_panic_qr_max_data_size(qr_version, 0),
sizeof(qrbuf1));
kmsg_dump_rewind(&iter);
@@ -735,19 +735,20 @@ static int drm_panic_get_qr_code_raw(u8 **qr_image)
qrbuf2, sizeof(qrbuf2));
}
-static int drm_panic_get_qr_code(u8 **qr_image)
+static int drm_panic_get_qr_code(u8 **qr_image, unsigned int qr_version)
{
if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0)
- return drm_panic_get_qr_code_url(qr_image);
+ return drm_panic_get_qr_code_url(qr_image, qr_version);
else
- return drm_panic_get_qr_code_raw(qr_image);
+ return drm_panic_get_qr_code_raw(qr_image, qr_version);
}
/*
* Draw the panic message at the center of the screen, with a QR Code
*/
static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
- u32 fg_color, u32 bg_color)
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version)
{
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
@@ -774,7 +775,7 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4);
- qr_width = drm_panic_get_qr_code(&qr_image);
+ qr_width = drm_panic_get_qr_code(&qr_image, qr_version);
if (qr_width <= 0)
return -ENOSPC;
@@ -816,9 +817,11 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
return 0;
}
-static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
+static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version)
{
- if (_draw_panic_screen_qr_code(sb, fg_color, bg_color))
+ if (_draw_panic_screen_qr_code(sb, fg_color, bg_color, qr_version))
draw_panic_screen_user(sb, fg_color, bg_color);
return 0;
}
@@ -891,17 +894,18 @@ static bool drm_panic_is_format_supported(const struct drm_format_info *format)
return drm_draw_can_convert_from_xrgb8888(format->format);
}
-static int draw_panic_dispatch(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
+static int draw_panic_dispatch(struct drm_scanout_buffer *sb, enum drm_panic_type panic_type,
+ u32 fg_color, u32 bg_color, unsigned int qr_version)
{
int ret;
- switch (drm_panic_type) {
+ switch (panic_type) {
case DRM_PANIC_TYPE_KMSG:
ret = draw_panic_screen_kmsg(sb, fg_color, bg_color);
break;
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
case DRM_PANIC_TYPE_QR:
- ret = draw_panic_screen_qr_code(sb, fg_color, bg_color);
+ ret = draw_panic_screen_qr_code(sb, fg_color, bg_color, qr_version);
break;
#endif
case DRM_PANIC_TYPE_USER:
@@ -951,6 +955,11 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
#else
u32 bg_color = 0x00000000;
#endif
+#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
+ unsigned int qr_version = panic_qr_version;
+#else
+ unsigned int qr_version = 0;
+#endif
if (!drm_panic_trylock(plane->dev, flags))
return;
@@ -966,7 +975,7 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
drm_panic_set_description(description);
- ret = draw_panic_dispatch(&sb, fg_color, bg_color);
+ ret = draw_panic_dispatch(&sb, drm_panic_type, fg_color, bg_color, qr_version);
if (!ret) {
/*
* Only flush if we have a panic screen to display. Otherwise
diff --git a/drivers/gpu/drm/tests/drm_panic_test.c b/drivers/gpu/drm/tests/drm_panic_test.c
index 969a096ec840..196496238d23 100644
--- a/drivers/gpu/drm/tests/drm_panic_test.c
+++ b/drivers/gpu/drm/tests/drm_panic_test.c
@@ -58,7 +58,7 @@ static int draw_panic_screen_kmsg_default(struct drm_scanout_buffer *sb)
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
static int draw_panic_screen_qr_code_default(struct drm_scanout_buffer *sb)
{
- return draw_panic_screen_qr_code(sb, 0x00ffffff, 0x00000000);
+ return draw_panic_screen_qr_code(sb, 0x00ffffff, 0x00000000, 40);
}
#endif
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 06/12] drm/panic: Pass global module parameters to drm_panic_dispatch()
2026-08-18 12:28 ` [PATCH 06/12] drm/panic: Pass global module parameters to drm_panic_dispatch() Thomas Zimmermann
@ 2026-08-18 13:09 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:09 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: intel-xe, ojeda, linux-hyperv, Frank.Li, intel-gfx, wsa+renesas,
dri-devel, imx
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Concurrent debugfs writes or KUnit tests across multiple DRM devices race on global static buffers without adequate synchronization.
--
--- Patch [6]: [PATCH 06/12] drm/panic: Pass global module parameters to drm_panic_dispatch() ---
commit 9f2a1b5158feac614c09a17c83f387591642e034
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Pass global module parameters to drm_panic_dispatch()
This patch sets the panic type and QR version in draw_panic_plane() as a single
point of truth, passing these values into the individual drawing functions.
In the test case, it always creates a QR code with version 40.
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> index ede620b27515a..12c39217cbcb5 100644
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
[ ... ]
> @@ -951,6 +955,11 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
> #else
> u32 bg_color = 0x00000000;
> #endif
> +#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
> + unsigned int qr_version = panic_qr_version;
> +#else
> + unsigned int qr_version = 0;
> +#endif
>
> if (!drm_panic_trylock(plane->dev, flags))
> return;
> @@ -966,7 +975,7 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
>
> drm_panic_set_description(description);
>
> - ret = draw_panic_dispatch(&sb, fg_color, bg_color);
> + ret = draw_panic_dispatch(&sb, drm_panic_type, fg_color, bg_color, qr_version);
[Severity: High]
This is a pre-existing issue, but does the per-device locking in
drm_panic_trylock() adequately protect the global buffers?
Since the lock is per-device (plane->dev->mode_config.panic_lock), concurrent
debugfs writes across multiple DRM devices can still race and mutate global
static variables, such as the zlib stream workspace (struct z_stream_s stream)
or qrbuf1 and qrbuf2.
If a real kernel panic occurs while a debugfs write is concurrently using these
buffers, the true panic handler could inherit a corrupted zlib workspace state,
which might lead to a recursive fault and failure to display the panic screen.
Could this be protected by a global lock for non-panic paths?
[ ... ]
> diff --git a/drivers/gpu/drm/tests/drm_panic_test.c b/drivers/gpu/drm/tests/drm_panic_test.c
> index 969a096ec840f..196496238d231 100644
> --- a/drivers/gpu/drm/tests/drm_panic_test.c
> +++ b/drivers/gpu/drm/tests/drm_panic_test.c
> @@ -58,7 +58,7 @@ static int draw_panic_screen_kmsg_default(struct drm_scanout_buffer *sb)
> #if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
> static int draw_panic_screen_qr_code_default(struct drm_scanout_buffer *sb)
> {
> - return draw_panic_screen_qr_code(sb, 0x00ffffff, 0x00000000);
> + return draw_panic_screen_qr_code(sb, 0x00ffffff, 0x00000000, 40);
> }
> #endif
[Severity: High]
This isn't a bug introduced by this patch, but do the KUnit tests safely access
the global drawing state?
It appears the KUnit tests bypass draw_panic_plane() entirely and directly call
drawing functions like draw_panic_screen_qr_code() without acquiring any locks.
Could running the tests race on the global buffers and corrupt the zlib
workspace?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=6
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 07/12] drm/panic: Retry in dispatch function if panic output fails
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (5 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 06/12] drm/panic: Pass global module parameters to drm_panic_dispatch() Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:05 ` sashiko-bot
2026-08-18 12:28 ` [PATCH 08/12] drm/panic: Split draw_panic_plane() Thomas Zimmermann
` (4 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
On failed output of QR codes and kmsg buffers, fall back to a user
message. QR-code output already did this internally, but now formally
support it in drm_panic_dispatch(). Streamlines the existing logic.
Also pass module parameter drm_panic_type as a parameter to
drm_panic_dispatch(). This simpifies the implementation. It will later
allow for moving the function into a helper library.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 12c39217cbcb..0960750bfce2 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -746,9 +746,9 @@ static int drm_panic_get_qr_code(u8 **qr_image, unsigned int qr_version)
/*
* Draw the panic message at the center of the screen, with a QR Code
*/
-static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
- u32 fg_color, u32 bg_color,
- unsigned int qr_version)
+static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version)
{
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
@@ -816,15 +816,6 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
return 0;
}
-
-static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
- u32 fg_color, u32 bg_color,
- unsigned int qr_version)
-{
- if (_draw_panic_screen_qr_code(sb, fg_color, bg_color, qr_version))
- draw_panic_screen_user(sb, fg_color, bg_color);
- return 0;
-}
#else
static void drm_panic_qr_init(void) {};
static void drm_panic_qr_exit(void) {};
@@ -899,13 +890,22 @@ static int draw_panic_dispatch(struct drm_scanout_buffer *sb, enum drm_panic_typ
{
int ret;
+retry:
switch (panic_type) {
case DRM_PANIC_TYPE_KMSG:
ret = draw_panic_screen_kmsg(sb, fg_color, bg_color);
+ if (ret) {
+ panic_type = DRM_PANIC_TYPE_USER;
+ goto retry;
+ }
break;
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
case DRM_PANIC_TYPE_QR:
ret = draw_panic_screen_qr_code(sb, fg_color, bg_color, qr_version);
+ if (ret) {
+ panic_type = DRM_PANIC_TYPE_USER;
+ goto retry;
+ }
break;
#endif
case DRM_PANIC_TYPE_USER:
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 07/12] drm/panic: Retry in dispatch function if panic output fails
2026-08-18 12:28 ` [PATCH 07/12] drm/panic: Retry in dispatch function if panic output fails Thomas Zimmermann
@ 2026-08-18 13:05 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:05 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: linux-hyperv, intel-gfx, Frank.Li, imx, intel-xe, ojeda,
dri-devel, wsa+renesas
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Removing the internal fallback from `draw_panic_screen_qr_code` breaks the `drm_panic` KUnit tests.
--
--- Patch [7]: [PATCH 07/12] drm/panic: Retry in dispatch function if panic output fails ---
commit 23392eb57457ad383d9c4741f6fc2f3c107aabb3
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Retry in dispatch function if panic output fails
On failed output of QR codes and kmsg buffers, fall back to a user
message. QR-code output already did this internally, but now formally
support it in drm_panic_dispatch(). Streamlines the existing logic.
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
[ ... ]
> @@ -816,15 +816,6 @@ static int _draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
> drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
> return 0;
> }
> -
> -static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
> - u32 fg_color, u32 bg_color,
> - unsigned int qr_version)
> -{
> - if (_draw_panic_screen_qr_code(sb, fg_color, bg_color, qr_version))
> - draw_panic_screen_user(sb, fg_color, bg_color);
> - return 0;
> -}
[Severity: Medium]
Does removing this fallback cause the drm_panic KUnit tests to fail?
Looking at the test suite in drivers/gpu/drm/tests/drm_panic_test.c, it
calls this function directly without going through the new dispatch logic:
static int draw_panic_screen_qr_code_default(struct drm_scanout_buffer *sb)
{
return draw_panic_screen_qr_code(sb, 0x00ffffff, 0x00000000, 40);
}
When tested with a small resolution like 300x200, the scale evaluates to 0
and returns -ENOSPC. Previously, this wrapper caught the error, fell back
to draw_panic_screen_user, and safely returned 0, satisfying the KUnit
check.
Since the fallback was moved to draw_panic_dispatch and the test calls
the lower-level function directly, will this now propagate the -ENOSPC
error and spuriously fail the test?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=7
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 08/12] drm/panic: Split draw_panic_plane()
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (6 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 07/12] drm/panic: Retry in dispatch function if panic output fails Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:14 ` sashiko-bot
2026-08-18 12:28 ` [PATCH 09/12] drm/panic: Display panic screen via per-plane callback Thomas Zimmermann
` (3 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Move locking and parameters from draw_panic_plane() into the new
helper drm_panic_display_panic_screen(). Call draw_panic_plane() from
there.
The new helper drm_panic_display_panic_screen() is now the DRM core's
interface for displaying a panic screen. The code remaining in
draw_panic_plane() still does all the heavy lifting. It will become a
plane helper for DRM drivers.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_panic.c | 60 ++++++++++++++++++++-----------------
1 file changed, 33 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 0960750bfce2..2263a11efdf6 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -940,38 +940,21 @@ static void drm_panic_clear_description(void)
desc_line->txt = NULL;
}
-static void draw_panic_plane(struct drm_plane *plane, const char *description)
+static void draw_panic_plane(struct drm_plane *plane, const char *description,
+ enum drm_panic_type panic_type, u32 fg_color, u32 bg_color,
+ unsigned int qr_version)
{
struct drm_scanout_buffer sb = { };
int ret;
- unsigned long flags;
-#if defined(CONFIG_DRM_PANIC_FOREGROUND_COLOR)
- u32 fg_color = CONFIG_DRM_PANIC_FOREGROUND_COLOR;
-#else
- u32 fg_color = 0x00ffffff;
-#endif
-#if defined(CONFIG_DRM_PANIC_BACKGROUND_COLOR)
- u32 bg_color = CONFIG_DRM_PANIC_BACKGROUND_COLOR;
-#else
- u32 bg_color = 0x00000000;
-#endif
-#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
- unsigned int qr_version = panic_qr_version;
-#else
- unsigned int qr_version = 0;
-#endif
-
- if (!drm_panic_trylock(plane->dev, flags))
- return;
ret = plane->helper_private->get_scanout_buffer(plane, &sb);
if (ret || !drm_panic_is_format_supported(sb.format))
- goto unlock;
+ return;
/* One of these should be set, or it can't draw pixels */
if (!sb.set_pixel && !sb.pages && iosys_map_is_null(&sb.map[0]))
- goto unlock;
+ return;
drm_panic_set_description(description);
@@ -986,9 +969,33 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description)
}
drm_panic_clear_description();
+}
-unlock:
- drm_panic_unlock(plane->dev, flags);
+static void drm_panic_display_panic_screen(struct drm_plane *plane, const char *description)
+{
+#if defined(CONFIG_DRM_PANIC_FOREGROUND_COLOR)
+ u32 fg_color = CONFIG_DRM_PANIC_FOREGROUND_COLOR;
+#else
+ u32 fg_color = 0x00ffffff;
+#endif
+#if defined(CONFIG_DRM_PANIC_BACKGROUND_COLOR)
+ u32 bg_color = CONFIG_DRM_PANIC_BACKGROUND_COLOR;
+#else
+ u32 bg_color = 0x00000000;
+#endif
+#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
+ unsigned int qr_version = panic_qr_version;
+#else
+ unsigned int qr_version = 0;
+#endif
+ struct drm_device *dev = plane->dev;
+ unsigned long flags;
+
+ if (drm_panic_trylock(dev, flags)) {
+ draw_panic_plane(plane, description, drm_panic_type,
+ fg_color, bg_color, qr_version);
+ drm_panic_unlock(dev, flags);
+ }
}
static struct drm_plane *to_drm_plane(struct kmsg_dumper *kd)
@@ -1001,10 +1008,9 @@ static void drm_panic(struct kmsg_dumper *dumper, struct kmsg_dump_detail *detai
struct drm_plane *plane = to_drm_plane(dumper);
if (detail->reason == KMSG_DUMP_PANIC)
- draw_panic_plane(plane, detail->description);
+ drm_panic_display_panic_screen(plane, detail->description);
}
-
/*
* DEBUG FS, This is currently unsafe.
* Create one file per plane, so it's possible to debug one plane at a time.
@@ -1021,7 +1027,7 @@ static ssize_t debugfs_trigger_write(struct file *file, const char __user *user_
if (kstrtobool_from_user(user_buf, count, &run) == 0 && run) {
struct drm_plane *plane = file->private_data;
- draw_panic_plane(plane, "Test from debugfs");
+ drm_panic_display_panic_screen(plane, "Test from debugfs");
}
return count;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 08/12] drm/panic: Split draw_panic_plane()
2026-08-18 12:28 ` [PATCH 08/12] drm/panic: Split draw_panic_plane() Thomas Zimmermann
@ 2026-08-18 13:14 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:14 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: intel-xe, wsa+renesas, dri-devel, Frank.Li, ojeda, imx,
linux-hyperv, intel-gfx
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Medium] The `panic_type` parameter in `draw_panic_plane()` is ignored and shadowed by the global `drm_panic_type` variable.
Pre-existing issues:
- [High] Data race on global variables (`panic_msg`, `qrbuf1`, `qrbuf2`, `stream`) when triggering panic debugfs interface concurrently across multiple DRM devices.
--
--- Patch [8]: [PATCH 08/12] drm/panic: Split draw_panic_plane() ---
commit 87af97baf7c5108af8275ef63007ab9f483466ca
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Split draw_panic_plane()
Move locking and parameters from draw_panic_plane() into the new
helper drm_panic_display_panic_screen(). Call draw_panic_plane() from
there. The new helper is now the DRM core's interface for displaying a
panic screen.
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> index 0960750bfce25..2263a11efdf63 100644
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
> @@ -940,38 +940,21 @@ static void drm_panic_clear_description(void)
> desc_line->txt = NULL;
> }
>
> -static void draw_panic_plane(struct drm_plane *plane, const char *description)
> +static void draw_panic_plane(struct drm_plane *plane, const char *description,
> + enum drm_panic_type panic_type, u32 fg_color, u32 bg_color,
> + unsigned int qr_version)
> {
[Severity: Medium]
Does this function ignore the new panic_type parameter?
The parameter is passed to draw_panic_plane(), but if the code still uses the
global drm_panic_type variable internally when calling draw_panic_dispatch():
ret = draw_panic_dispatch(&sb, drm_panic_type, fg_color, bg_color, qr_version);
Any caller passing a specific panic_type will have its request overridden by
the global module parameter, which seems to break the intent of making this a
generic helper.
[ ... ]
> @@ -1021,7 +1027,7 @@ static ssize_t debugfs_trigger_write(struct file *file, const char __user *user_
> if (kstrtobool_from_user(user_buf, count, &run) == 0 && run) {
> struct drm_plane *plane = file->private_data;
>
> - draw_panic_plane(plane, "Test from debugfs");
> + drm_panic_display_panic_screen(plane, "Test from debugfs");
[Severity: High]
This is a pre-existing issue, but does this debugfs entry point allow a data
race on global variables across different DRM devices?
When a user writes concurrently to the debugfs files of two or more distinct
DRM devices, the debugfs_trigger_write() handler acquires a per-device lock
via drm_panic_trylock().
However, these concurrent writes can proceed to mutate the unprotected global
panic_msg array in drm_panic_set_description():
struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
desc_line->txt = description;
This could also affect other globals like qrbuf1, qrbuf2, and the zlib
stream workspace.
Could this concurrent execution across different DRM devices corrupt memory
and lead to an inconsistent state?
> }
> return count;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=8
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 09/12] drm/panic: Display panic screen via per-plane callback
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (7 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 08/12] drm/panic: Split draw_panic_plane() Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:19 ` sashiko-bot
2026-08-18 12:28 ` [PATCH 10/12] drm/panic: Internalize panic locking in DRM core and helpers Thomas Zimmermann
` (2 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Add display_panic_screen to struct drm_plane_funcs. If set on a plane,
register a panic handler for the plane from the DRM core's panic
handling. Invoke this helper from the core to display a panic screen.
The new callback replaces get_scanout_buffer as signal for panic
support in a plane. Using get_scanout_buffer is now merely a detail
of the implementation. This will also free struct drm_scanout_buffer
for use cases besides panic handling.
With display_panic_screen in place, rename draw_panic_plane() to
drm_plane_helper_display_panic_screen() and make it available to DRM
drivers via drm_panic_helper.h. Do not change the implementation or
functionality of this code. drm_plane_helper_display_panic_screen()
still supports kernel messages, user messages and QR codes on various
color formats.
Then update all drivers that implement panic support. Provide the
initializer macro DRM_PANIC_PLANE_FUNCS that sets display_panic_screen
to drm_plane_helper_display_panic_screen(). If panic handling has
been disabled, it instead clears the callback. There's again no change
in functionality.
This logically splits panic handling into a set of entry points in the
DRM code and a set of helper functions provided by drivers. The core
invokes the driver's helper via callback, while the driver can tailor
the implementation to its needs. Panic handling now works like most
other interfaces in the DRM framework.
Locking and state-handling happens in the DRM core or helpers. Drivers
that receive a call to display_panic_screen can assume that no concurrent
modeset takes place on the plane or its CRTC.
Also update the documentation. Move information about locking and state
guarantees from drm_panic_trylock() to display_panic_screen, where
drivers authors can find it easily. Move information about the features
of the current implementation to drm_panic_helper_display_panic_screen().
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
MAINTAINERS | 3 +-
.../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 2 +
drivers/gpu/drm/ast/ast_mode.c | 2 +
drivers/gpu/drm/drm_panic.c | 78 ++++++++++---------
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 2 +
drivers/gpu/drm/i915/display/i9xx_plane.c | 3 +
.../drm/i915/display/skl_universal_plane.c | 4 +
drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c | 2 +
drivers/gpu/drm/mgag200/mgag200_drv.h | 4 +-
drivers/gpu/drm/nouveau/dispnv50/wndw.c | 2 +
.../gpu/drm/renesas/rcar-du/rcar_du_plane.c | 2 +
.../drm/renesas/shmobile/shmob_drm_plane.c | 2 +
drivers/gpu/drm/sysfb/drm_sysfb_helper.h | 4 +-
drivers/gpu/drm/sysfb/vesadrm.c | 1 +
drivers/gpu/drm/tidss/tidss_plane.c | 2 +
drivers/gpu/drm/tiny/bochs.c | 4 +-
drivers/gpu/drm/virtio/virtgpu_plane.c | 2 +
include/drm/drm_mode_config.h | 4 +-
include/drm/drm_modeset_helper_vtables.h | 11 ++-
include/drm/drm_panic.h | 49 +++---------
include/drm/drm_panic_helper.h | 27 +++++++
include/drm/drm_plane.h | 57 ++++++++++++++
22 files changed, 180 insertions(+), 87 deletions(-)
create mode 100644 include/drm/drm_panic_helper.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 928b3ba23a76..472c5c48729d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9020,7 +9020,8 @@ F: drivers/gpu/drm/drm_draw.c
F: drivers/gpu/drm/drm_draw_internal.h
F: drivers/gpu/drm/drm_panic*.c
F: drivers/gpu/drm/tests/drm_panic_test.c
-F: include/drm/drm_panic*
+F: include/drm/drm_panic.h
+F: include/drm/drm_panic_helper.h
DRM PANIC QR CODE
M: Jocelyn Falempe <jfalempe@redhat.com>
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
index 824ef3ce5de0..eadc22a255bb 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
@@ -28,6 +28,7 @@
#include <drm/drm_blend.h>
#include "drm/drm_framebuffer.h"
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_fourcc.h>
@@ -2177,6 +2178,7 @@ static const struct drm_plane_funcs dm_plane_funcs = {
.atomic_set_property = dm_atomic_plane_set_property,
.atomic_get_property = dm_atomic_plane_get_property,
#endif
+ DRM_PANIC_PLANE_FUNCS,
};
int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index d5ed8c5c7925..ceebf9421100 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -43,6 +43,7 @@
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_managed.h>
#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -652,6 +653,7 @@ static const struct drm_plane_funcs ast_primary_plane_funcs = {
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
DRM_GEM_SHADOW_PLANE_FUNCS,
+ DRM_PANIC_PLANE_FUNCS,
};
static int ast_primary_plane_init(struct ast_device *ast)
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 2263a11efdf6..2ecb4d1f6d47 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -28,6 +28,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
#include <drm/drm_rect.h>
@@ -42,32 +43,18 @@ MODULE_LICENSE("GPL");
/**
* DOC: overview
*
- * To enable DRM panic for a driver, the primary plane must implement a
- * &drm_plane_helper_funcs.get_scanout_buffer helper function. It is then
- * automatically registered to the drm panic handler.
- * When a panic occurs, the &drm_plane_helper_funcs.get_scanout_buffer will be
- * called, and the driver can provide a framebuffer so the panic handler can
- * draw the panic screen on it. Currently only linear buffer and a few color
- * formats are supported.
- * Optionally the driver can also provide a &drm_plane_helper_funcs.panic_flush
- * callback, that will be called after that, to send additional commands to the
- * hardware to make the scanout buffer visible.
- */
-
-/*
- * This module displays a user friendly message on screen when a kernel panic
- * occurs. This is conflicting with fbcon, so you can only enable it when fbcon
- * is disabled.
- * It's intended for end-user, so have minimal technical/debug information.
+ * This module displays a user friendly message on screen when a kernel
+ * panic occurs. This is conflicting with fbcon, so you can only enable
+ * it when fbcon is disabled. It's intended for end users and therefore
+ * have minimal technical/debug information.
*
- * Implementation details:
+ * To enable DRM panic for a driver, the at least one primary plane must
+ * implement struct &drm_plane_funcs.display_panic_screen. The plane is
+ * then automatically registered to the drm panic handler.
*
- * It is a panic handler, so it can't take lock, allocate memory, run tasks/irq,
- * or attempt to sleep. It's a best effort, and it may not be able to display
- * the message in all situations (like if the panic occurs in the middle of a
- * modesetting).
- * It will display only one static frame, so performance optimizations are low
- * priority as the machine is already in an unusable state.
+ * When a panic occurs, the DRM panic handler calls struct
+ * &drm_plane_funcs.display_panic_screen. See
+ * drm_plane_helper_display_panic_screen() for a generic implementation.
*/
struct drm_panic_line {
@@ -821,12 +808,6 @@ static void drm_panic_qr_init(void) {};
static void drm_panic_qr_exit(void) {};
#endif
-enum drm_panic_type {
- DRM_PANIC_TYPE_KMSG,
- DRM_PANIC_TYPE_USER,
- DRM_PANIC_TYPE_QR,
-};
-
static enum drm_panic_type drm_panic_type = -1;
static const char *drm_panic_type_map[] = {
@@ -940,9 +921,29 @@ static void drm_panic_clear_description(void)
desc_line->txt = NULL;
}
-static void draw_panic_plane(struct drm_plane *plane, const char *description,
- enum drm_panic_type panic_type, u32 fg_color, u32 bg_color,
- unsigned int qr_version)
+/**
+ * drm_plane_helper_display_panic_screen - Displays a panic screen according to the given settings
+ * @plane: the DRM plane to display to
+ * @description: error message to display
+ * @panic_type: type of panic screen
+ * @fg_color: text foreground color
+ * @bg_color: text background color
+ * @qr_version: version of the QR code, if any
+ *
+ * This helper display a panic screen on common primary planes. The panic
+ * screen can either display a kernel message, a user message or a QR code.
+ *
+ * The helper uses struct drm_plane_helper_funcs.get_scanout_buffer, where
+ * the plane can provide a scanout buffer that the panic handler can draw to.
+ * Currently only linear buffer and a few color formats are supported.
+ *
+ * Optionally the plane can also provide a &drm_plane_helper_funcs.panic_flush
+ * callback, which the DRM panic handler calls after drawing to send additional
+ * commands to the hardware to make the scanout buffer visible.
+ */
+void drm_plane_helper_display_panic_screen(struct drm_plane *plane, const char *description,
+ enum drm_panic_type panic_type,
+ u32 fg_color, u32 bg_color, unsigned int qr_version)
{
struct drm_scanout_buffer sb = { };
int ret;
@@ -970,6 +971,7 @@ static void draw_panic_plane(struct drm_plane *plane, const char *description,
drm_panic_clear_description();
}
+EXPORT_SYMBOL(drm_plane_helper_display_panic_screen);
static void drm_panic_display_panic_screen(struct drm_plane *plane, const char *description)
{
@@ -992,8 +994,8 @@ static void drm_panic_display_panic_screen(struct drm_plane *plane, const char *
unsigned long flags;
if (drm_panic_trylock(dev, flags)) {
- draw_panic_plane(plane, description, drm_panic_type,
- fg_color, bg_color, qr_version);
+ plane->funcs->display_panic_screen(plane, description, drm_panic_type,
+ fg_color, bg_color, qr_version);
drm_panic_unlock(dev, flags);
}
}
@@ -1064,7 +1066,7 @@ bool drm_panic_is_enabled(struct drm_device *dev)
return false;
drm_for_each_plane(plane, dev)
- if (plane->helper_private && plane->helper_private->get_scanout_buffer)
+ if (plane->funcs && plane->funcs->display_panic_screen)
return true;
return false;
}
@@ -1083,7 +1085,7 @@ void drm_panic_register(struct drm_device *dev)
return;
drm_for_each_plane(plane, dev) {
- if (!plane->helper_private || !plane->helper_private->get_scanout_buffer)
+ if (!plane->funcs || !plane->funcs->display_panic_screen)
continue;
plane->kmsg_panic.dump = drm_panic;
plane->kmsg_panic.max_reason = KMSG_DUMP_PANIC;
@@ -1110,7 +1112,7 @@ void drm_panic_unregister(struct drm_device *dev)
return;
drm_for_each_plane(plane, dev) {
- if (!plane->helper_private || !plane->helper_private->get_scanout_buffer)
+ if (!plane->funcs || !plane->funcs->display_panic_screen)
continue;
kmsg_dump_unregister(&plane->kmsg_panic);
}
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
index 1855749c1e41..3bf228ef97ff 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
@@ -18,6 +18,7 @@
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
#include <drm/drm_vblank.h>
@@ -237,6 +238,7 @@ static const struct drm_plane_funcs hv_drm_plane_funcs = {
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
DRM_GEM_SHADOW_PLANE_FUNCS,
+ DRM_PANIC_PLANE_FUNCS,
};
static const struct drm_encoder_funcs hv_drm_simple_encoder_funcs_cleanup = {
diff --git a/drivers/gpu/drm/i915/display/i9xx_plane.c b/drivers/gpu/drm/i915/display/i9xx_plane.c
index df6da1c6a359..0902b17d0269 100644
--- a/drivers/gpu/drm/i915/display/i9xx_plane.c
+++ b/drivers/gpu/drm/i915/display/i9xx_plane.c
@@ -8,6 +8,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_print.h>
#include "i9xx_plane.h"
@@ -887,6 +888,7 @@ static const struct drm_plane_funcs i965_plane_funcs = {
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = i965_plane_format_mod_supported,
.format_mod_supported_async = intel_plane_format_mod_supported_async,
+ DRM_PANIC_PLANE_FUNCS,
};
static const struct drm_plane_funcs i8xx_plane_funcs = {
@@ -897,6 +899,7 @@ static const struct drm_plane_funcs i8xx_plane_funcs = {
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = i8xx_plane_format_mod_supported,
.format_mod_supported_async = intel_plane_format_mod_supported_async,
+ DRM_PANIC_PLANE_FUNCS,
};
static void i9xx_disable_tiling(struct intel_plane *plane)
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index 07a683293352..45e8379d93d4 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -7,6 +7,7 @@
#include <drm/drm_blend.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_print.h>
#include <drm/intel/step.h>
@@ -2707,6 +2708,7 @@ static const struct drm_plane_funcs skl_plane_funcs = {
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = skl_plane_format_mod_supported,
.format_mod_supported_async = intel_plane_format_mod_supported_async,
+ DRM_PANIC_PLANE_FUNCS,
};
static const struct drm_plane_funcs icl_plane_funcs = {
@@ -2717,6 +2719,7 @@ static const struct drm_plane_funcs icl_plane_funcs = {
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = icl_plane_format_mod_supported,
.format_mod_supported_async = intel_plane_format_mod_supported_async,
+ DRM_PANIC_PLANE_FUNCS,
};
static const struct drm_plane_funcs tgl_plane_funcs = {
@@ -2727,6 +2730,7 @@ static const struct drm_plane_funcs tgl_plane_funcs = {
.atomic_destroy_state = intel_plane_destroy_state,
.format_mod_supported = tgl_plane_format_mod_supported,
.format_mod_supported_async = intel_plane_format_mod_supported_async,
+ DRM_PANIC_PLANE_FUNCS,
};
static void
diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c
index c7ec09e557c1..377db7cea726 100644
--- a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c
+++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c
@@ -14,6 +14,7 @@
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_managed.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_print.h>
#include <video/imx-ipu-v3.h>
@@ -362,6 +363,7 @@ static const struct drm_plane_funcs ipu_plane_funcs = {
.atomic_duplicate_state = ipu_plane_duplicate_state,
.atomic_destroy_state = ipu_plane_destroy_state,
.format_mod_supported = ipu_plane_format_mod_supported,
+ DRM_PANIC_PLANE_FUNCS,
};
static int ipu_plane_atomic_check(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
index 3732c62351bb..d3995ce876f4 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
@@ -17,6 +17,7 @@
#include <drm/drm_encoder.h>
#include <drm/drm_gem.h>
#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_plane.h>
#include "mgag200_reg.h"
@@ -386,7 +387,8 @@ int mgag200_primary_plane_helper_get_scanout_buffer(struct drm_plane *plane,
.update_plane = drm_atomic_helper_update_plane, \
.disable_plane = drm_atomic_helper_disable_plane, \
.destroy = drm_plane_cleanup, \
- DRM_GEM_SHADOW_PLANE_FUNCS
+ DRM_GEM_SHADOW_PLANE_FUNCS, \
+ DRM_PANIC_PLANE_FUNCS
void mgag200_crtc_fill_gamma(struct mga_device *mdev, const struct drm_format_info *format);
void mgag200_crtc_load_gamma(struct mga_device *mdev,
diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
index 15a322422f4e..fc29a291e96f 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c
@@ -39,6 +39,7 @@
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
#include <drm/ttm/ttm_bo.h>
#include "nouveau_bo.h"
@@ -841,6 +842,7 @@ nv50_wndw = {
.atomic_duplicate_state = nv50_wndw_atomic_duplicate_state,
.atomic_destroy_state = nv50_wndw_atomic_destroy_state,
.format_mod_supported = nv50_plane_format_mod_supported,
+ DRM_PANIC_PLANE_FUNCS,
};
static const u64 nv50_cursor_format_modifiers[] = {
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c
index 8870766b9e54..f4b0e5b70c7d 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c
@@ -16,6 +16,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_panic_helper.h>
#include "rcar_du_drv.h"
#include "rcar_du_group.h"
@@ -773,6 +774,7 @@ static const struct drm_plane_funcs rcar_du_plane_funcs = {
.atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
.atomic_set_property = rcar_du_plane_atomic_set_property,
.atomic_get_property = rcar_du_plane_atomic_get_property,
+ DRM_PANIC_PLANE_FUNCS,
};
static const uint32_t formats[] = {
diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c
index 148de34af785..907f85462fa0 100644
--- a/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c
+++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c
@@ -14,6 +14,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_dma_helper.h>
+#include <drm/drm_panic_helper.h>
#include "shmob_drm_drv.h"
#include "shmob_drm_kms.h"
@@ -286,6 +287,7 @@ static const struct drm_plane_funcs shmob_drm_plane_funcs = {
.reset = shmob_drm_plane_reset,
.atomic_duplicate_state = shmob_drm_plane_atomic_duplicate_state,
.atomic_destroy_state = shmob_drm_plane_atomic_destroy_state,
+ DRM_PANIC_PLANE_FUNCS,
};
static const uint32_t formats[] = {
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
index 547f2327af5e..232be339659e 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
@@ -12,6 +12,7 @@
#include <drm/drm_device.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_modes.h>
+#include <drm/drm_panic_helper.h>
struct drm_format_info;
struct drm_scanout_buffer;
@@ -144,7 +145,8 @@ void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane,
.update_plane = drm_atomic_helper_update_plane, \
.disable_plane = drm_atomic_helper_disable_plane, \
.atomic_duplicate_state = drm_sysfb_plane_atomic_duplicate_state, \
- .atomic_destroy_state = drm_sysfb_plane_atomic_destroy_state
+ .atomic_destroy_state = drm_sysfb_plane_atomic_destroy_state, \
+ DRM_PANIC_PLANE_FUNCS
/*
* CRTC
diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c
index 6a67b2d2e451..14d2e6ab88aa 100644
--- a/drivers/gpu/drm/sysfb/vesadrm.c
+++ b/drivers/gpu/drm/sysfb/vesadrm.c
@@ -24,6 +24,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper.h>
#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c
index 6d82976c2db1..b0d5d6959b88 100644
--- a/drivers/gpu/drm/tidss/tidss_plane.c
+++ b/drivers/gpu/drm/tidss/tidss_plane.c
@@ -12,6 +12,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_panic_helper.h>
#include "tidss_crtc.h"
#include "tidss_dispc.h"
@@ -182,6 +183,7 @@ static const struct drm_plane_funcs tidss_plane_funcs = {
.atomic_create_state = drm_atomic_helper_plane_create_state,
.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ DRM_PANIC_PLANE_FUNCS,
};
struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c
index e2d957e51505..076d19b26511 100644
--- a/drivers/gpu/drm/tiny/bochs.c
+++ b/drivers/gpu/drm/tiny/bochs.c
@@ -20,6 +20,7 @@
#include <drm/drm_managed.h>
#include <drm/drm_module.h>
#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -501,7 +502,8 @@ static const struct drm_plane_funcs bochs_primary_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
.disable_plane = drm_atomic_helper_disable_plane,
.destroy = drm_plane_cleanup,
- DRM_GEM_SHADOW_PLANE_FUNCS
+ DRM_GEM_SHADOW_PLANE_FUNCS,
+ DRM_PANIC_PLANE_FUNCS,
};
static void bochs_crtc_helper_mode_set_nofb(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 1d1b27ece62a..108b5b93f130 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -30,6 +30,7 @@
#include <linux/virtio_dma_buf.h>
#include <drm/drm_managed.h>
#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
#include <drm/drm_print.h>
#include "virtgpu_drv.h"
@@ -94,6 +95,7 @@ static const struct drm_plane_funcs virtio_gpu_plane_funcs = {
.reset = drm_atomic_helper_plane_reset,
.atomic_duplicate_state = virtio_gpu_plane_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ DRM_PANIC_PLANE_FUNCS,
};
static int virtio_gpu_plane_atomic_check(struct drm_plane *plane,
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h
index d8f5b7e9673e..084517beb924 100644
--- a/include/drm/drm_mode_config.h
+++ b/include/drm/drm_mode_config.h
@@ -495,8 +495,8 @@ struct drm_mode_config {
*
* Raw spinlock used to protect critical sections of code that access
* the display hardware or modeset software state, which the panic
- * printing code must be protected against. See drm_panic_trylock(),
- * drm_panic_lock() and drm_panic_unlock().
+ * printing code must be protected against. See the documentation on
+ * DRM panic handling for more information.
*/
struct raw_spinlock panic_lock;
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
index ca6268945c28..614bdee0c1ed 100644
--- a/include/drm/drm_modeset_helper_vtables.h
+++ b/include/drm/drm_modeset_helper_vtables.h
@@ -1442,17 +1442,20 @@ struct drm_plane_helper_funcs {
/**
* @get_scanout_buffer:
*
- * Get the current scanout buffer, to display a message with drm_panic.
+ * Get the current scanout buffer to display a message with drm_panic.
* The driver should do the minimum changes to provide a buffer,
* that can be used to display the panic screen. Currently only linear
* buffers are supported. Non-linear buffer support is on the TODO list.
* The device &dev.mode_config.panic_lock is taken before calling this
* function, so you can safely access the &plane.state
* It is called from a panic callback, and must follow its restrictions.
- * Please look the documentation at drm_panic_trylock() for an in-depth
+ * Please look the documentation on DRM panic handling for an in-depth
* discussions of what's safe and what is not allowed.
+ *
* It's a best effort mode, so it's expected that in some complex cases
- * the panic screen won't be displayed.
+ * the panic screen won't be displayed. Drivers must not make any
+ * assumptions about the actual state of the hardware.
+ *
* The returned &drm_scanout_buffer.map must be valid if no error code is
* returned.
*
@@ -1472,7 +1475,7 @@ struct drm_plane_helper_funcs {
* It is only called if get_scanout_buffer() returned successfully, and
* the &dev.mode_config.panic_lock is held during the entire sequence.
* It is called from a panic callback, and must follow its restrictions.
- * Please look the documentation at drm_panic_trylock() for an in-depth
+ * Please look the documentation on DRM panic handling for an in-depth
* discussions of what's safe and what is not allowed.
*/
void (*panic_flush)(struct drm_plane *plane);
diff --git a/include/drm/drm_panic.h b/include/drm/drm_panic.h
index ac0e46b73436..430df536de14 100644
--- a/include/drm/drm_panic.h
+++ b/include/drm/drm_panic.h
@@ -15,6 +15,14 @@
#include <drm/drm_device.h>
#include <drm/drm_fourcc.h>
+struct page;
+
+enum drm_panic_type {
+ DRM_PANIC_TYPE_KMSG,
+ DRM_PANIC_TYPE_USER,
+ DRM_PANIC_TYPE_QR,
+};
+
/**
* struct drm_scanout_buffer - DRM scanout buffer
*
@@ -87,45 +95,8 @@ struct drm_scanout_buffer {
* @dev: struct drm_device
* @flags: unsigned long irq flags you need to pass to the unlock() counterpart
*
- * This function must be called by any panic printing code. The panic printing
- * attempt must be aborted if the trylock fails.
- *
- * Panic printing code can make the following assumptions while holding the
- * panic lock:
- *
- * - Anything protected by drm_panic_lock() and drm_panic_unlock() pairs is safe
- * to access.
- *
- * - Furthermore the panic printing code only registers in drm_dev_unregister()
- * and gets removed in drm_dev_unregister(). This allows the panic code to
- * safely access any state which is invariant in between these two function
- * calls, like the list of planes &drm_mode_config.plane_list or most of the
- * struct drm_plane structure.
- *
- * Specifically thanks to the protection around plane updates in
- * drm_atomic_helper_swap_state() the following additional guarantees hold:
- *
- * - It is safe to deference the drm_plane.state pointer.
- *
- * - Anything in struct drm_plane_state or the driver's subclass thereof which
- * stays invariant after the atomic check code has finished is safe to access.
- * Specifically this includes the reference counted pointers to framebuffer
- * and buffer objects.
- *
- * - Anything set up by &drm_plane_helper_funcs.fb_prepare and cleaned up
- * &drm_plane_helper_funcs.fb_cleanup is safe to access, as long as it stays
- * invariant between these two calls. This also means that for drivers using
- * dynamic buffer management the framebuffer is pinned, and therefer all
- * relevant datastructures can be accessed without taking any further locks
- * (which would be impossible in panic context anyway).
- *
- * - Importantly, software and hardware state set up by
- * &drm_plane_helper_funcs.begin_fb_access and
- * &drm_plane_helper_funcs.end_fb_access is not safe to access.
- *
- * Drivers must not make any assumptions about the actual state of the hardware,
- * unless they explicitly protected these hardware access with drm_panic_lock()
- * and drm_panic_unlock().
+ * The panic-printing code calls this function. The panic printing attempt must
+ * be aborted if the trylock fails.
*
* Return:
* %0 when failing to acquire the raw spinlock, nonzero on success.
diff --git a/include/drm/drm_panic_helper.h b/include/drm/drm_panic_helper.h
new file mode 100644
index 000000000000..e296620bbe34
--- /dev/null
+++ b/include/drm/drm_panic_helper.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+
+#ifndef __DRM_DRM_PANIC_HELPER_H_
+#define __DRM_DRM_PANIC_HELPER_H_
+
+#include <linux/types.h>
+
+enum drm_panic_type;
+
+struct drm_plane;
+
+/* drm_panic_helper.c */
+void drm_plane_helper_display_panic_screen(struct drm_plane *plane,
+ const char *description,
+ enum drm_panic_type panic_type,
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version);
+
+#if IS_ENABLED(CONFIG_DRM_PANIC)
+#define DRM_PANIC_PLANE_FUNCS \
+ .display_panic_screen = drm_plane_helper_display_panic_screen
+#else
+#define DRM_PANIC_PLANE_FUNCS \
+ .display_panic_screen = NULL
+#endif
+
+#endif
diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h
index 2c5a5a70a71b..317af17bac26 100644
--- a/include/drm/drm_plane.h
+++ b/include/drm/drm_plane.h
@@ -32,6 +32,8 @@
#include <drm/drm_modeset_lock.h>
#include <drm/drm_util.h>
+enum drm_panic_type;
+
struct drm_crtc;
struct drm_plane_size_hint;
struct drm_printer;
@@ -590,6 +592,61 @@ struct drm_plane_funcs {
bool (*format_mod_supported_async)(struct drm_plane *plane,
u32 format, u64 modifier);
+ /**
+ * @display_panic_screen:
+ *
+ * DRM panic handling invokes this callback on panics.
+ *
+ * It is a panic handler, so it can't take locks, allocate memory, run tasks/irq,
+ * or attempt to sleep. It's a best effort, and it may not be able to display
+ * the message in all situations (like if the panic occurs in the middle of a
+ * modesetting).
+ *
+ * Panic-printing code must acquire the panic lock before interacting with the
+ * scanout buffer. Such helpers can make the following assumptions while holding
+ * the panic lock:
+ *
+ * - Anything protected by drm_panic_lock() and drm_panic_unlock() pairs is safe
+ * to access.
+ *
+ * - Furthermore the panic printing code only registers in drm_dev_unregister()
+ * and gets removed in drm_dev_unregister(). This allows the panic code to
+ * safely access any state which is invariant in between these two function
+ * calls, like the list of planes &drm_mode_config.plane_list or most of the
+ * struct drm_plane structure.
+ *
+ * Specifically thanks to the protection around plane updates in
+ * drm_atomic_helper_swap_state() the following additional guarantees hold:
+ *
+ * - It is safe to deference the drm_plane.state pointer.
+ *
+ * - Anything in struct drm_plane_state or the driver's subclass thereof which
+ * stays invariant after the atomic check code has finished is safe to access.
+ * Specifically this includes the reference counted pointers to framebuffer
+ * and buffer objects.
+ *
+ * - Anything set up by &drm_plane_helper_funcs.fb_prepare and cleaned up
+ * &drm_plane_helper_funcs.fb_cleanup is safe to access, as long as it stays
+ * invariant between these two calls. This also means that for drivers using
+ * dynamic buffer management the framebuffer is pinned, and therefer all
+ * relevant datastructures can be accessed without taking any further locks
+ * (which would be impossible in panic context anyway).
+ *
+ * - Importantly, software and hardware state set up by
+ * &drm_plane_helper_funcs.begin_fb_access and
+ * &drm_plane_helper_funcs.end_fb_access is not safe to access.
+ *
+ * It will display only one static frame, so performance optimizations are
+ * low priority as the machine is already in an unusable state.
+ *
+ * This callback is optional. Planes without will not be considered
+ * for panic output.
+ */
+ void (*display_panic_screen)(struct drm_plane *plane,
+ const char *description,
+ enum drm_panic_type panic_type,
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version);
};
/**
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 09/12] drm/panic: Display panic screen via per-plane callback
2026-08-18 12:28 ` [PATCH 09/12] drm/panic: Display panic screen via per-plane callback Thomas Zimmermann
@ 2026-08-18 13:19 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:19 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: dri-devel, imx, intel-gfx, intel-xe, Frank.Li, linux-hyperv,
wsa+renesas, ojeda
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The `drm_plane_helper_display_panic_screen` helper unconditionally dereferences `plane->helper_private` and `plane->helper_private->get_scanout_buffer`. However, by adding `DRM_PANIC_PLANE_FUNCS` to shared plane function tables (like `skl_plane_funcs` in i915 and `dm_plane_funcs` in amdgpu), the `display_panic_screen` callback is now applied to all planes (including cursors and overlays). These non-primary planes use different helper functions (e.g., `intel_plane_helper_funcs` or `dm_plane_helper_funcs`) that do not implement `get_scanout_buffer`. This causes `drm_panic_register` to mistakenly register panic dumpers for these planes, and upon a panic, the handler executes a NULL pointer dereference, triggering a nested panic.
- [Medium] The patch replaces `get_scanout_buffer` with `display_panic_screen` as the signal for panic support. However, it fails to add `DRM_PANIC_PLANE_FUNCS` to the legacy fallback `primary_plane_funcs` in `drm_modeset_helper.c`. Older AMDGPU DCE drivers (e.g., DCE 6.0, 8.0, 10.0) use `drm_crtc_init()` which implicitly relies on this legacy structure for their primary planes. Although these drivers implement `get_scanout_buffer` in their helper functions (`dce_v10_0_drm_primary_plane_helper_funcs`), their planes now lack the `display_panic_screen` callback. Consequently, `drm_panic_is_enabled()` returns false and their panic dumpers are never registered, silently breaking panic screen support on these hardware generations.
- [Low] `drm_plane_helper_display_panic_screen` accepts a `panic_type` parameter (an `enum drm_panic_type`). However, when it calls `draw_panic_dispatch()`, it passes the global module parameter `drm_panic_type` instead of the passed-in `panic_type`. This logic bug prevents drivers from tailoring the panic screen implementation via the helper as intended.
--
--- Patch [9]: [PATCH 09/12] drm/panic: Display panic screen via per-plane callback ---
commit 69fa8c39afc844867e193df3659c6f6f1fb762f3
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Display panic screen via per-plane callback
This commit adds display_panic_screen to struct drm_plane_funcs to replace
get_scanout_buffer as the signal for panic support. It provides the helper
drm_plane_helper_display_panic_screen() and the DRM_PANIC_PLANE_FUNCS macro
to update drivers that implement panic support.
> diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
> index 824ef3ce5de0a..eadc22a255bb5 100644
> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c
[ ... ]
> @@ -2177,6 +2178,7 @@ static const struct drm_plane_funcs dm_plane_funcs = {
> .atomic_set_property = dm_atomic_plane_set_property,
> .atomic_get_property = dm_atomic_plane_get_property,
> #endif
> + DRM_PANIC_PLANE_FUNCS,
> };
[Severity: High]
Does adding this macro to shared plane function tables (like dm_plane_funcs
or skl_plane_funcs) apply the panic screen callback to all planes, including
cursors and overlays?
Non-primary planes use different helper functions that do not implement
get_scanout_buffer. Since drm_plane_helper_display_panic_screen()
unconditionally dereferences plane->helper_private->get_scanout_buffer,
could this cause a NULL pointer dereference in the panic handler for these
planes and trigger a nested panic?
> diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
> index 2263a11efdf63..2ecb4d1f6d470 100644
> --- a/drivers/gpu/drm/drm_panic.c
> +++ b/drivers/gpu/drm/drm_panic.c
[ ... ]
> +void drm_plane_helper_display_panic_screen(struct drm_plane *plane, const char *description,
> + enum drm_panic_type panic_type,
> + u32 fg_color, u32 bg_color, unsigned int qr_version)
> {
> struct drm_scanout_buffer sb = { };
> int ret;
[Severity: Low]
Is the panic_type parameter correctly passed to the dispatch function?
The function accepts a panic_type argument, but the implementation appears
to pass the global module parameter drm_panic_type to draw_panic_dispatch()
instead of the passed-in argument. Does this discard the caller's requested
panic type?
> diff --git a/include/drm/drm_panic_helper.h b/include/drm/drm_panic_helper.h
> new file mode 100644
> index 0000000000000..e296620bbe34b
> --- /dev/null
> +++ b/include/drm/drm_panic_helper.h
[ ... ]
> +#if IS_ENABLED(CONFIG_DRM_PANIC)
> +#define DRM_PANIC_PLANE_FUNCS \
> + .display_panic_screen = drm_plane_helper_display_panic_screen
> +#else
> +#define DRM_PANIC_PLANE_FUNCS \
> + .display_panic_screen = NULL
> +#endif
[Severity: Medium]
Does the legacy fallback primary_plane_funcs in drm_modeset_helper.c also
need to be updated with DRM_PANIC_PLANE_FUNCS?
Older AMDGPU DCE drivers use drm_crtc_init(), which implicitly relies on
this legacy structure for their primary planes. Although these drivers
implement get_scanout_buffer() in their helper functions, lacking this
macro in the fallback could cause drm_panic_is_enabled() to return false
and silently drop panic screen support on these hardware generations.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=9
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 10/12] drm/panic: Internalize panic locking in DRM core and helpers
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (8 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 09/12] drm/panic: Display panic screen via per-plane callback Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 12:28 ` [PATCH 11/12] drm/panic: Move panic display code into helper library Thomas Zimmermann
2026-08-18 12:28 ` [PATCH 12/12] drm/panic: Compile KUnit tests as module Thomas Zimmermann
11 siblings, 0 replies; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
None of the DRM drivers handles panic locking. Declare the interfaces
in an internal header file. Move the trylock required during the panic
into the DRM core's sources.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
Documentation/gpu/drm-kms.rst | 3 ++
MAINTAINERS | 3 +-
drivers/gpu/drm/drm_atomic_helper.c | 2 +-
drivers/gpu/drm/drm_drv.c | 2 +-
drivers/gpu/drm/drm_panic.c | 4 ++
drivers/gpu/drm/drm_panic_internal.h | 55 +++++++++++++++++++++++
include/drm/drm_panic.h | 65 ----------------------------
7 files changed, 66 insertions(+), 68 deletions(-)
create mode 100644 drivers/gpu/drm/drm_panic_internal.h
diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index 0dd440a14946..8988fd64369b 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -421,6 +421,9 @@ Plane Panic Functions Reference
.. kernel-doc:: include/drm/drm_panic.h
:internal:
+.. kernel-doc:: drivers/gpu/drm/drm_panic_internal.h
+ :internal:
+
.. kernel-doc:: drivers/gpu/drm/drm_panic.c
:export:
diff --git a/MAINTAINERS b/MAINTAINERS
index 472c5c48729d..f3dfdd9314bd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9018,7 +9018,8 @@ S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/drm_draw.c
F: drivers/gpu/drm/drm_draw_internal.h
-F: drivers/gpu/drm/drm_panic*.c
+F: drivers/gpu/drm/drm_panic.c
+F: drivers/gpu/drm/drm_panic_internal.h
F: drivers/gpu/drm/tests/drm_panic_test.c
F: include/drm/drm_panic.h
F: include/drm/drm_panic_helper.h
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 285aac3554df..af3934df3313 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -41,7 +41,6 @@
#include <drm/drm_drv.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_panic.h>
#include <drm/drm_print.h>
#include <drm/drm_self_refresh_helper.h>
#include <drm/drm_vblank.h>
@@ -49,6 +48,7 @@
#include "drm_crtc_helper_internal.h"
#include "drm_crtc_internal.h"
+#include "drm_panic_internal.h"
/**
* DOC: overview
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index c808958a2188..02ff02931515 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -50,13 +50,13 @@
#include <drm/drm_file.h>
#include <drm/drm_managed.h>
#include <drm/drm_mode_object.h>
-#include <drm/drm_panic.h>
#include <drm/drm_print.h>
#include <drm/drm_privacy_screen_machine.h>
#include <drm/drm_ras_genl_family.h>
#include "drm_crtc_internal.h"
#include "drm_internal.h"
+#include "drm_panic_internal.h"
MODULE_AUTHOR("Gareth Hughes, Leif Delgass, José Fonseca, Jon Smirl");
MODULE_DESCRIPTION("DRM shared core routines");
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index 2ecb4d1f6d47..b3a71fddf2de 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -35,6 +35,7 @@
#include "drm_crtc_internal.h"
#include "drm_draw_internal.h"
+#include "drm_panic_internal.h"
MODULE_AUTHOR("Jocelyn Falempe");
MODULE_DESCRIPTION("DRM panic handler");
@@ -973,6 +974,9 @@ void drm_plane_helper_display_panic_screen(struct drm_plane *plane, const char *
}
EXPORT_SYMBOL(drm_plane_helper_display_panic_screen);
+#define drm_panic_trylock(dev, flags) \
+ raw_spin_trylock_irqsave(&(dev)->mode_config.panic_lock, flags)
+
static void drm_panic_display_panic_screen(struct drm_plane *plane, const char *description)
{
#if defined(CONFIG_DRM_PANIC_FOREGROUND_COLOR)
diff --git a/drivers/gpu/drm/drm_panic_internal.h b/drivers/gpu/drm/drm_panic_internal.h
new file mode 100644
index 000000000000..c68d64c56e15
--- /dev/null
+++ b/drivers/gpu/drm/drm_panic_internal.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 or MIT */
+
+/*
+ * Copyright (c) 2024 Intel
+ * Copyright (c) 2024 Red Hat
+ */
+
+#ifndef __DRM_PANIC_INTERNAL_H__
+#define __DRM_PANIC_INTERNAL_H__
+
+#include <linux/spinlock.h>
+
+struct drm_device;
+
+#ifdef CONFIG_DRM_PANIC
+
+/**
+ * drm_panic_lock - protect panic printing relevant state
+ * @dev: struct drm_device
+ * @flags: unsigned long irq flags you need to pass to the unlock() counterpart
+ *
+ * This function must be called to protect software and hardware state that the
+ * panic printing code must be able to rely on. The protected sections must be
+ * as small as possible. It uses the irqsave/irqrestore variant, and can be
+ * called from irq handler. Examples include:
+ *
+ * - Access to peek/poke or other similar registers, if that is the way the
+ * driver prints the pixels into the scanout buffer at panic time.
+ *
+ * - Updates to pointers like &drm_plane.state, allowing the panic handler to
+ * safely deference these. This is done in drm_atomic_helper_swap_state().
+ *
+ * - An state that isn't invariant and that the driver must be able to access
+ * during panic printing.
+ */
+#define drm_panic_lock(dev, flags) \
+ raw_spin_lock_irqsave(&(dev)->mode_config.panic_lock, flags)
+
+/**
+ * drm_panic_unlock - end of the panic printing critical section
+ * @dev: struct drm_device
+ * @flags: irq flags that were returned when acquiring the lock
+ *
+ * Unlocks the raw spinlock acquired by either drm_panic_lock() or
+ * drm_panic_trylock().
+ */
+#define drm_panic_unlock(dev, flags) \
+ raw_spin_unlock_irqrestore(&(dev)->mode_config.panic_lock, flags)
+
+#else
+static inline void drm_panic_lock(struct drm_device *dev, unsigned long flags) {}
+static inline void drm_panic_unlock(struct drm_device *dev, unsigned long flags) {}
+#endif
+
+#endif /* __DRM_PANIC_INTERNAL_H__ */
diff --git a/include/drm/drm_panic.h b/include/drm/drm_panic.h
index 430df536de14..99572b7eeab9 100644
--- a/include/drm/drm_panic.h
+++ b/include/drm/drm_panic.h
@@ -8,11 +8,9 @@
#ifndef __DRM_PANIC_H__
#define __DRM_PANIC_H__
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/iosys-map.h>
-#include <drm/drm_device.h>
#include <drm/drm_fourcc.h>
struct page;
@@ -85,71 +83,8 @@ struct drm_scanout_buffer {
* set_pixel()
*/
void *private;
-
};
-#ifdef CONFIG_DRM_PANIC
-
-/**
- * drm_panic_trylock - try to enter the panic printing critical section
- * @dev: struct drm_device
- * @flags: unsigned long irq flags you need to pass to the unlock() counterpart
- *
- * The panic-printing code calls this function. The panic printing attempt must
- * be aborted if the trylock fails.
- *
- * Return:
- * %0 when failing to acquire the raw spinlock, nonzero on success.
- */
-#define drm_panic_trylock(dev, flags) \
- raw_spin_trylock_irqsave(&(dev)->mode_config.panic_lock, flags)
-
-/**
- * drm_panic_lock - protect panic printing relevant state
- * @dev: struct drm_device
- * @flags: unsigned long irq flags you need to pass to the unlock() counterpart
- *
- * This function must be called to protect software and hardware state that the
- * panic printing code must be able to rely on. The protected sections must be
- * as small as possible. It uses the irqsave/irqrestore variant, and can be
- * called from irq handler. Examples include:
- *
- * - Access to peek/poke or other similar registers, if that is the way the
- * driver prints the pixels into the scanout buffer at panic time.
- *
- * - Updates to pointers like &drm_plane.state, allowing the panic handler to
- * safely deference these. This is done in drm_atomic_helper_swap_state().
- *
- * - An state that isn't invariant and that the driver must be able to access
- * during panic printing.
- */
-
-#define drm_panic_lock(dev, flags) \
- raw_spin_lock_irqsave(&(dev)->mode_config.panic_lock, flags)
-
-/**
- * drm_panic_unlock - end of the panic printing critical section
- * @dev: struct drm_device
- * @flags: irq flags that were returned when acquiring the lock
- *
- * Unlocks the raw spinlock acquired by either drm_panic_lock() or
- * drm_panic_trylock().
- */
-#define drm_panic_unlock(dev, flags) \
- raw_spin_unlock_irqrestore(&(dev)->mode_config.panic_lock, flags)
-
-#else
-
-static inline bool drm_panic_trylock(struct drm_device *dev, unsigned long flags)
-{
- return true;
-}
-
-static inline void drm_panic_lock(struct drm_device *dev, unsigned long flags) {}
-static inline void drm_panic_unlock(struct drm_device *dev, unsigned long flags) {}
-
-#endif
-
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
size_t drm_panic_qr_max_data_size(u8 version, size_t url_len);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH 11/12] drm/panic: Move panic display code into helper library
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (9 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 10/12] drm/panic: Internalize panic locking in DRM core and helpers Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:13 ` sashiko-bot
2026-08-18 15:55 ` Randy Dunlap
2026-08-18 12:28 ` [PATCH 12/12] drm/panic: Compile KUnit tests as module Thomas Zimmermann
11 siblings, 2 replies; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Move drm_plane_helper_display_panic_screen() and the existing panic-
display code into drm_panic_helper.c. Rename symbols accordingly.
Also update the QR-related code and the bindings accordingly.
Link the rsp. object file into drm_kms_helper.ko. Eventually the code
will become a DRM drawing library, together with DRM's format and draw
helpers. This requires more preparation. until then, KMS helpers serve
as a module. All DRM drivers link it anyway.
Add a new section about drawing helpers to the documentation of the KMS
helpers. List format and panic helpers there.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
Documentation/gpu/drm-kms-helpers.rst | 11 +-
MAINTAINERS | 5 +-
drivers/gpu/drm/Kconfig | 5 +
drivers/gpu/drm/Makefile | 3 +-
drivers/gpu/drm/drm_crtc_internal.h | 2 -
drivers/gpu/drm/drm_drv.c | 1 -
drivers/gpu/drm/drm_kms_helper_common.c | 14 +
drivers/gpu/drm/drm_panic.c | 891 -----------------
drivers/gpu/drm/drm_panic_helper.c | 913 ++++++++++++++++++
...drm_panic_qr.rs => drm_panic_helper_qr.rs} | 4 +-
drivers/gpu/drm/drm_panic_internal.h | 4 +
...m_panic_test.c => drm_panic_helper_test.c} | 24 +-
include/drm/drm_panic.h | 7 -
include/drm/drm_panic_helper.h | 5 +
rust/bindings/bindings_helper.h | 4 +-
15 files changed, 972 insertions(+), 921 deletions(-)
create mode 100644 drivers/gpu/drm/drm_panic_helper.c
rename drivers/gpu/drm/{drm_panic_qr.rs => drm_panic_helper_qr.rs} (99%)
rename drivers/gpu/drm/tests/{drm_panic_test.c => drm_panic_helper_test.c} (88%)
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index 94cfc26acecc..400cd0112713 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -118,12 +118,21 @@ fbdev Helper Functions Reference
.. kernel-doc:: drivers/gpu/drm/drm_fb_helper.c
:export:
-format Helper Functions Reference
+Draw Helper Functions Reference
=================================
+Format-Conversion Helper Reference
+----------------------------------
+
.. kernel-doc:: drivers/gpu/drm/drm_format_helper.c
:export:
+Panic Helper Reference
+----------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_panic_helper.c
+ :export:
+
Framebuffer DMA Helper Functions Reference
==========================================
diff --git a/MAINTAINERS b/MAINTAINERS
index f3dfdd9314bd..f7e7c6e2d285 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9019,8 +9019,9 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/drm_draw.c
F: drivers/gpu/drm/drm_draw_internal.h
F: drivers/gpu/drm/drm_panic.c
+F: drivers/gpu/drm/drm_panic_helper.c
F: drivers/gpu/drm/drm_panic_internal.h
-F: drivers/gpu/drm/tests/drm_panic_test.c
+F: drivers/gpu/drm/tests/drm_panic_helper_test.c
F: include/drm/drm_panic.h
F: include/drm/drm_panic_helper.h
@@ -9031,7 +9032,7 @@ L: dri-devel@lists.freedesktop.org
L: rust-for-linux@vger.kernel.org
S: Supported
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
-F: drivers/gpu/drm/drm_panic_qr.rs
+F: drivers/gpu/drm/drm_panic_helper_qr.rs
DRM PRIVACY-SCREEN CLASS
M: Hans de Goede <hansg@kernel.org>
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 323422861e8f..cdfd72d9a9c5 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -45,6 +45,7 @@ config DRM_MIPI_DSI
config DRM_KMS_HELPER
tristate
depends on DRM
+ select DRM_PANIC_HELPER if DRM_PANIC
select FB_CORE if DRM_FBDEV_EMULATION
help
CRTC helpers for KMS drivers.
@@ -130,6 +131,10 @@ config DRM_PANIC_SCREEN_QR_VERSION
Smaller QR code are easier to read, but will contain less debugging
data. Default is 40.
+config DRM_PANIC_HELPER
+ bool
+ depends on DRM_TTM
+
config DRM_RAS
bool "DRM RAS support"
depends on DRM
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index e635fcffd379..f0c340aa2ec6 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -92,7 +92,6 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \
drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o
drm-$(CONFIG_DRM_PANIC) += drm_panic.o
drm-$(CONFIG_DRM_DRAW) += drm_draw.o
-drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o
drm-$(CONFIG_DRM_RAS) += drm_ras.o drm_ras_nl.o drm_ras_genl_family.o
obj-$(CONFIG_DRM) += drm.o
@@ -157,6 +156,8 @@ drm_kms_helper-y := \
drm_simple_kms_helper.o \
drm_vblank_helper.o
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
+drm_kms_helper-$(CONFIG_DRM_PANIC_HELPER) += drm_panic_helper.o
+drm_kms_helper-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_helper_qr.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 83146ffef00c..3492787c3697 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -322,13 +322,11 @@ bool drm_panic_is_enabled(struct drm_device *dev);
void drm_panic_register(struct drm_device *dev);
void drm_panic_unregister(struct drm_device *dev);
void drm_panic_init(void);
-void drm_panic_exit(void);
#else
static inline bool drm_panic_is_enabled(struct drm_device *dev) { return false; }
static inline void drm_panic_register(struct drm_device *dev) {}
static inline void drm_panic_unregister(struct drm_device *dev) {}
static inline void drm_panic_init(void) {}
-static inline void drm_panic_exit(void) {}
#endif
#endif /* __DRM_CRTC_INTERNAL_H__ */
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 02ff02931515..1e813645b3eb 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -1248,7 +1248,6 @@ static void drm_core_exit(void)
{
drm_ras_genl_family_unregister();
drm_privacy_screen_lookup_exit();
- drm_panic_exit();
accel_core_exit();
unregister_chrdev(DRM_MAJOR, "drm");
drm_debugfs_remove_root();
diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c
index 0c7550c0462b..b5d5b469b444 100644
--- a/drivers/gpu/drm/drm_kms_helper_common.c
+++ b/drivers/gpu/drm/drm_kms_helper_common.c
@@ -27,6 +27,20 @@
#include <linux/module.h>
+#include "drm_panic_internal.h"
+
+static int __init drm_kms_helper_init(void)
+{
+ return drm_panic_helper_init();
+}
+module_init(drm_kms_helper_init);
+
+static void __exit drm_kms_helper_exit(void)
+{
+ drm_panic_helper_exit();
+}
+module_exit(drm_kms_helper_exit);
+
MODULE_AUTHOR("David Airlie, Jesse Barnes");
MODULE_DESCRIPTION("DRM KMS helper");
MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c
index b3a71fddf2de..c8b59a6e7f99 100644
--- a/drivers/gpu/drm/drm_panic.c
+++ b/drivers/gpu/drm/drm_panic.c
@@ -7,34 +7,22 @@
*/
#include <linux/export.h>
-#include <linux/font.h>
-#include <linux/highmem.h>
#include <linux/init.h>
-#include <linux/iosys-map.h>
#include <linux/kdebug.h>
#include <linux/kmsg_dump.h>
-#include <linux/linux_logo.h>
#include <linux/list.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/overflow.h>
#include <linux/printk.h>
#include <linux/types.h>
-#include <linux/utsname.h>
-#include <linux/zlib.h>
#include <drm/drm_drv.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_panic.h>
-#include <drm/drm_panic_helper.h>
#include <drm/drm_plane.h>
#include <drm/drm_print.h>
-#include <drm/drm_rect.h>
#include "drm_crtc_internal.h"
-#include "drm_draw_internal.h"
#include "drm_panic_internal.h"
MODULE_AUTHOR("Jocelyn Falempe");
@@ -58,755 +46,10 @@ MODULE_LICENSE("GPL");
* drm_plane_helper_display_panic_screen() for a generic implementation.
*/
-struct drm_panic_line {
- u32 len;
- const char *txt;
-};
-
-#define PANIC_LINE(s) {.len = sizeof(s) - 1, .txt = s}
-
-static struct drm_panic_line panic_msg[] = {
- PANIC_LINE("KERNEL PANIC!"),
- PANIC_LINE(""),
- PANIC_LINE("Please reboot your computer."),
- PANIC_LINE(""),
- PANIC_LINE(""), /* will be replaced by the panic description */
-};
-
-static const size_t panic_msg_lines = ARRAY_SIZE(panic_msg);
-
-static const struct drm_panic_line logo_ascii[] = {
- PANIC_LINE(" .--. _"),
- PANIC_LINE(" |o_o | | |"),
- PANIC_LINE(" |:_/ | | |"),
- PANIC_LINE(" // \\ \\ |_|"),
- PANIC_LINE(" (| | ) _"),
- PANIC_LINE(" /'\\_ _/`\\ (_)"),
- PANIC_LINE(" \\___)=(___/"),
-};
-
-static const size_t logo_ascii_lines = ARRAY_SIZE(logo_ascii);
-
-#if defined(CONFIG_LOGO) && !defined(MODULE)
-static const struct linux_logo *logo_mono;
-
-static int drm_panic_setup_logo(void)
-{
- const struct linux_logo *logo = fb_find_logo(1);
- const unsigned char *logo_data;
- struct linux_logo *logo_dup;
-
- if (!logo || logo->type != LINUX_LOGO_MONO)
- return 0;
-
- /* The logo is __init, so we must make a copy for later use */
- logo_data = kmemdup(logo->data,
- size_mul(DIV_ROUND_UP(logo->width, BITS_PER_BYTE), logo->height),
- GFP_KERNEL);
- if (!logo_data)
- return -ENOMEM;
-
- logo_dup = kmemdup(logo, sizeof(*logo), GFP_KERNEL);
- if (!logo_dup) {
- kfree(logo_data);
- return -ENOMEM;
- }
-
- logo_dup->data = logo_data;
- logo_mono = logo_dup;
-
- return 0;
-}
-
-device_initcall(drm_panic_setup_logo);
-#else
-#define logo_mono ((const struct linux_logo *)NULL)
-#endif
-
-/*
- * Blit & Fill functions
- */
-static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect *clip,
- const u8 *sbuf8, unsigned int spitch, unsigned int scale,
- u32 fg_color)
-{
- unsigned int y, x;
-
- for (y = 0; y < drm_rect_height(clip); y++)
- for (x = 0; x < drm_rect_width(clip); x++)
- if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
- sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color);
-}
-
-static void drm_panic_write_pixel16(void *vaddr, unsigned int offset, u16 color)
-{
- u16 *p = vaddr + offset;
-
- *p = color;
-}
-
-static void drm_panic_write_pixel24(void *vaddr, unsigned int offset, u32 color)
-{
- u8 *p = vaddr + offset;
-
- *p++ = color & 0xff;
- color >>= 8;
- *p++ = color & 0xff;
- color >>= 8;
- *p = color & 0xff;
-}
-
-/*
- * Special case if the pixel crosses page boundaries
- */
-static void drm_panic_write_pixel24_xpage(void *vaddr, struct page *next_page,
- unsigned int offset, u32 color)
-{
- u8 *vaddr2;
- u8 *p = vaddr + offset;
-
- vaddr2 = kmap_local_page_try_from_panic(next_page);
-
- *p++ = color & 0xff;
- color >>= 8;
-
- if (offset == PAGE_SIZE - 1)
- p = vaddr2;
-
- *p++ = color & 0xff;
- color >>= 8;
-
- if (offset == PAGE_SIZE - 2)
- p = vaddr2;
-
- *p = color & 0xff;
- kunmap_local(vaddr2);
-}
-
-static void drm_panic_write_pixel32(void *vaddr, unsigned int offset, u32 color)
-{
- u32 *p = vaddr + offset;
-
- *p = color;
-}
-
-static void drm_panic_write_pixel(void *vaddr, unsigned int offset, u32 color, unsigned int cpp)
-{
- switch (cpp) {
- case 2:
- drm_panic_write_pixel16(vaddr, offset, color);
- break;
- case 3:
- drm_panic_write_pixel24(vaddr, offset, color);
- break;
- case 4:
- drm_panic_write_pixel32(vaddr, offset, color);
- break;
- default:
- pr_debug_once("Can't blit with pixel width %d\n", cpp);
- }
-}
-
-/*
- * The scanout buffer pages are not mapped, so for each pixel,
- * use kmap_local_page_try_from_panic() to map the page, and write the pixel.
- * Try to keep the map from the previous pixel, to avoid too much map/unmap.
- */
-static void drm_panic_blit_page(struct page **pages, unsigned int dpitch,
- unsigned int cpp, const u8 *sbuf8,
- unsigned int spitch, struct drm_rect *clip,
- unsigned int scale, u32 fg32)
-{
- unsigned int y, x;
- unsigned int page = ~0;
- unsigned int height = drm_rect_height(clip);
- unsigned int width = drm_rect_width(clip);
- void *vaddr = NULL;
-
- for (y = 0; y < height; y++) {
- for (x = 0; x < width; x++) {
- if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) {
- unsigned int new_page;
- unsigned int offset;
-
- offset = (y + clip->y1) * dpitch + (x + clip->x1) * cpp;
- new_page = offset >> PAGE_SHIFT;
- offset = offset % PAGE_SIZE;
- if (new_page != page) {
- if (!pages[new_page])
- continue;
- if (vaddr)
- kunmap_local(vaddr);
- page = new_page;
- vaddr = kmap_local_page_try_from_panic(pages[page]);
- }
- if (!vaddr)
- continue;
-
- // Special case for 24bit, as a pixel might cross page boundaries
- if (cpp == 3 && offset + 3 > PAGE_SIZE)
- drm_panic_write_pixel24_xpage(vaddr, pages[page + 1],
- offset, fg32);
- else
- drm_panic_write_pixel(vaddr, offset, fg32, cpp);
- }
- }
- }
- if (vaddr)
- kunmap_local(vaddr);
-}
-
-/*
- * drm_panic_blit - convert a monochrome image to a linear framebuffer
- * @sb: destination scanout buffer
- * @clip: destination rectangle
- * @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
- * @spitch: source pitch in bytes
- * @scale: integer scale, source buffer is scale time smaller than destination
- * rectangle
- * @fg_color: foreground color, in destination format
- *
- * This can be used to draw a font character, which is a monochrome image, to a
- * framebuffer in other supported format.
- */
-static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
- const u8 *sbuf8, unsigned int spitch,
- unsigned int scale, u32 fg_color)
-
-{
- struct iosys_map map;
-
- if (sb->set_pixel)
- return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color);
-
- if (sb->pages)
- return drm_panic_blit_page(sb->pages, sb->pitch[0], sb->format->cpp[0],
- sbuf8, spitch, clip, scale, fg_color);
-
- map = sb->map[0];
- iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
-
- switch (sb->format->cpp[0]) {
- case 2:
- drm_draw_blit16(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
- break;
- case 3:
- drm_draw_blit24(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
- break;
- case 4:
- drm_draw_blit32(&map, sb->pitch[0], sbuf8, spitch,
- drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
- break;
- default:
- WARN_ONCE(1, "Can't blit with pixel width %d\n", sb->format->cpp[0]);
- }
-}
-
-static void drm_panic_fill_pixel(struct drm_scanout_buffer *sb,
- struct drm_rect *clip,
- u32 color)
-{
- unsigned int y, x;
-
- for (y = 0; y < drm_rect_height(clip); y++)
- for (x = 0; x < drm_rect_width(clip); x++)
- sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, color);
-}
-
-static void drm_panic_fill_page(struct page **pages, unsigned int dpitch,
- unsigned int cpp, struct drm_rect *clip,
- u32 color)
-{
- unsigned int y, x;
- unsigned int page = ~0;
- void *vaddr = NULL;
-
- for (y = clip->y1; y < clip->y2; y++) {
- for (x = clip->x1; x < clip->x2; x++) {
- unsigned int new_page;
- unsigned int offset;
-
- offset = y * dpitch + x * cpp;
- new_page = offset >> PAGE_SHIFT;
- offset = offset % PAGE_SIZE;
- if (new_page != page) {
- if (vaddr)
- kunmap_local(vaddr);
- page = new_page;
- vaddr = kmap_local_page_try_from_panic(pages[page]);
- }
- if (!vaddr)
- continue;
-
- // Special case for 24bit, as a pixel might cross page boundaries
- if (cpp == 3 && offset + 3 > PAGE_SIZE)
- drm_panic_write_pixel24_xpage(vaddr, pages[page + 1],
- offset, color);
- else
- drm_panic_write_pixel(vaddr, offset, color, cpp);
- }
- }
- if (vaddr)
- kunmap_local(vaddr);
-}
-
-/*
- * drm_panic_fill - Fill a rectangle with a color
- * @sb: destination scanout buffer
- * @clip: destination rectangle
- * @color: foreground color, in destination format
- *
- * Fill a rectangle with a color, in a linear framebuffer.
- */
-static void drm_panic_fill(struct drm_scanout_buffer *sb, struct drm_rect *clip,
- u32 color)
-{
- struct iosys_map map;
-
- if (sb->set_pixel)
- return drm_panic_fill_pixel(sb, clip, color);
-
- if (sb->pages)
- return drm_panic_fill_page(sb->pages, sb->pitch[0], sb->format->cpp[0],
- clip, color);
-
- map = sb->map[0];
- iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
-
- switch (sb->format->cpp[0]) {
- case 2:
- drm_draw_fill16(&map, sb->pitch[0], drm_rect_height(clip),
- drm_rect_width(clip), color);
- break;
- case 3:
- drm_draw_fill24(&map, sb->pitch[0], drm_rect_height(clip),
- drm_rect_width(clip), color);
- break;
- case 4:
- drm_draw_fill32(&map, sb->pitch[0], drm_rect_height(clip),
- drm_rect_width(clip), color);
- break;
- default:
- WARN_ONCE(1, "Can't fill with pixel width %d\n", sb->format->cpp[0]);
- }
-}
-
-static unsigned int get_max_line_len(const struct drm_panic_line *lines, int len)
-{
- int i;
- unsigned int max = 0;
-
- for (i = 0; i < len; i++)
- max = max(lines[i].len, max);
- return max;
-}
-
-/*
- * Draw a text in a rectangle on a framebuffer. The text is truncated if it overflows the rectangle
- */
-static void draw_txt_rectangle(struct drm_scanout_buffer *sb,
- const struct font_desc *font,
- const struct drm_panic_line *msg,
- unsigned int msg_lines,
- bool centered,
- struct drm_rect *clip,
- u32 color)
-{
- int i, j;
- const u8 *src;
- size_t font_pitch = DIV_ROUND_UP(font->width, 8);
- struct drm_rect rec;
-
- msg_lines = min(msg_lines, drm_rect_height(clip) / font->height);
- for (i = 0; i < msg_lines; i++) {
- size_t line_len = min(msg[i].len, drm_rect_width(clip) / font->width);
-
- rec.y1 = clip->y1 + i * font->height;
- rec.y2 = rec.y1 + font->height;
- rec.x1 = clip->x1;
-
- if (centered)
- rec.x1 += (drm_rect_width(clip) - (line_len * font->width)) / 2;
-
- for (j = 0; j < line_len; j++) {
- src = font_data_glyph_buf(font->data, font->width, font->height,
- (unsigned char)msg[i].txt[j]);
- rec.x2 = rec.x1 + font->width;
- if (src)
- drm_panic_blit(sb, &rec, src, font_pitch, 1, color);
- rec.x1 += font->width;
- }
- }
-}
-
-static void drm_panic_logo_rect(struct drm_rect *rect, const struct font_desc *font)
-{
- if (logo_mono) {
- drm_rect_init(rect, 0, 0, logo_mono->width, logo_mono->height);
- } else {
- int logo_width = get_max_line_len(logo_ascii, logo_ascii_lines) * font->width;
-
- drm_rect_init(rect, 0, 0, logo_width, logo_ascii_lines * font->height);
- }
-}
-
-static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *rect,
- const struct font_desc *font, u32 fg_color)
-{
- if (rect->x2 > sb->width || rect->y2 > sb->height)
- return;
-
- if (logo_mono)
- drm_panic_blit(sb, rect, logo_mono->data,
- DIV_ROUND_UP(drm_rect_width(rect), 8), 1, fg_color);
- else
- draw_txt_rectangle(sb, font, logo_ascii, logo_ascii_lines, false, rect,
- fg_color);
-}
-
-static int draw_panic_screen_user(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
-{
- const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
- struct drm_rect r_screen, r_logo, r_msg;
- unsigned int msg_width, msg_height;
-
- if (!font)
- return -EINVAL;
-
- fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
- bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
-
- r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
- drm_panic_logo_rect(&r_logo, font);
-
- msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
- msg_height = min(panic_msg_lines * font->height, sb->height);
- r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
-
- /* Center the panic message */
- drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, (sb->height - r_msg.y2) / 2);
-
- /* Fill with the background color, and draw text on top */
- drm_panic_fill(sb, &r_screen, bg_color);
-
- if (!drm_rect_overlap(&r_logo, &r_msg))
- drm_panic_logo_draw(sb, &r_logo, font, fg_color);
-
- draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
-
- return 0;
-}
-
-/*
- * 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, font->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 int draw_panic_screen_kmsg(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color)
-{
- 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;
-
- if (!font || font->width > sb->width)
- return -EINVAL;
-
- fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
- bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
-
- yoffset = sb->height - font->height - (sb->height % font->height) / 2;
-
- /* 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--;
- }
- }
-
- return 0;
-}
-
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
-/*
- * It is unwise to allocate memory in the panic callback, so the buffers are
- * pre-allocated. Only 2 buffers and the zlib workspace are needed.
- * Two buffers are enough, using the following buffer usage:
- * 1) kmsg messages are dumped in buffer1
- * 2) kmsg is zlib-compressed into buffer2
- * 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1
- * 4) QR-code image is generated in buffer2
- * The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 bytes for
- * data segments.
- *
- * Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, which fits in
- * a V40 QR-code (177x177).
- *
- * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will be put
- * directly in the QR code.
- * 1) kmsg messages are dumped in buffer1
- * 2) kmsg message is encoded as byte stream in buffer2
- * 3) QR-code image is generated in buffer1
- */
-
static uint panic_qr_version = CONFIG_DRM_PANIC_SCREEN_QR_VERSION;
module_param(panic_qr_version, uint, 0644);
MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR code");
-
-#define MAX_QR_DATA 2956
-#define MAX_ZLIB_RATIO 3
-#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also be > 4071 */
-#define QR_BUFFER2_SIZE 4096
-#define QR_MARGIN 4 /* 4 modules of foreground color around the qr code */
-
-/* Compression parameters */
-#define COMPR_LEVEL 6
-#define WINDOW_BITS 12
-#define MEM_LEVEL 4
-
-static u8 qrbuf1[QR_BUFFER1_SIZE];
-static u8 qrbuf2[QR_BUFFER2_SIZE];
-static struct z_stream_s stream;
-
-static void __init drm_panic_qr_init(void)
-{
- int wssize;
-
- wssize = zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL);
- if (WARN_ON_ONCE(wssize < 0))
- return;
- /* best-effort allocation; can be NULL */
- stream.workspace = kmalloc(wssize, GFP_KERNEL);
-
- /* touch memory so that pages are there in the case of a panic */
- memset(qrbuf1, 0, sizeof(qrbuf1));
- memset(qrbuf2, 0, sizeof(qrbuf2));
- if (stream.workspace)
- memset(stream.workspace, 0, wssize);
-}
-
-static void drm_panic_qr_exit(void)
-{
- kfree(stream.workspace);
- stream.workspace = NULL;
-}
-
-static int drm_panic_get_qr_code_url(u8 **qr_image, unsigned int qr_version)
-{
- struct kmsg_dump_iter iter;
- char url[256];
- size_t kmsg_len, max_kmsg_size;
- char *kmsg;
- int ret;
- size_t max_qr_data_size, url_len;
-
- ret = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=",
- utsname()->machine, utsname()->release);
- if (ret >= sizeof(url))
- return -EINVAL;
- url_len = ret;
-
- max_qr_data_size = drm_panic_qr_max_data_size(qr_version, url_len);
- max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, sizeof(qrbuf1));
-
- /* get kmsg to buffer 1 */
- kmsg_dump_rewind(&iter);
- kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
-
- if (!kmsg_len)
- return -ENODATA;
- kmsg = qrbuf1;
-
-try_again:
- if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
- MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
- return -EINVAL;
-
- stream.next_in = kmsg;
- stream.avail_in = kmsg_len;
- stream.total_in = 0;
- stream.next_out = qrbuf2;
- stream.avail_out = sizeof(qrbuf2);
- stream.total_out = 0;
-
- if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END)
- return -EINVAL;
-
- if (zlib_deflateEnd(&stream) != Z_OK)
- return -EINVAL;
-
- if (stream.total_out > max_qr_data_size) {
- /* too much data for the QR code, so skip the first line and try again */
- kmsg = strchr(kmsg, '\n');
- if (!kmsg)
- return -EINVAL;
- /* skip the first \n */
- kmsg += 1;
- kmsg_len = strlen(kmsg);
- goto try_again;
- }
- *qr_image = qrbuf2;
-
- /* generate qr code image in buffer2 */
- return drm_panic_qr_generate(url, qrbuf2, stream.total_out, sizeof(qrbuf2),
- qrbuf1, sizeof(qrbuf1));
-}
-
-static int drm_panic_get_qr_code_raw(u8 **qr_image, unsigned int qr_version)
-{
- struct kmsg_dump_iter iter;
- size_t kmsg_len;
- size_t max_kmsg_size = min(drm_panic_qr_max_data_size(qr_version, 0),
- sizeof(qrbuf1));
-
- kmsg_dump_rewind(&iter);
- kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
- if (!kmsg_len)
- return -ENODATA;
-
- *qr_image = qrbuf1;
- return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, sizeof(qrbuf1),
- qrbuf2, sizeof(qrbuf2));
-}
-
-static int drm_panic_get_qr_code(u8 **qr_image, unsigned int qr_version)
-{
- if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0)
- return drm_panic_get_qr_code_url(qr_image, qr_version);
- else
- return drm_panic_get_qr_code_raw(qr_image, qr_version);
-}
-
-/*
- * Draw the panic message at the center of the screen, with a QR Code
- */
-static int draw_panic_screen_qr_code(struct drm_scanout_buffer *sb,
- u32 fg_color, u32 bg_color,
- unsigned int qr_version)
-{
- const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
- struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
- unsigned int max_qr_size, scale;
- unsigned int msg_width, msg_height;
- int qr_width, qr_canvas_width, qr_pitch, v_margin;
- u8 *qr_image;
-
- if (!stream.workspace)
- return -ENOMEM;
- if (!font)
- return -EINVAL;
-
- fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
- bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
-
- r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
-
- drm_panic_logo_rect(&r_logo, font);
-
- msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
- msg_height = min(panic_msg_lines * font->height, sb->height);
- r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
-
- max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4);
-
- qr_width = drm_panic_get_qr_code(&qr_image, qr_version);
- if (qr_width <= 0)
- return -ENOSPC;
-
- qr_canvas_width = qr_width + QR_MARGIN * 2;
- scale = max_qr_size / qr_canvas_width;
- /* QR code is not readable if not scaled at least by 2 */
- if (scale < 2)
- return -ENOSPC;
-
- pr_debug("QR width %d and scale %d\n", qr_width, scale);
- r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale);
-
- v_margin = sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg);
- if (v_margin < 0)
- return -ENOSPC;
- v_margin /= 5;
-
- drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin);
- r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1 + QR_MARGIN * scale,
- qr_width * scale, qr_width * scale);
-
- /* Center the panic message */
- drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2,
- 3 * v_margin + drm_rect_height(&r_qr_canvas));
-
- /* Fill with the background color, and draw text on top */
- drm_panic_fill(sb, &r_screen, bg_color);
-
- if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr_canvas))
- drm_panic_logo_draw(sb, &r_logo, font, fg_color);
-
- draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
-
- /* Draw the qr code */
- qr_pitch = DIV_ROUND_UP(qr_width, 8);
- drm_panic_fill(sb, &r_qr_canvas, fg_color);
- drm_panic_fill(sb, &r_qr, bg_color);
- drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
- return 0;
-}
-#else
-static void drm_panic_qr_init(void) {};
-static void drm_panic_qr_exit(void) {};
#endif
static enum drm_panic_type drm_panic_type = -1;
@@ -853,127 +96,6 @@ MODULE_PARM_DESC(panic_screen,
#endif
CONFIG_DRM_PANIC_SCREEN "]");
-/*
- * drm_panic_is_format_supported()
- * @format: a fourcc color code
- * Returns: true if supported, false otherwise.
- *
- * Check if drm_panic will be able to use this color format.
- */
-static bool drm_panic_is_format_supported(const struct drm_format_info *format)
-{
- if (format->num_planes != 1)
- return false;
- return drm_draw_can_convert_from_xrgb8888(format->format);
-}
-
-static int draw_panic_dispatch(struct drm_scanout_buffer *sb, enum drm_panic_type panic_type,
- u32 fg_color, u32 bg_color, unsigned int qr_version)
-{
- int ret;
-
-retry:
- switch (panic_type) {
- case DRM_PANIC_TYPE_KMSG:
- ret = draw_panic_screen_kmsg(sb, fg_color, bg_color);
- if (ret) {
- panic_type = DRM_PANIC_TYPE_USER;
- goto retry;
- }
- break;
-#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
- case DRM_PANIC_TYPE_QR:
- ret = draw_panic_screen_qr_code(sb, fg_color, bg_color, qr_version);
- if (ret) {
- panic_type = DRM_PANIC_TYPE_USER;
- goto retry;
- }
- break;
-#endif
- case DRM_PANIC_TYPE_USER:
- default:
- ret = draw_panic_screen_user(sb, fg_color, bg_color);
- }
-
- return ret;
-}
-
-static void drm_panic_set_description(const char *description)
-{
- u32 len;
-
- if (description) {
- struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
-
- desc_line->txt = description;
- len = strlen(description);
- /* ignore the last newline character */
- if (len && description[len - 1] == '\n')
- len -= 1;
- desc_line->len = len;
- }
-}
-
-static void drm_panic_clear_description(void)
-{
- struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
-
- desc_line->len = 0;
- desc_line->txt = NULL;
-}
-
-/**
- * drm_plane_helper_display_panic_screen - Displays a panic screen according to the given settings
- * @plane: the DRM plane to display to
- * @description: error message to display
- * @panic_type: type of panic screen
- * @fg_color: text foreground color
- * @bg_color: text background color
- * @qr_version: version of the QR code, if any
- *
- * This helper display a panic screen on common primary planes. The panic
- * screen can either display a kernel message, a user message or a QR code.
- *
- * The helper uses struct drm_plane_helper_funcs.get_scanout_buffer, where
- * the plane can provide a scanout buffer that the panic handler can draw to.
- * Currently only linear buffer and a few color formats are supported.
- *
- * Optionally the plane can also provide a &drm_plane_helper_funcs.panic_flush
- * callback, which the DRM panic handler calls after drawing to send additional
- * commands to the hardware to make the scanout buffer visible.
- */
-void drm_plane_helper_display_panic_screen(struct drm_plane *plane, const char *description,
- enum drm_panic_type panic_type,
- u32 fg_color, u32 bg_color, unsigned int qr_version)
-{
- struct drm_scanout_buffer sb = { };
- int ret;
-
- ret = plane->helper_private->get_scanout_buffer(plane, &sb);
-
- if (ret || !drm_panic_is_format_supported(sb.format))
- return;
-
- /* One of these should be set, or it can't draw pixels */
- if (!sb.set_pixel && !sb.pages && iosys_map_is_null(&sb.map[0]))
- return;
-
- drm_panic_set_description(description);
-
- ret = draw_panic_dispatch(&sb, drm_panic_type, fg_color, bg_color, qr_version);
- if (!ret) {
- /*
- * Only flush if we have a panic screen to display. Otherwise
- * it's probably better to leave the display output as-is.
- */
- if (plane->helper_private->panic_flush)
- plane->helper_private->panic_flush(plane);
- }
-
- drm_panic_clear_description();
-}
-EXPORT_SYMBOL(drm_plane_helper_display_panic_screen);
-
#define drm_panic_trylock(dev, flags) \
raw_spin_trylock_irqsave(&(dev)->mode_config.panic_lock, flags)
@@ -1132,17 +254,4 @@ void __init drm_panic_init(void)
CONFIG_DRM_PANIC_SCREEN);
drm_panic_type = DRM_PANIC_TYPE_USER;
}
- drm_panic_qr_init();
-}
-
-/**
- * drm_panic_exit() - Free the resources taken by drm_panic_exit()
- */
-void drm_panic_exit(void)
-{
- drm_panic_qr_exit();
}
-
-#ifdef CONFIG_DRM_KUNIT_TEST
-#include "tests/drm_panic_test.c"
-#endif
diff --git a/drivers/gpu/drm/drm_panic_helper.c b/drivers/gpu/drm/drm_panic_helper.c
new file mode 100644
index 000000000000..3d612f9f3a37
--- /dev/null
+++ b/drivers/gpu/drm/drm_panic_helper.c
@@ -0,0 +1,913 @@
+// SPDX-License-Identifier: GPL-2.0 or MIT
+
+#include <linux/export.h>
+#include <linux/font.h>
+#include <linux/highmem.h>
+#include <linux/iosys-map.h>
+#include <linux/linux_logo.h>
+#include <linux/utsname.h>
+#include <linux/zlib.h>
+
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_rect.h>
+
+#include "drm_draw_internal.h"
+#include "drm_panic_internal.h"
+
+struct drm_panic_line {
+ u32 len;
+ const char *txt;
+};
+
+#define PANIC_LINE(s) {.len = sizeof(s) - 1, .txt = s}
+
+static struct drm_panic_line panic_msg[] = {
+ PANIC_LINE("KERNEL PANIC!"),
+ PANIC_LINE(""),
+ PANIC_LINE("Please reboot your computer."),
+ PANIC_LINE(""),
+ PANIC_LINE(""), /* will be replaced by the panic description */
+};
+
+static const size_t panic_msg_lines = ARRAY_SIZE(panic_msg);
+
+static const struct drm_panic_line logo_ascii[] = {
+ PANIC_LINE(" .--. _"),
+ PANIC_LINE(" |o_o | | |"),
+ PANIC_LINE(" |:_/ | | |"),
+ PANIC_LINE(" // \\ \\ |_|"),
+ PANIC_LINE(" (| | ) _"),
+ PANIC_LINE(" /'\\_ _/`\\ (_)"),
+ PANIC_LINE(" \\___)=(___/"),
+};
+
+static const size_t logo_ascii_lines = ARRAY_SIZE(logo_ascii);
+
+#if defined(CONFIG_LOGO) && !defined(MODULE)
+static const struct linux_logo *logo_mono;
+
+static int __init drm_panic_helper_setup_logo(void)
+{
+ const struct linux_logo *logo = fb_find_logo(1);
+ const unsigned char *logo_data;
+ struct linux_logo *logo_dup;
+
+ if (!logo || logo->type != LINUX_LOGO_MONO)
+ return 0;
+
+ /* The logo is __init, so we must make a copy for later use */
+ logo_data = kmemdup(logo->data,
+ size_mul(DIV_ROUND_UP(logo->width, BITS_PER_BYTE), logo->height),
+ GFP_KERNEL);
+ if (!logo_data)
+ return -ENOMEM;
+
+ logo_dup = kmemdup(logo, sizeof(*logo), GFP_KERNEL);
+ if (!logo_dup) {
+ kfree(logo_data);
+ return -ENOMEM;
+ }
+
+ logo_dup->data = logo_data;
+ logo_mono = logo_dup;
+
+ return 0;
+}
+#else
+#define logo_mono ((const struct linux_logo *)NULL)
+static int __init drm_panic_helper_setup_logo(void)
+{
+ return 0;
+}
+#endif
+
+/*
+ * Blit & Fill functions
+ */
+static void drm_panic_helper_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect *clip,
+ const u8 *sbuf8, unsigned int spitch, unsigned int scale,
+ u32 fg_color)
+{
+ unsigned int y, x;
+
+ for (y = 0; y < drm_rect_height(clip); y++)
+ for (x = 0; x < drm_rect_width(clip); x++)
+ if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale))
+ sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color);
+}
+
+static void drm_panic_helper_write_pixel16(void *vaddr, unsigned int offset, u16 color)
+{
+ u16 *p = vaddr + offset;
+
+ *p = color;
+}
+
+static void drm_panic_helper_write_pixel24(void *vaddr, unsigned int offset, u32 color)
+{
+ u8 *p = vaddr + offset;
+
+ *p++ = color & 0xff;
+ color >>= 8;
+ *p++ = color & 0xff;
+ color >>= 8;
+ *p = color & 0xff;
+}
+
+/*
+ * Special case if the pixel crosses page boundaries
+ */
+static void drm_panic_helper_write_pixel24_xpage(void *vaddr, struct page *next_page,
+ unsigned int offset, u32 color)
+{
+ u8 *vaddr2;
+ u8 *p = vaddr + offset;
+
+ vaddr2 = kmap_local_page_try_from_panic(next_page);
+
+ *p++ = color & 0xff;
+ color >>= 8;
+
+ if (offset == PAGE_SIZE - 1)
+ p = vaddr2;
+
+ *p++ = color & 0xff;
+ color >>= 8;
+
+ if (offset == PAGE_SIZE - 2)
+ p = vaddr2;
+
+ *p = color & 0xff;
+ kunmap_local(vaddr2);
+}
+
+static void drm_panic_helper_write_pixel32(void *vaddr, unsigned int offset, u32 color)
+{
+ u32 *p = vaddr + offset;
+
+ *p = color;
+}
+
+static void drm_panic_helper_write_pixel(void *vaddr, unsigned int offset, u32 color,
+ unsigned int cpp)
+{
+ switch (cpp) {
+ case 2:
+ drm_panic_helper_write_pixel16(vaddr, offset, color);
+ break;
+ case 3:
+ drm_panic_helper_write_pixel24(vaddr, offset, color);
+ break;
+ case 4:
+ drm_panic_helper_write_pixel32(vaddr, offset, color);
+ break;
+ default:
+ pr_debug_once("Can't blit with pixel width %d\n", cpp);
+ }
+}
+
+/*
+ * The scanout buffer pages are not mapped, so for each pixel,
+ * use kmap_local_page_try_from_panic() to map the page, and write the pixel.
+ * Try to keep the map from the previous pixel, to avoid too much map/unmap.
+ */
+static void drm_panic_helper_blit_page(struct page **pages, unsigned int dpitch,
+ unsigned int cpp, const u8 *sbuf8,
+ unsigned int spitch, struct drm_rect *clip,
+ unsigned int scale, u32 fg32)
+{
+ unsigned int y, x;
+ unsigned int page = ~0;
+ unsigned int height = drm_rect_height(clip);
+ unsigned int width = drm_rect_width(clip);
+ void *vaddr = NULL;
+
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ if (drm_draw_is_pixel_fg(sbuf8, spitch, x / scale, y / scale)) {
+ unsigned int new_page;
+ unsigned int offset;
+
+ offset = (y + clip->y1) * dpitch + (x + clip->x1) * cpp;
+ new_page = offset >> PAGE_SHIFT;
+ offset = offset % PAGE_SIZE;
+ if (new_page != page) {
+ if (!pages[new_page])
+ continue;
+ if (vaddr)
+ kunmap_local(vaddr);
+ page = new_page;
+ vaddr = kmap_local_page_try_from_panic(pages[page]);
+ }
+ if (!vaddr)
+ continue;
+
+ // Special case for 24bit, as a pixel might cross page boundaries
+ if (cpp == 3 && offset + 3 > PAGE_SIZE)
+ drm_panic_helper_write_pixel24_xpage(vaddr,
+ pages[page + 1],
+ offset, fg32);
+ else
+ drm_panic_helper_write_pixel(vaddr, offset, fg32, cpp);
+ }
+ }
+ }
+ if (vaddr)
+ kunmap_local(vaddr);
+}
+
+/*
+ * drm_panic_helper_blit - convert a monochrome image to a linear framebuffer
+ * @sb: destination scanout buffer
+ * @clip: destination rectangle
+ * @sbuf8: source buffer, in monochrome format, 8 pixels per byte.
+ * @spitch: source pitch in bytes
+ * @scale: integer scale, source buffer is scale time smaller than destination
+ * rectangle
+ * @fg_color: foreground color, in destination format
+ *
+ * This can be used to draw a font character, which is a monochrome image, to a
+ * framebuffer in other supported format.
+ */
+static void drm_panic_helper_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip,
+ const u8 *sbuf8, unsigned int spitch,
+ unsigned int scale, u32 fg_color)
+
+{
+ struct iosys_map map;
+
+ if (sb->set_pixel)
+ return drm_panic_helper_blit_pixel(sb, clip, sbuf8, spitch, scale, fg_color);
+
+ if (sb->pages)
+ return drm_panic_helper_blit_page(sb->pages, sb->pitch[0], sb->format->cpp[0],
+ sbuf8, spitch, clip, scale, fg_color);
+
+ map = sb->map[0];
+ iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
+
+ switch (sb->format->cpp[0]) {
+ case 2:
+ drm_draw_blit16(&map, sb->pitch[0], sbuf8, spitch,
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
+ break;
+ case 3:
+ drm_draw_blit24(&map, sb->pitch[0], sbuf8, spitch,
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
+ break;
+ case 4:
+ drm_draw_blit32(&map, sb->pitch[0], sbuf8, spitch,
+ drm_rect_height(clip), drm_rect_width(clip), scale, fg_color);
+ break;
+ default:
+ WARN_ONCE(1, "Can't blit with pixel width %d\n", sb->format->cpp[0]);
+ }
+}
+
+static void drm_panic_helper_fill_pixel(struct drm_scanout_buffer *sb,
+ struct drm_rect *clip,
+ u32 color)
+{
+ unsigned int y, x;
+
+ for (y = 0; y < drm_rect_height(clip); y++)
+ for (x = 0; x < drm_rect_width(clip); x++)
+ sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, color);
+}
+
+static void drm_panic_helper_fill_page(struct page **pages, unsigned int dpitch,
+ unsigned int cpp, struct drm_rect *clip,
+ u32 color)
+{
+ unsigned int y, x;
+ unsigned int page = ~0;
+ void *vaddr = NULL;
+
+ for (y = clip->y1; y < clip->y2; y++) {
+ for (x = clip->x1; x < clip->x2; x++) {
+ unsigned int new_page;
+ unsigned int offset;
+
+ offset = y * dpitch + x * cpp;
+ new_page = offset >> PAGE_SHIFT;
+ offset = offset % PAGE_SIZE;
+ if (new_page != page) {
+ if (vaddr)
+ kunmap_local(vaddr);
+ page = new_page;
+ vaddr = kmap_local_page_try_from_panic(pages[page]);
+ }
+ if (!vaddr)
+ continue;
+
+ // Special case for 24bit, as a pixel might cross page boundaries
+ if (cpp == 3 && offset + 3 > PAGE_SIZE)
+ drm_panic_helper_write_pixel24_xpage(vaddr, pages[page + 1],
+ offset, color);
+ else
+ drm_panic_helper_write_pixel(vaddr, offset, color, cpp);
+ }
+ }
+ if (vaddr)
+ kunmap_local(vaddr);
+}
+
+/*
+ * drm_panic_helper_fill - Fill a rectangle with a color
+ * @sb: destination scanout buffer
+ * @clip: destination rectangle
+ * @color: foreground color, in destination format
+ *
+ * Fill a rectangle with a color, in a linear framebuffer.
+ */
+static void drm_panic_helper_fill(struct drm_scanout_buffer *sb, struct drm_rect *clip,
+ u32 color)
+{
+ struct iosys_map map;
+
+ if (sb->set_pixel)
+ return drm_panic_helper_fill_pixel(sb, clip, color);
+
+ if (sb->pages)
+ return drm_panic_helper_fill_page(sb->pages, sb->pitch[0], sb->format->cpp[0],
+ clip, color);
+
+ map = sb->map[0];
+ iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]);
+
+ switch (sb->format->cpp[0]) {
+ case 2:
+ drm_draw_fill16(&map, sb->pitch[0], drm_rect_height(clip),
+ drm_rect_width(clip), color);
+ break;
+ case 3:
+ drm_draw_fill24(&map, sb->pitch[0], drm_rect_height(clip),
+ drm_rect_width(clip), color);
+ break;
+ case 4:
+ drm_draw_fill32(&map, sb->pitch[0], drm_rect_height(clip),
+ drm_rect_width(clip), color);
+ break;
+ default:
+ WARN_ONCE(1, "Can't fill with pixel width %d\n", sb->format->cpp[0]);
+ }
+}
+
+static unsigned int get_max_line_len(const struct drm_panic_line *lines, int len)
+{
+ int i;
+ unsigned int max = 0;
+
+ for (i = 0; i < len; i++)
+ max = max(lines[i].len, max);
+ return max;
+}
+
+/*
+ * Draw a text in a rectangle on a framebuffer. The text is truncated if it overflows the rectangle
+ */
+static void draw_txt_rectangle(struct drm_scanout_buffer *sb,
+ const struct font_desc *font,
+ const struct drm_panic_line *msg,
+ unsigned int msg_lines,
+ bool centered,
+ struct drm_rect *clip,
+ u32 color)
+{
+ int i, j;
+ const u8 *src;
+ size_t font_pitch = DIV_ROUND_UP(font->width, 8);
+ struct drm_rect rec;
+
+ msg_lines = min(msg_lines, drm_rect_height(clip) / font->height);
+ for (i = 0; i < msg_lines; i++) {
+ size_t line_len = min(msg[i].len, drm_rect_width(clip) / font->width);
+
+ rec.y1 = clip->y1 + i * font->height;
+ rec.y2 = rec.y1 + font->height;
+ rec.x1 = clip->x1;
+
+ if (centered)
+ rec.x1 += (drm_rect_width(clip) - (line_len * font->width)) / 2;
+
+ for (j = 0; j < line_len; j++) {
+ src = font_data_glyph_buf(font->data, font->width, font->height,
+ (unsigned char)msg[i].txt[j]);
+ rec.x2 = rec.x1 + font->width;
+ if (src)
+ drm_panic_helper_blit(sb, &rec, src, font_pitch, 1, color);
+ rec.x1 += font->width;
+ }
+ }
+}
+
+static void drm_panic_helper_logo_rect(struct drm_rect *rect, const struct font_desc *font)
+{
+ if (logo_mono) {
+ drm_rect_init(rect, 0, 0, logo_mono->width, logo_mono->height);
+ } else {
+ int logo_width = get_max_line_len(logo_ascii, logo_ascii_lines) * font->width;
+
+ drm_rect_init(rect, 0, 0, logo_width, logo_ascii_lines * font->height);
+ }
+}
+
+static void drm_panic_helper_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *rect,
+ const struct font_desc *font, u32 fg_color)
+{
+ if (rect->x2 > sb->width || rect->y2 > sb->height)
+ return;
+
+ if (logo_mono)
+ drm_panic_helper_blit(sb, rect, logo_mono->data,
+ DIV_ROUND_UP(drm_rect_width(rect), 8), 1, fg_color);
+ else
+ draw_txt_rectangle(sb, font, logo_ascii, logo_ascii_lines, false, rect,
+ fg_color);
+}
+
+static int drm_panic_helper_draw_screen_user(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color)
+{
+ const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
+ struct drm_rect r_screen, r_logo, r_msg;
+ unsigned int msg_width, msg_height;
+
+ if (!font)
+ return -EINVAL;
+
+ fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
+ bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
+
+ r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
+ drm_panic_helper_logo_rect(&r_logo, font);
+
+ msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
+ msg_height = min(panic_msg_lines * font->height, sb->height);
+ r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
+
+ /* Center the panic message */
+ drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, (sb->height - r_msg.y2) / 2);
+
+ /* Fill with the background color, and draw text on top */
+ drm_panic_helper_fill(sb, &r_screen, bg_color);
+
+ if (!drm_rect_overlap(&r_logo, &r_msg))
+ drm_panic_helper_logo_draw(sb, &r_logo, font, fg_color);
+
+ draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
+
+ return 0;
+}
+
+/*
+ * 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, font->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 int drm_panic_helper_draw_screen_kmsg(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color)
+{
+ 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;
+
+ if (!font || font->width > sb->width)
+ return -EINVAL;
+
+ fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
+ bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
+
+ yoffset = sb->height - font->height - (sb->height % font->height) / 2;
+
+ /* Fill with the background color, and draw text on top */
+ drm_panic_helper_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--;
+ }
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
+/*
+ * It is unwise to allocate memory in the panic callback, so the buffers are
+ * pre-allocated. Only 2 buffers and the zlib workspace are needed.
+ * Two buffers are enough, using the following buffer usage:
+ * 1) kmsg messages are dumped in buffer1
+ * 2) kmsg is zlib-compressed into buffer2
+ * 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1
+ * 4) QR-code image is generated in buffer2
+ * The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 bytes for
+ * data segments.
+ *
+ * Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, which fits in
+ * a V40 QR-code (177x177).
+ *
+ * If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will be put
+ * directly in the QR code.
+ * 1) kmsg messages are dumped in buffer1
+ * 2) kmsg message is encoded as byte stream in buffer2
+ * 3) QR-code image is generated in buffer1
+ */
+
+#define MAX_QR_DATA 2956
+#define MAX_ZLIB_RATIO 3
+#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also be > 4071 */
+#define QR_BUFFER2_SIZE 4096
+#define QR_MARGIN 4 /* 4 modules of foreground color around the qr code */
+
+/* Compression parameters */
+#define COMPR_LEVEL 6
+#define WINDOW_BITS 12
+#define MEM_LEVEL 4
+
+static u8 qrbuf1[QR_BUFFER1_SIZE];
+static u8 qrbuf2[QR_BUFFER2_SIZE];
+static struct z_stream_s stream;
+
+static void __init drm_panic_helper_qr_init(void)
+{
+ int wssize;
+
+ wssize = zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL);
+ if (WARN_ON_ONCE(wssize < 0))
+ return;
+ /* best-effort allocation; can be NULL */
+ stream.workspace = kmalloc(wssize, GFP_KERNEL);
+
+ /* touch memory so that pages are there in the case of a panic */
+ memset(qrbuf1, 0, sizeof(qrbuf1));
+ memset(qrbuf2, 0, sizeof(qrbuf2));
+ if (stream.workspace)
+ memset(stream.workspace, 0, wssize);
+}
+
+static void __exit drm_panic_helper_qr_exit(void)
+{
+ kfree(stream.workspace);
+ stream.workspace = NULL;
+}
+
+static int drm_panic_helper_get_qr_code_url(u8 **qr_image, unsigned int qr_version)
+{
+ struct kmsg_dump_iter iter;
+ char url[256];
+ size_t kmsg_len, max_kmsg_size;
+ char *kmsg;
+ int ret;
+ size_t max_qr_data_size, url_len;
+
+ ret = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&z=",
+ utsname()->machine, utsname()->release);
+ if (ret >= sizeof(url))
+ return -EINVAL;
+ url_len = ret;
+
+ max_qr_data_size = drm_panic_helper_qr_max_data_size(qr_version, url_len);
+ max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, sizeof(qrbuf1));
+
+ /* get kmsg to buffer 1 */
+ kmsg_dump_rewind(&iter);
+ kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
+
+ if (!kmsg_len)
+ return -ENODATA;
+ kmsg = qrbuf1;
+
+try_again:
+ if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
+ MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
+ return -EINVAL;
+
+ stream.next_in = kmsg;
+ stream.avail_in = kmsg_len;
+ stream.total_in = 0;
+ stream.next_out = qrbuf2;
+ stream.avail_out = sizeof(qrbuf2);
+ stream.total_out = 0;
+
+ if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END)
+ return -EINVAL;
+
+ if (zlib_deflateEnd(&stream) != Z_OK)
+ return -EINVAL;
+
+ if (stream.total_out > max_qr_data_size) {
+ /* too much data for the QR code, so skip the first line and try again */
+ kmsg = strchr(kmsg, '\n');
+ if (!kmsg)
+ return -EINVAL;
+ /* skip the first \n */
+ kmsg += 1;
+ kmsg_len = strlen(kmsg);
+ goto try_again;
+ }
+ *qr_image = qrbuf2;
+
+ /* generate qr code image in buffer2 */
+ return drm_panic_helper_qr_generate(url, qrbuf2, stream.total_out, ARRAY_SIZE(qrbuf2),
+ qrbuf1, sizeof(qrbuf1));
+}
+
+static int drm_panic_helper_get_qr_code_raw(u8 **qr_image, unsigned int qr_version)
+{
+ struct kmsg_dump_iter iter;
+ size_t kmsg_len;
+ size_t max_kmsg_size = min(drm_panic_helper_qr_max_data_size(qr_version, 0),
+ sizeof(qrbuf1));
+
+ kmsg_dump_rewind(&iter);
+ kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
+ if (!kmsg_len)
+ return -ENODATA;
+
+ *qr_image = qrbuf1;
+ return drm_panic_helper_qr_generate(NULL, qrbuf1, kmsg_len, sizeof(qrbuf1),
+ qrbuf2, sizeof(qrbuf2));
+}
+
+static int drm_panic_helper_get_qr_code(u8 **qr_image, unsigned int qr_version)
+{
+ if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0)
+ return drm_panic_helper_get_qr_code_url(qr_image, qr_version);
+ else
+ return drm_panic_helper_get_qr_code_raw(qr_image, qr_version);
+}
+
+/*
+ * Draw the panic message at the center of the screen, with a QR Code
+ */
+static int drm_panic_helper_draw_screen_qr_code(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version)
+{
+ const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
+ struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
+ unsigned int max_qr_size, scale;
+ unsigned int msg_width, msg_height;
+ int qr_width, qr_canvas_width, qr_pitch, v_margin;
+ u8 *qr_image;
+
+ if (!stream.workspace)
+ return -ENOMEM;
+ if (!font)
+ return -EINVAL;
+
+ fg_color = drm_draw_color_from_xrgb8888(fg_color, sb->format->format);
+ bg_color = drm_draw_color_from_xrgb8888(bg_color, sb->format->format);
+
+ r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
+
+ drm_panic_helper_logo_rect(&r_logo, font);
+
+ msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
+ msg_height = min(panic_msg_lines * font->height, sb->height);
+ r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
+
+ max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4);
+
+ qr_width = drm_panic_helper_get_qr_code(&qr_image, qr_version);
+ if (qr_width <= 0)
+ return -ENOSPC;
+
+ qr_canvas_width = qr_width + QR_MARGIN * 2;
+ scale = max_qr_size / qr_canvas_width;
+ /* QR code is not readable if not scaled at least by 2 */
+ if (scale < 2)
+ return -ENOSPC;
+
+ pr_debug("QR width %d and scale %d\n", qr_width, scale);
+ r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale);
+
+ v_margin = sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg);
+ if (v_margin < 0)
+ return -ENOSPC;
+ v_margin /= 5;
+
+ drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin);
+ r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1 + QR_MARGIN * scale,
+ qr_width * scale, qr_width * scale);
+
+ /* Center the panic message */
+ drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2,
+ 3 * v_margin + drm_rect_height(&r_qr_canvas));
+
+ /* Fill with the background color, and draw text on top */
+ drm_panic_helper_fill(sb, &r_screen, bg_color);
+
+ if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr_canvas))
+ drm_panic_helper_logo_draw(sb, &r_logo, font, fg_color);
+
+ draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
+
+ /* Draw the qr code */
+ qr_pitch = DIV_ROUND_UP(qr_width, 8);
+ drm_panic_helper_fill(sb, &r_qr_canvas, fg_color);
+ drm_panic_helper_fill(sb, &r_qr, bg_color);
+ drm_panic_helper_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
+ return 0;
+}
+#else
+static void __init drm_panic_helper_qr_init(void) {};
+static void __exit drm_panic_helper_qr_exit(void) {};
+#endif
+
+/*
+ * drm_panic_helper_format_is_supported()
+ * @format: a fourcc color code
+ * Returns: true if supported, false otherwise.
+ *
+ * Check if drm_panic will be able to use this color format.
+ */
+static bool drm_panic_helper_format_is_supported(const struct drm_format_info *format)
+{
+ if (format->num_planes != 1)
+ return false;
+ return drm_draw_can_convert_from_xrgb8888(format->format);
+}
+
+static int draw_panic_helper_dispatch(struct drm_scanout_buffer *sb,
+ enum drm_panic_type panic_type,
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version)
+{
+ int ret;
+
+retry:
+ switch (panic_type) {
+ case DRM_PANIC_TYPE_KMSG:
+ ret = drm_panic_helper_draw_screen_kmsg(sb, fg_color, bg_color);
+ if (ret) {
+ panic_type = DRM_PANIC_TYPE_USER;
+ goto retry;
+ }
+ break;
+#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
+ case DRM_PANIC_TYPE_QR:
+ ret = drm_panic_helper_draw_screen_qr_code(sb, fg_color, bg_color, qr_version);
+ if (ret) {
+ panic_type = DRM_PANIC_TYPE_USER;
+ goto retry;
+ }
+ break;
+#endif
+ case DRM_PANIC_TYPE_USER:
+ default:
+ ret = drm_panic_helper_draw_screen_user(sb, fg_color, bg_color);
+ }
+
+ return ret;
+}
+
+static void drm_panic_helper_set_description(const char *description)
+{
+ u32 len;
+
+ if (description) {
+ struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
+
+ desc_line->txt = description;
+ len = strlen(description);
+ /* ignore the last newline character */
+ if (len && description[len - 1] == '\n')
+ len -= 1;
+ desc_line->len = len;
+ }
+}
+
+static void drm_panic_helper_clear_description(void)
+{
+ struct drm_panic_line *desc_line = &panic_msg[panic_msg_lines - 1];
+
+ desc_line->len = 0;
+ desc_line->txt = NULL;
+}
+
+/**
+ * drm_plane_helper_display_panic_screen - Displays a panic screen according to the given settings
+ * @plane: the DRM plane to display to
+ * @description: error message to display
+ * @panic_type: type of panic screen
+ * @fg_color: text foreground color
+ * @bg_color: text background color
+ * @qr_version: version of the QR code, if any
+ *
+ * This helper display a panic screen on common primary planes. The panic
+ * screen can either display a kernel message, a user message or a QR code.
+ *
+ * The helper uses struct drm_plane_helper_funcs.get_scanout_buffer, where
+ * the plane can provide a scanout buffer that the panic handler can draw to.
+ * Currently only linear buffer and a few color formats are supported.
+ *
+ * Optionally the plane can also provide a &drm_plane_helper_funcs.panic_flush
+ * callback, which the DRM panic handler calls after drawing to send additional
+ * commands to the hardware to make the scanout buffer visible.
+ */
+void drm_plane_helper_display_panic_screen(struct drm_plane *plane, const char *description,
+ enum drm_panic_type panic_type,
+ u32 fg_color, u32 bg_color, unsigned int qr_version)
+{
+ struct drm_scanout_buffer sb = { };
+ int ret;
+
+ ret = plane->helper_private->get_scanout_buffer(plane, &sb);
+
+ if (ret || !drm_panic_helper_format_is_supported(sb.format))
+ return;
+
+ /* One of these should be set, or it can't draw pixels */
+ if (!sb.set_pixel && !sb.pages && iosys_map_is_null(&sb.map[0]))
+ return;
+
+ drm_panic_helper_set_description(description);
+
+ ret = draw_panic_helper_dispatch(&sb, panic_type, fg_color, bg_color, qr_version);
+ if (!ret) {
+ /*
+ * Only flush if we have a panic screen to display. Otherwise
+ * it's probably better to leave the display output as-is.
+ */
+ if (plane->helper_private->panic_flush)
+ plane->helper_private->panic_flush(plane);
+ }
+
+ drm_panic_helper_clear_description();
+}
+EXPORT_SYMBOL(drm_plane_helper_display_panic_screen);
+
+int __init drm_panic_helper_init(void)
+{
+ drm_panic_helper_setup_logo();
+ drm_panic_helper_qr_init();
+
+ return 0;
+}
+
+void __exit drm_panic_helper_exit(void)
+{
+ drm_panic_helper_qr_exit();
+}
+
+#ifdef CONFIG_DRM_KUNIT_TEST
+#include "tests/drm_panic_helper_test.c"
+#endif
diff --git a/drivers/gpu/drm/drm_panic_qr.rs b/drivers/gpu/drm/drm_panic_helper_qr.rs
similarity index 99%
rename from drivers/gpu/drm/drm_panic_qr.rs
rename to drivers/gpu/drm/drm_panic_helper_qr.rs
index ac27e86c601c..22966956331e 100644
--- a/drivers/gpu/drm/drm_panic_qr.rs
+++ b/drivers/gpu/drm/drm_panic_helper_qr.rs
@@ -938,7 +938,7 @@ fn draw_all(&mut self, data: impl Iterator<Item = u8>) {
///
/// They must remain valid for the duration of the function call.
#[export]
-pub unsafe extern "C" fn drm_panic_qr_generate(
+pub unsafe extern "C" fn drm_panic_helper_qr_generate(
url: *const kernel::ffi::c_char,
data: *mut u8,
data_len: usize,
@@ -994,7 +994,7 @@ fn draw_all(&mut self, data: impl Iterator<Item = u8>) {
/// Always safe to call.
// Required to be unsafe due to the `#[export]` annotation.
#[export]
-pub unsafe extern "C" fn drm_panic_qr_max_data_size(version: u8, url_len: usize) -> usize {
+pub unsafe extern "C" fn drm_panic_helper_qr_max_data_size(version: u8, url_len: usize) -> usize {
#[expect(clippy::manual_range_contains)]
if version < 1 || version > 40 {
return 0;
diff --git a/drivers/gpu/drm/drm_panic_internal.h b/drivers/gpu/drm/drm_panic_internal.h
index c68d64c56e15..8aa91e80eb4d 100644
--- a/drivers/gpu/drm/drm_panic_internal.h
+++ b/drivers/gpu/drm/drm_panic_internal.h
@@ -52,4 +52,8 @@ static inline void drm_panic_lock(struct drm_device *dev, unsigned long flags) {
static inline void drm_panic_unlock(struct drm_device *dev, unsigned long flags) {}
#endif
+/* drm_panic_helper.c */
+int drm_panic_helper_init(void);
+void drm_panic_helper_exit(void);
+
#endif /* __DRM_PANIC_INTERNAL_H__ */
diff --git a/drivers/gpu/drm/tests/drm_panic_test.c b/drivers/gpu/drm/tests/drm_panic_helper_test.c
similarity index 88%
rename from drivers/gpu/drm/tests/drm_panic_test.c
rename to drivers/gpu/drm/tests/drm_panic_helper_test.c
index 196496238d23..fcff8d2dccbe 100644
--- a/drivers/gpu/drm/tests/drm_panic_test.c
+++ b/drivers/gpu/drm/tests/drm_panic_helper_test.c
@@ -3,7 +3,7 @@
* Copyright (c) 2025 Red Hat.
* Author: Jocelyn Falempe <jfalempe@redhat.com>
*
- * KUNIT tests for drm panic
+ * KUNIT tests for DRM panic helpers
*/
#include <drm/drm_fourcc.h>
@@ -41,24 +41,24 @@ struct drm_test_mode {
.width = w, \
.height = h, \
.format = f, \
- .draw_screen = draw_panic_screen_ ## name ## _default, \
+ .draw_screen = drm_panic_helper_draw_screen_ ## name ## _default, \
.fname = #name, \
}, \
-static int draw_panic_screen_user_default(struct drm_scanout_buffer *sb)
+static int drm_panic_helper_draw_screen_user_default(struct drm_scanout_buffer *sb)
{
- return draw_panic_screen_user(sb, 0x00ffffff, 0x00000000);
+ return drm_panic_helper_draw_screen_user(sb, 0x00ffffff, 0x00000000);
}
-static int draw_panic_screen_kmsg_default(struct drm_scanout_buffer *sb)
+static int drm_panic_helper_draw_screen_kmsg_default(struct drm_scanout_buffer *sb)
{
- return draw_panic_screen_kmsg(sb, 0x00ffffff, 0x00000000);
+ return drm_panic_helper_draw_screen_kmsg(sb, 0x00ffffff, 0x00000000);
}
#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
-static int draw_panic_screen_qr_code_default(struct drm_scanout_buffer *sb)
+static int drm_panic_helper_draw_screen_qr_code_default(struct drm_scanout_buffer *sb)
{
- return draw_panic_screen_qr_code(sb, 0x00ffffff, 0x00000000, 40);
+ return drm_panic_helper_draw_screen_qr_code(sb, 0x00ffffff, 0x00000000, 40);
}
#endif
@@ -81,7 +81,7 @@ static int drm_test_panic_init(struct kunit *test)
test->priv = priv;
- drm_panic_set_description("Kunit testing");
+ drm_panic_helper_set_description("Kunit testing");
return 0;
}
@@ -226,10 +226,10 @@ static struct kunit_case drm_panic_screen_user_test[] = {
{ }
};
-static struct kunit_suite drm_panic_suite = {
- .name = "drm_panic",
+static struct kunit_suite drm_panic_helper_suite = {
+ .name = "drm_panic_helper",
.init = drm_test_panic_init,
.test_cases = drm_panic_screen_user_test,
};
-kunit_test_suite(drm_panic_suite);
+kunit_test_suite(drm_panic_helper_suite);
diff --git a/include/drm/drm_panic.h b/include/drm/drm_panic.h
index 99572b7eeab9..3e628cbaffca 100644
--- a/include/drm/drm_panic.h
+++ b/include/drm/drm_panic.h
@@ -85,11 +85,4 @@ struct drm_scanout_buffer {
void *private;
};
-#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
-size_t drm_panic_qr_max_data_size(u8 version, size_t url_len);
-
-u8 drm_panic_qr_generate(const char *url, u8 *data, size_t data_len, size_t data_size,
- u8 *tmp, size_t tmp_size);
-#endif
-
#endif /* __DRM_PANIC_H__ */
diff --git a/include/drm/drm_panic_helper.h b/include/drm/drm_panic_helper.h
index e296620bbe34..6dc63288bc2a 100644
--- a/include/drm/drm_panic_helper.h
+++ b/include/drm/drm_panic_helper.h
@@ -16,6 +16,11 @@ void drm_plane_helper_display_panic_screen(struct drm_plane *plane,
u32 fg_color, u32 bg_color,
unsigned int qr_version);
+/* drm_panic_helper_qr.rs */
+size_t drm_panic_helper_qr_max_data_size(u8 version, size_t url_len);
+u8 drm_panic_helper_qr_generate(const char *url, u8 *data, size_t data_len, size_t data_size,
+ u8 *tmp, size_t tmp_size);
+
#if IS_ENABLED(CONFIG_DRM_PANIC)
#define DRM_PANIC_PLANE_FUNCS \
.display_panic_screen = drm_plane_helper_display_panic_screen
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 1124785e210b..0a11b1f5c4b3 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -102,8 +102,8 @@
#include <../../drivers/base/base.h>
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
-// Used by `#[export]` in `drivers/gpu/drm/drm_panic_qr.rs`.
-#include <drm/drm_panic.h>
+// Used by `#[export]` in `drivers/gpu/drm/drm_panic_helper_qr.rs`.
+#include <drm/drm_panic_helper.h>
#endif
/* `bindgen` gets confused at certain things. */
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 11/12] drm/panic: Move panic display code into helper library
2026-08-18 12:28 ` [PATCH 11/12] drm/panic: Move panic display code into helper library Thomas Zimmermann
@ 2026-08-18 13:13 ` sashiko-bot
2026-08-18 15:55 ` Randy Dunlap
1 sibling, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:13 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: wsa+renesas, imx, ojeda, Frank.Li, intel-xe, linux-hyperv,
intel-gfx, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] Missing fallback stubs for `drm_panic_helper_init` and `drm_panic_helper_exit` cause link failure when `CONFIG_DRM_PANIC_HELPER` is disabled.
- [Low] `DRM_KMS_HELPER` selects `DRM_PANIC_HELPER` without satisfying its `DRM_TTM` dependency.
--
--- Patch [11]: [PATCH 11/12] drm/panic: Move panic display code into helper library ---
commit 3d718c4f09d4c3ad0b378e7ca17f9b9b1d4d1627
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Move panic display code into helper library
This commit extracts panic display functionality from drm_panic.c into a
new drm_panic_helper.c file. The KMS helper module is updated to link
this new helper object, and relevant Kconfig and Makefile entries are
added.
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 323422861e8f6..cdfd72d9a9c5e 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -45,6 +45,7 @@ config DRM_MIPI_DSI
> config DRM_KMS_HELPER
> tristate
> depends on DRM
> + select DRM_PANIC_HELPER if DRM_PANIC
> select FB_CORE if DRM_FBDEV_EMULATION
> help
[ ... ]
> @@ -130,6 +131,10 @@ config DRM_PANIC_SCREEN_QR_VERSION
> Smaller QR code are easier to read, but will contain less debugging
> data. Default is 40.
>
> +config DRM_PANIC_HELPER
> + bool
> + depends on DRM_TTM
[Severity: Low]
Will this cause an unmet direct dependency warning in Kconfig?
DRM_KMS_HELPER selects DRM_PANIC_HELPER, but it doesn't inherit or ensure
the DRM_TTM dependency is met. If a kernel is configured with DRM_KMS_HELPER
and DRM_PANIC enabled, but without DRM_TTM, it might lead to Kconfig warnings
or a build failure.
> diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c
> index 0c7550c0462b5..b5d5b469b4443 100644
> --- a/drivers/gpu/drm/drm_kms_helper_common.c
> +++ b/drivers/gpu/drm/drm_kms_helper_common.c
> @@ -27,6 +27,20 @@
>
> #include <linux/module.h>
>
> +#include "drm_panic_internal.h"
> +
> +static int __init drm_kms_helper_init(void)
> +{
> + return drm_panic_helper_init();
> +}
> +module_init(drm_kms_helper_init);
[ ... ]
> diff --git a/drivers/gpu/drm/drm_panic_internal.h b/drivers/gpu/drm/drm_panic_internal.h
> index c68d64c56e156..8aa91e80eb4dc 100644
> --- a/drivers/gpu/drm/drm_panic_internal.h
> +++ b/drivers/gpu/drm/drm_panic_internal.h
> @@ -52,4 +52,8 @@ static inline void drm_panic_lock(struct drm_device *dev, unsigned long flags) {
> static inline void drm_panic_unlock(struct drm_device *dev, unsigned long flags) {}
> #endif
>
> +/* drm_panic_helper.c */
> +int drm_panic_helper_init(void);
> +void drm_panic_helper_exit(void);
> +
[Severity: Low]
Does this cause a link failure when CONFIG_DRM_PANIC_HELPER is disabled?
The calls to drm_panic_helper_init() and drm_panic_helper_exit() in
drm_kms_helper_common.c are unconditional. Without fallback static inline
stubs in drm_panic_internal.h when the feature is disabled, it seems this
will lead to unresolved external references during the linking phase.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=11
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH 11/12] drm/panic: Move panic display code into helper library
2026-08-18 12:28 ` [PATCH 11/12] drm/panic: Move panic display code into helper library Thomas Zimmermann
2026-08-18 13:13 ` sashiko-bot
@ 2026-08-18 15:55 ` Randy Dunlap
1 sibling, 0 replies; 23+ messages in thread
From: Randy Dunlap @ 2026-08-18 15:55 UTC (permalink / raw)
To: Thomas Zimmermann, jfalempe, javierm, airlied, simona,
maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews
Hi,
On 8/18/26 5:28 AM, Thomas Zimmermann wrote:
> M: Hans de Goede <hansg@kernel.org>
> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
> index 323422861e8f..cdfd72d9a9c5 100644
> --- a/drivers/gpu/drm/Kconfig
> +++ b/drivers/gpu/drm/Kconfig
> @@ -45,6 +45,7 @@ config DRM_MIPI_DSI
> config DRM_KMS_HELPER
> tristate
> depends on DRM
> + select DRM_PANIC_HELPER if DRM_PANIC
but DRM_PANIC_HELPER depends on DRM_TTM so
DRM_KMS_HELPER should also depend on DRM_TTM. Otherwise:
WARNING: unmet direct dependencies detected for DRM_PANIC_HELPER
Depends on [n]: HAS_IOMEM [=y] && DRM [=y] && DRM_TTM [=n]
Selected by [y]:
- DRM_KMS_HELPER [=y] && HAS_IOMEM [=y] && DRM [=y] && DRM_PANIC [=y]
> select FB_CORE if DRM_FBDEV_EMULATION
> help
> CRTC helpers for KMS drivers.
> @@ -130,6 +131,10 @@ config DRM_PANIC_SCREEN_QR_VERSION
> Smaller QR code are easier to read, but will contain less debugging
> data. Default is 40.
>
> +config DRM_PANIC_HELPER
> + bool
> + depends on DRM_TTM
> +
> config DRM_RAS
> bool "DRM RAS support"
> depends on DRM
--
~Randy
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH 12/12] drm/panic: Compile KUnit tests as module
2026-08-18 12:27 [PATCH 00/12] drm/panic: Split into core and helpers Thomas Zimmermann
` (10 preceding siblings ...)
2026-08-18 12:28 ` [PATCH 11/12] drm/panic: Move panic display code into helper library Thomas Zimmermann
@ 2026-08-18 12:28 ` Thomas Zimmermann
2026-08-18 13:18 ` sashiko-bot
11 siblings, 1 reply; 23+ messages in thread
From: Thomas Zimmermann @ 2026-08-18 12:28 UTC (permalink / raw)
To: jfalempe, javierm, airlied, simona, maarten.lankhorst, mripard
Cc: dri-devel, linux-doc, amd-gfx, rust-for-linux, linux-hyperv,
intel-gfx, intel-xe, imx, linux-arm-kernel, nouveau,
linux-renesas-soc, virtualization, sashiko-reviews,
Thomas Zimmermann
Fix dependency of the panic-helper KUnit tests on CONFIG_DRM_KUNIT_TEST=y
and make them build as a module. Export the necessary interfaces from the
panic helpers iff the KUnit framework has been enabled.
Select DRM_PANIC_HELPER from DRM_KUNIT_TEST to meet the dependencies of
the panic-helper tests.
It is now possible to run the tests by modprobing drm_panic_helper_test.ko
at runtime.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/Kconfig.debug | 1 +
drivers/gpu/drm/drm_panic_helper.c | 26 ++++++++++---------
drivers/gpu/drm/tests/Makefile | 1 +
drivers/gpu/drm/tests/drm_panic_helper_test.c | 15 ++++++++---
include/drm/drm_panic_helper.h | 8 ++++++
5 files changed, 35 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/Kconfig.debug b/drivers/gpu/drm/Kconfig.debug
index 1f4c98cbf090..b909cd0a619f 100644
--- a/drivers/gpu/drm/Kconfig.debug
+++ b/drivers/gpu/drm/Kconfig.debug
@@ -69,6 +69,7 @@ config DRM_KUNIT_TEST
select DRM_EXPORT_FOR_TESTS if m
select DRM_GEM_SHMEM_HELPER
select DRM_KUNIT_TEST_HELPERS
+ select DRM_PANIC_HELPER
select DRM_SYSFB_HELPER
select PRIME_NUMBERS
default KUNIT_ALL_TESTS
diff --git a/drivers/gpu/drm/drm_panic_helper.c b/drivers/gpu/drm/drm_panic_helper.c
index 3d612f9f3a37..1e114cdab3dd 100644
--- a/drivers/gpu/drm/drm_panic_helper.c
+++ b/drivers/gpu/drm/drm_panic_helper.c
@@ -8,6 +8,8 @@
#include <linux/utsname.h>
#include <linux/zlib.h>
+#include <kunit/visibility.h>
+
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_modeset_helper_vtables.h>
@@ -431,8 +433,8 @@ static void drm_panic_helper_logo_draw(struct drm_scanout_buffer *sb, struct drm
fg_color);
}
-static int drm_panic_helper_draw_screen_user(struct drm_scanout_buffer *sb,
- u32 fg_color, u32 bg_color)
+VISIBLE_IF_KUNIT int drm_panic_helper_draw_screen_user(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color)
{
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
struct drm_rect r_screen, r_logo, r_msg;
@@ -464,6 +466,7 @@ static int drm_panic_helper_draw_screen_user(struct drm_scanout_buffer *sb,
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(drm_panic_helper_draw_screen_user);
/*
* Draw one line of kmsg, and handle wrapping if it won't fit in the screen width.
@@ -502,8 +505,8 @@ static int draw_line_with_wrap(struct drm_scanout_buffer *sb, const struct font_
* 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 int drm_panic_helper_draw_screen_kmsg(struct drm_scanout_buffer *sb,
- u32 fg_color, u32 bg_color)
+VISIBLE_IF_KUNIT int drm_panic_helper_draw_screen_kmsg(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color)
{
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);
@@ -547,6 +550,7 @@ static int drm_panic_helper_draw_screen_kmsg(struct drm_scanout_buffer *sb,
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(drm_panic_helper_draw_screen_kmsg);
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
/*
@@ -697,9 +701,9 @@ static int drm_panic_helper_get_qr_code(u8 **qr_image, unsigned int qr_version)
/*
* Draw the panic message at the center of the screen, with a QR Code
*/
-static int drm_panic_helper_draw_screen_qr_code(struct drm_scanout_buffer *sb,
- u32 fg_color, u32 bg_color,
- unsigned int qr_version)
+VISIBLE_IF_KUNIT int drm_panic_helper_draw_screen_qr_code(struct drm_scanout_buffer *sb,
+ u32 fg_color, u32 bg_color,
+ unsigned int qr_version)
{
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
@@ -767,6 +771,7 @@ static int drm_panic_helper_draw_screen_qr_code(struct drm_scanout_buffer *sb,
drm_panic_helper_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
return 0;
}
+EXPORT_SYMBOL_IF_KUNIT(drm_panic_helper_draw_screen_qr_code);
#else
static void __init drm_panic_helper_qr_init(void) {};
static void __exit drm_panic_helper_qr_exit(void) {};
@@ -819,7 +824,7 @@ static int draw_panic_helper_dispatch(struct drm_scanout_buffer *sb,
return ret;
}
-static void drm_panic_helper_set_description(const char *description)
+VISIBLE_IF_KUNIT void drm_panic_helper_set_description(const char *description)
{
u32 len;
@@ -834,6 +839,7 @@ static void drm_panic_helper_set_description(const char *description)
desc_line->len = len;
}
}
+EXPORT_SYMBOL_IF_KUNIT(drm_panic_helper_set_description);
static void drm_panic_helper_clear_description(void)
{
@@ -907,7 +913,3 @@ void __exit drm_panic_helper_exit(void)
{
drm_panic_helper_qr_exit();
}
-
-#ifdef CONFIG_DRM_KUNIT_TEST
-#include "tests/drm_panic_helper_test.c"
-#endif
diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index e7193b49cf91..42207df1d1c8 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_managed_test.o \
drm_mm_test.o \
drm_modes_test.o \
+ drm_panic_helper_test.o \
drm_plane_helper_test.o \
drm_probe_helper_test.o \
drm_rect_test.o \
diff --git a/drivers/gpu/drm/tests/drm_panic_helper_test.c b/drivers/gpu/drm/tests/drm_panic_helper_test.c
index fcff8d2dccbe..75110da226dd 100644
--- a/drivers/gpu/drm/tests/drm_panic_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_panic_helper_test.c
@@ -6,13 +6,17 @@
* KUNIT tests for DRM panic helpers
*/
-#include <drm/drm_fourcc.h>
-#include <drm/drm_panic.h>
+#include <linux/highmem.h>
+#include <linux/units.h>
+#include <linux/vmalloc.h>
#include <kunit/test.h>
-#include <linux/units.h>
-#include <linux/vmalloc.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_panic.h>
+#include <drm/drm_panic_helper.h>
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static void drm_panic_check_color_byte(struct kunit *test, u8 b)
{
@@ -233,3 +237,6 @@ static struct kunit_suite drm_panic_helper_suite = {
};
kunit_test_suite(drm_panic_helper_suite);
+
+MODULE_DESCRIPTION("KUnit test suite for DRM panic handling");
+MODULE_LICENSE("GPL");
diff --git a/include/drm/drm_panic_helper.h b/include/drm/drm_panic_helper.h
index 6dc63288bc2a..76ff3a83e07b 100644
--- a/include/drm/drm_panic_helper.h
+++ b/include/drm/drm_panic_helper.h
@@ -8,6 +8,7 @@
enum drm_panic_type;
struct drm_plane;
+struct drm_scanout_buffer;
/* drm_panic_helper.c */
void drm_plane_helper_display_panic_screen(struct drm_plane *plane,
@@ -15,6 +16,13 @@ void drm_plane_helper_display_panic_screen(struct drm_plane *plane,
enum drm_panic_type panic_type,
u32 fg_color, u32 bg_color,
unsigned int qr_version);
+#if IS_ENABLED(CONFIG_KUNIT)
+int drm_panic_helper_draw_screen_user(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color);
+int drm_panic_helper_draw_screen_kmsg(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color);
+int drm_panic_helper_draw_screen_qr_code(struct drm_scanout_buffer *sb, unsigned int qr_version,
+ u32 fg_color, u32 bg_color);
+void drm_panic_helper_set_description(const char *description);
+#endif
/* drm_panic_helper_qr.rs */
size_t drm_panic_helper_qr_max_data_size(u8 version, size_t url_len);
--
2.55.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH 12/12] drm/panic: Compile KUnit tests as module
2026-08-18 12:28 ` [PATCH 12/12] drm/panic: Compile KUnit tests as module Thomas Zimmermann
@ 2026-08-18 13:18 ` sashiko-bot
0 siblings, 0 replies; 23+ messages in thread
From: sashiko-bot @ 2026-08-18 13:18 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: imx, dri-devel, Frank.Li, intel-xe, intel-gfx, wsa+renesas,
linux-hyperv, ojeda
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Mismatched parameter order between declaration and definition for `drm_panic_helper_draw_screen_qr_code`.
- [Low] Unmet direct dependency warning when DRM_PANIC_HELPER is selected without DRM_TTM.
- [Low] Missing indirect dependencies on DRM_DRAW and FONT_SUPPORT leading to linker errors.
--
--- Patch [12]: [PATCH 12/12] drm/panic: Compile KUnit tests as module ---
commit a56a5f0ffd0c185768aecc54426d672e40b1b0ae
Author: Thomas Zimmermann <tzimmermann@suse.de>
drm/panic: Compile KUnit tests as module
Fix dependency of the panic-helper KUnit tests on CONFIG_DRM_KUNIT_TEST=y
and make them build as a module. Export the necessary interfaces from the
panic helpers iff the KUnit framework has been enabled.
> diff --git a/drivers/gpu/drm/Kconfig.debug b/drivers/gpu/drm/Kconfig.debug
> index 1f4c98cbf0906..b909cd0a619fe 100644
> --- a/drivers/gpu/drm/Kconfig.debug
> +++ b/drivers/gpu/drm/Kconfig.debug
> @@ -69,6 +69,7 @@ config DRM_KUNIT_TEST
> select DRM_EXPORT_FOR_TESTS if m
> select DRM_GEM_SHMEM_HELPER
> select DRM_KUNIT_TEST_HELPERS
> + select DRM_PANIC_HELPER
[Severity: Low]
When DRM_KUNIT_TEST is enabled on a configuration where DRM_TTM is disabled,
does this cause an unmet direct dependency warning? DRM_PANIC_HELPER has a
depends on DRM_TTM requirement in drivers/gpu/drm/Kconfig.
Also, does selecting DRM_PANIC_HELPER without ensuring DRM_DRAW and
FONT_SUPPORT are available cause linker errors?
The drm_panic_helper.c file unconditionally calls functions from the DRM
draw library (drm_draw_color_from_xrgb8888) and the font library
(get_default_font). Normally, DRM_PANIC selects DRM_DRAW and FONT_SUPPORT,
ensuring these symbols are available, but selecting DRM_PANIC_HELPER
directly bypasses these.
[ ... ]
> diff --git a/include/drm/drm_panic_helper.h b/include/drm/drm_panic_helper.h
> index 6dc63288bc2ab..76ff3a83e07b7 100644
> --- a/include/drm/drm_panic_helper.h
> +++ b/include/drm/drm_panic_helper.h
[ ... ]
> @@ -15,6 +16,13 @@ void drm_plane_helper_display_panic_screen(struct drm_plane *plane,
> enum drm_panic_type panic_type,
> u32 fg_color, u32 bg_color,
> unsigned int qr_version);
> +#if IS_ENABLED(CONFIG_KUNIT)
> +int drm_panic_helper_draw_screen_user(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color);
> +int drm_panic_helper_draw_screen_kmsg(struct drm_scanout_buffer *sb, u32 fg_color, u32 bg_color);
> +int drm_panic_helper_draw_screen_qr_code(struct drm_scanout_buffer *sb, unsigned int qr_version,
> + u32 fg_color, u32 bg_color);
[Severity: Medium]
Does this prototype match the definition in
drivers/gpu/drm/drm_panic_helper.c?
Here it is declared as:
int drm_panic_helper_draw_screen_qr_code(struct drm_scanout_buffer *sb,
unsigned int qr_version,
u32 fg_color,
u32 bg_color);
But the implementation is defined as:
VISIBLE_IF_KUNIT int drm_panic_helper_draw_screen_qr_code(
struct drm_scanout_buffer *sb,
u32 fg_color,
u32 bg_color,
unsigned int qr_version)
Because u32 and unsigned int are functionally equivalent types, the compiler
silently accepts this mismatch without any warnings. Will any caller
relying on the header's parameter order inadvertently swap the qr_version
and fg_color arguments, causing the function to fail with -ENOSPC or render
with corrupted colors?
> +void drm_panic_helper_set_description(const char *description);
> +#endif
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260818125012.468092-1-tzimmermann@suse.de?part=12
^ permalink raw reply [flat|nested] 23+ messages in thread