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.

1326 lines
36 KiB

  1. /*++
  2. Copyright (c) Microsoft Corporation. All rights reserved.
  3. Module Name:
  4. devnode.c
  5. Abstract:
  6. This file contains routines to maintain our private device node list.
  7. Author:
  8. Forrest Foltz (forrestf) 27-Mar-1996
  9. Revision History:
  10. Modified for nt kernel.
  11. --*/
  12. #include "pnpmgrp.h"
  13. //
  14. // Internal definitions
  15. //
  16. typedef struct _ENUM_CONTEXT{
  17. PENUM_CALLBACK CallersCallback;
  18. PVOID CallersContext;
  19. } ENUM_CONTEXT, *PENUM_CONTEXT;
  20. //
  21. // Internal References
  22. //
  23. NTSTATUS
  24. PipForAllDeviceNodesCallback(
  25. IN PDEVICE_NODE DeviceNode,
  26. IN PVOID Context
  27. );
  28. BOOLEAN
  29. PipAreDriversLoadedWorker(
  30. IN PNP_DEVNODE_STATE CurrentNodeState,
  31. IN PNP_DEVNODE_STATE PreviousNodeState
  32. );
  33. #ifdef ALLOC_PRAGMA
  34. #pragma alloc_text(PAGE, PipAreDriversLoaded)
  35. #pragma alloc_text(PAGE, PipAreDriversLoadedWorker)
  36. #pragma alloc_text(PAGE, PipAllocateDeviceNode)
  37. #pragma alloc_text(PAGE, PipForAllDeviceNodes)
  38. #pragma alloc_text(PAGE, PipForDeviceNodeSubtree)
  39. #pragma alloc_text(PAGE, PipForAllChildDeviceNodes)
  40. #pragma alloc_text(PAGE, PipForAllDeviceNodesCallback)
  41. #pragma alloc_text(PAGE, IopDestroyDeviceNode)
  42. #pragma alloc_text(PAGE, PpDevNodeLockTree)
  43. #pragma alloc_text(PAGE, PpDevNodeUnlockTree)
  44. #pragma alloc_text(PAGE, PipIsProblemReadonly)
  45. #pragma alloc_text(PAGE, PipIsDevNodeDNStarted)
  46. #pragma alloc_text(PAGE, PipSetDevNodeProblem)
  47. #pragma alloc_text(PAGE, PipClearDevNodeProblem)
  48. //#pragma alloc_text(NONPAGE, PpDevNodeInsertIntoTree)
  49. //#pragma alloc_text(NONPAGE, PpDevNodeRemoveFromTree)
  50. //#pragma alloc_text(NONPAGE, PipRestoreDevNodeState)
  51. //#pragma alloc_text(NONPAGE, PipSetDevNodeState)
  52. #if DBG
  53. #pragma alloc_text(PAGE, PpDevNodeAssertLockLevel)
  54. #endif // DBG
  55. #endif // ALLOC_PRAGMA
  56. BOOLEAN
  57. PipAreDriversLoaded(
  58. IN PDEVICE_NODE DeviceNode
  59. )
  60. /*++
  61. Routine Description:
  62. This routine determines whether a devnode should be treated as if it has
  63. drivers attached to the PDO's stack (ie it's been added.)
  64. Arguments:
  65. DeviceNode - Device node to examine.
  66. Return Value:
  67. TRUE if drivers are loaded, FALSE otherwise.
  68. --*/
  69. {
  70. PAGED_CODE();
  71. return PipAreDriversLoadedWorker(
  72. DeviceNode->State,
  73. DeviceNode->PreviousState
  74. );
  75. }
  76. BOOLEAN
  77. PipAreDriversLoadedWorker(
  78. IN PNP_DEVNODE_STATE CurrentNodeState,
  79. IN PNP_DEVNODE_STATE PreviousNodeState
  80. )
  81. /*++
  82. Routine Description:
  83. This routine determines whether a devnode should be treated as if it has
  84. drivers attached to the PDO's stack (ie it's been added.)
  85. Arguments:
  86. CurrentNodeState - Current state of device node to examine.
  87. PreviousNodeState - Previous state of device node to examine.
  88. Return Value:
  89. TRUE if drivers are loaded, FALSE otherwise.
  90. --*/
  91. {
  92. PAGED_CODE();
  93. switch(CurrentNodeState) {
  94. case DeviceNodeDriversAdded:
  95. case DeviceNodeResourcesAssigned:
  96. case DeviceNodeStartCompletion:
  97. case DeviceNodeStartPostWork:
  98. case DeviceNodeStarted:
  99. case DeviceNodeQueryStopped:
  100. case DeviceNodeStopped:
  101. case DeviceNodeRestartCompletion:
  102. case DeviceNodeEnumerateCompletion:
  103. case DeviceNodeQueryRemoved:
  104. case DeviceNodeRemovePendingCloses:
  105. case DeviceNodeDeletePendingCloses:
  106. case DeviceNodeAwaitingQueuedRemoval:
  107. return TRUE;
  108. case DeviceNodeAwaitingQueuedDeletion:
  109. return PipAreDriversLoadedWorker(
  110. PreviousNodeState,
  111. DeviceNodeUnspecified
  112. );
  113. case DeviceNodeUninitialized:
  114. case DeviceNodeInitialized:
  115. case DeviceNodeRemoved:
  116. return FALSE;
  117. case DeviceNodeDeleted:
  118. //
  119. // This can be seen by user mode because we defer delinking devices
  120. // from the tree during removal.
  121. //
  122. return FALSE;
  123. case DeviceNodeStartPending:
  124. case DeviceNodeEnumeratePending:
  125. case DeviceNodeUnspecified:
  126. default:
  127. ASSERT(0);
  128. return FALSE;
  129. }
  130. }
  131. BOOLEAN
  132. PipIsDevNodeDNStarted(
  133. IN PDEVICE_NODE DeviceNode
  134. )
  135. /*++
  136. Routine Description:
  137. This routine takes a devnode and determines whether the devnode should
  138. have the user mode DN_STARTED bit set.
  139. Arguments:
  140. DeviceNode - Device node to examine.
  141. Return Value:
  142. TRUE if the devnode should be considered started, FALSE otherwise.
  143. --*/
  144. {
  145. PAGED_CODE();
  146. switch (DeviceNode->State) {
  147. case DeviceNodeStartPending:
  148. case DeviceNodeStartCompletion:
  149. case DeviceNodeStartPostWork:
  150. case DeviceNodeStarted:
  151. case DeviceNodeQueryStopped:
  152. case DeviceNodeEnumeratePending:
  153. case DeviceNodeEnumerateCompletion:
  154. case DeviceNodeStopped:
  155. case DeviceNodeRestartCompletion:
  156. return TRUE;
  157. case DeviceNodeUninitialized:
  158. case DeviceNodeInitialized:
  159. case DeviceNodeDriversAdded:
  160. case DeviceNodeResourcesAssigned:
  161. case DeviceNodeRemoved:
  162. case DeviceNodeQueryRemoved:
  163. case DeviceNodeRemovePendingCloses:
  164. case DeviceNodeDeletePendingCloses:
  165. case DeviceNodeAwaitingQueuedRemoval:
  166. case DeviceNodeAwaitingQueuedDeletion:
  167. return FALSE;
  168. case DeviceNodeDeleted:
  169. //
  170. // This can be seen by user mode because we defer delinking devices
  171. // from the tree during removal.
  172. //
  173. return FALSE;
  174. case DeviceNodeUnspecified:
  175. default:
  176. ASSERT(0);
  177. return FALSE;
  178. }
  179. }
  180. VOID
  181. PipClearDevNodeProblem(
  182. IN PDEVICE_NODE DeviceNode
  183. )
  184. {
  185. PAGED_CODE();
  186. DeviceNode->Flags &= ~DNF_HAS_PROBLEM;
  187. DeviceNode->Problem = 0;
  188. }
  189. VOID
  190. PipSetDevNodeProblem(
  191. IN PDEVICE_NODE DeviceNode,
  192. IN ULONG Problem
  193. )
  194. {
  195. PAGED_CODE();
  196. ASSERT(DeviceNode->State != DeviceNodeUninitialized || !(DeviceNode->Flags & DNF_ENUMERATED) || Problem == CM_PROB_INVALID_DATA);
  197. ASSERT(DeviceNode->State != DeviceNodeStarted);
  198. ASSERT(Problem != 0);
  199. DeviceNode->Flags |= DNF_HAS_PROBLEM; \
  200. DeviceNode->Problem = Problem;
  201. }
  202. VOID
  203. PipSetDevNodeState(
  204. IN PDEVICE_NODE DeviceNode,
  205. IN PNP_DEVNODE_STATE State,
  206. OUT PNP_DEVNODE_STATE *OldState OPTIONAL
  207. )
  208. /*++
  209. Routine Description:
  210. This routine sets a devnodes state and optional returns the prior state.
  211. The prior state is saved and can be restored via PipRestoreDevNodeState.
  212. Arguments:
  213. DeviceNode - Device node to update state.
  214. State - State to place devnode in.
  215. OldState - Optionally receives prior state of devnode.
  216. Return Value:
  217. None.
  218. --*/
  219. {
  220. PNP_DEVNODE_STATE previousState;
  221. KIRQL oldIrql;
  222. ASSERT(State != DeviceNodeQueryStopped || DeviceNode->State == DeviceNodeStarted);
  223. #if DBG
  224. if ((State == DeviceNodeDeleted) ||
  225. (State == DeviceNodeDeletePendingCloses)) {
  226. ASSERT(!(DeviceNode->Flags & DNF_ENUMERATED));
  227. }
  228. #endif
  229. KeAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
  230. previousState = DeviceNode->State;
  231. if (DeviceNode->State != State) {
  232. //
  233. // Update the devnode's current and previous state.
  234. //
  235. DeviceNode->State = State;
  236. DeviceNode->PreviousState = previousState;
  237. //
  238. // Push prior state onto the history stack.
  239. //
  240. DeviceNode->StateHistory[DeviceNode->StateHistoryEntry] = previousState;
  241. DeviceNode->StateHistoryEntry++;
  242. DeviceNode->StateHistoryEntry %= STATE_HISTORY_SIZE;
  243. }
  244. KeReleaseSpinLock(&IopPnPSpinLock, oldIrql);
  245. IopDbgPrint((IOP_INFO_LEVEL,
  246. "%wZ: %s => %s\n",
  247. &DeviceNode->InstancePath,
  248. PP_DEVNODESTATE_NAME(previousState),
  249. PP_DEVNODESTATE_NAME(State)));
  250. if (ARGUMENT_PRESENT(OldState)) {
  251. *OldState = previousState;
  252. }
  253. if (State == DeviceNodeDeleted) {
  254. PpRemoveDeviceActionRequests(DeviceNode->PhysicalDeviceObject);
  255. }
  256. }
  257. VOID
  258. PipRestoreDevNodeState(
  259. IN PDEVICE_NODE DeviceNode
  260. )
  261. /*++
  262. Routine Description:
  263. This routine restores a devnodes state to the state pushed by the last
  264. PipSetDevNodeState call. This function can only be called once for each
  265. call to PipSetDevNodeState.
  266. Arguments:
  267. DeviceNode - Device node to restore state.
  268. Return Value:
  269. None.
  270. --*/
  271. {
  272. PNP_DEVNODE_STATE previousState, newState;
  273. KIRQL oldIrql;
  274. ASSERT((DeviceNode->State == DeviceNodeQueryRemoved) ||
  275. (DeviceNode->State == DeviceNodeQueryStopped) ||
  276. (DeviceNode->State == DeviceNodeAwaitingQueuedRemoval) ||
  277. (DeviceNode->State == DeviceNodeAwaitingQueuedDeletion));
  278. KeAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
  279. //
  280. // Update the devnode's state.
  281. //
  282. previousState = DeviceNode->State;
  283. newState = DeviceNode->State = DeviceNode->PreviousState;
  284. //
  285. // Push the old state onto the history stack.
  286. //
  287. DeviceNode->StateHistory[DeviceNode->StateHistoryEntry] = previousState;
  288. DeviceNode->StateHistoryEntry++;
  289. DeviceNode->StateHistoryEntry %= STATE_HISTORY_SIZE;
  290. #if DBG
  291. //
  292. // Put a sentinel on the stack - restoring twice is a bug.
  293. //
  294. DeviceNode->PreviousState = DeviceNodeUnspecified;
  295. #endif
  296. KeReleaseSpinLock(&IopPnPSpinLock, oldIrql);
  297. IopDbgPrint((IOP_INFO_LEVEL,
  298. "%wZ: %s => %s\n",
  299. &DeviceNode->InstancePath,
  300. PP_DEVNODESTATE_NAME(previousState),
  301. PP_DEVNODESTATE_NAME(newState)));
  302. }
  303. BOOLEAN
  304. PipIsProblemReadonly(
  305. IN ULONG Problem
  306. )
  307. /*++
  308. Routine Description:
  309. This routine returns TRUE if the specified CM_PROB code cannot be cleared
  310. by user mode, FALSE otherwise.
  311. Arguments:
  312. Problem - CM_PROB_...
  313. Return Value:
  314. TRUE/FALSE.
  315. --*/
  316. {
  317. PAGED_CODE();
  318. switch(Problem) {
  319. case CM_PROB_OUT_OF_MEMORY: // Nonresettable due to IoReportResourceUsage path.
  320. case CM_PROB_NORMAL_CONFLICT:
  321. case CM_PROB_PARTIAL_LOG_CONF:
  322. case CM_PROB_DEVICE_NOT_THERE:
  323. case CM_PROB_HARDWARE_DISABLED:
  324. case CM_PROB_DISABLED_SERVICE:
  325. case CM_PROB_TRANSLATION_FAILED:
  326. case CM_PROB_NO_SOFTCONFIG:
  327. case CM_PROB_BIOS_TABLE:
  328. case CM_PROB_IRQ_TRANSLATION_FAILED:
  329. case CM_PROB_DUPLICATE_DEVICE:
  330. case CM_PROB_SYSTEM_SHUTDOWN:
  331. case CM_PROB_HELD_FOR_EJECT:
  332. case CM_PROB_REGISTRY_TOO_LARGE:
  333. case CM_PROB_INVALID_DATA:
  334. case CM_PROB_SETPROPERTIES_FAILED:
  335. return TRUE;
  336. case CM_PROB_FAILED_INSTALL:
  337. case CM_PROB_FAILED_ADD:
  338. case CM_PROB_FAILED_START:
  339. case CM_PROB_NOT_CONFIGURED:
  340. case CM_PROB_NEED_RESTART:
  341. case CM_PROB_REINSTALL:
  342. case CM_PROB_REGISTRY:
  343. case CM_PROB_DISABLED:
  344. case CM_PROB_FAILED_DRIVER_ENTRY:
  345. case CM_PROB_DRIVER_FAILED_PRIOR_UNLOAD:
  346. case CM_PROB_DRIVER_FAILED_LOAD:
  347. case CM_PROB_DRIVER_SERVICE_KEY_INVALID:
  348. case CM_PROB_LEGACY_SERVICE_NO_DEVICES:
  349. case CM_PROB_HALTED:
  350. case CM_PROB_FAILED_POST_START:
  351. case CM_PROB_WILL_BE_REMOVED:
  352. case CM_PROB_DRIVER_BLOCKED:
  353. return FALSE;
  354. case CM_PROB_PHANTOM:
  355. //
  356. // Should never see in kernel mode
  357. //
  358. case CM_PROB_DEVLOADER_FAILED:
  359. case CM_PROB_DEVLOADER_NOT_FOUND:
  360. case CM_PROB_REENUMERATION:
  361. case CM_PROB_VXDLDR:
  362. case CM_PROB_NOT_VERIFIED:
  363. case CM_PROB_LIAR:
  364. case CM_PROB_FAILED_FILTER:
  365. case CM_PROB_MOVED:
  366. case CM_PROB_TOO_EARLY:
  367. case CM_PROB_NO_VALID_LOG_CONF:
  368. case CM_PROB_UNKNOWN_RESOURCE:
  369. case CM_PROB_ENTRY_IS_WRONG_TYPE:
  370. case CM_PROB_LACKED_ARBITRATOR:
  371. case CM_PROB_BOOT_CONFIG_CONFLICT:
  372. case CM_PROB_DEVLOADER_NOT_READY:
  373. case CM_PROB_CANT_SHARE_IRQ:
  374. //
  375. // Win9x specific
  376. //
  377. default:
  378. ASSERT(0);
  379. //
  380. // We return TRUE in this path because that prevents these problems
  381. // from being set on devnodes (SetDeviceProblem won't allow usage
  382. // of ReadOnly problems)
  383. //
  384. return TRUE;
  385. }
  386. }
  387. NTSTATUS
  388. PipAllocateDeviceNode(
  389. IN PDEVICE_OBJECT PhysicalDeviceObject,
  390. OUT PDEVICE_NODE *DeviceNode
  391. )
  392. /*++
  393. Routine Description:
  394. This function allocates a device node from nonpaged pool and initializes
  395. the fields which do not require to hold lock to do so. Since adding
  396. the device node to pnp mgr's device node tree requires acquiring lock,
  397. this routine does not add the device node to device node tree.
  398. Arguments:
  399. PhysicalDeviceObject - Supplies a pointer to its corresponding physical device
  400. object.
  401. Return Value:
  402. a pointer to the newly created device node. Null is returned if failed.
  403. --*/
  404. {
  405. PAGED_CODE();
  406. *DeviceNode = ExAllocatePoolWithTag(
  407. NonPagedPool,
  408. sizeof(DEVICE_NODE),
  409. IOP_DNOD_TAG
  410. );
  411. if (*DeviceNode == NULL ){
  412. return STATUS_INSUFFICIENT_RESOURCES;
  413. }
  414. InterlockedIncrement((LONG *)&IopNumberDeviceNodes);
  415. RtlZeroMemory(*DeviceNode, sizeof(DEVICE_NODE));
  416. (*DeviceNode)->InterfaceType = InterfaceTypeUndefined;
  417. (*DeviceNode)->BusNumber = (ULONG)-1;
  418. (*DeviceNode)->ChildInterfaceType = InterfaceTypeUndefined;
  419. (*DeviceNode)->ChildBusNumber = (ULONG)-1;
  420. (*DeviceNode)->ChildBusTypeIndex = (USHORT)-1;
  421. (*DeviceNode)->State = DeviceNodeUninitialized;
  422. (*DeviceNode)->DisableableDepends = 0;
  423. PpHotSwapInitRemovalPolicy(*DeviceNode);
  424. InitializeListHead(&(*DeviceNode)->DeviceArbiterList);
  425. InitializeListHead(&(*DeviceNode)->DeviceTranslatorList);
  426. if (PhysicalDeviceObject){
  427. (*DeviceNode)->PhysicalDeviceObject = PhysicalDeviceObject;
  428. PhysicalDeviceObject->DeviceObjectExtension->DeviceNode = (PVOID)*DeviceNode;
  429. PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
  430. }
  431. InitializeListHead(&(*DeviceNode)->TargetDeviceNotify);
  432. InitializeListHead(&(*DeviceNode)->DockInfo.ListEntry);
  433. InitializeListHead(&(*DeviceNode)->PendedSetInterfaceState);
  434. InitializeListHead(&(*DeviceNode)->LegacyBusListEntry);
  435. if (PpSystemHiveTooLarge) {
  436. //
  437. // FUTURE: Make this an informational status, as that's how it is used.
  438. //
  439. return STATUS_SYSTEM_HIVE_TOO_LARGE;
  440. }
  441. return STATUS_SUCCESS;
  442. }
  443. NTSTATUS
  444. PipForAllDeviceNodes(
  445. IN PENUM_CALLBACK Callback,
  446. IN PVOID Context
  447. )
  448. /*++
  449. Routine Description:
  450. This function walks the device node tree and invokes the caller specified
  451. 'Callback' function for each device node.
  452. Note, this routine (or its worker routine) traverses the tree in a top
  453. down manner.
  454. Arguments:
  455. Callback - Supplies the call back routine for each device node.
  456. Context - Supplies a parameter/context for the callback function.
  457. Return Value:
  458. Status returned from Callback, if not successfull then the tree walking stops.
  459. --*/
  460. {
  461. PAGED_CODE();
  462. return PipForDeviceNodeSubtree(IopRootDeviceNode, Callback, Context);
  463. }
  464. NTSTATUS
  465. PipForDeviceNodeSubtree(
  466. IN PDEVICE_NODE DeviceNode,
  467. IN PENUM_CALLBACK Callback,
  468. IN PVOID Context
  469. )
  470. /*++
  471. Routine Description:
  472. This function walks the device node tree under but not including the passed
  473. in device node and perform caller specified 'Callback' function for each
  474. device node.
  475. Note, this routine (or its worker routine) traverses the tree in a top
  476. down manner.
  477. Arguments:
  478. Callback - Supplies the call back routine for each device node.
  479. Context - Supplies a parameter/context for the callback function.
  480. Return Value:
  481. Status returned from Callback, if not successfull then the tree walking stops.
  482. --*/
  483. {
  484. ENUM_CONTEXT enumContext;
  485. NTSTATUS status;
  486. PAGED_CODE();
  487. enumContext.CallersCallback = Callback;
  488. enumContext.CallersContext = Context;
  489. //
  490. // Start with a pointer to the root device node, recursively examine all the
  491. // children until we the callback function says stop or we've looked at all
  492. // of them.
  493. //
  494. PpDevNodeLockTree(PPL_SIMPLE_READ);
  495. status = PipForAllChildDeviceNodes(DeviceNode,
  496. PipForAllDeviceNodesCallback,
  497. (PVOID)&enumContext );
  498. PpDevNodeUnlockTree(PPL_SIMPLE_READ);
  499. return status;
  500. }
  501. NTSTATUS
  502. PipForAllChildDeviceNodes(
  503. IN PDEVICE_NODE Parent,
  504. IN PENUM_CALLBACK Callback,
  505. IN PVOID Context
  506. )
  507. /*++
  508. Routine Description:
  509. This function walks the Parent's device node subtree and perform caller specified
  510. 'Callback' function for each device node under Parent.
  511. Note, befor calling this rotuine, callers must acquire the enumeration mutex
  512. of the 'Parent' device node to make sure its children won't go away unless the
  513. call tells them to.
  514. Arguments:
  515. Parent - Supplies a pointer to the device node whose subtree is to be walked.
  516. Callback - Supplies the call back routine for each device node.
  517. Context - Supplies a parameter/context for the callback function.
  518. Return Value:
  519. NTSTATUS value.
  520. --*/
  521. {
  522. PDEVICE_NODE nextChild = Parent->Child;
  523. PDEVICE_NODE child;
  524. NTSTATUS status = STATUS_SUCCESS;
  525. PAGED_CODE();
  526. //
  527. // Process siblings until we find the end of the sibling list or
  528. // the Callback() returns FALSE. Set result = TRUE at the top of
  529. // the loop so that if there are no siblings we will return TRUE,
  530. // e.g. Keep Enumerating.
  531. //
  532. // Note, we need to find next child before calling Callback function
  533. // in case the current child is deleted by the Callback function.
  534. //
  535. while (nextChild && NT_SUCCESS(status)) {
  536. child = nextChild;
  537. nextChild = child->Sibling;
  538. status = Callback(child, Context);
  539. }
  540. return status;
  541. }
  542. NTSTATUS
  543. PipForAllDeviceNodesCallback(
  544. IN PDEVICE_NODE DeviceNode,
  545. IN PVOID Context
  546. )
  547. /*++
  548. Routine Description:
  549. This function is the worker routine for PipForAllChildDeviceNodes routine.
  550. Arguments:
  551. DeviceNode - Supplies a pointer to the device node whose subtree is to be walked.
  552. Context - Supplies a context which contains the caller specified call back
  553. function and parameter.
  554. Return Value:
  555. NTSTATUS value.
  556. --*/
  557. {
  558. PENUM_CONTEXT enumContext;
  559. NTSTATUS status;
  560. PAGED_CODE();
  561. enumContext = (PENUM_CONTEXT)Context;
  562. //
  563. // First call the caller's callback for this devnode
  564. //
  565. status =
  566. enumContext->CallersCallback(DeviceNode, enumContext->CallersContext);
  567. if (NT_SUCCESS(status)) {
  568. //
  569. // Now enumerate the children, if any.
  570. //
  571. if (DeviceNode->Child) {
  572. status = PipForAllChildDeviceNodes(
  573. DeviceNode,
  574. PipForAllDeviceNodesCallback,
  575. Context);
  576. }
  577. }
  578. return status;
  579. }
  580. VOID
  581. IopDestroyDeviceNode(
  582. IN PDEVICE_NODE DeviceNode
  583. )
  584. /*++
  585. Routine Description:
  586. This function is invoked by IopDeleteDevice to clean up the device object's
  587. device node structure.
  588. Arguments:
  589. DeviceNode - Supplies a pointer to the device node whose subtree is to be walked.
  590. Context - Supplies a context which contains the caller specified call back
  591. function and parameter.
  592. Return Value:
  593. NTSTATUS value.
  594. --*/
  595. {
  596. #if DBG
  597. PDEVICE_OBJECT dbgDeviceObject;
  598. #endif
  599. PAGED_CODE();
  600. if (DeviceNode) {
  601. if ((DeviceNode->PhysicalDeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE) &&
  602. DeviceNode->Parent != NULL) {
  603. PP_SAVE_DEVNODE_TO_TRIAGE_DUMP(DeviceNode);
  604. KeBugCheckEx( PNP_DETECTED_FATAL_ERROR,
  605. PNP_ERR_ACTIVE_PDO_FREED,
  606. (ULONG_PTR)DeviceNode->PhysicalDeviceObject,
  607. 0,
  608. 0);
  609. }
  610. if (DeviceNode->Flags & DNF_LEGACY_RESOURCE_DEVICENODE) {
  611. //
  612. // Release the resources this device consumes (the devicenode will
  613. // get deleted after the release). Basically cleanup after bad
  614. // (legacy) drivers.
  615. //
  616. IopLegacyResourceAllocation( ArbiterRequestUndefined,
  617. IoPnpDriverObject,
  618. DeviceNode->PhysicalDeviceObject,
  619. NULL,
  620. NULL);
  621. return;
  622. }
  623. #if DBG
  624. //
  625. // If Only Parent is NOT NULL, most likely the driver forgot to
  626. // release resources before deleting its FDO. (The driver previously
  627. // call legacy assign resource interface.)
  628. //
  629. ASSERT(DeviceNode->Child == NULL &&
  630. DeviceNode->Sibling == NULL &&
  631. DeviceNode->LastChild == NULL
  632. );
  633. ASSERT(DeviceNode->DockInfo.SerialNumber == NULL &&
  634. IsListEmpty(&DeviceNode->DockInfo.ListEntry));
  635. if (DeviceNode->PhysicalDeviceObject->Flags & DO_BUS_ENUMERATED_DEVICE) {
  636. ASSERT (DeviceNode->Parent == 0);
  637. }
  638. if (DeviceNode->PreviousResourceList) {
  639. ExFreePool(DeviceNode->PreviousResourceList);
  640. }
  641. if (DeviceNode->PreviousResourceRequirements) {
  642. ExFreePool(DeviceNode->PreviousResourceRequirements);
  643. }
  644. //
  645. // device should not appear to be not-disableable if/when we get here
  646. // if either of these two lines ASSERT, email: jamiehun
  647. //
  648. ASSERT((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) == 0);
  649. ASSERT(DeviceNode->DisableableDepends == 0);
  650. if (DeviceNode->InstancePath.Length) {
  651. dbgDeviceObject = IopDeviceObjectFromDeviceInstance(&DeviceNode->InstancePath);
  652. if (dbgDeviceObject) {
  653. ASSERT(dbgDeviceObject != DeviceNode->PhysicalDeviceObject);
  654. ObDereferenceObject(dbgDeviceObject);
  655. }
  656. }
  657. #endif
  658. if (DeviceNode->DuplicatePDO) {
  659. ObDereferenceObject(DeviceNode->DuplicatePDO);
  660. }
  661. if (DeviceNode->ServiceName.Length != 0) {
  662. ExFreePool(DeviceNode->ServiceName.Buffer);
  663. }
  664. if (DeviceNode->InstancePath.Length != 0) {
  665. ExFreePool(DeviceNode->InstancePath.Buffer);
  666. }
  667. if (DeviceNode->ResourceRequirements) {
  668. ExFreePool(DeviceNode->ResourceRequirements);
  669. }
  670. //
  671. // Dereference all the arbiters and translators on this PDO.
  672. //
  673. IopUncacheInterfaceInformation(DeviceNode->PhysicalDeviceObject) ;
  674. //
  675. // Release any pended IoSetDeviceInterface structures
  676. //
  677. while (!IsListEmpty(&DeviceNode->PendedSetInterfaceState)) {
  678. PPENDING_SET_INTERFACE_STATE entry;
  679. entry = (PPENDING_SET_INTERFACE_STATE)RemoveHeadList(&DeviceNode->PendedSetInterfaceState);
  680. ExFreePool(entry->LinkName.Buffer);
  681. ExFreePool(entry);
  682. }
  683. DeviceNode->PhysicalDeviceObject->DeviceObjectExtension->DeviceNode = NULL;
  684. ExFreePool(DeviceNode);
  685. InterlockedDecrement((LONG *)&IopNumberDeviceNodes);
  686. }
  687. }
  688. VOID
  689. PpDevNodeInsertIntoTree(
  690. IN PDEVICE_NODE ParentNode,
  691. IN PDEVICE_NODE DeviceNode
  692. )
  693. /*++
  694. Routine Description:
  695. This function is called to insert a new devnode into the device tree.
  696. Note that there are two classes of callers:
  697. PnP callers
  698. Legacy callers
  699. All PnP callers hold the device tree lock. Legacy callers however come in
  700. with no locks, as they might be brought into being due to a PnP event. To
  701. deal with the later case, inserts are atomic and legacy callers can never
  702. remove themselves from the tree.
  703. Arguments:
  704. ParentNode - Supplies a pointer to the device node's parent
  705. DeviceNode - Supplies a pointer to the device node which needs to be
  706. inserted into the tree.
  707. Return Value:
  708. None.
  709. --*/
  710. {
  711. ULONG depth;
  712. KIRQL oldIrql;
  713. //
  714. // Acquire spinlock to deal with legacy/PnP synchronization.
  715. //
  716. KeAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
  717. //
  718. // Determine the depth of the devnode.
  719. //
  720. depth = ParentNode->Level + 1;
  721. DeviceNode->Level = depth;
  722. //
  723. // Update the maximum depth of the tree.
  724. //
  725. if (depth > IopMaxDeviceNodeLevel) {
  726. IopMaxDeviceNodeLevel = depth;
  727. }
  728. //
  729. // Put this devnode at the end of the parent's list of children. Note that
  730. // the Child/Sibling fields are really the last things to be updated. This
  731. // has to be done as walkers of the tree hold no locks that protect the
  732. // tree from legacy inserts.
  733. //
  734. DeviceNode->Parent = ParentNode;
  735. KeMemoryBarrier();
  736. if (ParentNode->LastChild) {
  737. ASSERT(ParentNode->LastChild->Sibling == NULL);
  738. ParentNode->LastChild->Sibling = DeviceNode;
  739. ParentNode->LastChild = DeviceNode;
  740. } else {
  741. ASSERT(ParentNode->Child == NULL);
  742. ParentNode->Child = ParentNode->LastChild = DeviceNode;
  743. }
  744. KeReleaseSpinLock(&IopPnPSpinLock, oldIrql);
  745. //
  746. // Tree has changed
  747. //
  748. IoDeviceNodeTreeSequence += 1;
  749. }
  750. VOID
  751. PpDevNodeRemoveFromTree(
  752. IN PDEVICE_NODE DeviceNode
  753. )
  754. /*++
  755. Routine Description:
  756. This function removes the device node from the device node tree
  757. Arguments:
  758. DeviceNode - Device node to remove
  759. Return Value:
  760. --*/
  761. {
  762. PDEVICE_NODE *node;
  763. KIRQL oldIrql;
  764. //
  765. // Acquire spinlock to deal with legacy/PnP synchronization.
  766. //
  767. KeAcquireSpinLock(&IopPnPSpinLock, &oldIrql);
  768. //
  769. // Unlink the pointer to this device node. (If this is the
  770. // first entry, unlink it from the parents child pointer, else
  771. // remove it from the sibling list)
  772. //
  773. node = &DeviceNode->Parent->Child;
  774. while (*node != DeviceNode) {
  775. node = &(*node)->Sibling;
  776. }
  777. *node = DeviceNode->Sibling;
  778. if (DeviceNode->Parent->Child == NULL) {
  779. DeviceNode->Parent->LastChild = NULL;
  780. } else {
  781. while (*node) {
  782. node = &(*node)->Sibling;
  783. }
  784. DeviceNode->Parent->LastChild = CONTAINING_RECORD(node, DEVICE_NODE, Sibling);
  785. }
  786. KeReleaseSpinLock(&IopPnPSpinLock, oldIrql);
  787. //
  788. // Remove this device node from Legacy Bus information table.
  789. //
  790. IopRemoveLegacyBusDeviceNode(DeviceNode);
  791. //
  792. // Orphan any outstanding device change notifications on these nodes.
  793. //
  794. IopOrphanNotification(DeviceNode);
  795. //
  796. // No longer linked
  797. //
  798. DeviceNode->Parent = NULL;
  799. DeviceNode->Child = NULL;
  800. DeviceNode->Sibling = NULL;
  801. DeviceNode->LastChild = NULL;
  802. }
  803. VOID
  804. PpDevNodeLockTree(
  805. IN PNP_LOCK_LEVEL LockLevel
  806. )
  807. /*++
  808. Routine Description:
  809. This function acquires the tree lock with the appropriate level of
  810. restrictions.
  811. Arguments:
  812. LockLevel:
  813. PPL_SIMPLE_READ - Allows simple examination of the tree.
  814. PPL_TREEOP_ALLOW_READS - Called as part of a StartEnum/Remove/Power
  815. operation, blocks other such operations.
  816. Simple reads can go through however.
  817. PPL_TREEOP_BLOCK_READS - Called as part of a StartEnum/Remove/Power
  818. operation, blocks other such operations.
  819. Simple reads are also blocked.
  820. PPL_TREEOP_BLOCK_READS_FROM_ALLOW - Switch to PPL_TREEOP_BLOCK_READS
  821. when already in
  822. PPL_TREEOP_BLOCK_READS. Note that
  823. PpDevNodeUnlockTree must be
  824. subsequently called on both to
  825. release.
  826. Return Value:
  827. None.
  828. --*/
  829. {
  830. ULONG refCount, remainingCount;
  831. PAGED_CODE();
  832. //
  833. // Block any attempt to suspend the thread via user mode.
  834. //
  835. KeEnterCriticalRegion();
  836. switch(LockLevel) {
  837. case PPL_SIMPLE_READ:
  838. ExAcquireSharedWaitForExclusive(&IopDeviceTreeLock, TRUE);
  839. break;
  840. case PPL_TREEOP_ALLOW_READS:
  841. ExAcquireResourceExclusiveLite(&PiEngineLock, TRUE);
  842. ExAcquireSharedWaitForExclusive(&IopDeviceTreeLock, TRUE);
  843. break;
  844. case PPL_TREEOP_BLOCK_READS:
  845. ExAcquireResourceExclusiveLite(&PiEngineLock, TRUE);
  846. ExAcquireResourceExclusiveLite(&IopDeviceTreeLock, TRUE);
  847. break;
  848. case PPL_TREEOP_BLOCK_READS_FROM_ALLOW:
  849. //
  850. // Drop the tree lock and require exclusive.
  851. //
  852. ASSERT(ExIsResourceAcquiredExclusiveLite(&PiEngineLock));
  853. //
  854. // "Shared" is a subset of exclusive. ExIsResourceAcquiredShared
  855. // will return nonzero if it's owned exclusive. We flush out that
  856. // case here.
  857. //
  858. ASSERT(ExIsResourceAcquiredSharedLite(&IopDeviceTreeLock) &&
  859. (!ExIsResourceAcquiredExclusiveLite(&IopDeviceTreeLock)));
  860. //
  861. // Drop the tree lock entirely.
  862. //
  863. refCount = ExIsResourceAcquiredSharedLite(&IopDeviceTreeLock);
  864. for(remainingCount = refCount; remainingCount; remainingCount--) {
  865. ExReleaseResourceLite(&IopDeviceTreeLock);
  866. }
  867. //
  868. // Grab it exclusively while keeping the original count.
  869. //
  870. for(remainingCount = refCount; remainingCount; remainingCount--) {
  871. ExAcquireResourceExclusiveLite(&IopDeviceTreeLock, TRUE);
  872. }
  873. break;
  874. default:
  875. ASSERT(0);
  876. break;
  877. }
  878. }
  879. VOID
  880. PpDevNodeUnlockTree(
  881. IN PNP_LOCK_LEVEL LockLevel
  882. )
  883. /*++
  884. Routine Description:
  885. This function releases the tree lock with the appropriate level of
  886. restrictions.
  887. Arguments:
  888. LockLevel:
  889. PPL_SIMPLE_READ - Allows simple examination of the tree.
  890. PPL_TREEOP_ALLOW_READS - Called as part of a StartEnum/Remove/Power
  891. operation, blocks other such operations.
  892. Simple reads can go through however.
  893. PPL_TREEOP_BLOCK_READS - Called as part of a StartEnum/Remove/Power
  894. operation, blocks other such operations.
  895. Simple reads are also blocked.
  896. PPL_TREEOP_BLOCK_READS_FROM_ALLOW - Switch to PPL_TREEOP_BLOCK_READS
  897. when already in
  898. PPL_TREEOP_BLOCK_READS. Note that
  899. PpDevNodeUnlockTree must be
  900. subsequently called on both to
  901. release.
  902. Return Value:
  903. None.
  904. --*/
  905. {
  906. PAGED_CODE();
  907. PPDEVNODE_ASSERT_LOCK_HELD(LockLevel);
  908. switch(LockLevel) {
  909. case PPL_SIMPLE_READ:
  910. ExReleaseResourceLite(&IopDeviceTreeLock);
  911. break;
  912. case PPL_TREEOP_ALLOW_READS:
  913. ExReleaseResourceLite(&IopDeviceTreeLock);
  914. ExReleaseResourceLite(&PiEngineLock);
  915. break;
  916. case PPL_TREEOP_BLOCK_READS:
  917. ExReleaseResourceLite(&IopDeviceTreeLock);
  918. ExReleaseResourceLite(&PiEngineLock);
  919. break;
  920. case PPL_TREEOP_BLOCK_READS_FROM_ALLOW:
  921. //
  922. // The engine lock should still be held here. Now we adjust the
  923. // tree lock. Go back to allow by converting the exclusive lock to
  924. // shared. Note that this doesn't chance the acquisition count.
  925. //
  926. ASSERT(ExIsResourceAcquiredExclusiveLite(&IopDeviceTreeLock));
  927. ASSERT(ExIsResourceAcquiredExclusiveLite(&PiEngineLock));
  928. ExConvertExclusiveToSharedLite(&IopDeviceTreeLock);
  929. break;
  930. default:
  931. ASSERT(0);
  932. break;
  933. }
  934. KeLeaveCriticalRegion();
  935. }
  936. #if DBG
  937. VOID
  938. PpDevNodeAssertLockLevel(
  939. IN PNP_LOCK_LEVEL LockLevel,
  940. IN PCSTR File,
  941. IN ULONG Line
  942. )
  943. /*++
  944. Routine Description:
  945. This asserts the lock is currently held at the appropriate level.
  946. Arguments:
  947. LockLevel:
  948. PPL_SIMPLE_READ - Allows simple examination of the tree.
  949. PPL_TREEOP_ALLOW_READS - Called as part of a StartEnum/Remove/Power
  950. operation, blocks other such operations.
  951. Simple reads can go through however.
  952. PPL_TREEOP_BLOCK_READS - Called as part of a StartEnum/Remove/Power
  953. operation, blocks other such operations.
  954. Simple reads are also blocked.
  955. PPL_TREEOP_BLOCK_READS_FROM_ALLOW - Switch to PPL_TREEOP_BLOCK_READS
  956. when already in
  957. PPL_TREEOP_BLOCK_READS. Note that
  958. PpDevNodeUnlockTree must be
  959. subsequently called on both to
  960. release.
  961. File: Name of c-file asserting the lock is held.
  962. Line: Line number in above c-file.
  963. Return Value:
  964. None.
  965. --*/
  966. {
  967. PAGED_CODE();
  968. UNREFERENCED_PARAMETER (File);
  969. UNREFERENCED_PARAMETER (Line);
  970. switch(LockLevel) {
  971. case PPL_SIMPLE_READ:
  972. ASSERT(ExIsResourceAcquiredSharedLite(&IopDeviceTreeLock));
  973. break;
  974. case PPL_TREEOP_ALLOW_READS:
  975. ASSERT(ExIsResourceAcquiredSharedLite(&IopDeviceTreeLock));
  976. ASSERT(ExIsResourceAcquiredExclusiveLite(&PiEngineLock));
  977. break;
  978. case PPL_TREEOP_BLOCK_READS_FROM_ALLOW:
  979. //
  980. // This isn't really a lock level, but this assert-o-matic function
  981. // is called from Unlock, in which case this level means "drop back
  982. // to PPL_TREEOP_ALLOW_READS *from* PPL_TREEOP_BLOCK_READS." So...
  983. //
  984. // Fall through
  985. //
  986. case PPL_TREEOP_BLOCK_READS:
  987. ASSERT(ExIsResourceAcquiredExclusiveLite(&IopDeviceTreeLock));
  988. ASSERT(ExIsResourceAcquiredExclusiveLite(&PiEngineLock));
  989. break;
  990. default:
  991. ASSERT(0);
  992. break;
  993. }
  994. }
  995. #endif // DBG