xref: /linux/drivers/gpu/drm/tests/drm_panic_test.c (revision c0d6f52f9b62479d61f8cd4faf9fb2f8bce6e301)
1 // SPDX-License-Identifier: GPL-2.0 or MIT
2 /*
3  * Copyright (c) 2025 Red Hat.
4  * Author: Jocelyn Falempe <jfalempe@redhat.com>
5  *
6  * KUNIT tests for drm panic
7  */
8 
9 #include <drm/drm_fourcc.h>
10 #include <drm/drm_panic.h>
11 
12 #include <kunit/test.h>
13 
14 #include <linux/units.h>
15 #include <linux/vmalloc.h>
16 
17 /* Check the framebuffer color only if the panic colors are the default */
18 #if (CONFIG_DRM_PANIC_BACKGROUND_COLOR == 0 && \
19 	CONFIG_DRM_PANIC_FOREGROUND_COLOR == 0xffffff)
20 
21 static void drm_panic_check_color_byte(struct kunit *test, u8 b)
22 {
23 	KUNIT_EXPECT_TRUE(test, (b == 0 || b == 0xff));
24 }
25 #else
26 static void drm_panic_check_color_byte(struct kunit *test, u8 b) {}
27 #endif
28 
29 struct drm_test_mode {
30 	const int width;
31 	const int height;
32 	const u32 format;
33 	void (*draw_screen)(struct drm_scanout_buffer *sb);
34 	const char *fname;
35 };
36 
37 /*
38  * Run all tests for the 3 panic screens: user, kmsg and qr_code
39  */
40 #define DRM_TEST_MODE_LIST(func) \
41 	DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_XRGB8888, func) \
42 	DRM_PANIC_TEST_MODE(300, 200, DRM_FORMAT_XRGB8888, func) \
43 	DRM_PANIC_TEST_MODE(1920, 1080, DRM_FORMAT_XRGB8888, func) \
44 	DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB565, func) \
45 	DRM_PANIC_TEST_MODE(1024, 768, DRM_FORMAT_RGB888, func) \
46 
47 #define DRM_PANIC_TEST_MODE(w, h, f, name) { \
48 	.width = w, \
49 	.height = h, \
50 	.format = f, \
51 	.draw_screen = draw_panic_screen_##name, \
52 	.fname = #name, \
53 	}, \
54 
55 static const struct drm_test_mode drm_test_modes_cases[] = {
56 	DRM_TEST_MODE_LIST(user)
57 	DRM_TEST_MODE_LIST(kmsg)
58 #if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE)
59 	DRM_TEST_MODE_LIST(qr_code)
60 #endif
61 };
62 
63 #undef DRM_PANIC_TEST_MODE
64 
65 static int drm_test_panic_init(struct kunit *test)
66 {
67 	struct drm_scanout_buffer *priv;
68 
69 	priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
70 	KUNIT_ASSERT_NOT_NULL(test, priv);
71 
72 	test->priv = priv;
73 
74 	drm_panic_set_description("Kunit testing");
75 
76 	return 0;
77 }
78 
79 /*
80  * Test drawing the panic screen, using a memory mapped framebuffer
81  * Set the whole buffer to 0xa5, and then check that all pixels have been
82  * written.
83  */
84 static void drm_test_panic_screen_user_map(struct kunit *test)
85 {
86 	struct drm_scanout_buffer *sb = test->priv;
87 	const struct drm_test_mode *params = test->param_value;
88 	char *fb;
89 	int fb_size;
90 	int i;
91 
92 	sb->format = drm_format_info(params->format);
93 	fb_size = params->width * params->height * sb->format->cpp[0];
94 
95 	fb = vmalloc(fb_size);
96 	KUNIT_ASSERT_NOT_NULL(test, fb);
97 
98 	memset(fb, 0xa5, fb_size);
99 
100 	iosys_map_set_vaddr(&sb->map[0], fb);
101 	sb->width = params->width;
102 	sb->height = params->height;
103 	sb->pitch[0] = params->width * sb->format->cpp[0];
104 
105 	params->draw_screen(sb);
106 
107 	for (i = 0; i < fb_size; i++)
108 		drm_panic_check_color_byte(test, fb[i]);
109 
110 	vfree(fb);
111 }
112 
113 /*
114  * Test drawing the panic screen, using a list of pages framebuffer
115  * Set the whole buffer to 0xa5, and then check that all pixels have been
116  * written.
117  */
118 static void drm_test_panic_screen_user_page(struct kunit *test)
119 {
120 	struct drm_scanout_buffer *sb = test->priv;
121 	const struct drm_test_mode *params = test->param_value;
122 	int fb_size, p, i, npages;
123 	struct page **pages;
124 	u8 *vaddr;
125 
126 	sb->format = drm_format_info(params->format);
127 	fb_size = params->width * params->height * sb->format->cpp[0];
128 	npages = DIV_ROUND_UP(fb_size, PAGE_SIZE);
129 
130 	pages = kmalloc_array(npages, sizeof(struct page *), GFP_KERNEL);
131 	KUNIT_ASSERT_NOT_NULL(test, pages);
132 
133 	for (p = 0; p < npages; p++) {
134 		pages[p] = alloc_page(GFP_KERNEL);
135 		if (!pages[p]) {
136 			npages = p - 1;
137 			KUNIT_FAIL(test, "Can't allocate page\n");
138 			goto free_pages;
139 		}
140 		vaddr = kmap_local_page(pages[p]);
141 		memset(vaddr, 0xa5, PAGE_SIZE);
142 		kunmap_local(vaddr);
143 	}
144 	sb->pages = pages;
145 	sb->width = params->width;
146 	sb->height = params->height;
147 	sb->pitch[0] = params->width * sb->format->cpp[0];
148 
149 	params->draw_screen(sb);
150 
151 	for (p = 0; p < npages; p++) {
152 		int bytes_in_page = (p == npages - 1) ? fb_size - p * PAGE_SIZE : PAGE_SIZE;
153 
154 		vaddr = kmap_local_page(pages[p]);
155 		for (i = 0; i < bytes_in_page; i++)
156 			drm_panic_check_color_byte(test, vaddr[i]);
157 
158 		kunmap_local(vaddr);
159 	}
160 
161 free_pages:
162 	for (p = 0; p < npages; p++)
163 		__free_page(pages[p]);
164 	kfree(pages);
165 }
166 
167 static void drm_test_panic_set_pixel(struct drm_scanout_buffer *sb,
168 				     unsigned int x,
169 				     unsigned int y,
170 				     u32 color)
171 {
172 	struct kunit *test = (struct kunit *)sb->private;
173 
174 	KUNIT_ASSERT_TRUE(test, x < sb->width && y < sb->height);
175 }
176 
177 /*
178  * Test drawing the panic screen, using the set_pixel callback
179  * Check that all calls to set_pixel() are within the framebuffer
180  */
181 static void drm_test_panic_screen_user_set_pixel(struct kunit *test)
182 {
183 	struct drm_scanout_buffer *sb = test->priv;
184 	const struct drm_test_mode *params = test->param_value;
185 
186 	sb->format = drm_format_info(params->format);
187 	sb->set_pixel = drm_test_panic_set_pixel;
188 	sb->width = params->width;
189 	sb->height = params->height;
190 	sb->private = test;
191 
192 	params->draw_screen(sb);
193 }
194 
195 static void drm_test_panic_desc(const struct drm_test_mode *t, char *desc)
196 {
197 	sprintf(desc, "Panic screen %s, mode: %d x %d \t%p4cc",
198 		t->fname, t->width, t->height, &t->format);
199 }
200 
201 KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_map, drm_test_modes_cases, drm_test_panic_desc);
202 KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_page, drm_test_modes_cases, drm_test_panic_desc);
203 KUNIT_ARRAY_PARAM(drm_test_panic_screen_user_set_pixel, drm_test_modes_cases, drm_test_panic_desc);
204 
205 static struct kunit_case drm_panic_screen_user_test[] = {
206 	KUNIT_CASE_PARAM(drm_test_panic_screen_user_map,
207 			 drm_test_panic_screen_user_map_gen_params),
208 	KUNIT_CASE_PARAM(drm_test_panic_screen_user_page,
209 			 drm_test_panic_screen_user_page_gen_params),
210 	KUNIT_CASE_PARAM(drm_test_panic_screen_user_set_pixel,
211 			 drm_test_panic_screen_user_set_pixel_gen_params),
212 	{ }
213 };
214 
215 static struct kunit_suite drm_panic_suite = {
216 	.name = "drm_panic",
217 	.init = drm_test_panic_init,
218 	.test_cases = drm_panic_screen_user_test,
219 };
220 
221 kunit_test_suite(drm_panic_suite);
222