Jiri Slaby a43d3d
From: Peter Zijlstra <peterz@infradead.org>
Jiri Slaby a43d3d
Date: Thu, 26 Jan 2023 16:34:27 +0100
Jiri Slaby a43d3d
Subject: [PATCH] x86/static_call: Add support for Jcc tail-calls
Jiri Slaby a43d3d
References: bsc#1012628
Jiri Slaby a43d3d
Patch-mainline: 6.2.1
Jiri Slaby a43d3d
Git-commit: 923510c88d2b7d947c4217835fd9ca6bd65cc56c
Jiri Slaby a43d3d
Jiri Slaby a43d3d
commit 923510c88d2b7d947c4217835fd9ca6bd65cc56c upstream.
Jiri Slaby a43d3d
Jiri Slaby a43d3d
Clang likes to create conditional tail calls like:
Jiri Slaby a43d3d
Jiri Slaby a43d3d
  0000000000000350 <amd_pmu_add_event>:
Jiri Slaby a43d3d
  350:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1) 351: R_X86_64_NONE      __fentry__-0x4
Jiri Slaby a43d3d
  355:       48 83 bf 20 01 00 00 00         cmpq   $0x0,0x120(%rdi)
Jiri Slaby a43d3d
  35d:       0f 85 00 00 00 00       jne    363 <amd_pmu_add_event+0x13>     35f: R_X86_64_PLT32     __SCT__amd_pmu_branch_add-0x4
Jiri Slaby a43d3d
  363:       e9 00 00 00 00          jmp    368 <amd_pmu_add_event+0x18>     364: R_X86_64_PLT32     __x86_return_thunk-0x4
Jiri Slaby a43d3d
Jiri Slaby a43d3d
Where 0x35d is a static call site that's turned into a conditional
Jiri Slaby a43d3d
tail-call using the Jcc class of instructions.
Jiri Slaby a43d3d
Jiri Slaby a43d3d
Teach the in-line static call text patching about this.
Jiri Slaby a43d3d
Jiri Slaby a43d3d
Notably, since there is no conditional-ret, in that case patch the Jcc
Jiri Slaby a43d3d
to point at an empty stub function that does the ret -- or the return
Jiri Slaby a43d3d
thunk when needed.
Jiri Slaby a43d3d
Jiri Slaby a43d3d
Reported-by: "Erhard F." <erhard_f@mailbox.org>
Jiri Slaby a43d3d
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Jiri Slaby a43d3d
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Jiri Slaby a43d3d
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Jiri Slaby a43d3d
Link: https://lore.kernel.org/r/Y9Kdg9QjHkr9G5b5@hirez.programming.kicks-ass.net
Jiri Slaby a43d3d
Cc: Nathan Chancellor <nathan@kernel.org>
Jiri Slaby a43d3d
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Jiri Slaby a43d3d
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Jiri Slaby a43d3d
---
Jiri Slaby a43d3d
 arch/x86/kernel/static_call.c | 50 ++++++++++++++++++++++++++++++++---
Jiri Slaby a43d3d
 1 file changed, 47 insertions(+), 3 deletions(-)
Jiri Slaby a43d3d
Jiri Slaby a43d3d
diff --git a/arch/x86/kernel/static_call.c b/arch/x86/kernel/static_call.c
Jiri Slaby a43d3d
index 2ebc3389..b70670a9 100644
Jiri Slaby a43d3d
--- a/arch/x86/kernel/static_call.c
Jiri Slaby a43d3d
+++ b/arch/x86/kernel/static_call.c
Jiri Slaby a43d3d
@@ -9,6 +9,7 @@ enum insn_type {
Jiri Slaby a43d3d
 	NOP = 1,  /* site cond-call */
Jiri Slaby a43d3d
 	JMP = 2,  /* tramp / site tail-call */
Jiri Slaby a43d3d
 	RET = 3,  /* tramp / site cond-tail-call */
Jiri Slaby a43d3d
+	JCC = 4,
Jiri Slaby a43d3d
 };
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
 /*
Jiri Slaby a43d3d
@@ -25,12 +26,40 @@ static const u8 xor5rax[] = { 0x2e, 0x2e, 0x2e, 0x31, 0xc0 };
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
 static const u8 retinsn[] = { RET_INSN_OPCODE, 0xcc, 0xcc, 0xcc, 0xcc };
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
+static u8 __is_Jcc(u8 *insn) /* Jcc.d32 */
Jiri Slaby a43d3d
+{
Jiri Slaby a43d3d
+	u8 ret = 0;
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+	if (insn[0] == 0x0f) {
Jiri Slaby a43d3d
+		u8 tmp = insn[1];
Jiri Slaby a43d3d
+		if ((tmp & 0xf0) == 0x80)
Jiri Slaby a43d3d
+			ret = tmp;
Jiri Slaby a43d3d
+	}
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+	return ret;
Jiri Slaby a43d3d
+}
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+extern void __static_call_return(void);
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+asm (".global __static_call_return\n\t"
Jiri Slaby a43d3d
+     ".type __static_call_return, @function\n\t"
Jiri Slaby a43d3d
+     ASM_FUNC_ALIGN "\n\t"
Jiri Slaby a43d3d
+     "__static_call_return:\n\t"
Jiri Slaby a43d3d
+     ANNOTATE_NOENDBR
Jiri Slaby a43d3d
+     ANNOTATE_RETPOLINE_SAFE
Jiri Slaby a43d3d
+     "ret; int3\n\t"
Jiri Slaby a43d3d
+     ".size __static_call_return, . - __static_call_return \n\t");
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
 static void __ref __static_call_transform(void *insn, enum insn_type type,
Jiri Slaby a43d3d
 					  void *func, bool modinit)
