Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | // SPDX-License-Identifier: GPL-2.0 /* * KUnit function redirection (static stubbing) API. * * Copyright (C) 2022, Google LLC. * Author: David Gow <davidgow@google.com> */ #include <kunit/test.h> #include <kunit/static_stub.h> #include "hooks-impl.h" /* Context for a static stub. This is stored in the resource data. */ struct kunit_static_stub_ctx { void *real_fn_addr; void *replacement_addr; }; static void __kunit_static_stub_resource_free(struct kunit_resource *res) { kfree(res->data); } /* Matching function for kunit_find_resource(). match_data is real_fn_addr. */ static bool __kunit_static_stub_resource_match(struct kunit *test, struct kunit_resource *res, void *match_real_fn_addr) { /* This pointer is only valid if res is a static stub resource. */ struct kunit_static_stub_ctx *ctx = res->data; /* Make sure the resource is a static stub resource. */ if (res->free != &__kunit_static_stub_resource_free) return false; return ctx->real_fn_addr == match_real_fn_addr; } /* Hook to return the address of the replacement function. */ void *__kunit_get_static_stub_address_impl(struct kunit *test, void *real_fn_addr) { struct kunit_resource *res; struct kunit_static_stub_ctx *ctx; void *replacement_addr; res = kunit_find_resource(test, __kunit_static_stub_resource_match, real_fn_addr); if (!res) return NULL; ctx = res->data; replacement_addr = ctx->replacement_addr; kunit_put_resource(res); return replacement_addr; } void kunit_deactivate_static_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 = kunit_find_resource(test, __kunit_static_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() */ kunit_remove_resource(test, res); kunit_put_resource(res); } EXPORT_SYMBOL_GPL(kunit_deactivate_static_stub); /* Helper function for kunit_activate_static_stub(). The macro does * typechecking, so use it instead. */ void __kunit_activate_static_stub(struct kunit *test, void *real_fn_addr, void *replacement_addr) { struct kunit_static_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_static_stub(test, replacement_addr); return; } /* Look up any existing stubs for this function, and replace them. */ res = kunit_find_resource(test, __kunit_static_stub_resource_match, real_fn_addr); if (res) { ctx = res->data; ctx->replacement_addr = replacement_addr; /* We got an extra reference from find_resource(), so put it. */ kunit_put_resource(res); } else { ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); ctx->real_fn_addr = real_fn_addr; ctx->replacement_addr = replacement_addr; res = kunit_alloc_resource(test, NULL, &__kunit_static_stub_resource_free, GFP_KERNEL, ctx); } } EXPORT_SYMBOL_GPL(__kunit_activate_static_stub); |