/*++ Copyright (c) 1989 Microsoft Corporation Module Name: trapc.c Abstract: This module contains some trap handling code written in C. Only by the kernel. Author: Ken Reneris 6-9-93 Revision History: --*/ #include "ki.h" NTSTATUS Ki386CheckDivideByZeroTrap ( IN PKTRAP_FRAME UserFrame ); VOID KipWorkAroundCompiler ( USHORT * StatusWord, USHORT * ControlWord ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, Ki386CheckDivideByZeroTrap) #endif #define REG(field) ((ULONG)(&((KTRAP_FRAME *)0)->field)) #define GETREG(frame,reg) ((PULONG) (((ULONG) frame)+reg))[0] typedef struct { UCHAR RmDisplaceOnly; // RM of displacment only, no base reg UCHAR RmSib; // RM of SIB UCHAR RmDisplace; // bit mask of RMs which have a displacement UCHAR Disp; // sizeof displacement (in bytes) } KMOD, *PKMOD; static UCHAR RM32[] = { /* 000 */ REG(Eax), /* 001 */ REG(Ecx), /* 010 */ REG(Edx), /* 011 */ REG(Ebx), /* 100 */ REG(HardwareEsp), /* 101 */ REG(Ebp), // SIB /* 110 */ REG(Esi), /* 111 */ REG(Edi) }; static UCHAR RM8[] = { /* 000 */ REG(Eax), // al /* 001 */ REG(Ecx), // cl /* 010 */ REG(Edx), // dl /* 011 */ REG(Ebx), // bl /* 100 */ REG(Eax) + 1, // ah /* 101 */ REG(Ecx) + 1, // ch /* 110 */ REG(Edx) + 1, // dh /* 111 */ REG(Ebx) + 1 // bh }; static KMOD MOD32[] = { /* 00 */ 5, 4, 0x20, 4, /* 01 */ 0xff, 4, 0xff, 1, /* 10 */ 0xff, 4, 0xff, 4, /* 11 */ 0xff, 0xff, 0x00, 0 } ; static struct { UCHAR Opcode1, Opcode2; // instruction opcode UCHAR ModRm, type; // if 2nd part of opcode is encoded in ModRm } NoWaitNpxInstructions[] = { /* FNINIT */ 0xDB, 0xE3, 0, 1, /* FNCLEX */ 0xDB, 0xE2, 0, 1, /* FNSTENV */ 0xD9, 0x06, 1, 1, /* FNSAVE */ 0xDD, 0x06, 1, 1, /* FNSTCW */ 0xD9, 0x07, 1, 2, /* FNSTSW */ 0xDD, 0x07, 1, 3, /* FNSTSW AX*/ 0xDF, 0xE0, 0, 4, 0x00, 0x00, 0, 1 }; NTSTATUS Ki386CheckDivideByZeroTrap ( IN PKTRAP_FRAME UserFrame ) /*++ Routine Description: This function gains control when the x86 processor generates a divide by zero trap. The x86 design generates such a trap on divide by zero and on division overflows. In order to determine which expection code to dispatch, the divisor of the "div" or "idiv" instruction needs to be inspected. Arguments: UserFrame - Trap frame of the divide by zero trap Return Value: exception code dispatch --*/ { ULONG operandsize, operandmask, i, accum; PUCHAR istream, pRM; UCHAR ibyte, rm; PKMOD Mod; BOOLEAN fPrefix; NTSTATUS status; status = STATUS_INTEGER_DIVIDE_BY_ZERO; if (UserFrame->SegCs == KGDT_R0_CODE) { // // Divide by zero exception from Kernel Mode? // Likely bad hardware interrupt and the device or vector table is corrupt. // Bugcheck NOW so we can figure out what went wrong. If we try and // proceed, then we'll likely fault in reading the top of user space, and then // double fault (page fault in the div zero handler.) -- This is a debugging // consideration. You can't put breakpoints on the trap labels so this is hard to debug. // We cannot recover // // KeBugCheck (UNEXPECTED_KERNEL_MODE_TRAP); } try { // // read instruction prefixes // fPrefix = TRUE; pRM = RM32; operandsize = 4; operandmask = 0xffffffff; istream = (PUCHAR) UserFrame->Eip; while (fPrefix) { ibyte = ProbeAndReadUchar(istream); istream++; switch (ibyte) { case 0x2e: // cs override case 0x36: // ss override case 0x3e: // ds override case 0x26: // es override case 0x64: // fs override case 0x65: // gs override case 0xF3: // rep case 0xF2: // rep case 0xF0: // lock break; case 0x66: // 16 bit operand override operandsize = 2; operandmask = 0xffff; break; case 0x67: // 16 bit address size override // this is some non-flat code goto try_exit; default: fPrefix = FALSE; break; } } // // Check instruction opcode // if (ibyte != 0xf7 && ibyte != 0xf6) { // this is not a DIV or IDIV opcode goto try_exit; } if (ibyte == 0xf6) { // this is a byte div or idiv operandsize = 1; operandmask = 0xff; } // // Get Mod R/M // ibyte = ProbeAndReadUchar (istream); istream++; Mod = MOD32 + (ibyte >> 6); rm = ibyte & 7; // // put register values into accum // if (operandsize == 1 && (ibyte & 0xc0) == 0xc0) { pRM = RM8; } accum = 0; if (rm != Mod->RmDisplaceOnly) { if (rm == Mod->RmSib) { // get SIB ibyte = ProbeAndReadUchar(istream); istream++; i = (ibyte >> 3) & 7; if (i != 4) { accum = GETREG(UserFrame, RM32[i]); accum = accum << (ibyte >> 6); // apply scaler } i = ibyte & 7; accum = accum + GETREG(UserFrame, RM32[i]); } else { // get register's value accum = GETREG(UserFrame, pRM[rm]); } } // // apply displacement to accum // if (Mod->RmDisplace & (1 << rm)) { if (Mod->Disp == 4) { i = ProbeAndReadUlong ((PULONG) istream); } else { ibyte = ProbeAndReadChar (istream); i = (signed long) ((signed char) ibyte); // sign extend } accum += i; } // // if this is an effective address, go get the data value // if (Mod->Disp && accum) { switch (operandsize) { case 1: accum = ProbeAndReadUchar((PUCHAR) accum); break; case 2: accum = ProbeAndReadUshort((PUSHORT) accum); break; case 4: accum = ProbeAndReadUlong((PULONG) accum); break; } } // // accum now contains the instruction operand, see if the // operand was really a zero // if (accum & operandmask) { // operand was non-zero, must be an overflow status = STATUS_INTEGER_OVERFLOW; } try_exit: ; } except (EXCEPTION_EXECUTE_HANDLER) { // do nothing... } return status; } UCHAR KiNextIStreamByte ( IN PKTRAP_FRAME UserFrame, IN PUCHAR *istream ) /*++ Routine Description: Reads the next byte from the istream pointed to by the UserFrame, and advances the EIP. Note: this function works for 32 bit code only --*/ { UCHAR ibyte; if (UserFrame->SegCs == KGDT_R0_CODE) { ibyte = **istream; } else { ibyte = ProbeAndReadUchar (*istream); } *istream += 1; return ibyte; } BOOLEAN Ki386CheckDelayedNpxTrap ( IN PKTRAP_FRAME UserFrame, IN PFX_SAVE_AREA NpxFrame ) /*++ Routine Description: This function gains control from the Trap07 handler. It examines the user mode instruction to see if it's a NoWait NPX instruction. Such instructions do not generate floating point exceptions - this check needs to be done due to the way 80386/80387 systems are implemented. Such machines will generate a floating point exception interrupt when the kernel performs an FRSTOR to reload the thread's NPX context. If the thread's next instruction is a NoWait style instruction, then we clear the exception or emulate the instruction. AND... due to a different 80386/80387 "feature" the kernel needs to use FWAIT at times which can causes 80487's to generate delayed exceptions that can lead to the same problem described above. Arguments: UserFrame - Trap frame of the exception NpxFrame - Thread's NpxFrame (WARNING: does not have NpxState) Interrupts are disabled Return Value: FALSE - Dispatch NPX exception to user mode TRUE - Exception handled, continue --*/ { EXCEPTION_RECORD ExceptionRecord; UCHAR ibyte1, ibyte2, inmodrm, status; USHORT StatusWord, ControlWord, UsersWord; PUCHAR istream; BOOLEAN fPrefix; UCHAR rm; PKMOD Mod; ULONG accum, i; status = 0; // // read instruction prefixes // fPrefix = TRUE; istream = (PUCHAR) UserFrame->Eip; try { while (fPrefix) { ibyte1 = KiNextIStreamByte (UserFrame, &istream); switch (ibyte1) { case 0x2e: // cs override case 0x36: // ss override case 0x3e: // ds override case 0x26: // es override case 0x64: // fs override case 0x65: // gs override break; default: fPrefix = FALSE; break; } } // // Check for coprocessor NoWait NPX instruction // ibyte2 = KiNextIStreamByte (UserFrame, &istream); inmodrm = (ibyte2 >> 3) & 0x7; for (i=0; NoWaitNpxInstructions[i].Opcode1; i++) { if (NoWaitNpxInstructions[i].Opcode1 == ibyte1) { // // first opcode byte matched - check second part of opcode // if (NoWaitNpxInstructions[i].ModRm) { // // modrm only applies for opcode in range 0-0xbf // if (((ibyte2 & 0xc0) != 0xc0) && (NoWaitNpxInstructions[i].Opcode2 == inmodrm)) { // // This is a no-wait NPX instruction // status = NoWaitNpxInstructions[i].type; break; } } else { if (NoWaitNpxInstructions[i].Opcode2 == ibyte2) { // // This is a no-wait NPX instruction // status = NoWaitNpxInstructions[i].type; break; } } } } } except (EXCEPTION_EXECUTE_HANDLER) { // do nothing... } if (status == 0) { // // Dispatch coprocessor exception to user mode // return FALSE; } if (status == 1) { // // Ignore pending exception, user mode instruction does not trap // on pending execptions and it will clear/mask the pending exceptions // _asm { mov eax, cr0 and eax, NOT (CR0_MP+CR0_EM+CR0_TS) mov cr0, eax } NpxFrame->Cr0NpxState &= ~CR0_TS; return TRUE; } // // This is either FNSTSW or FNSTCW. Both of these instructions get // a value from the coprocessor without effecting the pending exception // state. To do this we emulate the instructions. // // // Read the coprocessors Status & Control word state, then re-enable // interrupts. (it's safe to context switch after that point) // // // NOTE: The new compiler is generating a FWAIT at the // entry to the try/except block if it sees inline // fp instructions, even if they are only control word accesses. // put this stuff in another function to fool it. // KipWorkAroundCompiler (&StatusWord, &ControlWord); if (status == 4) { // // Emulate FNSTSW AX // UserFrame->Eip = (ULONG)istream; UserFrame->Eax = (UserFrame->Eax & 0xFFFF0000) | StatusWord; return TRUE; } if (status == 2) { UsersWord = ControlWord; } else { UsersWord = StatusWord; } try { // // (PERFNOTE: the operand decode code should really share code with // KiCheckDivideByZeroTrap, but this is a late change therefore the // code was copied to keep the impact of the change localized) // // // decode Mod/RM byte // Mod = MOD32 + (ibyte2 >> 6); rm = ibyte2 & 7; // // Decode the instruction's word pointer into accum // accum = 0; if (rm != Mod->RmDisplaceOnly) { if (rm == Mod->RmSib) { // get SIB ibyte1 = KiNextIStreamByte (UserFrame, &istream); i = (ibyte1 >> 3) & 7; if (i != 4) { accum = GETREG(UserFrame, RM32[i]); accum = accum << (ibyte1 >> 6); // apply scaler } i = ibyte1 & 7; accum = accum + GETREG(UserFrame, RM32[i]); } else { // get register's value accum = GETREG(UserFrame, RM32[rm]); } } // // apply displacement to accum // if (Mod->RmDisplace & (1 << rm)) { if (Mod->Disp == 4) { i = (KiNextIStreamByte (UserFrame, &istream) << 0) | (KiNextIStreamByte (UserFrame, &istream) << 8) | (KiNextIStreamByte (UserFrame, &istream) << 16) | (KiNextIStreamByte (UserFrame, &istream) << 24); } else { ibyte1 = KiNextIStreamByte (UserFrame, &istream); i = (signed long) ((signed char) ibyte1); // sign extend } accum += i; } // // Set the word pointer // if (UserFrame->SegCs == KGDT_R0_CODE) { *((PUSHORT) accum) = UsersWord; } else { ProbeAndWriteUshort ((PUSHORT) accum, UsersWord); } UserFrame->Eip = (ULONG)istream; } except (KiCopyInformation(&ExceptionRecord, (GetExceptionInformation())->ExceptionRecord)) { // // Faulted addressing user's memory. // Set the address of the exception to the current program address // and raise the exception by calling the exception dispatcher. // ExceptionRecord.ExceptionAddress = (PVOID)(UserFrame->Eip); KiDispatchException( &ExceptionRecord, NULL, // ExceptionFrame UserFrame, UserMode, TRUE ); } return TRUE; } // // Code description is above. We do this here to stop the compiler // from putting fwait in the try/except block // // Read the coprocessor's Status & Control word state, then re-enable // interrupts. (it's safe to context switch after that point) // // VOID KipWorkAroundCompiler ( IN PUSHORT StatusWord, IN PUSHORT ControlWord ) { USHORT sw; USHORT cw; sw = *StatusWord; cw = *ControlWord; _asm { mov eax, cr0 mov ecx, eax and eax, NOT (CR0_MP+CR0_EM+CR0_TS) mov cr0, eax fnstsw sw fnstcw cw mov cr0, ecx sti } *StatusWord = sw; *ControlWord = cw; }