Add a KUnit test suite in lib/tests/liveupdate_feature_kunit.c covering the Live Update feature header declared in include/linux/kho/abi/luo.h. The suite contains two focused test cases: - luo_ser_hdr_set_get_test: verifies DECLARE_LUO_SER_WRAP structure sizing, header initialization, payload pointer resolution, and 2-bit feature flag setting and retrieval across flags entry boundaries, including out-of-range queries. - luo_ser_hdr_truth_table_test: verifies luo_ser_hdr_validate against all rows of the feature state truth table (absent, optional, required, and reserved 0b10 encodings), as well as empty supported tables. Signed-off-by: Tarun Sahu --- MAINTAINERS | 1 + lib/Kconfig.debug | 16 ++++ lib/tests/Makefile | 1 + lib/tests/liveupdate_feature_kunit.c | 118 +++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 lib/tests/liveupdate_feature_kunit.c diff --git a/MAINTAINERS b/MAINTAINERS index 5d1b58271989..dad09961c275 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15191,6 +15191,7 @@ F: include/linux/liveupdate.h F: include/uapi/linux/liveupdate.h F: kernel/liveupdate/ F: lib/tests/liveupdate.c +F: lib/tests/liveupdate_feature_kunit.c F: mm/memfd_luo.c F: tools/testing/selftests/liveupdate/ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 134b15a44625..d8208f3f4515 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -2906,6 +2906,22 @@ config LIVEUPDATE_TEST If unsure, say N +config LIVEUPDATE_FEATURE_KUNIT_TEST + tristate "KUnit test for the Live Update feature header helpers" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + help + This option enables the KUnit test suite for the Live Update + Orchestrator feature header helpers defined in + include/linux/kho/abi/luo.h. It checks the feature flag encoding, + the flags array sizing, the source/target kernel validation + truth table, header initialization and the payload size checks. + + The helpers are self-contained, so the test does not depend on + CONFIG_LIVEUPDATE and can also run under UML. + + If unsure, say N. + config CMDLINE_KUNIT_TEST tristate "KUnit test for cmdline API" if !KUNIT_ALL_TESTS depends on KUNIT diff --git a/lib/tests/Makefile b/lib/tests/Makefile index 3cac3b63a752..2f1f1f765617 100644 --- a/lib/tests/Makefile +++ b/lib/tests/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_KFIFO_KUNIT_TEST) += kfifo_kunit.o obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o obj-$(CONFIG_LIVEUPDATE_TEST) += liveupdate.o +obj-$(CONFIG_LIVEUPDATE_FEATURE_KUNIT_TEST) += liveupdate_feature_kunit.o CFLAGS_longest_symbol_kunit.o += $(call cc-disable-warning, missing-prototypes) obj-$(CONFIG_LONGEST_SYM_KUNIT_TEST) += longest_symbol_kunit.o diff --git a/lib/tests/liveupdate_feature_kunit.c b/lib/tests/liveupdate_feature_kunit.c new file mode 100644 index 000000000000..fc9d2de263d9 --- /dev/null +++ b/lib/tests/liveupdate_feature_kunit.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for the Live Update Orchestrator feature header helpers + * in include/linux/kho/abi/luo.h. + * + * Copyright (c) 2026, Google LLC. + * Tarun Sahu + */ + +#include +#include +#include + +struct luo_test_multi_ser { + u64 val; +}; + +static_assert(sizeof(struct luo_test_multi_ser) == 8); +DECLARE_LUO_SER_WRAP(luo_test_multi_ser, 32, 35); + +struct luo_test_one_ser { + u64 val; +}; + +static_assert(sizeof(struct luo_test_one_ser) == 8); +DECLARE_LUO_SER_WRAP(luo_test_one_ser, 24, 1); + +struct luo_test_zero_ser { + u64 val; +}; + +static_assert(sizeof(struct luo_test_zero_ser) == 8); +DECLARE_LUO_SER_WRAP(luo_test_zero_ser, 16, 0); + +static void luo_ser_hdr_set_get_test(struct kunit *test) +{ + struct luo_test_multi_ser_wrap out; + struct luo_test_zero_ser_wrap zero; + + memset(&out, 0, sizeof(out)); + luo_test_multi_ser_wrap_init(&out); + + KUNIT_EXPECT_EQ(test, out.hdr.size, 32U); + KUNIT_EXPECT_EQ(test, out.hdr.nr_features, 35U); + KUNIT_EXPECT_EQ(test, LUO_SER_NR_FLAGS(out.hdr.nr_features), 2U); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_size(&out.hdr), 24UL); + KUNIT_EXPECT_PTR_EQ(test, luo_ser_payload(&out.hdr), (void *)&out.ser); + + /* 0, 31 in flags[0]; 32, 34 in flags[1] */ + luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_REQUIRED); + luo_ser_set_feat(&out.hdr, 31, LUO_FEAT_OPTIONAL); + luo_ser_set_feat(&out.hdr, 32, LUO_FEAT_REQUIRED); + luo_ser_set_feat(&out.hdr, 34, LUO_FEAT_OPTIONAL); + + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 0), (int)LUO_FEAT_REQUIRED); + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 1), (int)LUO_FEAT_ABSENT); + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 31), (int)LUO_FEAT_OPTIONAL); + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 32), (int)LUO_FEAT_REQUIRED); + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 33), (int)LUO_FEAT_ABSENT); + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 34), (int)LUO_FEAT_OPTIONAL); + + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 35), (int)LUO_FEAT_ABSENT); + KUNIT_EXPECT_EQ(test, (int)luo_ser_get_feat(&out.hdr, 100), (int)LUO_FEAT_ABSENT); + + memset(&zero, 0, sizeof(zero)); + luo_test_zero_ser_wrap_init(&zero); + KUNIT_EXPECT_EQ(test, sizeof(struct luo_ser_hdr), 8UL); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_size(&zero.hdr), 8UL); + KUNIT_EXPECT_PTR_EQ(test, luo_ser_payload(&zero.hdr), (void *)&zero.ser); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&zero.hdr, NULL, 0), 0); +} + +static void luo_ser_hdr_truth_table_test(struct kunit *test) +{ + struct luo_test_one_ser_wrap out; + bool supp_yes[1] = { true }; + bool supp_no[1] = { false }; + + memset(&out, 0, sizeof(out)); + luo_test_one_ser_wrap_init(&out); + luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_ABSENT); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1), 0); + + luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_OPTIONAL); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1), 0); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_yes, 1), 0); + + luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_REQUIRED); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1), + -EOPNOTSUPP); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_yes, 1), 0); + + out.flags[0] = (u64)LUO_FEAT_RESERVED; + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_no, 1), 0); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, supp_yes, 1), 0); + + luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_REQUIRED); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, NULL, 0), + -EOPNOTSUPP); + luo_ser_set_feat(&out.hdr, 0, LUO_FEAT_OPTIONAL); + KUNIT_EXPECT_EQ(test, luo_ser_hdr_validate(&out.hdr, NULL, 0), 0); +} + +static struct kunit_case luo_feature_test_cases[] = { + KUNIT_CASE(luo_ser_hdr_set_get_test), + KUNIT_CASE(luo_ser_hdr_truth_table_test), + {} +}; + +static struct kunit_suite luo_feature_test_suite = { + .name = "liveupdate_feature", + .test_cases = luo_feature_test_cases, +}; + +kunit_test_suite(luo_feature_test_suite); + +MODULE_DESCRIPTION("KUnit tests for the Live Update feature header helpers"); +MODULE_LICENSE("GPL"); -- 2.56.0.307.g5fe7bd680c-goog