1
2#include <linux/static_call.h>
3#include <linux/memory.h>
4#include <linux/bug.h>
5#include <asm/text-patching.h>
6
7enum insn_type {
8 CALL = 0,
9 NOP = 1,
10 JMP = 2,
11 RET = 3,
12};
13
14
15
16
17
18static const u8 xor5rax[] = { 0x66, 0x66, 0x48, 0x31, 0xc0 };
19
20static void __ref __static_call_transform(void *insn, enum insn_type type, void *func)
21{
22 const void *emulate = NULL;
23 int size = CALL_INSN_SIZE;
24 const void *code;
25
26 switch (type) {
27 case CALL:
28 code = text_gen_insn(CALL_INSN_OPCODE, insn, func);
29 if (func == &__static_call_return0) {
30 emulate = code;
31 code = &xor5rax;
32 }
33
34 break;
35
36 case NOP:
37 code = x86_nops[5];
38 break;
39
40 case JMP:
41 code = text_gen_insn(JMP32_INSN_OPCODE, insn, func);
42 break;
43
44 case RET:
45 code = text_gen_insn(RET_INSN_OPCODE, insn, func);
46 size = RET_INSN_SIZE;
47 break;
48 }
49
50 if (memcmp(insn, code, size) == 0)
51 return;
52
53 if (unlikely(system_state == SYSTEM_BOOTING))
54 return text_poke_early(insn, code, size);
55
56 text_poke_bp(insn, code, size, emulate);
57}
58
59static void __static_call_validate(void *insn, bool tail, bool tramp)
60{
61 u8 opcode = *(u8 *)insn;
62
63 if (tramp && memcmp(insn+5, "SCT", 3)) {
64 pr_err("trampoline signature fail");
65 BUG();
66 }
67
68 if (tail) {
69 if (opcode == JMP32_INSN_OPCODE ||
70 opcode == RET_INSN_OPCODE)
71 return;
72 } else {
73 if (opcode == CALL_INSN_OPCODE ||
74 !memcmp(insn, x86_nops[5], 5) ||
75 !memcmp(insn, xor5rax, 5))
76 return;
77 }
78
79
80
81
82 pr_err("unexpected static_call insn opcode 0x%x at %pS\n", opcode, insn);
83 BUG();
84}
85
86static inline enum insn_type __sc_insn(bool null, bool tail)
87{
88
89
90
91
92
93
94
95
96
97
98 return 2*tail + null;
99}
100
101void arch_static_call_transform(void *site, void *tramp, void *func, bool tail)
102{
103 mutex_lock(&text_mutex);
104
105 if (tramp) {
106 __static_call_validate(tramp, true, true);
107 __static_call_transform(tramp, __sc_insn(!func, true), func);
108 }
109
110 if (IS_ENABLED(CONFIG_HAVE_STATIC_CALL_INLINE) && site) {
111 __static_call_validate(site, tail, false);
112 __static_call_transform(site, __sc_insn(!func, tail), func);
113 }
114
115 mutex_unlock(&text_mutex);
116}
117EXPORT_SYMBOL_GPL(arch_static_call_transform);
118