124 lines
3.3 KiB
C
124 lines
3.3 KiB
C
// 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);
|