xref: /openbmc/linux/include/kunit/static_stub.h (revision e047c5eaa76324575e1f95664be4c74ce0e2571b)
1*e047c5eaSDavid Gow /* SPDX-License-Identifier: GPL-2.0 */
2*e047c5eaSDavid Gow /*
3*e047c5eaSDavid Gow  * KUnit function redirection (static stubbing) API.
4*e047c5eaSDavid Gow  *
5*e047c5eaSDavid Gow  * Copyright (C) 2022, Google LLC.
6*e047c5eaSDavid Gow  * Author: David Gow <davidgow@google.com>
7*e047c5eaSDavid Gow  */
8*e047c5eaSDavid Gow #ifndef _KUNIT_STATIC_STUB_H
9*e047c5eaSDavid Gow #define _KUNIT_STATIC_STUB_H
10*e047c5eaSDavid Gow 
11*e047c5eaSDavid Gow #if !IS_ENABLED(CONFIG_KUNIT)
12*e047c5eaSDavid Gow 
13*e047c5eaSDavid Gow /* If CONFIG_KUNIT is not enabled, these stubs quietly disappear. */
14*e047c5eaSDavid Gow #define KUNIT_TRIGGER_STATIC_STUB(real_fn_name, args...) do {} while (0)
15*e047c5eaSDavid Gow 
16*e047c5eaSDavid Gow #else
17*e047c5eaSDavid Gow 
18*e047c5eaSDavid Gow #include <kunit/test.h>
19*e047c5eaSDavid Gow #include <kunit/test-bug.h>
20*e047c5eaSDavid Gow 
21*e047c5eaSDavid Gow #include <linux/compiler.h> /* for {un,}likely() */
22*e047c5eaSDavid Gow #include <linux/sched.h> /* for task_struct */
23*e047c5eaSDavid Gow 
24*e047c5eaSDavid Gow 
25*e047c5eaSDavid Gow /**
26*e047c5eaSDavid Gow  * KUNIT_STATIC_STUB_REDIRECT() - call a replacement 'static stub' if one exists
27*e047c5eaSDavid Gow  * @real_fn_name: The name of this function (as an identifier, not a string)
28*e047c5eaSDavid Gow  * @args: All of the arguments passed to this function
29*e047c5eaSDavid Gow  *
30*e047c5eaSDavid Gow  * This is a function prologue which is used to allow calls to the current
31*e047c5eaSDavid Gow  * function to be redirected by a KUnit test. KUnit tests can call
32*e047c5eaSDavid Gow  * kunit_activate_static_stub() to pass a replacement function in. The
33*e047c5eaSDavid Gow  * replacement function will be called by KUNIT_TRIGGER_STATIC_STUB(), which
34*e047c5eaSDavid Gow  * will then return from the function. If the caller is not in a KUnit context,
35*e047c5eaSDavid Gow  * the function will continue execution as normal.
36*e047c5eaSDavid Gow  *
37*e047c5eaSDavid Gow  * Example:
38*e047c5eaSDavid Gow  *
39*e047c5eaSDavid Gow  * .. code-block:: c
40*e047c5eaSDavid Gow  *
41*e047c5eaSDavid Gow  *	int real_func(int n)
42*e047c5eaSDavid Gow  *	{
43*e047c5eaSDavid Gow  *		KUNIT_STATIC_STUB_REDIRECT(real_func, n);
44*e047c5eaSDavid Gow  *		return 0;
45*e047c5eaSDavid Gow  *	}
46*e047c5eaSDavid Gow  *
47*e047c5eaSDavid Gow  *	int replacement_func(int n)
48*e047c5eaSDavid Gow  *	{
49*e047c5eaSDavid Gow  *		return 42;
50*e047c5eaSDavid Gow  *	}
51*e047c5eaSDavid Gow  *
52*e047c5eaSDavid Gow  *	void example_test(struct kunit *test)
53*e047c5eaSDavid Gow  *	{
54*e047c5eaSDavid Gow  *		kunit_activate_static_stub(test, real_func, replacement_func);
55*e047c5eaSDavid Gow  *		KUNIT_EXPECT_EQ(test, real_func(1), 42);
56*e047c5eaSDavid Gow  *	}
57*e047c5eaSDavid Gow  *
58*e047c5eaSDavid Gow  */
59*e047c5eaSDavid Gow #define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...)		\
60*e047c5eaSDavid Gow do {									\
61*e047c5eaSDavid Gow 	typeof(&real_fn_name) replacement;				\
62*e047c5eaSDavid Gow 	struct kunit *current_test = kunit_get_current_test();		\
63*e047c5eaSDavid Gow 									\
64*e047c5eaSDavid Gow 	if (likely(!current_test))					\
65*e047c5eaSDavid Gow 		break;							\
66*e047c5eaSDavid Gow 									\
67*e047c5eaSDavid Gow 	replacement = kunit_hooks.get_static_stub_address(current_test,	\
68*e047c5eaSDavid Gow 							&real_fn_name);	\
69*e047c5eaSDavid Gow 									\
70*e047c5eaSDavid Gow 	if (unlikely(replacement))					\
71*e047c5eaSDavid Gow 		return replacement(args);				\
72*e047c5eaSDavid Gow } while (0)
73*e047c5eaSDavid Gow 
74*e047c5eaSDavid Gow /* Helper function for kunit_activate_static_stub(). The macro does
75*e047c5eaSDavid Gow  * typechecking, so use it instead.
76*e047c5eaSDavid Gow  */
77*e047c5eaSDavid Gow void __kunit_activate_static_stub(struct kunit *test,
78*e047c5eaSDavid Gow 				  void *real_fn_addr,
79*e047c5eaSDavid Gow 				  void *replacement_addr);
80*e047c5eaSDavid Gow 
81*e047c5eaSDavid Gow /**
82*e047c5eaSDavid Gow  * kunit_activate_static_stub() - replace a function using static stubs.
83*e047c5eaSDavid Gow  * @test: A pointer to the 'struct kunit' test context for the current test.
84*e047c5eaSDavid Gow  * @real_fn_addr: The address of the function to replace.
85*e047c5eaSDavid Gow  * @replacement_addr: The address of the function to replace it with.
86*e047c5eaSDavid Gow  *
87*e047c5eaSDavid Gow  * When activated, calls to real_fn_addr from within this test (even if called
88*e047c5eaSDavid Gow  * indirectly) will instead call replacement_addr. The function pointed to by
89*e047c5eaSDavid Gow  * real_fn_addr must begin with the static stub prologue in
90*e047c5eaSDavid Gow  * KUNIT_TRIGGER_STATIC_STUB() for this to work. real_fn_addr and
91*e047c5eaSDavid Gow  * replacement_addr must have the same type.
92*e047c5eaSDavid Gow  *
93*e047c5eaSDavid Gow  * The redirection can be disabled again with kunit_deactivate_static_stub().
94*e047c5eaSDavid Gow  */
95*e047c5eaSDavid Gow #define kunit_activate_static_stub(test, real_fn_addr, replacement_addr) do {	\
96*e047c5eaSDavid Gow 	typecheck_fn(typeof(&real_fn_addr), replacement_addr);			\
97*e047c5eaSDavid Gow 	__kunit_activate_static_stub(test, real_fn_addr, replacement_addr);	\
98*e047c5eaSDavid Gow } while (0)
99*e047c5eaSDavid Gow 
100*e047c5eaSDavid Gow 
101*e047c5eaSDavid Gow /**
102*e047c5eaSDavid Gow  * kunit_deactivate_static_stub() - disable a function redirection
103*e047c5eaSDavid Gow  * @test: A pointer to the 'struct kunit' test context for the current test.
104*e047c5eaSDavid Gow  * @real_fn_addr: The address of the function to no-longer redirect
105*e047c5eaSDavid Gow  *
106*e047c5eaSDavid Gow  * Deactivates a redirection configured with kunit_activate_static_stub(). After
107*e047c5eaSDavid Gow  * this function returns, calls to real_fn_addr() will execute the original
108*e047c5eaSDavid Gow  * real_fn, not any previously-configured replacement.
109*e047c5eaSDavid Gow  */
110*e047c5eaSDavid Gow void kunit_deactivate_static_stub(struct kunit *test, void *real_fn_addr);
111*e047c5eaSDavid Gow 
112*e047c5eaSDavid Gow #endif
113*e047c5eaSDavid Gow #endif
114