From nobody Fri Dec 19 12:46:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 44386264F81; Tue, 20 May 2025 08:25:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729517; cv=none; b=pbJN/ntFFynQCunJARlMVD0PJiz7dfdyGcLxjfRTFOdRSQyrYZZI8L3/DY8QlJI26rpdn0QHidHe6C7H6P0B7Vtb3mq/NZipuFBWQDfkDVtJOjrXZUjQfiqxTpqBg30WFqkNetu2VK2mmkyk1n9c8zIVC8/pSGWJMZAp5O+Cpog= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729517; c=relaxed/simple; bh=7Gqk9JjSJ/fG63XRlpdt6ZRfS4huFSCyqSgLScQXexo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=U/Xh5UrXd5Vd0YauT0HDgSAEDVBiAHdTA69CDm8CtJHFktfGkBbaLGr6dUaCwfGC6q352JaU0jpow8EGy70d6Xoy+vPhN5CxKZmH3fwqvBODfC8ibDByXnbzjfWb5D/oIVQvZyPlxHYOrJ/QBhLOq3oJxcXyxxEVwiO8OoLmwGc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=qMit0kiO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="qMit0kiO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 63AB6C4CEEF; Tue, 20 May 2025 08:25:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1747729516; bh=7Gqk9JjSJ/fG63XRlpdt6ZRfS4huFSCyqSgLScQXexo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=qMit0kiOEB2436VZRhaAyPfwuGQI592EUgAsBXHXPCcKrKRPSE4tjz5DFYy3QQOTl isndEA+qHni+Eo4sn/Hzd7sH0AJyd18tEwwptvTbRydhR2bL3DhMBM8mH52ZKi6Lzi kdze5DomGgJqMntYHrRTvnTuXP016katxJBlx8REouhhIkWd+2gkS+FbFJGGrVqTmF fnxQsaQuJCDiLm4/UW3YBEyi4glUfI8ZbveQcX+OJRiCrGj4TzaAxlTduX29Kr881U KEGsFaCqh9CkVzOVxy2uXsusJzWOvlicrqdHOEJXsoWiWfX/YJOVQAEhhVjyMUmQLg H7qjnDnqvhDXA== From: Tzung-Bi Shih To: bleung@chromium.org, brendan.higgins@linux.dev, davidgow@google.com Cc: tzungbi@kernel.org, rmoar@google.com, rostedt@goodmis.org, mhiramat@kernel.org, naveen@kernel.org, anil.s.keshavamurthy@intel.com, davem@davemloft.net, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org, Daniel Latypov Subject: [RFC PATCH 1/7] kunit: expose ftrace-based API for stubbing out functions during tests Date: Tue, 20 May 2025 08:24:28 +0000 Message-ID: <20250520082435.2255639-2-tzungbi@kernel.org> X-Mailer: git-send-email 2.49.0.1101.gccaa498523-goog In-Reply-To: <20250520082435.2255639-1-tzungbi@kernel.org> References: <20250520082435.2255639-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Daniel Latypov Allow function redirection using ftrace and kernel livepatch. This is basically equivalent to the static_stub support in the previous patch, but does not require the function being replaced to be modified (save for the addition of KUNIT_STUBBABLE/noinline). This is hidden behind the CONFIG_KUNIT_FTRACE_STUBS option, and has a number of dependencies, including ftrace, livepatch and CONFIG_KALLSYMS_ALL. As a result, it only works on architectures where these are available. You can run the KUnit example tests with the following: $ ./tools/testing/kunit/kunit.py run --kunitconfig lib/kunit/stubs_example.= kunitconfig --arch=3Dx86_64 To the end user, replacing a function is very simple, e.g. KUNIT_STUBBABLE void real_func(int n); void replacement_func(int n); /* in tests */ kunit_activate_ftrace_stub(test, real_func, replacement_func); The implementation is inspired by Steven's snippet here [1]. Some more details: * stubbing is automatically undone at the end of tests * it can also be manually undone with kunit_deactive_ftrace_stub() * stubbing only applies when current->kunit_test =3D=3D test * note: currently can't have more than one test running at a time * KUNIT_STUBBABLE marks tests as noinline when CONFIG_KUNIT_STUBS is set * this ensures we can actually stub all calls * KUNIT_STUBBABLE_TRAMPOLINE is a version that evaluates to __always_inline when stubbing is not enabled * This may need to be used with a wrapper function. * See the doc comment for more details. Sharp-edges: * kernel livepatch only works on some arches (not UML) * if you don't use noinline/KUNIT_STUBBABLE, functions might be inlined and thus none of this works: * if it's always inlined, at least the attempt to stub will fail * if it's sometimes inlined, then the stub silently won't work [1] https://lore.kernel.org/lkml/20220224091550.2b7e8784@gandalf.local.home [2] https://lore.kernel.org/linux-kselftest/CAGS_qxqtMpjKX+CMF6eBUWfsc-TKR9= -Bk+hYM=3DPHa0_wUtQQuA@mail.gmail.com/ Co-developed-by: David Gow Signed-off-by: David Gow Signed-off-by: Daniel Latypov [tzungbi: * Resolve contextual conflicts for rebasing. * klp_arch_set_pc() -> ftrace_regs_set_instruction_pointer(). * Fix type check in kunit_activate_ftrace_stub() just like what has been done in kunit_activate_static_stub(). * Include in lib/kunit/ftrace_stub.c. * Fix typo in include/kunit/ftrace_stub.h as pointed out in [2]. * eavlautes -> evaluates. * active -> activate. * Fix typo in kunit-example-test.c and ftrace_stub.c. * static -> ftrace. * EXPORT_SYMBOL_IF_KUNIT() for ftrace_location() as lib/kunit/ftrace_stub.c uses it. If CONFIG_KUNIT=3Dm, it can't find the symbol ftrace_location(). * KUNIT_ASSERT_FAILURE() -> KUNIT_FAIL_AND_ABORT() due to 7d4087b01389. ] Signed-off-by: Tzung-Bi Shih --- include/kunit/ftrace_stub.h | 84 +++++++++++++++++ kernel/trace/ftrace.c | 3 + lib/kunit/Kconfig | 11 +++ lib/kunit/Makefile | 4 + lib/kunit/ftrace_stub.c | 139 ++++++++++++++++++++++++++++ lib/kunit/kunit-example-test.c | 27 +++++- lib/kunit/stubs_example.kunitconfig | 11 +++ 7 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 include/kunit/ftrace_stub.h create mode 100644 lib/kunit/ftrace_stub.c create mode 100644 lib/kunit/stubs_example.kunitconfig diff --git a/include/kunit/ftrace_stub.h b/include/kunit/ftrace_stub.h new file mode 100644 index 000000000000..79c496b51ac5 --- /dev/null +++ b/include/kunit/ftrace_stub.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _KUNIT_FTRACE_STUB_H +#define _KUNIT_FTRACE_STUB_H + +/** KUNIT_STUBBABLE - marks a function as stubbable when stubbing support = is + * enabled. + * + * Stubbing uses ftrace internally, so we can only stub out functions when= they + * are not inlined. This macro evaluates to noinline when stubbing support= is + * enabled to thus make it safe. + * + * If you cannot add this annotation to the function, you can instead use + * KUNIT_STUBBABLE_TRAMPOLINE, which is the same, but evaluates to + * __always_inline when stubbing is not enabled. + * + * Consider copy_to_user, which is marked as __always_inline: + * + * .. code-block:: c + * static KUNIT_STUBBABLE_TRAMPOLINE unsigned long + * copy_to_user_trampoline(void __user *to, const void *from, unsigned lon= g n) + * { + * return copy_to_user(to, from, n); + * } + * + * Then we simply need to update our code to go through this function inst= ead + * (in the places where we want to stub it out). + */ +#if IS_ENABLED(CONFIG_KUNIT_FTRACE_STUBS) +#define KUNIT_STUBBABLE noinline +#define KUNIT_STUBBABLE_TRAMPOLINE noinline +#else +#define KUNIT_STUBBABLE +#define KUNIT_STUBBABLE_TRAMPOLINE __always_inline +#endif + +struct kunit; + +/** + * kunit_activate_ftrace_stub() - makes all calls to @func go to @replacem= ent during @test. + * @test: The test context object. + * @func: The function to stub out, must be annotated with KUNIT_STUBBABLE. + * @replacement: The function to replace @func with. + * + * All calls to @func will instead call @replacement for the duration of t= he + * current test. If called from outside the test's thread, the function wi= ll + * not be redirected. + * + * The redirection can be disabled again with kunit_deactivate_ftrace_stub= (). + * + * Example: + * + * .. code-block:: c + * KUNIT_STUBBABLE int real_func(int n) + * { + * pr_info("real_func() called with %d", n); + * return 0; + * } + * + * void replacement_func(int n) + * { + * pr_info("replacement_func() called with %d", n); + * return 42; + * } + * + * void example_test(struct kunit *test) + * { + * kunit_activate_ftrace_stub(test, real_func, replacement_func); + * KUNIT_EXPECT_EQ(test, real_func(1), 42); + * } + * + */ +#define kunit_activate_ftrace_stub(test, func, replacement) do { \ + typecheck_fn(typeof(&func), replacement); \ + __kunit_activate_ftrace_stub(test, #func, func, replacement); \ +} while (0) + +void __kunit_activate_ftrace_stub(struct kunit *test, + const char *name, + void *real_fn_addr, + void *replacement_addr); + + +void kunit_deactivate_ftrace_stub(struct kunit *test, void *real_fn_addr); +#endif /* _KUNIT_STUB_H */ diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1a48aedb5255..7e86bc57d462 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -36,6 +36,8 @@ #include #include =20 +#include + #include =20 #include @@ -1663,6 +1665,7 @@ unsigned long ftrace_location(unsigned long ip) } return loc; } +EXPORT_SYMBOL_IF_KUNIT(ftrace_location); =20 /** * ftrace_text_reserved - return true if range contains an ftrace location diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index a97897edd964..933fda1df5c3 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -93,4 +93,15 @@ config KUNIT_AUTORUN_ENABLED In most cases this should be left as Y. Only if additional opt-in behavior is needed should this be set to N. =20 +config KUNIT_FTRACE_STUBS + bool "Support for stubbing out functions in KUnit tests with ftrace and k= ernel livepatch" + depends on FTRACE=3Dy && FUNCTION_TRACER=3Dy && MODULES=3Dy && DEBUG_KERN= EL=3Dy && KALLSYMS_ALL=3Dy && LIVEPATCH=3Dy + help + Builds support for stubbing out functions for the duration of KUnit + test cases or suites using ftrace and kernel livepatch. + See KUNIT_EXAMPLE_TEST for an example. + + NOTE: this does not work on all architectures (like UML, arm64) and + relies on a lot of magic (see the dependencies list). + endif # KUNIT diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index 5aa51978e456..0ecb255576e2 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -29,3 +29,7 @@ obj-$(CONFIG_KUNIT_TEST) +=3D assert_test.o endif =20 obj-$(CONFIG_KUNIT_EXAMPLE_TEST) +=3D kunit-example-test.o + +ifeq ($(CONFIG_KUNIT_FTRACE_STUBS),y) +kunit-objs +=3D ftrace_stub.o +endif diff --git a/lib/kunit/ftrace_stub.c b/lib/kunit/ftrace_stub.c new file mode 100644 index 000000000000..1cf6edceb19c --- /dev/null +++ b/lib/kunit/ftrace_stub.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include + +#include +#include +#include + +struct kunit_ftrace_stub_ctx { + struct kunit *test; + unsigned long real_fn_addr; /* used as a key to lookup the stub */ + unsigned long replacement_addr; + struct ftrace_ops ops; /* a copy of kunit_stub_base_ops with .private set= */ +}; + +static void kunit_stub_trampoline(unsigned long ip, unsigned long parent_i= p, + struct ftrace_ops *ops, + struct ftrace_regs *fregs) +{ + struct kunit_ftrace_stub_ctx *ctx =3D ops->private; + int lock_bit; + + if (current->kunit_test !=3D ctx->test) + return; + + lock_bit =3D ftrace_test_recursion_trylock(ip, parent_ip); + KUNIT_ASSERT_GE(ctx->test, lock_bit, 0); + + ftrace_regs_set_instruction_pointer(fregs, ctx->replacement_addr); + + ftrace_test_recursion_unlock(lock_bit); +} + +static struct ftrace_ops kunit_stub_base_ops =3D { + .func =3D &kunit_stub_trampoline, + .flags =3D FTRACE_OPS_FL_IPMODIFY | +#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS + FTRACE_OPS_FL_SAVE_REGS | +#endif + FTRACE_OPS_FL_DYNAMIC +}; + +static void __kunit_ftrace_stub_resource_free(struct kunit_resource *res) +{ + struct kunit_ftrace_stub_ctx *ctx =3D res->data; + + unregister_ftrace_function(&ctx->ops); + kfree(ctx); +} + +/* Matching function for kunit_find_resource(). match_data is real_fn_addr= . */ +static bool __kunit_ftrace_stub_resource_match(struct kunit *test, + struct kunit_resource *res, + void *match_real_fn_addr) +{ + /* This pointer is only valid if res is a ftrace stub resource. */ + struct kunit_ftrace_stub_ctx *ctx =3D res->data; + + /* Make sure the resource is a ftrace stub resource. */ + if (res->free !=3D &__kunit_ftrace_stub_resource_free) + return false; + + return ctx->real_fn_addr =3D=3D (unsigned long)match_real_fn_addr; +} + +void kunit_deactivate_ftrace_stub(struct kunit *test, void *real_fn_addr) +{ + struct kunit_resource *res; + + KUNIT_ASSERT_PTR_NE_MSG(test, real_fn_addr, NULL, + "Tried to deactivate a NULL stub."); + + /* Look up the existing stub for this function. */ + res =3D kunit_find_resource(test, + __kunit_ftrace_stub_resource_match, + real_fn_addr); + + /* Error out if the stub doesn't exist. */ + KUNIT_ASSERT_PTR_NE_MSG(test, res, NULL, + "Tried to deactivate a nonexistent stub."); + + /* Free the stub. We 'put' twice, as we got a reference + * from kunit_find_resource(). The free function will deactivate the + * ftrace stub. + */ + kunit_remove_resource(test, res); + kunit_put_resource(res); +} +EXPORT_SYMBOL_GPL(kunit_deactivate_ftrace_stub); + +void __kunit_activate_ftrace_stub(struct kunit *test, + const char *name, + void *real_fn_addr, + void *replacement_addr) +{ + unsigned long ftrace_ip; + struct kunit_ftrace_stub_ctx *ctx; + int ret; + + ftrace_ip =3D ftrace_location((unsigned long)real_fn_addr); + if (!ftrace_ip) + KUNIT_FAIL_AND_ABORT(test, "%s ip is invalid: not a function, or is mark= ed notrace or inline", name); + + /* Allocate the stub context, which contains pointers to the replacement + * function and the test object. It's also registered as a KUnit + * resource which can be looked up by address (to deactivate manually) + * and is destroyed automatically on test exit. + */ + ctx =3D kmalloc(sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, ctx, "failed to allocate kunit stu= b for %s", name); + + ctx->test =3D test; + ctx->ops =3D kunit_stub_base_ops; + ctx->ops.private =3D ctx; + ctx->real_fn_addr =3D (unsigned long)real_fn_addr; + ctx->replacement_addr =3D (unsigned long)replacement_addr; + + ret =3D ftrace_set_filter_ip(&ctx->ops, ftrace_ip, 0, 0); + if (ret) { + kfree(ctx); + KUNIT_FAIL_AND_ABORT(test, "failed to set filter ip for %s: %d", name, r= et); + } + + ret =3D register_ftrace_function(&ctx->ops); + if (ret) { + kfree(ctx); + if (ret =3D=3D -EBUSY) + KUNIT_FAIL_AND_ABORT(test, "failed to register stub (-EBUSY) for %s, li= kely due to already stubbing it?", name); + KUNIT_FAIL_AND_ABORT(test, "failed to register stub for %s: %d", name, r= et); + } + + /* Register the stub as a resource with a cleanup function */ + kunit_alloc_resource(test, NULL, + __kunit_ftrace_stub_resource_free, + GFP_KERNEL, ctx); +} +EXPORT_SYMBOL_GPL(__kunit_activate_ftrace_stub); diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c index 3056d6bc705d..1974e8d24a50 100644 --- a/lib/kunit/kunit-example-test.c +++ b/lib/kunit/kunit-example-test.c @@ -8,6 +8,7 @@ =20 #include #include +#include =20 /* * This is the most fundamental element of KUnit, the test case. A test ca= se @@ -152,7 +153,7 @@ static void example_all_expect_macros_test(struct kunit= *test) } =20 /* This is a function we'll replace with static stubs. */ -static int add_one(int i) +static KUNIT_STUBBABLE int add_one(int i) { /* This will trigger the stub if active. */ KUNIT_STATIC_STUB_REDIRECT(add_one, i); @@ -277,6 +278,29 @@ static void example_slow_test(struct kunit *test) KUNIT_EXPECT_EQ(test, 1 + 1, 2); } =20 +/* + * This test shows the use of ftrace stubs. + */ +static void example_ftrace_stub_test(struct kunit *test) +{ +#if !IS_ENABLED(CONFIG_KUNIT_FTRACE_STUBS) + kunit_skip(test, "KUNIT_FTRACE_STUBS not enabled"); +#else + /* By default, function is not stubbed. */ + KUNIT_EXPECT_EQ(test, add_one(1), 2); + + /* Replace add_one() with subtract_one(). */ + kunit_activate_ftrace_stub(test, add_one, subtract_one); + + /* add_one() is now replaced. */ + KUNIT_EXPECT_EQ(test, add_one(1), 0); + + /* Return add_one() to normal. */ + kunit_deactivate_ftrace_stub(test, add_one); + KUNIT_EXPECT_EQ(test, add_one(1), 2); +#endif +} + /* * Here we make a list of all the test cases we want to add to the test su= ite * below. @@ -297,6 +321,7 @@ static struct kunit_case example_test_cases[] =3D { KUNIT_CASE(example_priv_test), KUNIT_CASE_PARAM(example_params_test, example_gen_params), KUNIT_CASE_SLOW(example_slow_test), + KUNIT_CASE(example_ftrace_stub_test), {} }; =20 diff --git a/lib/kunit/stubs_example.kunitconfig b/lib/kunit/stubs_example.= kunitconfig new file mode 100644 index 000000000000..a47369199fb9 --- /dev/null +++ b/lib/kunit/stubs_example.kunitconfig @@ -0,0 +1,11 @@ +CONFIG_KUNIT=3Dy +CONFIG_KUNIT_FTRACE_STUBS=3Dy +CONFIG_KUNIT_EXAMPLE_TEST=3Dy + +# Depedencies +CONFIG_FTRACE=3Dy +CONFIG_FUNCTION_TRACER=3Dy +CONFIG_MODULES=3Dy +CONFIG_DEBUG_KERNEL=3Dy +CONFIG_KALLSYMS_ALL=3Dy +CONFIG_LIVEPATCH=3Dy --=20 2.49.0.1101.gccaa498523-goog From nobody Fri Dec 19 12:46:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CAEC1264F9F; Tue, 20 May 2025 08:25:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729519; cv=none; b=MxiTvNVxa1MatBTN317xBbxq9cggeh94yHqHxLRpm57AOTYBKucvGXkk/g1S02EKOajoDmhJLEcrFzYdLUXtOVxM/lgIVSUTGFOyoGQ0iXHSUPQqC2fLuKg4UJ3Ugsw6F9sMpyQu0DiZ9kw0oUm/2siccdeFUZfSdXf6NenjEX0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729519; c=relaxed/simple; bh=knILOCtQNGTaQcP/xzsYnjivS1CVRek5fFnGxHke0Uk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fmcfdmY1ETTG7Mw08CxZ41uqYBIKVPX8nbzx+GLRJEjiX2pEoJlunjlSPhd4jDmgrzIxVtdaIRcOse5PEXowh19bTU6L81w70PcFsFvWdXdKW49YUBHZv77txNn6ujullUX+5XMkLZYrkU48b6uqUNrkJ9TivEyBVL7OHWzZyXs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BrejAkOf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BrejAkOf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 12D1EC4CEF2; Tue, 20 May 2025 08:25:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1747729519; bh=knILOCtQNGTaQcP/xzsYnjivS1CVRek5fFnGxHke0Uk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BrejAkOfewqIy4AhB4I0JPfDy+t1hfnEMabu75VOwLXXaxy0asR9U9sA1GJ1h0Khp 1GzX8zG2P0pPSn56ppD5jwCeUykEGqMIF4r8FxSXBzQ8rYWU5Wq3EhGMrIUGcOtRaZ niA3MjU/VLOHnz5m/Kd3lXvXJuEZH9e4/e2fyCEurCTFEufDJeia46nrUMp3hATops MpE4B7XFRGLZpdFOyptQnV5T3Xzby5jVkZzF6jFoDhoeqJrchdTYUHHs3oXDqmUdjq SwonEXGGOUaiUnq4FQ/kh4AbebmOnPTw1NsbL+nzJYu67FeZ7B+UA776f4C+7pqYm6 Bsib8KP3+4dDA== From: Tzung-Bi Shih To: bleung@chromium.org, brendan.higgins@linux.dev, davidgow@google.com Cc: tzungbi@kernel.org, rmoar@google.com, rostedt@goodmis.org, mhiramat@kernel.org, naveen@kernel.org, anil.s.keshavamurthy@intel.com, davem@davemloft.net, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 2/7] platform/chrome: kunit: cros_ec_i2c: Add tests with ftrace stub Date: Tue, 20 May 2025 08:24:29 +0000 Message-ID: <20250520082435.2255639-3-tzungbi@kernel.org> X-Mailer: git-send-email 2.49.0.1101.gccaa498523-goog In-Reply-To: <20250520082435.2255639-1-tzungbi@kernel.org> References: <20250520082435.2255639-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For running the tests: $ ./tools/testing/kunit/kunit.py run \ --arch=3Dx86_64 \ --kconfig_add CONFIG_CHROME_PLATFORMS=3Dy \ --kconfig_add CONFIG_CROS_EC=3Dy \ --kconfig_add CONFIG_FTRACE=3Dy \ --kconfig_add CONFIG_FUNCTION_TRACER=3Dy \ --kconfig_add CONFIG_MODULES=3Dy \ --kconfig_add CONFIG_DEBUG_KERNEL=3Dy \ --kconfig_add CONFIG_KALLSYMS_ALL=3Dy \ --kconfig_add CONFIG_LIVEPATCH=3Dy \ --kconfig_add CONFIG_KUNIT_FTRACE_STUBS=3Dy \ --kconfig_add CONFIG_I2C=3Dy \ --kconfig_add CONFIG_CROS_EC_I2C=3Dy \ --kconfig_add CONFIG_CROS_KUNIT_EC_I2C_TEST=3Dy \ cros_ec_i2c* Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/Kconfig | 9 + drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_ec_i2c_test.c | 479 +++++++++++++++++++++ 3 files changed, 489 insertions(+) create mode 100644 drivers/platform/chrome/cros_ec_i2c_test.c diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kcon= fig index 10941ac37305..5e0b44fb7ca7 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -327,4 +327,13 @@ config CROS_KUNIT_EC_PROTO_TEST help Kunit tests for ChromeOS EC protocol. =20 +config CROS_KUNIT_EC_I2C_TEST + tristate "Kunit tests for ChromeOS EC over I2C" if !KUNIT_ALL_TESTS + depends on KUNIT && CROS_EC + default KUNIT_ALL_TESTS + depends on KUNIT_FTRACE_STUBS + depends on CROS_EC_I2C + help + Kunit tests for ChromeOS EC over I2C. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Mak= efile index b981a1bb5bd8..9808f25aea38 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -44,3 +44,4 @@ obj-$(CONFIG_WILCO_EC) +=3D wilco_ec/ # Kunit test cases obj-$(CONFIG_CROS_KUNIT_EC_PROTO_TEST) +=3D cros_kunit_proto_test.o cros_kunit_proto_test-objs :=3D cros_ec_proto_test_util.o cros_ec_proto_t= est.o +obj-$(CONFIG_CROS_KUNIT_EC_I2C_TEST) +=3D cros_ec_i2c_test.o diff --git a/drivers/platform/chrome/cros_ec_i2c_test.c b/drivers/platform/= chrome/cros_ec_i2c_test.c new file mode 100644 index 000000000000..3a7f1a17d82d --- /dev/null +++ b/drivers/platform/chrome/cros_ec_i2c_test.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kunit tests for ChromeOS Embedded Controller I2C interface. + */ +#include +#include + +#include +#include +#include + +#include "cros_ec.h" + +#define BUFSIZE 128 +#define I2C_ADDR 0x06 + +struct cros_ec_i2c_test_priv { + struct i2c_adapter *fake_adap; + struct i2c_client *client; + + int fake_cros_ec_register_called; + struct cros_ec_device *ec_dev; + + int fake_cros_ec_unregister_called; + + struct i2c_msg *i2c_msgs; + int i2c_msg_num; + int fake_i2c_xfer_res; + int fake_i2c_xfer_len; + u8 *fake_i2c_xfer_data; + u8 fake_i2c_xfer_sum; + int fake_i2c_xfer_ret; +}; + +static int fake_cros_ec_register(struct cros_ec_device *ec_dev) +{ + struct kunit *test =3D current->kunit_test; + struct cros_ec_i2c_test_priv *priv =3D test->priv; + + priv->fake_cros_ec_register_called +=3D 1; + + priv->ec_dev =3D ec_dev; + priv->ec_dev->din_size =3D BUFSIZE; + priv->ec_dev->din =3D kunit_kmalloc(test, priv->ec_dev->din_size, GFP_KER= NEL); + KUNIT_ASSERT_NOT_NULL(test, priv->ec_dev->din); + priv->ec_dev->dout_size =3D BUFSIZE; + priv->ec_dev->dout =3D kunit_kmalloc(test, priv->ec_dev->dout_size, GFP_K= ERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->ec_dev->dout); + return 0; +} + +static void fake_cros_ec_unregister(struct cros_ec_device *ec_dev) +{ + struct kunit *test =3D current->kunit_test; + struct cros_ec_i2c_test_priv *priv =3D test->priv; + + priv->fake_cros_ec_unregister_called +=3D 1; +} + +static int fake_cros_ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg = msgs[], int num) +{ + struct kunit *test =3D current->kunit_test; + struct cros_ec_i2c_test_priv *priv =3D test->priv; + int i; + + priv->i2c_msgs =3D kunit_kmalloc_array(test, sizeof(struct i2c_msg), num,= GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->i2c_msgs); + + for (i =3D 0; i < num; ++i) { + memcpy(priv->i2c_msgs + i, msgs + i, sizeof(struct i2c_msg)); + + priv->i2c_msgs[i].buf =3D kunit_kmalloc(test, msgs[i].len, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->i2c_msgs[i].buf); + memcpy(priv->i2c_msgs[i].buf, msgs[i].buf, msgs[i].len); + + if (msgs[i].flags =3D=3D I2C_M_RD) { + msgs[i].buf[0] =3D priv->fake_i2c_xfer_res; + msgs[i].buf[1] =3D priv->fake_i2c_xfer_len; + + if (priv->fake_i2c_xfer_data) + memcpy(&msgs[i].buf[2], priv->fake_i2c_xfer_data, + priv->fake_i2c_xfer_len); + + msgs[i].buf[priv->fake_i2c_xfer_len + 2] =3D priv->fake_i2c_xfer_sum; + } + } + priv->i2c_msg_num =3D num; + + return priv->fake_i2c_xfer_ret; +} + +static u32 fake_cros_ec_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm fake_cros_ec_i2c_algorithm =3D { + .master_xfer =3D fake_cros_ec_i2c_xfer, + .functionality =3D fake_cros_ec_i2c_functionality, +}; + +static int cros_ec_i2c_test_init(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv; + static const struct i2c_board_info board_info =3D { + I2C_BOARD_INFO("cros-ec-i2c", I2C_ADDR), + }; + + kunit_activate_ftrace_stub(test, cros_ec_register, fake_cros_ec_register); + kunit_activate_ftrace_stub(test, cros_ec_unregister, fake_cros_ec_unregis= ter); + + priv =3D kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv); + test->priv =3D priv; + + priv->fake_adap =3D kunit_kzalloc(test, sizeof(*priv->fake_adap), GFP_KER= NEL); + KUNIT_ASSERT_NOT_NULL(test, priv->fake_adap); + + priv->fake_adap->owner =3D THIS_MODULE; + strscpy(priv->fake_adap->name, "cros-ec-i2c-test"); + priv->fake_adap->algo =3D &fake_cros_ec_i2c_algorithm; + priv->fake_adap->retries =3D 3; + + KUNIT_ASSERT_EQ(test, i2c_add_adapter(priv->fake_adap), 0); + + priv->client =3D i2c_new_client_device(priv->fake_adap, &board_info); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->client); + KUNIT_EXPECT_EQ(test, priv->client->addr, I2C_ADDR); + + KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_register_called, 1); + KUNIT_ASSERT_NOT_NULL(test, priv->ec_dev); + KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_unregister_called, 0); + return 0; +} + +static void cros_ec_i2c_test_exit(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + + i2c_unregister_device(priv->client); + KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_unregister_called, 1); + + i2c_del_adapter(priv->fake_adap); + + kunit_deactivate_ftrace_stub(test, cros_ec_register); + kunit_deactivate_ftrace_stub(test, cros_ec_unregister); +} + +static int cros_ec_i2c_test_cmd_xfer_init(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv; + + cros_ec_i2c_test_init(test); + priv =3D test->priv; + priv->ec_dev->proto_version =3D 2; + return 0; +} + +static void cros_ec_i2c_test_cmd_xfer_normal(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command *msg; + int ret, i; + u8 sum; + + msg =3D kunit_kmalloc(test, sizeof(*msg) + 2 /* max(outsize, insize) */, = GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, msg); + msg->version =3D 0x1000; + msg->command =3D 0x1001; + msg->outsize =3D 2; + msg->insize =3D 1; + msg->data[0] =3D 0xbe; + msg->data[1] =3D 0xef; + + priv->fake_i2c_xfer_res =3D 0; + priv->fake_i2c_xfer_data =3D kunit_kmalloc(test, msg->insize, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->fake_i2c_xfer_data); + priv->fake_i2c_xfer_data[0] =3D 0xaa; + priv->fake_i2c_xfer_len =3D msg->insize; + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + sum =3D priv->fake_i2c_xfer_res + priv->fake_i2c_xfer_len + priv->fake_i2= c_xfer_data[0]; + priv->fake_i2c_xfer_sum =3D sum; + + ret =3D priv->ec_dev->cmd_xfer(priv->ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, 1 /* insize */); + KUNIT_EXPECT_EQ(test, msg->result, 0); + KUNIT_EXPECT_EQ(test, msg->data[0], 0xaa); + + KUNIT_EXPECT_EQ(test, priv->i2c_msg_num, 2); + /* + * Validate output message only which is supposed to be received by EC de= vices. + */ + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].addr, I2C_ADDR); + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].flags, 0); + /* + * Total length of the message: EC_MSG_TX_HEADER_BYTES (version, command,= length) + + * outsize + EC_MSG_TX_TRAILER_BYTES (checksum). + */ + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].len, EC_MSG_TX_PROTO_BYTES + 2 /*= outsize */); + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].buf[0], (u8)(EC_CMD_VERSION0 + 0x= 1000)); + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].buf[1], (u8)0x1001); + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].buf[2], 2 /* outsize */); + for (sum =3D 0, i =3D 0; i < EC_MSG_TX_HEADER_BYTES + 2 /* outsize */; ++= i) + sum +=3D priv->i2c_msgs[0].buf[i]; + KUNIT_EXPECT_EQ(test, + priv->i2c_msgs[0].buf[EC_MSG_TX_HEADER_BYTES + 2 /* outsize */], sum); +} + +static void cros_ec_i2c_test_cmd_xfer_error(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + int ret; + + priv->fake_i2c_xfer_ret =3D -EBUSY; + ret =3D priv->ec_dev->cmd_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + + priv->fake_i2c_xfer_ret =3D 1; + ret =3D priv->ec_dev->cmd_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EIO); + + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + priv->fake_i2c_xfer_res =3D EC_RES_IN_PROGRESS; + ret =3D priv->ec_dev->cmd_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EAGAIN); +} + +static void cros_ec_i2c_test_cmd_xfer_response_too_long(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + int ret; + + priv->fake_i2c_xfer_res =3D 0; + priv->fake_i2c_xfer_data =3D kunit_kmalloc(test, 1, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->fake_i2c_xfer_data); + priv->fake_i2c_xfer_len =3D msg.insize + 1; /* make it greater than msg.i= nsize */ + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + + ret =3D priv->ec_dev->cmd_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -ENOSPC); +} + +static void cros_ec_i2c_test_cmd_xfer_response_bad_checksum(struct kunit *= test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + int ret; + + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + priv->fake_i2c_xfer_sum =3D (u8)0xbad; + + ret =3D priv->ec_dev->cmd_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EBADMSG); +} + +static struct kunit_case cros_ec_i2c_test_cmd_xfer_cases[] =3D { + KUNIT_CASE(cros_ec_i2c_test_cmd_xfer_normal), + KUNIT_CASE(cros_ec_i2c_test_cmd_xfer_error), + KUNIT_CASE(cros_ec_i2c_test_cmd_xfer_response_too_long), + KUNIT_CASE(cros_ec_i2c_test_cmd_xfer_response_bad_checksum), + {}, +}; + +static struct kunit_suite cros_ec_i2c_test_cmd_xfer_suite =3D { + .name =3D "cros_ec_i2c_test_cmd_xfer_suite", + .init =3D cros_ec_i2c_test_cmd_xfer_init, + .exit =3D cros_ec_i2c_test_exit, + .test_cases =3D cros_ec_i2c_test_cmd_xfer_cases, +}; + +static int cros_ec_i2c_test_pkt_xfer_init(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv; + + cros_ec_i2c_test_init(test); + priv =3D test->priv; + priv->ec_dev->proto_version =3D 3; + return 0; +} + +static void cros_ec_i2c_test_pkt_xfer_normal(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command *msg; + struct ec_host_request *request; + struct ec_host_response *response; + int ret, i; + u8 sum; + + msg =3D kunit_kmalloc(test, sizeof(*msg) + 2 /* max(outsize, insize) */, = GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, msg); + msg->version =3D 0x1000; + msg->command =3D 0x1001; + msg->outsize =3D 2; + msg->insize =3D 1; + msg->data[0] =3D 0xbe; + msg->data[1] =3D 0xef; + + priv->fake_i2c_xfer_res =3D 0; + priv->fake_i2c_xfer_len =3D sizeof(*response) + msg->insize; + priv->fake_i2c_xfer_data =3D kunit_kzalloc(test, priv->fake_i2c_xfer_len,= GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->fake_i2c_xfer_data); + response =3D (struct ec_host_response *)priv->fake_i2c_xfer_data; + response->struct_version =3D EC_HOST_RESPONSE_VERSION; + response->data_len =3D msg->insize; + priv->fake_i2c_xfer_data[sizeof(*response)] =3D 0xaa; + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + for (sum =3D 0, i =3D 0; i < priv->fake_i2c_xfer_len; ++i) + sum +=3D priv->fake_i2c_xfer_data[i]; + response->checksum =3D -sum; + + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, 1 /* insize */); + KUNIT_EXPECT_EQ(test, msg->result, 0); + KUNIT_EXPECT_EQ(test, msg->data[0], 0xaa); + + KUNIT_EXPECT_EQ(test, priv->i2c_msg_num, 2); + /* + * Validate output message only which is supposed to be received by EC de= vices. + */ + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].addr, I2C_ADDR); + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].flags, 0); + /* + * Total length of the message: sizeof(struct ec_host_request_i2c) + outs= ize. + * The test can't access struct ec_host_request_i2c directly (in cros_ec_= i2c.c). + * However, it is a {u8 + struct ec_host_request} struct. + */ + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].len, 1 + sizeof(*request) + 2 /* = outsize */); + /* + * i2c_msgs[0].buf is a struct ec_host_request_i2c. Again, the test can'= t access the + * struct directly. Access the u8 and struct ec_host_request separately. + */ + KUNIT_EXPECT_EQ(test, priv->i2c_msgs[0].buf[0], EC_COMMAND_PROTOCOL_3); + request =3D (struct ec_host_request *)&priv->i2c_msgs[0].buf[1]; + KUNIT_EXPECT_EQ(test, request->struct_version, EC_HOST_REQUEST_VERSION); + KUNIT_EXPECT_EQ(test, request->command, 0x1001); + KUNIT_EXPECT_EQ(test, request->command_version, (u8)0x1000); + KUNIT_EXPECT_EQ(test, request->data_len, 2 /* outsize */); + for (sum =3D 0, i =3D 0; i < sizeof(*request); ++i) + sum +=3D ((u8 *)request)[i]; + sum +=3D 0xbe + 0xef; + KUNIT_EXPECT_EQ(test, sum, 0); +} + +static void cros_ec_i2c_test_pkt_xfer_msg_too_long(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D { + .insize =3D 10, + .outsize =3D 10, + }; + int ret; + + priv->ec_dev->din_size =3D 0; + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); + + priv->ec_dev->din_size =3D BUFSIZE; + priv->ec_dev->dout_size =3D 0; + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void cros_ec_i2c_test_pkt_xfer_error(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + int ret; + + priv->fake_i2c_xfer_ret =3D -EBUSY; + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EBUSY); + + priv->fake_i2c_xfer_ret =3D 1; + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EIO); +} + +static void cros_ec_i2c_test_pkt_xfer_in_progress(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + int ret; + + priv->fake_i2c_xfer_res =3D EC_RES_IN_PROGRESS; + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EAGAIN); +} + +static void cros_ec_i2c_test_pkt_xfer_to_v2_proto(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + int ret; + + priv->fake_i2c_xfer_res =3D EC_RES_INVALID_COMMAND; + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EPROTONOSUPPORT); +} + +static void cros_ec_i2c_test_pkt_xfer_response_too_short(struct kunit *tes= t) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + int ret; + + priv->fake_i2c_xfer_len =3D sizeof(struct ec_host_response) - 1; /* make = it shorter */ + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EBADMSG); +} + +static void cros_ec_i2c_test_pkt_xfer_response_too_long(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + struct ec_host_response *response; + int ret; + + priv->fake_i2c_xfer_len =3D sizeof(*response); + priv->fake_i2c_xfer_data =3D kunit_kzalloc(test, priv->fake_i2c_xfer_len,= GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->fake_i2c_xfer_data); + response =3D (struct ec_host_response *)priv->fake_i2c_xfer_data; + response->struct_version =3D EC_HOST_RESPONSE_VERSION; + response->data_len =3D 100; + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EMSGSIZE); +} + +static void cros_ec_i2c_test_pkt_xfer_bad_checksum(struct kunit *test) +{ + struct cros_ec_i2c_test_priv *priv =3D test->priv; + struct cros_ec_command msg =3D {}; + struct ec_host_response *response; + int ret; + + priv->fake_i2c_xfer_len =3D sizeof(*response); + priv->fake_i2c_xfer_data =3D kunit_kzalloc(test, priv->fake_i2c_xfer_len,= GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->fake_i2c_xfer_data); + response =3D (struct ec_host_response *)priv->fake_i2c_xfer_data; + response->struct_version =3D EC_HOST_RESPONSE_VERSION; + response->checksum =3D (u8)0xbad; + priv->fake_i2c_xfer_ret =3D 2 /* # of i2c_msg */; + + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EBADMSG); +} + +static struct kunit_case cros_ec_i2c_test_pkt_xfer_cases[] =3D { + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_normal), + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_msg_too_long), + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_error), + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_in_progress), + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_to_v2_proto), + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_response_too_short), + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_response_too_long), + KUNIT_CASE(cros_ec_i2c_test_pkt_xfer_bad_checksum), + {}, +}; + +static struct kunit_suite cros_ec_i2c_test_pkt_xfer_suite =3D { + .name =3D "cros_ec_i2c_test_pkt_xfer_suite", + .init =3D cros_ec_i2c_test_pkt_xfer_init, + .exit =3D cros_ec_i2c_test_exit, + .test_cases =3D cros_ec_i2c_test_pkt_xfer_cases, +}; + +kunit_test_suites( + &cros_ec_i2c_test_cmd_xfer_suite, + &cros_ec_i2c_test_pkt_xfer_suite, +); + +MODULE_LICENSE("GPL"); --=20 2.49.0.1101.gccaa498523-goog From nobody Fri Dec 19 12:46:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DFFD326560B; Tue, 20 May 2025 08:25:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729522; cv=none; b=cpSedkCAqXRXPAegFq55qaSyT9UjPRlL/Qyw/0I7K3bROZk1TTZD3u9r+eOckw29u7acTG38wAIu2M4Jd32pwXbnFLysHS+EvrHliu0XDQzFDkdKR15Y7OgVo2O6EH+Jjbw2zBLYg5M+ti/ZOb2uQNKLitweCGCMqomTCAbtIyc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729522; c=relaxed/simple; bh=Kdt2GQcqm/bi8fPVEBnTYb5UzxELQd2ak+jgUFrgci4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=bfVbl4lmYrXbTnsII+j2SPHvfx8KIZKo9ysKW/eP0HmA1cyfc5A5vj567g+WExlyH2T4XZ6mzpswSZYatCUJaGYpC5zg+NHpgSU6G5MWA9Tpjwz2y2gcWoIJC0ubDOpQ4i5tpVBqi5VLivs2R+7wwz5I96m+N0em6sAwFrBrtEg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bK2kBCzq; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bK2kBCzq" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9589BC4CEE9; Tue, 20 May 2025 08:25:19 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1747729521; bh=Kdt2GQcqm/bi8fPVEBnTYb5UzxELQd2ak+jgUFrgci4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bK2kBCzqWtLk+BlQ1GzdSc4V/kPSbnmNN9WWBJkghfu/t+l6RQN5rSe2z7V1F78OX IEdlWzGHEVa5tAxd/rX58cHI7em6IrWAixiysvFdRdg1uEWNCse7YRWSa+BwBcAIf9 ZTGZfyER8hK19Y8ve9IAQEhVCgs0drEQHLlOEXHGV7zI7pAJi+nv/Cibf4if3KOXdi 2c75BKeO+45CuxhlMkBKWwQzJJc4CXvmFOc1sXEBBENR97//Jke4YaU+6kF21U4Bin q3WfXFh6/8/ZetL0lq/5bWKIr0Gszr5/g7dkS++eMySSmMz2RjxxlOi/3YpvhGqDyc ftSIWDyVOk7RQ== From: Tzung-Bi Shih To: bleung@chromium.org, brendan.higgins@linux.dev, davidgow@google.com Cc: tzungbi@kernel.org, rmoar@google.com, rostedt@goodmis.org, mhiramat@kernel.org, naveen@kernel.org, anil.s.keshavamurthy@intel.com, davem@davemloft.net, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 3/7] platform/chrome: kunit: cros_ec_i2c: Use static stub instead Date: Tue, 20 May 2025 08:24:30 +0000 Message-ID: <20250520082435.2255639-4-tzungbi@kernel.org> X-Mailer: git-send-email 2.49.0.1101.gccaa498523-goog In-Reply-To: <20250520082435.2255639-1-tzungbi@kernel.org> References: <20250520082435.2255639-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For running the tests: $ ./tools/testing/kunit/kunit.py run \ --arch=3Dx86_64 \ --kconfig_add CONFIG_CHROME_PLATFORMS=3Dy \ --kconfig_add CONFIG_CROS_EC=3Dy \ --kconfig_add CONFIG_I2C=3Dy \ --kconfig_add CONFIG_CROS_EC_I2C=3Dy \ --kconfig_add CONFIG_CROS_KUNIT_EC_I2C_TEST=3Dy \ cros_ec_i2c* Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/Kconfig | 1 - drivers/platform/chrome/cros_ec.c | 6 ++++++ drivers/platform/chrome/cros_ec_i2c_test.c | 10 +++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kcon= fig index 5e0b44fb7ca7..bf10c0625be8 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -331,7 +331,6 @@ config CROS_KUNIT_EC_I2C_TEST tristate "Kunit tests for ChromeOS EC over I2C" if !KUNIT_ALL_TESTS depends on KUNIT && CROS_EC default KUNIT_ALL_TESTS - depends on KUNIT_FTRACE_STUBS depends on CROS_EC_I2C help Kunit tests for ChromeOS EC over I2C. diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cr= os_ec.c index 110771a8645e..62327fa0ff34 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -18,6 +18,8 @@ #include #include =20 +#include + #include "cros_ec.h" =20 static struct cros_ec_platform ec_p =3D { @@ -179,6 +181,8 @@ static int cros_ec_ready_event(struct notifier_block *n= b, */ int cros_ec_register(struct cros_ec_device *ec_dev) { + KUNIT_STATIC_STUB_REDIRECT(cros_ec_register, ec_dev); + struct device *dev =3D ec_dev->dev; int err =3D 0; =20 @@ -318,6 +322,8 @@ EXPORT_SYMBOL(cros_ec_register); */ void cros_ec_unregister(struct cros_ec_device *ec_dev) { + KUNIT_STATIC_STUB_REDIRECT(cros_ec_unregister, ec_dev); + platform_device_unregister(ec_dev->pd); platform_device_unregister(ec_dev->ec); mutex_destroy(&ec_dev->lock); diff --git a/drivers/platform/chrome/cros_ec_i2c_test.c b/drivers/platform/= chrome/cros_ec_i2c_test.c index 3a7f1a17d82d..5d41cdeec4b7 100644 --- a/drivers/platform/chrome/cros_ec_i2c_test.c +++ b/drivers/platform/chrome/cros_ec_i2c_test.c @@ -3,7 +3,7 @@ * Kunit tests for ChromeOS Embedded Controller I2C interface. */ #include -#include +#include =20 #include #include @@ -106,8 +106,8 @@ static int cros_ec_i2c_test_init(struct kunit *test) I2C_BOARD_INFO("cros-ec-i2c", I2C_ADDR), }; =20 - kunit_activate_ftrace_stub(test, cros_ec_register, fake_cros_ec_register); - kunit_activate_ftrace_stub(test, cros_ec_unregister, fake_cros_ec_unregis= ter); + kunit_activate_static_stub(test, cros_ec_register, fake_cros_ec_register); + kunit_activate_static_stub(test, cros_ec_unregister, fake_cros_ec_unregis= ter); =20 priv =3D kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv); @@ -142,8 +142,8 @@ static void cros_ec_i2c_test_exit(struct kunit *test) =20 i2c_del_adapter(priv->fake_adap); =20 - kunit_deactivate_ftrace_stub(test, cros_ec_register); - kunit_deactivate_ftrace_stub(test, cros_ec_unregister); + kunit_deactivate_static_stub(test, cros_ec_register); + kunit_deactivate_static_stub(test, cros_ec_unregister); } =20 static int cros_ec_i2c_test_cmd_xfer_init(struct kunit *test) --=20 2.49.0.1101.gccaa498523-goog From nobody Fri Dec 19 12:46:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D0D1826656F; Tue, 20 May 2025 08:25:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729524; cv=none; b=XZu9Fmdf/nTqZTPs5rqLwha9IQmrKJcBGR7nF/2uZyxfk4PZG5aRe4GiOo3u4ew40yo+82fZaBl6f+crrX7Y+g9buiRWM+fQvOByOPwEt7FLONElcXuerr0P0UAxckl/gEJgBAyvuUSrthVWIE/KWkracqbivIXNh4pPIVpUmQo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729524; c=relaxed/simple; bh=giFQ1SuT7sFCI5lClrg/bhLGk/DZw7docWUYgops1cQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Yjg9wmJE0HsPGqBrltJcABYUgx9bk3q+f/f74tQKEH32q1dspZ21RUENLfKN/NLNRBYGIx8UPvDc2kCxtXvPmd/eLRnTKFt7MzizvovJi5z8L+4nLvi0Sgu4p/G14MWq+mLMiZsJOnUn2/Q+yygx3FSJMomPNnbLdXgOh/NImW0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Z0qj9Gr9; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Z0qj9Gr9" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 24478C4CEE9; Tue, 20 May 2025 08:25:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1747729524; bh=giFQ1SuT7sFCI5lClrg/bhLGk/DZw7docWUYgops1cQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Z0qj9Gr9sZ/7uGSzCdDba4+gG0o5K0isLIlIUNCIRKCgouR2MedDm2gej5UWqnmff dfIjAEwsbGfqS/rkDP6o4BQ80DvF99guFax4RtDUZu2TUtCG9HOd9X46gCF/Q8pbBS iVwRM7B/z1wYRD8/PA0WRlcWNyN+6Kbei8rUWJISdXyXgcu+rNDdcdmYUXWSWNkbiM m2rtIbS7UcDc3GcXtl2gXY8eQszCo4IvPe0/J0ApeCG1ehruBAmEpogbFV3PjRdK1Y OHz/LQTB5Gel/CnwfNbHX0o7Yh8MevoFCW4i2g1DivMdeM0GA54hlaEjtwG31cZrkK c0tUEsvoCw0TA== From: Tzung-Bi Shih To: bleung@chromium.org, brendan.higgins@linux.dev, davidgow@google.com Cc: tzungbi@kernel.org, rmoar@google.com, rostedt@goodmis.org, mhiramat@kernel.org, naveen@kernel.org, anil.s.keshavamurthy@intel.com, davem@davemloft.net, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 4/7] platform/chrome: kunit: cros_ec_spi: Add tests with ftrace stub Date: Tue, 20 May 2025 08:24:31 +0000 Message-ID: <20250520082435.2255639-5-tzungbi@kernel.org> X-Mailer: git-send-email 2.49.0.1101.gccaa498523-goog In-Reply-To: <20250520082435.2255639-1-tzungbi@kernel.org> References: <20250520082435.2255639-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For running the tests: $ ./tools/testing/kunit/kunit.py run \ --arch=3Dx86_64 \ --kconfig_add CONFIG_FTRACE=3Dy \ --kconfig_add CONFIG_FUNCTION_TRACER=3Dy \ --kconfig_add CONFIG_MODULES=3Dy \ --kconfig_add CONFIG_DEBUG_KERNEL=3Dy \ --kconfig_add CONFIG_KALLSYMS_ALL=3Dy \ --kconfig_add CONFIG_LIVEPATCH=3Dy \ --kconfig_add CONFIG_KUNIT_FTRACE_STUBS=3Dy \ --kconfig_add CONFIG_CHROME_PLATFORMS=3Dy \ --kconfig_add CONFIG_CROS_EC=3Dy \ --kconfig_add CONFIG_SPI=3Dy \ --kconfig_add CONFIG_CROS_EC_SPI=3Dy \ --kconfig_add CONFIG_CROS_KUNIT_EC_SPI_TEST=3Dy \ cros_ec_spi* Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/Kconfig | 9 + drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_ec_spi_test.c | 361 +++++++++++++++++++++ 3 files changed, 371 insertions(+) create mode 100644 drivers/platform/chrome/cros_ec_spi_test.c diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kcon= fig index bf10c0625be8..aa13e871a31f 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -335,4 +335,13 @@ config CROS_KUNIT_EC_I2C_TEST help Kunit tests for ChromeOS EC over I2C. =20 +config CROS_KUNIT_EC_SPI_TEST + tristate "Kunit tests for ChromeOS EC over SPI" if !KUNIT_ALL_TESTS + depends on KUNIT && CROS_EC + default KUNIT_ALL_TESTS + depends on KUNIT_FTRACE_STUBS + depends on CROS_EC_SPI + help + Kunit tests for ChromeOS EC over SPI. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Mak= efile index 9808f25aea38..74649b8f21b3 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -45,3 +45,4 @@ obj-$(CONFIG_WILCO_EC) +=3D wilco_ec/ obj-$(CONFIG_CROS_KUNIT_EC_PROTO_TEST) +=3D cros_kunit_proto_test.o cros_kunit_proto_test-objs :=3D cros_ec_proto_test_util.o cros_ec_proto_t= est.o obj-$(CONFIG_CROS_KUNIT_EC_I2C_TEST) +=3D cros_ec_i2c_test.o +obj-$(CONFIG_CROS_KUNIT_EC_SPI_TEST) +=3D cros_ec_spi_test.o diff --git a/drivers/platform/chrome/cros_ec_spi_test.c b/drivers/platform/= chrome/cros_ec_spi_test.c new file mode 100644 index 000000000000..2a021569a726 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_spi_test.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kunit tests for ChromeOS Embedded Controller SPI interface. + */ +#include +#include + +#include +#include +#include +#include + +#include "cros_ec.h" + +#define BUFSIZE 128 +#define SPI_BUS_NUM 0x6 + +struct fake_xt { + u8 *buf; + size_t len; + + struct list_head list; +}; + +struct cros_ec_spi_test_priv { + struct class *fake_class; + struct device dev; + struct spi_controller *fake_ctlr; + struct spi_device *fake_spi_device; + + int fake_cros_ec_register_called; + struct cros_ec_device *ec_dev; + + int fake_cros_ec_unregister_called; + + struct list_head tx_head; + struct list_head rx_head; +}; + +static struct fake_xt *queue_fake_xt(struct kunit *test, struct list_head = *head, size_t len) +{ + struct fake_xt *xt =3D kunit_kmalloc(test, sizeof(*xt), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, xt); + + xt->buf =3D kunit_kzalloc(test, len, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, xt->buf); + xt->len =3D len; + list_add_tail(&xt->list, head); + return xt; +} + +static struct fake_xt *dequeue_fake_xt(struct list_head *head) +{ + struct fake_xt *xt =3D list_first_entry(head, struct fake_xt, list); + list_del(&xt->list); + return xt; +} + +static int fake_cros_ec_register(struct cros_ec_device *ec_dev) +{ + struct kunit *test =3D current->kunit_test; + struct cros_ec_spi_test_priv *priv =3D test->priv; + + priv->fake_cros_ec_register_called +=3D 1; + + priv->ec_dev =3D ec_dev; + priv->ec_dev->din_size =3D BUFSIZE; + priv->ec_dev->din =3D kunit_kmalloc(test, priv->ec_dev->din_size, GFP_KER= NEL); + KUNIT_ASSERT_NOT_NULL(test, priv->ec_dev->din); + priv->ec_dev->dout_size =3D BUFSIZE; + priv->ec_dev->dout =3D kunit_kmalloc(test, priv->ec_dev->dout_size, GFP_K= ERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv->ec_dev->dout); + return 0; +} + +static void fake_cros_ec_unregister(struct cros_ec_device *ec_dev) +{ + struct kunit *test =3D current->kunit_test; + struct cros_ec_spi_test_priv *priv =3D test->priv; + + priv->fake_cros_ec_unregister_called +=3D 1; +} + +static int fake_spi_transfer_one_message(struct spi_controller *ctlr, stru= ct spi_message *msg) +{ + /* Note: the function's context is another kernel thread. */ + struct kunit *test =3D spi_controller_get_devdata(ctlr); + struct cros_ec_spi_test_priv *priv =3D test->priv; + struct spi_transfer *t; + + list_for_each_entry(t, &msg->transfers, transfer_list) { + if (t->tx_buf) { + struct fake_xt *tx =3D queue_fake_xt(test, &priv->tx_head, t->len); + memcpy(tx->buf, t->tx_buf, t->len); + } + + if (t->rx_buf && !list_empty(&priv->rx_head)) { + struct fake_xt *rx =3D dequeue_fake_xt(&priv->rx_head); + memcpy(t->rx_buf, rx->buf, min(t->len, rx->len)); + } + } + + msg->status =3D 0; + spi_finalize_current_message(ctlr); + return 0; +} + +static int find_target_driver(struct device_driver *drv, void *data) +{ + struct device_driver **pdrv =3D data; + + if (strcmp(drv->name, "cros-ec-spi") =3D=3D 0) + *pdrv =3D drv; + return 0; +} + +static int cros_ec_spi_test_init(struct kunit *test) +{ + struct cros_ec_spi_test_priv *priv; + struct spi_board_info board_info =3D {}; + int ret; + struct device_driver *drv; + enum probe_type orig; + + kunit_activate_ftrace_stub(test, cros_ec_register, fake_cros_ec_register); + kunit_activate_ftrace_stub(test, cros_ec_unregister, fake_cros_ec_unregis= ter); + + sized_strscpy(board_info.modalias, "cros-ec-spi", SPI_NAME_SIZE); + + priv =3D kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv); + test->priv =3D priv; + + priv->fake_class =3D class_create("fake-class"); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->fake_class); + + priv->dev.class =3D priv->fake_class; + priv->dev.parent =3D NULL; + dev_set_name(&priv->dev, "cros-ec-spi-test-dev"); + ret =3D device_register(&priv->dev); + if (ret) + put_device(&priv->dev); + KUNIT_ASSERT_EQ(test, ret, 0); + + priv->fake_ctlr =3D spi_alloc_host(&priv->dev, 0); + KUNIT_ASSERT_NOT_NULL(test, priv->fake_ctlr); + spi_controller_set_devdata(priv->fake_ctlr, test); + + priv->fake_ctlr->transfer_one_message =3D fake_spi_transfer_one_message; + priv->fake_ctlr->bus_num =3D SPI_BUS_NUM; + ret =3D spi_register_controller(priv->fake_ctlr); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* + * Force to use synchronous probe so that the upcoming SPI device gets + * probed correctly and synchronously. + */ + ret =3D bus_for_each_drv(&spi_bus_type, NULL, &drv, find_target_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_ASSERT_NOT_NULL(test, drv); + orig =3D drv->probe_type; + drv->probe_type =3D PROBE_FORCE_SYNCHRONOUS; + + priv->fake_spi_device =3D spi_new_device(priv->fake_ctlr, &board_info); + /* Restore to original probe type. */ + drv->probe_type =3D orig; + KUNIT_ASSERT_NOT_NULL(test, priv->fake_spi_device); + + KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_register_called, 1); + KUNIT_ASSERT_NOT_NULL(test, priv->ec_dev); + KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_unregister_called, 0); + + INIT_LIST_HEAD(&priv->tx_head); + INIT_LIST_HEAD(&priv->rx_head); + + return 0; +} + +static void cros_ec_spi_test_exit(struct kunit *test) +{ + struct cros_ec_spi_test_priv *priv =3D test->priv; + + spi_unregister_device(priv->fake_spi_device); + KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_unregister_called, 1); + + spi_unregister_controller(priv->fake_ctlr); + device_del(&priv->dev); + class_destroy(priv->fake_class); + + kunit_deactivate_ftrace_stub(test, cros_ec_register); + kunit_deactivate_ftrace_stub(test, cros_ec_unregister); +} + +static int cros_ec_spi_test_cmd_xfer_init(struct kunit *test) +{ + struct cros_ec_spi_test_priv *priv; + + cros_ec_spi_test_init(test); + priv =3D test->priv; + priv->ec_dev->proto_version =3D 2; + return 0; +} + +static void cros_ec_spi_test_cmd_xfer_normal(struct kunit *test) +{ + struct cros_ec_spi_test_priv *priv =3D test->priv; + struct cros_ec_command *msg; + struct fake_xt *xt; + int ret, i, len; + u8 sum; + + msg =3D kunit_kzalloc(test, sizeof(*msg) + 2 /* max(outsize, insize) */, = GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, msg); + msg->version =3D 0x1000; + msg->command =3D 0x1001; + msg->outsize =3D 2; + msg->insize =3D 1; + msg->data[0] =3D 0xbe; + msg->data[1] =3D 0xef; + + /* + * cros_ec_spi sends out the msg and tries to read further EC_MSG_TX_PROT= O_BYTES + + * msg->outsize bytes to verify if the EC can proceed the command by look= ing for + * EC_SPI_PAST_END, EC_SPI_RX_BAD_DATA, or EC_SPI_NOT_READY. It's fine e= ven if we + * prepare 0 length data. + */ + queue_fake_xt(test, &priv->rx_head, 0); + + len =3D 1 /* for EC_SPI_FRAME_START */ + EC_MSG_RX_PROTO_BYTES + msg->ins= ize; + xt =3D queue_fake_xt(test, &priv->rx_head, len); + xt->buf[0] =3D EC_SPI_FRAME_START; + xt->buf[1] =3D 0 /* result */; + xt->buf[2] =3D msg->insize; + xt->buf[3] =3D 0xaa; + for (sum =3D 0, i =3D 1; i < len - 1; ++i) + sum +=3D xt->buf[i]; + xt->buf[len - 1] =3D sum; + + ret =3D priv->ec_dev->cmd_xfer(priv->ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, 1 /* insize */); + KUNIT_EXPECT_EQ(test, msg->result, 0); + KUNIT_EXPECT_EQ(test, msg->data[0], 0xaa); + + KUNIT_EXPECT_FALSE(test, list_empty(&priv->tx_head)); + xt =3D dequeue_fake_xt(&priv->tx_head); + KUNIT_EXPECT_TRUE(test, list_empty(&priv->tx_head)); + KUNIT_EXPECT_NOT_NULL(test, xt); + KUNIT_EXPECT_NOT_NULL(test, xt->buf); + KUNIT_EXPECT_EQ(test, xt->len, EC_MSG_TX_PROTO_BYTES + 2 /* outsize */); + KUNIT_EXPECT_EQ(test, xt->buf[0], (u8)(EC_CMD_VERSION0 + 0x1000)); + KUNIT_EXPECT_EQ(test, xt->buf[1], (u8)0x1001); + KUNIT_EXPECT_EQ(test, xt->buf[2], 2 /* outsize */); + KUNIT_EXPECT_EQ(test, xt->buf[3], 0xbe); + KUNIT_EXPECT_EQ(test, xt->buf[4], 0xef); + for (sum =3D 0, i =3D 0; i < EC_MSG_TX_HEADER_BYTES + 2 /* outsize */; ++= i) + sum +=3D xt->buf[i]; + KUNIT_EXPECT_EQ(test, xt->buf[EC_MSG_TX_HEADER_BYTES + 2 /* outsize */], = sum); +} + +static struct kunit_case cros_ec_spi_test_cmd_xfer_cases[] =3D { + KUNIT_CASE(cros_ec_spi_test_cmd_xfer_normal), + {} +}; + +static struct kunit_suite cros_ec_spi_test_cmd_xfer_suite =3D { + .name =3D "cros_ec_spi_test_cmd_xfer_suite", + .init =3D cros_ec_spi_test_cmd_xfer_init, + .exit =3D cros_ec_spi_test_exit, + .test_cases =3D cros_ec_spi_test_cmd_xfer_cases, +}; + +static int cros_ec_spi_test_pkt_xfer_init(struct kunit *test) +{ + struct cros_ec_spi_test_priv *priv; + + cros_ec_spi_test_init(test); + priv =3D test->priv; + priv->ec_dev->proto_version =3D 3; + return 0; +} + +static void cros_ec_spi_test_pkt_xfer_normal(struct kunit *test) +{ + struct cros_ec_spi_test_priv *priv =3D test->priv; + struct cros_ec_command *msg; + struct fake_xt *xt; + struct ec_host_request *request; + struct ec_host_response *response; + int ret, i, len; + u8 sum; + + msg =3D kunit_kzalloc(test, sizeof(*msg) + 2 /* max(outsize, insize) */, = GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, msg); + msg->version =3D 0x1000; + msg->command =3D 0x1001; + msg->outsize =3D 2; + msg->insize =3D 1; + msg->data[0] =3D 0xbe; + msg->data[1] =3D 0xef; + + /* + * cros_ec_spi sends out the msg and tries to read further EC_MSG_TX_PROT= O_BYTES + + * msg->outsize bytes to verify if the EC can proceed the command by look= ing for + * EC_SPI_PAST_END, EC_SPI_RX_BAD_DATA, or EC_SPI_NOT_READY. It's fine e= ven if we + * prepare 0 length data. + */ + queue_fake_xt(test, &priv->rx_head, 0); + + len =3D 1 /* for EC_SPI_FRAME_START */ + sizeof(*response) + msg->insize; + xt =3D queue_fake_xt(test, &priv->rx_head, len); + xt->buf[0] =3D EC_SPI_FRAME_START; + response =3D (struct ec_host_response *)&xt->buf[1]; + response->struct_version =3D EC_HOST_RESPONSE_VERSION; + response->result =3D 0; + response->data_len =3D msg->insize; + xt->buf[sizeof(*response) + msg->insize] =3D 0xaa; + for (sum =3D 0, i =3D 1; i < len; ++i) + sum +=3D xt->buf[i]; + response->checksum =3D -sum; + + ret =3D priv->ec_dev->pkt_xfer(priv->ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, 1 /* insize */); + KUNIT_EXPECT_EQ(test, msg->result, 0); + KUNIT_EXPECT_EQ(test, msg->data[0], 0xaa); + + KUNIT_EXPECT_FALSE(test, list_empty(&priv->tx_head)); + xt =3D dequeue_fake_xt(&priv->tx_head); + KUNIT_EXPECT_TRUE(test, list_empty(&priv->tx_head)); + KUNIT_EXPECT_NOT_NULL(test, xt); + KUNIT_EXPECT_NOT_NULL(test, xt->buf); + KUNIT_EXPECT_EQ(test, xt->len, sizeof(*request) + 2 /* outsize */); + request =3D (struct ec_host_request *)xt->buf; + KUNIT_EXPECT_EQ(test, request->struct_version, EC_HOST_REQUEST_VERSION); + KUNIT_EXPECT_EQ(test, request->command_version, (u8)0x1000); + KUNIT_EXPECT_EQ(test, request->command, 0x1001); + KUNIT_EXPECT_EQ(test, request->data_len, 2 /* outsize */); + KUNIT_EXPECT_EQ(test, xt->buf[sizeof(*request)], 0xbe); + KUNIT_EXPECT_EQ(test, xt->buf[sizeof(*request) + 1], 0xef); + for (sum =3D 0, i =3D 0; i < sizeof(*request) + 2 /* outsize */; ++i) + sum +=3D xt->buf[i]; + KUNIT_EXPECT_EQ(test, sum, 0); +} + +static struct kunit_case cros_ec_spi_test_pkt_xfer_cases[] =3D { + KUNIT_CASE(cros_ec_spi_test_pkt_xfer_normal), + {} +}; + +static struct kunit_suite cros_ec_spi_test_pkt_xfer_suite =3D { + .name =3D "cros_ec_spi_test_pkt_xfer_suite", + .init =3D cros_ec_spi_test_pkt_xfer_init, + .exit =3D cros_ec_spi_test_exit, + .test_cases =3D cros_ec_spi_test_pkt_xfer_cases, +}; + +kunit_test_suites( + &cros_ec_spi_test_cmd_xfer_suite, + &cros_ec_spi_test_pkt_xfer_suite, +); + +MODULE_LICENSE("GPL"); --=20 2.49.0.1101.gccaa498523-goog From nobody Fri Dec 19 12:46:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 62528266594; Tue, 20 May 2025 08:25:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729527; cv=none; b=lWrLIKg3d11xgxkZFYEOCQyHJbBuaDix58tL127tX0YP1Q3PhySEMelO29Unn7caeUpDZfpAcveMQMZkN6ahUn32S+rw2us9aoTsfTsg9eG8Cvd0VcftaZFrTv3+79DAW6eCcZnhZh5AJvSkYKqmaFdB5JWcHqZYiCJdyK6c9oU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729527; c=relaxed/simple; bh=+6EIVL7ThX53vHz5L3JTUOBo0sIuRo6+hjEqxH0fBc4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cmHiWUN95zPn8NdDwszPQg4YI6ZNg7ulaTH5PtvvVWcK2WMxGwSszzJ0dqlf4olUn170PDchfyHKNpNsBgyHtob4Xwsx/sWxB/ozWsZ4dh+Ea51kwKgeN+y3RLtT3uaDwEWuA6jE3Nv38WwGWYeGWyZ17nWPdbF0gs8Gh7lZ2/8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=tnQDSbdo; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="tnQDSbdo" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A898AC4CEF0; Tue, 20 May 2025 08:25:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1747729526; bh=+6EIVL7ThX53vHz5L3JTUOBo0sIuRo6+hjEqxH0fBc4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tnQDSbdozV4AvdMgXyfkOoq41Q5I5qRVREKTf4Q9I6Hwr4/RW5fKsDC4NoLquJXGx qI+wgDxmLoe95stKSfr1OEgWVDRRILLjd/GanzdaFrqnfR7AsV6lAJ85dgjCsd6d/8 k/oa6mRol6vdGcA2CfFB6L/ihFqhIyJoRwFYJZQSnUJgcBzQA4+HI3SocOMUaetab0 +SuYejWOmillTMGk3OIgbbbNSxjETQe+pz4ckmqxaNECmz2ftKprczx/EA3wWj5XDU 7g9qqWZR5jRjfVokFDVUkB6XjOW7qaBti2tRirzkDRSR/eOHd9du/EKdjGIN0NNW3U aDzj0tFVzLHtw== From: Tzung-Bi Shih To: bleung@chromium.org, brendan.higgins@linux.dev, davidgow@google.com Cc: tzungbi@kernel.org, rmoar@google.com, rostedt@goodmis.org, mhiramat@kernel.org, naveen@kernel.org, anil.s.keshavamurthy@intel.com, davem@davemloft.net, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 5/7] platform/chrome: kunit: cros_ec_spi: Call .probe() directly Date: Tue, 20 May 2025 08:24:32 +0000 Message-ID: <20250520082435.2255639-6-tzungbi@kernel.org> X-Mailer: git-send-email 2.49.0.1101.gccaa498523-goog In-Reply-To: <20250520082435.2255639-1-tzungbi@kernel.org> References: <20250520082435.2255639-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Get the spi_driver and call .probe() directly. For running the tests: $ ./tools/testing/kunit/kunit.py run \ --arch=3Dx86_64 \ --kconfig_add CONFIG_FTRACE=3Dy \ --kconfig_add CONFIG_FUNCTION_TRACER=3Dy \ --kconfig_add CONFIG_MODULES=3Dy \ --kconfig_add CONFIG_DEBUG_KERNEL=3Dy \ --kconfig_add CONFIG_KALLSYMS_ALL=3Dy \ --kconfig_add CONFIG_LIVEPATCH=3Dy \ --kconfig_add CONFIG_KUNIT_FTRACE_STUBS=3Dy \ --kconfig_add CONFIG_CHROME_PLATFORMS=3Dy \ --kconfig_add CONFIG_CROS_EC=3Dy \ --kconfig_add CONFIG_SPI=3Dy \ --kconfig_add CONFIG_CROS_EC_SPI=3Dy \ --kconfig_add CONFIG_CROS_KUNIT_EC_SPI_TEST=3Dy \ cros_ec_spi* Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/cros_ec_spi_test.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_spi_test.c b/drivers/platform/= chrome/cros_ec_spi_test.c index 2a021569a726..52dea75ecabf 100644 --- a/drivers/platform/chrome/cros_ec_spi_test.c +++ b/drivers/platform/chrome/cros_ec_spi_test.c @@ -27,6 +27,7 @@ struct cros_ec_spi_test_priv { struct device dev; struct spi_controller *fake_ctlr; struct spi_device *fake_spi_device; + struct spi_driver *spi_drv; =20 int fake_cros_ec_register_called; struct cros_ec_device *ec_dev; @@ -117,16 +118,12 @@ static int find_target_driver(struct device_driver *d= rv, void *data) static int cros_ec_spi_test_init(struct kunit *test) { struct cros_ec_spi_test_priv *priv; - struct spi_board_info board_info =3D {}; int ret; struct device_driver *drv; - enum probe_type orig; =20 kunit_activate_ftrace_stub(test, cros_ec_register, fake_cros_ec_register); kunit_activate_ftrace_stub(test, cros_ec_unregister, fake_cros_ec_unregis= ter); =20 - sized_strscpy(board_info.modalias, "cros-ec-spi", SPI_NAME_SIZE); - priv =3D kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv); test->priv =3D priv; @@ -151,21 +148,17 @@ static int cros_ec_spi_test_init(struct kunit *test) ret =3D spi_register_controller(priv->fake_ctlr); KUNIT_ASSERT_EQ(test, ret, 0); =20 - /* - * Force to use synchronous probe so that the upcoming SPI device gets - * probed correctly and synchronously. - */ ret =3D bus_for_each_drv(&spi_bus_type, NULL, &drv, find_target_driver); KUNIT_ASSERT_EQ(test, ret, 0); KUNIT_ASSERT_NOT_NULL(test, drv); - orig =3D drv->probe_type; - drv->probe_type =3D PROBE_FORCE_SYNCHRONOUS; =20 - priv->fake_spi_device =3D spi_new_device(priv->fake_ctlr, &board_info); - /* Restore to original probe type. */ - drv->probe_type =3D orig; + priv->fake_spi_device =3D spi_alloc_device(priv->fake_ctlr); KUNIT_ASSERT_NOT_NULL(test, priv->fake_spi_device); =20 + priv->spi_drv =3D container_of(drv, struct spi_driver, driver); + ret =3D priv->spi_drv->probe(priv->fake_spi_device); + KUNIT_ASSERT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_register_called, 1); KUNIT_ASSERT_NOT_NULL(test, priv->ec_dev); KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_unregister_called, 0); @@ -180,9 +173,10 @@ static void cros_ec_spi_test_exit(struct kunit *test) { struct cros_ec_spi_test_priv *priv =3D test->priv; =20 - spi_unregister_device(priv->fake_spi_device); + priv->spi_drv->remove(priv->fake_spi_device); KUNIT_EXPECT_EQ(test, priv->fake_cros_ec_unregister_called, 1); =20 + spi_dev_put(priv->fake_spi_device); spi_unregister_controller(priv->fake_ctlr); device_del(&priv->dev); class_destroy(priv->fake_class); --=20 2.49.0.1101.gccaa498523-goog From nobody Fri Dec 19 12:46:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EA5AF2673B9; Tue, 20 May 2025 08:25:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729530; cv=none; b=K+5LciKd5DJr4zcZ/NQBlKelA38EYI3iF5wEt1mCuYsqhklQ+miL4u9yUNuS4nBQsHfAwADKzbT1VBMdEjLTAFzCLm3MPmcv9nvb/6Dm1VaiAzU9YpmUSzb09kOZaNd3QF/srvwwc2/eUaNanaB91M5T2nySIhLEuCm8Je2/FuU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729530; c=relaxed/simple; bh=VwHSk8MoYaaONWTt5aKNb+9EuN0bahyPpgTUvEZBz4s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NVV+mOIy+o+dVI0w6x1bMAAAbhcnTCpqb/jjbzDr8YdfoIUNqxWjsepoPCmtNfN79YTepKkvSHqzD08XdZIa4qzg8x00pCPq7ZeODqUnQurkJgQczDVa0gv4mNKHbP5vaN+LbSiqrXmfG2ksYU8gIdNlbKtcvaJq5VW0nI56lVk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ky4A4Ctf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ky4A4Ctf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 38962C4CEEF; Tue, 20 May 2025 08:25:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1747729529; bh=VwHSk8MoYaaONWTt5aKNb+9EuN0bahyPpgTUvEZBz4s=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ky4A4CtfyytMm4EZKwN2pFBoplenp40uWUZDqWIy7EcQ5KzsNaYiCR0ckq8ErOuGo 7L7IT5edJPSP5J6klwgsm84DozTjGie09nRTocSDERuVrZOYOFDsfaWIQ/th5pC7Ag 5SIwxcH9fwJYRHcRzGRWauzMii7PhmgHYiFyQz6I87hVX3P5g6QnPmch8LEHkOjyY6 mqgEisTw4WDaTON+DA5WO5xovqS17uMADZ5UvdgISTeugnDqGtyOsEFjqOkNTH6rT+ E3p6niRgfbfhOWz/bfzmXvavojmk15ji2/DO2TPPFXG6hxLZ/PoihrZmfN6DcgOIpp rJD54ndJ63T5A== From: Tzung-Bi Shih To: bleung@chromium.org, brendan.higgins@linux.dev, davidgow@google.com Cc: tzungbi@kernel.org, rmoar@google.com, rostedt@goodmis.org, mhiramat@kernel.org, naveen@kernel.org, anil.s.keshavamurthy@intel.com, davem@davemloft.net, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 6/7] kunit: Expose 'kprobes stub' API to redirect functions Date: Tue, 20 May 2025 08:24:33 +0000 Message-ID: <20250520082435.2255639-7-tzungbi@kernel.org> X-Mailer: git-send-email 2.49.0.1101.gccaa498523-goog In-Reply-To: <20250520082435.2255639-1-tzungbi@kernel.org> References: <20250520082435.2255639-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add function redirection API based on Kprobes. Suggested-by: Masami Hiramatsu Signed-off-by: Tzung-Bi Shih --- include/kunit/kprobes_stub.h | 19 ++++++ lib/kunit/Kconfig | 7 +++ lib/kunit/Makefile | 4 ++ lib/kunit/kprobes_stub.c | 113 +++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+) create mode 100644 include/kunit/kprobes_stub.h create mode 100644 lib/kunit/kprobes_stub.c diff --git a/include/kunit/kprobes_stub.h b/include/kunit/kprobes_stub.h new file mode 100644 index 000000000000..af77c86fe48e --- /dev/null +++ b/include/kunit/kprobes_stub.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _KUNIT_KPROBES_STUB_H +#define _KUNIT_KPROBES_STUB_H + +struct kunit; + +#define kunit_activate_kprobes_stub(test, func, replacement) do { \ + typecheck_fn(typeof(&func), replacement); \ + __kunit_activate_kprobes_stub(test, #func, func, replacement); \ +} while (0) + +void __kunit_activate_kprobes_stub(struct kunit *test, + const char *name, + void *real_fn_addr, + void *replacement_addr); + +void kunit_deactivate_kprobes_stub(struct kunit *test, void *real_fn_addr); + +#endif /* _KUNIT_KPROBES_STUB_H */ diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig index 933fda1df5c3..13fdd3471060 100644 --- a/lib/kunit/Kconfig +++ b/lib/kunit/Kconfig @@ -104,4 +104,11 @@ config KUNIT_FTRACE_STUBS NOTE: this does not work on all architectures (like UML, arm64) and relies on a lot of magic (see the dependencies list). =20 +config KUNIT_KPROBES_STUBS + bool "Support for stubbing out functions in KUnit tests with kprobes" + depends on KPROBES + help + Builds support for stubbing out functions for the duration of KUnit + test cases or suites using kprobes. + endif # KUNIT diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile index 0ecb255576e2..727ce86eb61f 100644 --- a/lib/kunit/Makefile +++ b/lib/kunit/Makefile @@ -33,3 +33,7 @@ obj-$(CONFIG_KUNIT_EXAMPLE_TEST) +=3D kunit-example-test.o ifeq ($(CONFIG_KUNIT_FTRACE_STUBS),y) kunit-objs +=3D ftrace_stub.o endif + +ifeq ($(CONFIG_KUNIT_KPROBES_STUBS),y) +kunit-objs +=3D kprobes_stub.o +endif diff --git a/lib/kunit/kprobes_stub.c b/lib/kunit/kprobes_stub.c new file mode 100644 index 000000000000..95f1dcba346b --- /dev/null +++ b/lib/kunit/kprobes_stub.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit function redirection (kprobes stubbing) API. + */ + +#include +#include + +#include + +struct kunit_kprobes_stub_ctx { + unsigned long real_fn_addr; + unsigned long replacement_addr; + struct kprobe kp; +}; + +static void __kunit_kprobes_stub_resource_free(struct kunit_resource *res) +{ + struct kunit_kprobes_stub_ctx *ctx =3D res->data; + + unregister_kprobe(&ctx->kp); + kfree(ctx); +} + +static int kprobe_handler(struct kprobe *kp, struct pt_regs *regs) +{ + struct kunit_kprobes_stub_ctx *ctx =3D container_of(kp, struct kunit_kpro= bes_stub_ctx, kp); + + instruction_pointer_set(regs, ctx->replacement_addr); + return 1; +} + +/* Matching function for kunit_find_resource(). match_data is real_fn_add= r. */ +static bool __kunit_kprobes_stub_resource_match(struct kunit *test, + struct kunit_resource *res, + void *match_real_fn_addr) +{ + struct kunit_kprobes_stub_ctx *ctx =3D res->data; + + /* Make sure the resource is a kprobes stub resource. */ + if (res->free !=3D &__kunit_kprobes_stub_resource_free) + return false; + + return ctx->real_fn_addr =3D=3D (unsigned long)match_real_fn_addr; +} + +void __kunit_activate_kprobes_stub(struct kunit *test, + const char *name, + void *real_fn_addr, + void *replacement_addr) +{ + struct kunit_kprobes_stub_ctx *ctx; + struct kunit_resource *res; + + KUNIT_ASSERT_PTR_NE_MSG(test, real_fn_addr, NULL, + "Tried to activate a stub for function NULL"); + + /* If the replacement address is NULL, deactivate the stub. */ + if (!replacement_addr) { + kunit_deactivate_kprobes_stub(test, real_fn_addr); + return; + } + + /* Look up any existing stubs for this function, and replace them. */ + res =3D kunit_find_resource(test, + __kunit_kprobes_stub_resource_match, + real_fn_addr); + if (res) { + ctx =3D res->data; + ctx->replacement_addr =3D (unsigned long)replacement_addr; + + /* We got an extra reference from find_resource(), so put it. */ + kunit_put_resource(res); + } else { + ctx =3D kzalloc(sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL_MSG(test, ctx, "Failed to allocate kunit st= ub for %s", + name); + + ctx->real_fn_addr =3D (unsigned long)real_fn_addr; + ctx->replacement_addr =3D (unsigned long)replacement_addr; + + ctx->kp.addr =3D real_fn_addr; + ctx->kp.pre_handler =3D kprobe_handler; + KUNIT_ASSERT_EQ_MSG(test, register_kprobe(&ctx->kp), 0, + "Failed to allocate kunit stub for %s", name); + + kunit_alloc_resource(test, NULL, + __kunit_kprobes_stub_resource_free, + GFP_KERNEL, ctx); + } +} +EXPORT_SYMBOL_GPL(__kunit_activate_kprobes_stub); + +void kunit_deactivate_kprobes_stub(struct kunit *test, void *real_fn_addr) +{ + struct kunit_resource *res; + + KUNIT_ASSERT_PTR_NE_MSG(test, real_fn_addr, NULL, "Tried to deactivate a = NULL stub."); + + res =3D kunit_find_resource(test, + __kunit_kprobes_stub_resource_match, + real_fn_addr); + KUNIT_ASSERT_PTR_NE_MSG(test, res, NULL, + "Tried to deactivate a nonexistent stub."); + + /* + * Free the stub. We 'put' twice, as we got a reference + * from kunit_find_resource() + */ + kunit_remove_resource(test, res); + kunit_put_resource(res); +} +EXPORT_SYMBOL_GPL(kunit_deactivate_kprobes_stub); --=20 2.49.0.1101.gccaa498523-goog From nobody Fri Dec 19 12:46:35 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7501C267702; Tue, 20 May 2025 08:25:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729532; cv=none; b=Yr4rzQkST8BQ6a+CUQ3dm+WEpQ+oTE1QGajbU54U3r/DQ2e/2yuEs2Wb+w63jdDhZHT+QKJk/h1FYzp2c3HZCTYuNbH+rVXd0sZzP7kwaLeZv83Z8/x4PSS3mGkvGJsVRFwsSawI5r0cp53+eZChhakhLWypGBSrEfpScrw4otY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1747729532; c=relaxed/simple; bh=nBLwYdLCrdKN8wRv7ArzFbqRn5yd+GRLAnV4S2iIl/w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=LjIjtGyYUPStqASVCYfN586idW8nywvrux9C2yrm9vwo2ypwFinLd7YhYnIRASWhFQLRYD5Px1m+M97UjBccEN4jBlSACns42/n7V+4j7A2VpmQnD820ryWBCIUk4clyqLtZUlYulmkPD4z6TcLWIPKmyLKu6UqcI8rExxr8jls= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UbZuTXHP; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UbZuTXHP" Received: by smtp.kernel.org (Postfix) with ESMTPSA id BC02FC4CEF0; Tue, 20 May 2025 08:25:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1747729531; bh=nBLwYdLCrdKN8wRv7ArzFbqRn5yd+GRLAnV4S2iIl/w=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UbZuTXHPjSMU8w6PSVIPcLhHf3wW8xc3x6FhbA9MDjKaVOBpg/qasfeExFkxPaz78 XxUo0xIt3rJd+gvg4sH91FqOqjMqlDalB0O/oL+bwmS0242Izjxe0zS5jkTVjBw12l VlL5Ad4N4ujQhOCExsEpuXTAtep7YkVg1C5SeViXPaOvUV4DWo+j9/XpW8xK6+M1pD elAD7VWTGl/xt5nGn+y9OoeFxLqDIuzpP29WFZbFjkolvbyNlvIE/MRMT9gUumcb1q Y5/fkQqgRTl1kMoghMKY8A3z1sdL/LvMexP1wjKJxD9ys6k1or6wrnGPUSz61b0qID 9kjWbIDCuDuTQ== From: Tzung-Bi Shih To: bleung@chromium.org, brendan.higgins@linux.dev, davidgow@google.com Cc: tzungbi@kernel.org, rmoar@google.com, rostedt@goodmis.org, mhiramat@kernel.org, naveen@kernel.org, anil.s.keshavamurthy@intel.com, davem@davemloft.net, chrome-platform@lists.linux.dev, linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com, linux-trace-kernel@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFC PATCH 7/7] platform/chrome: kunit: cros_ec_spi: Use kprobes stub instead Date: Tue, 20 May 2025 08:24:34 +0000 Message-ID: <20250520082435.2255639-8-tzungbi@kernel.org> X-Mailer: git-send-email 2.49.0.1101.gccaa498523-goog In-Reply-To: <20250520082435.2255639-1-tzungbi@kernel.org> References: <20250520082435.2255639-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" For running the tests: $ ./tools/testing/kunit/kunit.py run \ --arch=3Dx86_64 \ --kconfig_add CONFIG_KPROBES=3Dy \ --kconfig_add CONFIG_KUNIT_KPROBES_STUBS=3Dy \ --kconfig_add CONFIG_CHROME_PLATFORMS=3Dy \ --kconfig_add CONFIG_CROS_EC=3Dy \ --kconfig_add CONFIG_SPI=3Dy \ --kconfig_add CONFIG_CROS_EC_SPI=3Dy \ --kconfig_add CONFIG_CROS_KUNIT_EC_SPI_TEST=3Dy \ cros_ec_spi* Signed-off-by: Tzung-Bi Shih --- drivers/platform/chrome/Kconfig | 2 +- drivers/platform/chrome/cros_ec_spi_test.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kcon= fig index aa13e871a31f..aacce3323384 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -339,7 +339,7 @@ config CROS_KUNIT_EC_SPI_TEST tristate "Kunit tests for ChromeOS EC over SPI" if !KUNIT_ALL_TESTS depends on KUNIT && CROS_EC default KUNIT_ALL_TESTS - depends on KUNIT_FTRACE_STUBS + depends on KUNIT_KPROBES_STUBS depends on CROS_EC_SPI help Kunit tests for ChromeOS EC over SPI. diff --git a/drivers/platform/chrome/cros_ec_spi_test.c b/drivers/platform/= chrome/cros_ec_spi_test.c index 52dea75ecabf..74fcbd37b87b 100644 --- a/drivers/platform/chrome/cros_ec_spi_test.c +++ b/drivers/platform/chrome/cros_ec_spi_test.c @@ -3,7 +3,7 @@ * Kunit tests for ChromeOS Embedded Controller SPI interface. */ #include -#include +#include =20 #include #include @@ -121,8 +121,8 @@ static int cros_ec_spi_test_init(struct kunit *test) int ret; struct device_driver *drv; =20 - kunit_activate_ftrace_stub(test, cros_ec_register, fake_cros_ec_register); - kunit_activate_ftrace_stub(test, cros_ec_unregister, fake_cros_ec_unregis= ter); + kunit_activate_kprobes_stub(test, cros_ec_register, fake_cros_ec_register= ); + kunit_activate_kprobes_stub(test, cros_ec_unregister, fake_cros_ec_unregi= ster); =20 priv =3D kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, priv); @@ -181,8 +181,8 @@ static void cros_ec_spi_test_exit(struct kunit *test) device_del(&priv->dev); class_destroy(priv->fake_class); =20 - kunit_deactivate_ftrace_stub(test, cros_ec_register); - kunit_deactivate_ftrace_stub(test, cros_ec_unregister); + kunit_deactivate_kprobes_stub(test, cros_ec_register); + kunit_deactivate_kprobes_stub(test, cros_ec_unregister); } =20 static int cros_ec_spi_test_cmd_xfer_init(struct kunit *test) --=20 2.49.0.1101.gccaa498523-goog