exception.s revision 314210
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 314210 2017-02-24 16:02: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 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) 137 pushl $0; TRAP(T_RESERVED) 138IDTVEC(fpu) 139 pushl $0; TRAP(T_ARITHTRAP) 140IDTVEC(align) 141 TRAP(T_ALIGNFLT) 142IDTVEC(xmm) 143 pushl $0; TRAP(T_XMMFLT) 144 145 /* 146 * All traps except ones for syscalls jump to alltraps. If 147 * interrupts were enabled when the trap occurred, then interrupts 148 * are enabled now if the trap was through a trap gate, else 149 * disabled if the trap was through an interrupt gate. Note that 150 * int0x80_syscall is a trap gate. Interrupt gates are used by 151 * page faults, non-maskable interrupts, debug and breakpoint 152 * exceptions. 153 */ 154 SUPERALIGN_TEXT 155 .globl alltraps 156 .type alltraps,@function 157alltraps: 158 pushal 159 pushl $0 160 movw %ds,(%esp) 161 pushl $0 162 movw %es,(%esp) 163 pushl $0 164 movw %fs,(%esp) 165alltraps_with_regs_pushed: 166 SET_KERNEL_SREGS 167 cld 168 FAKE_MCOUNT(TF_EIP(%esp)) 169calltrap: 170 pushl %esp 171 call trap 172 add $4, %esp 173 174 /* 175 * Return via doreti to handle ASTs. 176 */ 177 MEXITCOUNT 178 jmp doreti 179 180/* 181 * Privileged instruction fault. 182 */ 183#ifdef KDTRACE_HOOKS 184 SUPERALIGN_TEXT 185IDTVEC(ill) 186 /* Check if there is no DTrace hook registered. */ 187 cmpl $0,dtrace_invop_jump_addr 188 je norm_ill 189 190 /* Check if this is a user fault. */ 191 cmpl $GSEL_KPL, 4(%esp) /* Check the code segment. */ 192 193 /* If so, just handle it as a normal trap. */ 194 jne norm_ill 195 196 /* 197 * This is a kernel instruction fault that might have been caused 198 * by a DTrace provider. 199 */ 200 pushal /* Push all registers onto the stack. */ 201 202 /* 203 * Set our jump address for the jump back in the event that 204 * the exception wasn't caused by DTrace at all. 205 */ 206 movl $norm_ill, dtrace_invop_calltrap_addr 207 208 /* Jump to the code hooked in by DTrace. */ 209 jmpl *dtrace_invop_jump_addr 210 211 /* 212 * Process the instruction fault in the normal way. 213 */ 214norm_ill: 215 pushl $0 216 TRAP(T_PRIVINFLT) 217#endif 218 219/* 220 * Call gate entry for syscalls (lcall 7,0). 221 * This is used by FreeBSD 1.x a.out executables and "old" NetBSD executables. 222 * 223 * The intersegment call has been set up to specify one dummy parameter. 224 * This leaves a place to put eflags so that the call frame can be 225 * converted to a trap frame. Note that the eflags is (semi-)bogusly 226 * pushed into (what will be) tf_err and then copied later into the 227 * final spot. It has to be done this way because esp can't be just 228 * temporarily altered for the pushfl - an interrupt might come in 229 * and clobber the saved cs/eip. 230 */ 231 SUPERALIGN_TEXT 232IDTVEC(lcall_syscall) 233 pushfl /* save eflags */ 234 popl 8(%esp) /* shuffle into tf_eflags */ 235 pushl $7 /* sizeof "lcall 7,0" */ 236 pushl $0 /* tf_trapno */ 237 pushal 238 pushl $0 239 movw %ds,(%esp) 240 pushl $0 241 movw %es,(%esp) 242 pushl $0 243 movw %fs,(%esp) 244 SET_KERNEL_SREGS 245 cld 246 FAKE_MCOUNT(TF_EIP(%esp)) 247 pushl %esp 248 call syscall 249 add $4, %esp 250 MEXITCOUNT 251 jmp doreti 252 253/* 254 * Trap gate entry for syscalls (int 0x80). 255 * This is used by FreeBSD ELF executables, "new" NetBSD executables, and all 256 * Linux executables. 257 * 258 * Even though the name says 'int0x80', this is actually a trap gate, not an 259 * interrupt gate. Thus interrupts are enabled on entry just as they are for 260 * a normal syscall. 261 */ 262 SUPERALIGN_TEXT 263IDTVEC(int0x80_syscall) 264 pushl $2 /* sizeof "int 0x80" */ 265 pushl $0 /* tf_trapno */ 266 pushal 267 pushl $0 268 movw %ds,(%esp) 269 pushl $0 270 movw %es,(%esp) 271 pushl $0 272 movw %fs,(%esp) 273 SET_KERNEL_SREGS 274 cld 275 FAKE_MCOUNT(TF_EIP(%esp)) 276 pushl %esp 277 call syscall 278 add $4, %esp 279 MEXITCOUNT 280 jmp doreti 281 282ENTRY(fork_trampoline) 283 pushl %esp /* trapframe pointer */ 284 pushl %ebx /* arg1 */ 285 pushl %esi /* function */ 286 call fork_exit 287 addl $12,%esp 288 /* cut from syscall */ 289 290 /* 291 * Return via doreti to handle ASTs. 292 */ 293 MEXITCOUNT 294 jmp doreti 295 296 297/* 298 * To efficiently implement classification of trap and interrupt handlers 299 * for profiling, there must be only trap handlers between the labels btrap 300 * and bintr, and only interrupt handlers between the labels bintr and 301 * eintr. This is implemented (partly) by including files that contain 302 * some of the handlers. Before including the files, set up a normal asm 303 * environment so that the included files doen't need to know that they are 304 * included. 305 */ 306 307 .data 308 .p2align 4 309 .text 310 SUPERALIGN_TEXT 311MCOUNT_LABEL(bintr) 312 313#ifdef DEV_ATPIC 314#include <i386/i386/atpic_vector.s> 315#endif 316 317#if defined(DEV_APIC) && defined(DEV_ATPIC) 318 .data 319 .p2align 4 320 .text 321 SUPERALIGN_TEXT 322#endif 323 324#ifdef DEV_APIC 325#include <i386/i386/apic_vector.s> 326#endif 327 328 .data 329 .p2align 4 330 .text 331 SUPERALIGN_TEXT 332#include <i386/i386/vm86bios.s> 333 334 .text 335MCOUNT_LABEL(eintr) 336 337/* 338 * void doreti(struct trapframe) 339 * 340 * Handle return from interrupts, traps and syscalls. 341 */ 342 .text 343 SUPERALIGN_TEXT 344 .type doreti,@function 345 .globl doreti 346doreti: 347 FAKE_MCOUNT($bintr) /* init "from" bintr -> doreti */ 348doreti_next: 349 /* 350 * Check if ASTs can be handled now. ASTs cannot be safely 351 * processed when returning from an NMI. 352 */ 353 cmpb $T_NMI,TF_TRAPNO(%esp) 354#ifdef HWPMC_HOOKS 355 je doreti_nmi 356#else 357 je doreti_exit 358#endif 359 /* 360 * PSL_VM must be checked first since segment registers only 361 * have an RPL in non-VM86 mode. 362 * ASTs can not be handled now if we are in a vm86 call. 363 */ 364 testl $PSL_VM,TF_EFLAGS(%esp) 365 jz doreti_notvm86 366 movl PCPU(CURPCB),%ecx 367 testl $PCB_VM86CALL,PCB_FLAGS(%ecx) 368 jz doreti_ast 369 jmp doreti_exit 370 371doreti_notvm86: 372 testb $SEL_RPL_MASK,TF_CS(%esp) /* are we returning to user mode? */ 373 jz doreti_exit /* can't handle ASTs now if not */ 374 375doreti_ast: 376 /* 377 * Check for ASTs atomically with returning. Disabling CPU 378 * interrupts provides sufficient locking even in the SMP case, 379 * since we will be informed of any new ASTs by an IPI. 380 */ 381 cli 382 movl PCPU(CURTHREAD),%eax 383 testl $TDF_ASTPENDING | TDF_NEEDRESCHED,TD_FLAGS(%eax) 384 je doreti_exit 385 sti 386 pushl %esp /* pass a pointer to the trapframe */ 387 call ast 388 add $4,%esp 389 jmp doreti_ast 390 391 /* 392 * doreti_exit: pop registers, iret. 393 * 394 * The segment register pop is a special case, since it may 395 * fault if (for example) a sigreturn specifies bad segment 396 * registers. The fault is handled in trap.c. 397 */ 398doreti_exit: 399 MEXITCOUNT 400 401 .globl doreti_popl_fs 402doreti_popl_fs: 403 popl %fs 404 .globl doreti_popl_es 405doreti_popl_es: 406 popl %es 407 .globl doreti_popl_ds 408doreti_popl_ds: 409 popl %ds 410 popal 411 addl $8,%esp 412 .globl doreti_iret 413doreti_iret: 414 iret 415 416 /* 417 * doreti_iret_fault and friends. Alternative return code for 418 * the case where we get a fault in the doreti_exit code 419 * above. trap() (i386/i386/trap.c) catches this specific 420 * case, sends the process a signal and continues in the 421 * corresponding place in the code below. 422 */ 423 ALIGN_TEXT 424 .globl doreti_iret_fault 425doreti_iret_fault: 426 subl $8,%esp 427 pushal 428 pushl $0 429 movw %ds,(%esp) 430 .globl doreti_popl_ds_fault 431doreti_popl_ds_fault: 432 pushl $0 433 movw %es,(%esp) 434 .globl doreti_popl_es_fault 435doreti_popl_es_fault: 436 pushl $0 437 movw %fs,(%esp) 438 .globl doreti_popl_fs_fault 439doreti_popl_fs_fault: 440 sti 441 movl $0,TF_ERR(%esp) /* XXX should be the error code */ 442 movl $T_PROTFLT,TF_TRAPNO(%esp) 443 jmp alltraps_with_regs_pushed 444#ifdef HWPMC_HOOKS 445doreti_nmi: 446 /* 447 * Since we are returning from an NMI, check if the current trap 448 * was from user mode and if so whether the current thread 449 * needs a user call chain capture. 450 */ 451 testb $SEL_RPL_MASK,TF_CS(%esp) 452 jz doreti_exit 453 movl PCPU(CURTHREAD),%eax /* curthread present? */ 454 orl %eax,%eax 455 jz doreti_exit 456 testl $TDP_CALLCHAIN,TD_PFLAGS(%eax) /* flagged for capture? */ 457 jz doreti_exit 458 /* 459 * Take the processor out of NMI mode by executing a fake "iret". 460 */ 461 pushfl 462 pushl %cs 463 pushl $outofnmi 464 iret 465outofnmi: 466 /* 467 * Call the callchain capture hook after turning interrupts back on. 468 */ 469 movl pmc_hook,%ecx 470 orl %ecx,%ecx 471 jz doreti_exit 472 pushl %esp /* frame pointer */ 473 pushl $PMC_FN_USER_CALLCHAIN /* command */ 474 movl PCPU(CURTHREAD),%eax 475 pushl %eax /* curthread */ 476 sti 477 call *%ecx 478 addl $12,%esp 479 jmp doreti_ast 480 ENTRY(end_exceptions) 481#endif 482