/*++ Copyright (c) 1992 Microsoft Corporation Module Name: spboot.c Abstract: accessing and configuring boot variables. Author: Sunil Pai (sunilp) 26-October-1993 Revision History: --*/ #include "spprecmp.h" #pragma hdrstop #include #include #if defined(EFI_NVRAM_ENABLED) #include #include #endif #include "bootvar.h" // // Globals to this module // static ULONG Timeout; static PWSTR Default; ULONG DefaultSignature; static PWSTR *BootVars[MAXBOOTVARS]; static BOOLEAN CleanSysPartOrphan = FALSE; PWSTR *CurrentNtDirectoryList = NULL; // do NOT change the order of the elements in this array. PCHAR NvramVarNames[MAXBOOTVARS] = { LOADIDENTIFIERVAR, OSLOADERVAR, OSLOADPARTITIONVAR, OSLOADFILENAMEVAR, OSLOADOPTIONSVAR, SYSTEMPARTITIONVAR }; PCHAR OldBootVars[MAXBOOTVARS]; PWSTR NewBootVars[MAXBOOTVARS]; #if defined(_X86_) BOOLEAN IsArcChecked = FALSE; BOOLEAN IsArcMachine; #endif PSP_BOOT_ENTRY SpBootEntries = NULL; PBOOT_OPTIONS SpBootOptions = NULL; RedirectSwitchesModeEnum RedirectSwitchesMode = UseDefaultSwitches; REDIRECT_SWITCHES RedirectSwitches; #ifdef _X86_ extern BOOLEAN g_Win9xBackup; #endif // // Local functions. // PWSTR SpArcPathFromBootSet( IN BOOTVAR BootVariable, IN ULONG Component ); BOOLEAN SpConvertArcBootEntries ( IN ULONG MaxComponents ); VOID SpCreateBootEntry( IN ULONG_PTR Status, IN PDISK_REGION BootFileRegion, IN PWSTR BootFilePath, IN PDISK_REGION OsLoadRegion, IN PWSTR OsLoadPath, IN PWSTR OsLoadOptions, IN PWSTR FriendlyName ); PCHAR SppGetArcEnvVar( IN BOOTVAR Variable ); VOID SpFreeBootEntries ( VOID ); BOOLEAN SppSetArcEnvVar( IN BOOTVAR Variable, IN PWSTR *VarComponents, IN BOOLEAN bWriteVar ); #if defined(EFI_NVRAM_ENABLED) typedef struct _HARDDISK_NAME_TRANSLATION { struct _HARDDISK_NAME_TRANSLATION *Next; PWSTR VolumeName; PWSTR PartitionName; } HARDDISK_NAME_TRANSLATION, *PHARDDISK_NAME_TRANSLATION; PHARDDISK_NAME_TRANSLATION SpHarddiskNameTranslations = NULL; BOOLEAN SpBuildHarddiskNameTranslations ( VOID ); BOOLEAN SpFlushEfiBootEntries ( VOID ); BOOLEAN SpReadAndConvertEfiBootEntries ( VOID ); ULONG SpSafeWcslen ( IN PWSTR String, IN PWSTR Max ); VOID SpTranslateFilePathToRegion ( IN PFILE_PATH FilePath, OUT PDISK_REGION *DiskRegion, OUT PWSTR *PartitionNtName, OUT PWSTR *PartitionRelativePath ); #define ADD_OFFSET(_p,_o) (PVOID)((PUCHAR)(_p) + (_p)->_o) #endif // // Function implementation // BOOLEAN SpInitBootVars( ) /*++ Routine Description: Captures the state of the NVRAM Boot Variables. Arguments: None. Return Value: --*/ { BOOLEAN Status = TRUE; BOOTVAR i; ULONG Component, MaxComponents, SysPartComponents; PCHAR puArcString; // SGI CLEAR_CLIENT_SCREEN(); SpDisplayStatusText(SP_STAT_EXAMINING_FLEXBOOT,DEFAULT_STATUS_ATTRIBUTE); // // Initialize the boot variables from the corresponding NVRAM variables // #if defined(EFI_NVRAM_ENABLED) if (SpIsEfi()) { // // Build a list of all of the \Device\HarddiskN\PartitionM symbolic // links, along with their translations to \Device\HarddiskVolumeN // device names. This list is used to translate the // \Device\HarddiskVolumeN names returned by NtTranslateFilePath into // names that setupdd can translate to ARC names. // SpBuildHarddiskNameTranslations(); // // Read the boot entries from NVRAM and convert them into our // internal format. // Status = SpReadAndConvertEfiBootEntries(); } else #endif { if (SpIsArc()) { ULONG NumComponents; for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { OldBootVars[i] = SppGetArcEnvVar( i ); SpGetEnvVarWComponents( OldBootVars[i], BootVars + i, &NumComponents ); } Timeout = DEFAULT_TIMEOUT; Default = NULL; #if defined(_AMD64_) || defined(_X86_) } else { Spx86InitBootVars( BootVars, &Default, &Timeout ); #endif // defined(_AMD64_) || defined(_X86_) } // // We now go back and replace all NULL OsLoadOptions with "", because we // validate a boot set by making sure that all components are non-NULL. // // First, find the maximum number of components in any of the other // boot variables, so that we can make OsLoadOptions have this many. // (We also disregard SYSTEMPARTITION since some machines have this component // sitting all by itself on a new machine.) // MaxComponents = 0; for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { if(i != OSLOADOPTIONS) { for(Component = 0; BootVars[i][Component]; Component++); if (i == SYSTEMPARTITION) { SysPartComponents = Component; } else if(Component > MaxComponents) { MaxComponents = Component; } } } if(SysPartComponents > MaxComponents) { CleanSysPartOrphan = TRUE; } for(Component = 0; BootVars[OSLOADOPTIONS][Component]; Component++); if(Component < MaxComponents) { // // Then we need to add empty strings to fill it out. // BootVars[OSLOADOPTIONS] = SpMemRealloc(BootVars[OSLOADOPTIONS], (MaxComponents + 1) * sizeof(PWSTR *)); ASSERT(BootVars[OSLOADOPTIONS]); BootVars[OSLOADOPTIONS][MaxComponents] = NULL; for(; Component < MaxComponents; Component++) { BootVars[OSLOADOPTIONS][Component] = SpDupStringW(L""); } } // // Now convert the ARC boot sets into our internal format. // Status = SpConvertArcBootEntries(MaxComponents); } CLEAR_CLIENT_SCREEN(); return ( Status ); } BOOLEAN SpFlushBootVars( ) /*++ Routine Description: Updates the NVRAM variables / boot.ini from the current state of the boot variables. Arguments: Return Value: --*/ { BOOLEAN Status, OldStatus; BOOTVAR i, iFailPoint; CHAR TimeoutValue[24]; #if defined(EFI_NVRAM_ENABLED) if (SpIsEfi()) { // // This is an EFI machine. Write changed boot entries back to NVRAM. // Status = SpFlushEfiBootEntries(); } else #endif { Status = FALSE; if (SpIsArc()) { // // Run through all the boot variables and set the corresponding // NVRAM variables for(OldStatus = TRUE, i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { Status = SppSetArcEnvVar( i, BootVars[i], OldStatus ); if(Status != OldStatus) { iFailPoint = i; OldStatus = Status; } } // if we failed in writing any of the variables, then restore everything we // modified back to its original state. if(!Status) { for(i = FIRSTBOOTVAR; i < iFailPoint; i++) { HalSetEnvironmentVariable(NvramVarNames[i], OldBootVars[i]); } } // Free all of the old boot variable strings for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { SpMemFree(OldBootVars[i]); OldBootVars[i] = NULL; } // // Now set the timeout. // if(Status) { Status = FALSE; sprintf(TimeoutValue,"%u",Timeout); if((HalSetEnvironmentVariable("COUNTDOWN",TimeoutValue) == ESUCCESS) && (HalSetEnvironmentVariable("AUTOLOAD" ,"YES" ) == ESUCCESS)) { Status = TRUE; } } #if defined(_AMD64_) || defined(_X86_) } else { Status = Spx86FlushBootVars( BootVars, Timeout, Default ); #endif // defined(_AMD64_) || defined(_X86_) } } return( Status ); } VOID SpFreeBootVars( ) /*++ Routine Description: To free any memory allocated and do other cleanup Arguments: None Return Value: None --*/ { BOOTVAR i; // // Free internal-format boot entries. // SpFreeBootEntries(); #if defined(EFI_NVRAM_ENABLED) if (!SpIsEfi()) #endif { // // Go through the globals and free them // for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { if( BootVars[i] ) { SpFreeEnvVarComponents( BootVars[i] ); BootVars[i] = NULL; } } if ( Default ) { SpMemFree( Default ); Default = NULL; } } return; } VOID SpAddBootSet( IN PWSTR *BootSet, IN BOOLEAN DefaultOS, IN ULONG Signature ) /*++ Routine Description: To add a new system to the installed system list. The system is added as the first bootset. If is found in the currently installed boot sets the boot set is extracted and shifted to position 0. Arguments: BootSet - A list of the boot variables to use. Default - Whether this system is to be the default system to boot. Return Value: Component list of the value of the boot variable. --*/ { BOOTVAR i; ULONG MatchComponent, j; LONG k; BOOLEAN ValidBootSet, ComponentMatched; PWSTR Temp; ASSERT( !SpIsEfi() ); // // Validate the BootSet passed in // for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { ASSERT( BootSet[i] ); } // // Examine all the boot sets and make sure we don't have a boot set // already matching. Note that we will compare all variables in // tandem. We are not interested in matches which are generated by // the variables not being in tandem because they are difficult to // shift around. // ValidBootSet = TRUE; ComponentMatched = FALSE; for( MatchComponent = 0; BootVars[OSLOADPARTITION][MatchComponent]; MatchComponent++ ) { // // Validate the boot set at the current component // for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { ValidBootSet = ValidBootSet && BootVars[i][MatchComponent]; } if( !ValidBootSet ) { break; } // // Valid Boot Set, compare the components against what we have in the // current BootSet // ComponentMatched = TRUE; for(i = FIRSTBOOTVAR; ComponentMatched && i <= LASTBOOTVAR; i++) { ComponentMatched = !_wcsicmp( BootSet[i], BootVars[i][MatchComponent] ); } if( ComponentMatched ) { break; } } // // If component didn't match then prepend the BootSet to the boot sets // that currently exist. It is important to prepend the BootSet, because // appending the BootSet doesn't guarantee a matched BootSet in the // environment variables. If a match was found then we // have a cleanly matched set which can be exchanged with the first // one in the set. // if( ComponentMatched ) { // If the currently selected OS is to be the default: // Shift down all variables from position 0 to MatchComponent - 1 // and store whatever was there at MatchComponent at position 0 // if ( DefaultOS && MatchComponent != 0 ) { for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { Temp = BootVars[i][MatchComponent]; for( k = MatchComponent - 1; k >= 0; k-- ) { BootVars[i][k + 1] = BootVars[i][k]; } BootVars[i][0] = Temp; } } } else { for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { // // Find out the size of the current value // for(j = 0; BootVars[i][j]; j++) { } // // Realloc the current buffer to hold one more // BootVars[i] = SpMemRealloc( BootVars[i], (j + 1 + 1)*sizeof(PWSTR) ); // // Shift all the variables down one and store the current value // at index 0; // for( k = j; k >= 0 ; k-- ) { BootVars[i][k+1] = BootVars[i][k]; } BootVars[i][0] = SpDupStringW( BootSet[i] ); ASSERT( BootVars[i][0] ); } } // // If this has been indicated as the default then set this to be the // default OS after freeing the current default variable // if( DefaultOS ) { if( Default ) { SpMemFree( Default ); } Default = SpMemAlloc( MAX_PATH * sizeof(WCHAR) ); ASSERT( Default ); wcscpy( Default, BootSet[OSLOADPARTITION] ); wcscat( Default, BootSet[OSLOADFILENAME] ); DefaultSignature = Signature; } return; } VOID SpDeleteBootSet( IN PWSTR *BootSet, OUT PWSTR *OldOsLoadOptions OPTIONAL ) /*++ Routine Description: To delete all boot sets in the list matching the boot set provided. Note that the information to use in comparing the bootset is provided by selectively providing fields in the boot set. So in the boot set if the system partition is not provided it is not used in the comparison to see if the boot sets match. By providing all NULL members we can delete all the boot sets currently present. Arguments: BootSet - A list of the boot variables to use. Return Value: None. --*/ { ULONG Component, j; BOOLEAN ValidBootSet, ComponentMatched; BOOTVAR i; PWSTR OsPartPath; ASSERT( !SpIsEfi() ); Component = 0; while(TRUE) { // // See if we have any boot sets left, if none left we are done // ValidBootSet = TRUE; for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { ValidBootSet = ValidBootSet && BootVars[i][Component]; } if( !ValidBootSet ) { break; } // // Valid Boot Set, compare the components against what we have in the // current BootSet. Use only members of the BootSet which are not NULL // ComponentMatched = TRUE; for(i = FIRSTBOOTVAR; ComponentMatched && i <= LASTBOOTVAR; i++) { if( BootSet[i] ) { if((i == OSLOADPARTITION) || (i == SYSTEMPARTITION)) { // // Then we may have a boot set existing in tertiary ARC path form, so // we first translate this path to a primary or secondary ARC path. // OsPartPath = SpArcPathFromBootSet(i, Component); ComponentMatched = !_wcsicmp( BootSet[i], OsPartPath ); SpMemFree(OsPartPath); } else { ComponentMatched = !_wcsicmp( BootSet[i], BootVars[i][Component] ); } } } if( (ComponentMatched) #ifdef PRERELEASE // // If we're being asked to delete a boot entry, and this // isn't the *exact* entry (i.e. it's a duplicate) that // also has some private OSLOADOPTIONS, then keep it around. // && !( wcsstr(BootVars[OSLOADOPTIONS][Component], L"/kernel") || wcsstr(BootVars[OSLOADOPTIONS][Component], L"/hal") || wcsstr(BootVars[OSLOADOPTIONS][Component], L"/pae") || wcsstr(BootVars[OSLOADOPTIONS][Component], L"/sos") ) #endif ) { // // Delete all the values in the current component and advance // all the other components one index up // for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { if((i == OSLOADOPTIONS) && OldOsLoadOptions && !(*OldOsLoadOptions)) { // // If we've been passed a pointer to OldOsLoadOptions, // and haven't previously found a pertinent entry, then // save this one // *OldOsLoadOptions = BootVars[i][Component]; } else { SpMemFree(BootVars[i][Component]); } j = Component; do { BootVars[i][j] = BootVars[i][j+1]; j++; } while(BootVars[i][j] != NULL); } } else { Component++; } } return; } VOID SpCleanSysPartOrphan( VOID ) { INT Component, Orphan; BOOLEAN DupFound; PWSTR NormalizedArcPath; if(!CleanSysPartOrphan) { return; } ASSERT( !SpIsEfi() ); // // find the last SystemPartition entry // for(Orphan = 0; BootVars[SYSTEMPARTITION][Orphan]; Orphan++); // // it's position better be > 0, otherwise, just exit // if(Orphan < 2) { return; } else { NormalizedArcPath = SpNormalizeArcPath(BootVars[SYSTEMPARTITION][--Orphan]); } // // Make sure that this component is duplicated somewhere else in the // SystemPartition list. // for(Component = Orphan - 1, DupFound = FALSE; ((Component >= 0) && !DupFound); Component--) { DupFound = !_wcsicmp(NormalizedArcPath, BootVars[SYSTEMPARTITION][Component]); } if(DupFound) { SpMemFree(BootVars[SYSTEMPARTITION][Orphan]); BootVars[SYSTEMPARTITION][Orphan] = NULL; } SpMemFree(NormalizedArcPath); } PWSTR SpArcPathFromBootSet( IN BOOTVAR BootVariable, IN ULONG Component ) /*++ Routine Description: Given the index of a boot set, return the primary (multi) or secondary ("absolute" scsi) ARC path for the specified variable. This takes into account the NT 3.1 case where we had 'tertiary' ARC paths where a relative scsi ordinal was passed in via the /scsiordinal switch. Arguments: BootVariable - supplies the index of the variable we want to return. Component - supplies the index of the boot set to use. Return Value: String representing the primary or secondary ARC path. This string must be freed by the caller with SpMemFree. --*/ { ASSERT( !SpIsEfi() ); if(!SpIsArc()){ PWSTR p = NULL, q = NULL, ReturnedPath = NULL, RestOfString; WCHAR ForceOrdinalSwitch[] = L"/scsiordinal:"; WCHAR ScsiPrefix[] = L"scsi("; WCHAR OrdinalString[11]; ULONG ScsiOrdinal, PrefixLength; // // Check to see if this boot set had the /scsiordinal option switch // if(BootVars[OSLOADOPTIONS][Component]) { wcscpy(TemporaryBuffer, BootVars[OSLOADOPTIONS][Component]); SpStringToLower(TemporaryBuffer); if(p = wcsstr(TemporaryBuffer, ForceOrdinalSwitch)) { p += sizeof(ForceOrdinalSwitch)/sizeof(WCHAR) - 1; if(!(*p)) { p = NULL; } } } if(p) { // // We have found a scsiordinal, so use it // ScsiOrdinal = SpStringToLong(p, &RestOfString, 10); wcscpy(TemporaryBuffer, BootVars[BootVariable][Component]); SpStringToLower(TemporaryBuffer); if(p = wcsstr(TemporaryBuffer, ScsiPrefix)) { p += sizeof(ScsiPrefix)/sizeof(WCHAR) - 1; if(*p) { q = wcschr(p, L')'); } else { p = NULL; } } if(q) { // // build the new secondary ARC path // swprintf(OrdinalString, L"%u", ScsiOrdinal); PrefixLength = (ULONG)(p - TemporaryBuffer); ReturnedPath = SpMemAlloc((PrefixLength + wcslen(OrdinalString) + wcslen(q) + 1) * sizeof(WCHAR) ); wcsncpy(ReturnedPath, TemporaryBuffer, PrefixLength); ReturnedPath[PrefixLength] = L'\0'; wcscat(ReturnedPath, OrdinalString); wcscat(ReturnedPath, q); } } if(!ReturnedPath) { // // We didn't find a scsiordinal, this is a multi-style path, or // there was some problem, so just use the boot variable as-is. // ReturnedPath = SpDupStringW(BootVars[BootVariable][Component]); } return ReturnedPath; } else { // // Nothing to do on ARC machines. // return SpDupStringW(BootVars[BootVariable][Component]); } } #if defined(REMOTE_BOOT) BOOLEAN SpFlushRemoteBootVars( IN PDISK_REGION TargetRegion ) { #if defined(EFI_NVRAM_ENABLED) if (SpIsEfi()) { // // Insert EFI code here. // return FALSE; } else #endif { if (SpIsArc()) { // // Insert ARC code here. // return FALSE; #if defined(_AMD64_) || defined(_X86_) } else { return Spx86FlushRemoteBootVars( TargetRegion, BootVars, Default ); #endif // defined(_AMD64_) || defined(_X86_) } } } #endif // defined(REMOTE_BOOT) BOOLEAN SppSetArcEnvVar( IN BOOTVAR Variable, IN PWSTR *VarComponents, IN BOOLEAN bWriteVar ) /*++ Routine Description: Set the value of the arc environment variable Arguments: VarName - supplies the name of the arc environment variable whose value is to be set. VarComponents - Set of components of the variable value to be set bWriteVar - if TRUE, then write the variable to nvram, otherwise just return FALSE (having put the first component in NewBootVars). Return Value: TRUE if values were written to nvram / FALSE otherwise --*/ { ULONG Length, NBVLen, i; PWSTR Temp; PUCHAR Value; ARC_STATUS ArcStatus; ASSERT( !SpIsEfi() ); if( VarComponents == NULL ) { Temp = SpDupStringW( L"" ); NewBootVars[Variable] = SpDupStringW( L"" ); } else { for( i = 0, Length = 0; VarComponents[i]; i++ ) { Length = Length + (wcslen(VarComponents[i]) + 1) * sizeof(WCHAR); if(i == 0) { NBVLen = Length; // we just want to store the first component } } Temp = SpMemAlloc( Length ); ASSERT( Temp ); wcscpy( Temp, L"" ); NewBootVars[Variable] = SpMemAlloc( NBVLen ); ASSERT( NewBootVars[Variable] ); wcscpy( NewBootVars[Variable], L"" ); for( i = 0; VarComponents[i]; i++ ) { wcscat( Temp, VarComponents[i] ); if( VarComponents[i + 1] ) { wcscat( Temp, L";" ); } if(i == 0) { wcscat( NewBootVars[Variable], VarComponents[i]); } } } if(bWriteVar) { Value = SpToOem( Temp ); ArcStatus = HalSetEnvironmentVariable( NvramVarNames[ Variable ], Value ); SpMemFree( Value ); } else { ArcStatus = ENOMEM; } SpMemFree( Temp ); return ( ArcStatus == ESUCCESS ); } #ifdef _X86_ BOOLEAN SpIsArc( VOID ) /*++ Routine Description: Run time check to determine if this is an Arc system. We attempt to read an Arc variable using the Hal. This will fail for Bios based systems. Arguments: None Return Value: True = This is an Arc system. --*/ { #define BUFFERLENGTH 512 ARC_STATUS ArcStatus = EBADF; UCHAR *buf; if (IsArcChecked) { return IsArcMachine; } IsArcChecked = TRUE; IsArcMachine = FALSE; // // Get the env var into the temp buffer. // buf = SpMemAlloc( BUFFERLENGTH ); if( buf ) { ArcStatus = HalGetEnvironmentVariable( NvramVarNames[ OSLOADER ], BUFFERLENGTH, //sizeof(TemporaryBuffer), buf //(PUCHAR)TemporaryBuffer ); SpMemFree( buf ); } if (ArcStatus == ESUCCESS) { IsArcMachine = TRUE; } return IsArcMachine; } #endif VOID SpFreeBootEntries ( VOID ) /*++ Routine Description: Frees memory used to hold internal-format boot entries and options. Arguments: None. Return Value: None. --*/ { PSP_BOOT_ENTRY bootEntry; // // Free boot options. These will only be allocated on EFI machines. // if (SpBootOptions != NULL) { ASSERT(SpIsEfi()); SpMemFree(SpBootOptions); SpBootOptions = NULL; } // // Free internal-format boot entries. These will be allocated on all // machines. // while (SpBootEntries != NULL) { bootEntry = SpBootEntries; SpBootEntries = bootEntry->Next; // // Space for some fields is allocated with the base structure. // If a fields address indicates that it was allocated with the // base structure, don't try to free it. // #define IS_SEPARATE_ALLOCATION(_p) \ ((bootEntry->_p != NULL) && \ (((PUCHAR)bootEntry->_p < (PUCHAR)bootEntry) || \ ((PUCHAR)bootEntry->_p > (PUCHAR)bootEntry->AllocationEnd))) #define FREE_IF_SEPARATE_ALLOCATION(_p) \ if (IS_SEPARATE_ALLOCATION(_p)) { \ SpMemFree(bootEntry->_p); \ } FREE_IF_SEPARATE_ALLOCATION(FriendlyName); FREE_IF_SEPARATE_ALLOCATION(OsLoadOptions); FREE_IF_SEPARATE_ALLOCATION(LoaderPath); FREE_IF_SEPARATE_ALLOCATION(LoaderPartitionNtName); FREE_IF_SEPARATE_ALLOCATION(LoaderFile); FREE_IF_SEPARATE_ALLOCATION(OsPath); FREE_IF_SEPARATE_ALLOCATION(OsPartitionNtName); FREE_IF_SEPARATE_ALLOCATION(OsDirectory); FREE_IF_SEPARATE_ALLOCATION(Pid20Array); SpMemFree(bootEntry); } ASSERT(SpBootEntries == NULL); return; } // SpFreeBootEntries PCHAR SppGetArcEnvVar( IN BOOTVAR Variable ) /*++ Routine Description: Query the value of an ARC environment variable. A buffer will be returned in all cases -- if the variable does not exist, the buffer will be empty. Arguments: VarName - supplies the name of the arc environment variable whose value is desired. Return Value: Buffer containing value of the environemnt variable. The caller must free this buffer with SpMemFree. --*/ { ARC_STATUS ArcStatus; ASSERT( !SpIsEfi() ); // // Get the env var into the temp buffer. // ArcStatus = HalGetEnvironmentVariable( NvramVarNames[ Variable ], sizeof(TemporaryBuffer), (PCHAR) TemporaryBuffer ); if(ArcStatus != ESUCCESS) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: arc status %u getting env var %s\n",ArcStatus,NvramVarNames[Variable])); // // return empty buffer. // TemporaryBuffer[0] = 0; } return(SpDupString((PCHAR)TemporaryBuffer)); } #ifdef _X86_ // // NEC98 // BOOLEAN SpReInitializeBootVars_Nec98( VOID ) { return SppReInitializeBootVars_Nec98( BootVars, &Default, &Timeout ); } #endif PWSTR SpGetDefaultBootEntry ( OUT UINT *DefaultSignatureOut ) { *DefaultSignatureOut = DefaultSignature; return Default; } VOID SpDetermineUniqueAndPresentBootEntries( VOID ) /*++ Routine Description: This routine goes through the list of NT boot entries and marks all such entries that are both unique and present. Arguments: None. This routine modifies entries in the SpBootEntries list as appropriate. Return Value: None. --*/ { PSP_BOOT_ENTRY BootEntry; PSP_BOOT_ENTRY BootEntry2; // // Initialize // CLEAR_CLIENT_SCREEN(); SpDisplayStatusText(SP_STAT_LOOKING_FOR_WINNT,DEFAULT_STATUS_ATTRIBUTE); // // Go through all the matched boot sets and find out which NTs are // upgradeable/repairable. The criteria here are: // // 1. The system partition should exist and be valid. // 2. The OS load partition should exist. // 3. An NT should exist in . // 4. OsLoadPartition should be a non-FT partition, or it should be a // member 0 of a mirror. // for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) { // // Initialize to false. // BootEntry->Processable = FALSE; // // If this entry has been deleted or is not an NT boot entry, skip it. // if (!IS_BOOT_ENTRY_WINDOWS(BootEntry) || IS_BOOT_ENTRY_DELETED(BootEntry)) { continue; } // // Check if the system and OS partitions are present and valid. // if ((BootEntry->LoaderPartitionDiskRegion == NULL) || (BootEntry->OsPartitionDiskRegion == NULL)) { continue; } if (!BootEntry->LoaderPartitionDiskRegion->PartitionedSpace) { continue; } // // Check whether this directory has been covered before in the // boot entry list. This happens when multiple boot entries point // at the same tree. The comparison is done based on the system // partition region, the OS partition region, and the OS directory. // for ( BootEntry2 = SpBootEntries; BootEntry2 != BootEntry; BootEntry2 = BootEntry2->Next ) { if ((BootEntry->LoaderPartitionDiskRegion == BootEntry2->LoaderPartitionDiskRegion) && (BootEntry->OsPartitionDiskRegion == BootEntry2->OsPartitionDiskRegion) && (_wcsicmp(BootEntry->OsDirectory, BootEntry2->OsDirectory) == 0)) { break; } } if (BootEntry != BootEntry2) { // // This entry duplicates a previous entry. Skip it. // continue; } // // This boot entry is the first one to point to this OS directory. // Check whether an NT installation is actually present there. // if (SpIsNtInDirectory(BootEntry->OsPartitionDiskRegion, BootEntry->OsDirectory) // && !BootEntry->OsPartitionDiskRegion->FtPartition ) { } BootEntry->Processable = TRUE; } CLEAR_CLIENT_SCREEN(); return; } VOID SpRemoveInstallationFromBootList( IN PDISK_REGION SysPartitionRegion, OPTIONAL IN PDISK_REGION NtPartitionRegion, OPTIONAL IN PWSTR SysRoot, OPTIONAL IN PWSTR SystemLoadIdentifier, OPTIONAL IN PWSTR SystemLoadOptions, OPTIONAL IN ENUMARCPATHTYPE ArcPathType, #if defined(REMOTE_BOOT) IN BOOLEAN RemoteBootPath, #endif // defined(REMOTE_BOOT) OUT PWSTR *OldOsLoadOptions OPTIONAL ) { PWSTR BootSet[MAXBOOTVARS]; PWSTR TempSysRoot = NULL; PWSTR FirstBackslash; BOOTVAR i; WCHAR Drive[] = L"?:"; PWSTR tmp2; PSP_BOOT_ENTRY bootEntry; // // Tell the user what we are doing. // CLEAR_CLIENT_SCREEN(); SpDisplayStatusText(SP_STAT_CLEANING_FLEXBOOT,DEFAULT_STATUS_ATTRIBUTE); // // Find all boot entries that match the input specifications, and mark // them for deletion. // for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) { ASSERT(bootEntry->FriendlyName != NULL); if (IS_BOOT_ENTRY_WINDOWS(bootEntry)) { ASSERT(bootEntry->OsLoadOptions != NULL); } if (IS_BOOT_ENTRY_WINDOWS(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry) && ((SysPartitionRegion == NULL) || (bootEntry->LoaderPartitionDiskRegion == SysPartitionRegion)) && ((NtPartitionRegion == NULL) || (bootEntry->OsPartitionDiskRegion == NtPartitionRegion)) && ((SysRoot == NULL) || ((bootEntry->OsDirectory != NULL) && (_wcsicmp(bootEntry->OsDirectory, SysRoot) == 0))) && ((SystemLoadIdentifier == NULL) || (_wcsicmp(bootEntry->FriendlyName, SystemLoadIdentifier) == 0)) && ((SystemLoadOptions == NULL) || (_wcsicmp(bootEntry->OsLoadOptions, SystemLoadOptions) == 0))) { bootEntry->Status |= BE_STATUS_DELETED; if ((OldOsLoadOptions != NULL) && (*OldOsLoadOptions == NULL)) { *OldOsLoadOptions = SpDupStringW(bootEntry->OsLoadOptions); } } } // // If not on an EFI machine, then also delete matching ARC boot sets. // if (!SpIsEfi()) { // // Set up the boot set // for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { BootSet[i] = NULL; } tmp2 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2); if( NtPartitionRegion ) { SpArcNameFromRegion(NtPartitionRegion,tmp2,sizeof(TemporaryBuffer)/2,PartitionOrdinalOnDisk,ArcPathType); BootSet[OSLOADPARTITION] = SpDupStringW(tmp2); } if( SysPartitionRegion ) { SpArcNameFromRegion(SysPartitionRegion,tmp2,sizeof(TemporaryBuffer)/2,PartitionOrdinalOnDisk,ArcPathType); BootSet[SYSTEMPARTITION] = SpDupStringW(tmp2); } BootSet[OSLOADFILENAME] = SysRoot; BootSet[LOADIDENTIFIER] = SystemLoadIdentifier; BootSet[OSLOADOPTIONS] = SystemLoadOptions; #if defined(REMOTE_BOOT) // // If this is a remote boot path, then move anything in OSLOADPARTITION // after (and including) the first backslash over to the OSLOADFILENAME -- // this is the way that boot.ini is parsed when it is read, so it will // allow SpDeleteBootSet to match it properly. // if (RemoteBootPath && NtPartitionRegion && (FirstBackslash = wcschr(BootSet[OSLOADPARTITION], L'\\'))) { wcscpy(tmp2, FirstBackslash); wcscat(tmp2, SysRoot); TempSysRoot = SpDupStringW(tmp2); BootSet[OSLOADFILENAME] = TempSysRoot; *FirstBackslash = L'\0'; // truncate BootSet[OSLOADPARTITION] } #endif // defined(REMOTE_BOOT) // // Delete the boot set // SpDeleteBootSet(BootSet, OldOsLoadOptions); // // To take care of the case where the OSLOADPARTITION is a DOS drive letter // in the boot set, change the OSLOADPARTITION to a drive and retry // deletion // if( BootSet[OSLOADPARTITION] != NULL ) { SpMemFree(BootSet[OSLOADPARTITION]); } if( NtPartitionRegion && (ULONG)(Drive[0] = NtPartitionRegion->DriveLetter) != 0) { BootSet[OSLOADPARTITION] = Drive; SpDeleteBootSet(BootSet, OldOsLoadOptions); } #if defined(_AMD64_) || defined(_X86_) // // If OldOsLoadOptions contains "/scsiordinal:", then remove it // if( ( OldOsLoadOptions != NULL ) && ( *OldOsLoadOptions != NULL ) ) { PWSTR p, q; WCHAR SaveChar; SpStringToLower(*OldOsLoadOptions); p = wcsstr( *OldOsLoadOptions, L"/scsiordinal:" ); if( p != NULL ) { SaveChar = *p; *p = (WCHAR)'\0'; wcscpy(TemporaryBuffer, *OldOsLoadOptions); *p = SaveChar; q = wcschr( p, (WCHAR)' ' ); if( q != NULL ) { wcscat( TemporaryBuffer, q ); } SpMemFree( *OldOsLoadOptions ); *OldOsLoadOptions = SpDupStringW( ( PWSTR )TemporaryBuffer ); } } #endif // defined(_AMD64_) || defined(_X86_) // // Cleanup // if( BootSet[SYSTEMPARTITION] != NULL ) { SpMemFree(BootSet[SYSTEMPARTITION]); } if (TempSysRoot != NULL) { SpMemFree(TempSysRoot); } } return; } VOID SpAddInstallationToBootList( IN PVOID SifHandle, IN PDISK_REGION SystemPartitionRegion, IN PWSTR SystemPartitionDirectory, IN PDISK_REGION NtPartitionRegion, IN PWSTR Sysroot, IN BOOLEAN BaseVideoOption, IN PWSTR OldOsLoadOptions OPTIONAL ) /*++ Routine Description: Construct a boot set for the given installation parameters and add it to the current boot list. Perform modifications to the os load options if necessary. Notes: if this code changes, please ensure that SpAddUserDefinedInstallationToBootList() stays in sync if appropriate. --*/ { PWSTR BootVars[MAXBOOTVARS]; PWSTR SystemPartitionArcName; PWSTR TargetPartitionArcName; PWSTR tmp; PWSTR tmp2; PWSTR SifKeyName; ULONG Signature; BOOLEAN AddBaseVideo = FALSE; WCHAR BaseVideoString[] = L"/basevideo"; WCHAR BaseVideoSosString[] = L"/sos"; BOOLEAN AddSosToBaseVideoString; HEADLESS_RSP_QUERY_INFO Response; WCHAR HeadlessRedirectString[] = L"/redirect"; #if defined(_AMD64_) || defined(_X86_) WCHAR BootFastString[] = L"/fastdetect"; BOOLEAN AddBootFastString = TRUE; #endif // defined(_AMD64_) || defined(_X86_) ENUMARCPATHTYPE ArcPathType = PrimaryArcPath; WCHAR HalString[] = L"/hal="; BOOLEAN OldOsLoadOptionsReplaced; NTSTATUS Status; SIZE_T Length; PWSTR LoadOptions; PWSTR LoadIdentifier; // // Tell the user what we are doing. // CLEAR_CLIENT_SCREEN(); SpDisplayStatusText(SP_STAT_INITING_FLEXBOOT,DEFAULT_STATUS_ATTRIBUTE); OldOsLoadOptionsReplaced = FALSE; if( OldOsLoadOptions ) { PWSTR p; tmp = SpDupStringW( OldOsLoadOptions ); if (tmp) { SpStringToLower(tmp); if( p = wcsstr(tmp, HalString) ) { // found /hal= WCHAR SaveChar; PWSTR q; SaveChar = *p; *p = L'\0'; wcscpy( TemporaryBuffer, OldOsLoadOptions ); q = TemporaryBuffer + wcslen( tmp ); *q = L'\0'; Length = wcslen( tmp ); *p = SaveChar; for( ; *p && (*p != L' '); p++ ) { Length++; } for( ; *p && (*p == L' '); p++ ) { Length++; } if( *p ) { wcscat( TemporaryBuffer, OldOsLoadOptions+Length ); } OldOsLoadOptions = SpDupStringW( TemporaryBuffer ); OldOsLoadOptionsReplaced = TRUE; } SpMemFree( tmp ); } } tmp2 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2); if (!SpIsEfi()) { // // Get an ARC name for the system partition. // if (SystemPartitionRegion != NULL) { SpArcNameFromRegion( SystemPartitionRegion, tmp2, sizeof(TemporaryBuffer)/2, PartitionOrdinalOnDisk, PrimaryArcPath ); SystemPartitionArcName = SpDupStringW(tmp2); } else { SystemPartitionArcName = NULL; } // // Get an ARC name for the target partition. // // // If the partition is on a SCSI disk that has more than 1024 cylinders // and the partition has sectors located on cylinders beyond cylinder // 1024, the get the arc name in the secondary format. See also // spcopy.c!SpCreateNtbootddSys(). // if( !SpIsArc() && #if defined(REMOTE_BOOT) !RemoteBootSetup && #endif // defined(REMOTE_BOOT) #if defined(_AMD64_) || defined(_X86_) !SpUseBIOSToBoot(NtPartitionRegion, NULL, SifHandle) && #endif // defined(_AMD64_) || defined(_X86_) (HardDisks[NtPartitionRegion->DiskNumber].ScsiMiniportShortname[0]) ) { ArcPathType = SecondaryArcPath; } else { ArcPathType = PrimaryArcPath; } SpArcNameFromRegion( NtPartitionRegion, tmp2, sizeof(TemporaryBuffer)/2, PartitionOrdinalOnDisk, ArcPathType ); TargetPartitionArcName = SpDupStringW(tmp2); } // // OSLOADOPTIONS is specified in the setup information file. // tmp = SpGetSectionKeyIndex( WinntSifHandle, SIF_SETUPDATA, SIF_OSLOADOPTIONSVAR, 0 ); if (tmp == NULL) { tmp = SpGetSectionKeyIndex( SifHandle, SIF_SETUPDATA, SIF_OSLOADOPTIONSVAR, 0 ); } // // If OsLoadOptionsVar wasn't specified, then we'll preserve any flags // the user had specified. // if(!tmp && OldOsLoadOptions) { tmp = OldOsLoadOptions; } AddSosToBaseVideoString = BaseVideoOption; AddBaseVideo = BaseVideoOption; if(tmp) { // // make sure we don't already have a /basevideo option, so we // won't add another // wcscpy(TemporaryBuffer, tmp); SpStringToLower(TemporaryBuffer); if(wcsstr(TemporaryBuffer, BaseVideoString)) { // already have /basevideo BaseVideoOption = TRUE; AddBaseVideo = FALSE; } if(wcsstr(TemporaryBuffer, BaseVideoSosString)) { // already have /sos AddSosToBaseVideoString = FALSE; } #if defined(_AMD64_) || defined(_X86_) if(wcsstr(TemporaryBuffer, BootFastString)) { // already have /bootfast AddBootFastString = FALSE; } #endif // defined(_AMD64_) || defined(_X86_) } if(AddBaseVideo || AddSosToBaseVideoString #if defined(_AMD64_) || defined(_X86_) || AddBootFastString #endif // defined(_AMD64_) || defined(_X86_) ) { Length = ((tmp ? wcslen(tmp) + 1 : 0) * sizeof(WCHAR)); if( AddBaseVideo ) { Length += sizeof(BaseVideoString); } if( AddSosToBaseVideoString ) { Length += sizeof( BaseVideoSosString ); } #if defined(_AMD64_) || defined(_X86_) if( AddBootFastString ) { Length += sizeof( BootFastString ); } #endif // defined(_AMD64_) || defined(_X86_) tmp2 = SpMemAlloc(Length); *tmp2 = ( WCHAR )'\0'; if( AddBaseVideo ) { wcscat(tmp2, BaseVideoString); } if( AddSosToBaseVideoString ) { if( *tmp2 != (WCHAR)'\0' ) { wcscat(tmp2, L" "); } wcscat(tmp2, BaseVideoSosString); } #if defined(_AMD64_) || defined(_X86_) if( AddBootFastString ) { if( *tmp2 != (WCHAR)'\0' ) { wcscat(tmp2, L" "); } wcscat(tmp2, BootFastString); } #endif // defined(_AMD64_) || defined(_X86_) if(tmp) { if( *tmp2 != (WCHAR)'\0' ) { wcscat(tmp2, L" "); } wcscat(tmp2, tmp); } LoadOptions = SpDupStringW(tmp2); SpMemFree(tmp2); } else { LoadOptions = SpDupStringW(tmp ? tmp : L""); } // // Add on headless redirect parameter if we are redirecting right now. // Length = sizeof(HEADLESS_RSP_QUERY_INFO); Status = HeadlessDispatch(HeadlessCmdQueryInformation, NULL, 0, &Response, &Length ); if (NT_SUCCESS(Status) && (Response.PortType == HeadlessSerialPort) && Response.Serial.TerminalAttached) { // // Before we go adding a /redirect string, we need to make // sure there's not already one. // if( !wcsstr(LoadOptions, HeadlessRedirectString) ) { Length = (wcslen(LoadOptions) + 1) * sizeof(WCHAR); Length += sizeof(HeadlessRedirectString); tmp2 = SpMemAlloc(Length); ASSERT(tmp2 != NULL); *tmp2 = UNICODE_NULL; wcscat(tmp2, LoadOptions); if (*tmp2 != UNICODE_NULL) { wcscat(tmp2, L" "); } wcscat(tmp2, HeadlessRedirectString); SpMemFree(LoadOptions); LoadOptions = tmp2; } } // // LOADIDENTIFIER is specified in the setup information file. // We need to surround it in double quotes. // Which value to use depends on the BaseVideo flag. // SifKeyName = BaseVideoOption ? SIF_BASEVIDEOLOADID : SIF_LOADIDENTIFIER; tmp = SpGetSectionKeyIndex(SifHandle,SIF_SETUPDATA,SifKeyName,0); if(!tmp) { SpFatalSifError(SifHandle,SIF_SETUPDATA,SifKeyName,0,0); } if(!SpIsArc()) { // // Need quotation marks around the description on amd64/x86. // LoadIdentifier = SpMemAlloc((wcslen(tmp)+3)*sizeof(WCHAR)); LoadIdentifier[0] = L'\"'; wcscpy(LoadIdentifier+1,tmp); wcscat(LoadIdentifier,L"\""); } else { LoadIdentifier = SpDupStringW(tmp); } // // Create a new internal-format boot entry. // tmp = TemporaryBuffer; wcscpy(tmp,SystemPartitionDirectory); SpConcatenatePaths( tmp, #if defined(_AMD64_) || defined(_X86_) SpIsArc() ? L"arcldr.exe" : L"ntldr" #elif defined(_IA64_) L"ia64ldr.efi" #else L"osloader.exe" #endif // defined(_AMD64_) || defined(_X86_) ); tmp = SpDupStringW(tmp); SpCreateBootEntry( BE_STATUS_NEW, SystemPartitionRegion, tmp, NtPartitionRegion, Sysroot, LoadOptions, LoadIdentifier ); SpMemFree(tmp); // // If not on an EFI machine, add a new ARC-style boot set. // if (!SpIsEfi()) { BootVars[OSLOADOPTIONS] = LoadOptions; BootVars[LOADIDENTIFIER] = LoadIdentifier; // // OSLOADER is the system partition path + the system partition directory + // osloader.exe. (ntldr on amd64/x86 machines). // if (SystemPartitionRegion != NULL) { tmp = TemporaryBuffer; wcscpy(tmp,SystemPartitionArcName); SpConcatenatePaths(tmp,SystemPartitionDirectory); SpConcatenatePaths( tmp, #if defined(_AMD64_) || defined(_X86_) (SpIsArc() ? L"arcldr.exe" : L"ntldr") #elif defined(_IA64_) L"ia64ldr.efi" #else L"osloader.exe" #endif // defined(_AMD64_) || defined(_X86_) ); BootVars[OSLOADER] = SpDupStringW(tmp); } else { BootVars[OSLOADER] = SpDupStringW(L""); } // // OSLOADPARTITION is the ARC name of the windows nt partition. // BootVars[OSLOADPARTITION] = TargetPartitionArcName; // // OSLOADFILENAME is sysroot. // BootVars[OSLOADFILENAME] = Sysroot; // // SYSTEMPARTITION is the ARC name of the system partition. // if (SystemPartitionRegion != NULL) { BootVars[SYSTEMPARTITION] = SystemPartitionArcName; } else { BootVars[SYSTEMPARTITION] = L""; } // // get the disk signature // if ((NtPartitionRegion->DiskNumber != 0xffffffff) && HardDisks[NtPartitionRegion->DiskNumber].Signature) { Signature = HardDisks[NtPartitionRegion->DiskNumber].Signature; } else { Signature = 0; } // // Add the boot set and make it the default. // SpAddBootSet(BootVars, TRUE, Signature); SpMemFree(BootVars[OSLOADER]); } // // Free memory allocated. // SpMemFree(LoadOptions); SpMemFree(LoadIdentifier); if (!SpIsEfi()) { if (SystemPartitionArcName != NULL) { SpMemFree(SystemPartitionArcName); } SpMemFree(TargetPartitionArcName); } if( OldOsLoadOptionsReplaced ) { SpMemFree( OldOsLoadOptions ); } } VOID SpCompleteBootListConfig( WCHAR DriveLetter ) { if(!RepairWinnt) { if (!SpIsArc()) { Timeout = 1; } else { Timeout = 5; // // If this is a winnt setup, there will be a boot set to start // text setup ("Install/Upgrade Windows NT"). Remove it here. // if(WinntSetup) { PSP_BOOT_ENTRY bootEntry; for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) { if (IS_BOOT_ENTRY_WINDOWS(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry) && (_wcsicmp(bootEntry->OsLoadOptions, L"WINNT32") == 0)) { bootEntry->Status |= BE_STATUS_DELETED; } } if (!SpIsEfi()) { PWSTR BootVars[MAXBOOTVARS]; RtlZeroMemory(BootVars,sizeof(BootVars)); BootVars[OSLOADOPTIONS] = L"WINNT32"; SpDeleteBootSet(BootVars, NULL); } } } } #ifdef _X86_ if (g_Win9xBackup) { SpRemoveExtraBootIniEntry(); } #endif // // Flush boot vars. // On some machines, NVRAM update takes a few seconds, // so change the message to tell the user we are doing something different. // SpDisplayStatusText(SP_STAT_UPDATING_NVRAM,DEFAULT_STATUS_ATTRIBUTE); if(!SpFlushBootVars()) { if(SpIsEfi() || !SpIsArc()) { // // Fatal on x86 and EFI machines, nonfatal on arc machines. // if (SpIsEfi()) { SpStartScreen(SP_SCRN_CANT_INIT_FLEXBOOT_EFI, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE ); } else { WCHAR DriveLetterString[2]; DriveLetterString[0] = DriveLetter; DriveLetterString[1] = L'\0'; SpStringToUpper(DriveLetterString); SpStartScreen(SP_SCRN_CANT_INIT_FLEXBOOT, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, DriveLetterString, DriveLetterString ); } SpDisplayStatusText(SP_STAT_F3_EQUALS_EXIT,DEFAULT_STATUS_ATTRIBUTE); SpInputDrain(); while(SpInputGetKeypress() != KEY_F3) ; SpDone(0,FALSE,TRUE); } else { BOOL b; b = TRUE; while(b) { ULONG ValidKeys[2] = { ASCI_CR, 0 }; SpStartScreen( SP_SCRN_CANT_UPDATE_BOOTVARS, 3, HEADER_HEIGHT+1, FALSE, FALSE, DEFAULT_ATTRIBUTE, NewBootVars[LOADIDENTIFIER], NewBootVars[OSLOADER], NewBootVars[OSLOADPARTITION], NewBootVars[OSLOADFILENAME], NewBootVars[OSLOADOPTIONS], NewBootVars[SYSTEMPARTITION] ); SpDisplayStatusOptions( DEFAULT_STATUS_ATTRIBUTE, SP_STAT_ENTER_EQUALS_CONTINUE, 0 ); switch(SpWaitValidKey(ValidKeys,NULL,NULL)) { case ASCI_CR: b = FALSE; } } } } if(SpIsArc() && !SpIsEfi()) { // Free all of the boot variable strings BOOTVAR i; for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { SpMemFree(NewBootVars[i]); NewBootVars[i] = NULL; } } } VOID SpPtDeleteBootSetsForRegion( PDISK_REGION Region ) /*++ Routine Description: This routine goes through all the valid boot entries and deletes the ones which point to the specified region. Arguments: Region : The region whose references from boot entries need to be removed Return Value: None. --*/ { PWSTR bootSet[MAXBOOTVARS]; ENUMARCPATHTYPE arcPathType; ULONG i; PSP_BOOT_ENTRY bootEntry; if (Region->PartitionedSpace) { BOOLEAN IsSystemPartition = SPPT_IS_REGION_SYSTEMPARTITION(Region); // // Find all boot entries that have the specified region as the // OS load partition, and mark them for deletion. // for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) { if (IS_BOOT_ENTRY_WINDOWS(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry) && (IsSystemPartition ? (bootEntry->LoaderPartitionDiskRegion == Region) : (bootEntry->OsPartitionDiskRegion == Region))) { bootEntry->Status |= BE_STATUS_DELETED; // // Make the regions also NULL since they might have actually // been deleted // bootEntry->LoaderPartitionDiskRegion = NULL; bootEntry->OsPartitionDiskRegion = NULL; } } // // If we're not on an EFI machine, we also have to munge the ARC // boot variables. // if (!SpIsEfi()) { // // Set up the boot set // for (i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) { bootSet[i] = NULL; } // // We go through this loop twice, once for primary ARC path // and once for secondary. We delete any image which has // the OS load partition on the region we are deleting. // for (i = 0; i < 2; i++) { if (i == 0) { arcPathType = PrimaryArcPath; } else { arcPathType = SecondaryArcPath; } SpArcNameFromRegion( Region, TemporaryBuffer, sizeof(TemporaryBuffer), PartitionOrdinalOnDisk, arcPathType); if ((TemporaryBuffer)[0] != L'\0') { ULONG Index = IsSystemPartition ? SYSTEMPARTITION : OSLOADPARTITION; bootSet[Index] = SpDupStringW(TemporaryBuffer); SpDeleteBootSet(bootSet, NULL); SpMemFree(bootSet[Index]); } } } } } VOID SpGetNtDirectoryList( OUT PWSTR **DirectoryList, OUT PULONG DirectoryCount ) /*++ Routine Description: Determine the list of directories into which NT may be installed. This is independent of the partitions onto which it may be installed. The determination of which directories nt might be in is based on boot.ini in the amd64/x86 cases, or on arc firmware (OSLOADFILENAME var) in the arc case. Arguments: DirectoryList - receives a pointer to an array of strings, each of which contains a possible windows nt tree. DirectoryCount - receives the number of elements in DirectoryList. This may be 0. Return Value: None. The caller must free the array in DirectoryList if DirectoryCount is returned as non-0. --*/ { ULONG count; PSP_BOOT_ENTRY BootEntry; PSP_BOOT_ENTRY BootEntry2; PWSTR *DirList; // // Free any previously allocated list. // if (CurrentNtDirectoryList != NULL) { SpMemFree(CurrentNtDirectoryList); } // // Walk the boot entry list to determine how many unique NT directory names // exist. // count = 0; for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) { if (!IS_BOOT_ENTRY_WINDOWS(BootEntry) || (BootEntry->OsDirectory == NULL)) { continue; } for (BootEntry2 = SpBootEntries; BootEntry2 != BootEntry; BootEntry2 = BootEntry2->Next) { if (!IS_BOOT_ENTRY_WINDOWS(BootEntry2) || (BootEntry2->OsDirectory == NULL)) { continue; } if (_wcsicmp(BootEntry2->OsDirectory, BootEntry->OsDirectory) == 0) { break; } } if (BootEntry2 == BootEntry) { count++; } } // // Allocate space for the list. // DirList = SpMemAlloc(count * sizeof(PWSTR)); ASSERT(DirList != NULL); // // Populate the list. // count = 0; for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) { if (!IS_BOOT_ENTRY_WINDOWS(BootEntry) || (BootEntry->OsDirectory == NULL)) { continue; } for (BootEntry2 = SpBootEntries; BootEntry2 != BootEntry; BootEntry2 = BootEntry2->Next) { if (!IS_BOOT_ENTRY_WINDOWS(BootEntry2) || (BootEntry2->OsDirectory == NULL)) { continue; } if (_wcsicmp(BootEntry2->OsDirectory, BootEntry->OsDirectory) == 0) { break; } } if (BootEntry2 == BootEntry) { DirList[count++] = BootEntry->OsDirectory; } } // // Return a pointer to the list that we allocated. // CurrentNtDirectoryList = DirList; *DirectoryList = DirList; *DirectoryCount = count; return; } BOOLEAN SpConvertArcBootEntries ( IN ULONG MaxComponents ) /*++ Routine Description: Convert ARC boot entries (read from boot.ini or from ARC NVRAM) into our internal format. Arguments: MaxComponents - maximum number of elements in any NVRAM variable. Return Value: BOOLEAN - FALSE if any unexpected errors occurred. --*/ { LONG i; PDISK_REGION systemPartitionRegion; PDISK_REGION ntPartitionRegion; PWSTR loaderName; for (i = (LONG)MaxComponents - 1; i >= 0; i--) { // // Skip this boot set if it is not complete. // if ((BootVars[SYSTEMPARTITION][i] != NULL) && (BootVars[OSLOADPARTITION][i] != NULL) && (BootVars[OSLOADER][i] != NULL) && (BootVars[OSLOADFILENAME][i] != NULL) && (BootVars[OSLOADOPTIONS][i] != NULL) && (BootVars[LOADIDENTIFIER][i] != NULL)) { // // Translate the SYSTEMPARTITION and OSLOADPARTITION ARC names // into disk region pointers. Get the loader file name from // OSLOADER, which contains an ARC name (same as OSLOADPARTITION) // and a file name. // systemPartitionRegion = SpRegionFromArcName( BootVars[SYSTEMPARTITION][i], PartitionOrdinalCurrent, NULL ); ntPartitionRegion = SpRegionFromArcName( BootVars[OSLOADPARTITION][i], PartitionOrdinalCurrent, NULL ); // // Take care of duplicate arc names for the same disk by searching // and validating the NT directory is present on the partition // while (ntPartitionRegion && !SpIsNtInDirectory(ntPartitionRegion, BootVars[OSLOADFILENAME][i])) { // // Continue to look for same name region from the current // searched region // ntPartitionRegion = SpRegionFromArcName( BootVars[OSLOADPARTITION][i], PartitionOrdinalCurrent, ntPartitionRegion ); } loaderName = wcschr(BootVars[OSLOADER][i], L'\\'); // // If all of the above worked, then add an internal-format boot // entry for this ARC boot set. // if ((systemPartitionRegion != NULL) && (ntPartitionRegion != NULL) && (loaderName != NULL)) { SpCreateBootEntry( BE_STATUS_FROM_BOOT_INI, systemPartitionRegion, loaderName, ntPartitionRegion, BootVars[OSLOADFILENAME][i], BootVars[OSLOADOPTIONS][i], BootVars[LOADIDENTIFIER][i] ); } } } return TRUE; } VOID SpUpdateRegionForBootEntries( VOID ) /*++ Routine Description: Update the region pointers for all the given boot entries. NOTE : The region pointers change with every commit so we can't cache them across commits. Arguments: None. Return Value: None. --*/ { PSP_BOOT_ENTRY BootEntry; // // Walk through each boot entry and update its system partition region // pointer and NT partition region pointer. // for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) { if (!IS_BOOT_ENTRY_DELETED(BootEntry)) { if (BootEntry->LoaderPartitionNtName != NULL) { BootEntry->LoaderPartitionDiskRegion = SpRegionFromNtName(BootEntry->LoaderPartitionNtName, PartitionOrdinalCurrent); } else { BootEntry->LoaderPartitionDiskRegion = NULL; } if (BootEntry->OsPartitionNtName != NULL) { BootEntry->OsPartitionDiskRegion = SpRegionFromNtName(BootEntry->OsPartitionNtName, PartitionOrdinalCurrent); } else { BootEntry->OsPartitionDiskRegion = NULL; } } } return; } // SpUpdateRegionForBootEntries VOID SpCreateBootEntry ( IN ULONG_PTR Status, IN PDISK_REGION BootFileRegion, IN PWSTR BootFilePath, IN PDISK_REGION OsLoadRegion, IN PWSTR OsLoadPath, IN PWSTR OsLoadOptions, IN PWSTR FriendlyName ) /*++ Routine Description: Create an internal-format boot entry. Arguments: Status - The status to be assigned to the boot entry. This should be either zero (for an entry already in NVRAM) or BE_STATUS_NEW for a new boot entry. Entries marked BE_STATUS_NEW are written to NVRAM at the end of textmode setup. BootFileRegion - The disk region on which the OS loader resides. BootFilePath - The volume-relative path to the OS loader. Must start with a backslash. OsLoadRegion - The disk region on which the OS resides. OsLoadPath - The volume-relative path to the OS root directory (\WINDOWS). Must start with a backslash. OsLoadOptions - Boot options for the OS. Can be an empty string. FriendlyName - The user-visible name for the boot entry. (This is ARC's LOADIDENTIFIER.) Return Value: None. Only memory allocation failures are possible, and these are handled out-of-band. --*/ { NTSTATUS status; ULONG requiredLength; ULONG osOptionsOffset; ULONG osLoadOptionsLength; ULONG osLoadPathOffset; ULONG osLoadPathLength; ULONG osOptionsLength; ULONG friendlyNameOffset; ULONG friendlyNameLength; ULONG bootPathOffset; ULONG bootPathLength; PSP_BOOT_ENTRY myBootEntry; PSP_BOOT_ENTRY previousBootEntry; PSP_BOOT_ENTRY nextBootEntry; PBOOT_ENTRY ntBootEntry; PWINDOWS_OS_OPTIONS osOptions; PFILE_PATH osLoadPath; PWSTR friendlyName; PFILE_PATH bootPath; PWSTR p; PWSTR bootFileDevice; PWSTR osLoadDevice; // // Get NT names for the input disk regions. // bootFileDevice = SpMemAlloc(512); SpNtNameFromRegion(BootFileRegion, bootFileDevice, 512, PartitionOrdinalCurrent); osLoadDevice = SpMemAlloc(512); SpNtNameFromRegion(OsLoadRegion, osLoadDevice, 512, PartitionOrdinalCurrent); // // Calculate how long the internal boot entry needs to be. This includes // our internal structure, plus the BOOT_ENTRY structure that the NT APIs // use. // // Our structure: // requiredLength = FIELD_OFFSET(SP_BOOT_ENTRY, NtBootEntry); // // Base part of NT structure: // requiredLength += FIELD_OFFSET(BOOT_ENTRY, OsOptions); // // Save offset to BOOT_ENTRY.OsOptions. Add in base part of // WINDOWS_OS_OPTIONS. Calculate length in bytes of OsLoadOptions // and add that in. // osOptionsOffset = requiredLength; requiredLength += FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions); osLoadOptionsLength = (wcslen(OsLoadOptions) + 1) * sizeof(WCHAR); requiredLength += osLoadOptionsLength; // // Round up to a ULONG boundary for the OS FILE_PATH in the // WINDOWS_OS_OPTIONS. Save offset to OS FILE_PATH. Add in base part // of FILE_PATH. Add in length in bytes of OS device NT name and OS // directory. Calculate total length of OS FILE_PATH and of // WINDOWS_OS_OPTIONS. // requiredLength = ALIGN_UP(requiredLength, ULONG); osLoadPathOffset = requiredLength; requiredLength += FIELD_OFFSET(FILE_PATH, FilePath); requiredLength += (wcslen(osLoadDevice) + 1 + wcslen(OsLoadPath) + 1) * sizeof(WCHAR); osLoadPathLength = requiredLength - osLoadPathOffset; osOptionsLength = requiredLength - osOptionsOffset; // // Round up to a ULONG boundary for the friendly name in the BOOT_ENTRY. // Save offset to friendly name. Calculate length in bytes of friendly name // and add that in. // requiredLength = ALIGN_UP(requiredLength, ULONG); friendlyNameOffset = requiredLength; friendlyNameLength = (wcslen(FriendlyName) + 1) * sizeof(WCHAR); requiredLength += friendlyNameLength; // // Round up to a ULONG boundary for the boot FILE_PATH in the BOOT_ENTRY. // Save offset to boot FILE_PATH. Add in base part of FILE_PATH. Add in // length in bytes of boot device NT name and boot file. Calculate total // length of boot FILE_PATH. // requiredLength = ALIGN_UP(requiredLength, ULONG); bootPathOffset = requiredLength; requiredLength += FIELD_OFFSET(FILE_PATH, FilePath); requiredLength += (wcslen(bootFileDevice) + 1 + wcslen(BootFilePath) + 1) * sizeof(WCHAR); bootPathLength = requiredLength - bootPathOffset; // // Allocate memory for the boot entry. // myBootEntry = SpMemAlloc(requiredLength); ASSERT(myBootEntry != NULL); RtlZeroMemory(myBootEntry, requiredLength); // // Calculate addresses of various substructures using the saved offsets. // ntBootEntry = &myBootEntry->NtBootEntry; osOptions = (PWINDOWS_OS_OPTIONS)ntBootEntry->OsOptions; osLoadPath = (PFILE_PATH)((PUCHAR)myBootEntry + osLoadPathOffset); friendlyName = (PWSTR)((PUCHAR)myBootEntry + friendlyNameOffset); bootPath = (PFILE_PATH)((PUCHAR)myBootEntry + bootPathOffset); // // Fill in the internal-format structure. // myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + requiredLength; myBootEntry->Status = Status | BE_STATUS_ORDERED; myBootEntry->FriendlyName = friendlyName; myBootEntry->FriendlyNameLength = friendlyNameLength; myBootEntry->OsLoadOptions = osOptions->OsLoadOptions; myBootEntry->OsLoadOptionsLength = osLoadOptionsLength; myBootEntry->LoaderPath = bootPath; myBootEntry->OsPath = osLoadPath; myBootEntry->LoaderPartitionDiskRegion = BootFileRegion; myBootEntry->OsPartitionDiskRegion = OsLoadRegion; // // Fill in the base part of the NT boot entry. // ntBootEntry->Version = BOOT_ENTRY_VERSION; ntBootEntry->Length = requiredLength - FIELD_OFFSET(SP_BOOT_ENTRY, NtBootEntry); ntBootEntry->Attributes = BOOT_ENTRY_ATTRIBUTE_ACTIVE | BOOT_ENTRY_ATTRIBUTE_WINDOWS; ntBootEntry->FriendlyNameOffset = (ULONG)((PUCHAR)friendlyName - (PUCHAR)ntBootEntry); ntBootEntry->BootFilePathOffset = (ULONG)((PUCHAR)bootPath - (PUCHAR)ntBootEntry); ntBootEntry->OsOptionsLength = osOptionsLength; // // Fill in the base part of the WINDOWS_OS_OPTIONS, including the // OsLoadOptions. // strcpy(osOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE); osOptions->Version = WINDOWS_OS_OPTIONS_VERSION; osOptions->Length = osOptionsLength; osOptions->OsLoadPathOffset = (ULONG)((PUCHAR)osLoadPath - (PUCHAR)osOptions); wcscpy(osOptions->OsLoadOptions, OsLoadOptions); // // Fill in the OS FILE_PATH. // osLoadPath->Version = FILE_PATH_VERSION; osLoadPath->Length = osLoadPathLength; osLoadPath->Type = FILE_PATH_TYPE_NT; p = (PWSTR)osLoadPath->FilePath; myBootEntry->OsPartitionNtName = p; wcscpy(p, osLoadDevice); p += wcslen(p) + 1; myBootEntry->OsDirectory = p; wcscpy(p, OsLoadPath); // // Copy the friendly name. // wcscpy(friendlyName, FriendlyName); // // Fill in the boot FILE_PATH. // bootPath->Version = FILE_PATH_VERSION; bootPath->Length = bootPathLength; bootPath->Type = FILE_PATH_TYPE_NT; p = (PWSTR)bootPath->FilePath; myBootEntry->LoaderPartitionNtName = p; wcscpy(p, bootFileDevice); p += wcslen(p) + 1; myBootEntry->LoaderFile = p; wcscpy(p, BootFilePath); // // Link the new boot entry into the list, after any removable media // entries that are at the front of the list. // previousBootEntry = NULL; nextBootEntry = SpBootEntries; while ((nextBootEntry != NULL) && IS_BOOT_ENTRY_REMOVABLE_MEDIA(nextBootEntry)) { previousBootEntry = nextBootEntry; nextBootEntry = nextBootEntry->Next; } myBootEntry->Next = nextBootEntry; if (previousBootEntry == NULL) { SpBootEntries = myBootEntry; } else { previousBootEntry->Next = myBootEntry; } // // Free local memory. // SpMemFree(bootFileDevice); SpMemFree(osLoadDevice); return; } // SpCreateBootEntry #if defined(EFI_NVRAM_ENABLED) BOOLEAN SpBuildHarddiskNameTranslations ( VOID ) /*++ Routine Description: Build a list of the translations of all \Device\HarddiskN\PartitionM symbolic links to \Device\HarddiskVolumeN device names. Arguments: None. Return Value: BOOLEAN - FALSE if an unexpected error occurred. --*/ { NTSTATUS status; OBJECT_ATTRIBUTES obja; UNICODE_STRING unicodeString; HANDLE deviceHandle; HANDLE diskHandle; HANDLE linkHandle; PUCHAR buffer1; PUCHAR buffer2; BOOLEAN restartScan; ULONG context1; ULONG context2; POBJECT_DIRECTORY_INFORMATION dirInfo1; POBJECT_DIRECTORY_INFORMATION dirInfo2; PWSTR linkName; PWSTR p; PHARDDISK_NAME_TRANSLATION translation; // // Allocate buffers for directory queries. // #define BUFFER_SIZE 2048 buffer1 = SpMemAlloc(BUFFER_SIZE); buffer2 = SpMemAlloc(BUFFER_SIZE); // // Open the \Device directory. // INIT_OBJA(&obja, &unicodeString, L"\\device"); status = ZwOpenDirectoryObject(&deviceHandle, DIRECTORY_ALL_ACCESS, &obja); if (!NT_SUCCESS(status)) { ASSERT(FALSE); goto cleanup; } restartScan = TRUE; context1 = 0; do { // // Search the \Device directory for HarddiskN subdirectories. // status = ZwQueryDirectoryObject( deviceHandle, buffer1, BUFFER_SIZE, TRUE, restartScan, &context1, NULL ); restartScan = FALSE; if (!NT_SUCCESS(status)) { if (status != STATUS_NO_MORE_ENTRIES) { ASSERT(FALSE); goto cleanup; } status = STATUS_SUCCESS; break; } // // We only care about directories with HarddiskN names. // dirInfo1 = (POBJECT_DIRECTORY_INFORMATION)buffer1; if ((dirInfo1->Name.Length < sizeof(L"harddisk")) || (dirInfo1->TypeName.Length < (sizeof(L"Directory") - sizeof(WCHAR))) || (_wcsnicmp(dirInfo1->TypeName.Buffer,L"Directory",wcslen(L"Directory")) != 0)) { continue; } SpStringToLower(dirInfo1->Name.Buffer); if (wcsncmp(dirInfo1->Name.Buffer, L"harddisk", wcslen(L"harddisk")) != 0) { continue; } p = dirInfo1->Name.Buffer + wcslen(L"Harddisk"); if (*p == 0) { continue; } do { if ((*p < L'0') || (*p > L'9')) { break; } p++; } while (*p != 0); if (*p != 0) { continue; } // // We have the name of a \Device\HarddiskN directory. Open it and look // for PartitionM names. // InitializeObjectAttributes( &obja, &dirInfo1->Name, OBJ_CASE_INSENSITIVE, deviceHandle, NULL ); status = ZwOpenDirectoryObject(&diskHandle, DIRECTORY_ALL_ACCESS, &obja); if (!NT_SUCCESS(status)) { goto cleanup; } restartScan = TRUE; context2 = 0; do { // // Search the \Device\HarddiskN directory for PartitionM symbolic // links. // status = ZwQueryDirectoryObject( diskHandle, buffer2, BUFFER_SIZE, TRUE, restartScan, &context2, NULL ); restartScan = FALSE; if (!NT_SUCCESS(status)) { if (status != STATUS_NO_MORE_ENTRIES) { ASSERT(FALSE); goto cleanup; } status = STATUS_SUCCESS; break; } // // We only care about symbolic links with PartitionN names. // dirInfo2 = (POBJECT_DIRECTORY_INFORMATION)buffer2; if ((dirInfo2->Name.Length < sizeof(L"partition")) || (dirInfo2->TypeName.Length < (sizeof(L"SymbolicLink") - sizeof(WCHAR))) || (_wcsnicmp(dirInfo2->TypeName.Buffer,L"SymbolicLink",wcslen(L"SymbolicLink")) != 0)) { continue; } SpStringToLower(dirInfo2->Name.Buffer); if (wcsncmp(dirInfo2->Name.Buffer, L"partition", wcslen(L"partition")) != 0) { continue; } p = dirInfo2->Name.Buffer + wcslen(L"partition"); if ((*p == 0) || (*p == L'0')) { // skip partition0 continue; } do { if ((*p < L'0') || (*p > L'9')) { break; } p++; } while (*p != 0); if (*p != 0) { continue; } // // Open the \Device\HarddiskN\PartitionM symbolic link. // linkName = SpMemAlloc(sizeof(L"\\device") + dirInfo1->Name.Length + dirInfo2->Name.Length + sizeof(WCHAR)); wcscpy(linkName, L"\\device"); SpConcatenatePaths(linkName, dirInfo1->Name.Buffer); SpConcatenatePaths(linkName, dirInfo2->Name.Buffer); // // Query the link to get the link target. // status = SpQueryCanonicalName(linkName, -1, TemporaryBuffer, sizeof(TemporaryBuffer)); if (!NT_SUCCESS(status)) { ASSERT(FALSE); SpMemFree(linkName); goto cleanup; } // // Create a translation entry. // translation = SpMemAlloc(sizeof(HARDDISK_NAME_TRANSLATION)); translation->Next = SpHarddiskNameTranslations; SpHarddiskNameTranslations = translation; translation->PartitionName = linkName; translation->VolumeName = SpDupStringW(TemporaryBuffer); } while (TRUE); ZwClose(diskHandle); } while (TRUE); ASSERT(status == STATUS_SUCCESS); cleanup: SpMemFree(buffer1); SpMemFree(buffer2); return (NT_SUCCESS(status) ? TRUE : FALSE); } // SpBuildHarddiskNameTranslations NTSTATUS SpGetBootEntryFilePath( IN ULONG Id, IN PWSTR LoaderPartitionNtName, IN PWSTR LoaderFile, OUT PWSTR* FilePath ) /*++ Routine Description: Construct a filepath including the loaderpartition name, the directory path to the OS loader and a filename for the boot entry specified. Arguments: Id the boot entry id LoaderPartitionNtName pointer to the string representing the disk partition LoaderFile pointer to the string representing the path to the EFI OS loader FilePath upon completion, this points to the completed filepath to the boot entry file Return Value: NTSTATUS --*/ { NTSTATUS status; WCHAR* p; ULONG FilePathSize; WCHAR idString[9]; // // use the EFI variable name as the filename // swprintf( idString, L"Boot%04x", Id); // // determine the size of the final filepath // // Note: FilePathSize should be a little bigger than actually needed // since we are including the full LoadFile string. Also, the '\' // characters may be extra. // FilePathSize = (wcslen(LoaderPartitionNtName) * sizeof(WCHAR)) + // partition sizeof(WCHAR) + // '\' (wcslen(LoaderFile) * sizeof(WCHAR)) + // path sizeof(WCHAR) + // '\' (wcslen(idString) * sizeof(WCHAR)) + // new filename sizeof(WCHAR); // null term. ASSERT(FilePathSize > 0); if (FilePathSize <= 0) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: invalid loader partition name and/or loader path\n")); return STATUS_INVALID_PARAMETER; } *FilePath = SpMemAlloc(FilePathSize); if (!*FilePath) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to allocate memory for FilePath\n")); return STATUS_NO_MEMORY; } wcscpy(*FilePath, LoaderPartitionNtName); SpConcatenatePaths(*FilePath, LoaderFile); // remove the os loader filename from the path p = wcsrchr(*FilePath, L'\\'); if (p != NULL) { p++; } else { // we could get here, but it would be wierd. p = *FilePath; wcscat(p, L"\\"); } // // insert the filename // wcscpy(p, idString); ASSERT((wcslen(*FilePath) + 1) * sizeof(WCHAR) <= FilePathSize); return STATUS_SUCCESS; } NTSTATUS SpGetAndWriteBootEntry( IN ULONG Id, IN PWSTR BootEntryPath ) /*++ Routine Description: Get the boot entry from NVRAM for the given boot entry Id. Construct a filename of the form BootXXXX, where XXXX = id. Put the file in the same directory as the EFI OS loader. The directory is determined from the LoaderFile string. Arguments: bootEntry pointer to a SP_BOOT_ENTRY structure of the entry to write BootEntryPath pinter to the ARC/NT style reference to the boot entry filename Return Value: NTSTATUS --*/ { NTSTATUS status; WCHAR idString[9]; HANDLE hfile; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK iostatus; UCHAR* bootVar; ULONG bootVarSize; UNICODE_STRING uFilePath; UINT64 BootNumber; UINT64 BootSize; GUID EfiBootVariablesGuid = EFI_GLOBAL_VARIABLE; hfile = NULL; // // Retrieve the NVRAM entry for the Id specified // swprintf( idString, L"Boot%04x", Id); bootVarSize = 0; status = HalGetEnvironmentVariableEx(idString, &EfiBootVariablesGuid, NULL, &bootVarSize, NULL); if (status != STATUS_BUFFER_TOO_SMALL) { ASSERT(FALSE); KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to get size for boot entry buffer.\n")); goto Done; } else { bootVar = SpMemAlloc(bootVarSize); if (!bootVar) { status = STATUS_NO_MEMORY; KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to allocate boot entry buffer.\n")); goto Done; } status = HalGetEnvironmentVariableEx(idString, &EfiBootVariablesGuid, bootVar, &bootVarSize, NULL); if (status != STATUS_SUCCESS) { ASSERT(FALSE); KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to get boot entry.\n")); goto Done; } } // // open the file // INIT_OBJA(&oa, &uFilePath, BootEntryPath); status = ZwCreateFile(&hfile, GENERIC_WRITE, &oa, &iostatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); if ( ! NT_SUCCESS(status) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to create boot entry recovery file.\n")); goto Done; } // // Write the bits to disk using the format required // by base/efiutil/efinvram/savrstor.c // // [BootNumber][BootSize][BootEntry (of BootSize)] // // // build the header info for the boot entry block // // [header] include the boot id BootNumber = Id; status = ZwWriteFile( hfile, NULL, NULL, NULL, &iostatus, &BootNumber, sizeof(BootNumber), NULL, NULL ); if ( ! NT_SUCCESS(status) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed writing boot number to boot entry recovery file.\n")); goto Done; } // [header] include the boot size BootSize = bootVarSize; status = ZwWriteFile( hfile, NULL, NULL, NULL, &iostatus, &BootSize, sizeof(BootSize), NULL, NULL ); if ( ! NT_SUCCESS(status) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed writing boot entry size to boot entry recovery file.\n")); goto Done; } // boot entry bits status = ZwWriteFile( hfile, NULL, NULL, NULL, &iostatus, bootVar, bootVarSize, NULL, NULL ); if ( ! NT_SUCCESS(status) ) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed writing boot entry to boot entry recovery file.\n")); goto Done; } Done: // // We are done // if (bootVar) { SpMemFree(bootVar); } if (hfile) { ZwClose( hfile ); } return status; } BOOLEAN SpFlushEfiBootEntries ( VOID ) /*++ Routine Description: Write boot entry changes back to NVRAM. Arguments: None. Return Value: BOOLEAN - FALSE if an unexpected error occurred. --*/ { PSP_BOOT_ENTRY bootEntry; ULONG count; PULONG order; ULONG i; NTSTATUS status; PWSTR BootEntryFilePath; ASSERT(SpIsEfi()); // // Walk the list of boot entries, looking for entries that have been // deleted. Delete these entries from NVRAM. Do not delete entries that // are both new AND deleted; these are entries that have never been // written to NVRAM. // for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) { if (IS_BOOT_ENTRY_DELETED(bootEntry) && !IS_BOOT_ENTRY_NEW(bootEntry)) { ASSERT(IS_BOOT_ENTRY_WINDOWS(bootEntry)); // // Delete this boot entry. // status = ZwDeleteBootEntry(bootEntry->NtBootEntry.Id); if (!NT_SUCCESS(status)) { return FALSE; } } } // // Walk the list of boot entries, looking for entries that have are new. // Add these entries to NVRAM. Do not write entries that are both new AND // deleted. // for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) { if (IS_BOOT_ENTRY_NEW(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry)) { ASSERT(IS_BOOT_ENTRY_WINDOWS(bootEntry)); // // Add this boot entry. // status = ZwAddBootEntry(&bootEntry->NtBootEntry, &bootEntry->NtBootEntry.Id); if (!NT_SUCCESS(status)) { return FALSE; } // // get the location we are going to store a copy of the NVRAM boot entry // BootEntryFilePath = NULL; status = SpGetBootEntryFilePath(bootEntry->NtBootEntry.Id, bootEntry->LoaderPartitionNtName, bootEntry->LoaderFile, &BootEntryFilePath ); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed getting boot entry filepath.\n")); } else { ASSERT(BootEntryFilePath); // // Fetch the bits from the newly created NVRAM entry and // write them as a file in the the EFI load path // status = SpGetAndWriteBootEntry(bootEntry->NtBootEntry.Id, BootEntryFilePath ); if (!NT_SUCCESS(status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed boot entry recovery file.\n")); } // // We are done with the boot entry filepath // SpMemFree(BootEntryFilePath); } // // Remember the ID of the new boot entry as the entry to be booted // immediately on the next boot. // SpBootOptions->NextBootEntryId = bootEntry->NtBootEntry.Id; } } // // Build the new boot order list. Insert all boot entries with // BE_STATUS_ORDERED into the list. (Don't insert deleted entries.) // count = 0; bootEntry = SpBootEntries; while (bootEntry != NULL) { if (IS_BOOT_ENTRY_ORDERED(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry)) { count++; } bootEntry = bootEntry->Next; } order = SpMemAlloc(count * sizeof(ULONG)); count = 0; bootEntry = SpBootEntries; while (bootEntry != NULL) { if (IS_BOOT_ENTRY_ORDERED(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry)) { order[count++] = bootEntry->NtBootEntry.Id; } bootEntry = bootEntry->Next; } // // Write the new boot entry order list to NVRAM. // status = ZwSetBootEntryOrder(order, count); SpMemFree(order); if (!NT_SUCCESS(status)) { return FALSE; } // // Write the new timeout value to NVRAM. // // Set the boot entry we added to be booted automatically on // the next boot, without waiting for a timeout at the boot menu. // // NB: SpCreateBootEntry() sets SpBootOptions->NextBootEntryId. // SpBootOptions->Timeout = Timeout; status = ZwSetBootOptions( SpBootOptions, BOOT_OPTIONS_FIELD_TIMEOUT | BOOT_OPTIONS_FIELD_NEXT_BOOT_ENTRY_ID ); if (!NT_SUCCESS(status)) { return FALSE; } return TRUE; } // SpFlushEfiBootEntries BOOLEAN SpReadAndConvertEfiBootEntries ( VOID ) /*++ Routine Description: Read boot entries from EFI NVRAM and convert them into our internal format. Arguments: None. Return Value: BOOLEAN - FALSE if an unexpected error occurred. --*/ { NTSTATUS status; ULONG length; PBOOT_ENTRY_LIST bootEntries; PBOOT_ENTRY_LIST bootEntryList; PBOOT_ENTRY bootEntry; PBOOT_ENTRY bootEntryCopy; PSP_BOOT_ENTRY myBootEntry; PSP_BOOT_ENTRY previousEntry; PWINDOWS_OS_OPTIONS osOptions; LONG i; PULONG order; ULONG count; // // SpStartSetup() does not expect our caller, SpInitBootVars(), to fail. // So textmode is going to continue even if we have failures here. // Therefore we need to leave here in a consistent state. That means // that we MUST allocate a buffer for SpBootOptions, even if we can't // get the real information from the kernel. // // // Get the global system boot options. // length = 0; status = ZwQueryBootOptions(NULL, &length); if (status != STATUS_BUFFER_TOO_SMALL) { ASSERT(FALSE); if (status == STATUS_SUCCESS) { status = STATUS_UNSUCCESSFUL; } } else { SpBootOptions = SpMemAlloc(length); status = ZwQueryBootOptions(SpBootOptions, &length); if (status != STATUS_SUCCESS) { ASSERT(FALSE); } } if (status != STATUS_SUCCESS) { // // An unexpected error occurred reading the boot options. Create // a fake boot options structure. // if (SpBootOptions != NULL) { SpMemFree(SpBootOptions); } length = FIELD_OFFSET(BOOT_OPTIONS,HeadlessRedirection) + sizeof(WCHAR); SpBootOptions = SpMemAlloc(length); RtlZeroMemory(SpBootOptions, length); SpBootOptions->Version = BOOT_OPTIONS_VERSION; SpBootOptions->Length = length; } // // Get the system boot order list. // count = 0; status = ZwQueryBootEntryOrder(NULL, &count); if (status != STATUS_BUFFER_TOO_SMALL) { if (status == STATUS_SUCCESS) { // // There are no entries in the boot order list. Strange but // possible. // count = 0; } else { // // An unexpected error occurred. Just pretend that the boot // entry order list is empty. // ASSERT(FALSE); count = 0; } } if (count != 0) { order = SpMemAlloc(count * sizeof(ULONG)); status = ZwQueryBootEntryOrder(order, &count); if (status != STATUS_SUCCESS) { // // An unexpected error occurred. Just pretend that the boot // entry order list is empty. // ASSERT(FALSE); count = 0; } } // // Get all existing boot entries. // length = 0; status = ZwEnumerateBootEntries(NULL, &length); if (status != STATUS_BUFFER_TOO_SMALL) { if (status == STATUS_SUCCESS) { // // Somehow there are no boot entries in NVRAM. Handle this // by just creating an empty list. // length = 0; } else { // // An unexpected error occurred. Just pretend that no boot // entries exist. // ASSERT(FALSE); length = 0; } } if (length == 0) { ASSERT(SpBootEntries == NULL); } else { bootEntries = SpMemAlloc(length); status = ZwEnumerateBootEntries(bootEntries, &length); if (status != STATUS_SUCCESS) { ASSERT(FALSE); return FALSE; } // // Convert the boot entries into our internal representation. // bootEntryList = bootEntries; previousEntry = NULL; while (TRUE) { bootEntry = &bootEntryList->BootEntry; // // Calculate the length of our internal structure. This includes // the base part of SP_BOOT_ENTRY plus the NT BOOT_ENTRY. // length = FIELD_OFFSET(SP_BOOT_ENTRY, NtBootEntry) + bootEntry->Length; myBootEntry = SpMemAlloc(length); ASSERT(myBootEntry != NULL); RtlZeroMemory(myBootEntry, length); // // Copy the NT BOOT_ENTRY into the allocated buffer. // bootEntryCopy = &myBootEntry->NtBootEntry; memcpy(bootEntryCopy, bootEntry, bootEntry->Length); // // Fill in the base part of the structure. // myBootEntry->Next = NULL; myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + length - 1; myBootEntry->FriendlyName = ADD_OFFSET(bootEntryCopy, FriendlyNameOffset); myBootEntry->FriendlyNameLength = (wcslen(myBootEntry->FriendlyName) + 1) * sizeof(WCHAR); myBootEntry->LoaderPath = ADD_OFFSET(bootEntryCopy, BootFilePathOffset); // // If this is an NT boot entry, translate the file paths. // osOptions = (PWINDOWS_OS_OPTIONS)bootEntryCopy->OsOptions; if (IS_BOOT_ENTRY_WINDOWS(myBootEntry)) { PSP_BOOT_ENTRY bootEntry2; myBootEntry->OsLoadOptions = osOptions->OsLoadOptions; myBootEntry->OsLoadOptionsLength = (wcslen(myBootEntry->OsLoadOptions) + 1) * sizeof(WCHAR); myBootEntry->OsPath = ADD_OFFSET(osOptions, OsLoadPathOffset); // // Translate the OS FILE_PATH and the boot FILE_PATH. Note that // the translation can fail when the target device is not present. // SpTranslateFilePathToRegion( myBootEntry->OsPath, &myBootEntry->OsPartitionDiskRegion, &myBootEntry->OsPartitionNtName, &myBootEntry->OsDirectory ); SpTranslateFilePathToRegion( myBootEntry->LoaderPath, &myBootEntry->LoaderPartitionDiskRegion, &myBootEntry->LoaderPartitionNtName, &myBootEntry->LoaderFile ); } // // Link the new entry into the list. // if (previousEntry != NULL) { previousEntry->Next = myBootEntry; } else { SpBootEntries = myBootEntry; } previousEntry = myBootEntry; // // Move to the next entry in the enumeration list, if any. // if (bootEntryList->NextEntryOffset == 0) { break; } bootEntryList = ADD_OFFSET(bootEntryList, NextEntryOffset); } // // Free the enumeration buffer. // SpMemFree(bootEntries); } // // Boot entries are returned in an unspecified order. They are currently // in the SpBootEntries list in the order in which they were returned. // Sort the boot entry list based on the boot order. Do this by walking // the boot order array backwards, reinserting the entry corresponding to // each element of the array at the head of the list. // for (i = (LONG)count - 1; i >= 0; i--) { for (previousEntry = NULL, myBootEntry = SpBootEntries; myBootEntry != NULL; previousEntry = myBootEntry, myBootEntry = myBootEntry->Next) { if (myBootEntry->NtBootEntry.Id == order[i] ) { // // We found the boot entry with this ID. If it's not already // at the front of the list, move it there. // myBootEntry->Status |= BE_STATUS_ORDERED; if (previousEntry != NULL) { previousEntry->Next = myBootEntry->Next; myBootEntry->Next = SpBootEntries; SpBootEntries = myBootEntry; } else { ASSERT(SpBootEntries == myBootEntry); } break; } } } if (count != 0) { SpMemFree(order); } return TRUE; } // SpReadAndConvertEfiBootEntries ULONG SpSafeWcslen ( IN PWSTR String, IN PWSTR Max ) /*++ Routine Description: Calculate the length of a null-terminated string in a safe manner, avoiding walking off the end of the buffer if the string is not properly terminated. Arguments: String - Address of string. Max - Address of first byte beyond the maximum legal address for the string. In other words, the address of the first byte past the end of the buffer in which the string is contained. Return Value: ULONG - Length of the string, in characters, not including the null terminator. If the string is not terminated before the end of the buffer, 0xffffffff is returned. --*/ { PWSTR p = String; // // Walk through the string, looking for either the end of the buffer // or a null terminator. // while ((p < Max) && (*p != 0)) { p++; } // // If we didn't reach the end of the buffer, then we found a null // terminator. Return the length of the string, in characters. // if (p < Max) { return (ULONG)(p - String); } // // The string is not properly terminated. Return an error indicator. // return 0xffffffff; } // SpSafeWcslen VOID SpTranslateFilePathToRegion ( IN PFILE_PATH FilePath, OUT PDISK_REGION *DiskRegion, OUT PWSTR *PartitionNtName, OUT PWSTR *PartitionRelativePath ) /*++ Routine Description: Translate a FILE_PATH to a pointer to a disk region and the path relative to the region. Arguments: FilePath - Address of FILE_PATH. DiskRegion - Returns the address of the disk region described by FilePath. NULL is returned if the matching disk region cannot be found. PartitionNtName - Returns the NT name associated with the disk region. NULL is returned if the file path cannot be translated into NT format. PartitionRelativePath - Returns the volume-relative path of the file or directory described by the FilePath. NULL is returned if the file path cannot be translated into NT format. Return Value: None. --*/ { NTSTATUS status; ULONG length; PFILE_PATH ntFilePath; PWSTR p; PWSTR q; PHARDDISK_NAME_TRANSLATION translation; // // Translate the file path into NT format. (It is probably in EFI format.) // length = 0; status = ZwTranslateFilePath( FilePath, FILE_PATH_TYPE_NT, NULL, &length ); if (status != STATUS_BUFFER_TOO_SMALL) { *PartitionNtName = NULL; *DiskRegion = NULL; *PartitionRelativePath = NULL; return; } ntFilePath = SpMemAlloc(length); status = ZwTranslateFilePath( FilePath, FILE_PATH_TYPE_NT, ntFilePath, &length ); if (status != STATUS_SUCCESS) { ASSERT(FALSE); *PartitionNtName = NULL; *DiskRegion = NULL; *PartitionRelativePath = NULL; SpMemFree(ntFilePath); return; } // // NtTranslateFilePath returns a name of the form \Device\HarddiskVolumeN. // We need to have a name of the form \Device\HardiskN\PartitionM. (This is // because all of the ARC<->NT translations use the latter form.) Use the // translation list built by SpBuildHarddiskNameTranslations to do the // translation. // // If the returned name doesn't include "HarddiskVolume", or if no // translation is found, use the returned name and hope for the best. // p = (PWSTR)ntFilePath->FilePath; q = p; if (wcsstr(q, L"Volume") != NULL){ for ( translation = SpHarddiskNameTranslations; translation != NULL; translation = translation->Next ) { if (_wcsicmp(translation->VolumeName, q) == 0) { break; } } if (translation != NULL) { q = translation->PartitionName; } } // // We now have the file path in NT format. Get the disk region that // corresponds to the NT device name. Return the obtained information. // *PartitionNtName = SpDupStringW(q); *DiskRegion = SpRegionFromNtName(q, PartitionOrdinalCurrent); p += wcslen(p) + 1; *PartitionRelativePath = SpDupStringW(p); // // Free local memory. // SpMemFree(ntFilePath); return; } #endif // defined(EFI_NVRAM_ENABLED) NTSTATUS SpAddNTInstallToBootList( IN PVOID SifHandle, IN PDISK_REGION SystemPartitionRegion, IN PWSTR SystemPartitionDirectory, IN PDISK_REGION NtPartitionRegion, IN PWSTR Sysroot, IN PWSTR OsLoadOptions, OPTIONAL IN PWSTR LoadIdentifier OPTIONAL ) /*++ Routine Description: This routine takes the core components of a boot set and passes them on to SpAddUserDefinedInstallationToBootList, which does the real work of constructing a boot set. After the new boot set is created, the boot vars are flushed - the exact implementation of the flush depends on the architecture. On amd64/x86, we'll have a new boot.ini after this routine is done. Arguments: SifHandle - pointer to the setup sif file Return Value: STATUS_SUCCESS if the NT install was successfully added to the boot list if there was an error, the status is returned --*/ { NTSTATUS status; // // create the new user defined boot set // status = SpAddUserDefinedInstallationToBootList(SifHandle, SystemPartitionRegion, SystemPartitionDirectory, NtPartitionRegion, Sysroot, OsLoadOptions, LoadIdentifier ); if (! NT_SUCCESS(status)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpExportBootEntries: failed while installing new boot set: Status = %lx\n", status )); return status; } // // write the new boot set out // if (SpFlushBootVars() == FALSE) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpAddDiscoveredNTInstallToBootList: failed flushing boot vars\n" )); status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } return status; } NTSTATUS SpAddUserDefinedInstallationToBootList( IN PVOID SifHandle, IN PDISK_REGION SystemPartitionRegion, IN PWSTR SystemPartitionDirectory, IN PDISK_REGION NtPartitionRegion, IN PWSTR Sysroot, IN PWSTR OsLoadOptions, OPTIONAL IN PWSTR LoadIdentifier OPTIONAL ) /*++ Routine Description: This routine is based on SpAddInstallationToBootList, with the major differences being: there is no processing of the load options the user can specifiy the loadIdentifier Return Value: STATUS_SUCCESS if the NT install was successfully added to the boot list if there was an error, the status is returned --*/ { PWSTR BootVars[MAXBOOTVARS]; PWSTR SystemPartitionArcName; PWSTR TargetPartitionArcName; PWSTR tmp; PWSTR tmp2; PWSTR locOsLoadOptions; PWSTR locLoadIdentifier; ULONG Signature; ENUMARCPATHTYPE ArcPathType; NTSTATUS status; status = STATUS_SUCCESS; ArcPathType = PrimaryArcPath; tmp2 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2); if (!SpIsEfi()) { // // Get an ARC name for the system partition. // if (SystemPartitionRegion != NULL) { SpArcNameFromRegion( SystemPartitionRegion, tmp2, sizeof(TemporaryBuffer)/2, PartitionOrdinalOnDisk, PrimaryArcPath ); SystemPartitionArcName = SpDupStringW(tmp2); } else { SystemPartitionArcName = NULL; } // // Get an ARC name for the target partition. // // // If the partition is on a SCSI disk that has more than 1024 cylinders // and the partition has sectors located on cylinders beyond cylinder // 1024, the get the arc name in the secondary format. See also // spcopy.c!SpCreateNtbootddSys(). // if( !SpIsArc() && #if defined(REMOTE_BOOT) !RemoteBootSetup && #endif // defined(REMOTE_BOOT) #if defined(_AMD64_) || defined(_X86_) !SpUseBIOSToBoot(NtPartitionRegion, NULL, SifHandle) && #endif // defined(_AMD64_) || defined(_X86_) (HardDisks[NtPartitionRegion->DiskNumber].ScsiMiniportShortname[0]) ) { ArcPathType = SecondaryArcPath; } else { ArcPathType = PrimaryArcPath; } SpArcNameFromRegion( NtPartitionRegion, tmp2, sizeof(TemporaryBuffer)/2, PartitionOrdinalOnDisk, ArcPathType ); TargetPartitionArcName = SpDupStringW(tmp2); } // // Tweak the load identifier if necessary // if (LoadIdentifier) { if(!SpIsArc()) { // // Need quotation marks around the description on amd64/x86. // locLoadIdentifier = SpMemAlloc((wcslen(LoadIdentifier)+3)*sizeof(WCHAR)); locLoadIdentifier[0] = L'\"'; wcscpy(locLoadIdentifier+1,LoadIdentifier); wcscat(locLoadIdentifier,L"\""); } else { locLoadIdentifier = SpDupStringW(LoadIdentifier); } } else { locLoadIdentifier = SpDupStringW(L""); } ASSERT(locLoadIdentifier); // // Tweak the load options if necessary // if (OsLoadOptions) { locOsLoadOptions = SpDupStringW(OsLoadOptions); } else { locOsLoadOptions = SpDupStringW(L""); } ASSERT(locOsLoadOptions); // // Create a new internal-format boot entry. // tmp = TemporaryBuffer; wcscpy(tmp,SystemPartitionDirectory); SpConcatenatePaths( tmp, #if defined(_AMD64_) || defined(_X86_) SpIsArc() ? L"arcldr.exe" : L"ntldr" #elif defined(_IA64_) L"ia64ldr.efi" #else L"osloader.exe" #endif // defined(_AMD64_) || defined(_X86_) ); tmp = SpDupStringW(tmp); SpCreateBootEntry( BE_STATUS_NEW, SystemPartitionRegion, tmp, NtPartitionRegion, Sysroot, locOsLoadOptions, locLoadIdentifier ); SpMemFree(tmp); // // If not on an EFI machine, add a new ARC-style boot set. // if (!SpIsEfi()) { BootVars[OSLOADOPTIONS] = locOsLoadOptions; BootVars[LOADIDENTIFIER] = locLoadIdentifier; // // OSLOADER is the system partition path + the system partition directory + // osloader.exe. (ntldr on amd64 or x86 machines). // if (SystemPartitionRegion != NULL) { tmp = TemporaryBuffer; wcscpy(tmp,SystemPartitionArcName); SpConcatenatePaths(tmp,SystemPartitionDirectory); SpConcatenatePaths( tmp, #if defined(_AMD64_) || defined(_X86_) (SpIsArc() ? L"arcldr.exe" : L"ntldr") #elif defined(_IA64_) L"ia64ldr.efi" #else L"osloader.exe" #endif // defined(_AMD64_) || defined(_X86_) ); BootVars[OSLOADER] = SpDupStringW(tmp); } else { BootVars[OSLOADER] = SpDupStringW(L""); } // // OSLOADPARTITION is the ARC name of the windows nt partition. // BootVars[OSLOADPARTITION] = TargetPartitionArcName; // // OSLOADFILENAME is sysroot. // BootVars[OSLOADFILENAME] = Sysroot; // // SYSTEMPARTITION is the ARC name of the system partition. // if (SystemPartitionRegion != NULL) { BootVars[SYSTEMPARTITION] = SystemPartitionArcName; } else { BootVars[SYSTEMPARTITION] = L""; } // // get the disk signature // if ((NtPartitionRegion->DiskNumber != 0xffffffff) && HardDisks[NtPartitionRegion->DiskNumber].Signature) { Signature = HardDisks[NtPartitionRegion->DiskNumber].Signature; } else { Signature = 0; } // // Add the boot set and make it the default. // SpAddBootSet(BootVars, TRUE, Signature); SpMemFree(BootVars[OSLOADER]); } // // Free memory allocated. // if (locLoadIdentifier) { SpMemFree(locLoadIdentifier); } if (!SpIsEfi()) { if (SystemPartitionArcName) { SpMemFree(SystemPartitionArcName); } if (TargetPartitionArcName) { SpMemFree(TargetPartitionArcName); } } return status; } NTSTATUS SpExportBootEntries( IN OUT PLIST_ENTRY BootEntries, OUT PULONG BootEntryCnt ) /*++ Routine Description: This routine compiles a safely exportable string represenation of the boot options. Arguments: BootEntries - returns pointing to the head of the linked list containing the exported boot entries BootEntriesCnt - returns with the # of boot entries exported Return Value: STATUS_SUCCESS if the boot entries were successfully exported if there was an error, the status is returned --*/ { PSP_BOOT_ENTRY bootEntry; PSP_EXPORTED_BOOT_ENTRY ebootEntry; *BootEntryCnt = 0; // // make sure we were given the list head // ASSERT(BootEntries); if (!BootEntries) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpExportBootEntries: pointer to boot entry list is NULL\n" )); return STATUS_INVALID_PARAMETER; } // // make sure the list is empty // ASSERT(IsListEmpty(BootEntries)); if (! IsListEmpty(BootEntries)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpExportBootEntries: incoming boot entry list should be empty\n" )); return STATUS_INVALID_PARAMETER; } // // for each boot entry, collect a subset of information and compile // it in an exportable (safe) string form // for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) { // // allocate the node... // ebootEntry = SpMemAlloc(sizeof(SP_EXPORTED_BOOT_ENTRY)); ASSERT(ebootEntry); if (ebootEntry == NULL) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpExportBootEntries: failed allocationg new exported boot entry\n" )); return STATUS_NO_MEMORY; } RtlZeroMemory( ebootEntry, sizeof(SP_EXPORTED_BOOT_ENTRY) ); // // map selected fields from SpBootEntries to our export // ebootEntry->LoadIdentifier = SpDupStringW(bootEntry->FriendlyName); ebootEntry->OsLoadOptions = SpDupStringW(bootEntry->OsLoadOptions); ebootEntry->DriverLetter = bootEntry->OsPartitionDiskRegion->DriveLetter; ebootEntry->OsDirectory = SpDupStringW(bootEntry->OsDirectory); InsertTailList( BootEntries, &ebootEntry->ListEntry ); ++*BootEntryCnt; } if (*BootEntryCnt == 0) { ASSERT(IsListEmpty(BootEntries)); if(! IsListEmpty(BootEntries)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpExportBootEntries: exported boot entry list should be empty\n" )); return STATUS_UNSUCCESSFUL; } } else { ASSERT(! IsListEmpty(BootEntries)); if(IsListEmpty(BootEntries)) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpExportBootEntries: exported boot entry list should NOT be empty\n" )); return STATUS_UNSUCCESSFUL; } } return STATUS_SUCCESS; } NTSTATUS SpFreeExportedBootEntries( IN PLIST_ENTRY BootEntries, IN ULONG BootEntryCnt ) /*++ Routine Description: A convenience routine to free the exported boot entries Arguments: BootEntries - points to the head of the linked list containing the exported boot entries BootEntriesCnt - the # of boot entries exported Return Value: STATUS_SUCCESS if the exported boot entries were successfully freed if there was an error, the status is returned --*/ { PSP_EXPORTED_BOOT_ENTRY bootEntry; PLIST_ENTRY listEntry; ULONG cnt; NTSTATUS status; cnt = 0; while ( !IsListEmpty(BootEntries) ) { listEntry = RemoveHeadList(BootEntries); bootEntry = CONTAINING_RECORD(listEntry, SP_EXPORTED_BOOT_ENTRY, ListEntry ); if (bootEntry->LoadIdentifier) { SpMemFree(bootEntry->LoadIdentifier); } if (bootEntry->OsLoadOptions) { SpMemFree(bootEntry->OsLoadOptions); } if (bootEntry->OsDirectory) { SpMemFree(bootEntry->OsDirectory); } SpMemFree(bootEntry); cnt++; } ASSERT(cnt == BootEntryCnt); if (cnt == BootEntryCnt) { status = STATUS_SUCCESS; } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpFreeExportedBootEntries: incorrect # of boot entries freed\n" )); status = STATUS_UNSUCCESSFUL; } return status; } NTSTATUS SpSetRedirectSwitchMode( IN RedirectSwitchesModeEnum mode, IN PCHAR redirectSwitch, IN PCHAR redirectBaudRateSwitch ) /*++ Routine Description: This routine is used to manage how the redirect switches are set in the boot configuration (amd64/x86 ==> boot.ini) Depending on the mode chosen, the user may specify which parameters they want to set or if they just want the default (legacy) behavior. NOTE: The user specified switches are copied into globals for use by the Flush routines. The global, RedirectSwitchesMode, is set and remains set after this routine returns. All subsequent FlushBootVars will use this mode. Arguments: mode - how we affect the redirect switches redirectSwitch - the user defined redirect parameter redirectBaudRateSwitch - the user defined baudrate paramtere Return Value: STATUS_SUCCESS if the redirect values were successfully set if there was an error, the status is returned --*/ { NTSTATUS status; // // set the mode and user defined parameters // RedirectSwitchesMode = mode; // // null the redirect switches by default // RedirectSwitches.port[0] = '\0'; RedirectSwitches.baudrate[0] = '\0'; // // get copies of the user defined switches if specified // if (redirectSwitch) { strncpy(RedirectSwitches.port, redirectSwitch, MAXSIZE_REDIRECT_SWITCH); } if (redirectBaudRateSwitch) { strncpy(RedirectSwitches.baudrate, redirectBaudRateSwitch, MAXSIZE_REDIRECT_SWITCH); } // // update the boot options using the specified mode // if (SpFlushBootVars() == FALSE) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpAddDiscoveredNTInstallToBootList: failed flushing boot vars\n" )); status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } return status; } NTSTATUS SpSetDefaultBootEntry( ULONG BootEntryNumber ) /*++ Routine Description: Set the Default boot entry to the user specified boot entry. Arguments: BootEntryNumber - the position of the boot entry in the list which is intended to become the default. This number should be >= 1. Return Value: STATUS_SUCCESS if the default was successfully set STATUS_NOT_FOUND if the specified boot entry was not found or is missing if there was an error, the status is returned --*/ { PSP_BOOT_ENTRY bootEntry; NTSTATUS status; ULONG BootEntryCount; // // Find the user specified boot entry // BootEntryCount = 1; for (bootEntry = SpBootEntries; (bootEntry != NULL) && (BootEntryCount != BootEntryNumber); bootEntry = bootEntry->Next) { ++BootEntryCount; } ASSERT(BootEntryCount == BootEntryNumber); ASSERT(bootEntry); // // if we have found our match, then set the Default // if ((bootEntry != NULL) && (BootEntryCount == BootEntryNumber)) { PDISK_REGION Region; // // point to the disk region with the sig info // Region = bootEntry->OsPartitionDiskRegion; ASSERT(Region); if (! Region) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpSetDefaultBootEntry: new default partition region is NULL\n" )); return STATUS_UNSUCCESSFUL; } // // Free the previous Default // if( Default ) { SpMemFree( Default ); } Default = SpMemAlloc( MAX_PATH * sizeof(WCHAR) ); ASSERT( Default ); if (! Default) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpSetDefaultBootEntry: failed to allocate new Default\n" )); return STATUS_UNSUCCESSFUL; } // // fetch the arc name for the region // SpArcNameFromRegion( Region, TemporaryBuffer, sizeof(TemporaryBuffer)/2, PartitionOrdinalOnDisk, PrimaryArcPath ); // // store the new partition and directory info // wcscpy( Default, TemporaryBuffer); SpConcatenatePaths(Default, bootEntry->OsDirectory); // // get the disk signature of the new default disk // if ((Region->DiskNumber != 0xffffffff) && HardDisks[Region->DiskNumber].Signature) { DefaultSignature = HardDisks[Region->DiskNumber].Signature; } else { DefaultSignature = 0; } // // update the boot options using the specified mode // if(SpFlushBootVars() == FALSE) { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpSetDefaultBootEntry: failed flushing boot vars\n" )); status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } } else { KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SpSetDefaultBootEntry: failed to find specified boot entry to use as default\n" )); status = STATUS_NOT_FOUND; } return status; } #if defined(EFI_NVRAM_ENABLED) NTSTATUS SpUpdateDriverEntry( IN PCWSTR DriverName, IN PCWSTR FriendlyName, IN PCWSTR SrcNtDevice, IN PCWSTR SrcDir, IN PCWSTR DestNtDevice OPTIONAL, IN PCWSTR DestDir OPTIONAL ) /*++ Routine Description: Updates the driver entry for the specified driver. If there's no driver entry for the driver, it creates a new one and copies the driver from the source location to the destination location. If there is already a driver entry, the function will not change it; if necessary, the latest version of the driver will be copied from the source location to the location pointed to by the entry. Arguments: DriverName - the file name of the driver (no path) FriendlyName - if the function needs to create a new driver entry, this will be its description SrcNtDevice - NT device name of the location where the driver should be copied from SrcDir - path (relative to SrcNtDevice) to the location where the driver should be copied from DestNtDevice - NT device name of the location where the driver should be copied to. If NULL, SrcNTDevice will be used. DestDir - path( relative to DestNtDevice) to the location where the driver should be copied to. If NULL, SrcDir will be used. If the function needs to create a new driver entry, it will point to DestNTDevice\DestDir. Return value: STATUS_SUCCESS if successful, otherwise an error status. --*/ { NTSTATUS Status = STATUS_SUCCESS; PWSTR SrcPath = NULL; // path to the source driver file PWSTR DestPath = NULL; // path to the destination file PWSTR SrcFullNtPath = NULL; // full nt path (device + path) for source PWSTR DestFullNtPath = NULL; // full nt path (device + path) for destination PEFI_DRIVER_ENTRY_LIST DriverList = NULL; // list of driver entries PEFI_DRIVER_ENTRY DriverEntry = NULL; // the new entry to be added PFILE_PATH DriverOptionPath = NULL; // file path to the existing driver entry PWSTR OldDriverDevice = NULL; // nt device of the existing driver file PWSTR OldDriverPath = NULL; // path to the existing driver file PWSTR OldDriverFullPath = NULL; // full nt path to the existing driver file PULONG DriverEntryOrder = NULL; // holds the array of driver entries ULONG EntryId; // ID of the existing or newly added driver entry BOOLEAN SameSrcDest; // true if the source and destination dirs are the same ULONG Length = 0; if(NULL == DriverName || NULL == FriendlyName || NULL == SrcNtDevice || NULL == SrcDir) { Status = STATUS_INVALID_PARAMETER; goto exit; } if(NULL == DestNtDevice) { DestNtDevice = SrcNtDevice; } if(NULL == DestDir) { DestDir = SrcDir; } Status = ZwEnumerateDriverEntries(NULL, &Length); if(!NT_SUCCESS(Status)) { PEFI_DRIVER_ENTRY_LIST Entry; BOOLEAN bContinue; if(Status != STATUS_BUFFER_TOO_SMALL) { goto exit; } ASSERT(Length != 0); DriverList = (PEFI_DRIVER_ENTRY_LIST) SpMemAlloc(Length); Status = ZwEnumerateDriverEntries(DriverList, &Length); if(!NT_SUCCESS(Status)) { goto exit; } // // Search the list of entries for our driver // bContinue = TRUE; for(Entry = DriverList; bContinue; Entry = (PEFI_DRIVER_ENTRY_LIST) ((PCHAR) Entry + Entry->NextEntryOffset)) { PFILE_PATH FilePath = (PFILE_PATH) ((PCHAR) &Entry->DriverEntry + Entry->DriverEntry.DriverFilePathOffset); ULONG PathLength; PCWSTR FileName; bContinue = (Entry->NextEntryOffset != 0); EntryId = Entry->DriverEntry.Id; if(FilePath->Type != FILE_PATH_TYPE_NT) { PVOID Buffer; PathLength = 0; Status = ZwTranslateFilePath(FilePath, FILE_PATH_TYPE_NT, NULL, &PathLength); if(NT_SUCCESS(Status)) { Status = STATUS_UNSUCCESSFUL; } if(STATUS_BUFFER_TOO_SMALL == Status) { ASSERT(PathLength != 0); if(DriverOptionPath != NULL) { SpMemFree(DriverOptionPath); } DriverOptionPath = SpMemAlloc(PathLength); Status = ZwTranslateFilePath(FilePath, FILE_PATH_TYPE_NT, DriverOptionPath, &PathLength); } if(!NT_SUCCESS(Status)) { if(STATUS_OBJECT_PATH_NOT_FOUND == Status || STATUS_OBJECT_NAME_NOT_FOUND == Status) { // // This entry is stale; remove it // ZwDeleteDriverEntry(EntryId); } continue; } FilePath = DriverOptionPath; } PathLength = wcslen((PCWSTR) DriverOptionPath->FilePath) + 1; FileName = wcsrchr((PCWSTR) DriverOptionPath->FilePath + PathLength, L'\\'); if(FileName != NULL && 0 == _wcsicmp(DriverName, FileName + 1)) { OldDriverDevice = SpDupStringW((PCWSTR) DriverOptionPath->FilePath); OldDriverPath = SpDupStringW((PCWSTR) DriverOptionPath->FilePath + PathLength); wcscpy(TemporaryBuffer, OldDriverDevice); SpConcatenatePaths(TemporaryBuffer, OldDriverPath); OldDriverFullPath = SpDupStringW(TemporaryBuffer); break; } } } // // Build the NT paths for source and dest // wcscpy(TemporaryBuffer, SrcDir); SpConcatenatePaths(TemporaryBuffer, DriverName); SrcPath = SpDupStringW(TemporaryBuffer); wcscpy(TemporaryBuffer, SrcNtDevice); SpConcatenatePaths(TemporaryBuffer, SrcPath); SrcFullNtPath = SpDupStringW(TemporaryBuffer); wcscpy(TemporaryBuffer, DestDir); SpConcatenatePaths(TemporaryBuffer, DriverName); DestPath = SpDupStringW(TemporaryBuffer); wcscpy(TemporaryBuffer, DestNtDevice); SpConcatenatePaths(TemporaryBuffer, DestPath); DestFullNtPath = SpDupStringW(TemporaryBuffer); // // Note that there can be different ways to specify the NT path so // the caller should not use different forms for source and destination. // SameSrcDest = (0 == _wcsicmp(SrcFullNtPath, DestFullNtPath)); if(OldDriverFullPath != NULL) { // // There is already an entry for our driver; compare the versions // ULONGLONG VersionOld; ULONGLONG VersionNew; Status = SpGetFileVersionFromPath(OldDriverFullPath, &VersionOld); if(STATUS_OBJECT_NAME_NOT_FOUND == Status || STATUS_OBJECT_PATH_NOT_FOUND == Status) { // // This entry is stale; remove it // ZwDeleteDriverEntry(EntryId); goto create_entry; } if(!NT_SUCCESS(Status)) { goto exit; } Status = SpGetFileVersionFromPath(SrcFullNtPath, &VersionNew); if(!NT_SUCCESS(Status)) { goto exit; } if(VersionOld < VersionNew) { // // Copy the new driver and leave the driver entry alone // Status = SpCopyFileUsingNames((PWSTR) SrcFullNtPath, OldDriverFullPath, 0, COPY_NODECOMP); } } else { ULONG FriendlyNameOffset; ULONG FriendlyNameLength; ULONG NtDeviceLength; ULONG DestPathLength; ULONG FilePathLength; ULONG EntryLength; PFILE_PATH FilePath; create_entry: // // Copy the driver to its destination if not already there. // if(!SameSrcDest) { // // Make sure the dest dir is present; if this fails, the file copy will fail too // SpCreateDirectory(DestNtDevice, NULL, DestDir, 0, CREATE_DIRECTORY_FLAG_SKIPPABLE); Status = SpCopyFileUsingNames((PWSTR) SrcFullNtPath, (PWSTR) DestFullNtPath, 0, COPY_NODECOMP); if(!NT_SUCCESS(Status)) { goto exit; } } // // Add a new driver entry // FriendlyNameOffset = ALIGN_UP(sizeof(EFI_DRIVER_ENTRY), WCHAR); FriendlyNameLength = (wcslen(FriendlyName) + 1) * sizeof(WCHAR); NtDeviceLength = (wcslen(DestNtDevice) + 1) * sizeof(WCHAR); DestPathLength = (wcslen(DestPath) + 1) * sizeof(WCHAR); FilePathLength = FIELD_OFFSET(FILE_PATH, FilePath) + NtDeviceLength + DestPathLength; EntryLength = FriendlyNameOffset + ALIGN_UP(FriendlyNameLength, ULONG) + FilePathLength; DriverEntry = SpMemAlloc(EntryLength); DriverEntry->Version = EFI_DRIVER_ENTRY_VERSION; DriverEntry->Length = EntryLength; DriverEntry->FriendlyNameOffset = FriendlyNameOffset; DriverEntry->DriverFilePathOffset = FriendlyNameOffset + ALIGN_UP(FriendlyNameLength, ULONG); RtlCopyMemory((PCHAR) DriverEntry + DriverEntry->FriendlyNameOffset, FriendlyName, FriendlyNameLength); FilePath = (PFILE_PATH) ((PCHAR) DriverEntry + DriverEntry->DriverFilePathOffset); FilePath->Version = FILE_PATH_VERSION; FilePath->Length = FilePathLength; FilePath->Type = FILE_PATH_TYPE_NT; RtlCopyMemory(FilePath->FilePath, DestNtDevice, NtDeviceLength); RtlCopyMemory(FilePath->FilePath + NtDeviceLength, DestPath, DestPathLength); Status = ZwAddDriverEntry(DriverEntry, &EntryId); if(!NT_SUCCESS(Status)) { goto exit; } Length = 0; Status = ZwQueryDriverEntryOrder(NULL, &Length); if(!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) { goto exit; } DriverEntryOrder = (PULONG) SpMemAlloc((Length + 1) * sizeof(ULONG)); if(Length != 0) { Status = ZwQueryDriverEntryOrder(DriverEntryOrder, &Length); if(!NT_SUCCESS(Status)) { goto exit; } } DriverEntryOrder[Length] = EntryId; Status = ZwSetDriverEntryOrder(DriverEntryOrder, Length + 1); } // // Delete the source file // if(!SameSrcDest) { SpDeleteFile(SrcFullNtPath, NULL, NULL); } exit: if(SrcPath != NULL) { SpMemFree(SrcPath); } if(DestPath != NULL) { SpMemFree(DestPath); } if(SrcFullNtPath != NULL) { SpMemFree(SrcFullNtPath); } if(DestFullNtPath != NULL) { SpMemFree(DestFullNtPath); } if(DriverList != NULL) { SpMemFree(DriverList); } if(OldDriverDevice != NULL) { SpMemFree(OldDriverDevice); } if(OldDriverPath != NULL) { SpMemFree(OldDriverPath); } if(OldDriverFullPath != NULL) { SpMemFree(OldDriverFullPath); } if(DriverEntry != NULL) { SpMemFree(DriverEntry); } if(DriverOptionPath != NULL) { SpMemFree(DriverOptionPath); } if(DriverEntryOrder != NULL) { SpMemFree(DriverEntryOrder); } return Status; } #endif // EFI_NVRAM_ENABLED