xref: /linux/drivers/platform/wmi/tests/marshalling_kunit.c (revision 69050f8d6d075dc01af7a5f2f550a8067510366f)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * KUnit test for the ACPI-WMI marshalling code.
4  *
5  * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6  */
7 
8 #include <linux/acpi.h>
9 #include <linux/align.h>
10 #include <linux/module.h>
11 #include <linux/slab.h>
12 #include <linux/string.h>
13 #include <linux/types.h>
14 #include <linux/wmi.h>
15 
16 #include <kunit/resource.h>
17 #include <kunit/test.h>
18 
19 #include "../internal.h"
20 
21 struct wmi_acpi_param {
22 	const char *name;
23 	const union acpi_object obj;
24 	const struct wmi_buffer buffer;
25 };
26 
27 struct wmi_string_param {
28 	const char *name;
29 	const char *string;
30 	const struct wmi_buffer buffer;
31 };
32 
33 struct wmi_invalid_acpi_param {
34 	const char *name;
35 	const union acpi_object obj;
36 };
37 
38 struct wmi_invalid_string_param {
39 	const char *name;
40 	const struct wmi_buffer buffer;
41 };
42 
43 /* 0xdeadbeef */
44 static u8 expected_single_integer[] = {
45 	0xef, 0xbe, 0xad, 0xde,
46 };
47 
48 /* "TEST" */
49 static u8 expected_single_string[] = {
50 	0x0a, 0x00, 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00,
51 };
52 
53 static u8 test_buffer[] = {
54 	0xab, 0xcd,
55 };
56 
57 static u8 expected_single_buffer[] = {
58 	0xab, 0xcd,
59 };
60 
61 static union acpi_object simple_package_elements[] = {
62 	{
63 		.buffer = {
64 			.type = ACPI_TYPE_BUFFER,
65 			.length = sizeof(test_buffer),
66 			.pointer = test_buffer,
67 		},
68 	},
69 	{
70 		.integer = {
71 			.type = ACPI_TYPE_INTEGER,
72 			.value = 0x01020304,
73 		},
74 	},
75 };
76 
77 static u8 expected_simple_package[] = {
78 	0xab, 0xcd,
79 	0x00, 0x00,
80 	0x04, 0x03, 0x02, 0x01,
81 };
82 
83 static u8 test_small_buffer[] = {
84 	0xde,
85 };
86 
87 static union acpi_object complex_package_elements[] = {
88 	{
89 		.integer = {
90 			.type = ACPI_TYPE_INTEGER,
91 			.value = 0xdeadbeef,
92 		},
93 	},
94 	{
95 		.buffer = {
96 			.type = ACPI_TYPE_BUFFER,
97 			.length = sizeof(test_small_buffer),
98 			.pointer = test_small_buffer,
99 		},
100 	},
101 	{
102 		.string = {
103 			.type = ACPI_TYPE_STRING,
104 			.length = sizeof("TEST") - 1,
105 			.pointer = "TEST",
106 		},
107 	},
108 	{
109 		.buffer = {
110 			.type = ACPI_TYPE_BUFFER,
111 			.length = sizeof(test_small_buffer),
112 			.pointer = test_small_buffer,
113 		},
114 	},
115 	{
116 		.integer = {
117 			.type = ACPI_TYPE_INTEGER,
118 			.value = 0x01020304,
119 		},
120 	}
121 };
122 
123 static u8 expected_complex_package[] = {
124 	0xef, 0xbe, 0xad, 0xde,
125 	0xde,
126 	0x00,
127 	0x0a, 0x00, 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00,
128 	0xde,
129 	0x00,
130 	0x04, 0x03, 0x02, 0x01,
131 };
132 
133 static const struct wmi_acpi_param wmi_acpi_params_array[] = {
134 	{
135 		.name = "single_integer",
136 		.obj = {
137 			.integer = {
138 				.type = ACPI_TYPE_INTEGER,
139 				.value = 0xdeadbeef,
140 			},
141 		},
142 		.buffer = {
143 			.data = expected_single_integer,
144 			.length = sizeof(expected_single_integer),
145 		},
146 	},
147 	{
148 		.name = "single_string",
149 		.obj = {
150 			.string = {
151 				.type = ACPI_TYPE_STRING,
152 				.length = sizeof("TEST") - 1,
153 				.pointer = "TEST",
154 			},
155 		},
156 		.buffer = {
157 			.data = expected_single_string,
158 			.length = sizeof(expected_single_string),
159 		},
160 	},
161 	{
162 		.name = "single_buffer",
163 		.obj = {
164 			.buffer = {
165 				.type = ACPI_TYPE_BUFFER,
166 				.length = sizeof(test_buffer),
167 				.pointer = test_buffer,
168 			},
169 		},
170 		.buffer = {
171 			.data = expected_single_buffer,
172 			.length = sizeof(expected_single_buffer),
173 		},
174 	},
175 	{
176 		.name = "simple_package",
177 		.obj = {
178 			.package = {
179 				.type = ACPI_TYPE_PACKAGE,
180 				.count = ARRAY_SIZE(simple_package_elements),
181 				.elements = simple_package_elements,
182 			},
183 		},
184 		.buffer = {
185 			.data = expected_simple_package,
186 			.length = sizeof(expected_simple_package),
187 		},
188 	},
189 	{
190 		.name = "complex_package",
191 		.obj = {
192 			.package = {
193 				.type = ACPI_TYPE_PACKAGE,
194 				.count = ARRAY_SIZE(complex_package_elements),
195 				.elements = complex_package_elements,
196 			},
197 		},
198 		.buffer = {
199 			.data = expected_complex_package,
200 			.length = sizeof(expected_complex_package),
201 		},
202 	},
203 };
204 
205 static void wmi_acpi_param_get_desc(const struct wmi_acpi_param *param, char *desc)
206 {
207 	strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
208 }
209 
210 KUNIT_ARRAY_PARAM(wmi_unmarshal_acpi_object, wmi_acpi_params_array, wmi_acpi_param_get_desc);
211 
212 /* "WMI\0" */
213 static u8 padded_wmi_string[] = {
214 	0x0a, 0x00,
215 	0x57, 0x00,
216 	0x4D, 0x00,
217 	0x49, 0x00,
218 	0x00, 0x00,
219 	0x00, 0x00,
220 };
221 
222 static const struct wmi_string_param wmi_string_params_array[] = {
223 	{
224 		.name = "test",
225 		.string = "TEST",
226 		.buffer = {
227 			.length = sizeof(expected_single_string),
228 			.data = expected_single_string,
229 		},
230 	},
231 	{
232 		.name = "padded",
233 		.string = "WMI",
234 		.buffer = {
235 			.length = sizeof(padded_wmi_string),
236 			.data = padded_wmi_string,
237 		},
238 	},
239 };
240 
241 static void wmi_string_param_get_desc(const struct wmi_string_param *param, char *desc)
242 {
243 	strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
244 }
245 
246 KUNIT_ARRAY_PARAM(wmi_marshal_string, wmi_string_params_array, wmi_string_param_get_desc);
247 
248 static union acpi_object nested_package_elements[] = {
249 	{
250 		.package = {
251 			.type = ACPI_TYPE_PACKAGE,
252 			.count = ARRAY_SIZE(simple_package_elements),
253 			.elements = simple_package_elements,
254 		},
255 	}
256 };
257 
258 static const struct wmi_invalid_acpi_param wmi_invalid_acpi_params_array[] = {
259 	{
260 		.name = "nested_package",
261 		.obj = {
262 			.package = {
263 				.type = ACPI_TYPE_PACKAGE,
264 				.count = ARRAY_SIZE(nested_package_elements),
265 				.elements = nested_package_elements,
266 			},
267 		},
268 	},
269 	{
270 		.name = "reference",
271 		.obj = {
272 			.reference = {
273 				.type = ACPI_TYPE_LOCAL_REFERENCE,
274 				.actual_type = ACPI_TYPE_ANY,
275 				.handle = NULL,
276 			},
277 		},
278 	},
279 	{
280 		.name = "processor",
281 		.obj = {
282 			.processor = {
283 				.type = ACPI_TYPE_PROCESSOR,
284 				.proc_id = 0,
285 				.pblk_address = 0,
286 				.pblk_length = 0,
287 			},
288 		},
289 	},
290 	{
291 		.name = "power_resource",
292 		.obj = {
293 			.power_resource = {
294 				.type = ACPI_TYPE_POWER,
295 				.system_level = 0,
296 				.resource_order = 0,
297 			},
298 		},
299 	},
300 };
301 
302 static void wmi_invalid_acpi_param_get_desc(const struct wmi_invalid_acpi_param *param, char *desc)
303 {
304 	strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
305 }
306 
307 KUNIT_ARRAY_PARAM(wmi_unmarshal_acpi_object_failure, wmi_invalid_acpi_params_array,
308 		  wmi_invalid_acpi_param_get_desc);
309 
310 static u8 oversized_wmi_string[] = {
311 	0x04, 0x00, 0x00, 0x00,
312 };
313 
314 /*
315  * The error is that 3 bytes can not hold UTF-16 characters
316  * without cutting of the last one.
317  */
318 static u8 undersized_wmi_string[] = {
319 	0x03, 0x00, 0x00, 0x00, 0x00,
320 };
321 
322 static u8 non_ascii_wmi_string[] = {
323 	0x04, 0x00, 0xC4, 0x00, 0x00, 0x00,
324 };
325 
326 static const struct wmi_invalid_string_param wmi_invalid_string_params_array[] = {
327 	{
328 		.name = "empty_buffer",
329 		.buffer = {
330 			.length = 0,
331 			.data = ZERO_SIZE_PTR,
332 		},
333 
334 	},
335 	{
336 		.name = "oversized",
337 		.buffer = {
338 			.length = sizeof(oversized_wmi_string),
339 			.data = oversized_wmi_string,
340 		},
341 	},
342 	{
343 		.name = "undersized",
344 		.buffer = {
345 			.length = sizeof(undersized_wmi_string),
346 			.data = undersized_wmi_string,
347 		},
348 	},
349 	{
350 		.name = "non_ascii",
351 		.buffer = {
352 			.length = sizeof(non_ascii_wmi_string),
353 			.data = non_ascii_wmi_string,
354 		},
355 	},
356 };
357 
358 static void wmi_invalid_string_param_get_desc(const struct wmi_invalid_string_param *param,
359 					      char *desc)
360 {
361 	strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
362 }
363 
364 KUNIT_ARRAY_PARAM(wmi_marshal_string_failure, wmi_invalid_string_params_array,
365 		  wmi_invalid_string_param_get_desc);
366 
367 KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
368 
369 static void wmi_unmarshal_acpi_object_test(struct kunit *test)
370 {
371 	const struct wmi_acpi_param *param = test->param_value;
372 	struct wmi_buffer result;
373 	int ret;
374 
375 	ret = wmi_unmarshal_acpi_object(&param->obj, &result);
376 	if (ret < 0)
377 		KUNIT_FAIL_AND_ABORT(test, "Unmarshalling of ACPI object failed\n");
378 
379 	kunit_add_action(test, kfree_wrapper, result.data);
380 
381 	KUNIT_EXPECT_TRUE(test, IS_ALIGNED((uintptr_t)result.data, 8));
382 	KUNIT_EXPECT_EQ(test, result.length, param->buffer.length);
383 	KUNIT_EXPECT_MEMEQ(test, result.data, param->buffer.data, result.length);
384 }
385 
386 static void wmi_unmarshal_acpi_object_failure_test(struct kunit *test)
387 {
388 	const struct wmi_invalid_acpi_param *param = test->param_value;
389 	struct wmi_buffer result;
390 	int ret;
391 
392 	ret = wmi_unmarshal_acpi_object(&param->obj, &result);
393 	if (ret < 0)
394 		return;
395 
396 	kfree(result.data);
397 	KUNIT_FAIL(test, "Invalid ACPI object was not rejected\n");
398 }
399 
400 static void wmi_marshal_string_test(struct kunit *test)
401 {
402 	const struct wmi_string_param *param = test->param_value;
403 	struct acpi_buffer result;
404 	int ret;
405 
406 	ret = wmi_marshal_string(&param->buffer, &result);
407 	if (ret < 0)
408 		KUNIT_FAIL_AND_ABORT(test, "Marshalling of WMI string failed\n");
409 
410 	kunit_add_action(test, kfree_wrapper, result.pointer);
411 
412 	KUNIT_EXPECT_EQ(test, result.length, strlen(param->string));
413 	KUNIT_EXPECT_STREQ(test, result.pointer, param->string);
414 }
415 
416 static void wmi_marshal_string_failure_test(struct kunit *test)
417 {
418 	const struct wmi_invalid_string_param *param = test->param_value;
419 	struct acpi_buffer result;
420 	int ret;
421 
422 	ret = wmi_marshal_string(&param->buffer, &result);
423 	if (ret < 0)
424 		return;
425 
426 	kfree(result.pointer);
427 	KUNIT_FAIL(test, "Invalid string was not rejected\n");
428 }
429 
430 static struct kunit_case wmi_marshalling_test_cases[] = {
431 	KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_test,
432 			 wmi_unmarshal_acpi_object_gen_params),
433 	KUNIT_CASE_PARAM(wmi_marshal_string_test,
434 			 wmi_marshal_string_gen_params),
435 	KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_failure_test,
436 			 wmi_unmarshal_acpi_object_failure_gen_params),
437 	KUNIT_CASE_PARAM(wmi_marshal_string_failure_test,
438 			 wmi_marshal_string_failure_gen_params),
439 	{}
440 };
441 
442 static struct kunit_suite wmi_marshalling_test_suite = {
443 	.name = "wmi_marshalling",
444 	.test_cases = wmi_marshalling_test_cases,
445 };
446 
447 kunit_test_suite(wmi_marshalling_test_suite);
448 
449 MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
450 MODULE_DESCRIPTION("KUnit test for the ACPI-WMI marshalling code");
451 MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
452 MODULE_LICENSE("GPL");
453