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