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.

613 lines
15 KiB

  1. /*
  2. * ACPILOCK.C -- ACPI OS Independent functions for managing the ACPI Global Lock
  3. *
  4. */
  5. #include "pch.h"
  6. #define ACPI_LOCK_PENDING_BIT 0
  7. #define ACPI_LOCK_OWNED_BIT 1
  8. #define ACPI_LOCK_PENDING (1 << ACPI_LOCK_PENDING_BIT)
  9. #define ACPI_LOCK_OWNED (1 << ACPI_LOCK_OWNED_BIT)
  10. NTSTATUS EXPORT
  11. GlobalLockEventHandler (
  12. ULONG EventType,
  13. ULONG What,
  14. ULONG dwParam,
  15. PFNAA Callback,
  16. PVOID Context
  17. )
  18. /*++
  19. Routine Description:
  20. This is the internal front-end for global lock requests.
  21. Arguments:
  22. EventType - Only global lock acquire/release supported
  23. What - Acquire or Release
  24. Param - Not used
  25. Callback - Async callback (acquire only)
  26. Context - LockRequest struct and context for callback (must be same for acquire/release)
  27. Return Value:
  28. NTSTATUS
  29. --*/
  30. {
  31. NTSTATUS status;
  32. PACPI_GLOBAL_LOCK LockRequest = Context;
  33. ASSERT (EventType == EVTYPE_ACQREL_GLOBALLOCK);
  34. switch (What) {
  35. case GLOBALLOCK_ACQUIRE:
  36. //
  37. // Fill out the lock request. Internal requests have no Irp, just pass
  38. // in the address of the callback routine.
  39. //
  40. LockRequest->LockContext = Callback;
  41. LockRequest->Type = ACPI_GL_QTYPE_INTERNAL;
  42. status = ACPIAsyncAcquireGlobalLock (LockRequest);
  43. break;
  44. case GLOBALLOCK_RELEASE:
  45. status = ACPIReleaseGlobalLock (Context);
  46. break;
  47. default:
  48. ACPIBreakPoint ();
  49. status = STATUS_INVALID_PARAMETER;
  50. }
  51. return status;
  52. }
  53. NTSTATUS
  54. ACPIAsyncAcquireGlobalLock(
  55. PACPI_GLOBAL_LOCK Request
  56. )
  57. /*++
  58. Routine Description:
  59. Attempt to acquire the hardware Global Lock. If the global lock is busy due to another NT thread
  60. or the BIOS, the request is queued. The request will be satisfied when 1) All other requests in
  61. front of it on the queue have released the lock, and 2) The BIOS has released the lock.
  62. Arguments:
  63. Request - Contains context and callback
  64. Return Value:
  65. NTSTATUS
  66. --*/
  67. {
  68. KIRQL OldIrql;
  69. PLIST_ENTRY entry;
  70. PACPI_GLOBAL_LOCK queuedRequest;
  71. ACPIDebugEnter("ACPIAsyncAcquireGlobalLock");
  72. ACPIPrint( (
  73. ACPI_PRINT_IO,
  74. "ACPIAsyncAcquireGlobalLock: Entered with context %x\n",
  75. Request
  76. ) );
  77. //
  78. // If caller is the current owner, just increment the depth count
  79. //
  80. if (Request == AcpiInformation->GlobalLockOwnerContext) {
  81. AcpiInformation->GlobalLockOwnerDepth++;
  82. ACPIPrint( (
  83. ACPI_PRINT_IO,
  84. "ACPIAsyncAcquireGlobalLock: Recursive acquire by owner %x, new depth=%d\n",
  85. Request, AcpiInformation->GlobalLockOwnerDepth
  86. ) );
  87. return STATUS_SUCCESS;
  88. }
  89. //
  90. // Lock the Global Lock Queue. We don't want any state changes while we examine
  91. // the queue and (possibly) attempt to get the hardware global lock. For example,
  92. // if the list is empty, but the BIOS has the lock, we don't want to unlock the queue
  93. // until we have put the request on it -- so that the release interrupt will dispatch
  94. // the request.
  95. //
  96. KeAcquireSpinLock (&AcpiInformation->GlobalLockQueueLock, &OldIrql);
  97. //
  98. // Check if there are others in front of us. If not, we can try to get the lock
  99. //
  100. if (IsListEmpty (&AcpiInformation->GlobalLockQueue)) {
  101. //
  102. // Try to grab the lock. It will only be available if no other thread nor
  103. // the BIOS has it.
  104. //
  105. if (ACPIAcquireHardwareGlobalLock (AcpiInformation->GlobalLock)) {
  106. //
  107. // Got the lock. Setup owner and unlock the queue
  108. //
  109. AcpiInformation->GlobalLockOwnerContext = Request;
  110. AcpiInformation->GlobalLockOwnerDepth = 1;
  111. KeReleaseSpinLock (&AcpiInformation->GlobalLockQueueLock, OldIrql);
  112. ACPIPrint( (
  113. ACPI_PRINT_IO,
  114. "ACPIAsyncAcquireGlobalLock: Got lock immediately, Context %x\n",
  115. Request
  116. ) );
  117. return STATUS_SUCCESS;
  118. }
  119. }
  120. //
  121. // We have to wait for the lock.
  122. //
  123. // First, check if context is already queued.
  124. //
  125. for (entry = AcpiInformation->GlobalLockQueue.Flink;
  126. entry != &AcpiInformation->GlobalLockQueue;
  127. entry = entry->Flink) {
  128. queuedRequest = CONTAINING_RECORD (entry, ACPI_GLOBAL_LOCK, ListEntry);
  129. if (queuedRequest == Request) {
  130. //
  131. // Already queued, we just increment the depth count and exit.
  132. //
  133. ACPIPrint( (
  134. ACPI_PRINT_IO,
  135. "ACPIAsyncAcquireGlobalLock: Waiting for lock <again>, Context %x depth %x\n",
  136. Request, Request->Depth
  137. ) );
  138. queuedRequest->Depth++;
  139. KeReleaseSpinLock (&AcpiInformation->GlobalLockQueueLock, OldIrql);
  140. return STATUS_PENDING;
  141. }
  142. }
  143. //
  144. // Put this request on the global lock queue
  145. //
  146. Request->Depth = 1;
  147. InsertTailList (
  148. &AcpiInformation->GlobalLockQueue,
  149. &Request->ListEntry
  150. );
  151. ACPIPrint( (
  152. ACPI_PRINT_IO,
  153. "ACPIAsyncAcquireGlobalLock: Waiting for lock, Context %x\n",
  154. Request
  155. ) );
  156. KeReleaseSpinLock (&AcpiInformation->GlobalLockQueueLock, OldIrql);
  157. return STATUS_PENDING;
  158. ACPIDebugExit("ACPIAsyncAcquireGlobalLock");
  159. }
  160. NTSTATUS
  161. ACPIReleaseGlobalLock(
  162. PVOID OwnerContext
  163. )
  164. /*++
  165. Routine Description:
  166. Release the global lock. Caller must provide the owning context. If there are any additional
  167. requests on the queue, re-acquire the global lock and dispatch the next owner.
  168. The hardware lock is released and re-acquired so that we don't starve the BIOS from the lock.
  169. Arguments:
  170. OwnerContext - Must be same context that was used to acquire the lock
  171. Return Value:
  172. NTSTATUS
  173. --*/
  174. {
  175. KIRQL OldIrql;
  176. ACPIDebugEnter("ACPIReleaseGlobalLock");
  177. //
  178. // Caller must be the current owner of the lock
  179. //
  180. if (OwnerContext != AcpiInformation->GlobalLockOwnerContext) {
  181. ACPIPrint( (
  182. ACPI_PRINT_WARNING,
  183. "ACPIReleaseGlobalLock: Not owner, can't release! Owner is %x Caller context is %x\n",
  184. AcpiInformation->GlobalLockOwnerContext, OwnerContext
  185. ) );
  186. return STATUS_ACPI_MUTEX_NOT_OWNER;
  187. }
  188. //
  189. // Only the current owner of the global lock gets here.
  190. // Release the lock when the depth count reaches 0
  191. //
  192. if (--AcpiInformation->GlobalLockOwnerDepth > 0) {
  193. ACPIPrint( (
  194. ACPI_PRINT_IO,
  195. "ACPIReleaseGlobalLock: Recursively owned by context %x, depth remaining %d\n",
  196. AcpiInformation->GlobalLockOwnerContext, AcpiInformation->GlobalLockOwnerDepth
  197. ) );
  198. return STATUS_SUCCESS;
  199. }
  200. //
  201. // Mark lock not owned, and physically release the thing.
  202. // This allows the BIOS a chance at getting the lock.
  203. //
  204. AcpiInformation->GlobalLockOwnerContext = NULL;
  205. ACPIReleaseHardwareGlobalLock ();
  206. ACPIPrint( (
  207. ACPI_PRINT_IO,
  208. "ACPIReleaseGlobalLock: Lock released by context %x\n",
  209. OwnerContext
  210. ) );
  211. //
  212. // It is our responsibility to hand off the lock to the next-in-line.
  213. // First, check if there is anything on the queue.
  214. //
  215. if (IsListEmpty (&AcpiInformation->GlobalLockQueue)) {
  216. return STATUS_SUCCESS; // Nope, all done, nothing else to do
  217. }
  218. //
  219. // The queue is not empty, we must get the lock back
  220. //
  221. if (!ACPIAcquireHardwareGlobalLock (AcpiInformation->GlobalLock)) {
  222. return STATUS_SUCCESS; // BIOS has the lock, just wait for interrupt
  223. }
  224. //
  225. // Got the lock, now dispatch the next owner
  226. //
  227. ACPIStartNextGlobalLockRequest ();
  228. ACPIDebugExit("ACPIReleaseGlobalLock");
  229. return STATUS_SUCCESS;
  230. }
  231. void
  232. ACPIHardwareGlobalLockReleased (
  233. void
  234. )
  235. /*++
  236. Routine Description:
  237. Called from the ACPI interrupt DPC. We get here only if an attempt to get the global lock has
  238. been made, but failed because the BIOS had the lock. As a result, the lock was marked pending,
  239. and this interrupt has happened because the BIOS has released the lock.
  240. Therefore, this procedure must acquire the hardware lock and dispatch ownership to the next
  241. request on the queue.
  242. Arguments:
  243. NONE
  244. Return Value:
  245. NONE
  246. --*/
  247. {
  248. //
  249. // Attempt to get the global lock on behalf of the next request in the queue
  250. //
  251. if (!ACPIAcquireHardwareGlobalLock (AcpiInformation->GlobalLock)) {
  252. return; // BIOS has the lock (again), just wait for next interrupt
  253. }
  254. //
  255. // Got the lock, now dispatch the next owner
  256. //
  257. ACPIStartNextGlobalLockRequest ();
  258. }
  259. void
  260. ACPIStartNextGlobalLockRequest (
  261. void
  262. )
  263. /*++
  264. Routine Description:
  265. Get the next request off the queue, and give it the global lock.
  266. This routine can only be called by the thread that currently holds the hardware lock. If
  267. the queue is empty, the lock is released.
  268. Arguments:
  269. NONE
  270. Return Value:
  271. NONE
  272. --*/
  273. {
  274. PLIST_ENTRY link;
  275. PACPI_GLOBAL_LOCK request;
  276. PFNAA callback;
  277. PIRP irp;
  278. //
  279. // Get next request from the queue.
  280. //
  281. link = ExInterlockedRemoveHeadList (
  282. &AcpiInformation->GlobalLockQueue,
  283. &AcpiInformation->GlobalLockQueueLock
  284. );
  285. //
  286. // If something failed after the original thread tried to get the lock, then
  287. // the queue might be empty.
  288. //
  289. if (link == NULL) {
  290. ACPIPrint( (
  291. ACPI_PRINT_IO,
  292. "ACPIStartNextGlobalLockRequest: Queue is empty, releasing lock\n"
  293. ) );
  294. ACPIReleaseHardwareGlobalLock ();
  295. return;
  296. }
  297. //
  298. // Complete the next global lock request
  299. //
  300. request = CONTAINING_RECORD (link, ACPI_GLOBAL_LOCK, ListEntry);
  301. //
  302. // Bookkeeping
  303. //
  304. AcpiInformation->GlobalLockOwnerContext = request;
  305. AcpiInformation->GlobalLockOwnerDepth = request->Depth;
  306. ACPIPrint( (
  307. ACPI_PRINT_IO,
  308. "ACPIStartNextGlobalLockRequest: Dispatch new owner, ctxt %x callb %x\n",
  309. request, request->LockContext
  310. ) );
  311. //
  312. // Let the requestor know that it now has the lock
  313. //
  314. switch (request->Type) {
  315. case ACPI_GL_QTYPE_INTERNAL:
  316. //
  317. // Internal request - invoke the callback
  318. //
  319. callback = (PFNAA) request->LockContext;
  320. callback (request);
  321. break;
  322. case ACPI_GL_QTYPE_IRP:
  323. //
  324. // External request - complete the Irp
  325. //
  326. irp = (PIRP) request->LockContext;
  327. irp->IoStatus.Status = STATUS_SUCCESS;
  328. IoCompleteRequest (irp, IO_NO_INCREMENT);
  329. break;
  330. default: // Shouldn't happen...
  331. ACPIBreakPoint();
  332. break;
  333. }
  334. }
  335. BOOLEAN
  336. ACPIAcquireHardwareGlobalLock(
  337. PULONG GlobalLock
  338. )
  339. /*++
  340. Routine Description:
  341. Attempt to obtain the hardware global lock.
  342. Arguments:
  343. NONE
  344. Return Value:
  345. TRUE if acquired, FALSE if pending.
  346. --*/
  347. {
  348. ULONG lockValue;
  349. ULONG oldLockValue;
  350. BOOLEAN owned;
  351. //
  352. // don't bother with the lock if we are an ACPI only system.
  353. //
  354. if (AcpiInformation->ACPIOnly) {
  355. return(TRUE);
  356. }
  357. lockValue = *((ULONG volatile *)GlobalLock);
  358. do {
  359. //
  360. // Record the original state of the lock. Shift the contents of
  361. // the ACPI_LOCK_OWNED bit to the ACPI_LOCK_PENDING bit, and set the
  362. // ACPI_LOCK_OWNED bit.
  363. //
  364. // Finally, update the new value atomically, and repeat the whole
  365. // process if someone else changed it under us.
  366. //
  367. oldLockValue = lockValue;
  368. lockValue |= ACPI_LOCK_OWNED |
  369. ((lockValue & ACPI_LOCK_OWNED) >>
  370. (ACPI_LOCK_OWNED_BIT - ACPI_LOCK_PENDING_BIT));
  371. lockValue = InterlockedCompareExchange(GlobalLock,
  372. lockValue,
  373. oldLockValue);
  374. } while (lockValue != oldLockValue);
  375. //
  376. // If the lock owned bit was previously clear then we are the owner.
  377. //
  378. owned = ((lockValue & ACPI_LOCK_OWNED) == 0);
  379. return owned;
  380. }
  381. void
  382. ACPIReleaseHardwareGlobalLock(
  383. void
  384. )
  385. /*++
  386. Routine Description:
  387. Release the hardware global lock. If the BIOS is waiting for the lock (indicated by the
  388. pending bit), then set GBL_RLS to signal the BIOS.
  389. Arguments:
  390. NONE
  391. Return Value:
  392. NONE
  393. --*/
  394. {
  395. ULONG lockValue;
  396. ULONG oldLockValue;
  397. ULONG volatile *globalLock;
  398. globalLock = (ULONG volatile *)AcpiInformation->GlobalLock;
  399. lockValue = *globalLock;
  400. //
  401. // don't bother with the lock if we are an ACPI only system.
  402. //
  403. if (AcpiInformation->ACPIOnly) {
  404. return;
  405. }
  406. do {
  407. ASSERT((lockValue & ACPI_LOCK_OWNED) != 0);
  408. oldLockValue = lockValue;
  409. //
  410. // Clear the owned and pending bits in the lock, and atomically set
  411. // the new value. If the cmpxchg fails, go around again.
  412. //
  413. lockValue &= ~(ACPI_LOCK_OWNED | ACPI_LOCK_PENDING);
  414. lockValue = InterlockedCompareExchange(globalLock,
  415. lockValue,
  416. oldLockValue);
  417. } while (lockValue != oldLockValue);
  418. if ((lockValue & ACPI_LOCK_PENDING) != 0) {
  419. //
  420. // Signal to bios that the Lock has been released
  421. // Set GBL_RLS
  422. //
  423. WRITE_PM1_CONTROL( (USHORT)PM1_GBL_RLS, FALSE, WRITE_REGISTER_A_AND_B);
  424. }
  425. }