#include <linux/font.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_plane.h>
#include <drm/drm_print.h>
#include <drm/drm_rect.h>
#include "drm_crtc_internal.h"
MODULE_AUTHOR(…) …;
MODULE_DESCRIPTION(…) …;
MODULE_LICENSE(…) …;
static char drm_panic_screen[16] = …;
module_param_string(…);
MODULE_PARM_DESC(…) …;
struct drm_panic_line { … };
#define PANIC_LINE(s) …
static struct drm_panic_line panic_msg[] = …;
static const size_t panic_msg_lines = …;
static const struct drm_panic_line logo_ascii[] = …;
static const size_t logo_ascii_lines = …;
#if defined(CONFIG_LOGO) && !defined(MODULE)
static const struct linux_logo *logo_mono;
static int drm_panic_setup_logo(void)
{ … }
device_initcall(drm_panic_setup_logo);
#else
#define logo_mono …
#endif
static u16 convert_xrgb8888_to_rgb565(u32 pix)
{ … }
static u16 convert_xrgb8888_to_rgba5551(u32 pix)
{ … }
static u16 convert_xrgb8888_to_xrgb1555(u32 pix)
{ … }
static u16 convert_xrgb8888_to_argb1555(u32 pix)
{ … }
static u32 convert_xrgb8888_to_argb8888(u32 pix)
{ … }
static u32 convert_xrgb8888_to_xbgr8888(u32 pix)
{ … }
static u32 convert_xrgb8888_to_abgr8888(u32 pix)
{ … }
static u32 convert_xrgb8888_to_xrgb2101010(u32 pix)
{ … }
static u32 convert_xrgb8888_to_argb2101010(u32 pix)
{ … }
static u32 convert_from_xrgb8888(u32 color, u32 format)
{ … }
static bool drm_panic_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, int x, int y)
{ … }
static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
unsigned int scale, u16 fg16)
{ … }
static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
unsigned int scale, u32 fg32)
{ … }
static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch,
const u8 *sbuf8, unsigned int spitch,
unsigned int height, unsigned int width,
unsigned int scale, u32 fg32)
{ … }
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)
{ … }
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)
{ … }
static void drm_panic_fill16(struct iosys_map *dmap, unsigned int dpitch,
unsigned int height, unsigned int width,
u16 color)
{ … }
static void drm_panic_fill24(struct iosys_map *dmap, unsigned int dpitch,
unsigned int height, unsigned int width,
u32 color)
{ … }
static void drm_panic_fill32(struct iosys_map *dmap, unsigned int dpitch,
unsigned int height, unsigned int width,
u32 color)
{ … }
static void drm_panic_fill_pixel(struct drm_scanout_buffer *sb,
struct drm_rect *clip,
u32 color)
{ … }
static void drm_panic_fill(struct drm_scanout_buffer *sb, struct drm_rect *clip,
u32 color)
{ … }
static const u8 *get_char_bitmap(const struct font_desc *font, char c, size_t font_pitch)
{ … }
static unsigned int get_max_line_len(const struct drm_panic_line *lines, int len)
{ … }
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)
{ … }
static void drm_panic_logo_rect(struct drm_rect *rect, const struct font_desc *font)
{ … }
static void drm_panic_logo_draw(struct drm_scanout_buffer *sb, struct drm_rect *rect,
const struct font_desc *font, u32 fg_color)
{ … }
static void draw_panic_static_user(struct drm_scanout_buffer *sb)
{ … }
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)
{ … }
static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb)
{ … }
#if defined(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
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 …
#define MAX_ZLIB_RATIO …
#define QR_BUFFER1_SIZE …
#define QR_BUFFER2_SIZE …
#define QR_MARGIN …
#define COMPR_LEVEL …
#define WINDOW_BITS …
#define MEM_LEVEL …
static char *qrbuf1;
static char *qrbuf2;
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);
stream.workspace = kmalloc(zlib_deflate_workspacesize(WINDOW_BITS, MEM_LEVEL),
GFP_KERNEL);
}
static void drm_panic_qr_exit(void)
{
kfree(qrbuf1);
qrbuf1 = NULL;
kfree(qrbuf2);
qrbuf2 = NULL;
kfree(stream.workspace);
stream.workspace = NULL;
}
extern size_t drm_panic_qr_max_data_size(u8 version, size_t url_len);
extern u8 drm_panic_qr_generate(const char *url, u8 *data, size_t data_len, size_t data_size,
u8 *tmp, size_t tmp_size);
static int drm_panic_get_qr_code_url(u8 **qr_image)
{
struct kmsg_dump_iter iter;
char url[256];
size_t kmsg_len, max_kmsg_size;
char *kmsg;
int max_qr_data_size, url_len;
url_len = snprintf(url, sizeof(url), CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL "?a=%s&v=%s&zl=",
utsname()->machine, utsname()->release);
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);
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 = QR_BUFFER2_SIZE;
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) {
kmsg = strchr(kmsg, '\n');
if (!kmsg)
return -EINVAL;
kmsg += 1;
kmsg_len = strlen(kmsg);
goto try_again;
}
*qr_image = qrbuf2;
return drm_panic_qr_generate(url, qrbuf2, stream.total_out, QR_BUFFER2_SIZE,
qrbuf1, QR_BUFFER1_SIZE);
}
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);
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, QR_BUFFER1_SIZE,
qrbuf2, QR_BUFFER2_SIZE);
}
static int drm_panic_get_qr_code(u8 **qr_image)
{
if (strlen(CONFIG_DRM_PANIC_SCREEN_QR_CODE_URL) > 0)
return drm_panic_get_qr_code_url(qr_image);
else
return drm_panic_get_qr_code_raw(qr_image);
}
static int _draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
{
u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format);
u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format);
const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL);
struct drm_rect r_screen, 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 (!font || !qrbuf1 || !qrbuf2 || !stream.workspace)
return -ENOMEM;
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);
if (qr_width <= 0)
return -ENOSPC;
qr_canvas_width = qr_width + QR_MARGIN * 2;
scale = max_qr_size / qr_canvas_width;
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)) / 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);
drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2,
3 * v_margin + drm_rect_height(&r_qr_canvas));
drm_panic_fill(sb, &r_screen, bg_color);
if (!drm_rect_overlap(&r_logo, &r_msg) && !drm_rect_overlap(&r_logo, &r_qr))
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);
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;
}
static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
{
if (_draw_panic_static_qr_code(sb))
draw_panic_static_user(sb);
}
#else
static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb)
{ … }
static void drm_panic_qr_init(void) {};
static void drm_panic_qr_exit(void) {};
#endif
static bool drm_panic_is_format_supported(const struct drm_format_info *format)
{ … }
static void draw_panic_dispatch(struct drm_scanout_buffer *sb)
{ … }
static void drm_panic_set_description(const char *description)
{ … }
static void drm_panic_clear_description(void)
{ … }
static void draw_panic_plane(struct drm_plane *plane, const char *description)
{ … }
static struct drm_plane *to_drm_plane(struct kmsg_dumper *kd)
{ … }
static void drm_panic(struct kmsg_dumper *dumper, struct kmsg_dump_detail *detail)
{ … }
#ifdef CONFIG_DRM_PANIC_DEBUG
#include <linux/debugfs.h>
static ssize_t debugfs_trigger_write(struct file *file, const char __user *user_buf,
size_t count, loff_t *ppos)
{ … }
static const struct file_operations dbg_drm_panic_ops = …;
static void debugfs_register_plane(struct drm_plane *plane, int index)
{ … }
#else
static void debugfs_register_plane(struct drm_plane *plane, int index) {}
#endif
bool drm_panic_is_enabled(struct drm_device *dev)
{ … }
EXPORT_SYMBOL(…);
void drm_panic_register(struct drm_device *dev)
{ … }
void drm_panic_unregister(struct drm_device *dev)
{ … }
void __init drm_panic_init(void)
{ … }
void drm_panic_exit(void)
{ … }