.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | #include <linux/extable.h> |
---|
2 | 3 | #include <linux/uaccess.h> |
---|
3 | 4 | #include <linux/sched/debug.h> |
---|
4 | 5 | #include <xen/xen.h> |
---|
5 | 6 | |
---|
6 | 7 | #include <asm/fpu/internal.h> |
---|
| 8 | +#include <asm/sev-es.h> |
---|
7 | 9 | #include <asm/traps.h> |
---|
8 | 10 | #include <asm/kdebug.h> |
---|
9 | 11 | |
---|
10 | 12 | typedef bool (*ex_handler_t)(const struct exception_table_entry *, |
---|
11 | | - struct pt_regs *, int); |
---|
| 13 | + struct pt_regs *, int, unsigned long, |
---|
| 14 | + unsigned long); |
---|
12 | 15 | |
---|
13 | 16 | static inline unsigned long |
---|
14 | 17 | ex_fixup_addr(const struct exception_table_entry *x) |
---|
.. | .. |
---|
22 | 25 | } |
---|
23 | 26 | |
---|
24 | 27 | __visible bool ex_handler_default(const struct exception_table_entry *fixup, |
---|
25 | | - struct pt_regs *regs, int trapnr) |
---|
| 28 | + struct pt_regs *regs, int trapnr, |
---|
| 29 | + unsigned long error_code, |
---|
| 30 | + unsigned long fault_addr) |
---|
26 | 31 | { |
---|
27 | 32 | regs->ip = ex_fixup_addr(fixup); |
---|
28 | 33 | return true; |
---|
.. | .. |
---|
30 | 35 | EXPORT_SYMBOL(ex_handler_default); |
---|
31 | 36 | |
---|
32 | 37 | __visible bool ex_handler_fault(const struct exception_table_entry *fixup, |
---|
33 | | - struct pt_regs *regs, int trapnr) |
---|
| 38 | + struct pt_regs *regs, int trapnr, |
---|
| 39 | + unsigned long error_code, |
---|
| 40 | + unsigned long fault_addr) |
---|
34 | 41 | { |
---|
35 | 42 | regs->ip = ex_fixup_addr(fixup); |
---|
36 | 43 | regs->ax = trapnr; |
---|
37 | 44 | return true; |
---|
38 | 45 | } |
---|
39 | 46 | EXPORT_SYMBOL_GPL(ex_handler_fault); |
---|
40 | | - |
---|
41 | | -/* |
---|
42 | | - * Handler for UD0 exception following a failed test against the |
---|
43 | | - * result of a refcount inc/dec/add/sub. |
---|
44 | | - */ |
---|
45 | | -__visible bool ex_handler_refcount(const struct exception_table_entry *fixup, |
---|
46 | | - struct pt_regs *regs, int trapnr) |
---|
47 | | -{ |
---|
48 | | - /* First unconditionally saturate the refcount. */ |
---|
49 | | - *(int *)regs->cx = INT_MIN / 2; |
---|
50 | | - |
---|
51 | | - /* |
---|
52 | | - * Strictly speaking, this reports the fixup destination, not |
---|
53 | | - * the fault location, and not the actually overflowing |
---|
54 | | - * instruction, which is the instruction before the "js", but |
---|
55 | | - * since that instruction could be a variety of lengths, just |
---|
56 | | - * report the location after the overflow, which should be close |
---|
57 | | - * enough for finding the overflow, as it's at least back in |
---|
58 | | - * the function, having returned from .text.unlikely. |
---|
59 | | - */ |
---|
60 | | - regs->ip = ex_fixup_addr(fixup); |
---|
61 | | - |
---|
62 | | - /* |
---|
63 | | - * This function has been called because either a negative refcount |
---|
64 | | - * value was seen by any of the refcount functions, or a zero |
---|
65 | | - * refcount value was seen by refcount_dec(). |
---|
66 | | - * |
---|
67 | | - * If we crossed from INT_MAX to INT_MIN, OF (Overflow Flag: result |
---|
68 | | - * wrapped around) will be set. Additionally, seeing the refcount |
---|
69 | | - * reach 0 will set ZF (Zero Flag: result was zero). In each of |
---|
70 | | - * these cases we want a report, since it's a boundary condition. |
---|
71 | | - * The SF case is not reported since it indicates post-boundary |
---|
72 | | - * manipulations below zero or above INT_MAX. And if none of the |
---|
73 | | - * flags are set, something has gone very wrong, so report it. |
---|
74 | | - */ |
---|
75 | | - if (regs->flags & (X86_EFLAGS_OF | X86_EFLAGS_ZF)) { |
---|
76 | | - bool zero = regs->flags & X86_EFLAGS_ZF; |
---|
77 | | - |
---|
78 | | - refcount_error_report(regs, zero ? "hit zero" : "overflow"); |
---|
79 | | - } else if ((regs->flags & X86_EFLAGS_SF) == 0) { |
---|
80 | | - /* Report if none of OF, ZF, nor SF are set. */ |
---|
81 | | - refcount_error_report(regs, "unexpected saturation"); |
---|
82 | | - } |
---|
83 | | - |
---|
84 | | - return true; |
---|
85 | | -} |
---|
86 | | -EXPORT_SYMBOL(ex_handler_refcount); |
---|
87 | 47 | |
---|
88 | 48 | /* |
---|
89 | 49 | * Handler for when we fail to restore a task's FPU state. We should never get |
---|
.. | .. |
---|
96 | 56 | * out all the FPU registers) if we can't restore from the task's FPU state. |
---|
97 | 57 | */ |
---|
98 | 58 | __visible bool ex_handler_fprestore(const struct exception_table_entry *fixup, |
---|
99 | | - struct pt_regs *regs, int trapnr) |
---|
| 59 | + struct pt_regs *regs, int trapnr, |
---|
| 60 | + unsigned long error_code, |
---|
| 61 | + unsigned long fault_addr) |
---|
100 | 62 | { |
---|
101 | 63 | regs->ip = ex_fixup_addr(fixup); |
---|
102 | 64 | |
---|
.. | .. |
---|
108 | 70 | } |
---|
109 | 71 | EXPORT_SYMBOL_GPL(ex_handler_fprestore); |
---|
110 | 72 | |
---|
111 | | -__visible bool ex_handler_ext(const struct exception_table_entry *fixup, |
---|
112 | | - struct pt_regs *regs, int trapnr) |
---|
| 73 | +__visible bool ex_handler_uaccess(const struct exception_table_entry *fixup, |
---|
| 74 | + struct pt_regs *regs, int trapnr, |
---|
| 75 | + unsigned long error_code, |
---|
| 76 | + unsigned long fault_addr) |
---|
113 | 77 | { |
---|
114 | | - /* Special hack for uaccess_err */ |
---|
115 | | - current->thread.uaccess_err = 1; |
---|
| 78 | + WARN_ONCE(trapnr == X86_TRAP_GP, "General protection fault in user access. Non-canonical address?"); |
---|
116 | 79 | regs->ip = ex_fixup_addr(fixup); |
---|
117 | 80 | return true; |
---|
118 | 81 | } |
---|
119 | | -EXPORT_SYMBOL(ex_handler_ext); |
---|
| 82 | +EXPORT_SYMBOL(ex_handler_uaccess); |
---|
| 83 | + |
---|
| 84 | +__visible bool ex_handler_copy(const struct exception_table_entry *fixup, |
---|
| 85 | + struct pt_regs *regs, int trapnr, |
---|
| 86 | + unsigned long error_code, |
---|
| 87 | + unsigned long fault_addr) |
---|
| 88 | +{ |
---|
| 89 | + WARN_ONCE(trapnr == X86_TRAP_GP, "General protection fault in user access. Non-canonical address?"); |
---|
| 90 | + regs->ip = ex_fixup_addr(fixup); |
---|
| 91 | + regs->ax = trapnr; |
---|
| 92 | + return true; |
---|
| 93 | +} |
---|
| 94 | +EXPORT_SYMBOL(ex_handler_copy); |
---|
120 | 95 | |
---|
121 | 96 | __visible bool ex_handler_rdmsr_unsafe(const struct exception_table_entry *fixup, |
---|
122 | | - struct pt_regs *regs, int trapnr) |
---|
| 97 | + struct pt_regs *regs, int trapnr, |
---|
| 98 | + unsigned long error_code, |
---|
| 99 | + unsigned long fault_addr) |
---|
123 | 100 | { |
---|
124 | | - if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pF)\n", |
---|
| 101 | + if (pr_warn_once("unchecked MSR access error: RDMSR from 0x%x at rIP: 0x%lx (%pS)\n", |
---|
125 | 102 | (unsigned int)regs->cx, regs->ip, (void *)regs->ip)) |
---|
126 | 103 | show_stack_regs(regs); |
---|
127 | 104 | |
---|
.. | .. |
---|
134 | 111 | EXPORT_SYMBOL(ex_handler_rdmsr_unsafe); |
---|
135 | 112 | |
---|
136 | 113 | __visible bool ex_handler_wrmsr_unsafe(const struct exception_table_entry *fixup, |
---|
137 | | - struct pt_regs *regs, int trapnr) |
---|
| 114 | + struct pt_regs *regs, int trapnr, |
---|
| 115 | + unsigned long error_code, |
---|
| 116 | + unsigned long fault_addr) |
---|
138 | 117 | { |
---|
139 | | - if (pr_warn_once("unchecked MSR access error: WRMSR to 0x%x (tried to write 0x%08x%08x) at rIP: 0x%lx (%pF)\n", |
---|
| 118 | + if (pr_warn_once("unchecked MSR access error: WRMSR to 0x%x (tried to write 0x%08x%08x) at rIP: 0x%lx (%pS)\n", |
---|
140 | 119 | (unsigned int)regs->cx, (unsigned int)regs->dx, |
---|
141 | 120 | (unsigned int)regs->ax, regs->ip, (void *)regs->ip)) |
---|
142 | 121 | show_stack_regs(regs); |
---|
.. | .. |
---|
148 | 127 | EXPORT_SYMBOL(ex_handler_wrmsr_unsafe); |
---|
149 | 128 | |
---|
150 | 129 | __visible bool ex_handler_clear_fs(const struct exception_table_entry *fixup, |
---|
151 | | - struct pt_regs *regs, int trapnr) |
---|
| 130 | + struct pt_regs *regs, int trapnr, |
---|
| 131 | + unsigned long error_code, |
---|
| 132 | + unsigned long fault_addr) |
---|
152 | 133 | { |
---|
153 | 134 | if (static_cpu_has(X86_BUG_NULL_SEG)) |
---|
154 | 135 | asm volatile ("mov %0, %%fs" : : "rm" (__USER_DS)); |
---|
155 | 136 | asm volatile ("mov %0, %%fs" : : "rm" (0)); |
---|
156 | | - return ex_handler_default(fixup, regs, trapnr); |
---|
| 137 | + return ex_handler_default(fixup, regs, trapnr, error_code, fault_addr); |
---|
157 | 138 | } |
---|
158 | 139 | EXPORT_SYMBOL(ex_handler_clear_fs); |
---|
159 | 140 | |
---|
160 | | -__visible bool ex_has_fault_handler(unsigned long ip) |
---|
| 141 | +enum handler_type ex_get_fault_handler_type(unsigned long ip) |
---|
161 | 142 | { |
---|
162 | 143 | const struct exception_table_entry *e; |
---|
163 | 144 | ex_handler_t handler; |
---|
164 | 145 | |
---|
165 | 146 | e = search_exception_tables(ip); |
---|
166 | 147 | if (!e) |
---|
167 | | - return false; |
---|
| 148 | + return EX_HANDLER_NONE; |
---|
168 | 149 | handler = ex_fixup_handler(e); |
---|
169 | | - |
---|
170 | | - return handler == ex_handler_fault; |
---|
| 150 | + if (handler == ex_handler_fault) |
---|
| 151 | + return EX_HANDLER_FAULT; |
---|
| 152 | + else if (handler == ex_handler_uaccess || handler == ex_handler_copy) |
---|
| 153 | + return EX_HANDLER_UACCESS; |
---|
| 154 | + else |
---|
| 155 | + return EX_HANDLER_OTHER; |
---|
171 | 156 | } |
---|
172 | 157 | |
---|
173 | 158 | __nocfi |
---|
174 | | -int fixup_exception(struct pt_regs *regs, int trapnr) |
---|
| 159 | +int fixup_exception(struct pt_regs *regs, int trapnr, unsigned long error_code, |
---|
| 160 | + unsigned long fault_addr) |
---|
175 | 161 | { |
---|
176 | 162 | const struct exception_table_entry *e; |
---|
177 | 163 | ex_handler_t handler; |
---|
.. | .. |
---|
195 | 181 | return 0; |
---|
196 | 182 | |
---|
197 | 183 | handler = ex_fixup_handler(e); |
---|
198 | | - return handler(e, regs, trapnr); |
---|
| 184 | + return handler(e, regs, trapnr, error_code, fault_addr); |
---|
199 | 185 | } |
---|
200 | 186 | |
---|
201 | 187 | extern unsigned int early_recursion_flag; |
---|
.. | .. |
---|
231 | 217 | * result in a hard-to-debug panic. |
---|
232 | 218 | * |
---|
233 | 219 | * Keep in mind that not all vectors actually get here. Early |
---|
234 | | - * fage faults, for example, are special. |
---|
| 220 | + * page faults, for example, are special. |
---|
235 | 221 | */ |
---|
236 | | - if (fixup_exception(regs, trapnr)) |
---|
| 222 | + if (fixup_exception(regs, trapnr, regs->orig_ax, 0)) |
---|
237 | 223 | return; |
---|
238 | 224 | |
---|
239 | | - if (fixup_bug(regs, trapnr)) |
---|
240 | | - return; |
---|
| 225 | + if (trapnr == X86_TRAP_UD) { |
---|
| 226 | + if (report_bug(regs->ip, regs) == BUG_TRAP_TYPE_WARN) { |
---|
| 227 | + /* Skip the ud2. */ |
---|
| 228 | + regs->ip += LEN_UD2; |
---|
| 229 | + return; |
---|
| 230 | + } |
---|
| 231 | + |
---|
| 232 | + /* |
---|
| 233 | + * If this was a BUG and report_bug returns or if this |
---|
| 234 | + * was just a normal #UD, we want to continue onward and |
---|
| 235 | + * crash. |
---|
| 236 | + */ |
---|
| 237 | + } |
---|
241 | 238 | |
---|
242 | 239 | fail: |
---|
243 | 240 | early_printk("PANIC: early exception 0x%02x IP %lx:%lx error %lx cr2 0x%lx\n", |
---|