Commit 3f2a8fc4 authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar

static_call/x86: Add __static_call_return0()

Provide a stub function that return 0 and wire up the static call site
patching to replace the CALL with a single 5 byte instruction that
clears %RAX, the return value register.

The function can be cast to any function pointer type that has a
single %RAX return (including pointers). Also provide a version that
returns an int for convenience. We are clearing the entire %RAX register
in any case, whether the return value is 32 or 64 bits, since %RAX is
always a scratch register anyway.
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarFrederic Weisbecker <frederic@kernel.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
Link: https://lkml.kernel.org/r/20210118141223.123667-2-frederic@kernel.org
parent 880cfed3
...@@ -11,14 +11,26 @@ enum insn_type { ...@@ -11,14 +11,26 @@ enum insn_type {
RET = 3, /* tramp / site cond-tail-call */ RET = 3, /* tramp / site cond-tail-call */
}; };
/*
* data16 data16 xorq %rax, %rax - a single 5 byte instruction that clears %rax
* The REX.W cancels the effect of any data16.
*/
static const u8 xor5rax[] = { 0x66, 0x66, 0x48, 0x31, 0xc0 };
static void __ref __static_call_transform(void *insn, enum insn_type type, void *func) static void __ref __static_call_transform(void *insn, enum insn_type type, void *func)
{ {
const void *emulate = NULL;
int size = CALL_INSN_SIZE; int size = CALL_INSN_SIZE;
const void *code; const void *code;
switch (type) { switch (type) {
case CALL: case CALL:
code = text_gen_insn(CALL_INSN_OPCODE, insn, func); code = text_gen_insn(CALL_INSN_OPCODE, insn, func);
if (func == &__static_call_return0) {
emulate = code;
code = &xor5rax;
}
break; break;
case NOP: case NOP:
...@@ -41,7 +53,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type, void ...@@ -41,7 +53,7 @@ static void __ref __static_call_transform(void *insn, enum insn_type type, void
if (unlikely(system_state == SYSTEM_BOOTING)) if (unlikely(system_state == SYSTEM_BOOTING))
return text_poke_early(insn, code, size); return text_poke_early(insn, code, size);
text_poke_bp(insn, code, size, NULL); text_poke_bp(insn, code, size, emulate);
} }
static void __static_call_validate(void *insn, bool tail) static void __static_call_validate(void *insn, bool tail)
...@@ -54,7 +66,8 @@ static void __static_call_validate(void *insn, bool tail) ...@@ -54,7 +66,8 @@ static void __static_call_validate(void *insn, bool tail)
return; return;
} else { } else {
if (opcode == CALL_INSN_OPCODE || if (opcode == CALL_INSN_OPCODE ||
!memcmp(insn, ideal_nops[NOP_ATOMIC5], 5)) !memcmp(insn, ideal_nops[NOP_ATOMIC5], 5) ||
!memcmp(insn, xor5rax, 5))
return; return;
} }
......
...@@ -142,6 +142,8 @@ extern void __static_call_update(struct static_call_key *key, void *tramp, void ...@@ -142,6 +142,8 @@ extern void __static_call_update(struct static_call_key *key, void *tramp, void
extern int static_call_mod_init(struct module *mod); extern int static_call_mod_init(struct module *mod);
extern int static_call_text_reserved(void *start, void *end); extern int static_call_text_reserved(void *start, void *end);
extern long __static_call_return0(void);
#define DEFINE_STATIC_CALL(name, _func) \ #define DEFINE_STATIC_CALL(name, _func) \
DECLARE_STATIC_CALL(name, _func); \ DECLARE_STATIC_CALL(name, _func); \
struct static_call_key STATIC_CALL_KEY(name) = { \ struct static_call_key STATIC_CALL_KEY(name) = { \
...@@ -206,6 +208,11 @@ static inline int static_call_text_reserved(void *start, void *end) ...@@ -206,6 +208,11 @@ static inline int static_call_text_reserved(void *start, void *end)
return 0; return 0;
} }
static inline long __static_call_return0(void)
{
return 0;
}
#define EXPORT_STATIC_CALL(name) \ #define EXPORT_STATIC_CALL(name) \
EXPORT_SYMBOL(STATIC_CALL_KEY(name)); \ EXPORT_SYMBOL(STATIC_CALL_KEY(name)); \
EXPORT_SYMBOL(STATIC_CALL_TRAMP(name)) EXPORT_SYMBOL(STATIC_CALL_TRAMP(name))
...@@ -222,6 +229,11 @@ struct static_call_key { ...@@ -222,6 +229,11 @@ struct static_call_key {
void *func; void *func;
}; };
static inline long __static_call_return0(void)
{
return 0;
}
#define DEFINE_STATIC_CALL(name, _func) \ #define DEFINE_STATIC_CALL(name, _func) \
DECLARE_STATIC_CALL(name, _func); \ DECLARE_STATIC_CALL(name, _func); \
struct static_call_key STATIC_CALL_KEY(name) = { \ struct static_call_key STATIC_CALL_KEY(name) = { \
......
...@@ -438,6 +438,11 @@ int __init static_call_init(void) ...@@ -438,6 +438,11 @@ int __init static_call_init(void)
} }
early_initcall(static_call_init); early_initcall(static_call_init);
long __static_call_return0(void)
{
return 0;
}
#ifdef CONFIG_STATIC_CALL_SELFTEST #ifdef CONFIG_STATIC_CALL_SELFTEST
static int func_a(int x) static int func_a(int x)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment