exception.s revision 347568
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: stable/11/sys/i386/i386/exception.s 347568 2019-05-14 17:05:02Z kib $
35 */
36
37#include "opt_apic.h"
38#include "opt_atpic.h"
39#include "opt_hwpmc_hooks.h"
40
41#include <machine/asmacros.h>
42#include <machine/psl.h>
43#include <machine/trap.h>
44
45#include "assym.s"
46
47#define	SEL_RPL_MASK	0x0003
48#define	GSEL_KPL	0x0020	/* GSEL(GCODE_SEL, SEL_KPL) */
49
50#ifdef KDTRACE_HOOKS
51	.bss
52	.globl	dtrace_invop_jump_addr
53	.align	4
54	.type	dtrace_invop_jump_addr, @object
55	.size	dtrace_invop_jump_addr, 4
56dtrace_invop_jump_addr:
57	.zero	4
58	.globl	dtrace_invop_calltrap_addr
59	.align	4
60	.type	dtrace_invop_calltrap_addr, @object
61	.size	dtrace_invop_calltrap_addr, 4
62dtrace_invop_calltrap_addr:
63	.zero	8
64#endif
65	.text
66#ifdef HWPMC_HOOKS
67	ENTRY(start_exceptions)
68#endif
69/*****************************************************************************/
70/* Trap handling                                                             */
71/*****************************************************************************/
72/*
73 * Trap and fault vector routines.
74 *
75 * Most traps are 'trap gates', SDT_SYS386TGT.  A trap gate pushes state on
76 * the stack that mostly looks like an interrupt, but does not disable
77 * interrupts.  A few of the traps we are use are interrupt gates,
78 * SDT_SYS386IGT, which are nearly the same thing except interrupts are
79 * disabled on entry.
80 *
81 * The cpu will push a certain amount of state onto the kernel stack for
82 * the current process.  The amount of state depends on the type of trap
83 * and whether the trap crossed rings or not.  See i386/include/frame.h.
84 * At the very least the current EFLAGS (status register, which includes
85 * the interrupt disable state prior to the trap), the code segment register,
86 * and the return instruction pointer are pushed by the cpu.  The cpu
87 * will also push an 'error' code for certain traps.  We push a dummy
88 * error code for those traps where the cpu doesn't in order to maintain
89 * a consistent frame.  We also push a contrived 'trap number'.
90 *
91 * The cpu does not push the general registers, we must do that, and we
92 * must restore them prior to calling 'iret'.  The cpu adjusts the %cs and
93 * %ss segment registers, but does not mess with %ds, %es, or %fs.  Thus we
94 * must load them with appropriate values for supervisor mode operation.
95 */
96
97MCOUNT_LABEL(user)
98MCOUNT_LABEL(btrap)
99
100#define	TRAP(a)		pushl $(a) ; jmp alltraps
101
102IDTVEC(div)
103	pushl $0; TRAP(T_DIVIDE)
104IDTVEC(dbg)
105	pushl $0; TRAP(T_TRCTRAP)
106IDTVEC(nmi)
107	pushl $0; TRAP(T_NMI)
108IDTVEC(bpt)
109	pushl $0; TRAP(T_BPTFLT)
110IDTVEC(dtrace_ret)
111	pushl $0; TRAP(T_DTRACE_RET)
112IDTVEC(ofl)
113	pushl $0; TRAP(T_OFLOW)
114IDTVEC(bnd)
115	pushl $0; TRAP(T_BOUND)
116#ifndef KDTRACE_HOOKS
117IDTVEC(ill)
118	pushl $0; TRAP(T_PRIVINFLT)
119#endif
120IDTVEC(dna)
121	pushl $0; TRAP(T_DNA)
122IDTVEC(fpusegm)
123	pushl $0; TRAP(T_FPOPFLT)
124IDTVEC(tss)
125	TRAP(T_TSSFLT)
126IDTVEC(missing)
127	TRAP(T_SEGNPFLT)
128IDTVEC(stk)
129	TRAP(T_STKFLT)
130IDTVEC(prot)
131	TRAP(T_PROTFLT)
132IDTVEC(page)
133	TRAP(T_PAGEFLT)
134IDTVEC(mchk)
135	pushl $0; TRAP(T_MCHK)
136IDTVEC(rsvd_pti)
137IDTVEC(rsvd)
138	pushl $0; TRAP(T_RESERVED)
139IDTVEC(fpu)
140	pushl $0; TRAP(T_ARITHTRAP)
141IDTVEC(align)
142	TRAP(T_ALIGNFLT)
143IDTVEC(xmm)
144	pushl $0; TRAP(T_XMMFLT)
145
146	/*
147	 * All traps except ones for syscalls jump to alltraps.  If
148	 * interrupts were enabled when the trap occurred, then interrupts
149	 * are enabled now if the trap was through a trap gate, else
150	 * disabled if the trap was through an interrupt gate.  Note that
151	 * int0x80_syscall is a trap gate.   Interrupt gates are used by
152	 * page faults, non-maskable interrupts, debug and breakpoint
153	 * exceptions.
154	 */
155	SUPERALIGN_TEXT
156	.globl	alltraps
157	.type	alltraps,@function
158alltraps:
159	pushal
160	pushl	$0
161	movw	%ds,(%esp)
162	pushl	$0
163	movw	%es,(%esp)
164	pushl	$0
165	movw	%fs,(%esp)
166alltraps_with_regs_pushed:
167	SET_KERNEL_SREGS
168	cld
169	FAKE_MCOUNT(TF_EIP(%esp))
170calltrap:
171	pushl	%esp
172	call	trap
173	add	$4, %esp
174
175	/*
176	 * Return via doreti to handle ASTs.
177	 */
178	MEXITCOUNT
179	jmp	doreti
180
181/*
182 * Privileged instruction fault.
183 */
184#ifdef KDTRACE_HOOKS
185	SUPERALIGN_TEXT
186IDTVEC(ill)
187	/*
188	 * Check if a DTrace hook is registered.  The default (data) segment
189	 * cannot be used for this since %ds is not known good until we
190	 * verify that the entry was from kernel mode.
191	 */
192	cmpl	$0,%ss:dtrace_invop_jump_addr
193	je	norm_ill
194
195	/*
196	 * Check if this is a user fault.  If so, just handle it as a normal
197	 * trap.
198	 */
199	cmpl	$GSEL_KPL, 4(%esp)	/* Check the code segment */
200	jne	norm_ill
201	testl	$PSL_VM, 8(%esp)	/* and vm86 mode. */
202	jnz	norm_ill
203
204	/*
205	 * This is a kernel instruction fault that might have been caused
206	 * by a DTrace provider.
207	 */
208	pushal
209	cld
210
211	/*
212	 * Set our jump address for the jump back in the event that
213	 * the exception wasn't caused by DTrace at all.
214	 */
215	movl	$norm_ill, dtrace_invop_calltrap_addr
216
217	/* Jump to the code hooked in by DTrace. */
218	jmpl	*dtrace_invop_jump_addr
219
220	/*
221	 * Process the instruction fault in the normal way.
222	 */
223norm_ill:
224	pushl $0
225	TRAP(T_PRIVINFLT)
226#endif
227
228/*
229 * Call gate entry for syscalls (lcall 7,0).
230 * This is used by FreeBSD 1.x a.out executables and "old" NetBSD executables.
231 *
232 * The intersegment call has been set up to specify one dummy parameter.
233 * This leaves a place to put eflags so that the call frame can be
234 * converted to a trap frame. Note that the eflags is (semi-)bogusly
235 * pushed into (what will be) tf_err and then copied later into the
236 * final spot. It has to be done this way because esp can't be just
237 * temporarily altered for the pushfl - an interrupt might come in
238 * and clobber the saved cs/eip.
239 */
240	SUPERALIGN_TEXT
241IDTVEC(lcall_syscall)
242	pushfl				/* save eflags */
243	popl	8(%esp)			/* shuffle into tf_eflags */
244	pushl	$7			/* sizeof "lcall 7,0" */
245	pushl	$0			/* tf_trapno */
246	pushal
247	pushl	$0
248	movw	%ds,(%esp)
249	pushl	$0
250	movw	%es,(%esp)
251	pushl	$0
252	movw	%fs,(%esp)
253	SET_KERNEL_SREGS
254	cld
255	FAKE_MCOUNT(TF_EIP(%esp))
256	pushl	%esp
257	call	syscall
258	add	$4, %esp
259	MEXITCOUNT
260	jmp	doreti
261
262/*
263 * Trap gate entry for syscalls (int 0x80).
264 * This is used by FreeBSD ELF executables, "new" NetBSD executables, and all
265 * Linux executables.
266 *
267 * Even though the name says 'int0x80', this is actually a trap gate, not an
268 * interrupt gate.  Thus interrupts are enabled on entry just as they are for
269 * a normal syscall.
270 */
271	SUPERALIGN_TEXT
272IDTVEC(int0x80_syscall)
273	pushl	$2			/* sizeof "int 0x80" */
274	pushl	$0			/* tf_trapno */
275	pushal
276	pushl	$0
277	movw	%ds,(%esp)
278	pushl	$0
279	movw	%es,(%esp)
280	pushl	$0
281	movw	%fs,(%esp)
282	SET_KERNEL_SREGS
283	cld
284	FAKE_MCOUNT(TF_EIP(%esp))
285	pushl	%esp
286	call	syscall
287	add	$4, %esp
288	MEXITCOUNT
289	jmp	doreti
290
291ENTRY(fork_trampoline)
292	pushl	%esp			/* trapframe pointer */
293	pushl	%ebx			/* arg1 */
294	pushl	%esi			/* function */
295	call	fork_exit
296	addl	$12,%esp
297	/* cut from syscall */
298
299	/*
300	 * Return via doreti to handle ASTs.
301	 */
302	MEXITCOUNT
303	jmp	doreti
304
305
306/*
307 * To efficiently implement classification of trap and interrupt handlers
308 * for profiling, there must be only trap handlers between the labels btrap
309 * and bintr, and only interrupt handlers between the labels bintr and
310 * eintr.  This is implemented (partly) by including files that contain
311 * some of the handlers.  Before including the files, set up a normal asm
312 * environment so that the included files doen't need to know that they are
313 * included.
314 */
315
316	.data
317	.p2align 4
318	.text
319	SUPERALIGN_TEXT
320MCOUNT_LABEL(bintr)
321
322#ifdef DEV_ATPIC
323#include <i386/i386/atpic_vector.s>
324#endif
325
326#if defined(DEV_APIC) && defined(DEV_ATPIC)
327	.data
328	.p2align 4
329	.text
330	SUPERALIGN_TEXT
331#endif
332
333#ifdef DEV_APIC
334#include <i386/i386/apic_vector.s>
335#endif
336
337	.data
338	.p2align 4
339	.text
340	SUPERALIGN_TEXT
341#include <i386/i386/vm86bios.s>
342
343	.text
344MCOUNT_LABEL(eintr)
345
346/*
347 * void doreti(struct trapframe)
348 *
349 * Handle return from interrupts, traps and syscalls.
350 */
351	.text
352	SUPERALIGN_TEXT
353	.type	doreti,@function
354	.globl	doreti
355doreti:
356	FAKE_MCOUNT($bintr)		/* init "from" bintr -> doreti */
357doreti_next:
358	/*
359	 * Check if ASTs can be handled now.  ASTs cannot be safely
360	 * processed when returning from an NMI.
361	 */
362	cmpb	$T_NMI,TF_TRAPNO(%esp)
363#ifdef HWPMC_HOOKS
364	je	doreti_nmi
365#else
366	je	doreti_exit
367#endif
368	/*
369	 * PSL_VM must be checked first since segment registers only
370	 * have an RPL in non-VM86 mode.
371	 * ASTs can not be handled now if we are in a vm86 call.
372	 */
373	testl	$PSL_VM,TF_EFLAGS(%esp)
374	jz	doreti_notvm86
375	movl	PCPU(CURPCB),%ecx
376	testl	$PCB_VM86CALL,PCB_FLAGS(%ecx)
377	jz	doreti_ast
378	jmp	doreti_exit
379
380doreti_notvm86:
381	testb	$SEL_RPL_MASK,TF_CS(%esp) /* are we returning to user mode? */
382	jz	doreti_exit		/* can't handle ASTs now if not */
383
384doreti_ast:
385	/*
386	 * Check for ASTs atomically with returning.  Disabling CPU
387	 * interrupts provides sufficient locking even in the SMP case,
388	 * since we will be informed of any new ASTs by an IPI.
389	 */
390	cli
391	movl	PCPU(CURTHREAD),%eax
392	testl	$TDF_ASTPENDING | TDF_NEEDRESCHED,TD_FLAGS(%eax)
393	je	doreti_exit
394	sti
395	pushl	%esp			/* pass a pointer to the trapframe */
396	call	ast
397	add	$4,%esp
398	jmp	doreti_ast
399
400	/*
401	 * doreti_exit:	pop registers, iret.
402	 *
403	 *	The segment register pop is a special case, since it may
404	 *	fault if (for example) a sigreturn specifies bad segment
405	 *	registers.  The fault is handled in trap.c.
406	 */
407doreti_exit:
408	MEXITCOUNT
409	call	*mds_handler
410
411	.globl	doreti_popl_fs
412doreti_popl_fs:
413	popl	%fs
414	.globl	doreti_popl_es
415doreti_popl_es:
416	popl	%es
417	.globl	doreti_popl_ds
418doreti_popl_ds:
419	popl	%ds
420	popal
421	addl	$8,%esp
422	.globl	doreti_iret
423doreti_iret:
424	iret
425
426	/*
427	 * doreti_iret_fault and friends.  Alternative return code for
428	 * the case where we get a fault in the doreti_exit code
429	 * above.  trap() (i386/i386/trap.c) catches this specific
430	 * case, and continues in the corresponding place in the code
431	 * below.
432	 *
433	 * If the fault occured during return to usermode, we recreate
434	 * the trap frame and call trap() to send a signal.  Otherwise
435	 * the kernel was tricked into fault by attempt to restore invalid
436	 * usermode segment selectors on return from nested fault or
437	 * interrupt, where interrupted kernel entry code not yet loaded
438	 * kernel selectors.  In the latter case, emulate iret and zero
439	 * the invalid selector.
440	 */
441	ALIGN_TEXT
442	.globl	doreti_iret_fault
443doreti_iret_fault:
444	subl	$8,%esp
445	pushal
446	pushl	$0
447	movw	%ds,(%esp)
448	.globl	doreti_popl_ds_fault
449doreti_popl_ds_fault:
450	testb	$SEL_RPL_MASK,TF_CS-TF_DS(%esp)
451	jz	doreti_popl_ds_kfault
452	pushl	$0
453	movw	%es,(%esp)
454	.globl	doreti_popl_es_fault
455doreti_popl_es_fault:
456	testb	$SEL_RPL_MASK,TF_CS-TF_ES(%esp)
457	jz	doreti_popl_es_kfault
458	pushl	$0
459	movw	%fs,(%esp)
460	.globl	doreti_popl_fs_fault
461doreti_popl_fs_fault:
462	testb	$SEL_RPL_MASK,TF_CS-TF_FS(%esp)
463	jz	doreti_popl_fs_kfault
464	sti
465	movl	$0,TF_ERR(%esp)	/* XXX should be the error code */
466	movl	$T_PROTFLT,TF_TRAPNO(%esp)
467	jmp	alltraps_with_regs_pushed
468
469doreti_popl_ds_kfault:
470	movl	$0,(%esp)
471	jmp	doreti_popl_ds
472doreti_popl_es_kfault:
473	movl	$0,(%esp)
474	jmp	doreti_popl_es
475doreti_popl_fs_kfault:
476	movl	$0,(%esp)
477	jmp	doreti_popl_fs
478
479#ifdef HWPMC_HOOKS
480doreti_nmi:
481	/*
482	 * Since we are returning from an NMI, check if the current trap
483	 * was from user mode and if so whether the current thread
484	 * needs a user call chain capture.
485	 */
486	testb	$SEL_RPL_MASK,TF_CS(%esp)
487	jz	doreti_exit
488	movl	PCPU(CURTHREAD),%eax	/* curthread present? */
489	orl	%eax,%eax
490	jz	doreti_exit
491	testl	$TDP_CALLCHAIN,TD_PFLAGS(%eax) /* flagged for capture? */
492	jz	doreti_exit
493	/*
494	 * Take the processor out of NMI mode by executing a fake "iret".
495	 */
496	pushfl
497	pushl	%cs
498	pushl	$outofnmi
499	iret
500outofnmi:
501	/*
502	 * Call the callchain capture hook after turning interrupts back on.
503	 */
504	movl	pmc_hook,%ecx
505	orl	%ecx,%ecx
506	jz	doreti_exit
507	pushl	%esp			/* frame pointer */
508	pushl	$PMC_FN_USER_CALLCHAIN	/* command */
509	movl	PCPU(CURTHREAD),%eax
510	pushl	%eax			/* curthread */
511	sti
512	call	*%ecx
513	addl	$12,%esp
514	jmp	doreti_ast
515	ENTRY(end_exceptions)
516#endif
517