1 /** @file
2   This code produces the Smbios protocol. It also responsible for constructing
3   SMBIOS table into system table.
4 
5 Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "SmbiosDxe.h"
17 
18 //
19 // Module Global:
20 // Since this driver will only ever produce one instance of the
21 // protocol you are not required to dynamically allocate the PrivateData.
22 //
23 SMBIOS_INSTANCE mPrivateData;
24 
25 UINTN mPreAllocatedPages      = 0;
26 UINTN mPre64BitAllocatedPages = 0;
27 
28 //
29 // Chassis for SMBIOS entry point structure that is to be installed into EFI system config table.
30 //
31 SMBIOS_TABLE_ENTRY_POINT *EntryPointStructure    = NULL;
32 SMBIOS_TABLE_ENTRY_POINT EntryPointStructureData = {
33   //
34   // AnchorString
35   //
36   {
37     0x5f,
38     0x53,
39     0x4d,
40     0x5f
41   },
42   //
43   // EntryPointStructureChecksum,TO BE FILLED
44   //
45   0,
46   //
47   // EntryPointStructure Length
48   //
49   0x1f,
50   //
51   // MajorVersion
52   //
53   0,
54   //
55   // MinorVersion
56   //
57   0,
58   //
59   // MaxStructureSize, TO BE FILLED
60   //
61   0,
62   //
63   // EntryPointRevision
64   //
65   0,
66   //
67   // FormattedArea
68   //
69   {
70     0,
71     0,
72     0,
73     0,
74     0
75   },
76   //
77   // IntermediateAnchorString
78   //
79   {
80     0x5f,
81     0x44,
82     0x4d,
83     0x49,
84     0x5f
85   },
86   //
87   // IntermediateChecksum, TO BE FILLED
88   //
89   0,
90   //
91   // TableLength, TO BE FILLED
92   //
93   0,
94   //
95   // TableAddress, TO BE FILLED
96   //
97   0,
98   //
99   // NumberOfSmbiosStructures, TO BE FILLED
100   //
101   0,
102   //
103   // SmbiosBcdRevision
104   //
105   0
106 };
107 
108 SMBIOS_TABLE_3_0_ENTRY_POINT *Smbios30EntryPointStructure    = NULL;
109 SMBIOS_TABLE_3_0_ENTRY_POINT Smbios30EntryPointStructureData = {
110   //
111   // AnchorString _SM3_
112   //
113   {
114     0x5f,
115     0x53,
116     0x4d,
117     0x33,
118     0x5f,
119   },
120   //
121   // EntryPointStructureChecksum,TO BE FILLED
122   //
123   0,
124   //
125   // EntryPointLength
126   //
127   0x18,
128   //
129   // MajorVersion
130   //
131   0,
132   //
133   // MinorVersion
134   //
135   0,
136   //
137   // DocRev
138   //
139   0,
140   //
141   // EntryPointRevision
142   //
143   0x01,
144   //
145   // Reserved
146   //
147   0,
148   //
149   // TableMaximumSize,TO BE FILLED
150   //
151   0,
152   //
153   // TableAddress,TO BE FILLED
154   //
155   0
156 };
157 /**
158 
159   Get the full size of SMBIOS structure including optional strings that follow the formatted structure.
160 
161   @param This                   The EFI_SMBIOS_PROTOCOL instance.
162   @param Head                   Pointer to the beginning of SMBIOS structure.
163   @param Size                   The returned size.
164   @param NumberOfStrings        The returned number of optional strings that follow the formatted structure.
165 
166   @retval EFI_SUCCESS           Size retured in Size.
167   @retval EFI_INVALID_PARAMETER Input SMBIOS structure mal-formed or Size is NULL.
168 
169 **/
170 EFI_STATUS
171 EFIAPI
GetSmbiosStructureSize(IN CONST EFI_SMBIOS_PROTOCOL * This,IN EFI_SMBIOS_TABLE_HEADER * Head,OUT UINTN * Size,OUT UINTN * NumberOfStrings)172 GetSmbiosStructureSize (
173   IN   CONST EFI_SMBIOS_PROTOCOL        *This,
174   IN   EFI_SMBIOS_TABLE_HEADER          *Head,
175   OUT  UINTN                            *Size,
176   OUT  UINTN                            *NumberOfStrings
177   )
178 {
179   UINTN  FullSize;
180   UINTN  StrLen;
181   UINTN  MaxLen;
182   INT8*  CharInStr;
183 
184   if (Size == NULL || NumberOfStrings == NULL) {
185     return EFI_INVALID_PARAMETER;
186   }
187 
188   FullSize = Head->Length;
189   CharInStr = (INT8*)Head + Head->Length;
190   *Size = FullSize;
191   *NumberOfStrings = 0;
192   StrLen = 0;
193   //
194   // look for the two consecutive zeros, check the string limit by the way.
195   //
196   while (*CharInStr != 0 || *(CharInStr+1) != 0) {
197     if (*CharInStr == 0) {
198       *Size += 1;
199       CharInStr++;
200     }
201 
202     if (This->MajorVersion < 2 || (This->MajorVersion == 2 && This->MinorVersion < 7)){
203       MaxLen = SMBIOS_STRING_MAX_LENGTH;
204     } else if (This->MajorVersion < 3) {
205       //
206       // Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string.
207       // However, the length of the entire structure table (including all strings) must be reported
208       // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
209       // which is a WORD field limited to 65,535 bytes.
210       //
211       MaxLen = SMBIOS_TABLE_MAX_LENGTH;
212     } else {
213       //
214       // SMBIOS 3.0 defines the Structure table maximum size as DWORD field limited to 0xFFFFFFFF bytes.
215       // Locate the end of string as long as possible.
216       //
217       MaxLen = SMBIOS_3_0_TABLE_MAX_LENGTH;
218     }
219 
220     for (StrLen = 0 ; StrLen < MaxLen; StrLen++) {
221       if (*(CharInStr+StrLen) == 0) {
222         break;
223       }
224     }
225 
226     if (StrLen == MaxLen) {
227       return EFI_INVALID_PARAMETER;
228     }
229 
230     //
231     // forward the pointer
232     //
233     CharInStr += StrLen;
234     *Size += StrLen;
235     *NumberOfStrings += 1;
236   }
237 
238   //
239   // count ending two zeros.
240   //
241   *Size += 2;
242   return EFI_SUCCESS;
243 }
244 
245 /**
246 
247   Determin whether an SmbiosHandle has already in use.
248 
249   @param Head        Pointer to the beginning of SMBIOS structure.
250   @param Handle      A unique handle will be assigned to the SMBIOS record.
251 
252   @retval TRUE       Smbios handle already in use.
253   @retval FALSE      Smbios handle is NOT used.
254 
255 **/
256 BOOLEAN
257 EFIAPI
CheckSmbiosHandleExistance(IN LIST_ENTRY * Head,IN EFI_SMBIOS_HANDLE Handle)258 CheckSmbiosHandleExistance (
259   IN  LIST_ENTRY           *Head,
260   IN  EFI_SMBIOS_HANDLE    Handle
261   )
262 {
263   LIST_ENTRY              *Link;
264   SMBIOS_HANDLE_ENTRY     *HandleEntry;
265 
266   for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
267     HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK(Link);
268     if (HandleEntry->SmbiosHandle == Handle) {
269       return TRUE;
270     }
271   }
272 
273   return FALSE;
274 }
275 
276 /**
277 
278   Get the max SmbiosHandle that could be use.
279 
280   @param  This           The EFI_SMBIOS_PROTOCOL instance.
281   @param  MaxHandle      The max handle that could be assigned to the SMBIOS record.
282 
283 **/
284 VOID
285 EFIAPI
GetMaxSmbiosHandle(IN CONST EFI_SMBIOS_PROTOCOL * This,IN OUT EFI_SMBIOS_HANDLE * MaxHandle)286 GetMaxSmbiosHandle (
287   IN CONST  EFI_SMBIOS_PROTOCOL   *This,
288   IN OUT    EFI_SMBIOS_HANDLE     *MaxHandle
289   )
290 {
291   if (This->MajorVersion == 2 && This->MinorVersion == 0) {
292     *MaxHandle = 0xFFFE;
293   } else {
294     *MaxHandle = 0xFEFF;
295   }
296 }
297 
298 /**
299 
300   Get an SmbiosHandle that could use.
301 
302   @param  This                   The EFI_SMBIOS_PROTOCOL instance.
303   @param  SmbiosHandle           A unique handle will be assigned to the SMBIOS record.
304 
305   @retval EFI_SUCCESS            Smbios handle got.
306   @retval EFI_OUT_OF_RESOURCES   Smbios handle is NOT available.
307 
308 **/
309 EFI_STATUS
310 EFIAPI
GetAvailableSmbiosHandle(IN CONST EFI_SMBIOS_PROTOCOL * This,IN OUT EFI_SMBIOS_HANDLE * Handle)311 GetAvailableSmbiosHandle (
312   IN CONST EFI_SMBIOS_PROTOCOL   *This,
313   IN OUT   EFI_SMBIOS_HANDLE     *Handle
314   )
315 {
316   LIST_ENTRY              *Head;
317   SMBIOS_INSTANCE         *Private;
318   EFI_SMBIOS_HANDLE       MaxSmbiosHandle;
319   EFI_SMBIOS_HANDLE       AvailableHandle;
320 
321   GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
322 
323   Private = SMBIOS_INSTANCE_FROM_THIS (This);
324   Head = &Private->AllocatedHandleListHead;
325   for (AvailableHandle = 0; AvailableHandle < MaxSmbiosHandle; AvailableHandle++) {
326     if (!CheckSmbiosHandleExistance(Head, AvailableHandle)) {
327       *Handle = AvailableHandle;
328       return EFI_SUCCESS;
329     }
330   }
331 
332   return EFI_OUT_OF_RESOURCES;
333 }
334 
335 
336 /**
337   Add an SMBIOS record.
338 
339   @param  This                  The EFI_SMBIOS_PROTOCOL instance.
340   @param  ProducerHandle        The handle of the controller or driver associated with the SMBIOS information. NULL
341                                 means no handle.
342   @param  SmbiosHandle          On entry, the handle of the SMBIOS record to add. If FFFEh, then a unique handle
343                                 will be assigned to the SMBIOS record. If the SMBIOS handle is already in use,
344                                 EFI_ALREADY_STARTED is returned and the SMBIOS record is not updated.
345   @param  Record                The data for the fixed portion of the SMBIOS record. The format of the record is
346                                 determined by EFI_SMBIOS_TABLE_HEADER.Type. The size of the formatted area is defined
347                                 by EFI_SMBIOS_TABLE_HEADER.Length and either followed by a double-null (0x0000) or
348                                 a set of null terminated strings and a null.
349 
350   @retval EFI_SUCCESS           Record was added.
351   @retval EFI_OUT_OF_RESOURCES  Record was not added due to lack of system resources.
352   @retval EFI_ALREADY_STARTED   The SmbiosHandle passed in was already in use.
353 
354 **/
355 EFI_STATUS
356 EFIAPI
SmbiosAdd(IN CONST EFI_SMBIOS_PROTOCOL * This,IN EFI_HANDLE ProducerHandle,OPTIONAL IN OUT EFI_SMBIOS_HANDLE * SmbiosHandle,IN EFI_SMBIOS_TABLE_HEADER * Record)357 SmbiosAdd (
358   IN CONST EFI_SMBIOS_PROTOCOL  *This,
359   IN EFI_HANDLE                 ProducerHandle, OPTIONAL
360   IN OUT EFI_SMBIOS_HANDLE      *SmbiosHandle,
361   IN EFI_SMBIOS_TABLE_HEADER    *Record
362   )
363 {
364   VOID                        *Raw;
365   UINTN                       TotalSize;
366   UINTN                       RecordSize;
367   UINTN                       StructureSize;
368   UINTN                       NumberOfStrings;
369   EFI_STATUS                  Status;
370   LIST_ENTRY                  *Head;
371   SMBIOS_INSTANCE             *Private;
372   EFI_SMBIOS_ENTRY            *SmbiosEntry;
373   EFI_SMBIOS_HANDLE           MaxSmbiosHandle;
374   SMBIOS_HANDLE_ENTRY         *HandleEntry;
375   EFI_SMBIOS_RECORD_HEADER    *InternalRecord;
376   BOOLEAN                     Smbios32BitTable;
377   BOOLEAN                     Smbios64BitTable;
378 
379   if (SmbiosHandle == NULL) {
380     return EFI_INVALID_PARAMETER;
381   }
382 
383   Private = SMBIOS_INSTANCE_FROM_THIS (This);
384   //
385   // Check whether SmbiosHandle is already in use
386   //
387   Head = &Private->AllocatedHandleListHead;
388   if (*SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED && CheckSmbiosHandleExistance(Head, *SmbiosHandle)) {
389     return EFI_ALREADY_STARTED;
390   }
391 
392   //
393   // when SmbiosHandle is 0xFFFE, an available handle will be assigned
394   //
395   if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) {
396     Status = GetAvailableSmbiosHandle(This, SmbiosHandle);
397     if (EFI_ERROR(Status)) {
398       return Status;
399     }
400   } else {
401     //
402     // Check this handle validity
403     //
404     GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
405     if (*SmbiosHandle > MaxSmbiosHandle) {
406       return EFI_INVALID_PARAMETER;
407     }
408   }
409 
410   //
411   // Calculate record size and string number
412   //
413   Status = GetSmbiosStructureSize(This, Record, &StructureSize, &NumberOfStrings);
414   if (EFI_ERROR(Status)) {
415     return Status;
416   }
417 
418   Smbios32BitTable = FALSE;
419   Smbios64BitTable = FALSE;
420   if ((This->MajorVersion < 0x3) ||
421       ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) {
422     //
423     // For SMBIOS 32-bit table, the length of the entire structure table (including all strings) must be reported
424     // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
425     // which is a WORD field limited to 65,535 bytes. So the max size of 32-bit table should not exceed 65,535 bytes.
426     //
427     if ((EntryPointStructure != NULL) &&
428         (EntryPointStructure->TableLength + StructureSize > SMBIOS_TABLE_MAX_LENGTH)) {
429       DEBUG ((EFI_D_INFO, "SmbiosAdd: Total length exceeds max 32-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize));
430     } else {
431       Smbios32BitTable = TRUE;
432       DEBUG ((EFI_D_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 32-bit table\n", Record->Type, StructureSize));
433     }
434   }
435 
436   //
437   // For SMBIOS 3.0, Structure table maximum size in Entry Point structure is DWORD field limited to 0xFFFFFFFF bytes.
438   //
439   if ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) {
440     //
441     // For SMBIOS 64-bit table, Structure table maximum size in SMBIOS 3.0 (64-bit) Entry Point
442     // is a DWORD field limited to 0xFFFFFFFF bytes. So the max size of 64-bit table should not exceed 0xFFFFFFFF bytes.
443     //
444     if ((Smbios30EntryPointStructure != NULL) &&
445         (Smbios30EntryPointStructure->TableMaximumSize + StructureSize > SMBIOS_3_0_TABLE_MAX_LENGTH)) {
446       DEBUG ((EFI_D_INFO, "SmbiosAdd: Total length exceeds max 64-bit table length with type = %d size = 0x%x\n", Record->Type, StructureSize));
447     } else {
448       DEBUG ((EFI_D_INFO, "SmbiosAdd: Smbios type %d with size 0x%x is added to 64-bit table\n", Record->Type, StructureSize));
449       Smbios64BitTable = TRUE;
450     }
451   }
452 
453   if ((!Smbios32BitTable) && (!Smbios64BitTable)) {
454     //
455     // If both 32-bit and 64-bit table are not updated, quit
456     //
457     return EFI_OUT_OF_RESOURCES;
458   }
459 
460   //
461   // Enter into critical section
462   //
463   Status = EfiAcquireLockOrFail (&Private->DataLock);
464   if (EFI_ERROR (Status)) {
465     return Status;
466   }
467 
468   RecordSize  = sizeof (EFI_SMBIOS_RECORD_HEADER) + StructureSize;
469   TotalSize   = sizeof (EFI_SMBIOS_ENTRY) + RecordSize;
470 
471   //
472   // Allocate internal buffer
473   //
474   SmbiosEntry = AllocateZeroPool (TotalSize);
475   if (SmbiosEntry == NULL) {
476     EfiReleaseLock (&Private->DataLock);
477     return EFI_OUT_OF_RESOURCES;
478   }
479   HandleEntry = AllocateZeroPool (sizeof(SMBIOS_HANDLE_ENTRY));
480   if (HandleEntry == NULL) {
481     EfiReleaseLock (&Private->DataLock);
482     return EFI_OUT_OF_RESOURCES;
483   }
484 
485   //
486   // Build Handle Entry and insert into linked list
487   //
488   HandleEntry->Signature     = SMBIOS_HANDLE_ENTRY_SIGNATURE;
489   HandleEntry->SmbiosHandle  = *SmbiosHandle;
490   InsertTailList(&Private->AllocatedHandleListHead, &HandleEntry->Link);
491 
492   InternalRecord  = (EFI_SMBIOS_RECORD_HEADER *) (SmbiosEntry + 1);
493   Raw     = (VOID *) (InternalRecord + 1);
494 
495   //
496   // Build internal record Header
497   //
498   InternalRecord->Version     = EFI_SMBIOS_RECORD_HEADER_VERSION;
499   InternalRecord->HeaderSize  = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER);
500   InternalRecord->RecordSize  = RecordSize;
501   InternalRecord->ProducerHandle = ProducerHandle;
502   InternalRecord->NumberOfStrings = NumberOfStrings;
503   //
504   // Insert record into the internal linked list
505   //
506   SmbiosEntry->Signature    = EFI_SMBIOS_ENTRY_SIGNATURE;
507   SmbiosEntry->RecordHeader = InternalRecord;
508   SmbiosEntry->RecordSize   = TotalSize;
509   SmbiosEntry->Smbios32BitTable = Smbios32BitTable;
510   SmbiosEntry->Smbios64BitTable = Smbios64BitTable;
511   InsertTailList (&Private->DataListHead, &SmbiosEntry->Link);
512 
513   CopyMem (Raw, Record, StructureSize);
514   ((EFI_SMBIOS_TABLE_HEADER*)Raw)->Handle = *SmbiosHandle;
515 
516   //
517   // Some UEFI drivers (such as network) need some information in SMBIOS table.
518   // Here we create SMBIOS table and publish it in
519   // configuration table, so other UEFI drivers can get SMBIOS table from
520   // configuration table without depending on PI SMBIOS protocol.
521   //
522   SmbiosTableConstruction (Smbios32BitTable, Smbios64BitTable);
523 
524   //
525   // Leave critical section
526   //
527   EfiReleaseLock (&Private->DataLock);
528   return EFI_SUCCESS;
529 }
530 
531 /**
532   Update the string associated with an existing SMBIOS record.
533 
534   @param  This                  The EFI_SMBIOS_PROTOCOL instance.
535   @param  SmbiosHandle          SMBIOS Handle of structure that will have its string updated.
536   @param  StringNumber          The non-zero string number of the string to update
537   @param  String                Update the StringNumber string with String.
538 
539   @retval EFI_SUCCESS           SmbiosHandle had its StringNumber String updated.
540   @retval EFI_INVALID_PARAMETER SmbiosHandle does not exist.
541   @retval EFI_UNSUPPORTED       String was not added because it is longer than the SMBIOS Table supports.
542   @retval EFI_NOT_FOUND         The StringNumber.is not valid for this SMBIOS record.
543 
544 **/
545 EFI_STATUS
546 EFIAPI
SmbiosUpdateString(IN CONST EFI_SMBIOS_PROTOCOL * This,IN EFI_SMBIOS_HANDLE * SmbiosHandle,IN UINTN * StringNumber,IN CHAR8 * String)547 SmbiosUpdateString (
548   IN CONST EFI_SMBIOS_PROTOCOL      *This,
549   IN EFI_SMBIOS_HANDLE              *SmbiosHandle,
550   IN UINTN                          *StringNumber,
551   IN CHAR8                          *String
552   )
553 {
554   UINTN                     InputStrLen;
555   UINTN                     TargetStrLen;
556   UINTN                     StrIndex;
557   UINTN                     TargetStrOffset;
558   UINTN                     NewEntrySize;
559   CHAR8                     *StrStart;
560   VOID                      *Raw;
561   LIST_ENTRY                *Link;
562   LIST_ENTRY                *Head;
563   EFI_STATUS                Status;
564   SMBIOS_INSTANCE           *Private;
565   EFI_SMBIOS_ENTRY          *SmbiosEntry;
566   EFI_SMBIOS_ENTRY          *ResizedSmbiosEntry;
567   EFI_SMBIOS_HANDLE         MaxSmbiosHandle;
568   EFI_SMBIOS_TABLE_HEADER   *Record;
569   EFI_SMBIOS_RECORD_HEADER  *InternalRecord;
570 
571   //
572   // Check args validity
573   //
574   GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
575 
576   if (*SmbiosHandle > MaxSmbiosHandle) {
577     return EFI_INVALID_PARAMETER;
578   }
579 
580   if (String == NULL) {
581     return EFI_ABORTED;
582   }
583 
584   if (*StringNumber == 0) {
585     return EFI_NOT_FOUND;
586   }
587 
588   InputStrLen = AsciiStrLen(String);
589 
590   if (This->MajorVersion < 2 || (This->MajorVersion == 2 && This->MinorVersion < 7)) {
591     if (InputStrLen > SMBIOS_STRING_MAX_LENGTH) {
592       return EFI_UNSUPPORTED;
593     }
594   } else if (This->MajorVersion < 3) {
595     //
596     // Reference SMBIOS 2.7, chapter 6.1.3, it will have no limit on the length of each individual text string.
597     // However, the length of the entire structure table (including all strings) must be reported
598     // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
599     // which is a WORD field limited to 65,535 bytes.
600     //
601     if (InputStrLen > SMBIOS_TABLE_MAX_LENGTH) {
602       return EFI_UNSUPPORTED;
603     }
604   } else {
605     if (InputStrLen > SMBIOS_3_0_TABLE_MAX_LENGTH) {
606       //
607       // SMBIOS 3.0 defines the Structure table maximum size as DWORD field limited to 0xFFFFFFFF bytes.
608       // The input string length should not exceed 0xFFFFFFFF bytes.
609       //
610       return EFI_UNSUPPORTED;
611     }
612   }
613 
614   Private = SMBIOS_INSTANCE_FROM_THIS (This);
615   //
616   // Enter into critical section
617   //
618   Status = EfiAcquireLockOrFail (&Private->DataLock);
619   if (EFI_ERROR (Status)) {
620     return Status;
621   }
622 
623   Head = &Private->DataListHead;
624   for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
625     SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
626     Record = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
627 
628     if (Record->Handle == *SmbiosHandle) {
629       //
630       // Find out the specified SMBIOS record
631       //
632       if (*StringNumber > SmbiosEntry->RecordHeader->NumberOfStrings) {
633         EfiReleaseLock (&Private->DataLock);
634         return EFI_NOT_FOUND;
635       }
636       //
637       // Point to unformed string section
638       //
639       StrStart = (CHAR8 *) Record + Record->Length;
640 
641       for (StrIndex = 1, TargetStrOffset = 0; StrIndex < *StringNumber; StrStart++, TargetStrOffset++) {
642         //
643         // A string ends in 00h
644         //
645         if (*StrStart == 0) {
646           StrIndex++;
647         }
648 
649         //
650         // String section ends in double-null (0000h)
651         //
652         if (*StrStart == 0 && *(StrStart + 1) == 0) {
653           EfiReleaseLock (&Private->DataLock);
654           return EFI_NOT_FOUND;
655         }
656       }
657 
658       if (*StrStart == 0) {
659         StrStart++;
660         TargetStrOffset++;
661       }
662 
663       //
664       // Now we get the string target
665       //
666       TargetStrLen = AsciiStrLen(StrStart);
667       if (InputStrLen == TargetStrLen) {
668         AsciiStrCpyS(StrStart, TargetStrLen + 1, String);
669         //
670         // Some UEFI drivers (such as network) need some information in SMBIOS table.
671         // Here we create SMBIOS table and publish it in
672         // configuration table, so other UEFI drivers can get SMBIOS table from
673         // configuration table without depending on PI SMBIOS protocol.
674         //
675         SmbiosTableConstruction (SmbiosEntry->Smbios32BitTable, SmbiosEntry->Smbios64BitTable);
676         EfiReleaseLock (&Private->DataLock);
677         return EFI_SUCCESS;
678       }
679 
680       SmbiosEntry->Smbios32BitTable = FALSE;
681       SmbiosEntry->Smbios64BitTable = FALSE;
682       if ((This->MajorVersion < 0x3) ||
683           ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT0) == BIT0))) {
684         //
685         // 32-bit table is produced, check the valid length.
686         //
687         if ((EntryPointStructure != NULL) &&
688             (EntryPointStructure->TableLength + InputStrLen - TargetStrLen > SMBIOS_TABLE_MAX_LENGTH)) {
689           //
690           // The length of the entire structure table (including all strings) must be reported
691           // in the Structure Table Length field of the SMBIOS Structure Table Entry Point,
692           // which is a WORD field limited to 65,535 bytes.
693           //
694           DEBUG ((EFI_D_INFO, "SmbiosUpdateString: Total length exceeds max 32-bit table length\n"));
695         } else {
696           DEBUG ((EFI_D_INFO, "SmbiosUpdateString: New smbios record add to 32-bit table\n"));
697           SmbiosEntry->Smbios32BitTable = TRUE;
698         }
699       }
700 
701       if ((This->MajorVersion >= 0x3) && ((PcdGet32 (PcdSmbiosEntryPointProvideMethod) & BIT1) == BIT1)) {
702         //
703         // 64-bit table is produced, check the valid length.
704         //
705         if ((Smbios30EntryPointStructure != NULL) &&
706             (Smbios30EntryPointStructure->TableMaximumSize + InputStrLen - TargetStrLen > SMBIOS_3_0_TABLE_MAX_LENGTH)) {
707           DEBUG ((EFI_D_INFO, "SmbiosUpdateString: Total length exceeds max 64-bit table length\n"));
708         } else {
709           DEBUG ((EFI_D_INFO, "SmbiosUpdateString: New smbios record add to 64-bit table\n"));
710           SmbiosEntry->Smbios64BitTable = TRUE;
711         }
712       }
713 
714       if ((!SmbiosEntry->Smbios32BitTable) && (!SmbiosEntry->Smbios64BitTable)) {
715         EfiReleaseLock (&Private->DataLock);
716         return EFI_UNSUPPORTED;
717       }
718 
719       //
720       // Original string buffer size is not exactly match input string length.
721       // Re-allocate buffer is needed.
722       //
723       NewEntrySize = SmbiosEntry->RecordSize + InputStrLen - TargetStrLen;
724       ResizedSmbiosEntry = AllocateZeroPool (NewEntrySize);
725 
726       if (ResizedSmbiosEntry == NULL) {
727         EfiReleaseLock (&Private->DataLock);
728         return EFI_OUT_OF_RESOURCES;
729       }
730 
731       InternalRecord  = (EFI_SMBIOS_RECORD_HEADER *) (ResizedSmbiosEntry + 1);
732       Raw     = (VOID *) (InternalRecord + 1);
733 
734       //
735       // Build internal record Header
736       //
737       InternalRecord->Version     = EFI_SMBIOS_RECORD_HEADER_VERSION;
738       InternalRecord->HeaderSize  = (UINT16) sizeof (EFI_SMBIOS_RECORD_HEADER);
739       InternalRecord->RecordSize  = SmbiosEntry->RecordHeader->RecordSize + InputStrLen - TargetStrLen;
740       InternalRecord->ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
741       InternalRecord->NumberOfStrings = SmbiosEntry->RecordHeader->NumberOfStrings;
742 
743       //
744       // Copy SMBIOS structure and optional strings.
745       //
746       CopyMem (Raw, SmbiosEntry->RecordHeader + 1, Record->Length + TargetStrOffset);
747       CopyMem ((VOID*)((UINTN)Raw + Record->Length + TargetStrOffset), String, InputStrLen + 1);
748       CopyMem ((CHAR8*)((UINTN)Raw + Record->Length + TargetStrOffset + InputStrLen + 1),
749                (CHAR8*)Record + Record->Length + TargetStrOffset + TargetStrLen + 1,
750                SmbiosEntry->RecordHeader->RecordSize - sizeof (EFI_SMBIOS_RECORD_HEADER) - Record->Length - TargetStrOffset - TargetStrLen - 1);
751 
752       //
753       // Insert new record
754       //
755       ResizedSmbiosEntry->Signature    = EFI_SMBIOS_ENTRY_SIGNATURE;
756       ResizedSmbiosEntry->RecordHeader = InternalRecord;
757       ResizedSmbiosEntry->RecordSize   = NewEntrySize;
758       ResizedSmbiosEntry->Smbios32BitTable = SmbiosEntry->Smbios32BitTable;
759       ResizedSmbiosEntry->Smbios64BitTable = SmbiosEntry->Smbios64BitTable;
760       InsertTailList (Link->ForwardLink, &ResizedSmbiosEntry->Link);
761 
762       //
763       // Remove old record
764       //
765       RemoveEntryList(Link);
766       FreePool(SmbiosEntry);
767       //
768       // Some UEFI drivers (such as network) need some information in SMBIOS table.
769       // Here we create SMBIOS table and publish it in
770       // configuration table, so other UEFI drivers can get SMBIOS table from
771       // configuration table without depending on PI SMBIOS protocol.
772       //
773       SmbiosTableConstruction (ResizedSmbiosEntry->Smbios32BitTable, ResizedSmbiosEntry->Smbios64BitTable);
774       EfiReleaseLock (&Private->DataLock);
775       return EFI_SUCCESS;
776     }
777   }
778 
779   EfiReleaseLock (&Private->DataLock);
780   return EFI_INVALID_PARAMETER;
781 }
782 
783 /**
784   Remove an SMBIOS record.
785 
786   @param  This                  The EFI_SMBIOS_PROTOCOL instance.
787   @param  SmbiosHandle          The handle of the SMBIOS record to remove.
788 
789   @retval EFI_SUCCESS           SMBIOS record was removed.
790   @retval EFI_INVALID_PARAMETER SmbiosHandle does not specify a valid SMBIOS record.
791 
792 **/
793 EFI_STATUS
794 EFIAPI
SmbiosRemove(IN CONST EFI_SMBIOS_PROTOCOL * This,IN EFI_SMBIOS_HANDLE SmbiosHandle)795 SmbiosRemove (
796   IN CONST EFI_SMBIOS_PROTOCOL   *This,
797   IN EFI_SMBIOS_HANDLE           SmbiosHandle
798   )
799 {
800   LIST_ENTRY                 *Link;
801   LIST_ENTRY                 *Head;
802   EFI_STATUS                 Status;
803   EFI_SMBIOS_HANDLE          MaxSmbiosHandle;
804   SMBIOS_INSTANCE            *Private;
805   EFI_SMBIOS_ENTRY           *SmbiosEntry;
806   SMBIOS_HANDLE_ENTRY        *HandleEntry;
807   EFI_SMBIOS_TABLE_HEADER    *Record;
808 
809   //
810   // Check args validity
811   //
812   GetMaxSmbiosHandle(This, &MaxSmbiosHandle);
813 
814   if (SmbiosHandle > MaxSmbiosHandle) {
815     return EFI_INVALID_PARAMETER;
816   }
817 
818   Private = SMBIOS_INSTANCE_FROM_THIS (This);
819   //
820   // Enter into critical section
821   //
822   Status = EfiAcquireLockOrFail (&Private->DataLock);
823   if (EFI_ERROR (Status)) {
824     return Status;
825   }
826 
827   Head = &Private->DataListHead;
828   for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
829     SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
830     Record = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
831     if (Record->Handle == SmbiosHandle) {
832       //
833       // Remove specified smobios record from DataList
834       //
835       RemoveEntryList(Link);
836       //
837       // Remove this handle from AllocatedHandleList
838       //
839       Head = &Private->AllocatedHandleListHead;
840       for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
841         HandleEntry = SMBIOS_HANDLE_ENTRY_FROM_LINK(Link);
842         if (HandleEntry->SmbiosHandle == SmbiosHandle) {
843           RemoveEntryList(Link);
844           FreePool(HandleEntry);
845           break;
846         }
847       }
848       //
849       // Some UEFI drivers (such as network) need some information in SMBIOS table.
850       // Here we create SMBIOS table and publish it in
851       // configuration table, so other UEFI drivers can get SMBIOS table from
852       // configuration table without depending on PI SMBIOS protocol.
853       //
854       if (SmbiosEntry->Smbios32BitTable) {
855         DEBUG ((EFI_D_INFO, "SmbiosRemove: remove from 32-bit table\n"));
856       }
857       if (SmbiosEntry->Smbios64BitTable) {
858         DEBUG ((EFI_D_INFO, "SmbiosRemove: remove from 64-bit table\n"));
859       }
860       //
861       // Update the whole SMBIOS table again based on which table the removed SMBIOS record is in.
862       //
863       SmbiosTableConstruction (SmbiosEntry->Smbios32BitTable, SmbiosEntry->Smbios64BitTable);
864       FreePool(SmbiosEntry);
865       EfiReleaseLock (&Private->DataLock);
866       return EFI_SUCCESS;
867     }
868   }
869 
870   //
871   // Leave critical section
872   //
873   EfiReleaseLock (&Private->DataLock);
874   return EFI_INVALID_PARAMETER;
875 
876 }
877 
878 /**
879   Allow the caller to discover all or some of the SMBIOS records.
880 
881   @param  This                  The EFI_SMBIOS_PROTOCOL instance.
882   @param  SmbiosHandle          On entry, points to the previous handle of the SMBIOS record. On exit, points to the
883                                 next SMBIOS record handle. If it is FFFEh on entry, then the first SMBIOS record
884                                 handle will be returned. If it returns FFFEh on exit, then there are no more SMBIOS records.
885   @param  Type                  On entry it means return the next SMBIOS record of type Type. If a NULL is passed in
886                                 this functionally it ignored. Type is not modified by the GetNext() function.
887   @param  Record                On exit, points to the SMBIOS Record consisting of the formatted area followed by
888                                 the unformatted area. The unformatted area optionally contains text strings.
889   @param  ProducerHandle        On exit, points to the ProducerHandle registered by Add(). If no ProducerHandle was passed into Add() NULL is returned.
890                                 If a NULL pointer is passed in no data will be returned
891 
892   @retval EFI_SUCCESS           SMBIOS record information was successfully returned in Record.
893   @retval EFI_NOT_FOUND         The SMBIOS record with SmbiosHandle was the last available record.
894 
895 **/
896 EFI_STATUS
897 EFIAPI
SmbiosGetNext(IN CONST EFI_SMBIOS_PROTOCOL * This,IN OUT EFI_SMBIOS_HANDLE * SmbiosHandle,IN EFI_SMBIOS_TYPE * Type,OPTIONAL OUT EFI_SMBIOS_TABLE_HEADER ** Record,OUT EFI_HANDLE * ProducerHandle OPTIONAL)898 SmbiosGetNext (
899   IN CONST EFI_SMBIOS_PROTOCOL      *This,
900   IN OUT EFI_SMBIOS_HANDLE          *SmbiosHandle,
901   IN EFI_SMBIOS_TYPE                *Type,          OPTIONAL
902   OUT EFI_SMBIOS_TABLE_HEADER       **Record,
903   OUT EFI_HANDLE                    *ProducerHandle OPTIONAL
904   )
905 {
906   BOOLEAN                  StartPointFound;
907   LIST_ENTRY               *Link;
908   LIST_ENTRY               *Head;
909   SMBIOS_INSTANCE          *Private;
910   EFI_SMBIOS_ENTRY         *SmbiosEntry;
911   EFI_SMBIOS_TABLE_HEADER  *SmbiosTableHeader;
912 
913   if (SmbiosHandle == NULL) {
914     return EFI_INVALID_PARAMETER;
915   }
916 
917   StartPointFound = FALSE;
918   Private = SMBIOS_INSTANCE_FROM_THIS (This);
919   Head = &Private->DataListHead;
920   for (Link = Head->ForwardLink; Link != Head; Link = Link->ForwardLink) {
921     SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
922     SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
923 
924     //
925     // If SmbiosHandle is 0xFFFE, the first matched SMBIOS record handle will be returned
926     //
927     if (*SmbiosHandle == SMBIOS_HANDLE_PI_RESERVED) {
928       if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) {
929         continue;
930       }
931 
932       *SmbiosHandle = SmbiosTableHeader->Handle;
933       *Record =SmbiosTableHeader;
934       if (ProducerHandle != NULL) {
935         *ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
936       }
937       return EFI_SUCCESS;
938     }
939 
940     //
941     // Start this round search from the next SMBIOS handle
942     //
943     if (!StartPointFound && (*SmbiosHandle == SmbiosTableHeader->Handle)) {
944       StartPointFound = TRUE;
945       continue;
946     }
947 
948     if (StartPointFound) {
949       if ((Type != NULL) && (*Type != SmbiosTableHeader->Type)) {
950         continue;
951       }
952 
953       *SmbiosHandle = SmbiosTableHeader->Handle;
954       *Record = SmbiosTableHeader;
955       if (ProducerHandle != NULL) {
956         *ProducerHandle = SmbiosEntry->RecordHeader->ProducerHandle;
957       }
958 
959       return EFI_SUCCESS;
960     }
961   }
962 
963   *SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
964   return EFI_NOT_FOUND;
965 
966 }
967 
968 /**
969   Allow the caller to discover all of the SMBIOS records.
970 
971   @param  This                  The EFI_SMBIOS_PROTOCOL instance.
972   @param  CurrentSmbiosEntry    On exit, points to the SMBIOS entry on the list which includes the returned SMBIOS record information.
973                                 If *CurrentSmbiosEntry is NULL on entry, then the first SMBIOS entry on the list will be returned.
974   @param  Record                On exit, points to the SMBIOS Record consisting of the formatted area followed by
975                                 the unformatted area. The unformatted area optionally contains text strings.
976 
977   @retval EFI_SUCCESS           SMBIOS record information was successfully returned in Record.
978                                 *CurrentSmbiosEntry points to the SMBIOS entry which includes the returned SMBIOS record information.
979   @retval EFI_NOT_FOUND         There is no more SMBIOS entry.
980 
981 **/
982 EFI_STATUS
983 EFIAPI
GetNextSmbiosRecord(IN CONST EFI_SMBIOS_PROTOCOL * This,IN OUT EFI_SMBIOS_ENTRY ** CurrentSmbiosEntry,OUT EFI_SMBIOS_TABLE_HEADER ** Record)984 GetNextSmbiosRecord (
985   IN CONST EFI_SMBIOS_PROTOCOL         *This,
986   IN OUT EFI_SMBIOS_ENTRY              **CurrentSmbiosEntry,
987   OUT EFI_SMBIOS_TABLE_HEADER          **Record
988   )
989 {
990   LIST_ENTRY               *Link;
991   LIST_ENTRY               *Head;
992   SMBIOS_INSTANCE          *Private;
993   EFI_SMBIOS_ENTRY         *SmbiosEntry;
994   EFI_SMBIOS_TABLE_HEADER  *SmbiosTableHeader;
995 
996   Private = SMBIOS_INSTANCE_FROM_THIS (This);
997   if (*CurrentSmbiosEntry == NULL) {
998     //
999     // Get the beginning of SMBIOS entry.
1000     //
1001     Head = &Private->DataListHead;
1002   } else {
1003     //
1004     // Get previous SMBIOS entry and make it as start point.
1005     //
1006     Head = &(*CurrentSmbiosEntry)->Link;
1007   }
1008 
1009   Link  = Head->ForwardLink;
1010 
1011   if (Link == &Private->DataListHead) {
1012     //
1013     // If no more SMBIOS entry in the list, return not found.
1014     //
1015     return EFI_NOT_FOUND;
1016   }
1017 
1018   SmbiosEntry = SMBIOS_ENTRY_FROM_LINK(Link);
1019   SmbiosTableHeader = (EFI_SMBIOS_TABLE_HEADER*)(SmbiosEntry->RecordHeader + 1);
1020   *Record = SmbiosTableHeader;
1021   *CurrentSmbiosEntry = SmbiosEntry;
1022   return EFI_SUCCESS;
1023 }
1024 
1025 /**
1026   Assembles SMBIOS table from the SMBIOS protocol. Produce Table
1027   Entry Point and return the pointer to it.
1028 
1029   @param  TableEntryPointStructure   On exit, points to the SMBIOS entrypoint structure.
1030 
1031   @retval EFI_SUCCESS                Structure created sucessfully.
1032   @retval EFI_OUT_OF_RESOURCES       No enough memory.
1033 
1034 **/
1035 EFI_STATUS
1036 EFIAPI
SmbiosCreateTable(OUT VOID ** TableEntryPointStructure)1037 SmbiosCreateTable (
1038   OUT VOID    **TableEntryPointStructure
1039   )
1040 {
1041   UINT8                           *BufferPointer;
1042   UINTN                           RecordSize;
1043   UINTN                           NumOfStr;
1044   EFI_STATUS                      Status;
1045   EFI_SMBIOS_HANDLE               SmbiosHandle;
1046   EFI_SMBIOS_PROTOCOL             *SmbiosProtocol;
1047   EFI_PHYSICAL_ADDRESS            PhysicalAddress;
1048   EFI_SMBIOS_TABLE_HEADER         *SmbiosRecord;
1049   EFI_SMBIOS_TABLE_END_STRUCTURE  EndStructure;
1050   EFI_SMBIOS_ENTRY                *CurrentSmbiosEntry;
1051 
1052   Status            = EFI_SUCCESS;
1053   BufferPointer     = NULL;
1054 
1055   if (EntryPointStructure == NULL) {
1056     //
1057     // Initialize the EntryPointStructure with initial values.
1058     // It should be done only once.
1059     // Allocate memory (below 4GB).
1060     //
1061     DEBUG ((EFI_D_INFO, "SmbiosCreateTable: Initialize 32-bit entry point structure\n"));
1062     EntryPointStructureData.MajorVersion  = mPrivateData.Smbios.MajorVersion;
1063     EntryPointStructureData.MinorVersion  = mPrivateData.Smbios.MinorVersion;
1064     EntryPointStructureData.SmbiosBcdRevision = (UINT8) ((PcdGet16 (PcdSmbiosVersion) >> 4) & 0xf0) | (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x0f);
1065     PhysicalAddress = 0xffffffff;
1066     Status = gBS->AllocatePages (
1067                     AllocateMaxAddress,
1068                     EfiRuntimeServicesData,
1069                     EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)),
1070                     &PhysicalAddress
1071                     );
1072     if (EFI_ERROR (Status)) {
1073       DEBUG ((EFI_D_ERROR, "SmbiosCreateTable () could not allocate EntryPointStructure < 4GB\n"));
1074       Status = gBS->AllocatePages (
1075                       AllocateAnyPages,
1076                       EfiRuntimeServicesData,
1077                       EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_ENTRY_POINT)),
1078                       &PhysicalAddress
1079                       );
1080      if (EFI_ERROR (Status)) {
1081         return EFI_OUT_OF_RESOURCES;
1082       }
1083     }
1084 
1085     EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) (UINTN) PhysicalAddress;
1086 
1087     CopyMem (
1088       EntryPointStructure,
1089       &EntryPointStructureData,
1090       sizeof (SMBIOS_TABLE_ENTRY_POINT)
1091       );
1092   }
1093 
1094   //
1095   // Get Smbios protocol to traverse SMBIOS records.
1096   //
1097   SmbiosProtocol = &mPrivateData.Smbios;
1098 
1099   //
1100   // Make some statistics about all the structures
1101   //
1102   EntryPointStructure->NumberOfSmbiosStructures = 0;
1103   EntryPointStructure->TableLength              = 0;
1104   EntryPointStructure->MaxStructureSize         = 0;
1105 
1106   //
1107   // Calculate EPS Table Length
1108   //
1109   CurrentSmbiosEntry = NULL;
1110   do {
1111     Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1112 
1113     if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios32BitTable)) {
1114       GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1115       //
1116       // Record NumberOfSmbiosStructures, TableLength and MaxStructureSize
1117       //
1118       EntryPointStructure->NumberOfSmbiosStructures++;
1119       EntryPointStructure->TableLength = (UINT16) (EntryPointStructure->TableLength + RecordSize);
1120       if (RecordSize > EntryPointStructure->MaxStructureSize) {
1121         EntryPointStructure->MaxStructureSize = (UINT16) RecordSize;
1122       }
1123     }
1124   } while (!EFI_ERROR(Status));
1125 
1126   //
1127   // Create End-Of-Table structure
1128   //
1129   GetMaxSmbiosHandle(SmbiosProtocol, &SmbiosHandle);
1130   EndStructure.Header.Type = EFI_SMBIOS_TYPE_END_OF_TABLE;
1131   EndStructure.Header.Length = (UINT8) sizeof (EFI_SMBIOS_TABLE_HEADER);
1132   EndStructure.Header.Handle = SmbiosHandle;
1133   EndStructure.Tailing[0] = 0;
1134   EndStructure.Tailing[1] = 0;
1135   EntryPointStructure->NumberOfSmbiosStructures++;
1136   EntryPointStructure->TableLength = (UINT16) (EntryPointStructure->TableLength + sizeof (EndStructure));
1137   if (sizeof (EndStructure) > EntryPointStructure->MaxStructureSize) {
1138     EntryPointStructure->MaxStructureSize = (UINT16) sizeof (EndStructure);
1139   }
1140 
1141   if ((UINTN) EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength) > mPreAllocatedPages) {
1142     //
1143     // If new SMBIOS table size exceeds the previous allocated page,
1144     // it is time to re-allocate memory (below 4GB).
1145     //
1146     DEBUG ((EFI_D_INFO, "%a() re-allocate SMBIOS 32-bit table\n",
1147       __FUNCTION__));
1148     if (EntryPointStructure->TableAddress != 0) {
1149       //
1150       // Free the previous allocated page
1151       //
1152       FreePages (
1153             (VOID*)(UINTN)EntryPointStructure->TableAddress,
1154             mPreAllocatedPages
1155             );
1156       EntryPointStructure->TableAddress = 0;
1157       mPreAllocatedPages = 0;
1158     }
1159 
1160     PhysicalAddress = 0xffffffff;
1161     Status = gBS->AllocatePages (
1162                     AllocateMaxAddress,
1163                     EfiRuntimeServicesData,
1164                     EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength),
1165                     &PhysicalAddress
1166                     );
1167     if (EFI_ERROR (Status)) {
1168       DEBUG ((EFI_D_ERROR, "SmbiosCreateTable() could not allocate SMBIOS table < 4GB\n"));
1169       EntryPointStructure->TableAddress = 0;
1170       return EFI_OUT_OF_RESOURCES;
1171     } else {
1172       EntryPointStructure->TableAddress = (UINT32) PhysicalAddress;
1173       mPreAllocatedPages = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength);
1174     }
1175   }
1176 
1177   //
1178   // Assemble the tables
1179   //
1180   ASSERT (EntryPointStructure->TableAddress != 0);
1181   BufferPointer = (UINT8 *) (UINTN) EntryPointStructure->TableAddress;
1182   CurrentSmbiosEntry = NULL;
1183   do {
1184     Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1185 
1186     if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios32BitTable)) {
1187       GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1188       CopyMem (BufferPointer, SmbiosRecord, RecordSize);
1189       BufferPointer = BufferPointer + RecordSize;
1190     }
1191   } while (!EFI_ERROR(Status));
1192 
1193   //
1194   // Assemble End-Of-Table structure
1195   //
1196   CopyMem (BufferPointer, &EndStructure, sizeof (EndStructure));
1197 
1198   //
1199   // Fixup checksums in the Entry Point Structure
1200   //
1201   EntryPointStructure->IntermediateChecksum = 0;
1202   EntryPointStructure->EntryPointStructureChecksum = 0;
1203 
1204   EntryPointStructure->IntermediateChecksum =
1205     CalculateCheckSum8 ((UINT8 *) EntryPointStructure + 0x10, EntryPointStructure->EntryPointLength - 0x10);
1206   EntryPointStructure->EntryPointStructureChecksum =
1207     CalculateCheckSum8 ((UINT8 *) EntryPointStructure, EntryPointStructure->EntryPointLength);
1208 
1209   //
1210   // Returns the pointer
1211   //
1212   *TableEntryPointStructure = EntryPointStructure;
1213 
1214   return EFI_SUCCESS;
1215 }
1216 
1217 /**
1218   Assembles SMBIOS 64-bit table from the SMBIOS protocol. Produce Table
1219   Entry Point and return the pointer to it.
1220 
1221   @param  TableEntryPointStructure   On exit, points to the SMBIOS entrypoint structure.
1222 
1223   @retval EFI_SUCCESS                Structure created sucessfully.
1224   @retval EFI_OUT_OF_RESOURCES       No enough memory.
1225 
1226 **/
1227 EFI_STATUS
1228 EFIAPI
SmbiosCreate64BitTable(OUT VOID ** TableEntryPointStructure)1229 SmbiosCreate64BitTable (
1230   OUT VOID    **TableEntryPointStructure
1231   )
1232 {
1233   UINT8                           *BufferPointer;
1234   UINTN                           RecordSize;
1235   UINTN                           NumOfStr;
1236   EFI_STATUS                      Status;
1237   EFI_SMBIOS_HANDLE               SmbiosHandle;
1238   EFI_SMBIOS_PROTOCOL             *SmbiosProtocol;
1239   EFI_PHYSICAL_ADDRESS            PhysicalAddress;
1240   EFI_SMBIOS_TABLE_HEADER         *SmbiosRecord;
1241   EFI_SMBIOS_TABLE_END_STRUCTURE  EndStructure;
1242   EFI_SMBIOS_ENTRY                *CurrentSmbiosEntry;
1243 
1244   Status            = EFI_SUCCESS;
1245   BufferPointer     = NULL;
1246 
1247   if (Smbios30EntryPointStructure == NULL) {
1248     //
1249     // Initialize the Smbios30EntryPointStructure with initial values.
1250     // It should be done only once.
1251     // Allocate memory at any address.
1252     //
1253     DEBUG ((EFI_D_INFO, "SmbiosCreateTable: Initialize 64-bit entry point structure\n"));
1254     Smbios30EntryPointStructureData.MajorVersion  = mPrivateData.Smbios.MajorVersion;
1255     Smbios30EntryPointStructureData.MinorVersion  = mPrivateData.Smbios.MinorVersion;
1256     Smbios30EntryPointStructureData.DocRev        = PcdGet8 (PcdSmbiosDocRev);
1257     Status = gBS->AllocatePages (
1258                     AllocateAnyPages,
1259                     EfiRuntimeServicesData,
1260                     EFI_SIZE_TO_PAGES (sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT)),
1261                     &PhysicalAddress
1262                     );
1263     if (EFI_ERROR (Status)) {
1264       DEBUG ((EFI_D_ERROR, "SmbiosCreate64BitTable() could not allocate Smbios30EntryPointStructure\n"));
1265       return EFI_OUT_OF_RESOURCES;
1266     }
1267 
1268     Smbios30EntryPointStructure = (SMBIOS_TABLE_3_0_ENTRY_POINT *) (UINTN) PhysicalAddress;
1269 
1270     CopyMem (
1271       Smbios30EntryPointStructure,
1272       &Smbios30EntryPointStructureData,
1273       sizeof (SMBIOS_TABLE_3_0_ENTRY_POINT)
1274       );
1275   }
1276 
1277   //
1278   // Get Smbios protocol to traverse SMBIOS records.
1279   //
1280   SmbiosProtocol = &mPrivateData.Smbios;
1281   Smbios30EntryPointStructure->TableMaximumSize = 0;
1282 
1283   //
1284   // Calculate EPS Table Length
1285   //
1286   CurrentSmbiosEntry = NULL;
1287   do {
1288     Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1289 
1290     if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios64BitTable)) {
1291       GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1292       //
1293       // Record TableMaximumSize
1294       //
1295       Smbios30EntryPointStructure->TableMaximumSize = (UINT32) (Smbios30EntryPointStructure->TableMaximumSize + RecordSize);
1296     }
1297   } while (!EFI_ERROR(Status));
1298 
1299   //
1300   // Create End-Of-Table structure
1301   //
1302   GetMaxSmbiosHandle(SmbiosProtocol, &SmbiosHandle);
1303   EndStructure.Header.Type = EFI_SMBIOS_TYPE_END_OF_TABLE;
1304   EndStructure.Header.Length = (UINT8) sizeof (EFI_SMBIOS_TABLE_HEADER);
1305   EndStructure.Header.Handle = SmbiosHandle;
1306   EndStructure.Tailing[0] = 0;
1307   EndStructure.Tailing[1] = 0;
1308   Smbios30EntryPointStructure->TableMaximumSize = (UINT32) (Smbios30EntryPointStructure->TableMaximumSize + sizeof (EndStructure));
1309 
1310   if ((UINTN) EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize) > mPre64BitAllocatedPages) {
1311     //
1312     // If new SMBIOS table size exceeds the previous allocated page,
1313     // it is time to re-allocate memory at anywhere.
1314     //
1315     DEBUG ((EFI_D_INFO, "%a() re-allocate SMBIOS 64-bit table\n",
1316       __FUNCTION__));
1317     if (Smbios30EntryPointStructure->TableAddress != 0) {
1318       //
1319       // Free the previous allocated page
1320       //
1321       FreePages (
1322             (VOID*)(UINTN)Smbios30EntryPointStructure->TableAddress,
1323             mPre64BitAllocatedPages
1324             );
1325       Smbios30EntryPointStructure->TableAddress = 0;
1326       mPre64BitAllocatedPages = 0;
1327     }
1328 
1329     Status = gBS->AllocatePages (
1330                     AllocateAnyPages,
1331                     EfiRuntimeServicesData,
1332                     EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize),
1333                     &PhysicalAddress
1334                     );
1335     if (EFI_ERROR (Status)) {
1336       DEBUG ((EFI_D_ERROR, "SmbiosCreateTable() could not allocate SMBIOS 64-bit table\n"));
1337       Smbios30EntryPointStructure->TableAddress = 0;
1338       return EFI_OUT_OF_RESOURCES;
1339     } else {
1340       Smbios30EntryPointStructure->TableAddress = PhysicalAddress;
1341       mPre64BitAllocatedPages = EFI_SIZE_TO_PAGES (Smbios30EntryPointStructure->TableMaximumSize);
1342     }
1343   }
1344 
1345   //
1346   // Assemble the tables
1347   //
1348   ASSERT (Smbios30EntryPointStructure->TableAddress != 0);
1349   BufferPointer = (UINT8 *) (UINTN) Smbios30EntryPointStructure->TableAddress;
1350   CurrentSmbiosEntry = NULL;
1351   do {
1352     Status = GetNextSmbiosRecord (SmbiosProtocol, &CurrentSmbiosEntry, &SmbiosRecord);
1353 
1354     if ((Status == EFI_SUCCESS) && (CurrentSmbiosEntry->Smbios64BitTable)) {
1355       //
1356       // This record can be added to 64-bit table
1357       //
1358       GetSmbiosStructureSize(SmbiosProtocol, SmbiosRecord, &RecordSize, &NumOfStr);
1359       CopyMem (BufferPointer, SmbiosRecord, RecordSize);
1360       BufferPointer = BufferPointer + RecordSize;
1361     }
1362   } while (!EFI_ERROR(Status));
1363 
1364   //
1365   // Assemble End-Of-Table structure
1366   //
1367   CopyMem (BufferPointer, &EndStructure, sizeof (EndStructure));
1368 
1369   //
1370   // Fixup checksums in the Entry Point Structure
1371   //
1372   Smbios30EntryPointStructure->EntryPointStructureChecksum = 0;
1373   Smbios30EntryPointStructure->EntryPointStructureChecksum =
1374     CalculateCheckSum8 ((UINT8 *) Smbios30EntryPointStructure, Smbios30EntryPointStructure->EntryPointLength);
1375 
1376   //
1377   // Returns the pointer
1378   //
1379   *TableEntryPointStructure = Smbios30EntryPointStructure;
1380 
1381   return EFI_SUCCESS;
1382 }
1383 
1384 /**
1385   Create Smbios Table and installs the Smbios Table to the System Table.
1386 
1387   @param  Smbios32BitTable    The flag to update 32-bit table.
1388   @param  Smbios64BitTable    The flag to update 64-bit table.
1389 
1390 **/
1391 VOID
1392 EFIAPI
SmbiosTableConstruction(BOOLEAN Smbios32BitTable,BOOLEAN Smbios64BitTable)1393 SmbiosTableConstruction (
1394   BOOLEAN     Smbios32BitTable,
1395   BOOLEAN     Smbios64BitTable
1396   )
1397 {
1398   UINT8       *Eps;
1399   UINT8       *Eps64Bit;
1400   EFI_STATUS  Status;
1401 
1402   if (Smbios32BitTable) {
1403     Status = SmbiosCreateTable ((VOID **) &Eps);
1404     if (!EFI_ERROR (Status)) {
1405       gBS->InstallConfigurationTable (&gEfiSmbiosTableGuid, Eps);
1406     }
1407   }
1408 
1409   if (Smbios64BitTable) {
1410     Status = SmbiosCreate64BitTable ((VOID **) &Eps64Bit);
1411     if (!EFI_ERROR (Status)) {
1412       gBS->InstallConfigurationTable (&gEfiSmbios3TableGuid, Eps64Bit);
1413     }
1414   }
1415 }
1416 
1417 /**
1418 
1419   Driver to produce Smbios protocol and pre-allocate 1 page for the final SMBIOS table.
1420 
1421   @param ImageHandle     Module's image handle
1422   @param SystemTable     Pointer of EFI_SYSTEM_TABLE
1423 
1424   @retval EFI_SUCCESS    Smbios protocol installed
1425   @retval Other          No protocol installed, unload driver.
1426 
1427 **/
1428 EFI_STATUS
1429 EFIAPI
SmbiosDriverEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1430 SmbiosDriverEntryPoint (
1431   IN EFI_HANDLE           ImageHandle,
1432   IN EFI_SYSTEM_TABLE     *SystemTable
1433   )
1434 {
1435   EFI_STATUS            Status;
1436 
1437   mPrivateData.Signature                = SMBIOS_INSTANCE_SIGNATURE;
1438   mPrivateData.Smbios.Add               = SmbiosAdd;
1439   mPrivateData.Smbios.UpdateString      = SmbiosUpdateString;
1440   mPrivateData.Smbios.Remove            = SmbiosRemove;
1441   mPrivateData.Smbios.GetNext           = SmbiosGetNext;
1442   mPrivateData.Smbios.MajorVersion      = (UINT8) (PcdGet16 (PcdSmbiosVersion) >> 8);
1443   mPrivateData.Smbios.MinorVersion      = (UINT8) (PcdGet16 (PcdSmbiosVersion) & 0x00ff);
1444 
1445   InitializeListHead (&mPrivateData.DataListHead);
1446   InitializeListHead (&mPrivateData.AllocatedHandleListHead);
1447   EfiInitializeLock (&mPrivateData.DataLock, TPL_NOTIFY);
1448 
1449   //
1450   // Make a new handle and install the protocol
1451   //
1452   mPrivateData.Handle = NULL;
1453   Status = gBS->InstallProtocolInterface (
1454                   &mPrivateData.Handle,
1455                   &gEfiSmbiosProtocolGuid,
1456                   EFI_NATIVE_INTERFACE,
1457                   &mPrivateData.Smbios
1458                   );
1459 
1460   return Status;
1461 }
1462