Blame view

arch/tile/kernel/traps.c 7.77 KB
867e359b9   Chris Metcalf   arch/tile: core s...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  /*
   * Copyright 2010 Tilera Corporation. All Rights Reserved.
   *
   *   This program is free software; you can redistribute it and/or
   *   modify it under the terms of the GNU General Public License
   *   as published by the Free Software Foundation, version 2.
   *
   *   This program is distributed in the hope that it will be useful, but
   *   WITHOUT ANY WARRANTY; without even the implied warranty of
   *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
   *   NON INFRINGEMENT.  See the GNU General Public License for
   *   more details.
   */
  
  #include <linux/sched.h>
  #include <linux/kernel.h>
  #include <linux/kprobes.h>
  #include <linux/module.h>
  #include <linux/reboot.h>
  #include <linux/uaccess.h>
  #include <linux/ptrace.h>
0707ad30d   Chris Metcalf   arch/tile: Miscel...
22
23
  #include <asm/stack.h>
  #include <asm/traps.h>
867e359b9   Chris Metcalf   arch/tile: core s...
24
25
26
  
  #include <arch/interrupts.h>
  #include <arch/spr_def.h>
eb7c792da   Chris Metcalf   arch/tile: factor...
27
  #include <arch/opcode.h>
867e359b9   Chris Metcalf   arch/tile: core s...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  
  void __init trap_init(void)
  {
  	/* Nothing needed here since we link code at .intrpt1 */
  }
  
  int unaligned_fixup = 1;
  
  static int __init setup_unaligned_fixup(char *str)
  {
  	/*
  	 * Say "=-1" to completely disable it.  If you just do "=0", we
  	 * will still parse the instruction, then fire a SIGBUS with
  	 * the correct address from inside the single_step code.
  	 */
  	long val;
  	if (strict_strtol(str, 0, &val) != 0)
  		return 0;
  	unaligned_fixup = val;
0707ad30d   Chris Metcalf   arch/tile: Miscel...
47
48
  	pr_info("Fixups for unaligned data accesses are %s
  ",
867e359b9   Chris Metcalf   arch/tile: core s...
49
50
51
52
53
54
55
56
57
58
59
60
61
  	       unaligned_fixup >= 0 ?
  	       (unaligned_fixup ? "enabled" : "disabled") :
  	       "completely disabled");
  	return 1;
  }
  __setup("unaligned_fixup=", setup_unaligned_fixup);
  
  #if CHIP_HAS_TILE_DMA()
  
  static int dma_disabled;
  
  static int __init nodma(char *str)
  {
0707ad30d   Chris Metcalf   arch/tile: Miscel...
62
63
  	pr_info("User-space DMA is disabled
  ");
867e359b9   Chris Metcalf   arch/tile: core s...
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
  	dma_disabled = 1;
  	return 1;
  }
  __setup("nodma", nodma);
  
  /* How to decode SPR_GPV_REASON */
  #define IRET_ERROR (1U << 31)
  #define MT_ERROR   (1U << 30)
  #define MF_ERROR   (1U << 29)
  #define SPR_INDEX  ((1U << 15) - 1)
  #define SPR_MPL_SHIFT  9  /* starting bit position for MPL encoded in SPR */
  
  /*
   * See if this GPV is just to notify the kernel of SPR use and we can
   * retry the user instruction after adjusting some MPLs suitably.
   */
  static int retry_gpv(unsigned int gpv_reason)
  {
  	int mpl;
  
  	if (gpv_reason & IRET_ERROR)
  		return 0;
  
  	BUG_ON((gpv_reason & (MT_ERROR|MF_ERROR)) == 0);
  	mpl = (gpv_reason & SPR_INDEX) >> SPR_MPL_SHIFT;
  	if (mpl == INT_DMA_NOTIFY && !dma_disabled) {
  		/* User is turning on DMA. Allow it and retry. */
  		printk(KERN_DEBUG "Process %d/%s is now enabled for DMA
  ",
  		       current->pid, current->comm);
  		BUG_ON(current->thread.tile_dma_state.enabled);
  		current->thread.tile_dma_state.enabled = 1;
  		grant_dma_mpls();
  		return 1;
  	}
  
  	return 0;
  }
  
  #endif /* CHIP_HAS_TILE_DMA() */
867e359b9   Chris Metcalf   arch/tile: core s...
104
  #ifdef __tilegx__
0707ad30d   Chris Metcalf   arch/tile: Miscel...
105
  #define bundle_bits tilegx_bundle_bits
867e359b9   Chris Metcalf   arch/tile: core s...
106
  #else
0707ad30d   Chris Metcalf   arch/tile: Miscel...
107
  #define bundle_bits tile_bundle_bits
867e359b9   Chris Metcalf   arch/tile: core s...
108
  #endif
0707ad30d   Chris Metcalf   arch/tile: Miscel...
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  extern bundle_bits bpt_code;
  
  asm(".pushsection .rodata.bpt_code,\"a\";"
      ".align 8;"
      "bpt_code: bpt;"
      ".size bpt_code,.-bpt_code;"
      ".popsection");
  
  static int special_ill(bundle_bits bundle, int *sigp, int *codep)
  {
  	int sig, code, maxcode;
  
  	if (bundle == bpt_code) {
  		*sigp = SIGTRAP;
  		*codep = TRAP_BRKPT;
  		return 1;
  	}
  
  	/* If it's a "raise" bundle, then "ill" must be in pipe X1. */
  #ifdef __tilegx__
  	if ((bundle & TILEGX_BUNDLE_MODE_MASK) != 0)
  		return 0;
1fcbe027b   Chris Metcalf   arch/tile: suppor...
131
132
133
  	if (get_Opcode_X1(bundle) != RRR_0_OPCODE_X1)
  		return 0;
  	if (get_RRROpcodeExtension_X1(bundle) != UNARY_RRR_0_OPCODE_X1)
0707ad30d   Chris Metcalf   arch/tile: Miscel...
134
135
136
137
  		return 0;
  	if (get_UnaryOpcodeExtension_X1(bundle) != ILL_UNARY_OPCODE_X1)
  		return 0;
  #else
eb7c792da   Chris Metcalf   arch/tile: factor...
138
  	if (bundle & TILEPRO_BUNDLE_Y_ENCODING_MASK)
0707ad30d   Chris Metcalf   arch/tile: Miscel...
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
185
186
187
188
189
190
191
192
193
194
195
196
  		return 0;
  	if (get_Opcode_X1(bundle) != SHUN_0_OPCODE_X1)
  		return 0;
  	if (get_UnShOpcodeExtension_X1(bundle) != UN_0_SHUN_0_OPCODE_X1)
  		return 0;
  	if (get_UnOpcodeExtension_X1(bundle) != ILL_UN_0_SHUN_0_OPCODE_X1)
  		return 0;
  #endif
  
  	/* Check that the magic distinguishers are set to mean "raise". */
  	if (get_Dest_X1(bundle) != 29 || get_SrcA_X1(bundle) != 37)
  		return 0;
  
  	/* There must be an "addli zero, zero, VAL" in X0. */
  	if (get_Opcode_X0(bundle) != ADDLI_OPCODE_X0)
  		return 0;
  	if (get_Dest_X0(bundle) != TREG_ZERO)
  		return 0;
  	if (get_SrcA_X0(bundle) != TREG_ZERO)
  		return 0;
  
  	/*
  	 * Validate the proposed signal number and si_code value.
  	 * Note that we embed these in the static instruction itself
  	 * so that we perturb the register state as little as possible
  	 * at the time of the actual fault; it's unlikely you'd ever
  	 * need to dynamically choose which kind of fault to raise
  	 * from user space.
  	 */
  	sig = get_Imm16_X0(bundle) & 0x3f;
  	switch (sig) {
  	case SIGILL:
  		maxcode = NSIGILL;
  		break;
  	case SIGFPE:
  		maxcode = NSIGFPE;
  		break;
  	case SIGSEGV:
  		maxcode = NSIGSEGV;
  		break;
  	case SIGBUS:
  		maxcode = NSIGBUS;
  		break;
  	case SIGTRAP:
  		maxcode = NSIGTRAP;
  		break;
  	default:
  		return 0;
  	}
  	code = (get_Imm16_X0(bundle) >> 6) & 0xf;
  	if (code <= 0 || code > maxcode)
  		return 0;
  
  	/* Make it the requested signal. */
  	*sigp = sig;
  	*codep = code | __SI_FAULT;
  	return 1;
  }
867e359b9   Chris Metcalf   arch/tile: core s...
197
198
199
200
201
202
  void __kprobes do_trap(struct pt_regs *regs, int fault_num,
  		       unsigned long reason)
  {
  	siginfo_t info = { 0 };
  	int signo, code;
  	unsigned long address;
0707ad30d   Chris Metcalf   arch/tile: Miscel...
203
  	bundle_bits instr;
867e359b9   Chris Metcalf   arch/tile: core s...
204
205
206
207
208
209
210
211
212
213
214
  
  	/* Re-enable interrupts. */
  	local_irq_enable();
  
  	/*
  	 * If it hits in kernel mode and we can't fix it up, just exit the
  	 * current process and hope for the best.
  	 */
  	if (!user_mode(regs)) {
  		if (fixup_exception(regs))  /* only UNALIGN_DATA in practice */
  			return;
0707ad30d   Chris Metcalf   arch/tile: Miscel...
215
216
  		pr_alert("Kernel took bad trap %d at PC %#lx
  ",
867e359b9   Chris Metcalf   arch/tile: core s...
217
218
  		       fault_num, regs->pc);
  		if (fault_num == INT_GPV)
0707ad30d   Chris Metcalf   arch/tile: Miscel...
219
220
  			pr_alert("GPV_REASON is %#lx
  ", reason);
867e359b9   Chris Metcalf   arch/tile: core s...
221
222
223
224
225
226
227
  		show_regs(regs);
  		do_exit(SIGKILL);  /* FIXME: implement i386 die() */
  		return;
  	}
  
  	switch (fault_num) {
  	case INT_ILL:
0707ad30d   Chris Metcalf   arch/tile: Miscel...
228
229
230
  		if (copy_from_user(&instr, (void __user *)regs->pc,
  				   sizeof(instr))) {
  			pr_err("Unreadable instruction for INT_ILL:"
867e359b9   Chris Metcalf   arch/tile: core s...
231
232
233
234
235
  			       " %#lx
  ", regs->pc);
  			do_exit(SIGKILL);
  			return;
  		}
0707ad30d   Chris Metcalf   arch/tile: Miscel...
236
  		if (!special_ill(instr, &signo, &code)) {
867e359b9   Chris Metcalf   arch/tile: core s...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
  			signo = SIGILL;
  			code = ILL_ILLOPC;
  		}
  		address = regs->pc;
  		break;
  	case INT_GPV:
  #if CHIP_HAS_TILE_DMA()
  		if (retry_gpv(reason))
  			return;
  #endif
  		/*FALLTHROUGH*/
  	case INT_UDN_ACCESS:
  	case INT_IDN_ACCESS:
  #if CHIP_HAS_SN()
  	case INT_SN_ACCESS:
  #endif
  		signo = SIGILL;
  		code = ILL_PRVREG;
  		address = regs->pc;
  		break;
  	case INT_SWINT_3:
  	case INT_SWINT_2:
  	case INT_SWINT_0:
  		signo = SIGILL;
  		code = ILL_ILLTRP;
  		address = regs->pc;
  		break;
  	case INT_UNALIGN_DATA:
233325b94   Chris Metcalf   arch/tile: enable...
265
  #ifndef __tilegx__  /* Emulated support for single step debugging */
867e359b9   Chris Metcalf   arch/tile: core s...
266
267
268
  		if (unaligned_fixup >= 0) {
  			struct single_step_state *state =
  				current_thread_info()->step_state;
0707ad30d   Chris Metcalf   arch/tile: Miscel...
269
270
  			if (!state ||
  			    (void __user *)(regs->pc) != state->buffer) {
867e359b9   Chris Metcalf   arch/tile: core s...
271
272
273
274
275
276
277
278
279
280
281
282
  				single_step_once(regs);
  				return;
  			}
  		}
  #endif
  		signo = SIGBUS;
  		code = BUS_ADRALN;
  		address = 0;
  		break;
  	case INT_DOUBLE_FAULT:
  		/*
  		 * For double fault, "reason" is actually passed as
a78c942df   Chris Metcalf   arch/tile: parame...
283
  		 * SYSTEM_SAVE_K_2, the hypervisor's double-fault info, so
867e359b9   Chris Metcalf   arch/tile: core s...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  		 * we can provide the original fault number rather than
  		 * the uninteresting "INT_DOUBLE_FAULT" so the user can
  		 * learn what actually struck while PL0 ICS was set.
  		 */
  		fault_num = reason;
  		signo = SIGILL;
  		code = ILL_DBLFLT;
  		address = regs->pc;
  		break;
  #ifdef __tilegx__
  	case INT_ILL_TRANS:
  		signo = SIGSEGV;
  		code = SEGV_MAPERR;
  		if (reason & SPR_ILL_TRANS_REASON__I_STREAM_VA_RMASK)
  			address = regs->pc;
  		else
  			address = 0;  /* FIXME: GX: single-step for address */
  		break;
  #endif
  	default:
  		panic("Unexpected do_trap interrupt number %d", fault_num);
  		return;
  	}
  
  	info.si_signo = signo;
  	info.si_code = code;
0707ad30d   Chris Metcalf   arch/tile: Miscel...
310
  	info.si_addr = (void __user *)address;
867e359b9   Chris Metcalf   arch/tile: core s...
311
312
  	if (signo == SIGILL)
  		info.si_trapno = fault_num;
571d76acd   Chris Metcalf   arch/tile: suppor...
313
  	trace_unhandled_signal("trap", regs, address, signo);
867e359b9   Chris Metcalf   arch/tile: core s...
314
315
  	force_sig_info(signo, &info, current);
  }
867e359b9   Chris Metcalf   arch/tile: core s...
316
317
318
  void kernel_double_fault(int dummy, ulong pc, ulong lr, ulong sp, ulong r52)
  {
  	_dump_stack(dummy, pc, lr, sp, r52);
0707ad30d   Chris Metcalf   arch/tile: Miscel...
319
320
  	pr_emerg("Double fault: exiting
  ");
867e359b9   Chris Metcalf   arch/tile: core s...
321
322
  	machine_halt();
  }