Jiri Slaby a43d3d
 {
Jiri Slaby a43d3d
 	const void *emulate = NULL;
Jiri Slaby a43d3d
 	int size = CALL_INSN_SIZE;
Jiri Slaby a43d3d
 	const void *code;
Jiri Slaby a43d3d
+	u8 op, buf[6];
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+	if ((type == JMP || type == RET) && (op = __is_Jcc(insn)))
Jiri Slaby a43d3d
+		type = JCC;
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
 	switch (type) {
Jiri Slaby a43d3d
 	case CALL:
Jiri Slaby a43d3d
@@ -57,6 +86,20 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
Jiri Slaby a43d3d
 		else
Jiri Slaby a43d3d
 			code = &retinsn;
Jiri Slaby a43d3d
 		break;
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+	case JCC:
Jiri Slaby a43d3d
+		if (!func) {
Jiri Slaby a43d3d
+			func = __static_call_return;
Jiri Slaby a43d3d
+			if (cpu_feature_enabled(X86_FEATURE_RETHUNK))
Jiri Slaby a43d3d
+				func = x86_return_thunk;
Jiri Slaby a43d3d
+		}
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+		buf[0] = 0x0f;
Jiri Slaby a43d3d
+		__text_gen_insn(buf+1, op, insn+1, func, 5);
Jiri Slaby a43d3d
+		code = buf;
Jiri Slaby a43d3d
+		size = 6;
Jiri Slaby a43d3d
+
Jiri Slaby a43d3d
+		break;
Jiri Slaby a43d3d
 	}
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
 	if (memcmp(insn, code, size) == 0)
Jiri Slaby a43d3d
@@ -68,9 +111,9 @@ static void __ref __static_call_transform(void *insn, enum insn_type type,
Jiri Slaby a43d3d
 	text_poke_bp(insn, code, size, emulate);
Jiri Slaby a43d3d
 }
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
-static void __static_call_validate(void *insn, bool tail, bool tramp)
Jiri Slaby a43d3d
+static void __static_call_validate(u8 *insn, bool tail, bool tramp)
Jiri Slaby a43d3d
 {
Jiri Slaby a43d3d
-	u8 opcode = *(u8 *)insn;
Jiri Slaby a43d3d
+	u8 opcode = insn[0];
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
 	if (tramp && memcmp(insn+5, tramp_ud, 3)) {
Jiri Slaby a43d3d
 		pr_err("trampoline signature fail");
Jiri Slaby a43d3d
@@ -79,7 +122,8 @@ static void __static_call_validate(void *insn, bool tail, bool tramp)
Jiri Slaby a43d3d
 
Jiri Slaby a43d3d
 	if (tail) {
Jiri Slaby a43d3d
 		if (opcode == JMP32_INSN_OPCODE ||
Jiri Slaby a43d3d
-		    opcode == RET_INSN_OPCODE)
Jiri Slaby a43d3d
+		    opcode == RET_INSN_OPCODE ||
Jiri Slaby a43d3d
+		    __is_Jcc(insn))
Jiri Slaby a43d3d
 			return;
Jiri Slaby a43d3d
 	} else {
Jiri Slaby a43d3d
 		if (opcode == CALL_INSN_OPCODE ||
Jiri Slaby a43d3d
-- 
Jiri Slaby a43d3d
2.35.3
Jiri Slaby a43d3d