| 1 | |
| 2 | #include <linux/static_call.h>
|
| 3 | #include <linux/memory.h>
|
| 4 | #include <linux/bug.h>
|
| 5 | #include <asm/text-patching.h>
|
| 6 |
|
| 7 |
|
| 8 | #ifdef CONFIG_PARAVIRT_SPINLOCKS
|
| 9 | struct qspinlock;
|
| 10 | void __raw_callee_save___native_queued_spin_unlock(struct qspinlock *lock);
|
| 11 | #endif
|
| 12 |
|
| 13 | enum insn_type {
|
| 14 | CALL = 0,
|
| 15 | NOP = 1,
|
| 16 | JMP = 2,
|
| 17 | RET = 3,
|
| 18 | JCC = 4,
|
| 19 | };
|
| 20 |
|
| 21 | |
| 22 | * ud1 %esp, %ecx - a 3 byte #UD that is unique to trampolines, chosen such
|
| 23 | * that there is no false-positive trampoline identification while also being a
|
| 24 | * speculation stop.
|
| 25 |
|
| 26 | static const u8 tramp_ud[] = { 0x0f, 0xb9, 0xcc };
|
| 27 |
|
| 28 | |
| 29 | * cs cs cs xorl %eax, %eax - a single 5 byte instruction that clears %[er]ax
|
| 30 |
|
| 31 | static const u8 xor5rax[] = { 0x2e, 0x2e, 0x2e, 0x31, 0xc0 };
|
| 32 |
|
| 33 | static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
|
| 34 |
|
| 35 | |
| 36 | * ud1 (%edx),%rdi -- see __WARN_trap() / decode_bug()
|
| 37 |
|
| 38 | static const u8 warninsn[] = { 0x67, 0x48, 0x0f, 0xb9, 0x3a };
|
| 39 |
|
| 40 | #ifdef CONFIG_PARAVIRT_SPINLOCKS
|
| 41 | |
| 42 | * ds ds movb $0, (_ASM_ARG1)
|
| 43 |
|
| 44 | #ifdef CONFIG_64BIT
|
| 45 | static const u8 unlockinsn[] = { 0x3e, 0x3e, 0xc6, 0x07, 0x00 };
|
| 46 | #else
|
| 47 | static const u8 unlockinsn[] = { 0x3e, 0x3e, 0xc6, 0x00, 0x00 };
|
| 48 | #endif
|
| 49 | #endif
|
| 50 |
|
| 51 | static u8 __is_Jcc(u8 *insn)
|
| 52 | {
|
| 53 | u8 ret = 0;
|
| 54 |
|
| 55 | if (insn[0] == 0x0f) {
|
| 56 | u8 tmp = insn[1];
|
| 57 | if ((tmp & 0xf0) == 0x80)
|
| 58 | ret = tmp;
|
| 59 | }
|
| 60 |
|
| 61 | return ret;
|
| 62 | }
|
| 63 |
|
| 64 | extern void __static_call_return(void);
|
| 65 |
|
| 66 | asm (".global __static_call_return\n\t"
|
| 67 | ".type __static_call_return, @function\n\t"
|
| 68 | ASM_FUNC_ALIGN "\n\t"
|
| 69 | "__static_call_return:\n\t"
|
| 70 | ANNOTATE_NOENDBR "\n\t"
|
| 71 | ANNOTATE_RETPOLINE_SAFE "\n\t"
|
| 72 | "ret; int3\n\t"
|
| 73 | ".size __static_call_return, . - __static_call_return \n\t");
|
| 74 |
|
| 75 | static void __ref __static_call_transform(void *insn, enum insn_type type,
|
| 76 | void *func, bool modinit)
|
| 77 | {
|
| 78 | const void *emulate = NULL;
|
| 79 | int size = CALL_INSN_SIZE;
|
| 80 | const void *code;
|
| 81 | u8 op, buf[6];
|
| 82 |
|
| 83 | if ((type == JMP || type == RET) && (op = __is_Jcc(insn)))
|
| 84 | type = JCC;
|
| 85 |
|
| 86 | switch (type) {
|
| 87 | case CALL:
|
| 88 | func = callthunks_translate_call_dest(func);
|
| 89 | code = text_gen_insn(CALL_INSN_OPCODE, insn, func);
|
| 90 | if (func == &__static_call_return0) {
|
| 91 | emulate = code;
|
| 92 | code = &xor5rax;
|
| 93 | }
|
| 94 | if (func == &__WARN_trap) {
|
| 95 | emulate = code;
|
| 96 | code = &warninsn;
|
| 97 | }
|
| 98 | #ifdef CONFIG_PARAVIRT_SPINLOCKS
|
| 99 | if (func == &__raw_callee_save___native_queued_spin_unlock) {
|
| 100 | emulate = code;
|
| 101 | code = &unlockinsn;
|
| 102 | }
|
| 103 | #endif
|
| 104 | break;
|
| 105 |
|
| 106 | case NOP:
|
| 107 | code = x86_nops[5];
|
| 108 | break;
|
| 109 |
|
| 110 | case JMP:
|
| 111 | code = text_gen_insn(JMP32_INSN_OPCODE, insn, func);
|
| 112 | break;
|
| 113 |
|
| 114 | case RET:
|
| 115 | if (cpu_wants_rethunk_at(insn))
|
| 116 | code = text_gen_insn(JMP32_INSN_OPCODE, insn, x86_return_thunk);
|
| 117 | else
|
| 118 | code = &retinsn;
|
| 119 | break;
|
| 120 |
|
| 121 | case JCC:
|
| 122 | if (!func) {
|
| 123 | func = __static_call_return;
|
| 124 | if (cpu_wants_rethunk())
|
| 125 | func = x86_return_thunk;
|
| 126 | }
|
| 127 |
|
| 128 | buf[0] = 0x0f;
|
| 129 | __text_gen_insn(buf+1, op, insn+1, func, 5);
|
| 130 | code = buf;
|
| 131 | size = 6;
|
| 132 |
|
| 133 | break;
|
| 134 | }
|
| 135 |
|
| 136 | if (memcmp(insn, code, size) == 0)
|
| 137 | return;
|
| 138 |
|
| 139 | if (system_state == SYSTEM_BOOTING || modinit)
|
| 140 | return text_poke_early(insn, code, size);
|
| 141 |
|
| 142 | smp_text_poke_single(insn, code, size, emulate);
|
| 143 | }
|
| 144 |
|
| 145 | static void __static_call_validate(u8 *insn, bool tail, bool tramp)
|
| 146 | {
|
| 147 | u8 opcode = insn[0];
|
| 148 |
|
| 149 | if (tramp && memcmp(insn+5, tramp_ud, 3)) {
|
| 150 | pr_err("trampoline signature fail");
|
| 151 | BUG();
|
| 152 | }
|
| 153 |
|
| 154 | if (tail) {
|
| 155 | if (opcode == JMP32_INSN_OPCODE ||
|
| 156 | opcode == RET_INSN_OPCODE ||
|
| 157 | __is_Jcc(insn))
|
| 158 | return;
|
| 159 | } else {
|
| 160 | if (opcode == CALL_INSN_OPCODE ||
|
| 161 | !memcmp(insn, x86_nops[5], 5) ||
|
| 162 | !memcmp(insn, xor5rax, 5) ||
|
| 163 | !memcmp(insn, warninsn, 5))
|
| 164 | return;
|
| 165 | #ifdef CONFIG_PARAVIRT_SPINLOCKS
|
| 166 | if (!memcmp(insn, unlockinsn, 5))
|
| 167 | return;
|
| 168 | #endif
|
| 169 | }
|
| 170 |
|
| 171 | |
| 172 | * If we ever trigger this, our text is corrupt, we'll probably not live long.
|
| 173 |
|
| 174 | pr_err("unexpected static_call insn opcode 0x%x at %pS\n", opcode, insn);
|
| 175 | BUG();
|
| 176 | }
|
| 177 |
|
| 178 | static inline enum insn_type __sc_insn(bool null, bool tail)
|
| 179 | {
|
| 180 | |
| 181 | * Encode the following table without branches:
|
| 182 | *
|
| 183 | * tail null insn
|
| 184 | * -----+-------+------
|
| 185 | * 0 | 0 | CALL
|
| 186 | * 0 | 1 | NOP
|
| 187 | * 1 | 0 | JMP
|
| 188 | * 1 | 1 | RET
|
| 189 |
|
| 190 | return 2*tail + null;
|
| 191 | }
|
| 192 |
|
| 193 | void arch_static_call_transform(void *site, void *tramp, void *func, bool tail)
|
| 194 | {
|
| 195 | mutex_lock(&text_mutex);
|
| 196 |
|
| 197 | if (tramp && !site) {
|
| 198 | __static_call_validate(tramp, true, true);
|
| 199 | __static_call_transform(tramp, __sc_insn(!func, true), func, false);
|
| 200 | }
|
| 201 |
|
| 202 | if (IS_ENABLED(CONFIG_HAVE_STATIC_CALL_INLINE) && site) {
|
| 203 | __static_call_validate(site, tail, false);
|
| 204 | __static_call_transform(site, __sc_insn(!func, tail), func, false);
|
| 205 | }
|
| 206 |
|
| 207 | mutex_unlock(&text_mutex);
|
| 208 | }
|
| 209 | EXPORT_SYMBOL_GPL(arch_static_call_transform);
|
| 210 |
|
| 211 | noinstr void __static_call_update_early(void *tramp, void *func)
|
| 212 | {
|
| 213 | BUG_ON(system_state != SYSTEM_BOOTING);
|
| 214 | BUG_ON(static_call_initialized);
|
| 215 | __text_gen_insn(tramp, JMP32_INSN_OPCODE, tramp, func, JMP32_INSN_SIZE);
|
| 216 | sync_core();
|
| 217 | }
|
| 218 |
|
| 219 | #ifdef CONFIG_MITIGATION_RETHUNK
|
| 220 | |
| 221 | * This is called by apply_returns() to fix up static call trampolines,
|
| 222 | * specifically ARCH_DEFINE_STATIC_CALL_NULL_TRAMP which is recorded as
|
| 223 | * having a return trampoline.
|
| 224 | *
|
| 225 | * The problem is that static_call() is available before determining
|
| 226 | * X86_FEATURE_RETHUNK and, by implication, running alternatives.
|
| 227 | *
|
| 228 | * This means that __static_call_transform() above can have overwritten the
|
| 229 | * return trampoline and we now need to fix things up to be consistent.
|
| 230 |
|
| 231 | bool __static_call_fixup(void *tramp, u8 op, void *dest)
|
| 232 | {
|
| 233 | unsigned long addr = (unsigned long)tramp;
|
| 234 | |
| 235 | * Not all .return_sites are a static_call trampoline (most are not).
|
| 236 | * Check if the 3 bytes after the return are still kernel text, if not,
|
| 237 | * then this definitely is not a trampoline and we need not worry
|
| 238 | * further.
|
| 239 | *
|
| 240 | * This avoids the memcmp() below tripping over pagefaults etc..
|
| 241 |
|
| 242 | if (((addr >> PAGE_SHIFT) != ((addr + 7) >> PAGE_SHIFT)) &&
|
| 243 | !kernel_text_address(addr + 7))
|
| 244 | return false;
|
| 245 |
|
| 246 | if (memcmp(tramp+5, tramp_ud, 3)) {
|
| 247 |
|
| 248 | return false;
|
| 249 | }
|
| 250 |
|
| 251 | mutex_lock(&text_mutex);
|
| 252 | if (op == RET_INSN_OPCODE || dest == &__x86_return_thunk)
|
| 253 | __static_call_transform(tramp, RET, NULL, true);
|
| 254 | mutex_unlock(&text_mutex);
|
| 255 |
|
| 256 | return true;
|
| 257 | }
|
| 258 | #endif
|