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