1 /** @file
2   Translate the DataHub records via EFI_DATA_HUB_PROTOCOL to Smbios recorders
3   via EFI_SMBIOS_PROTOCOL.
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 "Thunk.h"
17 
18 EFI_SMBIOS_PROTOCOL  *mSmbiosProtocol = NULL;
19 
20 /**
21   Release the structure Node.
22 
23   @param StructureNode  Point to SMBIOS_STRUCTURE_NODE which will be removed.
24 **/
25 VOID
ReleaseStructureNode(SMBIOS_STRUCTURE_NODE * StructureNode)26 ReleaseStructureNode (
27   SMBIOS_STRUCTURE_NODE  *StructureNode
28   )
29 {
30   EFI_SMBIOS_PROTOCOL *Smbios;
31 
32   RemoveEntryList (&(StructureNode->Link));
33   Smbios = GetSmbiosProtocol();
34   ASSERT (Smbios != NULL);
35   Smbios->Remove (Smbios, StructureNode->SmbiosHandle);
36   gBS->FreePool (StructureNode);
37 }
38 
39 /**
40   Process a datahub's record and find corresponding translation way to translate
41   to SMBIOS record.
42 
43   @param Record  Point to datahub record.
44 **/
45 VOID
SmbiosProcessDataRecord(IN EFI_DATA_RECORD_HEADER * Record)46 SmbiosProcessDataRecord (
47   IN EFI_DATA_RECORD_HEADER  *Record
48   )
49 {
50   EFI_DATA_RECORD_HEADER        *RecordHeader;
51   EFI_SUBCLASS_TYPE1_HEADER     *DataHeader;
52   UINTN                         Index;
53   SMBIOS_CONVERSION_TABLE_ENTRY *Conversion;
54   UINT8                         *SrcData;
55   UINTN                         SrcDataSize;
56   LIST_ENTRY                    *Link;
57   SMBIOS_STRUCTURE_NODE         *StructureNode;
58   BOOLEAN                       StructureCreated;
59   EFI_STATUS                    Status;
60 
61   Conversion        = NULL;
62   StructureNode     = NULL;
63   StructureCreated  = FALSE;
64   RecordHeader      = Record;
65   DataHeader        = (EFI_SUBCLASS_TYPE1_HEADER *) (Record + 1);
66   SrcData           = (UINT8 *) (DataHeader + 1);
67   SrcDataSize       = RecordHeader->RecordSize - RecordHeader->HeaderSize - sizeof (EFI_SUBCLASS_TYPE1_HEADER);
68 
69   if (DataHeader->HeaderSize != sizeof (EFI_SUBCLASS_TYPE1_HEADER) ||
70       DataHeader->Instance == EFI_SUBCLASS_INSTANCE_RESERVED ||
71       DataHeader->SubInstance == EFI_SUBCLASS_INSTANCE_RESERVED
72       ) {
73     //
74     // Invalid Data Record
75     //
76     goto Done;
77   }
78 
79   Index = 0;
80   while(TRUE) {
81     //
82     // Find a matching entry in the conversion table for this
83     // (SubClass, RecordNumber) pair
84     //
85     for (; !CompareGuid (&(mConversionTable[Index].SubClass), &gZeroGuid); Index++) {
86       if (CompareGuid (
87             &(mConversionTable[Index].SubClass),
88             &(RecordHeader->DataRecordGuid)
89             )) {
90         if (mConversionTable[Index].RecordType == DataHeader->RecordType) {
91           break;
92         }
93       }
94     }
95 
96     if (CompareGuid (&(mConversionTable[Index].SubClass), &gZeroGuid)) {
97       //
98       // We cannot find a matching entry in conversion table,
99       // this means this data record cannot be used for SMBIOS.
100       // Just skip it.
101       //
102       goto Done;
103     }
104 
105     Conversion = &mConversionTable[Index++];
106 
107     //
108     // Find corresponding structure in the Structure List
109     //
110     for (Link = mStructureList.ForwardLink; Link != &mStructureList; Link = Link->ForwardLink) {
111 
112       StructureNode = CR (
113                         Link,
114                         SMBIOS_STRUCTURE_NODE,
115                         Link,
116                         SMBIOS_STRUCTURE_NODE_SIGNATURE
117                         );
118 
119       if (Conversion->StructureLocatingMethod == BySubclassInstanceSubinstanceProducer) {
120         //
121         // Look at SubClass, Instance, SubInstance and ProducerName for a matching
122         // node
123         //
124         if (CompareGuid (&(StructureNode->SubClass), &(RecordHeader->DataRecordGuid)) &&
125             StructureNode->Instance == DataHeader->Instance &&
126             StructureNode->SubInstance == DataHeader->SubInstance &&
127             CompareGuid (&(StructureNode->ProducerName), &(RecordHeader->ProducerName))
128               ) {
129           if (Conversion->SmbiosType >= 0x80) {
130             if (StructureNode->SmbiosType == ((SMBIOS_STRUCTURE_HDR *) SrcData)->Type) {
131               break;
132             }
133           } else if (StructureNode->SmbiosType == Conversion->SmbiosType) {
134             break;
135           }
136         }
137 
138       } else if (Conversion->StructureLocatingMethod == BySubClassInstanceProducer) {
139         //
140         // Look at SubClass, Instance and ProducerName for a matching node
141         //
142         if (CompareGuid (&(StructureNode->SubClass), &(RecordHeader->DataRecordGuid)) &&
143             StructureNode->Instance == DataHeader->Instance &&
144             CompareGuid (&(StructureNode->ProducerName), &(RecordHeader->ProducerName))
145               ) {
146           if (Conversion->SmbiosType >= 0x80) {
147             if (StructureNode->SmbiosType == ((SMBIOS_STRUCTURE_HDR *) SrcData)->Type) {
148               break;
149             }
150           } else if (StructureNode->SmbiosType == Conversion->SmbiosType) {
151             break;
152           }
153         }
154 
155       } else {
156         //
157         // Invalid conversion table entry
158         //
159         goto Done;
160       }
161     }
162 
163     if (Link == &mStructureList || StructureNode == NULL) {
164 
165       //
166       // Not found, create a new structure
167       //
168       StructureNode = AllocateZeroPool (sizeof (SMBIOS_STRUCTURE_NODE));
169 
170       if (StructureNode == NULL) {
171         goto Done;
172       }
173 
174       if (Conversion->StructureLocatingMethod == BySubclassInstanceSubinstanceProducer) {
175         //
176         // Fill in SubClass, Instance, SubInstance and ProducerName
177         //
178         CopyMem (&(StructureNode->SubClass), &(RecordHeader->DataRecordGuid), sizeof (EFI_GUID));
179         StructureNode->Instance     = DataHeader->Instance;
180         StructureNode->SubInstance  = DataHeader->SubInstance;
181         CopyMem (&(StructureNode->ProducerName), &(RecordHeader->ProducerName), sizeof (EFI_GUID));
182 
183       } else if (Conversion->StructureLocatingMethod == BySubClassInstanceProducer) {
184         //
185         // Fill in at SubClass, Instance and ProducerName, mark SubInstance as Non
186         // Applicable
187         //
188         CopyMem (&(StructureNode->SubClass), &(RecordHeader->DataRecordGuid), sizeof (EFI_GUID));
189         StructureNode->Instance     = DataHeader->Instance;
190         StructureNode->SubInstance  = EFI_SUBCLASS_INSTANCE_NON_APPLICABLE;
191         CopyMem (&(StructureNode->ProducerName), &(RecordHeader->ProducerName), sizeof (EFI_GUID));
192 
193       }
194       //
195       // Allocate the structure instance
196       //
197       StructureNode->StructureSize = SmbiosGetTypeMinimalLength (Conversion->SmbiosType);
198 
199       //
200       // StructureSize include the TWO trailing zero byte.
201       //
202       if (StructureNode->StructureSize < (sizeof(SMBIOS_STRUCTURE) + 2)) {
203         //
204         // Invalid Type
205         //
206         gBS->FreePool (StructureNode);
207         goto Done;
208       }
209 
210       //
211       // Assign correct SmbiosType when OEM type and Non-OEM type
212       //
213       if (Conversion->SmbiosType >= 0x80) {
214         StructureNode->SmbiosType = ((SMBIOS_STRUCTURE_HDR *) SrcData)->Type;
215       } else {
216         StructureNode->SmbiosType = Conversion->SmbiosType;
217       }
218 
219       StructureNode->SmbiosHandle        = SMBIOS_HANDLE_PI_RESERVED;
220       Status = SmbiosProtocolCreateRecord (
221                  NULL,
222                  StructureNode
223                  );
224       if (EFI_ERROR (Status)) {
225         goto Done;
226       }
227       //
228       // Temporary cache the structrue pointer to Smbios database.
229       //
230       StructureNode->Structure = GetSmbiosBufferFromHandle (StructureNode->SmbiosHandle, StructureNode->SmbiosType, NULL);
231 
232       InitializeListHead (&StructureNode->LinkDataFixup);
233 
234       //
235       // Insert the Structure Node into the Strucutre List
236       //
237       StructureNode->Signature = SMBIOS_STRUCTURE_NODE_SIGNATURE;
238       InsertTailList (&mStructureList, &(StructureNode->Link));
239 
240       StructureCreated = TRUE;
241 
242     }
243 
244 
245     //
246     // Re-calculate the structure pointer to Smbios database.
247     //
248     StructureNode->Structure = GetSmbiosBufferFromHandle (StructureNode->SmbiosHandle, StructureNode->SmbiosType, NULL);
249 
250     //
251     // Fill the Structure's field corresponding to this data record
252     //
253     if (Conversion->FieldFillingMethod == RecordDataUnchangedOffsetSpecified) {
254       //
255       // Field data is just the record data without transforming and
256       // offset is specified directly in the conversion table entry
257       //
258       if (Conversion->FieldOffset + SrcDataSize > StructureNode->Structure->Length) {
259         //
260         // Invalid Conversion Table Entry
261         //
262         if (StructureCreated) {
263           ReleaseStructureNode (StructureNode);
264         }
265 
266         goto Done;
267       }
268 
269       CopyMem ((UINT8 *) (StructureNode->Structure) + Conversion->FieldOffset, SrcData, SrcDataSize);
270 
271     } else if (Conversion->FieldFillingMethod == ByFunctionWithOffsetSpecified) {
272       //
273       // Field offfset is specified in the conversion table entry, but
274       // record data needs to be transformed to be filled into the field,
275       // so let the FieldFillingFunction do it.
276       //
277       if (Conversion->FieldFillingFunction == NULL) {
278         //
279         // Invalid Conversion Table Entry
280         //
281         if (StructureCreated) {
282           ReleaseStructureNode (StructureNode);
283         }
284 
285         goto Done;
286       }
287 
288       Status = Conversion->FieldFillingFunction (
289                             StructureNode,
290                             Conversion->FieldOffset,
291                             SrcData,
292                             (UINT32) SrcDataSize
293                             );
294       if (EFI_ERROR (Status)) {
295         if (StructureCreated) {
296           ReleaseStructureNode (StructureNode);
297         }
298 
299         goto Done;
300       }
301     } else if (Conversion->FieldFillingMethod == ByFunction) {
302       //
303       // Both field offset and field content are determined by
304       // FieldFillingFunction
305       //
306       if (Conversion->FieldFillingFunction == NULL) {
307         //
308         // Invalid Conversion Table Entry
309         //
310         if (StructureCreated) {
311           ReleaseStructureNode (StructureNode);
312         }
313 
314         goto Done;
315       }
316 
317       Status = Conversion->FieldFillingFunction (
318                             StructureNode,
319                             0,
320                             SrcData,
321                             (UINT32) SrcDataSize
322                             );
323       if (EFI_ERROR (Status)) {
324         if (StructureCreated) {
325           ReleaseStructureNode (StructureNode);
326         }
327 
328         goto Done;
329       }
330     } else if (Conversion->FieldFillingMethod == ByFunctionWithWholeDataRecord) {
331       //
332       // Both field offset and field content are determined by
333       // FieldFillingFunction and the function accepts the whole data record
334       // including the data header
335       //
336       if (Conversion->FieldFillingFunction == NULL) {
337         //
338         // Invalid Conversion Table Entry
339         //
340         if (StructureCreated) {
341           ReleaseStructureNode (StructureNode);
342         }
343 
344         goto Done;
345       }
346 
347       Status = Conversion->FieldFillingFunction (
348                             StructureNode,
349                             0,
350                             DataHeader,
351                             RecordHeader->RecordSize - RecordHeader->HeaderSize
352                             );
353       if (EFI_ERROR (Status)) {
354         if (StructureCreated) {
355           ReleaseStructureNode (StructureNode);
356         }
357 
358         goto Done;
359       }
360     } else {
361       //
362       // Invalid Conversion Table Entry
363       //
364       if (StructureCreated) {
365         ReleaseStructureNode (StructureNode);
366       }
367 
368       goto Done;
369     }
370 
371     //
372     // SmbiosEnlargeStructureBuffer is called to remove and add again
373     // this SMBIOS entry to reflash SMBIOS table in configuration table.
374     //
375     SmbiosEnlargeStructureBuffer (
376       StructureNode,
377       StructureNode->Structure->Length,
378       StructureNode->StructureSize,
379       StructureNode->StructureSize
380       );
381   }
382 Done:
383   return ;
384 }
385 
386 /**
387   Calculate the minimal length for a SMBIOS type. This length maybe not equal
388   to sizeof (SMBIOS_RECORD_STRUCTURE), but defined in conformance chapter in SMBIOS specification.
389 
390   @param Type  SMBIOS's type.
391 
392   @return the minimal length of a smbios record.
393 **/
394 UINT32
SmbiosGetTypeMinimalLength(IN UINT8 Type)395 SmbiosGetTypeMinimalLength (
396   IN UINT8  Type
397   )
398 {
399   UINTN Index;
400 
401   for (Index = 0; mTypeInfoTable[Index].MinLength != 0; Index++) {
402     if (mTypeInfoTable[Index].Type == Type) {
403       return mTypeInfoTable[Index].MinLength;
404     }
405   }
406 
407   return 0;
408 }
409 
410 /**
411   Get pointer of EFI_SMBIOS_PROTOCOL.
412 
413   @return pointer of EFI_SMBIOS_PROTOCOL.
414 **/
415 EFI_SMBIOS_PROTOCOL*
GetSmbiosProtocol(VOID)416 GetSmbiosProtocol(
417   VOID
418   )
419 {
420   EFI_STATUS  Status;
421 
422   if (mSmbiosProtocol == NULL) {
423     Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID*) &mSmbiosProtocol);
424     ASSERT_EFI_ERROR (Status);
425   }
426 
427   ASSERT (mSmbiosProtocol != NULL);
428   return mSmbiosProtocol;
429 }
430 
431 /**
432   Create a blank smbios record. The datahub record is only a field of smbios record.
433   So before fill any field from datahub's record. A blank smbios record need to be
434   created.
435 
436   @param ProducerHandle   The produce handle for a datahub record
437   @param StructureNode    Point to SMBIOS_STRUCTURE_NODE
438 
439   @retval EFI_OUT_OF_RESOURCES Fail to allocate memory for new blank SMBIOS record.
440   @retval EFI_SUCCESS          Success to create blank smbios record.
441 **/
442 EFI_STATUS
SmbiosProtocolCreateRecord(IN EFI_HANDLE ProducerHandle,OPTIONAL IN SMBIOS_STRUCTURE_NODE * StructureNode)443 SmbiosProtocolCreateRecord (
444   IN      EFI_HANDLE              ProducerHandle, OPTIONAL
445   IN      SMBIOS_STRUCTURE_NODE   *StructureNode
446   )
447 {
448   EFI_SMBIOS_PROTOCOL         *Smbios;
449   EFI_SMBIOS_TABLE_HEADER     *BlankRecord;
450   EFI_STATUS                  Status;
451   SMBIOS_STRUCTURE_NODE       *RefStructureNode;
452   LIST_ENTRY                  *Link;
453   LIST_ENTRY                  *Link1;
454   LIST_ENTRY                  *Link2;
455   SMBIOS_LINK_DATA_FIXUP_NODE *LinkDataFixupNode;
456   UINT8                       *BufferPointer;
457 
458   Smbios = GetSmbiosProtocol();
459   ASSERT (Smbios != NULL);
460 
461   //
462   // Prepare a blank smbios record.
463   //
464   BlankRecord = (EFI_SMBIOS_TABLE_HEADER*) AllocateZeroPool (StructureNode->StructureSize);
465   if (BlankRecord == NULL) {
466     return EFI_OUT_OF_RESOURCES;
467   }
468   BlankRecord->Type   = StructureNode->SmbiosType;
469   BlankRecord->Length = (UINT8) (StructureNode->StructureSize - 2);
470 
471   //
472   // Add blank record into SMBIOS database.
473   //
474   Status = Smbios->Add (Smbios, NULL, &StructureNode->SmbiosHandle, BlankRecord);
475   FreePool (BlankRecord);
476 
477   //
478   // Fix up the InterLink node for new added smbios record if some other
479   // existing smbios record want to link this new record's handle.
480   //
481   for (Link = mStructureList.ForwardLink; Link != &mStructureList; Link = Link->ForwardLink) {
482     RefStructureNode = CR (Link, SMBIOS_STRUCTURE_NODE, Link, SMBIOS_STRUCTURE_NODE_SIGNATURE);
483     for (Link1 = RefStructureNode->LinkDataFixup.ForwardLink; Link1 != &RefStructureNode->LinkDataFixup;) {
484       LinkDataFixupNode = CR (Link1, SMBIOS_LINK_DATA_FIXUP_NODE, Link, SMBIOS_LINK_DATA_FIXUP_NODE_SIGNATURE);
485       Link2 = Link1;
486       Link1 = Link1->ForwardLink;
487 
488       if ((StructureNode->SmbiosType != LinkDataFixupNode->TargetType) ||
489           !(CompareGuid (&StructureNode->SubClass, &LinkDataFixupNode->SubClass)) ||
490           (StructureNode->Instance != LinkDataFixupNode->LinkData.Instance) ||
491           (StructureNode->SubInstance != LinkDataFixupNode->LinkData.SubInstance)) {
492         continue;
493       }
494 
495       //
496       // Fill the field with the handle found
497       //
498       BufferPointer         = (UINT8 *) (RefStructureNode->Structure) + LinkDataFixupNode->Offset;
499       *BufferPointer        = (UINT8) (StructureNode->SmbiosHandle & 0xFF);
500       *(BufferPointer + 1)  = (UINT8) ((StructureNode->SmbiosHandle >> 8) & 0xFF);
501       BufferPointer         = NULL;
502 
503       RemoveEntryList (Link2);
504       FreePool (LinkDataFixupNode);
505     }
506   }
507 
508   return Status;
509 }
510 
511 /**
512   Get pointer of a SMBIOS record's buffer according to its handle.
513 
514   @param Handle         The handle of SMBIOS record want to be searched.
515   @param Type           The type of SMBIOS record want to be searched.
516   @param ProducerHandle The producer handle of SMBIOS record.
517 
518   @return EFI_SMBIOS_TABLE_HEADER Point to a SMBIOS record's buffer.
519 **/
520 EFI_SMBIOS_TABLE_HEADER*
GetSmbiosBufferFromHandle(IN EFI_SMBIOS_HANDLE Handle,IN EFI_SMBIOS_TYPE Type,IN EFI_HANDLE ProducerHandle OPTIONAL)521 GetSmbiosBufferFromHandle (
522   IN  EFI_SMBIOS_HANDLE  Handle,
523   IN  EFI_SMBIOS_TYPE    Type,
524   IN  EFI_HANDLE         ProducerHandle  OPTIONAL
525   )
526 {
527   EFI_SMBIOS_PROTOCOL*    Smbios;
528   EFI_SMBIOS_HANDLE       SearchingHandle;
529   EFI_SMBIOS_TABLE_HEADER *RecordInSmbiosDatabase;
530   EFI_STATUS              Status;
531 
532   SearchingHandle = SMBIOS_HANDLE_PI_RESERVED;
533   Smbios          = GetSmbiosProtocol();
534   ASSERT (Smbios != NULL);
535 
536   do {
537     Status = Smbios->GetNext (Smbios, &SearchingHandle, &Type, &RecordInSmbiosDatabase, NULL);
538   } while ((SearchingHandle != Handle) && (Status != EFI_NOT_FOUND));
539 
540   return RecordInSmbiosDatabase;
541 }
542 
543 /**
544 
545   Get the full size of smbios structure including optional strings that follow the formatted structure.
546 
547   @param Head                   Pointer to the beginning of smbios structure.
548   @param Size                   The returned size.
549   @param NumberOfStrings        The returned number of optional strings that follow the formatted structure.
550 
551   @retval EFI_SUCCESS           Size retured in Size.
552   @retval EFI_INVALID_PARAMETER Input smbios structure mal-formed or Size is NULL.
553 
554 **/
555 EFI_STATUS
556 EFIAPI
GetSmbiosStructureSize(IN EFI_SMBIOS_TABLE_HEADER * Head,OUT UINT32 * Size,OUT UINT8 * NumberOfStrings)557 GetSmbiosStructureSize (
558   IN   EFI_SMBIOS_TABLE_HEADER          *Head,
559   OUT  UINT32                           *Size,
560   OUT  UINT8                            *NumberOfStrings
561   )
562 {
563   UINT32 FullSize;
564   UINT8  StrLen;
565   INT8*  CharInStr;
566 
567   if (Size == NULL || NumberOfStrings == NULL) {
568     return EFI_INVALID_PARAMETER;
569   }
570 
571   FullSize = Head->Length;
572   CharInStr = (INT8*)Head + Head->Length;
573   *Size = FullSize;
574   *NumberOfStrings = 0;
575   StrLen = 0;
576   //
577   // look for the two consecutive zeros, check the string limit by the way.
578   //
579   while (*CharInStr != 0 || *(CharInStr+1) != 0) {
580     if (*CharInStr == 0) {
581       *Size += 1;
582       CharInStr++;
583     }
584 
585     for (StrLen = 0 ; StrLen < SMBIOS_STRING_MAX_LENGTH; StrLen++) {
586       if (*(CharInStr+StrLen) == 0) {
587         break;
588       }
589     }
590 
591     if (StrLen == SMBIOS_STRING_MAX_LENGTH) {
592       return EFI_INVALID_PARAMETER;
593     }
594     //
595     // forward the pointer
596     //
597     CharInStr += StrLen;
598     *Size += StrLen;
599     *NumberOfStrings += 1;
600 
601   }
602 
603   //
604   // count ending two zeros.
605   //
606   *Size += 2;
607   return EFI_SUCCESS;
608 }
609