/*++

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;

}