platform/wmi: Add kunit test for the marshalling code

The marshalling code used by the WMI driver core is implemented as
a separate component, suitable for unit tests.

Implmented such a unit test using KUnit. Those unit tests verify that
ACPI objects are correctly converted into WMI buffers and that WMI
strings are correctly converted into ACPI strings. They also verify
that invalid ACPI data (like nested packages) is rejected.

Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Link: https://patch.msgid.link/20260116204116.4030-3-W_Armin@gmx.de
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
Armin Wolf 2026-01-16 21:41:09 +01:00 committed by Ilpo Järvinen
parent 015b70a6ae
commit 29dfba69c3
No known key found for this signature in database
GPG key ID: 59AC4F6153E5CE31
5 changed files with 481 additions and 0 deletions

View file

@ -31,4 +31,6 @@ config ACPI_WMI_LEGACY_DEVICE_NAMES
userspace applications but will cause the registration of WMI devices with
the same GUID to fail in some corner cases.
source "drivers/platform/wmi/tests/Kconfig"
endif # ACPI_WMI

View file

@ -6,3 +6,6 @@
wmi-y := core.o marshalling.o
obj-$(CONFIG_ACPI_WMI) += wmi.o
# Unit tests
obj-y += tests/

View file

@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# ACPI WMI KUnit tests
#
config ACPI_WMI_MARSHALLING_KUNIT_TEST
tristate "KUnit Test for ACPI-WMI marshalling" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
help
This builds unit tests for the ACPI-WMI marshalling code.
For more information on KUnit and unit tests in general, please refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
If unsure, say N.

View file

@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# Makefile for linux/drivers/platform/x86/wmi/tests
# ACPI WMI KUnit tests
#
wmi_marshalling_kunit-y := marshalling_kunit.o
obj-$(CONFIG_ACPI_WMI_MARSHALLING_KUNIT_TEST) += wmi_marshalling_kunit.o

View file

