Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

621 lines
16 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. trapc.c
  5. Abstract:
  6. This module contains some trap handling code written in C.
  7. Only by the kernel.
  8. Author:
  9. Ken Reneris 6-9-93
  10. Revision History:
  11. --*/
  12. #include "ki.h"
  13. NTSTATUS
  14. Ki386CheckDivideByZeroTrap (
  15. IN PKTRAP_FRAME UserFrame
  16. );
  17. VOID
  18. KipWorkAroundCompiler (
  19. USHORT * StatusWord,
  20. USHORT * ControlWord
  21. );
  22. #ifdef ALLOC_PRAGMA
  23. #pragma alloc_text(PAGE, Ki386CheckDivideByZeroTrap)
  24. #endif
  25. #define REG(field) ((ULONG)(&((KTRAP_FRAME *)0)->field))
  26. #define GETREG(frame,reg) ((PULONG) (((ULONG) frame)+reg))[0]
  27. typedef struct {
  28. UCHAR RmDisplaceOnly; // RM of displacment only, no base reg
  29. UCHAR RmSib; // RM of SIB
  30. UCHAR RmDisplace; // bit mask of RMs which have a displacement
  31. UCHAR Disp; // sizeof displacement (in bytes)
  32. } KMOD, *PKMOD;
  33. static UCHAR RM32[] = {
  34. /* 000 */ REG(Eax),
  35. /* 001 */ REG(Ecx),
  36. /* 010 */ REG(Edx),
  37. /* 011 */ REG(Ebx),
  38. /* 100 */ REG(HardwareEsp),
  39. /* 101 */ REG(Ebp), // SIB
  40. /* 110 */ REG(Esi),
  41. /* 111 */ REG(Edi)
  42. };
  43. static UCHAR RM8[] = {
  44. /* 000 */ REG(Eax), // al
  45. /* 001 */ REG(Ecx), // cl
  46. /* 010 */ REG(Edx), // dl
  47. /* 011 */ REG(Ebx), // bl
  48. /* 100 */ REG(Eax) + 1, // ah
  49. /* 101 */ REG(Ecx) + 1, // ch
  50. /* 110 */ REG(Edx) + 1, // dh
  51. /* 111 */ REG(Ebx) + 1 // bh
  52. };
  53. static KMOD MOD32[] = {
  54. /* 00 */ 5, 4, 0x20, 4,
  55. /* 01 */ 0xff, 4, 0xff, 1,
  56. /* 10 */ 0xff, 4, 0xff, 4,
  57. /* 11 */ 0xff, 0xff, 0x00, 0
  58. } ;
  59. static struct {
  60. UCHAR Opcode1, Opcode2; // instruction opcode
  61. UCHAR ModRm, type; // if 2nd part of opcode is encoded in ModRm
  62. } NoWaitNpxInstructions[] = {
  63. /* FNINIT */ 0xDB, 0xE3, 0, 1,
  64. /* FNCLEX */ 0xDB, 0xE2, 0, 1,
  65. /* FNSTENV */ 0xD9, 0x06, 1, 1,
  66. /* FNSAVE */ 0xDD, 0x06, 1, 1,
  67. /* FNSTCW */ 0xD9, 0x07, 1, 2,
  68. /* FNSTSW */ 0xDD, 0x07, 1, 3,
  69. /* FNSTSW AX*/ 0xDF, 0xE0, 0, 4,
  70. 0x00, 0x00, 0, 1
  71. };
  72. NTSTATUS
  73. Ki386CheckDivideByZeroTrap (
  74. IN PKTRAP_FRAME UserFrame
  75. )
  76. /*++
  77. Routine Description:
  78. This function gains control when the x86 processor generates a
  79. divide by zero trap. The x86 design generates such a trap on
  80. divide by zero and on division overflows. In order to determine
  81. which expection code to dispatch, the divisor of the "div" or "idiv"
  82. instruction needs to be inspected.
  83. Arguments:
  84. UserFrame - Trap frame of the divide by zero trap
  85. Return Value:
  86. exception code dispatch
  87. --*/
  88. {
  89. ULONG operandsize, operandmask, i, accum;
  90. PUCHAR istream, pRM;
  91. UCHAR ibyte, rm;
  92. PKMOD Mod;
  93. BOOLEAN fPrefix;
  94. NTSTATUS status;
  95. status = STATUS_INTEGER_DIVIDE_BY_ZERO;
  96. if (UserFrame->SegCs == KGDT_R0_CODE) {
  97. //
  98. // Divide by zero exception from Kernel Mode?
  99. // Likely bad hardware interrupt and the device or vector table is corrupt.
  100. // Bugcheck NOW so we can figure out what went wrong. If we try and
  101. // proceed, then we'll likely fault in reading the top of user space, and then
  102. // double fault (page fault in the div zero handler.) -- This is a debugging
  103. // consideration. You can't put breakpoints on the trap labels so this is hard to debug.
  104. // We cannot recover
  105. //
  106. //
  107. KeBugCheck (UNEXPECTED_KERNEL_MODE_TRAP);
  108. }
  109. try {
  110. //
  111. // read instruction prefixes
  112. //
  113. fPrefix = TRUE;
  114. pRM = RM32;
  115. operandsize = 4;
  116. operandmask = 0xffffffff;
  117. istream = (PUCHAR) UserFrame->Eip;
  118. while (fPrefix) {
  119. ibyte = ProbeAndReadUchar(istream);
  120. istream++;
  121. switch (ibyte) {
  122. case 0x2e: // cs override
  123. case 0x36: // ss override
  124. case 0x3e: // ds override
  125. case 0x26: // es override
  126. case 0x64: // fs override
  127. case 0x65: // gs override
  128. case 0xF3: // rep
  129. case 0xF2: // rep
  130. case 0xF0: // lock
  131. break;
  132. case 0x66:
  133. // 16 bit operand override
  134. operandsize = 2;
  135. operandmask = 0xffff;
  136. break;
  137. case 0x67:
  138. // 16 bit address size override
  139. // this is some non-flat code
  140. goto try_exit;
  141. default:
  142. fPrefix = FALSE;
  143. break;
  144. }
  145. }
  146. //
  147. // Check instruction opcode
  148. //
  149. if (ibyte != 0xf7 && ibyte != 0xf6) {
  150. // this is not a DIV or IDIV opcode
  151. goto try_exit;
  152. }
  153. if (ibyte == 0xf6) {
  154. // this is a byte div or idiv
  155. operandsize = 1;
  156. operandmask = 0xff;
  157. }
  158. //
  159. // Get Mod R/M
  160. //
  161. ibyte = ProbeAndReadUchar (istream);
  162. istream++;
  163. Mod = MOD32 + (ibyte >> 6);
  164. rm = ibyte & 7;
  165. //
  166. // put register values into accum
  167. //
  168. if (operandsize == 1 && (ibyte & 0xc0) == 0xc0) {
  169. pRM = RM8;
  170. }
  171. accum = 0;
  172. if (rm != Mod->RmDisplaceOnly) {
  173. if (rm == Mod->RmSib) {
  174. // get SIB
  175. ibyte = ProbeAndReadUchar(istream);
  176. istream++;
  177. i = (ibyte >> 3) & 7;
  178. if (i != 4) {
  179. accum = GETREG(UserFrame, RM32[i]);
  180. accum = accum << (ibyte >> 6); // apply scaler
  181. }
  182. i = ibyte & 7;
  183. accum = accum + GETREG(UserFrame, RM32[i]);
  184. } else {
  185. // get register's value
  186. accum = GETREG(UserFrame, pRM[rm]);
  187. }
  188. }
  189. //
  190. // apply displacement to accum
  191. //
  192. if (Mod->RmDisplace & (1 << rm)) {
  193. if (Mod->Disp == 4) {
  194. i = ProbeAndReadUlong ((PULONG) istream);
  195. } else {
  196. ibyte = ProbeAndReadChar (istream);
  197. i = (signed long) ((signed char) ibyte); // sign extend
  198. }
  199. accum += i;
  200. }
  201. //
  202. // if this is an effective address, go get the data value
  203. //
  204. if (Mod->Disp && accum) {
  205. switch (operandsize) {
  206. case 1: accum = ProbeAndReadUchar((PUCHAR) accum); break;
  207. case 2: accum = ProbeAndReadUshort((PUSHORT) accum); break;
  208. case 4: accum = ProbeAndReadUlong((PULONG) accum); break;
  209. }
  210. }
  211. //
  212. // accum now contains the instruction operand, see if the
  213. // operand was really a zero
  214. //
  215. if (accum & operandmask) {
  216. // operand was non-zero, must be an overflow
  217. status = STATUS_INTEGER_OVERFLOW;
  218. }
  219. try_exit: ;
  220. } except (EXCEPTION_EXECUTE_HANDLER) {
  221. // do nothing...
  222. }
  223. return status;
  224. }
  225. UCHAR
  226. KiNextIStreamByte (
  227. IN PKTRAP_FRAME UserFrame,
  228. IN PUCHAR *istream
  229. )
  230. /*++
  231. Routine Description:
  232. Reads the next byte from the istream pointed to by the UserFrame, and
  233. advances the EIP.
  234. Note: this function works for 32 bit code only
  235. --*/
  236. {
  237. UCHAR ibyte;
  238. if (UserFrame->SegCs == KGDT_R0_CODE) {
  239. ibyte = **istream;
  240. } else {
  241. ibyte = ProbeAndReadUchar (*istream);
  242. }
  243. *istream += 1;
  244. return ibyte;
  245. }
  246. BOOLEAN
  247. Ki386CheckDelayedNpxTrap (
  248. IN PKTRAP_FRAME UserFrame,
  249. IN PFX_SAVE_AREA NpxFrame
  250. )
  251. /*++
  252. Routine Description:
  253. This function gains control from the Trap07 handler. It examines
  254. the user mode instruction to see if it's a NoWait NPX instruction.
  255. Such instructions do not generate floating point exceptions - this
  256. check needs to be done due to the way 80386/80387 systems are
  257. implemented. Such machines will generate a floating point exception
  258. interrupt when the kernel performs an FRSTOR to reload the thread's
  259. NPX context. If the thread's next instruction is a NoWait style
  260. instruction, then we clear the exception or emulate the instruction.
  261. AND... due to a different 80386/80387 "feature" the kernel needs
  262. to use FWAIT at times which can causes 80487's to generate delayed
  263. exceptions that can lead to the same problem described above.
  264. Arguments:
  265. UserFrame - Trap frame of the exception
  266. NpxFrame - Thread's NpxFrame (WARNING: does not have NpxState)
  267. Interrupts are disabled
  268. Return Value:
  269. FALSE - Dispatch NPX exception to user mode
  270. TRUE - Exception handled, continue
  271. --*/
  272. {
  273. EXCEPTION_RECORD ExceptionRecord;
  274. UCHAR ibyte1, ibyte2, inmodrm, status;
  275. USHORT StatusWord, ControlWord, UsersWord;
  276. PUCHAR istream;
  277. BOOLEAN fPrefix;
  278. UCHAR rm;
  279. PKMOD Mod;
  280. ULONG accum, i;
  281. status = 0;
  282. //
  283. // read instruction prefixes
  284. //
  285. fPrefix = TRUE;
  286. istream = (PUCHAR) UserFrame->Eip;
  287. try {
  288. while (fPrefix) {
  289. ibyte1 = KiNextIStreamByte (UserFrame, &istream);
  290. switch (ibyte1) {
  291. case 0x2e: // cs override
  292. case 0x36: // ss override
  293. case 0x3e: // ds override
  294. case 0x26: // es override
  295. case 0x64: // fs override
  296. case 0x65: // gs override
  297. break;
  298. default:
  299. fPrefix = FALSE;
  300. break;
  301. }
  302. }
  303. //
  304. // Check for coprocessor NoWait NPX instruction
  305. //
  306. ibyte2 = KiNextIStreamByte (UserFrame, &istream);
  307. inmodrm = (ibyte2 >> 3) & 0x7;
  308. for (i=0; NoWaitNpxInstructions[i].Opcode1; i++) {
  309. if (NoWaitNpxInstructions[i].Opcode1 == ibyte1) {
  310. //
  311. // first opcode byte matched - check second part of opcode
  312. //
  313. if (NoWaitNpxInstructions[i].ModRm) {
  314. //
  315. // modrm only applies for opcode in range 0-0xbf
  316. //
  317. if (((ibyte2 & 0xc0) != 0xc0) &&
  318. (NoWaitNpxInstructions[i].Opcode2 == inmodrm)) {
  319. //
  320. // This is a no-wait NPX instruction
  321. //
  322. status = NoWaitNpxInstructions[i].type;
  323. break;
  324. }
  325. } else {
  326. if (NoWaitNpxInstructions[i].Opcode2 == ibyte2) {
  327. //
  328. // This is a no-wait NPX instruction
  329. //
  330. status = NoWaitNpxInstructions[i].type;
  331. break;
  332. }
  333. }
  334. }
  335. }
  336. } except (EXCEPTION_EXECUTE_HANDLER) {
  337. // do nothing...
  338. }
  339. if (status == 0) {
  340. //
  341. // Dispatch coprocessor exception to user mode
  342. //
  343. return FALSE;
  344. }
  345. if (status == 1) {
  346. //
  347. // Ignore pending exception, user mode instruction does not trap
  348. // on pending execptions and it will clear/mask the pending exceptions
  349. //
  350. _asm {
  351. mov eax, cr0
  352. and eax, NOT (CR0_MP+CR0_EM+CR0_TS)
  353. mov cr0, eax
  354. }
  355. NpxFrame->Cr0NpxState &= ~CR0_TS;
  356. return TRUE;
  357. }
  358. //
  359. // This is either FNSTSW or FNSTCW. Both of these instructions get
  360. // a value from the coprocessor without effecting the pending exception
  361. // state. To do this we emulate the instructions.
  362. //
  363. //
  364. // Read the coprocessors Status & Control word state, then re-enable
  365. // interrupts. (it's safe to context switch after that point)
  366. //
  367. //
  368. // NOTE: The new compiler is generating a FWAIT at the
  369. // entry to the try/except block if it sees inline
  370. // fp instructions, even if they are only control word accesses.
  371. // put this stuff in another function to fool it.
  372. //
  373. KipWorkAroundCompiler (&StatusWord, &ControlWord);
  374. if (status == 4) {
  375. //
  376. // Emulate FNSTSW AX
  377. //
  378. UserFrame->Eip = (ULONG)istream;
  379. UserFrame->Eax = (UserFrame->Eax & 0xFFFF0000) | StatusWord;
  380. return TRUE;
  381. }
  382. if (status == 2) {
  383. UsersWord = ControlWord;
  384. } else {
  385. UsersWord = StatusWord;
  386. }
  387. try {
  388. //
  389. // (PERFNOTE: the operand decode code should really share code with
  390. // KiCheckDivideByZeroTrap, but this is a late change therefore the
  391. // code was copied to keep the impact of the change localized)
  392. //
  393. //
  394. // decode Mod/RM byte
  395. //
  396. Mod = MOD32 + (ibyte2 >> 6);
  397. rm = ibyte2 & 7;
  398. //
  399. // Decode the instruction's word pointer into accum
  400. //
  401. accum = 0;
  402. if (rm != Mod->RmDisplaceOnly) {
  403. if (rm == Mod->RmSib) {
  404. // get SIB
  405. ibyte1 = KiNextIStreamByte (UserFrame, &istream);
  406. i = (ibyte1 >> 3) & 7;
  407. if (i != 4) {
  408. accum = GETREG(UserFrame, RM32[i]);
  409. accum = accum << (ibyte1 >> 6); // apply scaler
  410. }
  411. i = ibyte1 & 7;
  412. accum = accum + GETREG(UserFrame, RM32[i]);
  413. } else {
  414. // get register's value
  415. accum = GETREG(UserFrame, RM32[rm]);
  416. }
  417. }
  418. //
  419. // apply displacement to accum
  420. //
  421. if (Mod->RmDisplace & (1 << rm)) {
  422. if (Mod->Disp == 4) {
  423. i = (KiNextIStreamByte (UserFrame, &istream) << 0) |
  424. (KiNextIStreamByte (UserFrame, &istream) << 8) |
  425. (KiNextIStreamByte (UserFrame, &istream) << 16) |
  426. (KiNextIStreamByte (UserFrame, &istream) << 24);
  427. } else {
  428. ibyte1 = KiNextIStreamByte (UserFrame, &istream);
  429. i = (signed long) ((signed char) ibyte1); // sign extend
  430. }
  431. accum += i;
  432. }
  433. //
  434. // Set the word pointer
  435. //
  436. if (UserFrame->SegCs == KGDT_R0_CODE) {
  437. *((PUSHORT) accum) = UsersWord;
  438. } else {
  439. ProbeAndWriteUshort ((PUSHORT) accum, UsersWord);
  440. }
  441. UserFrame->Eip = (ULONG)istream;
  442. } except (KiCopyInformation(&ExceptionRecord,
  443. (GetExceptionInformation())->ExceptionRecord)) {
  444. //
  445. // Faulted addressing user's memory.
  446. // Set the address of the exception to the current program address
  447. // and raise the exception by calling the exception dispatcher.
  448. //
  449. ExceptionRecord.ExceptionAddress = (PVOID)(UserFrame->Eip);
  450. KiDispatchException(
  451. &ExceptionRecord,
  452. NULL, // ExceptionFrame
  453. UserFrame,
  454. UserMode,
  455. TRUE
  456. );
  457. }
  458. return TRUE;
  459. }
  460. //
  461. // Code description is above. We do this here to stop the compiler
  462. // from putting fwait in the try/except block
  463. //
  464. // Read the coprocessor's Status & Control word state, then re-enable
  465. // interrupts. (it's safe to context switch after that point)
  466. //
  467. //
  468. VOID
  469. KipWorkAroundCompiler (
  470. IN PUSHORT StatusWord,
  471. IN PUSHORT ControlWord
  472. )
  473. {
  474. USHORT sw;
  475. USHORT cw;
  476. sw = *StatusWord;
  477. cw = *ControlWord;
  478. _asm {
  479. mov eax, cr0
  480. mov ecx, eax
  481. and eax, NOT (CR0_MP+CR0_EM+CR0_TS)
  482. mov cr0, eax
  483. fnstsw sw
  484. fnstcw cw
  485. mov cr0, ecx
  486. sti
  487. }
  488. *StatusWord = sw;
  489. *ControlWord = cw;
  490. }