Files
exectos/xtldr/config.cc
Aiken Harris 6c66028800
All checks were successful
Builds / ExectOS (amd64, release) (push) Successful in 27s
Builds / ExectOS (amd64, debug) (push) Successful in 29s
Builds / ExectOS (i686, release) (push) Successful in 27s
Builds / ExectOS (i686, debug) (push) Successful in 28s
Document XTLDR methods
2025-09-22 14:54:27 +02:00

1053 lines
35 KiB
C++

/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtldr/config.cc
* DESCRIPTION: XT Boot Loader Configuration
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
* Aiken Harris <harraiken91@gmail.com>
*/
#include <xtldr.hh>
/**
* Returns a boolean value of the specified configuration key.
*
* @param ConfigName
* Specifies the configuration key to return its boolean representation.
*
* @return This routine returns a boolean representation of the configuration value.
*
* @since XT 1.0
*/
XTCDECL
BOOLEAN
Configuration::GetBooleanValue(IN PCWSTR ConfigName)
{
PWCHAR Value;
/* Get config value */
GetValue(ConfigName, &Value);
/* Check if option is enabled */
if(RTL::WideString::CompareWideStringInsensitive(Value, L"ENABLED", 0) == 0 ||
RTL::WideString::CompareWideStringInsensitive(Value, L"ON", 0) == 0 ||
RTL::WideString::CompareWideStringInsensitive(Value, L"TRUE", 0) == 0 ||
RTL::WideString::CompareWideStringInsensitive(Value, L"YES", 0) == 0)
{
/* This option is enabled */
return TRUE;
}
/* Return FALSE by default */
return FALSE;
}
/**
* @brief Retrieves the value of a specific OS boot option from a list.
*
* @param Options
* A pointer to the head of a list of XTBL_CONFIG_ENTRY structures.
*
* @param OptionName
* A pointer to wide string that contains the name of the boot option to retrieve.
*
* @param OptionValue
* A pointer to a variable that receives a pointer to the retrieved boot option's value.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::GetBootOptionValue(IN PLIST_ENTRY Options,
IN PCWSTR OptionName,
OUT PWCHAR *OptionValue)
{
PXTBL_CONFIG_ENTRY ConfigEntry;
PLIST_ENTRY ConfigList;
ULONG KeyLength, ValueLength;
EFI_STATUS Status;
/* Assume the option will not be found */
*OptionValue = NULLPTR;
/* Get the length of the option name we are looking for */
KeyLength = RTL::WideString::WideStringLength(OptionName, 0);
/* Start iterating from the first entry in the options list */
ConfigList = Options->Flink;
while(ConfigList != Options)
{
/* Get the container record for the current config entry */
ConfigEntry = CONTAIN_RECORD(ConfigList, XTBL_CONFIG_ENTRY, Flink);
/* Compare the current entry's name with the requested option name */
if(RTL::WideString::CompareWideStringInsensitive(ConfigEntry->Name, OptionName, KeyLength) == 0)
{
/* Found the option, now prepare to copy its value */
ValueLength = RTL::WideString::WideStringLength(ConfigEntry->Value, 0);
/* Allocate memory for the output value string */
Status = Memory::AllocatePool((ValueLength + 1) * sizeof(WCHAR), (PVOID *)OptionValue);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
Debug::Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status);
*OptionValue = NULLPTR;
return Status;
}
/* Copy the value and NULL-terminate the new string */
RTL::Memory::CopyMemory(*OptionValue, ConfigEntry->Value, ValueLength * sizeof(WCHAR));
(*OptionValue)[ValueLength] = L'\0';
/* Successfully retrieved the option value, return success */
return STATUS_EFI_SUCCESS;
}
/* Move to the next entry in the list */
ConfigList = ConfigList->Flink;
}
/* Option not found */
return STATUS_EFI_NOT_FOUND;
}
/**
* Retrieves the list of user-editable boot options.
*
* @param OptionsArray
* A pointer to a variable that will receive the pointer to the array of editable option names.
*
* @param OptionsCount
* A pointer to a variable that will be updated with the number of elements in the OptionsArray.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
VOID
Configuration::GetEditableOptions(OUT PCWSTR **OptionsArray,
OUT PULONG OptionsCount)
{
ULONG Count = 0;
/* Return a pointer to the global array of editable options */
*OptionsArray = EditableConfigOptions;
/* Calculate the number of elements in the array */
while(EditableConfigOptions[Count])
{
Count++;
}
/* Return the number of elements */
*OptionsCount = Count;
}
/**
* Returns a value of the specified configuration key.
*
* @param ConfigName
* Specifies the configuration key to return its value.
*
* @return This routine returns a pointer to the configuration value, or NULLPTR if key was not found.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::GetValue(IN PCWSTR ConfigName,
OUT PWCHAR *ConfigValue)
{
PXTBL_CONFIG_ENTRY ConfigEntry;
PLIST_ENTRY ConfigListEntry;
SIZE_T KeyLength, ValueLength;
EFI_STATUS Status;
PWCHAR Value;
/* Assume the option will not be found */
*ConfigValue = NULLPTR;
/* Get config entry name length */
KeyLength = RTL::WideString::WideStringLength(ConfigName, 0);
/* Iterate through config entries */
ConfigListEntry = Config.Flink;
while(ConfigListEntry != &Config)
{
/* Get config entry */
ConfigEntry = CONTAIN_RECORD(ConfigListEntry, XTBL_CONFIG_ENTRY, Flink);
/* Check if requested configuration found */
if(RTL::WideString::CompareWideStringInsensitive(ConfigEntry->Name, ConfigName, KeyLength) == 0)
{
/* Get value length */
ValueLength = RTL::WideString::WideStringLength(ConfigEntry->Value, 0);
/* Allocate memory for value */
Status = Memory::AllocatePool((ValueLength + 1) * sizeof(WCHAR), (PVOID *)&Value);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, return NULLPTR */
Debug::Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\n", Status);
return Status;
}
/* Copy value and return it */
RTL::Memory::CopyMemory(Value, ConfigEntry->Value, ValueLength * sizeof(WCHAR));
Value[ValueLength] = L'\0';
*ConfigValue = Value;
return STATUS_EFI_SUCCESS;
}
/* Move to the next config entry */
ConfigListEntry = ConfigListEntry->Flink;
}
/* Config entry not found, return NULLPTR */
return STATUS_EFI_NOT_FOUND;
}
/**
* Initializes a list of operating systems for XTLDR boot menu.
*
* @param MenuEntries
* Supplies a pointer to memory area where operating systems list will be stored.
*
* @param EntriesCount
* Supplies a pointer to memory area where number of menu entries will be stored.
*
* @param DefaultId
* Supplies a pointer to memory area where ID of default menu entry will be stored.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::InitializeBootMenuList(IN ULONG MaxNameLength,
OUT PXTBL_BOOTMENU_ITEM *MenuEntries,
OUT PULONG EntriesCount,
OUT PULONG DefaultId)
{
EFI_GUID VendorGuid = XT_BOOT_LOADER_PROTOCOL_GUID;
PWCHAR DefaultMenuEntry, LastBooted, MenuEntryName, VisibleName;
PLIST_ENTRY MenuEntrySectionList, MenuEntryList;
PXTBL_CONFIG_SECTION MenuEntrySection;
PXTBL_CONFIG_ENTRY MenuEntryOption;
ULONG DefaultOS, NameLength,NumberOfEntries;
PXTBL_BOOTMENU_ITEM OsList;
EFI_STATUS Status;
/* Set default values */
DefaultOS = 0;
NumberOfEntries = 0;
/* Get default menu entry from configuration */
Configuration::GetValue(L"DEFAULT", &DefaultMenuEntry);
/* Check if configuration allows to use last booted OS */
if(Configuration::GetBooleanValue(L"KEEPLASTBOOT"))
{
/* Attempt to get last booted Operating System from NVRAM */
Status = EfiUtils::GetEfiVariable(&VendorGuid, L"XtLdrLastBootOS", (PVOID*)&LastBooted);
if(Status == STATUS_EFI_SUCCESS)
{
/* Set default menu entry to last booted OS */
DefaultMenuEntry = LastBooted;
}
}
/* Iterate through menu items to get a total number of entries */
MenuEntrySectionList = BootMenuList->Flink;
while(MenuEntrySectionList != BootMenuList)
{
/* Increase number of menu entries, and simply get next item */
NumberOfEntries++;
MenuEntrySectionList = MenuEntrySectionList->Flink;
}
/* Allocate memory for the OS list depending on the item count */
Status = Memory::AllocatePool(NumberOfEntries * sizeof(XTBL_BOOTMENU_ITEM), (PVOID*)&OsList);
if(Status != STATUS_EFI_SUCCESS || !OsList)
{
/* Memory allocation failure */
return STATUS_EFI_OUT_OF_RESOURCES;
}
/* Reset counter and iterate through all menu items once again */
NumberOfEntries = 0;
MenuEntrySectionList = BootMenuList->Flink;
while(MenuEntrySectionList != BootMenuList)
{
/* NULLify menu entry name */
MenuEntryName = NULLPTR;
/* Get menu section */
MenuEntrySection = CONTAIN_RECORD(MenuEntrySectionList, XTBL_CONFIG_SECTION, Flink);
/* Check if this is the default menu entry */
if((RTL::WideString::WideStringLength(MenuEntrySection->SectionName, 0) == RTL::WideString::WideStringLength(DefaultMenuEntry, 0)) &&
(RTL::WideString::CompareWideStringInsensitive(MenuEntrySection->SectionName, DefaultMenuEntry, 0) == 0))
{
/* Set default OS ID */
DefaultOS = NumberOfEntries;
}
/* Iterate through all entry parameters */
MenuEntryList = MenuEntrySection->Options.Flink;
while(MenuEntryList != &MenuEntrySection->Options)
{
/* Get menu entry parameter */
MenuEntryOption = CONTAIN_RECORD(MenuEntryList, XTBL_CONFIG_ENTRY, Flink);
/* Check if this is the menu entry display name */
if(RTL::WideString::CompareWideStringInsensitive(MenuEntryOption->Name, L"SYSTEMNAME", 0) == 0)
{
/* Set menu entry display name */
MenuEntryName = MenuEntryOption->Value;
}
/* Get next parameter for this menu entry */
MenuEntryList = MenuEntryList->Flink;
}
/* Add OS to the boot menu list */
OsList[NumberOfEntries].FullName = MenuEntryName;
OsList[NumberOfEntries].ShortName = MenuEntrySection->SectionName;
OsList[NumberOfEntries].Options = &MenuEntrySection->Options;
/* Check if the menu entry name fits the maximum length */
NameLength = RTL::WideString::WideStringLength(MenuEntryName, 0);
if(NameLength > MaxNameLength)
{
/* Menu entry name is too long, allocate memory for shorter name visible in the boot menu */
Status = Memory::AllocatePool((MaxNameLength + 1) * sizeof(WCHAR), (PVOID*)&VisibleName);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure */
return STATUS_EFI_OUT_OF_RESOURCES;
}
/* Copy shorter name and append "..." at the end */
RTL::Memory::CopyMemory(VisibleName, MenuEntryName, (MaxNameLength - 3) * sizeof(WCHAR));
RTL::Memory::CopyMemory(VisibleName + MaxNameLength - 3, L"...", 3 * sizeof(WCHAR));
VisibleName[MaxNameLength] = L'\0';
/* Set visible menu entry name */
OsList[NumberOfEntries].EntryName = VisibleName;
}
else
{
/* Menu entry name fits the maximum length, use it as is */
OsList[NumberOfEntries].EntryName = MenuEntryName;
}
/* Get next menu entry */
MenuEntrySectionList = MenuEntrySectionList->Flink;
NumberOfEntries++;
}
/* Set return values */
*DefaultId = DefaultOS;
*EntriesCount = NumberOfEntries;
*MenuEntries = OsList;
/* Return success */
return STATUS_EFI_SUCCESS;
}
/**
* Initializes the XTLDR configuration subsystem.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
VOID
Configuration::InitializeConfiguration()
{
/* Initialize XTLDR configuration linked lists */
RTL::LinkedList::InitializeListHead(&Config);
}
/**
* Loads and parses XTLDR configuration file.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::LoadConfiguration()
{
PLIST_ENTRY SectionListEntry;
EFI_STATUS Status;
PCHAR ConfigData;
/* Initialize configuration pointer */
RTL::LinkedList::InitializeListHead(&ConfigSections);
/* Read data from configuration file */
Status = ReadConfigFile(XTBL_LOADER_DIRECTORY_PATH, L"XTLDR.INI", &ConfigData);
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to read config file, try with architecture specific directory */
Status = ReadConfigFile(XTBL_ARCH_LOADER_DIRECTORY_PATH, L"XTLDR.INI", &ConfigData);
}
/* Check if configuration was read successfully */
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to load configuration */
Debug::Print(L"Failed to load FS0:/EFI/BOOT/XTLDR/XTLDR.INI configuration file (Status Code: 0x%zX)\n", Status);
return Status;
}
/* Parse configuration data */
Status = ParseConfigFile(ConfigData, &ConfigSections);
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to parse configuration */
Debug::Print(L"Failed to parse FS0:/EFI/BOOT/XTLDR/XTLDR.INI configuration file (Status Code: 0x%zX)\n", Status);
return Status;
}
/* Iterate through config sections */
SectionListEntry = ConfigSections.Flink;
while(SectionListEntry != &ConfigSections)
{
/* Get config section */
PXTBL_CONFIG_SECTION Section = CONTAIN_RECORD(SectionListEntry, XTBL_CONFIG_SECTION, Flink);
/* Look for global XTLDR configuration section */
if(RTL::WideString::CompareWideStringInsensitive(Section->SectionName, L"XTLDR", 5) == 0)
{
/* Update global configuration */
UpdateConfiguration(&Section->Options);
/* Remove XTLDR section from the list */
RTL::LinkedList::RemoveEntryList(SectionListEntry);
break;
}
/* Move to the next section */
SectionListEntry = SectionListEntry->Flink;
}
/* Update boot menu OS list */
BootMenuList = &ConfigSections;
/* Return success */
return STATUS_EFI_SUCCESS;
}
/**
* Parses command line arguments and updates global configuration.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::ParseCommandLine(VOID)
{
EFI_GUID LIPGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
PEFI_LOADED_IMAGE_PROTOCOL LoadedImage;
PWCHAR Argument, Key, LastArg, Value;
PXTBL_CONFIG_ENTRY Option;
EFI_STATUS Status;
SIZE_T KeyLength, ValueLength;
LIST_ENTRY Config;
/* Initialize configuration list */
RTL::LinkedList::InitializeListHead(&Config);
/* Handle loaded image protocol */
Status = XtLoader::GetEfiSystemTable()->BootServices->HandleProtocol(XtLoader::GetEfiImageHandle(),
&LIPGuid, (PVOID *)&LoadedImage);
if(Status == STATUS_EFI_SUCCESS)
{
/* Check if launched from UEFI shell */
if(LoadedImage && LoadedImage->LoadOptions)
{
/* Tokenize provided options */
Argument = RTL::WideString::TokenizeWideString((PWCHAR)LoadedImage->LoadOptions, L" ", &LastArg);
/* Iterate over all arguments passed to boot loader */
while(Argument != NULLPTR)
{
/* Store key name */
Key = Argument;
/* Find end of the key */
while(*Argument != L'=' && *Argument != L'\0' && *Argument != L'\n')
{
/* Advance to the next character */
Argument++;
}
/* Mark end of the key and advance to the next character */
*Argument = L'\0';
Argument++;
/* Store value */
Value = Argument;
/* Find end of the value */
while(*Argument != L'\0' && *Argument != L'\n')
{
/* Advance to the next character */
Argument++;
}
/* Mark end of the value and advance to the next character */
*Argument = L'\0';
Argument++;
/* Get length of the key and its value */
KeyLength = RTL::WideString::WideStringLength(Key, 0);
ValueLength = RTL::WideString::WideStringLength(Value, 0);
/* Check if argument is valid */
if(KeyLength == 0 || ValueLength == 0)
{
/* Invalid argument, skip to the next one */
continue;
}
/* Allocate memory for new option */
Status = Memory::AllocatePool(sizeof(XTBL_CONFIG_ENTRY), (PVOID*)&Option);
if(Status == STATUS_EFI_SUCCESS)
{
/* Allocate more memory for option name */
Status = Memory::AllocatePool(sizeof(WCHAR) * (KeyLength + 1), (PVOID*)&Option->Name);
if(Status == STATUS_EFI_SUCCESS)
{
/* Allocate even more memory for option value */
Status = Memory::AllocatePool(sizeof(WCHAR) * (ValueLength + 1), (PVOID*)&Option->Value);
}
}
if(Status != STATUS_EFI_SUCCESS)
{
/* Some memory allocation failed */
return Status;
}
/* Set entry name and value */
RTL::Memory::CopyMemory(Option->Name, Key, (KeyLength * sizeof(WCHAR)));
RTL::Memory::CopyMemory(Option->Value, Value, (ValueLength * sizeof(WCHAR)));
Option->Name[KeyLength] = L'\0';
Option->Value[ValueLength] = L'\0';
/* Add entry to the list */
RTL::LinkedList::InsertTailList(&Config, &Option->Flink);
/* Take next argument */
Argument = RTL::WideString::TokenizeWideString(NULLPTR, L" ", &LastArg);
}
/* Update global configuration */
UpdateConfiguration(&Config);
}
}
/* Return success */
return STATUS_EFI_SUCCESS;
}
/**
* Parses configuration INI file.
*
* @param RawConfig
* Suplies a pointer to configuration INI file to be parsed.
*
* @param Configuration
* Supplies a pointer to memory region where parsed configuration will be stored.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::ParseConfigFile(IN CONST PCHAR RawConfig,
OUT PLIST_ENTRY Configuration)
{
SIZE_T SectionLength, KeyLength, ValueLength;
PCHAR InputData, Key, SectionName, Value;
PXTBL_CONFIG_SECTION Section;
PXTBL_CONFIG_ENTRY Option;
EFI_STATUS Status;
/* Initialize pointers */
InputData = RawConfig;
Section = NULLPTR;
Option = NULLPTR;
SectionName = NULLPTR;
Key = NULLPTR;
Value = NULLPTR;
/* Analyze configuration data until end of file is reached */
while(*InputData != '\0')
{
if(*InputData == ';' || *InputData == '#')
{
/* Skip comment until end of the line */
while(*InputData != '\0' && *InputData != '\n')
{
/* Advance to the next character */
InputData++;
}
}
else if(*InputData == ' ' || *InputData == '\t' || *InputData == '\r' || *InputData == '\n')
{
/* Skip whitespaces */
InputData++;
}
else if(*InputData == '[')
{
/* Skip leading bracket */
InputData++;
/* Store section name */
SectionName = InputData;
/* Find end of the section name */
while(*InputData != ']' && *InputData != '\0' && *InputData != '\n')
{
/* Advance to the next character */
InputData++;
}
/* Check if end of the section name is reached */
if(*InputData != ']')
{
/* Section name does not end */
return STATUS_EFI_INVALID_PARAMETER;
}
/* Mark end of the section name and advance to the next character */
*InputData = '\0';
InputData++;
/* Remove leading and trailing spaces from section name */
SectionName = RTL::String::TrimString(SectionName);
/* Find length of the section name */
SectionLength = RTL::String::StringLength(SectionName, 0);
/* Allocate memory for new section */
Status = Memory::AllocatePool(sizeof(XTBL_CONFIG_SECTION), (PVOID*)&Section);
if(Status == STATUS_EFI_SUCCESS)
{
/* Allocate more memory for section name */
Status = Memory::AllocatePool(sizeof(WCHAR) * (SectionLength + 1), (PVOID*)&Section->SectionName);
}
if(Status != STATUS_EFI_SUCCESS)
{
/* Some memory allocation failed */
return Status;
}
/* Initialize new section and convert its name to wide string */
RTL::LinkedList::InitializeListHead(&Section->Options);
RTL::String::StringToWideString(Section->SectionName, (PCSTR*)&SectionName, SectionLength);
/* Ensure string is NULL-terminated and add new section to the configuration list */
Section->SectionName[SectionLength] = L'\0';
RTL::LinkedList::InsertTailList(Configuration, &Section->Flink);
}
else
{
/* Store key */
Key = InputData;
/* Find end of the key */
while(*InputData != '=' && *InputData != '\0' && *InputData != '\n')
{
/* Advance to the next character */
InputData++;
}
/* Check if end of the key is reached */
if(*InputData != '=')
{
/* Key name does not end */
return STATUS_EFI_INVALID_PARAMETER;
}
/* Mark end of the key and advance to the next character */
*InputData = 0;
InputData++;
/* Skip all leading spaces in the value */
while(*InputData == ' ')
{
/* Advance to the next character */
InputData++;
}
/* Store value */
Value = InputData;
/* Find end of the value */
while(*InputData != '\0' && *InputData != '\n')
{
/* Advance to the next character */
InputData++;
}
/* Mark end of the value and advance to the next character */
*InputData = 0;
InputData++;
/* Remove leading and trailing spaces from key and value */
Key = RTL::String::TrimString(Key);
Value = RTL::String::TrimString(Value);
/* Find length of the key and its value */
KeyLength = RTL::String::StringLength(Key, 0);
ValueLength = RTL::String::StringLength(Value, 0);
/* Allocate memory for new option */
Status = Memory::AllocatePool(sizeof(XTBL_CONFIG_ENTRY), (PVOID*)&Option);
if(Status == STATUS_EFI_SUCCESS)
{
/* Allocate more memory for option name */
Status = Memory::AllocatePool(sizeof(WCHAR) * (KeyLength + 1), (PVOID*)&Option->Name);
if(Status == STATUS_EFI_SUCCESS)
{
/* Allocate even more memory for option value */
Status = Memory::AllocatePool(sizeof(WCHAR) * (ValueLength + 1), (PVOID*)&Option->Value);
}
}
if(Status != STATUS_EFI_SUCCESS)
{
/* Some memory allocation failed */
return Status;
}
/* Remove leading quotes from the value */
if(*Value == '"' || *Value == '\'')
{
Value++;
}
/* Remove trailing quotes from the value */
if(Value[ValueLength - 2] == '"' || Value[ValueLength - 2] == '\'')
{
Value[ValueLength - 2] = '\0';
}
/* Convert key and value to wide strings */
RTL::String::StringToWideString(Option->Name, (PCSTR*)&Key, RTL::String::StringLength(Key, 0) + 1);
RTL::String::StringToWideString(Option->Value, (PCSTR*)&Value, RTL::String::StringLength(Value, 0) + 1);
/* Ensure strings are NULL-terminated and add new option to the list */
Option->Name[KeyLength] = L'\0';
Option->Value[ValueLength] = L'\0';
RTL::LinkedList::InsertTailList(&Section->Options, &Option->Flink);
}
}
/* Return success */
return STATUS_EFI_SUCCESS;
}
/**
* Loads configuration file from the specified directory on the FS0:/ drive.
*
* @param ConfigDirectory
* Specifies a path to the directory containing the configuration file.
*
* @param ConfigFile
* Specifies the name of the configuration file.
*
* @param ConfigData
* Provides a buffer to store the data read from the configuration file.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::ReadConfigFile(IN PCWSTR ConfigDirectory,
IN PCWSTR ConfigFile,
OUT PCHAR *ConfigData)
{
PEFI_FILE_HANDLE DirHandle, FsHandle;
EFI_HANDLE DiskHandle;
EFI_STATUS Status;
SIZE_T FileSize;
/* Open EFI volume */
Status = Volume::OpenVolume(NULLPTR, &DiskHandle, &FsHandle);
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to open a volume */
return Status;
}
/* Open specified directory, containing the configuration file and close the FS immediately */
Status = FsHandle->Open(FsHandle, &DirHandle, (PWCHAR)ConfigDirectory, EFI_FILE_MODE_READ, 0);
FsHandle->Close(FsHandle);
/* Check if directory opened successfully */
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to open directory */
Volume::CloseVolume(&DiskHandle);
return Status;
}
/* Read configuration file and close directory */
Status = Volume::ReadFile(DirHandle, ConfigFile, (PVOID *)ConfigData, &FileSize);
DirHandle->Close(DirHandle);
/* Close EFI volume */
Volume::CloseVolume(&DiskHandle);
/* Return read status */
return Status;
}
/**
* Sets the value of a specific OS boot option in a list, or adds it if it doesn't exist.
*
* @param Options
* A pointer to the head of a list of XTBL_CONFIG_ENTRY structures.
*
* @param OptionName
* A pointer to a wide string that contains the name of the boot option to set.
*
* @param OptionValue
* A pointer to a wide string that contains the new value for the boot option.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::SetBootOptionValue(IN PLIST_ENTRY Options,
IN PCWSTR OptionName,
IN PCWSTR OptionValue)
{
PXTBL_CONFIG_ENTRY ConfigEntry;
PLIST_ENTRY ConfigList;
ULONG Length;
EFI_STATUS Status;
/* Get the length of the option name we are looking for */
Length = RTL::WideString::WideStringLength(OptionName, 0);
/* Start iterating from the first entry in the options list */
ConfigList = Options->Flink;
while(ConfigList != Options)
{
/* Get the container record for the current config entry */
ConfigEntry = CONTAIN_RECORD(ConfigList, XTBL_CONFIG_ENTRY, Flink);
/* Compare the current entry's name with the requested option name */
if(RTL::WideString::CompareWideStringInsensitive(ConfigEntry->Name, OptionName, Length) == 0)
{
/* Found the option, get its length */
Length = RTL::WideString::WideStringLength(OptionValue, 0);
/* Reallocate memory for the new value */
Status = Memory::FreePool(ConfigEntry->Value);
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to free memory, return status code */
return Status;
}
/* Allocate new memory for the updated value */
Status = Memory::AllocatePool((Length + 1) * sizeof(WCHAR), (PVOID *)&ConfigEntry->Value);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
Debug::Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
return Status;
}
/* Copy the value and NULL-terminate the new string */
RTL::Memory::CopyMemory(ConfigEntry->Value, OptionValue, Length * sizeof(WCHAR));
ConfigEntry->Value[Length] = L'\0';
return STATUS_EFI_SUCCESS;
}
/* Move to the next entry in the list */
ConfigList = ConfigList->Flink;
}
/* Option not found, allocate memory for the new one */
Status = Memory::AllocatePool(sizeof(XTBL_CONFIG_ENTRY), (PVOID *)&ConfigEntry);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
Debug::Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
return Status;
}
/* Allocate memory for the option name */
Length = RTL::WideString::WideStringLength(OptionName, 0);
Status = Memory::AllocatePool((Length + 1) * sizeof(WCHAR), (PVOID *)&ConfigEntry->Name);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
Debug::Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
Memory::FreePool(ConfigEntry);
return Status;
}
/* Copy the option name and NULL-terminate the new string */
RTL::Memory::CopyMemory(ConfigEntry->Name, OptionName, Length * sizeof(WCHAR));
ConfigEntry->Name[Length] = L'\0';
/* Allocate memory for the option value */
Length = RTL::WideString::WideStringLength(OptionValue, 0);
Status = Memory::AllocatePool((Length + 1) * sizeof(WCHAR), (PVOID *)&ConfigEntry->Value);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure, print debug message and return status code */
Debug::Print(L"ERROR: Memory allocation failure (Status Code: 0x%zX)\\n", Status);
Memory::FreePool(ConfigEntry->Name);
Memory::FreePool(ConfigEntry);
return Status;
}
/* Copy the value and NULL-terminate the new string */
RTL::Memory::CopyMemory(ConfigEntry->Value, OptionValue, Length * sizeof(WCHAR));
ConfigEntry->Value[Length] = L'\0';
/* Insert the new config entry at the end of the options list */
RTL::LinkedList::InsertTailList(Options, &ConfigEntry->Flink);
/* Return success */
return STATUS_EFI_SUCCESS;
}
/**
* Updates existing configuration value.
*
* @param ConfigName
* Specifies the configuration key to update.
*
* @param ConfigValue
* Specifies the new configuration value.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTCDECL
EFI_STATUS
Configuration::SetValue(IN PCWSTR ConfigName,
IN PCWSTR ConfigValue)
{
PXTBL_CONFIG_ENTRY ConfigEntry;
PLIST_ENTRY ConfigListEntry;
EFI_STATUS Status;
SIZE_T Length;
/* Get config entry name length */
Length = RTL::WideString::WideStringLength(ConfigName, 0);
/* Iterate through config entries */
ConfigListEntry = Config.Flink;
while(ConfigListEntry != &Config)
{
/* Get config entry */
ConfigEntry = CONTAIN_RECORD(ConfigListEntry, XTBL_CONFIG_ENTRY, Flink);
/* Check if requested configuration found */
if(RTL::WideString::CompareWideStringInsensitive(ConfigEntry->Name, ConfigName, Length) == 0)
{
/* Check new config value length */
Length = RTL::WideString::WideStringLength(ConfigValue, 0);
/* Reallocate memory for new config value */
Status = Memory::FreePool(ConfigEntry->Value);
if(Status == STATUS_EFI_SUCCESS)
{
/* Successfully freed memory, allocate a new pool */
Status = Memory::AllocatePool((Length + 1) * sizeof(WCHAR), (PVOID *)&ConfigEntry->Value);
}
/* Check memory reallocation status */
if(Status != STATUS_EFI_SUCCESS)
{
/* Failed to reallocate memory */
return Status;
}
/* Update config value */
RTL::Memory::CopyMemory(ConfigEntry->Value, ConfigValue, Length * sizeof(WCHAR));
ConfigEntry->Value[Length] = L'\0';
/* Return success */
return STATUS_EFI_SUCCESS;
}
/* Move to the next config entry */
ConfigListEntry = ConfigListEntry->Flink;
}
/* Config entry not found */
return STATUS_EFI_NOT_FOUND;
}
/**
* Adds new XTLDR configuration entries to the global configuration list. Existing entries are not overwritten.
*
* @param NewConfig
* Supplies a pointer to a linked list containing new configuration entries.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTCDECL
VOID
Configuration::UpdateConfiguration(IN PLIST_ENTRY NewConfig)
{
PXTBL_CONFIG_ENTRY ConfigEntry;
PWCHAR ConfigValue;
PLIST_ENTRY ConfigListEntry, NextListEntry;
/* Iterate through new config entries */
ConfigListEntry = NewConfig->Flink;
while(ConfigListEntry != NewConfig)
{
/* Get new config entry */
ConfigEntry = CONTAIN_RECORD(ConfigListEntry, XTBL_CONFIG_ENTRY, Flink);
/* Get next config entry */
NextListEntry = ConfigListEntry->Flink;
/* Make sure config entry does not exist yet */
GetValue(ConfigEntry->Name, &ConfigValue);
if(ConfigValue == NULLPTR)
{
/* Remove new config entry from input list and put it into global config list */
RTL::LinkedList::RemoveEntryList(&ConfigEntry->Flink);
RTL::LinkedList::InsertTailList(&Config, &ConfigEntry->Flink);
}
/* Move to the next new config entry */
ConfigListEntry = NextListEntry;
}
}