xref: /openbmc/linux/arch/x86/kernel/jump_label.c (revision 9fc0f798ab8a6c042d811e45086260eb59be45c1)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2d9f5ab7bSJason Baron /*
3d9f5ab7bSJason Baron  * jump label x86 support
4d9f5ab7bSJason Baron  *
5d9f5ab7bSJason Baron  * Copyright (C) 2009 Jason Baron <jbaron@redhat.com>
6d9f5ab7bSJason Baron  *
7d9f5ab7bSJason Baron  */
8d9f5ab7bSJason Baron #include <linux/jump_label.h>
9d9f5ab7bSJason Baron #include <linux/memory.h>
10d9f5ab7bSJason Baron #include <linux/uaccess.h>
11d9f5ab7bSJason Baron #include <linux/module.h>
12d9f5ab7bSJason Baron #include <linux/list.h>
13d9f5ab7bSJason Baron #include <linux/jhash.h>
14d9f5ab7bSJason Baron #include <linux/cpu.h>
15d9f5ab7bSJason Baron #include <asm/kprobes.h>
16d9f5ab7bSJason Baron #include <asm/alternative.h>
1735de5b06SAndy Lutomirski #include <asm/text-patching.h>
18d9f5ab7bSJason Baron 
19d9f5ab7bSJason Baron #ifdef HAVE_JUMP_LABEL
20d9f5ab7bSJason Baron 
21d9f5ab7bSJason Baron union jump_code_union {
22d9f5ab7bSJason Baron 	char code[JUMP_LABEL_NOP_SIZE];
23d9f5ab7bSJason Baron 	struct {
24d9f5ab7bSJason Baron 		char jump;
25d9f5ab7bSJason Baron 		int offset;
26d9f5ab7bSJason Baron 	} __attribute__((packed));
27d9f5ab7bSJason Baron };
28d9f5ab7bSJason Baron 
29fb40d7a8SSteven Rostedt static void bug_at(unsigned char *ip, int line)
30fb40d7a8SSteven Rostedt {
31fb40d7a8SSteven Rostedt 	/*
32fb40d7a8SSteven Rostedt 	 * The location is not an op that we were expecting.
33fb40d7a8SSteven Rostedt 	 * Something went wrong. Crash the box, as something could be
34fb40d7a8SSteven Rostedt 	 * corrupting the kernel.
35fb40d7a8SSteven Rostedt 	 */
366e03f66cSAndy Shevchenko 	pr_crit("jump_label: Fatal kernel bug, unexpected op at %pS [%p] (%5ph) %d\n", ip, ip, ip, line);
37fb40d7a8SSteven Rostedt 	BUG();
38fb40d7a8SSteven Rostedt }
39fb40d7a8SSteven Rostedt 
406fffacb3SPavel Tatashin static void __ref __jump_label_transform(struct jump_entry *entry,
41e71a5be1SJeremy Fitzhardinge 					 enum jump_label_type type,
429c85f3bdSSteven Rostedt 					 void *(*poker)(void *, const void *, size_t),
439c85f3bdSSteven Rostedt 					 int init)
44d9f5ab7bSJason Baron {
45*9fc0f798SArd Biesheuvel 	union jump_code_union jmp;
46a8fab074SHannes Frederic Sowa 	const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
479c85f3bdSSteven Rostedt 	const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];
48*9fc0f798SArd Biesheuvel 	const void *expect, *code;
49*9fc0f798SArd Biesheuvel 	int line;
50*9fc0f798SArd Biesheuvel 
51*9fc0f798SArd Biesheuvel 	jmp.jump = 0xe9;
52*9fc0f798SArd Biesheuvel 	jmp.offset = jump_entry_target(entry) -
53*9fc0f798SArd Biesheuvel 		     (jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
54d9f5ab7bSJason Baron 
556fffacb3SPavel Tatashin 	if (early_boot_irqs_disabled)
566fffacb3SPavel Tatashin 		poker = text_poke_early;
576fffacb3SPavel Tatashin 
5876b235c6SPeter Zijlstra 	if (type == JUMP_LABEL_JMP) {
59a8fab074SHannes Frederic Sowa 		if (init) {
60*9fc0f798SArd Biesheuvel 			expect = default_nop; line = __LINE__;
61a8fab074SHannes Frederic Sowa 		} else {
62*9fc0f798SArd Biesheuvel 			expect = ideal_nop; line = __LINE__;
63a8fab074SHannes Frederic Sowa 		}
649c85f3bdSSteven Rostedt 
65*9fc0f798SArd Biesheuvel 		code = &jmp.code;
669c85f3bdSSteven Rostedt 	} else {
679c85f3bdSSteven Rostedt 		if (init) {
68*9fc0f798SArd Biesheuvel 			expect = default_nop; line = __LINE__;
699c85f3bdSSteven Rostedt 		} else {
70*9fc0f798SArd Biesheuvel 			expect = &jmp.code; line = __LINE__;
719c85f3bdSSteven Rostedt 		}
72*9fc0f798SArd Biesheuvel 
73*9fc0f798SArd Biesheuvel 		code = ideal_nop;
749c85f3bdSSteven Rostedt 	}
75e71a5be1SJeremy Fitzhardinge 
76*9fc0f798SArd Biesheuvel 	if (memcmp((void *)jump_entry_code(entry), expect, JUMP_LABEL_NOP_SIZE))
77*9fc0f798SArd Biesheuvel 		bug_at((void *)jump_entry_code(entry), line);
78*9fc0f798SArd Biesheuvel 
7951b2c07bSJiri Kosina 	/*
8051b2c07bSJiri Kosina 	 * Make text_poke_bp() a default fallback poker.
8151b2c07bSJiri Kosina 	 *
8251b2c07bSJiri Kosina 	 * At the time the change is being done, just ignore whether we
8351b2c07bSJiri Kosina 	 * are doing nop -> jump or jump -> nop transition, and assume
8451b2c07bSJiri Kosina 	 * always nop being the 'currently valid' instruction
8551b2c07bSJiri Kosina 	 *
8651b2c07bSJiri Kosina 	 */
87*9fc0f798SArd Biesheuvel 	if (poker) {
88*9fc0f798SArd Biesheuvel 		(*poker)((void *)jump_entry_code(entry), code,
89*9fc0f798SArd Biesheuvel 			 JUMP_LABEL_NOP_SIZE);
90*9fc0f798SArd Biesheuvel 		return;
91*9fc0f798SArd Biesheuvel 	}
92*9fc0f798SArd Biesheuvel 
93*9fc0f798SArd Biesheuvel 	text_poke_bp((void *)jump_entry_code(entry), code, JUMP_LABEL_NOP_SIZE,
94*9fc0f798SArd Biesheuvel 		     (void *)jump_entry_code(entry) + JUMP_LABEL_NOP_SIZE);
95e71a5be1SJeremy Fitzhardinge }
96e71a5be1SJeremy Fitzhardinge 
97e71a5be1SJeremy Fitzhardinge void arch_jump_label_transform(struct jump_entry *entry,
98e71a5be1SJeremy Fitzhardinge 			       enum jump_label_type type)
99e71a5be1SJeremy Fitzhardinge {
100d9f5ab7bSJason Baron 	mutex_lock(&text_mutex);
101442e0973SLinus Torvalds 	__jump_label_transform(entry, type, NULL, 0);
102d9f5ab7bSJason Baron 	mutex_unlock(&text_mutex);
103d9f5ab7bSJason Baron }
104d9f5ab7bSJason Baron 
10511570da1SSteven Rostedt static enum {
10611570da1SSteven Rostedt 	JL_STATE_START,
10711570da1SSteven Rostedt 	JL_STATE_NO_UPDATE,
10811570da1SSteven Rostedt 	JL_STATE_UPDATE,
10911570da1SSteven Rostedt } jlstate __initdata_or_module = JL_STATE_START;
11011570da1SSteven Rostedt 
1119cdbe1cbSPeter Zijlstra __init_or_module void arch_jump_label_transform_static(struct jump_entry *entry,
112e71a5be1SJeremy Fitzhardinge 				      enum jump_label_type type)
113e71a5be1SJeremy Fitzhardinge {
11411570da1SSteven Rostedt 	/*
11511570da1SSteven Rostedt 	 * This function is called at boot up and when modules are
11611570da1SSteven Rostedt 	 * first loaded. Check if the default nop, the one that is
11711570da1SSteven Rostedt 	 * inserted at compile time, is the ideal nop. If it is, then
11811570da1SSteven Rostedt 	 * we do not need to update the nop, and we can leave it as is.
11911570da1SSteven Rostedt 	 * If it is not, then we need to update the nop to the ideal nop.
12011570da1SSteven Rostedt 	 */
12111570da1SSteven Rostedt 	if (jlstate == JL_STATE_START) {
12211570da1SSteven Rostedt 		const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP };
12311570da1SSteven Rostedt 		const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5];
12411570da1SSteven Rostedt 
12511570da1SSteven Rostedt 		if (memcmp(ideal_nop, default_nop, 5) != 0)
12611570da1SSteven Rostedt 			jlstate = JL_STATE_UPDATE;
12711570da1SSteven Rostedt 		else
12811570da1SSteven Rostedt 			jlstate = JL_STATE_NO_UPDATE;
12911570da1SSteven Rostedt 	}
13011570da1SSteven Rostedt 	if (jlstate == JL_STATE_UPDATE)
1319c85f3bdSSteven Rostedt 		__jump_label_transform(entry, type, text_poke_early, 1);
132e71a5be1SJeremy Fitzhardinge }
133e71a5be1SJeremy Fitzhardinge 
134d9f5ab7bSJason Baron #endif
135