473 lines
9.9 KiB
C
473 lines
9.9 KiB
C
// 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(¶m->obj, &result, param->buffer.length);
|
|
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(¶m->obj, &result, 0);
|
|
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(¶m->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(¶m->buffer, &result);
|
|
if (ret < 0)
|
|
return;
|
|
|
|
kfree(result.pointer);
|
|
KUNIT_FAIL(test, "Invalid string was not rejected\n");
|
|
}
|
|
|
|
static void wmi_unmarshal_acpi_object_undersized_test(struct kunit *test)
|
|
{
|
|
const union acpi_object obj = {
|
|
.integer = {
|
|
.type = ACPI_TYPE_INTEGER,
|
|
.value = 0xdeadbeef,
|
|
},
|
|
};
|
|
struct wmi_buffer result;
|
|
int ret;
|
|
|
|
ret = wmi_unmarshal_acpi_object(&obj, &result, sizeof(expected_single_integer) + 1);
|
|
if (ret < 0)
|
|
return;
|
|
|
|
kfree(result.data);
|
|
KUNIT_FAIL(test, "Undersized unmarshalling result 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),
|
|
KUNIT_CASE(wmi_unmarshal_acpi_object_undersized_test),
|
|
{}
|
|
};
|
|
|
|
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");
|