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