1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * KUnit test for the ACPI-WMI string conversion code.
4 *
5 * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
6 */
7
8 #include <linux/module.h>
9 #include <linux/slab.h>
10 #include <linux/string.h>
11 #include <linux/wmi.h>
12
13 #include <kunit/resource.h>
14 #include <kunit/test.h>
15
16 #include <asm/byteorder.h>
17
18 struct wmi_string_param {
19 const char *name;
20 const struct wmi_string *wmi_string;
21 /*
22 * Remember that using sizeof() on a struct wmi_string will
23 * always return a size of two bytes due to the flexible
24 * array member!
25 */
26 size_t wmi_string_length;
27 const u8 *utf8_string;
28 size_t utf8_string_length;
29 };
30
31 #define TEST_WMI_STRING_LENGTH 12
32
33 static const struct wmi_string test_wmi_string = {
34 .length = cpu_to_le16(10),
35 .chars = {
36 cpu_to_le16(u'T'),
37 cpu_to_le16(u'E'),
38 cpu_to_le16(u'S'),
39 cpu_to_le16(u'T'),
40 cpu_to_le16(u'\0'),
41 },
42 };
43
44 static const u8 test_utf8_string[] = "TEST";
45
46 #define SPECIAL_WMI_STRING_LENGTH 14
47
48 static const struct wmi_string special_wmi_string = {
49 .length = cpu_to_le16(12),
50 .chars = {
51 cpu_to_le16(u'Ä'),
52 cpu_to_le16(u'Ö'),
53 cpu_to_le16(u'Ü'),
54 cpu_to_le16(u'ß'),
55 cpu_to_le16(u'€'),
56 cpu_to_le16(u'\0'),
57 },
58 };
59
60 static const u8 special_utf8_string[] = "ÄÖÜ߀";
61
62 #define MULTI_POINT_WMI_STRING_LENGTH 12
63
64 static const struct wmi_string multi_point_wmi_string = {
65 .length = cpu_to_le16(10),
66 .chars = {
67 cpu_to_le16(u'K'),
68 /* */
69 cpu_to_le16(0xD83D),
70 cpu_to_le16(0xDC27),
71 cpu_to_le16(u'!'),
72 cpu_to_le16(u'\0'),
73 },
74 };
75
76 static const u8 multi_point_utf8_string[] = "K!";
77
78 #define PADDED_TEST_WMI_STRING_LENGTH 14
79
80 static const struct wmi_string padded_test_wmi_string = {
81 .length = cpu_to_le16(12),
82 .chars = {
83 cpu_to_le16(u'T'),
84 cpu_to_le16(u'E'),
85 cpu_to_le16(u'S'),
86 cpu_to_le16(u'T'),
87 cpu_to_le16(u'\0'),
88 cpu_to_le16(u'\0'),
89 },
90 };
91
92 static const u8 padded_test_utf8_string[] = "TEST\0";
93
94 #define OVERSIZED_TEST_WMI_STRING_LENGTH 14
95
96 static const struct wmi_string oversized_test_wmi_string = {
97 .length = cpu_to_le16(8),
98 .chars = {
99 cpu_to_le16(u'T'),
100 cpu_to_le16(u'E'),
101 cpu_to_le16(u'S'),
102 cpu_to_le16(u'T'),
103 cpu_to_le16(u'!'),
104 cpu_to_le16(u'\0'),
105 },
106 };
107
108 static const u8 oversized_test_utf8_string[] = "TEST!";
109
110 #define INVALID_TEST_WMI_STRING_LENGTH 14
111
112 static const struct wmi_string invalid_test_wmi_string = {
113 .length = cpu_to_le16(12),
114 .chars = {
115 cpu_to_le16(u'T'),
116 /* , with low surrogate missing */
117 cpu_to_le16(0xD83D),
118 cpu_to_le16(u'E'),
119 cpu_to_le16(u'S'),
120 cpu_to_le16(u'T'),
121 cpu_to_le16(u'\0'),
122 },
123 };
124
125 /* We have to split the string here to end the hex escape sequence */
126 static const u8 invalid_test_utf8_string[] = "T" "\xF0\x9F" "EST";
127
128 static const struct wmi_string_param wmi_string_params_array[] = {
129 {
130 .name = "ascii_string",
131 .wmi_string = &test_wmi_string,
132 .wmi_string_length = TEST_WMI_STRING_LENGTH,
133 .utf8_string = test_utf8_string,
134 .utf8_string_length = sizeof(test_utf8_string),
135 },
136 {
137 .name = "special_string",
138 .wmi_string = &special_wmi_string,
139 .wmi_string_length = SPECIAL_WMI_STRING_LENGTH,
140 .utf8_string = special_utf8_string,
141 .utf8_string_length = sizeof(special_utf8_string),
142 },
143 {
144 .name = "multi_point_string",
145 .wmi_string = &multi_point_wmi_string,
146 .wmi_string_length = MULTI_POINT_WMI_STRING_LENGTH,
147 .utf8_string = multi_point_utf8_string,
148 .utf8_string_length = sizeof(multi_point_utf8_string),
149 },
150 };
151
wmi_string_param_get_desc(const struct wmi_string_param * param,char * desc)152 static void wmi_string_param_get_desc(const struct wmi_string_param *param, char *desc)
153 {
154 strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
155 }
156
157 KUNIT_ARRAY_PARAM(wmi_string, wmi_string_params_array, wmi_string_param_get_desc);
158
wmi_string_to_utf8s_test(struct kunit * test)159 static void wmi_string_to_utf8s_test(struct kunit *test)
160 {
161 const struct wmi_string_param *param = test->param_value;
162 ssize_t ret;
163 u8 *result;
164
165 result = kunit_kzalloc(test, param->utf8_string_length, GFP_KERNEL);
166 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
167
168 ret = wmi_string_to_utf8s(param->wmi_string, result, param->utf8_string_length);
169
170 KUNIT_EXPECT_EQ(test, ret, param->utf8_string_length - 1);
171 KUNIT_EXPECT_MEMEQ(test, result, param->utf8_string, param->utf8_string_length);
172 }
173
wmi_string_from_utf8s_test(struct kunit * test)174 static void wmi_string_from_utf8s_test(struct kunit *test)
175 {
176 const struct wmi_string_param *param = test->param_value;
177 struct wmi_string *result;
178 size_t max_chars;
179 ssize_t ret;
180
181 max_chars = (param->wmi_string_length - sizeof(*result)) / 2;
182 result = kunit_kzalloc(test, param->wmi_string_length, GFP_KERNEL);
183 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
184
185 ret = wmi_string_from_utf8s(result, max_chars, param->utf8_string,
186 param->utf8_string_length);
187
188 KUNIT_EXPECT_EQ(test, ret, max_chars - 1);
189 KUNIT_EXPECT_MEMEQ(test, result, param->wmi_string, param->wmi_string_length);
190 }
191
wmi_string_to_utf8s_padded_test(struct kunit * test)192 static void wmi_string_to_utf8s_padded_test(struct kunit *test)
193 {
194 u8 result[sizeof(padded_test_utf8_string)];
195 ssize_t ret;
196
197 ret = wmi_string_to_utf8s(&padded_test_wmi_string, result, sizeof(result));
198
199 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
200 KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
201 }
202
wmi_string_from_utf8s_padded_test(struct kunit * test)203 static void wmi_string_from_utf8s_padded_test(struct kunit *test)
204 {
205 struct wmi_string *result;
206 size_t max_chars;
207 ssize_t ret;
208
209 max_chars = (PADDED_TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
210 result = kunit_kzalloc(test, PADDED_TEST_WMI_STRING_LENGTH, GFP_KERNEL);
211 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
212
213 ret = wmi_string_from_utf8s(result, max_chars, padded_test_utf8_string,
214 sizeof(padded_test_utf8_string));
215
216 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
217 KUNIT_EXPECT_MEMEQ(test, result, &test_wmi_string, sizeof(test_wmi_string));
218 }
219
wmi_string_to_utf8s_oversized_test(struct kunit * test)220 static void wmi_string_to_utf8s_oversized_test(struct kunit *test)
221 {
222 u8 result[sizeof(oversized_test_utf8_string)];
223 ssize_t ret;
224
225 ret = wmi_string_to_utf8s(&oversized_test_wmi_string, result, sizeof(result));
226
227 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
228 KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
229 }
230
wmi_string_from_utf8s_oversized_test(struct kunit * test)231 static void wmi_string_from_utf8s_oversized_test(struct kunit *test)
232 {
233 struct wmi_string *result;
234 size_t max_chars;
235 ssize_t ret;
236
237 max_chars = (TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
238 result = kunit_kzalloc(test, TEST_WMI_STRING_LENGTH, GFP_KERNEL);
239 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
240
241 ret = wmi_string_from_utf8s(result, max_chars, oversized_test_utf8_string,
242 sizeof(oversized_test_utf8_string));
243
244 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
245 KUNIT_EXPECT_MEMEQ(test, result, &test_wmi_string, sizeof(test_wmi_string));
246 }
247
wmi_string_to_utf8s_invalid_test(struct kunit * test)248 static void wmi_string_to_utf8s_invalid_test(struct kunit *test)
249 {
250 u8 result[sizeof(invalid_test_utf8_string)];
251 ssize_t ret;
252
253 ret = wmi_string_to_utf8s(&invalid_test_wmi_string, result, sizeof(result));
254
255 KUNIT_EXPECT_EQ(test, ret, sizeof(test_utf8_string) - 1);
256 KUNIT_EXPECT_MEMEQ(test, result, test_utf8_string, sizeof(test_utf8_string));
257 }
258
wmi_string_from_utf8s_invalid_test(struct kunit * test)259 static void wmi_string_from_utf8s_invalid_test(struct kunit *test)
260 {
261 struct wmi_string *result;
262 size_t max_chars;
263 ssize_t ret;
264
265 max_chars = (INVALID_TEST_WMI_STRING_LENGTH - sizeof(*result)) / 2;
266 result = kunit_kzalloc(test, INVALID_TEST_WMI_STRING_LENGTH, GFP_KERNEL);
267 KUNIT_ASSERT_NOT_ERR_OR_NULL(test, result);
268
269 ret = wmi_string_from_utf8s(result, max_chars, invalid_test_utf8_string,
270 sizeof(invalid_test_utf8_string));
271
272 KUNIT_EXPECT_EQ(test, ret, -EINVAL);
273 }
274
275 static struct kunit_case wmi_string_test_cases[] = {
276 KUNIT_CASE_PARAM(wmi_string_to_utf8s_test, wmi_string_gen_params),
277 KUNIT_CASE_PARAM(wmi_string_from_utf8s_test, wmi_string_gen_params),
278 KUNIT_CASE(wmi_string_to_utf8s_padded_test),
279 KUNIT_CASE(wmi_string_from_utf8s_padded_test),
280 KUNIT_CASE(wmi_string_to_utf8s_oversized_test),
281 KUNIT_CASE(wmi_string_from_utf8s_oversized_test),
282 KUNIT_CASE(wmi_string_to_utf8s_invalid_test),
283 KUNIT_CASE(wmi_string_from_utf8s_invalid_test),
284 {}
285 };
286
287 static struct kunit_suite wmi_string_test_suite = {
288 .name = "wmi_string",
289 .test_cases = wmi_string_test_cases,
290 };
291
292 kunit_test_suite(wmi_string_test_suite);
293
294 MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
295 MODULE_DESCRIPTION("KUnit test for the ACPI-WMI string conversion code");
296 MODULE_LICENSE("GPL");
297