mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-14 10:05:43 +01:00
e8a29b1a29
The internal parser is retro compatible with old Clover revisions since r3250. This is possible because a check for the existence of any variable inside SETTING_DATA structure is performed before the call. Variables are all accessed using the label property of the Mirror class, so as a string.
463 lines
9.7 KiB
C
463 lines
9.7 KiB
C
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "efidevp.h"
|
|
|
|
/* From utils.c */
|
|
extern void CatPrintf(char *target, const char *format, ...);
|
|
extern void *MallocCopy(unsigned int size, void *buf);
|
|
|
|
/*
|
|
* Get parameter in a pair of parentheses follow the given node name.
|
|
* For example, given the "Pci(0,1)" and NodeName "Pci", it returns "0,1".
|
|
*/
|
|
char *GetParamByNodeName (CHAR8 *Str, CHAR8 *NodeName)
|
|
{
|
|
CHAR8 *ParamStr;
|
|
CHAR8 *StrPointer;
|
|
UINT32 NodeNameLength;
|
|
UINT32 ParameterLength;
|
|
|
|
// Check whether the node name matchs
|
|
NodeNameLength = (UINT32)strlen (NodeName);
|
|
if (strncasecmp(Str, NodeName, NodeNameLength) != 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ParamStr = Str + NodeNameLength;
|
|
if (!IS_LEFT_PARENTH (*ParamStr))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Skip the found '(' and find first occurrence of ')'
|
|
ParamStr++;
|
|
ParameterLength = 0;
|
|
StrPointer = ParamStr;
|
|
|
|
while (!IS_NULL (*StrPointer))
|
|
{
|
|
if (IS_RIGHT_PARENTH (*StrPointer))
|
|
{
|
|
break;
|
|
}
|
|
StrPointer++;
|
|
ParameterLength++;
|
|
}
|
|
|
|
if (IS_NULL (*StrPointer))
|
|
{
|
|
// ')' not found
|
|
return NULL;
|
|
}
|
|
|
|
ParamStr = MallocCopy ((ParameterLength + 1), ParamStr);
|
|
if (ParamStr == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
// Terminate the parameter string
|
|
ParamStr[ParameterLength] = '\0';
|
|
|
|
return ParamStr;
|
|
}
|
|
|
|
/* Get current sub-string from a string list, before return
|
|
* the list header is moved to next sub-string. The sub-string is separated
|
|
* by the specified character. For example, the separator is ',', the string
|
|
* list is "2,0,3", it returns "2", the remain list move to "0,3"
|
|
*/
|
|
CHAR8 *SplitStr (CHAR8 **List, CHAR8 Separator)
|
|
{
|
|
char *Str;
|
|
char *ReturnStr;
|
|
|
|
Str = *List;
|
|
ReturnStr = Str;
|
|
|
|
if (IS_NULL (*Str))
|
|
{
|
|
return ReturnStr;
|
|
}
|
|
|
|
// Find first occurrence of the separator
|
|
while (!IS_NULL (*Str))
|
|
{
|
|
if (*Str == Separator)
|
|
{
|
|
break;
|
|
}
|
|
Str++;
|
|
}
|
|
|
|
if (*Str == Separator)
|
|
{
|
|
// Find a sub-string, terminate it
|
|
*Str = '\0';
|
|
Str++;
|
|
}
|
|
|
|
// Move to next sub-string
|
|
*List = Str;
|
|
|
|
return ReturnStr;
|
|
}
|
|
|
|
CHAR8 *GetNextParamStr (CHAR8 **List)
|
|
{
|
|
// The separator is comma
|
|
return SplitStr (List, ',');
|
|
}
|
|
|
|
// Get one device node from entire device path text.
|
|
CHAR8 *GetNextDeviceNodeStr (CHAR8 **DevicePath, BOOLEAN *IsInstanceEnd)
|
|
{
|
|
CHAR8 *Str;
|
|
CHAR8 *ReturnStr;
|
|
UINT32 ParenthesesStack;
|
|
|
|
Str = *DevicePath;
|
|
if (IS_NULL (*Str))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Skip the leading '/', '(', ')' and ','
|
|
while (!IS_NULL (*Str))
|
|
{
|
|
if (!IS_SLASH (*Str) && !IS_COMMA (*Str) && !IS_LEFT_PARENTH (*Str) && !IS_RIGHT_PARENTH (*Str))
|
|
{
|
|
break;
|
|
}
|
|
Str++;
|
|
}
|
|
|
|
ReturnStr = Str;
|
|
|
|
// Scan for the separator of this device node, '/' or ','
|
|
ParenthesesStack = 0;
|
|
while (!IS_NULL (*Str))
|
|
{
|
|
if ((IS_COMMA (*Str) || IS_SLASH (*Str)) && (ParenthesesStack == 0))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (IS_LEFT_PARENTH (*Str))
|
|
{
|
|
ParenthesesStack++;
|
|
}
|
|
else if (IS_RIGHT_PARENTH (*Str))
|
|
{
|
|
ParenthesesStack--;
|
|
}
|
|
|
|
Str++;
|
|
}
|
|
|
|
if (ParenthesesStack != 0)
|
|
{
|
|
// The '(' doesn't pair with ')', invalid device path text
|
|
return NULL;
|
|
}
|
|
|
|
if (IS_COMMA (*Str))
|
|
{
|
|
*IsInstanceEnd = 1;
|
|
*Str = '\0';
|
|
Str++;
|
|
}
|
|
else
|
|
{
|
|
*IsInstanceEnd = 0;
|
|
if (!IS_NULL (*Str))
|
|
{
|
|
*Str = '\0';
|
|
Str++;
|
|
}
|
|
}
|
|
|
|
*DevicePath = Str;
|
|
|
|
return ReturnStr;
|
|
}
|
|
|
|
/*
|
|
* Function unpacks a device path data structure so that all the nodes of a device path
|
|
* are naturally aligned.
|
|
*/
|
|
EFI_DEVICE_PATH_P *UnpackDevicePath (EFI_DEVICE_PATH_P *DevPath)
|
|
{
|
|
EFI_DEVICE_PATH_P *Src;
|
|
EFI_DEVICE_PATH_P *Dest;
|
|
EFI_DEVICE_PATH_P *NewPath;
|
|
UINT32 Size;
|
|
UINT32 Count;
|
|
|
|
if (DevPath == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Walk device path and round sizes to valid boundries
|
|
Src = DevPath;
|
|
Size = 0;
|
|
for (Count = 0;;Count++)
|
|
{
|
|
if(Count > MAX_DEVICE_PATH_LEN)
|
|
{
|
|
// BugBug: Code to catch bogus device path
|
|
fprintf(stderr, "UnpackDevicePath: Cannot find device path end! Probably a bogus device path\n");
|
|
return NULL;
|
|
}
|
|
Size += DevicePathNodeLength (Src);
|
|
Size += ALIGN_SIZE (Size);
|
|
|
|
if (IsDevicePathEnd (Src))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Src = (EFI_DEVICE_PATH_P *) NextDevicePathNode (Src);
|
|
}
|
|
|
|
// Allocate space for the unpacked path
|
|
NewPath = (EFI_DEVICE_PATH_P *)(UINT8*)calloc(Size, sizeof(UINT8));
|
|
|
|
if (NewPath != NULL)
|
|
{
|
|
|
|
assert(((UINT32) NewPath) % MIN_ALIGNMENT_SIZE == 0);
|
|
|
|
// Copy each node
|
|
Src = DevPath;
|
|
Dest = NewPath;
|
|
for (;;)
|
|
{
|
|
Size = DevicePathNodeLength (Src);
|
|
memcpy(Dest, Src, Size);
|
|
Size += ALIGN_SIZE (Size);
|
|
SetDevicePathNodeLength (Dest, Size);
|
|
Dest->Type |= EFI_DP_TYPE_UNPACKED;
|
|
Dest = (EFI_DEVICE_PATH_P *) (((UINT8 *) Dest) + Size);
|
|
|
|
if (IsDevicePathEnd (Src))
|
|
{
|
|
break;
|
|
}
|
|
|
|
Src = (EFI_DEVICE_PATH_P *) NextDevicePathNode (Src);
|
|
}
|
|
}
|
|
|
|
return NewPath;
|
|
}
|
|
|
|
// Returns the size of the device path, in bytes.
|
|
UINT32 DevicePathSize (const EFI_DEVICE_PATH_P *DevicePath)
|
|
{
|
|
const EFI_DEVICE_PATH_P*Start;
|
|
UINT32 Count = 0;
|
|
|
|
if (DevicePath == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Search for the end of the device path structure
|
|
Start = (EFI_DEVICE_PATH_P*) DevicePath;
|
|
for (Count = 0;!IsDevicePathEnd(DevicePath);Count++)
|
|
{
|
|
if(Count > MAX_DEVICE_PATH_LEN)
|
|
{
|
|
// BugBug: Code to catch bogus device path
|
|
fprintf(stderr, "DevicePathSize: Cannot find device path end! Probably a bogus device path\n");
|
|
return 0;
|
|
}
|
|
DevicePath = NextDevicePathNode (DevicePath);
|
|
}
|
|
|
|
// Compute the size and add back in the size of the end device path structure
|
|
return ((UINT32) DevicePath - (UINT32) Start) + sizeof (EFI_DEVICE_PATH_P);
|
|
}
|
|
|
|
// Creates a device node
|
|
EFI_DEVICE_PATH_P*CreateDeviceNode (UINT8 NodeType, UINT8 NodeSubType, UINT16 NodeLength)
|
|
{
|
|
EFI_DEVICE_PATH_P*Node;
|
|
|
|
if (NodeLength < sizeof (EFI_DEVICE_PATH_P))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Node = (EFI_DEVICE_PATH_P*) (UINT8*)calloc ((UINT32) NodeLength, sizeof(UINT8));
|
|
if (Node != NULL)
|
|
{
|
|
Node->Type = NodeType;
|
|
Node->SubType = NodeSubType;
|
|
SetDevicePathNodeLength (Node, NodeLength);
|
|
}
|
|
|
|
return Node;
|
|
}
|
|
|
|
// Duplicate a device path structure.
|
|
EFI_DEVICE_PATH_P*DuplicateDevicePathP (EFI_DEVICE_PATH_P *DevicePath)
|
|
{
|
|
EFI_DEVICE_PATH_P*NewDevicePath;
|
|
UINT32 Size;
|
|
|
|
if (DevicePath == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Compute the size
|
|
Size = DevicePathSize (DevicePath);
|
|
if (Size == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Allocate space for duplicate device path
|
|
NewDevicePath = MallocCopy(Size, DevicePath);
|
|
|
|
return NewDevicePath;
|
|
}
|
|
|
|
void EisaIdToText (UINT32 EisaId, CHAR8 *Text)
|
|
{
|
|
CHAR8 PnpIdStr[17];
|
|
|
|
//SPrint ("%X", 0x0a03) => "0000000000000A03"
|
|
snprintf(PnpIdStr, 17, "%X", EisaId >> 16);
|
|
snprintf(Text,0,"%c%c%c%s",'@' + ((EisaId >> 10) & 0x1f),'@' + ((EisaId >> 5) & 0x1f),'@' + ((EisaId >> 0) & 0x1f), PnpIdStr + (16 - 4));
|
|
}
|
|
|
|
void DevPathToTextPci (CHAR8 *Str, void *DevPath, BOOLEAN DisplayOnly, BOOLEAN AllowShortcuts)
|
|
{
|
|
PCI_DEVICE_PATH_P *Pci;
|
|
|
|
Pci = DevPath;
|
|
CatPrintf(Str, "Pci(0x%x,0x%x)", Pci->Device, Pci->Function);
|
|
}
|
|
|
|
void DevPathToTextAcpi (CHAR8 *Str, void *DevPath, BOOLEAN DisplayOnly, BOOLEAN AllowShortcuts)
|
|
{
|
|
ACPI_HID_DEVICE_PATH_P *Acpi;
|
|
|
|
Acpi = DevPath;
|
|
if ((Acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST)
|
|
{
|
|
switch (EISA_ID_TO_NUM (Acpi->HID))
|
|
{
|
|
case 0x0a03:
|
|
CatPrintf(Str, "PciRoot(0x%x)", Acpi->UID);
|
|
break;
|
|
default:
|
|
CatPrintf(Str, "Acpi(PNP%04x,0x%x)", EISA_ID_TO_NUM (Acpi->HID), Acpi->UID);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CatPrintf(Str, "Acpi(0x%08x,0x%x)", Acpi->HID, Acpi->UID);
|
|
}
|
|
}
|
|
|
|
void DevPathToTextEndInstance (CHAR8 *Str, void *DevPath, BOOLEAN DisplayOnly, BOOLEAN AllowShortcuts)
|
|
{
|
|
CatPrintf(Str, ",");
|
|
}
|
|
|
|
void DevPathToTextNodeUnknown (CHAR8 *Str, void *DevPath, BOOLEAN DisplayOnly, BOOLEAN AllowShortcuts)
|
|
{
|
|
CatPrintf(Str, "?");
|
|
}
|
|
|
|
DEVICE_PATH_TO_TEXT_TABLE DevPathToTextTable[] =
|
|
{
|
|
HARDWARE_DEVICE_PATH,
|
|
HW_PCI_DP,
|
|
DevPathToTextPci,
|
|
ACPI_DEVICE_PATH,
|
|
ACPI_DP,
|
|
DevPathToTextAcpi,
|
|
END_DEVICE_PATH_TYPE,
|
|
END_INSTANCE_DEVICE_PATH_SUBTYPE,
|
|
DevPathToTextEndInstance,
|
|
0,
|
|
0,
|
|
NULL
|
|
};
|
|
|
|
// Convert a device path to its text representation.
|
|
CHAR8 *ConvertDevicePathToAscii (const EFI_DEVICE_PATH_P*DevicePath, BOOLEAN DisplayOnly, BOOLEAN AllowShortcuts)
|
|
{
|
|
CHAR8 *Str;
|
|
EFI_DEVICE_PATH_P *DevPathNode;
|
|
EFI_DEVICE_PATH_P *UnpackDevPath;
|
|
UINT32 Index;
|
|
UINT32 NewSize;
|
|
void (*DumpNode) (CHAR8 *, void *, BOOLEAN, BOOLEAN);
|
|
|
|
if (DevicePath == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
Str = (CHAR8 *)calloc(MAX_PATH_LEN, sizeof(CHAR8));
|
|
|
|
// Unpacked the device path
|
|
UnpackDevPath = UnpackDevicePath ((EFI_DEVICE_PATH_P*) DevicePath);
|
|
assert(UnpackDevPath != NULL);
|
|
|
|
// Process each device path node
|
|
DevPathNode = UnpackDevPath;
|
|
while (!IsDevicePathEnd (DevPathNode))
|
|
{
|
|
// Find the handler to dump this device path node
|
|
DumpNode = NULL;
|
|
for (Index = 0; DevPathToTextTable[Index].Function; Index += 1)
|
|
{
|
|
if (DevicePathType (DevPathNode) == DevPathToTextTable[Index].Type &&
|
|
DevicePathSubType (DevPathNode) == DevPathToTextTable[Index].SubType)
|
|
{
|
|
DumpNode = DevPathToTextTable[Index].Function;
|
|
break;
|
|
}
|
|
}
|
|
// If not found, use a generic function
|
|
if (!DumpNode)
|
|
{
|
|
DumpNode = DevPathToTextNodeUnknown;
|
|
}
|
|
|
|
// Put a path seperator in if needed
|
|
if (strlen(Str) && DumpNode != DevPathToTextEndInstance)
|
|
{
|
|
if (*(Str + strlen(Str) - 1) != ',')
|
|
{
|
|
CatPrintf(Str, "/");
|
|
}
|
|
}
|
|
// Print this node of the device path
|
|
DumpNode (Str, DevPathNode, DisplayOnly, AllowShortcuts);
|
|
|
|
// Next device path node
|
|
DevPathNode = NextDevicePathNode (DevPathNode);
|
|
}
|
|
|
|
// Shrink pool used for string allocation
|
|
free(UnpackDevPath);
|
|
NewSize = ((UINT32)strlen(Str) + 1);
|
|
Str = realloc(Str, NewSize);
|
|
assert(Str != NULL);
|
|
Str[strlen(Str)] = 0;
|
|
return Str;
|
|
}
|
|
|