mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-26 12:05:36 +01:00
1196 lines
34 KiB
C++
1196 lines
34 KiB
C++
/*
|
|
* BootOptions.c
|
|
*
|
|
* Created by dmazar, 10.2012.
|
|
*
|
|
* Functions for modifying UEFI boot options.
|
|
*
|
|
*/
|
|
|
|
|
|
#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
|
|
#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 *
|
|
Clover_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 XBool 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 %llx is not fs volume", (uintptr_t)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 (Clover_FindDevicePathNodeWithType (*DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP));
|
|
if (TmpDevPath != NULL) {
|
|
FreePool(*DevicePath);
|
|
*DevicePath = TmpDevPath;
|
|
} /* else {
|
|
TmpDevPath = DuplicateDevicePath (Clover_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. */
|
|
XBool
|
|
DevicePathEqual (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath1,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath2
|
|
)
|
|
{
|
|
XBool 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, efiStrError(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, efiStrError(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;
|
|
|
|
DBG("%2llu) Boot%04hX: %ls, Attr: 0x%X\n",
|
|
Index, BootOption->BootNum, BootOption->Description, BootOption->Attributes);
|
|
DBG(" %ls\n", FileDevicePathToXStringW(BootOption->FilePathList).wc_str());
|
|
|
|
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: %llx, %ls\n", (uintptr_t)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", FileDevicePathToXStringW(SearchedDevicePath[0]).wc_str(), 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", FileDevicePathToXStringW(SearchedDevicePath[1]).wc_str(), 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], efiStrError(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 XBool AllBootOptions
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT16 *BootOrder;
|
|
UINTN BootOrderLen;
|
|
UINTN Index;
|
|
UINTN BootNum;
|
|
BO_BOOT_OPTION BootOption;
|
|
XBool 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], efiStrError(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", FileDevicePathToXStringW(BootOption->FilePathList).wc_str());
|
|
DBG(" BootIndex: %llu\n", BootIndex);
|
|
|
|
//
|
|
// Find free BootXXXX var slot.
|
|
//
|
|
Status = FindFreeBootNum (&BootOption->BootNum);
|
|
if (EFI_ERROR(Status)) {
|
|
DBG("FindFreeBootNum: %s\n", efiStrError(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, efiStrError(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 XBool 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: %llx, %ls, %ls\n %ls, %llu\n",
|
|
(uintptr_t)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", efiStrError(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, efiStrError(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: %llx, %ls\n", (uintptr_t)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], efiStrError(Status));
|
|
//WaitForKeyPress(L"press a key to continue\n\n");
|
|
continue;
|
|
}
|
|
|
|
//PrintBootOption (&BootOption, Index);
|
|
|
|
FilePathDP = (FILEPATH_DEVICE_PATH*) Clover_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", efiStrError(ReturnStatus));
|
|
return ReturnStatus;
|
|
}
|
|
|