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.
681 lines
15 KiB
681 lines
15 KiB
#include <efi.h>
|
|
#include <efilib.h>
|
|
|
|
#define MAX_ENV_SIZE 1024
|
|
|
|
#define MAXUSHORT (0xFFFF)
|
|
|
|
//
|
|
// Globals for stdout
|
|
//
|
|
SIMPLE_TEXT_OUTPUT_INTERFACE *ConOut;
|
|
SIMPLE_INPUT_INTERFACE *ConIn;
|
|
|
|
//
|
|
// Globals for protocol handler
|
|
//
|
|
EFI_HANDLE_PROTOCOL HandleProtocol;
|
|
EFI_LOCATE_HANDLE LocateHandle;
|
|
EFI_LOCATE_DEVICE_PATH LocateDevicePath;
|
|
EFI_IMAGE_LOAD LoadImage;
|
|
EFI_IMAGE_START StartImage;
|
|
EFI_SET_VARIABLE SetVariable;
|
|
EFI_HANDLE MenuImageHandle;
|
|
EFI_LOADED_IMAGE *ExeImage;
|
|
|
|
//
|
|
// globals for managing boot entries
|
|
//
|
|
UINT32 NvrAttributes;
|
|
UINTN NvrOrderCount;
|
|
UINT16 *NvrOrder;
|
|
|
|
//
|
|
// prototypes
|
|
//
|
|
UINT32 GetInputKey();
|
|
void DisplayKey(UINT32);
|
|
|
|
EFI_STATUS
|
|
OpenCreateFile (
|
|
UINT64 OCFlags,
|
|
EFI_FILE_HANDLE* StartHdl,
|
|
CHAR16* Name
|
|
);
|
|
|
|
EFI_STATUS
|
|
InsertBootOption(
|
|
VOID *BootOption,
|
|
UINT64 BootOptionSize
|
|
);
|
|
|
|
INTN
|
|
ParseNvrFile (
|
|
EFI_FILE_HANDLE NvrFile
|
|
);
|
|
|
|
EFI_STATUS
|
|
FindFreeBootOption(
|
|
CHAR16 *FreeIdx
|
|
);
|
|
|
|
INTN
|
|
RestoreNvr (
|
|
CHAR16* fileName
|
|
);
|
|
|
|
VOID
|
|
InitializeStdOut(
|
|
IN struct _EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
|
|
//
|
|
// Stash some of the efi stdout pointers
|
|
//
|
|
ConOut = SystemTable->ConOut;
|
|
ConIn = SystemTable->ConIn;
|
|
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
void
|
|
InitializeProtocols(
|
|
IN struct _EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
|
|
EFI_BOOT_SERVICES *bootServices;
|
|
EFI_RUNTIME_SERVICES *runtimeServices;
|
|
|
|
//
|
|
// Stash some of the handle protocol pointers
|
|
//
|
|
|
|
bootServices = SystemTable->BootServices;
|
|
|
|
HandleProtocol = bootServices->HandleProtocol;
|
|
LocateHandle = bootServices->LocateHandle;
|
|
LocateDevicePath = bootServices->LocateDevicePath;
|
|
|
|
LoadImage = bootServices->LoadImage;
|
|
StartImage = bootServices->StartImage;
|
|
|
|
//
|
|
// Stash some of the Runtime services pointers
|
|
//
|
|
|
|
runtimeServices = SystemTable->RuntimeServices;
|
|
|
|
SetVariable = runtimeServices->SetVariable;
|
|
|
|
}
|
|
|
|
EFI_STATUS
|
|
Init(
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BufferSize;
|
|
|
|
do {
|
|
|
|
//
|
|
// Initialize EFI routines
|
|
//
|
|
InitializeProtocols( SystemTable );
|
|
InitializeStdOut( SystemTable );
|
|
InitializeLib( ImageHandle, SystemTable );
|
|
|
|
//
|
|
// Save Image Handle
|
|
//
|
|
MenuImageHandle = ImageHandle;
|
|
|
|
BS->HandleProtocol (ImageHandle, &LoadedImageProtocol, &ExeImage);
|
|
|
|
//
|
|
//
|
|
//
|
|
NvrOrder = AllocatePool(MAX_ENV_SIZE + 32);
|
|
|
|
if (! NvrOrder) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// get boot order from nvram
|
|
//
|
|
BufferSize = MAX_ENV_SIZE;
|
|
|
|
Status = RT->GetVariable (
|
|
VarBootOrder,
|
|
&EfiGlobalVariable,
|
|
&NvrAttributes,
|
|
&BufferSize,
|
|
NvrOrder
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"Nvr: failed to load boot order array. defaulting\n");
|
|
BufferSize = 0;
|
|
NvrAttributes = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// get how many boot options there are
|
|
//
|
|
NvrOrderCount = BufferSize / sizeof(UINT16);
|
|
|
|
} while ( FALSE );
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
Shutdown(
|
|
VOID
|
|
)
|
|
{
|
|
if (NvrOrder) {
|
|
FreePool(NvrOrder);
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
EfiMain(
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT32 ch, user, cnt;
|
|
CHAR8 *LangCode;
|
|
|
|
// Initialize the EFI SDX libraries
|
|
InitializeLib( ImageHandle, SystemTable );
|
|
|
|
//
|
|
//
|
|
//
|
|
LangCode = LibGetVariable (VarLanguage, &EfiGlobalVariable);
|
|
|
|
if (LangCode) {
|
|
UINTN i;
|
|
Print(L"LangCode: ");
|
|
for (i = 0; i < ISO_639_2_ENTRY_SIZE; i++) {
|
|
Print(L"%c", LangCode[i]);
|
|
}
|
|
Print(L"\n");
|
|
FreePool(LangCode);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
Status = Init(ImageHandle, SystemTable);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
ch = cnt = 0;
|
|
user = 0;
|
|
|
|
while ( user != (UINT32) 'q' && ch <= (UINT32) 0xFF ) {
|
|
|
|
Print( L"Please press any key to continue (q to quit)\n");
|
|
user = GetInputKey();
|
|
if (user == L'q') {
|
|
break;
|
|
}
|
|
|
|
Print(L"Adding a boot entry\n");
|
|
|
|
RestoreNvr(L"blob.nvr");
|
|
|
|
}
|
|
|
|
//
|
|
// clean up
|
|
//
|
|
Shutdown();
|
|
|
|
// If you return the status, EFI will kindly give the user an English
|
|
// error message.
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
UINT32 GetInputKey()
|
|
{
|
|
EFI_INPUT_KEY pKey;
|
|
EFI_STATUS Status;
|
|
|
|
// Wait until a keystroke is available
|
|
WaitForSingleEvent(
|
|
ST->ConIn->WaitForKey,
|
|
0);
|
|
|
|
// Read the key that has been pressed
|
|
Status = ST->ConIn->ReadKeyStroke(
|
|
ST->ConIn,
|
|
&pKey);
|
|
|
|
if (EFI_ERROR(Status) || pKey.ScanCode != 0) {
|
|
return 0x20; // space
|
|
}
|
|
|
|
return pKey.UnicodeChar;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InsertBootOption(
|
|
VOID *BootOption,
|
|
UINT64 BootOptionSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 OptionStr[40];
|
|
|
|
Print(L"InsertBootOption: enter\n");
|
|
|
|
//
|
|
// attempt to insert boot option
|
|
//
|
|
do {
|
|
|
|
UINT16 Target;
|
|
|
|
//
|
|
//
|
|
//
|
|
Status = FindFreeBootOption(&Target);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print (L"Nvr: failed to find free boot option id: %hr\n", Status);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// update nvram with the new boot option
|
|
//
|
|
|
|
SPrint( OptionStr, sizeof(OptionStr), VarBootOption, Target);
|
|
|
|
Print(L"InsertBootOption: target = %x, OptionStr = %s\n", Target, OptionStr);
|
|
|
|
Status = RT->SetVariable (
|
|
OptionStr,
|
|
&EfiGlobalVariable,
|
|
NvrAttributes,
|
|
BootOptionSize,
|
|
BootOption
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print (L"Nvr: failed to add %hs - %hr\n", OptionStr, Status);
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// replace boot order with one including the new option
|
|
//
|
|
|
|
NvrOrder[NvrOrderCount] = Target;
|
|
|
|
NvrOrderCount++;
|
|
|
|
Status = RT->SetVariable (
|
|
VarBootOrder,
|
|
&EfiGlobalVariable,
|
|
NvrAttributes,
|
|
NvrOrderCount * sizeof(UINT16),
|
|
NvrOrder
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print (L"Nvr: failed to update %hs - %hr\n", VarBootOrder, Status);
|
|
break;
|
|
}
|
|
|
|
#if 1
|
|
|
|
//
|
|
// validate what we just wrote
|
|
//
|
|
{
|
|
UINTN BlobSize;
|
|
CHAR8 *Blob;
|
|
UINT16 i;
|
|
|
|
BlobSize = BootOptionSize;
|
|
Blob = AllocatePool(BootOptionSize);
|
|
|
|
do {
|
|
|
|
Status = RT->GetVariable (
|
|
OptionStr,
|
|
&EfiGlobalVariable,
|
|
NULL,
|
|
&BlobSize,
|
|
Blob
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print (L"Nvr: failed to read comparison blob: %hr\n", Status);
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < BootOptionSize; i++) {
|
|
if (((CHAR8*)BootOption)[i] != Blob[i]) {
|
|
Print(L"Nvr: diff[%d]: BootOption = %d, Blob = %d\n", i, ((CHAR8*)BootOption)[i], Blob[i]);
|
|
}
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
FreePool(Blob);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
} while ( FALSE );
|
|
|
|
Print(L"InsertBootOption: exit\n");
|
|
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
FindFreeBootOption(
|
|
CHAR16 *FreeIdx
|
|
)
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
UINT16 id;
|
|
UINT16 i;
|
|
BOOLEAN Found;
|
|
BOOLEAN HaveFreeIdx;
|
|
|
|
Print(L"FindFreeBootOption: enter\n");
|
|
|
|
HaveFreeIdx = FALSE;
|
|
|
|
*FreeIdx = MAXUSHORT;
|
|
|
|
//
|
|
// use a brute force search to find a new boot option id
|
|
//
|
|
for ( id = 0; id <= MAXUSHORT; id++ ) {
|
|
|
|
Print(L"FindFreeBootOption: id = %x\n", id);
|
|
|
|
Found = FALSE;
|
|
|
|
for (i = 0; i < NvrOrderCount; i++) {
|
|
|
|
if (NvrOrder[i] == id) {
|
|
Found = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (! Found) {
|
|
*FreeIdx = id;
|
|
HaveFreeIdx = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (HaveFreeIdx) {
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Print(L"FindFreeBootOption: FreeIdx = %x, status = %x\n", *FreeIdx, Status);
|
|
Print(L"FindFreeBootOption: exit\n");
|
|
|
|
return Status;
|
|
}
|
|
|
|
INTN
|
|
RestoreNvr (
|
|
CHAR16* fileName
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FILE_HANDLE nvrFile;
|
|
|
|
//
|
|
// Read from saved boot options file
|
|
//
|
|
Status = OpenCreateFile (EFI_FILE_MODE_READ,&nvrFile,fileName);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"\nCan not open the file %s\n",fileName);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// This updates nvram with saved boot options
|
|
//
|
|
return (ParseNvrFile (nvrFile));
|
|
|
|
}
|
|
|
|
EFI_STATUS
|
|
OpenCreateFile (
|
|
UINT64 OCFlags,
|
|
EFI_FILE_HANDLE* StartHdl,
|
|
CHAR16* Name
|
|
)
|
|
{
|
|
EFI_FILE_IO_INTERFACE *Vol;
|
|
EFI_FILE_HANDLE RootFs;
|
|
EFI_FILE_HANDLE CurDir;
|
|
EFI_FILE_HANDLE FileHandle;
|
|
CHAR16 FileName[100],*DevicePathAsString;
|
|
EFI_STATUS Status;
|
|
|
|
Print(L"OpenCreateFile: enter\n");
|
|
|
|
do {
|
|
|
|
//
|
|
// Open the volume for the device where the nvrutil was started.
|
|
//
|
|
Status = BS->HandleProtocol (
|
|
ExeImage->DeviceHandle,
|
|
&FileSystemProtocol,
|
|
&Vol
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"\n");
|
|
Print(L"Can not get a FileSystem handle for %s DeviceHandle\n",ExeImage->FilePath);
|
|
break;
|
|
}
|
|
|
|
Status = Vol->OpenVolume (Vol, &RootFs);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"\n");
|
|
Print(L"Can not open the volume for the file system\n");
|
|
break;
|
|
}
|
|
|
|
CurDir = RootFs;
|
|
|
|
//
|
|
// Open saved boot options file
|
|
//
|
|
FileName[0] = 0;
|
|
|
|
DevicePathAsString = DevicePathToStr(ExeImage->FilePath);
|
|
if (DevicePathAsString!=NULL) {
|
|
StrCpy(FileName,DevicePathAsString);
|
|
FreePool(DevicePathAsString);
|
|
}
|
|
|
|
StrCpy(FileName, L".\\");
|
|
StrCat(FileName,Name);
|
|
|
|
Status = CurDir->Open (CurDir,
|
|
&FileHandle,
|
|
FileName,
|
|
OCFlags,
|
|
0
|
|
);
|
|
|
|
*StartHdl=FileHandle;
|
|
|
|
} while ( FALSE );
|
|
|
|
Print(L"OpenCreateFile: exit\n");
|
|
|
|
return Status;
|
|
}
|
|
|
|
INTN
|
|
ParseNvrFile (
|
|
EFI_FILE_HANDLE NvrFile
|
|
)
|
|
{
|
|
BOOLEAN bSuccess;
|
|
EFI_STATUS Status;
|
|
CHAR8 *buffer;
|
|
UINTN k,size;
|
|
UINT64 BootNumber;
|
|
UINT64 BootSize;
|
|
VOID *BootOption;
|
|
UINTN blockBegin;
|
|
EFI_FILE_INFO *fileInfo;
|
|
|
|
Print(L"ParseNvrFile: enter\n");
|
|
|
|
buffer = NULL;
|
|
|
|
do {
|
|
|
|
//
|
|
//
|
|
//
|
|
size = 0;
|
|
Status = NvrFile->GetInfo(NvrFile,&GenericFileInfo,&size,NULL);
|
|
|
|
if (Status != EFI_BUFFER_TOO_SMALL) {
|
|
break;
|
|
}
|
|
|
|
Print(L"ParseNvrFile: size = %d vs %d\n", size, SIZE_OF_EFI_FILE_INFO+255*sizeof (CHAR16));
|
|
|
|
fileInfo = AllocateZeroPool(size);
|
|
|
|
if (fileInfo == NULL) {
|
|
Print(L"\n");
|
|
Print (L"Failed to allocate memory for File Info buffer!\n");
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
Status = NvrFile->GetInfo(NvrFile,&GenericFileInfo,&size,fileInfo);
|
|
|
|
size=(UINTN) fileInfo->FileSize;
|
|
|
|
FreePool (fileInfo);
|
|
|
|
buffer = AllocateZeroPool ((size+1));
|
|
|
|
if (buffer == NULL) {
|
|
Print(L"\n");
|
|
Print (L"Failed to allocate memory for File buffer!\n");
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
Status = NvrFile->Read(NvrFile,&size,buffer);
|
|
|
|
NvrFile->Close (NvrFile);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Print(L"\n");
|
|
Print (L"Failed to read nvr file!\n");
|
|
break;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
k=0;
|
|
|
|
while(k < size ) {
|
|
|
|
blockBegin = k;
|
|
|
|
CopyMem( &BootNumber, &buffer[k], sizeof(BootNumber));
|
|
k += sizeof(UINT64);
|
|
|
|
CopyMem( &BootSize, &buffer[k], sizeof(BootSize));
|
|
k += sizeof(UINT64);
|
|
|
|
BootOption = (VOID *)((CHAR8*)buffer + k);
|
|
k += BootSize;
|
|
|
|
//
|
|
// sanity check the file position vs. what the
|
|
// file header information tells us. The value
|
|
// k should be <= to size now.
|
|
//
|
|
if (k > size) {
|
|
Print (L"\nThe NVRAM file is corrupted.\n");
|
|
Status = EFI_BAD_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// write the current boot entry at free location
|
|
//
|
|
Status = InsertBootOption(
|
|
BootOption,
|
|
BootSize
|
|
);
|
|
|
|
if (EFI_ERROR(Status)) {
|
|
Print(L"\nError: Failed to append new boot entry to boot order array\n");
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
} while ( FALSE );
|
|
|
|
if (buffer) {
|
|
FreePool (buffer);
|
|
}
|
|
|
|
Print(L"ParseNvrFile: exit\n");
|
|
|
|
return Status;
|
|
|
|
}
|
|
|