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 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 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 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 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 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 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 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 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 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