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.

738 lines
15 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. pmsleep.c
  5. Abstract:
  6. This file provides the code that changes the system from
  7. the ACPI S0 (running) state to any one of the sleep states.
  8. Author:
  9. Jake Oshins (jakeo) Feb. 11, 1997
  10. Revision History:
  11. --*/
  12. #include "halp.h"
  13. #include "acpitabl.h"
  14. #include "xxacpi.h"
  15. #include "kddll.h"
  16. #include "ixsleep.h"
  17. //
  18. // Internal functions
  19. //
  20. NTSTATUS
  21. HalpAcpiSleep(
  22. IN PVOID Context,
  23. IN LONG NumberProcessors,
  24. IN volatile PLONG Number
  25. );
  26. VOID
  27. HalpSetClockBeforeSleep(
  28. VOID
  29. );
  30. VOID
  31. HalpSetClockAfterSleep(
  32. VOID
  33. );
  34. BOOLEAN
  35. HalpWakeupTimeElapsed(
  36. VOID
  37. );
  38. VOID
  39. HalpFreeTiledCR3 (
  40. VOID
  41. );
  42. VOID
  43. HalpReenableAcpi(
  44. VOID
  45. );
  46. VOID
  47. HalpPiix4Detect(
  48. BOOLEAN DuringBoot
  49. );
  50. typedef struct _ERESOURCE {
  51. LIST_ENTRY SystemResourcesList;
  52. PVOID OwnerTable;
  53. SHORT ActiveCount;
  54. USHORT Flag;
  55. PKSEMAPHORE SharedWaiters;
  56. PKEVENT ExclusiveWaiters;
  57. LIST_ENTRY OwnerThreads[2];
  58. ULONG ContentionCount;
  59. USHORT NumberOfSharedWaiters;
  60. USHORT NumberOfExclusiveWaiters;
  61. union {
  62. PVOID Address;
  63. ULONG CreatorBackTraceIndex;
  64. };
  65. KSPIN_LOCK SpinLock;
  66. } ERESOURCE, *PERESOURCE;
  67. #ifdef ALLOC_PRAGMA
  68. #pragma alloc_text(PAGELK, HalpAcpiPreSleep)
  69. #pragma alloc_text(PAGELK, HalpAcpiPostSleep)
  70. #pragma alloc_text(PAGELK, HalpWakeupTimeElapsed)
  71. #pragma alloc_text(PAGELK, HalpReenableAcpi)
  72. #pragma alloc_text(PAGELK, HaliSetWakeEnable)
  73. #pragma alloc_text(PAGELK, HaliSetWakeAlarm)
  74. #pragma alloc_text(PAGELK, HalpMapNvsArea)
  75. #pragma alloc_text(PAGELK, HalpFreeNvsBuffers)
  76. #endif
  77. HAL_WAKEUP_STATE HalpWakeupState;
  78. #if DBG
  79. BOOLEAN HalpFailSleep = FALSE;
  80. #endif
  81. #define PM1_TMR_EN 0x0001
  82. #define PM1_RTC_EN 0x0400
  83. //
  84. // For re-enabling the debugger's com port.
  85. //
  86. extern PUCHAR KdComPortInUse;
  87. extern PACPI_BIOS_MULTI_NODE HalpAcpiMultiNode;
  88. extern PUCHAR HalpAcpiNvsData;
  89. extern PVOID *HalpNvsVirtualAddress;
  90. BOOLEAN
  91. HalpAcpiPreSleep(
  92. SLEEP_STATE_CONTEXT Context
  93. )
  94. /*++
  95. Routine Description:
  96. Arguments:
  97. none
  98. Return Value:
  99. status
  100. --*/
  101. {
  102. USHORT pmTimer;
  103. PUSHORT pm1a;
  104. PUSHORT pm1b;
  105. PUSHORT pm1astatus;
  106. PUSHORT pm1bstatus;
  107. BOOLEAN wakeupElapsed;
  108. pm1astatus = (PUSHORT) HalpFixedAcpiDescTable.pm1a_evt_blk_io_port;
  109. pm1bstatus = (PUSHORT) HalpFixedAcpiDescTable.pm1b_evt_blk_io_port;
  110. pm1a = (PUSHORT)(HalpFixedAcpiDescTable.pm1a_evt_blk_io_port +
  111. (HalpFixedAcpiDescTable.pm1_evt_len / 2));
  112. pm1b = (PUSHORT)(HalpFixedAcpiDescTable.pm1b_evt_blk_io_port +
  113. (HalpFixedAcpiDescTable.pm1_evt_len / 2));
  114. HalpSleepContext.AsULONG = Context.AsULONG;
  115. #if DBG
  116. if (HalpFailSleep) {
  117. return FALSE;
  118. }
  119. #endif
  120. //
  121. // If we should have woken up already, don't sleep.
  122. //
  123. wakeupElapsed = HalpWakeupTimeElapsed();
  124. //
  125. // If an RTC alarm is set, then enable it and disable
  126. // periodic interrupts (for profiling.)
  127. //
  128. if (!wakeupElapsed) {
  129. HalpSetClockBeforeSleep();
  130. }
  131. //
  132. // Save the (A)PIC for any sleep state, as we need to play
  133. // with it on the way back up again.
  134. //
  135. HalpSaveInterruptControllerState();
  136. if (Context.bits.Flags & SLEEP_STATE_SAVE_MOTHERBOARD) {
  137. HalpSaveDmaControllerState();
  138. HalpSaveTimerState();
  139. }
  140. //
  141. // We need to make sure that the PM Timer is disabled from this
  142. // point onward. We also need to make that the RTC Enable is only
  143. // enabled if the RTC should wake up the computer
  144. //
  145. pmTimer = READ_PORT_USHORT(pm1a);
  146. if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
  147. pmTimer |= READ_PORT_USHORT(pm1b);
  148. }
  149. //
  150. // Clear the timer enable bit.
  151. //
  152. pmTimer &= ~PM1_TMR_EN;
  153. //
  154. // Check to see if we the machine supports RTC Wake in Fixed Feature
  155. // space. Some machines implement RTC support via control methods
  156. //
  157. if ( !(HalpFixedAcpiDescTable.flags & RTC_WAKE_GENERIC) ) {
  158. //
  159. // Check to see if we need to disable/enable the RTC alarm
  160. //
  161. if (!HalpWakeupState.RtcWakeupEnable) {
  162. pmTimer &= ~PM1_RTC_EN;
  163. } else {
  164. pmTimer |= PM1_RTC_EN;
  165. }
  166. }
  167. //
  168. // Write it back into the hardware.
  169. //
  170. WRITE_PORT_USHORT(pm1a, pmTimer);
  171. if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
  172. WRITE_PORT_USHORT(pm1b, pmTimer);
  173. }
  174. //
  175. // At this point, we should be running with interrupts disabled and
  176. // the TMR_EN bit cleared. This is a good place to clear the PM1 Status
  177. // Register
  178. //
  179. pmTimer = READ_PORT_USHORT( pm1astatus );
  180. if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
  181. pmTimer |= READ_PORT_USHORT( pm1bstatus );
  182. }
  183. WRITE_PORT_USHORT( pm1astatus, pmTimer );
  184. if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
  185. WRITE_PORT_USHORT( pm1bstatus, pmTimer );
  186. }
  187. //
  188. // Check to see if we need to disable all wakeup events.
  189. //
  190. if (!HalpWakeupState.GeneralWakeupEnable) {
  191. AcpiEnableDisableGPEvents(FALSE);
  192. } else {
  193. //
  194. // Only call this before going to sleep --- waking up should
  195. // reset the GPEs to the 'proper' value
  196. //
  197. AcpiGpeEnableWakeEvents();
  198. }
  199. HalpPreserveNvsArea();
  200. if (wakeupElapsed) {
  201. return FALSE;
  202. } else {
  203. return TRUE;
  204. }
  205. }
  206. BOOLEAN
  207. HalpAcpiPostSleep(
  208. ULONG Context
  209. )
  210. {
  211. USHORT pmTimer;
  212. PUSHORT pm1a;
  213. PUSHORT pm1b;
  214. BOOLEAN ProfileInterruptEnabled;
  215. #ifdef PICACPI
  216. extern ULONG HalpProfilingStopped;
  217. ProfileInterruptEnabled = (HalpProfilingStopped == 0);
  218. #else
  219. extern ULONG HalpProfileRunning;
  220. ProfileInterruptEnabled = (HalpProfileRunning == 1);
  221. #endif
  222. pm1a = (PUSHORT)(HalpFixedAcpiDescTable.pm1a_evt_blk_io_port +
  223. (HalpFixedAcpiDescTable.pm1_evt_len / 2));
  224. pm1b = (PUSHORT)(HalpFixedAcpiDescTable.pm1b_evt_blk_io_port +
  225. (HalpFixedAcpiDescTable.pm1_evt_len / 2));
  226. //
  227. // Read the currently set PM1 Enable bits
  228. //
  229. pmTimer = READ_PORT_USHORT(pm1a);
  230. if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
  231. pmTimer |= READ_PORT_USHORT(pm1b);
  232. }
  233. //
  234. // Set the timer enable bit. Clear the RTC enable bit
  235. //
  236. pmTimer |= PM1_TMR_EN;
  237. pmTimer &= ~PM1_RTC_EN;
  238. //
  239. // Write back the new PM1 Enable bits
  240. //
  241. WRITE_PORT_USHORT(pm1a, pmTimer);
  242. if (HalpFixedAcpiDescTable.pm1b_evt_blk_io_port) {
  243. WRITE_PORT_USHORT(pm1b, pmTimer);
  244. }
  245. //
  246. // Unset the RTC alarm and re-enable periodic interrupts.
  247. //
  248. HalpSetClockAfterSleep();
  249. HalpWakeupState.RtcWakeupEnable = FALSE;
  250. *((PULONG)HalpWakeVector) = 0;
  251. HalpSetInterruptControllerWakeupState(Context);
  252. if (HalpSleepContext.bits.Flags & SLEEP_STATE_SAVE_MOTHERBOARD) {
  253. //
  254. // If Kd was in use, then invalidate it. It will re-sync itself.
  255. //
  256. if (KdComPortInUse) {
  257. KdRestore(TRUE);
  258. }
  259. HalpRestoreDmaControllerState();
  260. HalpRestoreTimerState();
  261. }
  262. HalpPiix4Detect(FALSE);
  263. //
  264. // Enable all GPEs, not just the wake ones
  265. //
  266. AcpiEnableDisableGPEvents(TRUE);
  267. HalpRestoreNvsArea();
  268. HalpResetSBF();
  269. //
  270. // If we were profiling before, fire up the profile interrupt
  271. //
  272. if (ProfileInterruptEnabled) {
  273. HalStartProfileInterrupt(0);
  274. }
  275. return TRUE;
  276. }
  277. BOOLEAN
  278. HalpWakeupTimeElapsed(
  279. VOID
  280. )
  281. {
  282. LARGE_INTEGER wakeupTime, currentTime;
  283. TIME_FIELDS currentTimeFields;
  284. //
  285. // Check to see if a wakeup timer has already expired.
  286. //
  287. if (HalpWakeupState.RtcWakeupEnable) {
  288. HalQueryRealTimeClock(&currentTimeFields);
  289. RtlTimeFieldsToTime(&currentTimeFields,
  290. &currentTime);
  291. RtlTimeFieldsToTime(&HalpWakeupState.RtcWakeupTime,
  292. &wakeupTime);
  293. if ((ULONGLONG)wakeupTime.QuadPart <
  294. (ULONGLONG)currentTime.QuadPart) {
  295. return TRUE;
  296. }
  297. }
  298. return FALSE;
  299. }
  300. NTSTATUS
  301. HaliSetWakeAlarm (
  302. IN ULONGLONG WakeSystemTime,
  303. IN PTIME_FIELDS WakeTimeFields OPTIONAL
  304. )
  305. /*++
  306. Routine Description:
  307. This routine sets the real-time clock's alarm to go
  308. off at a specified time in the future and programs
  309. the ACPI chipset so that this wakes the computer.
  310. Arguments:
  311. WakeSystemTime - amount of time that passes before we wake
  312. WakeTimeFields - time to wake broken down into TIME_FIELDS
  313. Return Value:
  314. status
  315. --*/
  316. {
  317. if (WakeSystemTime == 0) {
  318. HalpWakeupState.RtcWakeupEnable = FALSE;
  319. return STATUS_SUCCESS;
  320. }
  321. ASSERT( WakeTimeFields );
  322. HalpWakeupState.RtcWakeupEnable = TRUE;
  323. HalpWakeupState.RtcWakeupTime = *WakeTimeFields;
  324. return HalpSetWakeAlarm(WakeSystemTime,
  325. WakeTimeFields);
  326. }
  327. VOID
  328. HaliSetWakeEnable(
  329. IN BOOLEAN Enable
  330. )
  331. /*++
  332. Routine Description:
  333. This routine is called to set the policy for waking up.
  334. As we go to sleep, the global HalpWakeupState will be
  335. read and the hardware set accordingly.
  336. Arguments:
  337. Enable - true or false
  338. Return Value:
  339. --*/
  340. {
  341. //
  342. // Always clear the RTC wake --- we expect that someone will
  343. // set the alarm after they call this function
  344. //
  345. HalpWakeupState.RtcWakeupEnable = FALSE;
  346. //
  347. // Toggle the generate wake up bit
  348. //
  349. HalpWakeupState.GeneralWakeupEnable = Enable;
  350. }
  351. VOID
  352. HalpReenableAcpi(
  353. VOID
  354. )
  355. /*++
  356. Routine Description:
  357. This calls into the ACPI driver to switch back into ACPI mode,
  358. presumably after S4 and sets the ACPI registers that the HAL
  359. controls.
  360. Arguments:
  361. Return Value:
  362. --*/
  363. {
  364. // TEMPTEMP?
  365. HalpInitializeClock();
  366. AcpiInitEnableAcpi(TRUE);
  367. AcpiEnableDisableGPEvents(TRUE);
  368. }
  369. VOID
  370. HalpMapNvsArea(
  371. VOID
  372. )
  373. {
  374. NTSTATUS status;
  375. ULONG i, bufferSize, bufferOffset, nodeCount;
  376. PAGED_CODE();
  377. status = HalpAcpiFindRsdt(&HalpAcpiMultiNode);
  378. if (!NT_SUCCESS(status)) {
  379. return;
  380. }
  381. if (HalpAcpiMultiNode->Count == 0) {
  382. //
  383. // There's no work to do here.
  384. //
  385. goto HalpMapNvsError;
  386. }
  387. //
  388. // Find total size of the buffer we need.
  389. //
  390. bufferSize = 0;
  391. nodeCount = 0;
  392. for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
  393. if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
  394. ASSERT(HalpAcpiMultiNode->E820Entry[i].Length.HighPart == 0);
  395. bufferSize += HalpAcpiMultiNode->E820Entry[i].Length.LowPart;
  396. nodeCount++;
  397. }
  398. }
  399. if (bufferSize == 0) {
  400. //
  401. // There's no work to do here.
  402. //
  403. goto HalpMapNvsError;
  404. }
  405. #if DBG
  406. if (bufferSize > (20 * PAGE_SIZE)) {
  407. DbgPrint("HALACPI: The BIOS wants the OS to preserve %x bytes\n", bufferSize);
  408. }
  409. #endif
  410. HalpAcpiNvsData = ExAllocatePoolWithTag(NonPagedPool,
  411. bufferSize,
  412. 'AlaH');
  413. if (!HalpAcpiNvsData) {
  414. DbgPrint("HALACPI: The BIOS's non-volatile data will not be preserved\n");
  415. goto HalpMapNvsError;
  416. }
  417. HalpNvsVirtualAddress = ExAllocatePoolWithTag(NonPagedPool,
  418. (nodeCount + 1) * sizeof(PVOID),
  419. 'AlaH');
  420. if (!HalpNvsVirtualAddress) {
  421. goto HalpMapNvsError;
  422. }
  423. //
  424. // Make a mapping for each run.
  425. //
  426. bufferOffset = 0;
  427. nodeCount = 0;
  428. for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
  429. if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
  430. HalpNvsVirtualAddress[nodeCount] =
  431. MmMapIoSpace(HalpAcpiMultiNode->E820Entry[i].Base,
  432. HalpAcpiMultiNode->E820Entry[i].Length.LowPart,
  433. TRUE);
  434. ASSERT(HalpNvsVirtualAddress[nodeCount]);
  435. nodeCount++;
  436. }
  437. }
  438. //
  439. // Mark the end.
  440. //
  441. HalpNvsVirtualAddress[nodeCount] = NULL;
  442. return;
  443. HalpMapNvsError:
  444. if (HalpAcpiMultiNode) ExFreePool(HalpAcpiMultiNode);
  445. if (HalpNvsVirtualAddress) ExFreePool(HalpNvsVirtualAddress);
  446. if (HalpAcpiNvsData) ExFreePool(HalpAcpiNvsData);
  447. HalpAcpiMultiNode = NULL;
  448. return;
  449. }
  450. VOID
  451. HalpPreserveNvsArea(
  452. VOID
  453. )
  454. {
  455. ULONG i, dataOffset = 0, nodeCount = 0;
  456. if (!HalpAcpiMultiNode) {
  457. //
  458. // Either there was nothing to save or there
  459. // was a fatal error.
  460. //
  461. return;
  462. }
  463. for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
  464. if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
  465. //
  466. // Copy from BIOS memory to temporary buffer.
  467. //
  468. RtlCopyMemory(HalpAcpiNvsData + dataOffset,
  469. HalpNvsVirtualAddress[nodeCount],
  470. HalpAcpiMultiNode->E820Entry[i].Length.LowPart);
  471. nodeCount++;
  472. dataOffset += HalpAcpiMultiNode->E820Entry[i].Length.LowPart;
  473. }
  474. }
  475. }
  476. VOID
  477. HalpRestoreNvsArea(
  478. VOID
  479. )
  480. {
  481. ULONG i, dataOffset = 0, nodeCount = 0;
  482. if (!HalpAcpiMultiNode) {
  483. //
  484. // Either there was nothing to save or there
  485. // was a fatal error.
  486. //
  487. return;
  488. }
  489. for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
  490. if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
  491. //
  492. // Copy from temporary buffer to BIOS area.
  493. //
  494. RtlCopyMemory(HalpNvsVirtualAddress[nodeCount],
  495. HalpAcpiNvsData + dataOffset,
  496. HalpAcpiMultiNode->E820Entry[i].Length.LowPart);
  497. nodeCount++;
  498. dataOffset += HalpAcpiMultiNode->E820Entry[i].Length.LowPart;
  499. }
  500. }
  501. }
  502. VOID
  503. HalpFreeNvsBuffers(
  504. VOID
  505. )
  506. {
  507. ULONG i, nodeCount = 0;
  508. PAGED_CODE();
  509. if (!HalpAcpiMultiNode) {
  510. //
  511. // Either there was nothing to save or there
  512. // was a fatal error.
  513. //
  514. return;
  515. }
  516. for (i = 0; i < HalpAcpiMultiNode->Count; i++) {
  517. if (HalpAcpiMultiNode->E820Entry[i].Type == AcpiAddressRangeNVS) {
  518. //
  519. // Give back all the PTEs that we took earlier
  520. //
  521. MmUnmapIoSpace(HalpNvsVirtualAddress[nodeCount],
  522. HalpAcpiMultiNode->E820Entry[i].Length.LowPart);
  523. nodeCount++;
  524. }
  525. }
  526. ASSERT(HalpAcpiMultiNode);
  527. ASSERT(HalpNvsVirtualAddress);
  528. ASSERT(HalpAcpiNvsData);
  529. ExFreePool(HalpAcpiMultiNode);
  530. ExFreePool(HalpNvsVirtualAddress);
  531. ExFreePool(HalpAcpiNvsData);
  532. HalpAcpiMultiNode = NULL;
  533. HalpNvsVirtualAddress = NULL;
  534. HalpAcpiNvsData = NULL;
  535. }