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: releng/11.2/sys/i386/i386/exception.s 329462 2018-02-17 18:00:01Z 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
56 dtrace_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
62 dtrace_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
97 MCOUNT_LABEL(user)
98 MCOUNT_LABEL(btrap)
99
100 #define TRAP(a) pushl $(a) ; jmp alltraps
101
102 IDTVEC(div)
103 pushl $0; TRAP(T_DIVIDE)
104 IDTVEC(dbg)
105 pushl $0; TRAP(T_TRCTRAP)
106 IDTVEC(nmi)
107 pushl $0; TRAP(T_NMI)
108 IDTVEC(bpt)
109 pushl $0; TRAP(T_BPTFLT)
110 IDTVEC(dtrace_ret)
111 pushl $0; TRAP(T_DTRACE_RET)
112 IDTVEC(ofl)
113 pushl $0; TRAP(T_OFLOW)
114 IDTVEC(bnd)
115 pushl $0; TRAP(T_BOUND)
116 #ifndef KDTRACE_HOOKS
117 IDTVEC(ill)
118 pushl $0; TRAP(T_PRIVINFLT)
119 #endif
120 IDTVEC(dna)
121 pushl $0; TRAP(T_DNA)
122 IDTVEC(fpusegm)
123 pushl $0; TRAP(T_FPOPFLT)
124 IDTVEC(tss)
125 TRAP(T_TSSFLT)
126 IDTVEC(missing)
127 TRAP(T_SEGNPFLT)
128 IDTVEC(stk)
129 TRAP(T_STKFLT)
130 IDTVEC(prot)
131 TRAP(T_PROTFLT)
132 IDTVEC(page)
133 TRAP(T_PAGEFLT)
134 IDTVEC(mchk)
135 pushl $0; TRAP(T_MCHK)
136 IDTVEC(rsvd_pti)
137 IDTVEC(rsvd)
138 pushl $0; TRAP(T_RESERVED)
139 IDTVEC(fpu)
140 pushl $0; TRAP(T_ARITHTRAP)
141 IDTVEC(align)
142 TRAP(T_ALIGNFLT)
143 IDTVEC(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
158 alltraps:
159 pushal
160 pushl $0
161 movw %ds,(%esp)
162 pushl $0
163 movw %es,(%esp)
164 pushl $0
165 movw %fs,(%esp)
166 alltraps_with_regs_pushed:
167 SET_KERNEL_SREGS
168 cld
169 FAKE_MCOUNT(TF_EIP(%esp))
170 calltrap:
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
186 IDTVEC(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 */
223 norm_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
241 IDTVEC(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
272 IDTVEC(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
291 ENTRY(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
320 MCOUNT_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
344 MCOUNT_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
355 doreti:
356 FAKE_MCOUNT($bintr) /* init "from" bintr -> doreti */
357 doreti_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
380 doreti_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
384 doreti_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 */
407 doreti_exit:
408 MEXITCOUNT
409
410 .globl doreti_popl_fs
411 doreti_popl_fs:
412 popl %fs
413 .globl doreti_popl_es
414 doreti_popl_es:
415 popl %es
416 .globl doreti_popl_ds
417 doreti_popl_ds:
418 popl %ds
419 popal
420 addl $8,%esp
421 .globl doreti_iret
422 doreti_iret:
423 iret
424
425 /*
426 * doreti_iret_fault and friends. Alternative return code for
427 * the case where we get a fault in the doreti_exit code
428 * above. trap() (i386/i386/trap.c) catches this specific
429 * case, and continues in the corresponding place in the code
430 * below.
431 *
432 * If the fault occured during return to usermode, we recreate
433 * the trap frame and call trap() to send a signal. Otherwise
434 * the kernel was tricked into fault by attempt to restore invalid
435 * usermode segment selectors on return from nested fault or
436 * interrupt, where interrupted kernel entry code not yet loaded
437 * kernel selectors. In the latter case, emulate iret and zero
438 * the invalid selector.
439 */
440 ALIGN_TEXT
441 .globl doreti_iret_fault
442 doreti_iret_fault:
443 subl $8,%esp
444 pushal
445 pushl $0
446 movw %ds,(%esp)
447 .globl doreti_popl_ds_fault
448 doreti_popl_ds_fault:
449 testb $SEL_RPL_MASK,TF_CS-TF_DS(%esp)
450 jz doreti_popl_ds_kfault
451 pushl $0
452 movw %es,(%esp)
453 .globl doreti_popl_es_fault
454 doreti_popl_es_fault:
455 testb $SEL_RPL_MASK,TF_CS-TF_ES(%esp)
456 jz doreti_popl_es_kfault
457 pushl $0
458 movw %fs,(%esp)
459 .globl doreti_popl_fs_fault
460 doreti_popl_fs_fault:
461 testb $SEL_RPL_MASK,TF_CS-TF_FS(%esp)
462 jz doreti_popl_fs_kfault
463 sti
464 movl $0,TF_ERR(%esp) /* XXX should be the error code */
465 movl $T_PROTFLT,TF_TRAPNO(%esp)
466 jmp alltraps_with_regs_pushed
467
468 doreti_popl_ds_kfault:
469 movl $0,(%esp)
470 jmp doreti_popl_ds
471 doreti_popl_es_kfault:
472 movl $0,(%esp)
473 jmp doreti_popl_es
474 doreti_popl_fs_kfault:
475 movl $0,(%esp)
476 jmp doreti_popl_fs
477
478 #ifdef HWPMC_HOOKS
479 doreti_nmi:
480 /*
481 * Since we are returning from an NMI, check if the current trap
482 * was from user mode and if so whether the current thread
483 * needs a user call chain capture.
484 */
485 testb $SEL_RPL_MASK,TF_CS(%esp)
486 jz doreti_exit
487 movl PCPU(CURTHREAD),%eax /* curthread present? */
488 orl %eax,%eax
489 jz doreti_exit
490 testl $TDP_CALLCHAIN,TD_PFLAGS(%eax) /* flagged for capture? */
491 jz doreti_exit
492 /*
493 * Take the processor out of NMI mode by executing a fake "iret".
494 */
495 pushfl
496 pushl %cs
497 pushl $outofnmi
498 iret
499 outofnmi:
500 /*
501 * Call the callchain capture hook after turning interrupts back on.
502 */
503 movl pmc_hook,%ecx
504 orl %ecx,%ecx
505 jz doreti_exit
506 pushl %esp /* frame pointer */
507 pushl $PMC_FN_USER_CALLCHAIN /* command */
508 movl PCPU(CURTHREAD),%eax
509 pushl %eax /* curthread */
510 sti
511 call *%ecx
512 addl $12,%esp
513 jmp doreti_ast
514 ENTRY(end_exceptions)
515 #endif
Cache object: 64415c3b8a9ea4f25a056efda2fd66a1
|