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