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.

641 lines
18 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. pnppower.c
  5. Abstract:
  6. This file contains the routines that integrate PnP and Power
  7. Author:
  8. Adrian J. Oney (AdriaO) 01-19-1999
  9. Revision History:
  10. Modified for nt kernel.
  11. --*/
  12. #include "pnpmgrp.h"
  13. //
  14. // Internal References
  15. //
  16. PWCHAR
  17. IopCaptureObjectName (
  18. IN PVOID Object
  19. );
  20. VOID
  21. IopFreePoDeviceNotifyListHead (
  22. PLIST_ENTRY ListHead
  23. );
  24. #ifdef ALLOC_PRAGMA
  25. #pragma alloc_text(PAGE, IopWarmEjectDevice)
  26. #pragma alloc_text(PAGELK, IoBuildPoDeviceNotifyList)
  27. #pragma alloc_text(PAGELK, IoFreePoDeviceNotifyList)
  28. #pragma alloc_text(PAGELK, IopFreePoDeviceNotifyListHead)
  29. #pragma alloc_text(PAGELK, IoGetPoNotifyParent)
  30. #pragma alloc_text(PAGELK, IoMovePoNotifyChildren)
  31. #pragma alloc_text(PAGELK, IopCaptureObjectName)
  32. #endif
  33. NTSTATUS
  34. IoBuildPoDeviceNotifyList (
  35. IN OUT PPO_DEVICE_NOTIFY_ORDER Order
  36. )
  37. {
  38. PLIST_ENTRY link;
  39. PPO_DEVICE_NOTIFY notify, parentnotify;
  40. PDEVICE_NODE node;
  41. PDEVICE_NODE parent;
  42. LONG maxLevel, level;
  43. UCHAR orderLevel;
  44. LIST_ENTRY RebaseList;
  45. ULONG i;
  46. //
  47. // Block PnP operations like rebalance.
  48. //
  49. PiLockDeviceActionQueue();
  50. RtlZeroMemory(Order, sizeof (*Order));
  51. //
  52. // This is used as a token to remember that we have locked DeviceActionQueue
  53. //
  54. Order->DevNodeSequence = IoDeviceNodeTreeSequence;
  55. for (i=0; i <= PO_ORDER_MAXIMUM; i++) {
  56. KeInitializeEvent(&Order->OrderLevel[i].LevelReady,
  57. NotificationEvent,
  58. FALSE);
  59. InitializeListHead(&Order->OrderLevel[i].WaitSleep);
  60. InitializeListHead(&Order->OrderLevel[i].ReadySleep);
  61. InitializeListHead(&Order->OrderLevel[i].Pending);
  62. InitializeListHead(&Order->OrderLevel[i].Complete);
  63. InitializeListHead(&Order->OrderLevel[i].ReadyS0);
  64. InitializeListHead(&Order->OrderLevel[i].WaitS0);
  65. }
  66. InitializeListHead(&RebaseList);
  67. //
  68. // Allocate notification structures for all nodes, and determine
  69. // maximum depth.
  70. //
  71. level = -1;
  72. node = IopRootDeviceNode;
  73. while (node->Child) {
  74. node = node->Child;
  75. level += 1;
  76. }
  77. //
  78. // ADRIAO 01/12/1999 N.B. -
  79. //
  80. // Note that we include devices without the started flag. However, two
  81. // things prevent us from excluding devices that aren't started:
  82. // 1) We must be able to send power messages to a device we are warm
  83. // undocking.
  84. // 2) Many devices may not be started, that is no guarentee they are in D3!
  85. // For example, they could easily have a boot config, and PNP still
  86. // relies heavily on BIOS boot configs to keep us from placing hardware
  87. // ontop of other devices with boot configs we haven't found or started
  88. // yet!
  89. //
  90. maxLevel = level;
  91. while (node != IopRootDeviceNode) {
  92. notify = ExAllocatePoolWithTag (
  93. NonPagedPool,
  94. sizeof(PO_DEVICE_NOTIFY),
  95. IOP_DPWR_TAG
  96. );
  97. if (!notify) {
  98. //
  99. // Reset the DevNodeSequence since we have failed and returning with the DeviceActionQueue unlocked.
  100. //
  101. Order->DevNodeSequence = 0;
  102. PiUnlockDeviceActionQueue();
  103. return STATUS_INSUFFICIENT_RESOURCES;
  104. }
  105. RtlZeroMemory (notify, sizeof(PO_DEVICE_NOTIFY));
  106. ASSERT(node->Notify == NULL) ;
  107. node->Notify = notify;
  108. notify->Node = node;
  109. notify->DeviceObject = node->PhysicalDeviceObject;
  110. notify->TargetDevice = IoGetAttachedDevice(node->PhysicalDeviceObject);
  111. notify->DriverName = IopCaptureObjectName(notify->TargetDevice->DriverObject);
  112. notify->DeviceName = IopCaptureObjectName(notify->TargetDevice);
  113. ObReferenceObject (notify->DeviceObject);
  114. ObReferenceObject (notify->TargetDevice);
  115. orderLevel = 0;
  116. if (notify->TargetDevice->DeviceType != FILE_DEVICE_SCREEN &&
  117. notify->TargetDevice->DeviceType != FILE_DEVICE_VIDEO) {
  118. orderLevel |= PO_ORDER_NOT_VIDEO;
  119. }
  120. if (notify->TargetDevice->Flags & DO_POWER_PAGABLE) {
  121. orderLevel |= PO_ORDER_PAGABLE;
  122. }
  123. //
  124. // If this is a level 0 node it's in the root. Look for
  125. // non-bus stuff in the root as those guys need to be re-based
  126. // below everything else.
  127. //
  128. notify->OrderLevel = orderLevel;
  129. //
  130. // If the node is root-enumerated, put it on the rebase list so
  131. // we can mark all its children later.
  132. // If the node is a leaf node it is ready to receive Sx irps.
  133. // If it has children, it must wait for its children to complete their Sx irps.
  134. //
  135. //
  136. if ((level == 0) &&
  137. (node->InterfaceType != Internal) &&
  138. !(node->Flags & DNF_HAL_NODE)) {
  139. InsertHeadList(&RebaseList, &notify->Link);
  140. } else {
  141. ++Order->OrderLevel[orderLevel].DeviceCount;
  142. if (node->Child == NULL) {
  143. InsertHeadList(&Order->OrderLevel[orderLevel].ReadySleep, &notify->Link);
  144. } else {
  145. InsertHeadList(&Order->OrderLevel[orderLevel].WaitSleep, &notify->Link);
  146. }
  147. }
  148. //
  149. // Next node
  150. //
  151. if (node->Sibling) {
  152. node = node->Sibling;
  153. while (node->Child) {
  154. node = node->Child;
  155. level += 1;
  156. if (level > maxLevel) {
  157. maxLevel = level;
  158. }
  159. }
  160. } else {
  161. node = node->Parent;
  162. level -= 1;
  163. }
  164. }
  165. //
  166. // Rebase anything on the rebase list to be after the normal pnp stuff
  167. //
  168. while (!IsListEmpty(&RebaseList)) {
  169. link = RemoveHeadList(&RebaseList);
  170. notify = CONTAINING_RECORD (link, PO_DEVICE_NOTIFY, Link);
  171. //
  172. // Rebase this node
  173. //
  174. node = notify->Node;
  175. notify->OrderLevel |= PO_ORDER_ROOT_ENUM;
  176. ++Order->OrderLevel[notify->OrderLevel].DeviceCount;
  177. if (node->Child == NULL) {
  178. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].ReadySleep, &notify->Link);
  179. } else {
  180. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].WaitSleep, &notify->Link);
  181. }
  182. //
  183. // Now rebase all the node's children
  184. //
  185. parent = node;
  186. while (node->Child) {
  187. node = node->Child;
  188. }
  189. while (node != parent) {
  190. notify = node->Notify;
  191. if (notify) {
  192. RemoveEntryList(&notify->Link);
  193. --Order->OrderLevel[notify->OrderLevel].DeviceCount;
  194. notify->OrderLevel |= PO_ORDER_ROOT_ENUM;
  195. ++Order->OrderLevel[notify->OrderLevel].DeviceCount;
  196. if (node->Child == NULL) {
  197. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].ReadySleep, &notify->Link);
  198. } else {
  199. InsertHeadList(&Order->OrderLevel[notify->OrderLevel].WaitSleep, &notify->Link);
  200. }
  201. }
  202. // next node
  203. if (node->Sibling) {
  204. node = node->Sibling;
  205. while (node->Child) {
  206. node = node->Child;
  207. }
  208. } else {
  209. node = node->Parent;
  210. }
  211. }
  212. }
  213. //
  214. // make one more pass through all the notify devices in order to count
  215. // the children of each parent. It would be nice if the PNP engine kept
  216. // track of the number of children in the devnode, but until that is done,
  217. // we need this second pass.
  218. //
  219. // Also make sure that each node's parent is an order level >= its children.
  220. //
  221. node = IopRootDeviceNode;
  222. while (node->Child) {
  223. node = node->Child;
  224. }
  225. while (node != IopRootDeviceNode) {
  226. if (node->Parent != IopRootDeviceNode) {
  227. parentnotify = node->Parent->Notify;
  228. parentnotify->ChildCount++;
  229. parentnotify->ActiveChild++;
  230. if (parentnotify->OrderLevel > node->Notify->OrderLevel) {
  231. //
  232. // The parent is a higher order level than its child. Move the
  233. // parent down to the same order as its child
  234. //
  235. RemoveEntryList(&parentnotify->Link);
  236. --Order->OrderLevel[parentnotify->OrderLevel].DeviceCount;
  237. parentnotify->OrderLevel = node->Notify->OrderLevel;
  238. ++Order->OrderLevel[parentnotify->OrderLevel].DeviceCount;
  239. InsertHeadList(&Order->OrderLevel[parentnotify->OrderLevel].WaitSleep, &parentnotify->Link);
  240. }
  241. }
  242. //
  243. // Next node
  244. //
  245. if (node->Sibling) {
  246. node = node->Sibling;
  247. while (node->Child) {
  248. node = node->Child;
  249. }
  250. } else {
  251. node = node->Parent;
  252. }
  253. }
  254. Order->WarmEjectPdoPointer = &IopWarmEjectPdo;
  255. //
  256. // The engine lock is release when the notify list is freed
  257. //
  258. return STATUS_SUCCESS;
  259. }
  260. PVOID
  261. IoGetPoNotifyParent(
  262. IN PPO_DEVICE_NOTIFY Notify
  263. )
  264. /*++
  265. Routine Description:
  266. Returns the notify structure of the specified device's parent.
  267. Arguments:
  268. Notify - Supplies the child device
  269. Return Value:
  270. Parent's notify structure if present
  271. NULL if there is no parent
  272. --*/
  273. {
  274. PDEVICE_NODE Node;
  275. Node = Notify->Node;
  276. if (Node->Parent != IopRootDeviceNode) {
  277. return(Node->Parent->Notify);
  278. } else {
  279. return(NULL);
  280. }
  281. }
  282. VOID
  283. IoMovePoNotifyChildren(
  284. IN PPO_DEVICE_NOTIFY Notify,
  285. IN PPO_DEVICE_NOTIFY_ORDER Order
  286. )
  287. /*++
  288. Routine Description:
  289. Removes any children of the supplied device that are at the
  290. same orderlevel as the supplied parent and reinserts them
  291. on the ReadyS0 list.
  292. Arguments:
  293. Notify - Supplies the device notify structure
  294. Orderr - Supplies the device notification order structure
  295. Return Value:
  296. None
  297. --*/
  298. {
  299. PDEVICE_NODE Node;
  300. PDEVICE_NODE Child;
  301. PPO_DEVICE_NOTIFY ChildNotify;
  302. PPO_NOTIFY_ORDER_LEVEL Level;
  303. Node = Notify->Node;
  304. Child = Node->Child;
  305. while (Child) {
  306. ChildNotify = Child->Notify;
  307. if (ChildNotify->OrderLevel == Notify->OrderLevel) {
  308. RemoveEntryList(&ChildNotify->Link);
  309. Level = &Order->OrderLevel[ChildNotify->OrderLevel];
  310. InsertTailList(&Level->ReadyS0, &ChildNotify->Link);
  311. }
  312. Child = Child->Sibling;
  313. }
  314. }
  315. VOID
  316. IopFreePoDeviceNotifyListHead (
  317. PLIST_ENTRY ListHead
  318. )
  319. {
  320. PLIST_ENTRY Link;
  321. PPO_DEVICE_NOTIFY Notify;
  322. PDEVICE_NODE Node;
  323. while (!IsListEmpty(ListHead)) {
  324. Link = RemoveHeadList(ListHead);
  325. Notify = CONTAINING_RECORD (Link, PO_DEVICE_NOTIFY, Link);
  326. Node = (PDEVICE_NODE) Notify->Node;
  327. Node->Notify = NULL;
  328. ObDereferenceObject (Notify->DeviceObject);
  329. ObDereferenceObject (Notify->TargetDevice);
  330. if (Notify->DeviceName) {
  331. ExFreePool (Notify->DeviceName);
  332. }
  333. if (Notify->DriverName) {
  334. ExFreePool (Notify->DriverName);
  335. }
  336. ExFreePool(Notify);
  337. }
  338. }
  339. VOID
  340. IoFreePoDeviceNotifyList (
  341. IN OUT PPO_DEVICE_NOTIFY_ORDER Order
  342. )
  343. {
  344. ULONG i;
  345. if (Order->DevNodeSequence) {
  346. Order->DevNodeSequence = 0;
  347. PiUnlockDeviceActionQueue();
  348. }
  349. //
  350. // Free the resources from the notify list
  351. //
  352. for (i=0; i <= PO_ORDER_MAXIMUM; i++) {
  353. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].WaitSleep);
  354. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].ReadySleep);
  355. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].Pending);
  356. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].Complete);
  357. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].ReadyS0);
  358. IopFreePoDeviceNotifyListHead(&Order->OrderLevel[i].WaitS0);
  359. }
  360. }
  361. PWCHAR
  362. IopCaptureObjectName (
  363. IN PVOID Object
  364. )
  365. {
  366. NTSTATUS Status;
  367. UCHAR Buffer[512];
  368. POBJECT_NAME_INFORMATION ObName;
  369. ULONG len;
  370. PWCHAR Name;
  371. ObName = (POBJECT_NAME_INFORMATION) Buffer;
  372. Status = ObQueryNameString (
  373. Object,
  374. ObName,
  375. sizeof (Buffer),
  376. &len
  377. );
  378. Name = NULL;
  379. if (NT_SUCCESS(Status) && ObName->Name.Buffer) {
  380. Name = ExAllocatePoolWithTag (
  381. NonPagedPool,
  382. ObName->Name.Length + sizeof(WCHAR),
  383. IOP_DPWR_TAG
  384. );
  385. if (Name) {
  386. RtlCopyMemory(Name, ObName->Name.Buffer, ObName->Name.Length);
  387. Name[ObName->Name.Length/sizeof(WCHAR)] = UNICODE_NULL;
  388. }
  389. }
  390. return Name;
  391. }
  392. NTSTATUS
  393. IopWarmEjectDevice(
  394. IN PDEVICE_OBJECT DeviceToEject,
  395. IN SYSTEM_POWER_STATE LightestSleepState
  396. )
  397. /*++
  398. Routine Description:
  399. This function is invoked to initiate a warm eject. The eject progresses
  400. from S1 to the passed in lightest sleep state.
  401. Arguments:
  402. DeviceToEject - The device to eject
  403. LightestSleepState - The lightest S state (at least S1) that the device
  404. may be ejected in. This might be S4 if we are truely
  405. low on power.
  406. Return Value:
  407. NTSTATUS value.
  408. --*/
  409. {
  410. NTSTATUS status;
  411. PAGED_CODE();
  412. //
  413. // Acquire the warm eject device lock. A warm eject requires we enter a
  414. // specific S-state, and two different devices may have conflicting options.
  415. // Therefore only one is allowed to occur at once.
  416. //
  417. // Note that this function is called in the context of a work item, so we
  418. // don't have to worry about suspend attacks.
  419. //
  420. status = KeWaitForSingleObject(
  421. &IopWarmEjectLock,
  422. Executive,
  423. KernelMode,
  424. FALSE,
  425. NULL
  426. );
  427. ASSERT(status == STATUS_SUCCESS) ;
  428. //
  429. // Acquire engine lock. We are not allowed to set or clear this field
  430. // unless we are under this lock.
  431. //
  432. PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
  433. //
  434. // Set the current Pdo to eject.
  435. //
  436. ASSERT(IopWarmEjectPdo == NULL);
  437. IopWarmEjectPdo = DeviceToEject;
  438. //
  439. // Release the engine lock.
  440. //
  441. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  442. //
  443. // Attempt to invalidate Po's cached notification list. This should cause
  444. // IoBuildPoDeviceNotifyList to be called at which time it will in theory
  445. // pickup the above placed warm eject Pdo.
  446. //
  447. // ADRIAO NOTE 01/07/1999 -
  448. // Actually, this whole IoDeviceNodeTreeSequence stuff isn't neccessary.
  449. // PnP will make no changes to the tree while the device tree lock is owned,
  450. // and it's owned for the duration of a power notification.
  451. //
  452. IoDeviceNodeTreeSequence++;
  453. //
  454. // Sleep...
  455. //
  456. // ADRIAO NOTE 2002/03/31 - Note that this is invoked in the system context,
  457. // not winlogon. This means that the invoking user
  458. // will not have his SeShutdownPrivilege checked.
  459. // However, SeLoadUnloadDriver was checked, which
  460. // itself is synonomous with Admin.
  461. //
  462. status = NtInitiatePowerAction(
  463. PowerActionWarmEject,
  464. LightestSleepState,
  465. POWER_ACTION_QUERY_ALLOWED |
  466. POWER_ACTION_UI_ALLOWED,
  467. FALSE // Asynchronous == FALSE
  468. );
  469. //
  470. // Tell someone if we didn't succeed.
  471. //
  472. // Don't throw UI on every failure because some failures
  473. // have been handled already (e.g. a specific device veto'ing
  474. // the action). Therefore, we'll check some specific error codes.
  475. //
  476. if( status == STATUS_PRIVILEGE_NOT_HELD ) {
  477. //
  478. // Intentionally ignore the return code here. We don't
  479. // want to step on our 'status' variable, and besides, there's
  480. // not much we could here on failure anyway.
  481. //
  482. PpSetPowerVetoEvent( PowerActionWarmEject,
  483. NULL,
  484. NULL,
  485. DeviceToEject,
  486. PNP_VetoInsufficientRights,
  487. NULL );
  488. }
  489. //
  490. // Acquire the engine lock. We are not allowed to set or clear this field
  491. // unless we are under this lock.
  492. //
  493. PpDevNodeLockTree(PPL_TREEOP_ALLOW_READS);
  494. //
  495. // Clear the current PDO to eject, and see if the Pdo was actually picked
  496. // up.
  497. //
  498. if (IopWarmEjectPdo) {
  499. if (NT_SUCCESS(status)) {
  500. //
  501. // If our device wasn't picked up, the return of
  502. // NtInitiatePowerAction should *not* be successful!
  503. //
  504. ASSERT(0);
  505. status = STATUS_UNSUCCESSFUL;
  506. }
  507. IopWarmEjectPdo = NULL;
  508. }
  509. //
  510. // Release the engine lock.
  511. //
  512. PpDevNodeUnlockTree(PPL_TREEOP_ALLOW_READS);
  513. //
  514. // Release the warm eject device lock
  515. //
  516. KeSetEvent(
  517. &IopWarmEjectLock,
  518. IO_NO_INCREMENT,
  519. FALSE
  520. );
  521. return status;
  522. }