summaryrefslogtreecommitdiff
path: root/arch/cris/arch-v32/kernel/traps.c
blob: 6e3787045560fa5209ec361e5959cb0b157d34a6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
 * Copyright (C) 2003, Axis Communications AB.
 */

#include <linux/config.h>
#include <linux/ptrace.h>
#include <asm/uaccess.h>

#include <asm/arch/hwregs/supp_reg.h>

extern void reset_watchdog(void);
extern void stop_watchdog(void);

extern int raw_printk(const char *fmt, ...);

void
show_registers(struct pt_regs *regs)
{
	/*
	 * It's possible to use either the USP register or current->thread.usp.
	 * USP might not correspond to the current proccess for all cases this
	 * function is called, and current->thread.usp isn't up to date for the
	 * current proccess. Experience shows that using USP is the way to go.
	 */
	unsigned long usp;
	unsigned long d_mmu_cause;
	unsigned long i_mmu_cause;

	usp = rdusp();

	raw_printk("CPU: %d\n", smp_processor_id());

	raw_printk("ERP: %08lx SRP: %08lx  CCS: %08lx USP: %08lx MOF: %08lx\n",
		regs->erp, regs->srp, regs->ccs, usp, regs->mof);

	raw_printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
		regs->r0, regs->r1, regs->r2, regs->r3);

	raw_printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
		regs->r4, regs->r5, regs->r6, regs->r7);

	raw_printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
		regs->r8, regs->r9, regs->r10, regs->r11);

	raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n",
		regs->r12, regs->r13, regs->orig_r10, regs->acr);

	raw_printk("sp: %08lx\n", regs);

	SUPP_BANK_SEL(BANK_IM);
	SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause);

	SUPP_BANK_SEL(BANK_DM);
	SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause);

	raw_printk("       Data MMU Cause: %08lx\n", d_mmu_cause);
	raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);

	raw_printk("Process %s (pid: %d, stackpage: %08lx)\n",
		current->comm, current->pid, (unsigned long) current);

	/* Show additional info if in kernel-mode. */
	if (!user_mode(regs)) {
		int i;
		unsigned char c;

		show_stack(NULL, (unsigned long *) usp);

		/*
		 * If the previous stack-dump wasn't a kernel one, dump the
		 * kernel stack now.
		 */
		if (usp != 0)
			show_stack(NULL, NULL);

		raw_printk("\nCode: ");

		if (regs->erp < PAGE_OFFSET)
			goto bad_value;

		/*
		 * Quite often the value at regs->erp doesn't point to the
		 * interesting instruction, which often is the previous
		 * instruction. So dump at an offset large enough that the
		 * instruction decoding should be in sync at the interesting
		 * point, but small enough to fit on a row. The regs->erp
		 * location is pointed out in a ksymoops-friendly way by
		 * wrapping the byte for that address in parenthesis.
		 */
		for (i = -12; i < 12; i++) {
			if (__get_user(c, &((unsigned char *) regs->erp)[i])) {
bad_value:
				raw_printk(" Bad IP value.");
				break;
			}

			if (i == 0)
				raw_printk("(%02x) ", c);
			else
				raw_printk("%02x ", c);
		}

		raw_printk("\n");
	}
}

/*
 * This gets called from entry.S when the watchdog has bitten. Show something
 * similiar to an Oops dump, and if the kernel if configured to be a nice doggy;
 * halt instead of reboot.
 */
void
watchdog_bite_hook(struct pt_regs *regs)
{
#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
	local_irq_disable();
	stop_watchdog();
	show_registers(regs);

	while (1)
		; /* Do nothing. */
#else
	show_registers(regs);
#endif
}

/* This is normally the Oops function. */
void
die_if_kernel(const char *str, struct pt_regs *regs, long err)
{
	if (user_mode(regs))
		return;

#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
	/*
	 * This printout might take too long and could trigger
	 * the watchdog normally. If NICE_DOGGY is set, simply
	 * stop the watchdog during the printout.
	 */
	stop_watchdog();
#endif

	raw_printk("%s: %04lx\n", str, err & 0xffff);

	show_registers(regs);

#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
	reset_watchdog();
#endif

	do_exit(SIGSEGV);
}

void arch_enable_nmi(void)
{
	unsigned long flags;
	local_save_flags(flags);
	flags |= (1<<30); /* NMI M flag is at bit 30 */
	local_irq_restore(flags);
}