@ -0,0 +1,452 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* KUnit test for the ACPI-WMI marshalling code.
*
* Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
*/
#include <linux/acpi.h>
#include <linux/align.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/wmi.h>
#include <kunit/resource.h>
#include <kunit/test.h>
#include "../internal.h"
struct wmi_acpi_param {
const char *name;
const union acpi_object obj;
const struct wmi_buffer buffer;
};
struct wmi_string_param {
const char *name;
const char *string;
const struct wmi_buffer buffer;
};
struct wmi_invalid_acpi_param {
const char *name;
const union acpi_object obj;
};
struct wmi_invalid_string_param {
const char *name;
const struct wmi_buffer buffer;
};
/* 0xdeadbeef */
static u8 expected_single_integer[] = {
0xef, 0xbe, 0xad, 0xde,
};
/* "TEST" */
static u8 expected_single_string[] = {
0x0a, 0x00, 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00,
};
static u8 test_buffer[] = {
0xab, 0xcd,
};
static u8 expected_single_buffer[] = {
0xab, 0xcd,
};
static union acpi_object simple_package_elements[] = {
{
.buffer = {
.type = ACPI_TYPE_BUFFER,
.length = sizeof(test_buffer),
.pointer = test_buffer,
},
},
{
.integer = {
.type = ACPI_TYPE_INTEGER,
.value = 0x01020304,
},
},
};
static u8 expected_simple_package[] = {
0xab, 0xcd,
0x00, 0x00,
0x04, 0x03, 0x02, 0x01,
};
static u8 test_small_buffer[] = {
0xde,
};
static union acpi_object complex_package_elements[] = {
{
.integer = {
.type = ACPI_TYPE_INTEGER,
.value = 0xdeadbeef,
},
},
{
.buffer = {
.type = ACPI_TYPE_BUFFER,
.length = sizeof(test_small_buffer),
.pointer = test_small_buffer,
},
},
{
.string = {
.type = ACPI_TYPE_STRING,
.length = sizeof("TEST") - 1,
.pointer = "TEST",
},
},
{
.buffer = {
.type = ACPI_TYPE_BUFFER,
.length = sizeof(test_small_buffer),
.pointer = test_small_buffer,
},
},
{
.integer = {
.type = ACPI_TYPE_INTEGER,
.value = 0x01020304,
},
}
};
static u8 expected_complex_package[] = {
0xef, 0xbe, 0xad, 0xde,
0xde,
0x00,
0x0a, 0x00, 0x54, 0x00, 0x45, 0x00, 0x53, 0x00, 0x54, 0x00, 0x00, 0x00,
0xde,
0x00,
0x04, 0x03, 0x02, 0x01,
};
static const struct wmi_acpi_param wmi_acpi_params_array[] = {
{
.name = "single_integer",
.obj = {
.integer = {
.type = ACPI_TYPE_INTEGER,
.value = 0xdeadbeef,
},
},
.buffer = {
.data = expected_single_integer,
.length = sizeof(expected_single_integer),
},
},
{
.name = "single_string",
.obj = {
.string = {
.type = ACPI_TYPE_STRING,
.length = sizeof("TEST") - 1,
.pointer = "TEST",
},
},
.buffer = {
.data = expected_single_string,
.length = sizeof(expected_single_string),
},
},
{
.name = "single_buffer",
.obj = {
.buffer = {
.type = ACPI_TYPE_BUFFER,
.length = sizeof(test_buffer),
.pointer = test_buffer,
},
},
.buffer = {
.data = expected_single_buffer,
.length = sizeof(expected_single_buffer),
},
},
{
.name = "simple_package",
.obj = {
.package = {
.type = ACPI_TYPE_PACKAGE,
.count = ARRAY_SIZE(simple_package_elements),
.elements = simple_package_elements,
},
},
.buffer = {
.data = expected_simple_package,
.length = sizeof(expected_simple_package),
},
},
{
.name = "complex_package",
.obj = {
.package = {
.type = ACPI_TYPE_PACKAGE,
.count = ARRAY_SIZE(complex_package_elements),
.elements = complex_package_elements,
},
},
.buffer = {
.data = expected_complex_package,
.length = sizeof(expected_complex_package),
},
},
};
static void wmi_acpi_param_get_desc(const struct wmi_acpi_param *param, char *desc)
{
strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(wmi_unmarshal_acpi_object, wmi_acpi_params_array, wmi_acpi_param_get_desc);
/* "WMI\0" */
static u8 padded_wmi_string[] = {
0x0a, 0x00,
0x57, 0x00,
0x4D, 0x00,
0x49, 0x00,
0x00, 0x00,
0x00, 0x00,
};
static const struct wmi_string_param wmi_string_params_array[] = {
{
.name = "test",
.string = "TEST",
.buffer = {
.length = sizeof(expected_single_string),
.data = expected_single_string,
},
},
{
.name = "padded",
.string = "WMI",
.buffer = {
.length = sizeof(padded_wmi_string),
.data = padded_wmi_string,
},
},
};
static void wmi_string_param_get_desc(const struct wmi_string_param *param, char *desc)
{
strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(wmi_marshal_string, wmi_string_params_array, wmi_string_param_get_desc);
static union acpi_object nested_package_elements[] = {
{
.package = {
.type = ACPI_TYPE_PACKAGE,
.count = ARRAY_SIZE(simple_package_elements),
.elements = simple_package_elements,
},
}
};
static const struct wmi_invalid_acpi_param wmi_invalid_acpi_params_array[] = {
{
.name = "nested_package",
.obj = {
.package = {
.type = ACPI_TYPE_PACKAGE,
.count = ARRAY_SIZE(nested_package_elements),
.elements = nested_package_elements,
},
},
},
{
.name = "reference",
.obj = {
.reference = {
.type = ACPI_TYPE_LOCAL_REFERENCE,
.actual_type = ACPI_TYPE_ANY,
.handle = NULL,
},
},
},
{
.name = "processor",
.obj = {
.processor = {
.type = ACPI_TYPE_PROCESSOR,
.proc_id = 0,
.pblk_address = 0,
.pblk_length = 0,
},
},
},
{
.name = "power_resource",
.obj = {
.power_resource = {
.type = ACPI_TYPE_POWER,
.system_level = 0,
.resource_order = 0,
},
},
},
};
static void wmi_invalid_acpi_param_get_desc(const struct wmi_invalid_acpi_param *param, char *desc)
{
strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(wmi_unmarshal_acpi_object_failure, wmi_invalid_acpi_params_array,
wmi_invalid_acpi_param_get_desc);
static u8 oversized_wmi_string[] = {
0x04, 0x00, 0x00, 0x00,
};
/*
* The error is that 3 bytes can not hold UTF-16 characters
* without cutting of the last one.
*/
static u8 undersized_wmi_string[] = {
0x03, 0x00, 0x00, 0x00, 0x00,
};
static u8 non_ascii_wmi_string[] = {
0x04, 0x00, 0xC4, 0x00, 0x00, 0x00,
};
static const struct wmi_invalid_string_param wmi_invalid_string_params_array[] = {
{
.name = "empty_buffer",
.buffer = {
.length = 0,
.data = ZERO_SIZE_PTR,
},
},
{
.name = "oversized",
.buffer = {
.length = sizeof(oversized_wmi_string),
.data = oversized_wmi_string,
},
},
{
.name = "undersized",
.buffer = {
.length = sizeof(undersized_wmi_string),
.data = undersized_wmi_string,
},
},
{
.name = "non_ascii",
.buffer = {
.length = sizeof(non_ascii_wmi_string),
.data = non_ascii_wmi_string,
},
},
};
static void wmi_invalid_string_param_get_desc(const struct wmi_invalid_string_param *param,
char *desc)
{
strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
}
KUNIT_ARRAY_PARAM(wmi_marshal_string_failure, wmi_invalid_string_params_array,
wmi_invalid_string_param_get_desc);
KUNIT_DEFINE_ACTION_WRAPPER(kfree_wrapper, kfree, const void *);
static void wmi_unmarshal_acpi_object_test(struct kunit *test)
{
const struct wmi_acpi_param *param = test->param_value;
struct wmi_buffer result;
int ret;
ret = wmi_unmarshal_acpi_object(&param->obj, &result);
if (ret < 0)
KUNIT_FAIL_AND_ABORT(test, "Unmarshalling of ACPI object failed\n");
kunit_add_action(test, kfree_wrapper, result.data);
KUNIT_EXPECT_TRUE(test, IS_ALIGNED((uintptr_t)result.data, 8));
KUNIT_EXPECT_EQ(test, result.length, param->buffer.length);
KUNIT_EXPECT_MEMEQ(test, result.data, param->buffer.data, result.length);
}
static void wmi_unmarshal_acpi_object_failure_test(struct kunit *test)
{
const struct wmi_invalid_acpi_param *param = test->param_value;
struct wmi_buffer result;
int ret;
ret = wmi_unmarshal_acpi_object(&param->obj, &result);
if (ret < 0)
return;
kfree(result.data);
KUNIT_FAIL(test, "Invalid ACPI object was not rejected\n");
}
static void wmi_marshal_string_test(struct kunit *test)
{
const struct wmi_string_param *param = test->param_value;
struct acpi_buffer result;
int ret;
ret = wmi_marshal_string(&param->buffer, &result);
if (ret < 0)
KUNIT_FAIL_AND_ABORT(test, "Marshalling of WMI string failed\n");
kunit_add_action(test, kfree_wrapper, result.pointer);
KUNIT_EXPECT_EQ(test, result.length, strlen(param->string));
KUNIT_EXPECT_STREQ(test, result.pointer, param->string);
}
static void wmi_marshal_string_failure_test(struct kunit *test)
{
const struct wmi_invalid_string_param *param = test->param_value;
struct acpi_buffer result;
int ret;
ret = wmi_marshal_string(&param->buffer, &result);
if (ret < 0)
return;
kfree(result.pointer);
KUNIT_FAIL(test, "Invalid string was not rejected\n");
}
static struct kunit_case wmi_marshalling_test_cases[] = {
KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_test,
wmi_unmarshal_acpi_object_gen_params),
KUNIT_CASE_PARAM(wmi_marshal_string_test,
wmi_marshal_string_gen_params),
KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_failure_test,
wmi_unmarshal_acpi_object_failure_gen_params),
KUNIT_CASE_PARAM(wmi_marshal_string_failure_test,
wmi_marshal_string_failure_gen_params),
{}
};
static struct kunit_suite wmi_marshalling_test_suite = {
.name = "wmi_marshalling",
.test_cases = wmi_marshalling_test_cases,
};
kunit_test_suite(wmi_marshalling_test_suite);
MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
MODULE_DESCRIPTION("KUnit test for the ACPI-WMI marshalling code");
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
MODULE_LICENSE("GPL");