/*++ Copyright (c) 1991 Microsoft Corporation Module Name: regboot.c Abstract: Provides a minimal registry implementation designed to be used by the osloader at boot time. This includes loading the system hive ( \config\SYSTEM ) into memory, and computing the driver load list from it. Author: John Vert (jvert) 10-Mar-1992 Revision History: Doug Fritz (dFritz) 07-Oct-1997 & KenRay Feb 98 - Filter hardware profiles based on detected hardware configuration (docking station) information --*/ #include "bldr.h" #include "msg.h" #include "cmp.h" #include "stdio.h" #include "string.h" #include #include #ifdef i386 #include "bldrx86.h" #endif #if defined(_IA64_) #include "bldria64.h" #endif #ifdef _WANT_MACHINE_IDENTIFICATION #include #include #endif #include "bldrint.h" #include "vmode.h" CMHIVE BootHive; ULONG CmLogLevel=100; ULONG CmLogSelect=0; ULONG ScreenWidth=80; #ifdef EFI ULONG ScreenHeight=24; #else ULONG ScreenHeight=25; #endif ULONG LkgStartTime; // // used by the advanced boot screen to force a LKG boot // BOOLEAN ForceLastKnownGood; // // Variable used to check whether to display // "Return to OS Choices Menu" or not in adv. boot // BOOLEAN BlShowReturnToOSChoices = TRUE; VOID BlRedrawProgressBar( VOID ); VOID BlOutputStartupMsg( ULONG uMsgID ); ULONG BlGetAdvancedBootID( LONG BootOption ); // // Private function prototypes // BOOLEAN BlInitializeHive( IN PVOID HiveImage, IN PCMHIVE Hive, IN BOOLEAN IsAlternate ); BOOLEAN BlpCheckRestartSetup( VOID ); PVOID BlpHiveAllocate( IN ULONG Length, IN BOOLEAN UseForIo, ULONG Tag ); VOID BlDockInfoFilterProfileList( IN OUT PCM_HARDWARE_PROFILE_LIST ProfileList, IN OUT PCM_HARDWARE_PROFILE_ALIAS_LIST AliasList ); VOID BlStartConfigPrompt( VOID ) /*++ Routine Description: This routine displays the LKG prompt, records the current time, and returns. The prompt is displayed before the kernel and HAL are loaded, and then removed afterwards. Arguments: None. Return Value: None. --*/ { ULONG Count; PTCHAR LkgPrompt; LkgPrompt = BlFindMessage(BL_LKG_MENU_PROMPT); if (LkgPrompt==NULL) { return; } // // display LKG prompt // #if 0 BlPositionCursor(1,3); ArcWrite(BlConsoleOutDeviceId, LkgPrompt, _tcslen(LkgPrompt)*sizeof(TCHAR), &Count); BlPositionCursor(1,2); #endif LkgStartTime = ArcGetRelativeTime(); #if defined(REMOTE_BOOT) && defined(i386) // // Wait to allow the user to type space or F8. If anything is typed then behave // conservatively and load the kernel, etc., from the server just in case CSC or // the local filesystem is broken. // if (BlBootingFromNet) { ULONG EndTime; ULONG Status; ULONG CurrentTime; EndTime = LkgStartTime + 3; if (EndTime <= ArcGetRelativeTime()) { EndTime = ArcGetRelativeTime()+1; } do { if (ArcGetReadStatus(ARC_CONSOLE_INPUT) == ESUCCESS) { // // There is a key pending, assume it is CSC related. If it isn't // CSC then it just means we laod a few extra files from the // server. // NetBootCSC = FALSE; break; } CurrentTime = ArcGetRelativeTime(); // // Terminate the loop if the EndTime has been reached, or // if the CurrentTime has wrapped at midnight. // } while ((CurrentTime < EndTime) && (CurrentTime >= LkgStartTime)); } #endif // defined(REMOTE_BOOT) && defined(i386) } BOOLEAN BlEndConfigPrompt( VOID ) /*++ Routine Description: This routine waits until the LKG timeout has expired or the user presses a key and then removes the LKG prompt. Arguments: None. Return Value: TRUE - Space bar pressed. FALSE - Space bar was not pressed. --*/ { ULONG EndTime; ULONG Count; ULONG Key; ULONG Status; ULONG CurrentTime; // // We do not wait for a keypress if there is not already one. // EndTime = 0; if( BlIsTerminalConnected() ) { // // If we're booting headless, give the user lots of time // to press any of the advanced options keys. // EndTime = ArcGetRelativeTime() + 5; } #if defined(REMOTE_BOOT) && defined(i386) // // If a key was detected and CSC turned off then re-enable CSC until // we find out if it should be disabled for this whole boot. // NetBootCSC = TRUE; #endif // defined(REMOTE_BOOT) && defined(i386) do { LONG AdvancedBoot = -1; BOOLEAN bOldState = BlShowReturnToOSChoices; BlShowReturnToOSChoices = FALSE; if ((Key = BlGetKey()) != 0) { // // return if the pending key was the spacebar. // if (Key == ' ') { return(TRUE); } // // look to see if the user pressed the F5 or F8 keys, // these keys trigger the advanced boot menu. the advanced // boot menu can also be entered from the main boot menu by // pressing the same keys. // if (Key == F5_KEY || Key == F8_KEY) { // // present the menu and get the user's request // LONG AdvancedBoot = -1; BOOLEAN bOldState = BlShowReturnToOSChoices; BlShowReturnToOSChoices = FALSE; if (DisplayLogoOnBoot) { if (!DbcsLangId) HW_CURSOR(0x80000000,0x3); else HW_CURSOR(0x80000000,0x12); } AdvancedBoot = BlDoAdvancedBoot( BL_ADVANCEDBOOT_TITLE, 0, FALSE, 0 ); if (DisplayLogoOnBoot) { PSTR BootOption; if ((AdvancedBoot != -1) && ((BootOption = BlGetAdvancedBootLoadOptions(AdvancedBoot)) != NULL ) && (!strncmp("SAFEBOOT",BootOption,8))) { DisplayLogoOnBoot = FALSE; // on safe boot let the "Checking file system" message // appear as it appears today (in graphics mode) } else { #ifndef EFI HW_CURSOR(0x80000000,0x12); if (DbcsLangId) TextClearDisplay(); VgaEnableVideo(); PaletteOn(); DrawBitmap (); BlUpdateBootStatus(); #endif } } BlShowReturnToOSChoices = bOldState; if (AdvancedBoot != -1) { // // they chose a valid boot option so append // any os load options and perform any necessary // option processing. // PSTR NewOptions = BlGetAdvancedBootLoadOptions(AdvancedBoot); if( BlGetAdvancedBootID(AdvancedBoot) == BL_MSG_REBOOT ) { BlClearScreen(); REBOOT_PROCESSOR(); } if (NewOptions != NULL && strstr(BlLoaderBlock->LoadOptions,NewOptions) == NULL) { ULONG len = strlen(NewOptions) + // new options 1 + // seperated by a space strlen(BlLoaderBlock->LoadOptions) + // old options 1; // null terminator NewOptions = BlAllocateHeap(len * sizeof(PCHAR)); strcpy(NewOptions,BlLoaderBlock->LoadOptions); strcat(NewOptions," "); strcat(NewOptions,BlGetAdvancedBootLoadOptions(AdvancedBoot)); BlLoaderBlock->LoadOptions = NewOptions; DBGTRACE(TEXT("Load Options = %S"), BlLoaderBlock->LoadOptions); } BlDoAdvancedBootLoadProcessing(AdvancedBoot); } } } CurrentTime = ArcGetRelativeTime(); // // Terminate the loop if the EndTime has been reached, or // if the CurrentTime has wrapped at midnight. // } while ((CurrentTime < EndTime) && (CurrentTime >= LkgStartTime)); // // make LKG prompt go away, so as not to startle the user. // remote the trailer & update progress bar // #if defined(_IA64_) BlOutputStartupMsg(BL_MSG_STARTING_WINDOWS); #endif BlRedrawProgressBar(); return(FALSE); } VOID BlpSwitchControlSet( OUT PCM_HARDWARE_PROFILE_LIST *ProfileList, OUT PCM_HARDWARE_PROFILE_ALIAS_LIST *AliasList, IN BOOLEAN UseLastKnownGood, OUT PHCELL_INDEX ControlSet ) /*++ Routine Description: Switches the current control set to the specified control set and rebuilds the hardware profile list. Arguments: ProfileList - Returns the new hardware profile list UseLastKnownGood - Supplies whether the LKG control set is to be used. ControlSet - Returns the HCELL_INDEX of the new control set. Return Value: None. --*/ { UNICODE_STRING ControlName; HCELL_INDEX NewControlSet; BOOLEAN AutoSelect; // ignored ULONG ProfileTimeout; // ignored // // Find the new control set. // if (UseLastKnownGood) { RtlInitUnicodeString(&ControlName, L"LastKnownGood"); } else { RtlInitUnicodeString(&ControlName, L"Default"); } NewControlSet = CmpFindControlSet(&BootHive.Hive, BootHive.Hive.BaseBlock->RootCell, &ControlName, &AutoSelect); if (NewControlSet == HCELL_NIL) { return; } CmpFindProfileOption(&BootHive.Hive, NewControlSet, ProfileList, AliasList, NULL); *ControlSet = NewControlSet; } ULONG BlCountLines( IN PTCHAR Lines ) /*++ Routine Description: Counts the number of lines in the given string. Arguments: Lines - Supplies a pointer to the start of the string Return Value: The number of lines in the string. --*/ { PTCHAR p; ULONG NumLines = 0; p=Lines; while (*p != TEXT('\0')) { if ((*p == TEXT('\r')) && (*(p+1) == TEXT('\n'))) { ++NumLines; ++p; // move forward to \n } ++p; } return(NumLines); } BOOLEAN BlConfigMenuPrompt( IN ULONG Timeout, IN OUT PBOOLEAN UseLastKnownGood, IN OUT PHCELL_INDEX ControlSet, OUT PCM_HARDWARE_PROFILE_LIST *ProfileList, OUT PCM_HARDWARE_PROFILE_ALIAS_LIST *AliasList, OUT PCM_HARDWARE_PROFILE *HardwareProfile ) /*++ Routine Description: This routine provides the user-interface for the configuration menu. The prompt is given if the user hits the break-in key, or if the LastKnownGood environment variable is TRUE and AutoSelect is FALSE, or if the timeout value on the hardware profile configuration is non-zero Arguments: Timeout - Supplies the timeout value for the menu. -1 or 0 implies the menu will never timeout. UseLastKnownGood - Returns the LastKnownGood setting that should be used for the boot. ControlSet - Returns the control set (either Default or LKG) ProfileList - Supplies the default list of profiles. Returns the current list of profiles. (may change due to switching to/from the LKG controlset) HardwareProfile - Returns the hardware profile that should be used. Return Value: TRUE - Boot should proceed. FALSE - The user has chosen to return to the firmware menu/flexboot menu. --*/ { ULONG HeaderLines; ULONG TrailerLines; ULONG i; ULONG Count; ULONG flags; ULONG Key; PTCHAR MenuHeader; PTCHAR MenuTrailer1; PTCHAR MenuTrailer2; PTCHAR p; ULONG OptionLength; TCHAR MenuOption[80]; PCM_HARDWARE_PROFILE Profile; ULONG ProfileCount; _TUCHAR LkgMnemonic; _TUCHAR DefaultMnemonic; PTCHAR Temp; ULONG DisplayLines; ULONG TopProfileLine=0; ULONG CurrentSelection = 0; ULONG CurrentProfile; ULONG EndTime; ULONG CurrentTime; PTCHAR TimeoutPrompt; if ((Timeout != (ULONG)-1) && (Timeout != 0)) { CurrentTime = ArcGetRelativeTime(); EndTime = CurrentTime + Timeout; TimeoutPrompt = BlFindMessage(BL_LKG_TIMEOUT); if (TimeoutPrompt != NULL) { p=_tcschr(TimeoutPrompt, TEXT('\n')); if (p) { *p = TEXT('\0'); } p=_tcschr(TimeoutPrompt, TEXT('\r')); if (p) { *p = TEXT('\0'); } } } else { TimeoutPrompt = NULL; } MenuHeader = BlFindMessage(BL_LKG_MENU_HEADER); Temp = BlFindMessage(BL_LKG_SELECT_MNEMONIC); if (Temp == NULL) { return(TRUE); } LkgMnemonic = (_TUCHAR)_totupper(Temp[0]); Temp = BlFindMessage(BL_DEFAULT_SELECT_MNEMONIC); if (Temp == NULL) { return(TRUE); } DefaultMnemonic = (_TUCHAR)_totupper(Temp[0]); if ((*UseLastKnownGood) && (*ProfileList) && ((*ProfileList)->CurrentProfileCount == 1)) { // // The user selected last known good via boot.ini/nvram/etc. Since this // was a concious decision, and we don't have more than one profile to // choose, just skip this UI altogether. // ASSERT(CurrentSelection == 0); goto ProcessSelection; } Restart: if (*ProfileList == NULL) { ProfileCount = 0; } else { ProfileCount = (*ProfileList)->CurrentProfileCount; } if (ProfileCount == 0) { MenuTrailer1 = BlFindMessage(BL_LKG_MENU_TRAILER_NO_PROFILES); } else { MenuTrailer1 = BlFindMessage(BL_LKG_MENU_TRAILER); } if (*UseLastKnownGood) { MenuTrailer2 = BlFindMessage(BL_SWITCH_DEFAULT_TRAILER); } else { MenuTrailer2 = BlFindMessage(BL_SWITCH_LKG_TRAILER); } if ((MenuHeader==NULL) || (MenuTrailer1==NULL) || (MenuTrailer2==NULL)) { return(TRUE); } // // strip trailing /r/n from MenuTrailer2 to prevent it from scrolling // the screen when we output it. // #if 0 p=MenuTrailer2 + strlen(MenuTrailer2) - 1; while ((*p == TEXT('\r')) || (*p == TEXT('\n'))) { *p = TEXT('\0'); --p; } #endif BlClearScreen(); #ifdef EFI BlEfiSetAttribute( DEFATT ); #else BlSetInverseMode(FALSE); #endif // // Count the number of lines in the header. // HeaderLines=BlCountLines(MenuHeader); // // Display the menu header. // ArcWrite(BlConsoleOutDeviceId, MenuHeader, _tcslen(MenuHeader)*sizeof(TCHAR), &Count); // // Count the number of lines in the trailer. // TrailerLines=BlCountLines(MenuTrailer1) + BlCountLines(MenuTrailer2); // // Display the trailing prompt. // if (TimeoutPrompt) { TrailerLines += 1; } BlPositionCursor(1, ScreenHeight-TrailerLines); ArcWrite(BlConsoleOutDeviceId, MenuTrailer1, _tcslen(MenuTrailer1)*sizeof(TCHAR), &Count); ArcWrite(BlConsoleOutDeviceId, MenuTrailer2, _tcslen(MenuTrailer2)*sizeof(TCHAR), &Count); // // Compute number of selections that can be displayed // DisplayLines = ScreenHeight-HeaderLines-TrailerLines-3; if (ProfileCount < DisplayLines) { DisplayLines = ProfileCount; } // // Start menu selection loop. // do { if (ProfileCount > 0) { // // Display options with current selection highlighted // for (i=0; i < DisplayLines; i++) { CurrentProfile = i+TopProfileLine; Profile = &(*ProfileList)->Profile[CurrentProfile]; BlPositionCursor(5, HeaderLines+i+2); #ifdef EFI BlEfiSetAttribute( (CurrentProfile == CurrentSelection) ? INVATT : DEFATT ); #else BlSetInverseMode((BOOLEAN)(CurrentProfile == CurrentSelection)); #endif #ifdef UNICODE ArcWrite(BlConsoleOutDeviceId, Profile->FriendlyName, Profile->NameLength, &Count ); #else RtlUnicodeToMultiByteN(MenuOption, sizeof(MenuOption), &OptionLength, Profile->FriendlyName, Profile->NameLength); ArcWrite(BlConsoleOutDeviceId, MenuOption, OptionLength, &Count); #endif #ifdef EFI BlEfiSetAttribute( DEFATT ); #else BlSetInverseMode(FALSE); #endif BlClearToEndOfLine(); } } else { // // No profile options available, just display the default // highlighted to indicate that ENTER will start the system. // Temp = BlFindMessage(BL_BOOT_DEFAULT_PROMPT); if (Temp != NULL) { BlPositionCursor(5, HeaderLines+3); #ifdef EFI BlEfiSetAttribute( INVATT ); #else BlSetInverseMode(TRUE); #endif ArcWrite(BlConsoleOutDeviceId, Temp, _tcslen(Temp)*sizeof(TCHAR), &Count); #ifdef EFI BlEfiSetAttribute( INVATT ); #else BlSetInverseMode(TRUE); #endif } } if (TimeoutPrompt) { CurrentTime = ArcGetRelativeTime(); _stprintf(MenuOption, TimeoutPrompt, EndTime-CurrentTime); BlPositionCursor(1, ScreenHeight); ArcWrite(BlConsoleOutDeviceId, MenuOption, _tcslen(MenuOption)*sizeof(TCHAR), &Count); BlClearToEndOfLine(); } // // Loop waiting for keypress or time change. // do { if ((Key = BlGetKey()) != 0) { TimeoutPrompt = NULL; // turn off timeout prompt BlPositionCursor(1,ScreenHeight); BlClearToEndOfLine(); break; } if (TimeoutPrompt) { if (ArcGetRelativeTime() != CurrentTime) { // // Time has changed, update the countdown and check for timeout // CurrentTime = ArcGetRelativeTime(); _stprintf(MenuOption, TimeoutPrompt, EndTime-CurrentTime); BlPositionCursor(1, ScreenHeight); ArcWrite(BlConsoleOutDeviceId, MenuOption, _tcslen(MenuOption)*sizeof(TCHAR), &Count); BlClearToEndOfLine(); if (EndTime == CurrentTime) { goto ProcessSelection; } } } } while ( TRUE ); switch (Key) { case UP_ARROW: // // Cursor up // if (ProfileCount > 0) { if (CurrentSelection==0) { CurrentSelection = ProfileCount - 1; if (TopProfileLine + DisplayLines <= CurrentSelection) { TopProfileLine = CurrentSelection - DisplayLines + 1; } } else { if (--CurrentSelection < TopProfileLine) { // // Scroll up // TopProfileLine = CurrentSelection; } } } break; case DOWN_ARROW: // // Cursor down // if (ProfileCount > 0) { CurrentSelection = (CurrentSelection+1) % ProfileCount; if (CurrentSelection == 0) { TopProfileLine = 0; } else if (TopProfileLine + DisplayLines <= CurrentSelection) { TopProfileLine = CurrentSelection - DisplayLines + 1; } } break; case F3_KEY: // // F3 // *ControlSet = HCELL_NIL; return(FALSE); default: // // Check to see if the Key indicates the user selection LKG // first, we have to make sure we are looking at an alpha char. // if ( ((Key >> 8) == 0) && _istalpha((TCHAR)Key) ) { if ((_totupper((TCHAR)Key) == LkgMnemonic) && (*UseLastKnownGood == FALSE)) { *UseLastKnownGood = TRUE; BlpSwitchControlSet(ProfileList, AliasList, TRUE, ControlSet); if (NULL != *ProfileList) { if ((*ProfileList)->CurrentProfileCount > 0) { BlDockInfoFilterProfileList (*ProfileList, *AliasList); } } goto Restart; // // regenerate profile list here // } else if ((_totupper((TCHAR)Key) == DefaultMnemonic) && (*UseLastKnownGood)) { *UseLastKnownGood = FALSE; BlpSwitchControlSet(ProfileList, AliasList, FALSE, ControlSet); if (NULL != *ProfileList) { if ((*ProfileList)->CurrentProfileCount > 0) { BlDockInfoFilterProfileList (*ProfileList, *AliasList); } } goto Restart; } } break; } // switch } while ( (Key != ASCII_CR) && (Key != ASCII_LF) ); ProcessSelection: if (ProfileCount > 0) { if (HW_PROFILE_STATUS_SUCCESS == BlLoaderBlock->Extension->Profile.Status) { flags = ((*ProfileList)->Profile[CurrentSelection].Flags); if (flags & CM_HP_FLAGS_PRISTINE) { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_PRISTINE_MATCH; } else if (flags & CM_HP_FLAGS_TRUE_MATCH) { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_TRUE_MATCH; } else if (flags & CM_HP_FLAGS_ALIASABLE) { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_ALIAS_MATCH; } } CmpSetCurrentProfile(&BootHive.Hive, *ControlSet, &(*ProfileList)->Profile[CurrentSelection]); } return(TRUE); } ARC_STATUS BlLoadBootDrivers( IN PPATH_SET DefaultPathSet, IN PLIST_ENTRY BootDriverListHead, OUT PCHAR BadFileName ) /*++ Routine Description: Walks the boot driver list and loads all the drivers Arguments: DefaultPathSet - Describes the possible locations drivers could be loaded from. BootDriverListHead - Supplies the head of the boot driver list BadFileName - Returns the filename of the critical driver that did not load. Not valid if ESUCCESS is returned. Return Value: ESUCCESS is returned if all the boot drivers were successfully loaded. Otherwise, an unsuccessful status is returned. --*/ { ULONG DeviceId; PBOOT_DRIVER_NODE DriverNode; PBOOT_DRIVER_LIST_ENTRY DriverEntry; PLIST_ENTRY NextEntry; CHAR DriverName[64]; PCHAR NameStart; CHAR DriverDevice[128]; CHAR DriverPath[128]; ARC_STATUS Status; UNICODE_STRING DeviceName; UNICODE_STRING FileName; WCHAR SystemRootBuffer[] = L"\\SystemRoot\\"; ULONG SystemRootLength; PWSTR p; ULONG Index; BOOLEAN AbsolutePath; FULL_PATH_SET LocalPathSet; PPATH_SOURCE PathSource; SystemRootLength = wcslen(SystemRootBuffer); NextEntry = BootDriverListHead->Flink; while (NextEntry != BootDriverListHead) { DriverNode = CONTAINING_RECORD(NextEntry, BOOT_DRIVER_NODE, ListEntry.Link); Status = ESUCCESS; DriverEntry = &DriverNode->ListEntry; if (DriverEntry->FilePath.Buffer[0] != L'\\') { // // This is a relative pathname, so generate the full pathname // relative to the boot partition. // sprintf(DriverPath, "%wZ", &DriverEntry->FilePath); AbsolutePath = FALSE; } else if (memcmp(DriverEntry->FilePath.Buffer, SystemRootBuffer, (SystemRootLength * sizeof(WCHAR))) == 0) { // // This is a pathname starting with "\SystemRoot\", so just ignore // that part and treat like the previous case. // FileName.Buffer = DriverEntry->FilePath.Buffer + SystemRootLength; FileName.Length = (USHORT)(DriverEntry->FilePath.Length - (SystemRootLength * sizeof(WCHAR))); sprintf(DriverPath, "%wZ", &FileName); AbsolutePath = FALSE; } else { // // This is an absolute pathname, of the form // "\ArcDeviceName\dir\subdir\filename" // // We need to open the specified ARC device and pass that // to BlLoadDeviceDriver. // p = DeviceName.Buffer = DriverEntry->FilePath.Buffer+1; DeviceName.Length = 0; DeviceName.MaximumLength = DriverEntry->FilePath.MaximumLength-sizeof(WCHAR); while ((*p != L'\\') && (DeviceName.Length < DeviceName.MaximumLength)) { ++p; DeviceName.Length += sizeof(WCHAR); } DeviceName.MaximumLength = DeviceName.Length; sprintf(DriverDevice, "%wZ", &DeviceName); Status = ArcOpen(DriverDevice,ArcOpenReadOnly,&DeviceId); FileName.Buffer = p+1; FileName.Length = DriverEntry->FilePath.Length - DeviceName.Length - 2*sizeof(WCHAR); FileName.MaximumLength = FileName.Length; // // Device successfully opened, parse out the path and filename. // sprintf(DriverPath, "%wZ", &FileName); AbsolutePath = TRUE; } // // Parse out the driver name from the driver path // NameStart = strrchr(DriverPath, '\\'); if (NameStart != NULL) { strcpy(DriverName, NameStart+1); *NameStart = '\0'; } else if (DriverPath[0]) { strcpy(DriverName, DriverPath); *DriverPath = '\0'; } else { NextEntry = DriverEntry->Link.Flink; continue; } // // Ensure DriverPath is terminated with a '\\' if it's filled out. // if (DriverPath[0]) { strcat(DriverPath, "\\"); } if (AbsolutePath) { // // There is only one entry if an absolute path is specified (in // this case we cannot do last known good). // PathSource = &LocalPathSet.Source[0]; PathSource->DeviceId = DeviceId; PathSource->DeviceName = DriverDevice; PathSource->DirectoryPath = "\\"; LocalPathSet.PathCount = 1; LocalPathSet.AliasName = NULL; strcpy(LocalPathSet.PathOffset, DriverPath); } else { // // It's relative. Copy over the DefaultPathSet array so we can // edit our own local copy. // *((PSPARSE_PATH_SET) &LocalPathSet) = *((PSPARSE_PATH_SET) DefaultPathSet); for(Index=0; Index < DefaultPathSet->PathCount; Index++) { LocalPathSet.Source[Index] = DefaultPathSet->Source[Index]; } // // Now append our relative path to the PathOffset already present // in our local copy. // strcat(LocalPathSet.PathOffset, DriverPath); } if (Status == ESUCCESS) { Status = BlLoadDeviceDriver(&LocalPathSet, DriverName, NULL, LDRP_ENTRY_PROCESSED, &DriverEntry->LdrEntry); } NextEntry = DriverEntry->Link.Flink; if (Status != ESUCCESS) { // // Attempt to load driver failed, remove it from the list. // RemoveEntryList(&DriverEntry->Link); // // Check the Error Control of the failed driver. If it // was critical, fail the boot. If the driver // wasn't critical, keep going. // if (DriverNode->ErrorControl == CriticalError) { strcpy(BadFileName, DriverPath); strcat(BadFileName, DriverName); return(Status); } } } return(ESUCCESS); } BOOLEAN BlRecoverHive( PVOID RegistryBase, ULONG_PTR LogBase ) /*++ Routine Description: Applies log from LogBase over the RegistryBase Arguments: Return Value: ESUCCESS is returned if the system hive was successfully loaded. Otherwise, an unsuccessful status is returned. --*/ { PHBASE_BLOCK BaseBlockHive; PHBASE_BLOCK BaseBlockLog; BOOLEAN RecoverData = FALSE; ULONG FileOffset = HSECTOR_SIZE; ULONG DirtyVectorSignature = 0; PUCHAR FlatLog; PUCHAR FlatReg; ULONG VectorSize; ULONG Length; ULONG ClusterSize; ULONG HeaderLength; RTL_BITMAP BitMap; PULONG Vector; ULONG Current; ULONG Start; ULONG End; PUCHAR MemoryBlock; PUCHAR Dest; ULONG i; BaseBlockHive = (PHBASE_BLOCK)RegistryBase; BaseBlockLog = (PHBASE_BLOCK)LogBase; FlatLog = (PUCHAR)LogBase; FlatReg = (PUCHAR)RegistryBase; ClusterSize = BaseBlockLog->Cluster * HSECTOR_SIZE; HeaderLength = ROUND_UP(HLOG_HEADER_SIZE, ClusterSize); FileOffset = ClusterSize; FileOffset = ROUND_UP(FileOffset, HeaderLength); if(HvpHeaderCheckSum(BaseBlockHive) != BaseBlockHive->CheckSum ) { // // recover header case // RtlCopyMemory((PVOID)BaseBlockHive,(PVOID)BaseBlockLog,ClusterSize); BaseBlockHive->Type = HFILE_TYPE_PRIMARY; } else { // // if not recoverheader (which implies recoverdata) // ASSERT( BaseBlockHive->Sequence1 != BaseBlockHive->Sequence2 ); } DirtyVectorSignature = *((PULONG)(FlatLog + FileOffset)); FileOffset += sizeof(DirtyVectorSignature); if (DirtyVectorSignature != HLOG_DV_SIGNATURE) { return FALSE; } Length = BaseBlockHive->Length; VectorSize = Length / HSECTOR_SIZE; Vector = (PULONG)(FlatLog + FileOffset); RtlInitializeBitMap(&BitMap, Vector, VectorSize); FileOffset += VectorSize / 8; FileOffset = ROUND_UP(FileOffset, ClusterSize); // // step through the diry map, and copy from the log to the flat hive // Current = 0; while (Current < VectorSize) { // // find next contiguous block of entries to read in // for (i = Current; i < VectorSize; i++) { if (RtlCheckBit(&BitMap, i) == 1) { break; } } Start = i; for ( ; i < VectorSize; i++) { if (RtlCheckBit(&BitMap, i) == 0) { break; } } End = i; Current = End; // // Start == number of 1st sector, End == number of Last sector + 1 // Length = (End - Start) * HSECTOR_SIZE; if( 0 == Length ) { // no more dirty blocks. break; } MemoryBlock = (PUCHAR)(FlatLog + FileOffset); FileOffset += Length; ASSERT((FileOffset % ClusterSize) == 0); Dest = (PUCHAR)(FlatReg + HBLOCK_SIZE + Start * HSECTOR_SIZE); // // copy recovered data in the right locations inside the flat hive image // RtlCopyMemory(Dest,MemoryBlock, Length); } BaseBlockHive->Sequence2 = BaseBlockHive->Sequence1; BaseBlockHive->CheckSum = HvpHeaderCheckSum(BaseBlockHive); return TRUE; } ARC_STATUS BlLoadAndInitSystemHive( IN ULONG DeviceId, IN PCHAR DeviceName, IN PCHAR DirectoryPath, IN PCHAR HiveName, IN BOOLEAN IsAlternate, OUT PBOOLEAN RestartSetup, OUT PBOOLEAN LogPresent ) /*++ Routine Description: Loads the registry SYSTEM hive, verifies it is a valid hive file, and inits the relevant registry structures. (particularly the HHIVE) Arguments: DeviceId - Supplies the file id of the device the system tree is on. DeviceName - Supplies the name of the device the system tree is on. DirectoryPath - Supplies a pointer to the zero-terminated directory path of the root of the NT tree. HiveName - Supplies the name of the system hive (ie, "SYSTEM", "SYSTEM.ALT", or "SYSTEM.SAV"). IsAlternate - Supplies whether or not the hive to be loaded is the alternate hive. RestartSetup - if the hive to be loaded is not the alternate, then this routine will check for a value of RestartSetup in the Setup key. If present and non-0, then this variable receives TRUE. Otherwise it receives FALSE. Return Value: ESUCCESS is returned if the system hive was successfully loaded. Otherwise, an unsuccessful status is returned. --*/ { ARC_STATUS Status; ULONG_PTR LogData; *RestartSetup = FALSE; *LogPresent = FALSE; BlClearToEndOfLine(); Status = BlLoadSystemHive(DeviceId, DeviceName, DirectoryPath, HiveName); if (Status!=ESUCCESS) { return(Status); } if (!BlInitializeHive(BlLoaderBlock->RegistryBase, &BootHive, IsAlternate)) { if( !IsAlternate ) { // // try to recover the hive // Status = BlLoadSystemHiveLog(DeviceId, DeviceName, DirectoryPath, "system.log", &LogData ); if (Status!=ESUCCESS) { return(Status); } *LogPresent = TRUE; if( !BlRecoverHive( BlLoaderBlock->RegistryBase, LogData ) ) { BlFreeDescriptor( (ULONG)((ULONG_PTR)LogData & (~KSEG0_BASE)) >> PAGE_SHIFT ); return(EINVAL); } BlFreeDescriptor( (ULONG)((ULONG_PTR)LogData & (~KSEG0_BASE)) >> PAGE_SHIFT ); // // we successfully recovered. Try setting up the hive again // if (!BlInitializeHive(BlLoaderBlock->RegistryBase, &BootHive, IsAlternate)) { return(EINVAL); } // // mark the hive as "recovered" // BootHive.Hive.BaseBlock->BootRecover = 1; } else { return(EINVAL); } } else { // // mark the hive as "no-recovered" // BootHive.Hive.BaseBlock->BootRecover = 0; } // // See whether we need to switch to the backup setup hive. // *RestartSetup = BlpCheckRestartSetup(); return(ESUCCESS); } HCELL_INDEX BlpDetermineControlSet( IN OUT BOOLEAN *LastKnownGoodBoot ) /*++ Routine Description: Determines the appropriate control set and static hardware profile. This routine ends the configuration prompt. If the user has hit a key, the configuration menu is displayed. If the user has not hit a key, but the default controlset specifies a non-zero timeout for the configuration menu, the configuration menu is displayed. If the configuration menu is displayed, further modifications to the control set and hardware profile can be made by the user. If not, the default hardware profile is selected. Arguments: LastKnownGoodBoot - On input, LastKnownGood indicates whether LKG has been selected. This value is updated to TRUE if the user chooses LKG via the profile configuration menu. Return Value: On success, HCELL_INDEX is control the set to boot from. On error, HCELL_NIL is returned and LastKnownGoodBoot is unchanged. --*/ { BOOLEAN UseLastKnownGood; BOOLEAN ConfigMenu = FALSE; HCELL_INDEX ControlSet; HCELL_INDEX ProfileControl; UNICODE_STRING DefaultControlName; UNICODE_STRING LkgControlName; PUNICODE_STRING ControlName; BOOLEAN AutoSelect; ULONG ProfileTimeout = (ULONG)0; PCM_HARDWARE_PROFILE_LIST ProfileList; PCM_HARDWARE_PROFILE_ALIAS_LIST AliasList; PCM_HARDWARE_PROFILE SelectedProfile; DOCKING_STATION_INFO dockInfo = { 0, 0, 0, FW_DOCKINFO_DOCK_STATE_UNKNOWN }; PCONFIGURATION_COMPONENT_DATA dockInfoData; ULONG flags; #if DOCKINFO_VERBOSE _TUCHAR Buffer[1024]; ULONG count; USHORT dkState; PTCHAR stateTxt; #endif // // Preinit for failure // RtlInitUnicodeString(&DefaultControlName, L"Default"); RtlInitUnicodeString(&LkgControlName, L"LastKnownGood"); UseLastKnownGood = (*LastKnownGoodBoot); if (ForceLastKnownGood) { // // last known good was selected from the // advanced boot menu. // this code path is entered when the user // enters the advanced boot menu via the // main boot menu. // UseLastKnownGood = TRUE; } if( !CmpValidateSelect(&BootHive.Hive, BootHive.Hive.BaseBlock->RootCell) ) { // // some of the essential values (Current,Default,Failed,LastKnownGood) // does not exist under \SYSTEM\Select key // return HCELL_NIL; } do_it_again: // // Get the appropriate control set // and check the hardware profile timeout value. // if (UseLastKnownGood) { ControlName = &LkgControlName; } else { ControlName = &DefaultControlName; } ControlSet = CmpFindControlSet(&BootHive.Hive, BootHive.Hive.BaseBlock->RootCell, ControlName, &AutoSelect); if (ControlSet == HCELL_NIL) { return(HCELL_NIL); } // // Check the hardware profile configuration options to // determine the timeout value for the config menu. // ProfileList = NULL; AliasList = NULL; ProfileControl = CmpFindProfileOption(&BootHive.Hive, ControlSet, &ProfileList, &AliasList, &ProfileTimeout); // // Pull the Docking information from the hardware tree. // dockInfoData = KeFindConfigurationEntry(BlLoaderBlock->ConfigurationRoot, PeripheralClass, DockingInformation, NULL); if (NULL == dockInfoData) { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_SUCCESS; BlLoaderBlock->Extension->Profile.DockingState = HW_PROFILE_DOCKSTATE_UNKNOWN; BlLoaderBlock->Extension->Profile.Capabilities = 0; BlLoaderBlock->Extension->Profile.DockID = 0; BlLoaderBlock->Extension->Profile.SerialNumber = 0; } else if (sizeof (dockInfo) <= dockInfoData->ComponentEntry.ConfigurationDataLength) { RtlCopyMemory ( &dockInfo, (PUCHAR) (dockInfoData->ConfigurationData) + sizeof(CM_PARTIAL_RESOURCE_LIST), sizeof (dockInfo)); BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_FAILURE; switch (dockInfo.ReturnCode) { case FW_DOCKINFO_SUCCESS: BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_SUCCESS; BlLoaderBlock->Extension->Profile.DockingState = HW_PROFILE_DOCKSTATE_DOCKED; BlLoaderBlock->Extension->Profile.Capabilities = dockInfo.Capabilities; BlLoaderBlock->Extension->Profile.DockID = dockInfo.DockID; BlLoaderBlock->Extension->Profile.SerialNumber = dockInfo.SerialNumber; break; case FW_DOCKINFO_SYSTEM_NOT_DOCKED: BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_SUCCESS; BlLoaderBlock->Extension->Profile.DockingState = HW_PROFILE_DOCKSTATE_UNDOCKED; BlLoaderBlock->Extension->Profile.Capabilities = dockInfo.Capabilities; BlLoaderBlock->Extension->Profile.DockID = dockInfo.DockID; BlLoaderBlock->Extension->Profile.SerialNumber = dockInfo.SerialNumber; break; case FW_DOCKINFO_DOCK_STATE_UNKNOWN: BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_SUCCESS; BlLoaderBlock->Extension->Profile.DockingState = HW_PROFILE_DOCKSTATE_UNKNOWN; BlLoaderBlock->Extension->Profile.Capabilities = dockInfo.Capabilities; BlLoaderBlock->Extension->Profile.DockID = dockInfo.DockID; BlLoaderBlock->Extension->Profile.SerialNumber = dockInfo.SerialNumber; break; case FW_DOCKINFO_FUNCTION_NOT_SUPPORTED: case FW_DOCKINFO_BIOS_NOT_CALLED: BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_SUCCESS; default: BlLoaderBlock->Extension->Profile.DockingState = HW_PROFILE_DOCKSTATE_UNSUPPORTED; BlLoaderBlock->Extension->Profile.Capabilities = dockInfo.Capabilities; BlLoaderBlock->Extension->Profile.DockID = dockInfo.DockID; BlLoaderBlock->Extension->Profile.SerialNumber = dockInfo.SerialNumber; break; } } else { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_SUCCESS; BlLoaderBlock->Extension->Profile.Capabilities = 0; BlLoaderBlock->Extension->Profile.DockID = 0; BlLoaderBlock->Extension->Profile.SerialNumber = 0; } #ifdef DOCKINFO_VERBOSE dkState = BlLoaderBlock->Extension->Profile.DockingState; if ((dkState & HW_PROFILE_DOCKSTATE_UNKNOWN) == HW_PROFILE_DOCKSTATE_UNKNOWN) { stateTxt = TEXT("Unknown"); } else if (dkState & HW_PROFILE_DOCKSTATE_DOCKED) { stateTxt = TEXT("Docked"); } else if (dkState & HW_PROFILE_DOCKSTATE_UNDOCKED) { stateTxt = TEXT("Undocked"); } else { stateTxt = TEXT("Truely unknown"); } _stprintf(Buffer, TEXT("Profile Docking: <%x, %s> := %x [%x, %x] \r\n\0"), BlLoaderBlock->Extension->Profile.Status, stateTxt, BlLoaderBlock->Extension->Profile.Capabilities, BlLoaderBlock->Extension->Profile.DockID, BlLoaderBlock->Extension->Profile.SerialNumber); ArcWrite(BlConsoleOutDeviceId, Buffer, _tcslen(Buffer)*sizeof(TCHAR), &count); _stprintf(Buffer, TEXT("press 'y' (lowercase) to continue...\r\n\0")); ArcWrite(BlConsoleOutDeviceId, Buffer, _tcslen(Buffer)*sizeof(TCHAR), &count); while(BlGetKey() != 'y') { // // Nothing // } #endif // // Filter the list of Hardware Profiles to // eliminate profiles that should not be considered // if (NULL != ProfileList) { if (ProfileList->CurrentProfileCount > 0) { BlDockInfoFilterProfileList (ProfileList, AliasList); } } // // Now check to see whether the config menu should be displayed. // Display the menu if: // - user has pressed a key OR // - we are booting from LKG and AutoSelect is FALSE. OR // - ProfileTimeout != 0 // if (!BlEndConfigPrompt()) { if (!UseLastKnownGood && ForceLastKnownGood) { // // last known good was selected from the // advanced boot menu. // this code path is entered when the user // enters the advanced boot menu by pressing // F8 while the cinfiguration hives are preparing to load. // // the currentcontrolset has already been set to the // "default" control set, so go back and try this again to // load the "lastknowngood" controlset. // UseLastKnownGood = TRUE; goto do_it_again; } ConfigMenu = FALSE; } else { ConfigMenu = TRUE; } if (ConfigMenu || ForceLastKnownGood || (UseLastKnownGood && !AutoSelect) || ((ProfileTimeout != 0) && (ProfileList != NULL) && (ProfileList->CurrentProfileCount > 1))) { // // Display the configuration menu. // BlRebootSystem = !BlConfigMenuPrompt(ProfileTimeout, &UseLastKnownGood, &ControlSet, &ProfileList, &AliasList, &SelectedProfile); if (BlRebootSystem) { REBOOT_PROCESSOR(); } BlClearScreen(); } else { if ((ProfileControl != HCELL_NIL) && (ProfileList != NULL)) { // // The system is configured to boot the default // profile directly. Since the returned profile // list is sorted by priority, the first entry in // the list is our default. // if (HW_PROFILE_STATUS_SUCCESS == BlLoaderBlock->Extension->Profile.Status) { flags = (ProfileList->Profile[0].Flags); if (flags & CM_HP_FLAGS_PRISTINE) { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_PRISTINE_MATCH; } else if (flags & CM_HP_FLAGS_TRUE_MATCH) { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_TRUE_MATCH; } else if (flags & CM_HP_FLAGS_ALIASABLE) { BlLoaderBlock->Extension->Profile.Status = HW_PROFILE_STATUS_ALIAS_MATCH; } } CmpSetCurrentProfile(&BootHive.Hive, ControlSet, &ProfileList->Profile[0]); } } // // Update the passed in parameter. We should only be doing this if we have // something real to return. // //ASSERT(ControlSet != HCELL_NIL); *LastKnownGoodBoot = UseLastKnownGood; return(ControlSet); } BOOLEAN BlpCheckRestartSetup( VOID ) /*++ Routine Description: Examine the system hive loaded and described by BootHive, to see whether it contains a Setup key, and if so, whether that key has a "RestartSetup" value that is non-0. Arguments: None. Return Value: Boolean value indicating whether the above condition is satisfied. --*/ { HCELL_INDEX KeyCell; HCELL_INDEX ValueCell; UNICODE_STRING UnicodeString; PCM_KEY_VALUE Value; PULONG Data; ULONG DataSize; // // Address the Setup key // RtlInitUnicodeString(&UnicodeString,L"Setup"); KeyCell = CmpFindSubKeyByName( &BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive,BootHive.Hive.BaseBlock->RootCell), &UnicodeString ); if(KeyCell == HCELL_NIL) { return(FALSE); } // // Find RestartSetup value in Setup key // RtlInitUnicodeString(&UnicodeString,L"RestartSetup"); ValueCell = CmpFindValueByName( &BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive,KeyCell), &UnicodeString ); if(ValueCell == HCELL_NIL) { return(FALSE); } // // Validate value and check. // Value = (PCM_KEY_VALUE)HvGetCell(&BootHive.Hive,ValueCell); if(Value->Type != REG_DWORD) { return(FALSE); } Data = (PULONG)(CmpIsHKeyValueSmall(DataSize,Value->DataLength) ? (struct _CELL_DATA *)&Value->Data : HvGetCell(&BootHive.Hive,Value->Data)); if(DataSize != sizeof(ULONG)) { return(FALSE); } return((BOOLEAN)(*Data != 0)); } #if defined(REMOTE_BOOT) BOOLEAN BlpQueryRemoteBootParameter( IN HCELL_INDEX ControlSet, IN PWSTR ValueName, IN ULONG ValueType, OUT PVOID ValueBuffer, IN ULONG ValueBufferLength ) /*++ Routine Description: Query a parameter from under Control\RemoteBoot. Arguments: ControlSet - The index of the current control set. ValueName - The name of the value to query. ValueType - The expected type of the value. ValueBuffer - The location to return the data. ValueBufferLength - The length of the buffer. Return Value: Boolean value indicating whether the data was read successfully. --*/ { UNICODE_STRING Name; HCELL_INDEX Control; HCELL_INDEX RemoteBoot; HCELL_INDEX ValueCell; PCM_KEY_VALUE Value; ULONG RealSize; BOOLEAN ValueSmall; // // Find Services node // RtlInitUnicodeString(&Name, L"Control"); Control = CmpFindSubKeyByName( &BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive,ControlSet), &Name); if (Control == HCELL_NIL) { return(FALSE); } // // Find RemoteBoot node // RtlInitUnicodeString(&Name, L"RemoteBoot"); RemoteBoot = CmpFindSubKeyByName( &BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive,Control), &Name); if (RemoteBoot == HCELL_NIL) { return(FALSE); } // // Find value // RtlInitUnicodeString(&Name, ValueName); ValueCell = CmpFindValueByName( &BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive,RemoteBoot), &Name); if (ValueCell == HCELL_NIL) { return(FALSE); } Value = (PCM_KEY_VALUE)HvGetCell(&BootHive.Hive, ValueCell); if (Value->Type != ValueType) { return(FALSE); } // // This determines if the value is small (stored right in Value) // or not, and also returns the real size of it. // ValueSmall = CmpIsHKeyValueSmall(RealSize,Value->DataLength); if (RealSize > ValueBufferLength) { return(FALSE); } RtlMoveMemory( ValueBuffer, (ValueSmall ? (struct _CELL_DATA *)&Value->Data : HvGetCell(&BootHive.Hive,Value->Data)), RealSize); return(TRUE); } #endif // defined(REMOTE_BOOT) PTCHAR BlScanRegistry( IN PWSTR BootFileSystemPath, IN OUT BOOLEAN *LastKnownGoodBoot, OUT PLIST_ENTRY BootDriverListHead, OUT PUNICODE_STRING AnsiCodepage, OUT PUNICODE_STRING OemCodepage, OUT PUNICODE_STRING LanguageTable, OUT PUNICODE_STRING OemHalFont, #ifdef _WANT_MACHINE_IDENTIFICATION OUT PUNICODE_STRING Biosinfo, #endif OUT PSETUP_LOADER_BLOCK SetupLoaderBlock, OUT BOOLEAN *ServerHive ) /*++ Routine Description: Scans the SYSTEM hive, determines the control set and static hardware profile (with appropriate input from the user) and finally computes the list of boot drivers to be loaded. Arguments: BootFileSystemPath - Supplies the name of the image the filesystem for the boot volume was read from. The last entry in BootDriverListHead will refer to this file, and to the registry key entry that controls it. LastKnownGoodBoot - On input, LastKnownGood indicates whether LKG has been selected. This value is updated to TRUE if the user chooses LKG via the profile configuration menu. BootDriverListHead - Receives a pointer to the first element of the list of boot drivers. Each element in this singly linked list will provide the loader with two paths. The first is the path of the file that contains the driver to load, the second is the path of the registry key that controls that driver. Both will be passed to the system via the loader heap. AnsiCodepage - Receives the name of the ANSI codepage data file OemCodepage - Receives the name of the OEM codepage data file Language - Receives the name of the language case table data file OemHalfont - receives the name of the OEM font to be used by the HAL. SetupLoaderBlock - if non-NULL, used to return information about the net boot card. ServerHive - Returns TRUE if this is a server hive, else FALSE. Return Value: NULL if all is well. NON-NULL if the hive is corrupt or inconsistent. Return value is a pointer to a string that describes what is wrong. On error LastKnownGood is unchanged. --*/ { HCELL_INDEX ControlSet; UNICODE_STRING ControlName; BOOLEAN AutoSelect; BOOLEAN KeepGoing; UNICODE_STRING TmpName; HCELL_INDEX Control; HCELL_INDEX ProductOptions; HCELL_INDEX ValueCell; PCM_KEY_VALUE Value; ULONG RealSize; BOOLEAN ValueSmall; PWCHAR CellString; BOOLEAN UsingLastKnownGood; #ifdef _WANT_MACHINE_IDENTIFICATION UNICODE_STRING regDate; CHAR date[9]; ANSI_STRING ansiString; UNICODE_STRING biosDate; WCHAR buffer[9]; BOOLEAN biosDateChanged; #endif // // Preinit. // UsingLastKnownGood = *LastKnownGoodBoot; // // Get the appropriate control set. // ControlSet = BlpDetermineControlSet(&UsingLastKnownGood); if (ControlSet == HCELL_NIL) { return(TEXT("CmpFindControlSet")); } if (!CmpFindNLSData(&BootHive.Hive, ControlSet, AnsiCodepage, OemCodepage, LanguageTable, OemHalFont)) { return(TEXT("CmpFindNLSData")); } InitializeListHead(BootDriverListHead); if (!CmpFindDrivers(&BootHive.Hive, ControlSet, BootLoad, BootFileSystemPath, BootDriverListHead)) { return(TEXT("CmpFindDriver")); } if (!CmpSortDriverList(&BootHive.Hive, ControlSet, BootDriverListHead)) { return(TEXT("Missing or invalid Control\\ServiceGroupOrder\\List registry value")); } if (!CmpResolveDriverDependencies(BootDriverListHead)) { return(TEXT("CmpResolveDriverDependencies")); } if (ServerHive != NULL) { *ServerHive = FALSE; // // Find Control node // RtlInitUnicodeString(&TmpName, L"Control"); Control = CmpFindSubKeyByName(&BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive, ControlSet), &TmpName ); if (Control == HCELL_NIL) { return(TEXT("Missing Control key")); } // // Find ProductOptions node // RtlInitUnicodeString(&TmpName, L"ProductOptions"); ProductOptions = CmpFindSubKeyByName(&BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive,Control), &TmpName ); if (ProductOptions == HCELL_NIL) { return(TEXT("Missing ProductOptions key")); } // // Find value // RtlInitUnicodeString(&TmpName, L"ProductType"); ValueCell = CmpFindValueByName(&BootHive.Hive, (PCM_KEY_NODE)HvGetCell(&BootHive.Hive, ProductOptions), &TmpName ); if (ValueCell == HCELL_NIL) { return(TEXT("Missing ProductType value")); } Value = (PCM_KEY_VALUE)HvGetCell(&BootHive.Hive, ValueCell); if (Value->Type != REG_SZ) { return(TEXT("Bad ProductType value")); } // // This determines if the value is small (stored right in Value) // or not, and also returns the real size of it. // CellString = (PWCHAR)(CmpIsHKeyValueSmall(RealSize, Value->DataLength) ? (struct _CELL_DATA *)&Value->Data : HvGetCell(&BootHive.Hive, Value->Data) ); // // Now compare if this is a server hive or not. // The proper way to check this is to check the string against // the "professional" type 'WinNT'. If it's not professional, // it must be a server. (There are multiple strings for different // server flavours.) // *ServerHive = (_wcsicmp(L"WinNT", CellString) != 0); } #if defined(REMOTE_BOOT) if (SetupLoaderBlock != NULL) { ULONG EnableIpSecurity; if (BlpQueryRemoteBootParameter( ControlSet, L"EnableIpSecurity", REG_DWORD, &EnableIpSecurity, sizeof(EnableIpSecurity))) { if (EnableIpSecurity != 0) { SetupLoaderBlock->Flags |= SETUPBLK_FLAGS_IPSEC_ENABLED; } } if (BlpQueryRemoteBootParameter( ControlSet, L"NetCardInfo", REG_BINARY, SetupLoaderBlock->NetbootCardInfo, SetupLoaderBlock->NetbootCardInfoLength)) { if (!BlpQueryRemoteBootParameter( ControlSet, L"HardwareId", REG_SZ, SetupLoaderBlock->NetbootCardHardwareId, sizeof(SetupLoaderBlock->NetbootCardHardwareId))) { SetupLoaderBlock->NetbootCardHardwareId[0] = L'\0'; } if (!BlpQueryRemoteBootParameter( ControlSet, L"DriverName", REG_SZ, SetupLoaderBlock->NetbootCardDriverName, sizeof(SetupLoaderBlock->NetbootCardDriverName))) { SetupLoaderBlock->NetbootCardDriverName[0] = L'\0'; } if (!BlpQueryRemoteBootParameter( ControlSet, L"ServiceName", REG_SZ, SetupLoaderBlock->NetbootCardServiceName, sizeof(SetupLoaderBlock->NetbootCardServiceName))) { SetupLoaderBlock->NetbootCardServiceName[0] = L'\0'; } } } #endif // defined(REMOTE_BOOT) #ifdef _WANT_MACHINE_IDENTIFICATION biosDateChanged = TRUE; if (CmpGetBiosDateFromRegistry(&BootHive.Hive, ControlSet, ®Date)) { // // Read the date from the BIOS ROM. // memcpy(date, (PVOID)0xffff5, 8); date[8] = '\0'; // // Convert the date into unicode string. // ansiString.Buffer = date; ansiString.Length = (USHORT) strlen(date); ansiString.MaximumLength = ansiString.Length + 1; biosDate.Buffer = buffer; biosDate.MaximumLength = (ansiString.Length << 1) + sizeof(UNICODE_NULL); RtlAnsiStringToUnicodeString(&biosDate, &ansiString, FALSE); // // Check if the dates are different. // if (RtlCompareUnicodeString(&biosDate, ®Date, FALSE) == 0) { biosDateChanged = FALSE; } } Biosinfo->Length = 0; if (biosDateChanged) { CmpGetBiosinfoFileNameFromRegistry(&BootHive.Hive, ControlSet, Biosinfo); } #endif // defined(_WANT_MACHINE_IDENTIFICATION) *LastKnownGoodBoot = UsingLastKnownGood; return( NULL ); } ARC_STATUS BlAddToBootDriverList( IN PLIST_ENTRY BootDriverListHead, IN PWSTR DriverName, IN PWSTR Name, IN PWSTR Group, IN ULONG Tag, IN ULONG ErrorControl, IN BOOLEAN InsertAtHead ) /*++ Routine Description: Adds a single driver to the boot driver list. The list is NOT re-sorted. Arguments: BootDriverListHead - Receives a pointer to the first element of the list of boot drivers. Each element in this singly linked list will provide the loader with two paths. The first is the path of the file that contains the driver to load, the second is the path of the registry key that controls that driver. Both will be passed to the system via the loader heap. DriverName - The name of the driver. This will be stored with \system32\drivers on the front. Name - The service name of the driver. Typically will be DriverName without the ".sys". Group - The group this driver is in. Tag - The tag value within the group for this driver. ErrorControl - The error control value for this driver. InsertAtHead - Should this driver be inserted at the head of the list, otw tail. Return Value: ESUCCESS if the driver is successfully inserted. ENOMEM if there is an allocation failure. --*/ { PBOOT_DRIVER_NODE DriverNode; PBOOT_DRIVER_LIST_ENTRY DriverListEntry; USHORT Length; DriverNode = BlpHiveAllocate(sizeof(BOOT_DRIVER_NODE),FALSE,0); if (DriverNode == FALSE) { return ENOMEM; } DriverListEntry = &DriverNode->ListEntry; // // FilePath // Length = sizeof(L"System32\\Drivers\\") + (wcslen(DriverName) * sizeof(WCHAR)); DriverListEntry->FilePath.Buffer = BlpHiveAllocate(Length,FALSE,0); if (DriverListEntry->FilePath.Buffer == NULL) { return ENOMEM; } DriverListEntry->FilePath.Length = 0; DriverListEntry->FilePath.MaximumLength = Length; RtlAppendUnicodeToString(&DriverListEntry->FilePath, L"System32\\Drivers\\"); RtlAppendUnicodeToString(&DriverListEntry->FilePath, DriverName); // // Registry Path // Length = sizeof(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\") + (wcslen(Name) * sizeof(WCHAR)); DriverListEntry->RegistryPath.Buffer = BlpHiveAllocate(Length,FALSE,0); if (DriverListEntry->RegistryPath.Buffer == NULL) { return ENOMEM; } DriverListEntry->RegistryPath.Length = 0; DriverListEntry->RegistryPath.MaximumLength = Length; RtlAppendUnicodeToString(&DriverListEntry->RegistryPath, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"); RtlAppendUnicodeToString(&DriverListEntry->RegistryPath, Name); // // Group // Length = (wcslen(Group) + 1) * sizeof(WCHAR); DriverNode->Group.Buffer = BlpHiveAllocate(Length,FALSE,0); if (DriverNode->Group.Buffer == NULL) { return ENOMEM; } DriverNode->Group.Length = 0; DriverNode->Group.MaximumLength = Length; RtlAppendUnicodeToString(&DriverNode->Group, Group); // // Name // Length = (wcslen(Name) + 1) * sizeof(WCHAR); DriverNode->Name.Buffer = BlpHiveAllocate(Length,FALSE,0); if (DriverNode->Name.Buffer == NULL) { return ENOMEM; } DriverNode->Name.Length = 0; DriverNode->Name.MaximumLength = Length; RtlAppendUnicodeToString(&DriverNode->Name, Name); // // Tag/ErrorControl // DriverNode->Tag = Tag; DriverNode->ErrorControl = ErrorControl; if (InsertAtHead) { InsertHeadList(BootDriverListHead, &DriverListEntry->Link); } else { InsertTailList(BootDriverListHead, &DriverListEntry->Link); } return ESUCCESS; } #define HFILE_TYPE_ALTERNATE 1 // alternate, in order for boot to be able to boot downlevel OSes BOOLEAN BlInitializeHive( IN PVOID HiveImage, IN PCMHIVE Hive, IN BOOLEAN IsAlternate ) /*++ Routine Description: Initializes the hive data structure based on the in-memory hive image. Arguments: HiveImage - Supplies a pointer to the in-memory hive image. Hive - Supplies the CMHIVE structure to be filled in. IsAlternate - Supplies whether or not the hive is the alternate hive, which indicates that the primary hive is corrupt and should be rewritten by the system. Return Value: TRUE - Hive successfully initialized. FALSE - Hive is corrupt. --*/ { NTSTATUS status; ULONG HiveCheckCode; status = HvInitializeHive( &Hive->Hive, HINIT_MEMORY_INPLACE, FALSE, IsAlternate ? HFILE_TYPE_ALTERNATE : HFILE_TYPE_PRIMARY, HiveImage, (PALLOCATE_ROUTINE)BlpHiveAllocate, // allocate NULL, // free NULL, // setsize NULL, // write NULL, // read NULL, // flush 1, // cluster NULL ); if (!NT_SUCCESS(status)) { return FALSE; } HiveCheckCode = CmCheckRegistry(Hive,CM_CHECK_REGISTRY_LOADER_CLEAN|CM_CHECK_REGISTRY_HIVE_CHECK); if (HiveCheckCode != 0) { return(FALSE); } else { return TRUE; } } PVOID BlpHiveAllocate( IN ULONG Length, IN BOOLEAN UseForIo, IN ULONG Tag ) /*++ Routine Description: Wrapper for hive allocation calls. It just calls BlAllocateHeap. Arguments: Length - Supplies the size of block required in bytes. UseForIo - Supplies whether or not the memory is to be used for I/O (this is currently ignored) Return Value: address of the block of memory or NULL if no memory available --*/ { return(BlAllocateHeap(Length)); } NTSTATUS HvLoadHive( PHHIVE Hive ) { UNREFERENCED_PARAMETER(Hive); return(STATUS_SUCCESS); } NTSTATUS HvMapHive( PHHIVE Hive ) { UNREFERENCED_PARAMETER(Hive); return(STATUS_SUCCESS); } NTSTATUS HvpAdjustHiveFreeDisplay( IN PHHIVE Hive, IN ULONG HiveLength, IN HSTORAGE_TYPE Type ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(HiveLength); UNREFERENCED_PARAMETER(Type); return(STATUS_SUCCESS); } VOID HvpAddFreeCellHint( PHHIVE Hive, HCELL_INDEX Cell, ULONG Index, HSTORAGE_TYPE Type ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(Cell); UNREFERENCED_PARAMETER(Index); UNREFERENCED_PARAMETER(Type); } VOID HvpRemoveFreeCellHint( PHHIVE Hive, HCELL_INDEX Cell, ULONG Index, HSTORAGE_TYPE Type ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(Cell); UNREFERENCED_PARAMETER(Index); UNREFERENCED_PARAMETER(Type); } HCELL_INDEX HvpFindFreeCell( PHHIVE Hive, ULONG Index, ULONG NewSize, HSTORAGE_TYPE Type, HCELL_INDEX Vicinity ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(Index); UNREFERENCED_PARAMETER(Type); UNREFERENCED_PARAMETER(NewSize); UNREFERENCED_PARAMETER(Vicinity); return HCELL_NIL; } VOID CmpTouchView( IN PCMHIVE CmHive, IN PCM_VIEW_OF_FILE CmView, IN ULONG Cell ) { UNREFERENCED_PARAMETER(CmHive); UNREFERENCED_PARAMETER(CmView); UNREFERENCED_PARAMETER(Cell); } NTSTATUS CmpMapThisBin( PCMHIVE CmHive, HCELL_INDEX Cell, BOOLEAN Touch ) { UNREFERENCED_PARAMETER(CmHive); UNREFERENCED_PARAMETER(Cell); UNREFERENCED_PARAMETER(Touch); return(STATUS_SUCCESS); } /* NTSTATUS CmpMapCmView( IN PCMHIVE CmHive, IN ULONG FileOffset, OUT PCM_VIEW_OF_FILE *CmView ) { UNREFERENCED_PARAMETER(CmHive); UNREFERENCED_PARAMETER(FileOffset); UNREFERENCED_PARAMETER(CmView); return(STATUS_SUCCESS); } VOID CmpPinCmView ( IN PCMHIVE CmHive, PCM_VIEW_OF_FILE CmView ) { UNREFERENCED_PARAMETER(CmHive); UNREFERENCED_PARAMETER(CmView); } VOID CmpUnPinCmView ( IN PCMHIVE CmHive, IN PCM_VIEW_OF_FILE CmView, IN BOOLEAN SetClean ) { UNREFERENCED_PARAMETER(CmHive); UNREFERENCED_PARAMETER(CmView); UNREFERENCED_PARAMETER(SetClean); } VOID CmpLazyFlush( VOID ) { } */ /* NTSTATUS CmpDoFileSetSize( PHHIVE Hive, ULONG FileType, ULONG FileSize ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(FileType); UNREFERENCED_PARAMETER(FileSize); return(STATUS_SUCCESS); } */ BOOLEAN HvMarkCellDirty( PHHIVE Hive, HCELL_INDEX Cell ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(Cell); return(TRUE); } BOOLEAN HvMarkDirty( PHHIVE Hive, HCELL_INDEX Start, ULONG Length, BOOLEAN DirtyAndPin ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(Start); UNREFERENCED_PARAMETER(Length); UNREFERENCED_PARAMETER(DirtyAndPin); return(TRUE); } BOOLEAN HvpDoWriteHive( PHHIVE Hive, ULONG FileType ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(FileType); return(TRUE); } BOOLEAN HvpGrowLog1( PHHIVE Hive, ULONG Count ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(Count); return(TRUE); } BOOLEAN HvpGrowLog2( PHHIVE Hive, ULONG Size ) { UNREFERENCED_PARAMETER(Hive); UNREFERENCED_PARAMETER(Size); return(TRUE); } BOOLEAN CmpValidateHiveSecurityDescriptors( IN PHHIVE Hive, OUT PBOOLEAN ResetSD ) { UNREFERENCED_PARAMETER(Hive); return(TRUE); } BOOLEAN CmpTestRegistryLock() { return TRUE; } BOOLEAN CmpTestRegistryLockExclusive() { return TRUE; } BOOLEAN HvIsBinDirty( IN PHHIVE Hive, IN HCELL_INDEX Cell ) { return(FALSE); } PHBIN HvpAddBin( IN PHHIVE Hive, IN ULONG NewSize, IN HSTORAGE_TYPE Type ) { return(NULL); } VOID CmpReleaseGlobalQuota( IN ULONG Size ) { } #if DOCKINFO_VERBOSE VOID BlDiagDisplayProfileList( IN PCM_HARDWARE_PROFILE_LIST ProfileList, IN PCM_HARDWARE_PROFILE_ALIAS_LIST AliasList, IN BOOLEAN WaitForUserInput ) /*++ Routine Description: This is a diagnostic function only! Display hardware profile list on console, optionally wait for user input before proceeding. Arguments: ProfileList - Supplies a list of hardware profiles to display WaitForUserInput - Prompt user to hit a key ('y') to continue, and wait for user's input if TRUE. Don't wait if FALSE. Return Value: None. --*/ { TCHAR Buffer[200]; TCHAR StrFriendlyName[30]; PTCHAR AliasType [] = { TEXT("NotAliasable"), // 0 TEXT("Aliasable "), // 1 TEXT("True Match "), // 2 TEXT("True & Alias"), // 3 TEXT("Pristine "), // 4 TEXT("Pris & Alias"), // 5 TEXT("Pris & True "), // 6 TEXT("P & A & T ") // 7 }; ULONG Count; ULONG i; // display header _stprintf(Buffer, TEXT("Profiles: \r\n\0")); ArcWrite(BlConsoleOutDeviceId, Buffer, _tcslen(Buffer)*sizeof(TCHAR), &Count); // for each hardware profile for (i = 0; i < ProfileList->CurrentProfileCount; ++i) { #ifdef UNICODE wcsncpy( StrFriendlyName, ProfileList->Profile[i].FriendlyName, ProfileList->Profile[i].NameLength, ); StrFriendlyName[29] = L'\0'; StrFriendlyName[ProfileList->Profile[i].NameLength] = L'\0'; #else // copy and convert unicode fields to ascii for output RtlUnicodeToMultiByteN(StrFriendlyName, sizeof(StrFriendlyName), &Count, ProfileList->Profile[i].FriendlyName, ProfileList->Profile[i].NameLength); StrFriendlyName[Count] = '\0'; #endif // display info for current profile _stprintf(Buffer, TEXT(" <%2ld> %2ld - %s \"%s\"\r\n\0"), ProfileList->Profile[i].PreferenceOrder, ProfileList->Profile[i].Id, AliasType[ ProfileList->Profile[i].Flags ], StrFriendlyName); ArcWrite( BlConsoleOutDeviceId, Buffer, _tcslen(Buffer)*sizeof(TCHAR), &Count ); } // display header _stprintf(Buffer, TEXT("Aliases: DockState [DockID, SerialNumber]\r\n\0")); ArcWrite(BlConsoleOutDeviceId, Buffer, _tcslen(Buffer)*sizeof(TCHAR), &Count); if (AliasList) { for (i = 0; i < AliasList->CurrentAliasCount; i++) { _stprintf(Buffer, TEXT(" <%2ld> %x [%x, %x]\r\n\0"), AliasList->Alias[i].ProfileNumber, AliasList->Alias[i].DockState, AliasList->Alias[i].DockID, AliasList->Alias[i].SerialNumber); ArcWrite(BlConsoleOutDeviceId, Buffer, _tcslen(Buffer)*sizeof(TCHAR), &Count); } } if(WaitForUserInput) { // display prompt and wait for user input to continue _stprintf(Buffer, TEXT("press 'y' (lowercase) to continue...\r\n\0")); ArcWrite(BlConsoleOutDeviceId, Buffer, _tcslen(Buffer)*sizeof(TCHAR), &Count); while (BlGetKey() != 'y') { // // nothing // } } } #endif VOID BlDockInfoFilterDockingState( IN OUT PCM_HARDWARE_PROFILE_LIST ProfileList, IN OUT PCM_HARDWARE_PROFILE_ALIAS_LIST AliasList, IN ULONG DockingState, IN ULONG DockID, IN ULONG SerialNumber ) /*++ Routine Description: Discard all hardware profiles that do not have the DOCKINFO_UNDOCKED bit set in the DockState field Arguments: ProfileList - Supplies a list of hardware profiles. Returns a list containing a subset of the supplied hardware profiles. Return Value: None. --*/ { ULONG i = 0; ULONG j; ULONG len; ULONG mask = HW_PROFILE_DOCKSTATE_UNDOCKED | HW_PROFILE_DOCKSTATE_DOCKED; BOOLEAN trueMatch = FALSE; #if DOCKINFO_VERBOSE TCHAR buffer[200]; ULONG count; #endif if (AliasList) { while (i < AliasList->CurrentAliasCount) { if (((AliasList->Alias[i].DockState & mask) != 0) && ((AliasList->Alias[i].DockState & mask) != DockingState)) { // // This alias claims to be docked or undocked, but does not // match the current state. Therefore skip it. // ; } else if ((AliasList->Alias[i].DockID == DockID) && (AliasList->Alias[i].SerialNumber == SerialNumber)) { // // This alias matches so mark the profile. // for (j = 0; j < ProfileList->CurrentProfileCount; j++) { if (ProfileList->Profile[j].Id == AliasList->Alias[i].ProfileNumber) { ProfileList->Profile[j].Flags = CM_HP_FLAGS_TRUE_MATCH; trueMatch = TRUE; } } } i++; } } #if DOCKINFO_VERBOSE _stprintf(buffer, TEXT("Filtering Profiles ...\r\n\0")); ArcWrite(BlConsoleOutDeviceId, buffer, _tcslen(buffer)*sizeof(TCHAR), &count); #endif i = 0; while (i < ProfileList->CurrentProfileCount) { if ((ProfileList->Profile[i].Flags & CM_HP_FLAGS_PRISTINE) && !trueMatch && AliasList) { // // Leave this one in the list // i++; continue; } else if (ProfileList->Profile[i].Flags & CM_HP_FLAGS_ALIASABLE) { // // Leave this one in the list // i++; continue; } else if (ProfileList->Profile[i].Flags & CM_HP_FLAGS_TRUE_MATCH) { // // Leave this one in the list // i++; continue; } // // discard this profile by (1) shifting remaining profiles in // array to fill in the space of this discarded profile // and (2) decrementing profile count // len = ProfileList->CurrentProfileCount - i - 1; if (0 < len) { RtlMoveMemory(&ProfileList->Profile[i], &ProfileList->Profile[i+1], sizeof(CM_HARDWARE_PROFILE) * len); } --ProfileList->CurrentProfileCount; } } VOID BlDockInfoFilterProfileList( IN OUT PCM_HARDWARE_PROFILE_LIST ProfileList, IN OUT PCM_HARDWARE_PROFILE_ALIAS_LIST AliasList ) /*++ Routine Description: Filters hardware profile list by discarding hardware profiles that do not match the docking station information returned by NTDETECT. Arguments: ProfileList - Supplies a list of hardware profiles. - Returns a list containing a subset of the supplied hardware profiles. Return Value: None. --*/ { #if DOCKINFO_VERBOSE // display ProfileList prior to filtering BlDiagDisplayProfileList(ProfileList, AliasList, TRUE); #endif if (1 == ProfileList->CurrentProfileCount) { if (ProfileList->Profile[0].Flags & CM_HP_FLAGS_PRISTINE) { // // Nothing to filter. // return; } } BlDockInfoFilterDockingState ( ProfileList, AliasList, BlLoaderBlock->Extension->Profile.DockingState, BlLoaderBlock->Extension->Profile.DockID, BlLoaderBlock->Extension->Profile.SerialNumber); #if DOCKINFO_VERBOSE // display ProfileList prior to filtering BlDiagDisplayProfileList(ProfileList, AliasList, TRUE); #endif } int BlIsReturnToOSChoicesValid( VOID ) /*++ Routine Description: Indicates whether the "Return to OS Choices Menu" should be shown as advanced boot option or not. Arguments: None Return Value: 1 if yes otherwise 0. --*/ { return BlShowReturnToOSChoices; }