Skip to content

Commit cb5164a

Browse files
author
Jocelyn Falempe
committed
drm/panic: Add a QR code panic screen
This patch adds a new panic screen, with a QR code and the kmsg data embedded. If DRM_PANIC_SCREEN_QR_CODE_URL is set, then the kmsg data will be compressed with zlib and encoded as a numerical segment, and appended to the URL as a URL parameter. This allows to save space, and put about ~7500 bytes of kmsg data, in a V40 QR code. Linux distributions can customize the URL, and put a web frontend to directly open a bug report with the kmsg data. Otherwise the kmsg data will be encoded as a binary segment (ie raw ascii) and only a maximum of 2953 bytes of kmsg data will be available in the QR code. You can also limit the QR code size with DRM_PANIC_SCREEN_QR_VERSION. Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240822073852.562286-5-jfalempe@redhat.com
1 parent 8f4eca6 commit cb5164a

File tree

6 files changed

+1290
-0
lines changed

6 files changed

+1290
-0
lines changed

drivers/gpu/drm/Kconfig

+31
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,37 @@ config DRM_PANIC_SCREEN
149149
or by writing to /sys/module/drm/parameters/panic_screen sysfs entry
150150
Default is "user"
151151

152+
config DRM_PANIC_SCREEN_QR_CODE
153+
bool "Add a panic screen with a QR code"
154+
depends on DRM_PANIC && RUST
155+
help
156+
This option adds a QR code generator, and a panic screen with a QR
157+
code. The QR code will contain the last lines of kmsg and other debug
158+
information. This should be easier for the user to report a kernel
159+
panic, with all debug information available.
160+
To use this panic screen, also set DRM_PANIC_SCREEN to "qr_code"
161+
162+
config DRM_PANIC_SCREEN_QR_CODE_URL
163+
string "Base URL of the QR code in the panic screen"
164+
depends on DRM_PANIC_SCREEN_QR_CODE
165+
help
166+
This option sets the base URL to report the kernel panic. If it's set
167+
the QR code will contain the URL and the kmsg compressed with zlib as
168+
a URL parameter. If it's empty, the QR code will contain the kmsg as
169+
uncompressed text only.
170+
There is a demo code in javascript, to decode and uncompress the kmsg
171+
data from the URL parameter at https://github.com/kdj0c/panic_report
172+
173+
config DRM_PANIC_SCREEN_QR_VERSION
174+
int "Maximum version (size) of the QR code."
175+
depends on DRM_PANIC_SCREEN_QR_CODE
176+
default 40
177+
help
178+
This option limits the version (or size) of the QR code. QR code
179+
version ranges from Version 1 (21x21) to Version 40 (177x177).
180+
Smaller QR code are easier to read, but will contain less debugging
181+
data. Default is 40.
182+
152183
config DRM_DEBUG_DP_MST_TOPOLOGY_REFS
153184
bool "Enable refcount backtrace history in the DP MST helpers"
154185
depends on STACKTRACE_SUPPORT

drivers/gpu/drm/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ drm-$(CONFIG_DRM_PRIVACY_SCREEN) += \
8989
drm_privacy_screen_x86.o
9090
drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o
9191
drm-$(CONFIG_DRM_PANIC) += drm_panic.o
92+
drm-$(CONFIG_DRM_PANIC_SCREEN_QR_CODE) += drm_panic_qr.o
9293
obj-$(CONFIG_DRM) += drm.o
9394

9495
obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o

drivers/gpu/drm/drm_crtc_internal.h

+4
Original file line numberDiff line numberDiff line change
@@ -320,10 +320,14 @@ drm_edid_load_firmware(struct drm_connector *connector)
320320
bool drm_panic_is_enabled(struct drm_device *dev);
321321
void drm_panic_register(struct drm_device *dev);
322322
void drm_panic_unregister(struct drm_device *dev);
323+
void drm_panic_init(void);
324+
void drm_panic_exit(void);
323325
#else
324326
static inline bool drm_panic_is_enabled(struct drm_device *dev) { return false; }
325327
static inline void drm_panic_register(struct drm_device *dev) {}
326328
static inline void drm_panic_unregister(struct drm_device *dev) {}
329+
static inline void drm_panic_init(void) {}
330+
static inline void drm_panic_exit(void) {}
327331
#endif
328332

