Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4589 lines
122 KiB

/*++
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 <hdlsblk.h>
#include <hdlsterm.h>
#if defined(EFI_NVRAM_ENABLED)
#include <efi.h>
#include <efiapi.h>
#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 _X86_
} else {
Spx86InitBootVars( BootVars, &Default, &Timeout );
#endif
}
//
// 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(_X86_)
} else {
Status = Spx86FlushBootVars( BootVars, Timeout, Default );
#endif
}
}
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{ // not x86
//
// 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(_X86_)
} else {
return Spx86FlushRemoteBootVars( TargetRegion, BootVars, Default );
#endif
}
}
}
#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 <OSLoadPartition><OsDirectory>.
// 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);
}
#ifdef _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
//
// 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";
#ifdef _X86_
WCHAR BootFastString[] = L"/fastdetect";
BOOLEAN AddBootFastString = TRUE;
#endif
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)
#ifdef _X86_
!SpUseBIOSToBoot(NtPartitionRegion, NULL, SifHandle) &&
#endif
(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;
}
#ifdef _X86_
if(wcsstr(TemporaryBuffer, BootFastString)) { // already have /bootfast
AddBootFastString = FALSE;
}
#endif
}
if(AddBaseVideo || AddSosToBaseVideoString
#ifdef _X86_
|| AddBootFastString
#endif
) {
Length = ((tmp ? wcslen(tmp) + 1 : 0) * sizeof(WCHAR));
if( AddBaseVideo ) {
Length += sizeof(BaseVideoString);
}
if( AddSosToBaseVideoString ) {
Length += sizeof( BaseVideoSosString );
}
#ifdef _X86_
if( AddBootFastString ) {
Length += sizeof( BootFastString );
}
#endif
tmp2 = SpMemAlloc(Length);
*tmp2 = ( WCHAR )'\0';
if( AddBaseVideo ) {
wcscat(tmp2, BaseVideoString);
}
if( AddSosToBaseVideoString ) {
if( *tmp2 != (WCHAR)'\0' ) {
wcscat(tmp2, L" ");
}
wcscat(tmp2, BaseVideoSosString);
}
#ifdef _X86_
if( AddBootFastString ) {
if( *tmp2 != (WCHAR)'\0' ) {
wcscat(tmp2, L" ");
}
wcscat(tmp2, BootFastString);
}
#endif
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 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,
#ifdef _X86_
SpIsArc() ? L"arcldr.exe" : L"ntldr"
#elif _IA64_
L"ia64ldr.efi"
#else
L"osloader.exe"
#endif
);
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 x86 machines).
//
if (SystemPartitionRegion != NULL) {
tmp = TemporaryBuffer;
wcscpy(tmp,SystemPartitionArcName);
SpConcatenatePaths(tmp,SystemPartitionDirectory);
SpConcatenatePaths(
tmp,
#ifdef _X86_
(SpIsArc() ? L"arcldr.exe" : L"ntldr")
#elif _IA64_
L"ia64ldr.efi"
#else
L"osloader.exe"
#endif
);
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 x86 case, 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);
INIT_OBJA(&obja, &unicodeString, linkName);
status = ZwOpenSymbolicLinkObject(
&linkHandle,
READ_CONTROL | SYMBOLIC_LINK_QUERY,
&obja
);
if (!NT_SUCCESS(status)) {
ASSERT(FALSE);
SpMemFree(linkName);
goto cleanup;
}
//
// Query the link to get the link target.
//
unicodeString.Buffer = TemporaryBuffer;
unicodeString.Length = 0;
unicodeString.MaximumLength = sizeof(TemporaryBuffer);
status = ZwQuerySymbolicLinkObject(
linkHandle,
&unicodeString,
NULL
);
ZwClose(linkHandle);
if (!NT_SUCCESS(status)) {
ASSERT(FALSE);
SpMemFree(linkName);
goto cleanup;
}
//
// Terminate the returned string.
//
TemporaryBuffer[unicodeString.Length/sizeof(WCHAR)] = 0;
//
// 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"HarddiskVolume") != 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 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)
#ifdef _X86_
!SpUseBIOSToBoot(NtPartitionRegion, NULL, SifHandle) &&
#endif
(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 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,
#ifdef _X86_
SpIsArc() ? L"arcldr.exe" : L"ntldr"
#elif _IA64_
L"ia64ldr.efi"
#else
L"osloader.exe"
#endif
);
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 x86 machines).
//
if (SystemPartitionRegion != NULL) {
tmp = TemporaryBuffer;
wcscpy(tmp,SystemPartitionArcName);
SpConcatenatePaths(tmp,SystemPartitionDirectory);
SpConcatenatePaths(
tmp,
#ifdef _X86_
(SpIsArc() ? L"arcldr.exe" : L"ntldr")
#elif _IA64_
L"ia64ldr.efi"
#else
L"osloader.exe"
#endif
);
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 (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;
}