Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/sh/kernel/traps.c
26424 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <linux/bug.h>
3
#include <linux/io.h>
4
#include <linux/types.h>
5
#include <linux/kdebug.h>
6
#include <linux/signal.h>
7
#include <linux/sched.h>
8
#include <linux/sched/debug.h>
9
#include <linux/sched/task_stack.h>
10
#include <linux/uaccess.h>
11
#include <linux/hardirq.h>
12
#include <linux/kernel.h>
13
#include <linux/kexec.h>
14
#include <linux/sched/signal.h>
15
16
#include <linux/extable.h>
17
#include <linux/module.h> /* print_modules */
18
19
#include <asm/ftrace.h>
20
#include <asm/unwinder.h>
21
#include <asm/traps.h>
22
23
static DEFINE_SPINLOCK(die_lock);
24
25
void __noreturn die(const char *str, struct pt_regs *regs, long err)
26
{
27
static int die_counter;
28
29
oops_enter();
30
31
spin_lock_irq(&die_lock);
32
console_verbose();
33
bust_spinlocks(1);
34
35
printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
36
print_modules();
37
show_regs(regs);
38
39
printk("Process: %s (pid: %d, stack limit = %p)\n", current->comm,
40
task_pid_nr(current), task_stack_page(current) + 1);
41
42
if (!user_mode(regs) || in_interrupt())
43
dump_mem("Stack: ", KERN_DEFAULT, regs->regs[15],
44
THREAD_SIZE + (unsigned long)task_stack_page(current));
45
46
notify_die(DIE_OOPS, str, regs, err, 255, SIGSEGV);
47
48
bust_spinlocks(0);
49
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
50
spin_unlock_irq(&die_lock);
51
oops_exit();
52
53
if (kexec_should_crash(current))
54
crash_kexec(regs);
55
56
if (in_interrupt())
57
panic("Fatal exception in interrupt");
58
59
if (panic_on_oops)
60
panic("Fatal exception");
61
62
make_task_dead(SIGSEGV);
63
}
64
65
void die_if_kernel(const char *str, struct pt_regs *regs, long err)
66
{
67
if (!user_mode(regs))
68
die(str, regs, err);
69
}
70
71
/*
72
* try and fix up kernelspace address errors
73
* - userspace errors just cause EFAULT to be returned, resulting in SEGV
74
* - kernel/userspace interfaces cause a jump to an appropriate handler
75
* - other kernel errors are bad
76
*/
77
void die_if_no_fixup(const char *str, struct pt_regs *regs, long err)
78
{
79
if (!user_mode(regs)) {
80
const struct exception_table_entry *fixup;
81
fixup = search_exception_tables(regs->pc);
82
if (fixup) {
83
regs->pc = fixup->fixup;
84
return;
85
}
86
87
die(str, regs, err);
88
}
89
}
90
91
#ifdef CONFIG_GENERIC_BUG
92
static void handle_BUG(struct pt_regs *regs)
93
{
94
const struct bug_entry *bug;
95
unsigned long bugaddr = regs->pc;
96
enum bug_trap_type tt;
97
98
if (!is_valid_bugaddr(bugaddr))
99
goto invalid;
100
101
bug = find_bug(bugaddr);
102
103
/* Switch unwinders when unwind_stack() is called */
104
if (bug->flags & BUGFLAG_UNWINDER)
105
unwinder_faulted = 1;
106
107
tt = report_bug(bugaddr, regs);
108
if (tt == BUG_TRAP_TYPE_WARN) {
109
regs->pc += instruction_size(bugaddr);
110
return;
111
}
112
113
invalid:
114
die("Kernel BUG", regs, TRAPA_BUG_OPCODE & 0xff);
115
}
116
117
int is_valid_bugaddr(unsigned long addr)
118
{
119
insn_size_t opcode;
120
121
if (addr < PAGE_OFFSET)
122
return 0;
123
if (get_kernel_nofault(opcode, (insn_size_t *)addr))
124
return 0;
125
if (opcode == TRAPA_BUG_OPCODE)
126
return 1;
127
128
return 0;
129
}
130
#endif
131
132
/*
133
* Generic trap handler.
134
*/
135
BUILD_TRAP_HANDLER(debug)
136
{
137
TRAP_HANDLER_DECL;
138
139
/* Rewind */
140
regs->pc -= instruction_size(__raw_readw(regs->pc - 4));
141
142
if (notify_die(DIE_TRAP, "debug trap", regs, 0, vec & 0xff,
143
SIGTRAP) == NOTIFY_STOP)
144
return;
145
146
force_sig(SIGTRAP);
147
}
148
149
/*
150
* Special handler for BUG() traps.
151
*/
152
BUILD_TRAP_HANDLER(bug)
153
{
154
TRAP_HANDLER_DECL;
155
156
/* Rewind */
157
regs->pc -= instruction_size(__raw_readw(regs->pc - 4));
158
159
if (notify_die(DIE_TRAP, "bug trap", regs, 0, TRAPA_BUG_OPCODE & 0xff,
160
SIGTRAP) == NOTIFY_STOP)
161
return;
162
163
#ifdef CONFIG_GENERIC_BUG
164
if (__kernel_text_address(instruction_pointer(regs))) {
165
insn_size_t insn = *(insn_size_t *)instruction_pointer(regs);
166
if (insn == TRAPA_BUG_OPCODE)
167
handle_BUG(regs);
168
return;
169
}
170
#endif
171
172
force_sig(SIGTRAP);
173
}
174
175
BUILD_TRAP_HANDLER(nmi)
176
{
177
TRAP_HANDLER_DECL;
178
179
arch_ftrace_nmi_enter();
180
181
nmi_enter();
182
this_cpu_inc(irq_stat.__nmi_count);
183
184
switch (notify_die(DIE_NMI, "NMI", regs, 0, vec & 0xff, SIGINT)) {
185
case NOTIFY_OK:
186
case NOTIFY_STOP:
187
break;
188
case NOTIFY_BAD:
189
die("Fatal Non-Maskable Interrupt", regs, SIGINT);
190
default:
191
printk(KERN_ALERT "Got NMI, but nobody cared. Ignoring...\n");
192
break;
193
}
194
195
nmi_exit();
196
197
arch_ftrace_nmi_exit();
198
}
199
200