329333
#endif /* __DRM_CRTC_INTERNAL_H__ */

drivers/gpu/drm/drm_drv.c

+3
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ static const struct file_operations drm_stub_fops = {
10671067
static void drm_core_exit(void)
10681068
{
10691069
drm_privacy_screen_lookup_exit();
1070+
drm_panic_exit();
10701071
accel_core_exit();
10711072
unregister_chrdev(DRM_MAJOR, "drm");
10721073
debugfs_remove(drm_debugfs_root);
@@ -1099,6 +1100,8 @@ static int __init drm_core_init(void)
10991100
if (ret < 0)
11001101
goto error;
11011102

1103+
drm_panic_init();
1104+
11021105
drm_privacy_screen_lookup_init();
11031106

11041107
drm_core_init_complete = true;

drivers/gpu/drm/drm_panic.c

+248
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <linux/overflow.h>
1919
#include <linux/printk.h>
2020
#include <linux/types.h>
21+
#include <linux/utsname.h>
22+
#include <linux/zlib.h>
2123

2224
#include <drm/drm_drv.h>
2325
#include <drm/drm_fourcc.h>
@@ -26,6 +28,7 @@
2628
#include <drm/drm_panic.h>
2729
#include <drm/drm_plane.h>
2830
#include <drm/drm_print.h>
31+
#include <drm/drm_rect.h>
2932

3033
#include "drm_crtc_internal.h"
3134

@@ -627,6 +630,233 @@ static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
627630
}
628631
}
629632

