|
@@ -58,6 +58,21 @@ do { \
|
|
printk(KERN_DEBUG "%s: " fmt "\n", __func__, ##args); \
|
|
printk(KERN_DEBUG "%s: " fmt "\n", __func__, ##args); \
|
|
} while (0)
|
|
} while (0)
|
|
|
|
|
|
|
|
+#define DUMP_BYTES(buf, len, fmt, args...) \
|
|
|
|
+do { \
|
|
|
|
+ if (unlikely(debug_alternative)) { \
|
|
|
|
+ int j; \
|
|
|
|
+ \
|
|
|
|
+ if (!(len)) \
|
|
|
|
+ break; \
|
|
|
|
+ \
|
|
|
|
+ printk(KERN_DEBUG fmt, ##args); \
|
|
|
|
+ for (j = 0; j < (len) - 1; j++) \
|
|
|
|
+ printk(KERN_CONT "%02hhx ", buf[j]); \
|
|
|
|
+ printk(KERN_CONT "%02hhx\n", buf[j]); \
|
|
|
|
+ } \
|
|
|
|
+} while (0)
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Each GENERIC_NOPX is of X bytes, and defined as an array of bytes
|
|
* Each GENERIC_NOPX is of X bytes, and defined as an array of bytes
|
|
* that correspond to that nop. Getting from one nop to the next, we
|
|
* that correspond to that nop. Getting from one nop to the next, we
|
|
@@ -243,6 +258,71 @@ extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
|
|
extern s32 __smp_locks[], __smp_locks_end[];
|
|
extern s32 __smp_locks[], __smp_locks_end[];
|
|
void *text_poke_early(void *addr, const void *opcode, size_t len);
|
|
void *text_poke_early(void *addr, const void *opcode, size_t len);
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Are we looking at a near JMP with a 1 or 4-byte displacement.
|
|
|
|
+ */
|
|
|
|
+static inline bool is_jmp(const u8 opcode)
|
|
|
|
+{
|
|
|
|
+ return opcode == 0xeb || opcode == 0xe9;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void __init_or_module
|
|
|
|
+recompute_jump(struct alt_instr *a, u8 *orig_insn, u8 *repl_insn, u8 *insnbuf)
|
|
|
|
+{
|
|
|
|
+ u8 *next_rip, *tgt_rip;
|
|
|
|
+ s32 n_dspl, o_dspl;
|
|
|
|
+ int repl_len;
|
|
|
|
+
|
|
|
|
+ if (a->replacementlen != 5)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ o_dspl = *(s32 *)(insnbuf + 1);
|
|
|
|
+
|
|
|
|
+ /* next_rip of the replacement JMP */
|
|
|
|
+ next_rip = repl_insn + a->replacementlen;
|
|
|
|
+ /* target rip of the replacement JMP */
|
|
|
|
+ tgt_rip = next_rip + o_dspl;
|
|
|
|
+ n_dspl = tgt_rip - orig_insn;
|
|
|
|
+
|
|
|
|
+ DPRINTK("target RIP: %p, new_displ: 0x%x", tgt_rip, n_dspl);
|
|
|
|
+
|
|
|
|
+ if (tgt_rip - orig_insn >= 0) {
|
|
|
|
+ if (n_dspl - 2 <= 127)
|
|
|
|
+ goto two_byte_jmp;
|
|
|
|
+ else
|
|
|
|
+ goto five_byte_jmp;
|
|
|
|
+ /* negative offset */
|
|
|
|
+ } else {
|
|
|
|
+ if (((n_dspl - 2) & 0xff) == (n_dspl - 2))
|
|
|
|
+ goto two_byte_jmp;
|
|
|
|
+ else
|
|
|
|
+ goto five_byte_jmp;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+two_byte_jmp:
|
|
|
|
+ n_dspl -= 2;
|
|
|
|
+
|
|
|
|
+ insnbuf[0] = 0xeb;
|
|
|
|
+ insnbuf[1] = (s8)n_dspl;
|
|
|
|
+ add_nops(insnbuf + 2, 3);
|
|
|
|
+
|
|
|
|
+ repl_len = 2;
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+five_byte_jmp:
|
|
|
|
+ n_dspl -= 5;
|
|
|
|
+
|
|
|
|
+ insnbuf[0] = 0xe9;
|
|
|
|
+ *(s32 *)&insnbuf[1] = n_dspl;
|
|
|
|
+
|
|
|
|
+ repl_len = 5;
|
|
|
|
+
|
|
|
|
+done:
|
|
|
|
+
|
|
|
|
+ DPRINTK("final displ: 0x%08x, JMP 0x%lx",
|
|
|
|
+ n_dspl, (unsigned long)orig_insn + n_dspl + repl_len);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Replace instructions with better alternatives for this CPU type. This runs
|
|
* Replace instructions with better alternatives for this CPU type. This runs
|
|
* before SMP is initialized to avoid SMP problems with self modifying code.
|
|
* before SMP is initialized to avoid SMP problems with self modifying code.
|
|
@@ -268,6 +348,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
|
|
* order.
|
|
* order.
|
|
*/
|
|
*/
|
|
for (a = start; a < end; a++) {
|
|
for (a = start; a < end; a++) {
|
|
|
|
+ int insnbuf_sz = 0;
|
|
|
|
+
|
|
instr = (u8 *)&a->instr_offset + a->instr_offset;
|
|
instr = (u8 *)&a->instr_offset + a->instr_offset;
|
|
replacement = (u8 *)&a->repl_offset + a->repl_offset;
|
|
replacement = (u8 *)&a->repl_offset + a->repl_offset;
|
|
BUG_ON(a->instrlen > sizeof(insnbuf));
|
|
BUG_ON(a->instrlen > sizeof(insnbuf));
|
|
@@ -281,24 +363,35 @@ void __init_or_module apply_alternatives(struct alt_instr *start,
|
|
instr, a->instrlen,
|
|
instr, a->instrlen,
|
|
replacement, a->replacementlen);
|
|
replacement, a->replacementlen);
|
|
|
|
|
|
|
|
+ DUMP_BYTES(instr, a->instrlen, "%p: old_insn: ", instr);
|
|
|
|
+ DUMP_BYTES(replacement, a->replacementlen, "%p: rpl_insn: ", replacement);
|
|
|
|
+
|
|
memcpy(insnbuf, replacement, a->replacementlen);
|
|
memcpy(insnbuf, replacement, a->replacementlen);
|
|
|
|
+ insnbuf_sz = a->replacementlen;
|
|
|
|
|
|
/* 0xe8 is a relative jump; fix the offset. */
|
|
/* 0xe8 is a relative jump; fix the offset. */
|
|
if (*insnbuf == 0xe8 && a->replacementlen == 5) {
|
|
if (*insnbuf == 0xe8 && a->replacementlen == 5) {
|
|
*(s32 *)(insnbuf + 1) += replacement - instr;
|
|
*(s32 *)(insnbuf + 1) += replacement - instr;
|
|
- DPRINTK("Fix CALL offset: 0x%x", *(s32 *)(insnbuf + 1));
|
|
|
|
|
|
+ DPRINTK("Fix CALL offset: 0x%x, CALL 0x%lx",
|
|
|
|
+ *(s32 *)(insnbuf + 1),
|
|
|
|
+ (unsigned long)instr + *(s32 *)(insnbuf + 1) + 5);
|
|
}
|
|
}
|
|
|
|
|
|
- if (a->instrlen > a->replacementlen)
|
|
|
|
|
|
+ if (a->replacementlen && is_jmp(replacement[0]))
|
|
|
|
+ recompute_jump(a, instr, replacement, insnbuf);
|
|
|
|
+
|
|
|
|
+ if (a->instrlen > a->replacementlen) {
|
|
add_nops(insnbuf + a->replacementlen,
|
|
add_nops(insnbuf + a->replacementlen,
|
|
a->instrlen - a->replacementlen);
|
|
a->instrlen - a->replacementlen);
|
|
|
|
+ insnbuf_sz += a->instrlen - a->replacementlen;
|
|
|
|
+ }
|
|
|
|
+ DUMP_BYTES(insnbuf, insnbuf_sz, "%p: final_insn: ", instr);
|
|
|
|
|
|
- text_poke_early(instr, insnbuf, a->instrlen);
|
|
|
|
|
|
+ text_poke_early(instr, insnbuf, insnbuf_sz);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
#ifdef CONFIG_SMP
|
|
-
|
|
|
|
static void alternatives_smp_lock(const s32 *start, const s32 *end,
|
|
static void alternatives_smp_lock(const s32 *start, const s32 *end,
|
|
u8 *text, u8 *text_end)
|
|
u8 *text, u8 *text_end)
|
|
{
|
|
{
|
|
@@ -449,7 +542,7 @@ int alternatives_text_reserved(void *start, void *end)
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
-#endif
|
|
|
|
|
|
+#endif /* CONFIG_SMP */
|
|
|
|
|
|
#ifdef CONFIG_PARAVIRT
|
|
#ifdef CONFIG_PARAVIRT
|
|
void __init_or_module apply_paravirt(struct paravirt_patch_site *start,
|
|
void __init_or_module apply_paravirt(struct paravirt_patch_site *start,
|