Home | History | Annotate | Download | only in SmbiosDxe
      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 - 2016, 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
    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
    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
    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
    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
    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
    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
    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
    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
    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
   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 = 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
   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 = 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
   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
   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