| // SPDX-License-Identifier: GPL-2.0 or MIT |
| /* |
| * Copyright (c) 2025 Red Hat. |
| * Author: Jocelyn Falempe <jfalempe@redhat.com> |
| * |
| * KUNIT tests for drm panic |
| */ |
| |
| #include <drm/drm_fourcc.h> |
| #include <drm/drm_panic.h> |
| |
| #include <kunit/test.h> |
| |
| #include <linux/units.h> |
| #include <linux/vmalloc.h> |
| |
| /* Check the framebuffer color only if the panic colors are the default */ |
| #if (CONFIG_DRM_PANIC_BACKGROUND_COLOR == 0 && \ |
| CONFIG_DRM_PANIC_FOREGROUND_COLOR == 0xffffff) |
| |
| static void drm_panic_check_color_byte(struct kunit *test, u8 b) |
| { |
| KUNIT_EXPECT_TRUE(test, (b == 0 || b == 0xff)); |
| } |
| #else |
| static void drm_panic_check_color_byte(struct kunit *test, u8 b) {} |
| #endif |
| |
| struct drm_test_mode { |
| const int width; |
| const int height; |
| const u32 format; |
| void (*draw_screen)(struct drm_scanout_buffer *sb); |
| const char *fname; |
| }; |
| |
| /* |
| * Run all tests for the 3 panic screens: user, kmsg and qr_code |
| */ |
| #define DRM_TEST_MODE_LIST(func) \ |
| DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_XRGB8888, func) \ |
| DRM_PANIC_TEST_MODE(300, 200, DRM_FORMAT_XRGB8888, func) \ |
| DRM_PANIC_TEST_MODE(1920, 1080, DRM_FORMAT_XRGB8888, func) \ |
| DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB565, func) \ |
| DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB888, func) \ |
| |
| #define DRM_PANIC_TEST_MODE(w, h, f, name) { \ |
| .width = w, \ |
| .height = h, \ |
| .format = f, \ |
| .draw_screen = draw_panic_screen_##name, \ |
| .fname = #name, \ |
| }, \ |
| |
| static const struct drm_test_mode drm_test_modes_cases[] = { |
| DRM_TEST_MODE_LIST(user) |
| DRM_TEST_MODE_LIST(kmsg) |
| #if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE) |
| DRM_TEST_MODE_LIST(qr_code) |
| #endif |
| }; |
| |
| #undef DRM_PANIC_TEST_MODE |
| |
| static int drm_test_panic_init(struct kunit *test) |
| { |
| struct drm_scanout_buffer *priv; |
| |
| priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); |
| KUNIT_ASSERT_NOT_NULL(test, priv); |
| |
| test->priv = priv; |
| |
| drm_panic_set_description("Kunit testing"); |
| |
| return 0; |
| } |
| |
| /* |
| * Test drawing the panic screen, using a memory mapped framebuffer |
| * Set the whole buffer to 0xa5, and then check that all pixels have been |
| * written. |
| */ |
| static void drm_test_panic_screen_user_map(struct kunit *test) |
| { |
| struct drm_scanout_buffer *sb = test->priv; |
| const struct drm_test_mode *params = test->param_value; |
| char *fb; |
| int fb_size; |
| int i; |
| |
| sb->format = drm_format_info(params->format); |
| fb_size = params->width * params->height * sb->format->cpp[0]; |
| |
| fb = vmalloc(fb_size); |
| KUNIT_ASSERT_NOT_NULL(test, fb); |
| |
| memset(fb, 0xa5, fb_size); |
| |
| iosys_map_set_vaddr(&sb->map[0], fb); |
| sb->width = params->width; |
| sb->height = params->height; |
| sb->pitch[0] = params->width * sb->format->cpp[0]; |
| |
| params->draw_screen(sb); |
| |
| for (i = 0; i < fb_size; i++) |
| drm_panic_check_color_byte(test, fb[i]); |
| |
| vfree(fb); |
| } |
| |
| /* |
| * Test drawing the panic screen, using a list of pages framebuffer |
| * Set the whole buffer to 0xa5, and then check that all pixels have been |
| * written. |
| */ |
| static void drm_test_panic_screen_user_page(struct kunit *test) |
| { |
| struct drm_scanout_buffer *sb = test->priv; |
| const struct drm_test_mode *params = test->param_value; |
| int fb_size, p, i, npages; |
| struct page **pages; |
| u8 *vaddr; |
| |
| sb->format = drm_format_info(params->format); |
| fb_size = params->width * params->height * sb->format->cpp[0]; |
| npages = DIV_ROUND_UP(fb_size, PAGE_SIZE); |
| |
| pages = kmalloc_objs(struct page *, npages); |
| KUNIT_ASSERT_NOT_NULL(test, pages); |
| |
| for (p = 0; p < npages; p++) { |
| pages[p] = alloc_page(GFP_KERNEL); |
| if (!pages[p]) { |
| npages = p - 1; |
| KUNIT_FAIL(test, "Can't allocate page\n"); |
| goto free_pages; |
| } |
| vaddr = kmap_local_page(pages[p]); |
| memset(vaddr, 0xa5, PAGE_SIZE); |
| kunmap_local(vaddr); |
| } |
| sb->pages = pages; |
| sb->width = params->width; |
| sb->height = params->height; |
| sb->pitch[0] = params->width * sb->format->cpp[0]; |
| |
| params->draw_screen(sb); |
| |
| for (p = 0; p < npages; p++) { |
| int bytes_in_page = (p == npages - 1) ? fb_size - p * PAGE_SIZE : PAGE_SIZE; |
| |
| vaddr = kmap_local_page(pages[p]); |
| for (i = 0; i < bytes_in_page; i++) |
| drm_panic_check_color_byte(test, vaddr[i]); |
| |
| kunmap_local(vaddr); |
| } |
| |
| free_pages: |
| for (p = 0; p < npages; p++) |
| __free_page(pages[p]); |
| kfree(pages); |
| } |
| |
| static void drm_test_panic_set_pixel(struct drm_scanout_buffer *sb, |
| unsigned int x, |
| unsigned int y, |
| u32 color) |
| { |
| struct kunit *test = (struct kunit *)sb->private; |
| |
| KUNIT_ASSERT_TRUE(test, x < sb->width && y < sb->height); |
| } |
| |
| /* |
| * Test drawing the panic screen, using the set_pixel callback |
| * Check that all calls to set_pixel() are within the framebuffer |
| */ |
| static void drm_test_panic_screen_user_set_pixel(struct kunit *test) |
| { |
| struct drm_scanout_buffer *sb = test->priv; |
| const struct drm_test_mode *params = test->param_value; |
| |
| sb->format = drm_format_info(params->format); |
| sb->set_pixel = drm_test_panic_set_pixel; |
| sb->width = params->width; |
| sb->height = params->height; |
| sb->private = test; |
| |
| params->draw_screen(sb); |
| } |
| |
| static void drm_test_panic_desc(const struct drm_test_mode *t, char *desc) |
| { |
| sprintf(desc, "Panic screen %s, mode: %d x %d \t%p4cc", |
| t->fname, t->width, t->height, &t->format); |
| } |
| |
| KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_map, drm_test_modes_cases, drm_test_panic_desc); |
| KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_page, drm_test_modes_cases, drm_test_panic_desc); |
| KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_set_pixel, drm_test_modes_cases, drm_test_panic_desc); |
| |
| static struct kunit_case drm_panic_screen_user_test[] = { |
| KUNIT_CASE_PARAM(drm_test_panic_screen_user_map, |
| drm_test_panic_screen_user_map_gen_params), |
| KUNIT_CASE_PARAM(drm_test_panic_screen_user_page, |
| drm_test_panic_screen_user_page_gen_params), |
| KUNIT_CASE_PARAM(drm_test_panic_screen_user_set_pixel, |
| drm_test_panic_screen_user_set_pixel_gen_params), |
| { } |
| }; |
| |
| static struct kunit_suite drm_panic_suite = { |
| .name = "drm_panic", |
| .init = drm_test_panic_init, |
| .test_cases = drm_panic_screen_user_test, |
| }; |
| |
| kunit_test_suite(drm_panic_suite); |