Home | History | Annotate | Download | only in UefiLib
      1 /*++
      2 
      3 Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
      4 This program and the accompanying materials
      5 are licensed and made available under the terms and conditions of the BSD License
      6 which accompanies this distribution.  The full text of the license may be found at
      7 http://opensource.org/licenses/bsd-license.php
      8 
      9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     11 
     12 
     13 **/
     14 
     15 #include "EdkIIGlueUefi.h"
     16 #include "Library/EdkIIGlueMemoryAllocationLib.h"
     17 
     18 
     19 /**
     20   This function searches the list of configuration tables stored in the EFI System
     21   Table for a table with a GUID that matches TableGuid.  If a match is found,
     22   then a pointer to the configuration table is returned in Table, and EFI_SUCCESS
     23   is returned.  If a matching GUID is not found, then EFI_NOT_FOUND is returned.
     24 
     25   @param  TableGuid       Pointer to table's GUID type..
     26   @param  Table           Pointer to the table associated with TableGuid in the EFI System Table.
     27 
     28   @retval EFI_SUCCESS     A configuration table matching TableGuid was found.
     29   @retval EFI_NOT_FOUND   A configuration table matching TableGuid could not be found.
     30 
     31 **/
     32 EFI_STATUS
     33 EFIAPI
     34 EfiGetSystemConfigurationTable (
     35   IN  EFI_GUID  *TableGuid,
     36   OUT VOID      **Table
     37   )
     38 {
     39   EFI_SYSTEM_TABLE  *SystemTable;
     40   UINTN             Index;
     41 
     42   ASSERT (TableGuid != NULL);
     43   ASSERT (Table != NULL);
     44 
     45   SystemTable = gST;
     46   *Table = NULL;
     47   for (Index = 0; Index < SystemTable->NumberOfTableEntries; Index++) {
     48     if (CompareGuid (TableGuid, &(SystemTable->ConfigurationTable[Index].VendorGuid))) {
     49       *Table = SystemTable->ConfigurationTable[Index].VendorTable;
     50       return EFI_SUCCESS;
     51     }
     52   }
     53 
     54   return EFI_NOT_FOUND;
     55 }
     56 
     57 /**
     58   This function causes the notification function to be executed for every protocol
     59   of type ProtocolGuid instance that exists in the system when this function is
     60   invoked.  In addition, every time a protocol of type ProtocolGuid instance is
     61   installed or reinstalled, the notification function is also executed.
     62 
     63   @param  ProtocolGuid    Supplies GUID of the protocol upon whose installation the event is fired.
     64   @param  NotifyTpl       Supplies the task priority level of the event notifications.
     65   @param  NotifyFunction  Supplies the function to notify when the event is signaled.
     66   @param  NotifyContext   The context parameter to pass to NotifyFunction.
     67   @param  Registration    A pointer to a memory location to receive the registration value.
     68 
     69   @return The notification event that was created.
     70 
     71 **/
     72 EFI_EVENT
     73 EFIAPI
     74 EfiCreateProtocolNotifyEvent(
     75   IN  EFI_GUID          *ProtocolGuid,
     76   IN  EFI_TPL           NotifyTpl,
     77   IN  EFI_EVENT_NOTIFY  NotifyFunction,
     78   IN  VOID              *NotifyContext,  OPTIONAL
     79   OUT VOID              **Registration
     80   )
     81 {
     82   EFI_STATUS  Status;
     83   EFI_EVENT   Event;
     84 
     85   //
     86   // Create the event
     87   //
     88 
     89   Status = gBS->CreateEvent (
     90                   EFI_EVENT_NOTIFY_SIGNAL,
     91                   NotifyTpl,
     92                   NotifyFunction,
     93                   NotifyContext,
     94                   &Event
     95                   );
     96   ASSERT_EFI_ERROR (Status);
     97 
     98   //
     99   // Register for protocol notifactions on this event
    100   //
    101 
    102   Status = gBS->RegisterProtocolNotify (
    103                   ProtocolGuid,
    104                   Event,
    105                   Registration
    106                   );
    107 
    108   ASSERT_EFI_ERROR (Status);
    109 
    110   //
    111   // Kick the event so we will perform an initial pass of
    112   // current installed drivers
    113   //
    114 
    115   gBS->SignalEvent (Event);
    116   return Event;
    117 }
    118 
    119 /**
    120   This function creates an event using NotifyTpl, NoifyFunction, and NotifyContext.
    121   This event is signaled with EfiNamedEventSignal().  This provide the ability for
    122   one or more listeners on the same event named by the GUID specified by Name.
    123 
    124   @param  Name                  Supplies GUID name of the event.
    125   @param  NotifyTpl             Supplies the task priority level of the event notifications.
    126   @param  NotifyFunction        Supplies the function to notify when the event is signaled.
    127   @param  NotifyContext         The context parameter to pass to NotifyFunction.
    128   @param  Registration          A pointer to a memory location to receive the registration value.
    129 
    130   @retval EFI_SUCCESS           A named event was created.
    131   @retval EFI_OUT_OF_RESOURCES  There are not enough resource to create the named event.
    132 
    133 **/
    134 EFI_STATUS
    135 EFIAPI
    136 EfiNamedEventListen (
    137   IN CONST EFI_GUID    *Name,
    138   IN EFI_TPL           NotifyTpl,
    139   IN EFI_EVENT_NOTIFY  NotifyFunction,
    140   IN CONST VOID        *NotifyContext,  OPTIONAL
    141   OUT VOID             *Registration OPTIONAL
    142   )
    143 {
    144   EFI_STATUS  Status;
    145   EFI_EVENT   Event;
    146   VOID        *RegistrationLocal;
    147 
    148   //
    149   // Create event
    150   //
    151   Status = gBS->CreateEvent (
    152                   EFI_EVENT_NOTIFY_SIGNAL,
    153                   NotifyTpl,
    154                   NotifyFunction,
    155                   (VOID *) NotifyContext,
    156                   &Event
    157                   );
    158   ASSERT_EFI_ERROR (Status);
    159 
    160   //
    161   // The Registration is not optional to RegisterProtocolNotify().
    162   // To make it optional to EfiNamedEventListen(), may need to substitute with a local.
    163   //
    164   if (Registration != NULL) {
    165     RegistrationLocal = Registration;
    166   } else {
    167     RegistrationLocal = &RegistrationLocal;
    168   }
    169 
    170   //
    171   // Register for an installation of protocol interface
    172   //
    173 
    174   Status = gBS->RegisterProtocolNotify (
    175                   (EFI_GUID *) Name,
    176                   Event,
    177                   RegistrationLocal
    178                   );
    179   ASSERT_EFI_ERROR (Status);
    180 
    181   return EFI_SUCCESS;
    182 }
    183 
    184 /**
    185   This function signals the named event specified by Name.  The named event must
    186   have been created with EfiNamedEventListen().
    187 
    188   @param  Name                  Supplies GUID name of the event.
    189 
    190   @retval EFI_SUCCESS           A named event was signaled.
    191   @retval EFI_OUT_OF_RESOURCES  There are not enough resource to signal the named event.
    192 
    193 **/
    194 EFI_STATUS
    195 EFIAPI
    196 EfiNamedEventSignal (
    197   IN CONST EFI_GUID  *Name
    198   )
    199 {
    200   EFI_STATUS  Status;
    201   EFI_HANDLE  Handle;
    202 
    203   Handle = NULL;
    204   Status = gBS->InstallProtocolInterface (
    205                   &Handle,
    206                   (EFI_GUID *) Name,
    207                   EFI_NATIVE_INTERFACE,
    208                   NULL
    209                   );
    210   ASSERT_EFI_ERROR (Status);
    211 
    212   Status = gBS->UninstallProtocolInterface (
    213                   Handle,
    214                   (EFI_GUID *) Name,
    215                   NULL
    216                   );
    217   ASSERT_EFI_ERROR (Status);
    218 
    219   return EFI_SUCCESS;
    220 }
    221 
    222 /**
    223   Returns the current TPL.
    224 
    225   This function returns the current TPL.  There is no EFI service to directly
    226   retrieve the current TPL. Instead, the RaiseTPL() function is used to raise
    227   the TPL to TPL_HIGH_LEVEL.  This will return the current TPL.  The TPL level
    228   can then immediately be restored back to the current TPL level with a call
    229   to RestoreTPL().
    230 
    231   @param  VOID
    232 
    233   @retvale EFI_TPL              The current TPL.
    234 
    235 **/
    236 EFI_TPL
    237 EFIAPI
    238 EfiGetCurrentTpl (
    239   VOID
    240   )
    241 {
    242   EFI_TPL Tpl;
    243 
    244   Tpl = gBS->RaiseTPL (EFI_TPL_HIGH_LEVEL);
    245   gBS->RestoreTPL (Tpl);
    246 
    247   return Tpl;
    248 }
    249 
    250 
    251 /**
    252   This function initializes a basic mutual exclusion lock to the released state
    253   and returns the lock.  Each lock provides mutual exclusion access at its task
    254   priority level.  Since there is no preemption or multiprocessor support in EFI,
    255   acquiring the lock only consists of raising to the locks TPL.
    256 
    257   @param  Lock       A pointer to the lock data structure to initialize.
    258   @param  Priority   EFI TPL associated with the lock.
    259 
    260   @return The lock.
    261 
    262 **/
    263 EFI_LOCK *
    264 EFIAPI
    265 GlueEfiInitializeLock (
    266   IN OUT EFI_LOCK  *Lock,
    267   IN EFI_TPL        Priority
    268   )
    269 {
    270   ASSERT (Lock != NULL);
    271   ASSERT (Priority <= EFI_TPL_HIGH_LEVEL);
    272 
    273   Lock->Tpl       = Priority;
    274   Lock->OwnerTpl  = EFI_TPL_APPLICATION;
    275   Lock->Lock      = EfiLockReleased ;
    276   return Lock;
    277 }
    278 
    279 /**
    280   This function raises the system's current task priority level to the task
    281   priority level of the mutual exclusion lock.  Then, it places the lock in the
    282   acquired state.
    283 
    284   @param  Priority  The task priority level of the lock.
    285 
    286 **/
    287 VOID
    288 EFIAPI
    289 GlueEfiAcquireLock (
    290   IN EFI_LOCK  *Lock
    291   )
    292 {
    293   ASSERT (Lock != NULL);
    294   ASSERT (Lock->Lock == EfiLockReleased);
    295 
    296   Lock->OwnerTpl = gBS->RaiseTPL (Lock->Tpl);
    297   Lock->Lock     = EfiLockAcquired;
    298 }
    299 
    300 /**
    301   This function raises the system's current task priority level to the task
    302   priority level of the mutual exclusion lock.  Then, it attempts to place the
    303   lock in the acquired state.
    304 
    305   @param  Lock              A pointer to the lock to acquire.
    306 
    307   @retval EFI_SUCCESS       The lock was acquired.
    308   @retval EFI_ACCESS_DENIED The lock could not be acquired because it is already owned.
    309 
    310 **/
    311 EFI_STATUS
    312 EFIAPI
    313 GlueEfiAcquireLockOrFail (
    314   IN EFI_LOCK  *Lock
    315   )
    316 {
    317 
    318   ASSERT (Lock != NULL);
    319   ASSERT (Lock->Lock != EfiLockUninitialized);
    320 
    321   if (Lock->Lock == EfiLockAcquired) {
    322     //
    323     // Lock is already owned, so bail out
    324     //
    325     return EFI_ACCESS_DENIED;
    326   }
    327 
    328   Lock->OwnerTpl = gBS->RaiseTPL (Lock->Tpl);
    329 
    330   Lock->Lock = EfiLockAcquired;
    331 
    332   return EFI_SUCCESS;
    333 }
    334 
    335 /**
    336   This function transitions a mutual exclusion lock from the acquired state to
    337   the released state, and restores the system's task priority level to its
    338   previous level.
    339 
    340   @param  Lock  A pointer to the lock to release.
    341 
    342 **/
    343 VOID
    344 EFIAPI
    345 GlueEfiReleaseLock (
    346   IN EFI_LOCK  *Lock
    347   )
    348 {
    349   EFI_TPL Tpl;
    350 
    351   ASSERT (Lock != NULL);
    352   ASSERT (Lock->Lock == EfiLockAcquired);
    353 
    354   Tpl = Lock->OwnerTpl;
    355 
    356   Lock->Lock = EfiLockReleased;
    357 
    358   gBS->RestoreTPL (Tpl);
    359 }
    360 
    361 /**
    362   Tests whether a controller handle is being managed by a specific driver.
    363 
    364   This function tests whether the driver specified by DriverBindingHandle is
    365   currently managing the controller specified by ControllerHandle.  This test
    366   is performed by evaluating if the the protocol specified by ProtocolGuid is
    367   present on ControllerHandle and is was opened by DriverBindingHandle with an
    368   attribute of EFI_OPEN_PROTOCOL_BY_DRIVER.
    369   If ProtocolGuid is NULL, then ASSERT().
    370 
    371   @param  ControllerHandle     A handle for a controller to test.
    372   @param  DriverBindingHandle  Specifies the driver binding handle for the
    373                                driver.
    374   @param  ProtocolGuid         Specifies the protocol that the driver specified
    375                                by DriverBindingHandle opens in its Start()
    376                                function.
    377 
    378   @retval EFI_SUCCESS          ControllerHandle is managed by the driver
    379                                specifed by DriverBindingHandle.
    380   @retval EFI_UNSUPPORTED      ControllerHandle is not managed by the driver
    381                                specifed by DriverBindingHandle.
    382 
    383 **/
    384 EFI_STATUS
    385 EFIAPI
    386 EfiTestManagedDevice (
    387   IN CONST EFI_HANDLE       ControllerHandle,
    388   IN CONST EFI_HANDLE       DriverBindingHandle,
    389   IN CONST EFI_GUID         *ProtocolGuid
    390   )
    391 {
    392   EFI_STATUS     Status;
    393   VOID           *ManagedInterface;
    394 
    395   ASSERT (ProtocolGuid != NULL);
    396 
    397   Status = gBS->OpenProtocol (
    398                   ControllerHandle,
    399                   (EFI_GUID *) ProtocolGuid,
    400                   &ManagedInterface,
    401                   DriverBindingHandle,
    402                   ControllerHandle,
    403                   EFI_OPEN_PROTOCOL_BY_DRIVER
    404                   );
    405   if (!EFI_ERROR (Status)) {
    406     gBS->CloseProtocol (
    407            ControllerHandle,
    408            (EFI_GUID *) ProtocolGuid,
    409            DriverBindingHandle,
    410            ControllerHandle
    411            );
    412     return EFI_UNSUPPORTED;
    413   }
    414 
    415   if (Status != EFI_ALREADY_STARTED) {
    416     return EFI_UNSUPPORTED;
    417   }
    418 
    419   return EFI_SUCCESS;
    420 }
    421 
    422 /**
    423   Tests whether a child handle is a child device of the controller.
    424 
    425   This function tests whether ChildHandle is one of the children of
    426   ControllerHandle.  This test is performed by checking to see if the protocol
    427   specified by ProtocolGuid is present on ControllerHandle and opened by
    428   ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
    429   If ProtocolGuid is NULL, then ASSERT().
    430 
    431   @param  ControllerHandle     A handle for a (parent) controller to test.
    432   @param  ChildHandle          A child handle to test.
    433   @param  ConsumsedGuid        Supplies the protocol that the child controller
    434                                opens on its parent controller.
    435 
    436   @retval EFI_SUCCESS          ChildHandle is a child of the ControllerHandle.
    437   @retval EFI_UNSUPPORTED      ChildHandle is not a child of the
    438                                ControllerHandle.
    439 
    440 **/
    441 EFI_STATUS
    442 EFIAPI
    443 EfiTestChildHandle (
    444   IN CONST EFI_HANDLE       ControllerHandle,
    445   IN CONST EFI_HANDLE       ChildHandle,
    446   IN CONST EFI_GUID         *ProtocolGuid
    447   )
    448 {
    449   EFI_STATUS                            Status;
    450   EFI_OPEN_PROTOCOL_INFORMATION_ENTRY   *OpenInfoBuffer;
    451   UINTN                                 EntryCount;
    452   UINTN                                 Index;
    453 
    454   ASSERT (ProtocolGuid != NULL);
    455 
    456   //
    457   // Retrieve the list of agents that are consuming the specific protocol
    458   // on ControllerHandle.
    459   //
    460   Status = gBS->OpenProtocolInformation (
    461                   ControllerHandle,
    462                   (EFI_GUID *) ProtocolGuid,
    463                   &OpenInfoBuffer,
    464                   &EntryCount
    465                   );
    466   if (EFI_ERROR (Status)) {
    467     return EFI_UNSUPPORTED;
    468   }
    469 
    470   //
    471   // Inspect if ChildHandle is one of the agents.
    472   //
    473   Status = EFI_UNSUPPORTED;
    474   for (Index = 0; Index < EntryCount; Index++) {
    475     if ((OpenInfoBuffer[Index].ControllerHandle == ChildHandle) &&
    476         (OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
    477       Status = EFI_SUCCESS;
    478       break;
    479     }
    480   }
    481 
    482   FreePool (OpenInfoBuffer);
    483   return Status;
    484 }
    485 
    486 /**
    487   Tests whether a language code has format of ISO639-2.
    488 
    489   @param  Languages     The language code to be tested.
    490 
    491   @retval TRUE          Language code format is ISO 639-2.
    492   @retval FALSE         Language code format is not ISO639-2.
    493 
    494 **/
    495 STATIC
    496 BOOLEAN
    497 IsIso639LanguageCode (
    498   IN CONST CHAR8          *Languages
    499   )
    500 {
    501   UINTN  Index;
    502 
    503   //
    504   // Find out format of Languages
    505   //
    506   for (Index = 0; Languages[Index] != 0 && Languages[Index] != ';' && Languages[Index] != '-'; Index++);
    507   if (Languages[Index] != 0) {
    508     //
    509     // RFC4646 language code
    510     //
    511     return FALSE;
    512   }
    513 
    514   //
    515   // No ';' and '-', it's either ISO639-2 code (list) or single RFC4646 code
    516   //
    517   if (Index == 2) {
    518     //
    519     // Single RFC4646 language code without country code, e.g. "en"
    520     //
    521     return FALSE;
    522   }
    523 
    524   //
    525   // Languages in format of ISO639-2
    526   //
    527   return TRUE;
    528 }
    529 
    530 /**
    531   Compare the first language instance of two language codes, either could be a
    532   single language code or a language code list. This function assume Language1
    533   and Language2 has the same language code format, i.e. either ISO639-2 or RFC4646.
    534 
    535   @param  Language1     The first language code to be tested.
    536   @param  Language2     The second language code to be tested.
    537 
    538   @retval TRUE          Language code match.
    539   @retval FALSE         Language code mismatch.
    540 
    541 **/
    542 STATIC
    543 BOOLEAN
    544 CompareLanguageCode (
    545   IN CONST CHAR8          *Language1,
    546   IN CONST CHAR8          *Language2
    547   )
    548 {
    549   UINTN Index;
    550 
    551   //
    552   // Compare first two bytes of language tag
    553   //
    554   if ((Language1[0] != Language2[0]) || (Language1[1] != Language2[1])) {
    555     return FALSE;
    556   }
    557 
    558   if (IsIso639LanguageCode (Language1)) {
    559     //
    560     // ISO639-2 language code, compare the third byte of language tag
    561     //
    562     return (BOOLEAN) ((Language1[2] == Language2[2]) ? TRUE : FALSE);
    563   }
    564 
    565   //
    566   // RFC4646 language code
    567   //
    568   for (Index = 0; Language1[Index] != 0 && Language1[Index] != ';'; Index++);
    569   if ((AsciiStrnCmp (Language1, Language2, Index) == 0) && (Language2[Index] == 0 || Language2[Index] == ';')) {
    570     return TRUE;
    571   }
    572 
    573   return FALSE;
    574 }
    575 
    576 /**
    577   Step to next language code of a language code list.
    578 
    579   @param  Languages     The language code list to traverse.
    580 
    581   @return Pointer to next language code or NULL terminator if it's the last one.
    582 
    583 **/
    584 STATIC
    585 CONST
    586 CHAR8 *
    587 NextSupportedLanguage (
    588   IN CONST CHAR8          *Languages
    589   )
    590 {
    591   UINTN    Index;
    592 
    593   if (IsIso639LanguageCode (Languages)) {
    594     //
    595     // ISO639-2 language code
    596     //
    597     return (Languages + 3);
    598   }
    599 
    600   //
    601   // Search in RFC4646 language code list
    602   //
    603   for (Index = 0; Languages[Index] != 0 && Languages[Index] != ';'; Index++);
    604   if (Languages[Index] == ';') {
    605     Index++;
    606   }
    607   return (Languages + Index);
    608 }
    609 
    610 /**
    611   This function looks up a Unicode string in UnicodeStringTable.  If Language is
    612   a member of SupportedLanguages and a Unicode string is found in UnicodeStringTable
    613   that matches the language code specified by Language, then it is returned in
    614   UnicodeString.
    615 
    616   @param  Language                A pointer to the ISO 639-2 language code for the
    617                                   Unicode string to look up and return.
    618   @param  SupportedLanguages      A pointer to the set of ISO 639-2 language codes
    619                                   that the Unicode string table supports.  Language
    620                                   must be a member of this set.
    621   @param  UnicodeStringTable      A pointer to the table of Unicode strings.
    622   @param  UnicodeString           A pointer to the Unicode string from UnicodeStringTable
    623                                   that matches the language specified by Language.
    624 
    625   @retval  EFI_SUCCESS            The Unicode string that matches the language
    626                                   specified by Language was found
    627                                   in the table of Unicoide strings UnicodeStringTable,
    628                                   and it was returned in UnicodeString.
    629   @retval  EFI_INVALID_PARAMETER  Language is NULL.
    630   @retval  EFI_INVALID_PARAMETER  UnicodeString is NULL.
    631   @retval  EFI_UNSUPPORTED        SupportedLanguages is NULL.
    632   @retval  EFI_UNSUPPORTED        UnicodeStringTable is NULL.
    633   @retval  EFI_UNSUPPORTED        The language specified by Language is not a
    634                                   member of SupportedLanguages.
    635   @retval  EFI_UNSUPPORTED        The language specified by Language is not
    636                                   supported by UnicodeStringTable.
    637 
    638 **/
    639 EFI_STATUS
    640 EFIAPI
    641 LookupUnicodeString (
    642   IN CONST CHAR8                     *Language,
    643   IN CONST CHAR8                     *SupportedLanguages,
    644   IN CONST EFI_UNICODE_STRING_TABLE  *UnicodeStringTable,
    645   OUT CHAR16                         **UnicodeString
    646   )
    647 {
    648   //
    649   // Make sure the parameters are valid
    650   //
    651   if (Language == NULL || UnicodeString == NULL) {
    652     return EFI_INVALID_PARAMETER;
    653   }
    654 
    655   //
    656   // If there are no supported languages, or the Unicode String Table is empty, then the
    657   // Unicode String specified by Language is not supported by this Unicode String Table
    658   //
    659   if (SupportedLanguages == NULL || UnicodeStringTable == NULL) {
    660     return EFI_UNSUPPORTED;
    661   }
    662 
    663   //
    664   // Make sure Language is in the set of Supported Languages
    665   //
    666   while (*SupportedLanguages != 0) {
    667     if (CompareLanguageCode (Language, SupportedLanguages)) {
    668 
    669       //
    670       // Search the Unicode String Table for the matching Language specifier
    671       //
    672       while (UnicodeStringTable->Language != NULL) {
    673         if (CompareLanguageCode (Language, UnicodeStringTable->Language)) {
    674 
    675           //
    676           // A matching string was found, so return it
    677           //
    678           *UnicodeString = UnicodeStringTable->UnicodeString;
    679           return EFI_SUCCESS;
    680         }
    681 
    682         UnicodeStringTable++;
    683       }
    684 
    685       return EFI_UNSUPPORTED;
    686     }
    687 
    688     SupportedLanguages = NextSupportedLanguage (SupportedLanguages);
    689   }
    690 
    691   return EFI_UNSUPPORTED;
    692 }
    693 
    694 /**
    695   This function adds a Unicode string to UnicodeStringTable.
    696   If Language is a member of SupportedLanguages then UnicodeString is added to
    697   UnicodeStringTable.  New buffers are allocated for both Language and
    698   UnicodeString.  The contents of Language and UnicodeString are copied into
    699   these new buffers.  These buffers are automatically freed when
    700   FreeUnicodeStringTable() is called.
    701 
    702   @param  Language                A pointer to the ISO 639-2 language code for the Unicode
    703                                   string to add.
    704   @param  SupportedLanguages      A pointer to the set of ISO 639-2 language codes
    705                                   that the Unicode string table supports.
    706                                   Language must be a member of this set.
    707   @param  UnicodeStringTable      A pointer to the table of Unicode strings.
    708   @param  UnicodeString           A pointer to the Unicode string to add.
    709 
    710   @retval EFI_SUCCESS             The Unicode string that matches the language
    711                                   specified by Language was found in the table of
    712                                   Unicode strings UnicodeStringTable, and it was
    713                                   returned in UnicodeString.
    714   @retval EFI_INVALID_PARAMETER   Language is NULL.
    715   @retval EFI_INVALID_PARAMETER   UnicodeString is NULL.
    716   @retval EFI_INVALID_PARAMETER   UnicodeString is an empty string.
    717   @retval EFI_UNSUPPORTED         SupportedLanguages is NULL.
    718   @retval EFI_ALREADY_STARTED     A Unicode string with language Language is
    719                                   already present in UnicodeStringTable.
    720   @retval EFI_OUT_OF_RESOURCES    There is not enough memory to add another
    721                                   Unicode string to UnicodeStringTable.
    722   @retval EFI_UNSUPPORTED         The language specified by Language is not a
    723                                   member of SupportedLanguages.
    724 
    725 **/
    726 EFI_STATUS
    727 EFIAPI
    728 AddUnicodeString (
    729   IN CONST CHAR8               *Language,
    730   IN CONST CHAR8               *SupportedLanguages,
    731   IN EFI_UNICODE_STRING_TABLE  **UnicodeStringTable,
    732   IN CONST CHAR16              *UnicodeString
    733   )
    734 {
    735   UINTN                     NumberOfEntries;
    736   EFI_UNICODE_STRING_TABLE  *OldUnicodeStringTable;
    737   EFI_UNICODE_STRING_TABLE  *NewUnicodeStringTable;
    738   UINTN                     UnicodeStringLength;
    739 
    740   //
    741   // Make sure the parameter are valid
    742   //
    743   if (Language == NULL || UnicodeString == NULL || UnicodeStringTable == NULL) {
    744     return EFI_INVALID_PARAMETER;
    745   }
    746 
    747   //
    748   // If there are no supported languages, then a Unicode String can not be added
    749   //
    750   if (SupportedLanguages == NULL) {
    751     return EFI_UNSUPPORTED;
    752   }
    753 
    754   //
    755   // If the Unicode String is empty, then a Unicode String can not be added
    756   //
    757   if (UnicodeString[0] == 0) {
    758     return EFI_INVALID_PARAMETER;
    759   }
    760 
    761   //
    762   // Make sure Language is a member of SupportedLanguages
    763   //
    764   while (*SupportedLanguages != 0) {
    765     if (CompareLanguageCode (Language, SupportedLanguages)) {
    766 
    767       //
    768       // Determine the size of the Unicode String Table by looking for a NULL Language entry
    769       //
    770       NumberOfEntries = 0;
    771       if (*UnicodeStringTable != NULL) {
    772         OldUnicodeStringTable = *UnicodeStringTable;
    773         while (OldUnicodeStringTable->Language != NULL) {
    774           if (CompareLanguageCode (Language, OldUnicodeStringTable->Language)) {
    775             return EFI_ALREADY_STARTED;
    776           }
    777 
    778           OldUnicodeStringTable++;
    779           NumberOfEntries++;
    780         }
    781       }
    782 
    783       //
    784       // Allocate space for a new Unicode String Table.  It must hold the current number of
    785       // entries, plus 1 entry for the new Unicode String, plus 1 entry for the end of table
    786       // marker
    787       //
    788       NewUnicodeStringTable = AllocatePool ((NumberOfEntries + 2) * sizeof (EFI_UNICODE_STRING_TABLE));
    789       if (NewUnicodeStringTable == NULL) {
    790         return EFI_OUT_OF_RESOURCES;
    791       }
    792 
    793       //
    794       // If the current Unicode String Table contains any entries, then copy them to the
    795       // newly allocated Unicode String Table.
    796       //
    797       if (*UnicodeStringTable != NULL) {
    798         CopyMem (
    799            NewUnicodeStringTable,
    800            *UnicodeStringTable,
    801            NumberOfEntries * sizeof (EFI_UNICODE_STRING_TABLE)
    802            );
    803       }
    804 
    805       //
    806       // Allocate space for a copy of the Language specifier
    807       //
    808       NewUnicodeStringTable[NumberOfEntries].Language = AllocateCopyPool (AsciiStrSize (Language), Language);
    809       if (NewUnicodeStringTable[NumberOfEntries].Language == NULL) {
    810         (gBS->FreePool) (NewUnicodeStringTable);
    811         return EFI_OUT_OF_RESOURCES;
    812       }
    813 
    814       //
    815       // Compute the length of the Unicode String
    816       //
    817       for (UnicodeStringLength = 0; UnicodeString[UnicodeStringLength] != 0; UnicodeStringLength++)
    818         ;
    819 
    820       //
    821       // Allocate space for a copy of the Unicode String
    822       //
    823       NewUnicodeStringTable[NumberOfEntries].UnicodeString = AllocateCopyPool (
    824                                                               (UnicodeStringLength + 1) * sizeof (CHAR16),
    825                                                               UnicodeString
    826                                                               );
    827       if (NewUnicodeStringTable[NumberOfEntries].UnicodeString == NULL) {
    828         (gBS->FreePool) (NewUnicodeStringTable[NumberOfEntries].Language);
    829         (gBS->FreePool) (NewUnicodeStringTable);
    830         return EFI_OUT_OF_RESOURCES;
    831       }
    832 
    833       //
    834       // Mark the end of the Unicode String Table
    835       //
    836       NewUnicodeStringTable[NumberOfEntries + 1].Language       = NULL;
    837       NewUnicodeStringTable[NumberOfEntries + 1].UnicodeString  = NULL;
    838 
    839       //
    840       // Free the old Unicode String Table
    841       //
    842       if (*UnicodeStringTable != NULL) {
    843         (gBS->FreePool) (*UnicodeStringTable);
    844       }
    845 
    846       //
    847       // Point UnicodeStringTable at the newly allocated Unicode String Table
    848       //
    849       *UnicodeStringTable = NewUnicodeStringTable;
    850 
    851       return EFI_SUCCESS;
    852     }
    853 
    854     SupportedLanguages = NextSupportedLanguage (SupportedLanguages);
    855   }
    856 
    857   return EFI_UNSUPPORTED;
    858 }
    859 
    860 /**
    861   This function frees the table of Unicode strings in UnicodeStringTable.
    862   If UnicodeStringTable is NULL, then EFI_SUCCESS is returned.
    863   Otherwise, each language code, and each Unicode string in the Unicode string
    864   table are freed, and EFI_SUCCESS is returned.
    865 
    866   @param  UnicodeStringTable  A pointer to the table of Unicode strings.
    867 
    868   @retval EFI_SUCCESS         The Unicode string table was freed.
    869 
    870 **/
    871 EFI_STATUS
    872 EFIAPI
    873 FreeUnicodeStringTable (
    874   IN EFI_UNICODE_STRING_TABLE  *UnicodeStringTable
    875   )
    876 {
    877   UINTN Index;
    878 
    879   //
    880   // If the Unicode String Table is NULL, then it is already freed
    881   //
    882   if (UnicodeStringTable == NULL) {
    883     return EFI_SUCCESS;
    884   }
    885 
    886   //
    887   // Loop through the Unicode String Table until we reach the end of table marker
    888   //
    889   for (Index = 0; UnicodeStringTable[Index].Language != NULL; Index++) {
    890 
    891     //
    892     // Free the Language string from the Unicode String Table
    893     //
    894     (gBS->FreePool) (UnicodeStringTable[Index].Language);
    895 
    896     //
    897     // Free the Unicode String from the Unicode String Table
    898     //
    899     if (UnicodeStringTable[Index].UnicodeString != NULL) {
    900       (gBS->FreePool) (UnicodeStringTable[Index].UnicodeString);
    901     }
    902   }
    903 
    904   //
    905   // Free the Unicode String Table itself
    906   //
    907   (gBS->FreePool) (UnicodeStringTable);
    908 
    909   return EFI_SUCCESS;
    910 }
    911 
    912