1 // SPDX-License-Identifier: GPL-2.0 or MIT 2 /* 3 * Copyright (c) 2023 Red Hat. 4 * Author: Jocelyn Falempe <jfalempe@redhat.com> 5 * inspired by the drm_log driver from David Herrmann <dh.herrmann@gmail.com> 6 * Tux Ascii art taken from cowsay written by Tony Monroe 7 */ 8 9 #include <linux/font.h> 10 #include <linux/init.h> 11 #include <linux/iosys-map.h> 12 #include <linux/kdebug.h> 13 #include <linux/kmsg_dump.h> 14 #include <linux/linux_logo.h> 15 #include <linux/list.h> 16 #include <linux/math.h> 17 #include <linux/module.h> 18 #include <linux/overflow.h> 19 #include <linux/printk.h> 20 #include <linux/types.h> 21 22 #include <drm/drm_drv.h> 23 #include <drm/drm_fourcc.h> 24 #include <drm/drm_framebuffer.h> 25 #include <drm/drm_modeset_helper_vtables.h> 26 #include <drm/drm_panic.h> 27 #include <drm/drm_plane.h> 28 #include <drm/drm_print.h> 29 30 MODULE_AUTHOR("Jocelyn Falempe"); 31 MODULE_DESCRIPTION("DRM panic handler"); 32 MODULE_LICENSE("GPL"); 33 34 static char drm_panic_screen[16] = CONFIG_DRM_PANIC_SCREEN; 35 module_param_string(panic_screen, drm_panic_screen, sizeof(drm_panic_screen), 0644); 36 MODULE_PARM_DESC(panic_screen, 37 "Choose what will be displayed by drm_panic, 'user' or 'kmsg' [default=" 38 CONFIG_DRM_PANIC_SCREEN "]"); 39 40 /** 41 * DOC: overview 42 * 43 * To enable DRM panic for a driver, the primary plane must implement a 44 * &drm_plane_helper_funcs.get_scanout_buffer helper function. It is then 45 * automatically registered to the drm panic handler. 46 * When a panic occurs, the &drm_plane_helper_funcs.get_scanout_buffer will be 47 * called, and the driver can provide a framebuffer so the panic handler can 48 * draw the panic screen on it. Currently only linear buffer and a few color 49 * formats are supported. 50 * Optionally the driver can also provide a &drm_plane_helper_funcs.panic_flush 51 * callback, that will be called after that, to send additional commands to the 52 * hardware to make the scanout buffer visible. 53 */ 54 55 /* 56 * This module displays a user friendly message on screen when a kernel panic 57 * occurs. This is conflicting with fbcon, so you can only enable it when fbcon 58 * is disabled. 59 * It's intended for end-user, so have minimal technical/debug information. 60 * 61 * Implementation details: 62 * 63 * It is a panic handler, so it can't take lock, allocate memory, run tasks/irq, 64 * or attempt to sleep. It's a best effort, and it may not be able to display 65 * the message in all situations (like if the panic occurs in the middle of a 66 * modesetting). 67 * It will display only one static frame, so performance optimizations are low 68 * priority as the machine is already in an unusable state. 69 */ 70 71 struct drm_panic_line { 72 u32 len; 73 const char *txt; 74 }; 75 76 #define PANIC_LINE(s) {.len = sizeof(s) - 1, .txt = s} 77 78 static struct drm_panic_line panic_msg[] = { 79 PANIC_LINE("KERNEL PANIC !"), 80 PANIC_LINE(""), 81 PANIC_LINE("Please reboot your computer."), 82 }; 83 84 static const struct drm_panic_line logo_ascii[] = { 85 PANIC_LINE(" .--. _"), 86 PANIC_LINE(" |o_o | | |"), 87 PANIC_LINE(" |:_/ | | |"), 88 PANIC_LINE(" // \\ \\ |_|"), 89 PANIC_LINE(" (| | ) _"), 90 PANIC_LINE(" /'\\_ _/`\\ (_)"), 91 PANIC_LINE(" \\___)=(___/"), 92 }; 93 94 #if defined(CONFIG_LOGO) && !defined(MODULE) 95 static const struct linux_logo *logo_mono; 96 97 static int drm_panic_setup_logo(void) 98 { 99 const struct linux_logo *logo = fb_find_logo(1); 100 const unsigned char *logo_data; 101 struct linux_logo *logo_dup; 102 103 if (!logo || logo->type != LINUX_LOGO_MONO) 104 return 0; 105 106 /* The logo is __init, so we must make a copy for later use */ 107 logo_data = kmemdup(logo->data, 108 size_mul(DIV_ROUND_UP(logo->width, BITS_PER_BYTE), logo->height), 109 GFP_KERNEL); 110 if (!logo_data) 111 return -ENOMEM; 112 113 logo_dup = kmemdup(logo, sizeof(*logo), GFP_KERNEL); 114 if (!logo_dup) { 115 kfree(logo_data); 116 return -ENOMEM; 117 } 118 119 logo_dup->data = logo_data; 120 logo_mono = logo_dup; 121 122 return 0; 123 } 124 125 device_initcall(drm_panic_setup_logo); 126 #else 127 #define logo_mono ((const struct linux_logo *)NULL) 128 #endif 129 130 /* 131 * Color conversion 132 */ 133 134 static u16 convert_xrgb8888_to_rgb565(u32 pix) 135 { 136 return ((pix & 0x00F80000) >> 8) | 137 ((pix & 0x0000FC00) >> 5) | 138 ((pix & 0x000000F8) >> 3); 139 } 140 141 static u16 convert_xrgb8888_to_rgba5551(u32 pix) 142 { 143 return ((pix & 0x00f80000) >> 8) | 144 ((pix & 0x0000f800) >> 5) | 145 ((pix & 0x000000f8) >> 2) | 146 BIT(0); /* set alpha bit */ 147 } 148 149 static u16 convert_xrgb8888_to_xrgb1555(u32 pix) 150 { 151 return ((pix & 0x00f80000) >> 9) | 152 ((pix & 0x0000f800) >> 6) | 153 ((pix & 0x000000f8) >> 3); 154 } 155 156 static u16 convert_xrgb8888_to_argb1555(u32 pix) 157 { 158 return BIT(15) | /* set alpha bit */ 159 ((pix & 0x00f80000) >> 9) | 160 ((pix & 0x0000f800) >> 6) | 161 ((pix & 0x000000f8) >> 3); 162 } 163 164 static u32 convert_xrgb8888_to_argb8888(u32 pix) 165 { 166 return pix | GENMASK(31, 24); /* fill alpha bits */ 167 } 168 169 static u32 convert_xrgb8888_to_xbgr8888(u32 pix) 170 { 171 return ((pix & 0x00ff0000) >> 16) << 0 | 172 ((pix & 0x0000ff00) >> 8) << 8 | 173 ((pix & 0x000000ff) >> 0) << 16 | 174 ((pix & 0xff000000) >> 24) << 24; 175 } 176 177 static u32 convert_xrgb8888_to_abgr8888(u32 pix) 178 { 179 return ((pix & 0x00ff0000) >> 16) << 0 | 180 ((pix & 0x0000ff00) >> 8) << 8 | 181 ((pix & 0x000000ff) >> 0) << 16 | 182 GENMASK(31, 24); /* fill alpha bits */ 183 } 184 185 static u32 convert_xrgb8888_to_xrgb2101010(u32 pix) 186 { 187 pix = ((pix & 0x000000FF) << 2) | 188 ((pix & 0x0000FF00) << 4) | 189 ((pix & 0x00FF0000) << 6); 190 return pix | ((pix >> 8) & 0x00300C03); 191 } 192 193 static u32 convert_xrgb8888_to_argb2101010(u32 pix) 194 { 195 pix = ((pix & 0x000000FF) << 2) | 196 ((pix & 0x0000FF00) << 4) | 197 ((pix & 0x00FF0000) << 6); 198 return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); 199 } 200 201 /* 202 * convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format 203 * @color: input color, in xrgb8888 format 204 * @format: output format 205 * 206 * Returns: 207 * Color in the format specified, casted to u32. 208 * Or 0 if the format is not supported. 209 */ 210 static u32 convert_from_xrgb8888(u32 color, u32 format) 211 { 212 switch (format) { 213 case DRM_FORMAT_RGB565: 214 return convert_xrgb8888_to_rgb565(color); 215 case DRM_FORMAT_RGBA5551: 216 return convert_xrgb8888_to_rgba5551(color); 217 case DRM_FORMAT_XRGB1555: 218 return convert_xrgb8888_to_xrgb1555(color); 219 case DRM_FORMAT_ARGB1555: 220 return convert_xrgb8888_to_argb1555(color); 221 case DRM_FORMAT_RGB888: 222 case DRM_FORMAT_XRGB8888: 223 return color; 224 case DRM_FORMAT_ARGB8888: 225 return convert_xrgb8888_to_argb8888(color); 226 case DRM_FORMAT_XBGR8888: 227 return convert_xrgb8888_to_xbgr8888(color); 228 case DRM_FORMAT_ABGR8888: 229 return convert_xrgb8888_to_abgr8888(color); 230 case DRM_FORMAT_XRGB2101010: 231 return convert_xrgb8888_to_xrgb2101010(color); 232 case DRM_FORMAT_ARGB2101010: 233 return convert_xrgb8888_to_argb2101010(color); 234 default: 235 WARN_ONCE(1, "Can't convert to %p4cc\n", &format); 236 return 0; 237 } 238 } 239 240 /* 241 * Blit & Fill 242 */ 243 /* check if the pixel at coord x,y is 1 (foreground) or 0 (background) */ 244 static bool drm_panic_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, int x, int y) 245 { 246 return (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) != 0; 247 } 248 249 static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch, 250 const u8 *sbuf8, unsigned int spitch, 251 unsigned int height, unsigned int width, 252 u16 fg16) 253 { 254 unsigned int y, x; 255 256 for (y = 0; y < height; y++) 257 for (x = 0; x < width; x++) 258 if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) 259 iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, fg16); 260 } 261 262 static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch, 263 const u8 *sbuf8, unsigned int spitch, 264 unsigned int height, unsigned int width, 265 u32 fg32) 266 { 267 unsigned int y, x; 268 269 for (y = 0; y < height; y++) { 270 for (x = 0; x < width; x++) { 271 u32 off = y * dpitch + x * 3; 272 273 if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) { 274 /* write blue-green-red to output in little endianness */ 275 iosys_map_wr(dmap, off, u8, (fg32 & 0x000000FF) >> 0); 276 iosys_map_wr(dmap, off + 1, u8, (fg32 & 0x0000FF00) >> 8); 277 iosys_map_wr(dmap, off + 2, u8, (fg32 & 0x00FF0000) >> 16); 278 } 279 } 280 } 281 } 282 283 static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch, 284 const u8 *sbuf8, unsigned int spitch, 285 unsigned int height, unsigned int width, 286 u32 fg32) 287 { 288 unsigned int y, x; 289 290 for (y = 0; y < height; y++) 291 for (x = 0; x < width; x++) 292 if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) 293 iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, fg32); 294 } 295 296 static void drm_panic_blit_pixel(struct drm_scanout_buffer *sb, struct drm_rect *clip, 297 const u8 *sbuf8, unsigned int spitch, u32 fg_color) 298 { 299 unsigned int y, x; 300 301 for (y = 0; y < drm_rect_height(clip); y++) 302 for (x = 0; x < drm_rect_width(clip); x++) 303 if (drm_panic_is_pixel_fg(sbuf8, spitch, x, y)) 304 sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, fg_color); 305 } 306 307 /* 308 * drm_panic_blit - convert a monochrome image to a linear framebuffer 309 * @sb: destination scanout buffer 310 * @clip: destination rectangle 311 * @sbuf8: source buffer, in monochrome format, 8 pixels per byte. 312 * @spitch: source pitch in bytes 313 * @fg_color: foreground color, in destination format 314 * 315 * This can be used to draw a font character, which is a monochrome image, to a 316 * framebuffer in other supported format. 317 */ 318 static void drm_panic_blit(struct drm_scanout_buffer *sb, struct drm_rect *clip, 319 const u8 *sbuf8, unsigned int spitch, u32 fg_color) 320 { 321 struct iosys_map map; 322 323 if (sb->set_pixel) 324 return drm_panic_blit_pixel(sb, clip, sbuf8, spitch, fg_color); 325 326 map = sb->map[0]; 327 iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]); 328 329 switch (sb->format->cpp[0]) { 330 case 2: 331 drm_panic_blit16(&map, sb->pitch[0], sbuf8, spitch, 332 drm_rect_height(clip), drm_rect_width(clip), fg_color); 333 break; 334 case 3: 335 drm_panic_blit24(&map, sb->pitch[0], sbuf8, spitch, 336 drm_rect_height(clip), drm_rect_width(clip), fg_color); 337 break; 338 case 4: 339 drm_panic_blit32(&map, sb->pitch[0], sbuf8, spitch, 340 drm_rect_height(clip), drm_rect_width(clip), fg_color); 341 break; 342 default: 343 WARN_ONCE(1, "Can't blit with pixel width %d\n", sb->format->cpp[0]); 344 } 345 } 346 347 static void drm_panic_fill16(struct iosys_map *dmap, unsigned int dpitch, 348 unsigned int height, unsigned int width, 349 u16 color) 350 { 351 unsigned int y, x; 352 353 for (y = 0; y < height; y++) 354 for (x = 0; x < width; x++) 355 iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, color); 356 } 357 358 static void drm_panic_fill24(struct iosys_map *dmap, unsigned int dpitch, 359 unsigned int height, unsigned int width, 360 u32 color) 361 { 362 unsigned int y, x; 363 364 for (y = 0; y < height; y++) { 365 for (x = 0; x < width; x++) { 366 unsigned int off = y * dpitch + x * 3; 367 368 /* write blue-green-red to output in little endianness */ 369 iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0); 370 iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8); 371 iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16); 372 } 373 } 374 } 375 376 static void drm_panic_fill32(struct iosys_map *dmap, unsigned int dpitch, 377 unsigned int height, unsigned int width, 378 u32 color) 379 { 380 unsigned int y, x; 381 382 for (y = 0; y < height; y++) 383 for (x = 0; x < width; x++) 384 iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, color); 385 } 386 387 static void drm_panic_fill_pixel(struct drm_scanout_buffer *sb, 388 struct drm_rect *clip, 389 u32 color) 390 { 391 unsigned int y, x; 392 393 for (y = 0; y < drm_rect_height(clip); y++) 394 for (x = 0; x < drm_rect_width(clip); x++) 395 sb->set_pixel(sb, clip->x1 + x, clip->y1 + y, color); 396 } 397 398 /* 399 * drm_panic_fill - Fill a rectangle with a color 400 * @sb: destination scanout buffer 401 * @clip: destination rectangle 402 * @color: foreground color, in destination format 403 * 404 * Fill a rectangle with a color, in a linear framebuffer. 405 */ 406 static void drm_panic_fill(struct drm_scanout_buffer *sb, struct drm_rect *clip, 407 u32 color) 408 { 409 struct iosys_map map; 410 411 if (sb->set_pixel) 412 return drm_panic_fill_pixel(sb, clip, color); 413 414 map = sb->map[0]; 415 iosys_map_incr(&map, clip->y1 * sb->pitch[0] + clip->x1 * sb->format->cpp[0]); 416 417 switch (sb->format->cpp[0]) { 418 case 2: 419 drm_panic_fill16(&map, sb->pitch[0], drm_rect_height(clip), 420 drm_rect_width(clip), color); 421 break; 422 case 3: 423 drm_panic_fill24(&map, sb->pitch[0], drm_rect_height(clip), 424 drm_rect_width(clip), color); 425 break; 426 case 4: 427 drm_panic_fill32(&map, sb->pitch[0], drm_rect_height(clip), 428 drm_rect_width(clip), color); 429 break; 430 default: 431 WARN_ONCE(1, "Can't fill with pixel width %d\n", sb->format->cpp[0]); 432 } 433 } 434 435 static const u8 *get_char_bitmap(const struct font_desc *font, char c, size_t font_pitch) 436 { 437 return font->data + (c * font->height) * font_pitch; 438 } 439 440 static unsigned int get_max_line_len(const struct drm_panic_line *lines, int len) 441 { 442 int i; 443 unsigned int max = 0; 444 445 for (i = 0; i < len; i++) 446 max = max(lines[i].len, max); 447 return max; 448 } 449 450 /* 451 * Draw a text in a rectangle on a framebuffer. The text is truncated if it overflows the rectangle 452 */ 453 static void draw_txt_rectangle(struct drm_scanout_buffer *sb, 454 const struct font_desc *font, 455 const struct drm_panic_line *msg, 456 unsigned int msg_lines, 457 bool centered, 458 struct drm_rect *clip, 459 u32 color) 460 { 461 int i, j; 462 const u8 *src; 463 size_t font_pitch = DIV_ROUND_UP(font->width, 8); 464 struct drm_rect rec; 465 466 msg_lines = min(msg_lines, drm_rect_height(clip) / font->height); 467 for (i = 0; i < msg_lines; i++) { 468 size_t line_len = min(msg[i].len, drm_rect_width(clip) / font->width); 469 470 rec.y1 = clip->y1 + i * font->height; 471 rec.y2 = rec.y1 + font->height; 472 rec.x1 = clip->x1; 473 474 if (centered) 475 rec.x1 += (drm_rect_width(clip) - (line_len * font->width)) / 2; 476 477 for (j = 0; j < line_len; j++) { 478 src = get_char_bitmap(font, msg[i].txt[j], font_pitch); 479 rec.x2 = rec.x1 + font->width; 480 drm_panic_blit(sb, &rec, src, font_pitch, color); 481 rec.x1 += font->width; 482 } 483 } 484 } 485 486 static void draw_panic_static_user(struct drm_scanout_buffer *sb) 487 { 488 size_t msg_lines = ARRAY_SIZE(panic_msg); 489 size_t logo_ascii_lines = ARRAY_SIZE(logo_ascii); 490 u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format); 491 u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format); 492 const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); 493 struct drm_rect r_screen, r_logo, r_msg; 494 unsigned int logo_width, logo_height; 495 496 if (!font) 497 return; 498 499 r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height); 500 501 if (logo_mono) { 502 logo_width = logo_mono->width; 503 logo_height = logo_mono->height; 504 } else { 505 logo_width = get_max_line_len(logo_ascii, logo_ascii_lines) * font->width; 506 logo_height = logo_ascii_lines * font->height; 507 } 508 509 r_logo = DRM_RECT_INIT(0, 0, logo_width, logo_height); 510 r_msg = DRM_RECT_INIT(0, 0, 511 min(get_max_line_len(panic_msg, msg_lines) * font->width, sb->width), 512 min(msg_lines * font->height, sb->height)); 513 514 /* Center the panic message */ 515 drm_rect_translate(&r_msg, (sb->width - r_msg.x2) / 2, (sb->height - r_msg.y2) / 2); 516 517 /* Fill with the background color, and draw text on top */ 518 drm_panic_fill(sb, &r_screen, bg_color); 519 520 if ((r_msg.x1 >= logo_width || r_msg.y1 >= logo_height) && 521 logo_width <= sb->width && logo_height <= sb->height) { 522 if (logo_mono) 523 drm_panic_blit(sb, &r_logo, logo_mono->data, DIV_ROUND_UP(logo_width, 8), 524 fg_color); 525 else 526 draw_txt_rectangle(sb, font, logo_ascii, logo_ascii_lines, false, &r_logo, 527 fg_color); 528 } 529 draw_txt_rectangle(sb, font, panic_msg, msg_lines, true, &r_msg, fg_color); 530 } 531 532 /* 533 * Draw one line of kmsg, and handle wrapping if it won't fit in the screen width. 534 * Return the y-offset of the next line. 535 */ 536 static int draw_line_with_wrap(struct drm_scanout_buffer *sb, const struct font_desc *font, 537 struct drm_panic_line *line, int yoffset, u32 fg_color) 538 { 539 int chars_per_row = sb->width / font->width; 540 struct drm_rect r_txt = DRM_RECT_INIT(0, yoffset, sb->width, sb->height); 541 struct drm_panic_line line_wrap; 542 543 if (line->len > chars_per_row) { 544 line_wrap.len = line->len % chars_per_row; 545 line_wrap.txt = line->txt + line->len - line_wrap.len; 546 draw_txt_rectangle(sb, font, &line_wrap, 1, false, &r_txt, fg_color); 547 r_txt.y1 -= font->height; 548 if (r_txt.y1 < 0) 549 return r_txt.y1; 550 while (line_wrap.txt > line->txt) { 551 line_wrap.txt -= chars_per_row; 552 line_wrap.len = chars_per_row; 553 draw_txt_rectangle(sb, font, &line_wrap, 1, false, &r_txt, fg_color); 554 r_txt.y1 -= font->height; 555 if (r_txt.y1 < 0) 556 return r_txt.y1; 557 } 558 } else { 559 draw_txt_rectangle(sb, font, line, 1, false, &r_txt, fg_color); 560 r_txt.y1 -= font->height; 561 } 562 return r_txt.y1; 563 } 564 565 /* 566 * Draw the kmsg buffer to the screen, starting from the youngest message at the bottom, 567 * and going up until reaching the top of the screen. 568 */ 569 static void draw_panic_static_kmsg(struct drm_scanout_buffer *sb) 570 { 571 u32 fg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_FOREGROUND_COLOR, sb->format->format); 572 u32 bg_color = convert_from_xrgb8888(CONFIG_DRM_PANIC_BACKGROUND_COLOR, sb->format->format); 573 const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); 574 struct drm_rect r_screen = DRM_RECT_INIT(0, 0, sb->width, sb->height); 575 struct kmsg_dump_iter iter; 576 char kmsg_buf[512]; 577 size_t kmsg_len; 578 struct drm_panic_line line; 579 int yoffset; 580 581 if (!font) 582 return; 583 584 yoffset = sb->height - font->height - (sb->height % font->height) / 2; 585 586 /* Fill with the background color, and draw text on top */ 587 drm_panic_fill(sb, &r_screen, bg_color); 588 589 kmsg_dump_rewind(&iter); 590 while (kmsg_dump_get_buffer(&iter, false, kmsg_buf, sizeof(kmsg_buf), &kmsg_len)) { 591 char *start; 592 char *end; 593 594 /* ignore terminating NUL and newline */ 595 start = kmsg_buf + kmsg_len - 2; 596 end = kmsg_buf + kmsg_len - 1; 597 while (start > kmsg_buf && yoffset >= 0) { 598 while (start > kmsg_buf && *start != '\n') 599 start--; 600 /* don't count the newline character */ 601 line.txt = start + (start == kmsg_buf ? 0 : 1); 602 line.len = end - line.txt; 603 604 yoffset = draw_line_with_wrap(sb, font, &line, yoffset, fg_color); 605 end = start; 606 start--; 607 } 608 } 609 } 610 611 /* 612 * drm_panic_is_format_supported() 613 * @format: a fourcc color code 614 * Returns: true if supported, false otherwise. 615 * 616 * Check if drm_panic will be able to use this color format. 617 */ 618 static bool drm_panic_is_format_supported(const struct drm_format_info *format) 619 { 620 if (format->num_planes != 1) 621 return false; 622 return convert_from_xrgb8888(0xffffff, format->format) != 0; 623 } 624 625 static void draw_panic_dispatch(struct drm_scanout_buffer *sb) 626 { 627 if (!strcmp(drm_panic_screen, "kmsg")) { 628 draw_panic_static_kmsg(sb); 629 } else { 630 draw_panic_static_user(sb); 631 } 632 } 633 634 static void draw_panic_plane(struct drm_plane *plane) 635 { 636 struct drm_scanout_buffer sb = { }; 637 int ret; 638 unsigned long flags; 639 640 if (!drm_panic_trylock(plane->dev, flags)) 641 return; 642 643 ret = plane->helper_private->get_scanout_buffer(plane, &sb); 644 645 if (!ret && drm_panic_is_format_supported(sb.format)) { 646 draw_panic_dispatch(&sb); 647 if (plane->helper_private->panic_flush) 648 plane->helper_private->panic_flush(plane); 649 } 650 drm_panic_unlock(plane->dev, flags); 651 } 652 653 static struct drm_plane *to_drm_plane(struct kmsg_dumper *kd) 654 { 655 return container_of(kd, struct drm_plane, kmsg_panic); 656 } 657 658 static void drm_panic(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) 659 { 660 struct drm_plane *plane = to_drm_plane(dumper); 661 662 if (reason == KMSG_DUMP_PANIC) 663 draw_panic_plane(plane); 664 } 665 666 667 /* 668 * DEBUG FS, This is currently unsafe. 669 * Create one file per plane, so it's possible to debug one plane at a time. 670 * TODO: It would be better to emulate an NMI context. 671 */ 672 #ifdef CONFIG_DRM_PANIC_DEBUG 673 #include <linux/debugfs.h> 674 675 static ssize_t debugfs_trigger_write(struct file *file, const char __user *user_buf, 676 size_t count, loff_t *ppos) 677 { 678 bool run; 679 680 if (kstrtobool_from_user(user_buf, count, &run) == 0 && run) { 681 struct drm_plane *plane = file->private_data; 682 683 draw_panic_plane(plane); 684 } 685 return count; 686 } 687 688 static const struct file_operations dbg_drm_panic_ops = { 689 .owner = THIS_MODULE, 690 .write = debugfs_trigger_write, 691 .open = simple_open, 692 }; 693 694 static void debugfs_register_plane(struct drm_plane *plane, int index) 695 { 696 char fname[32]; 697 698 snprintf(fname, 32, "drm_panic_plane_%d", index); 699 debugfs_create_file(fname, 0200, plane->dev->debugfs_root, 700 plane, &dbg_drm_panic_ops); 701 } 702 #else 703 static void debugfs_register_plane(struct drm_plane *plane, int index) {} 704 #endif /* CONFIG_DRM_PANIC_DEBUG */ 705 706 /** 707 * drm_panic_register() - Initialize DRM panic for a device 708 * @dev: the drm device on which the panic screen will be displayed. 709 */ 710 void drm_panic_register(struct drm_device *dev) 711 { 712 struct drm_plane *plane; 713 int registered_plane = 0; 714 715 if (!dev->mode_config.num_total_plane) 716 return; 717 718 drm_for_each_plane(plane, dev) { 719 if (!plane->helper_private || !plane->helper_private->get_scanout_buffer) 720 continue; 721 plane->kmsg_panic.dump = drm_panic; 722 plane->kmsg_panic.max_reason = KMSG_DUMP_PANIC; 723 if (kmsg_dump_register(&plane->kmsg_panic)) 724 drm_warn(dev, "Failed to register panic handler\n"); 725 else { 726 debugfs_register_plane(plane, registered_plane); 727 registered_plane++; 728 } 729 } 730 if (registered_plane) 731 drm_info(dev, "Registered %d planes with drm panic\n", registered_plane); 732 } 733 EXPORT_SYMBOL(drm_panic_register); 734 735 /** 736 * drm_panic_unregister() 737 * @dev: the drm device previously registered. 738 */ 739 void drm_panic_unregister(struct drm_device *dev) 740 { 741 struct drm_plane *plane; 742 743 if (!dev->mode_config.num_total_plane) 744 return; 745 746 drm_for_each_plane(plane, dev) { 747 if (!plane->helper_private || !plane->helper_private->get_scanout_buffer) 748 continue; 749 kmsg_dump_unregister(&plane->kmsg_panic); 750 } 751 } 752 EXPORT_SYMBOL(drm_panic_unregister); 753