CloverBootloader/rEFIt_UEFI/Platform/BootOptions.cpp

1200 lines
34 KiB
C++

/*
* BootOptions.c
*
* Created by dmazar, 10.2012.
*
* Functions for modifying UEFI boot options.
*
*/
#include "Platform.h"
#include "BootOptions.h"
#include "BasicIO.h"
#include "Nvram.h"
#ifndef DEBUG_ALL
#define DEBUG_BO 1
#else
#define DEBUG_BO DEBUG_ALL
#endif
#if DEBUG_BO == 0
#define DBG(...)
#else
#define DBG(...) DebugLog(DEBUG_BO, __VA_ARGS__)
#endif
#define BOOT_ORDER_VAR L"BootOrder"
/** Returns upper case version of char - valid only for ASCII chars in unicode. */
CHAR16
EFIAPI
ToUpperChar(
IN CHAR16 Chr
)
{
CHAR8 C;
if (Chr > 0xFF) return Chr;
C = (CHAR8)Chr;
return ((C >= 'a' && C <= 'z') ? C - ('a' - 'A') : C);
}
/** Returns 0 if two strings are equal, !=0 otherwise. Compares just first 8 bits of chars (valid for ASCII), case insensitive. */
/*
UINTN
EFIAPI
StrCmpiBasic(
IN CHAR16 *String1,
IN CHAR16 *String2
)
{
CHAR16 Chr1;
CHAR16 Chr2;
if (String1 == NULL || String2 == NULL) {
return 1;
}
if (*String1 == L'\0' && *String2 == L'\0') {
return 0;
}
if (*String1 == L'\0' || *String2 == L'\0') {
return 1;
}
Chr1 = ToUpperChar(*String1);
Chr2 = ToUpperChar(*String2);
while ((*String1 != L'\0') && (Chr1 == Chr2)) {
String1++;
String2++;
Chr1 = ToUpperChar(*String1);
Chr2 = ToUpperChar(*String2);
}
return Chr1 - Chr2;
}
*/
/**
* Returns the first occurrence of a Null-terminated Unicode SearchString
* in a Null-terminated Unicode String.
* Compares just first 8 bits of chars (valid for ASCII), case insensitive.
*
* Copied and modified from BaseLib/String.c
*/
/*
CHAR16 *
EFIAPI
StrStriBasic (
IN CONST CHAR16 *String,
IN CONST CHAR16 *SearchString
)
{
CONST CHAR16 *FirstMatch;
CONST CHAR16 *SearchStringTmp;
if (String == NULL || SearchString == NULL) {
return NULL;
}
if (*SearchString == L'\0') {
return (CHAR16 *) String;
}
while (*String != L'\0') {
SearchStringTmp = SearchString;
FirstMatch = String;
while ((ToUpperChar(*String) == ToUpperChar(*SearchStringTmp))
&& (*String != L'\0')) {
String++;
SearchStringTmp++;
}
if (*SearchStringTmp == L'\0') {
return (CHAR16 *) FirstMatch;
}
if (*String == L'\0') {
return NULL;
}
String = FirstMatch + 1;
}
return NULL;
}
*/
/** Finds and returns pointer to specified DevPath node in DevicePath or NULL.
* If SubType == 0 then it is ignored.
*/
EFI_DEVICE_PATH_PROTOCOL *
FindDevicePathNodeWithType (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
IN UINT8 Type,
IN UINT8 SubType OPTIONAL
)
{
while ( !IsDevicePathEnd (DevicePath) ) {
if (DevicePathType (DevicePath) == Type && (SubType == 0 || DevicePathSubType (DevicePath) == SubType)) {
return DevicePath;
}
DevicePath = NextDevicePathNode (DevicePath);
}
//
// Not found
//
return NULL;
}
/** Creates device path for boot option: device path for file system volume + file name.
* If UseShortForm == TRUE, then only the hard drive media dev path will be used instead
* of full device path.
* Long (full) form:
* PciRoot(0x0)/Pci(0x1f,0x2)/Sata(0x1,0x0)/HD(1,GPT,96004846-a018-49ad-bc9f-4e5a340adc4b,0x800,0x64000)/\EFI\BOOT\File.efi
* Short form:
* HD(1,GPT,96004846-a018-49ad-bc9f-4e5a340adc4b,0x800,0x64000)/\EFI\BOOT\File.efi
* Caller is responsible for releasing DevicePath with FreePool().
*/
EFI_STATUS
CreateBootOptionDevicePath (
IN EFI_HANDLE FileDeviceHandle,
IN CONST XStringW& FileName,
IN BOOLEAN UseShortForm,
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
)
{
EFI_STATUS Status;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
EFI_DEVICE_PATH_PROTOCOL *TmpDevPath;
//
// Check that FileDeviceHandle is file system volume
//
Status = gBS->HandleProtocol (FileDeviceHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID**)&Volume);
if (EFI_ERROR(Status)) {
DBG("CreateBootOptionDevicePath: FileDeviceHandle %p is not fs volume", FileDeviceHandle);
return EFI_INVALID_PARAMETER;
}
//
// Create file path node with FileName
//
*DevicePath = FileDevicePath(FileDeviceHandle, FileName);
if (*DevicePath == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Extract only short form if specified
//
if (UseShortForm) {
//
// Find HD Media dev path node and extract only that portion of dev path
//
TmpDevPath = DuplicateDevicePath (FindDevicePathNodeWithType (*DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP));
if (TmpDevPath != NULL) {
FreePool(*DevicePath);
*DevicePath = TmpDevPath;
} /* else {
TmpDevPath = DuplicateDevicePath (FindDevicePathNodeWithType (*DevicePath, HARDWARE_DEVICE_PATH, HW_VENDOR_DP));
if (TmpDevPath != NULL) {
FreePool(*DevicePath);
*DevicePath = TmpDevPath;
}
}*/
}
return EFI_SUCCESS;
}
/** Returns TRUE if dev paths are equal. Has special ascii case insensitive compare for file dev paths. */
BOOLEAN
DevicePathEqual (
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath1,
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath2
)
{
BOOLEAN Equal;
UINT8 Type1;
UINT8 SubType1;
UINTN Len1;
CHAR16 *FPath1;
CHAR16 *FPath2;
Equal = FALSE;
while (TRUE) {
Type1 = DevicePathType (DevicePath1);
SubType1 = DevicePathSubType (DevicePath1);
Len1 = DevicePathNodeLength (DevicePath1);
/*
DBG("Type: %d, %d\n", Type1, DevicePathType (DevicePath2));
DBG("SubType: %d, %d\n", SubType1, DevicePathSubType (DevicePath2));
DBG("Len: %d, %d\n", Len1, DevicePathNodeLength (DevicePath2));
DBG("%ls\n", DevicePathToStr(DevicePath1));
DBG("%ls\n", DevicePathToStr(DevicePath2));
*/
if (Type1 != DevicePathType (DevicePath2)
|| SubType1 != DevicePathSubType (DevicePath2)
|| Len1 != DevicePathNodeLength (DevicePath2)
)
{
// Not equal
//DBG("Not equal type/subtype/len\n");
break;
}
//
// Same type/subtype/len ...
//
if (IsDevicePathEnd (DevicePath1)) {
// END node - they are the same
Equal = TRUE;
break;
}
//
// Do mem compare of nodes or special compare for selected types/subtypes
//
if (Type1 == MEDIA_DEVICE_PATH && SubType1 == MEDIA_FILEPATH_DP) {
//
// Special compare: case insensitive file path compare + skip leading \ char
//
FPath1 = &((FILEPATH_DEVICE_PATH *)DevicePath1)->PathName[0];
if (FPath1[0] == L'\\') {
FPath1++;
}
FPath2 = &((FILEPATH_DEVICE_PATH *)DevicePath2)->PathName[0];
if (FPath2[0] == L'\\') {
FPath2++;
}
//DBG("COMPARING: '%ls' and '%ls'\n", FPath1, FPath2);
if (StriCmp(FPath1, FPath2) != 0) {
// Not equal
//DBG("Not equal fpaths\n");
break;
}
} else {
if (CompareMem(DevicePath1, DevicePath2, DevicePathNodeLength (DevicePath1)) != 0) {
// Not equal
//DBG("Not equal nodes\n");
break;
}
}
//
// Advance to next node
//
DevicePath1 = NextDevicePathNode (DevicePath1);
DevicePath2 = NextDevicePathNode (DevicePath2);
}
return Equal;
}
/** Prints BootOrder with DBG. */
VOID
PrintBootOrder (
IN UINT16 BootOrder[],
IN UINTN BootOrderLen
)
{
UINTN Index;
DBG(" %llu: ", BootOrderLen);
for (Index = 0; Index < BootOrderLen; Index++) {
if (Index > 0) {
DBG(", ");
}
DBG("Boot%04hX", BootOrder[Index]);
}
DBG("\n");
//WaitForKeyPress(L"press a key to continue\n");
}
/** Returns gEfiGlobalVariableGuid:BootOrder as UINT16 array and it's length (num of elements).
* Caller is responsible for releasing BootOrder mem (FreePool()).
*/
EFI_STATUS
GetBootOrder (
OUT UINT16 *BootOrder[],
OUT UINTN *BootOrderLen
)
{
UINTN BootOrderSize;
DBG("BootOrder:");
//
// Basic checks
//
if (BootOrder == NULL || BootOrderLen == NULL) {
DBG(" EFI_INVALID_PARAMETER\n");
return EFI_INVALID_PARAMETER;
}
//
// Get gEfiGlobalVariableGuid:BootOrder and it's length
//
*BootOrder = (__typeof_am__(*BootOrder))GetNvramVariable (BOOT_ORDER_VAR, &gEfiGlobalVariableGuid, NULL, &BootOrderSize);
if (*BootOrder == NULL) {
DBG(" EFI_NOT_FOUND\n");
return EFI_NOT_FOUND;
}
*BootOrderLen = BootOrderSize / sizeof(UINT16);
PrintBootOrder(*BootOrder, *BootOrderLen);
return EFI_SUCCESS;
}
/** Updates BootOrder by adding new boot option BootNumNew at index BootIndexNew. */
EFI_STATUS
AddToBootOrder (
IN UINT16 BootNumNew,
IN UINTN BootIndexNew
)
{
EFI_STATUS Status;
UINT16 *BootOrder = NULL;
UINT16 *BootOrderNew = NULL;
UINTN BootOrderLen = 0;
UINTN Index;
DBG("AddToBootOrder: Boot%04hX at index %llu\n", BootNumNew, BootIndexNew);
Status = GetBootOrder (&BootOrder, &BootOrderLen);
if (EFI_ERROR(Status)) {
return Status;
}
if (BootIndexNew > BootOrderLen) {
BootIndexNew = BootOrderLen;
DBG("AddToBootOrder: Index too big. Setting to: %llu\n", BootIndexNew);
}
//
// Make new order buffer with space for our option
//
BootOrderNew = (__typeof__(BootOrderNew))AllocateZeroPool ((BootOrderLen + 1) * sizeof(UINT16));
if (BootOrderNew == NULL) {
DBG("AddToBootOrder: EFI_OUT_OF_RESOURCES\n");
if (BootOrder) {
FreePool(BootOrder);
}
return EFI_OUT_OF_RESOURCES;
}
BootOrderLen += 1;
//
// Make BootOrderNew array
//
// copy all before BootIndex first
for (Index = 0; Index < BootIndexNew; Index++) {
BootOrderNew[Index] = BootOrder[Index];
}
// then add our new BootNumNew
BootOrderNew[BootIndexNew] = BootNumNew;
// then add the rest of previous indexes
for (Index = BootIndexNew + 1; Index < BootOrderLen; Index++) {
BootOrderNew[Index] = BootOrder[Index - 1];
}
//
// Save it
//
Status = gRT->SetVariable (BOOT_ORDER_VAR,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS,
BootOrderLen * sizeof(UINT16),
BootOrderNew
);
DBG("SetVariable: %ls = %s\n", BOOT_ORDER_VAR, strerror(Status));
PrintBootOrder(BootOrderNew, BootOrderLen);
FreePool(BootOrder);
FreePool(BootOrderNew);
// Debug: Get and print new BootOrder value
//GetBootOrder (&BootOrder, &BootOrderLen);
return Status;
}
/** Updates BootOrder by deleting boot option BootNum. */
EFI_STATUS
DeleteFromBootOrder (
IN UINT16 BootNum
)
{
EFI_STATUS Status;
UINT16 *BootOrder = NULL;
UINTN BootOrderLen = 0;
UINTN Index;
DBG("DeleteFromBootOrder: %04hX\n", BootNum);
Status = GetBootOrder (&BootOrder, &BootOrderLen);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Find BootNum and remove it by sliding others to it's place
//
for (Index = 0; Index < BootOrderLen; Index++) {
if (BootOrder[Index] == BootNum) {
break;
}
}
if (Index >= BootOrderLen) {
DBG("Not found in BootOrder len=%llu\n", BootOrderLen);
FreePool(BootOrder);
return EFI_NOT_FOUND;
}
DBG(" found at index %llu\n", Index);
//
// BootNum found at Index - copy the rest over it
//
if (Index < BootOrderLen - 1) {
CopyMem (&BootOrder[Index],
&BootOrder[Index + 1],
(BootOrderLen - (Index + 1)) * sizeof(UINT16)
);
}
BootOrderLen -= 1;
//
// Save it
//
Status = gRT->SetVariable (BOOT_ORDER_VAR,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS,
BootOrderLen * sizeof(UINT16),
BootOrder
);
DBG("SetVariable: %ls = %s\n", BOOT_ORDER_VAR, strerror(Status));
FreePool(BootOrder);
// Debug: Get and print new BootOrder value
//GetBootOrder (&BootOrder, &BootOrderLen);
if (EFI_ERROR(Status)) {
return Status;
}
return EFI_SUCCESS;
}
/** Prints BootOption with DBG(). */
VOID
PrintBootOption (
IN BO_BOOT_OPTION *BootOption,
IN UINTN Index
)
{
UINTN VarSizeTmp;
CHAR16 *FPStr;
DBG("%2llu) Boot%04hX: %ls, Attr: 0x%X\n",
Index, BootOption->BootNum, BootOption->Description, BootOption->Attributes);
FPStr = FileDevicePathToStr(BootOption->FilePathList);
DBG(" %ls\n", FPStr);
FreePool(FPStr);
VarSizeTmp = sizeof(BootOption->Attributes)
+ sizeof(BootOption->FilePathListLength)
+ BootOption->DescriptionSize
+ BootOption->FilePathListLength
+ BootOption->OptionalDataSize;
DBG(" Size: %llu (Attr:%lu + FPl:%lu + Desc:%llu + FP:%d + Opt:%llu = %llu -> %ls)\n",
BootOption->VariableSize,
sizeof(BootOption->Attributes),
sizeof(BootOption->FilePathListLength),
BootOption->DescriptionSize,
BootOption->FilePathListLength,
BootOption->OptionalDataSize,
VarSizeTmp,
VarSizeTmp == BootOption->VariableSize ? L"OK" : L"ERROR"
);
//DBG(" FilePathList: %p, %d", BootOption->FilePathList, BootOption->FilePathListLength);
//DBG(" FP F: %ls\n", FileDevicePathToStr(BootOption->FilePathList));
//DBG(" Description: %p, %d, %ls\n", BootOption->Description, BootOption->DescriptionSize, BootOption->Description);
//DBG("OptionalData: %p, %d\n", BootOption->OptionalData, BootOption->OptionalDataSize);
}
/** Parses BootXXXX (XXXX = BootNum) var (from BootOption->Variable) and returns it in BootOption.
* BootOption->Variable, BootOption->VariableSize and BootOption->BootNum must be set.
*/
EFI_STATUS
ParseBootOption (
OUT BO_BOOT_OPTION *BootOption
)
{
UINT8 *Ptr8;
UINT8 *VarStart;
UINT8 *VarEnd;
if (BootOption->Variable == NULL ||
BootOption->VariableSize <= sizeof(BootOption->Attributes) + sizeof(BootOption->FilePathListLength)
) {
DBG("ParseBootOption: invalid input params\n");
return EFI_INVALID_PARAMETER;
}
VarStart = (UINT8*)BootOption->Variable;
VarEnd = VarStart + BootOption->VariableSize;
Ptr8 = VarStart;
// Attributes
BootOption->Attributes = *((UINT32*)Ptr8);
Ptr8 += sizeof(BootOption->Attributes);
// FilePathListLength
BootOption->FilePathListLength = *((UINT16*)Ptr8);
Ptr8 += sizeof(BootOption->FilePathListLength);
// Description and it's size
BootOption->Description = (CHAR16*)Ptr8;
BootOption->DescriptionSize = StrSize(BootOption->Description);
Ptr8 += BootOption->DescriptionSize;
if (Ptr8 > VarEnd) {
DBG("ParseBootOption: invalid boot variable\n");
return EFI_INVALID_PARAMETER;
}
// FilePathList
BootOption->FilePathList = (EFI_DEVICE_PATH_PROTOCOL*)Ptr8;
Ptr8 += BootOption->FilePathListLength;
if (Ptr8 > VarEnd) {
DBG("ParseBootOption: invalid boot variable\n");
return EFI_INVALID_PARAMETER;
}
// OptionalData and it's size
if (Ptr8 < VarEnd) {
BootOption->OptionalData = Ptr8;
BootOption->OptionalDataSize = VarEnd - Ptr8;
} else {
BootOption->OptionalData = NULL;
BootOption->OptionalDataSize = 0;
}
Ptr8 += BootOption->OptionalDataSize;
return EFI_SUCCESS;
}
/** Compiles boot option params set in BootOption to valid BootXXXX var content (BootOption->Variable).
* BootOption fields Attributes, FilePathListLength, FilePathList, Description, OptionalData
* and OptionalDataSize must be set.
*/
EFI_STATUS
CompileBootOption (
BO_BOOT_OPTION *BootOption
)
{
UINT8 *Ptr8;
if (BootOption->Description == NULL ||
BootOption->FilePathList == NULL ||
BootOption->FilePathListLength == 0 ||
(BootOption->OptionalData != NULL && BootOption->OptionalDataSize == 0)
) {
DBG("CompileBootOption: invalid input params\n");
return EFI_INVALID_PARAMETER;
}
BootOption->DescriptionSize = StrSize(BootOption->Description);
BootOption->VariableSize = sizeof(BootOption->Attributes) //UINT32
+ sizeof(BootOption->FilePathListLength) //UINT16
+ BootOption->DescriptionSize
+ BootOption->FilePathListLength
+ BootOption->OptionalDataSize;
BootOption->Variable = (__typeof__(BootOption->Variable))AllocateZeroPool (BootOption->VariableSize);
if (BootOption->Variable == NULL) {
DBG("CompileBootOption: EFI_OUT_OF_RESOURCES\n");
return EFI_OUT_OF_RESOURCES;
}
Ptr8 = (UINT8*)BootOption->Variable;
// Attributes
*((UINT32*)Ptr8) = BootOption->Attributes;
Ptr8 += sizeof(BootOption->Attributes);
// FilePathListLength;
*((UINT16*)Ptr8) = BootOption->FilePathListLength;
Ptr8 += sizeof(BootOption->FilePathListLength);
// Description
CopyMem ((CHAR16*)Ptr8, BootOption->Description, BootOption->DescriptionSize);
Ptr8 += BootOption->DescriptionSize;
// FilePathList
CopyMem (Ptr8, BootOption->FilePathList, BootOption->FilePathListLength);
Ptr8 += BootOption->FilePathListLength;
// OptionalData
if (BootOption->OptionalDataSize > 0) {
CopyMem (Ptr8, BootOption->OptionalData, BootOption->OptionalDataSize);
// Ptr8 += BootOption->OptionalDataSize;
}
return EFI_SUCCESS;
}
/** Reads BootXXXX (XXXX = BootNum) var, parses it and returns in BootOption.
* Caller is responsible for releasing BootOption->Variable with FreePool().
*/
EFI_STATUS
GetBootOption (
IN UINT16 BootNum,
OUT BO_BOOT_OPTION *BootOption
)
{
CHAR16 VarName[16];
//
// Get BootXXXX var.
//
BootOption->BootNum = BootNum;
snwprintf(VarName, sizeof(VarName), "Boot%04hX", BootNum);
BootOption->Variable = (__typeof__(BootOption->Variable))GetNvramVariable (VarName, &gEfiGlobalVariableGuid, NULL, (UINTN *)(UINTN)(OFFSET_OF(BO_BOOT_OPTION, VariableSize) + (UINTN)BootOption));
if (BootOption->Variable == NULL) {
return EFI_NOT_FOUND;
}
//
// Parse it.
//
return ParseBootOption (BootOption);
}
/** Returns BootNum: XXXX of first unoccupied BootXXXX var slot. */
EFI_STATUS
FindFreeBootNum (
OUT UINT16 *BootNum
)
{
EFI_STATUS Status;
UINTN Index;
CHAR16 VarName[16];
UINTN VarSize;
for (Index = 0; Index <= 0xFFFF; Index++) {
snwprintf(VarName, sizeof(VarName), "Boot%04llX", Index);
VarSize = 0;
Status = gRT->GetVariable (VarName, &gEfiGlobalVariableGuid, NULL, &VarSize, NULL);
if (Status == EFI_NOT_FOUND) {
*BootNum = (UINT16)Index;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/** Searches BootXXXX vars for entry that points to given FileDeviceHandle/FileName
* and returns BootNum (XXXX in BootXXXX variable name) and BootIndex (index in BootOrder)
* if found.
*/
EFI_STATUS
FindBootOptionForFile (
IN EFI_HANDLE FileDeviceHandle,
IN CONST XStringW& FileName,
OUT UINT16 *BootNum,
OUT UINTN *BootIndex
)
{
EFI_STATUS Status;
UINT16 *BootOrder;
UINTN BootOrderLen;
UINTN Index;
BO_BOOT_OPTION BootOption;
EFI_DEVICE_PATH_PROTOCOL *SearchedDevicePath[2];
UINTN SearchedDevicePathSize[2];
DBG("FindBootOptionForFile: %p, %ls\n", FileDeviceHandle, FileName.wc_str());
//
// Get BootOrder - we will search only options listed in BootOrder.
//
Status = GetBootOrder (&BootOrder, &BootOrderLen);
if (EFI_ERROR(Status)) {
return EFI_OUT_OF_RESOURCES; //Slice: I don't want here to be EFI_NOT_FOUND
}
//
// Create FileDeviceHandle/FileName device paths (long and short form) - we will search boot options for that.
//
Status = CreateBootOptionDevicePath (FileDeviceHandle, FileName, FALSE, &SearchedDevicePath[0]);
if (EFI_ERROR(Status)) {
return EFI_OUT_OF_RESOURCES;
}
SearchedDevicePathSize[0] = GetDevicePathSize (SearchedDevicePath[0]);
DBG(" Searching for: %ls (Len: %llu)\n", FileDevicePathToStr(SearchedDevicePath[0]), SearchedDevicePathSize[0]);
Status = CreateBootOptionDevicePath (FileDeviceHandle, FileName, TRUE, &SearchedDevicePath[1]);
if (EFI_ERROR(Status)) {
return EFI_OUT_OF_RESOURCES;
}
SearchedDevicePathSize[1] = GetDevicePathSize (SearchedDevicePath[1]);
DBG(" and for: %ls (Len: %llu)\n", FileDevicePathToStr(SearchedDevicePath[1]), SearchedDevicePathSize[1]);
//
// Iterate over all BootXXXX vars (actually, only ones that are in BootOrder list)
//
BootOption.Variable = NULL;
for (Index = 0; Index < BootOrderLen; Index++) {
if (BootOption.Variable != NULL) {
FreePool(BootOption.Variable);
BootOption.Variable = NULL;
}
//
// Get boot option
//
Status = GetBootOption (BootOrder[Index], &BootOption);
if (EFI_ERROR(Status)) {
DBG("FindBootOptionForFile: Boot%04hX: %s\n", BootOrder[Index], strerror(Status));
//WaitForKeyPress(L"press a key to continue\n\n");
continue;
}
//PrintBootOption (&BootOption, Index);
if (DevicePathEqual (SearchedDevicePath[0], BootOption.FilePathList) ||
DevicePathEqual (SearchedDevicePath[1], BootOption.FilePathList)) {
DBG("FindBootOptionForFile: Found Boot%04hX, at index %llu\n", BootOrder[Index], Index);
if (BootNum != NULL) {
*BootNum = BootOrder[Index];
}
if (BootIndex != NULL) {
*BootIndex = Index;
}
FreePool(BootOption.Variable);
//WaitForKeyPress(L"press a key to continue\n\n");
return EFI_SUCCESS;
}
//WaitForKeyPress(L"press a key to continue\n\n");
}
if (BootOption.Variable != NULL) {
FreePool(BootOption.Variable);
}
DBG("FindBootOptionForFile: Not found.\n");
return EFI_NOT_FOUND;
}
/** Prints BootXXXX vars found listed in BootOrder, plus print others if AllBootOptions == TRUE. */
VOID
PrintBootOptions (
IN BOOLEAN AllBootOptions
)
{
EFI_STATUS Status;
UINT16 *BootOrder;
UINTN BootOrderLen;
UINTN Index;
UINTN BootNum;
BO_BOOT_OPTION BootOption;
BOOLEAN FoundOthers;
DBG("\nBoot options:\n-------------\n");
//
// Get BootOrder - we will search only options listed in BootOrder.
//
Status = GetBootOrder (&BootOrder, &BootOrderLen);
if (EFI_ERROR(Status)) {
return;
}
//
// Iterate over all BootXXXX vars (actually, only ones that are in BootOrder list)
//
BootOption.Variable = NULL;
for (Index = 0; Index < BootOrderLen; Index++) {
//
// Get boot option
//
Status = GetBootOption (BootOrder[Index], &BootOption);
if (EFI_ERROR(Status)) {
DBG("%2llu) Boot%04hX: ERROR, not found: %s\n", Index, BootOrder[Index], strerror(Status));
continue;
}
PrintBootOption (&BootOption, Index);
FreePool(BootOption.Variable);
}
if (AllBootOptions) {
DBG("\nBoot options not in BootOrder list:\n");
FoundOthers = FALSE;
//
// Additionally print BootXXXX vars which are not in BootOrder
//
BootOption.Variable = NULL;
for (BootNum = 0; BootNum <= 0xFFFF; BootNum++) {
//
// Check if it is in BootOrder
//
for (Index = 0; Index < BootOrderLen; Index++) {
if (BootNum == (UINTN)BootOrder[Index]) {
break;
}
}
if (Index < BootOrderLen) {
// exists in BootOrder - skip it
continue;
}
//
// Get boot option
//
Status = GetBootOption ((UINT16)BootNum, &BootOption);
if (EFI_ERROR(Status)) {
continue;
}
PrintBootOption (&BootOption, 0);
FreePool(BootOption.Variable);
FoundOthers = TRUE;
}
if (!FoundOthers) {
DBG(" not found\n");
}
}
DBG("-------------\n");
//WaitForKeyPress(L"press a key to continue\n\n");
}
/** Adds new boot option with data specified in BootOption,
* to be at BootIndex in the list of options (0 based).
* BootOption should be populated with: Attributes, FilePathListLength,
* FilePathList, Description, OptionalData and OptionalDataSize.
* BootOption->BootNum will contain XXXX from added BootXXXX var
* on success.
*/
EFI_STATUS
AddBootOption (
IN BO_BOOT_OPTION *BootOption,
IN UINTN BootIndex
)
{
EFI_STATUS Status;
CHAR16 VarName[16];
DBG("AddBootOption: %ls\n", BootOption->Description);
DBG(" FilePath: %ls\n", FileDevicePathToStr(BootOption->FilePathList));
DBG(" BootIndex: %llu\n", BootIndex);
//
// Find free BootXXXX var slot.
//
Status = FindFreeBootNum (&BootOption->BootNum);
if (EFI_ERROR(Status)) {
DBG("FindFreeBootNum: %s\n", strerror(Status));
return Status;
}
DBG(" Found BootNum: %04hX\n", BootOption->BootNum);
snwprintf(VarName, sizeof(VarName), "Boot%04hX", BootOption->BootNum);
//
// Prepare BootOption variable
//
Status = CompileBootOption (BootOption);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Save BootXXXX var
//
Status = gRT->SetVariable (VarName,
&gEfiGlobalVariableGuid,
EFI_VARIABLE_NON_VOLATILE
| EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS,
BootOption->VariableSize,
BootOption->Variable
);
if (EFI_ERROR(Status)) {
DBG("SetVariable: %ls = %s\n", VarName, strerror(Status));
return Status;
}
DBG(" %ls saved\n", VarName);
//
// Free allocated space
//
FreePool(BootOption->Variable);
//
// Update BootOrder - add our new boot option as BootIndex in the list
//
Status = AddToBootOrder (BootOption->BootNum, BootIndex);
return Status;
}
/** Adds new boot option for given file system device handle FileDeviceHandle, file path FileName
* and Description, to be BootIndex in the list of options (0 based).
* If UseShortForm == TRUE, then only the hard drive media dev path will be used instead
* of full device path.
* Long (full) form:
* PciRoot(0x0)/Pci(0x1f,0x2)/Sata(0x1,0x0)/HD(1,GPT,96004846-a018-49ad-bc9f-4e5a340adc4b,0x800,0x64000)/\EFI\BOOT\File.efi
* Short form:
* HD(1,GPT,96004846-a018-49ad-bc9f-4e5a340adc4b,0x800,0x64000)/\EFI\BOOT\File.efi
*/
EFI_STATUS
AddBootOptionForFile (
IN EFI_HANDLE FileDeviceHandle,
IN CONST XStringW& FileName,
IN BOOLEAN UseShortForm,
IN CONST CHAR16 *Description,
IN UINT8 *OptionalData,
IN UINTN OptionalDataSize,
IN UINTN BootIndex,
OUT UINT16 *BootNum
)
{
EFI_STATUS Status;
BO_BOOT_OPTION BootOption;
DBG("\nAddBootOptionForFile: %p, %ls, %ls\n %ls, %llu\n",
FileDeviceHandle, FileName.wc_str(),
UseShortForm ? L"ShortDevPath" : L"FullDevPath",
Description, BootIndex);
//
// Prepare BootOption FilePath from FileDeviceHandle and FileName
//
Status = CreateBootOptionDevicePath (FileDeviceHandle, FileName, UseShortForm, &BootOption.FilePathList);
if (EFI_ERROR(Status)) {
return Status;
}
//
// Prepare BootOption variable
//
BootOption.Attributes = LOAD_OPTION_ACTIVE;
BootOption.FilePathListLength = (UINT16)GetDevicePathSize (BootOption.FilePathList);
BootOption.Description = Description;
BootOption.OptionalData = OptionalData;
BootOption.OptionalDataSize = OptionalDataSize;
Status = AddBootOption (&BootOption, BootIndex);
if (EFI_ERROR(Status)) {
FreePool(BootOption.FilePathList);
DBG("AddBootOptionForFile: Error: %s\n", strerror(Status));
return Status;
}
//
// Free allocated space
//
FreePool(BootOption.FilePathList);
//
// Output vars
//
if (BootNum != NULL) {
*BootNum = BootOption.BootNum;
}
DBG("AddBootOptionForFile: done.\n");
//WaitForKeyPress(L"press a key to continue\n\n");
return EFI_SUCCESS;
}
/** Deletes boot option specified with BootNum (XXXX in BootXXXX var name). */
EFI_STATUS
DeleteBootOption (
IN UINT16 BootNum
)
{
EFI_STATUS Status;
CHAR16 VarName[16];
DBG("DeleteBootOption: Boot%04hX\n", BootNum);
snwprintf(VarName, sizeof(VarName), "Boot%04hX", BootNum);
//
// Delete BootXXXX var
//
Status = gRT->SetVariable (VarName,
&gEfiGlobalVariableGuid,
0,
0,
NULL
);
if (EFI_ERROR(Status)) {
DBG(" Error del. variable: %ls = %s\n", VarName, strerror(Status));
return Status;
}
DBG(" %ls deleted\n", VarName);
//
// Update BootOrder - delete our boot option from the list
//
Status = DeleteFromBootOrder (BootNum);
//if (EFI_ERROR(Status)) {
// return Status;
//}
return Status;
}
//
// Delete all boot options for file specified with FileDeviceHandle and FileName.
//
EFI_STATUS
DeleteBootOptionForFile (
IN EFI_HANDLE FileDeviceHandle,
IN CONST XStringW& FileName
)
{
EFI_STATUS Status;
IN UINT16 BootNum;
DBG("\nDeleteBootOptionForFile: %p, %ls\n", FileDeviceHandle, FileName.wc_str());
do {
Status = FindBootOptionForFile (FileDeviceHandle, FileName, &BootNum, NULL);
if (!EFI_ERROR(Status)) {
DBG("\tdeleted option: %04hX\n", BootNum);
DeleteBootOption (BootNum);
}
} while (!EFI_ERROR(Status));
// DBG("DeleteBootOptionForFile: done.\n");
//WaitForKeyPress(L"press a key to continue\n\n");
return Status;
}
/** Deletes all boot option that points to a file which contains FileName in it's path. */
EFI_STATUS
DeleteBootOptionsContainingFile (
IN CHAR16 *FileName
)
{
EFI_STATUS Status;
EFI_STATUS ReturnStatus;
UINT16 *BootOrder;
UINTN BootOrderLen;
UINTN Index;
BO_BOOT_OPTION BootOption;
FILEPATH_DEVICE_PATH *FilePathDP;
DBG("DeleteBootOptionContainingFile: %ls\n", FileName);
//
// Get BootOrder - we will search only options listed in BootOrder.
//
Status = GetBootOrder (&BootOrder, &BootOrderLen);
if (EFI_ERROR(Status)) {
return Status;
}
ReturnStatus = EFI_NOT_FOUND;
//
// Iterate over all BootXXXX vars (actually, only ones that are in BootOrder list)
//
BootOption.Variable = NULL;
for (Index = 0; Index < BootOrderLen; Index++) {
if (BootOption.Variable != NULL) {
FreePool(BootOption.Variable);
BootOption.Variable = NULL;
}
//
// Get boot option
//
Status = GetBootOption (BootOrder[Index], &BootOption);
if (EFI_ERROR(Status)) {
DBG("DeleteBootOptionContainingFile: Boot%04hX: ERROR: %s\n", BootOrder[Index], strerror(Status));
//WaitForKeyPress(L"press a key to continue\n\n");
continue;
}
//PrintBootOption (&BootOption, Index);
FilePathDP = (FILEPATH_DEVICE_PATH*) FindDevicePathNodeWithType (BootOption.FilePathList, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP);
if ((FilePathDP != NULL) &&
(StriStr (FilePathDP->PathName, FileName) != NULL)) {
DBG("DeleteBootOptionContainingFile: Found Boot%04hX, at index %llu\n", BootOrder[Index], Index);
Status = DeleteBootOption (BootOrder[Index]);
if (!EFI_ERROR(Status)) {
ReturnStatus = EFI_SUCCESS;
}
}
}
if (BootOption.Variable != NULL) {
FreePool(BootOption.Variable);
}
DBG("DeleteBootOptionContainingFile: %s\n", strerror(ReturnStatus));
return ReturnStatus;
}