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.

1205 lines
33 KiB

  1. title "Irql Processing"
  2. ;++
  3. ;
  4. ; Copyright (c) 1989 Microsoft Corporation
  5. ;
  6. ; Module Name:
  7. ;
  8. ; ixlock.asm
  9. ;
  10. ; Abstract:
  11. ;
  12. ; This module implements various locking functions optimized for this hal.
  13. ;
  14. ; Author:
  15. ;
  16. ; Ken Reneris (kenr) 21-April-1994
  17. ;
  18. ; Environment:
  19. ;
  20. ; Kernel mode only.
  21. ;
  22. ; Revision History:
  23. ;
  24. ;--
  25. .486p
  26. .xlist
  27. include hal386.inc
  28. include callconv.inc ; calling convention macros
  29. include i386\ix8259.inc
  30. include i386\kimacro.inc
  31. include mac386.inc
  32. .list
  33. EXTRNP _KeBugCheckEx,5,IMPORT
  34. EXTRNP _KeSetEventBoostPriority, 2, IMPORT
  35. EXTRNP _KeWaitForSingleObject,5, IMPORT
  36. extrn FindHigherIrqlMask:DWORD
  37. extrn SWInterruptHandlerTable:DWORD
  38. EXTRNP _KeRaiseIrql,2
  39. EXTRNP _KeLowerIrql,1
  40. ifdef NT_UP
  41. LOCK_ADD equ add
  42. LOCK_DEC equ dec
  43. LOCK_CMPXCHG equ cmpxchg
  44. else
  45. LOCK_ADD equ lock add
  46. LOCK_DEC equ lock dec
  47. LOCK_CMPXCHG equ lock cmpxchg
  48. endif
  49. _TEXT$01 SEGMENT PARA PUBLIC 'CODE'
  50. ASSUME DS:FLAT, ES:FLAT, SS:FLAT, FS:NOTHING, GS:NOTHING
  51. PAGE
  52. subttl "AcquireSpinLock"
  53. ;++
  54. ;
  55. ; KIRQL
  56. ; KfAcquireSpinLock (
  57. ; IN PKSPIN_LOCK SpinLock
  58. ; )
  59. ;
  60. ; Routine Description:
  61. ;
  62. ; This function raises to DISPATCH_LEVEL and then acquires a the
  63. ; kernel spin lock.
  64. ;
  65. ; In a UP hal spinlock serialization is accomplished by raising the
  66. ; IRQL to DISPATCH_LEVEL. The SpinLock is not used; however, for
  67. ; debugging purposes if the UP hal is compiled with the NT_UP flag
  68. ; not set (ie, MP) we take the SpinLock.
  69. ;
  70. ; Arguments:
  71. ;
  72. ; (ecx) = SpinLock Supplies a pointer to an kernel spin lock.
  73. ;
  74. ; Return Value:
  75. ;
  76. ; OldIrql
  77. ;
  78. ;--
  79. cPublicFastCall KfAcquireSpinLock,1
  80. cPublicFpo 0,0
  81. mov eax, PCR[PcIrql] ; (eax) = Old Irql
  82. mov dword ptr PCR[PcIrql], DISPATCH_LEVEL ; set new irql
  83. ifndef NT_UP
  84. asl10: ACQUIRE_SPINLOCK ecx,<short asl20>
  85. endif
  86. ifdef IRQL_METRICS
  87. inc HalRaiseIrqlCount
  88. endif
  89. if DBG
  90. cmp al, DISPATCH_LEVEL ; old > new?
  91. ja short asl99 ; yes, go bugcheck
  92. endif
  93. fstRET KfAcquireSpinLock
  94. ifndef NT_UP
  95. asl20: SPIN_ON_SPINLOCK ecx,<short asl10>
  96. endif
  97. if DBG
  98. cPublicFpo 2,1
  99. asl99: movzx eax, al
  100. stdCall _KeBugCheckEx,<IRQL_NOT_GREATER_OR_EQUAL,eax,DISPATCH_LEVEL,0,1>
  101. ; never returns
  102. endif
  103. fstRET KfAcquireSpinLock
  104. fstENDP KfAcquireSpinLock
  105. ;++
  106. ;
  107. ; KIRQL
  108. ; KeAcquireSpinLockRaiseToSynch (
  109. ; IN PKSPIN_LOCK SpinLock
  110. ; )
  111. ;
  112. ; Routine Description:
  113. ;
  114. ; This function acquires the SpinLock at SYNCH_LEVEL. The function
  115. ; is optmized for hoter locks (the lock is tested before acquired.
  116. ; Any spin should occur at OldIrql; however, since this is a UP hal
  117. ; we don't have the code for it)
  118. ;
  119. ; In a UP hal spinlock serialization is accomplished by raising the
  120. ; IRQL to SYNCH_LEVEL. The SpinLock is not used; however, for
  121. ; debugging purposes if the UP hal is compiled with the NT_UP flag
  122. ; not set (ie, MP) we take the SpinLock.
  123. ;
  124. ; Arguments:
  125. ;
  126. ; (ecx) = SpinLock Supplies a pointer to an kernel spin lock.
  127. ;
  128. ; Return Value:
  129. ;
  130. ; OldIrql
  131. ;
  132. ;--
  133. cPublicFastCall KeAcquireSpinLockRaiseToSynch,1
  134. cPublicFpo 0,0
  135. mov eax, PCR[PcIrql] ; (eax) = Old Irql
  136. mov dword ptr PCR[PcIrql], SYNCH_LEVEL ; set new irql
  137. ifndef NT_UP
  138. asls10: ACQUIRE_SPINLOCK ecx,<short asls20>
  139. endif
  140. ifdef IRQL_METRICS
  141. inc HalRaiseIrqlCount
  142. endif
  143. if DBG
  144. cmp al, SYNCH_LEVEL ; old > new?
  145. ja short asls99 ; yes, go bugcheck
  146. endif
  147. fstRET KeAcquireSpinLockRaiseToSynch
  148. ifndef NT_UP
  149. asls20: SPIN_ON_SPINLOCK ecx,<short asls10>
  150. endif
  151. if DBG
  152. cPublicFpo 2,1
  153. asls99: movzx eax, al
  154. stdCall _KeBugCheckEx,<IRQL_NOT_GREATER_OR_EQUAL,eax,SYNCH_LEVEL,0,2>
  155. ; never returns
  156. endif
  157. fstRET KeAcquireSpinLockRaiseToSynch
  158. fstENDP KeAcquireSpinLockRaiseToSynch
  159. PAGE
  160. SUBTTL "Release Kernel Spin Lock"
  161. ;++
  162. ;
  163. ; VOID
  164. ; KfReleaseSpinLock (
  165. ; IN PKSPIN_LOCK SpinLock,
  166. ; IN KIRQL NewIrql
  167. ; )
  168. ;
  169. ; Routine Description:
  170. ;
  171. ; This function releases a kernel spin lock and lowers to the new irql
  172. ;
  173. ; In a UP hal spinlock serialization is accomplished by raising the
  174. ; IRQL to DISPATCH_LEVEL. The SpinLock is not used; however, for
  175. ; debugging purposes if the UP hal is compiled with the NT_UP flag
  176. ; not set (ie, MP) we use the SpinLock.
  177. ;
  178. ; Arguments:
  179. ;
  180. ; (ecx) = SpinLock Supplies a pointer to an executive spin lock.
  181. ; (dl) = NewIrql New irql value to set
  182. ;
  183. ; Return Value:
  184. ;
  185. ; None.
  186. ;
  187. ;--
  188. align 16
  189. cPublicFastCall KfReleaseSpinLock ,2
  190. cPublicFpo 0,0
  191. ifndef NT_UP
  192. RELEASE_SPINLOCK ecx ; release it
  193. endif
  194. xor ecx, ecx
  195. if DBG
  196. cmp dl, PCR[PcIrql]
  197. ja short rsl99
  198. endif
  199. pushfd
  200. cli
  201. movzx edx, dl
  202. mov PCR[PcIrql], edx ; store old irql
  203. mov cl, dl ; (ecx) = 32bit extended OldIrql
  204. mov edx, PCR[PcIRR]
  205. and edx, FindHigherIrqlMask[ecx*4] ; (edx) is the bitmask of
  206. ; pending interrupts we need to
  207. jne short rsl20 ; dispatch now.
  208. popfd
  209. fstRet KfReleaseSpinLock ; all done
  210. if DBG
  211. rsl99: mov eax, PCR[PcIrql]
  212. movzx edx, dl
  213. stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,eax,edx,0,3>
  214. ; never returns
  215. endif
  216. cPublicFpo 0,1
  217. rsl20: bsr ecx, edx ; (ecx) = Pending irq level
  218. cmp ecx, DISPATCH_LEVEL
  219. jle short rsl40
  220. mov eax, PCR[PcIDR] ; Clear all the interrupt
  221. SET_8259_MASK ; masks
  222. rsl40:
  223. mov edx, 1
  224. shl edx, cl
  225. xor PCR[PcIRR], edx ; clear bit in IRR
  226. call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int.
  227. popfd
  228. cPublicFpo 0, 0
  229. fstRet KfReleaseSpinLock ; all done
  230. fstENDP KfReleaseSpinLock
  231. ;++
  232. ;
  233. ; VOID
  234. ; FASTCALL
  235. ; ExAcquireFastMutex (
  236. ; IN PFAST_MUTEX FastMutex
  237. ; )
  238. ;
  239. ; Routine description:
  240. ;
  241. ; This function acquires ownership of the FastMutex
  242. ;
  243. ; Arguments:
  244. ;
  245. ; (ecx) = FastMutex - Supplies a pointer to the fast mutex
  246. ;
  247. ; Return Value:
  248. ;
  249. ; None.
  250. ;
  251. ;--
  252. cPublicFastCall ExAcquireFastMutex,1
  253. cPublicFpo 0,0
  254. mov eax, PCR[PcIrql] ; (eax) = OldIrql
  255. if DBG
  256. cmp eax, APC_LEVEL ; Is OldIrql > NewIrql?
  257. ja short afm99 ; Yes, bugcheck
  258. pushfd
  259. cli ; prevent swapcontext while
  260. ; snapping current thread.
  261. mov edx, PCR[PcPrcb]
  262. mov edx, [edx].PbCurrentThread ; (edx) = Current Thread
  263. popfd
  264. cmp [ecx].FmOwner, edx ; Already owned by this thread?
  265. je short afm98 ; Yes, error
  266. endif
  267. mov dword ptr PCR[PcIrql], APC_LEVEL ; Set NewIrql
  268. LOCK_DEC dword ptr [ecx].FmCount ; Get count
  269. jnz short afm10 ; Not the owner, go wait.
  270. ;
  271. ; Leave a notion of owner behind.
  272. ;
  273. ; Note: if you change this, change ExAcquireFastMutexUnsafe.
  274. ;
  275. if DBG
  276. pushfd
  277. cli ; prevent swapcontext while
  278. ; snapping current thread.
  279. mov edx, PCR[PcPrcb]
  280. mov edx, [edx].PbCurrentThread ; (edx) = Current Thread
  281. popfd
  282. mov [ecx].FmOwner, edx ; Save in Fast Mutex
  283. else
  284. ;
  285. ; Use esp to track the owning thread for debugging purposes.
  286. ; !thread from kd will find the owning thread. Note that the
  287. ; owner isn't cleared on release, check if the mutex is owned
  288. ; first.
  289. ;
  290. mov [ecx].FmOwner, esp
  291. endif
  292. mov dword ptr [ecx].FmOldIrql, eax
  293. fstRet ExAcquireFastMutex
  294. afm10:
  295. inc dword ptr [ecx].FmContention
  296. cPublicFpo 0,2
  297. push eax ; save OldIrql
  298. push ecx ; Save FAST_MUTEX
  299. add ecx, FmEvent ; Wait on Event
  300. stdCall _KeWaitForSingleObject,<ecx,WrExecutive,0,0,0>
  301. pop ecx ; (ecx) = FAST_MUTEX
  302. pop eax ; (al) = OldIrql
  303. cPublicFpo 1,0
  304. afm_ret:
  305. ;
  306. ; Leave a notion of owner behind.
  307. ;
  308. ; Note: if you change this, change ExAcquireFastMutexUnsafe.
  309. ;
  310. if DBG
  311. pushfd
  312. cli ; prevent swapcontext while
  313. ; snapping current thread.
  314. mov edx, PCR[PcPrcb]
  315. mov edx, [edx].PbCurrentThread ; (edx) = Current Thread
  316. popfd
  317. mov [ecx].FmOwner, edx ; Save in Fast Mutex
  318. else
  319. ;
  320. ; Use esp to track the owning thread for debugging purposes.
  321. ; !thread from kd will find the owning thread. Note that the
  322. ; owner isn't cleared on release, check if the mutex is owned
  323. ; first.
  324. ;
  325. mov [ecx].FmOwner, esp
  326. endif
  327. mov byte ptr [ecx].FmOldIrql, al
  328. fstRet ExAcquireFastMutex
  329. if DBG
  330. ; KeBugCheckEx(MUTEX_ALREADY_OWNED, FastMutex, CurrentThread, 0, 4)
  331. ; (never returns)
  332. afm98: stdCall _KeBugCheckEx,<MUTEX_ALREADY_OWNED,ecx,edx,0,4>
  333. ; KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL, CurrentIrql, APC_LEVEL, 0, 5)
  334. ; (never returns)
  335. afm99: movzx eax, al
  336. stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,eax,APC_LEVEL,0,5>
  337. fstRet ExAcquireFastMutex
  338. endif
  339. fstENDP ExAcquireFastMutex
  340. ;++
  341. ;
  342. ; VOID
  343. ; FASTCALL
  344. ; ExReleaseFastMutex (
  345. ; IN PFAST_MUTEX FastMutex
  346. ; )
  347. ;
  348. ; Routine description:
  349. ;
  350. ; This function releases ownership of the FastMutex
  351. ;
  352. ; Arguments:
  353. ;
  354. ; (ecx) = FastMutex - Supplies a pointer to the fast mutex
  355. ;
  356. ; Return Value:
  357. ;
  358. ; None.
  359. ;
  360. ;--
  361. cPublicFastCall ExReleaseFastMutex,1
  362. cPublicFpo 0,0
  363. xor eax, eax
  364. if DBG
  365. pushfd
  366. cli ; prevent swapcontext while
  367. ; snapping current thread.
  368. mov edx, PCR[PcPrcb]
  369. mov edx, [edx].PbCurrentThread ; (edx) = CurrentThread
  370. popfd
  371. cmp [ecx].FmOwner, edx ; Owner == CurrentThread?
  372. jne short rfm_threaderror ; No, bugcheck
  373. or byte ptr [ecx].FmOwner, 1 ; not the owner anymore
  374. endif
  375. mov al, byte ptr [ecx].FmOldIrql ; (eax) = OldIrql
  376. LOCK_ADD dword ptr [ecx].FmCount, 1 ; Remove our count
  377. js short rfm05 ; if < 0, set event
  378. jnz short rfm10 ; if != 0, don't set event
  379. rfm05:
  380. cPublicFpo 0,2
  381. push eax ; Save OldIrql
  382. add ecx, FmEvent
  383. stdCall _KeSetEventBoostPriority, <ecx, 0>
  384. pop eax
  385. cPublicFpo 0,0
  386. rfm10:
  387. cli
  388. mov PCR[PcIrql], eax
  389. mov edx, PCR[PcIRR]
  390. and edx, FindHigherIrqlMask[eax*4] ; (edx) is the bitmask of
  391. ; pending interrupts we need to
  392. jne short rfm20 ; dispatch now.
  393. sti
  394. fstRet ExReleaseFastMutex ; all done
  395. if DBG
  396. ; KeBugCheck(THREAD_NOT_MUTEX_OWNER, FastMutex, Thread, Owner, 6)
  397. ; (never returns)
  398. rfm_threaderror:
  399. stdCall _KeBugCheckEx,<THREAD_NOT_MUTEX_OWNER,ecx,edx,[ecx].FmOwner,6>
  400. endif
  401. rfm20: bsr ecx, edx ; (ecx) = Pending irq level
  402. cmp ecx, DISPATCH_LEVEL
  403. jle short rfm40
  404. mov eax, PCR[PcIDR] ; Clear all the interrupt
  405. SET_8259_MASK ; masks
  406. rfm40:
  407. mov edx, 1
  408. shl edx, cl
  409. xor PCR[PcIRR], edx ; clear bit in IRR
  410. call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int.
  411. sti
  412. fstRet ExReleaseFastMutex ; all done
  413. fstENDP ExReleaseFastMutex
  414. ;++
  415. ;
  416. ; BOOLEAN
  417. ; FASTCALL
  418. ; ExTryToAcquireFastMutex (
  419. ; IN PFAST_MUTEX FastMutex
  420. ; )
  421. ;
  422. ; Routine description:
  423. ;
  424. ; This function acquires ownership of the FastMutex
  425. ;
  426. ; Arguments:
  427. ;
  428. ; (ecx) = FastMutex - Supplies a pointer to the fast mutex
  429. ;
  430. ; Return Value:
  431. ;
  432. ; Returns TRUE if the FAST_MUTEX was acquired; otherwise false
  433. ;
  434. ;--
  435. cPublicFastCall ExTryToAcquireFastMutex,1
  436. cPublicFpo 0,0
  437. if DBG
  438. mov eax, PCR[PcIrql] ; (al) = OldIrql
  439. cmp eax, APC_LEVEL ; Is OldIrql > NewIrql?
  440. ja short tam99 ; Yes, bugcheck
  441. push eax ; Save OldIrql
  442. else
  443. push dword ptr PCR[PcIrql] ; Save OldIrql
  444. endif
  445. ;
  446. ; Try to acquire.
  447. ;
  448. mov edx, 0 ; Value to set
  449. mov eax, 1 ; Value to compare against
  450. mov dword ptr PCR[PcIrql], APC_LEVEL
  451. LOCK_CMPXCHG dword ptr [ecx].FmCount, edx ; Attempt to acquire
  452. jnz short tam20 ; got it?
  453. pop dword ptr [ecx].FmOldIrql ; Store OldIrql
  454. if DBG
  455. pushfd
  456. cli ; prevent swapcontext while
  457. ; snapping current thread.
  458. mov edx, PCR[PcPrcb]
  459. mov edx, [edx].PbCurrentThread ; (edx) = Current Thread
  460. popfd
  461. mov [ecx].FmOwner, edx ; Save in Fast Mutex
  462. else
  463. ;
  464. ; Use esp to track the owning thread for debugging purposes.
  465. ; !thread from kd will find the owning thread. Note that the
  466. ; owner isn't cleared on release, check if the mutex is owned
  467. ; first.
  468. ;
  469. mov dword ptr [ecx].FmOwner, esp
  470. endif
  471. mov eax, 1 ; return TRUE
  472. fstRet ExTryToAcquireFastMutex
  473. tam20: pop dword ptr PCR[PcIrql] ; restore entry Irql
  474. YIELD
  475. xor eax, eax ; return FALSE
  476. fstRet ExTryToAcquireFastMutex ; all done
  477. if DBG
  478. ; KeBugCheckEx(IRQL_NOT_LESS_OR_EQUAL, CurrentIrql, APC_LEVEL, 0, 5)
  479. ; (never returns)
  480. tam99: movzx eax, al
  481. stdCall _KeBugCheckEx,<IRQL_NOT_LESS_OR_EQUAL,eax,APC_LEVEL,0,7>
  482. xor eax, eax ; return FALSE
  483. fstRet ExTryToAcquireFastMutex
  484. endif
  485. fstENDP ExTryToAcquireFastMutex
  486. page ,132
  487. subttl "Acquire Queued SpinLock"
  488. ;++
  489. ;
  490. ; KIRQL
  491. ; KeAcquireQueuedSpinLock (
  492. ; IN KSPIN_LOCK_QUEUE_NUMBER Number
  493. ; )
  494. ;
  495. ; KIRQL
  496. ; KeAcquireQueuedSpinLockRaiseToSynch (
  497. ; IN KSPIN_LOCK_QUEUE_NUMBER Number
  498. ; )
  499. ;
  500. ; VOID
  501. ; KeAcquireInStackQueuedSpinLock (
  502. ; IN PKSPIN_LOCK SpinLock,
  503. ; IN PKLOCK_QUEUE_HANDLE LockHandle
  504. ; )
  505. ;
  506. ; VOID
  507. ; KeAcquireInStackQueuedSpinLockRaiseToSynch (
  508. ; IN PKSPIN_LOCK SpinLock,
  509. ; IN PKLOCK_QUEUE_HANDLE LockHandle
  510. ; )
  511. ;
  512. ; Routine Description:
  513. ;
  514. ; This function raises the current IRQL to DISPATCH/SYNCH level
  515. ; and acquires the specified queued spinlock.
  516. ;
  517. ; Arguments:
  518. ;
  519. ; Number (ecx) - Supplies the queued spinlock number.
  520. ;
  521. ; Return Value:
  522. ;
  523. ; The previous IRQL is returned as the function value.
  524. ;
  525. ;
  526. ; Routine Description:
  527. ;
  528. ; The Kx versions use a LOCK_QUEUE_HANDLE structure rather than
  529. ; LOCK_QUEUE structures in the PRCB. Old IRQL is stored in the
  530. ; LOCK_QUEUE_HANDLE.
  531. ;
  532. ; Arguments:
  533. ;
  534. ; SpinLock (ecx) Address of Actual Lock.
  535. ; LockHandle (edx) Address of lock context.
  536. ;
  537. ; Return Value:
  538. ;
  539. ; None. Actually returns OldIrql because common code is used
  540. ; for all implementations.
  541. ;
  542. ;--
  543. ; compile time assert sizeof(KSPIN_LOCK_QUEUE) == 8
  544. .errnz (LOCK_QUEUE_HEADER_SIZE - 8)
  545. ; VOID
  546. ; KeAcquireInStackQueuedSpinLockRaiseToSynch (
  547. ; IN PKSPIN_LOCK SpinLock,
  548. ; IN PKLOCK_QUEUE_HANDLE LockHandle
  549. ; )
  550. align 16
  551. cPublicFastCall KeAcquireInStackQueuedSpinLockRaiseToSynch,2
  552. cPublicFpo 0,1
  553. push SYNCH_LEVEL ; raise to SYNCH_LEVEL
  554. jmp short aqsl5 ; continue in common code
  555. fstENDP KeAcquireInStackQueuedSpinLockRaiseToSynch
  556. ; VOID
  557. ; KeAcquireInStackQueuedSpinLockRaiseToSynch (
  558. ; IN PKSPIN_LOCK SpinLock,
  559. ; IN PKLOCK_QUEUE_HANDLE LockHandle
  560. ; )
  561. cPublicFastCall KeAcquireInStackQueuedSpinLock,2
  562. cPublicFpo 0,1
  563. ; Get old IRQL and raise to DISPATCH_LEVEL
  564. push DISPATCH_LEVEL
  565. aqsl5:
  566. pop eax
  567. push dword ptr PCR[PcIrql]
  568. mov dword ptr PCR[PcIrql], eax
  569. if DBG
  570. cmp [esp], eax
  571. ja short aqsl
  572. endif
  573. ifndef NT_UP
  574. ; store OldIrql and address of actual lock in the queued
  575. ; spinlock structure in the lock queue handle structure.
  576. mov eax, [esp]
  577. mov [edx].LqhLock, ecx
  578. mov dword ptr [edx].LqhNext, 0
  579. mov [edx].LqhOldIrql, al
  580. ; continue in common code. common code expects the
  581. ; address of the "lock structure" in edx, this is at
  582. ; offset LqhNext in the lock queue handle structure.
  583. ; not accidentally this offset is zero.
  584. .errnz LqhNext
  585. ;; lea edx, [edx].LqhNext
  586. jmp short aqsl15 ; continue in common code
  587. else
  588. pop eax ; get old irql and set
  589. mov [edx].LqhOldIrql, al ; in lock queue handle.
  590. ifdef IRQL_METRICS
  591. inc HalRaiseIrqlCount
  592. endif
  593. fstRET KeAcquireInStackQueuedSpinLock
  594. endif
  595. fstENDP KeAcquireInStackQueuedSpinLock
  596. ; KIRQL
  597. ; KeAcquireQueuedSpinLockRaiseToSynch (
  598. ; IN KSPIN_LOCK_QUEUE_NUMBER Number
  599. ; )
  600. cPublicFastCall KeAcquireQueuedSpinLockRaiseToSynch,1
  601. cPublicFpo 0,1
  602. push SYNCH_LEVEL
  603. jmp short aqsl10 ; continue in common code
  604. fstENDP KeAcquireQueuedSpinLockRaiseToSynch
  605. ; KIRQL
  606. ; KeAcquireQueuedSpinLock (
  607. ; IN KSPIN_LOCK_QUEUE_NUMBER Number
  608. ; )
  609. ;
  610. cPublicFastCall KeAcquireQueuedSpinLock,1
  611. cPublicFpo 0,1
  612. ; Get old IRQL and raise to DISPATCH_LEVEL
  613. push DISPATCH_LEVEL
  614. aqsl10:
  615. pop eax
  616. push dword ptr PCR[PcIrql]
  617. mov dword ptr PCR[PcIrql], eax
  618. if DBG
  619. cmp [esp], eax
  620. ja short aqsl
  621. endif
  622. ifndef NT_UP
  623. ; Get address of Lock Queue entry
  624. mov edx, PCR[PcPrcb] ; get address of PRCB
  625. lea edx, [edx+ecx*8].PbLockQueue ; get &PRCB->LockQueue[Number]
  626. ; Get address of the actual lock.
  627. mov ecx, [edx].LqLock
  628. aqsl15:
  629. mov eax, edx ; save Lock Queue entry address
  630. ; Exchange the value of the lock with the address of this
  631. ; Lock Queue entry.
  632. xchg [ecx], edx
  633. cmp edx, 0 ; check if lock is held
  634. jnz short @f ; jiff held
  635. ; note: the actual lock address will be word aligned, we use
  636. ; the bottom two bits as indicators, bit 0 is LOCK_QUEUE_WAIT,
  637. ; bit 1 is LOCK_QUEUE_OWNER.
  638. or ecx, LOCK_QUEUE_OWNER ; mark self as lock owner
  639. mov [eax].LqLock, ecx
  640. ; lock has been acquired, return.
  641. endif
  642. aqsl20: pop eax ; restore return value
  643. ifdef IRQL_METRICS
  644. inc HalRaiseIrqlCount
  645. endif
  646. fstRET KeAcquireQueuedSpinLock
  647. ifndef NT_UP
  648. @@:
  649. ; The lock is already held by another processor. Set the wait
  650. ; bit in this processor's Lock Queue entry, then set the next
  651. ; field in the Lock Queue entry of the last processor to attempt
  652. ; to acquire the lock (this is the address returned by the xchg
  653. ; above) to point to THIS processor's lock queue entry.
  654. or ecx, LOCK_QUEUE_WAIT ; set lock bit
  655. mov [eax].LqLock, ecx
  656. mov [edx].LqNext, eax ; set previous acquirer's
  657. ; next field.
  658. ; Wait.
  659. @@:
  660. YIELD ; fire avoidance.
  661. test [eax].LqLock, LOCK_QUEUE_WAIT ; check if still waiting
  662. jz short aqsl20 ; jif lock acquired
  663. jmp short @b ; else, continue waiting
  664. endif
  665. if DBG
  666. ; Raising to a lower IRQL. BugCheck.
  667. ;
  668. ; KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL,
  669. ; current (old) IRQL,
  670. ; desired IRQL,
  671. ; lock number (only if Ke routine, not Kx),
  672. ; 8);
  673. aqsl: pop edx
  674. stdCall _KeBugCheckEx,<IRQL_NOT_GREATER_OR_EQUAL,edx,eax,ecx,8>
  675. ; never returns (but help the debugger back-trace)
  676. int 3
  677. endif
  678. fstENDP KeAcquireQueuedSpinLock
  679. page ,132
  680. subttl "Release Queued SpinLock"
  681. ;++
  682. ;
  683. ; VOID
  684. ; KeReleaseInStackQueuedSpinLock (
  685. ; IN PKLOCK_QUEUE_HANDLE LockHandle
  686. ; )
  687. ;
  688. ; Routine Description:
  689. ;
  690. ; This function releases a queued spinlock and lowers the IRQL to
  691. ; its previous value.
  692. ;
  693. ; This differs from KeReleaseQueuedSpinLock in that this version
  694. ; uses a caller supplied lock context where that one uses a
  695. ; predefined lock context in the processor's PRCB.
  696. ;
  697. ; This version sets up a compatible register context and uses
  698. ; KeReleaseQueuedSpinLock to do the actual work.
  699. ;
  700. ; Arguments:
  701. ;
  702. ; LockHandle (ecx) - Address of Lock Queue Handle structure.
  703. ;
  704. ; Return Value:
  705. ;
  706. ; None.
  707. ;
  708. ;--
  709. cPublicFastCall KeReleaseInStackQueuedSpinLock,1
  710. cPublicFpo 0,0
  711. movzx edx, byte ptr [ecx].LqhOldIrql ; get old irql
  712. ifndef NT_UP
  713. lea eax, [ecx].LqhNext ; get address of lock struct
  714. jmp short rqsl10 ; continue in common code
  715. else
  716. jmp short rqsl30 ; continue in common code
  717. endif
  718. fstENDP KeReleaseInStackQueuedSpinLock
  719. ;++
  720. ;
  721. ; VOID
  722. ; KeReleaseQueuedSpinLock (
  723. ; IN KSPIN_LOCK_QUEUE_NUMBER Number,
  724. ; IN KIRQL OldIrql
  725. ; )
  726. ;
  727. ; Routine Description:
  728. ;
  729. ; This function releases a queued spinlock and lowers the IRQL to
  730. ; its previous value.
  731. ;
  732. ; Arguments:
  733. ;
  734. ; Number (ecx) - Supplies the queued spinlock number.
  735. ; OldIrql (dl) - Supplies the IRQL value to lower to.
  736. ;
  737. ; Return Value:
  738. ;
  739. ; None.
  740. ;
  741. ;--
  742. cPublicFastCall KeReleaseQueuedSpinLock,2
  743. cPublicFpo 0,0
  744. ifndef NT_UP
  745. ; Get address of Lock Queue entry
  746. mov eax, PCR[PcPrcb] ; get address of PRCB
  747. lea eax, [eax+ecx*8].PbLockQueue ; get &PRCB->LockQueue[Number]
  748. rqsl10:
  749. push ebx ; need another register
  750. cPublicFpo 0,1
  751. ; Clear the lock field in the Lock Queue entry.
  752. mov ebx, [eax].LqNext
  753. mov ecx, [eax].LqLock
  754. ;; and ecx, NOT (LOCK_QUEUE_OWNER) ; clear lock bit
  755. ; Quick check: If Lock Queue entry's Next field is not NULL,
  756. ; there is another waiter. Don't bother with ANY atomic ops
  757. ; in this case.
  758. ;
  759. ; Note: test clears CF and sets ZF appropriately, the following
  760. ; btr sets CF appropriately for the owner check.
  761. test ebx, ebx
  762. ; clear the "I am owner" bit in the Lock entry.
  763. btr ecx, 1 ; clear owner bit
  764. if DBG
  765. jnc short rqsl98 ; bugcheck if was not set
  766. ; tests CF
  767. endif
  768. mov [eax].LqLock, ecx ; clear lock bit in queue entry
  769. jnz short rqsl40 ; jif another processor waits
  770. ; ebx contains zero here which will be used to set the new owner NULL
  771. push eax ; save &PRCB->LockQueue[Number]
  772. cPublicFpo 0,2
  773. ; Use compare exchange to attempt to clear the actual lock.
  774. ; If there are still no processors waiting for the lock when
  775. ; the compare exchange happens, the old contents of the lock
  776. ; should be the address of this lock entry (eax).
  777. lock cmpxchg [ecx], ebx ; store 0 if no waiters
  778. pop eax ; restore lock queue address
  779. cPublicFpo 0,1
  780. jnz short rqsl60 ; jif store failed
  781. ; The lock has been released. Lower IRQL and return to caller.
  782. rqsl20:
  783. pop ebx ; restore ebx
  784. cPublicFpo 0,0
  785. endif
  786. rqsl30:
  787. pushfd ; disable interrupts
  788. cli
  789. movzx edx, dl
  790. xor ecx, ecx
  791. mov dword ptr PCR[PcIrql], edx ; set new (lower) OldIrql
  792. mov cl, dl ; ecx = zero extended OldIrql
  793. mov edx, PCR[PcIRR] ; Check interrupt requests
  794. and edx, FindHigherIrqlMask[ecx*4] ; edx = pending interrupts
  795. ; enabled by lower IRQL.
  796. jne short rqsl80 ; Dispatch pending interrupts.
  797. popfd ; restore interrupt state
  798. fstRET KeReleaseQueuedSpinLock
  799. ifndef NT_UP
  800. ; Another processor is waiting on this lock. Hand the lock
  801. ; to that processor by getting the address of its LockQueue
  802. ; entry, turning ON its owner bit and OFF its wait bit.
  803. rqsl40: xor [ebx].LqLock, (LOCK_QUEUE_OWNER+LOCK_QUEUE_WAIT)
  804. ; Done, the other processor now owns the lock, clear the next
  805. ; field in my LockQueue entry (to preserve the order for entering
  806. ; the queue again) and proceed to lower IRQL and return.
  807. mov [eax].LqNext, 0
  808. jmp short rqsl20
  809. ; We get here if another processor is attempting to acquire
  810. ; the lock but had not yet updated the next field in this
  811. ; processor's Queued Lock Next field. Wait for the next
  812. ; field to be updated.
  813. rqsl60: mov ebx, [eax].LqNext
  814. test ebx, ebx ; check if still 0
  815. jnz short rqsl40 ; jif Next field now set.
  816. YIELD ; wait a bit
  817. jmp short rqsl60 ; continue waiting
  818. endif
  819. cPublicFpo 0,1
  820. rqsl80: bsr ecx, edx ; (ecx) = Pending irq level
  821. cmp ecx, DISPATCH_LEVEL ; if new int at dispatch level
  822. jle short @f ; no need to change 8259 masks
  823. mov eax, PCR[PcIDR] ; Clear all the interrupt
  824. SET_8259_MASK ; masks
  825. @@:
  826. mov edx, 1
  827. shl edx, cl
  828. xor PCR[PcIRR], edx ; clear bit in IRR
  829. call SWInterruptHandlerTable[ecx*4] ; Dispatch the pending int.
  830. popfd
  831. cPublicFpo 0, 0
  832. fstRet KfReleaseSpinLock ; all done
  833. ifndef NT_UP
  834. if DBG
  835. cPublicFpo 0,1
  836. rqsl98: stdCall _KeBugCheckEx,<SPIN_LOCK_NOT_OWNED,ecx,eax,0,1>
  837. int 3 ; so stacktrace works
  838. endif
  839. endif
  840. fstENDP KeReleaseQueuedSpinLock
  841. page ,132
  842. subttl "Try to Acquire Queued SpinLock"
  843. ;++
  844. ;
  845. ; LOGICAL
  846. ; KeTryToAcquireQueuedSpinLock (
  847. ; IN KSPIN_LOCK_QUEUE_NUMBER Number,
  848. ; OUT PKIRQL OldIrql
  849. ; )
  850. ;
  851. ; LOGICAL
  852. ; KeTryToAcquireQueuedSpinLockRaiseToSynch (
  853. ; IN KSPIN_LOCK_QUEUE_NUMBER Number,
  854. ; OUT PKIRQL OldIrql
  855. ; )
  856. ;
  857. ; Routine Description:
  858. ;
  859. ; This function raises the current IRQL to DISPATCH/SYNCH level
  860. ; and attempts to acquire the specified queued spinlock. If the
  861. ; spinlock is already owned by another thread, IRQL is restored
  862. ; to its previous value and FALSE is returned.
  863. ;
  864. ; Arguments:
  865. ;
  866. ; Number (ecx) - Supplies the queued spinlock number.
  867. ; OldIrql (edx) - A pointer to the variable to receive the old
  868. ; IRQL.
  869. ;
  870. ; Return Value:
  871. ;
  872. ; TRUE if the lock was acquired, FALSE otherwise.
  873. ; N.B. ZF is set if FALSE returned, clear otherwise.
  874. ;
  875. ;--
  876. align 16
  877. cPublicFastCall KeTryToAcquireQueuedSpinLockRaiseToSynch,2
  878. cPublicFpo 0,0
  879. mov eax, SYNCH_LEVEL ; raise to SYNCH
  880. jmp short taqsl10 ; continue in common code
  881. fstENDP KeTryToAcquireQueuedSpinLockRaiseToSynch
  882. cPublicFastCall KeTryToAcquireQueuedSpinLock,2
  883. cPublicFpo 0,0
  884. mov eax, DISPATCH_LEVEL ; raise to DPC level
  885. ; Attempt to get the lock with interrupts disabled, raising
  886. ; the priority in the interrupt controller only if acquisition
  887. ; is successful.
  888. taqsl10:
  889. if DBG
  890. cmp al, PCR[PcIrql]
  891. jb short taqsl98
  892. endif
  893. ifndef NT_UP
  894. push edx ; save address of OldIrql
  895. pushfd ; save interrupt state
  896. cli ; disable interrupts
  897. ; Get address of Lock Queue entry
  898. mov edx, PCR[PcPrcb] ; get address of PRCB
  899. lea edx, [edx+ecx*8].PbLockQueue ; get &PRCB->LockQueue[Number]
  900. ; Get address of the actual lock.
  901. mov ecx, [edx].LqLock
  902. if DBG
  903. test ecx, LOCK_QUEUE_OWNER+LOCK_QUEUE_WAIT
  904. jnz short taqsl99 ; jiff lock already held (or
  905. ; this proc already waiting).
  906. endif
  907. cmp dword ptr [ecx], 0 ; check if already taken
  908. push eax ; save new IRQL
  909. jnz taqsl60 ; jif already taken
  910. xor eax, eax ; comparison value (not locked)
  911. ; Store the Lock Queue entry address in the lock ONLY if the
  912. ; current lock value is 0.
  913. lock cmpxchg [ecx], edx
  914. jnz short taqsl60
  915. ; Lock has been acquired.
  916. ; note: the actual lock address will be word aligned, we use
  917. ; the bottom two bits as indicators, bit 0 is LOCK_QUEUE_WAIT,
  918. ; bit 1 is LOCK_QUEUE_OWNER.
  919. or ecx, LOCK_QUEUE_OWNER ; mark self as lock owner
  920. mov [edx].LqLock, ecx
  921. pop eax
  922. endif
  923. ; Raise IRQL and return success.
  924. mov ecx, PCR[PcIrql] ; ecx = OldIrql
  925. mov dword ptr PCR[PcIrql], eax ; set new IRQL
  926. ifndef NT_UP
  927. popfd ; restore interrupt state
  928. pop edx
  929. endif
  930. mov [edx], cl ; *OldIrql = OldIrql
  931. xor eax, eax
  932. or eax, 1 ; return TRUE
  933. fstRET KeTryToAcquireQueuedSpinLock
  934. ifndef NT_UP
  935. taqsl60:
  936. ; The lock is already held by another processor. Indicate
  937. ; failure to the caller.
  938. pop eax ; pop new IRQL off stack
  939. popfd ; restore interrupt state
  940. pop edx ; pop saved OldIrql address
  941. xor eax, eax ; return FALSE
  942. fstRET KeTryToAcquireQueuedSpinLock
  943. endif
  944. if DBG
  945. taqsl98: stdCall _KeBugCheckEx,<IRQL_NOT_GREATER_OR_EQUAL,PCR[PcIrql],eax,ecx,9>
  946. taqsl99: stdCall _KeBugCheckEx,<SPIN_LOCK_ALREADY_OWNED,ecx,edx,0,0>
  947. ; never returns (help the debugger back-trace)
  948. int 3
  949. endif
  950. fstENDP KeTryToAcquireQueuedSpinLock
  951. _TEXT$01 ends
  952. end