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.

591 lines
20 KiB

  1. /*++
  2. Copyright (c) 1993 Digital Equipment Corporation
  3. Module Name:
  4. trigger.c
  5. Abstract:
  6. This module implements functions that handle synchronous and asynchronous
  7. arithmetic exceptions. The Alpha SRM specifies certain code generation
  8. rules which if followed allow this code (in conjunction with internal
  9. processor register state) to effect a precise, synchronous exception
  10. given an imprecise, asynchronous exception. This capability is required
  11. for software emulation of the IEEE single and double floating operations.
  12. Author:
  13. Thomas Van Baak (tvb) 5-Mar-1993
  14. Environment:
  15. Kernel mode only.
  16. Revision History:
  17. --*/
  18. #include "ki.h"
  19. #pragma hdrstop
  20. #include "alphaops.h"
  21. //
  22. // Define forward referenced function prototypes.
  23. //
  24. BOOLEAN
  25. KiLocateTriggerPc (
  26. IN OUT PEXCEPTION_RECORD ExceptionRecord,
  27. IN OUT PKTRAP_FRAME TrapFrame
  28. );
  29. //
  30. // Define debugging macros.
  31. //
  32. #if DBG
  33. extern ULONG RtlDebugFlags;
  34. #define DBGPRINT ((RtlDebugFlags & 0x4) != 0) && DbgPrint
  35. #else
  36. #define DBGPRINT 0 && DbgPrint
  37. #endif
  38. //
  39. // Define non-IEEE (a/k/a `high performance') arithmetic exception types.
  40. // The PALcode exception record is extended by one word and the 4th word
  41. // contains the reason the arithmetic exception is not an IEEE exception.
  42. //
  43. #define NON_IEEE(ExceptionRecord, Reason) \
  44. (ExceptionRecord)->NumberParameters = 4; \
  45. (ExceptionRecord)->ExceptionInformation[3] = (Reason);
  46. #define TRIGGER_FLOATING_REGISTER_MASK_CLEAR 1
  47. #define TRIGGER_INTEGER_REGISTER_MASK_SET 2
  48. #define TRIGGER_NO_SOFTWARE_COMPLETION 3
  49. #define TRIGGER_INVALID_INSTRUCTION_FOUND 4
  50. #define TRIGGER_INSTRUCTION_FETCH_ERROR 5
  51. #define TRIGGER_INSTRUCTION_NOT_FOUND 6
  52. #define TRIGGER_SOURCE_IS_DESTINATION 7
  53. #define TRIGGER_WRONG_INSTRUCTION 8
  54. BOOLEAN
  55. KiFloatingException (
  56. IN OUT PEXCEPTION_RECORD ExceptionRecord,
  57. IN OUT PKEXCEPTION_FRAME ExceptionFrame,
  58. IN OUT PKTRAP_FRAME TrapFrame,
  59. IN BOOLEAN ImpreciseTrap,
  60. IN OUT PULONG SoftFpcrCopy
  61. )
  62. /*++
  63. Routine Description:
  64. This function is called to emulate a floating operation and convert the
  65. exception status to the proper value. If the exception is a fault, the
  66. faulting floating point instruction is emulated. If the exception is an
  67. imprecise trap, an attempt is made to locate and to emulate the original
  68. trapping floating point instruction.
  69. Arguments:
  70. ExceptionRecord - Supplies a pointer to an exception record.
  71. ExceptionFrame - Supplies a pointer to an exception frame.
  72. TrapFrame - Supplies a pointer to a trap frame.
  73. ImpreciseTrap - Supplies a boolean value that specifies whether the
  74. exception is an imprecise trap.
  75. SoftFpcrCopy - Supplies a pointer to a longword variable that receives
  76. a copy of the software FPCR.
  77. Return Value:
  78. A value of TRUE is returned if the floating exception is successfully
  79. emulated. Otherwise, a value of FALSE is returned.
  80. --*/
  81. {
  82. BOOLEAN Status;
  83. PSW_FPCR SoftwareFpcr;
  84. PTEB Teb;
  85. try {
  86. //
  87. // Obtain a copy of the software FPCR longword from the TEB.
  88. //
  89. Teb = NtCurrentTeb();
  90. *SoftFpcrCopy = Teb->FpSoftwareStatusRegister;
  91. SoftwareFpcr = (PSW_FPCR)SoftFpcrCopy;
  92. DBGPRINT("KiFloatingException: SoftFpcr = %.8lx\n", *SoftFpcrCopy);
  93. #if DBG
  94. //
  95. // If the floating emulation inhibit flag is set, then bypass all
  96. // software emulation by the kernel and return FALSE to raise the
  97. // original PALcode exception.
  98. //
  99. // N.B. This is for user-mode development and testing and is not
  100. // part of the API.
  101. //
  102. if (SoftwareFpcr->NoSoftwareEmulation != 0) {
  103. DBGPRINT("KiFloatingException: NoSoftwareEmulation\n");
  104. return FALSE;
  105. }
  106. #endif
  107. //
  108. // If the arithmetic exception is an imprecise trap, the address of
  109. // the trapping instruction is somewhere before the exception address.
  110. //
  111. // Otherwise the exception is a fault and the address of the faulting
  112. // instruction is the exception address.
  113. //
  114. if (ImpreciseTrap != FALSE) {
  115. //
  116. // If the arithmetic trap ignore mode is enabled, then do not
  117. // spend time to locate or to emulate the trapping instruction,
  118. // leave unpredictable results in the destination register, do
  119. // not set correct IEEE sticky bits in the software FPCR, leave
  120. // the hardware FPCR sticky status bits as they are, and return
  121. // TRUE to continue execution. It is assumed that user code will
  122. // check the hardware FPCR exception status bits to determine if
  123. // the instruction succeeded or not (Insignia SoftPc feature).
  124. //
  125. if (SoftwareFpcr->ArithmeticTrapIgnore != 0) {
  126. return TRUE;
  127. }
  128. //
  129. // Attempt to locate the trapping instruction. If the instruction
  130. // stream is such that this is not possible or was not intended,
  131. // then set an exception code that best reflects the exception
  132. // summary register bits and return FALSE to raise the exception.
  133. //
  134. // Otherwise emulate the trigger instruction in order to compute
  135. // the correct destination result value, the correct IEEE status
  136. // bits, and raise any enabled IEEE exceptions.
  137. //
  138. if (KiLocateTriggerPc(ExceptionRecord, TrapFrame) == FALSE) {
  139. KiSetFloatingStatus(ExceptionRecord);
  140. return FALSE;
  141. }
  142. Status = KiEmulateFloating(ExceptionRecord,
  143. ExceptionFrame,
  144. TrapFrame,
  145. SoftwareFpcr);
  146. } else {
  147. //
  148. // Attempt to emulate the faulting instruction in order to perform
  149. // floating operations not supported by EV4, to compute the correct
  150. // destination result value, the correct IEEE status bits, and
  151. // raise any enabled IEEE exceptions.
  152. //
  153. Status = KiEmulateFloating(ExceptionRecord,
  154. ExceptionFrame,
  155. TrapFrame,
  156. SoftwareFpcr);
  157. //
  158. // If the emulation resulted in a floating point exception and
  159. // the arithmetic trap ignore mode is enabled, then set the return
  160. // value to TRUE to suppress the exception and continue execution.
  161. //
  162. if ((Status == FALSE) &&
  163. (SoftwareFpcr->ArithmeticTrapIgnore != 0) &&
  164. (ExceptionRecord->ExceptionCode != STATUS_ILLEGAL_INSTRUCTION)) {
  165. Status = TRUE;
  166. }
  167. }
  168. //
  169. // Store the updated software FPCR longword in the TEB.
  170. //
  171. Teb->FpSoftwareStatusRegister = *SoftFpcrCopy;
  172. DBGPRINT("KiFloatingException: SoftFpcr = %.8lx\n", *SoftFpcrCopy);
  173. } except (EXCEPTION_EXECUTE_HANDLER) {
  174. //
  175. // An exception occurred accessing the TEB.
  176. //
  177. ExceptionRecord->ExceptionCode = GetExceptionCode();
  178. return FALSE;
  179. }
  180. return Status;
  181. }
  182. BOOLEAN
  183. KiLocateTriggerPc (
  184. IN OUT PEXCEPTION_RECORD ExceptionRecord,
  185. IN OUT PKTRAP_FRAME TrapFrame
  186. )
  187. /*++
  188. Routine Description:
  189. This function is called to try to determine the precise location of the
  190. instruction that caused an arithmetic exception. The instruction that
  191. caused the trap to occur is known as the trigger instruction. On entry,
  192. the actual address of the trigger instruction is unknown and the exception
  193. address is the continuation address. The continuation address is the
  194. address of the instruction that would have executed had the trap not
  195. occurred. The instructions following the trigger instruction up to the
  196. continuation address are known as the trap shadow of the trigger
  197. instruction.
  198. Alpha AXP produces imprecise, asynchronous arithmetic exceptions. The
  199. exceptions are imprecise because the exception address when a trap is
  200. taken may be more than one instruction beyond the address of the
  201. instruction that actually caused the trap to occur.
  202. The arithmetic exceptions are traps (rather than faults) because the
  203. exception address is not the address of the trapping instruction
  204. itself, but the address of the next instruction to execute, which is
  205. always (at least) one instruction beyond the address of the trapping
  206. instruction.
  207. It is possible for multiple exceptions to occur and result in a single
  208. trap. This function only determines the address of the first trapping
  209. instruction.
  210. Unpredictable values may have been stored in the destination register
  211. of trapping instructions. Thus to insure that the trigger instruction
  212. can be located, and that the trigger instruction and any instructions
  213. in the trap shadow can be re-executed, certain restrictions are placed
  214. on the type of instructions or the mix of operands in the trap shadow.
  215. The code generation rules serve only to guarantee that the instruction
  216. backup algorithm and subsequent re-execution can always be successful.
  217. Hence the restrictions on such constructs as branches, jumps, and the
  218. re-use of source or destination operands within the trap shadow.
  219. Arguments:
  220. ExceptionRecord - Supplies a pointer to an exception record.
  221. TrapFrame - Supplies a pointer to a trap frame.
  222. Return Value:
  223. If the trigger PC was precisely determined, the exception address in
  224. the exception record is set to the trigger PC, the continuation address
  225. in the trap frame is updated, and a value of TRUE is returned. Otherwise
  226. no values are stored and a value of FALSE is returned.
  227. --*/
  228. {
  229. PEXC_SUM ExceptionSummary;
  230. ULONG Fa;
  231. ULONG Fb;
  232. ULONG Fc;
  233. ULONG FloatRegisterTrashMask;
  234. ULONG FloatRegisterWriteMask;
  235. ALPHA_INSTRUCTION Instruction;
  236. ULONG IntegerRegisterWriteMask;
  237. ULONG Opcode;
  238. ULONG_PTR TrapShadowLowLimit;
  239. ULONG_PTR TriggerPc;
  240. KPROCESSOR_MODE PreviousMode;
  241. //
  242. // Obtain a copy of the float and integer register write mask registers
  243. // and the exception summary register from the exception record built by
  244. // PALcode.
  245. //
  246. FloatRegisterWriteMask = (ULONG)ExceptionRecord->ExceptionInformation[0];
  247. IntegerRegisterWriteMask = (ULONG)ExceptionRecord->ExceptionInformation[1];
  248. ExceptionSummary = (PEXC_SUM)&(ExceptionRecord->ExceptionInformation[2]);
  249. DBGPRINT("KiLocateTriggerPc: WriteMask %.8lx.%.8lx, ExceptionSummary %.8lx\n",
  250. FloatRegisterWriteMask, IntegerRegisterWriteMask,
  251. *(PULONG)ExceptionSummary);
  252. //
  253. // Capture previous mode from trap frame not current thread.
  254. //
  255. PreviousMode = (KPROCESSOR_MODE)(((PSR *)(&TrapFrame->Psr))->MODE);
  256. if (FloatRegisterWriteMask == 0) {
  257. //
  258. // It should not be possible to have a floating point exception without
  259. // at least one of the destination float register bits set. The trap
  260. // shadow is invalid.
  261. //
  262. DBGPRINT("KiLocateTriggerPc: FloatRegisterWriteMask clear\n");
  263. NON_IEEE(ExceptionRecord, TRIGGER_FLOATING_REGISTER_MASK_CLEAR);
  264. return FALSE;
  265. }
  266. if (IntegerRegisterWriteMask != 0) {
  267. //
  268. // It is not possible to precisely locate the trigger instruction
  269. // when the integer overflow bit is set. The trap shadow is invalid.
  270. //
  271. DBGPRINT("KiLocateTriggerPc: IntegerRegisterMask set.\n");
  272. NON_IEEE(ExceptionRecord, TRIGGER_INTEGER_REGISTER_MASK_SET);
  273. return FALSE;
  274. }
  275. if (ExceptionSummary->SoftwareCompletion == 0) {
  276. //
  277. // The exception summary software completion bit is the AND of the
  278. // /S bits of all trapping instructions in the trap shadow. Since
  279. // the software completion bit is not set, it can be assumed the
  280. // code that was executing does not want precise exceptions, or if
  281. // it does, the code does not comply with the Alpha AXP guidelines
  282. // for locating the trigger PC. The trap shadow is invalid.
  283. //
  284. DBGPRINT("KiLocateTriggerPc: SoftwareCompletion clear\n");
  285. NON_IEEE(ExceptionRecord, TRIGGER_NO_SOFTWARE_COMPLETION);
  286. return FALSE;
  287. }
  288. //
  289. // Search for the trigger instruction starting with the instruction before
  290. // the continuation PC (the instruction pointed to by Fir either did not
  291. // complete or did not even start). Limit the search to the arbitrary
  292. // limit of N instructions back from the current PC to prevent unbounded
  293. // searches. The search is complete when all trapping destination register
  294. // bits in the float write mask register have been accounted for.
  295. //
  296. FloatRegisterTrashMask = 0;
  297. TriggerPc = (ULONG_PTR)TrapFrame->Fir;
  298. TrapShadowLowLimit = TriggerPc - (500 * sizeof(ULONG));
  299. try {
  300. do {
  301. TriggerPc -= 4;
  302. if (TriggerPc < TrapShadowLowLimit) {
  303. //
  304. // The trigger PC is too far away from the exception PC to
  305. // be reasonable. The trap shadow is invalid.
  306. //
  307. DBGPRINT("KiLocateTriggerPc: Trap shadow too long\n");
  308. NON_IEEE(ExceptionRecord, TRIGGER_INSTRUCTION_NOT_FOUND);
  309. return FALSE;
  310. }
  311. if (PreviousMode != KernelMode) {
  312. Instruction.Long = ProbeAndReadUlong((PULONG)TriggerPc);
  313. } else {
  314. Instruction.Long = *((PULONG)TriggerPc);
  315. }
  316. //
  317. // Examine the opcode of this instruction to determine if the
  318. // trap shadow is invalid.
  319. //
  320. Opcode = Instruction.Memory.Opcode;
  321. if (Opcode == JMP_OP) {
  322. //
  323. // This is one of the jump instructions: jump, return, or
  324. // either form of jsr. The trap shadow is invalid.
  325. //
  326. DBGPRINT("KiLocateTriggerPc: Jump within Trap Shadow\n");
  327. NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
  328. return FALSE;
  329. } else if ((Opcode >= BR_OP) && (Opcode <= BGT_OP)) {
  330. //
  331. // The instruction is one of 16 branch opcodes that consists
  332. // of BR, the 6 floating point branch, BSR, and the 8 integer
  333. // branch instructions. The trap shadow is invalid.
  334. //
  335. DBGPRINT("KiLocateTriggerPc: Branch within Trap Shadow\n");
  336. NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
  337. return FALSE;
  338. } else if ((Instruction.Memory.Opcode == MEMSPC_OP) &&
  339. ((Instruction.Memory.MemDisp == TRAPB_FUNC) ||
  340. (Instruction.Memory.MemDisp == EXCB_FUNC))) {
  341. //
  342. // The instruction is a type of TRAPB instruction. The trap
  343. // shadow is invalid.
  344. //
  345. DBGPRINT("KiLocateTriggerPc: Trapb within Trap Shadow\n");
  346. NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
  347. return FALSE;
  348. } else if (Opcode == CALLPAL_OP) {
  349. //
  350. // The instruction is a Call PAL. The trap shadow is invalid.
  351. //
  352. DBGPRINT("KiLocateTriggerPc: Call PAL within Trap Shadow\n");
  353. NON_IEEE(ExceptionRecord, TRIGGER_INVALID_INSTRUCTION_FOUND);
  354. return FALSE;
  355. } else if ((Opcode == IEEEFP_OP) || (Opcode == FPOP_OP)) {
  356. //
  357. // The instruction is an IEEE floating point instruction.
  358. // Decode the destination register of the floating point
  359. // instruction in order to check against the register mask.
  360. //
  361. Fc = Instruction.FpOp.Fc;
  362. if (Fc != FZERO_REG) {
  363. FloatRegisterTrashMask |= (1 << Fc);
  364. }
  365. FloatRegisterWriteMask &= ~(1 << Fc);
  366. }
  367. } while (FloatRegisterWriteMask != 0);
  368. //
  369. // If the instruction thought to be the trigger instruction does not
  370. // have the /S bit set, then the trap shadow is invalid (some other
  371. // instruction must have caused software completion bit to be set).
  372. //
  373. if ((Instruction.FpOp.Function & FP_TRAP_ENABLE_S) == 0) {
  374. DBGPRINT("KiLocateTriggerPc: Trigger instruction missing /S\n");
  375. NON_IEEE(ExceptionRecord, TRIGGER_WRONG_INSTRUCTION);
  376. return FALSE;
  377. }
  378. //
  379. // If either of the operand registers of the trigger instruction is
  380. // also the destination register of the trigger instruction or any
  381. // instruction in the trap shadow, then the trap shadow in invalid.
  382. // This is because the original value of the operand register(s) may
  383. // have been destroyed making it impossible to re-execute the trigger
  384. // instruction.
  385. //
  386. Fa = Instruction.FpOp.Fa;
  387. Fb = Instruction.FpOp.Fb;
  388. if ((FloatRegisterTrashMask & ((1 << Fa) | (1 << Fb))) != 0) {
  389. DBGPRINT("KiLocateTriggerPc: Source is destination\n");
  390. NON_IEEE(ExceptionRecord, TRIGGER_SOURCE_IS_DESTINATION);
  391. return FALSE;
  392. }
  393. } except (EXCEPTION_EXECUTE_HANDLER) {
  394. //
  395. // An exception occurred while fetching the value of the
  396. // next previous instruction. The trap shadow is invalid.
  397. //
  398. DBGPRINT("KiLocateTriggerPc: Instruction fetch error\n");
  399. NON_IEEE(ExceptionRecord, TRIGGER_INSTRUCTION_FETCH_ERROR);
  400. return FALSE;
  401. }
  402. //
  403. // The trigger instruction was successfully located. Set the precise
  404. // exception address in the exception record, set the new continuation
  405. // address in the trap frame, and return a value of TRUE.
  406. //
  407. DBGPRINT("KiLocateTriggerPc: Exception PC = %p, Trigger PC = %p\n",
  408. ExceptionRecord->ExceptionAddress, TriggerPc);
  409. ExceptionRecord->ExceptionAddress = (PVOID)TriggerPc;
  410. TrapFrame->Fir = (ULONGLONG)(LONG_PTR)(TriggerPc + 4);
  411. return TRUE;
  412. }
  413. VOID
  414. KiSetFloatingStatus (
  415. IN OUT PEXCEPTION_RECORD ExceptionRecord
  416. )
  417. /*++
  418. Routine Description:
  419. This function is called to convert the exception summary register bits
  420. into a status code value.
  421. Arguments:
  422. ExceptionRecord - Supplies a pointer to an exception record.
  423. Return Value:
  424. None.
  425. --*/
  426. {
  427. PEXC_SUM ExceptionSummary;
  428. //
  429. // Perform the following triage on the exception summary register to
  430. // report the type of exception, even if though the PC reported is
  431. // imprecise.
  432. //
  433. DBGPRINT("KiSetFloatingStatus: ExceptionSummary = %.8lx\n",
  434. ExceptionRecord->ExceptionInformation[2]);
  435. ExceptionSummary = (PEXC_SUM)(&ExceptionRecord->ExceptionInformation[2]);
  436. if (ExceptionSummary->InvalidOperation != 0) {
  437. ExceptionRecord->ExceptionCode = STATUS_FLOAT_INVALID_OPERATION;
  438. } else if (ExceptionSummary->DivisionByZero != 0) {
  439. ExceptionRecord->ExceptionCode = STATUS_FLOAT_DIVIDE_BY_ZERO;
  440. } else if (ExceptionSummary->Overflow != 0) {
  441. ExceptionRecord->ExceptionCode = STATUS_FLOAT_OVERFLOW;
  442. } else if (ExceptionSummary->Underflow != 0) {
  443. ExceptionRecord->ExceptionCode = STATUS_FLOAT_UNDERFLOW;
  444. } else if (ExceptionSummary->InexactResult != 0) {
  445. ExceptionRecord->ExceptionCode = STATUS_FLOAT_INEXACT_RESULT;
  446. } else if (ExceptionSummary->IntegerOverflow != 0) {
  447. ExceptionRecord->ExceptionCode = STATUS_INTEGER_OVERFLOW;
  448. } else {
  449. ExceptionRecord->ExceptionCode = STATUS_FLOAT_STACK_CHECK;
  450. }
  451. }