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
EfiGetSystemConfigurationTable(IN EFI_GUID * TableGuid,OUT VOID ** Table)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
EfiCreateProtocolNotifyEvent(IN EFI_GUID * ProtocolGuid,IN EFI_TPL NotifyTpl,IN EFI_EVENT_NOTIFY NotifyFunction,IN VOID * NotifyContext,OPTIONAL OUT VOID ** Registration)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
EfiNamedEventListen(IN CONST EFI_GUID * Name,IN EFI_TPL NotifyTpl,IN EFI_EVENT_NOTIFY NotifyFunction,IN CONST VOID * NotifyContext,OPTIONAL OUT VOID * Registration OPTIONAL)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
EfiNamedEventSignal(IN CONST EFI_GUID * Name)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
EfiGetCurrentTpl(VOID)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
GlueEfiInitializeLock(IN OUT EFI_LOCK * Lock,IN EFI_TPL Priority)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
GlueEfiAcquireLock(IN EFI_LOCK * Lock)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
GlueEfiAcquireLockOrFail(IN EFI_LOCK * Lock)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
GlueEfiReleaseLock(IN EFI_LOCK * Lock)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
EfiTestManagedDevice(IN CONST EFI_HANDLE ControllerHandle,IN CONST EFI_HANDLE DriverBindingHandle,IN CONST EFI_GUID * ProtocolGuid)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
EfiTestChildHandle(IN CONST EFI_HANDLE ControllerHandle,IN CONST EFI_HANDLE ChildHandle,IN CONST EFI_GUID * ProtocolGuid)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
IsIso639LanguageCode(IN CONST CHAR8 * Languages)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
CompareLanguageCode(IN CONST CHAR8 * Language1,IN CONST CHAR8 * Language2)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 *
NextSupportedLanguage(IN CONST CHAR8 * Languages)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
LookupUnicodeString(IN CONST CHAR8 * Language,IN CONST CHAR8 * SupportedLanguages,IN CONST EFI_UNICODE_STRING_TABLE * UnicodeStringTable,OUT CHAR16 ** UnicodeString)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
AddUnicodeString(IN CONST CHAR8 * Language,IN CONST CHAR8 * SupportedLanguages,IN EFI_UNICODE_STRING_TABLE ** UnicodeStringTable,IN CONST CHAR16 * UnicodeString)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
FreeUnicodeStringTable(IN EFI_UNICODE_STRING_TABLE * UnicodeStringTable)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