xref: /openbmc/linux/arch/x86/kernel/cet.c (revision 2da5b91f)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include <linux/ptrace.h>
4 #include <asm/bugs.h>
5 #include <asm/traps.h>
6 
7 static __ro_after_init bool ibt_fatal = true;
8 
9 extern void ibt_selftest_ip(void); /* code label defined in asm below */
10 
11 enum cp_error_code {
12 	CP_EC        = (1 << 15) - 1,
13 
14 	CP_RET       = 1,
15 	CP_IRET      = 2,
16 	CP_ENDBR     = 3,
17 	CP_RSTRORSSP = 4,
18 	CP_SETSSBSY  = 5,
19 
20 	CP_ENCL	     = 1 << 15,
21 };
22 
23 DEFINE_IDTENTRY_ERRORCODE(exc_control_protection)
24 {
25 	if (!cpu_feature_enabled(X86_FEATURE_IBT)) {
26 		pr_err("Unexpected #CP\n");
27 		BUG();
28 	}
29 
30 	if (WARN_ON_ONCE(user_mode(regs) || (error_code & CP_EC) != CP_ENDBR))
31 		return;
32 
33 	if (unlikely(regs->ip == (unsigned long)&ibt_selftest_ip)) {
34 		regs->ax = 0;
35 		return;
36 	}
37 
38 	pr_err("Missing ENDBR: %pS\n", (void *)instruction_pointer(regs));
39 	if (!ibt_fatal) {
40 		printk(KERN_DEFAULT CUT_HERE);
41 		__warn(__FILE__, __LINE__, (void *)regs->ip, TAINT_WARN, regs, NULL);
42 		return;
43 	}
44 	BUG();
45 }
46 
47 /* Must be noinline to ensure uniqueness of ibt_selftest_ip. */
48 noinline bool ibt_selftest(void)
49 {
50 	unsigned long ret;
51 
52 	asm ("	lea ibt_selftest_ip(%%rip), %%rax\n\t"
53 	     ANNOTATE_RETPOLINE_SAFE
54 	     "	jmp *%%rax\n\t"
55 	     "ibt_selftest_ip:\n\t"
56 	     UNWIND_HINT_FUNC
57 	     ANNOTATE_NOENDBR
58 	     "	nop\n\t"
59 
60 	     : "=a" (ret) : : "memory");
61 
62 	return !ret;
63 }
64 
65 static int __init ibt_setup(char *str)
66 {
67 	if (!strcmp(str, "off"))
68 		setup_clear_cpu_cap(X86_FEATURE_IBT);
69 
70 	if (!strcmp(str, "warn"))
71 		ibt_fatal = false;
72 
73 	return 1;
74 }
75 
76 __setup("ibt=", ibt_setup);
77