Leaked source code of windows server 2003
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.

1646 lines
38 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] = (UCHAR) (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] = (UCHAR) (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 PLONG Context
  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. PKPRCB Prcb;
  512. UNREFERENCED_PARAMETER (Context);
  513. if (KiFastSystemCallIsIA32) {
  514. Prcb = KeGetCurrentPrcb();
  515. //
  516. // Use Intel defined way of doing this.
  517. //
  518. WRMSR(MSR_SYSENTER_CS, KGDT_R0_CODE);
  519. WRMSR(MSR_SYSENTER_EIP, (ULONGLONG)(ULONG)KiFastCallEntry);
  520. WRMSR(MSR_SYSENTER_ESP, (ULONGLONG)(ULONG)Prcb->DpcStack);
  521. #if 0
  522. } else {
  523. //
  524. // Use the non-Intel way. (Note: Now that Intel has also
  525. // defined a way, most new processors do it that way).
  526. //
  527. LARGE_INTEGER Value;
  528. Value.u.HighPart = ((KGDT_R3_CODE | 3) << 16) | KGDT_R0_CODE;
  529. Value.u.LowPart = (ULONG)KiFastCallEntry;
  530. WRMSR(MSR_SYSCALL_TARGET_ADDR, Value.QuadPart);
  531. //
  532. // Now enable the feature.
  533. //
  534. Value.QuadPart = RDMSR(MSR_EXT_FEATURE_ENABLE);
  535. Value.u.LowPart |= MSR_EFER_SCE;
  536. WRMSR(MSR_EXT_FEATURE_ENABLE, Value.QuadPart);
  537. #endif
  538. }
  539. }
  540. VOID
  541. KiRestoreFastSyscallReturnState(
  542. VOID
  543. )
  544. {
  545. ULONG_PTR Void = 0;
  546. if (KeFeatureBits & KF_FAST_SYSCALL) {
  547. if (KiFastSystemCallDisable == 0) {
  548. //
  549. // Fast system call is enabled.
  550. //
  551. if (KiFastSystemCallIsIA32 == TRUE) {
  552. KiSystemCallExitAdjust = KiSystemCallExit2 - KiSystemCallExit;
  553. KiFastSystemCallCode = KiFastSystemCallIa32;
  554. } else {
  555. KiSystemCallExitAdjust = KiSystemCallExit3 - KiSystemCallExit;
  556. KiFastSystemCallCode = KiFastSystemCallAmdK6;
  557. }
  558. } else {
  559. //
  560. // Fast system call has been explicitly disabled or is
  561. // not implemented on all processors in the system.
  562. //
  563. KeFeatureBits &= ~KF_FAST_SYSCALL;
  564. }
  565. }
  566. if (KeFeatureBits & KF_FAST_SYSCALL) {
  567. //
  568. // On all processors, set the MSRs that support syscall/sysexit.
  569. //
  570. KeIpiGenericCall(
  571. (PKIPI_BROADCAST_WORKER)KiLoadFastSyscallMachineSpecificRegisters,
  572. Void
  573. );
  574. }
  575. //
  576. // Set the appropriate code for system call into the system
  577. // call area of the shared user data area.
  578. //
  579. KiEnableFastSyscallReturn();
  580. }
  581. VOID
  582. KeRestoreProcessorSpecificFeatures(
  583. VOID
  584. )
  585. /*++
  586. Routine Description:
  587. Restore processor specific features. This routine is called
  588. when processors have been restored to a powered on state to
  589. restore those things which are not part of the processor's
  590. "normal" context which may have been lost. For example, this
  591. routine is called when a system is resumed from hibernate or
  592. suspend.
  593. Arguments:
  594. None.
  595. Return Value:
  596. None.
  597. --*/
  598. {
  599. KeRestoreMtrr();
  600. KeRestorePAT();
  601. KiRestoreFastSyscallReturnState();
  602. }
  603. #if !defined(NT_UP)
  604. VOID
  605. FASTCALL
  606. KiAcquireQueuedSpinLockCheckForFreeze(
  607. IN PKSPIN_LOCK_QUEUE QueuedLock,
  608. IN PKTRAP_FRAME TrapFrame
  609. )
  610. /*++
  611. Routine Description:
  612. This routine is called to acquire a queued spin lock while at high
  613. priority. While the lock is not available, a check is made to see
  614. if another processor has requested this processor freeze execution.
  615. Note: This routine must be called with current IRQL at or above
  616. dispatch lever, or interrupts disabled.
  617. Arguments:
  618. QueuedLock Supplies the address of the queued spinlock.
  619. TrapFrame Supplies the address of the trap frame to pass to
  620. KiFreezeTargetExecution.
  621. Return Value:
  622. None.
  623. --*/
  624. {
  625. PKSPIN_LOCK_QUEUE Previous;
  626. PKPRCB Prcb;
  627. volatile ULONG_PTR * LockPointer;
  628. LockPointer = (volatile ULONG_PTR *)&QueuedLock->Lock;
  629. Previous = InterlockedExchangePointer(QueuedLock->Lock, QueuedLock);
  630. if (Previous == NULL) {
  631. //
  632. // This processor now owns this lock.
  633. //
  634. *LockPointer |= LOCK_QUEUE_OWNER;
  635. } else {
  636. //
  637. // Lock is already held, update thew next pointer in the
  638. // previous queue entry to point to this new waiter and
  639. // wait until the lock is granted.
  640. //
  641. // The following loop is careful not to write ANYTHING
  642. // while waiting unless a freeze execution has been
  643. // requested. This includes any stack variables or
  644. // return addresses.
  645. //
  646. *LockPointer |= LOCK_QUEUE_WAIT;
  647. Previous->Next = QueuedLock;
  648. Prcb = KeGetCurrentPrcb();
  649. while (*LockPointer & LOCK_QUEUE_WAIT) {
  650. if (Prcb->RequestSummary & IPI_FREEZE) {
  651. ULONG OldSummary;
  652. ULONG NewSummary;
  653. ULONG Summary;
  654. OldSummary = Prcb->RequestSummary;
  655. NewSummary = OldSummary & ~IPI_FREEZE;
  656. Summary = InterlockedCompareExchange((PVOID)&Prcb->RequestSummary,
  657. NewSummary,
  658. OldSummary);
  659. //
  660. // If something else edited the RequestSummary, we'll
  661. // get it next time around (unless the IPI has been
  662. // handled).
  663. //
  664. if (Summary == OldSummary) {
  665. //
  666. // IPI_FREEZE cleared in RequestSummary. Now
  667. // freeze as requested.
  668. //
  669. KiFreezeTargetExecution(TrapFrame, NULL);
  670. }
  671. }
  672. //
  673. // Don't be a hog.
  674. //
  675. KeYieldProcessor();
  676. }
  677. }
  678. //
  679. // Lock has been acquired.
  680. //
  681. }
  682. #endif
  683. /*++
  684. QLOCK_STAT_GATHER
  685. If this flag is defined, the queued spinlock routines are
  686. replaced by wrappers used to gather performance characteristics
  687. of the code acquiring the locks.
  688. --*/
  689. #if defined(QLOCK_STAT_GATHER)
  690. #define QLOCK_STAT_CLEAN
  691. #define QLOCKS_NUMBER 16
  692. #define QLOCKS_MAX_LOG 512
  693. ULONG
  694. FASTCALL
  695. KiRDTSC(
  696. PULONGLONG Time
  697. );
  698. //
  699. // The following structure is used to accumulate data about each
  700. // acquire/release pair for a lock.
  701. //
  702. typedef struct {
  703. ULONGLONG Key;
  704. ULONGLONG Time;
  705. ULONGLONG WaitTime;
  706. ULONG Count;
  707. ULONG Waiters;
  708. ULONG Depth;
  709. ULONG IncreasedDepth;
  710. ULONG Clean;
  711. } QLOCKDATA, *PQLOCKDATA;
  712. //
  713. // House keeping data for each lock.
  714. //
  715. typedef struct {
  716. //
  717. // The following fields are used to keep data from acquire
  718. // to release.
  719. //
  720. ULONGLONG AcquireTime;
  721. ULONGLONG WaitToAcquire;
  722. ULONG_PTR AcquirePoint;
  723. BOOLEAN Clean;
  724. //
  725. // Remaining fields accumulate global stats for this lock.
  726. //
  727. ULONG Count;
  728. ULONG Pairs;
  729. ULONG FailedTry;
  730. UCHAR MaxDepth;
  731. UCHAR PreviousDepth;
  732. ULONG NoWait;
  733. } QLOCKHOUSE, *PQLOCKHOUSE;
  734. QLOCKDATA KiQueuedSpinLockLog[QLOCKS_NUMBER][QLOCKS_MAX_LOG];
  735. QLOCKHOUSE KiQueuedSpinLockHouse[QLOCKS_NUMBER];
  736. //
  737. // Implement the lock queue mechanisms in C for when we are
  738. // gathering performance data.
  739. //
  740. VOID
  741. FASTCALL
  742. KiAcquireQueuedLock(
  743. IN PKSPIN_LOCK_QUEUE QueuedLock
  744. )
  745. {
  746. PKSPIN_LOCK_QUEUE Previous;
  747. volatile ULONG_PTR * LockPointer;
  748. LockPointer = (volatile ULONG_PTR *)&QueuedLock->Lock;
  749. Previous = InterlockedExchangePointer(QueuedLock->Lock, QueuedLock);
  750. if (Previous == NULL) {
  751. //
  752. // This processor now owns this lock.
  753. //
  754. #if defined(QLOCK_STAT_CLEAN)
  755. ULONG LockNumber;
  756. LockNumber = QueuedLock - KeGetCurrentPrcb()->LockQueue;
  757. //
  758. // The following check allows the conversion from QueuedLock to
  759. // lock number to work (validly) even if in stack queued spin
  760. // locks are using this routine.
  761. //
  762. if (LockNumber < QLOCKS_NUMBER) {
  763. KiQueuedSpinLockHouse[LockNumber].Clean = 1;
  764. }
  765. #endif
  766. *LockPointer |= LOCK_QUEUE_OWNER;
  767. } else {
  768. //
  769. // Lock is already held, update thew next pointer in the
  770. // previous queue entry to point to this new waiter and
  771. // wait until the lock is granted.
  772. //
  773. *LockPointer |= LOCK_QUEUE_WAIT;
  774. Previous->Next = QueuedLock;
  775. while (*LockPointer & LOCK_QUEUE_WAIT) {
  776. KeYieldProcessor();
  777. }
  778. }
  779. //
  780. // Lock has been acquired.
  781. //
  782. }
  783. VOID
  784. FASTCALL
  785. KiReleaseQueuedLock(
  786. IN PKSPIN_LOCK_QUEUE QueuedLock
  787. )
  788. {
  789. PKSPIN_LOCK_QUEUE Waiter;
  790. //
  791. // Get the address of the actual lock and strip out the bottom
  792. // two bits which are used for status.
  793. //
  794. ASSERT((((ULONG_PTR)QueuedLock->Lock) & 3) == LOCK_QUEUE_OWNER);
  795. QueuedLock->Lock = (PKSPIN_LOCK)((ULONG_PTR)QueuedLock->Lock & ~3);
  796. Waiter = (PKSPIN_LOCK_QUEUE)*QueuedLock->Lock;
  797. if (Waiter == QueuedLock) {
  798. //
  799. // Good chance noone is queued on this lock, to be sure
  800. // we need to do an interlocked operation on it.
  801. // Note: This is just an optimization, there is no point
  802. // in doing the interlocked compare exchange if someone
  803. // else has already joined the queue.
  804. //
  805. Waiter = InterlockedCompareExchangePointer(QueuedLock->Lock,
  806. NULL,
  807. QueuedLock);
  808. }
  809. if (Waiter != QueuedLock) {
  810. //
  811. // There is another waiter. It is possible for the waiter
  812. // to have only just performed the exchange that put its
  813. // context in the lock and to have not yet updated the
  814. // 'next' pointer in the previous context (which could be
  815. // this context), so we wait for our next pointer to be
  816. // non-null before continuing.
  817. //
  818. volatile PKSPIN_LOCK_QUEUE * NextQueuedLock = &QueuedLock->Next;
  819. while ((Waiter = *NextQueuedLock) == NULL) {
  820. KeYieldProcessor();
  821. }
  822. //
  823. // Pass the lock on to the next in line.
  824. //
  825. *((PULONG_PTR)&Waiter->Lock) ^= (LOCK_QUEUE_WAIT | LOCK_QUEUE_OWNER);
  826. QueuedLock->Next = NULL;
  827. }
  828. }
  829. KIRQL
  830. FASTCALL
  831. KiQueueStatAcquireQueuedLock(
  832. IN KSPIN_LOCK_QUEUE_NUMBER Number
  833. )
  834. {
  835. KIRQL PreviousIrql;
  836. PreviousIrql = KfRaiseIrql(DISPATCH_LEVEL);
  837. KiAcquireQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
  838. return PreviousIrql;
  839. }
  840. KIRQL
  841. FASTCALL
  842. KiQueueStatAcquireQueuedLockRTS(
  843. IN KSPIN_LOCK_QUEUE_NUMBER Number
  844. )
  845. {
  846. KIRQL PreviousIrql;
  847. PreviousIrql = KfRaiseIrql(SYNCH_LEVEL);
  848. KiAcquireQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
  849. return PreviousIrql;
  850. }
  851. LOGICAL
  852. FASTCALL
  853. KiQueueStatTryAcquire(
  854. IN KSPIN_LOCK_QUEUE_NUMBER Number,
  855. IN PKIRQL OldIrql,
  856. IN KIRQL NewIrql
  857. )
  858. {
  859. KIRQL PreviousIrql;
  860. LOGICAL Acquired = FALSE;
  861. PKSPIN_LOCK_QUEUE Previous;
  862. PKSPIN_LOCK_QUEUE QueuedLock;
  863. ULONG_PTR * LockPointer;
  864. ULONG_PTR Lock;
  865. _disable();
  866. QueuedLock = &KeGetCurrentPrcb()->LockQueue[Number];
  867. LockPointer = (ULONG_PTR *)&QueuedLock->Lock;
  868. Lock = *LockPointer;
  869. Previous = InterlockedCompareExchangePointer(Lock, QueuedLock, NULL);
  870. if (Previous == NULL) {
  871. //
  872. // This processor now owns this lock. Set the owner bit in
  873. // the queued lock lock pointer, raise IRQL to the requested
  874. // level, set the old IRQL in the caller provided location
  875. // and return success.
  876. //
  877. Lock |= LOCK_QUEUE_OWNER;
  878. *LockPointer = Lock;
  879. Acquired = TRUE;
  880. PreviousIrql = KfRaiseIrql(NewIrql);
  881. *OldIrql = PreviousIrql;
  882. }
  883. _enable();
  884. return Acquired;
  885. }
  886. VOID
  887. FASTCALL
  888. KiQueueStatReleaseQueuedLock(
  889. IN KSPIN_LOCK_QUEUE_NUMBER Number,
  890. IN KIRQL OldIrql
  891. )
  892. {
  893. KiReleaseQueuedLock(&KeGetCurrentPrcb()->LockQueue[Number]);
  894. KfLowerIrql(OldIrql);
  895. }
  896. UCHAR
  897. FASTCALL
  898. KiQueuedLockDepth(
  899. IN PKSPIN_LOCK_QUEUE QueuedLock
  900. )
  901. {
  902. //
  903. // Run down the list of waiters and see how many there are.
  904. //
  905. ULONG Depth = 0;
  906. ULONG_PTR LastAcquire;
  907. ULONG Debug;
  908. //
  909. // Get address of last acquirer in queue (stip the status bits
  910. // out of the address).
  911. //
  912. LastAcquire = (ULONG_PTR)QueuedLock->Lock;
  913. LastAcquire &= ~3;
  914. LastAcquire = *(PULONG_PTR)LastAcquire;
  915. //
  916. // Run down the list advancing QueuedLock until the end is reached.
  917. //
  918. while (LastAcquire != (ULONG_PTR)QueuedLock) {
  919. Debug = 0;
  920. //
  921. // If the waiter is not at the end of the list and has not yet
  922. // updated the forward pointer, wait for that update to happen.
  923. //
  924. if (QueuedLock->Next == NULL) {
  925. volatile PKSPIN_LOCK_QUEUE * NextQueuedLock = &QueuedLock->Next;
  926. while (*NextQueuedLock == NULL) {
  927. KeYieldProcessor();
  928. if (++Debug > 10000000) {
  929. DbgBreakPoint();
  930. Debug = 0;
  931. }
  932. }
  933. }
  934. Depth++;
  935. QueuedLock = QueuedLock->Next;
  936. }
  937. return (UCHAR) Depth;
  938. }
  939. //
  940. // The following routines complete the queued spinlock package.
  941. //
  942. VOID
  943. FASTCALL
  944. KeAcquireInStackQueuedSpinLockAtDpcLevel(
  945. IN PKSPIN_LOCK SpinLock,
  946. IN PKLOCK_QUEUE_HANDLE LockHandle
  947. )
  948. {
  949. LockHandle->LockQueue.Next = NULL;
  950. LockHandle->LockQueue.Lock = SpinLock;
  951. KiAcquireQueuedLock(&LockHandle->LockQueue);
  952. }
  953. VOID
  954. FASTCALL
  955. KeReleaseInStackQueuedSpinLockFromDpcLevel (
  956. IN PKLOCK_QUEUE_HANDLE LockHandle
  957. )
  958. {
  959. KiReleaseQueuedLock(&LockHandle->LockQueue);
  960. }
  961. //
  962. // Although part of the queued spinlock package, the following
  963. // routines need to be implemented in assembly code to gather
  964. // lock statistics.
  965. //
  966. #if 0
  967. VOID
  968. FASTCALL
  969. KeAcquireQueuedSpinLockAtDpcLevel(
  970. IN PKSPIN_LOCK_QUEUE QueuedLock
  971. )
  972. {
  973. KiAcquireQueuedLock(QueuedLock);
  974. }
  975. VOID
  976. FASTCALL
  977. KeReleaseQueuedSpinLockFromDpcLevel (
  978. IN PKSPIN_LOCK_QUEUE QueuedLock
  979. )
  980. {
  981. KiReleaseQueuedLock(QueuedLock);
  982. }
  983. #endif
  984. VOID
  985. FASTCALL
  986. KiQueueStatTrySucceeded(
  987. IN PKSPIN_LOCK_QUEUE QueuedLock,
  988. IN ULONG_PTR CallersAddress
  989. )
  990. {
  991. PKPRCB Prcb;
  992. ULONG LockNumber;
  993. Prcb = KeGetCurrentPrcb();
  994. LockNumber = QueuedLock - Prcb->LockQueue;
  995. //
  996. // Record time now.
  997. //
  998. KiRDTSC(&KiQueuedSpinLockHouse[LockNumber].AcquireTime);
  999. KiQueuedSpinLockHouse[LockNumber].WaitToAcquire = 0;
  1000. KiQueuedSpinLockHouse[LockNumber].AcquirePoint = CallersAddress;
  1001. }
  1002. VOID
  1003. FASTCALL
  1004. KiQueueStatTryFailed(
  1005. IN PKSPIN_LOCK_QUEUE QueuedLock
  1006. )
  1007. {
  1008. PKPRCB Prcb;
  1009. ULONG LockNumber;
  1010. Prcb = KeGetCurrentPrcb();
  1011. LockNumber = QueuedLock - Prcb->LockQueue;
  1012. KiQueuedSpinLockHouse[LockNumber].FailedTry++;
  1013. }
  1014. VOID
  1015. FASTCALL
  1016. KiQueueStatTry(
  1017. IN PULONG Everything
  1018. )
  1019. /*++
  1020. Routine Description:
  1021. Log success or failure of a TryToAcquire.
  1022. If success, logs the same data as KiQueueStatAcquire except
  1023. the wait time is 0.
  1024. Arguments:
  1025. Argument points to an array of ULONG data.
  1026. +0 xxxxxxRR RR is result (1 = success, 0 = fail)
  1027. +4 aaaaaaaa Argument to try to acquire (ie lock number)
  1028. +8 cccccccc Caller address
  1029. Return Value:
  1030. None.
  1031. --*/
  1032. {
  1033. UCHAR Success = *(PUCHAR)Everything;
  1034. ULONG LockNumber = Everything[1];
  1035. if (!Success) {
  1036. KiQueuedSpinLockHouse[LockNumber].FailedTry++;
  1037. return;
  1038. }
  1039. KiRDTSC(&KiQueuedSpinLockHouse[LockNumber].AcquireTime);
  1040. KiQueuedSpinLockHouse[LockNumber].WaitToAcquire = 0;
  1041. KiQueuedSpinLockHouse[LockNumber].AcquirePoint = Everything[2];
  1042. }
  1043. VOID
  1044. FASTCALL
  1045. KiQueueStatAcquire(
  1046. IN PULONG Everything
  1047. )
  1048. /*++
  1049. Routine Description:
  1050. This routine is called when a lock has been acquired. It's
  1051. purpose it to record wait time, acquisition time and who
  1052. acquired the lock.
  1053. Arguments:
  1054. Argument points to an array of ULONG data.
  1055. +0 aaaaaaaa LockNumber
  1056. +4 tltltltl time low = time wait to acquire began
  1057. +8 thththth time high =
  1058. +c cccccccc Caller address
  1059. Return Value:
  1060. None.
  1061. --*/
  1062. {
  1063. ULONG LockNumber = Everything[0];
  1064. PQLOCKHOUSE LockHome;
  1065. //
  1066. // Make this routine work with either a lock number of lock address.
  1067. //
  1068. if (LockNumber > QLOCKS_NUMBER) {
  1069. LockNumber = ((PKSPIN_LOCK_QUEUE)Everything[0]) -
  1070. KeGetCurrentPrcb()->LockQueue;
  1071. }
  1072. LockHome = &KiQueuedSpinLockHouse[LockNumber];
  1073. LockHome->WaitToAcquire = *(PULONGLONG)&Everything[1];
  1074. LockHome->AcquirePoint = Everything[3];
  1075. KiRDTSC(&LockHome->AcquireTime);
  1076. }
  1077. VOID
  1078. FASTCALL
  1079. KiQueueStatRelease(
  1080. IN PULONG Everything
  1081. )
  1082. /*++
  1083. Routine Description:
  1084. This routine is called when a lock is released to log statistics
  1085. about the lock. This routine is called with the lock still held,
  1086. the statistics update is protected by the lock itself.
  1087. Arguments:
  1088. Argument points to an array of ULONG data.
  1089. +0 aaaaaaaa Lock number
  1090. +4 cccccccc Caller address
  1091. Return Value:
  1092. None.
  1093. --*/
  1094. {
  1095. PQLOCKDATA Entry;
  1096. ULONGLONG Key;
  1097. ULONGLONG Now;
  1098. UCHAR Waiters;
  1099. PQLOCKHOUSE LockHome;
  1100. ULONG LockNumber = Everything[0];
  1101. LONGLONG HoldTime;
  1102. ULONG Clean;
  1103. KiRDTSC(&Now);
  1104. //
  1105. // Make this routine work with either a lock number of lock address.
  1106. //
  1107. if (LockNumber > QLOCKS_NUMBER) {
  1108. LockNumber = ((PKSPIN_LOCK_QUEUE)Everything[0]) -
  1109. KeGetCurrentPrcb()->LockQueue;
  1110. }
  1111. LockHome = &KiQueuedSpinLockHouse[LockNumber];
  1112. //
  1113. // Make up the key for this acquire/release pair.
  1114. //
  1115. ((PLARGE_INTEGER)&Key)->HighPart = LockHome->AcquirePoint;
  1116. ((PLARGE_INTEGER)&Key)->LowPart = Everything[1];
  1117. //
  1118. // Get the count of processors now waiting on this lock.
  1119. //
  1120. Waiters = KiQueuedLockDepth(&KeGetCurrentPrcb()->LockQueue[LockNumber]);
  1121. if (Waiters > LockHome->MaxDepth) {
  1122. LockHome->MaxDepth = Waiters;
  1123. }
  1124. //
  1125. // Reset per acquire/release data. This is data we don't want
  1126. // lying around for the next pair if we happen to throw away this
  1127. // particular data point.
  1128. //
  1129. Clean = LockHome->Clean;
  1130. LockHome->Clean = 0;
  1131. LockHome->AcquirePoint = 0;
  1132. HoldTime = Now - LockHome->AcquireTime;
  1133. if (HoldTime < 0) {
  1134. //
  1135. // This happens when KeSetSystemTime is called.
  1136. // Drop any negative results.
  1137. //
  1138. return;
  1139. }
  1140. //
  1141. // Update global statistics.
  1142. //
  1143. LockHome->Count++;
  1144. LockHome->NoWait += Clean;
  1145. //
  1146. // Search for a match in the log and add in the new data.
  1147. //
  1148. for (Entry = KiQueuedSpinLockLog[LockNumber]; TRUE; Entry++) {
  1149. if (Entry->Key == 0) {
  1150. //
  1151. // We have reached the end of the list of valid
  1152. // entries without finding a key match. If there's
  1153. // room, create a new entry.
  1154. //
  1155. if (LockHome->Pairs >= QLOCKS_MAX_LOG) {
  1156. //
  1157. // No room, just return.
  1158. //
  1159. return;
  1160. }
  1161. LockHome->Pairs++;
  1162. Entry->Key = Key;
  1163. }
  1164. if (Entry->Key == Key) {
  1165. //
  1166. // Found a match (or created a new pair). Update statistics
  1167. // for this acquire/release pair.
  1168. //
  1169. Entry->Time += HoldTime;
  1170. if (LockHome->WaitToAcquire) {
  1171. Entry->WaitTime += (LockHome->AcquireTime - LockHome->WaitToAcquire);
  1172. }
  1173. Entry->Count++;
  1174. Entry->Waiters += (Waiters != 0);
  1175. Entry->Depth += Waiters;
  1176. //
  1177. // There should be one less waiter now than there was
  1178. // before we acquired the lock. If not, a new waiter
  1179. // has joined the queue. This is is condition we want
  1180. // to know about as it indicates contention on this
  1181. // lock.
  1182. //
  1183. if ((Waiters) && (Waiters >= LockHome->PreviousDepth)) {
  1184. Entry->IncreasedDepth++;
  1185. }
  1186. LockHome->PreviousDepth = Waiters;
  1187. Entry->Clean += Clean;
  1188. break;
  1189. }
  1190. }
  1191. }
  1192. #endif
  1193. #ifdef _X86_
  1194. #pragma optimize("y", off) // RtlCaptureContext needs EBP to be correct
  1195. #endif
  1196. VOID
  1197. __cdecl
  1198. KeSaveStateForHibernate(
  1199. IN PKPROCESSOR_STATE ProcessorState
  1200. )
  1201. /*++
  1202. Routine Description:
  1203. Saves all processor-specific state that must be preserved
  1204. across an S4 state (hibernation).
  1205. N.B. #pragma surrounding this function is required in order
  1206. to create the frame pointer than RtlCaptureContext relies
  1207. on.
  1208. N.B. _CRTAPI1 (__cdecl) decoration is also required so that
  1209. RtlCaptureContext can compute the correct ESP.
  1210. Arguments:
  1211. ProcessorState - Supplies the KPROCESSOR_STATE where the
  1212. current CPU's state is to be saved.
  1213. Return Value:
  1214. None.
  1215. --*/
  1216. {
  1217. RtlCaptureContext(&ProcessorState->ContextFrame);
  1218. KiSaveProcessorControlState(ProcessorState);
  1219. }
  1220. #ifdef _X86_
  1221. #pragma optimize("", on)
  1222. #endif