From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- roms/edk2/MdePkg/Library/UefiLib/UefiLib.c | 2013 ++++++++++++++++++++++++++++ 1 file changed, 2013 insertions(+) create mode 100644 roms/edk2/MdePkg/Library/UefiLib/UefiLib.c (limited to 'roms/edk2/MdePkg/Library/UefiLib/UefiLib.c') diff --git a/roms/edk2/MdePkg/Library/UefiLib/UefiLib.c b/roms/edk2/MdePkg/Library/UefiLib/UefiLib.c new file mode 100644 index 000000000..07c45d1e9 --- /dev/null +++ b/roms/edk2/MdePkg/Library/UefiLib/UefiLib.c @@ -0,0 +1,2013 @@ +/** @file + The UEFI Library provides functions and macros that simplify the development of + UEFI Drivers and UEFI Applications. These functions and macros help manage EFI + events, build simple locks utilizing EFI Task Priority Levels (TPLs), install + EFI Driver Model related protocols, manage Unicode string tables for UEFI Drivers, + and print messages on the console output and standard error devices. + + Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#include "UefiLibInternal.h" + +/** + Empty constructor function that is required to resolve dependencies between + libraries. + + ** DO NOT REMOVE ** + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The constructor executed correctly. + +**/ +EFI_STATUS +EFIAPI +UefiLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EFI_SUCCESS; +} + +/** + Compare whether two names of languages are identical. + + @param Language1 Name of language 1. + @param Language2 Name of language 2. + + @retval TRUE Language 1 and language 2 are the same. + @retval FALSE Language 1 and language 2 are not the same. + +**/ +BOOLEAN +CompareIso639LanguageCode ( + IN CONST CHAR8 *Language1, + IN CONST CHAR8 *Language2 + ) +{ + UINT32 Name1; + UINT32 Name2; + + Name1 = ReadUnaligned24 ((CONST UINT32 *) Language1); + Name2 = ReadUnaligned24 ((CONST UINT32 *) Language2); + + return (BOOLEAN) (Name1 == Name2); +} + +/** + Retrieves a pointer to the system configuration table from the EFI System Table + based on a specified GUID. + + This function searches the list of configuration tables stored in the EFI System Table + for a table with a GUID that matches TableGuid. If a match is found, then a pointer to + the configuration table is returned in Table., and EFI_SUCCESS is returned. If a matching GUID + is not found, then EFI_NOT_FOUND is returned. + If TableGuid is NULL, then ASSERT(). + If Table is NULL, then ASSERT(). + + @param TableGuid The pointer to table's GUID type. + @param Table The pointer to the table associated with TableGuid in the EFI System Table. + + @retval EFI_SUCCESS A configuration table matching TableGuid was found. + @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found. + +**/ +EFI_STATUS +EFIAPI +EfiGetSystemConfigurationTable ( + IN EFI_GUID *TableGuid, + OUT VOID **Table + ) +{ + EFI_SYSTEM_TABLE *SystemTable; + UINTN Index; + + ASSERT (TableGuid != NULL); + ASSERT (Table != NULL); + + SystemTable = gST; + *Table = NULL; + for (Index = 0; Index < SystemTable->NumberOfTableEntries; Index++) { + if (CompareGuid (TableGuid, &(SystemTable->ConfigurationTable[Index].VendorGuid))) { + *Table = SystemTable->ConfigurationTable[Index].VendorTable; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Creates and returns a notification event and registers that event with all the protocol + instances specified by ProtocolGuid. + + This function causes the notification function to be executed for every protocol of type + ProtocolGuid instance that exists in the system when this function is invoked. If there are + no instances of ProtocolGuid in the handle database at the time this function is invoked, + then the notification function is still executed one time. In addition, every time a protocol + of type ProtocolGuid instance is installed or reinstalled, the notification function is also + executed. This function returns the notification event that was created. + If ProtocolGuid is NULL, then ASSERT(). + If NotifyTpl is not a legal TPL value, then ASSERT(). + If NotifyFunction is NULL, then ASSERT(). + If Registration is NULL, then ASSERT(). + + + @param ProtocolGuid Supplies GUID of the protocol upon whose installation the event is fired. + @param NotifyTpl Supplies the task priority level of the event notifications. + @param NotifyFunction Supplies the function to notify when the event is signaled. + @param NotifyContext The context parameter to pass to NotifyFunction. + @param Registration A pointer to a memory location to receive the registration value. + This value is passed to LocateHandle() to obtain new handles that + have been added that support the ProtocolGuid-specified protocol. + + @return The notification event that was created. + +**/ +EFI_EVENT +EFIAPI +EfiCreateProtocolNotifyEvent( + IN EFI_GUID *ProtocolGuid, + IN EFI_TPL NotifyTpl, + IN EFI_EVENT_NOTIFY NotifyFunction, + IN VOID *NotifyContext, OPTIONAL + OUT VOID **Registration + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + ASSERT (ProtocolGuid != NULL); + ASSERT (NotifyFunction != NULL); + ASSERT (Registration != NULL); + + // + // Create the event + // + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NotifyTpl, + NotifyFunction, + NotifyContext, + &Event + ); + ASSERT_EFI_ERROR (Status); + + // + // Register for protocol notifications on this event + // + + Status = gBS->RegisterProtocolNotify ( + ProtocolGuid, + Event, + Registration + ); + + ASSERT_EFI_ERROR (Status); + + // + // Kick the event so we will perform an initial pass of + // current installed drivers + // + + gBS->SignalEvent (Event); + return Event; +} + +/** + Creates a named event that can be signaled with EfiNamedEventSignal(). + + This function creates an event using NotifyTpl, NoifyFunction, and NotifyContext. + This event is signaled with EfiNamedEventSignal(). This provides the ability for one or more + listeners on the same event named by the GUID specified by Name. + If Name is NULL, then ASSERT(). + If NotifyTpl is not a legal TPL value, then ASSERT(). + If NotifyFunction is NULL, then ASSERT(). + + @param Name Supplies the GUID name of the event. + @param NotifyTpl Supplies the task priority level of the event notifications. + @param NotifyFunction Supplies the function to notify when the event is signaled. + @param NotifyContext The context parameter to pass to NotifyFunction. + @param Registration A pointer to a memory location to receive the registration value. + + @retval EFI_SUCCESS A named event was created. + @retval EFI_OUT_OF_RESOURCES There are not enough resource to create the named event. + +**/ +EFI_STATUS +EFIAPI +EfiNamedEventListen ( + IN CONST EFI_GUID *Name, + IN EFI_TPL NotifyTpl, + IN EFI_EVENT_NOTIFY NotifyFunction, + IN CONST VOID *NotifyContext, OPTIONAL + OUT VOID *Registration OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + VOID *RegistrationLocal; + + ASSERT (Name != NULL); + ASSERT (NotifyFunction != NULL); + ASSERT (NotifyTpl <= TPL_HIGH_LEVEL); + + // + // Create event + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + NotifyTpl, + NotifyFunction, + (VOID *) NotifyContext, + &Event + ); + ASSERT_EFI_ERROR (Status); + + // + // The Registration is not optional to RegisterProtocolNotify(). + // To make it optional to EfiNamedEventListen(), may need to substitute with a local. + // + if (Registration != NULL) { + RegistrationLocal = Registration; + } else { + RegistrationLocal = &RegistrationLocal; + } + + // + // Register for an installation of protocol interface + // + + Status = gBS->RegisterProtocolNotify ( + (EFI_GUID *) Name, + Event, + RegistrationLocal + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Signals a named event created with EfiNamedEventListen(). + + This function signals the named event specified by Name. The named event must have been + created with EfiNamedEventListen(). + If Name is NULL, then ASSERT(). + + @param Name Supplies the GUID name of the event. + + @retval EFI_SUCCESS A named event was signaled. + @retval EFI_OUT_OF_RESOURCES There are not enough resource to signal the named event. + +**/ +EFI_STATUS +EFIAPI +EfiNamedEventSignal ( + IN CONST EFI_GUID *Name + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + ASSERT(Name != NULL); + + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + (EFI_GUID *) Name, + EFI_NATIVE_INTERFACE, + NULL + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->UninstallProtocolInterface ( + Handle, + (EFI_GUID *) Name, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Signals an event group by placing a new event in the group temporarily and + signaling it. + + @param[in] EventGroup Supplies the unique identifier of the event + group to signal. + + @retval EFI_SUCCESS The event group was signaled successfully. + @retval EFI_INVALID_PARAMETER EventGroup is NULL. + @return Error codes that report problems about event + creation or signaling. +**/ +EFI_STATUS +EFIAPI +EfiEventGroupSignal ( + IN CONST EFI_GUID *EventGroup + ) +{ + EFI_STATUS Status; + EFI_EVENT Event; + + if (EventGroup == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + EfiEventEmptyFunction, + NULL, + EventGroup, + &Event + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = gBS->SignalEvent (Event); + gBS->CloseEvent (Event); + + return Status; +} + +/** + An empty function that can be used as NotifyFunction parameter of + CreateEvent() or CreateEventEx(). + + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +EfiEventEmptyFunction ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ +} + +/** + Returns the current TPL. + + This function returns the current TPL. There is no EFI service to directly + retrieve the current TPL. Instead, the RaiseTPL() function is used to raise + the TPL to TPL_HIGH_LEVEL. This will return the current TPL. The TPL level + can then immediately be restored back to the current TPL level with a call + to RestoreTPL(). + + @return The current TPL. + +**/ +EFI_TPL +EFIAPI +EfiGetCurrentTpl ( + VOID + ) +{ + EFI_TPL Tpl; + + Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + gBS->RestoreTPL (Tpl); + + return Tpl; +} + + +/** + Initializes a basic mutual exclusion lock. + + This function initializes a basic mutual exclusion lock to the released state + and returns the lock. Each lock provides mutual exclusion access at its task + priority level. Since there is no preemption or multiprocessor support in EFI, + acquiring the lock only consists of raising to the locks TPL. + If Lock is NULL, then ASSERT(). + If Priority is not a valid TPL value, then ASSERT(). + + @param Lock A pointer to the lock data structure to initialize. + @param Priority EFI TPL is associated with the lock. + + @return The lock. + +**/ +EFI_LOCK * +EFIAPI +EfiInitializeLock ( + IN OUT EFI_LOCK *Lock, + IN EFI_TPL Priority + ) +{ + ASSERT (Lock != NULL); + ASSERT (Priority <= TPL_HIGH_LEVEL); + + Lock->Tpl = Priority; + Lock->OwnerTpl = TPL_APPLICATION; + Lock->Lock = EfiLockReleased ; + return Lock; +} + +/** + Acquires ownership of a lock. + + This function raises the system's current task priority level to the task + priority level of the mutual exclusion lock. Then, it places the lock in the + acquired state. + If Lock is NULL, then ASSERT(). + If Lock is not initialized, then ASSERT(). + If Lock is already in the acquired state, then ASSERT(). + + @param Lock A pointer to the lock to acquire. + +**/ +VOID +EFIAPI +EfiAcquireLock ( + IN EFI_LOCK *Lock + ) +{ + ASSERT (Lock != NULL); + ASSERT (Lock->Lock == EfiLockReleased); + + Lock->OwnerTpl = gBS->RaiseTPL (Lock->Tpl); + Lock->Lock = EfiLockAcquired; +} + +/** + Acquires ownership of a lock. + + This function raises the system's current task priority level to the task priority + level of the mutual exclusion lock. Then, it attempts to place the lock in the acquired state. + If the lock is already in the acquired state, then EFI_ACCESS_DENIED is returned. + Otherwise, EFI_SUCCESS is returned. + If Lock is NULL, then ASSERT(). + If Lock is not initialized, then ASSERT(). + + @param Lock A pointer to the lock to acquire. + + @retval EFI_SUCCESS The lock was acquired. + @retval EFI_ACCESS_DENIED The lock could not be acquired because it is already owned. + +**/ +EFI_STATUS +EFIAPI +EfiAcquireLockOrFail ( + IN EFI_LOCK *Lock + ) +{ + + ASSERT (Lock != NULL); + ASSERT (Lock->Lock != EfiLockUninitialized); + + if (Lock->Lock == EfiLockAcquired) { + // + // Lock is already owned, so bail out + // + return EFI_ACCESS_DENIED; + } + + Lock->OwnerTpl = gBS->RaiseTPL (Lock->Tpl); + + Lock->Lock = EfiLockAcquired; + + return EFI_SUCCESS; +} + +/** + Releases ownership of a lock. + + This function transitions a mutual exclusion lock from the acquired state to + the released state, and restores the system's task priority level to its + previous level. + If Lock is NULL, then ASSERT(). + If Lock is not initialized, then ASSERT(). + If Lock is already in the released state, then ASSERT(). + + @param Lock A pointer to the lock to release. + +**/ +VOID +EFIAPI +EfiReleaseLock ( + IN EFI_LOCK *Lock + ) +{ + EFI_TPL Tpl; + + ASSERT (Lock != NULL); + ASSERT (Lock->Lock == EfiLockAcquired); + + Tpl = Lock->OwnerTpl; + + Lock->Lock = EfiLockReleased; + + gBS->RestoreTPL (Tpl); +} + +/** + Tests whether a controller handle is being managed by a specific driver. + + This function tests whether the driver specified by DriverBindingHandle is + currently managing the controller specified by ControllerHandle. This test + is performed by evaluating if the the protocol specified by ProtocolGuid is + present on ControllerHandle and is was opened by DriverBindingHandle with an + attribute of EFI_OPEN_PROTOCOL_BY_DRIVER. + If ProtocolGuid is NULL, then ASSERT(). + + @param ControllerHandle A handle for a controller to test. + @param DriverBindingHandle Specifies the driver binding handle for the + driver. + @param ProtocolGuid Specifies the protocol that the driver specified + by DriverBindingHandle opens in its Start() + function. + + @retval EFI_SUCCESS ControllerHandle is managed by the driver + specified by DriverBindingHandle. + @retval EFI_UNSUPPORTED ControllerHandle is not managed by the driver + specified by DriverBindingHandle. + +**/ +EFI_STATUS +EFIAPI +EfiTestManagedDevice ( + IN CONST EFI_HANDLE ControllerHandle, + IN CONST EFI_HANDLE DriverBindingHandle, + IN CONST EFI_GUID *ProtocolGuid + ) +{ + EFI_STATUS Status; + VOID *ManagedInterface; + + ASSERT (ProtocolGuid != NULL); + + Status = gBS->OpenProtocol ( + ControllerHandle, + (EFI_GUID *) ProtocolGuid, + &ManagedInterface, + DriverBindingHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (!EFI_ERROR (Status)) { + gBS->CloseProtocol ( + ControllerHandle, + (EFI_GUID *) ProtocolGuid, + DriverBindingHandle, + ControllerHandle + ); + return EFI_UNSUPPORTED; + } + + if (Status != EFI_ALREADY_STARTED) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Tests whether a child handle is a child device of the controller. + + This function tests whether ChildHandle is one of the children of + ControllerHandle. This test is performed by checking to see if the protocol + specified by ProtocolGuid is present on ControllerHandle and opened by + ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER. + If ProtocolGuid is NULL, then ASSERT(). + + @param ControllerHandle A handle for a (parent) controller to test. + @param ChildHandle A child handle to test. + @param ProtocolGuid Supplies the protocol that the child controller + opens on its parent controller. + + @retval EFI_SUCCESS ChildHandle is a child of the ControllerHandle. + @retval EFI_UNSUPPORTED ChildHandle is not a child of the + ControllerHandle. + +**/ +EFI_STATUS +EFIAPI +EfiTestChildHandle ( + IN CONST EFI_HANDLE ControllerHandle, + IN CONST EFI_HANDLE ChildHandle, + IN CONST EFI_GUID *ProtocolGuid + ) +{ + EFI_STATUS Status; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer; + UINTN EntryCount; + UINTN Index; + + ASSERT (ProtocolGuid != NULL); + + // + // Retrieve the list of agents that are consuming the specific protocol + // on ControllerHandle. + // + Status = gBS->OpenProtocolInformation ( + ControllerHandle, + (EFI_GUID *) ProtocolGuid, + &OpenInfoBuffer, + &EntryCount + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Inspect if ChildHandle is one of the agents. + // + Status = EFI_UNSUPPORTED; + for (Index = 0; Index < EntryCount; Index++) { + if ((OpenInfoBuffer[Index].ControllerHandle == ChildHandle) && + (OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) { + Status = EFI_SUCCESS; + break; + } + } + + FreePool (OpenInfoBuffer); + return Status; +} + +/** + This function checks the supported languages list for a target language, + This only supports RFC 4646 Languages. + + @param SupportedLanguages The supported languages + @param TargetLanguage The target language + + @retval Returns EFI_SUCCESS if the language is supported, + EFI_UNSUPPORTED otherwise +**/ +EFI_STATUS +EFIAPI +IsLanguageSupported ( + IN CONST CHAR8 *SupportedLanguages, + IN CONST CHAR8 *TargetLanguage + ) +{ + UINTN Index; + while (*SupportedLanguages != 0) { + for (Index = 0; SupportedLanguages[Index] != 0 && SupportedLanguages[Index] != ';'; Index++); + if ((AsciiStrnCmp(SupportedLanguages, TargetLanguage, Index) == 0) && (TargetLanguage[Index] == 0)) { + return EFI_SUCCESS; + } + SupportedLanguages += Index; + for (; *SupportedLanguages != 0 && *SupportedLanguages == ';'; SupportedLanguages++); + } + + return EFI_UNSUPPORTED; +} + +/** + This function looks up a Unicode string in UnicodeStringTable. + + If Language is a member of SupportedLanguages and a Unicode string is found in + UnicodeStringTable that matches the language code specified by Language, then it + is returned in UnicodeString. + + @param Language A pointer to the ISO 639-2 language code for the + Unicode string to look up and return. + @param SupportedLanguages A pointer to the set of ISO 639-2 language codes + that the Unicode string table supports. Language + must be a member of this set. + @param UnicodeStringTable A pointer to the table of Unicode strings. + @param UnicodeString A pointer to the Unicode string from UnicodeStringTable + that matches the language specified by Language. + + @retval EFI_SUCCESS The Unicode string that matches the language + specified by Language was found + in the table of Unicode strings UnicodeStringTable, + and it was returned in UnicodeString. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER UnicodeString is NULL. + @retval EFI_UNSUPPORTED SupportedLanguages is NULL. + @retval EFI_UNSUPPORTED UnicodeStringTable is NULL. + @retval EFI_UNSUPPORTED The language specified by Language is not a + member of SupportedLanguages. + @retval EFI_UNSUPPORTED The language specified by Language is not + supported by UnicodeStringTable. + +**/ +EFI_STATUS +EFIAPI +LookupUnicodeString ( + IN CONST CHAR8 *Language, + IN CONST CHAR8 *SupportedLanguages, + IN CONST EFI_UNICODE_STRING_TABLE *UnicodeStringTable, + OUT CHAR16 **UnicodeString + ) +{ + // + // Make sure the parameters are valid + // + if (Language == NULL || UnicodeString == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // If there are no supported languages, or the Unicode String Table is empty, then the + // Unicode String specified by Language is not supported by this Unicode String Table + // + if (SupportedLanguages == NULL || UnicodeStringTable == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure Language is in the set of Supported Languages + // + while (*SupportedLanguages != 0) { + if (CompareIso639LanguageCode (Language, SupportedLanguages)) { + + // + // Search the Unicode String Table for the matching Language specifier + // + while (UnicodeStringTable->Language != NULL) { + if (CompareIso639LanguageCode (Language, UnicodeStringTable->Language)) { + + // + // A matching string was found, so return it + // + *UnicodeString = UnicodeStringTable->UnicodeString; + return EFI_SUCCESS; + } + + UnicodeStringTable++; + } + + return EFI_UNSUPPORTED; + } + + SupportedLanguages += 3; + } + + return EFI_UNSUPPORTED; +} + + + +/** + This function looks up a Unicode string in UnicodeStringTable. + + If Language is a member of SupportedLanguages and a Unicode string is found in + UnicodeStringTable that matches the language code specified by Language, then + it is returned in UnicodeString. + + @param Language A pointer to an ASCII string containing the ISO 639-2 or the + RFC 4646 language code for the Unicode string to look up and + return. If Iso639Language is TRUE, then this ASCII string is + not assumed to be Null-terminated, and only the first three + characters are used. If Iso639Language is FALSE, then this ASCII + string must be Null-terminated. + @param SupportedLanguages A pointer to a Null-terminated ASCII string that contains a + set of ISO 639-2 or RFC 4646 language codes that the Unicode + string table supports. Language must be a member of this set. + If Iso639Language is TRUE, then this string contains one or more + ISO 639-2 language codes with no separator characters. If Iso639Language + is FALSE, then is string contains one or more RFC 4646 language + codes separated by ';'. + @param UnicodeStringTable A pointer to the table of Unicode strings. Type EFI_UNICODE_STRING_TABLE + is defined in "Related Definitions". + @param UnicodeString A pointer to the Null-terminated Unicode string from UnicodeStringTable + that matches the language specified by Language. + @param Iso639Language Specifies the supported language code format. If it is TRUE, then + Language and SupportedLanguages follow ISO 639-2 language code format. + Otherwise, they follow RFC 4646 language code format. + + + @retval EFI_SUCCESS The Unicode string that matches the language specified by Language + was found in the table of Unicode strings UnicodeStringTable, and + it was returned in UnicodeString. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER UnicodeString is NULL. + @retval EFI_UNSUPPORTED SupportedLanguages is NULL. + @retval EFI_UNSUPPORTED UnicodeStringTable is NULL. + @retval EFI_UNSUPPORTED The language specified by Language is not a member of SupportedLanguages. + @retval EFI_UNSUPPORTED The language specified by Language is not supported by UnicodeStringTable. + +**/ +EFI_STATUS +EFIAPI +LookupUnicodeString2 ( + IN CONST CHAR8 *Language, + IN CONST CHAR8 *SupportedLanguages, + IN CONST EFI_UNICODE_STRING_TABLE *UnicodeStringTable, + OUT CHAR16 **UnicodeString, + IN BOOLEAN Iso639Language + ) +{ + BOOLEAN Found; + UINTN Index; + CHAR8 *LanguageString; + + // + // Make sure the parameters are valid + // + if (Language == NULL || UnicodeString == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // If there are no supported languages, or the Unicode String Table is empty, then the + // Unicode String specified by Language is not supported by this Unicode String Table + // + if (SupportedLanguages == NULL || UnicodeStringTable == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure Language is in the set of Supported Languages + // + Found = FALSE; + if (Iso639Language) { + while (*SupportedLanguages != 0) { + if (CompareIso639LanguageCode (Language, SupportedLanguages)) { + Found = TRUE; + break; + } + SupportedLanguages += 3; + } + } else { + Found = !IsLanguageSupported(Language, SupportedLanguages); + } + + + // + // If Language is not a member of SupportedLanguages, then return EFI_UNSUPPORTED + // + if (!Found) { + return EFI_UNSUPPORTED; + } + + // + // Search the Unicode String Table for the matching Language specifier + // + while (UnicodeStringTable->Language != NULL) { + LanguageString = UnicodeStringTable->Language; + while (0 != *LanguageString) { + for (Index = 0 ;LanguageString[Index] != 0 && LanguageString[Index] != ';'; Index++); + if (AsciiStrnCmp(LanguageString, Language, Index) == 0) { + *UnicodeString = UnicodeStringTable->UnicodeString; + return EFI_SUCCESS; + } + LanguageString += Index; + for (Index = 0 ;LanguageString[Index] != 0 && LanguageString[Index] == ';'; Index++); + } + UnicodeStringTable++; + } + + return EFI_UNSUPPORTED; +} + + +/** + This function adds a Unicode string to UnicodeStringTable. + + If Language is a member of SupportedLanguages then UnicodeString is added to + UnicodeStringTable. New buffers are allocated for both Language and + UnicodeString. The contents of Language and UnicodeString are copied into + these new buffers. These buffers are automatically freed when + FreeUnicodeStringTable() is called. + + @param Language A pointer to the ISO 639-2 language code for the Unicode + string to add. + @param SupportedLanguages A pointer to the set of ISO 639-2 language codes + that the Unicode string table supports. + Language must be a member of this set. + @param UnicodeStringTable A pointer to the table of Unicode strings. + @param UnicodeString A pointer to the Unicode string to add. + + @retval EFI_SUCCESS The Unicode string that matches the language + specified by Language was found in the table of + Unicode strings UnicodeStringTable, and it was + returned in UnicodeString. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER UnicodeString is NULL. + @retval EFI_INVALID_PARAMETER UnicodeString is an empty string. + @retval EFI_UNSUPPORTED SupportedLanguages is NULL. + @retval EFI_ALREADY_STARTED A Unicode string with language Language is + already present in UnicodeStringTable. + @retval EFI_OUT_OF_RESOURCES There is not enough memory to add another + Unicode string to UnicodeStringTable. + @retval EFI_UNSUPPORTED The language specified by Language is not a + member of SupportedLanguages. + +**/ +EFI_STATUS +EFIAPI +AddUnicodeString ( + IN CONST CHAR8 *Language, + IN CONST CHAR8 *SupportedLanguages, + IN OUT EFI_UNICODE_STRING_TABLE **UnicodeStringTable, + IN CONST CHAR16 *UnicodeString + ) +{ + UINTN NumberOfEntries; + EFI_UNICODE_STRING_TABLE *OldUnicodeStringTable; + EFI_UNICODE_STRING_TABLE *NewUnicodeStringTable; + UINTN UnicodeStringLength; + + // + // Make sure the parameter are valid + // + if (Language == NULL || UnicodeString == NULL || UnicodeStringTable == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // If there are no supported languages, then a Unicode String can not be added + // + if (SupportedLanguages == NULL) { + return EFI_UNSUPPORTED; + } + + // + // If the Unicode String is empty, then a Unicode String can not be added + // + if (UnicodeString[0] == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Make sure Language is a member of SupportedLanguages + // + while (*SupportedLanguages != 0) { + if (CompareIso639LanguageCode (Language, SupportedLanguages)) { + + // + // Determine the size of the Unicode String Table by looking for a NULL Language entry + // + NumberOfEntries = 0; + if (*UnicodeStringTable != NULL) { + OldUnicodeStringTable = *UnicodeStringTable; + while (OldUnicodeStringTable->Language != NULL) { + if (CompareIso639LanguageCode (Language, OldUnicodeStringTable->Language)) { + return EFI_ALREADY_STARTED; + } + + OldUnicodeStringTable++; + NumberOfEntries++; + } + } + + // + // Allocate space for a new Unicode String Table. It must hold the current number of + // entries, plus 1 entry for the new Unicode String, plus 1 entry for the end of table + // marker + // + NewUnicodeStringTable = AllocatePool ((NumberOfEntries + 2) * sizeof (EFI_UNICODE_STRING_TABLE)); + if (NewUnicodeStringTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // If the current Unicode String Table contains any entries, then copy them to the + // newly allocated Unicode String Table. + // + if (*UnicodeStringTable != NULL) { + CopyMem ( + NewUnicodeStringTable, + *UnicodeStringTable, + NumberOfEntries * sizeof (EFI_UNICODE_STRING_TABLE) + ); + } + + // + // Allocate space for a copy of the Language specifier + // + NewUnicodeStringTable[NumberOfEntries].Language = AllocateCopyPool (3, Language); + if (NewUnicodeStringTable[NumberOfEntries].Language == NULL) { + FreePool (NewUnicodeStringTable); + return EFI_OUT_OF_RESOURCES; + } + + // + // Compute the length of the Unicode String + // + for (UnicodeStringLength = 0; UnicodeString[UnicodeStringLength] != 0; UnicodeStringLength++) + ; + + // + // Allocate space for a copy of the Unicode String + // + NewUnicodeStringTable[NumberOfEntries].UnicodeString = AllocateCopyPool ( + (UnicodeStringLength + 1) * sizeof (CHAR16), + UnicodeString + ); + if (NewUnicodeStringTable[NumberOfEntries].UnicodeString == NULL) { + FreePool (NewUnicodeStringTable[NumberOfEntries].Language); + FreePool (NewUnicodeStringTable); + return EFI_OUT_OF_RESOURCES; + } + + // + // Mark the end of the Unicode String Table + // + NewUnicodeStringTable[NumberOfEntries + 1].Language = NULL; + NewUnicodeStringTable[NumberOfEntries + 1].UnicodeString = NULL; + + // + // Free the old Unicode String Table + // + if (*UnicodeStringTable != NULL) { + FreePool (*UnicodeStringTable); + } + + // + // Point UnicodeStringTable at the newly allocated Unicode String Table + // + *UnicodeStringTable = NewUnicodeStringTable; + + return EFI_SUCCESS; + } + + SupportedLanguages += 3; + } + + return EFI_UNSUPPORTED; +} + + +/** + This function adds the Null-terminated Unicode string specified by UnicodeString + to UnicodeStringTable. + + If Language is a member of SupportedLanguages then UnicodeString is added to + UnicodeStringTable. New buffers are allocated for both Language and UnicodeString. + The contents of Language and UnicodeString are copied into these new buffers. + These buffers are automatically freed when EfiLibFreeUnicodeStringTable() is called. + + @param Language A pointer to an ASCII string containing the ISO 639-2 or + the RFC 4646 language code for the Unicode string to add. + If Iso639Language is TRUE, then this ASCII string is not + assumed to be Null-terminated, and only the first three + chacters are used. If Iso639Language is FALSE, then this + ASCII string must be Null-terminated. + @param SupportedLanguages A pointer to a Null-terminated ASCII string that contains + a set of ISO 639-2 or RFC 4646 language codes that the Unicode + string table supports. Language must be a member of this set. + If Iso639Language is TRUE, then this string contains one or more + ISO 639-2 language codes with no separator characters. + If Iso639Language is FALSE, then is string contains one or more + RFC 4646 language codes separated by ';'. + @param UnicodeStringTable A pointer to the table of Unicode strings. Type EFI_UNICODE_STRING_TABLE + is defined in "Related Definitions". + @param UnicodeString A pointer to the Unicode string to add. + @param Iso639Language Specifies the supported language code format. If it is TRUE, + then Language and SupportedLanguages follow ISO 639-2 language code format. + Otherwise, they follow RFC 4646 language code format. + + @retval EFI_SUCCESS The Unicode string that matches the language specified by + Language was found in the table of Unicode strings UnicodeStringTable, + and it was returned in UnicodeString. + @retval EFI_INVALID_PARAMETER Language is NULL. + @retval EFI_INVALID_PARAMETER UnicodeString is NULL. + @retval EFI_INVALID_PARAMETER UnicodeString is an empty string. + @retval EFI_UNSUPPORTED SupportedLanguages is NULL. + @retval EFI_ALREADY_STARTED A Unicode string with language Language is already present in + UnicodeStringTable. + @retval EFI_OUT_OF_RESOURCES There is not enough memory to add another Unicode string UnicodeStringTable. + @retval EFI_UNSUPPORTED The language specified by Language is not a member of SupportedLanguages. + +**/ +EFI_STATUS +EFIAPI +AddUnicodeString2 ( + IN CONST CHAR8 *Language, + IN CONST CHAR8 *SupportedLanguages, + IN OUT EFI_UNICODE_STRING_TABLE **UnicodeStringTable, + IN CONST CHAR16 *UnicodeString, + IN BOOLEAN Iso639Language + ) +{ + UINTN NumberOfEntries; + EFI_UNICODE_STRING_TABLE *OldUnicodeStringTable; + EFI_UNICODE_STRING_TABLE *NewUnicodeStringTable; + UINTN UnicodeStringLength; + BOOLEAN Found; + UINTN Index; + CHAR8 *LanguageString; + + // + // Make sure the parameter are valid + // + if (Language == NULL || UnicodeString == NULL || UnicodeStringTable == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // If there are no supported languages, then a Unicode String can not be added + // + if (SupportedLanguages == NULL) { + return EFI_UNSUPPORTED; + } + + // + // If the Unicode String is empty, then a Unicode String can not be added + // + if (UnicodeString[0] == 0) { + return EFI_INVALID_PARAMETER; + } + + // + // Make sure Language is a member of SupportedLanguages + // + Found = FALSE; + if (Iso639Language) { + while (*SupportedLanguages != 0) { + if (CompareIso639LanguageCode (Language, SupportedLanguages)) { + Found = TRUE; + break; + } + SupportedLanguages += 3; + } + } else { + Found = !IsLanguageSupported(Language, SupportedLanguages); + } + // + // If Language is not a member of SupportedLanguages, then return EFI_UNSUPPORTED + // + if (!Found) { + return EFI_UNSUPPORTED; + } + + // + // Determine the size of the Unicode String Table by looking for a NULL Language entry + // + NumberOfEntries = 0; + if (*UnicodeStringTable != NULL) { + OldUnicodeStringTable = *UnicodeStringTable; + while (OldUnicodeStringTable->Language != NULL) { + LanguageString = OldUnicodeStringTable->Language; + + while (*LanguageString != 0) { + for (Index = 0; LanguageString[Index] != 0 && LanguageString[Index] != ';'; Index++); + + if (AsciiStrnCmp (Language, LanguageString, Index) == 0) { + return EFI_ALREADY_STARTED; + } + LanguageString += Index; + for (; *LanguageString != 0 && *LanguageString == ';'; LanguageString++); + } + OldUnicodeStringTable++; + NumberOfEntries++; + } + } + + // + // Allocate space for a new Unicode String Table. It must hold the current number of + // entries, plus 1 entry for the new Unicode String, plus 1 entry for the end of table + // marker + // + NewUnicodeStringTable = AllocatePool ((NumberOfEntries + 2) * sizeof (EFI_UNICODE_STRING_TABLE)); + if (NewUnicodeStringTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // If the current Unicode String Table contains any entries, then copy them to the + // newly allocated Unicode String Table. + // + if (*UnicodeStringTable != NULL) { + CopyMem ( + NewUnicodeStringTable, + *UnicodeStringTable, + NumberOfEntries * sizeof (EFI_UNICODE_STRING_TABLE) + ); + } + + // + // Allocate space for a copy of the Language specifier + // + NewUnicodeStringTable[NumberOfEntries].Language = AllocateCopyPool (AsciiStrSize(Language), Language); + if (NewUnicodeStringTable[NumberOfEntries].Language == NULL) { + FreePool (NewUnicodeStringTable); + return EFI_OUT_OF_RESOURCES; + } + + // + // Compute the length of the Unicode String + // + for (UnicodeStringLength = 0; UnicodeString[UnicodeStringLength] != 0; UnicodeStringLength++); + + // + // Allocate space for a copy of the Unicode String + // + NewUnicodeStringTable[NumberOfEntries].UnicodeString = AllocateCopyPool (StrSize (UnicodeString), UnicodeString); + if (NewUnicodeStringTable[NumberOfEntries].UnicodeString == NULL) { + FreePool (NewUnicodeStringTable[NumberOfEntries].Language); + FreePool (NewUnicodeStringTable); + return EFI_OUT_OF_RESOURCES; + } + + // + // Mark the end of the Unicode String Table + // + NewUnicodeStringTable[NumberOfEntries + 1].Language = NULL; + NewUnicodeStringTable[NumberOfEntries + 1].UnicodeString = NULL; + + // + // Free the old Unicode String Table + // + if (*UnicodeStringTable != NULL) { + FreePool (*UnicodeStringTable); + } + + // + // Point UnicodeStringTable at the newly allocated Unicode String Table + // + *UnicodeStringTable = NewUnicodeStringTable; + + return EFI_SUCCESS; +} + +/** + This function frees the table of Unicode strings in UnicodeStringTable. + + If UnicodeStringTable is NULL, then EFI_SUCCESS is returned. + Otherwise, each language code, and each Unicode string in the Unicode string + table are freed, and EFI_SUCCESS is returned. + + @param UnicodeStringTable A pointer to the table of Unicode strings. + + @retval EFI_SUCCESS The Unicode string table was freed. + +**/ +EFI_STATUS +EFIAPI +FreeUnicodeStringTable ( + IN EFI_UNICODE_STRING_TABLE *UnicodeStringTable + ) +{ + UINTN Index; + + // + // If the Unicode String Table is NULL, then it is already freed + // + if (UnicodeStringTable == NULL) { + return EFI_SUCCESS; + } + + // + // Loop through the Unicode String Table until we reach the end of table marker + // + for (Index = 0; UnicodeStringTable[Index].Language != NULL; Index++) { + + // + // Free the Language string from the Unicode String Table + // + FreePool (UnicodeStringTable[Index].Language); + + // + // Free the Unicode String from the Unicode String Table + // + if (UnicodeStringTable[Index].UnicodeString != NULL) { + FreePool (UnicodeStringTable[Index].UnicodeString); + } + } + + // + // Free the Unicode String Table itself + // + FreePool (UnicodeStringTable); + + return EFI_SUCCESS; +} + +#ifndef DISABLE_NEW_DEPRECATED_INTERFACES + +/** + [ATTENTION] This function will be deprecated for security reason. + + Returns a pointer to an allocated buffer that contains the contents of a + variable retrieved through the UEFI Runtime Service GetVariable(). The + returned buffer is allocated using AllocatePool(). The caller is responsible + for freeing this buffer with FreePool(). + + If Name is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + + @param[in] Name The pointer to a Null-terminated Unicode string. + @param[in] Guid The pointer to an EFI_GUID structure + + @retval NULL The variable could not be retrieved. + @retval NULL There are not enough resources available for the variable contents. + @retval Other A pointer to allocated buffer containing the variable contents. + +**/ +VOID * +EFIAPI +GetVariable ( + IN CONST CHAR16 *Name, + IN CONST EFI_GUID *Guid + ) +{ + EFI_STATUS Status; + UINTN Size; + VOID *Value; + + ASSERT (Name != NULL); + ASSERT (Guid != NULL); + + // + // Try to get the variable size. + // + Value = NULL; + Size = 0; + Status = gRT->GetVariable ((CHAR16 *) Name, (EFI_GUID *) Guid, NULL, &Size, Value); + if (Status != EFI_BUFFER_TOO_SMALL) { + return NULL; + } + + // + // Allocate buffer to get the variable. + // + Value = AllocatePool (Size); + if (Value == NULL) { + return NULL; + } + + // + // Get the variable data. + // + Status = gRT->GetVariable ((CHAR16 *) Name, (EFI_GUID *) Guid, NULL, &Size, Value); + if (EFI_ERROR (Status)) { + FreePool(Value); + return NULL; + } + + return Value; +} + +/** + [ATTENTION] This function will be deprecated for security reason. + + Returns a pointer to an allocated buffer that contains the contents of a + variable retrieved through the UEFI Runtime Service GetVariable(). This + function always uses the EFI_GLOBAL_VARIABLE GUID to retrieve variables. + The returned buffer is allocated using AllocatePool(). The caller is + responsible for freeing this buffer with FreePool(). + + If Name is NULL, then ASSERT(). + + @param[in] Name The pointer to a Null-terminated Unicode string. + + @retval NULL The variable could not be retrieved. + @retval NULL There are not enough resources available for the variable contents. + @retval Other A pointer to allocated buffer containing the variable contents. + +**/ +VOID * +EFIAPI +GetEfiGlobalVariable ( + IN CONST CHAR16 *Name + ) +{ + return GetVariable (Name, &gEfiGlobalVariableGuid); +} +#endif + +/** + Returns the status whether get the variable success. The function retrieves + variable through the UEFI Runtime Service GetVariable(). The + returned buffer is allocated using AllocatePool(). The caller is responsible + for freeing this buffer with FreePool(). + + If Name is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + If Value is NULL, then ASSERT(). + + @param[in] Name The pointer to a Null-terminated Unicode string. + @param[in] Guid The pointer to an EFI_GUID structure + @param[out] Value The buffer point saved the variable info. + @param[out] Size The buffer size of the variable. + + @return EFI_OUT_OF_RESOURCES Allocate buffer failed. + @return EFI_SUCCESS Find the specified variable. + @return Others Errors Return errors from call to gRT->GetVariable. + +**/ +EFI_STATUS +EFIAPI +GetVariable2 ( + IN CONST CHAR16 *Name, + IN CONST EFI_GUID *Guid, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + ASSERT (Name != NULL && Guid != NULL && Value != NULL); + + // + // Try to get the variable size. + // + BufferSize = 0; + *Value = NULL; + if (Size != NULL) { + *Size = 0; + } + + Status = gRT->GetVariable ((CHAR16 *) Name, (EFI_GUID *) Guid, NULL, &BufferSize, *Value); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Allocate buffer to get the variable. + // + *Value = AllocatePool (BufferSize); + ASSERT (*Value != NULL); + if (*Value == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get the variable data. + // + Status = gRT->GetVariable ((CHAR16 *) Name, (EFI_GUID *) Guid, NULL, &BufferSize, *Value); + if (EFI_ERROR (Status)) { + FreePool(*Value); + *Value = NULL; + } + + if (Size != NULL) { + *Size = BufferSize; + } + + return Status; +} + +/** Return the attributes of the variable. + + Returns the status whether get the variable success. The function retrieves + variable through the UEFI Runtime Service GetVariable(). The + returned buffer is allocated using AllocatePool(). The caller is responsible + for freeing this buffer with FreePool(). The attributes are returned if + the caller provides a valid Attribute parameter. + + If Name is NULL, then ASSERT(). + If Guid is NULL, then ASSERT(). + If Value is NULL, then ASSERT(). + + @param[in] Name The pointer to a Null-terminated Unicode string. + @param[in] Guid The pointer to an EFI_GUID structure + @param[out] Value The buffer point saved the variable info. + @param[out] Size The buffer size of the variable. + @param[out] Attr The pointer to the variable attributes as found in var store + + @retval EFI_OUT_OF_RESOURCES Allocate buffer failed. + @retval EFI_SUCCESS Find the specified variable. + @retval Others Errors Return errors from call to gRT->GetVariable. + +**/ +EFI_STATUS +EFIAPI +GetVariable3( + IN CONST CHAR16 *Name, + IN CONST EFI_GUID *Guid, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL, + OUT UINT32 *Attr OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + + ASSERT(Name != NULL && Guid != NULL && Value != NULL); + + // + // Try to get the variable size. + // + BufferSize = 0; + *Value = NULL; + if (Size != NULL) { + *Size = 0; + } + + if (Attr != NULL) { + *Attr = 0; + } + + Status = gRT->GetVariable((CHAR16 *)Name, (EFI_GUID *)Guid, Attr, &BufferSize, *Value); + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Allocate buffer to get the variable. + // + *Value = AllocatePool(BufferSize); + ASSERT(*Value != NULL); + if (*Value == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Get the variable data. + // + Status = gRT->GetVariable((CHAR16 *)Name, (EFI_GUID *)Guid, Attr, &BufferSize, *Value); + if (EFI_ERROR(Status)) { + FreePool(*Value); + *Value = NULL; + } + + if (Size != NULL) { + *Size = BufferSize; + } + + return Status; +} + +/** + Returns a pointer to an allocated buffer that contains the contents of a + variable retrieved through the UEFI Runtime Service GetVariable(). This + function always uses the EFI_GLOBAL_VARIABLE GUID to retrieve variables. + The returned buffer is allocated using AllocatePool(). The caller is + responsible for freeing this buffer with FreePool(). + + If Name is NULL, then ASSERT(). + If Value is NULL, then ASSERT(). + + @param[in] Name The pointer to a Null-terminated Unicode string. + @param[out] Value The buffer point saved the variable info. + @param[out] Size The buffer size of the variable. + + @return EFI_OUT_OF_RESOURCES Allocate buffer failed. + @return EFI_SUCCESS Find the specified variable. + @return Others Errors Return errors from call to gRT->GetVariable. + +**/ +EFI_STATUS +EFIAPI +GetEfiGlobalVariable2 ( + IN CONST CHAR16 *Name, + OUT VOID **Value, + OUT UINTN *Size OPTIONAL + ) +{ + return GetVariable2 (Name, &gEfiGlobalVariableGuid, Value, Size); +} + +/** + Returns a pointer to an allocated buffer that contains the best matching language + from a set of supported languages. + + This function supports both ISO 639-2 and RFC 4646 language codes, but language + code types may not be mixed in a single call to this function. The language + code returned is allocated using AllocatePool(). The caller is responsible for + freeing the allocated buffer using FreePool(). This function supports a variable + argument list that allows the caller to pass in a prioritized list of language + codes to test against all the language codes in SupportedLanguages. + + If SupportedLanguages is NULL, then ASSERT(). + + @param[in] SupportedLanguages A pointer to a Null-terminated ASCII string that + contains a set of language codes in the format + specified by Iso639Language. + @param[in] Iso639Language If not zero, then all language codes are assumed to be + in ISO 639-2 format. If zero, then all language + codes are assumed to be in RFC 4646 language format + @param[in] ... A variable argument list that contains pointers to + Null-terminated ASCII strings that contain one or more + language codes in the format specified by Iso639Language. + The first language code from each of these language + code lists is used to determine if it is an exact or + close match to any of the language codes in + SupportedLanguages. Close matches only apply to RFC 4646 + language codes, and the matching algorithm from RFC 4647 + is used to determine if a close match is present. If + an exact or close match is found, then the matching + language code from SupportedLanguages is returned. If + no matches are found, then the next variable argument + parameter is evaluated. The variable argument list + is terminated by a NULL. + + @retval NULL The best matching language could not be found in SupportedLanguages. + @retval NULL There are not enough resources available to return the best matching + language. + @retval Other A pointer to a Null-terminated ASCII string that is the best matching + language in SupportedLanguages. + +**/ +CHAR8 * +EFIAPI +GetBestLanguage ( + IN CONST CHAR8 *SupportedLanguages, + IN UINTN Iso639Language, + ... + ) +{ + VA_LIST Args; + CHAR8 *Language; + UINTN CompareLength; + UINTN LanguageLength; + CONST CHAR8 *Supported; + CHAR8 *BestLanguage; + + ASSERT (SupportedLanguages != NULL); + + VA_START (Args, Iso639Language); + while ((Language = VA_ARG (Args, CHAR8 *)) != NULL) { + // + // Default to ISO 639-2 mode + // + CompareLength = 3; + LanguageLength = MIN (3, AsciiStrLen (Language)); + + // + // If in RFC 4646 mode, then determine the length of the first RFC 4646 language code in Language + // + if (Iso639Language == 0) { + for (LanguageLength = 0; Language[LanguageLength] != 0 && Language[LanguageLength] != ';'; LanguageLength++); + } + + // + // Trim back the length of Language used until it is empty + // + while (LanguageLength > 0) { + // + // Loop through all language codes in SupportedLanguages + // + for (Supported = SupportedLanguages; *Supported != '\0'; Supported += CompareLength) { + // + // In RFC 4646 mode, then Loop through all language codes in SupportedLanguages + // + if (Iso639Language == 0) { + // + // Skip ';' characters in Supported + // + for (; *Supported != '\0' && *Supported == ';'; Supported++); + // + // Determine the length of the next language code in Supported + // + for (CompareLength = 0; Supported[CompareLength] != 0 && Supported[CompareLength] != ';'; CompareLength++); + // + // If Language is longer than the Supported, then skip to the next language + // + if (LanguageLength > CompareLength) { + continue; + } + } + // + // See if the first LanguageLength characters in Supported match Language + // + if (AsciiStrnCmp (Supported, Language, LanguageLength) == 0) { + VA_END (Args); + // + // Allocate, copy, and return the best matching language code from SupportedLanguages + // + BestLanguage = AllocateZeroPool (CompareLength + 1); + if (BestLanguage == NULL) { + return NULL; + } + return CopyMem (BestLanguage, Supported, CompareLength); + } + } + + if (Iso639Language != 0) { + // + // If ISO 639 mode, then each language can only be tested once + // + LanguageLength = 0; + } else { + // + // If RFC 4646 mode, then trim Language from the right to the next '-' character + // + for (LanguageLength--; LanguageLength > 0 && Language[LanguageLength] != '-'; LanguageLength--); + } + } + } + VA_END (Args); + + // + // No matches were found + // + return NULL; +} + +/** + Returns an array of protocol instance that matches the given protocol. + + @param[in] Protocol Provides the protocol to search for. + @param[out] NoProtocols The number of protocols returned in Buffer. + @param[out] Buffer A pointer to the buffer to return the requested + array of protocol instances that match Protocol. + The returned buffer is allocated using + EFI_BOOT_SERVICES.AllocatePool(). The caller is + responsible for freeing this buffer with + EFI_BOOT_SERVICES.FreePool(). + + @retval EFI_SUCCESS The array of protocols was returned in Buffer, + and the number of protocols in Buffer was + returned in NoProtocols. + @retval EFI_NOT_FOUND No protocols found. + @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the + matching results. + @retval EFI_INVALID_PARAMETER Protocol is NULL. + @retval EFI_INVALID_PARAMETER NoProtocols is NULL. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + +**/ +EFI_STATUS +EFIAPI +EfiLocateProtocolBuffer ( + IN EFI_GUID *Protocol, + OUT UINTN *NoProtocols, + OUT VOID ***Buffer + ) +{ + EFI_STATUS Status; + UINTN NoHandles; + EFI_HANDLE *HandleBuffer; + UINTN Index; + + // + // Check input parameters + // + if (Protocol == NULL || NoProtocols == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Initialze output parameters + // + *NoProtocols = 0; + *Buffer = NULL; + + // + // Retrieve the array of handles that support Protocol + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + Protocol, + NULL, + &NoHandles, + &HandleBuffer + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Allocate array of protocol instances + // + Status = gBS->AllocatePool ( + EfiBootServicesData, + NoHandles * sizeof (VOID *), + (VOID **)Buffer + ); + if (EFI_ERROR (Status)) { + // + // Free the handle buffer + // + gBS->FreePool (HandleBuffer); + return EFI_OUT_OF_RESOURCES; + } + ZeroMem (*Buffer, NoHandles * sizeof (VOID *)); + + // + // Lookup Protocol on each handle in HandleBuffer to fill in the array of + // protocol instances. Handle case where protocol instance was present when + // LocateHandleBuffer() was called, but is not present when HandleProtocol() + // is called. + // + for (Index = 0, *NoProtocols = 0; Index < NoHandles; Index++) { + Status = gBS->HandleProtocol ( + HandleBuffer[Index], + Protocol, + &((*Buffer)[*NoProtocols]) + ); + if (!EFI_ERROR (Status)) { + (*NoProtocols)++; + } + } + + // + // Free the handle buffer + // + gBS->FreePool (HandleBuffer); + + // + // Make sure at least one protocol instance was found + // + if (*NoProtocols == 0) { + gBS->FreePool (*Buffer); + *Buffer = NULL; + return EFI_NOT_FOUND; + } + + return EFI_SUCCESS; +} + +/** + Open or create a file or directory, possibly creating the chain of + directories leading up to the directory. + + EfiOpenFileByDevicePath() first locates EFI_SIMPLE_FILE_SYSTEM_PROTOCOL on + FilePath, and opens the root directory of that filesystem with + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume(). + + On the remaining device path, the longest initial sequence of + FILEPATH_DEVICE_PATH nodes is node-wise traversed with + EFI_FILE_PROTOCOL.Open(). + + (As a consequence, if OpenMode includes EFI_FILE_MODE_CREATE, and Attributes + includes EFI_FILE_DIRECTORY, and each FILEPATH_DEVICE_PATH specifies a single + pathname component, then EfiOpenFileByDevicePath() ensures that the specified + series of subdirectories exist on return.) + + The EFI_FILE_PROTOCOL identified by the last FILEPATH_DEVICE_PATH node is + output to the caller; intermediate EFI_FILE_PROTOCOL instances are closed. If + there are no FILEPATH_DEVICE_PATH nodes past the node that identifies the + filesystem, then the EFI_FILE_PROTOCOL of the root directory of the + filesystem is output to the caller. If a device path node that is different + from FILEPATH_DEVICE_PATH is encountered relative to the filesystem, the + traversal is stopped with an error, and a NULL EFI_FILE_PROTOCOL is output. + + @param[in,out] FilePath On input, the device path to the file or directory + to open or create. The caller is responsible for + ensuring that the device path pointed-to by FilePath + is well-formed. On output, FilePath points one past + the last node in the original device path that has + been successfully processed. FilePath is set on + output even if EfiOpenFileByDevicePath() returns an + error. + + @param[out] File On error, File is set to NULL. On success, File is + set to the EFI_FILE_PROTOCOL of the root directory + of the filesystem, if there are no + FILEPATH_DEVICE_PATH nodes in FilePath; otherwise, + File is set to the EFI_FILE_PROTOCOL identified by + the last node in FilePath. + + @param[in] OpenMode The OpenMode parameter to pass to + EFI_FILE_PROTOCOL.Open(). + + @param[in] Attributes The Attributes parameter to pass to + EFI_FILE_PROTOCOL.Open(). + + @retval EFI_SUCCESS The file or directory has been opened or + created. + + @retval EFI_INVALID_PARAMETER FilePath is NULL; or File is NULL; or FilePath + contains a device path node, past the node + that identifies + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL, that is not a + FILEPATH_DEVICE_PATH node. + + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + + @return Error codes propagated from the + LocateDevicePath() and OpenProtocol() boot + services, and from the + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume() + and EFI_FILE_PROTOCOL.Open() member functions. +**/ +EFI_STATUS +EFIAPI +EfiOpenFileByDevicePath ( + IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath, + OUT EFI_FILE_PROTOCOL **File, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + EFI_STATUS Status; + EFI_HANDLE FileSystemHandle; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem; + EFI_FILE_PROTOCOL *LastFile; + FILEPATH_DEVICE_PATH *FilePathNode; + CHAR16 *AlignedPathName; + CHAR16 *PathName; + EFI_FILE_PROTOCOL *NextFile; + + if (File == NULL) { + return EFI_INVALID_PARAMETER; + } + *File = NULL; + + if (FilePath == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Look up the filesystem. + // + Status = gBS->LocateDevicePath ( + &gEfiSimpleFileSystemProtocolGuid, + FilePath, + &FileSystemHandle + ); + if (EFI_ERROR (Status)) { + return Status; + } + Status = gBS->OpenProtocol ( + FileSystemHandle, + &gEfiSimpleFileSystemProtocolGuid, + (VOID **)&FileSystem, + gImageHandle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Open the root directory of the filesystem. After this operation succeeds, + // we have to release LastFile on error. + // + Status = FileSystem->OpenVolume (FileSystem, &LastFile); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Traverse the device path nodes relative to the filesystem. + // + while (!IsDevicePathEnd (*FilePath)) { + if (DevicePathType (*FilePath) != MEDIA_DEVICE_PATH || + DevicePathSubType (*FilePath) != MEDIA_FILEPATH_DP) { + Status = EFI_INVALID_PARAMETER; + goto CloseLastFile; + } + FilePathNode = (FILEPATH_DEVICE_PATH *)*FilePath; + + // + // FilePathNode->PathName may be unaligned, and the UEFI specification + // requires pointers that are passed to protocol member functions to be + // aligned. Create an aligned copy of the pathname if necessary. + // + if ((UINTN)FilePathNode->PathName % sizeof *FilePathNode->PathName == 0) { + AlignedPathName = NULL; + PathName = FilePathNode->PathName; + } else { + AlignedPathName = AllocateCopyPool ( + (DevicePathNodeLength (FilePathNode) - + SIZE_OF_FILEPATH_DEVICE_PATH), + FilePathNode->PathName + ); + if (AlignedPathName == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto CloseLastFile; + } + PathName = AlignedPathName; + } + + // + // Open or create the file corresponding to the next pathname fragment. + // + Status = LastFile->Open ( + LastFile, + &NextFile, + PathName, + OpenMode, + Attributes + ); + + // + // Release any AlignedPathName on both error and success paths; PathName is + // no longer needed. + // + if (AlignedPathName != NULL) { + FreePool (AlignedPathName); + } + if (EFI_ERROR (Status)) { + goto CloseLastFile; + } + + // + // Advance to the next device path node. + // + LastFile->Close (LastFile); + LastFile = NextFile; + *FilePath = NextDevicePathNode (FilePathNode); + } + + *File = LastFile; + return EFI_SUCCESS; + +CloseLastFile: + LastFile->Close (LastFile); + + // + // We are on the error path; we must have set an error Status for returning + // to the caller. + // + ASSERT (EFI_ERROR (Status)); + return Status; +} -- cgit