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.

735 lines
24 KiB

  1. title "Context Swap"
  2. ;++
  3. ;
  4. ; Copyright (c) 2000 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; ctxswap.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements the code necessary to field the dispatch interrupt
  13. ; and perform context switching.
  14. ;
  15. ; Author:
  16. ;
  17. ; David N. Cutler (davec) 26-Aug-2000
  18. ;
  19. ; Environment:
  20. ;
  21. ; Kernel mode only.
  22. ;
  23. ;--
  24. include ksamd64.inc
  25. extern KeAcquireQueuedSpinLockAtDpcLevel:proc
  26. extern KeAcquireQueuedSpinLockRaiseToSynch:proc
  27. extern KeBugCheck:proc
  28. extern KeReleaseQueuedSpinLock:proc
  29. extern KeReleaseQueuedSpinLockFromDpcLevel:proc
  30. extern KiDeliverApc:proc
  31. extern KiDispatcherLock:qword
  32. extern KiQuantumEnd:proc
  33. extern KiReadyThread:proc
  34. extern KiRetireDpcList:proc
  35. extern __imp_HalRequestSoftwareInterrupt:qword
  36. subttl "Unlock Dispatcher Database"
  37. ;++
  38. ;
  39. ; VOID
  40. ; KiUnlockDispatcherDatabase (
  41. ; IN KIRQL OldIrql
  42. ; )
  43. ;
  44. ; Routine Description:
  45. ;
  46. ; This routine is entered at SYNCH_LEVEL with the dispatcher database
  47. ; locked. Its function is to either unlock the dispatcher database and
  48. ; return or initiate a context switch if another thread has been selected
  49. ; for execution.
  50. ;
  51. ; Arguments:
  52. ;
  53. ; OldIrql (cl) - Supplies the IRQL when the dispatcher database lock was
  54. ; acquired.
  55. ;
  56. ; Return Value:
  57. ;
  58. ; None.
  59. ;
  60. ;--
  61. UdFrame struct
  62. P1Home dq ? ; queued spin lock number parameter
  63. P2Home dq ? ; previous IRQL paramater
  64. SavedIrql db ? ; saved previous IRQL
  65. Fill db 7 dup (?) ; fill to 8 mod 16
  66. UdFrame ends
  67. NESTED_ENTRY KiUnlockDispatcherDatabase, _TEXT$00
  68. alloc_stack (sizeof UdFrame) ; allocate stack frame
  69. END_PROLOGUE
  70. ;
  71. ; Check if a new thread is scheduled for execution.
  72. ;
  73. cmp qword ptr gs:[PcNextThread], 0 ; check if thread scheduled
  74. jne short KiUD30 ; if ne, new thread scheduled
  75. ;
  76. ; Release dispatcher database lock, lower IRQL to its previous level,
  77. ; and return.
  78. ;
  79. ifndef NT_UP
  80. KiUD10: mov dl, cl ; set old IRQL value
  81. mov ecx, LockQueueDispatcherLock ; set lock queue number
  82. call KeReleaseQueuedSpinLock ; release dispatcher lock
  83. else
  84. KiUD10: movzx ecx, cl ; set IRQL to previous level
  85. SetIrql ;
  86. endif
  87. add rsp, sizeof UdFrame ; deallocate stack frame
  88. ret ; return
  89. ;
  90. ; A new thread has been selected to run on the current processor, but the new
  91. ; IRQL is not below dispatch level. If the current processor is not executing
  92. ; a DPC, then request a dispatch interrupt on the current processor.
  93. ;
  94. KiUD20: cmp qword ptr gs:[PcDpcRoutineActive], 0 ; check if DPC routine active
  95. jne short KiUD10 ; if ne, DPC routine is active
  96. mov UdFrame.SavedIrql[rsp], cl ; save previous IRQL
  97. mov cl, DISPATCH_LEVEL ; request dispatch interrupt
  98. call __imp_HalRequestSoftwareInterrupt ;
  99. mov cl, UdFrame.SavedIrql[rsp] ; restore previous IRQL
  100. jmp short KiUD10
  101. ;
  102. ; Check if the previous IRQL is less than dispatch level.
  103. ;
  104. KiUD30: cmp cl, DISPATCH_LEVEL ; check if IRQL below dispatch level
  105. jge short KiUD20 ; if ge, not below dispatch level
  106. add rsp, sizeof UdFrame ; deallocate stack frame
  107. jmp short KxUnlockDispatcherDatabase ; finish in common code
  108. NESTED_END KiUnlockDispatcherDatabase, _TEXT$00
  109. ;
  110. ; There is a new thread scheduled for execution and the previous IRQL is
  111. ; less than dispatch level. Context switch to the new thread immediately.
  112. ;
  113. ; N.B. The following routine is entered by falling through the from above
  114. ; code.
  115. ;
  116. ; N.B. The following routine is carefully written as a nested function that
  117. ; appears to have been called directly by the caller of the above
  118. ; function which unlocks the dispatcher database.
  119. ;
  120. ; Arguments:
  121. ;
  122. ; OldIrql (cl) - Supplies the IRQL when the dispatcher database lock was
  123. ; acquired.
  124. ;
  125. NESTED_ENTRY KxUnlockDispatcherDatabase, _TEXT$00
  126. GENERATE_EXCEPTION_FRAME ; generate exception frame
  127. mov rbx, gs:[PcCurrentPrcb] ; get current PRCB address
  128. mov rsi, PbNextThread[rbx] ; get next thread address
  129. mov rdi, PbCurrentThread[rbx] ; get current thread address
  130. and qword ptr PbNextThread[rbx], 0 ; clear next thread address
  131. mov PbCurrentThread[rbx], rsi ; set current thread address
  132. mov ThWaitIrql[rdi], cl ; save previous IRQL
  133. ifndef NT_UP
  134. mov byte ptr ThIdleSwapBlock[rdi], 1 ; block swap from idle
  135. endif
  136. mov rcx, rdi ; set address of current thread
  137. call KiReadyThread ; reready thread for execution
  138. xor eax, eax ; set NPX save false
  139. mov cl, ThWaitIrql[rdi] ; set APC interrupt bypass disable
  140. ifndef NT_UP
  141. xor edx, edx ; set swap from idle false
  142. endif
  143. call SwapContext ; swap context
  144. movzx ecx, byte ptr ThWaitIrql[rsi] ; get original wait IRQL
  145. or al, al ; check if kernel APC pending
  146. jz short KiXD10 ; if z, no kernel APC pending
  147. mov ecx, APC_LEVEL ; set IRQL to APC level
  148. SetIrql ;
  149. xor ecx, ecx ; set previous mode to kernel
  150. xor edx, edx ; clear exception frame address
  151. xor r8, r8 ; clear trap frame address
  152. call KiDeliverApc ; deliver kernel mode APC
  153. xor ecx, ecx ; set original wait IRQL
  154. KiXD10: ; reference label
  155. SetIrql ; set IRQL to previous level
  156. RESTORE_EXCEPTION_STATE ; restore exception state/deallocate
  157. ret ; return
  158. NESTED_END KxUnlockDispatcherDatabase, _TEXT$00
  159. subttl "Swap Context"
  160. ;++
  161. ;
  162. ; BOOLEAN
  163. ; KiSwapContext (
  164. ; IN PKTHREAD Thread
  165. ; )
  166. ;
  167. ; Routine Description:
  168. ;
  169. ; This function is a small wrapper that marshalls arguments and calls the
  170. ; actual swap context routine.
  171. ;
  172. ; Arguments:
  173. ;
  174. ; Thread (rcx) - Supplies the address of the new thread.
  175. ;
  176. ; Return Value:
  177. ;
  178. ; If a kernel APC is pending, then a value of TRUE is returned. Otherwise,
  179. ; a value of FALSE is returned.
  180. ;
  181. ;--
  182. NESTED_ENTRY KiSwapContext, _TEXT$00
  183. GENERATE_EXCEPTION_FRAME ; generate exception frame
  184. mov rbx, gs:[PcCurrentPrcb] ; get current PRCB address
  185. mov rsi, rcx ; set next thread address
  186. mov rdi, PbCurrentThread[rbx] ; get current thread address
  187. mov PbCurrentThread[rbx], rsi ; set current thread address
  188. xor eax, eax ; set NPX save false
  189. mov cl, ThWaitIrql[rdi] ; set APC interrupt bypass disable
  190. ifndef NT_UP
  191. xor edx, edx ; set swap from idle false
  192. endif
  193. call SwapContext ; swap context
  194. RESTORE_EXCEPTION_STATE ; restore exception state/deallocate
  195. ret ; return
  196. NESTED_END KiSwapContext, _TEXT$00
  197. subttl "Dispatch Interrupt"
  198. ;++
  199. ;
  200. ; Routine Description:
  201. ;
  202. ; This routine is entered as the result of a software interrupt generated
  203. ; at DISPATCH_LEVEL. Its function is to process the DPC list, and then
  204. ; perform a context switch if a new thread has been selected for execution
  205. ; on the current processor.
  206. ;
  207. ; This routine is entered at DISPATCH_LEVEL with the dispatcher database
  208. ; unlocked.
  209. ;
  210. ; Arguments:
  211. ;
  212. ; None
  213. ;
  214. ; Return Value:
  215. ;
  216. ; None.
  217. ;
  218. ;--
  219. DiFrame struct
  220. P1Home dq ? ; PRCB address parameter
  221. Fill dq ? ; fill to 8 mod 16
  222. SavedRbx dq ? ; saved RBX
  223. DiFrame ends
  224. NESTED_ENTRY KiDispatchInterrupt, _TEXT$00
  225. push_reg rbx ; save nonvolatile register
  226. alloc_stack (sizeof DiFrame - 8) ; allocate stack frame
  227. END_PROLOGUE
  228. mov rbx, gs:[PcCurrentPrcb] ; get current PRCB address
  229. and dword ptr PbDpcInterruptRequested[rbx], 0 ; clear request
  230. ;
  231. ; Check if the DPC queue has any entries to process.
  232. ;
  233. KiDI10: cli ; disable interrupts
  234. mov eax, PbDpcQueueDepth[rbx] ; get DPC queue depth
  235. or eax, PbTimerHand[rbx] ; merge timer hand value
  236. jz short KiDI20 ; if z, no DPCs to process
  237. mov PbSavedRsp[rbx], rsp ; save current stack pointer
  238. mov rsp, PbDpcStack[rbx] ; set DPC stack pointer
  239. mov rcx, rbx ; set PRCB address parameter
  240. call KiRetireDpcList ; process the DPC list
  241. mov rsp, PbSavedRsp[rbx] ; restore current stack pointer
  242. ;
  243. ; Check to determine if quantum end is requested.
  244. ;
  245. KiDI20: sti ; enable interrupts
  246. cmp dword ptr PbQuantumEnd[rbx], 0 ; check if quantum end request
  247. je short KiDI50 ; if e, quantum end not requested
  248. ;
  249. ; Process quantum end event.
  250. ;
  251. ; N.B. If a new thread is selected as a result of processing the quantum end
  252. ; request, then the new thread is returned with the dispatcher database
  253. ; locked. Otherwise, NULL is returned with the dispatcher database
  254. ; unlocked.
  255. ;
  256. and dword ptr PbQuantumEnd[rbx], 0 ; clear quantum end indicator
  257. call KiQuantumEnd ; process quantum end
  258. test rax, rax ; test if new thread selected
  259. jnz short KiDI60 ; if ne, new thread selected
  260. ;
  261. ; A new thread has not been selected for execution. Restore nonvolatile
  262. ; registers, deallocate stack frame, and return.
  263. ;
  264. KiDI30: mov rbx, DiFrame.SavedRbx[rsp] ; restore nonvolatile register
  265. add rsp, sizeof DiFrame ; deallocate stack frame
  266. ret ; return
  267. ;
  268. ; The dispatch lock could not be acquired. Lower IRQL to dispatch level, and
  269. ; loop processing the DPC list and quantum end events.
  270. ;
  271. KiDI40: mov ecx, DISPATCH_LEVEL ; set IRQL to DISPATCH_LEVEL
  272. SetIrql ;
  273. jmp short KiDI10 ; try again
  274. ;
  275. ; Check to determine if a new thread has been selected for execution on this
  276. ; processor.
  277. ;
  278. KiDI50: cmp qword ptr PbNextThread[rbx], 0 ; check if new thread selected
  279. je short KiDI30 ; if eq, then no new thread
  280. ifndef NT_UP
  281. mov ecx, SYNCH_LEVEL ; set IRQL to SYNCH_LEVEL
  282. SetIrql ;
  283. lea rcx, KiDispatcherLock ; get dispatcher database lock address
  284. lea rdx, PbLockQueue + (16 * LockQueueDispatcherLock)[rbx] ; lock queue
  285. xor eax, eax ; set comperand value to NULL
  286. lock cmpxchg [rcx], rdx ; try to acquire dispatcher lock
  287. jnz short KiDI40 ; if nz, dispatcher lock not acquired
  288. endif
  289. mov rax, PbNextThread[rbx] ; get next thread address
  290. ;
  291. ; Swap context to a new thread.
  292. ;
  293. KiDI60: add rsp, sizeof DiFrame - 8 ; deallocate stack frame
  294. pop rbx ; restore nonvolatile register
  295. jmp short KxDispatchInterrupt ; finish in common code
  296. NESTED_END KiDispatchInterrupt, _TEXT$00
  297. ;
  298. ; There is a new thread scheduled for execution and the dispatcher lock
  299. ; has been acquired. Context switch to the new thread immediately.
  300. ;
  301. ; N.B. The following routine is entered by falling through from the above
  302. ; routine.
  303. ;
  304. ; N.B. The following routine is carefully written as a nested function that
  305. ; appears to have been called directly by the caller of the above
  306. ; function which processes the dispatch interrupt.
  307. ;
  308. ; Arguments:
  309. ;
  310. ; Thread (rax) - Supplies the address of the next thread to run on the
  311. ; current processor.
  312. ;
  313. NESTED_ENTRY KxDispatchInterrupt, _TEXT$00
  314. GENERATE_EXCEPTION_FRAME ; generate exception frame
  315. mov rbx, gs:[PcCurrentPrcb] ; get current PRCB address
  316. mov rsi, rax ; set address of next thread
  317. mov rdi, PbCurrentThread[rbx] ; get current thread address
  318. and qword ptr PbNextThread[rbx], 0 ; clear next thread address
  319. mov PbCurrentThread[rbx], rsi ; set current thread address
  320. ifndef NT_UP
  321. mov byte ptr ThIdleSwapBlock[rdi], 1 ; block swap from idle
  322. endif
  323. mov rcx, rdi ; set address of current thread
  324. call KiReadyThread ; reready thread for execution
  325. mov eax, TRUE ; set NPX save true
  326. mov cl, APC_LEVEL ; set APC interrupt bypass disable
  327. ifndef NT_UP
  328. xor edx, edx ; set swap from idle false
  329. endif
  330. call SwapContext ; call context swap routine
  331. RESTORE_EXCEPTION_STATE ; restore exception state/deallocate
  332. ret ; return
  333. NESTED_END KxDispatchInterrupt, _TEXT$00
  334. subttl "Swap Context"
  335. ;++
  336. ;
  337. ; Routine Description:
  338. ;
  339. ; This routine is called to swap context from one thread to the next. It
  340. ; swaps context, flushes the translation buffer, swaps the process address
  341. ; space if necessary, and returns to its caller.
  342. ;
  343. ; N.B. This routine is only called by code within this module and the idle
  344. ; thread code and uses special register calling conventions.
  345. ;
  346. ; Arguments:
  347. ;
  348. ; al - Supplies a boolean value that determines whether the full legacy
  349. ; floating state needs to be saved.
  350. ;
  351. ; cl - Supplies the APC interrupt bypass disable IRQL value.
  352. ;
  353. ; edx - Supplies a logical value that specifies whether the context swap
  354. ; is being called from the idle thread (MP systems only).
  355. ;
  356. ; rbx - Supplies the address of the current PRCB.
  357. ;
  358. ; rdi - Supplies the address of previous thread.
  359. ;
  360. ; rsi - Supplies the address of next thread.
  361. ;
  362. ; Return value:
  363. ;
  364. ; al - Supplies the kernel APC pending flag.
  365. ;
  366. ; rbx - Supplies the address of the current PRCB.
  367. ;
  368. ; rsi - Supplies the address of current thread.
  369. ;
  370. ;--
  371. NESTED_ENTRY SwapContext, _TEXT$00
  372. push_reg rbp ; save nonvolatile register
  373. alloc_stack (KSWITCH_FRAME_LENGTH - (2 * 8)) ; allocate stack frame
  374. END_PROLOGUE
  375. mov SwNpxSave[rsp], al ; save NPX save
  376. mov SwApcBypass[rsp], cl ; save APC bypass disable
  377. ;
  378. ; Set the new thread state to running.
  379. ;
  380. ; N.B. The state of the new thread MUST be set to running before releasing
  381. ; the dispatcher lock.
  382. ;
  383. mov byte ptr ThState[rsi], Running ; set thread state to running
  384. ;
  385. ; Acquire the context swap lock so the address space of the previous process
  386. ; cannot be deleted, then release the dispatcher database lock.
  387. ;
  388. ; N.B. The context swap lock is used to protect the address space until the
  389. ; context switch has sufficiently progressed to the point where the
  390. ; previous process address space is no longer needed. This lock is also
  391. ; acquired by the reaper thread before it finishes thread termination.
  392. ;
  393. ifndef NT_UP
  394. test edx, edx ; test if call from idle thread
  395. jnz short KiSC05 ; if nz, call from idle thread
  396. lea rcx, PbLockQueue + (16 * LockQueueContextSwapLock)[rbx] ; lock queue
  397. call KeAcquireQueuedSpinLockAtDpcLevel ; acquire context swap lock
  398. lea rcx, PbLockQueue + (16 * LockQueueDispatcherLock)[rbx] ; lock queue
  399. call KeReleaseQueuedSpinLockFromDpcLevel ; release dispatcher lock
  400. endif
  401. ;
  402. ; Check if an attempt is being made to context switch while in a DPC routine.
  403. ;
  404. KiSC05: cmp dword ptr PbDpcRoutineActive[rbx], 0 ; check if DPC active
  405. jne KiSC60 ; if ne, DPC is active
  406. ;
  407. ; Accumulate the total time spent in a thread.
  408. ;
  409. ifdef PERF_DATA
  410. rdtsc ; read cycle counter
  411. sub eax, PbThreadStartCount + 0[rbx] ; sub out thread start time
  412. sbb edx, PbThreadStartCount + 4[rbx] ;
  413. add EtPerformanceCountLow[rdi], eax ; accumlate thread run time
  414. adc EtPerformanceCountHigh[rdi], edx ;
  415. add PbThreadStartCount + 4[rbx], eax ; set new thread start time
  416. adc PbThreadStartCount + 8[rbx], edx ;
  417. endif
  418. ;
  419. ; Save the kernel mode XMM control/status register. If the current thread
  420. ; executes in user mode, then also save the legacy floating point state.
  421. ;
  422. stmxcsr SwMxCsr[rsp] ; save kernel mode XMM control/status
  423. cmp byte ptr ThNpxState[rdi], UserMode ; check if user mode thread
  424. jne short KiSC10 ; if ne, not user mode thread
  425. mov rbp, ThInitialStack[rdi] ; get previous thread initial stack
  426. cmp byte ptr SwNpxSave[rsp], TRUE ; check if full save required
  427. jne short KiSC07 ; if ne, full save not required
  428. fnsaved [rbp] ; save full legacy floating point state
  429. jmp short KiSC10 ;
  430. ;
  431. ; Full floating save not required.
  432. ;
  433. KiSC07: fnstenvd [rbp] ; save legacy floating environment
  434. ;
  435. ; Switch kernel stacks.
  436. ;
  437. KiSC10: mov ThKernelStack[rdi], rsp ; save old kernel stack pointer
  438. mov rsp, ThKernelStack[rsi] ; get new kernel stack pointer
  439. ;
  440. ; Swap the process address space if the new process is not the same as the
  441. ; previous process.
  442. ;
  443. mov rax, ThApcState + AsProcess[rdi] ; get previous process address
  444. cmp rax, ThApcState + AsProcess[rsi] ; check if process address match
  445. je short KiSC20 ; if e, process addresses match
  446. mov r14, ThApcState + AsProcess[rsi] ; get new process address
  447. ;
  448. ; Update the processor set masks.
  449. ;
  450. ifndef NT_UP
  451. mov rcx, PbSetMember[rbx] ; get processor set member
  452. xor PrActiveProcessors[rax], rcx ; clear bit in previous set
  453. xor PrActiveProcessors[r14], rcx ; set bit in new set
  454. if DBG
  455. test PrActiveProcessors[rax], rcx ; test if bit clear in previous set
  456. jnz short @f ; if nz, bit not clear in previous set
  457. test PrActiveProcessors[r14], rcx ; test if bit set in new set
  458. jnz short KiSC15 ; if nz, bit set in new set
  459. @@: int 3 ; debug break - incorrect active mask
  460. endif
  461. endif
  462. ;
  463. ; Load new CR3 value which will flush the TB and set the IOPM map offset in
  464. ; the TSS.
  465. ;
  466. KiSC15: mov r15, gs:[PcTss] ; get processor TSS address
  467. mov cx, PrIopmOffset[r14] ; get process IOPM offset
  468. mov TssIoMapBase[r15], cx ; set TSS IOPM offset
  469. mov rax, PrDirectoryTableBase[r14] ; get new directory base
  470. mov cr3, rax ; flush TLB and set new directory base
  471. ;
  472. ; Release the context swap lock.
  473. ;
  474. KiSC20: mov byte ptr ThIdleSwapBlock[rdi], 0 ; unblock swap from idle
  475. ifndef NT_UP
  476. lea rcx, PbLockQueue + (16 * LockQueueContextSwapLock)[rbx] ; lock queue
  477. call KeReleaseQueuedSpinLockFromDpcLevel ; release context swap lock
  478. endif
  479. ;
  480. ; Set the new kernel stack base in the TSS.
  481. ;
  482. mov r15, gs:[PcTss] ; get processor TSS address
  483. mov rbp, ThInitialStack[rsi] ; get new stack base address
  484. mov TssRsp0[r15], rbp ; set stack base address in TSS
  485. ;
  486. ; If the new thread executes in user mode, then restore the legacy floating
  487. ; state, load the compatibility mode TEB address, load the native user mode
  488. ; TEB address, and reload the segment registers if needed.
  489. ;
  490. ; N.B. The upper 32-bits of the compatibility mode TEB address are always
  491. ; zero.
  492. ;
  493. cmp byte ptr ThNpxState[rsi], UserMode ; check if user mode thread
  494. jne KiSC30 ; if ne, not user mode thread
  495. cmp byte ptr SwNpxSave[rsp], TRUE ; check if full restore required
  496. jne short KiSC22 ; if ne, full restore not required
  497. mov cx, LfControlWord[rbp] ; save current control word
  498. mov word ptr LfControlWord[rbp], 03fh ; set to mask all exceptions
  499. frstord [rbp] ; restore legacy floating point state
  500. mov LfControlWord[rbp], cx ; restore control word
  501. fldcw word ptr LfControlWord[rbp] ; load legacy control word
  502. jmp short KiSC24 ;
  503. ;
  504. ; Full legacy floating restore not required.
  505. ;
  506. KiSC22: fldenv [rbp] ; restore legacy floating environment
  507. ;
  508. ; Set base of compatibility mode TEB.
  509. ;
  510. KiSC24: mov eax, ThTeb[rsi] ; compute compatibility mode TEB address
  511. add eax, CmThreadEnvironmentBlockOffset ;
  512. mov rcx, gs:[PcGdt] ; get GDT base address
  513. mov KgdtBaseLow + KGDT64_R3_CMTEB[rcx], ax ; set CMTEB base address
  514. shr eax, 16 ;
  515. mov KgdtBaseMiddle + KGDT64_R3_CMTEB[rcx], al ;
  516. mov KgdtBaseHigh + KGDT64_R3_CMTEB[rcx], ah ;
  517. ;
  518. ; If the user segment selectors have been changed, then reload them with
  519. ; their cannonical values.
  520. ;
  521. mov ax, ds ; compute sum of segment selectors
  522. mov cx, es ;
  523. add ax, cx ;
  524. mov cx, gs ;
  525. add ax, cx ;
  526. cmp ax, ((KGDT64_R3_DATA or RPL_MASK) * 3) ; check if sum matches
  527. je short KiSC25 ; if e, sum matches expected value
  528. mov cx, KGDT64_R3_DATA or RPL_MASK ; reload user segment selectors
  529. mov ds, cx ;
  530. mov es, cx ;
  531. ;
  532. ; N.B. The following reload of the GS selector destroys the system MSR_GS_BASE
  533. ; register. Thus this sequence must be done with interrupt off.
  534. ;
  535. mov eax, gs:[PcSelf] ; get current PCR address
  536. mov edx, gs:[PcSelf + 4] ;
  537. cli ; disable interrupts
  538. mov gs, cx ; reload GS segment selector
  539. mov ecx, MSR_GS_BASE ; get GS base MSR number
  540. wrmsr ; write system PCR base address
  541. sti ; enable interrupts
  542. KiSC25: mov ax, KGDT64_R3_CMTEB or RPL_MASK ; reload FS segment selector
  543. mov fs, ax ;
  544. mov eax, ThTeb[rsi] ; get low part of user TEB address
  545. mov edx, ThTeb + 4[rsi] ; get high part of user TEB address
  546. mov gs:[PcTeb], eax ; set user TEB address in PCR
  547. mov gs:[PcTeb + 4], edx ;
  548. mov ecx, MSR_GS_SWAP ; get GS base swap MSR number
  549. wrmsr ; write user TEB base address
  550. ;
  551. ; Restore kernel mode XMM control/status and update context switch counters.
  552. ;
  553. KiSC30: ldmxcsr SwMxCsr[rsp] ; kernel mode XMM control/status
  554. inc dword ptr ThContextSwitches[rsi] ; thread count
  555. inc dword ptr PbContextSwitches[rbx] ; processor count
  556. ;
  557. ; If the new thread has a kernel mode APC pending, then request an APC
  558. ; interrupt if APC bypass is disabled.
  559. ;
  560. mov al, ThApcState + AsKernelApcPending[rsi] ; get APC pending
  561. test al, al ; test if kernel APC pending
  562. jz short KiSC50 ; if z, kernel APC not pending
  563. cmp byte ptr SwApcBypass[rsp], APC_LEVEL ; check if APC bypass enabled
  564. jb short KiSC40 ; if b, APC bypass is enabled
  565. mov cl, APC_LEVEL ; request APC interrupt
  566. call __imp_HalRequestSoftwareInterrupt ;
  567. clc ; clear carry flag
  568. KiSC40: setb al ; set return value
  569. KiSC50: add rsp, KSWITCH_FRAME_LENGTH - (2 * 8) ; deallocate stack frame
  570. pop rbp ; restore nonvolatile register
  571. ret ; return
  572. ;
  573. ; An attempt is being made to context switch while in a DPC routine. This is
  574. ; most likely caused by a DPC routine calling one of the wait functions.
  575. ;
  576. KiSC60: mov ecx, ATTEMPTED_SWITCH_FROM_DPC ; set bug check code
  577. call KeBugCheck ; bug check system - no return
  578. ret ; return
  579. NESTED_END SwapContext, _TEXT$00
  580. end