exception.s revision 204309
1/*-
2 * Copyright (c) 1989, 1990 William F. Jolitz.
3 * Copyright (c) 1990 The Regents of the University of California.
4 * Copyright (c) 2007 The FreeBSD Foundation
5 * All rights reserved.
6 *
7 * Portions of this software were developed by A. Joseph Koshy under
8 * sponsorship from the FreeBSD Foundation and Google, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 4. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $FreeBSD: head/sys/i386/i386/exception.s 204309 2010-02-25 14:13:39Z attilio $
35 */
36
37#include "opt_apic.h"
38#include "opt_hwpmc_hooks.h"
39#include "opt_kdtrace.h"
40#include "opt_npx.h"
41
42#include <machine/asmacros.h>
43#include <machine/psl.h>
44#include <machine/trap.h>
45
46#include "assym.s"
47
48#define	SEL_RPL_MASK	0x0003
49#define	GSEL_KPL	0x0020	/* GSEL(GCODE_SEL, SEL_KPL) */
50
51#ifdef KDTRACE_HOOKS
52	.bss
53	.globl	dtrace_invop_jump_addr
54	.align	4
55	.type	dtrace_invop_jump_addr, @object
56        .size	dtrace_invop_jump_addr, 4
57dtrace_invop_jump_addr:
58	.zero	4
59	.globl	dtrace_invop_calltrap_addr
60	.align	4
61	.type	dtrace_invop_calltrap_addr, @object
62        .size	dtrace_invop_calltrap_addr, 4
63dtrace_invop_calltrap_addr:
64	.zero	8
65#endif
66	.text
67#ifdef HWPMC_HOOKS
68	ENTRY(start_exceptions)
69#endif
70/*****************************************************************************/
71/* Trap handling                                                             */
72/*****************************************************************************/
73/*
74 * Trap and fault vector routines.
75 *
76 * Most traps are 'trap gates', SDT_SYS386TGT.  A trap gate pushes state on
77 * the stack that mostly looks like an interrupt, but does not disable
78 * interrupts.  A few of the traps we are use are interrupt gates,
79 * SDT_SYS386IGT, which are nearly the same thing except interrupts are
80 * disabled on entry.
81 *
82 * The cpu will push a certain amount of state onto the kernel stack for
83 * the current process.  The amount of state depends on the type of trap
84 * and whether the trap crossed rings or not.  See i386/include/frame.h.
85 * At the very least the current EFLAGS (status register, which includes
86 * the interrupt disable state prior to the trap), the code segment register,
87 * and the return instruction pointer are pushed by the cpu.  The cpu
88 * will also push an 'error' code for certain traps.  We push a dummy
89 * error code for those traps where the cpu doesn't in order to maintain
90 * a consistent frame.  We also push a contrived 'trap number'.
91 *
92 * The cpu does not push the general registers, we must do that, and we
93 * must restore them prior to calling 'iret'.  The cpu adjusts the %cs and
94 * %ss segment registers, but does not mess with %ds, %es, or %fs.  Thus we
95 * must load them with appropriate values for supervisor mode operation.
96 */
97
98MCOUNT_LABEL(user)
99MCOUNT_LABEL(btrap)
100
101#define	TRAP(a)		pushl $(a) ; jmp alltraps
102
103IDTVEC(div)
104	pushl $0; TRAP(T_DIVIDE)
105IDTVEC(dbg)
106	pushl $0; TRAP(T_TRCTRAP)
107IDTVEC(nmi)
108	pushl $0; TRAP(T_NMI)
109IDTVEC(bpt)
110	pushl $0; TRAP(T_BPTFLT)
111IDTVEC(ofl)
112	pushl $0; TRAP(T_OFLOW)
113IDTVEC(bnd)
114	pushl $0; TRAP(T_BOUND)
115#ifndef KDTRACE_HOOKS
116IDTVEC(ill)
117	pushl $0; TRAP(T_PRIVINFLT)
118#endif
119IDTVEC(dna)
120	pushl $0; TRAP(T_DNA)
121IDTVEC(fpusegm)
122	pushl $0; TRAP(T_FPOPFLT)
123IDTVEC(tss)
124	TRAP(T_TSSFLT)
125IDTVEC(missing)
126	TRAP(T_SEGNPFLT)
127IDTVEC(stk)
128	TRAP(T_STKFLT)
129IDTVEC(prot)
130	TRAP(T_PROTFLT)
131IDTVEC(page)
132	TRAP(T_PAGEFLT)
133IDTVEC(mchk)
134	pushl $0; TRAP(T_MCHK)
135IDTVEC(rsvd)
136	pushl $0; TRAP(T_RESERVED)
137IDTVEC(fpu)
138	pushl $0; TRAP(T_ARITHTRAP)
139IDTVEC(align)
140	TRAP(T_ALIGNFLT)
141IDTVEC(xmm)
142	pushl $0; TRAP(T_XMMFLT)
143
144	/*
145	 * alltraps entry point.  Interrupts are enabled if this was a trap
146	 * gate (TGT), else disabled if this was an interrupt gate (IGT).
147	 * Note that int0x80_syscall is a trap gate.   Interrupt gates are
148	 * used by page faults, non-maskable interrupts, debug and breakpoint
149	 * exceptions.
150	 */
151
152	SUPERALIGN_TEXT
153	.globl	alltraps
154	.type	alltraps,@function
155alltraps:
156	pushal
157	pushl	%ds
158	pushl	%es
159	pushl	%fs
160alltraps_with_regs_pushed:
161	SET_KERNEL_SREGS
162	FAKE_MCOUNT(TF_EIP(%esp))
163calltrap:
164	pushl	%esp
165	call	trap
166	add	$4, %esp
167
168	/*
169	 * Return via doreti to handle ASTs.
170	 */
171	MEXITCOUNT
172	jmp	doreti
173
174/*
175 * Privileged instruction fault.
176 */
177#ifdef KDTRACE_HOOKS
178	SUPERALIGN_TEXT
179IDTVEC(ill)
180	/* Check if there is no DTrace hook registered. */
181	cmpl	$0,dtrace_invop_jump_addr
182	je	norm_ill
183
184	/* Check if this is a user fault. */
185	cmpl	$GSEL_KPL, 4(%esp)	/* Check the code segment. */
186
187	/* If so, just handle it as a normal trap. */
188	jne	norm_ill
189
190	/*
191	 * This is a kernel instruction fault that might have been caused
192	 * by a DTrace provider.
193	 */
194	pushal				/* Push all registers onto the stack. */
195
196	/*
197	 * Set our jump address for the jump back in the event that
198	 * the exception wasn't caused by DTrace at all.
199	 */
200	movl	$norm_ill, dtrace_invop_calltrap_addr
201
202	/* Jump to the code hooked in by DTrace. */
203	jmpl	*dtrace_invop_jump_addr
204
205	/*
206	 * Process the instruction fault in the normal way.
207	 */
208norm_ill:
209	pushl $0
210	TRAP(T_PRIVINFLT)
211#endif
212
213/*
214 * SYSCALL CALL GATE (old entry point for a.out binaries)
215 *
216 * The intersegment call has been set up to specify one dummy parameter.
217 *
218 * This leaves a place to put eflags so that the call frame can be
219 * converted to a trap frame. Note that the eflags is (semi-)bogusly
220 * pushed into (what will be) tf_err and then copied later into the
221 * final spot. It has to be done this way because esp can't be just
222 * temporarily altered for the pushfl - an interrupt might come in
223 * and clobber the saved cs/eip.
224 */
225	SUPERALIGN_TEXT
226IDTVEC(lcall_syscall)
227	pushfl				/* save eflags */
228	popl	8(%esp)			/* shuffle into tf_eflags */
229	pushl	$7			/* sizeof "lcall 7,0" */
230	subl	$4,%esp			/* skip over tf_trapno */
231	pushal
232	pushl	%ds
233	pushl	%es
234	pushl	%fs
235	SET_KERNEL_SREGS
236	FAKE_MCOUNT(TF_EIP(%esp))
237	pushl	%esp
238	call	syscall
239	add	$4, %esp
240	MEXITCOUNT
241	jmp	doreti
242
243/*
244 * Call gate entry for FreeBSD ELF and Linux/NetBSD syscall (int 0x80)
245 *
246 * Even though the name says 'int0x80', this is actually a TGT (trap gate)
247 * rather then an IGT (interrupt gate).  Thus interrupts are enabled on
248 * entry just as they are for a normal syscall.
249 */
250	SUPERALIGN_TEXT
251IDTVEC(int0x80_syscall)
252	pushl	$2			/* sizeof "int 0x80" */
253	subl	$4,%esp			/* skip over tf_trapno */
254	pushal
255	pushl	%ds
256	pushl	%es
257	pushl	%fs
258	SET_KERNEL_SREGS
259	FAKE_MCOUNT(TF_EIP(%esp))
260	pushl	%esp
261	call	syscall
262	add	$4, %esp
263	MEXITCOUNT
264	jmp	doreti
265
266ENTRY(fork_trampoline)
267	pushl	%esp			/* trapframe pointer */
268	pushl	%ebx			/* arg1 */
269	pushl	%esi			/* function */
270	call	fork_exit
271	addl	$12,%esp
272	/* cut from syscall */
273
274	/*
275	 * Return via doreti to handle ASTs.
276	 */
277	MEXITCOUNT
278	jmp	doreti
279
280
281/*
282 * To efficiently implement classification of trap and interrupt handlers
283 * for profiling, there must be only trap handlers between the labels btrap
284 * and bintr, and only interrupt handlers between the labels bintr and
285 * eintr.  This is implemented (partly) by including files that contain
286 * some of the handlers.  Before including the files, set up a normal asm
287 * environment so that the included files doen't need to know that they are
288 * included.
289 */
290
291	.data
292	.p2align 4
293	.text
294	SUPERALIGN_TEXT
295MCOUNT_LABEL(bintr)
296
297#include <i386/i386/atpic_vector.s>
298
299#ifdef DEV_APIC
300	.data
301	.p2align 4
302	.text
303	SUPERALIGN_TEXT
304
305#include <i386/i386/apic_vector.s>
306#endif
307
308	.data
309	.p2align 4
310	.text
311	SUPERALIGN_TEXT
312#include <i386/i386/vm86bios.s>
313
314	.text
315MCOUNT_LABEL(eintr)
316
317/*
318 * void doreti(struct trapframe)
319 *
320 * Handle return from interrupts, traps and syscalls.
321 */
322	.text
323	SUPERALIGN_TEXT
324	.type	doreti,@function
325doreti:
326	FAKE_MCOUNT($bintr)		/* init "from" bintr -> doreti */
327doreti_next:
328	/*
329	 * Check if ASTs can be handled now.  ASTs cannot be safely
330	 * processed when returning from an NMI.
331	 */
332	cmpb	$T_NMI,TF_TRAPNO(%esp)
333#ifdef HWPMC_HOOKS
334	je	doreti_nmi
335#else
336	je	doreti_exit
337#endif
338	/*
339	 * PSL_VM must be checked first since segment registers only
340	 * have an RPL in non-VM86 mode.
341	 */
342	testl	$PSL_VM,TF_EFLAGS(%esp)	/* are we in vm86 mode? */
343	jz	doreti_notvm86
344	movl	PCPU(CURPCB),%ecx
345	testl	$PCB_VM86CALL,PCB_FLAGS(%ecx)	/* are we in a vm86 call? */
346	jz	doreti_ast		/* can handle ASTS now if not */
347  	jmp	doreti_exit
348
349doreti_notvm86:
350	testb	$SEL_RPL_MASK,TF_CS(%esp) /* are we returning to user mode? */
351	jz	doreti_exit		/* can't handle ASTs now if not */
352
353doreti_ast:
354	/*
355	 * Check for ASTs atomically with returning.  Disabling CPU
356	 * interrupts provides sufficient locking even in the SMP case,
357	 * since we will be informed of any new ASTs by an IPI.
358	 */
359	cli
360	movl	PCPU(CURTHREAD),%eax
361	testl	$TDF_ASTPENDING | TDF_NEEDRESCHED,TD_FLAGS(%eax)
362	je	doreti_exit
363	sti
364	pushl	%esp			/* pass a pointer to the trapframe */
365	call	ast
366	add	$4,%esp
367	jmp	doreti_ast
368
369	/*
370	 * doreti_exit:	pop registers, iret.
371	 *
372	 *	The segment register pop is a special case, since it may
373	 *	fault if (for example) a sigreturn specifies bad segment
374	 *	registers.  The fault is handled in trap.c.
375	 */
376doreti_exit:
377	MEXITCOUNT
378
379	.globl	doreti_popl_fs
380doreti_popl_fs:
381	popl	%fs
382	.globl	doreti_popl_es
383doreti_popl_es:
384	popl	%es
385	.globl	doreti_popl_ds
386doreti_popl_ds:
387	popl	%ds
388	popal
389	addl	$8,%esp
390	.globl	doreti_iret
391doreti_iret:
392	iret
393
394  	/*
395	 * doreti_iret_fault and friends.  Alternative return code for
396	 * the case where we get a fault in the doreti_exit code
397	 * above.  trap() (i386/i386/trap.c) catches this specific
398	 * case, sends the process a signal and continues in the
399	 * corresponding place in the code below.
400	 */
401	ALIGN_TEXT
402	.globl	doreti_iret_fault
403doreti_iret_fault:
404	subl	$8,%esp
405	pushal
406	pushl	%ds
407	.globl	doreti_popl_ds_fault
408doreti_popl_ds_fault:
409	pushl	%es
410	.globl	doreti_popl_es_fault
411doreti_popl_es_fault:
412	pushl	%fs
413	.globl	doreti_popl_fs_fault
414doreti_popl_fs_fault:
415	movl	$0,TF_ERR(%esp)	/* XXX should be the error code */
416	movl	$T_PROTFLT,TF_TRAPNO(%esp)
417	jmp	alltraps_with_regs_pushed
418#ifdef HWPMC_HOOKS
419doreti_nmi:
420	/*
421	 * Since we are returning from an NMI, check if the current trap
422	 * was from user mode and if so whether the current thread
423	 * needs a user call chain capture.
424	 */
425	testb	$SEL_RPL_MASK,TF_CS(%esp)
426	jz	doreti_exit
427	movl	PCPU(CURTHREAD),%eax	/* curthread present? */
428	orl	%eax,%eax
429	jz	doreti_exit
430	testl	$TDP_CALLCHAIN,TD_PFLAGS(%eax) /* flagged for capture? */
431	jz	doreti_exit
432	/*
433	 * Take the processor out of NMI mode by executing a fake "iret".
434	 */
435	pushfl
436	pushl	%cs
437	pushl	$outofnmi
438	iret
439outofnmi:
440	/*
441	 * Call the callchain capture hook after turning interrupts back on.
442	 */
443	movl	pmc_hook,%ecx
444	orl	%ecx,%ecx
445	jz	doreti_exit
446	pushl	%esp			/* frame pointer */
447	pushl	$PMC_FN_USER_CALLCHAIN	/* command */
448	movl	PCPU(CURTHREAD),%eax
449	pushl	%eax			/* curthread */
450	sti
451	call	*%ecx
452	addl	$12,%esp
453	jmp	doreti_ast
454	ENTRY(end_exceptions)
455#endif
456