633+
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
634+
/*
635+
* It is unwise to allocate memory in the panic callback, so the buffers are
636+
* pre-allocated. Only 2 buffers and the zlib workspace are needed.
637+
* Two buffers are enough, using the following buffer usage:
638+
* 1) kmsg messages are dumped in buffer1
639+
* 2) kmsg is zlib-compressed into buffer2
640+
* 3) compressed kmsg is encoded as QR-code Numeric stream in buffer1
641+
* 4) QR-code image is generated in buffer2
642+
* The Max QR code size is V40, 177x177, 4071 bytes for image, 2956 bytes for
643+
* data segments.
644+
*
645+
* Typically, ~7500 bytes of kmsg, are compressed into 2800 bytes, which fits in
646+
* a V40 QR-code (177x177).
647+
*
648+
* If CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL is not set, the kmsg data will be put
649+
* directly in the QR code.
650+
* 1) kmsg messages are dumped in buffer1
651+
* 2) kmsg message is encoded as byte stream in buffer2
652+
* 3) QR-code image is generated in buffer1
653+
*/
654+
655+
static uint panic_qr_version = CONFIG_DRM_PANIC_SCREEN_QR_VERSION;
656+
module_param(panic_qr_version, uint, 0644);
657+
MODULE_PARM_DESC(panic_qr_version, "maximum version (size) of the QR code");
658+
659+
#define MAX_QR_DATA 2956
660+
#define MAX_ZLIB_RATIO 3
661+
#define QR_BUFFER1_SIZE (MAX_ZLIB_RATIO * MAX_QR_DATA) /* Must also be > 4071 */
662+
#define QR_BUFFER2_SIZE 4096
663+
#define QR_MARGIN 4 /* 4 modules of foreground color around the qr code */
664+
665+
/* Compression parameters */
666+
#define COMPR_LEVEL 6
667+
#define WINDOW_BITS 12
668+
#define MEM_LEVEL 4
669+
670+
static char *qrbuf1;
671+
static char *qrbuf2;
672+
static struct z_stream_s stream;
673+
674+
static void __init drm_panic_qr_init(void)
675+
{
676+
qrbuf1 = kmalloc(QR_BUFFER1_SIZE, GFP_KERNEL);
677+
qrbuf2 = kmalloc(QR_BUFFER2_SIZE, GFP_KERNEL);
678+
stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
679+
GFP_KERNEL);
680+
}
681+
682+
static void drm_panic_qr_exit(void)
683+
{
684+
kfree(qrbuf1);
685+
qrbuf1 = NULL;
686+
kfree(qrbuf2);
687+
qrbuf2 = NULL;
688+
kfree(stream.workspace);
689+
stream.workspace = NULL;
690+
}
691+
692+
extern size_t drm_panic_qr_max_data_size(u8 version, size_t url_len);
693+
694+
extern u8 drm_panic_qr_generate(const char *url, u8 *data, size_t data_len, size_t data_size,
695+
u8 *tmp, size_t tmp_size);
696+
697+
static int drm_panic_get_qr_code_url(u8 **qr_image)
698+
{
699+
struct kmsg_dump_iter iter;
700+
char url[256];
701+
size_t kmsg_len, max_kmsg_size;
702+
char *kmsg;
703+
int max_qr_data_size, url_len;
704+
705+
url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&zl=",
706+
utsname()->machine, utsname()->release);
707+
708+
max_qr_data_size = drm_panic_qr_max_data_size(panic_qr_version, url_len);
709+
max_kmsg_size = min(MAX_ZLIB_RATIO * max_qr_data_size, QR_BUFFER1_SIZE);
710+
711+
/* get kmsg to buffer 1 */
712+
kmsg_dump_rewind(&iter);
713+
kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
714+
715+
if (!kmsg_len)
716+
return -ENODATA;
717+
kmsg = qrbuf1;
718+
719+
try_again:
720+
if (zlib_deflateInit2(&stream, COMPR_LEVEL, Z_DEFLATED, WINDOW_BITS,
721+
MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK)
722+
return -EINVAL;
723+
724+
stream.next_in = kmsg;
725+
stream.avail_in = kmsg_len;
726+
stream.total_in = 0;
727+
stream.next_out = qrbuf2;
728+
stream.avail_out = QR_BUFFER2_SIZE;
729+
stream.total_out = 0;
730+
731+
if (zlib_deflate(&stream, Z_FINISH) != Z_STREAM_END)
732+
return -EINVAL;
733+
734+
if (zlib_deflateEnd(&stream) != Z_OK)
735+
return -EINVAL;
736+
737+
if (stream.total_out > max_qr_data_size) {
738+
/* too much data for the QR code, so skip the first line and try again */
739+
kmsg = strchr(kmsg, '\n');
740+
if (!kmsg)
741+
return -EINVAL;
742+
/* skip the first \n */
743+
kmsg += 1;
744+
kmsg_len = strlen(kmsg);
745+
goto try_again;
746+
}
747+
*qr_image = qrbuf2;
748+
749+
/* generate qr code image in buffer2 */
750+
return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SIZE,
751+
qrbuf1, QR_BUFFER1_SIZE);
752+
}
753+
754+
static int drm_panic_get_qr_code_raw(u8 **qr_image)
755+
{
756+
struct kmsg_dump_iter iter;
757+
size_t kmsg_len;
758+
size_t max_kmsg_size = min(drm_panic_qr_max_data_size(panic_qr_version, 0),
759+
QR_BUFFER1_SIZE);
760+
761+
kmsg_dump_rewind(&iter);
762+
kmsg_dump_get_buffer(&iter, false, qrbuf1, max_kmsg_size, &kmsg_len);
763+
if (!kmsg_len)
764+
return -ENODATA;
765+
766+
*qr_image = qrbuf1;
767+
return drm_panic_qr_generate(NULL, qrbuf1, kmsg_len, QR_BUFFER1_SIZE,
768+
qrbuf2, QR_BUFFER2_SIZE);
769+
}
770+
771+
static int drm_panic_get_qr_code(u8 **qr_image)
772+
{
773+
if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0)
774+
return drm_panic_get_qr_code_url(qr_image);
775+
else
776+
return drm_panic_get_qr_code_raw(qr_image);
777+
}
778+
779+
/*
780+
* Draw the panic message at the center of the screen, with a QR Code
781+
*/
782+
static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
783+
{
784+
u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
785+
u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
786+
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
787+
struct drm_rect r_screen, r_logo, r_msg, r_qr, r_qr_canvas;
788+
unsigned int max_qr_size, scale;
789+
unsigned int msg_width, msg_height;
790+
int qr_width, qr_canvas_width, qr_pitch, v_margin;
791+
u8 *qr_image;
792+
793+
if (!font || !qrbuf1 || !qrbuf2 || !stream.workspace)
794+
return -ENOMEM;
795+
796+
r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height);
797+
798+
drm_panic_logo_rect(&r_logo, font);
799+
800+
msg_width = min(get_max_line_len(panic_msg, panic_msg_lines) * font->width, sb->width);
801+
msg_height = min(panic_msg_lines * font->height, sb->height);
802+
r_msg = DRM_RECT_INIT(0, 0, msg_width, msg_height);
803+
804+
max_qr_size = min(3 * sb->width / 4, 3 * sb->height / 4);
805+
806+
qr_width = drm_panic_get_qr_code(&qr_image);
807+
if (qr_width <= 0)
808+
return -ENOSPC;
809+
810+
qr_canvas_width = qr_width + QR_MARGIN * 2;
811+
scale = max_qr_size / qr_canvas_width;
812+
/* QR code is not readable if not scaled at least by 2 */
813+
if (scale < 2)
814+
return -ENOSPC;
815+
816+
pr_debug("QR width %d and scale %d\n", qr_width, scale);
817+
r_qr_canvas = DRM_RECT_INIT(0, 0, qr_canvas_width * scale, qr_canvas_width * scale);
818+
819+
v_margin = (sb->height - drm_rect_height(&r_qr_canvas) - drm_rect_height(&r_msg)) / 5;
820+
821+
drm_rect_translate(&r_qr_canvas, (sb->width - r_qr_canvas.x2) / 2, 2 * v_margin);
822+
r_qr = DRM_RECT_INIT(r_qr_canvas.x1 + QR_MARGIN * scale, r_qr_canvas.y1 + QR_MARGIN * scale,
823+
qr_width * scale, qr_width * scale);
824+
825+
/* Center the panic message */
826+
drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2,
827+
3 * v_margin + drm_rect_height(&r_qr_canvas));
828+
829+
/* Fill with the background color, and draw text on top */
830+
drm_panic_fill(sb, &r_screen, bg_color);
831+
832+
if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr))
833+
drm_panic_logo_draw(sb, &r_logo, font, fg_color);
834+
835+
draw_txt_rectangle(sb, font, panic_msg, panic_msg_lines, true, &r_msg, fg_color);
836+
837+
/* Draw the qr code */
838+
qr_pitch = DIV_ROUND_UP(qr_width, 8);
839+
drm_panic_fill(sb, &r_qr_canvas, fg_color);
840+
drm_panic_fill(sb, &r_qr, bg_color);
841+
drm_panic_blit(sb, &r_qr, qr_image, qr_pitch, scale, fg_color);
842+
return 0;
843+
}
844+
845+
static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
846+
{
847+
if (_draw_panic_static_qr_code(sb))
848+
draw_panic_static_user(sb);
849+
}
850+
#else
851+
static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
852+
{
853+
draw_panic_static_user(sb);
854+
}
855+
856+
static void drm_panic_qr_init(void) {};
857+
static void drm_panic_qr_exit(void) {};
858+
#endif
859+
630860
/*
631861
* drm_panic_is_format_supported()
632862
* @format: a fourcc color code
@@ -645,6 +875,8 @@ static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
645875
{
646876
if (!strcmp(drm_panic_screen, "kmsg")) {
647877
draw_panic_static_kmsg(sb);
878+
} else if (!strcmp(drm_panic_screen, "qr_code")) {
879+
draw_panic_static_qr_code(sb);
648880
} else {
649881
draw_panic_static_user(sb);
650882
}
@@ -814,3 +1046,19 @@ void drm_panic_unregister(struct drm_device *dev)
8141046
kmsg_dump_unregister(&plane->kmsg_panic);
8151047
}
8161048
}
1049+
1050+
/**
1051+
* drm_panic_init() - initialize DRM panic.
1052+
*/
1053+
void __init drm_panic_init(void)
1054+
{
1055+
drm_panic_qr_init();
1056+
}
1057+
1058+
/**
1059+
* drm_panic_exit() - Free the resources taken by drm_panic_exit()
1060+
*/
1061+
void drm_panic_exit(void)
1062+
{
1063+
drm_panic_qr_exit();
1064+
}

0 commit comments

Comments
 (0)