summaryrefslogtreecommitdiffstats
path: root/arch/mips/lib/traps.c
blob: 5fc32fe7e429560588713c32abcef98217874538 (plain)
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#include <common.h>
#include <abort.h>
#include <asm/mipsregs.h>
#include <asm/ptrace.h>

static int mips_ignore_data_abort;
static int mips_data_abort_occurred;

void data_abort_mask(void)
{
	mips_data_abort_occurred = 0;
	mips_ignore_data_abort = 1;
}

int data_abort_unmask(void)
{
	mips_ignore_data_abort = 0;

	return mips_data_abort_occurred != 0;
}

void barebox_exc_handler(struct pt_regs *regs);

/*
 * Trap codes from OpenBSD trap.h
 */
#define T_INT			0	/* Interrupt pending */
#define T_TLB_MOD		1	/* TLB modified fault */
#define T_TLB_LD_MISS		2	/* TLB miss on load or ifetch */
#define T_TLB_ST_MISS		3	/* TLB miss on a store */
#define T_ADDR_ERR_LD		4	/* Address error on a load or ifetch */
#define T_ADDR_ERR_ST		5	/* Address error on a store */
#define T_BUS_ERR_IFETCH	6	/* Bus error on an ifetch */
#define T_BUS_ERR_LD_ST		7	/* Bus error on a load or store */
#define T_SYSCALL		8	/* System call */
#define T_BREAK			9	/* Breakpoint */
#define T_RES_INST		10	/* Reserved instruction exception */
#define T_COP_UNUSABLE		11	/* Coprocessor unusable */
#define T_OVFLOW		12	/* Arithmetic overflow */
#define T_TRAP			13	/* Trap instruction */
#define T_VCEI			14	/* Virtual coherency instruction */
#define T_FPE			15	/* Floating point exception */
#define T_IWATCH		16	/* Inst. Watch address reference */
#define T_DWATCH		23	/* Data Watch address reference */
#define T_VCED			31	/* Virtual coherency data */

#define CR_EXC_CODE             0x0000007c
#define CR_EXC_CODE_SHIFT       2

static inline u32 get_exc_code(u32 cause)
{
	return (cause & CR_EXC_CODE) >> CR_EXC_CODE_SHIFT;
}

static char *get_exc_name(u32 cause)
{
	switch (get_exc_code(cause)) {

	case T_INT:
		return "interrupt pending";

	case T_TLB_MOD:
		return "TLB modified";

	case T_TLB_LD_MISS:
		return "TLB miss on load or ifetch";

	case T_TLB_ST_MISS:
		return "TLB miss on store";

	case T_ADDR_ERR_LD:
		return "address error on load or ifetch";

	case T_ADDR_ERR_ST:
		return "address error on store";

	case T_BUS_ERR_IFETCH:
		return "bus error on ifetch";

	case T_BUS_ERR_LD_ST:
		return "bus error on load or store";

	case T_SYSCALL:
		return "system call";

	case T_BREAK:
		return "breakpoint";

	case T_RES_INST:
		return "reserved instruction";

	case T_COP_UNUSABLE:
		return "coprocessor unusable";

	case T_OVFLOW:
		return "arithmetic overflow";

	case T_TRAP:
		return "trap instruction";

	case T_VCEI:
		return "virtual coherency instruction";

	case T_FPE:
		return "floating point";

	case T_IWATCH:
		return "iwatch";

	case T_DWATCH:
		return "dwatch";

	case T_VCED:
		return "virtual coherency data";
	}

	return "unknown exception";
}

static void show_regs(const struct pt_regs *regs)
{
	int i;
	const int field = 2 * sizeof(unsigned long);

	/*
	 * Saved main processor registers
	 */
	for (i = 0; i < 32; ) {
		if ((i % 4) == 0)
			printf("$%2d   :", i);
		if (i == 0)
			printf(" %0*lx", field, 0UL);
		else if (i == 26 || i == 27)
			printf(" %*s", field, "");
		else
			printf(" %0*lx", field, regs->regs[i]);

		i++;
		if ((i % 4) == 0)
			printf("\n");
	}

	printf("Hi    : %0*lx\n", field, regs->hi);
	printf("Lo    : %0*lx\n", field, regs->lo);

	/*
	 * Saved cp0 registers
	 */
	printf("epc   : %0*lx\n", field, regs->cp0_epc);
	printf("ra    : %0*lx\n", field, regs->regs[31]);

	printf("Status: %08x\n", (uint32_t)regs->cp0_status);
	printf("Cause : %08x\n", (uint32_t)regs->cp0_cause);
	printf("Config: %08x\n\n", read_c0_config());
}

void barebox_exc_handler(struct pt_regs *regs)
{
	unsigned int cause = regs->cp0_cause;

	if (get_exc_code(cause) == T_ADDR_ERR_LD && mips_ignore_data_abort) {

		mips_data_abort_occurred = 1;

		regs->cp0_epc += 4;

		/*
		 * Don't let your children do this ...
		 */
		__asm__ __volatile__(
			"move\t$29, %0\n\t"
			"j\tret_from_exception"
			:/* no outputs */
			:"r" (&regs));

		/* Unreached */

	} else {
		printf("\nOoops, %s!\n\n", get_exc_name(cause));
		show_regs(regs);
	}

	hang();
}