windows-nt/Source/XPSP1/NT/base/efiutil/efinvram/nvrio.c
2020-09-26 16:20:57 +08:00

2082 lines
46 KiB
C

/*++
Module Name:
nvrio.c
Abstract:
Access function to r/w environment variables from NVRAM
Author:
Mudit Vats (v-muditv) 12-13-99
Revision History:
--*/
#include <precomp.h>
#define FIELD_OFFSET(type, field) ((UINT32)(UINTN)&(((type *)0)->field))
#define ALIGN_DOWN(length, type) \
((UINT32)(length) & ~(sizeof(type) - 1))
#define ALIGN_UP(length, type) \
(ALIGN_DOWN(((UINT32)(length) + sizeof(type) - 1), type))
#define EFI_ATTR EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
VOID* LoadOptions [MAXBOOTVARS];
UINT64 LoadOptionsSize [MAXBOOTVARS];
VOID* BootOrder;
UINT64 BootOrderCount;
UINT64 OsBootOptionCount;
#define LOAD_OPTION_ACTIVE 0x00000001
INT32
SafeWcslen (
CHAR16 *String,
CHAR16 *Max
)
{
CHAR16 *p = String;
while ( (p < Max) && (*p != 0) ) {
p++;
}
if ( p < Max ) {
return(UINT32)(p - String);
}
return -1;
} // SafeWclen
#define ISWINDOWSOSCHECK_DEBUG 0
BOOLEAN
isWindowsOsBootOption(
char* elo,
UINT64 eloSize
)
//
// Purpose: determine if the EFI_LOAD_OPTION structure in question is referring to
// a Windows OS boot option
//
// Return:
//
// TRUE elo refers to a Windows OS option
//
{
CHAR16 *max;
INT32 l;
UINTN length;
PEFI_LOAD_OPTION pElo;
char* devicePath;
char* osOptions;
PWINDOWS_OS_OPTIONS pOsOptions;
char* aOsOptions;
BOOLEAN status;
status = TRUE;
aOsOptions = NULL;
pElo = (EFI_LOAD_OPTION*)elo;
if ( eloSize < sizeof(EFI_LOAD_OPTION) ) {
status = FALSE;
goto Done;
}
#if ISWINDOWSOSCHECK_DEBUG
Print( L"Is %s a Windows OS boot option?\n", pElo->Description );
#endif
//
// Is the description properly terminated?
//
max = (CHAR16 *)(elo + eloSize);
l = SafeWcslen( pElo->Description, max );
if ( l < 0 ) {
#if ISWINDOWSOSCHECK_DEBUG
Print (L"Failed: SafeWcslen( pElo->Description, max )\n");
#endif
status = FALSE;
goto Done;
}
//
// get the WINDOWS_OS_OPTIONS structure from the OptionalData field
//
osOptions = elo +
FIELD_OFFSET(EFI_LOAD_OPTION,Description) +
StrSize(pElo->Description) +
pElo->FilePathListLength;
length = (UINTN)eloSize;
length -= (UINTN)(osOptions - elo);
#if ISWINDOWSOSCHECK_DEBUG
Print (L"length = %x\n", length);
#endif
//
// make sure osOptions are atleast the size of the
// WINDOWS_OS_OPTIONS header
//
//
if ( length < FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions) ) {
#if ISWINDOWSOSCHECK_DEBUG
Print (L"Failed: invalid length: %x\n", length);
#endif
status = FALSE;
goto Done;
}
//
// align the os options
//
aOsOptions = GetAlignedOsOptions(elo, eloSize);
pOsOptions = (WINDOWS_OS_OPTIONS*)aOsOptions;
#if ISWINDOWSOSCHECK_DEBUG
DisplayOsOptions(aOsOptions);
#endif
//
// Does the OsOptions structure look like a WINDOWS_OS_OPTIONS structure?
//
if ( (length != pOsOptions->Length) ||
(WINDOWS_OS_OPTIONS_VERSION != pOsOptions->Version) ||
(strcmpa(pOsOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE) != 0) ) {
#if ISWINDOWSOSCHECK_DEBUG
Print (L"Failed: OsOptions doesn't look like WINDOWS_OS_OPTIONS structure.\n");
Print (L"test1: %x\n", length != pOsOptions->Length);
Print (L"test2: %x\n", WINDOWS_OS_OPTIONS_VERSION != pOsOptions->Version);
Print (L"test3: %x\n", strcmpa(pOsOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE) != 0 );
#endif
status = FALSE;
goto Done;
}
//
// Is the OsLoadOptions string properly terminated?
//
//
// create a new max ptr to accomodate the fact that we are
// now using an aligned copy of OsOptions from the Pool
//
max = (CHAR16*)(aOsOptions + pOsOptions->Length);
#if ISWINDOWSOSCHECK_DEBUG
Print (L"max = %x, osloadoptions = %x, diff = %x, strsize=%x\n",
max,
pOsOptions->OsLoadOptions,
(char*)max - (char*)pOsOptions->OsLoadOptions,
StrSize(pOsOptions->OsLoadOptions)
);
#endif
l = SafeWcslen( pOsOptions->OsLoadOptions, max );
if ( l < 0 ) {
#if ISWINDOWSOSCHECK_DEBUG
Print (L"Failed: SafeWcslen( osLoadOptions, max ) = %x\n", l);
#endif
status = FALSE;
goto Done;
}
Done:
//
// we are done with the os options
//
if (aOsOptions != NULL) {
FreePool(aOsOptions);
}
return status;
}
#define GETBOOTVARS_DEBUG 0
VOID
GetBootManagerVars(
)
{
UINT32 i,j;
CHAR16 szTemp[10];
VOID* bootvar;
UINT64 BootOrderSize = 0;
//
// Initialize EFI LoadOptions.
//
BootOrderSize = 0;
BootOrderCount = 0;
OsBootOptionCount = 0;
BootOrder = NULL;
#if 0
ZeroMem( LoadOptions, sizeof(VOID*) * MAXBOOTVARS );
ZeroMem( LoadOptionsSize, sizeof(UINT64) * MAXBOOTVARS );
#endif
//
// Ensure that the Load Options have been freed
//
ASSERT(BootOrderCount == 0);
//
// Get BootOrder.
//
BootOrder = LibGetVariableAndSize( L"BootOrder", &VenEfi, &BootOrderSize );
if ( BootOrder ) {
BootOrderCount = BootOrderSize / sizeof(CHAR16);
#if GETBOOTVARS_DEBUG
Print (L"BootOrderCount = %x\n", BootOrderCount);
#endif
//
// Get the boot options.
//
for ( i=0; i<BootOrderCount; i++ ) {
SPrint( szTemp, sizeof(szTemp), L"Boot%04x", ((CHAR16*) BootOrder)[i] );
ASSERT(LoadOptions[i] == NULL);
ASSERT(LoadOptionsSize[i] == 0);
LoadOptions[i] = LibGetVariableAndSize( szTemp, &VenEfi, &(LoadOptionsSize[i]) );
#if GETBOOTVARS_DEBUG
Print (L"i = %x, szTemp = %s, BOCnt = %x, LOptions = %x, BSize = %x\n",
i,
szTemp,
OsBootOptionCount,
LoadOptions[i],
LoadOptionsSize[i]
);
#endif
OsBootOptionCount++;
}
}
}
#define ERASEBOOTOPT_DEBUG 0
BOOLEAN
EraseOsBootOption(
UINTN BootVarNum
)
{
UINTN j;
CHAR16 szTemp[10];
CHAR16* tmpBootOrder;
VOID* bootvar;
UINT64 BootOrderSize = 0;
VOID* pDummy;
UINTN dummySize;
//
// Initialize EFI LoadOptions.
//
BootOrderSize = 0;
BootOrderCount = 0;
BootOrder = NULL;
//
// Get BootOrder.
//
BootOrder = LibGetVariableAndSize( L"BootOrder", &VenEfi, &BootOrderSize );
BootOrderCount = BootOrderSize / sizeof(CHAR16);
ASSERT(BootOrderCount == OsBootOptionCount);
ASSERT(BootVarNum < MAXBOOTVARS);
ASSERT(BootOrderCount >= 1);
#if ERASEBOOTOPT_DEBUG
Print (L"BootOrderCount = %x\n", BootOrderCount);
Print (L"BootVarNum = %x\n", BootVarNum);
#endif
//
// if the boot option is populated, then erase it
//
if (LoadOptions[BootVarNum]) {
//
// free the local load option
//
FreePool(LoadOptions[BootVarNum]);
//
// zero the local memory for the load options
//
LoadOptions[BootVarNum] = (VOID*)0;
LoadOptionsSize[BootVarNum] = 0;
//
// Get the boot option
//
SPrint( szTemp, sizeof(szTemp), L"Boot%04x", ((CHAR16*) BootOrder)[BootVarNum] );
#if ERASEBOOTOPT_DEBUG
Print (L"BootXXXX = %s\n", szTemp);
#endif
pDummy = LibGetVariableAndSize( szTemp, &VenEfi, &dummySize );
//
// whack the nvram entry
//
SetVariable(
szTemp,
&VenEfi,
EFI_ATTR,
0,
NULL
);
#if ERASEBOOTOPT_DEBUG
Print (L"Adjusting boot order [begin]\n");
#endif
//
// adjust the counters for os boot options
//
OsBootOptionCount--;
BootOrderCount--;
//
// Shift the remaining entries in the boot order and the load options
//
tmpBootOrder = (CHAR16*)BootOrder;
for (j = BootVarNum; j < BootOrderCount; j++) {
tmpBootOrder[j] = tmpBootOrder[j + 1];
LoadOptions[j] = LoadOptions[j + 1];
LoadOptionsSize[j] = LoadOptionsSize[j + 1];
}
//
// Set the modified boot order
//
SetVariable(
L"BootOrder",
&VenEfi,
EFI_ATTR,
BootOrderCount * sizeof(CHAR16),
BootOrder
);
#if ERASEBOOTOPT_DEBUG
Print (L"Adjusting boot order [end]\n");
#endif
return TRUE;
}
return FALSE;
}
BOOLEAN
EraseAllOsBootOptions(
)
{
UINT32 i;
UINT64 BootOrderSize = 0;
BOOLEAN status;
UINT64 maxBootCount;
#if ERASEBOOTOPT_DEBUG
CHAR16 szInput[1024];
#endif
//
// Initialize EFI LoadOptions.
//
BootOrderSize = 0;
BootOrderCount = 0;
BootOrder = NULL;
//
// Get BootOrder.
//
BootOrder = LibGetVariableAndSize( L"BootOrder", &VenEfi, &BootOrderSize );
BootOrderCount = BootOrderSize / sizeof(CHAR16);
ASSERT(BootOrderCount == OsBootOptionCount);
//
// Make sure there is atleast one OS boot option
//
if ( BootOrder && OsBootOptionCount) {
maxBootCount = BootOrderCount;
//
// erase invidual boot options.
//
for ( i = 0; i < maxBootCount; i++ ) {
#if ERASEBOOTOPT_DEBUG
Print (L"BootOrderCount = %x, Erasing boot option: %x\n", BootOrderCount, i);
#endif
//
// remove the boot entry at the head of the list
//
status = EraseOsBootOption(0);
#if ERASEBOOTOPT_DEBUG
Input (L"Here!\n", szInput, sizeof(szInput));
Print(L"\n");
#endif
if (status == FALSE) {
Print (L"Error: failed to erase boot entry %x\n", i);
break;
}
}
}
return status;
}
BOOLEAN
PushToTop(
IN UINT32 BootVarNum
)
{
UINT32 i;
CHAR16 szTemp[10];
char* osbootoption;
UINT64 osbootoptionsize;
CHAR16 savBootOption;
CHAR16* tmpBootOrder;
UINT64 BootOrderSize = 0;
i=0;
BootOrderSize = 0;
BootOrder = NULL;
//
// Get BootOrder.
//
BootOrder = LibGetVariableAndSize( L"BootOrder", &VenEfi, &BootOrderSize );
//
// Make sure there is atleast one OS boot option
//
if ( BootOrder && OsBootOptionCount) {
BootOrderCount = BootOrderSize / sizeof(CHAR16);
//
// Get the boot option.
//
tmpBootOrder = (CHAR16*)BootOrder;
savBootOption = tmpBootOrder[BootVarNum];
SPrint( szTemp, sizeof(szTemp), L"Boot%04x", savBootOption );
osbootoption = LibGetVariableAndSize( szTemp, &VenEfi, &osbootoptionsize );
//
// Now adjust the boot order
//
i=BootVarNum;
while (i > 0) {
tmpBootOrder[i] = tmpBootOrder[i-1];
i--;
}
tmpBootOrder[0] = savBootOption;
//
// Set the changed boot order
//
SetVariable(
L"BootOrder",
&VenEfi,
EFI_ATTR,
BootOrderCount * sizeof(CHAR16),
BootOrder
);
return TRUE;
}
return FALSE;
}
VOID
FreeBootManagerVars(
)
{
UINTN i;
for ( i=0; i<BootOrderCount; i++ ) {
if ( LoadOptions[i] )
FreePool( LoadOptions[i] );
}
if ( BootOrder )
FreePool( BootOrder );
//
// zero the local memory for the load options
//
ZeroMem( LoadOptions, sizeof(VOID*) * MAXBOOTVARS );
ZeroMem( LoadOptionsSize, sizeof(UINT64) * MAXBOOTVARS );
}
BOOLEAN
CopyVar(
IN UINT32 VarNum
)
{
CHAR16 i;
if ( VarNum < BootOrderCount ) {
LoadOptions[BootOrderCount] = AllocateZeroPool( LoadOptionsSize[VarNum] );
if ( LoadOptions[BootOrderCount] && LoadOptions[VarNum] ) {
CopyMem( LoadOptions[BootOrderCount], LoadOptions[VarNum], LoadOptionsSize[VarNum] );
LoadOptionsSize[BootOrderCount] = LoadOptionsSize[VarNum];
BootOrder = ReallocatePool(
(VOID*) BootOrder,
BootOrderCount * sizeof(CHAR16),
( BootOrderCount + 1 ) * sizeof(CHAR16)
);
((CHAR16*) BootOrder)[BootOrderCount] = FindFreeBootOption();
BootOrderCount++;
OsBootOptionCount++;
return TRUE;
} else
return FALSE;
}
return FALSE;
}
CHAR16
FindFreeBootOption(
)
{
CHAR16 i;
CHAR16 BootOptionBitmap[MAXBOOTVARS];
SetMem( BootOptionBitmap, sizeof(BootOptionBitmap), 0 );
for ( i=0; i<BootOrderCount; i++ ) {
BootOptionBitmap[ ((CHAR16*)BootOrder)[i] ] = 1;
}
for ( i=0; i < MAXBOOTVARS; i++ ) {
if ( BootOptionBitmap[i] == 0 )
return i;
}
return 0xFFFF;
}
BOOLEAN
SetBootManagerVar(
UINTN BootVarNum
)
{
CHAR16 szTemp[50];
BOOLEAN status;
status = TRUE;
SPrint( szTemp, sizeof(szTemp), L"Boot%04x", ((CHAR16*) BootOrder)[BootVarNum] );
if (LoadOptions[BootVarNum]) {
SetVariable(
szTemp,
&VenEfi,
EFI_ATTR,
LoadOptionsSize[BootVarNum],
LoadOptions[BootVarNum]
);
SetVariable(
L"BootOrder",
&VenEfi,
EFI_ATTR,
BootOrderCount * sizeof(CHAR16),
BootOrder
);
}
else
{
status = FALSE;
}
return status;
}
VOID
SetBootManagerVars(
)
{
UINTN BootVarNum;
BOOLEAN status;
for ( BootVarNum = 0; BootVarNum < BootOrderCount; BootVarNum++ ) {
status = SetBootManagerVar(BootVarNum);
if (status == FALSE) {
Print (L"ERROR: Attempt to write non-existent boot option to NVRAM!\n");
}
}
}
UINT64
GetBootOrderCount(
)
{
return BootOrderCount;
}
UINT64
GetOsBootOptionsCount(
)
{
return OsBootOptionCount;
}
VOID
SetEnvVar(
IN CHAR16* szVarName,
IN CHAR16* szVarValue,
IN UINT32 deleteOnly
)
/*
deleteOnly
TRUE - Env var szVarName is deleted from nvr.
FALSE - Env var szVarName overwrites or creates
*/
{
EFI_STATUS status;
//
// Erase the previous value
//
SetVariable(
szVarName,
&VenEfi,
0,
0,
NULL
);
if ( !deleteOnly ) {
//
// Store the new value
//
status = SetVariable(
szVarName,
&VenEfi,
EFI_ATTR,
StrSize( szVarValue ),
szVarValue
);
}
}
VOID
SubString(
IN OUT char* Dest,
IN UINT32 Start,
IN UINT32 End,
IN char* Src
)
{
UINTN i;
UINTN j=0;
for ( i=Start; i<End; i++ ) {
Dest[ j++ ] = Src[ i ];
}
Dest[ j ] = '\0';
}
VOID
InsertString(
IN OUT char* Dest,
IN UINT32 Start,
IN UINT32 End,
IN char* InsertString
)
{
UINT32 i;
UINT32 j=0;
char first[1024];
char last[1024];
SubString( first, 0, Start, Dest );
SubString( last, End, (UINT32) StrLenA(Dest), Dest );
StrCatA( first, InsertString );
StrCatA( first, last );
StrCpyA( Dest, first );
}
VOID
UtoA(
OUT char* c,
IN CHAR16* u
)
{
UINT32 i = 0;
while ( u[i] ) {
c[i] = u[i] & 0xFF;
i++;
}
c[i] = '\0';
}
VOID
AtoU(
OUT CHAR16* u,
IN char* c
)
{
UINT32 i = 0;
while ( c[i] ) {
u[i] = (CHAR16)c[i];
i++;
}
u[i] = (CHAR16)'\0';
}
VOID
SetFieldFromLoadOption(
IN UINT32 BootVarNum,
IN UINT32 FieldType,
IN VOID* Data
)
{
CHAR16 LoadIdentifier[200];
char OsLoadOptions[200];
char EfiFilePath[1024];
char OsLoadPath[1024];
//
// Make sure it is a valid OS load option
//
if (BootVarNum >= BootOrderCount)
return ;
if (LoadOptions[BootVarNum] == NULL)
return;
GetOsLoadOptionVars(
BootVarNum,
LoadIdentifier,
OsLoadOptions,
EfiFilePath,
OsLoadPath
);
//
// Set the field.
//
switch (FieldType) {
case DESCRIPTION:
StrCpy( LoadIdentifier, Data );
break;
case OSLOADOPTIONS:
StrCpy( (CHAR16*)OsLoadOptions, (CHAR16*)Data );
break;
#if 0
case EFIFILEPATHLIST:
SetFilePathFromShort( (EFI_DEVICE_PATH*) EfiFilePath, (CHAR16*) Data );
break;
case OSFILEPATHLIST: {
PFILE_PATH pFilePath;
pFilePath = (FILE_PATH*)OsLoadPath;
SetFilePathFromShort( (EFI_DEVICE_PATH*) pFilePath->FilePath, (CHAR16*) Data );
break;
}
#endif
default:
break;
}
//
// Pack the new parameters into the the current load option
//
PackLoadOption( BootVarNum,
LoadIdentifier,
(CHAR16*)OsLoadOptions,
EfiFilePath,
OsLoadPath
);
//
// save the new load option into NVRAM
//
SetBootManagerVar(BootVarNum);
}
VOID
GetFilePathShort(
EFI_DEVICE_PATH *FilePath,
CHAR16 *FilePathShort
)
{
UINT32 i, j, End;
EFI_DEVICE_PATH *n = FilePath;
//
// Advance to FilePath node.
//
while (( n->Type != END_DEVICE_PATH_TYPE ) &&
( n->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE ) ) {
if (( n->Type == MEDIA_DEVICE_PATH ) &&
( n->SubType == MEDIA_FILEPATH_DP )) {
j = 0;
End = DevicePathNodeLength(n);
for ( i=sizeof(EFI_DEVICE_PATH); i<End; i++ ) {
((char*) FilePathShort)[j++] = ( (char*) n)[i];
}
break;
}
n = NextDevicePathNode(n);
}
}
VOID
SetFilePathFromShort(
EFI_DEVICE_PATH *FilePath,
CHAR16* FilePathShort
)
{
UINT32 i, j, End;
EFI_DEVICE_PATH *n = FilePath;
UINT64 DevicePathSize;
//
// Advance to FilePath node.
//
while (( n->Type != END_DEVICE_PATH_TYPE ) &&
( n->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE ) ) {
if (( n->Type == MEDIA_DEVICE_PATH ) &&
( n->SubType == MEDIA_FILEPATH_DP )) {
j = 0;
End = DevicePathNodeLength(n);
//
// Set the new file path
//
DevicePathSize = GetDevPathSize(n);
for ( i=sizeof(EFI_DEVICE_PATH); i<DevicePathSize; i++ ) {
((char*) n)[i] = 0;
}
j=sizeof(EFI_DEVICE_PATH);
for ( i=0; i<StrSize(FilePathShort); i++ ) {
((char*)n)[j++] = ((char*)FilePathShort)[i];
}
SetDevicePathNodeLength( n, StrSize(FilePathShort) + sizeof(EFI_DEVICE_PATH) );
n = NextDevicePathNode(n);
SetDevicePathEndNode(n);
break;
}
n = NextDevicePathNode(n);
}
}
char*
GetAlignedELOFilePath(
char* elo
)
{
UINTN abufSize;
char* abuf;
PEFI_LOAD_OPTION pElo;
pElo = (EFI_LOAD_OPTION*)elo;
abufSize = pElo->FilePathListLength;
abuf = AllocatePool(abufSize);
CopyMem(abuf,
elo +
FIELD_OFFSET(EFI_LOAD_OPTION, Description) +
StrSize(pElo->Description),
abufSize
);
return abuf;
}
char*
GetAlignedOptionalData(
char* elo,
UINT64 eloSize,
UINT64* dataSize
)
{
UINTN abufSize;
char* abuf;
PEFI_LOAD_OPTION pElo;
UINTN offset;
pElo = (EFI_LOAD_OPTION*)elo;
offset = FIELD_OFFSET(EFI_LOAD_OPTION, Description) +
StrSize(pElo->Description) +
pElo->FilePathListLength;
abufSize = eloSize - offset;
abuf = AllocatePool(abufSize);
CopyMem(abuf,
elo + offset,
abufSize
);
*dataSize = abufSize;
return abuf;
}
char*
GetAlignedOsOptions(
char* elo,
UINT64 eloSize
)
{
UINT64 dummy;
char* abuf;
abuf = GetAlignedOptionalData(elo,
eloSize,
&dummy
);
return abuf;
}
char*
GetAlignedOsLoadPath(
IN char* osOptions,
OUT UINTN* osLoadPathSize
)
//
// we need to align the FilePath structure because the load options are
// variable in length, so the FilePath structure may not be aligned
//
{
UINTN abufSize;
char* abuf;
PWINDOWS_OS_OPTIONS pOsOptions;
pOsOptions = (WINDOWS_OS_OPTIONS*)osOptions;
abufSize = pOsOptions->Length -
FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions) -
StrSize(pOsOptions->OsLoadOptions);
abuf = AllocatePool(abufSize);
CopyMem(abuf,
&osOptions[pOsOptions->OsLoadPathOffset],
abufSize
);
*osLoadPathSize = abufSize;
return abuf;
}
VOID
DisplayLoadPath(
char* osLoadPath
)
{
PFILE_PATH pFilePath;
pFilePath = (FILE_PATH*)osLoadPath;
Print (L"osOptions->FILE_PATH->Version = %x\n", pFilePath->Version);
Print (L"osOptions->FILE_PATH->Length = %x\n", pFilePath->Length);
Print (L"osOptions->FILE_PATH->Type = %x\n", pFilePath->Type);
if (pFilePath->Type == FILE_PATH_TYPE_EFI) {
CHAR16 FilePathShort[200];
GetFilePathShort(
(EFI_DEVICE_PATH *)pFilePath->FilePath,
FilePathShort
);
Print (L"osOptions->FILE_PATH->FilePath(EFI:DP:Short) = %s\n", FilePathShort);
}
}
VOID
DisplayOsOptions(
char* osOptions
)
{
PWINDOWS_OS_OPTIONS pOsOptions;
CHAR16 wideSig[256];
char* aOsLoadPath;
UINTN aOsLoadPathSize;
pOsOptions = (WINDOWS_OS_OPTIONS*)osOptions;
Print (L">>>>\n");
//
// display the attributes
//
AtoU(wideSig, pOsOptions->Signature);
Print (L"osOptions->Signature = %s\n", wideSig);
Print (L"osOptions->Version = %x\n", pOsOptions->Version);
Print (L"osOptions->Length = %x\n", pOsOptions->Length);
Print (L"osOptions->OsLoadPathOffset = %x\n", pOsOptions->OsLoadPathOffset);
// display the os load options
Print (L"osOptions->OsLoadOptions = %s\n", pOsOptions->OsLoadOptions);
//
// display the FILE PATH
//
//
// we need to align the FilePath structure because the load options are
// variable in length, so the FilePath structure may not be aligned
//
aOsLoadPath = GetAlignedOsLoadPath(osOptions, &aOsLoadPathSize);
DisplayLoadPath(aOsLoadPath);
FreePool(aOsLoadPath);
Print (L"<<<<\n");
}
VOID
DisplayELO(
char* elo,
UINT64 eloSize
)
{
PEFI_LOAD_OPTION pElo;
#if 0
UINT64 eloSize;
#endif
CHAR16 FilePathShort[200];
char* aOsOptions;
pElo = (EFI_LOAD_OPTION*)elo;
Print (L"elo->Attributes = %x\n", pElo->Attributes);
Print (L"elo->FilePathListLength = %x\n", pElo->FilePathListLength);
Print (L"elo->Description = %s\n", pElo->Description);
GetFilePathShort(
(EFI_DEVICE_PATH *)&elo[FIELD_OFFSET(EFI_LOAD_OPTION, Description) + StrSize(pElo->Description)],
FilePathShort
);
Print (L"elo->FilePath(EFI:DP:SHORT) = %s\n", FilePathShort);
#if 0
eloSize = FIELD_OFFSET(EFI_LOAD_OPTION, Description) + StrSize(pElo->Description) + pElo->FilePathListLength;
DisplayOsOptions(&elo[eloSize]);
#else
aOsOptions = GetAlignedOsOptions(
elo,
eloSize
);
DisplayOsOptions(aOsOptions);
FreePool(aOsOptions);
#endif
}
VOID
BuildNewOsOptions(
IN CHAR16* osLoadOptions,
IN char* osLoadPath,
OUT char** osOptions
)
//
//
// Note: osLoadPath must be aligned
//
{
char* newOsOptions;
PWINDOWS_OS_OPTIONS pNewOsOptions;
UINT32 osLoadOptionsLength;
UINT32 osOptionsLength;
PFILE_PATH pOsLoadPath;
//
// NOTE: aligning the FILE_PATH structure (osLoadPath) works
// by aligning the osLoadOptionsLength because the
// WINDOWS_OS_OPTIONS structure has a UINT32 variable
// before the OsLoadOptions. If anything changes above
// the OsLoadOptions in the WINDOWS_OS_OPTIONS structure
// the alignment method may have to change in this structure.
//
//
//
// determine the size of the os load options (UNICODE) string
//
osLoadOptionsLength = (UINT32)StrSize(osLoadOptions);
osLoadOptionsLength = ALIGN_UP(osLoadOptionsLength, UINT32);
#if DEBUG_PACK
Print (L"osLoadOptionsLength = %x\n", osLoadOptionsLength);
#endif
pOsLoadPath = (FILE_PATH*)osLoadPath;
#if DEBUG_PACK
Print (L"pOsLoadPath->Length = %x\n", pOsLoadPath->Length);
#endif
//
// determine the size of the new WINDOWS_OS_OPTIONS structure
//
osOptionsLength = FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions) + osLoadOptionsLength + pOsLoadPath->Length;
#if DEBUG_PACK
Print (L"osOptionsLength = %x\n", osOptionsLength);
#endif
//
// Allocate memory for the WINDOWS_OS_OPTIONS
//
newOsOptions = AllocatePool(osOptionsLength);
ASSERT(newOsOptions != NULL);
pNewOsOptions = (WINDOWS_OS_OPTIONS*)newOsOptions;
//
// populate the new os options
//
StrCpyA((char *)pNewOsOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE);
pNewOsOptions->Version = WINDOWS_OS_OPTIONS_VERSION;
pNewOsOptions->Length = (UINT32)osOptionsLength;
pNewOsOptions->OsLoadPathOffset = FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions) + osLoadOptionsLength;
StrCpy(pNewOsOptions->OsLoadOptions, osLoadOptions);
CopyMem( &newOsOptions[pNewOsOptions->OsLoadPathOffset], osLoadPath, pOsLoadPath->Length );
*osOptions = newOsOptions;
}
VOID
PackLoadOption(
IN UINT32 BootVarNum,
IN CHAR16* LoadIdentifier,
IN CHAR16* OsLoadOptions,
IN char* EfiFilePath,
IN char* OsLoadPath
)
/*
PackLoadOption
Purpose: To construct an EFI_LOAD_OPTION structure using user arguments
and load the structure into into BootXXXX, where XXXX = BootVarNum.
See EFI spec, ch. 17
Args:
BootVarNum The boot option being written/modified
*/
{
PEFI_LOAD_OPTION pOldElo;
PEFI_LOAD_OPTION pElo;
char* elo;
char* oldElo;
UINT64 oldEloSize;
UINT64 eloSize;
UINT8* oldEloFilePath;
UINT64 TempEfiFilePathListSize;
char* aFilePath;
#if DEBUG_PACK
CHAR16 szInput[1024];
Print (L"BootVarNum = %x\n", BootVarNum);
Print (L"LoadIdentifier = %s\n", LoadIdentifier);
Print (L"OsLoadOptions = %s\n", OsLoadOptions);
Input (L"Here! [Pack begin] \n", szInput, sizeof(szInput));
Print(L"\n");
#endif
oldElo = LoadOptions[BootVarNum];
oldEloSize = LoadOptionsSize[BootVarNum];
#if DEBUG_PACK
DisplayELO(oldElo, oldEloSize);
Input (L"Here! [Pack begin] \n", szInput, sizeof(szInput));
Print(L"\n");
#endif
//
// allocate the elo structure with maximal amount of memory allowed for
// an EFI_LOAD_OPTION
//
elo = AllocatePool(MAXBOOTVARSIZE);
if (elo == NULL) {
Print (L"PackAndWriteToNvr: AllocatePool\n");
return;
}
pElo = (EFI_LOAD_OPTION*)elo;
pOldElo = (EFI_LOAD_OPTION*)oldElo;
//
// Efi Attribute.
//
pElo->Attributes = pOldElo->Attributes;
eloSize = sizeof(UINT32);
//
// FilePathListLength
//
pElo->FilePathListLength = pOldElo->FilePathListLength;
eloSize += sizeof(UINT16);
//
// Description.
//
StrCpy( pElo->Description, LoadIdentifier );
eloSize += StrSize(LoadIdentifier);
//
// copy the FilePath from the old/existing ELO structure
//
// Note: we don't actually need an aligned filepath block for this
// copy, but there may come a time when we want to modify
// the filepath, which will require an aligned block.
//
aFilePath = GetAlignedELOFilePath(oldElo);
CopyMem( &elo[eloSize],
aFilePath,
pOldElo->FilePathListLength
);
eloSize += pOldElo->FilePathListLength;
FreePool(aFilePath);
#if DEBUG_PACK
Print (L"eloSize = %x\n", eloSize);
Input (L"Here! \n", szInput, sizeof(szInput));
Print(L"\n");
#endif
//
// add or modify the boot option
//
if ( BootVarNum == -1 ) {
Print(L"Adding currently disabled\n");
} else {
char* osOptions;
char* aOsLoadPath;
char* aOldOsOptions;
PWINDOWS_OS_OPTIONS pOldOsOptions;
PWINDOWS_OS_OPTIONS pOsOptions;
UINTN aOsLoadPathSize;
//
// OptionalData.
//
// For a Windows OS boot option, the OptionalData field in the EFI_LOAD_OPTION
// structure is a WINDOWS_OS_OPTION structure.
//
// get the WINDOWS_OS_OPTIONS from the old/existing boot entry
//
aOldOsOptions = GetAlignedOsOptions(oldElo, oldEloSize);
pOldOsOptions = (WINDOWS_OS_OPTIONS*)aOldOsOptions;
//
// Get the LoadPath from the old/existing WINDOWS_OS_OPTIONS structure
//
// we need to align the FilePath structure because the load options are
// variable in length, so the FilePath structure may not be aligned
//
aOsLoadPath = GetAlignedOsLoadPath(aOldOsOptions, &aOsLoadPathSize);
FreePool(aOldOsOptions);
//
// Construct a new WINDOWS_OS_STRUCTURE with the new values
//
BuildNewOsOptions(
OsLoadOptions,
aOsLoadPath,
&osOptions
);
FreePool(aOsLoadPath);
#if DEBUG_PACK
Input (L"build\n", szInput, sizeof(szInput) );
Print(L"\n");
DisplayOsOptions(osOptions);
Input (L"elo freed\n", szInput, sizeof(szInput) );
Print(L"\n");
#endif
pOsOptions = (WINDOWS_OS_OPTIONS*)osOptions;
//
// Copy the new WINDOWS_OS_OPTIONS structure into the new EFI_LOAD_OPTION structure
//
CopyMem( &elo[eloSize], osOptions, pOsOptions->Length);
eloSize += pOsOptions->Length;
#if DEBUG_PACK
Print (L"osOptions->Length = %x\n", pOsOptions->Length);
Print (L"eloSize = %x\n", eloSize);
#endif
FreePool(osOptions);
//
// Modify current boot options.
//
LoadOptions[BootVarNum] = ReallocatePool( LoadOptions[BootVarNum], LoadOptionsSize[BootVarNum], eloSize );
LoadOptionsSize[BootVarNum] = eloSize;
CopyMem( LoadOptions[BootVarNum], elo, eloSize );
}
FreePool(elo);
ASSERT(eloSize < MAXBOOTVARSIZE);
#if DEBUG_PACK
Input (L"elo freed\n", szInput, sizeof(szInput) );
Print(L"\n");
Print (L">>\n");
DisplayELO((char*)LoadOptions[BootVarNum], LoadOptionsSize[BootVarNum]);
Print (L"<<\n");
Input (L"pack done\n", szInput, sizeof(szInput) );
Print(L"\n");
#endif
}
EFI_STATUS
AppendEntryToBootOrder(
UINT16 BootNumber
)
{
EFI_STATUS status;
UINT64 oldBootOrderSize;
UINT64 newBootOrderSize;
VOID* newBootOrder;
VOID* oldBootOrder;
newBootOrder = NULL;
oldBootOrder = NULL;
//
// get the existing boot order array
//
oldBootOrder = LibGetVariableAndSize( L"BootOrder", &VenEfi, &oldBootOrderSize );
if ((!oldBootOrder) &&
(oldBootOrderSize != 0)
) {
Print(L"\nError: Failed to get old boot order array.\n");
status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// allocate the new boot order array
//
newBootOrderSize = oldBootOrderSize + sizeof(BootNumber);
newBootOrder = AllocatePool( newBootOrderSize );
if (! newBootOrder) {
Print(L"\nError: Failed to allocate new boot order array.\n");
status = EFI_OUT_OF_RESOURCES;
goto Done;
}
//
// append the new boot entry to the bottom of the list
//
CopyMem(
(CHAR8*)newBootOrder,
oldBootOrder,
oldBootOrderSize
);
CopyMem(
(CHAR8*)newBootOrder + oldBootOrderSize,
&BootNumber,
sizeof(BootNumber) );
status = SetVariable(
L"BootOrder",
&VenEfi,
EFI_ATTR,
newBootOrderSize,
newBootOrder
);
Done:
if (oldBootOrder) {
FreePool( oldBootOrder );
}
if (newBootOrder) {
FreePool(newBootOrder);
}
return status;
}
EFI_STATUS
WritePackedDataToNvr(
UINT16 BootNumber,
VOID *BootOption,
UINT32 BootSize
)
{
EFI_STATUS status;
CHAR16 VariableName[10];
SPrint( VariableName, sizeof(VariableName), L"Boot%04x", BootNumber );
status = SetVariable(
VariableName,
&VenEfi,
EFI_ATTR,
BootSize,
BootOption
);
if (status == EFI_SUCCESS) {
status = AppendEntryToBootOrder(BootNumber);
if (status != EFI_SUCCESS) {
Print(L"\nError: Failed to append new boot entry to boot order array\n");
goto Done;
}
} else {
Print(L"\nError: Failed to set new boot entry variable\n");
goto Done;
}
//
// repopulate the local info about boot entries
//
FreeBootManagerVars();
GetBootManagerVars();
Done:
return status;
}
#if DEBUG_PACK
VOID
DisplayELOFromLoadOption(
IN UINT32 OptionNum
)
{
char* elo;
PEFI_LOAD_OPTION pElo;
//
// Make sure it is a valid OS load option
//
if (OptionNum > BootOrderCount) {
return;
}
if (LoadOptions[OptionNum] == NULL) {
return;
}
pElo = (EFI_LOAD_OPTION*)LoadOptions[OptionNum];
elo = (char*)LoadOptions[OptionNum];
DisplayELO(elo, LoadOptionsSize[OptionNum]);
}
#endif
VOID
GetFieldFromLoadOption(
IN UINT32 OptionNum,
IN UINT32 FieldType,
OUT VOID* Data,
OUT UINT64* DataSize
)
{
char* elo;
PEFI_LOAD_OPTION pElo;
//
// Make sure it is a valid OS load option
//
if (OptionNum > BootOrderCount) {
return;
}
if (LoadOptions[OptionNum] == NULL) {
*DataSize = 0;
return;
}
pElo = (EFI_LOAD_OPTION*)LoadOptions[OptionNum];
elo = (char*)LoadOptions[OptionNum];
switch ( FieldType ) {
case ATTRIBUTE: {
*((UINT32*) Data) = pElo->Attributes;
*DataSize = sizeof(UINT32);
break;
}
case FILEPATHLISTLENGTH: {
*((UINT16*) Data) = pElo->FilePathListLength;
*DataSize = sizeof(UINT16);
break;
}
case DESCRIPTION: {
StrCpy((CHAR16*)Data, pElo->Description);
*DataSize = StrSize(pElo->Description);
break;
}
case EFIFILEPATHLIST: {
char* aFilePath;
aFilePath = GetAlignedELOFilePath(elo);
CopyMem(Data,
aFilePath,
pElo->FilePathListLength
);
FreePool(aFilePath);
*DataSize = pElo->FilePathListLength;
break;
}
case OPTIONALDATA: {
char* aOptionalData;
UINT64 eloSize;
eloSize = LoadOptionsSize[OptionNum];
aOptionalData = GetAlignedOptionalData(elo,
eloSize,
DataSize
);
CopyMem(Data, aOptionalData, *DataSize);
FreePool(aOptionalData);
break;
}
default:
*DataSize = 0;
break;
}
}
BOOLEAN
GetLoadIdentifier(
IN UINT32 BootVarNum,
OUT CHAR16* LoadIdentifier
)
{
UINT64 DataSize = 0;
GetFieldFromLoadOption(
BootVarNum,
DESCRIPTION,
LoadIdentifier,
&DataSize
);
if (!DataSize)
return FALSE;
return TRUE;
}
VOID
GetEfiOsLoaderFilePath(
IN UINT32 BootVarNum,
OUT char* FilePath
)
{
UINT64 DataSize = 0;
GetFieldFromLoadOption(
BootVarNum,
EFIFILEPATHLIST,
FilePath,
&DataSize
);
}
BOOLEAN
GetOsLoadOptionVars(
IN UINT32 BootVarNum,
OUT CHAR16* LoadIdentifier,
OUT char* OsLoadOptions,
OUT char* EfiFilePath,
OUT char* OsLoadPath
)
{
if (BootVarNum >= BootOrderCount)
return FALSE;
if (!LoadOptions[BootVarNum])
return FALSE;
GetLoadIdentifier( BootVarNum, LoadIdentifier );
GetOptionalDataValue( BootVarNum, OSLOADOPTIONS, OsLoadOptions );
GetEfiOsLoaderFilePath( BootVarNum, EfiFilePath );
GetOptionalDataValue( BootVarNum, OSLOADPATH, OsLoadPath);
return TRUE;
}
VOID
GetOptionalDataValue(
IN UINT32 BootVarNum,
IN UINT32 Selection,
OUT char* OptionalDataValue
)
{
char osOptions[MAXBOOTVARSIZE];
UINT64 osOptionsSize;
PWINDOWS_OS_OPTIONS pOsOptions;
if (BootVarNum < MAXBOOTVARS) {
GetFieldFromLoadOption(
BootVarNum,
OPTIONALDATA,
osOptions,
&osOptionsSize
);
pOsOptions = (PWINDOWS_OS_OPTIONS)osOptions;
switch (Selection) {
case OSLOADOPTIONS: {
StrCpy( (CHAR16*)OptionalDataValue, pOsOptions->OsLoadOptions );
break;
}
case OSLOADPATH: {
char* aOsLoadPath;
UINTN aOsLoadPathSize;
aOsLoadPath = GetAlignedOsLoadPath(osOptions, &aOsLoadPathSize);
CopyMem(OptionalDataValue,
aOsLoadPath,
aOsLoadPathSize
);
FreePool(aOsLoadPath);
break;
}
default: {
break;
}
}
}
}
UINTN
GetDevPathSize(
IN EFI_DEVICE_PATH *DevPath
)
{
EFI_DEVICE_PATH *Start;
//
// Search for the end of the device path structure
//
Start = DevPath;
while (DevPath->Type != END_DEVICE_PATH_TYPE) {
DevPath = NextDevicePathNode(DevPath);
}
//
// Compute the size
//
return(UINTN) ((UINT64) DevPath - (UINT64) Start);
}
UINT32
GetPartitions(
)
{
EFI_HANDLE EspHandles[100],FSPath;
UINT64 HandleArraySize = 100 * sizeof(EFI_HANDLE);
UINT64 CachedDevicePaths[100];
UINTN i, j;
UINTN CachedDevicePathsCount;
UINT64 SystemPartitionPathSize;
EFI_DEVICE_PATH *dp;
EFI_STATUS Status;
UINT32 PartitionCount;
char AlignedNode[1024];
//
// Get all handles that supports the block I/O protocol.
//
ZeroMem( EspHandles, HandleArraySize );
Status = LocateHandle (
ByProtocol,
&EfiESPProtocol,
0,
(UINTN *) &HandleArraySize,
EspHandles
);
//
// Cache all of the EFI Device Paths.
//
for (i = 0; EspHandles[i] != 0; i++) {
Status = HandleProtocol (
EspHandles[i],
&DevicePathProtocol,
&( (EFI_DEVICE_PATH *) CachedDevicePaths[i] )
);
}
//
// Save the number of cached Device Paths.
//
CachedDevicePathsCount = i;
PartitionCount = 0;
//
// Find the first partition on the first hard drive
// partition. That is our SystemPartition.
//
for ( i=0; i<CachedDevicePathsCount; i++ ) {
dp = (EFI_DEVICE_PATH*) CachedDevicePaths[i];
while (( DevicePathType(dp) != END_DEVICE_PATH_TYPE ) &&
( DevicePathSubType(dp) != END_ENTIRE_DEVICE_PATH_SUBTYPE )) {
if (( DevicePathType(dp) == MEDIA_DEVICE_PATH ) &&
( DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP )) {
CopyMem( AlignedNode, dp, DevicePathNodeLength(dp) );
HandleProtocol (EspHandles[i],&FileSystemProtocol,&FSPath);
if ( FSPath != NULL) {
PartitionCount++;
}
}
dp = NextDevicePathNode(dp);
}
}
return PartitionCount;
}
EFI_HANDLE
GetDeviceHandleForPartition(
)
{
EFI_HANDLE EspHandles[100],FSPath;
UINT64 HandleArraySize = 100 * sizeof(EFI_HANDLE);
UINT64 CachedDevicePaths[100];
UINTN i, j;
UINTN CachedDevicePathsCount;
UINT64 SystemPartitionPathSize;
EFI_DEVICE_PATH *dp;
EFI_STATUS Status;
char AlignedNode[1024];
//
// Get all handles that supports the block I/O protocol.
//
ZeroMem( EspHandles, HandleArraySize );
Status = LocateHandle (
ByProtocol,
&EfiESPProtocol,
0,
(UINTN *) &HandleArraySize,
EspHandles
);
//
// Cache all of the EFI Device Paths.
//
for (i = 0; EspHandles[i] != 0; i++) {
Status = HandleProtocol (
EspHandles[i],
&DevicePathProtocol,
&( (EFI_DEVICE_PATH *) CachedDevicePaths[i] )
);
}
//
// Save the number of cached Device Paths.
//
CachedDevicePathsCount = i;
//
// Find the first ESP partition on the first hard drive
// partition. That is our SystemPartition.
//
for ( i=0; i<CachedDevicePathsCount; i++ ) {
dp = (EFI_DEVICE_PATH*) CachedDevicePaths[i];
while (( DevicePathType(dp) != END_DEVICE_PATH_TYPE ) &&
( DevicePathSubType(dp) != END_ENTIRE_DEVICE_PATH_SUBTYPE )) {
if (( DevicePathType(dp) == MEDIA_DEVICE_PATH ) &&
( DevicePathSubType(dp) == MEDIA_HARDDRIVE_DP )) {
CopyMem( AlignedNode, dp, DevicePathNodeLength(dp) );
HandleProtocol (EspHandles[i],&FileSystemProtocol,&FSPath);
if ( FSPath != NULL) {
//
// Found the correct device path partition.
// Return the device handle.
//
return( EspHandles[i] );
}
}
dp = NextDevicePathNode(dp);
}
}
return NULL;
}
/*
** BUGBUG: These functions need to be eventually placed in lib\str.c
*/
INTN
RUNTIMEFUNCTION
StrCmpA (
IN CHAR8 *s1,
IN CHAR8 *s2
)
/* compare strings */
{
while (*s1) {
if (*s1 != *s2) {
break;
}
s1 += 1;
s2 += 1;
}
return *s1 - *s2;
}
VOID
RUNTIMEFUNCTION
StrCpyA (
IN CHAR8 *Dest,
IN CHAR8 *Src
)
/* copy strings */
{
while (*Src) {
*(Dest++) = *(Src++);
}
*Dest = 0;
}
VOID
RUNTIMEFUNCTION
StrCatA (
IN CHAR8 *Dest,
IN CHAR8 *Src
)
{
StrCpyA(Dest+StrLenA(Dest), Src);
}
UINTN
RUNTIMEFUNCTION
StrLenA (
IN CHAR8 *s1
)
/* string length */
{
UINTN len;
for (len=0; *s1; s1+=1, len+=1) ;
return len;
}
UINTN
RUNTIMEFUNCTION
StrSizeA (
IN CHAR8 *s1
)
/* string size */
{
UINTN len;
for (len=0; *s1; s1+=1, len+=1) ;
return(len + 1) * sizeof(CHAR8);
}