Commit 63f62add authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar
Browse files

x86/alternatives: Add and use text_gen_insn() helper



Provide a simple helper function to create common instruction
encodings.

Tested-by: default avatarAlexei Starovoitov <ast@kernel.org>
Tested-by: default avatarSteven Rostedt (VMware) <rostedt@goodmis.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Daniel Bristot de Oliveira <bristot@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20191111132457.703538332@infradead.org


Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 18cbc8be
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ extern void text_poke_bp(void *addr, const void *opcode, size_t len, const void
extern void text_poke_queue(void *addr, const void *opcode, size_t len, const void *emulate);
extern void text_poke_finish(void);

extern void *text_gen_insn(u8 opcode, const void *addr, const void *dest);

extern int after_bootmem;
extern __ro_after_init struct mm_struct *poking_mm;
extern __ro_after_init unsigned long poking_addr;
+36 −0
Original line number Diff line number Diff line
@@ -1237,3 +1237,39 @@ void text_poke_bp(void *addr, const void *opcode, size_t len, const void *emulat
	text_poke_loc_init(&tp, addr, opcode, len, emulate);
	text_poke_bp_batch(&tp, 1);
}

union text_poke_insn {
	u8 text[POKE_MAX_OPCODE_SIZE];
	struct {
		u8 opcode;
		s32 disp;
	} __attribute__((packed));
};

void *text_gen_insn(u8 opcode, const void *addr, const void *dest)
{
	static union text_poke_insn insn; /* text_mutex */
	int size = 0;

	lockdep_assert_held(&text_mutex);

	insn.opcode = opcode;

#define __CASE(insn)	\
	case insn##_INSN_OPCODE: size = insn##_INSN_SIZE; break

	switch(opcode) {
	__CASE(INT3);
	__CASE(CALL);
	__CASE(JMP32);
	__CASE(JMP8);
	}

	if (size > 1) {
		insn.disp = (long)dest - (long)(addr + size);
		if (size == 2)
			BUG_ON((insn.disp >> 31) != (insn.disp >> 7));
	}

	return &insn.text;
}
+11 −20
Original line number Diff line number Diff line
@@ -16,15 +16,7 @@
#include <asm/alternative.h>
#include <asm/text-patching.h>

union jump_code_union {
	char code[JUMP_LABEL_NOP_SIZE];
	struct {
		char jump;
		int offset;
	} __attribute__((packed));
};

static void bug_at(unsigned char *ip, int line)
static void bug_at(const void *ip, int line)
{
	/*
	 * The location is not an op that we were expecting.
@@ -38,33 +30,32 @@ static void bug_at(unsigned char *ip, int line)
static const void *
__jump_label_set_jump_code(struct jump_entry *entry, enum jump_label_type type, int init)
{
	static union jump_code_union code; /* relies on text_mutex */
	const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
	const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];
	const void *expect;
	const void *expect, *code;
	const void *addr, *dest;
	int line;

	lockdep_assert_held(&text_mutex);
	addr = (void *)jump_entry_code(entry);
	dest = (void *)jump_entry_target(entry);

	code.jump = JMP32_INSN_OPCODE;
	code.offset = jump_entry_target(entry) -
		       (jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
	code = text_gen_insn(JMP32_INSN_OPCODE, addr, dest);

	if (init) {
		expect = default_nop; line = __LINE__;
	} else if (type == JUMP_LABEL_JMP) {
		expect = ideal_nop; line = __LINE__;
	} else {
		expect = code.code; line = __LINE__;
		expect = code; line = __LINE__;
	}

	if (memcmp((void *)jump_entry_code(entry), expect, JUMP_LABEL_NOP_SIZE))
		bug_at((void *)jump_entry_code(entry), line);
	if (memcmp(addr, expect, JUMP_LABEL_NOP_SIZE))
		bug_at(addr, line);

	if (type == JUMP_LABEL_NOP)
		memcpy(&code, ideal_nop, JUMP_LABEL_NOP_SIZE);
		code = ideal_nop;

	return &code;
	return code;
}

static void inline __jump_label_transform(struct jump_entry *entry,
+1 −6
Original line number Diff line number Diff line
@@ -447,18 +447,13 @@ void arch_optimize_kprobes(struct list_head *oplist)
void arch_unoptimize_kprobe(struct optimized_kprobe *op)
{
	u8 insn_buff[RELATIVEJUMP_SIZE];
	u8 emulate_buff[RELATIVEJUMP_SIZE];

	/* Set int3 to first byte for kprobes */
	insn_buff[0] = BREAKPOINT_INSTRUCTION;
	memcpy(insn_buff + 1, op->optinsn.copied_insn, RELATIVE_ADDR_SIZE);

	emulate_buff[0] = RELATIVEJUMP_OPCODE;
	*(s32 *)(&emulate_buff[1]) = (s32)((long)op->optinsn.insn -
			((long)op->kp.addr + RELATIVEJUMP_SIZE));

	text_poke_bp(op->kp.addr, insn_buff, RELATIVEJUMP_SIZE,
		     emulate_buff);
		     text_gen_insn(JMP32_INSN_OPCODE, op->kp.addr, op->optinsn.insn));
}

/*