From bf9fb17c6672868d95126321762c8fdfe0ff0a2a Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Tue, 9 Apr 2024 18:30:41 +0200 Subject: drm/panic: Add a drm panic handler This module displays a user friendly message when a kernel panic occurs. It currently doesn't contain any debug information, but that can be added later. v2 * Use get_scanout_buffer() instead of the drm client API. (Thomas Zimmermann) * Add the panic reason to the panic message (Nerdopolis) * Add an exclamation mark (Nerdopolis) v3 * Rework the drawing functions, to write the pixels line by line and to use the drm conversion helper to support other formats. (Thomas Zimmermann) v4 * Use drm_fb_r1_to_32bit for fonts (Thomas Zimmermann) * Remove the default y to DRM_PANIC config option (Thomas Zimmermann) * Add foreground/background color config option * Fix the bottom lines not painted if the framebuffer height is not a multiple of the font height. * Automatically register the device to drm_panic, if the function get_scanout_buffer exists. (Thomas Zimmermann) v5 * Change the drawing API, use drm_fb_blit_from_r1() to draw the font. * Also add drm_fb_fill() to fill area with background color. * Add draw_pixel_xy() API for drivers that can't provide a linear buffer. * Add a flush() callback for drivers that needs to synchronize the buffer. * Add a void *private field, so drivers can pass private data to draw_pixel_xy() and flush(). v6 * Fix sparse warning for panic_msg and logo. v7 * Add select DRM_KMS_HELPER for the color conversion functions. v8 * Register directly each plane to the panic notifier (Sima) * Add raw_spinlock to properly handle concurrency (Sima) * Register plane instead of device, to avoid looping through plane list, and simplify code. * Replace get_scanout_buffer() logic with drm_panic_set_buffer() (Thomas Zimmermann) * Removed the draw_pixel_xy() API, will see later if it can be added back. v9 * Revert to using get_scanout_buffer() (Sima) * Move get_scanout_buffer() and panic_flush() to the plane helper functions (Thomas Zimmermann) * Register all planes with get_scanout_buffer() to the panic notifier * Use drm_panic_lock() to protect against race (Sima) v10 * Move blit and fill functions back in drm_panic (Thomas Zimmermann). * Simplify the text drawing functions. * Use kmsg_dumper instead of panic_notifier (Sima). v12 * Use array for map and pitch in struct drm_scanout_buffer to support multi-planar format later. (Thomas Zimmermann) * Better indent struct drm_scanout_buffer declaration. (Thomas Zimmermann) Signed-off-by: Jocelyn Falempe Link: https://patchwork.freedesktop.org/patch/msgid/20240409163432.352518-3-jfalempe@redhat.com Acked-by: Daniel Vetter --- drivers/gpu/drm/drm_panic.c | 290 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 drivers/gpu/drm/drm_panic.c (limited to 'drivers/gpu/drm/drm_panic.c') diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c new file mode 100644 index 000000000000..7c81b3d73239 --- /dev/null +++ b/drivers/gpu/drm/drm_panic.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 or MIT +/* + * Copyright (c) 2023 Red Hat. + * Author: Jocelyn Falempe + * inspired by the drm_log driver from David Herrmann + * Tux Ascii art taken from cowsay written by Tony Monroe + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Jocelyn Falempe"); +MODULE_DESCRIPTION("DRM panic handler"); +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. + * + * Implementation details: + * + * 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. + */ + +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."), +}; + +static const struct drm_panic_line logo[] = { + PANIC_LINE(" .--. _"), + PANIC_LINE(" |o_o | | |"), + PANIC_LINE(" |:_/ | | |"), + PANIC_LINE(" // \\ \\ |_|"), + PANIC_LINE(" (| | ) _"), + PANIC_LINE(" /'\\_ _/`\\ (_)"), + PANIC_LINE(" \\___)=(___/"), +}; + +static void drm_panic_fill32(struct iosys_map *map, unsigned int pitch, + unsigned int height, unsigned int width, + u32 color) +{ + unsigned int y, x; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + iosys_map_wr(map, y * pitch + x * sizeof(u32), u32, color); +} + +static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch, + const u8 *sbuf8, unsigned int spitch, + unsigned int height, unsigned int width, + u32 fg32, u32 bg32) +{ + unsigned int y, x; + u32 val32; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + val32 = (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32; + iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, val32); + } + } +} + +static const u8 *get_char_bitmap(const struct font_desc *font, char c, size_t font_pitch) +{ + return font->data + (c * font->height) * font_pitch; +} + +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 fg_color, + u32 bg_color) +{ + int i, j; + const u8 *src; + size_t font_pitch = DIV_ROUND_UP(font->width, 8); + struct iosys_map dst; + unsigned int px_width = sb->format->cpp[0]; + int left = 0; + + 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); + + if (centered) + left = (drm_rect_width(clip) - (line_len * font->width)) / 2; + + dst = sb->map[0]; + iosys_map_incr(&dst, (clip->y1 + i * font->height) * sb->pitch[0] + + (clip->x1 + left) * px_width); + for (j = 0; j < line_len; j++) { + src = get_char_bitmap(font, msg[i].txt[j], font_pitch); + drm_panic_blit32(&dst, sb->pitch[0], src, font_pitch, + font->height, font->width, + fg_color, bg_color); + iosys_map_incr(&dst, font->width * px_width); + } + } +} + +/* + * Draw the panic message at the center of the screen + */ +static void draw_panic_static(struct drm_scanout_buffer *sb) +{ + size_t msg_lines = ARRAY_SIZE(panic_msg); + size_t logo_lines = ARRAY_SIZE(logo); + u32 fg_color = CONFIG_DRM_PANIC_FOREGROUND_COLOR; + u32 bg_color = CONFIG_DRM_PANIC_BACKGROUND_COLOR; + const struct font_desc *font = get_default_font(sb->width, sb->height, NULL, NULL); + struct drm_rect r_logo, r_msg; + + if (!font) + return; + + r_logo = DRM_RECT_INIT(0, 0, + get_max_line_len(logo, logo_lines) * font->width, + logo_lines * font->height); + r_msg = DRM_RECT_INIT(0, 0, + min(get_max_line_len(panic_msg, msg_lines) * font->width, sb->width), + min(msg_lines * font->height, sb->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_fill32(&sb->map[0], sb->pitch[0], sb->height, sb->width, bg_color); + + if ((r_msg.x1 >= drm_rect_width(&r_logo) || r_msg.y1 >= drm_rect_height(&r_logo)) && + drm_rect_width(&r_logo) < sb->width && drm_rect_height(&r_logo) < sb->height) { + draw_txt_rectangle(sb, font, logo, logo_lines, false, &r_logo, fg_color, bg_color); + } + draw_txt_rectangle(sb, font, panic_msg, msg_lines, true, &r_msg, fg_color, bg_color); +} + +/* + * 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 format->format == DRM_FORMAT_XRGB8888; +} + +static void draw_panic_plane(struct drm_plane *plane) +{ + struct drm_scanout_buffer sb; + int ret; + unsigned long flags; + + 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)) { + draw_panic_static(&sb); + if (plane->helper_private->panic_flush) + plane->helper_private->panic_flush(plane); + } + drm_panic_unlock(plane->dev, flags); +} + +static struct drm_plane *to_drm_plane(struct kmsg_dumper *kd) +{ + return container_of(kd, struct drm_plane, kmsg_panic); +} + +static void drm_panic(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) +{ + struct drm_plane *plane = to_drm_plane(dumper); + + if (reason == KMSG_DUMP_PANIC) + draw_panic_plane(plane); +} + +/** + * drm_panic_register() - Initialize DRM panic for a device + * @dev: the drm device on which the panic screen will be displayed. + */ +void drm_panic_register(struct drm_device *dev) +{ + struct drm_plane *plane; + int registered_plane = 0; + + if (!dev->mode_config.num_total_plane) + return; + + drm_for_each_plane(plane, dev) { + if (!plane->helper_private || !plane->helper_private->get_scanout_buffer) + continue; + plane->kmsg_panic.dump = drm_panic; + plane->kmsg_panic.max_reason = KMSG_DUMP_PANIC; + if (kmsg_dump_register(&plane->kmsg_panic)) + drm_warn(dev, "Failed to register panic handler\n"); + else + registered_plane++; + } + if (registered_plane) + drm_info(dev, "Registered %d planes with drm panic\n", registered_plane); +} +EXPORT_SYMBOL(drm_panic_register); + +/** + * drm_panic_unregister() + * @dev: the drm device previously registered. + */ +void drm_panic_unregister(struct drm_device *dev) +{ + struct drm_plane *plane; + + if (!dev->mode_config.num_total_plane) + return; + + drm_for_each_plane(plane, dev) { + if (!plane->helper_private || !plane->helper_private->get_scanout_buffer) + continue; + kmsg_dump_unregister(&plane->kmsg_panic); + } +} +EXPORT_SYMBOL(drm_panic_unregister); -- cgit v1.2.3-59-g8ed1b From 9544309775c334c9cc5f7d88be3daccc846199d1 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Tue, 9 Apr 2024 18:30:42 +0200 Subject: drm/panic: Add support for color format conversion Add support for the following formats: DRM_FORMAT_RGB565 DRM_FORMAT_RGBA5551 DRM_FORMAT_XRGB1555 DRM_FORMAT_ARGB1555 DRM_FORMAT_RGB888 DRM_FORMAT_XRGB8888 DRM_FORMAT_ARGB8888 DRM_FORMAT_XBGR8888 DRM_FORMAT_XRGB2101010 DRM_FORMAT_ARGB2101010 v10: * move and simplify the functions from the drm format helper to drm_panic v12: * Use array for map and pitch in struct drm_scanout_buffer to support multi-planar format later. (Thomas Zimmermann) Signed-off-by: Jocelyn Falempe Link: https://patchwork.freedesktop.org/patch/msgid/20240409163432.352518-4-jfalempe@redhat.com Acked-by: Daniel Vetter --- drivers/gpu/drm/drm_panic.c | 273 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 263 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/drm_panic.c') diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index 7c81b3d73239..452cfe08a7c8 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -81,15 +81,155 @@ static const struct drm_panic_line logo[] = { PANIC_LINE(" \\___)=(___/"), }; -static void drm_panic_fill32(struct iosys_map *map, unsigned int pitch, +/* + * Color conversion + */ + +static u16 convert_xrgb8888_to_rgb565(u32 pix) +{ + return ((pix & 0x00F80000) >> 8) | + ((pix & 0x0000FC00) >> 5) | + ((pix & 0x000000F8) >> 3); +} + +static u16 convert_xrgb8888_to_rgba5551(u32 pix) +{ + return ((pix & 0x00f80000) >> 8) | + ((pix & 0x0000f800) >> 5) | + ((pix & 0x000000f8) >> 2) | + BIT(0); /* set alpha bit */ +} + +static u16 convert_xrgb8888_to_xrgb1555(u32 pix) +{ + return ((pix & 0x00f80000) >> 9) | + ((pix & 0x0000f800) >> 6) | + ((pix & 0x000000f8) >> 3); +} + +static u16 convert_xrgb8888_to_argb1555(u32 pix) +{ + return BIT(15) | /* set alpha bit */ + ((pix & 0x00f80000) >> 9) | + ((pix & 0x0000f800) >> 6) | + ((pix & 0x000000f8) >> 3); +} + +static u32 convert_xrgb8888_to_argb8888(u32 pix) +{ + return pix | GENMASK(31, 24); /* fill alpha bits */ +} + +static u32 convert_xrgb8888_to_xbgr8888(u32 pix) +{ + return ((pix & 0x00ff0000) >> 16) << 0 | + ((pix & 0x0000ff00) >> 8) << 8 | + ((pix & 0x000000ff) >> 0) << 16 | + ((pix & 0xff000000) >> 24) << 24; +} + +static u32 convert_xrgb8888_to_abgr8888(u32 pix) +{ + return ((pix & 0x00ff0000) >> 16) << 0 | + ((pix & 0x0000ff00) >> 8) << 8 | + ((pix & 0x000000ff) >> 0) << 16 | + GENMASK(31, 24); /* fill alpha bits */ +} + +static u32 convert_xrgb8888_to_xrgb2101010(u32 pix) +{ + pix = ((pix & 0x000000FF) << 2) | + ((pix & 0x0000FF00) << 4) | + ((pix & 0x00FF0000) << 6); + return pix | ((pix >> 8) & 0x00300C03); +} + +static u32 convert_xrgb8888_to_argb2101010(u32 pix) +{ + pix = ((pix & 0x000000FF) << 2) | + ((pix & 0x0000FF00) << 4) | + ((pix & 0x00FF0000) << 6); + return GENMASK(31, 30) /* set alpha bits */ | pix | ((pix >> 8) & 0x00300C03); +} + +/* + * convert_from_xrgb8888 - convert one pixel from xrgb8888 to the desired format + * @color: input color, in xrgb8888 format + * @format: output format + * + * Returns: + * Color in the format specified, casted to u32. + * Or 0 if the format is not supported. + */ +static u32 convert_from_xrgb8888(u32 color, u32 format) +{ + switch (format) { + case DRM_FORMAT_RGB565: + return convert_xrgb8888_to_rgb565(color); + case DRM_FORMAT_RGBA5551: + return convert_xrgb8888_to_rgba5551(color); + case DRM_FORMAT_XRGB1555: + return convert_xrgb8888_to_xrgb1555(color); + case DRM_FORMAT_ARGB1555: + return convert_xrgb8888_to_argb1555(color); + case DRM_FORMAT_RGB888: + case DRM_FORMAT_XRGB8888: + return color; + case DRM_FORMAT_ARGB8888: + return convert_xrgb8888_to_argb8888(color); + case DRM_FORMAT_XBGR8888: + return convert_xrgb8888_to_xbgr8888(color); + case DRM_FORMAT_ABGR8888: + return convert_xrgb8888_to_abgr8888(color); + case DRM_FORMAT_XRGB2101010: + return convert_xrgb8888_to_xrgb2101010(color); + case DRM_FORMAT_ARGB2101010: + return convert_xrgb8888_to_argb2101010(color); + default: + WARN_ONCE(1, "Can't convert to %p4cc\n", &format); + return 0; + } +} + +/* + * Blit & Fill + */ +static void drm_panic_blit16(struct iosys_map *dmap, unsigned int dpitch, + const u8 *sbuf8, unsigned int spitch, unsigned int height, unsigned int width, - u32 color) + u16 fg16, u16 bg16) { unsigned int y, x; + u16 val16; - for (y = 0; y < height; y++) - for (x = 0; x < width; x++) - iosys_map_wr(map, y * pitch + x * sizeof(u32), u32, color); + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + val16 = (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg16 : bg16; + iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, val16); + } + } +} + +static void drm_panic_blit24(struct iosys_map *dmap, unsigned int dpitch, + const u8 *sbuf8, unsigned int spitch, + unsigned int height, unsigned int width, + u32 fg32, u32 bg32) +{ + unsigned int y, x; + u32 val32; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + u32 off = y * dpitch + x * 3; + + val32 = (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) ? fg32 : bg32; + + /* write blue-green-red to output in little endianness */ + iosys_map_wr(dmap, off, u8, (val32 & 0x000000FF) >> 0); + iosys_map_wr(dmap, off + 1, u8, (val32 & 0x0000FF00) >> 8); + iosys_map_wr(dmap, off + 2, u8, (val32 & 0x00FF0000) >> 16); + } + } } static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch, @@ -108,6 +248,115 @@ static void drm_panic_blit32(struct iosys_map *dmap, unsigned int dpitch, } } +/* + * drm_panic_blit - convert a monochrome image to a linear framebuffer + * @dmap: destination iosys_map + * @dpitch: destination pitch in bytes + * @sbuf8: source buffer, in monochrome format, 8 pixels per byte. + * @spitch: source pitch in bytes + * @height: height of the image to copy, in pixels + * @width: width of the image to copy, in pixels + * @fg_color: foreground color, in destination format + * @bg_color: background color, in destination format + * @pixel_width: pixel width in bytes. + * + * 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 iosys_map *dmap, unsigned int dpitch, + const u8 *sbuf8, unsigned int spitch, + unsigned int height, unsigned int width, + u32 fg_color, u32 bg_color, + unsigned int pixel_width) +{ + switch (pixel_width) { + case 2: + drm_panic_blit16(dmap, dpitch, sbuf8, spitch, + height, width, fg_color, bg_color); + break; + case 3: + drm_panic_blit24(dmap, dpitch, sbuf8, spitch, + height, width, fg_color, bg_color); + break; + case 4: + drm_panic_blit32(dmap, dpitch, sbuf8, spitch, + height, width, fg_color, bg_color); + break; + default: + WARN_ONCE(1, "Can't blit with pixel width %d\n", pixel_width); + } +} + +static void drm_panic_fill16(struct iosys_map *dmap, unsigned int dpitch, + unsigned int height, unsigned int width, + u16 color) +{ + unsigned int y, x; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + iosys_map_wr(dmap, y * dpitch + x * sizeof(u16), u16, color); +} + +static void drm_panic_fill24(struct iosys_map *dmap, unsigned int dpitch, + unsigned int height, unsigned int width, + u32 color) +{ + unsigned int y, x; + + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + unsigned int off = y * dpitch + x * 3; + + /* write blue-green-red to output in little endianness */ + iosys_map_wr(dmap, off, u8, (color & 0x000000FF) >> 0); + iosys_map_wr(dmap, off + 1, u8, (color & 0x0000FF00) >> 8); + iosys_map_wr(dmap, off + 2, u8, (color & 0x00FF0000) >> 16); + } + } +} + +static void drm_panic_fill32(struct iosys_map *dmap, unsigned int dpitch, + unsigned int height, unsigned int width, + u32 color) +{ + unsigned int y, x; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + iosys_map_wr(dmap, y * dpitch + x * sizeof(u32), u32, color); +} + +/* + * drm_panic_fill - Fill a rectangle with a color + * @dmap: destination iosys_map, pointing to the top left corner of the rectangle + * @dpitch: destination pitch in bytes + * @height: height of the rectangle, in pixels + * @width: width of the rectangle, in pixels + * @color: color to fill the rectangle. + * @pixel_width: pixel width in bytes + * + * Fill a rectangle with a color, in a linear framebuffer. + */ +static void drm_panic_fill(struct iosys_map *dmap, unsigned int dpitch, + unsigned int height, unsigned int width, + u32 color, unsigned int pixel_width) +{ + switch (pixel_width) { + case 2: + drm_panic_fill16(dmap, dpitch, height, width, color); + break; + case 3: + drm_panic_fill24(dmap, dpitch, height, width, color); + break; + case 4: + drm_panic_fill32(dmap, dpitch, height, width, color); + break; + default: + WARN_ONCE(1, "Can't fill with pixel width %d\n", pixel_width); + } +} + static const u8 *get_char_bitmap(const struct font_desc *font, char c, size_t font_pitch) { return font->data + (c * font->height) * font_pitch; @@ -154,9 +403,9 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb, (clip->x1 + left) * px_width); for (j = 0; j < line_len; j++) { src = get_char_bitmap(font, msg[i].txt[j], font_pitch); - drm_panic_blit32(&dst, sb->pitch[0], src, font_pitch, - font->height, font->width, - fg_color, bg_color); + drm_panic_blit(&dst, sb->pitch[0], src, font_pitch, + font->height, font->width, + fg_color, bg_color, px_width); iosys_map_incr(&dst, font->width * px_width); } } @@ -177,6 +426,9 @@ static void draw_panic_static(struct drm_scanout_buffer *sb) if (!font) return; + fg_color = convert_from_xrgb8888(fg_color, sb->format->format); + bg_color = convert_from_xrgb8888(bg_color, sb->format->format); + r_logo = DRM_RECT_INIT(0, 0, get_max_line_len(logo, logo_lines) * font->width, logo_lines * font->height); @@ -188,7 +440,8 @@ static void draw_panic_static(struct drm_scanout_buffer *sb) 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_fill32(&sb->map[0], sb->pitch[0], sb->height, sb->width, bg_color); + drm_panic_fill(&sb->map[0], sb->pitch[0], sb->height, sb->width, + bg_color, sb->format->cpp[0]); if ((r_msg.x1 >= drm_rect_width(&r_logo) || r_msg.y1 >= drm_rect_height(&r_logo)) && drm_rect_width(&r_logo) < sb->width && drm_rect_height(&r_logo) < sb->height) { @@ -208,7 +461,7 @@ static bool drm_panic_is_format_supported(const struct drm_format_info *format) { if (format->num_planes != 1) return false; - return format->format == DRM_FORMAT_XRGB8888; + return convert_from_xrgb8888(0xffffff, format->format) != 0; } static void draw_panic_plane(struct drm_plane *plane) -- cgit v1.2.3-59-g8ed1b From 813ca3aa8fecaca9cf81f831d2e036d997ac3ee7 Mon Sep 17 00:00:00 2001 From: Jocelyn Falempe Date: Tue, 9 Apr 2024 18:30:43 +0200 Subject: drm/panic: Add debugfs entry to test without triggering panic. Add a debugfs file, so you can test drm_panic without freezing your machine. This is unsafe, and should be enabled only for developer or tester. To display the drm_panic screen on the device 0: echo 1 > /sys/kernel/debug/dri/0/drm_panic_plane_0 v9: * Create a debugfs file for each plane in the device's debugfs directory. This allows to test for each plane of each GPU independently. Signed-off-by: Jocelyn Falempe Link: https://patchwork.freedesktop.org/patch/msgid/20240409163432.352518-5-jfalempe@redhat.com Acked-by: Daniel Vetter --- drivers/gpu/drm/Kconfig | 9 +++++++++ drivers/gpu/drm/drm_panic.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/drm_panic.c') diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index f8a26423369e..959b19a04101 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -127,6 +127,15 @@ config DRM_PANIC_BACKGROUND_COLOR depends on DRM_PANIC default 0x000000 +config DRM_PANIC_DEBUG + bool "Add a debug fs entry to trigger drm_panic" + depends on DRM_PANIC && DEBUG_FS + help + Add dri/[device]/drm_panic_plane_x in the kernel debugfs, to force the + panic handler to write the panic message to this plane scanout buffer. + This is unsafe and should not be enabled on a production build. + If in doubt, say "N". + config DRM_DEBUG_DP_MST_TOPOLOGY_REFS bool "Enable refcount backtrace history in the DP MST helpers" depends on STACKTRACE_SUPPORT diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index 452cfe08a7c8..7ece67086cec 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -496,6 +496,46 @@ static void drm_panic(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason) draw_panic_plane(plane); } + +/* + * DEBUG FS, This is currently unsafe. + * Create one file per plane, so it's possible to debug one plane at a time. + * TODO: It would be better to emulate an NMI context. + */ +#ifdef CONFIG_DRM_PANIC_DEBUG +#include + +static ssize_t debugfs_trigger_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + bool run; + + if (kstrtobool_from_user(user_buf, count, &run) == 0 && run) { + struct drm_plane *plane = file->private_data; + + draw_panic_plane(plane); + } + return count; +} + +static const struct file_operations dbg_drm_panic_ops = { + .owner = THIS_MODULE, + .write = debugfs_trigger_write, + .open = simple_open, +}; + +static void debugfs_register_plane(struct drm_plane *plane, int index) +{ + char fname[32]; + + snprintf(fname, 32, "drm_panic_plane_%d", index); + debugfs_create_file(fname, 0200, plane->dev->debugfs_root, + plane, &dbg_drm_panic_ops); +} +#else +static void debugfs_register_plane(struct drm_plane *plane, int index) {} +#endif /* CONFIG_DRM_PANIC_DEBUG */ + /** * drm_panic_register() - Initialize DRM panic for a device * @dev: the drm device on which the panic screen will be displayed. @@ -515,8 +555,10 @@ void drm_panic_register(struct drm_device *dev) plane->kmsg_panic.max_reason = KMSG_DUMP_PANIC; if (kmsg_dump_register(&plane->kmsg_panic)) drm_warn(dev, "Failed to register panic handler\n"); - else + else { + debugfs_register_plane(plane, registered_plane); registered_plane++; + } } if (registered_plane) drm_info(dev, "Registered %d planes with drm panic\n", registered_plane); -- cgit v1.2.3-59-g8ed1b