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.

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