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.

2789 lines
87 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. hwprofil.c
  5. Abstract:
  6. This module contains support for changing the Hardware profile
  7. based on the current docking state, either at boot time or by
  8. ACPI dock.
  9. Author:
  10. Kenneth D. Ray (kenray) Jan 1998
  11. Revision History:
  12. --*/
  13. #include "cmp.h"
  14. NTSTATUS
  15. CmDeleteKeyRecursive(
  16. HANDLE hKeyRoot,
  17. PWSTR Key,
  18. PVOID TemporaryBuffer,
  19. ULONG LengthTemporaryBuffer,
  20. BOOLEAN ThisKeyToo
  21. );
  22. NTSTATUS
  23. CmpGetAcpiProfileInformation (
  24. IN HANDLE IDConfigDB,
  25. OUT PCM_HARDWARE_PROFILE_LIST * ProfileList,
  26. OUT PCM_HARDWARE_PROFILE_ACPI_ALIAS_LIST * AliasList,
  27. IN PWCHAR NameBuffer,
  28. IN PUCHAR ValueBuffer,
  29. IN ULONG Len
  30. );
  31. NTSTATUS
  32. CmpFilterAcpiDockingState (
  33. IN PPROFILE_ACPI_DOCKING_STATE NewDockState,
  34. IN ULONG CurrentDockingState,
  35. IN PWCHAR CurrentAcpiSN,
  36. IN ULONG CurrentProfileNumber,
  37. IN OUT PCM_HARDWARE_PROFILE_LIST ProfileList,
  38. IN OUT PCM_HARDWARE_PROFILE_ACPI_ALIAS_LIST AliasList
  39. );
  40. NTSTATUS
  41. CmpMoveBiosAliasTable (
  42. IN HANDLE IDConfigDB,
  43. IN HANDLE CurrentInfo,
  44. IN ULONG CurrentProfileNumber,
  45. IN ULONG NewProfileNumber,
  46. IN PWCHAR nameBuffer,
  47. IN PCHAR valueBuffer,
  48. IN ULONG bufferLen
  49. );
  50. #pragma alloc_text(PAGE,CmDeleteKeyRecursive)
  51. #pragma alloc_text(PAGE,CmpCloneHwProfile)
  52. #pragma alloc_text(PAGE,CmSetAcpiHwProfile)
  53. #pragma alloc_text(PAGE,CmpFilterAcpiDockingState)
  54. #pragma alloc_text(PAGE,CmpGetAcpiProfileInformation)
  55. #pragma alloc_text(PAGE,CmpAddAcpiAliasEntry)
  56. #pragma alloc_text(PAGE,CmpMoveBiosAliasTable)
  57. #pragma alloc_text(PAGE,CmpCreateHwProfileFriendlyName)
  58. extern UNICODE_STRING CmSymbolicLinkValueName;
  59. NTSTATUS
  60. CmpGetAcpiProfileInformation (
  61. IN HANDLE IDConfigDB,
  62. OUT PCM_HARDWARE_PROFILE_LIST * ProfileList,
  63. OUT PCM_HARDWARE_PROFILE_ACPI_ALIAS_LIST * AliasList,
  64. IN PWCHAR nameBuffer,
  65. IN PUCHAR valueBuffer,
  66. IN ULONG bufferLen
  67. )
  68. /*++
  69. Routine Description:
  70. Obtain the alias and hardware profile information from the registry.
  71. --*/
  72. {
  73. NTSTATUS status = STATUS_SUCCESS;
  74. HANDLE acpiAlias = NULL;
  75. HANDLE profiles = NULL;
  76. HANDLE entry = NULL;
  77. ULONG len = 0;
  78. ULONG i, j;
  79. OBJECT_ATTRIBUTES attributes;
  80. UNICODE_STRING name;
  81. KEY_FULL_INFORMATION keyInfo;
  82. PKEY_VALUE_FULL_INFORMATION value;
  83. PKEY_BASIC_INFORMATION basicInfo;
  84. PAGED_CODE ();
  85. *ProfileList = NULL;
  86. *AliasList = NULL;
  87. value = (PKEY_VALUE_FULL_INFORMATION) valueBuffer;
  88. basicInfo = (PKEY_BASIC_INFORMATION) valueBuffer;
  89. //
  90. // Open a handle to the Profile information
  91. //
  92. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_HARDWARE_PROFILES);
  93. InitializeObjectAttributes (&attributes,
  94. &name,
  95. OBJ_CASE_INSENSITIVE,
  96. IDConfigDB,
  97. NULL);
  98. status = ZwOpenKey (&profiles,
  99. KEY_READ,
  100. &attributes);
  101. if (!NT_SUCCESS (status)) {
  102. profiles = NULL;
  103. goto Clean;
  104. }
  105. //
  106. // Find the number of profile Sub Keys
  107. //
  108. status = ZwQueryKey (profiles,
  109. KeyFullInformation,
  110. &keyInfo,
  111. sizeof (keyInfo),
  112. &len);
  113. if (!NT_SUCCESS (status)) {
  114. goto Clean;
  115. }
  116. ASSERT (0 < keyInfo.SubKeys);
  117. len = sizeof (CM_HARDWARE_PROFILE_LIST)
  118. + (sizeof (CM_HARDWARE_PROFILE) * (keyInfo.SubKeys - 1));
  119. * ProfileList = ExAllocatePool (PagedPool, len);
  120. if (NULL == *ProfileList) {
  121. status = STATUS_INSUFFICIENT_RESOURCES;
  122. goto Clean;
  123. }
  124. RtlZeroMemory (*ProfileList, len);
  125. (*ProfileList)->MaxProfileCount = keyInfo.SubKeys;
  126. (*ProfileList)->CurrentProfileCount = 0;
  127. //
  128. // Iterrate the profiles
  129. //
  130. for (i = 0; i < keyInfo.SubKeys; i++) {
  131. CM_HARDWARE_PROFILE TempProfile;
  132. UNICODE_STRING KeyName;
  133. ULONG realsize;
  134. //
  135. // Get the first key in the list.
  136. //
  137. status = ZwEnumerateKey (profiles,
  138. i,
  139. KeyBasicInformation,
  140. basicInfo,
  141. bufferLen - sizeof (UNICODE_NULL), // term 0
  142. &len);
  143. if (!NT_SUCCESS (status)) {
  144. //
  145. // This should never happen.
  146. //
  147. break;
  148. }
  149. basicInfo->Name [basicInfo->NameLength/sizeof(WCHAR)] = 0;
  150. name.Length = (USHORT) basicInfo->NameLength;
  151. name.MaximumLength = (USHORT) basicInfo->NameLength + sizeof (UNICODE_NULL);
  152. name.Buffer = basicInfo->Name;
  153. InitializeObjectAttributes (&attributes,
  154. &name,
  155. OBJ_CASE_INSENSITIVE,
  156. profiles,
  157. NULL);
  158. status = ZwOpenKey (&entry,
  159. KEY_READ,
  160. &attributes);
  161. if (!NT_SUCCESS (status)) {
  162. break;
  163. }
  164. //
  165. // Fill in the temporary profile structure with this
  166. // profile's data.
  167. //
  168. RtlUnicodeStringToInteger(&name, 0, &TempProfile.Id);
  169. //
  170. // Find the pref order of this entry.
  171. //
  172. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PREFERENCE_ORDER);
  173. status = NtQueryValueKey(entry,
  174. &name,
  175. KeyValueFullInformation,
  176. valueBuffer,
  177. bufferLen,
  178. &len);
  179. if ((!NT_SUCCESS (status)) || (value->Type != REG_DWORD)) {
  180. TempProfile.PreferenceOrder = -1;
  181. } else {
  182. TempProfile.PreferenceOrder
  183. = * (PULONG) ((PUCHAR) value + value->DataOffset);
  184. }
  185. //
  186. // Extract the friendly name
  187. //
  188. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_FRIENDLY_NAME);
  189. status = NtQueryValueKey(entry,
  190. &name,
  191. KeyValueFullInformation,
  192. valueBuffer,
  193. bufferLen,
  194. &len);
  195. if (!NT_SUCCESS (status) || (value->Type != REG_SZ)) {
  196. WCHAR tmpname[] = L"-------"; // as taken from cmboot.c
  197. ULONG len;
  198. PVOID buffer;
  199. len = sizeof (tmpname);
  200. buffer = ExAllocatePool (PagedPool, len);
  201. TempProfile.NameLength = len;
  202. TempProfile.FriendlyName = buffer;
  203. if (NULL == buffer) {
  204. status = STATUS_INSUFFICIENT_RESOURCES;
  205. ZwClose (entry);
  206. goto Clean;
  207. }
  208. RtlCopyMemory (buffer, tmpname, value->DataLength);
  209. } else {
  210. PVOID buffer;
  211. buffer = ExAllocatePool (PagedPool, value->DataLength);
  212. TempProfile.NameLength = value->DataLength;
  213. TempProfile.FriendlyName = buffer;
  214. if (NULL == buffer) {
  215. status = STATUS_INSUFFICIENT_RESOURCES;
  216. ZwClose (entry);
  217. goto Clean;
  218. }
  219. RtlCopyMemory (buffer,
  220. (PUCHAR) value + value->DataOffset,
  221. value->DataLength);
  222. }
  223. TempProfile.Flags = 0;
  224. //
  225. // Is this aliasable?
  226. //
  227. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ALIASABLE);
  228. status = NtQueryValueKey(entry,
  229. &name,
  230. KeyValueFullInformation,
  231. valueBuffer,
  232. bufferLen,
  233. &len);
  234. if (NT_SUCCESS (status) && (value->Type == REG_DWORD)) {
  235. if (* (PULONG) ((PUCHAR) value + value->DataOffset)) {
  236. TempProfile.Flags |= CM_HP_FLAGS_ALIASABLE;
  237. }
  238. } else {
  239. TempProfile.Flags |= CM_HP_FLAGS_ALIASABLE;
  240. }
  241. //
  242. // Is this pristine?
  243. //
  244. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PRISTINE);
  245. status = NtQueryValueKey(entry,
  246. &name,
  247. KeyValueFullInformation,
  248. valueBuffer,
  249. bufferLen,
  250. &len);
  251. if (NT_SUCCESS (status) && (value->Type == REG_DWORD)) {
  252. if (* (PULONG) ((PUCHAR) value + value->DataOffset)) {
  253. TempProfile.Flags = CM_HP_FLAGS_PRISTINE;
  254. // No other flags set;
  255. }
  256. }
  257. //
  258. // If we see a profile with the ID of zero (AKA an illegal)
  259. // ID for a hardware profile to possess, then we know that this
  260. // must be a pristine profile.
  261. //
  262. if (0 == TempProfile.Id) {
  263. TempProfile.Flags = CM_HP_FLAGS_PRISTINE;
  264. // NO other flags set.
  265. TempProfile.PreferenceOrder = -1; // move to the end of the list.
  266. }
  267. //
  268. // Insert this new profile into the appropriate spot in the
  269. // profile array. Entries are sorted by preference order.
  270. //
  271. for (j=0; j < (*ProfileList)->CurrentProfileCount; j++) {
  272. if ((*ProfileList)->Profile[j].PreferenceOrder >=
  273. TempProfile.PreferenceOrder) {
  274. //
  275. // Insert at position j.
  276. //
  277. RtlMoveMemory(&(*ProfileList)->Profile[j+1],
  278. &(*ProfileList)->Profile[j],
  279. sizeof(CM_HARDWARE_PROFILE) *
  280. ((*ProfileList)->MaxProfileCount-j-1));
  281. break;
  282. }
  283. }
  284. (*ProfileList)->Profile[j] = TempProfile;
  285. ++(*ProfileList)->CurrentProfileCount;
  286. ZwClose (entry);
  287. }
  288. //
  289. // Open a handle to the ACPI Alias information
  290. //
  291. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ACPI_ALIAS);
  292. InitializeObjectAttributes (&attributes,
  293. &name,
  294. OBJ_CASE_INSENSITIVE,
  295. IDConfigDB,
  296. NULL);
  297. status = ZwOpenKey (&acpiAlias,
  298. KEY_READ,
  299. &attributes);
  300. if (!NT_SUCCESS (status)) {
  301. //
  302. // So we don't have an alias table. This is ok.
  303. //
  304. status = STATUS_SUCCESS;
  305. acpiAlias = NULL;
  306. goto Clean;
  307. }
  308. //
  309. // Find the number of Acpi Alias Sub Keys
  310. //
  311. status = ZwQueryKey (acpiAlias,
  312. KeyFullInformation,
  313. &keyInfo,
  314. sizeof (keyInfo),
  315. &len);
  316. if (!NT_SUCCESS (status)) {
  317. goto Clean;
  318. }
  319. ASSERT (0 < keyInfo.SubKeys);
  320. * AliasList = ExAllocatePool (
  321. PagedPool,
  322. sizeof (CM_HARDWARE_PROFILE_LIST) +
  323. (sizeof (CM_HARDWARE_PROFILE) * (keyInfo.SubKeys - 1)));
  324. if (NULL == *AliasList) {
  325. status = STATUS_INSUFFICIENT_RESOURCES;
  326. goto Clean;
  327. }
  328. (*AliasList)->MaxAliasCount =
  329. (*AliasList)->CurrentAliasCount = keyInfo.SubKeys;
  330. //
  331. // Iterrate the alias entries
  332. //
  333. for (i = 0; i < keyInfo.SubKeys; i++) {
  334. //
  335. // Get the first key in the list.
  336. //
  337. status = ZwEnumerateKey (acpiAlias,
  338. i,
  339. KeyBasicInformation,
  340. basicInfo,
  341. bufferLen - sizeof (UNICODE_NULL), // term 0
  342. &len);
  343. if (!NT_SUCCESS (status)) {
  344. //
  345. // This should never happen.
  346. //
  347. break;
  348. }
  349. basicInfo->Name [basicInfo->NameLength/sizeof(WCHAR)] = 0;
  350. name.Length = (USHORT) basicInfo->NameLength;
  351. name.MaximumLength = (USHORT) basicInfo->NameLength + sizeof (UNICODE_NULL);
  352. name.Buffer = basicInfo->Name;
  353. InitializeObjectAttributes (&attributes,
  354. &name,
  355. OBJ_CASE_INSENSITIVE,
  356. acpiAlias,
  357. NULL);
  358. status = ZwOpenKey (&entry,
  359. KEY_READ,
  360. &attributes);
  361. if (!NT_SUCCESS (status)) {
  362. break;
  363. }
  364. //
  365. // Extract The Profile number to which this alias refers.
  366. //
  367. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PROFILE_NUMBER);
  368. status = NtQueryValueKey(entry,
  369. &name,
  370. KeyValueFullInformation,
  371. valueBuffer,
  372. bufferLen,
  373. &len);
  374. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  375. status = STATUS_REGISTRY_CORRUPT;
  376. ZwClose (entry);
  377. goto Clean;
  378. }
  379. (*AliasList)->Alias[i].ProfileNumber =
  380. * (PULONG) ((PUCHAR) value + value->DataOffset);
  381. //
  382. // Extract The Docking State.
  383. //
  384. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKING_STATE);
  385. status = NtQueryValueKey(entry,
  386. &name,
  387. KeyValueFullInformation,
  388. valueBuffer,
  389. bufferLen,
  390. &len);
  391. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  392. status = STATUS_REGISTRY_CORRUPT;
  393. ZwClose (entry);
  394. goto Clean;
  395. }
  396. (*AliasList)->Alias[i].DockState =
  397. * (PULONG) ((PUCHAR) value + value->DataOffset);
  398. //
  399. // Find the SerialNumber
  400. //
  401. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ACPI_SERIAL_NUMBER);
  402. status = NtQueryValueKey(entry,
  403. &name,
  404. KeyValueFullInformation,
  405. valueBuffer,
  406. bufferLen,
  407. &len);
  408. if (!NT_SUCCESS (status) || (value->Type != REG_BINARY)) {
  409. status = STATUS_REGISTRY_CORRUPT;
  410. ZwClose (entry);
  411. goto Clean;
  412. }
  413. (*AliasList)->Alias[i].SerialLength = value->DataLength;
  414. (*AliasList)->Alias[i].SerialNumber =
  415. (value->DataLength) ?
  416. ExAllocatePool (PagedPool, value->DataLength) :
  417. 0;
  418. if (value->DataLength && (NULL == (*AliasList)->Alias[i].SerialNumber)) {
  419. status = STATUS_INSUFFICIENT_RESOURCES;
  420. ZwClose (entry);
  421. goto Clean;
  422. }
  423. if (value->DataLength) {
  424. RtlCopyMemory ((*AliasList)->Alias[i].SerialNumber,
  425. (PUCHAR) value + value->DataOffset,
  426. value->DataLength);
  427. }
  428. ZwClose (entry);
  429. }
  430. Clean:
  431. if (NULL != acpiAlias) {
  432. NtClose (acpiAlias);
  433. }
  434. if (NULL != profiles) {
  435. NtClose (profiles);
  436. }
  437. if (!NT_SUCCESS (status)) {
  438. if (NULL != *ProfileList) {
  439. for (i = 0; i < (*ProfileList)->CurrentProfileCount; i++) {
  440. if ((*ProfileList)->Profile[i].FriendlyName) {
  441. ExFreePool ((*ProfileList)->Profile[i].FriendlyName);
  442. }
  443. }
  444. ExFreePool (*ProfileList);
  445. *ProfileList = 0;
  446. }
  447. if (NULL != *AliasList) {
  448. for (i = 0; i < (*AliasList)->CurrentAliasCount; i++) {
  449. if ((*AliasList)->Alias[i].SerialNumber) {
  450. ExFreePool ((*AliasList)->Alias[i].SerialNumber);
  451. }
  452. }
  453. ExFreePool (*AliasList);
  454. *AliasList = 0;
  455. }
  456. }
  457. return status;
  458. }
  459. NTSTATUS
  460. CmpAddAcpiAliasEntry (
  461. IN HANDLE IDConfigDB,
  462. IN PPROFILE_ACPI_DOCKING_STATE NewDockState,
  463. IN ULONG ProfileNumber,
  464. IN PWCHAR nameBuffer,
  465. IN PVOID valueBuffer,
  466. IN ULONG valueBufferLength,
  467. IN BOOLEAN PreventDuplication
  468. )
  469. /*++
  470. Routine Description:
  471. Set the Acpi Alais entry.
  472. Routine Description:
  473. Create an alias entry in the IDConfigDB database for the given
  474. hardware profile.
  475. Create the "AcpiAlias" key if it does not exist.
  476. Parameters:
  477. IDConfigDB - Pointer to "..\CurrentControlSet\Control\IDConfigDB"
  478. NewDockState - The new docking state for which this alias points.
  479. ProfileNumber -The profile number to which this alias points.
  480. nameBuffer - a temp scratch space for writing things.
  481. (assumed to be at least 128 WCHARS)
  482. --*/
  483. {
  484. OBJECT_ATTRIBUTES attributes;
  485. NTSTATUS status = STATUS_SUCCESS;
  486. ANSI_STRING ansiString;
  487. UNICODE_STRING name;
  488. HANDLE aliasKey = NULL;
  489. HANDLE aliasEntry = NULL;
  490. ULONG value;
  491. ULONG disposition;
  492. ULONG aliasNumber = 0;
  493. ULONG len;
  494. PKEY_VALUE_FULL_INFORMATION keyInfo;
  495. PAGED_CODE ();
  496. keyInfo = (PKEY_VALUE_FULL_INFORMATION) valueBuffer;
  497. //
  498. // Find the Alias Key or Create it if it does not already exist.
  499. //
  500. RtlInitUnicodeString (&name,CM_HARDWARE_PROFILE_STR_ACPI_ALIAS);
  501. InitializeObjectAttributes (&attributes,
  502. &name,
  503. OBJ_CASE_INSENSITIVE,
  504. IDConfigDB,
  505. NULL);
  506. status = NtOpenKey (&aliasKey,
  507. KEY_READ | KEY_WRITE,
  508. &attributes);
  509. if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
  510. status = NtCreateKey (&aliasKey,
  511. KEY_READ | KEY_WRITE,
  512. &attributes,
  513. 0, // no title
  514. NULL, // no class
  515. 0, // no options
  516. &disposition);
  517. }
  518. if (!NT_SUCCESS (status)) {
  519. aliasKey = NULL;
  520. goto Exit;
  521. }
  522. //
  523. // Create an entry key
  524. //
  525. while (aliasNumber < 200) {
  526. aliasNumber++;
  527. swprintf (nameBuffer, L"%04d", aliasNumber);
  528. RtlInitUnicodeString (&name, nameBuffer);
  529. InitializeObjectAttributes(&attributes,
  530. &name,
  531. OBJ_CASE_INSENSITIVE,
  532. aliasKey,
  533. NULL);
  534. status = NtOpenKey (&aliasEntry,
  535. KEY_READ | KEY_WRITE,
  536. &attributes);
  537. if (NT_SUCCESS (status)) {
  538. if (PreventDuplication) {
  539. //
  540. // If we have a matching DockingState, SerialNumber, and
  541. // Profile Number, then we should not make this alias
  542. //
  543. //
  544. // Extract The DockingState to which this alias refers.
  545. //
  546. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKING_STATE);
  547. status = NtQueryValueKey(aliasEntry,
  548. &name,
  549. KeyValueFullInformation,
  550. valueBuffer,
  551. valueBufferLength,
  552. &len);
  553. if (!NT_SUCCESS (status) || (keyInfo->Type != REG_DWORD)) {
  554. status = STATUS_REGISTRY_CORRUPT;
  555. goto Exit;
  556. }
  557. if (NewDockState->DockingState !=
  558. * (PULONG) ((PUCHAR) keyInfo + keyInfo->DataOffset)) {
  559. //
  560. // Not a dupe
  561. //
  562. NtClose (aliasEntry);
  563. aliasEntry = NULL;
  564. continue;
  565. }
  566. //
  567. // Extract the SerialNumber
  568. //
  569. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ACPI_SERIAL_NUMBER);
  570. status = NtQueryValueKey(aliasEntry,
  571. &name,
  572. KeyValueFullInformation,
  573. valueBuffer,
  574. valueBufferLength,
  575. &len);
  576. if (!NT_SUCCESS (status) || (keyInfo->Type != REG_BINARY)) {
  577. status = STATUS_REGISTRY_CORRUPT;
  578. goto Exit;
  579. }
  580. if (NewDockState->SerialLength != keyInfo->DataLength) {
  581. //
  582. // Not a dupe
  583. //
  584. NtClose (aliasEntry);
  585. aliasEntry = NULL;
  586. continue;
  587. }
  588. if (!RtlEqualMemory (NewDockState->SerialNumber,
  589. ((PUCHAR) keyInfo + keyInfo->DataOffset),
  590. NewDockState->SerialLength)) {
  591. //
  592. // Not a dupe
  593. //
  594. NtClose (aliasEntry);
  595. aliasEntry = NULL;
  596. continue;
  597. }
  598. status = STATUS_SUCCESS;
  599. goto Exit;
  600. }
  601. } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
  602. status = STATUS_SUCCESS;
  603. break;
  604. } else {
  605. break;
  606. }
  607. }
  608. if (!NT_SUCCESS (status)) {
  609. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: cmpCreateAcpiAliasEntry error finding new set %08lx\n",status));
  610. aliasEntry = 0;
  611. goto Exit;
  612. }
  613. status = NtCreateKey (&aliasEntry,
  614. KEY_READ | KEY_WRITE,
  615. &attributes,
  616. 0,
  617. NULL,
  618. 0,
  619. &disposition);
  620. if (!NT_SUCCESS (status)) {
  621. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: cmpCreateAcpiAliasEntry error creating new set %08lx\n",status));
  622. aliasEntry = 0;
  623. goto Exit;
  624. }
  625. //
  626. // Write the Docking State;
  627. //
  628. value = NewDockState->DockingState;
  629. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKING_STATE);
  630. status = NtSetValueKey (aliasEntry,
  631. &name,
  632. 0,
  633. REG_DWORD,
  634. &value,
  635. sizeof (value));
  636. //
  637. // Write the Serial Number
  638. //
  639. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ACPI_SERIAL_NUMBER);
  640. status = NtSetValueKey (aliasEntry,
  641. &name,
  642. 0,
  643. REG_BINARY,
  644. NewDockState->SerialNumber,
  645. NewDockState->SerialLength);
  646. //
  647. // Write the Profile Number
  648. //
  649. value = ProfileNumber;
  650. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PROFILE_NUMBER);
  651. status = NtSetValueKey (aliasEntry,
  652. &name,
  653. 0,
  654. REG_DWORD,
  655. &value,
  656. sizeof (value));
  657. Exit:
  658. if (aliasKey) {
  659. NtClose (aliasKey);
  660. }
  661. if (aliasEntry) {
  662. NtClose (aliasEntry);
  663. }
  664. return status;
  665. }
  666. NTSTATUS
  667. CmSetAcpiHwProfile (
  668. IN PPROFILE_ACPI_DOCKING_STATE NewDockState,
  669. IN PCM_ACPI_SELECTION_ROUTINE Select,
  670. IN PVOID Context,
  671. OUT PHANDLE NewProfile,
  672. OUT PBOOLEAN ProfileChanged
  673. )
  674. /*++
  675. Routine Description:
  676. The ACPI docking state of the machine has changed.
  677. Based on the new change calculate the new HW Profile(s) consitent with the
  678. new ACPI docking state.
  679. Pass the list of known profiles to the callers selection routine.
  680. Set the new current profile.
  681. Patch up any ACPI alias entries if a new profile for this ACPI state has
  682. been used.
  683. Arguments:
  684. NewDockStateArray - The list of possible Docking States that we might enter.
  685. Select - Call back to select which profile to enter, given the list of
  686. possible profiles.
  687. --*/
  688. {
  689. NTSTATUS status = STATUS_SUCCESS;
  690. HANDLE IDConfigDB = NULL;
  691. HANDLE HardwareProfile = NULL;
  692. HANDLE currentInfo = NULL;
  693. HANDLE currentSymLink = NULL;
  694. HANDLE parent = NULL;
  695. WCHAR nameBuffer[128];
  696. UNICODE_STRING name;
  697. UCHAR valueBuffer[256];
  698. ULONG len;
  699. ULONG i;
  700. ULONG selectedElement;
  701. ULONG profileNum;
  702. ULONG currentDockingState;
  703. ULONG currentProfileNumber;
  704. ULONG disposition;
  705. ULONG flags;
  706. PWCHAR currentAcpiSN = NULL;
  707. PCM_HARDWARE_PROFILE_ACPI_ALIAS_LIST AliasList = NULL;
  708. PCM_HARDWARE_PROFILE_LIST ProfileList = NULL;
  709. PKEY_VALUE_FULL_INFORMATION value;
  710. OBJECT_ATTRIBUTES attributes;
  711. PAGED_CODE ();
  712. *ProfileChanged = FALSE;
  713. value = (PKEY_VALUE_FULL_INFORMATION) valueBuffer;
  714. //
  715. // Open The Hardware Profile Database
  716. //
  717. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DATABASE);
  718. InitializeObjectAttributes (&attributes,
  719. &name,
  720. OBJ_CASE_INSENSITIVE,
  721. NULL,
  722. NULL);
  723. status = ZwOpenKey (&IDConfigDB,
  724. KEY_READ,
  725. &attributes);
  726. if (!NT_SUCCESS (status)) {
  727. IDConfigDB = NULL;
  728. goto Clean;
  729. }
  730. //
  731. // Obtain the total list of profiles
  732. //
  733. status = CmpGetAcpiProfileInformation (IDConfigDB,
  734. &ProfileList,
  735. &AliasList,
  736. nameBuffer,
  737. valueBuffer,
  738. sizeof (valueBuffer));
  739. if (!NT_SUCCESS (status)) {
  740. goto Clean;
  741. }
  742. //
  743. // Determine the current Dock information.
  744. //
  745. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CCS_CURRENT);
  746. InitializeObjectAttributes (&attributes,
  747. &name,
  748. OBJ_CASE_INSENSITIVE,
  749. NULL,
  750. NULL);
  751. status = ZwOpenKey (&HardwareProfile,
  752. KEY_READ,
  753. &attributes);
  754. if (!NT_SUCCESS (status)) {
  755. HardwareProfile = NULL;
  756. goto Clean;
  757. }
  758. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CURRENT_DOCK_INFO);
  759. InitializeObjectAttributes (&attributes,
  760. &name,
  761. OBJ_CASE_INSENSITIVE,
  762. IDConfigDB,
  763. NULL);
  764. status = ZwOpenKey (&currentInfo,
  765. KEY_READ,
  766. &attributes);
  767. if (!NT_SUCCESS (status)) {
  768. currentInfo = NULL;
  769. goto Clean;
  770. }
  771. //
  772. // The current Docking State
  773. //
  774. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKING_STATE);
  775. status = NtQueryValueKey (currentInfo,
  776. &name,
  777. KeyValueFullInformation,
  778. valueBuffer,
  779. sizeof (valueBuffer),
  780. &len);
  781. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  782. status = STATUS_REGISTRY_CORRUPT;
  783. goto Clean;
  784. }
  785. currentDockingState = * (PULONG) ((PUCHAR) value + value->DataOffset);
  786. //
  787. // The current ACPI Serial Number
  788. //
  789. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ACPI_SERIAL_NUMBER);
  790. status = NtQueryValueKey(currentInfo,
  791. &name,
  792. KeyValueFullInformation,
  793. valueBuffer,
  794. sizeof (valueBuffer),
  795. &len);
  796. if (NT_SUCCESS (status) && (value->Type == REG_BINARY)) {
  797. currentAcpiSN = ExAllocatePool (PagedPool, value->DataLength);
  798. if (NULL == currentAcpiSN) {
  799. status = STATUS_INSUFFICIENT_RESOURCES;
  800. goto Clean;
  801. }
  802. RtlCopyMemory (currentAcpiSN,
  803. (PUCHAR) value + value->DataOffset,
  804. value->DataLength);
  805. } else {
  806. currentAcpiSN = 0;
  807. }
  808. //
  809. // The current Profile Number
  810. //
  811. RtlInitUnicodeString(&name, L"CurrentConfig");
  812. status = NtQueryValueKey(IDConfigDB,
  813. &name,
  814. KeyValueFullInformation,
  815. valueBuffer,
  816. sizeof (valueBuffer),
  817. &len);
  818. if (!NT_SUCCESS(status) || (value->Type != REG_DWORD)) {
  819. status = STATUS_REGISTRY_CORRUPT;
  820. goto Clean;
  821. }
  822. currentProfileNumber = *(PULONG)((PUCHAR)value + value->DataOffset);
  823. //
  824. // Filter the current list of hardware profiles based on the current
  825. // docking state, the new acpi state, and the acpi alias tables
  826. //
  827. status = CmpFilterAcpiDockingState (NewDockState,
  828. currentDockingState,
  829. currentAcpiSN,
  830. currentProfileNumber,
  831. ProfileList,
  832. AliasList);
  833. if (!NT_SUCCESS (status)) {
  834. goto Clean;
  835. }
  836. //
  837. // Allow the caller a chance to select from the filtered list.
  838. //
  839. status = Select (ProfileList, &selectedElement, Context);
  840. //
  841. // If the user selected -1 then he is not interested in selecting any of
  842. // the profiles.
  843. //
  844. if (-1 == selectedElement) {
  845. ASSERT (STATUS_MORE_PROCESSING_REQUIRED == status);
  846. goto Clean;
  847. }
  848. if (!NT_SUCCESS (status)) {
  849. goto Clean;
  850. }
  851. //
  852. // Fine! We have finally made the new selection.
  853. // Set it.
  854. //
  855. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CCS_HWPROFILE);
  856. InitializeObjectAttributes (&attributes,
  857. &name,
  858. OBJ_CASE_INSENSITIVE,
  859. NULL,
  860. NULL);
  861. status = ZwOpenKey (&parent, KEY_READ, &attributes);
  862. if (!NT_SUCCESS (status)) {
  863. parent = NULL;
  864. goto Clean;
  865. }
  866. //
  867. // How did we get here?
  868. //
  869. flags = ProfileList->Profile[selectedElement].Flags;
  870. profileNum = ProfileList->Profile[selectedElement].Id;
  871. //
  872. // Check for duplicate
  873. //
  874. if (flags & CM_HP_FLAGS_DUPLICATE) {
  875. //
  876. // If there is a duplicate then we need to adjust the pnp
  877. // bios alias table.
  878. //
  879. // This happens if we booted PnP bios detected docked, and then
  880. // we received a set state for ACPI as docked, then we have the
  881. // potential for duplicates. See Comment in CmpFilterAcpiDockingState
  882. // for details.
  883. //
  884. // We need to find any pnp bios alias entries that match the current
  885. // state and point them to the duplicate entry.
  886. //
  887. ASSERT (flags & CM_HP_FLAGS_TRUE_MATCH);
  888. ASSERT (!(flags & CM_HP_FLAGS_PRISTINE));
  889. status = CmpMoveBiosAliasTable (IDConfigDB,
  890. currentInfo,
  891. currentProfileNumber,
  892. profileNum,
  893. nameBuffer,
  894. valueBuffer,
  895. sizeof (valueBuffer));
  896. if (!NT_SUCCESS (status)) {
  897. goto Clean;
  898. }
  899. }
  900. if ((flags & CM_HP_FLAGS_PRISTINE) || (profileNum != currentProfileNumber)){
  901. //
  902. // The profile Number Changed or will change.
  903. //
  904. *ProfileChanged = TRUE;
  905. ASSERT (currentInfo);
  906. ZwClose (currentInfo);
  907. currentInfo = NULL;
  908. if (flags & CM_HP_FLAGS_PRISTINE) {
  909. //
  910. // If the selected profile is pristine then we need to clone.
  911. //
  912. ASSERT (!(flags & CM_HP_FLAGS_TRUE_MATCH));
  913. status = CmpCloneHwProfile (IDConfigDB,
  914. parent,
  915. HardwareProfile,
  916. profileNum,
  917. NewDockState->DockingState,
  918. &HardwareProfile,
  919. &profileNum);
  920. if (!NT_SUCCESS (status)) {
  921. HardwareProfile = 0;
  922. goto Clean;
  923. }
  924. } else {
  925. ASSERT (HardwareProfile);
  926. ZwClose (HardwareProfile);
  927. //
  928. // Open the new profile
  929. //
  930. swprintf (nameBuffer, L"%04d\0", profileNum);
  931. RtlInitUnicodeString (&name, nameBuffer);
  932. InitializeObjectAttributes (&attributes,
  933. &name,
  934. OBJ_CASE_INSENSITIVE,
  935. parent,
  936. NULL);
  937. status = ZwOpenKey (&HardwareProfile, KEY_READ, &attributes);
  938. if (!NT_SUCCESS (status)) {
  939. HardwareProfile = NULL;
  940. goto Clean;
  941. }
  942. }
  943. ASSERT (currentProfileNumber != profileNum);
  944. //
  945. // Open the current info for the profile.
  946. //
  947. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CURRENT_DOCK_INFO);
  948. InitializeObjectAttributes (&attributes,
  949. &name,
  950. OBJ_CASE_INSENSITIVE,
  951. IDConfigDB,
  952. NULL);
  953. status = NtCreateKey (&currentInfo,
  954. KEY_READ | KEY_WRITE,
  955. &attributes,
  956. 0,
  957. NULL,
  958. REG_OPTION_VOLATILE,
  959. &disposition);
  960. if (!NT_SUCCESS (status)) {
  961. currentInfo = NULL;
  962. goto Clean;
  963. }
  964. //
  965. // Set CurrentConfig in the Database
  966. //
  967. RtlInitUnicodeString(&name, L"CurrentConfig");
  968. status = NtSetValueKey(IDConfigDB,
  969. &name,
  970. 0,
  971. REG_DWORD,
  972. &profileNum,
  973. sizeof (profileNum));
  974. if (!NT_SUCCESS(status)) {
  975. status = STATUS_REGISTRY_CORRUPT;
  976. goto Clean;
  977. }
  978. }
  979. //
  980. // Write the new Docking State to the current Info key
  981. //
  982. i = NewDockState->DockingState;
  983. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKING_STATE);
  984. status = ZwSetValueKey (currentInfo,
  985. &name,
  986. 0,
  987. REG_DWORD,
  988. &i,
  989. sizeof (ULONG));
  990. //
  991. // Write the new ACPI information to the current Info key
  992. //
  993. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ACPI_SERIAL_NUMBER);
  994. status = ZwSetValueKey (currentInfo,
  995. &name,
  996. 0,
  997. REG_BINARY,
  998. NewDockState->SerialNumber,
  999. NewDockState->SerialLength);
  1000. if (!(flags & CM_HP_FLAGS_TRUE_MATCH)) {
  1001. //
  1002. // Add the alias entry for this profile.
  1003. //
  1004. status = CmpAddAcpiAliasEntry (IDConfigDB,
  1005. NewDockState,
  1006. profileNum,
  1007. nameBuffer,
  1008. valueBuffer,
  1009. sizeof (valueBuffer),
  1010. FALSE); // Don't Prevent Duplication
  1011. }
  1012. if (profileNum != currentProfileNumber) {
  1013. //
  1014. // Move the symbolic link.
  1015. //
  1016. RtlInitUnicodeString(&name, CM_HARDWARE_PROFILE_STR_CCS_CURRENT);
  1017. InitializeObjectAttributes(&attributes,
  1018. &name,
  1019. OBJ_CASE_INSENSITIVE | OBJ_OPENLINK,
  1020. NULL,
  1021. NULL);
  1022. status = NtCreateKey(&currentSymLink,
  1023. KEY_CREATE_LINK,
  1024. &attributes,
  1025. 0,
  1026. NULL,
  1027. REG_OPTION_OPEN_LINK,
  1028. &disposition);
  1029. ASSERT (STATUS_SUCCESS == status);
  1030. ASSERT (REG_OPENED_EXISTING_KEY == disposition);
  1031. swprintf (nameBuffer,
  1032. L"\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\%04d",
  1033. profileNum);
  1034. RtlInitUnicodeString (&name, nameBuffer);
  1035. status = NtSetValueKey (currentSymLink,
  1036. &CmSymbolicLinkValueName,
  1037. 0,
  1038. REG_LINK,
  1039. name.Buffer,
  1040. name.Length);
  1041. ASSERT (STATUS_SUCCESS == status);
  1042. }
  1043. Clean:
  1044. if (NT_SUCCESS (status)) {
  1045. // NB more process required is not a success code.
  1046. *NewProfile = HardwareProfile;
  1047. } else if (NULL != HardwareProfile) {
  1048. ZwClose (HardwareProfile);
  1049. }
  1050. if (NULL != IDConfigDB) {
  1051. ZwClose (IDConfigDB);
  1052. }
  1053. if (NULL != currentInfo) {
  1054. ZwClose (currentInfo);
  1055. }
  1056. if (NULL != parent) {
  1057. ZwClose (parent);
  1058. }
  1059. if (NULL != currentAcpiSN) {
  1060. ExFreePool (currentAcpiSN);
  1061. }
  1062. if (NULL != ProfileList) {
  1063. for (i = 0; i < ProfileList->CurrentProfileCount; i++) {
  1064. if (ProfileList->Profile[i].FriendlyName) {
  1065. ExFreePool (ProfileList->Profile[i].FriendlyName);
  1066. }
  1067. }
  1068. ExFreePool (ProfileList);
  1069. }
  1070. if (NULL != AliasList) {
  1071. for (i = 0; i < AliasList->CurrentAliasCount; i++) {
  1072. if (AliasList->Alias[i].SerialNumber) {
  1073. ExFreePool (AliasList->Alias[i].SerialNumber);
  1074. }
  1075. }
  1076. ExFreePool (AliasList);
  1077. }
  1078. return status;
  1079. }
  1080. NTSTATUS
  1081. CmpFilterAcpiDockingState (
  1082. IN PPROFILE_ACPI_DOCKING_STATE NewDockingState,
  1083. IN ULONG CurrentDockState,
  1084. IN PWCHAR CurrentAcpiSN,
  1085. IN ULONG CurrentProfileNumber,
  1086. IN OUT PCM_HARDWARE_PROFILE_LIST ProfileList,
  1087. IN OUT PCM_HARDWARE_PROFILE_ACPI_ALIAS_LIST AliasList
  1088. )
  1089. /*++
  1090. Routine Description:
  1091. Given the new state of things and the current state of things,
  1092. prune the given list of profiles.
  1093. --*/
  1094. {
  1095. NTSTATUS status = STATUS_SUCCESS;
  1096. ULONG i = 0;
  1097. ULONG j;
  1098. ULONG len;
  1099. ULONG mask = HW_PROFILE_DOCKSTATE_UNDOCKED | HW_PROFILE_DOCKSTATE_DOCKED;
  1100. ULONG flags;
  1101. PCM_HARDWARE_PROFILE_ACPI_ALIAS alias;
  1102. BOOLEAN trueMatch = FALSE;
  1103. BOOLEAN dupDetect = FALSE;
  1104. BOOLEAN currentListed = FALSE;
  1105. BOOLEAN keepCurrent = FALSE;
  1106. PAGED_CODE ();
  1107. //
  1108. // Check for duplicate:
  1109. //
  1110. // If the user boots undocked, and then hot docks. We will generate
  1111. // a profile alias for the pnp reported undocked state [A], and one for the
  1112. // ACPI reported docked state [B}. If the use subsequently reboots docked,
  1113. // then we will create a third pnp reported docked state [C] profile alias.
  1114. // {C] is really a duplicate of [B}, but we wont know this until such time
  1115. // as the ACPI state for {B] is reported.
  1116. //
  1117. // The same can happen for undocked scenerios.
  1118. //
  1119. // Detection: If the Current Dock State is the same as
  1120. // NewDockingState.DockingState then there is a potential for a duplicate.
  1121. // In order to also have a duplicate we must have an acpi already pointing
  1122. // to a profile different than the current one.
  1123. // This must also be the first ACPI change since we booted, therefore
  1124. // CurrentAcpiSn Should be Zero.
  1125. // In other words there must be at least one true match and none of the
  1126. // true matches can point to the current profile.
  1127. //
  1128. if (AliasList) {
  1129. while (i < AliasList->CurrentAliasCount) {
  1130. alias = &AliasList->Alias[i];
  1131. if (((alias->DockState & mask) != 0) &&
  1132. ((alias->DockState & mask) !=
  1133. (NewDockingState->DockingState & mask))) {
  1134. //
  1135. // This alias claims to be docked or undocked, but does not
  1136. // match the current state. Therefore skip it.
  1137. //
  1138. ;
  1139. } else if (alias->SerialLength != NewDockingState->SerialLength) {
  1140. //
  1141. // This alias has an incompatible serial number
  1142. //
  1143. ;
  1144. } else if (alias->SerialLength ==
  1145. RtlCompareMemory (NewDockingState->SerialNumber,
  1146. alias->SerialNumber,
  1147. alias->SerialLength)) {
  1148. //
  1149. // NB RtlCompareMemory can work with zero length memory
  1150. // addresses. This is a requirement here.
  1151. //
  1152. //
  1153. // This alias matches so mark the profile.
  1154. //
  1155. for (j = 0; j < ProfileList->CurrentProfileCount; j++) {
  1156. if (ProfileList->Profile[j].Id == alias->ProfileNumber) {
  1157. //
  1158. // Alias entries should never point to a pristine profile
  1159. //
  1160. ASSERT (!(ProfileList->Profile[j].Flags &
  1161. CM_HP_FLAGS_PRISTINE));
  1162. ProfileList->Profile[j].Flags |= CM_HP_FLAGS_TRUE_MATCH;
  1163. trueMatch = TRUE;
  1164. }
  1165. if ((CurrentDockState == NewDockingState->DockingState) &&
  1166. (NULL == CurrentAcpiSN)) {
  1167. //
  1168. // The dock state did not change during this acpi
  1169. // event; therefore, we might just have a duplicate
  1170. // on our hands.
  1171. //
  1172. dupDetect = TRUE;
  1173. }
  1174. if (alias->ProfileNumber == CurrentProfileNumber) {
  1175. //
  1176. // There exists an entry in the acpi alias table that
  1177. // if chosen would result in no change of Hardware
  1178. // Profile. Therefore, we should chose this one, and
  1179. // ignore the duplicate.
  1180. //
  1181. currentListed = TRUE;
  1182. }
  1183. }
  1184. }
  1185. i++;
  1186. }
  1187. }
  1188. if ((!dupDetect) &&
  1189. (NULL == CurrentAcpiSN) &&
  1190. (!trueMatch) &&
  1191. (CurrentDockState == NewDockingState->DockingState)) {
  1192. //
  1193. // (1) The docking state did not change,
  1194. // (2) the current profile has not yet, on this boot, been marked with
  1195. // an ACPI serial number.
  1196. // (3) There was no Alias match.
  1197. //
  1198. // Therefore we should keep the current profile regardless of it being
  1199. // aliasable.
  1200. //
  1201. keepCurrent = TRUE;
  1202. trueMatch = TRUE; // prevent pristine from being listed.
  1203. }
  1204. i = 0;
  1205. while (i < ProfileList->CurrentProfileCount) {
  1206. flags = ProfileList->Profile[i].Flags;
  1207. if (dupDetect) {
  1208. if (flags & CM_HP_FLAGS_TRUE_MATCH) {
  1209. if (currentListed) {
  1210. if (ProfileList->Profile[i].Id == CurrentProfileNumber) {
  1211. //
  1212. // Let this one live. This results in no change of
  1213. // profile number.
  1214. //
  1215. i++;
  1216. continue;
  1217. }
  1218. //
  1219. // Bounce any true matches that do not result in no change
  1220. // of profile.
  1221. //
  1222. ;
  1223. } else {
  1224. //
  1225. // We did not find the current one listed so we definately
  1226. // have a duplicate.
  1227. //
  1228. // Mark it as such. and list it live.
  1229. //
  1230. ProfileList->Profile[i].Flags |= CM_HP_FLAGS_DUPLICATE;
  1231. i++;
  1232. continue;
  1233. }
  1234. }
  1235. //
  1236. // Bounce all non True matches in a duplicate detected situation.
  1237. //
  1238. ;
  1239. } else if ((flags & CM_HP_FLAGS_PRISTINE) && !trueMatch) {
  1240. //
  1241. // Leave this one in the list
  1242. //
  1243. i++;
  1244. continue;
  1245. } else if (flags & CM_HP_FLAGS_ALIASABLE) {
  1246. //
  1247. // Leave this one in the list
  1248. //
  1249. ASSERT (! (flags & CM_HP_FLAGS_PRISTINE));
  1250. i++;
  1251. continue;
  1252. } else if (flags & CM_HP_FLAGS_TRUE_MATCH) {
  1253. //
  1254. // Leave this one in the list
  1255. //
  1256. i++;
  1257. continue;
  1258. } else if (keepCurrent &&
  1259. (ProfileList->Profile[i].Id == CurrentProfileNumber)) {
  1260. //
  1261. // Leave this one in the list
  1262. //
  1263. i++;
  1264. continue;
  1265. }
  1266. //
  1267. // discard this profile by (1) shifting remaining profiles in
  1268. // array to fill in the space of this discarded profile
  1269. // and (2) decrementing profile count
  1270. //
  1271. len = ProfileList->CurrentProfileCount - i - 1;
  1272. if (0 < len) {
  1273. RtlMoveMemory(&ProfileList->Profile[i],
  1274. &ProfileList->Profile[i+1],
  1275. sizeof(CM_HARDWARE_PROFILE) * len);
  1276. }
  1277. --ProfileList->CurrentProfileCount;
  1278. }
  1279. return status;
  1280. }
  1281. NTSTATUS
  1282. CmpMoveBiosAliasTable (
  1283. IN HANDLE IDConfigDB,
  1284. IN HANDLE CurrentInfo,
  1285. IN ULONG CurrentProfileNumber,
  1286. IN ULONG NewProfileNumber,
  1287. IN PWCHAR nameBuffer,
  1288. IN PCHAR valueBuffer,
  1289. IN ULONG bufferLen
  1290. )
  1291. /*++
  1292. Routine Description:
  1293. Search the Alias table for bios entries which match the current
  1294. docking state, and point from current profile number to new profile number.
  1295. Assumption: If the profile is cloned (therefore created by
  1296. CmpCloneHwProfile, and we have just moved the bios table to point
  1297. away from this entry, then we *should* be able to safely delete
  1298. the old hardware profile key.
  1299. (in both IDConfigDB\HardwareProfiles and CCS\HardwareProfiles
  1300. --*/
  1301. {
  1302. NTSTATUS status = STATUS_SUCCESS;
  1303. HANDLE alias = NULL;
  1304. HANDLE entry = NULL;
  1305. HANDLE hwprofile = NULL;
  1306. UNICODE_STRING name;
  1307. ULONG currentDockId;
  1308. ULONG currentSerialNumber;
  1309. ULONG len;
  1310. ULONG i;
  1311. OBJECT_ATTRIBUTES attributes;
  1312. KEY_FULL_INFORMATION keyInfo;
  1313. PKEY_BASIC_INFORMATION basicInfo;
  1314. PKEY_VALUE_FULL_INFORMATION value;
  1315. PAGED_CODE ();
  1316. value = (PKEY_VALUE_FULL_INFORMATION) valueBuffer;
  1317. basicInfo = (PKEY_BASIC_INFORMATION) valueBuffer;
  1318. //
  1319. // Extract the current Serial Number and DockID
  1320. //
  1321. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_SERIAL_NUMBER);
  1322. status = NtQueryValueKey(CurrentInfo,
  1323. &name,
  1324. KeyValueFullInformation,
  1325. valueBuffer,
  1326. bufferLen,
  1327. &len);
  1328. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  1329. status = STATUS_REGISTRY_CORRUPT;
  1330. goto Clean;
  1331. }
  1332. currentSerialNumber = * (PULONG) ((PUCHAR) value + value->DataOffset);
  1333. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKID);
  1334. status = NtQueryValueKey(CurrentInfo,
  1335. &name,
  1336. KeyValueFullInformation,
  1337. valueBuffer,
  1338. bufferLen,
  1339. &len);
  1340. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  1341. status = STATUS_REGISTRY_CORRUPT;
  1342. goto Clean;
  1343. }
  1344. currentDockId = * (PULONG) ((PUCHAR) value + value->DataOffset);
  1345. //
  1346. // Open a handle to the Alias information
  1347. //
  1348. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ALIAS);
  1349. InitializeObjectAttributes (&attributes,
  1350. &name,
  1351. OBJ_CASE_INSENSITIVE,
  1352. IDConfigDB,
  1353. NULL);
  1354. status = ZwOpenKey (&alias,
  1355. KEY_READ,
  1356. &attributes);
  1357. if (!NT_SUCCESS (status)) {
  1358. //
  1359. // So we don't have an alias table. This is ok, albeit a bit strange
  1360. //
  1361. status = STATUS_SUCCESS;
  1362. alias = NULL;
  1363. goto Clean;
  1364. }
  1365. status = ZwQueryKey (alias,
  1366. KeyFullInformation,
  1367. &keyInfo,
  1368. sizeof (keyInfo),
  1369. &len);
  1370. if (!NT_SUCCESS (status)) {
  1371. goto Clean;
  1372. }
  1373. ASSERT (0 < keyInfo.SubKeys);
  1374. //
  1375. // Iterrate the alias entries
  1376. //
  1377. for (i = 0; i < keyInfo.SubKeys; i++) {
  1378. //
  1379. // Get the first key in the list.
  1380. //
  1381. status = ZwEnumerateKey (alias,
  1382. i,
  1383. KeyBasicInformation,
  1384. basicInfo,
  1385. bufferLen - sizeof (UNICODE_NULL), // term 0
  1386. &len);
  1387. if (!NT_SUCCESS (status)) {
  1388. //
  1389. // This should never happen.
  1390. //
  1391. break;
  1392. }
  1393. basicInfo->Name [basicInfo->NameLength/sizeof(WCHAR)] = 0;
  1394. name.Length = (USHORT) basicInfo->NameLength;
  1395. name.MaximumLength = (USHORT) basicInfo->NameLength + sizeof (UNICODE_NULL);
  1396. name.Buffer = basicInfo->Name;
  1397. InitializeObjectAttributes (&attributes,
  1398. &name,
  1399. OBJ_CASE_INSENSITIVE,
  1400. alias,
  1401. NULL);
  1402. status = ZwOpenKey (&entry,
  1403. KEY_READ | KEY_WRITE,
  1404. &attributes);
  1405. if (!NT_SUCCESS (status)) {
  1406. break;
  1407. }
  1408. //
  1409. // Extract The Profile number to which this alias refers.
  1410. //
  1411. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PROFILE_NUMBER);
  1412. status = NtQueryValueKey(entry,
  1413. &name,
  1414. KeyValueFullInformation,
  1415. valueBuffer,
  1416. bufferLen,
  1417. &len);
  1418. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  1419. status = STATUS_REGISTRY_CORRUPT;
  1420. goto Clean;
  1421. }
  1422. if (CurrentProfileNumber != *(PULONG)((PUCHAR)value + value->DataOffset)) {
  1423. //
  1424. // Not a match
  1425. //
  1426. ZwClose (entry);
  1427. entry = NULL;
  1428. continue;
  1429. }
  1430. //
  1431. // Compare the Dock ID
  1432. //
  1433. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKID);
  1434. status = NtQueryValueKey(entry,
  1435. &name,
  1436. KeyValueFullInformation,
  1437. valueBuffer,
  1438. bufferLen,
  1439. &len);
  1440. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  1441. status = STATUS_REGISTRY_CORRUPT;
  1442. goto Clean;
  1443. }
  1444. if (currentDockId != * (PULONG) ((PUCHAR) value + value->DataOffset)) {
  1445. //
  1446. // Not a match
  1447. //
  1448. ZwClose (entry);
  1449. entry = NULL;
  1450. continue;
  1451. }
  1452. //
  1453. // Compare the SerialNumber
  1454. //
  1455. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_SERIAL_NUMBER);
  1456. status = NtQueryValueKey(entry,
  1457. &name,
  1458. KeyValueFullInformation,
  1459. valueBuffer,
  1460. bufferLen,
  1461. &len);
  1462. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  1463. status = STATUS_REGISTRY_CORRUPT;
  1464. goto Clean;
  1465. }
  1466. if (currentSerialNumber != *(PULONG)((PUCHAR)value + value->DataOffset)) {
  1467. //
  1468. // Not a match
  1469. //
  1470. ZwClose (entry);
  1471. entry = NULL;
  1472. continue;
  1473. }
  1474. //
  1475. // This must be a match.
  1476. // move the profile number
  1477. //
  1478. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PROFILE_NUMBER);
  1479. status = NtSetValueKey (entry,
  1480. &name,
  1481. 0,
  1482. REG_DWORD,
  1483. &NewProfileNumber,
  1484. sizeof (NewProfileNumber));
  1485. ASSERT (STATUS_SUCCESS == status);
  1486. ZwClose (entry);
  1487. entry = NULL;
  1488. //
  1489. // We most likely have left a dangling profile here.
  1490. // Try to attempt to clean it up.
  1491. //
  1492. // If this profile is cloned then we created it and can therefore
  1493. // get rid of it.
  1494. //
  1495. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_HARDWARE_PROFILES);
  1496. InitializeObjectAttributes (&attributes,
  1497. &name,
  1498. OBJ_CASE_INSENSITIVE,
  1499. IDConfigDB,
  1500. NULL);
  1501. status = ZwOpenKey (&hwprofile, KEY_READ | KEY_WRITE, &attributes);
  1502. if (!NT_SUCCESS (status)) {
  1503. hwprofile = NULL;
  1504. status = STATUS_REGISTRY_CORRUPT;
  1505. goto Clean;
  1506. }
  1507. swprintf (nameBuffer, L"%04d\0", CurrentProfileNumber);
  1508. RtlInitUnicodeString (&name, nameBuffer);
  1509. InitializeObjectAttributes (&attributes,
  1510. &name,
  1511. OBJ_CASE_INSENSITIVE,
  1512. hwprofile,
  1513. NULL);
  1514. status = ZwOpenKey (&entry, KEY_ALL_ACCESS, &attributes);
  1515. if (!NT_SUCCESS (status)) {
  1516. entry = NULL;
  1517. status = STATUS_REGISTRY_CORRUPT;
  1518. goto Clean;
  1519. }
  1520. //
  1521. // Test for the Cloned Bit.
  1522. //
  1523. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CLONED);
  1524. status = NtQueryValueKey(entry,
  1525. &name,
  1526. KeyValueFullInformation,
  1527. valueBuffer,
  1528. bufferLen,
  1529. &len);
  1530. if (!NT_SUCCESS (status) || (value->Type != REG_DWORD)) {
  1531. status = STATUS_REGISTRY_CORRUPT;
  1532. goto Clean;
  1533. }
  1534. if (*(PULONG)((PUCHAR)value + value->DataOffset)) {
  1535. //
  1536. // We cloned this one.
  1537. //
  1538. status = ZwDeleteKey (entry);
  1539. ASSERT (NT_SUCCESS (status));
  1540. ZwClose (entry);
  1541. ZwClose (hwprofile);
  1542. entry = hwprofile = NULL;
  1543. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CCS_HWPROFILE);
  1544. InitializeObjectAttributes (&attributes,
  1545. &name,
  1546. OBJ_CASE_INSENSITIVE,
  1547. NULL,
  1548. NULL);
  1549. status = ZwOpenKey (&hwprofile, KEY_READ | KEY_WRITE, &attributes);
  1550. if (!NT_SUCCESS (status)) {
  1551. hwprofile = NULL;
  1552. status = STATUS_REGISTRY_CORRUPT;
  1553. goto Clean;
  1554. }
  1555. swprintf (nameBuffer, L"%04d\0", CurrentProfileNumber);
  1556. status = CmDeleteKeyRecursive (hwprofile,
  1557. nameBuffer,
  1558. valueBuffer,
  1559. bufferLen,
  1560. TRUE);
  1561. ASSERT (NT_SUCCESS (status));
  1562. ZwClose (hwprofile);
  1563. hwprofile = NULL;
  1564. } else {
  1565. //
  1566. // We didn't clone this one.
  1567. // don't do anything else.
  1568. //
  1569. ZwClose (entry);
  1570. ZwClose (hwprofile);
  1571. entry = hwprofile = NULL;
  1572. }
  1573. CM_HARDWARE_PROFILE_STR_CCS_HWPROFILE;
  1574. }
  1575. Clean:
  1576. if (alias) {
  1577. ZwClose (alias);
  1578. }
  1579. if (entry) {
  1580. ZwClose (entry);
  1581. }
  1582. if (hwprofile) {
  1583. ZwClose (hwprofile);
  1584. }
  1585. return status;
  1586. }
  1587. NTSTATUS
  1588. CmpCloneHwProfile (
  1589. IN HANDLE IDConfigDB,
  1590. IN HANDLE Parent,
  1591. IN HANDLE OldProfile,
  1592. IN ULONG OldProfileNumber,
  1593. IN USHORT DockingState,
  1594. OUT PHANDLE NewProfile,
  1595. OUT PULONG NewProfileNumber
  1596. )
  1597. /*++
  1598. Routine Description
  1599. The given hardware profile key needs cloning.
  1600. Clone the key and then return the new profile.
  1601. Return:
  1602. STATUS_SUCCESS - if the profile has been cloned, in which case the new
  1603. profile key has been opened for read / write privs. The old profile
  1604. will be closed.
  1605. <unsuccessful> - for a given error. NewProfile is invalid and the Old
  1606. Profile has also been closed.
  1607. (Copied lovingly from CmpCloneControlSet)
  1608. --*/
  1609. {
  1610. NTSTATUS status = STATUS_SUCCESS;
  1611. UNICODE_STRING newProfileName;
  1612. UNICODE_STRING name;
  1613. UNICODE_STRING friendlyName;
  1614. UNICODE_STRING guidStr;
  1615. PCM_KEY_BODY oldProfileKey;
  1616. PCM_KEY_BODY newProfileKey;
  1617. OBJECT_ATTRIBUTES attributes;
  1618. PSECURITY_DESCRIPTOR security;
  1619. ULONG securityLength;
  1620. WCHAR nameBuffer [64];
  1621. HANDLE IDConfigDBEntry = NULL;
  1622. ULONG disposition;
  1623. ULONG value;
  1624. UUID uuid;
  1625. PKEY_BASIC_INFORMATION keyBasicInfo;
  1626. PKEY_FULL_INFORMATION keyFullInfo;
  1627. PKEY_VALUE_FULL_INFORMATION keyValueInfo;
  1628. ULONG length, profileSubKeys, i;
  1629. UCHAR valueBuffer[256];
  1630. HANDLE hardwareProfiles=NULL;
  1631. HANDLE profileEntry=NULL;
  1632. PAGED_CODE ();
  1633. keyFullInfo = (PKEY_FULL_INFORMATION) valueBuffer;
  1634. keyBasicInfo = (PKEY_BASIC_INFORMATION) valueBuffer;
  1635. keyValueInfo = (PKEY_VALUE_FULL_INFORMATION) valueBuffer;
  1636. *NewProfile = 0;
  1637. *NewProfileNumber = OldProfileNumber;
  1638. //
  1639. // Find the new profile number.
  1640. //
  1641. while (*NewProfileNumber < 200) {
  1642. (*NewProfileNumber)++;
  1643. swprintf (nameBuffer, L"%04d", *NewProfileNumber);
  1644. RtlInitUnicodeString (&newProfileName, nameBuffer);
  1645. InitializeObjectAttributes(&attributes,
  1646. &newProfileName,
  1647. OBJ_CASE_INSENSITIVE,
  1648. Parent,
  1649. NULL);
  1650. status = NtOpenKey (NewProfile,
  1651. KEY_READ | KEY_WRITE,
  1652. &attributes);
  1653. if (NT_SUCCESS (status)) {
  1654. NtClose (*NewProfile);
  1655. } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
  1656. status = STATUS_SUCCESS;
  1657. break;
  1658. } else {
  1659. break;
  1660. }
  1661. }
  1662. if (!NT_SUCCESS (status)) {
  1663. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHwProfile error finding new profile key %08lx\n", status));
  1664. goto Exit;
  1665. }
  1666. //
  1667. // Get the security descriptor from the old key to create the new clone one.
  1668. //
  1669. status = NtQuerySecurityObject (OldProfile,
  1670. DACL_SECURITY_INFORMATION,
  1671. NULL,
  1672. 0,
  1673. &securityLength);
  1674. if (STATUS_BUFFER_TOO_SMALL == status) {
  1675. security = ExAllocatePool (PagedPool, securityLength);
  1676. if (security != NULL) {
  1677. status = NtQuerySecurityObject(OldProfile,
  1678. DACL_SECURITY_INFORMATION,
  1679. security,
  1680. securityLength,
  1681. &securityLength);
  1682. if (!NT_SUCCESS (status)) {
  1683. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHwProfile"
  1684. " - NtQuerySecurityObject failed %08lx\n", status));
  1685. ExFreePool(security);
  1686. security=NULL;
  1687. }
  1688. }
  1689. } else {
  1690. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHwProfile"
  1691. " - NtQuerySecurityObject returned %08lx\n", status));
  1692. security=NULL;
  1693. }
  1694. //
  1695. // Create the new key
  1696. //
  1697. InitializeObjectAttributes (&attributes,
  1698. &newProfileName,
  1699. OBJ_CASE_INSENSITIVE,
  1700. Parent,
  1701. security);
  1702. status = NtCreateKey (NewProfile,
  1703. KEY_READ | KEY_WRITE,
  1704. &attributes,
  1705. 0,
  1706. NULL,
  1707. 0,
  1708. &disposition);
  1709. if (NULL != security) {
  1710. ExFreePool (security);
  1711. }
  1712. if (!NT_SUCCESS (status)) {
  1713. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHwProfile couldn't create Clone %08lx\n",status));
  1714. goto Exit;
  1715. }
  1716. //
  1717. // Check to make sure the key was created. If it already exists,
  1718. // something is wrong.
  1719. //
  1720. if (disposition != REG_CREATED_NEW_KEY) {
  1721. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHwProfile: Clone tree already exists!\n"));
  1722. //
  1723. // WARNNOTE:
  1724. // If somebody somehow managed to create a key in our way,
  1725. // they'll thwart duplication of the prestine. Tough luck.
  1726. // Claim it worked and go on.
  1727. //
  1728. status = STATUS_SUCCESS;
  1729. goto Exit;
  1730. }
  1731. //
  1732. // Create the IDConfigDB Entry
  1733. //
  1734. swprintf (nameBuffer, L"Hardware Profiles\\%04d", *NewProfileNumber);
  1735. RtlInitUnicodeString (&name, nameBuffer);
  1736. InitializeObjectAttributes (&attributes,
  1737. &name,
  1738. OBJ_CASE_INSENSITIVE,
  1739. IDConfigDB,
  1740. NULL);
  1741. status = NtCreateKey (&IDConfigDBEntry,
  1742. KEY_READ | KEY_WRITE,
  1743. &attributes,
  1744. 0,
  1745. NULL,
  1746. 0,
  1747. &disposition);
  1748. if (!NT_SUCCESS (status)) {
  1749. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHwProfile couldn't create Clone %08lx\n",status));
  1750. IDConfigDBEntry = NULL;
  1751. goto Exit;
  1752. }
  1753. //
  1754. // Determine the next PreferenceOrder for the new profile. (The
  1755. // PrefenceOrder for the new profile will be incrementally next from the
  1756. // greatest PreferenceOrder value of all the current profiles; assumes
  1757. // current set of PreferenceOrder values is incremental)
  1758. //
  1759. //
  1760. // Open the Hardware Profiles key
  1761. //
  1762. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_HARDWARE_PROFILES);
  1763. InitializeObjectAttributes (&attributes,
  1764. &name,
  1765. OBJ_CASE_INSENSITIVE,
  1766. IDConfigDB,
  1767. NULL);
  1768. status = ZwOpenKey (&hardwareProfiles,
  1769. KEY_READ,
  1770. &attributes);
  1771. if (!NT_SUCCESS (status)) {
  1772. hardwareProfiles = NULL;
  1773. goto Exit;
  1774. }
  1775. //
  1776. // Find the number of profile Sub Keys
  1777. //
  1778. status = ZwQueryKey (hardwareProfiles,
  1779. KeyFullInformation,
  1780. valueBuffer,
  1781. sizeof (valueBuffer),
  1782. &length);
  1783. if (!NT_SUCCESS (status)) {
  1784. goto Exit;
  1785. }
  1786. //
  1787. // At very least, the Pristine and the new profile key we just created,
  1788. // should be there.
  1789. //
  1790. profileSubKeys = keyFullInfo->SubKeys;
  1791. ASSERT (1 < profileSubKeys);
  1792. //
  1793. // Initialize the highest PreferenceOrder value found to -1.
  1794. //
  1795. value = -1;
  1796. //
  1797. // Iterrate the profiles
  1798. //
  1799. for (i = 0; i < profileSubKeys; i++) {
  1800. //
  1801. // Enumerate all profile subkeys, noting their PreferenceOrder values.
  1802. //
  1803. status = ZwEnumerateKey (hardwareProfiles,
  1804. i,
  1805. KeyBasicInformation,
  1806. valueBuffer,
  1807. sizeof(valueBuffer) - sizeof (UNICODE_NULL), //term 0
  1808. &length);
  1809. if(!NT_SUCCESS(status)) {
  1810. break;
  1811. }
  1812. //
  1813. // Zero-terminate the subkey name just in case.
  1814. //
  1815. keyBasicInfo->Name[keyBasicInfo->NameLength/sizeof(WCHAR)] = 0;
  1816. //
  1817. // If this is the Pristine, or the NewProfile key, ignore it.
  1818. //
  1819. if ((!_wtoi(keyBasicInfo->Name)) ||
  1820. ((ULONG)(_wtoi(keyBasicInfo->Name)) == *NewProfileNumber)) {
  1821. continue;
  1822. }
  1823. //
  1824. // Open this profile key
  1825. //
  1826. name.Length = (USHORT) keyBasicInfo->NameLength;
  1827. name.MaximumLength = (USHORT) keyBasicInfo->NameLength + sizeof (UNICODE_NULL);
  1828. name.Buffer = keyBasicInfo->Name;
  1829. InitializeObjectAttributes (&attributes,
  1830. &name,
  1831. OBJ_CASE_INSENSITIVE,
  1832. hardwareProfiles,
  1833. NULL);
  1834. status = ZwOpenKey (&profileEntry,
  1835. KEY_READ,
  1836. &attributes);
  1837. if (!NT_SUCCESS (status)) {
  1838. profileEntry = NULL;
  1839. continue;
  1840. }
  1841. //
  1842. // Extract The PreferenceOrder value for this Profile.
  1843. //
  1844. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PREFERENCE_ORDER);
  1845. status = NtQueryValueKey(profileEntry,
  1846. &name,
  1847. KeyValueFullInformation,
  1848. valueBuffer,
  1849. sizeof(valueBuffer),
  1850. &length);
  1851. if (!NT_SUCCESS (status) || (keyValueInfo->Type != REG_DWORD)) {
  1852. //
  1853. // No PreferenceOrder; continue on as best we can
  1854. //
  1855. ZwClose(profileEntry);
  1856. profileEntry=NULL;
  1857. continue;
  1858. }
  1859. //
  1860. // If this is a the highest PreferenceOrder so far, reassign value to
  1861. // this PreferenceOrder, OR assign it this valid PreferenceOrder if
  1862. // value is still unassigned.
  1863. //
  1864. if (((*(PULONG) ((PUCHAR)keyValueInfo + keyValueInfo->DataOffset)) > value) ||
  1865. (value == -1)) {
  1866. value = (* (PULONG) ((PUCHAR)keyValueInfo + keyValueInfo->DataOffset));
  1867. }
  1868. ZwClose(profileEntry);
  1869. profileEntry=NULL;
  1870. }
  1871. //
  1872. // Increment value one above the greatest PreferenceOrder found.
  1873. // (If no other profiles were found, (value+=1) == 0, the most preferred
  1874. // profile)
  1875. //
  1876. value += 1;
  1877. //
  1878. // Give the new profile a preference order.
  1879. //
  1880. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PREFERENCE_ORDER);
  1881. status = NtSetValueKey (IDConfigDBEntry,
  1882. &name,
  1883. 0,
  1884. REG_DWORD,
  1885. &value,
  1886. sizeof (value));
  1887. //
  1888. // Give the new profile a friendly name, based on the DockingState
  1889. //
  1890. status = CmpCreateHwProfileFriendlyName(IDConfigDB,
  1891. DockingState,
  1892. *NewProfileNumber,
  1893. &friendlyName);
  1894. if (NT_SUCCESS(status)) {
  1895. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_FRIENDLY_NAME);
  1896. status = NtSetValueKey (IDConfigDBEntry,
  1897. &name,
  1898. 0,
  1899. REG_SZ,
  1900. friendlyName.Buffer,
  1901. friendlyName.Length + sizeof(UNICODE_NULL));
  1902. RtlFreeUnicodeString(&friendlyName);
  1903. }
  1904. //
  1905. // Set the aliasable flag on the new "cloned profile" to be false
  1906. //
  1907. value = FALSE;
  1908. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_ALIASABLE);
  1909. status = NtSetValueKey (IDConfigDBEntry,
  1910. &name,
  1911. 0,
  1912. REG_DWORD,
  1913. &value,
  1914. sizeof (value));
  1915. //
  1916. // Set the cloned profile on the new "cloned profile" to be true;
  1917. //
  1918. value = TRUE;
  1919. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CLONED);
  1920. status = NtSetValueKey (IDConfigDBEntry,
  1921. &name,
  1922. 0,
  1923. REG_DWORD,
  1924. &value,
  1925. sizeof (value));
  1926. //
  1927. // Set the HwProfileGuid for the brand new profile
  1928. //
  1929. status = ExUuidCreate (&uuid);
  1930. if (NT_SUCCESS (status)) {
  1931. status = RtlStringFromGUID (&uuid, &guidStr);
  1932. if (NT_SUCCESS (status)) {
  1933. RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_HW_PROFILE_GUID);
  1934. status = NtSetValueKey (IDConfigDBEntry,
  1935. &name,
  1936. 0,
  1937. REG_SZ,
  1938. guidStr.Buffer,
  1939. guidStr.MaximumLength);
  1940. RtlFreeUnicodeString(&guidStr);
  1941. } else {
  1942. //
  1943. // What's a fella to do?
  1944. // let's just go on.
  1945. //
  1946. status = STATUS_SUCCESS;
  1947. }
  1948. } else {
  1949. //
  1950. // let's just go on.
  1951. //
  1952. status = STATUS_SUCCESS;
  1953. }
  1954. //
  1955. // Clone the key
  1956. //
  1957. // (Copied lovingly from CmpCloneControlSet)
  1958. //
  1959. //
  1960. status = ObReferenceObjectByHandle (OldProfile,
  1961. KEY_READ,
  1962. CmpKeyObjectType,
  1963. KernelMode,
  1964. (PVOID *)(&oldProfileKey),
  1965. NULL);
  1966. if (!NT_SUCCESS(status)) {
  1967. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHWProfile: couldn't reference CurrentHandle %08lx\n",
  1968. status));
  1969. goto Exit;
  1970. }
  1971. status = ObReferenceObjectByHandle (*NewProfile,
  1972. KEY_WRITE,
  1973. CmpKeyObjectType,
  1974. KernelMode,
  1975. (PVOID *)(&newProfileKey),
  1976. NULL);
  1977. if (!NT_SUCCESS(status)) {
  1978. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHWProfile: couldn't reference CurrentHandle %08lx\n",
  1979. status));
  1980. goto Exit;
  1981. }
  1982. CmpLockRegistryExclusive();
  1983. //
  1984. // Note: This copy tree command does not copy the values in the
  1985. // root keys. We are relying on this, since the values stored there
  1986. // are things like "pristine" which we do not wish to have moved to the
  1987. // new tree.
  1988. //
  1989. if (CmpCopyTree(oldProfileKey->KeyControlBlock->KeyHive,
  1990. oldProfileKey->KeyControlBlock->KeyCell,
  1991. newProfileKey->KeyControlBlock->KeyHive,
  1992. newProfileKey->KeyControlBlock->KeyCell)) {
  1993. //
  1994. // Set the max subkey name property for the new target key.
  1995. //
  1996. PCM_KEY_NODE SourceNode;
  1997. PCM_KEY_NODE DestNode;
  1998. SourceNode = (PCM_KEY_NODE)HvGetCell(oldProfileKey->KeyControlBlock->KeyHive,oldProfileKey->KeyControlBlock->KeyCell);
  1999. if( SourceNode != NULL ) {
  2000. DestNode = (PCM_KEY_NODE)HvGetCell(newProfileKey->KeyControlBlock->KeyHive,newProfileKey->KeyControlBlock->KeyCell);
  2001. if( DestNode != NULL ) {
  2002. //
  2003. // CmpCopyTree doesn't do this.
  2004. //
  2005. ASSERT_CELL_DIRTY(newProfileKey->KeyControlBlock->KeyHive,newProfileKey->KeyControlBlock->KeyCell);
  2006. DestNode->MaxNameLen = SourceNode->MaxNameLen;
  2007. DestNode->MaxClassLen = SourceNode->MaxClassLen;
  2008. HvReleaseCell(newProfileKey->KeyControlBlock->KeyHive,newProfileKey->KeyControlBlock->KeyCell);
  2009. CmpRebuildKcbCache(newProfileKey->KeyControlBlock);
  2010. status = STATUS_SUCCESS;
  2011. } else {
  2012. status = STATUS_INSUFFICIENT_RESOURCES;
  2013. }
  2014. HvReleaseCell(oldProfileKey->KeyControlBlock->KeyHive,oldProfileKey->KeyControlBlock->KeyCell);
  2015. } else {
  2016. status = STATUS_INSUFFICIENT_RESOURCES;
  2017. }
  2018. } else {
  2019. CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CM: CmpCloneHwProfile: tree copy failed.\n"));
  2020. status = STATUS_REGISTRY_CORRUPT;
  2021. }
  2022. CmpUnlockRegistry();
  2023. Exit:
  2024. NtClose (OldProfile);
  2025. if (IDConfigDBEntry) {
  2026. NtClose (IDConfigDBEntry);
  2027. }
  2028. if (hardwareProfiles) {
  2029. NtClose (hardwareProfiles);
  2030. }
  2031. if (!NT_SUCCESS (status)) {
  2032. if (*NewProfile) {
  2033. NtClose (*NewProfile);
  2034. }
  2035. }
  2036. return status;
  2037. }
  2038. NTSTATUS
  2039. CmDeleteKeyRecursive(
  2040. HANDLE hKeyRoot,
  2041. PWSTR Key,
  2042. PVOID TemporaryBuffer,
  2043. ULONG LengthTemporaryBuffer,
  2044. BOOLEAN ThisKeyToo
  2045. )
  2046. /*++
  2047. Routine Description:
  2048. Routine to recursively delete all subkeys under the given
  2049. key, including the key given.
  2050. Arguments:
  2051. hKeyRoot: Handle to root relative to which the key to be deleted is
  2052. specified.
  2053. Key: Root relative path of the key which is to be recursively deleted.
  2054. ThisKeyToo: Whether after deletion of all subkeys, this key itself is to
  2055. be deleted.
  2056. Return Value:
  2057. Status is returned.
  2058. --*/
  2059. {
  2060. ULONG ResultLength;
  2061. PKEY_BASIC_INFORMATION KeyInfo;
  2062. NTSTATUS Status;
  2063. UNICODE_STRING UnicodeString;
  2064. OBJECT_ATTRIBUTES Obja;
  2065. PWSTR SubkeyName;
  2066. HANDLE hKey;
  2067. //
  2068. // Initialize
  2069. //
  2070. KeyInfo = (PKEY_BASIC_INFORMATION)TemporaryBuffer;
  2071. //
  2072. // Open the key
  2073. //
  2074. RtlInitUnicodeString (&UnicodeString,Key);
  2075. InitializeObjectAttributes(&Obja,
  2076. &UnicodeString,
  2077. OBJ_CASE_INSENSITIVE,
  2078. hKeyRoot,
  2079. NULL);
  2080. Status = ZwOpenKey(&hKey,KEY_ALL_ACCESS,&Obja);
  2081. if( !NT_SUCCESS(Status) ) {
  2082. return(Status);
  2083. }
  2084. //
  2085. // Enumerate all subkeys of the current key. if any exist they should
  2086. // be deleted first. since deleting the subkey affects the subkey
  2087. // index, we always enumerate on subkeyindex 0
  2088. //
  2089. while(1) {
  2090. Status = ZwEnumerateKey(
  2091. hKey,
  2092. 0,
  2093. KeyBasicInformation,
  2094. TemporaryBuffer,
  2095. LengthTemporaryBuffer,
  2096. &ResultLength
  2097. );
  2098. if(!NT_SUCCESS(Status)) {
  2099. break;
  2100. }
  2101. //
  2102. // Zero-terminate the subkey name just in case.
  2103. //
  2104. KeyInfo->Name[KeyInfo->NameLength/sizeof(WCHAR)] = 0;
  2105. //
  2106. // Make a duplicate of the subkey name because the name is
  2107. // in TemporaryBuffer, which might get clobbered by recursive
  2108. // calls to this routine.
  2109. //
  2110. SubkeyName = ExAllocatePool (PagedPool,
  2111. ((wcslen (KeyInfo->Name) + 1) *
  2112. sizeof (WCHAR)));
  2113. if (!SubkeyName) {
  2114. Status = STATUS_INSUFFICIENT_RESOURCES;
  2115. break;
  2116. }
  2117. wcscpy(SubkeyName, KeyInfo->Name);
  2118. Status = CmDeleteKeyRecursive( hKey,
  2119. SubkeyName,
  2120. TemporaryBuffer,
  2121. LengthTemporaryBuffer,
  2122. TRUE);
  2123. ExFreePool(SubkeyName);
  2124. if(!NT_SUCCESS(Status)) {
  2125. break;
  2126. }
  2127. }
  2128. //
  2129. // Check the status, if the status is anything other than
  2130. // STATUS_NO_MORE_ENTRIES we failed in deleting some subkey,
  2131. // so we cannot delete this key too
  2132. //
  2133. if( Status == STATUS_NO_MORE_ENTRIES) {
  2134. Status = STATUS_SUCCESS;
  2135. }
  2136. if (!NT_SUCCESS (Status)) {
  2137. ZwClose(hKey);
  2138. return (Status);
  2139. }
  2140. //
  2141. // else delete the current key if asked to do so
  2142. //
  2143. if( ThisKeyToo ) {
  2144. Status = ZwDeleteKey (hKey);
  2145. }
  2146. ZwClose(hKey);
  2147. return(Status);
  2148. }
  2149. NTSTATUS
  2150. CmpCreateHwProfileFriendlyName (
  2151. IN HANDLE IDConfigDB,
  2152. IN ULONG DockingState,
  2153. IN ULONG NewProfileNumber,
  2154. OUT PUNICODE_STRING FriendlyName
  2155. )
  2156. /*++
  2157. Routine Description:
  2158. Create a new FriendlyName for a new Hardware Profile, given the DockState.
  2159. If a new profile name based on the DockState cannot be created, an attempt
  2160. is made to create a default FriendlyName based on NewProfileNumber. If
  2161. successful, a unicode string with the new profile friendlyName is created.
  2162. It is the responsibility of the caller to free this using
  2163. RtlFreeUnicodeString. If unsuccesful, no string is returned.
  2164. Arguments:
  2165. IDConfigDB: Handle to the IDConfigDB registry key.
  2166. DockingState: The Docking State of the profile for which the new
  2167. FriendlyName is being created. This should be one of:
  2168. HW_PROFILE_DOCKSTATE_DOCKED,
  2169. HW_PROFILE_DOCKSTATE_UNDOCKED, or
  2170. HW_PROFILE_DOCKSTATE_UNKNOWN
  2171. NewProfileNumber: The number of the new profile being created. If unable to
  2172. create a DockState specific FriendlyName, this value will
  2173. be used to create a (not-so) FriendlyName.
  2174. FriendlyName: Supplies a unicode string to receive the FriendlyName for this
  2175. new profile. The caller is expected to free this with
  2176. RtlFreeUnicodeString.
  2177. Return:
  2178. NTSTATUS code. Currently returns STATUS_SUCCESS, or STATUS_UNSUCCESSFUL.
  2179. Notes:
  2180. The new FriendlyName is generated from the DockState and appropriate
  2181. counter, and may not necessarily be unique among the existing Hardware
  2182. Profiles.
  2183. The naming scheme used here (including the localized strings in the kernel
  2184. message table) should be kept in sync with that provided to the user through
  2185. the Hardware Profile control panel applet.
  2186. --*/
  2187. {
  2188. NTSTATUS status = STATUS_SUCCESS;
  2189. ANSI_STRING ansiString;
  2190. UNICODE_STRING unicodeString;
  2191. UNICODE_STRING labelName, keyName;
  2192. PMESSAGE_RESOURCE_ENTRY messageEntry;
  2193. PKLDR_DATA_TABLE_ENTRY dataTableEntry;
  2194. ULONG messageId;
  2195. UCHAR valueBuffer[256];
  2196. WCHAR friendlyNameBuffer[MAX_FRIENDLY_NAME_LENGTH/sizeof(WCHAR)];
  2197. PKEY_VALUE_FULL_INFORMATION keyValueInfo;
  2198. ULONG length, index;
  2199. HANDLE hardwareProfiles=NULL;
  2200. OBJECT_ATTRIBUTES attributes;
  2201. PAGED_CODE ();
  2202. //
  2203. // Make sure we were given a place to put the FriendlyName
  2204. //
  2205. if (!FriendlyName) {
  2206. return STATUS_INVALID_PARAMETER;
  2207. }
  2208. //
  2209. // If we don't have a handle to IDConfigDB, try to assign a default
  2210. // FriendlyName on the way out.
  2211. //
  2212. if (!IDConfigDB) {
  2213. status = STATUS_INVALID_PARAMETER;
  2214. goto Exit;
  2215. }
  2216. //
  2217. // Determine the appropriate message to use, based on the DockState.
  2218. //
  2219. if ((DockingState & HW_PROFILE_DOCKSTATE_UNKNOWN) == HW_PROFILE_DOCKSTATE_UNKNOWN){
  2220. messageId = HARDWARE_PROFILE_UNKNOWN_STRING;
  2221. RtlInitUnicodeString(&labelName, CM_HARDWARE_PROFILE_STR_UNKNOWN);
  2222. } else if (DockingState & HW_PROFILE_DOCKSTATE_DOCKED) {
  2223. messageId = HARDWARE_PROFILE_DOCKED_STRING;
  2224. RtlInitUnicodeString(&labelName, CM_HARDWARE_PROFILE_STR_DOCKED);
  2225. } else if (DockingState & HW_PROFILE_DOCKSTATE_UNDOCKED) {
  2226. messageId = HARDWARE_PROFILE_UNDOCKED_STRING;
  2227. RtlInitUnicodeString(&labelName, CM_HARDWARE_PROFILE_STR_UNDOCKED);
  2228. } else {
  2229. messageId = HARDWARE_PROFILE_UNKNOWN_STRING;
  2230. RtlInitUnicodeString(&labelName, CM_HARDWARE_PROFILE_STR_UNKNOWN);
  2231. }
  2232. //
  2233. // Find the message entry in the kernel's own message table. KeLoaderBlock
  2234. // is available when we're creating hardware profiles during system
  2235. // initialization only; for profiles created thereafter, use the the first
  2236. // entry of the PsLoadedModuleList to get the image base of the kernel.
  2237. //
  2238. if (KeLoaderBlock) {
  2239. dataTableEntry = CONTAINING_RECORD(KeLoaderBlock->LoadOrderListHead.Flink,
  2240. KLDR_DATA_TABLE_ENTRY,
  2241. InLoadOrderLinks);
  2242. } else if (PsLoadedModuleList.Flink) {
  2243. dataTableEntry = CONTAINING_RECORD(PsLoadedModuleList.Flink,
  2244. KLDR_DATA_TABLE_ENTRY,
  2245. InLoadOrderLinks);
  2246. } else {
  2247. status = STATUS_UNSUCCESSFUL;
  2248. goto Exit;
  2249. }
  2250. status = RtlFindMessage(dataTableEntry->DllBase,
  2251. (ULONG_PTR)11, // RT_MESSAGETABLE
  2252. MAKELANGID(LANG_NEUTRAL,SUBLANG_SYS_DEFAULT), // System default language
  2253. messageId,
  2254. &messageEntry);
  2255. if (!NT_SUCCESS(status)) {
  2256. goto Exit;
  2257. }
  2258. if(!(messageEntry->Flags & MESSAGE_RESOURCE_UNICODE)) {
  2259. //
  2260. // If the message is not unicode, convert to unicode.
  2261. // Let the conversion routine allocate the buffer.
  2262. //
  2263. RtlInitAnsiString(&ansiString,messageEntry->Text);
  2264. status = RtlAnsiStringToUnicodeString(&unicodeString,&ansiString,TRUE);
  2265. } else {
  2266. //
  2267. // Message is already unicode. Make a copy.
  2268. //
  2269. status = RtlCreateUnicodeString(&unicodeString,(PWSTR)messageEntry->Text);
  2270. }
  2271. if(!NT_SUCCESS(status)) {
  2272. goto Exit;
  2273. }
  2274. //
  2275. // Strip the trailing CRLF.
  2276. //
  2277. if (unicodeString.Length > 2 * sizeof(WCHAR)) {
  2278. unicodeString.Length -= 2 * sizeof(WCHAR);
  2279. unicodeString.Buffer[unicodeString.Length / sizeof(WCHAR)] = UNICODE_NULL;
  2280. }
  2281. //
  2282. // Check that the size of the label, with any numeric tag that may
  2283. // potentially be added (up to 4 digits, preceded by a space) is not too
  2284. // big.
  2285. //
  2286. if ((unicodeString.Length + 5*sizeof(WCHAR) + sizeof(UNICODE_NULL)) >
  2287. MAX_FRIENDLY_NAME_LENGTH) {
  2288. status = STATUS_UNSUCCESSFUL;
  2289. goto Clean;
  2290. }
  2291. //
  2292. // Open the Hardware Profiles key.
  2293. //
  2294. RtlInitUnicodeString(&keyName, CM_HARDWARE_PROFILE_STR_HARDWARE_PROFILES);
  2295. InitializeObjectAttributes(&attributes,
  2296. &keyName,
  2297. OBJ_CASE_INSENSITIVE,
  2298. IDConfigDB,
  2299. NULL);
  2300. status = ZwOpenKey(&hardwareProfiles,
  2301. KEY_READ,
  2302. &attributes);
  2303. if (!NT_SUCCESS(status)) {
  2304. hardwareProfiles = NULL;
  2305. goto Clean;
  2306. }
  2307. //
  2308. // Retrieve the counter of FriendlyNames we have previously assigned, based
  2309. // on this DockState.
  2310. //
  2311. keyValueInfo = (PKEY_VALUE_FULL_INFORMATION) valueBuffer;
  2312. status = ZwQueryValueKey(hardwareProfiles,
  2313. &labelName,
  2314. KeyValueFullInformation,
  2315. valueBuffer,
  2316. sizeof(valueBuffer),
  2317. &length);
  2318. if (NT_SUCCESS(status) && (keyValueInfo->Type == REG_DWORD)) {
  2319. //
  2320. // Increment the counter.
  2321. //
  2322. index = (* (PULONG) ((PUCHAR)keyValueInfo + keyValueInfo->DataOffset));
  2323. index++;
  2324. } else {
  2325. //
  2326. // Missing or invalid counter value; start the counter at "1".
  2327. //
  2328. index = 1;
  2329. }
  2330. //
  2331. // Update the counter in the registry.
  2332. //
  2333. status = ZwSetValueKey(hardwareProfiles,
  2334. &labelName,
  2335. 0,
  2336. REG_DWORD,
  2337. &index,
  2338. sizeof(index));
  2339. if (!NT_SUCCESS(status)) {
  2340. goto Clean;
  2341. }
  2342. //
  2343. // Copy the FriendlyName, adding the index if necessary.
  2344. //
  2345. if ((messageId == HARDWARE_PROFILE_UNKNOWN_STRING) || (index > 1)) {
  2346. swprintf(friendlyNameBuffer, L"%s %u",
  2347. unicodeString.Buffer, index);
  2348. } else {
  2349. wcscpy(friendlyNameBuffer, unicodeString.Buffer);
  2350. }
  2351. Clean:
  2352. RtlFreeUnicodeString(&unicodeString);
  2353. if (hardwareProfiles!=NULL) {
  2354. ZwClose(hardwareProfiles);
  2355. }
  2356. Exit:
  2357. if (!NT_SUCCESS(status)) {
  2358. //
  2359. // If we failed to assign a counter-based FriendlyName for whatever
  2360. // reason, give the new profile a new (not so) friendly name as a last
  2361. // resort.
  2362. //
  2363. swprintf (friendlyNameBuffer, L"%04d", NewProfileNumber);
  2364. status = STATUS_SUCCESS;
  2365. }
  2366. //
  2367. // Create the unicode string to return to the caller.
  2368. //
  2369. if (!RtlCreateUnicodeString(FriendlyName, (PWSTR)friendlyNameBuffer)) {
  2370. status = STATUS_UNSUCCESSFUL;
  2371. }
  2372. return status;
  2373. } // CmpCreateHwProfileFriendlyName