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.

1642 lines
36 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. misc.c
  5. Abstract:
  6. This module implements machine dependent miscellaneous kernel functions.
  7. Author:
  8. Ken Reneris 7-5-95
  9. Environment:
  10. Kernel mode only.
  11. Revision History:
  12. --*/
  13. #include "ki.h"
  14. #include "fastsys.inc"
  15. extern BOOLEAN KeI386FxsrPresent;
  16. extern BOOLEAN KeI386XMMIPresent;
  17. extern UCHAR KiSystemCallExitBranch[];
  18. extern UCHAR KiFastCallEntry[];
  19. extern UCHAR KiDefaultSystemCall[];
  20. extern UCHAR KiSystemCallExit[];
  21. extern UCHAR KiSystemCallExit2[];
  22. extern UCHAR KiSystemCallExit3[];
  23. extern UCHAR KiFastSystemCallIa32[];
  24. extern UCHAR KiFastSystemCallAmdK6[];
  25. extern ULONG_PTR KiSystemCallExitAdjust;
  26. extern ULONG KiFastSystemCallDisable;
  27. PVOID KiFastSystemCallCode = KiDefaultSystemCall;
  28. ULONG_PTR KiSystemCallExitAdjust;
  29. UCHAR KiSystemCallExitAdjusted;
  30. BOOLEAN KiFastSystemCallIsIA32;
  31. BOOLEAN KiFastCallCopyDoneOnce = FALSE;
  32. VOID
  33. KeRestoreMtrr (
  34. VOID
  35. );
  36. VOID
  37. KeRestorePAT(
  38. VOID
  39. );
  40. //
  41. //
  42. // Internal format of the floating_save structure which is passed
  43. //
  44. typedef struct _CONTROL_WORD {
  45. USHORT ControlWord;
  46. ULONG MXCsr;
  47. } CONTROL_WORD, *PCONTROL_WORD;
  48. typedef struct {
  49. UCHAR Flags;
  50. KIRQL Irql;
  51. KIRQL PreviousNpxIrql;
  52. UCHAR Spare[2];
  53. union {
  54. CONTROL_WORD Fcw;
  55. PFX_SAVE_AREA Context;
  56. ULONG_PTR ContextAddressAsULONG;
  57. } u;
  58. ULONG Cr0NpxState;
  59. PKTHREAD Thread; // debug
  60. } FLOAT_SAVE, *PFLOAT_SAVE;
  61. #define FLOAT_SAVE_COMPLETE_CONTEXT 0x01
  62. #define FLOAT_SAVE_FREE_CONTEXT_HEAP 0x02
  63. #define FLOAT_SAVE_VALID 0x04
  64. #define FLOAT_SAVE_ALIGN_ADJUSTED 0x08
  65. #define FLOAT_SAVE_RESERVED 0xF0
  66. //
  67. // Allocate Pool returns a pointer which is 8 byte aligned. The
  68. // floating point save area needs to be 16 byte aligned. When
  69. // allocating the save area we add the difference and adjust if
  70. // needed.
  71. //
  72. #define ALIGN_ADJUST 8
  73. NTSTATUS
  74. KeSaveFloatingPointState (
  75. OUT PKFLOATING_SAVE PublicFloatSave
  76. )
  77. /*++
  78. Routine Description:
  79. This routine saves the thread's current non-volatile NPX state,
  80. and sets a new initial floating point state for the caller.
  81. Arguments:
  82. FloatSave - receives the current non-volatile npx state for the thread
  83. Return Value:
  84. --*/
  85. {
  86. PKTHREAD Thread;
  87. PFX_SAVE_AREA NpxFrame;
  88. KIRQL Irql;
  89. USHORT ControlWord;
  90. ULONG MXCsr;
  91. PKPRCB Prcb;
  92. PFLOAT_SAVE FloatSave;
  93. //
  94. // If the system is using floating point emulation, then
  95. // return an error
  96. //
  97. if (!KeI386NpxPresent) {
  98. return STATUS_ILLEGAL_FLOAT_CONTEXT;
  99. }
  100. //
  101. // Get the current irql and thread
  102. //
  103. FloatSave = (PFLOAT_SAVE) PublicFloatSave;
  104. Irql = KeGetCurrentIrql();
  105. Thread = KeGetCurrentThread();
  106. ASSERT (Thread->NpxIrql <= Irql);
  107. FloatSave->Flags = 0;
  108. FloatSave->Irql = Irql;
  109. FloatSave->PreviousNpxIrql = Thread->NpxIrql;
  110. FloatSave->Thread = Thread;
  111. //
  112. // If the irql has changed we need to save the complete floating
  113. // state context as the prior level has been interrupted.
  114. //
  115. if (Thread->NpxIrql != Irql) {
  116. //
  117. // If this is apc level we don't have anyplace to hold this
  118. // context, allocate some heap.
  119. //
  120. if (Irql == APC_LEVEL) {
  121. FloatSave->u.Context = ExAllocatePoolWithTag (
  122. NonPagedPool,
  123. sizeof (FX_SAVE_AREA) + ALIGN_ADJUST,
  124. ' XPN'
  125. );
  126. if (!FloatSave->u.Context) {
  127. return STATUS_INSUFFICIENT_RESOURCES;
  128. }
  129. FloatSave->Flags |= FLOAT_SAVE_FREE_CONTEXT_HEAP;
  130. //
  131. // ExAllocatePoolWithTag returns an 8 byte aligned pointer.
  132. // The FXSAVE instruction requires 16 byte alignment. Adjust
  133. // the base address of the save area if needed.
  134. //
  135. if ((FloatSave->u.ContextAddressAsULONG & ALIGN_ADJUST) != 0) {
  136. FloatSave->u.ContextAddressAsULONG += ALIGN_ADJUST;
  137. FloatSave->Flags |= FLOAT_SAVE_ALIGN_ADJUSTED;
  138. }
  139. ASSERT((FloatSave->u.ContextAddressAsULONG & 0xF) == 0);
  140. } else {
  141. ASSERT (Irql == DISPATCH_LEVEL);
  142. FloatSave->u.Context = &KeGetCurrentPrcb()->NpxSaveArea;
  143. }
  144. FloatSave->Flags |= FLOAT_SAVE_COMPLETE_CONTEXT;
  145. }
  146. //
  147. // Stop context switching and allow access to the local fp unit
  148. //
  149. _asm {
  150. cli
  151. mov eax, cr0
  152. mov ecx, eax
  153. and eax, not (CR0_MP|CR0_EM|CR0_TS)
  154. cmp eax, ecx
  155. je short sav10
  156. mov cr0, eax
  157. sav10:
  158. }
  159. Prcb = KeGetCurrentPrcb();
  160. //
  161. // Get ownership of npx register set for this context
  162. //
  163. if (Prcb->NpxThread != Thread) {
  164. //
  165. // If the other context is loaded in the npx registers, flush
  166. // it to that threads save area
  167. //
  168. if (Prcb->NpxThread) {
  169. NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Prcb->NpxThread->InitialStack) -
  170. sizeof(FX_SAVE_AREA)));
  171. if (KeI386FxsrPresent) {
  172. Kix86FxSave(NpxFrame);
  173. } else {
  174. Kix86FnSave(NpxFrame);
  175. }
  176. NpxFrame->NpxSavedCpu = 0;
  177. Prcb->NpxThread->NpxState = NPX_STATE_NOT_LOADED;
  178. }
  179. Prcb->NpxThread = Thread;
  180. }
  181. NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
  182. sizeof(FX_SAVE_AREA)));
  183. //
  184. // Save the previous state as required
  185. //
  186. if (FloatSave->Flags & FLOAT_SAVE_COMPLETE_CONTEXT) {
  187. //
  188. // Need to save the entire context
  189. //
  190. if (Thread->NpxState == NPX_STATE_LOADED) {
  191. if (KeI386FxsrPresent) {
  192. Kix86FxSave((FloatSave->u.Context));
  193. } else {
  194. Kix86FnSave((FloatSave->u.Context));
  195. }
  196. FloatSave->u.Context->NpxSavedCpu = 0;
  197. FloatSave->u.Context->Cr0NpxState = NpxFrame->Cr0NpxState;
  198. } else {
  199. RtlCopyMemory (FloatSave->u.Context, NpxFrame, sizeof(FX_SAVE_AREA));
  200. FloatSave->u.Context->NpxSavedCpu = 0;
  201. }
  202. } else {
  203. //
  204. // Save only the non-volatile state
  205. //
  206. if (Thread->NpxState == NPX_STATE_LOADED) {
  207. _asm {
  208. mov eax, FloatSave
  209. fnstcw [eax] FLOAT_SAVE.u.Fcw.ControlWord
  210. }
  211. if ((KeI386FxsrPresent) && (KeI386XMMIPresent)) {
  212. Kix86StMXCsr(&FloatSave->u.Fcw.MXCsr);
  213. }
  214. } else {
  215. //
  216. // Save the control word from the npx frame.
  217. //
  218. if (KeI386FxsrPresent) {
  219. FloatSave->u.Fcw.ControlWord = (USHORT) NpxFrame->U.FxArea.ControlWord;
  220. FloatSave->u.Fcw.MXCsr = NpxFrame->U.FxArea.MXCsr;
  221. } else {
  222. FloatSave->u.Fcw.ControlWord = (USHORT) NpxFrame->U.FnArea.ControlWord;
  223. }
  224. }
  225. //
  226. // Save Cr0NpxState, but clear CR0_TS as there's not non-volatile
  227. // pending fp exceptions
  228. //
  229. FloatSave->Cr0NpxState = NpxFrame->Cr0NpxState & ~CR0_TS;
  230. }
  231. //
  232. // The previous state is saved. Set an initial default
  233. // FP state for the caller
  234. //
  235. NpxFrame->Cr0NpxState = 0;
  236. Thread->NpxState = NPX_STATE_LOADED;
  237. Thread->NpxIrql = Irql;
  238. ControlWord = 0x27f; // 64bit mode
  239. MXCsr = 0x1f80;
  240. _asm {
  241. fninit
  242. fldcw ControlWord
  243. }
  244. if ((KeI386FxsrPresent) && (KeI386XMMIPresent)) {
  245. Kix86LdMXCsr(&MXCsr);
  246. }
  247. _asm {
  248. sti
  249. }
  250. FloatSave->Flags |= FLOAT_SAVE_VALID;
  251. return STATUS_SUCCESS;
  252. }
  253. NTSTATUS
  254. KeRestoreFloatingPointState (
  255. IN PKFLOATING_SAVE PublicFloatSave
  256. )
  257. /*++
  258. Routine Description:
  259. This routine retores the thread's current non-volatile NPX state,
  260. to the passed in state.
  261. Arguments:
  262. FloatSave - the non-volatile npx state for the thread to restore
  263. Return Value:
  264. --*/
  265. {
  266. PKTHREAD Thread;
  267. PFX_SAVE_AREA NpxFrame;
  268. ULONG Cr0State;
  269. PFLOAT_SAVE FloatSave;
  270. ASSERT (KeI386NpxPresent);
  271. FloatSave = (PFLOAT_SAVE) PublicFloatSave;
  272. Thread = FloatSave->Thread;
  273. NpxFrame = (PFX_SAVE_AREA)(((ULONG)(Thread->InitialStack) -
  274. sizeof(FX_SAVE_AREA)));
  275. //
  276. // Verify float save looks like it's from the right context
  277. //
  278. if ((FloatSave->Flags & (FLOAT_SAVE_VALID | FLOAT_SAVE_RESERVED)) != FLOAT_SAVE_VALID) {
  279. //
  280. // Invalid floating point save area.
  281. //
  282. KeBugCheckEx(INVALID_FLOATING_POINT_STATE,
  283. 0,
  284. FloatSave->Flags,
  285. 0,
  286. 0);
  287. }
  288. if (FloatSave->Irql != KeGetCurrentIrql()) {
  289. //
  290. // Invalid IRQL. IRQL now must be the same as when the
  291. // context was saved. (Why? Because we save it in different
  292. // places depending on the IRQL at that time).
  293. //
  294. KeBugCheckEx(INVALID_FLOATING_POINT_STATE,
  295. 1,
  296. FloatSave->Irql,
  297. KeGetCurrentIrql(),
  298. 0);
  299. }
  300. if (Thread != KeGetCurrentThread()) {
  301. //
  302. // Invalid Thread. The thread this floating point context
  303. // belongs to is not the current thread (or the saved thread
  304. // field is trash).
  305. //
  306. KeBugCheckEx(INVALID_FLOATING_POINT_STATE,
  307. 2,
  308. (ULONG_PTR)Thread,
  309. (ULONG_PTR)KeGetCurrentThread(),
  310. 0);
  311. }
  312. //
  313. // Synchronize with context switches and the npx trap handlers
  314. //
  315. _asm {
  316. cli
  317. }
  318. //
  319. // Restore the required state
  320. //
  321. if (FloatSave->Flags & FLOAT_SAVE_COMPLETE_CONTEXT) {
  322. //
  323. // Restore the entire fp state to the threads save area
  324. //
  325. if (Thread->NpxState == NPX_STATE_LOADED) {
  326. //
  327. // This state in the fp unit is no longer needed, just disregard it
  328. //
  329. Thread->NpxState = NPX_STATE_NOT_LOADED;
  330. KeGetCurrentPrcb()->NpxThread = NULL;
  331. }
  332. //
  333. // Copy restored state to npx frame
  334. //
  335. RtlCopyMemory (NpxFrame, FloatSave->u.Context, sizeof(FX_SAVE_AREA));
  336. } else {
  337. //
  338. // Restore the non-volatile state
  339. //
  340. if (Thread->NpxState == NPX_STATE_LOADED) {
  341. //
  342. // Init fp state and restore control word
  343. //
  344. _asm {
  345. fninit
  346. mov eax, FloatSave
  347. fldcw [eax] FLOAT_SAVE.u.Fcw.ControlWord
  348. }
  349. if ((KeI386FxsrPresent) && (KeI386XMMIPresent)) {
  350. Kix86LdMXCsr(&FloatSave->u.Fcw.MXCsr);
  351. }
  352. } else {
  353. //
  354. // Fp state not loaded. Restore control word in npx frame
  355. //
  356. if (KeI386FxsrPresent) {
  357. NpxFrame->U.FxArea.ControlWord = FloatSave->u.Fcw.ControlWord;
  358. NpxFrame->U.FxArea.StatusWord = 0;
  359. NpxFrame->U.FxArea.TagWord = 0;
  360. NpxFrame->NpxSavedCpu = 0;
  361. NpxFrame->U.FxArea.MXCsr = FloatSave->u.Fcw.MXCsr;
  362. } else {
  363. NpxFrame->U.FnArea.ControlWord = FloatSave->u.Fcw.ControlWord;
  364. NpxFrame->U.FnArea.StatusWord = 0;
  365. NpxFrame->U.FnArea.TagWord = 0xffff;
  366. }
  367. }
  368. NpxFrame->Cr0NpxState = FloatSave->Cr0NpxState;
  369. }
  370. //
  371. // Restore NpxIrql and Cr0
  372. //
  373. Thread->NpxIrql = FloatSave->PreviousNpxIrql;
  374. Cr0State = Thread->NpxState | NpxFrame->Cr0NpxState;
  375. _asm {
  376. mov eax, cr0
  377. mov ecx, eax
  378. and eax, not (CR0_MP|CR0_EM|CR0_TS)
  379. or eax, Cr0State
  380. cmp eax, ecx
  381. je short res10
  382. mov cr0, eax
  383. res10:
  384. sti
  385. }
  386. //
  387. // Done
  388. //
  389. if ((FloatSave->Flags & FLOAT_SAVE_FREE_CONTEXT_HEAP) != 0) {
  390. //
  391. // If FXSAVE area was adjusted for alignment after allocation,
  392. // undo that adjustment before freeing.
  393. //
  394. if ((FloatSave->Flags & FLOAT_SAVE_ALIGN_ADJUSTED) != 0) {
  395. FloatSave->u.ContextAddressAsULONG -= ALIGN_ADJUST;
  396. }
  397. ExFreePool (FloatSave->u.Context);
  398. }
  399. FloatSave->Flags = 0;
  400. return STATUS_SUCCESS;
  401. }
  402. VOID
  403. KiDisableFastSyscallReturn(
  404. VOID
  405. )
  406. /*++
  407. Routine Description:
  408. The fast syscall/return feature cannot be used until
  409. certain processor specific registers have been initialized.
  410. This routine is called when the system is switching to a
  411. state where not all processors are powered on.
  412. This routine adjusts the exit path for system calls to
  413. use the iretd instruction instead of the faster sysexit
  414. instruction, it accomplishes this by adjusting the offset
  415. of a branch.
  416. Arguments:
  417. None.
  418. Return Value:
  419. None.
  420. --*/
  421. {
  422. if (KiSystemCallExitAdjusted) {
  423. KiSystemCallExitBranch[1] -= KiSystemCallExitAdjusted;
  424. KiSystemCallExitAdjusted = 0;
  425. }
  426. }
  427. VOID
  428. KiEnableFastSyscallReturn(
  429. VOID
  430. )
  431. /*++
  432. Routine Description:
  433. The fast syscall/return feature cannot be used until
  434. certain processor specific registers have been initialized.
  435. This routine is called once the registers are known to
  436. have been set on all processors.
  437. This routine adjusts the exit path for system calls to
  438. use the appropriate sequence for the processor, it does
  439. this by adjusting the offset of a branch.
  440. Arguments:
  441. None.
  442. Return Value:
  443. None.
  444. --*/
  445. {
  446. //
  447. // Adjust the second byte of the two byte branch instruction.
  448. // It can never be otherwise, but, make sure we aren't going
  449. // to adjust it out of range.
  450. //
  451. //
  452. // The following is a workaround for the fact that in resume
  453. // from hibernate the kernel is read only. Basically, we
  454. // won't try to do it again, we also don't undo it when
  455. // hibernating/suspending.
  456. //
  457. if ((KiSystemCallExitAdjusted == KiSystemCallExitAdjust) &&
  458. KiFastCallCopyDoneOnce) {
  459. //
  460. // It's already done, don't try to do it again.
  461. //
  462. return;
  463. }
  464. if ((KiSystemCallExitAdjust + KiSystemCallExitBranch[1]) < 0x80) {
  465. //
  466. // It's good, undo any previous adjustment.
  467. //
  468. KiDisableFastSyscallReturn();
  469. //
  470. // Adjust the branch.
  471. //
  472. KiSystemCallExitAdjusted = (UCHAR)KiSystemCallExitAdjust;
  473. KiSystemCallExitBranch[1] += KiSystemCallExitAdjusted;
  474. //
  475. // Copy the appropriate system entry code into user shared
  476. // data where it can be executed from user mode.
  477. //
  478. RtlCopyMemory(SharedUserData->SystemCall,
  479. KiFastSystemCallCode,
  480. sizeof(SharedUserData->SystemCall));
  481. KiFastCallCopyDoneOnce = TRUE;
  482. }
  483. }
  484. VOID
  485. KePrepareToLoseProcessorSpecificState(
  486. VOID
  487. )
  488. {
  489. //
  490. // The kernel has been marked read only, adjusting
  491. // code right now won't work. Fortunately, we
  492. // don't actually need to do this as the SYSEXIT
  493. // instruction doesn't depend on the SYSENTER MSRs.
  494. //
  495. // KiDisableFastSyscallReturn();
  496. }
  497. VOID
  498. KiLoadFastSyscallMachineSpecificRegisters(
  499. IN volatile PLONG Void
  500. )
  501. /*++
  502. Routine Description:
  503. Load MSRs used to support Fast Syscall/return. This routine is
  504. run on all processors.
  505. Arguments:
  506. None.
  507. Return Value:
  508. None.
  509. --*/
  510. {
  511. if (KiFastSystemCallIsIA32) {
  512. //
  513. // Use Intel defined way of doing this.
  514. //
  515. WRMSR(MSR_SYSENTER_CS, KGDT_R0_CODE);
  516. WRMSR(MSR_SYSENTER_EIP, (ULONGLONG)(ULONG)KiFastCallEntry);
  517. WRMSR(MSR_SYSENTER_ESP, 0);
  518. #if 0
  519. } else {
  520. //
  521. // Use the non-Intel way. (Note: Now that Intel has also
  522. // defined a way, most new processors do it that way).
  523. //
  524. LARGE_INTEGER Value;
  525. Value.u.HighPart = ((KGDT_R3_CODE | 3) << 16) | KGDT_R0_CODE;
  526. Value.u.LowPart = (ULONG)KiFastCallEntry;
  527. WRMSR(MSR_SYSCALL_TARGET_ADDR, Value.QuadPart);
  528. //
  529. // Now enable the feature.
  530. //
  531. Value.QuadPart = RDMSR(MSR_EXT_FEATURE_ENABLE);
  532. Value.u.LowPart |= MSR_EFER_SCE;
  533. WRMSR(MSR_EXT_FEATURE_ENABLE, Value.QuadPart);
  534. #endif
  535. }
  536. }
  537. VOID
  538. KiRestoreFastSyscallReturnState(
  539. VOID
  540. )
  541. {
  542. ULONG_PTR Void = 0;
  543. if (KeFeatureBits & KF_FAST_SYSCALL) {
  544. if (KiFastSystemCallDisable == 0) {
  545. //
  546. // Fast system call is enabled.
  547. //
  548. if (KiFastSystemCallIsIA32 == TRUE) {
  549. KiSystemCallExitAdjust = KiSystemCallExit2 - KiSystemCallExit;
  550. KiFastSystemCallCode = KiFastSystemCallIa32;
  551. } else {
  552. KiSystemCallExitAdjust = KiSystemCallExit3 - KiSystemCallExit;
  553. KiFastSystemCallCode = KiFastSystemCallAmdK6;
  554. }
  555. } else {
  556. //
  557. // Fast system call has been explicitly disabled or is
  558. // not implemented on all processors in the system.
  559. //
  560. KeFeatureBits &= ~KF_FAST_SYSCALL;
  561. }
  562. }
  563. if (KeFeatureBits & KF_FAST_SYSCALL) {
  564. //
  565. // On all processors, set the MSRs that support syscall/sysexit.
  566. //
  567. KiIpiGenericCall(
  568. (PKIPI_BROADCAST_WORKER)KiLoadFastSyscallMachineSpecificRegisters,
  569. Void
  570. );
  571. }
  572. //
  573. // Set the appropriate code for system call into the system
  574. // call area of the shared user data area.
  575. //
  576. KiEnableFastSyscallReturn();
  577. }
  578. VOID
  579. KeRestoreProcessorSpecificFeatures(
  580. VOID
  581. )
  582. /*++
  583. Routine Description:
  584. Restore processor specific features. This routine is called
  585. when processors have been restored to a powered on state to
  586. restore those things which are not part of the processor's
  587. "normal" context which may have been lost. For example, this
  588. routine is called when a system is resumed from hibernate or
  589. suspend.
  590. Arguments:
  591. None.
  592. Return Value:
  593. None.
  594. --*/
  595. {
  596. KeRestoreMtrr();
  597. KeRestorePAT();
  598. KiRestoreFastSyscallReturnState();
  599. }
  600. #if !defined(NT_UP)
  601. VOID
  602. FASTCALL
  603. KiAcquireQueuedSpinLockCheckForFreeze(
  604. IN PKSPIN_LOCK_QUEUE QueuedLock,
  605. IN PKTRAP_FRAME TrapFrame
  606. )
  607. /*++
  608. Routine Description:
  609. This routine is called to acquire a queued spin lock while at high
  610. priority. While the lock is not available, a check is made to see
  611. if another processor has requested this processor freeze execution.
  612. Note: This routine must be called with current IRQL at or above
  613. dispatch lever, or interrupts disabled.
  614. Arguments:
  615. QueuedLock Supplies the address of the queued spinlock.
  616. TrapFrame Supplies the address of the trap frame to pass to
  617. KiFreezeTargetExecution.
  618. Return Value:
  619. None.
  620. --*/
  621. {
  622. PKSPIN_LOCK_QUEUE Previous;
  623. PKSPIN_LOCK Lock;
  624. PKPRCB Prcb;
  625. volatile ULONG_PTR * LockPointer;
  626. LockPointer = (volatile ULONG_PTR *)&QueuedLock->Lock;
  627. Previous = InterlockedExchangePointer(QueuedLock->Lock, QueuedLock);
  628. if (Previous == NULL) {
  629. //
  630. // This processor now owns this lock.
  631. //
  632. *LockPointer |= LOCK_QUEUE_OWNER;
  633. } else {
  634. //
  635. // Lock is already held, update thew next pointer in the
  636. // previous queue entry to point to this new waiter and
  637. // wait until the lock is granted.
  638. //
  639. // The following loop is careful not to write ANYTHING
  640. // while waiting unless a freeze execution has been
  641. // requested. This includes any stack variables or
  642. // return addresses.
  643. //
  644. *LockPointer |= LOCK_QUEUE_WAIT;
  645. Previous->Next = QueuedLock;
  646. Prcb = KeGetCurrentPrcb();
  647. while (*LockPointer & LOCK_QUEUE_WAIT) {
  648. if (Prcb->RequestSummary & IPI_FREEZE) {
  649. ULONG OldSummary;
  650. ULONG NewSummary;
  651. ULONG Summary;
  652. OldSummary = Prcb->RequestSummary;
  653. NewSummary = OldSummary & ~IPI_FREEZE;
  654. Summary = InterlockedCompareExchange((PVOID)&Prcb->RequestSummary,
  655. NewSummary,
  656. OldSummary);
  657. //
  658. // If something else edited the RequestSummary, we'll
  659. // get it next time around (unless the IPI has been
  660. // handled).
  661. //
  662. if (Summary == OldSummary) {
  663. //
  664. // IPI_FREEZE cleared in RequestSummary. Now
  665. // freeze as requested.
  666. //
  667. KiFreezeTargetExecution(TrapFrame, NULL);
  668. }
  669. }
  670. //
  671. // Don't be a hog.
  672. //
  673. KeYieldProcessor();
  674. }
  675. }
  676. //
  677. // Lock has been acquired.
  678. //
  679. }
  680. #endif
  681. /*++
  682. QLOCK_STAT_GATHER
  683. If this flag is defined, the queued spinlock routines are
  684. replaced by wrappers used to gather performance characteristics
  685. of the code acquiring the locks.
  686. --*/
  687. #if defined(QLOCK_STAT_GATHER)
  688. #define QLOCK_STAT_CLEAN
  689. #define QLOCKS_NUMBER 16
  690. #define QLOCKS_MAX_LOG 512
  691. ULONG
  692. FASTCALL
  693. KiRDTSC(
  694. PULONGLONG Time
  695. );
  696. //
  697. // The following structure is used to accumulate data about each
  698. // acquire/release pair for a lock.
  699. //
  700. typedef struct {
  701. ULONGLONG Key;
  702. ULONGLONG Time;
  703. ULONGLONG WaitTime;
  704. ULONG Count;
  705. ULONG Waiters;
  706. ULONG Depth;
  707. ULONG IncreasedDepth;
  708. ULONG Clean;
  709. } QLOCKDATA, *PQLOCKDATA;
  710. //
  711. // House keeping data for each lock.
  712. //
  713. typedef struct {
  714. //
  715. // The following fields are used to keep data from acquire
  716. // to release.
  717. //
  718. ULONGLONG AcquireTime;
  719. ULONGLONG WaitToAcquire;
  720. ULONG_PTR AcquirePoint;
  721. BOOLEAN Clean;
  722. //
  723. // Remaining fields accumulate global stats for this lock.
  724. //
  725. ULONG Count;
  726. ULONG Pairs;
  727. ULONG FailedTry;
  728. UCHAR MaxDepth;
  729. UCHAR PreviousDepth;
  730. ULONG NoWait;
  731. } QLOCKHOUSE, *PQLOCKHOUSE;
  732. QLOCKDATA KiQueuedSpinLockLog[QLOCKS_NUMBER][QLOCKS_MAX_LOG];
  733. QLOCKHOUSE KiQueuedSpinLockHouse[QLOCKS_NUMBER];
  734. //
  735. // Implement the lock queue mechanisms in C for when we are
  736. // gathering performance data.
  737. //
  738. VOID
  739. FASTCALL
  740. KiAcquireQueuedLock(
  741. IN PKSPIN_LOCK_QUEUE QueuedLock
  742. )
  743. {
  744. PKSPIN_LOCK_QUEUE Previous;
  745. PKSPIN_LOCK Lock;
  746. volatile ULONG_PTR * LockPointer;
  747. LockPointer = (volatile ULONG_PTR *)&QueuedLock->Lock;
  748. Previous = InterlockedExchangePointer(QueuedLock->Lock, QueuedLock);
  749. if (Previous == NULL) {
  750. //
  751. // This processor now owns this lock.
  752. //
  753. #if defined(QLOCK_STAT_CLEAN)
  754. ULONG LockNumber;
  755. LockNumber = QueuedLock - KeGetCurrentPrcb()->LockQueue;
  756. //
  757. // The following check allows the conversion from QueuedLock to
  758. // lock number to work (validly) even if in stack queued spin
  759. // locks are using this routine.
  760. //
  761. if (LockNumber < QLOCKS_NUMBER) {
  762. KiQueuedSpinLockHouse[LockNumber].Clean = 1;
  763. }
  764. #endif
  765. *LockPointer |= LOCK_QUEUE_OWNER;
  766. } else {
  767. //
  768. // Lock is already held, update thew next pointer in the
  769. // previous queue entry to point to this new waiter and
  770. // wait until the lock is granted.
  771. //
  772. *LockPointer |= LOCK_QUEUE_WAIT;
  773. Previous->Next = QueuedLock;
  774. while (*LockPointer & LOCK_QUEUE_WAIT) {
  775. KeYieldProcessor();
  776. }
  777. }
  778. //
  779. // Lock has been acquired.
  780. //
  781. }
  782. VOID
  783. FASTCALL
  784. KiReleaseQueuedLock(
  785. IN PKSPIN_LOCK_QUEUE QueuedLock
  786. )
  787. {
  788. PKSPIN_LOCK_QUEUE Waiter;
  789. //
  790. // Get the address of the actual lock and strip out the bottom
  791. // two bits which are used for status.
  792. //
  793. ASSERT((((ULONG_PTR)QueuedLock->Lock) & 3) == LOCK_QUEUE_OWNER);
  794. QueuedLock->Lock = (PKSPIN_LOCK)((ULONG_PTR)QueuedLock->Lock & ~3);
  795. Waiter = (PKSPIN_LOCK_QUEUE)*QueuedLock->Lock;
  796. if (Waiter == QueuedLock) {
  797. //
  798. // Good chance noone is queued on this lock, to be sure
  799. // we need to do an interlocked operation on it.
  800. // Note: This is just an optimization, there is no point
  801. // in doing the interlocked compare exchange if someone
  802. // else has already joined the queue.
  803. //
  804. Waiter = InterlockedCompareExchangePointer(QueuedLock->Lock,
  805. NULL,
  806. QueuedLock);
  807. }
  808. if (Waiter != QueuedLock) {
  809. //
  810. // There is another waiter. It is possible for the waiter
  811. // to have only just performed the exchange that put its
  812. // context in the lock and to have not yet updated the
  813. // 'next' pointer in the previous context (which could be
  814. // this context), so we wait for our next pointer to be
  815. // non-null before continuing.
  816. //
  817. volatile PKSPIN_LOCK_QUEUE * NextQueuedLock = &QueuedLock->Next;
  818. while ((Waiter = *NextQueuedLock) == NULL) {
  819. KeYieldProcessor();
  820. }
  821. //
  822. // Pass the lock on to the next in line.
  823. //
  824. *((PULONG_PTR)&Waiter->Lock) ^= (LOCK_QUEUE_WAIT | LOCK_QUEUE_OWNER);
  825. QueuedLock->Next = NULL;
  826. }
  827. }
  828. KIRQL
  829. FASTCALL
  830. KiQueueStatAcquireQueuedLock(
  831. IN KSPIN_LOCK_QUEUE_NUMBER Number
  832. )
  833. {
  834. KIRQL PreviousIrql;
  835. PreviousIrql = KfRaiseIrql(DISPATCH_LEVEL);
  836. KiAcquireQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
  837. return PreviousIrql;
  838. }
  839. KIRQL
  840. FASTCALL
  841. KiQueueStatAcquireQueuedLockRTS(
  842. IN KSPIN_LOCK_QUEUE_NUMBER Number
  843. )
  844. {
  845. KIRQL PreviousIrql;
  846. PreviousIrql = KfRaiseIrql(SYNCH_LEVEL);
  847. KiAcquireQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
  848. return PreviousIrql;
  849. }
  850. LOGICAL
  851. FASTCALL
  852. KiQueueStatTryAcquire(
  853. IN KSPIN_LOCK_QUEUE_NUMBER Number,
  854. IN PKIRQL OldIrql,
  855. IN KIRQL NewIrql
  856. )
  857. {
  858. KIRQL PreviousIrql;
  859. LOGICAL Acquired = FALSE;
  860. PKSPIN_LOCK_QUEUE Previous;
  861. PKSPIN_LOCK_QUEUE QueuedLock;
  862. ULONG_PTR * LockPointer;
  863. ULONG_PTR Lock;
  864. _disable();
  865. QueuedLock = &KeGetCurrentPrcb()->LockQueue[Number];
  866. LockPointer = (ULONG_PTR *)&QueuedLock->Lock;
  867. Lock = *LockPointer;
  868. Previous = InterlockedCompareExchangePointer(Lock, QueuedLock, NULL);
  869. if (Previous == NULL) {
  870. //
  871. // This processor now owns this lock. Set the owner bit in
  872. // the queued lock lock pointer, raise IRQL to the requested
  873. // level, set the old IRQL in the caller provided location
  874. // and return success.
  875. //
  876. Lock |= LOCK_QUEUE_OWNER;
  877. *LockPointer = Lock;
  878. Acquired = TRUE;
  879. PreviousIrql = KfRaiseIrql(NewIrql);
  880. *OldIrql = PreviousIrql;
  881. }
  882. _enable();
  883. return Acquired;
  884. }
  885. VOID
  886. FASTCALL
  887. KiQueueStatReleaseQueuedLock(
  888. IN KSPIN_LOCK_QUEUE_NUMBER Number,
  889. IN KIRQL OldIrql
  890. )
  891. {
  892. KiReleaseQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
  893. KfLowerIrql(OldIrql);
  894. }
  895. UCHAR
  896. FASTCALL
  897. KiQueuedLockDepth(
  898. IN PKSPIN_LOCK_QUEUE QueuedLock
  899. )
  900. {
  901. //
  902. // Run down the list of waiters and see how many there are.
  903. //
  904. ULONG Depth = 0;
  905. ULONG_PTR LastAcquire;
  906. ULONG Debug;
  907. //
  908. // Get address of last acquirer in queue (stip the status bits
  909. // out of the address).
  910. //
  911. LastAcquire = (ULONG_PTR)QueuedLock->Lock;
  912. LastAcquire &= ~3;
  913. LastAcquire = *(PULONG_PTR)LastAcquire;
  914. //
  915. // Run down the list advancing QueuedLock until the end is reached.
  916. //
  917. while (LastAcquire != (ULONG_PTR)QueuedLock) {
  918. Debug = 0;
  919. //
  920. // If the waiter is not at the end of the list and has not yet
  921. // updated the forward pointer, wait for that update to happen.
  922. //
  923. if (QueuedLock->Next == NULL) {
  924. volatile PKSPIN_LOCK_QUEUE * NextQueuedLock = &QueuedLock->Next;
  925. while (*NextQueuedLock == NULL) {
  926. KeYieldProcessor();
  927. if (++Debug > 10000000) {
  928. DbgBreakPoint();
  929. Debug = 0;
  930. }
  931. }
  932. }
  933. Depth++;
  934. QueuedLock = QueuedLock->Next;
  935. }
  936. return Depth;
  937. }
  938. //
  939. // The following routines complete the queued spinlock package.
  940. //
  941. VOID
  942. FASTCALL
  943. KeAcquireInStackQueuedSpinLockAtDpcLevel(
  944. IN PKSPIN_LOCK SpinLock,
  945. IN PKLOCK_QUEUE_HANDLE LockHandle
  946. )
  947. {
  948. LockHandle->LockQueue.Next = NULL;
  949. LockHandle->LockQueue.Lock = SpinLock;
  950. KiAcquireQueuedLock(&LockHandle->LockQueue);
  951. }
  952. VOID
  953. FASTCALL
  954. KeReleaseInStackQueuedSpinLockFromDpcLevel (
  955. IN PKLOCK_QUEUE_HANDLE LockHandle
  956. )
  957. {
  958. KiReleaseQueuedLock(&LockHandle->LockQueue);
  959. }
  960. //
  961. // Although part of the queued spinlock package, the following
  962. // routines need to be implemented in assembly code to gather
  963. // lock statistics.
  964. //
  965. #if 0
  966. VOID
  967. FASTCALL
  968. KeAcquireQueuedSpinLockAtDpcLevel(
  969. IN PKSPIN_LOCK_QUEUE QueuedLock
  970. )
  971. {
  972. KiAcquireQueuedLock(QueuedLock);
  973. }
  974. VOID
  975. FASTCALL
  976. KeReleaseQueuedSpinLockFromDpcLevel (
  977. IN PKSPIN_LOCK_QUEUE QueuedLock
  978. )
  979. {
  980. KiReleaseQueuedLock(QueuedLock);
  981. }
  982. #endif
  983. VOID
  984. FASTCALL
  985. KiQueueStatTrySucceeded(
  986. IN PKSPIN_LOCK_QUEUE QueuedLock,
  987. IN ULONG_PTR CallersAddress
  988. )
  989. {
  990. PKPRCB Prcb;
  991. ULONG LockNumber;
  992. Prcb = KeGetCurrentPrcb();
  993. LockNumber = QueuedLock - Prcb->LockQueue;
  994. //
  995. // Record time now.
  996. //
  997. KiRDTSC(&KiQueuedSpinLockHouse[LockNumber].AcquireTime);
  998. KiQueuedSpinLockHouse[LockNumber].WaitToAcquire = 0;
  999. KiQueuedSpinLockHouse[LockNumber].AcquirePoint = CallersAddress;
  1000. }
  1001. VOID
  1002. FASTCALL
  1003. KiQueueStatTryFailed(
  1004. IN PKSPIN_LOCK_QUEUE QueuedLock
  1005. )
  1006. {
  1007. PKPRCB Prcb;
  1008. ULONG LockNumber;
  1009. Prcb = KeGetCurrentPrcb();
  1010. LockNumber = QueuedLock - Prcb->LockQueue;
  1011. KiQueuedSpinLockHouse[LockNumber].FailedTry++;
  1012. }
  1013. VOID
  1014. FASTCALL
  1015. KiQueueStatTry(
  1016. IN PULONG Everything
  1017. )
  1018. /*++
  1019. Routine Description:
  1020. Log success or failure of a TryToAcquire.
  1021. If success, logs the same data as KiQueueStatAcquire except
  1022. the wait time is 0.
  1023. Arguments:
  1024. Argument points to an array of ULONG data.
  1025. +0 xxxxxxRR RR is result (1 = success, 0 = fail)
  1026. +4 aaaaaaaa Argument to try to acquire (ie lock number)
  1027. +8 cccccccc Caller address
  1028. Return Value:
  1029. None.
  1030. --*/
  1031. {
  1032. UCHAR Success = *(PUCHAR)Everything;
  1033. ULONG LockNumber = Everything[1];
  1034. if (!Success) {
  1035. KiQueuedSpinLockHouse[LockNumber].FailedTry++;
  1036. return;
  1037. }
  1038. KiRDTSC(&KiQueuedSpinLockHouse[LockNumber].AcquireTime);
  1039. KiQueuedSpinLockHouse[LockNumber].WaitToAcquire = 0;
  1040. KiQueuedSpinLockHouse[LockNumber].AcquirePoint = Everything[2];
  1041. }
  1042. VOID
  1043. FASTCALL
  1044. KiQueueStatAcquire(
  1045. IN PULONG Everything
  1046. )
  1047. /*++
  1048. Routine Description:
  1049. This routine is called when a lock has been acquired. It's
  1050. purpose it to record wait time, acquisition time and who
  1051. acquired the lock.
  1052. Arguments:
  1053. Argument points to an array of ULONG data.
  1054. +0 aaaaaaaa LockNumber
  1055. +4 tltltltl time low = time wait to acquire began
  1056. +8 thththth time high =
  1057. +c cccccccc Caller address
  1058. Return Value:
  1059. None.
  1060. --*/
  1061. {
  1062. ULONG LockNumber = Everything[0];
  1063. PQLOCKHOUSE LockHome;
  1064. //
  1065. // Make this routine work with either a lock number of lock address.
  1066. //
  1067. if (LockNumber > QLOCKS_NUMBER) {
  1068. LockNumber = ((PKSPIN_LOCK_QUEUE)Everything[0]) -
  1069. KeGetCurrentPrcb()->LockQueue;
  1070. }
  1071. LockHome = &KiQueuedSpinLockHouse[LockNumber];
  1072. LockHome->WaitToAcquire = *(PULONGLONG)&Everything[1];
  1073. LockHome->AcquirePoint = Everything[3];
  1074. KiRDTSC(&LockHome->AcquireTime);
  1075. }
  1076. VOID
  1077. FASTCALL
  1078. KiQueueStatRelease(
  1079. IN PULONG Everything
  1080. )
  1081. /*++
  1082. Routine Description:
  1083. This routine is called when a lock is released to log statistics
  1084. about the lock. This routine is called with the lock still held,
  1085. the statistics update is protected by the lock itself.
  1086. Arguments:
  1087. Argument points to an array of ULONG data.
  1088. +0 aaaaaaaa Lock number
  1089. +4 cccccccc Caller address
  1090. Return Value:
  1091. None.
  1092. --*/
  1093. {
  1094. PQLOCKDATA Entry;
  1095. ULONGLONG Key;
  1096. ULONGLONG Now;
  1097. UCHAR Waiters;
  1098. PQLOCKHOUSE LockHome;
  1099. ULONG LockNumber = Everything[0];
  1100. LONGLONG HoldTime;
  1101. ULONG Clean;
  1102. KiRDTSC(&Now);
  1103. //
  1104. // Make this routine work with either a lock number of lock address.
  1105. //
  1106. if (LockNumber > QLOCKS_NUMBER) {
  1107. LockNumber = ((PKSPIN_LOCK_QUEUE)Everything[0]) -
  1108. KeGetCurrentPrcb()->LockQueue;
  1109. }
  1110. LockHome = &KiQueuedSpinLockHouse[LockNumber];
  1111. //
  1112. // Make up the key for this acquire/release pair.
  1113. //
  1114. ((PLARGE_INTEGER)&Key)->HighPart = LockHome->AcquirePoint;
  1115. ((PLARGE_INTEGER)&Key)->LowPart = Everything[1];
  1116. //
  1117. // Get the count of processors now waiting on this lock.
  1118. //
  1119. Waiters = KiQueuedLockDepth(&KeGetCurrentPrcb()->LockQueue[LockNumber]);
  1120. if (Waiters > LockHome->MaxDepth) {
  1121. LockHome->MaxDepth = Waiters;
  1122. }
  1123. //
  1124. // Reset per acquire/release data. This is data we don't want
  1125. // lying around for the next pair if we happen to throw away this
  1126. // particular data point.
  1127. //
  1128. Clean = LockHome->Clean;
  1129. LockHome->Clean = 0;
  1130. LockHome->AcquirePoint = 0;
  1131. HoldTime = Now - LockHome->AcquireTime;
  1132. if (HoldTime < 0) {
  1133. //
  1134. // This happens when KeSetSystemTime is called.
  1135. // Drop any negative results.
  1136. //
  1137. return;
  1138. }
  1139. //
  1140. // Update global statistics.
  1141. //
  1142. LockHome->Count++;
  1143. LockHome->NoWait += Clean;
  1144. //
  1145. // Search for a match in the log and add in the new data.
  1146. //
  1147. for (Entry = KiQueuedSpinLockLog[LockNumber]; TRUE; Entry++) {
  1148. if (Entry->Key == 0) {
  1149. //
  1150. // We have reached the end of the list of valid
  1151. // entries without finding a key match. If there's
  1152. // room, create a new entry.
  1153. //
  1154. if (LockHome->Pairs >= QLOCKS_MAX_LOG) {
  1155. //
  1156. // No room, just return.
  1157. //
  1158. return;
  1159. }
  1160. LockHome->Pairs++;
  1161. Entry->Key = Key;
  1162. }
  1163. if (Entry->Key == Key) {
  1164. //
  1165. // Found a match (or created a new pair). Update statistics
  1166. // for this acquire/release pair.
  1167. //
  1168. Entry->Time += HoldTime;
  1169. if (LockHome->WaitToAcquire) {
  1170. Entry->WaitTime += (LockHome->AcquireTime - LockHome->WaitToAcquire);
  1171. }
  1172. Entry->Count++;
  1173. Entry->Waiters += (Waiters != 0);
  1174. Entry->Depth += Waiters;
  1175. //
  1176. // There should be one less waiter now than there was
  1177. // before we acquired the lock. If not, a new waiter
  1178. // has joined the queue. This is is condition we want
  1179. // to know about as it indicates contention on this
  1180. // lock.
  1181. //
  1182. if ((Waiters) && (Waiters >= LockHome->PreviousDepth)) {
  1183. Entry->IncreasedDepth++;
  1184. }
  1185. LockHome->PreviousDepth = Waiters;
  1186. Entry->Clean += Clean;
  1187. break;
  1188. }
  1189. }
  1190. }
  1191. #endif
  1192. #ifdef _X86_
  1193. #pragma optimize("y", off) // RtlCaptureContext needs EBP to be correct
  1194. #endif
  1195. VOID
  1196. __cdecl
  1197. KeSaveStateForHibernate(
  1198. IN PKPROCESSOR_STATE ProcessorState
  1199. )
  1200. /*++
  1201. Routine Description:
  1202. Saves all processor-specific state that must be preserved
  1203. across an S4 state (hibernation).
  1204. N.B. #pragma surrounding this function is required in order
  1205. to create the frame pointer than RtlCaptureContext relies
  1206. on.
  1207. N.B. _CRTAPI1 (__cdecl) decoration is also required so that
  1208. RtlCaptureContext can compute the correct ESP.
  1209. Arguments:
  1210. ProcessorState - Supplies the KPROCESSOR_STATE where the
  1211. current CPU's state is to be saved.
  1212. Return Value:
  1213. None.
  1214. --*/
  1215. {
  1216. RtlCaptureContext(&ProcessorState->ContextFrame);
  1217. KiSaveProcessorControlState(ProcessorState);
  1218. }
  1219. #ifdef _X86_
  1220. #pragma optimize("", on)
  1221. #endif