/*++ Copyright (c) 1991 Microsoft Corporation Module Name: cmsysini.c Abstract: This module contains init support for the configuration manager, particularly the registry. Author: Bryan M. Willman (bryanwi) 26-Aug-1991 Revision History: Elliot Shmukler (t-ellios) 24-Aug-1998 Added CmpSaveBootControlSet & CmpDeleteCloneTree in order to perform some of the LKG work that has been moved into the kernel. Modified system initialization to permit operation and LKG control set saves without a CurrentControlSet clone. --*/ #include "cmp.h" #pragma hdrstop #include "arccodes.h" // paths #define INIT_SYSTEMROOT_HIVEPATH L"\\SystemRoot\\System32\\Config\\" #define INIT_REGISTRY_MASTERPATH L"\\REGISTRY\\" #if DBG // Logging support ULONG CmLogLevel = 1; ULONG CmLogSelect = CMS_DEFAULT; #endif extern PKPROCESS CmpSystemProcess; extern ERESOURCE CmpRegistryLock; extern FAST_MUTEX CmpKcbLock; extern FAST_MUTEX CmpPostLock; extern BOOLEAN CmFirstTime; // List of MACHINE hives to load. extern HIVE_LIST_ENTRY CmpMachineHiveList[]; #define SYSTEM_HIVE_INDEX 3 #define CLONE_HIVE_INDEX 6 #define SYSTEM_PATH L"\\registry\\machine\\system" // special keys for backwards compatibility with 1.0 #define HKEY_PERFORMANCE_TEXT (( HANDLE ) (ULONG_PTR)((LONG)0x80000050) ) #define HKEY_PERFORMANCE_NLSTEXT (( HANDLE ) (ULONG_PTR)((LONG)0x80000060) ) extern UNICODE_STRING CmpSystemFileName; extern UNICODE_STRING CmSymbolicLinkValueName; extern UNICODE_STRING CmpLoadOptions; // sys options from FW or boot.ini extern PWCHAR CmpProcessorControl; extern PWCHAR CmpControlSessionManager; // Object type definition support. // Key objects (CmpKeyObjectType) represent open instances of keys in the // registry. They do not have object names, rather, their names are // defined by the registry backing store. // Master Hive // The KEY_NODEs for \REGISTRY, \REGISTRY\MACHINE, and \REGISTRY\USER // are stored in a small memory only hive called the Master Hive. // All other hives have link nodes in this hive which point to them. extern PCMHIVE CmpMasterHive; extern BOOLEAN CmpNoMasterCreates; // Init False, Set TRUE after we're done to // prevent random creates in the // master hive, which is not backed // by a file. extern LIST_ENTRY CmpHiveListHead; // List of CMHIVEs // Addresses of object type descriptors: extern POBJECT_TYPE CmpKeyObjectType; // Define attributes that Key objects are not allowed to have. #define CMP_KEY_INVALID_ATTRIBUTES (OBJ_EXCLUSIVE |\ OBJ_PERMANENT) // Global control values // Write-Control: // CmpNoWrite is initially true. When set this way write and flush // do nothing, simply returning success. When cleared to FALSE, I/O // is enabled. This change is made after the I/O system is started // AND autocheck (chkdsk) has done its thing. extern BOOLEAN CmpNoWrite; extern BOOLEAN HvShutdownComplete; // Buffer used for quick-stash transfers in CmSetValueKey extern PUCHAR CmpStashBuffer; extern ULONG CmpStashBufferSize; // set to true if disk full when trying to save the changes made between system hive loading and registry initalization extern BOOLEAN CmpCannotWriteConfiguration; // Global "constants" extern UNICODE_STRING nullclass; // Private prototypes VOID CmpCreatePredefined( IN HANDLE Root, IN PWSTR KeyName, IN HANDLE PredefinedHandle ); VOID CmpCreatePerfKeys( VOID ); BOOLEAN CmpLinkKeyToHive( PWSTR KeyPath, PWSTR HivePath ); BOOLEAN CmpValidateAlternate( IN HANDLE FileHandle, IN PCMHIVE PrimaryHive ); NTSTATUS CmpCreateControlSet( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); NTSTATUS CmpCloneControlSet( VOID ); BOOLEAN CmpCreateObjectTypes( VOID ); BOOLEAN CmpCreateRegistryRoot( VOID ); BOOLEAN CmpCreateRootNode( IN PHHIVE Hive, IN PWSTR Name, OUT PHCELL_INDEX RootCellIndex ); VOID CmpFreeDriverList( IN PHHIVE Hive, IN PLIST_ENTRY DriverList ); VOID CmpInitializeHiveList( VOID ); BOOLEAN CmpInitializeSystemHive( IN PLOADER_PARAMETER_BLOCK LoaderBlock ); NTSTATUS CmpInterlockedFunction ( PWCHAR RegistryValueKey, VOID (*InterlockedFunction)(VOID) ); VOID CmpConfigureProcessors ( VOID ); #if i386 VOID KeOptimizeProcessorControlState ( VOID ); #endif NTSTATUS CmpAddDockingInfo ( IN HANDLE Key, IN PROFILE_PARAMETER_BLOCK * ProfileBlock ); NTSTATUS CmpAddAliasEntry ( IN HANDLE IDConfigDB, IN PROFILE_PARAMETER_BLOCK * ProfileBlock, IN ULONG ProfileNumber ); NTSTATUS CmpDeleteCloneTree(VOID); VOID CmpDiskFullWarning( VOID ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,CmInitSystem1) #pragma alloc_text(INIT,CmpCreateControlSet) #pragma alloc_text(INIT,CmpCloneControlSet) #pragma alloc_text(INIT,CmpCreateObjectTypes) #pragma alloc_text(INIT,CmpCreateRegistryRoot) #pragma alloc_text(INIT,CmpCreateRootNode) #pragma alloc_text(INIT,CmpInitializeSystemHive) #pragma alloc_text(INIT,CmGetSystemDriverList) #pragma alloc_text(INIT,CmpFreeDriverList) #pragma alloc_text(PAGE,CmpInitializeHiveList) #pragma alloc_text(PAGE,CmpLinkHiveToMaster) #pragma alloc_text(PAGE,CmpSetVersionData) #pragma alloc_text(PAGE,CmBootLastKnownGood) #pragma alloc_text(PAGE,CmpSaveBootControlSet) #pragma alloc_text(PAGE,CmpInitHiveFromFile) #pragma alloc_text(INIT,CmpIsLastKnownGoodBoot) #pragma alloc_text(PAGE,CmpLinkKeyToHive) #pragma alloc_text(PAGE,CmShutdownSystem) #pragma alloc_text(PAGE,CmpValidateAlternate) #pragma alloc_text(PAGE,CmpCreatePredefined) #pragma alloc_text(PAGE,CmpCreatePerfKeys) #pragma alloc_text(PAGE,CmpInterlockedFunction) #pragma alloc_text(PAGE,CmpConfigureProcessors) #pragma alloc_text(INIT,CmpAddDockingInfo) #pragma alloc_text(INIT,CmpAddAliasEntry) #pragma alloc_text(PAGE,CmpDeleteCloneTree) #endif BOOLEAN CmInitSystem1( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) /*++ Routine Description: This function is called as part of phase1 init, after the object manager has been inited, but before IoInit. It's purpose is to set up basic registry object operations, and transform data captured during boot into registry format (whether it was read from the SYSTEM hive file by the osloader or computed by recognizers.) After this call, Nt*Key calls work, but only part of the name space is available and any changes written must be held in memory. CmpMachineHiveList entries marked CM_PHASE_1 are available after return from this call, but writes must be held in memory. This function will: 1. Create the regisrty worker/lazy-write thread 2. Create the registry key object type 4. Create the master hive 5. Create the \REGISTRY node 6. Create a KEY object that refers to \REGISTRY 7. Create \REGISTRY\MACHINE node 8. Create the SYSTEM hive, fill in with data from loader 9. Create the HARDWARE hive, fill in with data from loader 10. Create: \REGISTRY\MACHINE\SYSTEM \REGISTRY\MACHINE\HARDWARE Both of which will be link nodes in the master hive. NOTE: We do NOT free allocated pool in failure case. This is because our caller is going to bugcheck anyway, and having the memory object to look at is useful. Arguments: LoaderBlock - supplies the LoaderBlock passed in from the OSLoader. By looking through the memory descriptor list we can find the SYSTEM hive which the OSLoader has placed in memory for us. Return Value: TRUE if all operations were successful, false if any failed. Bugchecks when something went wrong (CONFIG_INITIALIZATION_FAILED,4,.....) --*/ { HANDLE key1; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS status; NTSTATUS status2; PSECURITY_DESCRIPTOR SecurityDescriptor; PCMHIVE HardwareHive; PCMHIVE CloneHive; UNICODE_STRING NameString; PAGED_CODE(); CMLOG(CML_MAJOR, CMS_INIT) KdPrint(("CmInitSystem1\n")); // Initialize Names of all registry paths. // This simply initializes unicode strings so we don't have to bother // with it later. This can not fail. CmpInitializeRegistryNames(); // Compute registry global quota CmpComputeGlobalQuotaAllowed(); // Initialize the hive list head InitializeListHead(&CmpHiveListHead); // Initialize the global registry resource ExInitializeResource(&CmpRegistryLock); // Initialize the KCB tree mutex ExInitializeFastMutex(&CmpKcbLock); // Initialize the PostList mutex ExInitializeFastMutex(&CmpPostLock); // Initialize the cache CmpInitializeCache (); // Save the current process to allow us to attach to it later. CmpSystemProcess = &PsGetCurrentProcess()->Pcb; CmpLockRegistryExclusive(); // Create the Key object type. if (!CmpCreateObjectTypes()) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: CmpCreateObjectTypes failed\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,1,0,0); // could not registrate with object manager return FALSE; } // Create the master hive and initialize it. status = CmpInitializeHive(&CmpMasterHive, HINIT_CREATE, HIVE_VOLATILE, HFILE_TYPE_PRIMARY, // i.e. no logging, no alterate NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: CmpInitializeHive(master) failed\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,2,0,0); // could not initialize master hive return (FALSE); } // try to allocate a stash buffer. if we can't get 1 page this // early on, we're in deep trouble, so punt. CmpStashBuffer = ExAllocatePoolWithTag(PagedPool, HBLOCK_SIZE,'bSmC'); if (CmpStashBuffer == NULL) { KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,3,0,0); // odds against this are huge return FALSE; } CmpStashBufferSize = HBLOCK_SIZE; // Create the \REGISTRY node if (!CmpCreateRegistryRoot()) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: CmpCreateRegistryRoot failed\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,4,0,0); // could not create root of the registry return FALSE; } // --- 6. Create \REGISTRY\MACHINE and \REGISTRY\USER nodes --- // Get default security descriptor for the nodes we will create. SecurityDescriptor = CmpHiveRootSecurityDescriptor(); InitializeObjectAttributes(&ObjectAttributes, &CmRegistryMachineName, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, SecurityDescriptor); if (!NT_SUCCESS(NtCreateKey(&key1, KEY_READ | KEY_WRITE, &ObjectAttributes, 0, &nullclass, 0, NULL))) { ExFreePool(SecurityDescriptor); CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: NtCreateKey(MACHINE) failed\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,5,0,0); // could not create HKLM return FALSE; } NtClose(key1); InitializeObjectAttributes( &ObjectAttributes, &CmRegistryUserName, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, SecurityDescriptor ); if (!NT_SUCCESS(NtCreateKey(&key1, KEY_READ | KEY_WRITE, &ObjectAttributes, 0, &nullclass, 0, NULL))) { ExFreePool(SecurityDescriptor); CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: NtCreateKey(USER) failed\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,6,0,0); // could not create HKUSER return FALSE; } NtClose(key1); // --- 7. Create the SYSTEM hive, fill in with data from loader --- if (!CmpInitializeSystemHive(LoaderBlock)) { ExFreePool(SecurityDescriptor); CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmpInitSystem1: ")); KdPrint(("Hive allocation failure for SYSTEM\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,7,0,0); // could not create SystemHive return(FALSE); } // Create the symbolic link \Registry\Machine\System\CurrentControlSet status = CmpCreateControlSet(LoaderBlock); if (!NT_SUCCESS(status)) { KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,8,0,0); // could not create CurrentControlSet return(FALSE); } // Handle the copying of the CurrentControlSet to a Clone volatile // hive (but only if we really want to have a clone) #if CLONE_CONTROL_SET // Create the Clone temporary hive, link it into the master hive, // and make a symbolic link to it. status = CmpInitializeHive(&CloneHive, HINIT_CREATE, HIVE_VOLATILE, HFILE_TYPE_PRIMARY, NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmpInitSystem1: ")); KdPrint(("Could not initialize CLONE hive\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,9,0,0); // could not initialize clone hive return(FALSE); } if (CmpLinkHiveToMaster( &CmRegistrySystemCloneName, NULL, CloneHive, TRUE, SecurityDescriptor ) != STATUS_SUCCESS) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: CmpLinkHiveToMaster(Clone) failed\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,10,0,0); // could not link clone hive to master hive return FALSE; } CmpAddToHiveFileList(CloneHive); CmpMachineHiveList[CLONE_HIVE_INDEX].CmHive = CloneHive; CmpLinkKeyToHive(L"\\Registry\\Machine\\System\\Clone", L"\\Registry\\Machine\\CLONE\\CLONE"); status = CmpCloneControlSet();// Clone the current control set for the service controller ASSERT(NT_SUCCESS(status));// If this didn't work, it's bad, but not bad enough to fail the boot #endif // --- 8. Create the HARDWARE hive, fill in with data from loader --- status = CmpInitializeHive(&HardwareHive, HINIT_CREATE, HIVE_VOLATILE, HFILE_TYPE_PRIMARY, // i.e. no log, no alternate NULL, NULL, NULL, NULL, NULL, NULL); if (!NT_SUCCESS(status)) { ExFreePool(SecurityDescriptor); CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmpInitSystem1: ")); KdPrint(("Could not initialize HARDWARE hive\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,11,0,0); // could not initialize hardware hive return(FALSE); } // Allocate the root node if (CmpLinkHiveToMaster( &CmRegistryMachineHardwareName, NULL, HardwareHive, TRUE, SecurityDescriptor ) != STATUS_SUCCESS) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: CmpLinkHiveToMaster(Hardware) failed\n")); } KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,12,0,0); // could not link hardware hive to master hive return FALSE; } CmpAddToHiveFileList(HardwareHive); ExFreePool(SecurityDescriptor); CmpMachineHiveList[0].CmHive = HardwareHive; // put loader configuration tree data to our hardware registry. status = CmpInitializeHardwareConfiguration(LoaderBlock); if (!NT_SUCCESS(status)) { KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,13,0,0); // could not initialize hardware configuration return(FALSE); } CmpNoMasterCreates = TRUE; CmpUnlockRegistry(); // put machine dependant configuration data to our hardware registry. status = CmpInitializeMachineDependentConfiguration(LoaderBlock); // store boot loader command tail in registry RtlInitUnicodeString(&NameString, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control"); InitializeObjectAttributes(&ObjectAttributes, &NameString, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, NULL); status2 = NtOpenKey(&key1, KEY_WRITE, &ObjectAttributes); if (NT_SUCCESS(status2)) { RtlInitUnicodeString(&NameString, L"SystemStartOptions"); NtSetValueKey( key1, &NameString, 0, // TitleIndex REG_SZ, CmpLoadOptions.Buffer, CmpLoadOptions.Length ); NtClose(key1); } ExFreePool(CmpLoadOptions.Buffer); if (!NT_SUCCESS(status)) { KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,4,14,0,0); // could not open CurrentControlSet\\Control return(FALSE); } return TRUE; } VOID CmpInitializeHiveList( VOID ) /*++ Routine Description: This function is called to map hive files to hives. It both maps existing hives to files, and creates new hives from files. It operates on files in "\SYSTEMROOT\CONFIG". NOTE: MUST run in the context of the process that the CmpWorker thread runs in. Caller is expected to arrange this. NOTE: Will bugcheck on failure. Arguments: Return Value: NONE. --*/ { #define MAX_NAME 128 UCHAR FileBuffer[MAX_NAME]; UCHAR RegBuffer[MAX_NAME]; UNICODE_STRING TempName; UNICODE_STRING FileName; UNICODE_STRING RegName; USHORT FileStart; USHORT RegStart; ULONG i; PCMHIVE CmHive; BOOLEAN Allocate; HANDLE PrimaryHandle; HANDLE LogHandle; ULONG PrimaryDisposition; ULONG SecondaryDisposition; ULONG Length; NTSTATUS Status; PSECURITY_DESCRIPTOR SecurityDescriptor; BOOLEAN RegistryLocked = TRUE; PVOID ErrorParameters; ULONG ErrorResponse; ULONG ClusterSize; PAGED_CODE(); CMLOG(CML_MAJOR, CMS_INIT) KdPrint(("CmpInitializeHiveList\n")); CmpNoWrite = FALSE; FileName.MaximumLength = MAX_NAME; FileName.Length = 0; FileName.Buffer = (PWSTR)&(FileBuffer[0]); RegName.MaximumLength = MAX_NAME; RegName.Length = 0; RegName.Buffer = (PWSTR)&(RegBuffer[0]); RtlInitUnicodeString(&TempName, INIT_SYSTEMROOT_HIVEPATH); RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName); FileStart = FileName.Length; RtlInitUnicodeString(&TempName, INIT_REGISTRY_MASTERPATH); RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName); RegStart = RegName.Length; SecurityDescriptor = CmpHiveRootSecurityDescriptor(); for (i = 0; CmpMachineHiveList[i].Name != NULL; i++) { // Compute the name of the file, and the name to link to in // the registry. // REGISTRY RegName.Length = RegStart; RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].BaseName); RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName); // REGISTRY\MACHINE or REGISTRY\USER if (RegName.Buffer[ (RegName.Length / sizeof( WCHAR )) - 1 ] == '\\') { RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name); RtlAppendStringToString((PSTRING)&RegName, (PSTRING)&TempName); } // REGISTRY\[MACHINE|USER]\HIVE // \config RtlInitUnicodeString(&TempName, CmpMachineHiveList[i].Name); FileName.Length = FileStart; RtlAppendStringToString((PSTRING)&FileName, (PSTRING)&TempName); // \config\hive if (CmpMachineHiveList[i].CmHive == NULL) { // Hive has not been inited in any way. Allocate = TRUE; Status = CmpInitHiveFromFile(&FileName, CmpMachineHiveList[i].Flags, &CmHive, &Allocate, &RegistryLocked ); if ( (!NT_SUCCESS(Status)) || (CmHive->FileHandles[HFILE_TYPE_LOG] == NULL) ) { ErrorParameters = &FileName; ExRaiseHardError( STATUS_CANNOT_LOAD_REGISTRY_FILE, 1, 1, (PULONG_PTR)&ErrorParameters, OptionOk, &ErrorResponse ); continue; // If we are here it isn't SYSTEM, // so punt and go on. } CMLOG(CML_MINOR, CMS_INIT) { KdPrint(("CmpInitializeHiveList:\n")); KdPrint(("\tCmHive for '%ws' @", CmpMachineHiveList[i])); KdPrint(("%08lx", CmHive)); } // Link hive into master hive if (CmpLinkHiveToMaster( &RegName, NULL, CmHive, Allocate, SecurityDescriptor ) != STATUS_SUCCESS) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmpInitializeHiveList: ")); KdPrint(("CmpLinkHiveToMaster failed\n")); KdPrint(("\ti=%d s='%ws'\n", i, CmpMachineHiveList[i])); } KeBugCheckEx(CONFIG_LIST_FAILED,5,2,i,(ULONG_PTR)&RegName); } CmpAddToHiveFileList(CmHive); if (Allocate) { HvSyncHive((PHHIVE)CmHive); } } else { CmHive = CmpMachineHiveList[i].CmHive; if (!(CmHive->Hive.HiveFlags & HIVE_VOLATILE)) { // CmHive already exists. It is not an entirely volatile // hive (we do nothing for those.) // First, open the files (Primary and Alternate) that // back the hive. Stuff their handles into the CmHive // object. Force the size of the files to match the // in memory images. Call HvSyncHive to write changes // out to disk. Status = CmpOpenHiveFiles(&FileName, L".ALT", &PrimaryHandle, &LogHandle, &PrimaryDisposition, &SecondaryDisposition, TRUE, TRUE, &ClusterSize); if ( ( ! NT_SUCCESS(Status)) || (LogHandle == NULL) ) { ErrorParameters = &FileName; ExRaiseHardError( STATUS_CANNOT_LOAD_REGISTRY_FILE, 1, 1, (PULONG_PTR)&ErrorParameters, OptionOk, &ErrorResponse ); // WARNNOTE // We've just told the user that something essential, // like the SYSTEM hive, is hosed. Don't try to run, // we just risk destroying user data. Punt. KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO,5,3,i,Status); } CmHive->FileHandles[HFILE_TYPE_ALTERNATE] = LogHandle; CmHive->FileHandles[HFILE_TYPE_PRIMARY] = PrimaryHandle; Length = CmHive->Hive.Storage[Stable].Length + HBLOCK_SIZE; // When an in-memory hive is opened with no backing // file, ClusterSize is assumed to be 1. When the file // is opened later (for the SYSTEM hive) we need // to update this field in the hive if we are // booting from media where the cluster size > 1 if (CmHive->Hive.Cluster != ClusterSize) { // The cluster size is different than previous assumed. // Since a cluster in the dirty vector must be either // completely dirty or completely clean, go through the // dirty vector and mark all clusters that contain a dirty // logical sector as completely dirty. PRTL_BITMAP BitMap; ULONG Index; BitMap = &(CmHive->Hive.DirtyVector); for (Index = 0; Index < CmHive->Hive.DirtyVector.SizeOfBitMap; Index += ClusterSize) { if (!RtlAreBitsClear (BitMap, Index, ClusterSize)) { RtlSetBits (BitMap, Index, ClusterSize); } } // Update DirtyCount and Cluster CmHive->Hive.DirtyCount = RtlNumberOfSetBits(&CmHive->Hive.DirtyVector); CmHive->Hive.Cluster = ClusterSize; } if (!CmpFileSetSize( (PHHIVE)CmHive, HFILE_TYPE_PRIMARY, Length) || !CmpFileSetSize( (PHHIVE)CmHive, HFILE_TYPE_ALTERNATE, Length) ) { // WARNNOTE // Data written into the system hive since boot // cannot be written out, punt. CmpCannotWriteConfiguration = TRUE; //KeBugCheckEx(CANNOT_WRITE_CONFIGURATION,5,4,i,0); } ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0); if ( (PrimaryDisposition == FILE_CREATED) || (SecondaryDisposition == FILE_CREATED) || (!CmpValidateAlternate(LogHandle,CmHive))) { // Force all data to be written to the secondary (.alt) CmHive->FileHandles[HFILE_TYPE_EXTERNAL] = CmHive->FileHandles[HFILE_TYPE_ALTERNATE]; Status = HvWriteHive((PHHIVE)CmHive); CmHive->FileHandles[HFILE_TYPE_EXTERNAL] = NULL; if (!NT_SUCCESS(Status)) { // WARNNOTE // If we keep running here, we risk REALLY // screwing the user over (eating all their // data), while if we fail, we'll at least // fail clean. CmpCannotWriteConfiguration = TRUE; //KeBugCheckEx(CANNOT_WRITE_CONFIGURATION,5,5,0,0); } } CmpAddToHiveFileList(CmpMachineHiveList[i].CmHive); HvSyncHive((PHHIVE)CmHive); if( CmpCannotWriteConfiguration ) { // The system disk is full; Give user a chance to log-on and make room CmpDiskFullWarning(); CmpLazyFlush(); } } } } // for ExFreePool(SecurityDescriptor); CmpLinkKeyToHive(L"\\Registry\\Machine\\Security\\SAM", L"\\Registry\\Machine\\SAM\\SAM");// Create symbolic link from SECURITY hive into SAM hive. CmpCreatePerfKeys();// Create predefined handles. } BOOLEAN CmpCreateObjectTypes( VOID ) /*++ Routine Description: Create the Key object type Arguments: NONE. Return Value: TRUE == success, FALSE == failure. --*/ { NTSTATUS Status; OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; UNICODE_STRING TypeName; // Structure that describes the mapping of generic access rights to object // specific access rights for registry key objects. GENERIC_MAPPING CmpKeyMapping = { KEY_READ, KEY_WRITE, KEY_EXECUTE, KEY_ALL_ACCESS }; PAGED_CODE(); // --- Create the registry key object type --- // Initialize string descriptor. RtlInitUnicodeString(&TypeName, L"Key"); // Create key object type descriptor. RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); ObjectTypeInitializer.InvalidAttributes = CMP_KEY_INVALID_ATTRIBUTES; ObjectTypeInitializer.GenericMapping = CmpKeyMapping; ObjectTypeInitializer.ValidAccessMask = KEY_ALL_ACCESS; ObjectTypeInitializer.DefaultPagedPoolCharge = sizeof(CM_KEY_BODY); ObjectTypeInitializer.SecurityRequired = TRUE; ObjectTypeInitializer.PoolType = PagedPool; ObjectTypeInitializer.MaintainHandleCount = FALSE; ObjectTypeInitializer.UseDefaultObject = TRUE; ObjectTypeInitializer.DumpProcedure = NULL; ObjectTypeInitializer.OpenProcedure = NULL; ObjectTypeInitializer.CloseProcedure = CmpCloseKeyObject; ObjectTypeInitializer.DeleteProcedure = CmpDeleteKeyObject; ObjectTypeInitializer.ParseProcedure = CmpParseKey; ObjectTypeInitializer.SecurityProcedure = CmpSecurityMethod; ObjectTypeInitializer.QueryNameProcedure = CmpQueryKeyName; Status = ObCreateObjectType( &TypeName, &ObjectTypeInitializer, (PSECURITY_DESCRIPTOR)NULL, &CmpKeyObjectType ); if (!NT_SUCCESS(Status)) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmpCreateObjectTypes: ")); KdPrint(("ObCreateObjectType(Key) failed %08lx\n", Status)); } return FALSE; } return TRUE; } BOOLEAN CmpCreateRegistryRoot( VOID ) /*++ Routine Description: Manually create \REGISTRY in the master hive, create a key object to refer to it, and insert the key object into the root (\) of the object space. Arguments: None Return Value: TRUE == success, FALSE == failure --*/ { NTSTATUS Status; UNICODE_STRING NullString = { 0, 0, NULL }; HANDLE ObjHandle; PVOID ObjectPointer; PCM_KEY_BODY Object; OBJECT_ATTRIBUTES ObjectAttributes; PCM_KEY_CONTROL_BLOCK kcb; HCELL_INDEX RootCellIndex; PSECURITY_DESCRIPTOR SecurityDescriptor; PAGED_CODE(); // --- Create hive entry for \REGISTRY --- if (!CmpCreateRootNode( &(CmpMasterHive->Hive), L"REGISTRY", &RootCellIndex)) { return FALSE; } // --- Create a KEY object that refers to \REGISTRY --- // Create the object manager object // WARNING: \\REGISTRY is not in pool, so if anybody ever tries to // free it, we are in deep trouble. On the other hand, // this implies somebody has removed \\REGISTRY from the // root, so we're in trouble anyway. SecurityDescriptor = CmpHiveRootSecurityDescriptor(); InitializeObjectAttributes( &ObjectAttributes, &CmRegistryRootName, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, SecurityDescriptor ); Status = ObCreateObject( KernelMode, CmpKeyObjectType, &ObjectAttributes, UserMode, NULL, // Parse context sizeof(CM_KEY_BODY), 0, 0, (PVOID *)&Object ); ExFreePool(SecurityDescriptor); if (!NT_SUCCESS(Status)) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmpCreateRegistryRoot: ")); KdPrint(("ObCreateObject(\\REGISTRY) failed %08lx\n", Status)); } return FALSE; } // Create the key control block kcb = CmpCreateKeyControlBlock( &(CmpMasterHive->Hive), RootCellIndex, (PCM_KEY_NODE)HvGetCell(&CmpMasterHive->Hive,RootCellIndex), NULL, FALSE, &CmRegistryRootName ); if (kcb==NULL) { return(FALSE); } // Initialize the type specific body Object->Type = KEY_BODY_TYPE; Object->KeyControlBlock = kcb; Object->NotifyBlock = NULL; Object->Process = PsGetCurrentProcess(); ENLIST_KEYBODY_IN_KEYBODY_LIST(Object); // Put the object in the root directory Status = ObInsertObject( Object, NULL, (ACCESS_MASK)0, 0, NULL, &ObjHandle ); if (!NT_SUCCESS(Status)) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmpCreateRegistryRoot: ")); KdPrint(("ObInsertObject(\\REGISTRY) failed %08lx\n", Status)); } return FALSE; } // We cannot make the root permanent because registry objects in // general are not allowed to be. (They're stable via virtue of being // stored in the registry, not the object manager.) But we never // ever want the root to go away. So reference it. if (! NT_SUCCESS(Status = ObReferenceObjectByHandle( ObjHandle, KEY_READ, NULL, KernelMode, &ObjectPointer, NULL ))) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmpCreateRegistryRoot: ")); KdPrint(("ObReferenceObjectByHandle failed %08lx\n", Status)); } return FALSE; } return TRUE; } BOOLEAN CmpCreateRootNode( IN PHHIVE Hive, IN PWSTR Name, OUT PHCELL_INDEX RootCellIndex ) /*++ Routine Description: Manually create the root node of a hive. Arguments: Hive - pointer to a Hive (Hv level) control structure Name - pointer to a unicode name string RootCellIndex - supplies pointer to a variable to recieve the cell index of the created node. Return Value: TRUE == success, FALSE == failure --*/ { UNICODE_STRING temp; PCELL_DATA CellData; CM_KEY_REFERENCE Key; LARGE_INTEGER systemtime; PAGED_CODE(); // Allocate the node. RtlInitUnicodeString(&temp, Name); *RootCellIndex = HvAllocateCell( Hive, CmpHKeyNodeSize(Hive, &temp), Stable ); if (*RootCellIndex == HCELL_NIL) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmpCreateRootNode: HvAllocateCell failed\n")); } return FALSE; } Hive->BaseBlock->RootCell = *RootCellIndex; CellData = HvGetCell(Hive, *RootCellIndex); // Initialize the node CellData->u.KeyNode.Signature = CM_KEY_NODE_SIGNATURE; CellData->u.KeyNode.Flags = KEY_HIVE_ENTRY | KEY_NO_DELETE; KeQuerySystemTime(&systemtime); CellData->u.KeyNode.LastWriteTime = systemtime; // CellData->u.KeyNode.TitleIndex = 0; CellData->u.KeyNode.Parent = HCELL_NIL; CellData->u.KeyNode.SubKeyCounts[Stable] = 0; CellData->u.KeyNode.SubKeyCounts[Volatile] = 0; CellData->u.KeyNode.SubKeyLists[Stable] = HCELL_NIL; CellData->u.KeyNode.SubKeyLists[Volatile] = HCELL_NIL; CellData->u.KeyNode.ValueList.Count = 0; CellData->u.KeyNode.ValueList.List = HCELL_NIL; CellData->u.KeyNode.Security = HCELL_NIL; CellData->u.KeyNode.Class = HCELL_NIL; CellData->u.KeyNode.ClassLength = 0; CellData->u.KeyNode.MaxValueDataLen = 0; CellData->u.KeyNode.MaxNameLen = 0; CellData->u.KeyNode.MaxValueNameLen = 0; CellData->u.KeyNode.MaxClassLen = 0; CellData->u.KeyNode.NameLength = CmpCopyName(Hive, CellData->u.KeyNode.Name, &temp); if (CellData->u.KeyNode.NameLength < temp.Length) { CellData->u.KeyNode.Flags |= KEY_COMP_NAME; } Key.KeyHive = Hive; Key.KeyCell = *RootCellIndex; return TRUE; } NTSTATUS CmpLinkHiveToMaster( PUNICODE_STRING LinkName, HANDLE RootDirectory, PCMHIVE CmHive, BOOLEAN Allocate, PSECURITY_DESCRIPTOR SecurityDescriptor ) /*++ Routine Description: The existing, "free floating" hive CmHive describes is linked into the name space at the node named by LinkName. The node will be created. The hive is assumed to already have an appropriate root node. Arguments: LinkName - supplies a pointer to a unicode string which describes where in the registry name space the hive is to be linked. All components but the last must exist. The last must not. RootDirectory - Supplies the handle the LinkName is relative to. CmHive - pointer to a CMHIVE structure describing the hive to link in. Allocate - TRUE indicates that the root cell is to be created FALSE indicates the root cell already exists. SecurityDescriptor - supplies a pointer to the security descriptor to be placed on the hive root. Return Value: TRUE == success, FALSE == failure --*/ { OBJECT_ATTRIBUTES ObjectAttributes; HANDLE KeyHandle; CM_PARSE_CONTEXT ParseContext; NTSTATUS Status; PCM_KEY_BODY KeyBody; PAGED_CODE(); // Fill in special ParseContext to indicate that we are creating // a link node and opening or creating a root node. ParseContext.TitleIndex = 0; ParseContext.Class.Length = 0; ParseContext.Class.MaximumLength = 0; ParseContext.Class.Buffer = NULL; ParseContext.CreateOptions = 0; ParseContext.CreateLink = TRUE; ParseContext.ChildHive.KeyHive = &CmHive->Hive; if (Allocate) { // Creating a new root node ParseContext.ChildHive.KeyCell = HCELL_NIL; } else { // Opening an existing root node ParseContext.ChildHive.KeyCell = CmHive->Hive.BaseBlock->RootCell; } // Create a path to the hive InitializeObjectAttributes( &ObjectAttributes, LinkName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, (HANDLE)RootDirectory, SecurityDescriptor ); Status = ObOpenObjectByName( &ObjectAttributes, CmpKeyObjectType, KernelMode, NULL, KEY_READ | KEY_WRITE, (PVOID)&ParseContext, &KeyHandle ); if (!NT_SUCCESS(Status)) { CMLOG(CML_MAJOR, CMS_INIT_ERROR) { KdPrint(("CmpLinkHiveToMaster: ")); KdPrint(("ObOpenObjectByName() failed %08lx\n", Status)); KdPrint(("\tLinkName='%ws'\n", LinkName)); } return Status; } // Report the notification event Status = ObReferenceObjectByHandle(KeyHandle, 0, CmpKeyObjectType, KernelMode, (PVOID *)&KeyBody, NULL); ASSERT(NT_SUCCESS(Status)); if (NT_SUCCESS(Status)) { CmpReportNotify(KeyBody->KeyControlBlock, KeyBody->KeyControlBlock->KeyHive, KeyBody->KeyControlBlock->KeyCell, REG_NOTIFY_CHANGE_NAME); ObDereferenceObject((PVOID)KeyBody); } ZwClose(KeyHandle); return STATUS_SUCCESS; } VOID CmpSetVersionData( VOID ) /*++ Routine Description: Create \REGISTRY\MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion: CurrentVersion = VER_PRODUCTVERSION_STR // From ntverp.h CurrentBuildNumber = VER_PRODUCTBUILD // From ntverp.h CurrentType = "[Multiprocessor|Uniprocessor] // From NT_UP [Retail|Free|Checked]" // From DBG, DEVL SystemRoot = "[c:\nt]" NOTE: It is not worth bugchecking over this, so if it doesn't work, just fail. Arguments: Return Value: --*/ { ANSI_STRING AnsiString; UNICODE_STRING NameString; UNICODE_STRING ValueString; HANDLE key1, key2; UCHAR WorkString[128]; WCHAR ValueBuffer[128]; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS status; PUCHAR proctype; PUCHAR buildtype; PSECURITY_DESCRIPTOR SecurityDescriptor; PAGED_CODE(); SecurityDescriptor = CmpHiveRootSecurityDescriptor();// Get default security descriptor for the nodes we will create. // Create the key RtlInitUnicodeString(&NameString, L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft"); InitializeObjectAttributes(&ObjectAttributes, &NameString, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, SecurityDescriptor); status = NtCreateKey(&key1, KEY_CREATE_SUB_KEY, &ObjectAttributes, 0, &nullclass, 0, NULL); if (!NT_SUCCESS(status)) { #if DBG DbgPrint("CMINIT: CreateKey of %wZ failed - Status == %lx\n", &NameString, status); #endif ExFreePool(SecurityDescriptor); return; } RtlInitUnicodeString(&NameString, L"Windows NT"); InitializeObjectAttributes(&ObjectAttributes, &NameString, OBJ_CASE_INSENSITIVE, key1, SecurityDescriptor); status = NtCreateKey(&key2, KEY_SET_VALUE, &ObjectAttributes, 0, &nullclass, 0, NULL); NtClose(key1); RtlInitUnicodeString(&NameString, L"CurrentVersion"); InitializeObjectAttributes(&ObjectAttributes, &NameString, OBJ_CASE_INSENSITIVE, key2, SecurityDescriptor); status = NtCreateKey(&key1, KEY_SET_VALUE, &ObjectAttributes, 0, &nullclass, 0, NULL); NtClose(key2); ExFreePool(SecurityDescriptor); if (!NT_SUCCESS(status)) { #if DBG DbgPrint("CMINIT: CreateKey of %wZ failed - Status == %lx\n", &NameString, status); #endif return; } // Set the value entries for the key RtlInitUnicodeString(&NameString, L"CurrentVersion"); status = NtSetValueKey( key1, &NameString, 0, // TitleIndex REG_SZ, CmVersionString.Buffer, CmVersionString.Length + sizeof( UNICODE_NULL ) ); #if DBG if (!NT_SUCCESS(status)) { DbgPrint("CMINIT: SetValueKey of %wZ failed - Status == %lx\n", &NameString, status); } #endif (RtlFreeStringRoutine)( CmVersionString.Buffer ); RtlInitUnicodeString( &CmVersionString, NULL ); RtlInitUnicodeString(&NameString, L"CurrentBuildNumber"); sprintf(WorkString, "%u", NtBuildNumber & 0xFFFF); RtlInitAnsiString( &AnsiString, WorkString ); ValueString.Buffer = ValueBuffer; ValueString.Length = 0; ValueString.MaximumLength = sizeof( ValueBuffer ); RtlAnsiStringToUnicodeString( &ValueString, &AnsiString, FALSE ); status = NtSetValueKey( key1, &NameString, 0, // TitleIndex REG_SZ, ValueString.Buffer, ValueString.Length + sizeof( UNICODE_NULL ) ); #if DBG if (!NT_SUCCESS(status)) { DbgPrint("CMINIT: SetValueKey of %wZ failed - Status == %lx\n", &NameString, status); } #endif RtlInitUnicodeString(&NameString, L"CurrentType"); #if defined(NT_UP) proctype = "Uniprocessor"; #else proctype = "Multiprocessor"; #endif #if DBG buildtype = "Checked"; #else #if DEVL buildtype = "Free"; #else buildtype = "Retail"; #endif #endif sprintf( WorkString, "%s %s", proctype, buildtype ); RtlInitAnsiString( &AnsiString, WorkString ); ValueString.Buffer = ValueBuffer; ValueString.Length = 0; ValueString.MaximumLength = sizeof( ValueBuffer ); RtlAnsiStringToUnicodeString( &ValueString, &AnsiString, FALSE ); status = NtSetValueKey( key1, &NameString, 0, // TitleIndex REG_SZ, ValueString.Buffer, ValueString.Length + sizeof( UNICODE_NULL ) ); RtlInitUnicodeString(&NameString, L"CSDVersion"); if (CmCSDVersionString.Length != 0) { status = NtSetValueKey( key1, &NameString, 0, // TitleIndex REG_SZ, CmCSDVersionString.Buffer, CmCSDVersionString.Length + sizeof( UNICODE_NULL ) ); #if DBG if (!NT_SUCCESS(status)) { DbgPrint("CMINIT: SetValueKey of %wZ failed - Status == %lx\n", &NameString, status); } #endif (RtlFreeStringRoutine)( CmCSDVersionString.Buffer ); RtlInitUnicodeString( &CmCSDVersionString, NULL ); } else { status = NtDeleteValueKey(key1, &NameString); #if DBG if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) { DbgPrint("CMINIT: DeleteValueKey of %wZ failed - Status == %lx\n", &NameString, status); } #endif } RtlInitUnicodeString(&NameString, L"SystemRoot"); status = NtSetValueKey(key1, &NameString, 0, REG_SZ, NtSystemRoot.Buffer, NtSystemRoot.Length + sizeof(UNICODE_NULL)); #if DBG if (!NT_SUCCESS(status)) { DbgPrint("CMINIT: SetValueKey of %wZ failed - Status == %lx\n", &NameString, status); } #endif NtClose(key1); // Set each processor to it's optimal configuration. // Note: this call is performed interlocked such that the user // can disable this automatic configuration update. CmpInterlockedFunction(CmpProcessorControl, CmpConfigureProcessors); return; } NTSTATUS CmpInterlockedFunction ( PWCHAR RegistryValueKey, VOID (*InterlockedFunction)(VOID) ) /*++ Routine Description: This routine guards calling the InterlockedFunction in the passed RegistryValueKey. The RegistryValueKey will record the status of the first call to the InterlockedFunction. If the system crashes durning this call then ValueKey will be left in a state where the InterlockedFunction will not be called on subsequent attempts. Arguments: RegistryValueKey - ValueKey name for Control\Session Manager InterlockedFunction - Function to call Return Value: STATUS_SUCCESS - The interlocked function was successfully called --*/ { OBJECT_ATTRIBUTES objectAttributes; HANDLE hControl, hSession; UNICODE_STRING Name; UCHAR Buffer [sizeof(KEY_VALUE_PARTIAL_INFORMATION)+sizeof(ULONG)]; ULONG length, Value; NTSTATUS status; PAGED_CODE(); // Open CurrentControlSet InitializeObjectAttributes ( &objectAttributes, &CmRegistryMachineSystemCurrentControlSet, OBJ_CASE_INSENSITIVE, NULL, NULL ); status = NtOpenKey (&hControl, KEY_READ | KEY_WRITE, &objectAttributes); if (!NT_SUCCESS(status)) { return status; } // Open Control\Session Manager RtlInitUnicodeString (&Name, CmpControlSessionManager); InitializeObjectAttributes ( &objectAttributes, &Name, OBJ_CASE_INSENSITIVE, hControl, NULL ); status = NtOpenKey (&hSession, KEY_READ | KEY_WRITE, &objectAttributes ); NtClose (hControl); if (!NT_SUCCESS(status)) { return status; } // Read ValueKey to interlock operation with RtlInitUnicodeString (&Name, RegistryValueKey); status = NtQueryValueKey (hSession, &Name, KeyValuePartialInformation, Buffer, sizeof (Buffer), &length ); Value = 0; if (NT_SUCCESS(status)) { Value = ((PKEY_VALUE_PARTIAL_INFORMATION)Buffer)->Data[0]; } // Value 0 - Before InterlockedFunction // 1 - In the middle of InterlockedFunction // 2 - After InterlockedFunction // If the value is a 0, then we haven't tried calling this // interlocked function, set the value to a 1 and try it. // If the value is a 1, then we crased durning an execution // of the interlocked function last time, don't try it again. // If the value is a 2, then we called the interlocked function // before and it worked. Call it again this time. if (Value != 1) { if (Value != 2) { // This interlocked function is not known to work. Write // a 1 to this value so we can detect if we crash durning // this call. Value = 1; NtSetValueKey (hSession, &Name, 0L, REG_DWORD, &Value, sizeof (Value)); NtFlushKey (hSession); // wait until it's on the disk } InterlockedFunction(); if (Value != 2) { // The worker function didn't crash - update the value for // this interlocked function to 2. Value = 2; NtSetValueKey (hSession, &Name, 0L, REG_DWORD, &Value, sizeof (Value)); } } else { status = STATUS_UNSUCCESSFUL; } NtClose (hSession); return status; } VOID CmpConfigureProcessors ( VOID ) /*++ Routine Description: Set each processor to it's optimal settings for NT. --*/ { ULONG i; PAGED_CODE(); // Set each processor into its best NT configuration for (i=0; i < (ULONG)KeNumberProcessors; i++) { KeSetSystemAffinityThread((KAFFINITY) 1 << i); #if i386 // for now x86 only KeOptimizeProcessorControlState (); #endif } // Restore threads affinity KeRevertToUserAffinityThread(); } BOOLEAN CmpInitializeSystemHive( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) /*++ Routine Description: Initializes the SYSTEM hive based on the raw hive image passed in from the OS Loader. Arguments: LoaderBlock - Supplies a pointer to the Loader Block passed in by the OS Loader. Return Value: TRUE - it worked FALSE - it failed --*/ { PCMHIVE SystemHive; PVOID HiveImageBase; BOOLEAN Allocate=FALSE; PSECURITY_DESCRIPTOR SecurityDescriptor; NTSTATUS Status; STRING TempString; PAGED_CODE(); // capture tail of boot.ini line (load options, portable) RtlInitAnsiString( &TempString, LoaderBlock->LoadOptions ); CmpLoadOptions.Length = 0; CmpLoadOptions.MaximumLength = (TempString.Length+1)*sizeof(WCHAR); CmpLoadOptions.Buffer = ExAllocatePool(PagedPool, (TempString.Length+1)*sizeof(WCHAR)); if (CmpLoadOptions.Buffer == NULL) { KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,5,6,0,0); // odds against this are huge } RtlAnsiStringToUnicodeString(&CmpLoadOptions, &TempString, FALSE); CmpLoadOptions.Buffer[TempString.Length] = UNICODE_NULL; CmpLoadOptions.Length += sizeof(WCHAR); // move the loaded registry into the real registry HiveImageBase = LoaderBlock->RegistryBase; if (HiveImageBase == NULL) { // No memory descriptor for the hive, so we must recreate it. Status = CmpInitializeHive(&SystemHive, HINIT_CREATE, 0, HFILE_TYPE_ALTERNATE, NULL, NULL, NULL, NULL, NULL, &CmpSystemFileName); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmpInitializeSystemHive: ")); KdPrint(("Couldn't initialize newly allocated SYSTEM hive\n")); } return(FALSE); } Allocate = TRUE; } else { // There is a memory image for the hive, copy it and make it active Status = CmpInitializeHive(&SystemHive, HINIT_MEMORY, 0, HFILE_TYPE_ALTERNATE, HiveImageBase, NULL, NULL, NULL, NULL, &CmpSystemFileName); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmpInitializeSystemHive: ")); KdPrint(("Couldn't initialize OS Loader-loaded SYSTEM hive\n")); } return(FALSE); } if ( CmCheckRegistry(SystemHive, TRUE) != 0) { // We couldn't use the SYSTEM hive passed in from the loader. // We are dead. KeBugCheckEx(BAD_SYSTEM_CONFIG_INFO,5,7,0,0); } Allocate = FALSE; } // Create the link node SecurityDescriptor = CmpHiveRootSecurityDescriptor(); Status = CmpLinkHiveToMaster(&CmRegistryMachineSystemName, NULL, SystemHive, Allocate, SecurityDescriptor); ExFreePool(SecurityDescriptor); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CmInitSystem1: CmpLinkHiveToMaster(Hardware) failed\n")); } return(FALSE); } CmpMachineHiveList[SYSTEM_HIVE_INDEX].CmHive = SystemHive; return(TRUE); } PHANDLE CmGetSystemDriverList( VOID ) /*++ Routine Description: Traverses the current SERVICES subtree and creates the list of drivers to be loaded during Phase 1 initialization. Arguments: None Return Value: A pointer to an array of handles, each of which refers to a key in the \Services section of the control set. The caller will traverse this array and load and initialize the drivers described by the keys. The last key will be NULL. The array is allocated in Pool and should be freed by the caller. --*/ { OBJECT_ATTRIBUTES ObjectAttributes; HANDLE SystemHandle; UNICODE_STRING Name; NTSTATUS Status; PCM_KEY_BODY KeyBody; LIST_ENTRY DriverList; PHHIVE Hive; HCELL_INDEX RootCell; HCELL_INDEX ControlCell; ULONG DriverCount; PLIST_ENTRY Current; PHANDLE Handle; PBOOT_DRIVER_LIST_ENTRY DriverEntry; BOOLEAN Success; BOOLEAN AutoSelect; PAGED_CODE(); InitializeListHead(&DriverList); RtlInitUnicodeString(&Name, L"\\Registry\\Machine\\System"); InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, NULL); Status = NtOpenKey(&SystemHandle, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CM: CmGetSystemDriverList couldn't open registry key %wZ\n",&Name)); KdPrint(("CM: status %08lx\n", Status)); } return(NULL); } Status = ObReferenceObjectByHandle( SystemHandle, KEY_QUERY_VALUE, CmpKeyObjectType, KernelMode, (PVOID *)(&KeyBody), NULL ); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CM: CmGetSystemDriverList couldn't dereference Systemhandle\n")); KdPrint(("CM: status %08lx\n", Status)); } NtClose(SystemHandle); return(NULL); } CmpLockRegistryExclusive(); Hive = KeyBody->KeyControlBlock->KeyHive; RootCell = KeyBody->KeyControlBlock->KeyCell; // Now we have found out the PHHIVE and HCELL_INDEX of the root of the // SYSTEM hive, we can use all the same code that the OS Loader does. RtlInitUnicodeString(&Name, L"Current"); ControlCell = CmpFindControlSet(Hive, RootCell, &Name, &AutoSelect); if (ControlCell == HCELL_NIL) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CM: CmGetSystemDriverList couldn't find control set\n")); } CmpUnlockRegistry(); ObDereferenceObject((PVOID)KeyBody); NtClose(SystemHandle); return(NULL); } Success = CmpFindDrivers(Hive, ControlCell, SystemLoad, NULL, &DriverList); if (!Success) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CM: CmGetSystemDriverList couldn't find any valid drivers\n")); } CmpFreeDriverList(Hive, &DriverList); CmpUnlockRegistry(); ObDereferenceObject((PVOID)KeyBody); NtClose(SystemHandle); return(NULL); } if (!CmpSortDriverList(Hive, ControlCell, &DriverList)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CM: CmGetSystemDriverList couldn't sort driver list\n")); } CmpFreeDriverList(Hive, &DriverList); CmpUnlockRegistry(); ObDereferenceObject((PVOID)KeyBody); NtClose(SystemHandle); return(NULL); } if (!CmpResolveDriverDependencies(&DriverList)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CM: CmGetSystemDriverList couldn't resolve driver dependencies\n")); } CmpFreeDriverList(Hive, &DriverList); CmpUnlockRegistry(); ObDereferenceObject((PVOID)KeyBody); NtClose(SystemHandle); return(NULL); } CmpUnlockRegistry(); ObDereferenceObject((PVOID)KeyBody); NtClose(SystemHandle); // We now have a fully sorted and ordered list of drivers to be loaded // by IoInit. // Count the nodes in the list. Current = DriverList.Flink; DriverCount = 0; while (Current != &DriverList) { ++DriverCount; Current = Current->Flink; } Handle = (PHANDLE)ExAllocatePool(NonPagedPool, (DriverCount+1) * sizeof(HANDLE)); if (Handle == NULL) { KeBugCheckEx(CONFIG_INITIALIZATION_FAILED,5,8,0,0); // odds against this are huge } // Walk the list, opening each registry key and adding it to the // table of handles. Current = DriverList.Flink; DriverCount = 0; while (Current != &DriverList) { DriverEntry = CONTAINING_RECORD(Current, BOOT_DRIVER_LIST_ENTRY, Link); InitializeObjectAttributes(&ObjectAttributes, &DriverEntry->RegistryPath, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, NULL); Status = NtOpenKey(Handle+DriverCount, KEY_READ | KEY_WRITE, &ObjectAttributes); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT_ERROR) { KdPrint(("CM: CmGetSystemDriverList couldn't open driver ")); KdPrint(("key %wZ\n", &DriverEntry->RegistryPath)); KdPrint((" status %08lx\n",Status)); } } else { ++DriverCount; } Current = Current->Flink; } Handle[DriverCount] = NULL; return(Handle); } VOID CmpFreeDriverList( IN PHHIVE Hive, IN PLIST_ENTRY DriverList ) /*++ Routine Description: Walks down the driver list, freeing each node in it. Note that this calls the hive's free routine pointer to free the memory. Arguments: Hive - Supplies a pointer to the hive control structure. DriverList - Supplies a pointer to the head of the Driver List. Note that the head of the list is not actually freed, only all the entries in the list. Return Value: None. --*/ { PLIST_ENTRY Next; PLIST_ENTRY Current; PAGED_CODE(); Current = DriverList->Flink; while (Current != DriverList) { Next = Current->Flink; (Hive->Free)((PVOID)Current, sizeof(BOOT_DRIVER_NODE)); Current = Next; } } NTSTATUS CmpInitHiveFromFile( IN PUNICODE_STRING FileName, IN ULONG HiveFlags, OUT PCMHIVE *CmHive, IN OUT PBOOLEAN Allocate, IN OUT PBOOLEAN RegistryLocked ) /*++ Routine Description: This routine opens a file and log, allocates a CMHIVE, and initializes it. Arguments: FileName - Supplies name of file to be loaded. HiveFlags - Supplies hive flags to be passed to CmpInitializeHive CmHive - Returns pointer to initialized hive (if successful) Allocate - IN: if TRUE ok to allocate, if FALSE hive must exist (bug .log may get created) OUT: TRUE if actually created hive, FALSE if existed before Return Value: NTSTATUS --*/ { PCMHIVE NewHive; ULONG Disposition; ULONG SecondaryDisposition; HANDLE PrimaryHandle; HANDLE LogHandle; NTSTATUS Status; ULONG FileType; ULONG Operation; BOOLEAN Success; PAGED_CODE(); *CmHive = NULL; Status = CmpOpenHiveFiles(FileName, L".LOG", &PrimaryHandle, &LogHandle, &Disposition, &SecondaryDisposition, *Allocate, FALSE, NULL); if (!NT_SUCCESS(Status)) { return(Status); } if (LogHandle == NULL) { FileType = HFILE_TYPE_PRIMARY; } else { FileType = HFILE_TYPE_LOG; } if (Disposition == FILE_CREATED) { Operation = HINIT_CREATE; *Allocate = TRUE; } else { Operation = HINIT_FILE; *Allocate = FALSE; } if( !(*RegistryLocked) ) { // Registry should be locked exclusive // if not, lock it now and signal this to the caller CmpLockRegistryExclusive(); *RegistryLocked = TRUE; } Status = CmpInitializeHive(&NewHive, Operation, HiveFlags, FileType, NULL, PrimaryHandle, NULL, LogHandle, NULL, FileName ); if (!NT_SUCCESS(Status)) { ZwClose(PrimaryHandle); if (LogHandle != NULL) { ZwClose(LogHandle); } return(Status); } else { *CmHive = NewHive; return(STATUS_SUCCESS); } } NTSTATUS CmpAddDockingInfo ( IN HANDLE Key, IN PROFILE_PARAMETER_BLOCK * ProfileBlock ) /*++ Routine Description: Write DockID SerialNumber DockState and Capabilities intot the given registry key. --*/ { NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING name; ULONG value; PAGED_CODE (); value = ProfileBlock->DockingState; RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKING_STATE); status = NtSetValueKey (Key, &name, 0, REG_DWORD, &value, sizeof (value)); if (!NT_SUCCESS (status)) { return status; } value = ProfileBlock->Capabilities; RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_CAPABILITIES); status = NtSetValueKey (Key, &name, 0, REG_DWORD, &value, sizeof (value)); if (!NT_SUCCESS (status)) { return status; } value = ProfileBlock->DockID; RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_DOCKID); status = NtSetValueKey (Key, &name, 0, REG_DWORD, &value, sizeof (value)); if (!NT_SUCCESS (status)) { return status; } value = ProfileBlock->SerialNumber; RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_SERIAL_NUMBER); status = NtSetValueKey (Key, &name, 0, REG_DWORD, &value, sizeof (value)); if (!NT_SUCCESS (status)) { return status; } return status; } NTSTATUS CmpAddAliasEntry ( IN HANDLE IDConfigDB, IN PROFILE_PARAMETER_BLOCK * ProfileBlock, IN ULONG ProfileNumber ) /*++ Routine Description: Create an alias entry in the IDConfigDB database for the given hardware profile. Create the "Alias" key if it does not exist. Parameters: IDConfigDB - Pointer to "..\CurrentControlSet\Control\IDConfigDB" ProfileBlock - Description of the current Docking information ProfileNumber - --*/ { OBJECT_ATTRIBUTES attributes; NTSTATUS status = STATUS_SUCCESS; CHAR asciiBuffer [128]; WCHAR unicodeBuffer [128]; ANSI_STRING ansiString; UNICODE_STRING name; HANDLE aliasKey = NULL; HANDLE aliasEntry = NULL; ULONG value; ULONG disposition; ULONG aliasNumber = 0; PAGED_CODE (); // Find the Alias Key or Create it if it does not already exist. RtlInitUnicodeString (&name,CM_HARDWARE_PROFILE_STR_ALIAS); InitializeObjectAttributes (&attributes, &name, OBJ_CASE_INSENSITIVE, IDConfigDB, NULL); status = NtOpenKey (&aliasKey, KEY_READ | KEY_WRITE, &attributes); if (STATUS_OBJECT_NAME_NOT_FOUND == status) { status = NtCreateKey (&aliasKey, KEY_READ | KEY_WRITE, &attributes, 0, // no title NULL, // no class 0, // no options &disposition); } if (!NT_SUCCESS (status)) { aliasKey = NULL; goto Exit; } // Create an entry key while (aliasNumber < 200) { aliasNumber++; sprintf(asciiBuffer, "%04d", aliasNumber); RtlInitAnsiString(&ansiString, asciiBuffer); name.MaximumLength = sizeof(unicodeBuffer); name.Buffer = unicodeBuffer; status = RtlAnsiStringToUnicodeString(&name, &ansiString, FALSE); ASSERT (STATUS_SUCCESS == status); InitializeObjectAttributes(&attributes, &name, OBJ_CASE_INSENSITIVE, aliasKey, NULL); status = NtOpenKey (&aliasEntry, KEY_READ | KEY_WRITE, &attributes); if (NT_SUCCESS (status)) { NtClose (aliasEntry); } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) { status = STATUS_SUCCESS; break; } else { break; } } if (!NT_SUCCESS (status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: cmpCreateAliasEntry error finding new set %08lx\n", status)); } aliasEntry = 0; goto Exit; } status = NtCreateKey (&aliasEntry, KEY_READ | KEY_WRITE, &attributes, 0, NULL, 0, &disposition); if (!NT_SUCCESS (status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: cmpCreateAliasEntry error creating new set %08lx\n", status)); } aliasEntry = 0; goto Exit; } // Write the standard goo CmpAddDockingInfo (aliasEntry, ProfileBlock); // Write the Profile Number value = ProfileNumber; RtlInitUnicodeString (&name, CM_HARDWARE_PROFILE_STR_PROFILE_NUMBER); status = NtSetValueKey (aliasEntry, &name, 0, REG_DWORD, &value, sizeof (value)); Exit: if (aliasKey) { NtClose (aliasKey); } if (aliasEntry) { NtClose (aliasEntry); } return status; } NTSTATUS CmpHwprofileDefaultSelect ( IN PCM_HARDWARE_PROFILE_LIST ProfileList, OUT PULONG ProfileIndexToUse, IN PVOID Context ) { UNREFERENCED_PARAMETER (Context); * ProfileIndexToUse = 0; return STATUS_SUCCESS; } NTSTATUS CmpCreateControlSet( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) /*++ Routine Description: This routine sets up the symbolic links from \Registry\Machine\System\CurrentControlSet to \Registry\Machine\System\ControlSetNNN \Registry\Machine\System\CurrentControlSet\Hardware Profiles\Current to \Registry\Machine\System\ControlSetNNN\Hardware Profiles\NNNN based on the value of \Registry\Machine\System\Select:Current. and \Registry\Machine\System\ControlSetNNN\Control\IDConfigDB:CurrentConfig Arguments: None Return Value: status --*/ { UNICODE_STRING IDConfigDBName; UNICODE_STRING SelectName; UNICODE_STRING CurrentName; OBJECT_ATTRIBUTES Attributes; HANDLE SelectHandle; HANDLE CurrentHandle; HANDLE IDConfigDB = NULL; HANDLE CurrentProfile = NULL; HANDLE ParentOfProfile = NULL; CHAR AsciiBuffer[128]; WCHAR UnicodeBuffer[128]; UCHAR ValueBuffer[128]; ULONG ControlSet; ULONG HWProfile; PKEY_VALUE_FULL_INFORMATION Value; ANSI_STRING AnsiString; NTSTATUS Status; ULONG ResultLength; ULONG Disposition; BOOLEAN signalAcpiEvent = FALSE; PAGED_CODE(); RtlInitUnicodeString(&SelectName, L"\\Registry\\Machine\\System\\Select"); InitializeObjectAttributes(&Attributes, &SelectName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&SelectHandle, KEY_READ, &Attributes); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCreateControlSet: Couldn't open Select node %08lx\n",Status)); } return(Status); } RtlInitUnicodeString(&CurrentName, L"Current"); Status = NtQueryValueKey(SelectHandle, &CurrentName, KeyValueFullInformation, ValueBuffer, sizeof(ValueBuffer), &ResultLength); NtClose(SelectHandle); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCreateControlSet: Couldn't query Select value %08lx\n",Status)); } return(Status); } Value = (PKEY_VALUE_FULL_INFORMATION)ValueBuffer; ControlSet = *(PULONG)((PUCHAR)Value + Value->DataOffset); RtlInitUnicodeString(&CurrentName, L"\\Registry\\Machine\\System\\CurrentControlSet"); InitializeObjectAttributes(&Attributes, &CurrentName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateKey(&CurrentHandle, KEY_CREATE_LINK, &Attributes, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, &Disposition); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCreateControlSet: couldn't create CurrentControlSet %08lx\n",Status)); } return(Status); } // Check to make sure that the key was created, not just opened. Since // this key is always created volatile, it should never be present in // the hive when we boot. ASSERT(Disposition == REG_CREATED_NEW_KEY); // Create symbolic link for current hardware profile. sprintf(AsciiBuffer, "\\Registry\\Machine\\System\\ControlSet%03d", ControlSet); RtlInitAnsiString(&AnsiString, AsciiBuffer); CurrentName.MaximumLength = sizeof(UnicodeBuffer); CurrentName.Buffer = UnicodeBuffer; Status = RtlAnsiStringToUnicodeString(&CurrentName, &AnsiString, FALSE); Status = NtSetValueKey(CurrentHandle, &CmSymbolicLinkValueName, 0, REG_LINK, CurrentName.Buffer, CurrentName.Length); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCreateControlSet: couldn't create symbolic link ")); KdPrint(("to %wZ\n",&CurrentName)); KdPrint((" Status=%08lx\n",Status)); } return(Status); } // Determine the Current Hardware Profile Number RtlInitUnicodeString(&IDConfigDBName, L"Control\\IDConfigDB"); InitializeObjectAttributes(&Attributes, &IDConfigDBName, OBJ_CASE_INSENSITIVE, CurrentHandle, NULL); Status = NtOpenKey(&IDConfigDB, KEY_READ, &Attributes); NtClose(CurrentHandle); if (!NT_SUCCESS(Status)) { IDConfigDB = 0; goto Cleanup; } RtlInitUnicodeString(&CurrentName, L"CurrentConfig"); Status = NtQueryValueKey(IDConfigDB, &CurrentName, KeyValueFullInformation, ValueBuffer, sizeof(ValueBuffer), &ResultLength); if (!NT_SUCCESS(Status) || (((PKEY_VALUE_FULL_INFORMATION)ValueBuffer)->Type != REG_DWORD)) { goto Cleanup; } Value = (PKEY_VALUE_FULL_INFORMATION)ValueBuffer; HWProfile = *(PULONG)((PUCHAR)Value + Value->DataOffset); // We know now the config set that the user selected. // namely: HWProfile. RtlInitUnicodeString(&CurrentName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles"); InitializeObjectAttributes(&Attributes, &CurrentName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&ParentOfProfile, KEY_READ, &Attributes); if (!NT_SUCCESS (Status)) { ParentOfProfile = 0; goto Cleanup; } sprintf(AsciiBuffer, "%04d",HWProfile); RtlInitAnsiString(&AnsiString, AsciiBuffer); CurrentName.MaximumLength = sizeof(UnicodeBuffer); CurrentName.Buffer = UnicodeBuffer; Status = RtlAnsiStringToUnicodeString(&CurrentName, &AnsiString, FALSE); ASSERT (STATUS_SUCCESS == Status); InitializeObjectAttributes(&Attributes, &CurrentName, OBJ_CASE_INSENSITIVE, ParentOfProfile, NULL); Status = NtOpenKey (&CurrentProfile, KEY_READ | KEY_WRITE, &Attributes); if (!NT_SUCCESS (Status)) { CurrentProfile = 0; goto Cleanup; } // We need to determine if Value was selected by exact match // (TRUE_MATCH) or because the profile selected was aliasable. // If aliasable we need to manufacture another alias entry in the // alias table. // If the profile information is there and not failed then we should // mark the Docking state information: // (DockID, SerialNumber, DockState, and Capabilities) if (NULL != LoaderBlock->Extension) { PLOADER_PARAMETER_EXTENSION extension; extension = LoaderBlock->Extension; switch (extension->Profile.Status) { case HW_PROFILE_STATUS_PRISTINE_MATCH: // If the selected profile is pristine then we need to clone. Status = CmpCloneHwProfile (IDConfigDB, ParentOfProfile, CurrentProfile, HWProfile, extension->Profile.DockingState, &CurrentProfile, &HWProfile); if (!NT_SUCCESS (Status)) { CurrentProfile = 0; goto Cleanup; } RtlInitUnicodeString(&CurrentName, L"CurrentConfig"); Status = NtSetValueKey (IDConfigDB, &CurrentName, 0, REG_DWORD, &HWProfile, sizeof (HWProfile)); if (!NT_SUCCESS (Status)) { goto Cleanup; } // Fall through case HW_PROFILE_STATUS_ALIAS_MATCH: // Create the alias entry for this profile. Status = CmpAddAliasEntry (IDConfigDB, &extension->Profile, HWProfile); #if 0 // If we are booting in the undocked state, and creating a brand // new alias for this undocked state, we need to create an acpi // alias as well that points to this new profile. // BUGBUG. // We are not checking for the case where we may manufacture more // than one bios alias entry for the undocked state. // If we do create more than one, then we will of course create // more than one acpi alias entry for the undocked state. // Therefore we are assuming that when undocked the machine always // returns the same serial number. (Not a bad assumption.) // But we are also assuming that if we have more than one NT4 style // HW profile (which would allow the user to select such a profile // for each boot, and the user selected more than one for two // different undocked boots, then we will create two undocked acpi // aliases. (Perhaps that is what we actually want. I'm not sure, // but at least it's noted. if (HW_PROFILE_DOCKSTATE_UNDOCKED == extension->Profile.DockingState) { PROFILE_ACPI_DOCKING_STATE newDockState; // Note the definition of PROFILE_ACPI_DOCKING_STATE has a // WCHAR array of length 1. newDockState.DockingState = extension->Profile.DockingState; newDockState.SerialLength = 2; newDockState.SerialNumber[0] = L'\0'; Status = CmpAddAcpiAliasEntry (IDConfigDB, &newDockState, HWProfile, UnicodeBuffer, ValueBuffer, sizeof (ValueBuffer), TRUE); // Prevent Duplication ASSERT (NT_SUCCESS (Status)); } #endif // Fall through case HW_PROFILE_STATUS_TRUE_MATCH: // Write DockID, SerialNumber, DockState, and Caps into the current // Hardware profile. #ifndef DISABLE_CLEAN_HW_PROFILE_INFO RtlInitUnicodeString (&CurrentName, CM_HARDWARE_PROFILE_STR_DOCKING_STATE); NtDeleteValueKey (CurrentProfile, &CurrentName); RtlInitUnicodeString (&CurrentName, CM_HARDWARE_PROFILE_STR_CAPABILITIES); NtDeleteValueKey (CurrentProfile, &CurrentName); RtlInitUnicodeString (&CurrentName, CM_HARDWARE_PROFILE_STR_DOCKID); NtDeleteValueKey (CurrentProfile, &CurrentName); RtlInitUnicodeString (&CurrentName, CM_HARDWARE_PROFILE_STR_SERIAL_NUMBER); NtDeleteValueKey (CurrentProfile, &CurrentName); #endif RtlInitUnicodeString (&CurrentName, CM_HARDWARE_PROFILE_STR_CURRENT_DOCK_INFO); InitializeObjectAttributes (&Attributes, &CurrentName, OBJ_CASE_INSENSITIVE, IDConfigDB, NULL); Status = NtCreateKey (&CurrentHandle, KEY_READ | KEY_WRITE, &Attributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); ASSERT (STATUS_SUCCESS == Status); Status = CmpAddDockingInfo (CurrentHandle, &extension->Profile); if (HW_PROFILE_DOCKSTATE_UNDOCKED == extension->Profile.DockingState) { signalAcpiEvent = TRUE; } break; case HW_PROFILE_STATUS_SUCCESS: case HW_PROFILE_STATUS_FAILURE: break; default: ASSERTMSG ("Invalid Profile status state", FALSE); } } // Create the symbolic link. RtlInitUnicodeString(&CurrentName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current"); InitializeObjectAttributes(&Attributes, &CurrentName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateKey(&CurrentHandle, KEY_CREATE_LINK, &Attributes, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, &Disposition); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCreateControlSet: couldn't create Hardware Profile\\Current %08lx\n",Status)); } } else { ASSERT(Disposition == REG_CREATED_NEW_KEY); sprintf(AsciiBuffer, "\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\%04d",HWProfile); RtlInitAnsiString(&AnsiString, AsciiBuffer); CurrentName.MaximumLength = sizeof(UnicodeBuffer); CurrentName.Buffer = UnicodeBuffer; Status = RtlAnsiStringToUnicodeString(&CurrentName, &AnsiString, FALSE); ASSERT (STATUS_SUCCESS == Status); Status = NtSetValueKey(CurrentHandle, &CmSymbolicLinkValueName, 0, REG_LINK, CurrentName.Buffer, CurrentName.Length); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCreateControlSet: couldn't create symbolic link ")); KdPrint(("to %wZ\n",&CurrentName)); KdPrint((" Status=%08lx\n",Status)); } } } if (signalAcpiEvent) { // We are booting in the undocked state. // This is interesting because our buddies in PnP cannot tell // us when we are booting without a dock. They can only tell // us when they see a hot undock. // Therefore in the interest of matching a boot undocked with // a hot undock, we need to simulate an acpi undock event. PROFILE_ACPI_DOCKING_STATE newDockState; HANDLE profile; BOOLEAN changed; newDockState.DockingState = HW_PROFILE_DOCKSTATE_UNDOCKED; newDockState.SerialLength = 2; newDockState.SerialNumber[0] = L'\0'; Status = CmSetAcpiHwProfile (&newDockState, CmpHwprofileDefaultSelect, NULL, &profile, &changed); ASSERT (NT_SUCCESS (Status)); NtClose (profile); } Cleanup: if (IDConfigDB) { NtClose (IDConfigDB); } if (CurrentProfile) { NtClose (CurrentProfile); } if (ParentOfProfile) { NtClose (ParentOfProfile); } return(STATUS_SUCCESS); } NTSTATUS CmpCloneControlSet( VOID ) /*++ Routine Description: First, create a new hive, \registry\machine\clone, which will be HIVE_VOLATILE. Second, link \Registry\Machine\System\Clone to it. Third, tree copy \Registry\Machine\System\CurrentControlSet into \Registry\Machine\System\Clone (and thus into the clone hive.) When the service controller is done with the clone hive, it can simply NtUnloadKey it to free its storage. Arguments: None. \Registry\Machine\System\CurrentControlSet must already exist. Return Value: NTSTATUS --*/ { UNICODE_STRING Current; UNICODE_STRING Clone; HANDLE CurrentHandle; HANDLE CloneHandle; OBJECT_ATTRIBUTES Attributes; NTSTATUS Status; PCM_KEY_BODY CurrentKey; PCM_KEY_BODY CloneKey; ULONG Disposition; PSECURITY_DESCRIPTOR Security; ULONG SecurityLength; PAGED_CODE(); RtlInitUnicodeString(&Current, L"\\Registry\\Machine\\System\\CurrentControlSet"); RtlInitUnicodeString(&Clone, L"\\Registry\\Machine\\System\\Clone"); InitializeObjectAttributes(&Attributes, &Current, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&CurrentHandle, KEY_READ, &Attributes); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCloneControlSet couldn't open CurrentControlSet %08lx\n",Status)); } return(Status); } // Get the security descriptor from the key so we can create the clone // tree with the correct ACL. Status = NtQuerySecurityObject(CurrentHandle, DACL_SECURITY_INFORMATION, NULL, 0, &SecurityLength); if (Status==STATUS_BUFFER_TOO_SMALL) { Security=ExAllocatePool(PagedPool,SecurityLength); if (Security!=NULL) { Status = NtQuerySecurityObject(CurrentHandle, DACL_SECURITY_INFORMATION, Security, SecurityLength, &SecurityLength); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCloneControlSet - NtQuerySecurityObject failed %08lx\n",Status)); } ExFreePool(Security); Security=NULL; } } } else { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCloneControlSet - NtQuerySecurityObject returned %08lx\n",Status)); } Security=NULL; } InitializeObjectAttributes(&Attributes, &Clone, OBJ_CASE_INSENSITIVE, NULL, Security); Status = NtCreateKey(&CloneHandle, KEY_READ | KEY_WRITE, &Attributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); if (Security!=NULL) { ExFreePool(Security); } if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCloneControlSet couldn't create Clone %08lx\n",Status)); } NtClose(CurrentHandle); return(Status); } // Check to make sure the key was created. If it already exists, // something is wrong. if (Disposition != REG_CREATED_NEW_KEY) { CMLOG(CML_BUGCHECK, CMS_INIT) { // KdPrint(("CM: CmpCloneControlSet: Clone tree already exists!\n")); } // WARNNOTE: // If somebody somehow managed to create a key in our way, // they'll thwart last known good. Tough luck. // Claim it worked and go on. Status = STATUS_SUCCESS; goto Exit; } Status = ObReferenceObjectByHandle(CurrentHandle, KEY_READ, CmpKeyObjectType, KernelMode, (PVOID *)(&CurrentKey), NULL); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCloneControlSet: couldn't reference CurrentHandle %08lx\n",Status)); } goto Exit; } Status = ObReferenceObjectByHandle(CloneHandle, KEY_WRITE, CmpKeyObjectType, KernelMode, (PVOID *)(&CloneKey), NULL); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCloneControlSet: couldn't reference CurrentHandle %08lx\n",Status)); } ObDereferenceObject((PVOID)CurrentKey); goto Exit; } CmpLockRegistryExclusive(); if (CmpCopyTree(CurrentKey->KeyControlBlock->KeyHive, CurrentKey->KeyControlBlock->KeyCell, CloneKey->KeyControlBlock->KeyHive, CloneKey->KeyControlBlock->KeyCell)) { // Set the max subkey name property for the new target key. CloneKey->KeyControlBlock->KeyNode->MaxNameLen = CurrentKey->KeyControlBlock->KeyNode->MaxNameLen; Status = STATUS_SUCCESS; } else { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpCloneControlSet: tree copy failed.\n")); } Status = STATUS_REGISTRY_CORRUPT; } CmpUnlockRegistry(); ObDereferenceObject((PVOID)CurrentKey); ObDereferenceObject((PVOID)CloneKey); Exit: NtClose(CurrentHandle); NtClose(CloneHandle); return(Status); } NTSTATUS CmpSaveBootControlSet(USHORT ControlSetNum) /*++ Routine Description: This routine is responsible for saving the control set used to accomplish the latest boot into a different control set (presumably so that the different control set may be marked as the LKG control set). This routine is called from NtInitializeRegistry when a boot is accepted via that routine. Arguments: ControlSetNum - The number of the control set that will be used to save the boot control set. Return Value: NTSTATUS result code from call, among the following: STATUS_SUCCESS - everything worked perfectly STATUS_REGISTRY_CORRUPT - could not save the boot control set, it is likely that the copy or sync operation used for this save failed and some part of the boot control set was not saved. --*/ { UNICODE_STRING SavedBoot, Boot; HANDLE BootHandle, SavedBootHandle; OBJECT_ATTRIBUTES Attributes; NTSTATUS Status; PCM_KEY_BODY BootKey, SavedBootKey; ULONG Disposition; PSECURITY_DESCRIPTOR Security; ULONG SecurityLength; BOOLEAN CopyRet; WCHAR Buffer[128]; // Figure out where the boot control set is #if CLONE_CONTROL_SET // If we have cloned the control set, then use the clone // since it is guaranteed to have an untouched copy of the // boot control set RtlInitUnicodeString(&Boot, L"\\Registry\\Machine\\System\\Clone"); InitializeObjectAttributes(&Attributes, &Boot, OBJ_CASE_INSENSITIVE, NULL, NULL); #else // If we are not using the clone, then just use the // current control set. InitializeObjectAttributes(&Attributes, &CmRegistryMachineSystemCurrentControlSet, OBJ_CASE_INSENSITIVE, NULL, NULL); #endif // Open the boot control set Status = NtOpenKey(&BootHandle, KEY_READ, &Attributes); if (!NT_SUCCESS(Status)) return(Status); // We may be saving the boot control set into a brand new // tree that we will create. If this is true, then we will // need to create the root node of this tree below // and give it the right security descriptor. So, we fish // the security descriptor out of the root node of the // boot control set tree. Status = NtQuerySecurityObject(BootHandle, DACL_SECURITY_INFORMATION, NULL, 0, &SecurityLength); if (Status==STATUS_BUFFER_TOO_SMALL) { Security=ExAllocatePool(PagedPool,SecurityLength); if (Security!=NULL) { Status = NtQuerySecurityObject(BootHandle, DACL_SECURITY_INFORMATION, Security, SecurityLength, &SecurityLength); if (!NT_SUCCESS(Status)) { ExFreePool(Security); Security=NULL; } } } else { Security=NULL; } // Now, create the path of the control set we will be saving to swprintf(Buffer, L"\\Registry\\Machine\\System\\ControlSet%03d", ControlSetNum); RtlInitUnicodeString(&SavedBoot, Buffer); // Open/Create the control set to which we are saving InitializeObjectAttributes(&Attributes, &SavedBoot, OBJ_CASE_INSENSITIVE, NULL, Security); Status = NtCreateKey(&SavedBootHandle, KEY_READ | KEY_WRITE, &Attributes, 0, NULL, REG_OPTION_NON_VOLATILE, &Disposition); if (Security) ExFreePool(Security); if (!NT_SUCCESS(Status)) { NtClose(BootHandle); return(Status); } // Get the key objects for out two controls Status = ObReferenceObjectByHandle(BootHandle, KEY_READ, CmpKeyObjectType, KernelMode, (PVOID *)(&BootKey), NULL); if (!NT_SUCCESS(Status)) goto Exit; Status = ObReferenceObjectByHandle(SavedBootHandle, KEY_WRITE, CmpKeyObjectType, KernelMode, (PVOID *)(&SavedBootKey), NULL); if (!NT_SUCCESS(Status)) { ObDereferenceObject((PVOID)BootKey); goto Exit; } // Lock the registry and do the actual saving CmpLockRegistryExclusive(); if (Disposition == REG_CREATED_NEW_KEY) { // If we are saving to a control set that we have just // created, it is most efficient to just copy // the boot control set tree into the new control set. // N.B. We copy the volatile keys only if we are using // a clone and thus our boot control set tree is // composed only of volatile keys. CopyRet = CmpCopyTreeEx(BootKey->KeyControlBlock->KeyHive, BootKey->KeyControlBlock->KeyCell, SavedBootKey->KeyControlBlock->KeyHive, SavedBootKey->KeyControlBlock->KeyCell, CLONE_CONTROL_SET); // Set the max subkey name property for the new target key. SavedBootKey->KeyControlBlock->KeyNode->MaxNameLen = BootKey->KeyControlBlock->KeyNode->MaxNameLen; } else { // If we are saving to a control set that already exists // then its likely that this control set is nearly identical // to the boot control set (control sets don't change much // between boots). // Furthermore, the control set we are saving to must be old // and hence has not been modified at all since it ceased // being a current control set. // Thus, it is most efficient for us to simply synchronize // the target control set with the boot control set. // N.B. We sync the volatile keys only if we are using // a clone for the same reasons as stated above. CopyRet = CmpSyncTrees(BootKey->KeyControlBlock->KeyHive, BootKey->KeyControlBlock->KeyCell, SavedBootKey->KeyControlBlock->KeyHive, SavedBootKey->KeyControlBlock->KeyCell, CLONE_CONTROL_SET); } // Check if the Copy/Sync succeeded and adjust our return code // accordingly. if (CopyRet) { Status = STATUS_SUCCESS; } else { Status = STATUS_REGISTRY_CORRUPT; } // All done. Clean up. CmpUnlockRegistry(); ObDereferenceObject((PVOID)BootKey); ObDereferenceObject((PVOID)SavedBootKey); Exit: NtClose(BootHandle); NtClose(SavedBootHandle); #if CLONE_CONTROL_SET // If we have been using a clone, then the clone is no longer // needed since we have saved its contents into a non-volatile // control set. Thus, we can just erase it. if(NT_SUCCESS(Status)) { CmpDeleteCloneTree(); } #endif return(Status); } NTSTATUS CmpDeleteCloneTree() /*++ Routine Description: Deletes the cloned CurrentControlSet by unloading the CLONE hive. Arguments: NONE. Return Value: NTSTATUS return from NtUnloadKey. --*/ { OBJECT_ATTRIBUTES Obja; InitializeObjectAttributes( &Obja, &CmRegistrySystemCloneName, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, NULL); return NtUnloadKey(&Obja); } VOID CmBootLastKnownGood( ULONG ErrorLevel ) /*++ Routine Description: This function is called to indicate a failure during the boot process. The actual result is based on the value of ErrorLevel: IGNORE - Will return, boot should proceed NORMAL - Will return, boot should proceed SEVERE - If not booting LastKnownGood, will switch to LastKnownGood and reboot the system. If already booting LastKnownGood, will return. Boot should proceed. CRITICAL - If not booting LastKnownGood, will switch to LastKnownGood and reboot the system. If already booting LastKnownGood, will bugcheck. Arguments: ErrorLevel - Supplies the severity level of the failure Return Value: None. If it returns, boot should proceed. May cause the system to reboot. --*/ { ARC_STATUS Status; PAGED_CODE(); if (CmFirstTime != TRUE) { // NtInitializeRegistry has been called, so handling // driver errors is not a task for ScReg. // Treat all errors as Normal return; } switch (ErrorLevel) { case NormalError: case IgnoreError: break; case SevereError: if (CmpIsLastKnownGoodBoot()) { break; } else { Status = HalSetEnvironmentVariable("LastKnownGood", "TRUE"); if (Status == ESUCCESS) { HalReturnToFirmware(HalRebootRoutine); } } break; case CriticalError: if (CmpIsLastKnownGoodBoot()) { KeBugCheckEx(CRITICAL_SERVICE_FAILED,5,9,0,0); } else { Status = HalSetEnvironmentVariable("LastKnownGood", "TRUE"); if (Status == ESUCCESS) { HalReturnToFirmware(HalRebootRoutine); } else { KeBugCheckEx(SET_ENV_VAR_FAILED,5,10,0,0); } } break; } return; } BOOLEAN CmpIsLastKnownGoodBoot( VOID ) /*++ Routine Description: Determines whether the current system boot is a LastKnownGood boot or not. It does this by comparing the following two values: \registry\machine\system\select:Current \registry\machine\system\select:LastKnownGood If both of these values refer to the same control set, and this control set is different from: \registry\machine\system\select:Default we are booting LastKnownGood. Arguments: None. Return Value: TRUE - Booting LastKnownGood FALSE - Not booting LastKnownGood --*/ { NTSTATUS Status; ULONG Default; ULONG Current; ULONG LKG; RTL_QUERY_REGISTRY_TABLE QueryTable[] = { {NULL, RTL_QUERY_REGISTRY_DIRECT, L"Current", &Current, REG_DWORD, (PVOID)&Current, 0 }, {NULL, RTL_QUERY_REGISTRY_DIRECT, L"LastKnownGood", &LKG, REG_DWORD, (PVOID)&LKG, 0 }, {NULL, RTL_QUERY_REGISTRY_DIRECT, L"Default", &Default, REG_DWORD, (PVOID)&Default, 0 }, {NULL, 0, NULL, NULL, REG_NONE, NULL, 0 } }; PAGED_CODE(); Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, L"\\Registry\\Machine\\System\\Select", QueryTable, NULL, NULL); // If this failed, something is severely wrong. ASSERT(NT_SUCCESS(Status)); if (!NT_SUCCESS(Status)) { CMLOG(CML_MAJOR, CMS_INIT) { KdPrint(("CmpIsLastKnownGoodBoot: RtlQueryRegistryValues ")); KdPrint(("failed, Status %08lx\n", Status)); } return(FALSE); } if ((LKG == Current) && (Current != Default)){ return(TRUE); } else { return(FALSE); } } VOID CmShutdownSystem( VOID ) /*++ Routine Description: Shuts down the registry. Call to CmpWorkerThread, which will call back to CmpDoFlushAll(); Arguments: NONE Return Value: NONE --*/ { REGISTRY_COMMAND CommandArea; PAGED_CODE(); CmpLockRegistryExclusive(); CommandArea.Command = REG_CMD_SHUTDOWN; CmpWorker(&CommandArea); HvShutdownComplete = TRUE; // Tell HvSyncHive to ignore all // further requests CmpUnlockRegistry(); return; } BOOLEAN CmpLinkKeyToHive( PWSTR KeyPath, PWSTR HivePath ) /*++ Routine Description: Creates a symbolic link at KeyPath that points to HivePath. Arguments: KeyPath - pointer to unicode string with name of key (e.g. L"\\Registry\\Machine\\Security\\SAM") HivePath - pointer to unicode string with name of hive root (e.g. L"\\Registry\\Machine\\SAM\\SAM") Return Value: TRUE if links were successfully created, FALSE otherwise --*/ { UNICODE_STRING KeyName; UNICODE_STRING LinkName; OBJECT_ATTRIBUTES Attributes; HANDLE LinkHandle; ULONG Disposition; NTSTATUS Status; PAGED_CODE(); // Create link for CLONE hive RtlInitUnicodeString(&KeyName, KeyPath); InitializeObjectAttributes(&Attributes, &KeyName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtCreateKey(&LinkHandle, KEY_CREATE_LINK, &Attributes, 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, &Disposition); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpLinkKeyToHive: couldn't create %S\n", &KeyName)); KdPrint((" Status = %08lx\n",Status)); } return(FALSE); } // Check to make sure that the key was created, not just opened. Since // this key is always created volatile, it should never be present in // the hive when we boot. if (Disposition != REG_CREATED_NEW_KEY) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpLinkKeyToHive: %S already exists!\n", &KeyName)); } NtClose(LinkHandle); return(FALSE); } RtlInitUnicodeString(&LinkName, HivePath); Status = NtSetValueKey(LinkHandle, &CmSymbolicLinkValueName, 0, REG_LINK, LinkName.Buffer, LinkName.Length); NtClose(LinkHandle); if (!NT_SUCCESS(Status)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CM: CmpLinkKeyToHive: couldn't create symbolic link for %S\n", HivePath)); } return(FALSE); } return(TRUE); } BOOLEAN CmpValidateAlternate( IN HANDLE FileHandle, IN PCMHIVE PrimaryHive ) /*++ Routine Description: Loads an alternate hive (SYSTEM.ALT) and verifies that it is a valid hive file. Arguments: FileHandle - Supplies a handle to the hive file. PrimaryHive - Supplies the CMHIVE structure of the primary hive. Return Value: TRUE - Hive is valid. FALSE - Hive is corrupt or in transition. --*/ { PCMHIVE CmHive; BOOLEAN Status; NTSTATUS Status2; Status2 = CmpInitializeHive(&CmHive, HINIT_FILE, 0, HFILE_TYPE_PRIMARY, NULL, FileHandle, NULL, NULL, NULL, &CmpSystemFileName // non system ALT will screw up ); if (!NT_SUCCESS(Status2)) { CMLOG(CML_BUGCHECK, CMS_INIT) { KdPrint(("CmpValidateSecondary: SYSTEM.ALT is corrupt\n")); } return(FALSE); } // Compare the timestamps in the baseblock. These must always be // equal to ensure that SYSTEM and SYSTEM.ALT are really the same // hive. if (CmHive->Hive.BaseBlock->TimeStamp.QuadPart != PrimaryHive->Hive.BaseBlock->TimeStamp.QuadPart) { CMLOG(CML_BUGCHECK,CMS_INIT) { KdPrint(("CmpValidateSecondary: timestamps don't match")); } Status = FALSE; } else { Status = TRUE; } // Hive is valid, free up everything we allocated. RemoveEntryList(&CmHive->HiveList); HvFreeHive(&CmHive->Hive); ASSERT( CmHive->HiveLock ); ExFreePool(CmHive->HiveLock); CmpFree(CmHive, sizeof(CMHIVE)); return(Status); } VOID CmpCreatePerfKeys( VOID ) /*++ Routine Description: Creates predefined keys for the performance text to support old apps on 1.0a Arguments: None. Return Value: None. --*/ { HANDLE Perflib; NTSTATUS Status; WCHAR LanguageId[4]; OBJECT_ATTRIBUTES Attributes; UNICODE_STRING String; USHORT Language; LONG i; WCHAR c; extern PWCHAR CmpRegistryPerflibString; RtlInitUnicodeString(&String, CmpRegistryPerflibString); InitializeObjectAttributes(&Attributes, &String, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = NtOpenKey(&Perflib, KEY_WRITE, &Attributes); if (!NT_SUCCESS(Status)) { return; } // Always create the predefined keys for the english language CmpCreatePredefined(Perflib, L"009", HKEY_PERFORMANCE_TEXT); // If the default language is not english, create a predefined key for // that, too. if (PsDefaultSystemLocaleId != 0x00000409) { Language = LANGIDFROMLCID(PsDefaultSystemLocaleId) & 0xff; LanguageId[3] = L'\0'; for (i=2;i>=0;i--) { c = Language % 16; if (c>9) { LanguageId[i]= c+L'A'-10; } else { LanguageId[i]= c+L'0'; } Language = Language >> 4; } CmpCreatePredefined(Perflib, LanguageId, HKEY_PERFORMANCE_NLSTEXT); } } VOID CmpCreatePredefined( IN HANDLE Root, IN PWSTR KeyName, IN HANDLE PredefinedHandle ) /*++ Routine Description: Creates a special key that will always return the given predefined handle instead of a real handle. Arguments: Root - supplies the handle the keyname is relative to KeyName - supplies the name of the key. PredefinedHandle - supplies the predefined handle to be returned when this key is opened. Return Value: None. --*/ { OBJECT_ATTRIBUTES ObjectAttributes; CM_PARSE_CONTEXT ParseContext; NTSTATUS Status; UNICODE_STRING Name; HANDLE Handle; ParseContext.Class.Length = 0; ParseContext.Class.Buffer = NULL; ParseContext.TitleIndex = 0; ParseContext.CreateOptions = REG_OPTION_VOLATILE | REG_OPTION_PREDEF_HANDLE; ParseContext.Disposition = 0; ParseContext.CreateLink = FALSE; ParseContext.PredefinedHandle = PredefinedHandle; RtlInitUnicodeString(&Name, KeyName); InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, Root, NULL); Status = ObOpenObjectByName(&ObjectAttributes, CmpKeyObjectType, KernelMode, NULL, KEY_READ, (PVOID)&ParseContext, &Handle); ASSERT(NT_SUCCESS(Status)); ZwClose(Handle); }